This commit is contained in:
hant 2025-12-02 23:13:38 +08:00
parent bacc28acce
commit 826f0e38a4
12 changed files with 1235 additions and 13 deletions

View File

@ -12,6 +12,7 @@ if (file_exists(__DIR__ . '/../vendor/autoload.php')) {
require 'phar://hanli-idle.phar/vendor/autoload.php';
}
use Game\Core\Input;
use Symfony\Component\Console\Input\ArgvInput;
use Symfony\Component\Console\Output\ConsoleOutput;
use Game\Core\Game;
@ -28,7 +29,7 @@ if (php_sapi_name() !== 'cli') {
}
// 创建输入输出
$input = new ArgvInput();
$input = new Input();
$output = new ConsoleOutput();
// 启动游戏

View File

@ -1 +1 @@
{"player":{"hp":0,"maxHp":50,"patk":10,"matk":5,"pdef":5,"mdef":3,"crit":5,"critdmg":130,"level":3,"exp":50,"maxExp":225,"inventory":[],"equip":{"weapon":{"name":"烈焰刀","type":"weapon","quality":"common","level":1,"patk":5,"matk":5,"pdef":0,"mdef":0,"hp":0,"crit":0,"critdmg":0,"heal":0,"affixes":[],"desc":"Lv.1 common品质的武器","id":"692eb3035a60f","quantity":1}},"spiritStones":67,"npcFlags":[],"talentPoints":6,"talents":{"hp":0,"patk":0,"matk":0,"pdef":0,"mdef":0,"crit":0,"critdmg":0},"partners":[{"id":"li_feiyu","name":"厉飞雨","level":2,"exp":140,"maxExp":150,"baseStats":{"hp":100,"patk":15,"matk":5,"pdef":5,"mdef":3,"crit":10,"critdmg":130,"growth":1.2},"equip":{"weapon":{"name":"剑","type":"weapon","quality":"common","level":1,"patk":8,"matk":1,"pdef":0,"mdef":0,"hp":0,"crit":0,"critdmg":0,"heal":0,"affixes":[],"desc":"Lv.1 common品质的武器","id":"692eb39459686","quantity":1}},"talents":{"hp":1,"patk":1,"matk":0,"pdef":0,"mdef":0,"crit":1,"critdmg":0},"talentWeights":{"hp":1,"patk":3,"matk":1,"pdef":1,"mdef":1,"crit":3,"critdmg":2}}]},"dungeonId":1,"state":0}
{"player":{"hp":31,"maxHp":50,"patk":10,"matk":5,"pdef":5,"mdef":3,"crit":5,"critdmg":130,"level":3,"exp":70,"maxExp":225,"inventory":[],"equip":{"weapon":{"name":"青钢剑","type":"weapon","quality":"rare","level":1,"patk":14,"matk":3,"pdef":0,"mdef":0,"hp":0,"crit":0,"critdmg":0,"heal":0,"affixes":["暴击伤害 +6"],"desc":"Lv.1 rare品质的武器","id":"692ee8e0df082","quantity":1},"armor":{"name":"皮甲","type":"armor","quality":"common","level":3,"patk":0,"matk":0,"pdef":6,"mdef":1,"hp":0,"crit":0,"critdmg":0,"heal":0,"affixes":[],"desc":"Lv.3 common品质的防具","id":"692efecedbc7a","quantity":1}},"spiritStones":72,"npcFlags":[],"talentPoints":0,"talents":{"hp":6,"patk":0,"matk":0,"pdef":0,"mdef":0,"crit":0,"critdmg":0},"mana":100,"maxMana":100,"spells":[],"spellBooks":{"3":1},"partners":[{"id":"li_feiyu","name":"厉飞雨","level":2,"exp":60,"maxExp":150,"baseStats":{"hp":100,"patk":15,"matk":5,"pdef":5,"mdef":3,"crit":10,"critdmg":130,"growth":1.2},"equip":{"weapon":{"name":"青钢剑","type":"weapon","quality":"common","level":1,"patk":6,"matk":2,"pdef":0,"mdef":0,"hp":0,"crit":0,"critdmg":0,"heal":0,"affixes":[],"desc":"Lv.1 common品质的武器","id":"692ee91453dd8","quantity":1}},"talents":{"hp":1,"patk":1,"matk":0,"pdef":0,"mdef":0,"crit":1,"critdmg":0},"talentWeights":{"hp":1,"patk":3,"matk":1,"pdef":1,"mdef":1,"crit":3,"critdmg":2},"mana":80,"maxMana":80,"spells":[],"spellBooks":[]}]},"dungeonId":1,"state":2}

View File

