- 修复装备配置中硬编码属性值的问题 * 5处直接指定patk/matk/pdef/mdef的装备改为使用模板 * 现在装备会正确生成词条和动态属性 * 装备品质与属性值完全匹配 - 实现战斗中的自动防护状态切换 * 当自身血量健康(>50%)且队友血量低(<30%)时 * 自动进入防护状态为队友抗伤 * 仅在有需要时自动切换,提高战斗智能性 * 显示战斗信息提示玩家防护状态的激活 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
1158 lines
44 KiB
PHP
1158 lines
44 KiB
PHP
<?php
|
||
namespace Game\Modules;
|
||
|
||
use Game\Core\Game;
|
||
use Game\Core\Input;
|
||
use Game\Core\Screen;
|
||
use Game\Core\ItemDisplay;
|
||
use Game\Core\SpellDisplay;
|
||
use Game\Core\SpellCalculator;
|
||
use Game\Core\Colors;
|
||
use Game\Entities\Player;
|
||
use Game\Entities\Actor;
|
||
use Game\Entities\Monster;
|
||
use Game\Entities\Partner;
|
||
|
||
class Battle
|
||
{
|
||
public Player $player;
|
||
/** @var Actor[] */
|
||
public array $enemies = [];
|
||
|
||
/** @var array<string, int> 同伴当前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 $blue;
|
||
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->blue = Colors::BLUE;
|
||
|
||
$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();
|
||
|
||
if ($this->player->hp <= 0) {
|
||
$out->writeln("{$this->red}你已经重伤倒地,无法继续战斗!请先去恢复生命值。{$this->reset}");
|
||
Screen::pause($out);
|
||
$this->game->state = Game::MENU;
|
||
return;
|
||
}
|
||
|
||
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->blue}🔮 {$stats['mana']}/{$stats['maxMana']}{$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->blue}🔮 {$partnerStats['mana']}/{$partnerStats['maxMana']}{$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';
|
||
}
|
||
|
||
/**
|
||
* 获取敌对阵营
|
||
* @return Actor[]
|
||
*/
|
||
private function getOpponents(Actor $actor): array
|
||
{
|
||
if ($actor instanceof Player || $actor instanceof Partner) {
|
||
return $this->getAliveEnemies();
|
||
} else {
|
||
// 怪物视角的敌人是玩家和同伴
|
||
$opponents = [];
|
||
if ($this->player->hp > 0) $opponents[] = $this->player;
|
||
foreach ($this->getAlivePartners() as $partner) {
|
||
$opponents[] = $partner;
|
||
}
|
||
return $opponents;
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 获取友方阵营
|
||
* @return Actor[]
|
||
*/
|
||
private function getAllies(Actor $actor): array
|
||
{
|
||
if ($actor instanceof Player || $actor instanceof Partner) {
|
||
$allies = [];
|
||
if ($this->player->hp > 0) $allies[] = $this->player;
|
||
foreach ($this->getAlivePartners() as $partner) {
|
||
$allies[] = $partner;
|
||
}
|
||
return $allies;
|
||
} else {
|
||
return $this->getAliveEnemies(); // 怪物的友方是其他怪物
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 计算目标的威胁值(用于敌人选择目标)
|
||
* 威胁值 = 输出能力 + 防御能力 + 防护状态加成
|
||
*/
|
||
private function calculateThreat(Actor $target): int
|
||
{
|
||
$stats = $target->getStats();
|
||
|
||
// 基础威胁值 = (物攻 + 魔攻) / 2
|
||
$threat = (int)(($stats['patk'] + $stats['matk']) / 2);
|
||
|
||
// 防御角色威胁值大幅上升
|
||
$threat += $target->getThreatBonus();
|
||
|
||
// 血量比例也会影响威胁值(血量越多越有威胁)
|
||
$threat += (int)($stats['hp'] / $stats['maxHp'] * 10);
|
||
|
||
return $threat;
|
||
}
|
||
|
||
/**
|
||
* 智能选择单体目标(考虑防护角色机制)
|
||
*/
|
||
private function selectTargetWithThreat(Actor $actor): ?Actor
|
||
{
|
||
$opponents = $this->getOpponents($actor);
|
||
$aliveOpponents = array_filter($opponents, fn($e) => $e->hp > 0);
|
||
|
||
if (empty($aliveOpponents)) {
|
||
return null;
|
||
}
|
||
|
||
// 优先级1:选择处于防护状态的目标(防御者)
|
||
$protectingTargets = array_filter($aliveOpponents, fn($e) => $e->isProtecting);
|
||
if (!empty($protectingTargets)) {
|
||
return current($protectingTargets);
|
||
}
|
||
|
||
// 优先级2:选择威胁值最高的目标
|
||
$maxThreat = -1;
|
||
$bestTarget = null;
|
||
|
||
foreach ($aliveOpponents as $target) {
|
||
$threat = $this->calculateThreat($target);
|
||
if ($threat > $maxThreat) {
|
||
$maxThreat = $threat;
|
||
$bestTarget = $target;
|
||
}
|
||
}
|
||
|
||
return $bestTarget ?? current($aliveOpponents);
|
||
}
|
||
|
||
/**
|
||
* 智能选择法术 (通用)
|
||
*/
|
||
private function smartSelectSpell(Actor $actor): ?array
|
||
{
|
||
// 1. 分析战场状态
|
||
$opponents = $this->getOpponents($actor);
|
||
$allies = $this->getAllies($actor);
|
||
|
||
$enemyCount = count($opponents);
|
||
|
||
$lowHpAllies = [];
|
||
foreach ($allies as $ally) {
|
||
$status = $ally->getStats();
|
||
if ($status['hp'] < $status['maxHp'] * 0.5) {
|
||
$lowHpAllies[] = $ally;
|
||
}
|
||
}
|
||
$lowHpCount = count($lowHpAllies);
|
||
// 2. 筛选可用法术并分类
|
||
$availableSpells = [
|
||
'heal_aoe' => [],
|
||
'heal_single' => [],
|
||
'damage_aoe' => [],
|
||
'damage_single' => [],
|
||
'support' => [],
|
||
'all' => []
|
||
];
|
||
|
||
foreach ($actor->skillSlots as $slot => $spellItem) {
|
||
if ($spellItem === null) continue;
|
||
|
||
$actualCost = SpellCalculator::calculateCost($spellItem);
|
||
|
||
if ($actor->mana >= $actualCost) {
|
||
$spellData = [
|
||
'item' => $spellItem,
|
||
'cost' => $actualCost,
|
||
'level' => $spellItem['enhanceLevel'] ?? 0
|
||
];
|
||
|
||
$type = $spellItem['spellType'] ?? 'damage_single';
|
||
|
||
if (isset($availableSpells[$type])) {
|
||
$availableSpells[$type][] = $spellData;
|
||
}
|
||
$availableSpells['all'][] = $spellData;
|
||
}
|
||
}
|
||
|
||
if (empty($availableSpells['all'])) {
|
||
return null;
|
||
}
|
||
|
||
// 3. 智能选择策略
|
||
$selected = null;
|
||
|
||
// 策略A: 优先治疗
|
||
if ($lowHpCount > 0) {
|
||
if ($lowHpCount > 1 && !empty($availableSpells['heal_aoe'])) {
|
||
$selected = $availableSpells['heal_aoe'][array_rand($availableSpells['heal_aoe'])];
|
||
} elseif (!empty($availableSpells['heal_single'])) {
|
||
$selected = $availableSpells['heal_single'][array_rand($availableSpells['heal_single'])];
|
||
} elseif (!empty($availableSpells['heal_aoe'])) {
|
||
$selected = $availableSpells['heal_aoe'][array_rand($availableSpells['heal_aoe'])];
|
||
}
|
||
}
|
||
|
||
// 策略B: 伤害输出
|
||
if ($selected === null) {
|
||
if ($enemyCount > 1 && !empty($availableSpells['damage_aoe'])) {
|
||
$selected = $availableSpells['damage_aoe'][array_rand($availableSpells['damage_aoe'])];
|
||
} elseif (!empty($availableSpells['damage_single'])) {
|
||
$selected = $availableSpells['damage_single'][array_rand($availableSpells['damage_single'])];
|
||
} elseif (!empty($availableSpells['damage_aoe'])) {
|
||
$selected = $availableSpells['damage_aoe'][array_rand($availableSpells['damage_aoe'])];
|
||
}
|
||
}
|
||
|
||
// 策略C: 兜底
|
||
if ($selected === null) {
|
||
$selected = $availableSpells['all'][array_rand($availableSpells['all'])];
|
||
}
|
||
|
||
if ($selected && in_array($selected['item']['spellType'],['heal_aoe', 'heal_single']) && $lowHpCount == 0){
|
||
return [];
|
||
}
|
||
|
||
return $selected;
|
||
}
|
||
|
||
/**
|
||
* 施放单体伤害法术 (通用)
|
||
*/
|
||
private function castDamageSingleSpell($out, Actor $caster, ?Actor $target, array $spellInfo, array $stats, int $damageBonus, string $name): bool
|
||
{
|
||
// 自动选择目标(使用威胁值系统)
|
||
if (!$target) {
|
||
$target = $this->selectTargetWithThreat($caster);
|
||
}
|
||
|
||
if (!$target) return true;
|
||
|
||
// 显示法术基础信息
|
||
|
||
|
||
$quality = $spellInfo['quality'] ?? 'common';
|
||
$qualityColor = SpellDisplay::getQualityColor($quality);
|
||
|
||
// 计算法术伤害
|
||
$damageResult = SpellCalculator::calculateDamage($spellInfo, $stats, $target->getStats(), $damageBonus);
|
||
$damage = $damageResult['damage'];
|
||
$isCrit = $damageResult['isCrit'];
|
||
|
||
// 显示法术施放信息
|
||
$casterName = ($caster instanceof Player) ? "你" : $caster->name;
|
||
$actionVerb = ($caster instanceof Player) ? "施放" : "施放了";
|
||
|
||
$out->writeln("{$this->cyan}║{$this->reset} {$this->magenta}✦{$this->reset} {$casterName} {$actionVerb} {$qualityColor}{$name}{$this->reset}");
|
||
|
||
if ($isCrit) {
|
||
$out->writeln("{$this->cyan}║{$this->reset} {$this->magenta}✨ {$this->red}{$this->bold}暴击!{$this->reset} 对 {$target->name} 造成 {$this->red}{$damage}{$this->reset} 点魔法伤害!");
|
||
} else {
|
||
$out->writeln("{$this->cyan}║{$this->reset} {$this->magenta}✨ 对 {$target->name} 造成 {$this->green}{$damage}{$this->reset} 点魔法伤害");
|
||
}
|
||
|
||
// 应用防护机制:防护角色承受更多伤害
|
||
$actualDamage = (int)($damage * (1 + $target->getProtectDamageTakenBonus()));
|
||
|
||
$target->hp -= $actualDamage;
|
||
|
||
// 如果防护角色正在保护队友,需要显示保护效果
|
||
if ($target->isProtecting && $actualDamage > $damage) {
|
||
$extraDamage = $actualDamage - $damage;
|
||
$out->writeln("{$this->cyan}║{$this->reset} {$this->yellow}🛡️ 防护状态:额外承受 {$extraDamage} 伤害!{$this->reset}");
|
||
}
|
||
|
||
if ($target->hp <= 0) {
|
||
$target->hp = 0;
|
||
$out->writeln("{$this->cyan}║{$this->reset} {$this->red}💀 {$target->name} 被击败了!{$this->reset}");
|
||
|
||
// 如果是玩家击败了所有敌人
|
||
if (($caster instanceof Player || $caster instanceof Partner) && empty($this->getAliveEnemies())) {
|
||
Screen::delay(500000);
|
||
$this->showVictory($out, $stats);
|
||
return true;
|
||
}
|
||
|
||
// 如果是敌人击败了玩家
|
||
if ($target instanceof Player) {
|
||
Screen::delay(500000);
|
||
$this->showDefeat($out, $caster);
|
||
return true;
|
||
}
|
||
}
|
||
|
||
return false;
|
||
}
|
||
|
||
/**
|
||
* 施放AOE伤害法术 (通用)
|
||
*/
|
||
private function castDamageAoeSpell($out, Actor $caster, ?Actor $target, array $spellInfo, array $stats, int $damageBonus, string $name): bool
|
||
{
|
||
// 显示法术基础信息
|
||
$quality = $spellInfo['quality'] ?? 'common';
|
||
$qualityColor = SpellDisplay::getQualityColor($quality);
|
||
|
||
$casterName = ($caster instanceof Player) ? "你" : $caster->name;
|
||
$actionVerb = ($caster instanceof Player) ? "施放" : "施放了";
|
||
|
||
$out->writeln("{$this->cyan}║{$this->reset} {$this->magenta}✦{$this->reset} {$casterName} {$actionVerb} {$qualityColor}{$name}{$this->reset}");
|
||
|
||
$out->writeln("{$this->cyan}║{$this->reset} {$this->magenta}✨ 魔法在整个战场爆炸!{$this->reset}");
|
||
|
||
$opponents = $this->getOpponents($caster);
|
||
$allOpponentsDefeated = true;
|
||
|
||
foreach ($opponents as $enemy) {
|
||
if ($enemy->hp <= 0) continue;
|
||
|
||
$damageResult = SpellCalculator::calculateDamage($spellInfo, $stats, $enemy->getStats(), $damageBonus, true);
|
||
$damage = $damageResult['damage'];
|
||
|
||
$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}");
|
||
|
||
if ($enemy instanceof Player) {
|
||
Screen::delay(500000);
|
||
$this->showDefeat($out, $caster);
|
||
return true;
|
||
}
|
||
} else {
|
||
$allOpponentsDefeated = false;
|
||
}
|
||
}
|
||
|
||
// 更新同伴血量显示 (如果敌人是同伴)
|
||
if (!($caster instanceof Player) && !($caster instanceof Partner)) {
|
||
foreach ($this->player->partners as $partner) {
|
||
if (isset($this->partnerHp[$partner->id])) {
|
||
$this->partnerHp[$partner->id] = $partner->hp;
|
||
}
|
||
}
|
||
}
|
||
|
||
if ($allOpponentsDefeated && ($caster instanceof Player || $caster instanceof Partner) && empty($this->getAliveEnemies())) {
|
||
Screen::delay(500000);
|
||
$this->showVictory($out, $stats);
|
||
return true;
|
||
}
|
||
|
||
return false;
|
||
}
|
||
|
||
/**
|
||
* 施放单体治疗法术 (通用)
|
||
*/
|
||
private function castHealSingleSpell($out, Actor $caster, ?Actor $target, array $spellInfo, array $stats, int $healBonus, string $name): bool
|
||
{
|
||
// 自动选择目标 (优先治疗血量最低的友方)
|
||
if (!$target) {
|
||
$allies = $this->getAllies($caster);
|
||
$lowestHpRatio = 1.0;
|
||
foreach ($allies as $ally) {
|
||
$ratio = $ally->hp / $ally->maxHp;
|
||
if ($ratio < $lowestHpRatio) {
|
||
$lowestHpRatio = $ratio;
|
||
$target = $ally;
|
||
}
|
||
}
|
||
// 如果都满血,治疗自己
|
||
if (!$target) $target = $caster;
|
||
}
|
||
|
||
// 显示法术基础信息
|
||
$quality = $spellInfo['quality'] ?? 'common';
|
||
$qualityColor = SpellDisplay::getQualityColor($quality);
|
||
|
||
$casterName = ($caster instanceof Player) ? "你" : $caster->name;
|
||
$actionVerb = ($caster instanceof Player) ? "施放" : "施放了";
|
||
|
||
$out->writeln("{$this->cyan}║{$this->reset} {$this->green}✦{$this->reset} {$casterName} {$actionVerb} {$qualityColor}{$name}{$this->reset}");
|
||
|
||
|
||
$healAmount = SpellCalculator::calculateHeal($spellInfo, $stats, $healBonus);
|
||
|
||
$actualHeal = $target->heal($healAmount);
|
||
|
||
// 更新同伴血量显示
|
||
if ($target instanceof Partner && isset($this->partnerHp[$target->id])) {
|
||
$this->partnerHp[$target->id] = $target->hp;
|
||
}
|
||
|
||
$targetName = ($target === $this->player) ? "你" : $target->name;
|
||
$out->writeln("{$this->cyan}║{$this->reset} {$this->green}💚 {$targetName} 恢复了 {$actualHeal} 点生命值{$this->reset}");
|
||
|
||
return false;
|
||
}
|
||
|
||
/**
|
||
* 施放群体治疗法术 (通用)
|
||
*/
|
||
private function castHealAoeSpell($out, Actor $caster, ?Actor $target, array $spellInfo, array $stats, int $healBonus, string $name): bool
|
||
{
|
||
// 显示法术基础信息
|
||
$quality = $spellInfo['quality'] ?? 'common';
|
||
$qualityColor = SpellDisplay::getQualityColor($quality);
|
||
|
||
$casterName = ($caster instanceof Player) ? "你" : $caster->name;
|
||
$actionVerb = ($caster instanceof Player) ? "施放" : "施放了";
|
||
|
||
$out->writeln("{$this->cyan}║{$this->reset} {$this->green}✦{$this->reset} {$casterName} {$actionVerb} {$qualityColor}{$name}{$this->reset}");
|
||
|
||
|
||
$healAmount = SpellCalculator::calculateHeal($spellInfo, $stats, $healBonus);
|
||
|
||
$allies = $this->getAllies($caster);
|
||
|
||
foreach ($allies as $ally) {
|
||
if ($ally->hp <= 0) continue;
|
||
|
||
// 如果是施法者本人,全额治疗;如果是队友,80%效果
|
||
$finalHeal = ($ally === $caster) ? $healAmount : (int)($healAmount * 0.8);
|
||
|
||
$actualHeal = $ally->heal($finalHeal);
|
||
|
||
// 更新同伴血量显示
|
||
if ($ally instanceof Partner && isset($this->partnerHp[$ally->id])) {
|
||
$this->partnerHp[$ally->id] = $ally->hp;
|
||
}
|
||
|
||
$allyName = ($ally === $this->player) ? "你" : $ally->name;
|
||
$out->writeln("{$this->cyan}║{$this->reset} {$this->green}💚 {$allyName} 恢复了 {$actualHeal} 点生命值{$this->reset}");
|
||
}
|
||
|
||
return false;
|
||
}
|
||
|
||
/**
|
||
* 施放辅助法术 (通用)
|
||
*/
|
||
/**
|
||
* 施放辅助法术 (通用)
|
||
*/
|
||
private function castSupportSpell($out, Actor $caster, ?Actor $target, array $spellInfo, array $stats, string $name): bool
|
||
{
|
||
// 显示法术基础信息
|
||
$quality = $spellInfo['quality'] ?? 'common';
|
||
$qualityColor = SpellDisplay::getQualityColor($quality);
|
||
|
||
$casterName = ($caster instanceof Player) ? "你" : $caster->name;
|
||
$actionVerb = ($caster instanceof Player) ? "施放" : "施放了";
|
||
|
||
$out->writeln("{$this->cyan}║{$this->reset} {$this->cyan}✦{$this->reset} {$casterName} {$actionVerb} {$qualityColor}{$name}{$this->reset}");
|
||
|
||
$subtype = $spellInfo['subtype'] ?? '';
|
||
|
||
if ($subtype === 'heal' || $subtype === 'heal_all') {
|
||
// 委托给治疗逻辑
|
||
$healBonus = 0; // 辅助类治疗通常没有额外加成
|
||
if ($subtype === 'heal') {
|
||
return $this->castHealSingleSpell($out, $caster, $target, $spellInfo, $stats, $healBonus, $name);
|
||
} else {
|
||
return $this->castHealAoeSpell($out, $caster, $target, $spellInfo, $stats, $healBonus, $name);
|
||
}
|
||
} elseif ($subtype === 'defend') {
|
||
// 简单的防御提升逻辑 (目前只是显示,实际效果需在伤害计算中支持buff)
|
||
$out->writeln("{$this->cyan}║{$this->reset} {$this->cyan}🛡️ 防御力提升!{$this->reset}");
|
||
}
|
||
|
||
return false;
|
||
}
|
||
|
||
/**
|
||
* 检查是否应该自动进入防护状态来保护低血量队友
|
||
* 条件:自身血量健康 (>50%) 且有队友血量低 (<30%)
|
||
*/
|
||
private function checkAutoEnterProtectMode(Actor $actor, $out): void
|
||
{
|
||
// 仅玩家和队友可以进入防护状态,敌人不需要
|
||
if (!($actor instanceof Player) && !($actor instanceof Partner)) {
|
||
return;
|
||
}
|
||
|
||
// 已经在防护状态,不需要再进入
|
||
if ($actor->isProtecting) {
|
||
return;
|
||
}
|
||
|
||
$stats = $actor->getStats();
|
||
$healthRatio = $stats['hp'] / $stats['maxHp'];
|
||
|
||
// 自身血量需要健康(>50%)
|
||
if ($healthRatio <= 0.5) {
|
||
return;
|
||
}
|
||
|
||
// 检查是否有队友血量低
|
||
$allies = $this->getAllies($actor);
|
||
$hasLowHpAlly = false;
|
||
|
||
foreach ($allies as $ally) {
|
||
// 跳过自己和倒下的队友
|
||
if ($ally === $actor || $ally->hp <= 0) {
|
||
continue;
|
||
}
|
||
|
||
$allyStats = $ally->getStats();
|
||
$allyHealthRatio = $allyStats['hp'] / $allyStats['maxHp'];
|
||
|
||
// 如果有队友血量低于30%,进入防护状态
|
||
if ($allyHealthRatio < 0.3) {
|
||
$hasLowHpAlly = true;
|
||
break;
|
||
}
|
||
}
|
||
|
||
// 自动进入防护状态
|
||
if ($hasLowHpAlly) {
|
||
$actor->enterProtectMode();
|
||
|
||
$actorName = ($actor instanceof Player) ? "你" : $actor->name;
|
||
$out->writeln("{$this->cyan}║{$this->reset} {$this->yellow}🛡️{$this->reset} {$actorName} 主动进入防护状态,为队友抗伤!{$this->reset}");
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 执行角色的回合行动 (通用)
|
||
*/
|
||
private function executeActorTurn(Actor $actor, $out): bool
|
||
{
|
||
if ($actor->hp <= 0){
|
||
return false;
|
||
}
|
||
|
||
// 检查是否应该自动进入防护状态
|
||
$this->checkAutoEnterProtectMode($actor, $out);
|
||
|
||
// 1. 尝试使用法术
|
||
$selectedSpell = $this->smartSelectSpell($actor);
|
||
|
||
if ($selectedSpell) {
|
||
$spellItem = $selectedSpell['item'];
|
||
$actualCost = $selectedSpell['cost'];
|
||
$enhanceLevel = $selectedSpell['level'];
|
||
|
||
// 消耗魔法值
|
||
$actor->spendMana($actualCost);
|
||
|
||
// 计算强化加成
|
||
$damageBonus = $enhanceLevel * 5;
|
||
$type = $spellItem['spellType'] ?? 'damage_single';
|
||
$name = $spellItem['name'] ?? '未知法术';
|
||
|
||
$spellInfo = $spellItem;
|
||
$spellInfo['type'] = $type;
|
||
|
||
$stats = $actor->getStats();
|
||
|
||
if ($type === 'damage_single') {
|
||
return $this->castDamageSingleSpell($out, $actor, null, $spellInfo, $stats, $damageBonus, $name);
|
||
} elseif ($type === 'damage_aoe') {
|
||
return $this->castDamageAoeSpell($out, $actor, null, $spellInfo, $stats, $damageBonus, $name);
|
||
} elseif ($type === 'heal_single') {
|
||
return $this->castHealSingleSpell($out, $actor, null, $spellInfo, $stats, $damageBonus, $name);
|
||
} elseif ($type === 'heal_aoe') {
|
||
return $this->castHealAoeSpell($out, $actor, null, $spellInfo, $stats, $damageBonus, $name);
|
||
} elseif ($type === 'support') {
|
||
return $this->castSupportSpell($out, $actor, null, $spellInfo, $stats, $name);
|
||
}
|
||
}
|
||
|
||
// 2. 如果没有使用法术,执行普通攻击
|
||
// 寻找目标(使用威胁值系统)
|
||
$target = $this->selectTargetWithThreat($actor);
|
||
|
||
if (!$target) return true; // 无目标,回合结束
|
||
|
||
$stats = $actor->getStats();
|
||
$targetStats = $target->getStats();
|
||
|
||
// 计算伤害
|
||
$physicalDamage = max(1, $stats['patk'] - $targetStats['pdef']);
|
||
$magicDamage = max(0, $stats['matk'] - $targetStats['mdef']);
|
||
$baseDamage = $physicalDamage + $magicDamage;
|
||
|
||
$critRate = $stats['crit'];
|
||
$critDmg = $stats['critdmg'];
|
||
$isCrit = rand(1, 100) <= $critRate;
|
||
|
||
$actorName = ($actor instanceof Player) ? "你" : $actor->name;
|
||
$targetName = ($target === $this->player) ? "你" : $target->name;
|
||
$actionVerb = ($actor instanceof Player) ? "攻击" : "向 {$targetName} 发起攻击";
|
||
|
||
if ($actor instanceof Player) {
|
||
$out->writeln("{$this->cyan}║{$this->reset} {$this->green}➤{$this->reset} 你攻击 {$targetName}...");
|
||
} else {
|
||
$out->writeln("{$this->cyan}║{$this->reset} {$this->red}➤{$this->reset} {$actorName} {$actionVerb}...");
|
||
}
|
||
|
||
if ($isCrit) {
|
||
$damage = (int)($baseDamage * ($critDmg / 100));
|
||
$out->writeln("{$this->cyan}║{$this->reset} {$this->red}💥 造成 {$damage} 点暴击伤害!{$this->reset}");
|
||
} else {
|
||
$damage = $baseDamage;
|
||
$out->writeln("{$this->cyan}║{$this->reset} {$this->white}⚔️ 造成 {$damage} 点伤害{$this->reset}");
|
||
}
|
||
|
||
// 应用防护机制:防护角色承受更多伤害
|
||
$actualDamage = (int)($damage * (1 + $target->getProtectDamageTakenBonus()));
|
||
|
||
$target->hp -= $actualDamage;
|
||
|
||
// 如果防护角色正在保护队友,需要显示保护效果
|
||
if ($target->isProtecting && $actualDamage > $damage) {
|
||
$extraDamage = $actualDamage - $damage;
|
||
$out->writeln("{$this->cyan}║{$this->reset} {$this->yellow}🛡️ 防护状态:额外承受 {$extraDamage} 伤害!{$this->reset}");
|
||
}
|
||
|
||
// 蓝量恢复机制
|
||
// 攻击者恢复 15 点
|
||
$actorRecovered = $actor->recoverMana(15);
|
||
// 受击者恢复 10 点
|
||
$targetRecovered = $target->recoverMana(10);
|
||
|
||
// 更新同伴血量显示
|
||
if ($target instanceof Partner && isset($this->partnerHp[$target->id])) {
|
||
$this->partnerHp[$target->id] = $target->hp;
|
||
}
|
||
|
||
if ($target->hp <= 0) {
|
||
$target->hp = 0;
|
||
$out->writeln("{$this->cyan}║{$this->reset} {$this->red}💀 {$targetName} 倒下了!{$this->reset}");
|
||
|
||
if (($actor instanceof Player || $actor instanceof Partner) && empty($this->getAliveEnemies())) {
|
||
Screen::delay(500000);
|
||
$this->showVictory($out, $stats);
|
||
return true;
|
||
}
|
||
|
||
if ($target instanceof Player) {
|
||
Screen::delay(500000);
|
||
$this->showDefeat($out, $actor);
|
||
return true;
|
||
}
|
||
}
|
||
|
||
return false;
|
||
}
|
||
|
||
private function playerAttack($out): bool
|
||
{
|
||
|
||
if ($this->executeActorTurn($this->player, $out)) {
|
||
return true; // 战斗结束
|
||
}
|
||
Screen::delay(300000);
|
||
return false;
|
||
}
|
||
|
||
/**
|
||
* 同伴攻击
|
||
*/
|
||
private function partnersAttack($out): bool
|
||
{
|
||
$alivePartners = $this->getAlivePartners();
|
||
|
||
foreach ($alivePartners as $partner) {
|
||
if ($this->executeActorTurn($partner, $out)) {
|
||
return true; // 战斗结束
|
||
}
|
||
Screen::delay(300000);
|
||
}
|
||
|
||
return false;
|
||
}
|
||
|
||
/**
|
||
* 怪物攻击
|
||
*/
|
||
private function enemiesAttack($out): bool
|
||
{
|
||
foreach ($this->enemies as $enemy) {
|
||
if ($enemy->hp <= 0) continue;
|
||
|
||
if ($this->executeActorTurn($enemy, $out)) {
|
||
return true; // 战斗结束
|
||
}
|
||
Screen::delay(300000);
|
||
}
|
||
|
||
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;
|
||
}
|
||
}
|