同伴当前HP */ private array $partnerHp = []; // 法术数据 private array $spellsData = []; private array $qualityColors = []; // 颜色定义 (use Colors constants) private string $red; private string $green; private string $yellow; private string $cyan; private string $white; private string $magenta; private string $bold; private string $reset; private int $round = 0; private int $totalMaxHp = 0; public function __construct(public Game $game) { $this->player = $game->player; $this->spellsData = require __DIR__ . '/../../src/Data/spells.php'; // Initialize color palette from centralized Colors $this->red = Colors::RED; $this->green = Colors::GREEN; $this->yellow = Colors::YELLOW; $this->cyan = Colors::CYAN; $this->white = Colors::WHITE; $this->magenta = Colors::MAGENTA; $this->bold = Colors::BOLD; $this->reset = Colors::RESET; $this->qualityColors = [ 'common' => Colors::WHITE, 'rare' => Colors::BLUE, 'epic' => Colors::MAGENTA, 'legendary' => Colors::YELLOW, ]; } /** * 初始化同伴HP */ private function initPartnerHp(): void { $this->partnerHp = []; foreach ($this->player->partners as $partner) { // 从Partner对象的hp属性读取,允许队友在战斗外也能恢复 $this->partnerHp[$partner->id] = $partner->hp; } } /** * 获取存活的同伴 */ private function getAlivePartners(): array { $alive = []; foreach ($this->player->partners as $partner) { if (($this->partnerHp[$partner->id] ?? 0) > 0) { $alive[] = $partner; } } return $alive; } /** * 将战斗中的队友HP同步回Partner对象 */ private function syncPartnerHp(): void { foreach ($this->player->partners as $partner) { $partner->hp = $this->partnerHp[$partner->id] ?? 0; } } /** * 获取存活的敌人 * @return Actor[] */ private function getAliveEnemies(): array { $alive = []; foreach ($this->enemies as $enemy) { if ($enemy->hp > 0) { $alive[] = $enemy; } } return $alive; } public function start() { $out = $this->game->output; // 初始化同伴HP $this->initPartnerHp(); while ($this->player->hp >= 0) { Screen::delay(500000); // 创建敌人群组 $this->enemies = Monster::createGroup($this->game->dungeonId); $this->totalMaxHp = 0; foreach ($this->enemies as $enemy) { $this->totalMaxHp += $enemy->hp; } $this->round = 0; // 显示遭遇界面 $this->showEncounter($out); $playerFirst = $this->determineFirstStrike(); // 战斗循环 while (true) { if ($this->checkExit($out)) { $this->syncPartnerHp(); return; } $this->round++; $this->renderBattleScreen($out, $playerFirst); Screen::delay(800000); // 回合开始停顿 if ($playerFirst) { // 玩家攻击 $result = $this->playerAttack($out); Screen::delay(800000); if ($result) break; // 同伴攻击 $result = $this->partnersAttack($out); Screen::delay(800000); if ($result) break; if ($this->checkExit($out)) { $this->syncPartnerHp(); return; } // 怪物攻击 if ($this->enemiesAttack($out)) { Screen::delay(1000000); $this->syncPartnerHp(); return; } Screen::delay(800000); } else { // 怪物先攻 if ($this->enemiesAttack($out)) { Screen::delay(1000000); $this->syncPartnerHp(); return; } Screen::delay(800000); if ($this->checkExit($out)) { $this->syncPartnerHp(); return; } // 玩家攻击 $result = $this->playerAttack($out); Screen::delay(800000); if ($result) break; // 同伴攻击 $result = $this->partnersAttack($out); Screen::delay(800000); if ($result) break; } Screen::delay(500000); // 同步队友HP到Partner对象,然后保存状态 $this->syncPartnerHp(); $this->game->saveState(); } } } private function showEncounter($out) { Screen::clear($out); $out->writeln(""); $out->writeln("{$this->yellow}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━{$this->reset}"); $out->writeln(""); $out->writeln(" {$this->red}⚔️ 遭遇敌人!{$this->reset}"); $out->writeln(""); foreach ($this->enemies as $enemy) { $out->writeln(" {$this->bold}{$this->white}{$enemy->name}{$this->reset} {$this->cyan}Lv.{$enemy->level}{$this->reset}"); } $out->writeln(""); $out->writeln("{$this->yellow}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━{$this->reset}"); Screen::delay(1000000); // 1秒 } private function renderBattleScreen($out, bool $playerFirst) { Screen::clear($out); $stats = $this->player->getStats(); $out->writeln("{$this->cyan}╔══════════════════════════════════════════╗{$this->reset}"); $out->writeln("{$this->cyan}║{$this->reset} {$this->bold}第 {$this->round} 回合{$this->reset} {$this->white}[q] 逃跑{$this->reset} {$this->cyan}║{$this->reset}"); $out->writeln("{$this->cyan}╠══════════════════════════════════════════╣{$this->reset}"); // 敌人信息 foreach ($this->enemies as $enemy) { if ($enemy->hp <= 0) { $out->writeln("{$this->cyan}║{$this->reset} {$this->red}💀{$this->reset} {$this->white}{$enemy->name}{$this->reset} {$this->red}[已击败]{$this->reset}"); continue; } $enemyStats = $enemy->getStats(); $enemyMaxHp = $enemyStats['maxHp']; $enemyHpPercent = $enemyMaxHp > 0 ? $enemy->hp / $enemyMaxHp : 0; $enemyHpBar = $this->renderHpBar($enemyHpPercent, 15); $enemyHpText = $enemy->hp . "/" . $enemyMaxHp; $out->writeln("{$this->cyan}║{$this->reset} {$this->red}👹{$this->reset} {$this->bold}{$enemy->name}{$this->reset} Lv.{$enemy->level}"); $out->writeln("{$this->cyan}║{$this->reset} {$enemyHpBar} {$this->white}{$enemyHpText}{$this->reset}"); $out->writeln("{$this->cyan}║{$this->reset} {$this->yellow}⚔️{$this->reset} {$enemyStats['patk']}/{$enemyStats['matk']} {$this->green}🛡️{$this->reset} {$enemyStats['pdef']}/{$enemyStats['mdef']} {$this->red}💥{$this->reset} {$enemyStats['crit']}/{$enemyStats['critdmg']}%"); } $out->writeln("{$this->cyan}║{$this->reset}"); // VS 分隔 $out->writeln("{$this->cyan}║{$this->reset} {$this->yellow}⚔️ VS ⚔️{$this->reset}"); $out->writeln("{$this->cyan}║{$this->reset}"); // 玩家信息 $playerHpPercent = $this->player->hp / $stats['maxHp']; $playerHpBar = $this->renderHpBar($playerHpPercent, 20); $playerHpText = $this->player->hp . "/" . $stats['maxHp']; $out->writeln("{$this->cyan}║{$this->reset} {$this->green}🧙{$this->reset} {$this->bold}玩家{$this->reset} Lv.{$this->player->level}"); $out->writeln("{$this->cyan}║{$this->reset} {$playerHpBar} {$this->white}{$playerHpText}{$this->reset}"); $out->writeln("{$this->cyan}║{$this->reset} {$this->yellow}⚔️{$this->reset} {$stats['patk']}/{$stats['matk']} {$this->green}🛡️{$this->reset} {$stats['pdef']}/{$stats['mdef']} {$this->red}💥{$this->reset} {$stats['crit']}/{$stats['critdmg']}%"); // 显示同伴信息 foreach ($this->player->partners as $partner) { $partnerStats = $partner->getStats(); $partnerHp = $this->partnerHp[$partner->id] ?? 0; $partnerMaxHp = $partnerStats['maxHp']; $partnerHpPercent = $partnerMaxHp > 0 ? $partnerHp / $partnerMaxHp : 0; $partnerHpBar = $this->renderHpBar($partnerHpPercent, 15); $partnerHpText = $partnerHp . "/" . $partnerMaxHp; $status = $partnerHp > 0 ? "" : " {$this->red}[倒下]{$this->reset}"; $out->writeln("{$this->cyan}║{$this->reset} {$this->magenta}👤{$this->reset} {$partner->name} Lv.{$partner->level}{$status}"); $out->writeln("{$this->cyan}║{$this->reset} {$partnerHpBar} {$this->white}{$partnerHpText}{$this->reset}"); $out->writeln("{$this->cyan}║{$this->reset} {$this->yellow}⚔️{$this->reset} {$partnerStats['patk']}/{$partnerStats['matk']} {$this->green}🛡️{$this->reset} {$partnerStats['pdef']}/{$partnerStats['mdef']} {$this->red}💥{$this->reset} {$partnerStats['crit']}/{$partnerStats['critdmg']}%"); } $out->writeln("{$this->cyan}╠══════════════════════════════════════════╣{$this->reset}"); $out->writeln("{$this->cyan}║{$this->reset} {$this->white}战斗日志:{$this->reset}"); } private function renderHpBar(float $percent, int $width): string { $filled = (int)($percent * $width); $empty = max($width - $filled,0); // 根据血量百分比选择颜色 if ($percent > 0.6) { $color = $this->green; } elseif ($percent > 0.3) { $color = $this->yellow; } else { $color = $this->red; } $bar = $color . str_repeat("█", $filled) . $this->white . str_repeat("░", $empty) . $this->reset; return "[" . $bar . "]"; } private function determineFirstStrike(): bool { // Use the leader's level for comparison $leader = $this->enemies[count($this->enemies) - 1]; // Assume leader is last or first? // In createGroup we put minions first, so leader is last. // Let's just use the highest level enemy. $maxLevel = 0; foreach ($this->enemies as $e) { if ($e->level > $maxLevel) $maxLevel = $e->level; } $levelDiff = $this->player->level - $maxLevel; $playerChance = 50; $levelBonus = max(-30, min(30, $levelDiff * 5)); $playerChance += $levelBonus; $roll = rand(1, 100); return $roll <= $playerChance; } /** * 玩家选择行动类型 */ private function playerChooseAction(): string { // 检查是否有装备的法术 $hasSpells = false; foreach ($this->player->skillSlots as $slot => $spell) { if ($spell !== null) { $hasSpells = true; break; } } if (!$hasSpells || $this->player->mana < 15) { return 'attack'; } // 如果玩家有法术且魔法值充足,40% 概率选择法术 $spellChance = rand(1, 100); if ($spellChance <= 40) { return 'spell'; } return 'attack'; } /** * 玩家施放法术 */ private function playerCastSpell($out): bool { $stats = $this->player->getStats(); // 筛选可用法术 $availableSpells = []; foreach ($this->player->skillSlots as $slot => $spellItem) { if ($spellItem === null) continue; $baseCost = $spellItem['cost'] ?? 20; $enhanceLevel = $spellItem['enhanceLevel'] ?? 0; // 强化减少消耗: 每级 -2 $costReduction = $enhanceLevel * 2; $actualCost = max(1, $baseCost - $costReduction); if ($this->player->mana >= $actualCost) { $availableSpells[] = [ 'item' => $spellItem, 'cost' => $actualCost, 'level' => $enhanceLevel ]; } } if (empty($availableSpells)) { // 如果没有可用的法术,改用普通攻击 $out->writeln("{$this->cyan}║{$this->reset} {$this->yellow}✦ 魔法值不足,改为普通攻击{$this->reset}"); return $this->executePhysicalAttack($out); } // 随机选择一个可用的法术 $selected = $availableSpells[array_rand($availableSpells)]; $spellItem = $selected['item']; $actualCost = $selected['cost']; $enhanceLevel = $selected['level']; // 消耗魔法值 $this->player->spendMana($actualCost); // 计算强化加成: 每级 +5% 伤害或治疗 $damageBonus = $enhanceLevel * 5; $type = $spellItem['spellType'] ?? 'damage_single'; $name = $spellItem['name'] ?? '未知法术'; // 构造兼容旧方法的 spellInfo $spellInfo = $spellItem; $spellInfo['type'] = $type; // 映射 spellType 到 type 以兼容 if ($type === 'damage_single') { return $this->castDamageSingleSpell($out, 0, $spellInfo, $stats, $damageBonus, $name); } elseif ($type === 'damage_aoe') { return $this->castDamageAoeSpell($out, 0, $spellInfo, $stats, $damageBonus, $name); } elseif ($type === 'heal_single') { return $this->castHealSingleSpell($out, $spellInfo, $stats, $damageBonus, $name); } elseif ($type === 'heal_aoe') { return $this->castHealAoeSpell($out, $spellInfo, $stats, $damageBonus, $name); } elseif ($type === 'support') { // 兼容旧版本的 support 类型 return $this->castSupportSpell($out, $spellInfo, $stats, $name); } return false; } /** * 施放单体伤害法术 */ private function castDamageSingleSpell($out, int $spellId, array $spellInfo, array $stats, int $damageBonus, string $name): bool { // 选择目标 $target = null; foreach ($this->enemies as $enemy) { if ($enemy->hp > 0) { $target = $enemy; break; } } if (!$target) return true; // 显示法术基础信息 $calcType = $spellInfo['calc_type'] ?? 'matk'; $calcDesc = SpellDisplay::getCalcTypeDescription($calcType); $cost = $spellInfo['cost'] ?? 20; $actualCost = max(1, $cost - (($spellInfo['enhanceLevel'] ?? 0) * 2)); $quality = $spellInfo['quality'] ?? 'common'; $qualityColor = SpellDisplay::getQualityColor($quality); // 计算法术伤害 $baseDamageMultiplier = $spellInfo['damage_ratio'][$this->getQualityIndex($quality)] ?? ($spellInfo['damage'] ?? 1.0); $actualDamageMultiplier = $baseDamageMultiplier * (1 + $damageBonus / 100); $baseDamage = (int)($stats['matk'] * $actualDamageMultiplier); // 计算抵抗 $resistance = $target->mdef; $damage = max(5, $baseDamage - $resistance); // 暴击机制同样适用 $critRate = $stats['crit']; $isCrit = rand(1, 100) <= $critRate; // 显示法术施放信息 $out->writeln("{$this->cyan}║{$this->reset} {$this->magenta}✦{$this->reset} 你施放 {$qualityColor}{$name}{$this->reset}"); $out->writeln("{$this->cyan}║{$this->reset} {$this->white}计算方式: {$calcDesc} | 消耗: {$actualCost} | 倍数: {$baseDamageMultiplier}x{$this->reset}"); if ($isCrit) { $critDmg = $stats['critdmg']; $damage = (int)($damage * ($critDmg / 100)); $out->writeln("{$this->cyan}║{$this->reset} {$this->magenta}✨ {$this->red}{$this->bold}暴击!{$this->reset} 造成 {$this->red}{$damage}{$this->reset} 点魔法伤害!"); } else { $out->writeln("{$this->cyan}║{$this->reset} {$this->magenta}✨ 造成 {$this->green}{$damage}{$this->reset} 点魔法伤害"); } $target->hp -= $damage; if ($target->hp <= 0) { $target->hp = 0; $out->writeln("{$this->cyan}║{$this->reset} {$this->red}💀 {$target->name} 被击败了!{$this->reset}"); if (empty($this->getAliveEnemies())) { Screen::delay(500000); $this->showVictory($out, $stats); return true; } } return false; } /** * 施放AOE伤害法术 */ private function castDamageAoeSpell($out, int $spellId, array $spellInfo, array $stats, int $damageBonus, string $name): bool { // 显示法术基础信息 $calcType = $spellInfo['calc_type'] ?? 'matk'; $calcDesc = SpellDisplay::getCalcTypeDescription($calcType); $cost = $spellInfo['cost'] ?? 20; $actualCost = max(1, $cost - (($spellInfo['enhanceLevel'] ?? 0) * 2)); $quality = $spellInfo['quality'] ?? 'common'; $qualityColor = SpellDisplay::getQualityColor($quality); $out->writeln("{$this->cyan}║{$this->reset} {$this->magenta}✦{$this->reset} 你施放 {$qualityColor}{$name}{$this->reset}"); $out->writeln("{$this->cyan}║{$this->reset} {$this->white}计算方式: {$calcDesc} | 消耗: {$actualCost}{$this->reset}"); $out->writeln("{$this->cyan}║{$this->reset} {$this->magenta}✨ 魔法在整个战场爆炸!{$this->reset}"); // 计算法术伤害 $baseDamageMultiplier = $spellInfo['damage_ratio'][$this->getQualityIndex($quality)] ?? ($spellInfo['damage'] ?? 0.8); $actualDamageMultiplier = $baseDamageMultiplier * (1 + $damageBonus / 100); $allEnemiesDefeated = true; foreach ($this->enemies as $enemy) { if ($enemy->hp <= 0) continue; $baseDamage = (int)($stats['matk'] * $actualDamageMultiplier); $resistance = $enemy->mdef; $damage = max(5, $baseDamage - $resistance); // AOE 法术也可以暴击 $critRate = $stats['crit']; $isCrit = rand(1, 100) <= ($critRate / 2); // AOE 暴击率减半 if ($isCrit) { $critDmg = $stats['critdmg']; $damage = (int)($damage * ($critDmg / 100)); } $out->writeln("{$this->cyan}║{$this->reset} {$enemy->name} 受到 {$damage} 点伤害"); $enemy->hp -= $damage; if ($enemy->hp <= 0) { $enemy->hp = 0; $out->writeln("{$this->cyan}║{$this->reset} {$this->red}💀 {$enemy->name} 被击败了!{$this->reset}"); } else { $allEnemiesDefeated = false; } } if ($allEnemiesDefeated && empty($this->getAliveEnemies())) { Screen::delay(500000); $this->showVictory($out, $stats); return true; } return false; } /** * 施放单体治疗法术 */ private function castHealSingleSpell($out, array $spellInfo, array $stats, int $healBonus, string $name): bool { // 显示法术基础信息 $calcType = $spellInfo['calc_type'] ?? 'matk'; $calcDesc = SpellDisplay::getCalcTypeDescription($calcType); $cost = $spellInfo['cost'] ?? 20; $quality = $spellInfo['quality'] ?? 'common'; $qualityColor = SpellDisplay::getQualityColor($quality); $actualCost = max(1, $cost - (($spellInfo['enhanceLevel'] ?? 0) * 2)); $out->writeln("{$this->cyan}║{$this->reset} {$this->green}✦{$this->reset} 你施放 {$qualityColor}{$name}{$this->reset}"); $out->writeln("{$this->cyan}║{$this->reset} {$this->white}计算方式: {$calcDesc} | 消耗: {$actualCost}{$this->reset}"); $heal = $spellInfo['heal'] ?? 0.5; $healBase = $spellInfo['heal_base'] ?? 20; $healAmount = (int)($stats['matk'] * $heal + $healBase); // 应用治疗加成 $healAmount = (int)($healAmount * (1 + $healBonus / 100)); $actualHeal = $this->player->heal($healAmount); $out->writeln("{$this->cyan}║{$this->reset} {$this->green}💚 恢复了 {$actualHeal} 点生命值{$this->reset}"); return false; } /** * 施放群体治疗法术 */ private function castHealAoeSpell($out, array $spellInfo, array $stats, int $healBonus, string $name): bool { // 显示法术基础信息 $calcType = $spellInfo['calc_type'] ?? 'matk'; $calcDesc = SpellDisplay::getCalcTypeDescription($calcType); $cost = $spellInfo['cost'] ?? 20; $quality = $spellInfo['quality'] ?? 'common'; $qualityColor = SpellDisplay::getQualityColor($quality); $actualCost = max(1, $cost - (($spellInfo['enhanceLevel'] ?? 0) * 2)); $out->writeln("{$this->cyan}║{$this->reset} {$this->green}✦{$this->reset} 你施放 {$qualityColor}{$name}{$this->reset}"); $out->writeln("{$this->cyan}║{$this->reset} {$this->white}计算方式: {$calcDesc} | 消耗: {$actualCost}{$this->reset}"); $heal = $spellInfo['heal'] ?? 0.5; $healBase = $spellInfo['heal_base'] ?? 20; $healAmount = (int)($stats['matk'] * $heal + $healBase); // 应用治疗加成 $healAmount = (int)($healAmount * (1 + $healBonus / 100)); $actualHeal = $this->player->heal($healAmount); $out->writeln("{$this->cyan}║{$this->reset} {$this->green}💚 你恢复了 {$actualHeal} 点生命值{$this->reset}"); // 同伴也恢复 $alivePartners = $this->getAlivePartners(); foreach ($alivePartners as $partner) { $partnerHeal = (int)($healAmount * 0.8); // 同伴恢复量为玩家的80% $actualPartnerHeal = $partner->heal($partnerHeal); $this->partnerHp[$partner->id] = $partner->hp; $out->writeln("{$this->cyan}║{$this->reset} {$this->green}💚 {$partner->name} 恢复了 {$actualPartnerHeal} 点生命值{$this->reset}"); } return false; } /** * 施放辅助法术 */ private function castSupportSpell($out, array $spellInfo, array $stats, string $name): bool { // 显示法术基础信息 $quality = $spellInfo['quality'] ?? 'common'; $qualityColor = SpellDisplay::getQualityColor($quality); $cost = $spellInfo['cost'] ?? 20; $actualCost = max(1, $cost - (($spellInfo['enhanceLevel'] ?? 0) * 2)); $out->writeln("{$this->cyan}║{$this->reset} {$this->cyan}✦{$this->reset} 你施放 {$qualityColor}{$name}{$this->reset}"); $out->writeln("{$this->cyan}║{$this->reset} {$this->white}消耗: {$actualCost}{$this->reset}"); $subtype = $spellInfo['subtype'] ?? ''; if ($subtype === 'heal' || $subtype === 'heal_all') { if ($subtype === 'heal') { // 恢复自己 $heal = $spellInfo['heal'] ?? 0.5; $healBase = $spellInfo['heal_base'] ?? 20; $healAmount = (int)($stats['matk'] * $heal + $healBase); $actualHeal = $this->player->heal($healAmount); $out->writeln("{$this->cyan}║{$this->reset} {$this->green}✦{$this->reset} 你施放 {$name}..."); $out->writeln("{$this->cyan}║{$this->reset} {$this->green}💚 恢复了 {$actualHeal} 点生命值{$this->reset}"); } else { // 恢复全体 $heal = $spellInfo['heal'] ?? 0.6; $healBase = $spellInfo['heal_base'] ?? 40; $healAmount = (int)($stats['matk'] * $heal + $healBase); $out->writeln("{$this->cyan}║{$this->reset} {$this->green}✦{$this->reset} 你施放 {$name}..."); $actualHeal = $this->player->heal($healAmount); $out->writeln("{$this->cyan}║{$this->reset} {$this->green}💚 你恢复了 {$actualHeal} 点生命值{$this->reset}"); // 同伴也恢复 $alivePartners = $this->getAlivePartners(); foreach ($alivePartners as $partner) { $partnerHeal = (int)($healAmount * 0.8); // 同伴恢复量为玩家的80% $actualPartnerHeal = $partner->heal($partnerHeal); $this->partnerHp[$partner->id] = $partner->hp; $out->writeln("{$this->cyan}║{$this->reset} {$this->green}💚 {$partner->name} 恢复了 {$actualPartnerHeal} 点生命值{$this->reset}"); } } } elseif ($subtype === 'defend') { $defenseBoost = $spellInfo['defense_boost'] ?? 30; $out->writeln("{$this->cyan}║{$this->reset} {$this->cyan}✦{$this->reset} 你施放 {$name}..."); $out->writeln("{$this->cyan}║{$this->reset} {$this->cyan}🛡️ 防御力提升!{$this->reset}"); } return false; } /** * 执行普通物理攻击 */ private function executePhysicalAttack($out): bool { $stats = $this->player->getStats(); $target = null; foreach ($this->enemies as $enemy) { if ($enemy->hp > 0) { $target = $enemy; break; } } if (!$target) return true; $physicalDamage = max(1, $stats['patk'] - $target->pdef); $magicDamage = max(0, $stats['matk'] - $target->mdef); $baseDamage = $physicalDamage + $magicDamage; $critRate = $stats['crit']; $critDmg = $stats['critdmg']; $isCrit = rand(1, 100) <= $critRate; if ($isCrit) { $damage = (int)($baseDamage * ($critDmg / 100)); $out->writeln("{$this->cyan}║{$this->reset} {$this->green}➤{$this->reset} 你攻击 {$target->name}... {$this->red}{$this->bold}暴击!{$this->reset}"); $out->writeln("{$this->cyan}║{$this->reset} {$this->red}💥 造成 {$damage} 点伤害!{$this->reset}"); } else { $damage = $baseDamage; $out->writeln("{$this->cyan}║{$this->reset} {$this->green}➤{$this->reset} 你攻击 {$target->name}..."); $out->writeln("{$this->cyan}║{$this->reset} {$this->white}⚔️ 造成 {$damage} 点伤害{$this->reset}"); } $target->hp -= $damage; if ($target->hp <= 0) { $target->hp = 0; $out->writeln("{$this->cyan}║{$this->reset} {$this->red}💀 {$target->name} 被击败了!{$this->reset}"); if (empty($this->getAliveEnemies())) { Screen::delay(500000); $this->showVictory($out, $stats); return true; } } return false; } /** * 获取法术信息 */ private function getSpellInfo(int $spellId): ?array { foreach ($this->spellsData as $category => $spells) { if (is_array($spells) && $category !== 'quality_levels' && $category !== 'upgrades') { if (isset($spells[$spellId])) { return $spells[$spellId]; } } } return null; } /** * 根据品质获取数组索引 */ private function getQualityIndex(string $quality): int { return match($quality) { 'common' => 0, 'rare' => 1, 'epic' => 2, 'legendary' => 3, default => 0, }; } private function playerAttack($out): bool { $stats = $this->player->getStats(); // 显示玩家选择菜单 $choice = $this->playerChooseAction(); if ($choice === 'spell') { return $this->playerCastSpell($out); } // 普通攻击逻辑 // Target first alive enemy $target = null; foreach ($this->enemies as $enemy) { if ($enemy->hp > 0) { $target = $enemy; break; } } if (!$target) return true; // All dead // 计算物理伤害和魔法伤害 $physicalDamage = max(1, $stats['patk'] - $target->pdef); $magicDamage = max(0, $stats['matk'] - $target->mdef); $baseDamage = $physicalDamage + $magicDamage; $critRate = $stats['crit']; $critDmg = $stats['critdmg']; $isCrit = rand(1, 100) <= $critRate; if ($isCrit) { $damage = (int)($baseDamage * ($critDmg / 100)); $out->writeln("{$this->cyan}║{$this->reset} {$this->green}➤{$this->reset} 你攻击 {$target->name}... {$this->red}{$this->bold}暴击!{$this->reset}"); $out->writeln("{$this->cyan}║{$this->reset} {$this->red}💥 造成 {$damage} 点伤害!{$this->reset}"); } else { $damage = $baseDamage; $out->writeln("{$this->cyan}║{$this->reset} {$this->green}➤{$this->reset} 你攻击 {$target->name}..."); $out->writeln("{$this->cyan}║{$this->reset} {$this->white}⚔️ 造成 {$damage} 点伤害{$this->reset}"); } $target->hp -= $damage; if ($target->hp <= 0) { $target->hp = 0; $out->writeln("{$this->cyan}║{$this->reset} {$this->red}💀 {$target->name} 被击败了!{$this->reset}"); // Check if all enemies are dead if (empty($this->getAliveEnemies())) { Screen::delay(500000); $this->showVictory($out, $stats); return true; } } return false; } /** * 同伴攻击 */ private function partnersAttack($out): bool { $alivePartners = $this->getAlivePartners(); foreach ($alivePartners as $partner) { // Target first alive enemy $target = null; foreach ($this->enemies as $enemy) { if ($enemy->hp > 0) { $target = $enemy; break; } } if (!$target) return true; // All dead $stats = $partner->getStats(); // 计算物理伤害和魔法伤害 $physicalDamage = max(1, $stats['patk'] - $target->pdef); $magicDamage = max(0, $stats['matk'] - $target->mdef); $baseDamage = $physicalDamage + $magicDamage; $critRate = $stats['crit']; $critDmg = $stats['critdmg']; $isCrit = rand(1, 100) <= $critRate; if ($isCrit) { $damage = (int)($baseDamage * ($critDmg / 100)); $out->writeln("{$this->cyan}║{$this->reset} {$this->magenta}➤{$this->reset} {$partner->name} 攻击 {$target->name}... {$this->red}{$this->bold}暴击!{$this->reset}"); $out->writeln("{$this->cyan}║{$this->reset} {$this->red}💥 造成 {$damage} 点伤害!{$this->reset}"); } else { $damage = $baseDamage; $out->writeln("{$this->cyan}║{$this->reset} {$this->magenta}➤{$this->reset} {$partner->name} 攻击 {$target->name}..."); $out->writeln("{$this->cyan}║{$this->reset} {$this->white}⚔️ 造成 {$damage} 点伤害{$this->reset}"); } $target->hp -= $damage; if ($target->hp <= 0) { $target->hp = 0; $out->writeln("{$this->cyan}║{$this->reset} {$this->red}💀 {$target->name} 被击败了!{$this->reset}"); if (empty($this->getAliveEnemies())) { Screen::delay(500000); $this->showVictory($out, $this->player->getStats()); return true; } } Screen::delay(400000); // 每个同伴攻击间隔 } return false; } private function enemiesAttack($out): bool { $aliveEnemies = $this->getAliveEnemies(); foreach ($aliveEnemies as $enemy) { // 选择攻击目标:玩家或存活的同伴 $alivePartners = $this->getAlivePartners(); $targets = ['player']; foreach ($alivePartners as $partner) { $targets[] = $partner->id; } $targetIdx = array_rand($targets); $target = $targets[$targetIdx]; if ($target === 'player') { // 攻击玩家 $playerStats = $this->player->getStats(); $physicalDamage = max(1, $enemy->patk - $playerStats['pdef']); $magicDamage = max(0, $enemy->matk - $playerStats['mdef']); $damage = $physicalDamage + $magicDamage; $out->writeln("{$this->cyan}║{$this->reset} {$this->red}➤{$this->reset} {$enemy->name} 向你发起攻击..."); $out->writeln("{$this->cyan}║{$this->reset} {$this->red}💢 你受到 {$damage} 点伤害{$this->reset}"); $this->player->hp -= $damage; if ($this->player->hp <= 0) { $this->player->hp = 0; Screen::delay(500000); $this->showDefeat($out, $enemy); return true; } } else { // 攻击同伴 $partner = $this->player->partners[$target]; $partnerStats = $partner->getStats(); $physicalDamage = max(1, $enemy->patk - $partnerStats['pdef']); $magicDamage = max(0, $enemy->matk - $partnerStats['mdef']); $damage = $physicalDamage + $magicDamage; $out->writeln("{$this->cyan}║{$this->reset} {$this->red}➤{$this->reset} {$enemy->name} 向 {$partner->name} 发起攻击..."); $out->writeln("{$this->cyan}║{$this->reset} {$this->red}💢 {$partner->name} 受到 {$damage} 点伤害{$this->reset}"); $this->partnerHp[$target] -= $damage; if ($this->partnerHp[$target] <= 0) { $this->partnerHp[$target] = 0; $out->writeln("{$this->cyan}║{$this->reset} {$this->red}💀 {$partner->name} 倒下了!{$this->reset}"); } } Screen::delay(400000); } return false; } private function showVictory($out, $stats) { Screen::clear($out); $out->writeln(""); $out->writeln("{$this->yellow}╔══════════════════════════════════════════╗{$this->reset}"); $out->writeln("{$this->yellow}║{$this->reset} {$this->yellow}║{$this->reset}"); $out->writeln("{$this->yellow}║{$this->reset} {$this->green}{$this->bold}🎉 胜 利 ! 🎉{$this->reset} {$this->yellow}║{$this->reset}"); $out->writeln("{$this->yellow}║{$this->reset} {$this->yellow}║{$this->reset}"); $out->writeln("{$this->yellow}╠══════════════════════════════════════════╣{$this->reset}"); $enemyNames = []; foreach ($this->enemies as $e) $enemyNames[] = $e->name; $out->writeln("{$this->yellow}║{$this->reset} 击败: {$this->white}" . implode(', ', array_unique($enemyNames)) . "{$this->reset}"); $out->writeln("{$this->yellow}║{$this->reset} 血量: {$this->green}{$this->player->hp}{$this->reset}/{$stats['maxHp']}"); // 汇总经验和灵石 $totalExp = 0; $totalStones = 0; $allDrops = []; foreach ($this->enemies as $enemy) { $totalExp += $enemy->expReward; $totalStones += $enemy->spiritStoneReward; // 掉落 - 从怪物穿着的装备随机掉落(50%概率) foreach ($enemy->getRandomEquipmentDrops(50) as $item) { $this->player->addItem($item); $allDrops[] = $item; } // 掉落 - 从怪物穿着的法术随机掉落(50%概率) foreach ($enemy->getRandomSpellDrops(50) as $spell) { $this->player->addItem($spell); $allDrops[] = $spell; } // 掉落 - 从掉落表中随机掉落物品 foreach ($enemy->dropTable as $drop) { if (rand(1, 100) <= $drop['rate']) { $this->player->addItem($drop['item']); $allDrops[] = $drop['item']; } } } // 经验 $levelUpMsg = ""; if ($this->player->gainExp($totalExp)) { $levelUpMsg = " {$this->yellow}🎊 升级! Lv.{$this->player->level}{$this->reset}"; } $out->writeln("{$this->yellow}║{$this->reset} 经验: {$this->cyan}+{$totalExp}{$this->reset}{$levelUpMsg}"); // 同伴经验 $alivePartners = $this->getAlivePartners(); if (!empty($alivePartners)) { $partnerExp = (int)($totalExp * 0.8); foreach ($alivePartners as $partner) { $partnerLevelUp = ""; if ($partner->gainExp($partnerExp)) { $partnerLevelUp = " {$this->yellow}🎊 升级! Lv.{$partner->level}{$this->reset}"; } $out->writeln("{$this->yellow}║{$this->reset} {$this->magenta}{$partner->name}{$this->reset}: {$this->cyan}+{$partnerExp}{$this->reset}{$partnerLevelUp}"); } } // 灵石 if ($totalStones > 0) { $this->player->addSpiritStones($totalStones); $out->writeln("{$this->yellow}║{$this->reset} 灵石: {$this->yellow}+{$totalStones}{$this->reset}"); } // 恢复魔法值 $manaRecover = (int)($this->player->maxMana * 0.3); // 恢复30%的最大魔法值 $actualManaRecover = $this->player->recoverMana($manaRecover); $out->writeln("{$this->yellow}║{$this->reset} 魔法: {$this->magenta}+{$actualManaRecover}{$this->reset}"); // 恢复队友魔法值 $alivePartners = $this->getAlivePartners(); foreach ($alivePartners as $partner) { $partnerManaRecover = (int)($partner->maxMana * 0.3); $actualPartnerManaRecover = $partner->recoverMana($partnerManaRecover); $this->partnerHp[$partner->id] = $partner->hp; // 同步HP } if (!empty($allDrops)) { $out->writeln("{$this->yellow}║{$this->reset} {$this->white}掉落:{$this->reset}"); foreach ($allDrops as $item) { $out->writeln("{$this->yellow}║{$this->reset} " . ItemDisplay::renderDrop($item, "")); } } $out->writeln("{$this->yellow}╚══════════════════════════════════════════╝{$this->reset}"); $out->writeln(""); $this->game->saveState(); Screen::delay(1500000); } private function showDefeat($out, ?Actor $killer = null) { Screen::clear($out); $killerName = $killer ? $killer->name : "敌人"; $out->writeln(""); $out->writeln("{$this->red}╔══════════════════════════════════════════╗{$this->reset}"); $out->writeln("{$this->red}║{$this->reset} {$this->red}║{$this->reset}"); $out->writeln("{$this->red}║{$this->reset} {$this->red}{$this->bold}💀 战 败 ! 💀{$this->reset} {$this->red}║{$this->reset}"); $out->writeln("{$this->red}║{$this->reset} {$this->red}║{$this->reset}"); $out->writeln("{$this->red}╠══════════════════════════════════════════╣{$this->reset}"); $out->writeln("{$this->red}║{$this->reset} 你被 {$this->white}{$killerName}{$this->reset} 击败了..."); $out->writeln("{$this->red}║{$this->reset}"); $out->writeln("{$this->red}║{$this->reset} {$this->white}不要气馁,休整后再战!{$this->reset}"); $out->writeln("{$this->red}╚══════════════════════════════════════════╝{$this->reset}"); $out->writeln(""); $this->game->state = Game::MENU; Screen::pause($out); } private function checkExit($out): bool { // Web 模式下跳过实时输入检测 $webInput = \Game\Core\WebInput::getInstance(); if ($webInput->isWebMode()) { return false; } stream_set_blocking(\STDIN, 0); $input = ''; while (($char = fgetc(\STDIN)) !== false) { $input .= $char; } stream_set_blocking(\STDIN, 1); if (str_contains($input, 'q') || str_contains($input, 'Q')) { $this->game->saveState(); Screen::clear($out); $out->writeln(""); $out->writeln("{$this->yellow}🏃 你逃离了战斗...{$this->reset}"); $out->writeln(""); Screen::delay(800000); $this->game->state = Game::MENU; return true; } return false; } }