@ -12,6 +12,7 @@ use Game\Modules\PartnerPanel;
use Game\Modules\TalentPanel;
use Game\Modules\DungeonSelectPanel;
use Game\Modules\EquipmentEnhancePanel;
use Game\Modules\SpellPanel;
class Game
{
@ -24,6 +25,7 @@ class Game
const TALENT = 7;
const DUNGEON_SELECT = 8;
const EQUIPMENT_ENHANCE = 9;
const SPELL = 10;
const EXIT = 0;
public Player $player;
@ -98,6 +100,12 @@ class Game
}
}
// 加载法术数据
$this->player->mana = $p['mana'] ?? $this->player->mana;
$this->player->maxMana = $p['maxMana'] ?? $this->player->maxMana;
$this->player->spells = $p['spells'] ?? $this->player->spells;
$this->player->spellBooks = $p['spellBooks'] ?? $this->player->spellBooks;
$this->dungeonId = $data['dungeonId'] ?? $this->dungeonId;
$this->state = self::MENU;
}
@ -119,6 +127,10 @@ class Game
'equip' => $partner->equip,
'talents' => $partner->talents,
'talentWeights' => $partner->talentWeights,
'mana' => $partner->mana,
'maxMana' => $partner->maxMana,
'spells' => $partner->spells,
'spellBooks' => $partner->spellBooks,
];
}
@ -141,6 +153,10 @@ class Game
'npcFlags' => $this->player->npcFlags,
'talentPoints' => $this->player->talentPoints,
'talents' => $this->player->talents,
'mana' => $this->player->mana,
'maxMana' => $this->player->maxMana,
'spells' => $this->player->spells,
'spellBooks' => $this->player->spellBooks,
'partners' => $partnersData,
],
'dungeonId' => $this->dungeonId,
@ -180,6 +196,9 @@ class Game
case self::EQUIPMENT_ENHANCE:
(new EquipmentEnhancePanel($this))->show();
break;
case self::SPELL:
(new SpellPanel($this))->show();
break;
case self::EXIT:
$this->output->writeln("再见!");
$this->saveState();

View File

@ -230,5 +230,17 @@ return [
'base_stats' => [30, 50, 80, 120], // 不同品质的基础治疗量
'growth' => 5, // 每级增加的治疗量
],
// 法术资源书 - 用于学习和升级法术
'spell_tome' => [
'names' => [
'common' => ['火球术卷', '冰雹术卷', '治愈术卷', '防护术卷'],
'rare' => ['冰锥术册', '雷击术册', '神圣庇护册', '炎爆术册'],
'epic' => ['烈焰焚天记', '流星雨诀', '恢复光环经'],
'legendary' => ['诛仙剑气秘籍', '灭世风暴秘典'],
],
'base_stats' => [0, 0, 0, 0], // 资源书本身无属性
'growth' => 0, // 资源书无等级
],
],
];

171
src/Data/spells.php Normal file
View File

@ -0,0 +1,171 @@
<?php
/**
* 法术系统配置
* 定义所有可用的法术及其属性
*/
return [
// 单体伤害法术
'damage_single' => [
1 => [
'name' => '火球术',
'type' => 'damage_single',
'quality' => 'common',
'cost' => 20,
'damage' => 1.2, // 伤害倍数 = 魔攻 * damage
'level_req' => 1,
'desc' => '发出一团火球,攻击单个敌人',
],
2 => [
'name' => '冰锥术',
'type' => 'damage_single',
'quality' => 'rare',
'cost' => 25,
'damage' => 1.3,
'level_req' => 5,
'desc' => '凝聚寒冰之力,发出锐利冰锥',
],
3 => [
'name' => '雷击术',
'type' => 'damage_single',
'quality' => 'rare',
'cost' => 30,
'damage' => 1.5,
'level_req' => 10,
'desc' => '召唤雷电直击单个敌人',
],
4 => [
'name' => '烈焰焚天',
'type' => 'damage_single',
'quality' => 'epic',
'cost' => 45,
'damage' => 1.8,
'level_req' => 20,
'desc' => '释放强大的火焰,对单个敌人造成巨大伤害',
],
5 => [
'name' => '诛仙剑气',
'type' => 'damage_single',
'quality' => 'legendary',
'cost' => 60,
'damage' => 2.2,
'level_req' => 35,
'desc' => '凝聚剑意,发出致命一击',
],
],
// AOE伤害法术
'damage_aoe' => [
10 => [
'name' => '冰雹术',
'type' => 'damage_aoe',
'quality' => 'common',
'cost' => 35,
'damage' => 0.8, // 对每个敌人的伤害倍数较低,但打全体
'level_req' => 8,
'desc' => '召唤冰雹,攻击所有敌人',
],
11 => [
'name' => '炎爆术',
'type' => 'damage_aoe',
'quality' => 'rare',
'cost' => 45,
'damage' => 0.95,
'level_req' => 15,
'desc' => '引发连锁爆炸,对所有敌人造成伤害',
],
12 => [
'name' => '流星雨',
'type' => 'damage_aoe',
'quality' => 'epic',
'cost' => 60,
'damage' => 1.1,
'level_req' => 25,
'desc' => '召唤流星坠落,轰击全体敌人',
],
13 => [
'name' => '灭世风暴',
'type' => 'damage_aoe',
'quality' => 'legendary',
'cost' => 80,
'damage' => 1.3,
'level_req' => 40,
'desc' => '引发天地异变,对所有敌人造成毁灭性伤害',
],
],
// 辅助法术(恢复、增益)
'support' => [
20 => [
'name' => '治愈术',
'type' => 'support',
'subtype' => 'heal',
'quality' => 'common',
'cost' => 15,
'heal' => 0.5, // 恢复量倍数 = 魔攻 * heal + 基础值
'heal_base' => 20,
'level_req' => 3,
'desc' => '恢复自己或队友的生命值',
],
21 => [
'name' => '神圣庇护',
'type' => 'support',
'subtype' => 'defend',
'quality' => 'rare',
'cost' => 25,
'defense_boost' => 30, // 增加固定防御值
'duration' => 3, // 持续回合数(如果支持的话)
'level_req' => 12,
'desc' => '增加自己或队友的防御力',
],
22 => [
'name' => '恢复光环',
'type' => 'support',
'subtype' => 'heal_all',
'quality' => 'epic',
'cost' => 50,
'heal' => 0.6,
'heal_base' => 40,
'level_req' => 30,
'desc' => '为所有队员恢复生命值',
],
],
// 法术品质对应的学习资源书数量
'quality_levels' => [
'common' => 1, // 普通资源书可学习普通法术
'rare' => 2, // 稀有资源书可学习稀有法术
'epic' => 3, // 史诗资源书可学习史诗法术
'legendary' => 4, // 传奇资源书可学习传奇法术
],
// 法术升级系统
'upgrades' => [
// 每个等级需要的资源书数量和属性提升
// level => ['cost' => 资源书数量, 'damage_bonus' => 伤害加成%, 'cost_reduction' => 消耗减少]
1 => ['cost' => 0, 'damage_bonus' => 0, 'cost_reduction' => 0],
2 => ['cost' => 2, 'damage_bonus' => 10, 'cost_reduction' => 2],
3 => ['cost' => 3, 'damage_bonus' => 20, 'cost_reduction' => 4],
4 => ['cost' => 4, 'damage_bonus' => 30, 'cost_reduction' => 6],
5 => ['cost' => 5, 'damage_bonus' => 40, 'cost_reduction' => 8],
6 => ['cost' => 6, 'damage_bonus' => 50, 'cost_reduction' => 10],
7 => ['cost' => 8, 'damage_bonus' => 60, 'cost_reduction' => 12],
8 => ['cost' => 10, 'damage_bonus' => 70, 'cost_reduction' => 14],
9 => ['cost' => 12, 'damage_bonus' => 80, 'cost_reduction' => 16],
10 => ['cost' => 15, 'damage_bonus' => 100, 'cost_reduction' => 20],
],
// 地牢法术掉落映射 - 定义各地牢的法术资源书掉落池
'dungeon_spell_drops' => [
1 => [1, 10], // 七玄门 (Lv.1-5): 火球术、冰雹术
2 => [2, 3, 11], // 太南谷 (Lv.5-10): 冰锥术、雷击术、炎爆术
3 => [20, 21], // 血色禁地 (Lv.10-15): 治愈术、神圣庇护
4 => [12], // 黄枫谷 (Lv.15-20): 流星雨
5 => [4], // 燕翎堡 (Lv.20-30): 烈焰焚天
6 => [5, 22], // 越京皇宫 (Lv.30-40): 诛仙剑气、恢复光环
7 => [13], // 乱星海-魁星岛 (Lv.40-50): 灭世风暴
8 => [13, 22], // 虚天殿 (Lv.50-60): 灭世风暴、恢复光环
9 => [5, 13], // 外星海 (Lv.60+): 诛仙剑气、灭世风暴
],
];

