153 lines
3.2 KiB
Vue
153 lines
3.2 KiB
Vue
<script setup lang="ts">
|
|
import { actInBattle, closeBattle, useGameState } from '../game/state'
|
|
|
|
const gameState = useGameState()
|
|
|
|
const runAction = (action: 'attack' | 'skill' | 'defend' | 'item') => {
|
|
actInBattle(action)
|
|
}
|
|
</script>
|
|
|
|
<template>
|
|
<div v-if="gameState.battle.active" class="battle-overlay">
|
|
<section class="battle-card">
|
|
<header class="battle-head">
|
|
<h2>战斗中:{{ gameState.battle.enemyName }}</h2>
|
|
<p>{{ gameState.battle.message }}</p>
|
|
</header>
|
|
|
|
<div class="bars">
|
|
<div>
|
|
<strong>我方</strong>
|
|
<p>HP {{ gameState.hp }} | MP {{ gameState.mp }} | 药草 {{ gameState.player.potions }}</p>
|
|
</div>
|
|
<div>
|
|
<strong>敌方</strong>
|
|
<p>HP {{ gameState.battle.enemyHp }} / {{ gameState.battle.enemyMaxHp }}</p>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="actions" v-if="gameState.battle.turn !== 'resolved'">
|
|
<button :disabled="gameState.battle.turn !== 'player'" @click="runAction('attack')">攻击</button>
|
|
<button :disabled="gameState.battle.turn !== 'player'" @click="runAction('skill')">技能</button>
|
|
<button :disabled="gameState.battle.turn !== 'player'" @click="runAction('defend')">防御</button>
|
|
<button :disabled="gameState.battle.turn !== 'player'" @click="runAction('item')">道具</button>
|
|
</div>
|
|
|
|
<div class="result" v-else>
|
|
<p>{{ gameState.battle.victory ? '战斗胜利' : '战斗失败' }}</p>
|
|
<button @click="closeBattle">返回探索</button>
|
|
</div>
|
|
|
|
<ul class="log">
|
|
<li v-for="(line, index) in gameState.battle.log" :key="`${index}-${line}`">{{ line }}</li>
|
|
</ul>
|
|
</section>
|
|
</div>
|
|
</template>
|
|
|
|
<style scoped>
|
|
.battle-overlay {
|
|
position: fixed;
|
|
inset: 0;
|
|
background: rgba(6, 10, 14, 0.78);
|
|
display: grid;
|
|
place-items: center;
|
|
z-index: 50;
|
|
padding: 14px;
|
|
}
|
|
|
|
.battle-card {
|
|
width: min(760px, 100%);
|
|
border-radius: 12px;
|
|
border: 1px solid #365064;
|
|
background: linear-gradient(180deg, #12202a, #0f1921);
|
|
padding: 14px;
|
|
}
|
|
|
|
.battle-head h2 {
|
|
margin: 0;
|
|
font-size: 20px;
|
|
}
|
|
|
|
.battle-head p {
|
|
margin: 6px 0 0;
|
|
color: #9cc0d6;
|
|
}
|
|
|
|
.bars {
|
|
margin-top: 12px;
|
|
display: grid;
|
|
grid-template-columns: 1fr 1fr;
|
|
gap: 8px;
|
|
}
|
|
|
|
.bars > div {
|
|
border: 1px solid #2f4555;
|
|
border-radius: 8px;
|
|
padding: 10px;
|
|
background: #172632;
|
|
}
|
|
|
|
.bars p {
|
|
margin: 6px 0 0;
|
|
font-size: 13px;
|
|
}
|
|
|
|
.actions {
|
|
margin-top: 12px;
|
|
display: grid;
|
|
grid-template-columns: repeat(4, 1fr);
|
|
gap: 8px;
|
|
}
|
|
|
|
button {
|
|
border: 1px solid #42627a;
|
|
border-radius: 8px;
|
|
background: #203646;
|
|
color: #d7e0e6;
|
|
padding: 10px;
|
|
cursor: pointer;
|
|
}
|
|
|
|
button:disabled {
|
|
opacity: 0.5;
|
|
cursor: not-allowed;
|
|
}
|
|
|
|
.result {
|
|
margin-top: 12px;
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: space-between;
|
|
}
|
|
|
|
.result p {
|
|
margin: 0;
|
|
}
|
|
|
|
.log {
|
|
margin: 12px 0 0;
|
|
border: 1px solid #2f4555;
|
|
border-radius: 8px;
|
|
background: #111d27;
|
|
padding: 10px 12px;
|
|
list-style: none;
|
|
display: grid;
|
|
gap: 6px;
|
|
max-height: 150px;
|
|
overflow: auto;
|
|
font-size: 13px;
|
|
}
|
|
|
|
@media (max-width: 720px) {
|
|
.bars {
|
|
grid-template-columns: 1fr;
|
|
}
|
|
|
|
.actions {
|
|
grid-template-columns: repeat(2, 1fr);
|
|
}
|
|
}
|
|
</style>
|