View File

@ -87,6 +87,9 @@ class Item
$growth = $typeConfig['growth'] ?? 0;
$item->heal = $baseStats[$qualityIndex] + ($level * $growth) + rand(0, 10);
$item->desc = "Lv.{$level} {$quality}品质的药剂";
} elseif ($type === 'spell_tome') {
// 法术资源书特殊处理
$item->desc = "Lv.{$level} {$quality}品质的法术资源书";
} else {
// 检查是否有特定物品配置
$specificConfig = $typeConfig['specific_config'][$item->name] ?? [];

View File

@ -13,6 +13,12 @@ class Partner
public array $baseStats = [];
public array $equip = []; // weapon, armor, ring, boots, necklace
// 法术系统
public int $mana = 80; // 当前魔法值(队友初始值低于玩家)
public int $maxMana = 80; // 最大魔法值
public array $spells = []; // 已学习的法术ID列表
public array $spellBooks = []; // 拥有的法术资源书
// 天赋系统
public array $talents = [
'hp' => 0,
@ -58,6 +64,12 @@ class Partner
$this->talents = $data['talents'] ?? $this->talents;
$this->talentWeights = $data['talentWeights'] ?? $this->talentWeights;
// 加载法术系统数据
$this->mana = $data['mana'] ?? $this->mana;
$this->maxMana = $data['maxMana'] ?? $this->maxMana;
$this->spells = $data['spells'] ?? $this->spells;
$this->spellBooks = $data['spellBooks'] ?? $this->spellBooks;
// 设置当前血量为最大血量
$stats = $this->getStats();
$this->hp = $data['hp'] ?? $stats['maxHp'];
@ -249,4 +261,54 @@ class Partner
return $this->hp - $oldHp; // 返回实际恢复的血量
}
/**
* 恢复魔法值,不超过上限
*/
public function recoverMana(int $amount): int
{
$oldMana = $this->mana;
$this->mana = min($this->mana + $amount, $this->maxMana);
return $this->mana - $oldMana;
}
/**
* 消耗魔法值(返回是否成功)
*/
public function spendMana(int $amount): bool
{
if ($this->mana >= $amount) {
$this->mana -= $amount;
return true;
}
return false;
}
/**
* 学习法术
*/
public function learnSpell(int $spellId): bool
{
if (!isset($this->spells[$spellId])) {
$this->spells[$spellId] = ['level' => 1];
return true;
}
return false;
}
/**
* 检查是否已学习法术
*/
public function hasSpell(int $spellId): bool
{
return isset($this->spells[$spellId]);
}
/**
* 获取法术等级
*/
public function getSpellLevel(int $spellId): int
{
return $this->spells[$spellId]['level'] ?? 0;
}
}

View File

@ -21,6 +21,12 @@ class Player
public int $spiritStones = 0; // 灵石
// 法术系统
public int $mana = 100; // 当前魔法值
public int $maxMana = 100; // 最大魔法值
public array $spells = []; // 已学习的法术ID列表 [1 => ['level' => 1], ...]
public array $spellBooks = []; // 拥有的法术资源书 [spell_id => 数量, ...]
// 天赋系统
public int $talentPoints = 0; // 可用天赋点
public array $talents = [ // 已分配的天赋点
@ -100,6 +106,109 @@ class Player
$this->hp = $stats['maxHp'];
}
/**
* 恢复魔法值,不超过上限
* @param int $amount 恢复量
* @return int 实际恢复量
*/
public function recoverMana(int $amount): int
{
$oldMana = $this->mana;
$this->mana = min($this->mana + $amount, $this->maxMana);
return $this->mana - $oldMana;
}
/**
* 完全恢复魔法值
*/
public function fullRecoverMana(): void
{
$this->mana = $this->maxMana;
}
/**
* 消耗魔法值(返回是否成功)
*/
public function spendMana(int $amount): bool
{
if ($this->mana >= $amount) {
$this->mana -= $amount;
return true;
}
return false;
}
/**
* 学习法术
*/
public function learnSpell(int $spellId): bool
{
if (!isset($this->spells[$spellId])) {
$this->spells[$spellId] = ['level' => 1];
return true;
}
return false;
}
/**
* 升级法术等级
*/
public function upgradeSpell(int $spellId): bool
{
if (!isset($this->spells[$spellId])) {
return false;
}
$this->spells[$spellId]['level'] = min($this->spells[$spellId]['level'] + 1, 10);
return true;
}
/**
* 检查是否已学习法术
*/
public function hasSpell(int $spellId): bool
{
return isset($this->spells[$spellId]);
}
/**
* 获取法术等级
*/
public function getSpellLevel(int $spellId): int
{
return $this->spells[$spellId]['level'] ?? 0;
}
/**
* 添加法术资源书
*/
public function addSpellBook(int $spellId, int $quantity = 1): void
{
if (!isset($this->spellBooks[$spellId])) {
$this->spellBooks[$spellId] = 0;
}
$this->spellBooks[$spellId] += $quantity;
}
/**
* 消耗法术资源书
*/
public function spendSpellBooks(int $spellId, int $quantity = 1): bool
{
if (($this->spellBooks[$spellId] ?? 0) >= $quantity) {
$this->spellBooks[$spellId] -= $quantity;
return true;
}
return false;
}
/**
* 获取法术资源书数量
*/
public function getSpellBookCount(int $spellId): int
{
return $this->spellBooks[$spellId] ?? 0;
}
public function gainExp(int $amount): bool
{
$this->exp += $amount;

View File

@ -18,6 +18,9 @@ class Battle
/** @var array<string, int> 同伴当前HP */
private array $partnerHp = [];
// 法术数据
private array $spellsData = [];
private array $qualityColors = [
'common' => "\033[37m", // 白色
'rare' => "\033[34m", // 蓝色
@ -41,6 +44,7 @@ class Battle
public function __construct(public Game $game)
{
$this->player = $game->player;
$this->spellsData = require __DIR__ . '/../../src/Data/spells.php';
}
/**
@ -289,14 +293,14 @@ class Battle
private function determineFirstStrike(): bool
{
// Use the leader's level for comparison
$leader = $this->enemies[count($this->enemies) - 1]; // Assume leader is last or first?
$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));
@ -305,10 +309,359 @@ class Battle
return $roll <= $playerChance;
}
/**
* 玩家选择行动类型
*/
private function playerChooseAction(): string
{
// 简化:默认使用普通攻击,除非有法术且魔法值充足
// 为了简化战斗流程,我们先自动选择攻击
// 在实战中可以添加交互菜单
// 检查是否有可以施放的法术
if (empty($this->player->spells) || $this->player->mana < 15) {
return 'attack';
}
// 暂时选择法术的概率(可根据需要调整)
// 如果玩家有法术且魔法值充足50% 概率选择法术
$spellChance = rand(1, 100);
if ($spellChance <= 40) { // 40% 概率使用法术
return 'spell';
}
return 'attack';
}
/**
* 玩家施放法术
*/
private function playerCastSpell($out): bool
{
$stats = $this->player->getStats();
// 随机选择一个已学的法术
$availableSpells = [];
foreach ($this->player->spells as $spellId => $spellData) {
$spellInfo = $this->getSpellInfo($spellId);
if (!$spellInfo) continue;
$cost = $spellInfo['cost'] ?? 0;
$upgrades = $this->spellsData['upgrades'] ?? [];
$level = $spellData['level'] ?? 1;
$upgradeInfo = $upgrades[$level] ?? [];
$costReduction = $upgradeInfo['cost_reduction'] ?? 0;
$actualCost = max(1, $cost - $costReduction);
if ($this->player->mana >= $actualCost) {
$availableSpells[$spellId] = ['info' => $spellInfo, 'cost' => $actualCost, 'level' => $level];
}
}
if (empty($availableSpells)) {
// 如果没有可用的法术,改用普通攻击
$out->writeln("{$this->cyan}{$this->reset} {$this->yellow}✦ 魔法值不足,改为普通攻击{$this->reset}");
// 递归调用普通攻击
return $this->executePhysicalAttack($out);
}
// 随机选择一个可用的法术
$selectedSpellId = array_rand($availableSpells);
$spellData = $availableSpells[$selectedSpellId];
$spellInfo = $spellData['info'];
$actualCost = $spellData['cost'];
$spellLevel = $spellData['level'];
// 消耗魔法值
$this->player->spendMana($actualCost);
// 获取法术升级信息
$upgrades = $this->spellsData['upgrades'] ?? [];
$upgradeInfo = $upgrades[$spellLevel] ?? [];
$damageBonus = $upgradeInfo['damage_bonus'] ?? 0;
$type = $spellInfo['type'] ?? '';
$name = $spellInfo['name'] ?? '未知法术';
if ($type === 'damage_single') {
return $this->castDamageSingleSpell($out, $selectedSpellId, $spellInfo, $stats, $damageBonus, $name);
} elseif ($type === 'damage_aoe') {
return $this->castDamageAoeSpell($out, $selectedSpellId, $spellInfo, $stats, $damageBonus, $name);
} elseif ($type === '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;
// 计算法术伤害
$baseDamageMultiplier = $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;
if ($isCrit) {
$critDmg = $stats['critdmg'];
$damage = (int)($damage * ($critDmg / 100));
$out->writeln("{$this->cyan}{$this->reset} {$this->magenta}{$this->reset} 你施放 {$name}... {$this->red}{$this->bold}暴击!{$this->reset}");
$out->writeln("{$this->cyan}{$this->reset} {$this->magenta}✨ 造成 {$damage} 点魔法伤害!{$this->reset}");
} else {
$out->writeln("{$this->cyan}{$this->reset} {$this->magenta}{$this->reset} 你施放 {$name}...");
$out->writeln("{$this->cyan}{$this->reset} {$this->magenta}✨ 造成 {$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
{
$out->writeln("{$this->cyan}{$this->reset} {$this->magenta}{$this->reset} 你施放 {$name}...");
$out->writeln("{$this->cyan}{$this->reset} {$this->magenta}✨ 魔法在整个战场爆炸!{$this->reset}");
// 计算法术伤害
$baseDamageMultiplier = $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 castSupportSpell($out, array $spellInfo, array $stats, string $name): bool
{
$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 generateSpellTomeDrop(Monster $enemy): ?array
{
// 获取当前地牢的法术掉落池
$dungeonId = $this->game->dungeonId;
$dungeonSpellDrops = $this->spellsData['dungeon_spell_drops'] ?? [];
if (!isset($dungeonSpellDrops[$dungeonId])) {
return null; // 该地牢没有配置法术掉落
}
$spellIds = $dungeonSpellDrops[$dungeonId];
if (empty($spellIds)) {
return null;
}
// 从该地牢的掉落池中随机选择一个法术
$spellId = $spellIds[array_rand($spellIds)];
$spellInfo = $this->getSpellInfo($spellId);
if (!$spellInfo) {
return null;
}
// 创建法术资源书物品
$tome = [
'name' => $spellInfo['name'] . '的法术书',
'type' => 'spell_tome',
'quality' => $spellInfo['quality'] ?? 'common',
'level' => $enemy->level,
'spell_id' => $spellId,
'spell_name' => $spellInfo['name'],
'desc' => "能够学习或升级 {$spellInfo['name']} 的法术资源书",
];
return $tome;
}
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) {
@ -317,7 +670,7 @@ class Battle
break;
}
}
if (!$target) return true; // All dead
// 计算物理伤害和魔法伤害
@ -345,7 +698,7 @@ class Battle
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);
@ -493,11 +846,11 @@ class Battle
$totalExp = 0;
$totalStones = 0;
$allDrops = [];
foreach ($this->enemies as $enemy) {
$totalExp += $enemy->expReward;
$totalStones += $enemy->spiritStoneReward;
// 掉落
foreach ($enemy->getEquippedItems() as $item) {
$this->player->addItem($item);
@ -509,6 +862,20 @@ class Battle
$allDrops[] = $drop['item'];
}
}
// 掉落法术资源书
$spellTomeDropChance = 15; // 15% 概率掉落法术资源书
if (rand(1, 100) <= $spellTomeDropChance && !empty($this->spellsData)) {
$spellTome = $this->generateSpellTomeDrop($enemy);
if ($spellTome) {
// 添加到玩家的法术资源书库存
$spellId = $spellTome['spell_id'] ?? 0;
if ($spellId > 0) {
$this->player->addSpellBook($spellId, 1);
$allDrops[] = $spellTome;
}
}
}
}
// 经验
@ -537,6 +904,19 @@ class Battle
$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) {

View File

@ -22,6 +22,7 @@ class Menu
$out->writeln("[5] 同伴管理");
$out->writeln("[6] 天赋系统");
$out->writeln("[7] 装备强化");
$out->writeln("[8] 法术系统");
$out->writeln("[0] 退出");
$out->writeln("=========================");
@ -48,6 +49,9 @@ class Menu
} elseif ($choice == 7) {
$this->game->state = Game::EQUIPMENT_ENHANCE;
} elseif ($choice == 8) {
$this->game->state = Game::SPELL;
} elseif ($choice == 0) {
$this->game->state = Game::EXIT;

View File

@ -84,8 +84,8 @@ class NpcPanel
if ($key === 'recruit' && isset($this->game->player->partners[$npc['id']])) {
$label = '已入队 ✓';
}
$this->game->output->writeln("[{$idx}] {$label}");
$idx_no = $idx + 1;
$this->game->output->writeln("[{$idx_no}] {$label}");
}
$this->game->output->writeln("[0] 离开");
@ -94,8 +94,8 @@ class NpcPanel
if ($choice == 0) return;
if (isset($actionKeys[$choice])) {
$actionType = $actionKeys[$choice];
if (isset($actionKeys[$choice-1])) {
$actionType = $actionKeys[$choice-1];
$actionData = $actions[$actionType];
$this->handleAction($actionType, $actionData, $npc);
Screen::pause($this->game->output);

461
src/Modules/SpellPanel.php Normal file
View File

@ -0,0 +1,461 @@
<?php
namespace Game\Modules;
use Game\Core\Game;
use Game\Core\Input;
use Game\Core\Screen;
class SpellPanel
{
private string $cyan = "\033[36m";
private string $white = "\033[37m";
private string $reset = "\033[0m";
private string $bold = "\033[1m";
private string $green = "\033[32m";
private string $yellow = "\033[33m";
private string $magenta = "\033[35m";
private string $red = "\033[31m";
public function __construct(public Game $game)
{
}
public function show()
{
static $spellsData = null;
if ($spellsData === null) {
$spellsData = require __DIR__ . '/../../src/Data/spells.php';
}
$out = $this->game->output;
$in = $this->game->input;
$player = $this->game->player;
while (true) {
// 清屏显示菜单
Screen::clear($out);
$out->writeln("{$this->bold}{$this->cyan}=== 法术系统 ==={$this->reset}");
$out->writeln("");
// 显示魔法值状态
$manaPercent = ($player->mana / $player->maxMana) * 100;
$manaBar = $this->getManaBar($player->mana, $player->maxMana);
$out->writeln("魔法值: {$manaBar} {$player->mana}/{$player->maxMana}");
$out->writeln("");
// 显示已学习的法术
$out->writeln("{$this->bold}{$this->yellow}已学习的法术:{$this->reset}");
if (empty($player->spells)) {
$out->writeln(" {$this->white}暂无法术{$this->reset}");
} else {
$spellIndex = 0;
foreach ($player->spells as $spellId => $spellData) {
$spellIndex++;
// 获取法术信息
$spellInfo = $this->getSpellInfo($spellId, $spellsData);
$level = $spellData['level'] ?? 1;
$name = $spellInfo['name'] ?? '未知法术';
$quality = $spellInfo['quality'] ?? 'common';
$qualityColor = $this->getQualityColor($quality);
$out->writeln(" [{$spellIndex}] {$qualityColor}{$name}{$this->reset} (Lv.{$level})");
}
}
$out->writeln("");
// 显示拥有的资源书
$out->writeln("{$this->bold}{$this->magenta}拥有的法术资源书:{$this->reset}");
if (empty($player->spellBooks)) {
$out->writeln(" {$this->white}暂无资源书{$this->reset}");
} else {
$bookIndex = 0;
foreach ($player->spellBooks as $spellId => $count) {
$bookIndex++;
$spellInfo = $this->getSpellInfo($spellId, $spellsData);
$name = $spellInfo['name'] ?? '未知法术';
$quality = $spellInfo['quality'] ?? 'common';
$qualityColor = $this->getQualityColor($quality);
$out->writeln(" [{$bookIndex}] {$qualityColor}{$name}的法术书{$this->reset} x{$count}");
}
}
$out->writeln("");
// 菜单选项
$out->writeln("{$this->bold}选择操作:{$this->reset}");
$out->writeln("[1] 查看法术详情");
$out->writeln("[2] 学习法术");
$out->writeln("[3] 升级法术");
$out->writeln("[0] 返回");
$choice = Input::ask($this->game->output, "请选择: ");;
switch ($choice) {
case '1':
$this->viewSpellDetails($spellsData);
break;
case '2':
$this->learnSpell($spellsData);
break;
case '3':
$this->upgradeSpell($spellsData);
break;
case '0':
$this->game->state = Game::MENU;
return;
default:
$out->writeln("{$this->red}无效选择{$this->reset}");
Screen::delay(500000);
}
}
}
/**
* 查看法术详情
*/
private function viewSpellDetails(array $spellsData)
{
$out = $this->game->output;
$player = $this->game->player;
while (true) {
Screen::clear($out);
$out->writeln("{$this->bold}{$this->cyan}=== 法术详情 ==={$this->reset}");
$out->writeln("");
if (empty($player->spells)) {
$out->writeln("{$this->red}还未学习任何法术{$this->reset}");
Screen::delay(1000000);
return;
}
// 列出已学习的法术
$spellArray = [];
$spellIndex = 0;
foreach ($player->spells as $spellId => $spellData) {
$spellIndex++;
$spellInfo = $this->getSpellInfo($spellId, $spellsData);
$name = $spellInfo['name'] ?? '未知法术';
$quality = $spellInfo['quality'] ?? 'common';
$qualityColor = $this->getQualityColor($quality);
$level = $spellData['level'] ?? 1;
$out->writeln("[{$spellIndex}] {$qualityColor}{$name}{$this->reset} (Lv.{$level})");
$spellArray[$spellIndex] = ['spellId' => $spellId, 'spellInfo' => $spellInfo];
}
$out->writeln("[0] 返回");
$choice = Input::ask($this->game->output, "请选择: ");;
if ($choice == '0') {
return;
}
if (isset($spellArray[$choice])) {
$selected = $spellArray[$choice];
$this->displaySpellDetail($selected['spellId'], $selected['spellInfo'], $spellsData);
} else {
$out->writeln("{$this->red}无效选择{$this->reset}");
Screen::delay(500000);
}
}
}
/**
* 显示单个法术的详细信息
*/
private function displaySpellDetail(int $spellId, array $spellInfo, array $spellsData)
{
$out = $this->game->output;
$player = $this->game->player;
Screen::clear($out);
$out->writeln("{$this->bold}{$this->cyan}=== {$spellInfo['name']} ==={$this->reset}");
$out->writeln("");
$quality = $spellInfo['quality'] ?? 'common';
$qualityColor = $this->getQualityColor($quality);
$level = $player->getSpellLevel($spellId);
$out->writeln("品质: {$qualityColor}{$this->getQualityName($quality)}{$this->reset}");
$out->writeln("等级: {$this->yellow}Lv.{$level}{$this->reset}");
$out->writeln("描述: {$spellInfo['desc']}");
$out->writeln("");
// 显示法术效果信息
$type = $spellInfo['type'] ?? '';
$baseCost = $spellInfo['cost'] ?? 0;
$upgrades = $spellsData['upgrades'] ?? [];
$upgradeInfo = $upgrades[$level] ?? [];
$costReduction = $upgradeInfo['cost_reduction'] ?? 0;
$actualCost = max(1, $baseCost - $costReduction);
$out->writeln("{$this->green}基础消耗: {$baseCost} → 当前消耗: {$actualCost}{$this->reset}");
if ($type === 'damage_single') {
$damage = $spellInfo['damage'] ?? 1.0;
$damageBonus = $upgradeInfo['damage_bonus'] ?? 0;
$actualDamage = $damage * (1 + $damageBonus / 100);
$out->writeln("伤害倍数: {$this->yellow}" . number_format($damage, 2) . "{$this->reset}{$this->green}" . number_format($actualDamage, 2) . "x{$this->reset}");
$out->writeln("效果: {$this->magenta}对单个敌人造成魔法伤害{$this->reset}");
} elseif ($type === 'damage_aoe') {
$damage = $spellInfo['damage'] ?? 1.0;
$damageBonus = $upgradeInfo['damage_bonus'] ?? 0;
$actualDamage = $damage * (1 + $damageBonus / 100);
$out->writeln("伤害倍数: {$this->yellow}" . number_format($damage, 2) . "{$this->reset}{$this->green}" . number_format($actualDamage, 2) . "x{$this->reset}");
$out->writeln("效果: {$this->magenta}对所有敌人造成魔法伤害{$this->reset}");
} elseif ($type === 'support') {
$subtype = $spellInfo['subtype'] ?? '';
if ($subtype === 'heal' || $subtype === 'heal_all') {
$heal = $spellInfo['heal'] ?? 0.5;
$healBase = $spellInfo['heal_base'] ?? 20;
$out->writeln("恢复效果: {$this->green}魔攻 x {$heal} + {$healBase}{$this->reset}");
$out->writeln("效果: {$this->magenta}恢复生命值{$this->reset}");
} elseif ($subtype === 'defend') {
$defenseBoos = $spellInfo['defense_boost'] ?? 0;
$out->writeln("防御增加: {$this->green}+{$defenseBoos}{$this->reset}");
$out->writeln("效果: {$this->magenta}增加防御力{$this->reset}");
}
}
$out->writeln("");
Screen::pause($out);
}
/**
* 学习法术
*/
private function learnSpell(array $spellsData)
{
$out = $this->game->output;
$player = $this->game->player;
Screen::clear($out);
$out->writeln("{$this->bold}{$this->cyan}=== 学习法术 ==={$this->reset}");
$out->writeln("");
// 获取所有可学习的法术(按品质分类)
$allSpells = [];
foreach ($spellsData as $category => $spells) {
if (is_array($spells) && $category !== 'quality_levels' && $category !== 'upgrades') {
foreach ($spells as $spellId => $spellInfo) {
if (is_numeric($spellId)) {
$allSpells[$spellId] = $spellInfo;
}
}
}
}
// 显示可以学习的法术
$learnableIndex = 0;
$learnableSpells = [];
foreach ($allSpells as $spellId => $spellInfo) {
// 检查是否已学习
if ($player->hasSpell($spellId)) {
continue;
}
// 检查等级要求
$levelReq = $spellInfo['level_req'] ?? 1;
if ($player->level < $levelReq) {
continue;
}
// 检查是否有资源书
$bookCount = $player->getSpellBookCount($spellId);
if ($bookCount < 1) {
continue;
}
$learnableIndex++;
$quality = $spellInfo['quality'] ?? 'common';
$qualityColor = $this->getQualityColor($quality);
$name = $spellInfo['name'] ?? '未知法术';
$out->writeln("[{$learnableIndex}] {$qualityColor}{$name}{$this->reset} (需求等级: {$levelReq})");
$learnableSpells[$learnableIndex] = ['spellId' => $spellId, 'spellInfo' => $spellInfo];
}
if (empty($learnableSpells)) {
$out->writeln("{$this->yellow}暂无可学习的法术(需要有资源书并满足等级要求){$this->reset}");
Screen::delay(1000000);
return;
}
$out->writeln("[0] 返回");
$choice = Input::ask($this->game->output, "请选择: ");;
if ($choice == '0') {
return;
}
if (isset($learnableSpells[$choice])) {
$selected = $learnableSpells[$choice];
$spellId = $selected['spellId'];
// 消耗资源书
$player->spendSpellBooks($spellId, 1);
// 学习法术
if ($player->learnSpell($spellId)) {
$out->writeln("{$this->green}✓ 成功学习 {$selected['spellInfo']['name']}{$this->reset}");
} else {
$out->writeln("{$this->red}✗ 学习失败{$this->reset}");
}
Screen::delay(1000000);
} else {
$out->writeln("{$this->red}无效选择{$this->reset}");
Screen::delay(500000);
}
}
/**
* 升级法术
*/
private function upgradeSpell(array $spellsData)
{
$out = $this->game->output;
$in = $this->game->input;
$player = $this->game->player;
Screen::clear($out);
$out->writeln("{$this->bold}{$this->cyan}=== 升级法术 ==={$this->reset}");
$out->writeln("");
if (empty($player->spells)) {
$out->writeln("{$this->red}还未学习任何法术{$this->reset}");
Screen::delay(1000000);
return;
}
// 列出已学习但未满级的法术
$upgradableIndex = 0;
$upgradableSpells = [];
$upgrades = $spellsData['upgrades'] ?? [];
foreach ($player->spells as $spellId => $spellData) {
$level = $spellData['level'] ?? 1;
// 检查是否已满级
if ($level >= 10) {
continue;
}
$spellInfo = $this->getSpellInfo($spellId, $spellsData);
$quality = $spellInfo['quality'] ?? 'common';
$qualityColor = $this->getQualityColor($quality);
$name = $spellInfo['name'] ?? '未知法术';
// 获取升级所需资源书数量
$nextLevel = $level + 1;
$upgradeInfo = $upgrades[$nextLevel] ?? ['cost' => 0];
$booksNeeded = $upgradeInfo['cost'] ?? 0;
$booksOwned = $player->getSpellBookCount($spellId);
$upgradableIndex++;
$canUpgrade = $booksOwned >= $booksNeeded;
$statusColor = $canUpgrade ? $this->green : $this->red;
$out->writeln("[{$upgradableIndex}] {$qualityColor}{$name}{$this->reset} Lv.{$level}");
$out->writeln(" 升级所需: {$statusColor}{$booksNeeded}{$this->reset} 本资源书 (拥有: {$booksOwned})");
$upgradableSpells[$upgradableIndex] = ['spellId' => $spellId, 'spellInfo' => $spellInfo];
}
if (empty($upgradableSpells)) {
$out->writeln("{$this->yellow}暂无可升级的法术{$this->reset}");
Screen::delay(1000000);
return;
}
$out->writeln("[0] 返回");
$choice = $in->prompt("请选择要升级的法术");
if ($choice == '0') {
return;
}
if (isset($upgradableSpells[$choice])) {
$selected = $upgradableSpells[$choice];
$spellId = $selected['spellId'];
$level = $player->getSpellLevel($spellId);
$nextLevel = $level + 1;
$upgradeInfo = $upgrades[$nextLevel] ?? ['cost' => 0];
$booksNeeded = $upgradeInfo['cost'] ?? 0;
// 检查是否有足够的资源书
if (!$player->spendSpellBooks($spellId, $booksNeeded)) {
$out->writeln("{$this->red}✗ 资源书不足!{$this->reset}");
Screen::delay(1000000);
return;
}
// 升级法术
if ($player->upgradeSpell($spellId)) {
$out->writeln("{$this->green}✓ 成功升级 {$selected['spellInfo']['name']} 至 Lv.{$nextLevel}{$this->reset}");
} else {
$out->writeln("{$this->red}✗ 升级失败{$this->reset}");
}
Screen::delay(1000000);
} else {
$out->writeln("{$this->red}无效选择{$this->reset}");
Screen::delay(500000);
}
}
/**
* 获取魔法值进度条
*/
private function getManaBar(int $current, int $max): string
{
$percent = ($current / $max) * 100;
$filled = (int)(20 * ($current / $max));
$empty = 20 - $filled;
$color = $percent >= 50 ? $this->green : ($percent >= 25 ? $this->yellow : $this->red);
return "{$color}[" . str_repeat('█', $filled) . str_repeat('░', $empty) . "]{$this->reset} " . number_format($percent, 0) . "%";
}
/**
* 获取法术的品质颜色
*/
private function getQualityColor(string $quality): string
{
return match($quality) {
'common' => "\033[37m", // 白色
'rare' => "\033[34m", // 蓝色
'epic' => "\033[35m", // 紫色
'legendary' => "\033[33m", // 黄色
default => $this->white
};
}
/**
* 获取品质名称
*/
private function getQualityName(string $quality): string
{
return match($quality) {
'common' => '普通',
'rare' => '稀有',
'epic' => '史诗',
'legendary' => '传奇',
default => '未知'
};
}
/**
* 获取法术信息
*/
private function getSpellInfo(int $spellId, array $spellsData): ?array
{
foreach ($spellsData as $category => $spells) {
if (is_array($spells) && $category !== 'quality_levels' && $category !== 'upgrades') {
if (isset($spells[$spellId])) {
return $spells[$spellId];
}
}
}
return null;
}
}