逻辑优化
This commit is contained in:
parent
595295ebf2
commit
9d9af5f96c
|
|
@ -4,7 +4,8 @@
|
|||
"type": "project",
|
||||
"require": {
|
||||
"php": ">=8.0",
|
||||
"symfony/console": "^6.4"
|
||||
"symfony/console": "^6.4",
|
||||
"symfony/var-dumper": "^6.4"
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
|
|
|
|||
90
composer.lock
generated
90
composer.lock
generated
|
|
@ -4,7 +4,7 @@
|
|||
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
|
||||
"This file is @generated automatically"
|
||||
],
|
||||
"content-hash": "ff92c56914631088226b9b90349a3d85",
|
||||
"content-hash": "f41a3afdcc806c29e3fe260d3a61b340",
|
||||
"packages": [
|
||||
{
|
||||
"name": "psr/container",
|
||||
|
|
@ -734,6 +734,94 @@
|
|||
}
|
||||
],
|
||||
"time": "2025-09-11T14:32:46+00:00"
|
||||
},
|
||||
{
|
||||
"name": "symfony/var-dumper",
|
||||
"version": "v6.4.26",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/symfony/var-dumper.git",
|
||||
"reference": "cfae1497a2f1eaad78dbc0590311c599c7178d4a"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/symfony/var-dumper/zipball/cfae1497a2f1eaad78dbc0590311c599c7178d4a",
|
||||
"reference": "cfae1497a2f1eaad78dbc0590311c599c7178d4a",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": ">=8.1",
|
||||
"symfony/deprecation-contracts": "^2.5|^3",
|
||||
"symfony/polyfill-mbstring": "~1.0"
|
||||
},
|
||||
"conflict": {
|
||||
"symfony/console": "<5.4"
|
||||
},
|
||||
"require-dev": {
|
||||
"symfony/console": "^5.4|^6.0|^7.0",
|
||||
"symfony/error-handler": "^6.3|^7.0",
|
||||
"symfony/http-kernel": "^5.4|^6.0|^7.0",
|
||||
"symfony/process": "^5.4|^6.0|^7.0",
|
||||
"symfony/uid": "^5.4|^6.0|^7.0",
|
||||
"twig/twig": "^2.13|^3.0.4"
|
||||
},
|
||||
"bin": [
|
||||
"Resources/bin/var-dump-server"
|
||||
],
|
||||
"type": "library",
|
||||
"autoload": {
|
||||
"files": [
|
||||
"Resources/functions/dump.php"
|
||||
],
|
||||
"psr-4": {
|
||||
"Symfony\\Component\\VarDumper\\": ""
|
||||
},
|
||||
"exclude-from-classmap": [
|
||||
"/Tests/"
|
||||
]
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Nicolas Grekas",
|
||||
"email": "p@tchwork.com"
|
||||
},
|
||||
{
|
||||
"name": "Symfony Community",
|
||||
"homepage": "https://symfony.com/contributors"
|
||||
}
|
||||
],
|
||||
"description": "Provides mechanisms for walking through any arbitrary PHP variable",
|
||||
"homepage": "https://symfony.com",
|
||||
"keywords": [
|
||||
"debug",
|
||||
"dump"
|
||||
],
|
||||
"support": {
|
||||
"source": "https://github.com/symfony/var-dumper/tree/v6.4.26"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
"url": "https://symfony.com/sponsor",
|
||||
"type": "custom"
|
||||
},
|
||||
{
|
||||
"url": "https://github.com/fabpot",
|
||||
"type": "github"
|
||||
},
|
||||
{
|
||||
"url": "https://github.com/nicolas-grekas",
|
||||
"type": "github"
|
||||
},
|
||||
{
|
||||
"url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
|
||||
"type": "tidelift"
|
||||
}
|
||||
],
|
||||
"time": "2025-09-25T15:37:27+00:00"
|
||||
}
|
||||
],
|
||||
"packages-dev": [],
|
||||
|
|
|
|||
|
|
@ -8,7 +8,6 @@ use Game\Modules\Menu;
|
|||
use Game\Modules\Battle;
|
||||
use Game\Modules\StatsPanel;
|
||||
use Game\Modules\NpcPanel;
|
||||
use Game\Modules\PartnerPanel;
|
||||
use Game\Modules\TalentPanel;
|
||||
use Game\Modules\DungeonSelectPanel;
|
||||
use Game\Modules\EquipmentEnhancePanel;
|
||||
|
|
@ -21,11 +20,9 @@ class Game
|
|||
const STATS = 3;
|
||||
const INVENTORY = 4;
|
||||
const NPC = 5;
|
||||
const PARTNER = 6;
|
||||
const TALENT = 7;
|
||||
const TALENT = 6;
|
||||
const EQUIPMENT_ENHANCE = 7;
|
||||
const DUNGEON_SELECT = 8;
|
||||
const EQUIPMENT_ENHANCE = 9;
|
||||
const SPELL = 10;
|
||||
const EXIT = 0;
|
||||
|
||||
public Player $player;
|
||||
|
|
@ -100,11 +97,10 @@ 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->player->skillSlots = $p['skillSlots'] ?? $this->player->skillSlots;
|
||||
|
||||
$this->dungeonId = $data['dungeonId'] ?? $this->dungeonId;
|
||||
$this->state = self::MENU;
|
||||
|
|
@ -128,8 +124,7 @@ class Game
|
|||
'talentWeights' => $partner->talentWeights,
|
||||
'mana' => $partner->mana,
|
||||
'maxMana' => $partner->maxMana,
|
||||
'spells' => $partner->spells,
|
||||
'spellBooks' => $partner->spellBooks,
|
||||
'skillSlots' => $partner->skillSlots,
|
||||
|
||||
'hp' => $partner->hp,
|
||||
'maxHp' => $partner->maxHp,
|
||||
|
|
@ -164,8 +159,7 @@ class Game
|
|||
'talents' => $this->player->talents,
|
||||
'mana' => $this->player->mana,
|
||||
'maxMana' => $this->player->maxMana,
|
||||
'spells' => $this->player->spells,
|
||||
'spellBooks' => $this->player->spellBooks,
|
||||
'skillSlots' => $this->player->skillSlots,
|
||||
'partners' => $partnersData,
|
||||
],
|
||||
'dungeonId' => $this->dungeonId,
|
||||
|
|
@ -196,18 +190,12 @@ class Game
|
|||
case self::NPC:
|
||||
(new NpcPanel($this))->show();
|
||||
break;
|
||||
case self::PARTNER:
|
||||
(new PartnerPanel($this))->show();
|
||||
break;
|
||||
case self::TALENT:
|
||||
(new TalentPanel($this))->show();
|
||||
break;
|
||||
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();
|
||||
|
|
|
|||
|
|
@ -84,16 +84,17 @@ class GameSession
|
|||
case Game::INVENTORY:
|
||||
(new \Game\Modules\InventoryPanel($this->game))->show();
|
||||
break;
|
||||
case Game::EQUIPMENT_ENHANCE:
|
||||
(new \Game\Modules\EquipmentEnhancePanel($this->game))->show();
|
||||
break;
|
||||
case Game::NPC:
|
||||
(new \Game\Modules\NpcPanel($this->game))->show();
|
||||
break;
|
||||
case Game::PARTNER:
|
||||
(new \Game\Modules\PartnerPanel($this->game))->show();
|
||||
break;
|
||||
case Game::TALENT:
|
||||
(new \Game\Modules\TalentPanel($this->game))->show();
|
||||
break;
|
||||
case Game::EXIT:
|
||||
exit;
|
||||
$this->output->writeln("再见!");
|
||||
$this->game->saveState();
|
||||
break;
|
||||
|
|
|
|||
|
|
@ -30,6 +30,7 @@ class ItemDisplay
|
|||
'ring' => '戒指',
|
||||
'necklace' => '项链',
|
||||
'consume' => '消耗品',
|
||||
'spell' => '法术',
|
||||
];
|
||||
|
||||
// 属性名称
|
||||
|
|
@ -42,6 +43,8 @@ class ItemDisplay
|
|||
'crit' => '暴击',
|
||||
'critdmg' => '暴伤',
|
||||
'heal' => '治疗',
|
||||
'cost' => '消耗',
|
||||
'damage' => '伤害倍率',
|
||||
];
|
||||
|
||||
// 颜色常量
|
||||
|
|
@ -101,7 +104,11 @@ class ItemDisplay
|
|||
foreach (self::$statNames as $key => $name) {
|
||||
$value = $item[$key] ?? 0;
|
||||
if ($value > 0) {
|
||||
$stats[] = "{$name}+{$value}";
|
||||
if ($key === 'damage') {
|
||||
$stats[] = "{$name}:{$value}x";
|
||||
} else {
|
||||
$stats[] = "{$name}+{$value}";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -115,19 +122,61 @@ class ItemDisplay
|
|||
{
|
||||
$lines = [];
|
||||
$enhanceLevel = $item['enhanceLevel'] ?? 0;
|
||||
$enhanceMultiplier = 1 + ($enhanceLevel * 0.05);
|
||||
|
||||
foreach (self::$statNames as $key => $name) {
|
||||
$baseValue = $item[$key] ?? 0;
|
||||
if ($baseValue > 0) {
|
||||
$finalValue = (int)($baseValue * $enhanceMultiplier);
|
||||
if ($enhanceLevel > 0 && $key !== 'heal') {
|
||||
$lines[] = $prefix . self::$cyan . $name . self::$reset . ": " .
|
||||
self::$white . $baseValue . self::$reset . " → " .
|
||||
self::$green . $finalValue . self::$reset;
|
||||
} else {
|
||||
$lines[] = $prefix . self::$cyan . $name . self::$reset . ": " .
|
||||
self::$green . $baseValue . self::$reset;
|
||||
// 法术强化逻辑不同
|
||||
$isSpell = ($item['type'] ?? '') === 'spell';
|
||||
|
||||
if ($isSpell) {
|
||||
// 法术强化: 伤害+5%, 消耗-2
|
||||
$damageMultiplier = 1 + ($enhanceLevel * 0.05);
|
||||
$costReduction = $enhanceLevel * 2;
|
||||
|
||||
foreach (self::$statNames as $key => $name) {
|
||||
$baseValue = $item[$key] ?? 0;
|
||||
if ($baseValue > 0) {
|
||||
if ($key === 'damage') {
|
||||
$finalValue = number_format($baseValue * $damageMultiplier, 2);
|
||||
if ($enhanceLevel > 0) {
|
||||
$lines[] = $prefix . self::$cyan . $name . self::$reset . ": " .
|
||||
self::$white . $baseValue . "x" . self::$reset . " → " .
|
||||
self::$green . $finalValue . "x" . self::$reset;
|
||||
} else {
|
||||
$lines[] = $prefix . self::$cyan . $name . self::$reset . ": " .
|
||||
self::$green . $baseValue . "x" . self::$reset;
|
||||
}
|
||||
} elseif ($key === 'cost') {
|
||||
$finalValue = max(1, $baseValue - $costReduction);
|
||||
if ($enhanceLevel > 0) {
|
||||
$lines[] = $prefix . self::$cyan . $name . self::$reset . ": " .
|
||||
self::$white . $baseValue . self::$reset . " → " .
|
||||
self::$green . $finalValue . self::$reset;
|
||||
} else {
|
||||
$lines[] = $prefix . self::$cyan . $name . self::$reset . ": " .
|
||||
self::$green . $baseValue . self::$reset;
|
||||
}
|
||||
} else {
|
||||
// For other stats on spells, just display the base value
|
||||
$lines[] = $prefix . self::$cyan . $name . self::$reset . ": " .
|
||||
self::$green . $baseValue . self::$reset;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// 装备强化逻辑
|
||||
$enhanceMultiplier = 1 + ($enhanceLevel * 0.05);
|
||||
|
||||
foreach (self::$statNames as $key => $name) {
|
||||
$baseValue = $item[$key] ?? 0;
|
||||
if ($baseValue > 0) {
|
||||
$finalValue = (int)($baseValue * $enhanceMultiplier);
|
||||
if ($enhanceLevel > 0 && $key !== 'heal') {
|
||||
$lines[] = $prefix . self::$cyan . $name . self::$reset . ": " .
|
||||
self::$white . $baseValue . self::$reset . " → " .
|
||||
self::$green . $finalValue . self::$reset;
|
||||
} else {
|
||||
$lines[] = $prefix . self::$cyan . $name . self::$reset . ": " .
|
||||
self::$green . $baseValue . self::$reset;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -58,7 +58,18 @@ return [
|
|||
'title' => '黄枫谷师姐',
|
||||
'min_level' => 15,
|
||||
'desc' => '黄枫谷陈家大小姐,对韩立有一番复杂的情愫。',
|
||||
'base_stats' => [
|
||||
'maxHp' => 500, 'patk' => 40, 'matk' => 60, 'pdef' => 30, 'mdef' => 45, 'crit' => 15, 'critdmg' => 150
|
||||
],
|
||||
// 天赋权重:法术型,偏魔攻和魔防
|
||||
'talent_weights' => [
|
||||
'hp' => 2, 'patk' => 1, 'matk' => 3, 'pdef' => 1, 'mdef' => 2, 'crit' => 2, 'critdmg' => 2
|
||||
],
|
||||
'actions' => [
|
||||
'recruit' => [
|
||||
'text' => "韩师弟...若你不嫌弃,巧倩愿意与你同行。",
|
||||
'cost' => 0,
|
||||
],
|
||||
'talk' => [
|
||||
'text' => "韩师弟...你也筑基成功了?\n当年血色禁地一别,没想到还能再见...",
|
||||
],
|
||||
|
|
@ -74,7 +85,18 @@ return [
|
|||
'title' => '阵法奇才',
|
||||
'min_level' => 20,
|
||||
'desc' => '精通阵法之道的奇女子,可惜天生龙吟之体,寿元无多。',
|
||||
'base_stats' => [
|
||||
'maxHp' => 400, 'patk' => 30, 'matk' => 80, 'pdef' => 25, 'mdef' => 60, 'crit' => 18, 'critdmg' => 160
|
||||
],
|
||||
// 天赋权重:阵法型,高魔攻和暴击
|
||||
'talent_weights' => [
|
||||
'hp' => 1, 'patk' => 1, 'matk' => 4, 'pdef' => 1, 'mdef' => 2, 'crit' => 3, 'critdmg' => 2
|
||||
],
|
||||
'actions' => [
|
||||
'recruit' => [
|
||||
'text' => "若韩前辈不嫌弃如音这副病体,如音愿为前辈布阵御敌。",
|
||||
'cost' => 0,
|
||||
],
|
||||
'talk' => [
|
||||
'text' => "韩前辈,这颠倒五行阵的阵盘我已经修复好了。\n若非前辈当年的千年灵草,如音恐怕早已不在人世...",
|
||||
],
|
||||
|
|
@ -91,7 +113,18 @@ return [
|
|||
'title' => '乱星海女修',
|
||||
'min_level' => 40,
|
||||
'desc' => '为了复活师姐妍丽,不惜修炼鬼道秘术的痴情女子。',
|
||||
'base_stats' => [
|
||||
'maxHp' => 1200, 'patk' => 100, 'matk' => 120, 'pdef' => 60, 'mdef' => 70, 'crit' => 22, 'critdmg' => 170
|
||||
],
|
||||
// 天赋权重:鬼道型,均衡发展偏魔攻
|
||||
'talent_weights' => [
|
||||
'hp' => 2, 'patk' => 2, 'matk' => 3, 'pdef' => 1, 'mdef' => 2, 'crit' => 2, 'critdmg' => 2
|
||||
],
|
||||
'actions' => [
|
||||
'recruit' => [
|
||||
'text' => "韩兄,为了妍丽师姐,元瑶愿与你同行,共闯天下!",
|
||||
'cost' => 0,
|
||||
],
|
||||
'talk' => [
|
||||
'text' => "韩兄,虚天殿一别,别来无恙。\n为了妍丽师姐,哪怕是刀山火海,我也要闯一闯!",
|
||||
],
|
||||
|
|
@ -105,10 +138,21 @@ return [
|
|||
[
|
||||
'id' => 'zi_ling',
|
||||
'name' => '紫灵仙子',
|
||||
'title' => '乱星海第一美女',
|
||||
'title' => '乌星海第一美女',
|
||||
'min_level' => 45,
|
||||
'desc' => '妙音门门主之女,拥有惊世容颜,与韩立关系匪浅。',
|
||||
'base_stats' => [
|
||||
'maxHp' => 1500, 'patk' => 90, 'matk' => 140, 'pdef' => 70, 'mdef' => 90, 'crit' => 25, 'critdmg' => 180
|
||||
],
|
||||
// 天赋权重:音律型,高魔攻和暴击暴伤
|
||||
'talent_weights' => [
|
||||
'hp' => 2, 'patk' => 1, 'matk' => 3, 'pdef' => 1, 'mdef' => 2, 'crit' => 3, 'critdmg' => 3
|
||||
],
|
||||
'actions' => [
|
||||
'recruit' => [
|
||||
'text' => "韩兄...紫灵愿以此身相报,生死相随。",
|
||||
'cost' => 0,
|
||||
],
|
||||
'talk' => [
|
||||
'text' => "韩兄...若非你多次相救,紫灵恐怕早已沦为他人玩物。\n这份恩情,紫灵铭记在心。",
|
||||
],
|
||||
|
|
|
|||
|
|
@ -21,8 +21,13 @@ class Actor
|
|||
public int $mana = 0;
|
||||
public int $maxMana = 0;
|
||||
|
||||
public array $spells = [];
|
||||
public array $spellBooks = [];
|
||||
// 技能槽位系统 (新法术系统)
|
||||
public array $skillSlots = [
|
||||
'skill1' => null,
|
||||
'skill2' => null,
|
||||
'skill3' => null,
|
||||
'skill4' => null,
|
||||
];
|
||||
|
||||
public array $equip = [];
|
||||
public array $inventory = [];
|
||||
|
|
@ -278,21 +283,4 @@ class Actor
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Default factory to create an Actor (delegates to Monster by default).
|
||||
*/
|
||||
public static function create(int $dungeonId): Actor
|
||||
{
|
||||
return Monster::create($dungeonId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Default factory to create a group of Actors (delegates to Monster::createGroup).
|
||||
* @return Actor[]
|
||||
*/
|
||||
public static function createGroup(int $dungeonId): array
|
||||
{
|
||||
return Monster::createGroup($dungeonId);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -236,6 +236,66 @@ class Item
|
|||
return self::createFromSpec($spec, $baseLevel);
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建法术物品
|
||||
* @param int $spellId 法术ID (来自 spells.php)
|
||||
* @param int $level 物品等级
|
||||
* @return array 法术物品数组
|
||||
*/
|
||||
public static function createSpell(int $spellId, int $level = 1): array
|
||||
{
|
||||
static $spellsData = null;
|
||||
if ($spellsData === null) {
|
||||
$spellsData = require __DIR__ . '/../../src/Data/spells.php';
|
||||
}
|
||||
|
||||
// 查找法术信息
|
||||
$spellInfo = null;
|
||||
foreach ($spellsData as $category => $spells) {
|
||||
if (is_array($spells) && $category !== 'quality_levels' && $category !== 'upgrades' && $category !== 'dungeon_spell_drops') {
|
||||
if (isset($spells[$spellId])) {
|
||||
$spellInfo = $spells[$spellId];
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!$spellInfo) {
|
||||
// 默认法术
|
||||
return [
|
||||
'id' => uniqid('spell_'),
|
||||
'type' => 'spell',
|
||||
'name' => '未知法术',
|
||||
'quality' => 'common',
|
||||
'level' => $level,
|
||||
'spellId' => $spellId,
|
||||
'enhanceLevel' => 0,
|
||||
'damage' => 1.0,
|
||||
'cost' => 20,
|
||||
'spellType' => 'damage_single',
|
||||
'desc' => '未知的法术',
|
||||
];
|
||||
}
|
||||
|
||||
return [
|
||||
'id' => uniqid('spell_'),
|
||||
'type' => 'spell',
|
||||
'name' => $spellInfo['name'],
|
||||
'quality' => $spellInfo['quality'] ?? 'common',
|
||||
'level' => $level,
|
||||
'spellId' => $spellId,
|
||||
'enhanceLevel' => 0,
|
||||
'damage' => $spellInfo['damage'] ?? 1.0,
|
||||
'cost' => $spellInfo['cost'] ?? 20,
|
||||
'spellType' => $spellInfo['type'] ?? 'damage_single',
|
||||
'subtype' => $spellInfo['subtype'] ?? null,
|
||||
'heal' => $spellInfo['heal'] ?? 0,
|
||||
'heal_base' => $spellInfo['heal_base'] ?? 0,
|
||||
'defense_boost' => $spellInfo['defense_boost'] ?? 0,
|
||||
'desc' => $spellInfo['desc'] ?? '',
|
||||
];
|
||||
}
|
||||
|
||||
public function toArray(): array
|
||||
{
|
||||
return [
|
||||
|
|
|
|||
|
|
@ -60,7 +60,8 @@ class Monster extends Actor
|
|||
|
||||
// 3. Hydrate monster base stats from maps.php
|
||||
$monster->hydrateFromConfig($selectedMonster);
|
||||
|
||||
$status = $monster->getStats();
|
||||
$monster->hp = $status['maxHp'];
|
||||
return $monster;
|
||||
}
|
||||
|
||||
|
|
@ -109,8 +110,7 @@ class Monster extends Actor
|
|||
}
|
||||
|
||||
// Create monster from selected config
|
||||
$monster = new self();
|
||||
$monster->hydrateFromConfig($selectedConfig);
|
||||
$monster = self::create($dungeonId);
|
||||
|
||||
// Add suffix to distinguish multiple monsters of same type
|
||||
if ($groupSize > 1) {
|
||||
|
|
@ -129,6 +129,7 @@ class Monster extends Actor
|
|||
$this->level = $config['level'] ?? 1;
|
||||
$this->baseHp = $config['hp'] ?? 20;
|
||||
$this->hp = $this->baseHp;
|
||||
$this->maxHp = $this->baseHp;
|
||||
$this->basePatk = $config['patk'] ?? $config['atk'] ?? 4;
|
||||
$this->patk = $this->basePatk;
|
||||
$this->baseMatk = $config['matk'] ?? 2;
|
||||
|
|
@ -138,10 +139,13 @@ class Monster extends Actor
|
|||
$this->baseMdef = $config['mdef'] ?? 0;
|
||||
$this->mdef = $this->baseMdef;
|
||||
$this->crit = $config['crit'] ?? 5;
|
||||
$this->critdmg = $config['critdmg'] ?? 150;
|
||||
$this->critdmg = $config['critdmg'] ?? 130;
|
||||
$this->expReward = $config['exp'] ?? 0;
|
||||
$this->spiritStoneReward = $config['spirit_stones'] ?? 0;
|
||||
|
||||
// 根据等级和基础属性分配天赋点
|
||||
$this->allocateTalentsByLevel();
|
||||
|
||||
// Drops & Equipment
|
||||
$drops = $config['drops'] ?? [];
|
||||
foreach ($drops as $drop) {
|
||||
|
|
@ -268,4 +272,49 @@ class Monster extends Actor
|
|||
}
|
||||
return $drops;
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据等级和基础属性分配天赋点和权重
|
||||
* 怪物根据等级获得天赋点,并按基础属性的占比分配权重
|
||||
*/
|
||||
private function allocateTalentsByLevel(): void
|
||||
{
|
||||
// 每级获得 3 点天赋点(与玩家一致)
|
||||
$talentPoints = ($this->level - 1) * 3;
|
||||
|
||||
if ($talentPoints <= 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
// 计算基础属性的权重比(考虑每点天赋的效果)
|
||||
// talentBonus 定义了每点天赋对应的属性增益
|
||||
$talentBonusMap = [
|
||||
'hp' => 10,
|
||||
'patk' => 5,
|
||||
'matk' => 4,
|
||||
'pdef' => 3,
|
||||
'mdef' => 3,
|
||||
'crit' => 1,
|
||||
'critdmg' => 5,
|
||||
];
|
||||
|
||||
// 计算每个属性的权重(基础属性值 / 天赋加成)
|
||||
$weights = [
|
||||
'hp' => max(1, (int)($this->baseHp / $talentBonusMap['hp'])),
|
||||
'patk' => max(1, (int)($this->basePatk / $talentBonusMap['patk'])),
|
||||
'matk' => max(1, (int)($this->baseMatk / $talentBonusMap['matk'])),
|
||||
'pdef' => max(1, (int)($this->basePdef / $talentBonusMap['pdef'])),
|
||||
'mdef' => max(1, (int)($this->baseMdef / $talentBonusMap['mdef'])),
|
||||
'crit' => max(1, (int)($this->crit / $talentBonusMap['crit'])),
|
||||
'critdmg' => 0,
|
||||
];
|
||||
|
||||
// dd($weights);
|
||||
// 设置权重
|
||||
$this->talentWeights = $weights;
|
||||
$this->talentPoints = $talentPoints;
|
||||
|
||||
// 自动按权重分配天赋点
|
||||
$this->autoAllocateTalents($talentPoints);
|
||||
}
|
||||
}
|
||||
|
|
@ -35,11 +35,10 @@ class Partner extends Actor
|
|||
$this->talents = $data['talents'] ?? $this->talents;
|
||||
$this->talentWeights = $data['talentWeights'] ?? $this->talentWeights;
|
||||
|
||||
// 加载法术系统数据
|
||||
// 加载技能槽位数据 (新法术系统)
|
||||
$this->mana = $data['mana'] ?? $this->mana;
|
||||
$this->maxMana = $data['maxMana'] ?? 100;
|
||||
$this->spells = $data['spells'] ?? $this->spells;
|
||||
$this->spellBooks = $data['spellBooks'] ?? $this->spellBooks;
|
||||
$this->skillSlots = $data['skillSlots'] ?? $this->skillSlots;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -45,77 +45,6 @@ class Player extends Actor
|
|||
}
|
||||
|
||||
|
||||
/**
|
||||
* 学习法术
|
||||
*/
|
||||
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;
|
||||
}
|
||||
|
||||
/**
|
||||
* Player特有的经验获取,升级时会恢复生命值
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -119,7 +119,7 @@ class Battle
|
|||
// 初始化同伴HP
|
||||
$this->initPartnerHp();
|
||||
|
||||
while ($this->player->hp > 0) {
|
||||
while ($this->player->hp >= 0) {
|
||||
Screen::delay(500000);
|
||||
|
||||
// 创建敌人群组
|
||||
|
|
@ -237,21 +237,15 @@ class Battle
|
|||
$out->writeln("{$this->cyan}║{$this->reset} {$this->red}💀{$this->reset} {$this->white}{$enemy->name}{$this->reset} {$this->red}[已击败]{$this->reset}");
|
||||
continue;
|
||||
}
|
||||
$enemyHpPercent = max(0, $enemy->hp) / $enemy->baseHp; // 使用baseHp作为最大值近似,或者应该在hydrate时保存maxHp
|
||||
// 实际上Monster没有maxHp属性,hp初始值就是最大值。但在战斗中hp会减少。
|
||||
// 我们需要知道最大HP。Monster::create时hp=baseHp+equipHp。
|
||||
// 简单起见,假设当前hp <= 初始hp。如果需要精确显示条,应该在Monster类加maxHp。
|
||||
// 这里暂时用 $enemy->baseHp + equipHp 估算,或者直接存一个 maxHp。
|
||||
// 为了简单,我们假设满血是初始状态。
|
||||
// 更好的做法是Monster类加一个maxHp属性。
|
||||
// 暂时用 $enemy->hp / $enemy->hp (如果满血) ... 不行。
|
||||
// 让我们修改Monster类加maxHp? 或者这里不显示条,只显示数值?
|
||||
// 或者我们假定 create 出来的 hp 就是 maxHp。
|
||||
// $enemy->maxHp = $enemy->hp; // 在create里做最好。
|
||||
// 这里先只显示数值吧,或者大概估算。
|
||||
$enemyStats = $enemy->getStats();
|
||||
$enemyMaxHp = $enemyStats['maxHp'];
|
||||
$enemyHpPercent = $enemyMaxHp > 0 ? $enemy->hp / $enemyMaxHp : 0;
|
||||
$enemyHpBar = $this->renderHpBar($enemyHpPercent, 15);
|
||||
$enemyHpText = $enemy->hp . "/" . $enemyMaxHp;
|
||||
|
||||
$hpText = max(0, $enemy->hp);
|
||||
$out->writeln("{$this->cyan}║{$this->reset} {$this->red}👹{$this->reset} {$this->bold}{$enemy->name}{$this->reset} Lv.{$enemy->level} HP: {$this->red}{$hpText}{$this->reset}");
|
||||
$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}");
|
||||
|
|
@ -329,19 +323,22 @@ class Battle
|
|||
*/
|
||||
private function playerChooseAction(): string
|
||||
{
|
||||
// 简化:默认使用普通攻击,除非有法术且魔法值充足
|
||||
// 为了简化战斗流程,我们先自动选择攻击
|
||||
// 在实战中可以添加交互菜单
|
||||
// 检查是否有装备的法术
|
||||
$hasSpells = false;
|
||||
foreach ($this->player->skillSlots as $slot => $spell) {
|
||||
if ($spell !== null) {
|
||||
$hasSpells = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// 检查是否有可以施放的法术
|
||||
if (empty($this->player->spells) || $this->player->mana < 15) {
|
||||
if (!$hasSpells || $this->player->mana < 15) {
|
||||
return 'attack';
|
||||
}
|
||||
|
||||
// 暂时选择法术的概率(可根据需要调整)
|
||||
// 如果玩家有法术且魔法值充足,50% 概率选择法术
|
||||
// 如果玩家有法术且魔法值充足,40% 概率选择法术
|
||||
$spellChance = rand(1, 100);
|
||||
if ($spellChance <= 40) { // 40% 概率使用法术
|
||||
if ($spellChance <= 40) {
|
||||
return 'spell';
|
||||
}
|
||||
|
||||
|
|
@ -355,53 +352,55 @@ class Battle
|
|||
{
|
||||
$stats = $this->player->getStats();
|
||||
|
||||
// 随机选择一个已学的法术
|
||||
// 筛选可用法术
|
||||
$availableSpells = [];
|
||||
foreach ($this->player->spells as $spellId => $spellData) {
|
||||
$spellInfo = $this->getSpellInfo($spellId);
|
||||
if (!$spellInfo) continue;
|
||||
foreach ($this->player->skillSlots as $slot => $spellItem) {
|
||||
if ($spellItem === null) 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);
|
||||
$baseCost = $spellItem['cost'] ?? 20;
|
||||
$enhanceLevel = $spellItem['enhanceLevel'] ?? 0;
|
||||
// 强化减少消耗: 每级 -2
|
||||
$costReduction = $enhanceLevel * 2;
|
||||
$actualCost = max(1, $baseCost - $costReduction);
|
||||
|
||||
if ($this->player->mana >= $actualCost) {
|
||||
$availableSpells[$spellId] = ['info' => $spellInfo, 'cost' => $actualCost, 'level' => $level];
|
||||
$availableSpells[] = [
|
||||
'item' => $spellItem,
|
||||
'cost' => $actualCost,
|
||||
'level' => $enhanceLevel
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
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'];
|
||||
$selected = $availableSpells[array_rand($availableSpells)];
|
||||
$spellItem = $selected['item'];
|
||||
$actualCost = $selected['cost'];
|
||||
$enhanceLevel = $selected['level'];
|
||||
|
||||
// 消耗魔法值
|
||||
$this->player->spendMana($actualCost);
|
||||
|
||||
// 获取法术升级信息
|
||||
$upgrades = $this->spellsData['upgrades'] ?? [];
|
||||
$upgradeInfo = $upgrades[$spellLevel] ?? [];
|
||||
$damageBonus = $upgradeInfo['damage_bonus'] ?? 0;
|
||||
// 计算强化加成: 每级 +5% 伤害
|
||||
$damageBonus = $enhanceLevel * 5;
|
||||
|
||||
$type = $spellInfo['type'] ?? '';
|
||||
$name = $spellInfo['name'] ?? '未知法术';
|
||||
$type = $spellItem['spellType'] ?? 'damage_single';
|
||||
$name = $spellItem['name'] ?? '未知法术';
|
||||
|
||||
// 构造兼容旧方法的 spellInfo
|
||||
$spellInfo = $spellItem;
|
||||
$spellInfo['type'] = $type; // 映射 spellType 到 type 以兼容
|
||||
|
||||
if ($type === 'damage_single') {
|
||||
return $this->castDamageSingleSpell($out, $selectedSpellId, $spellInfo, $stats, $damageBonus, $name);
|
||||
return $this->castDamageSingleSpell($out, 0, $spellInfo, $stats, $damageBonus, $name);
|
||||
} elseif ($type === 'damage_aoe') {
|
||||
return $this->castDamageAoeSpell($out, $selectedSpellId, $spellInfo, $stats, $damageBonus, $name);
|
||||
return $this->castDamageAoeSpell($out, 0, $spellInfo, $stats, $damageBonus, $name);
|
||||
} elseif ($type === 'support') {
|
||||
return $this->castSupportSpell($out, $spellInfo, $stats, $name);
|
||||
}
|
||||
|
|
@ -625,9 +624,9 @@ class Battle
|
|||
}
|
||||
|
||||
/**
|
||||
* 生成法术资源书掉落
|
||||
* 生成法术物品掉落 (新法术系统)
|
||||
*/
|
||||
private function generateSpellTomeDrop(Actor $enemy): ?array
|
||||
private function generateSpellDrop(Actor $enemy): ?array
|
||||
{
|
||||
// 尝试从独立的副本法术映射文件获取(每个副本有自己的法术池)
|
||||
$dungeonId = $this->game->dungeonId;
|
||||
|
|
@ -651,21 +650,9 @@ class Battle
|
|||
|
||||
// 从法术池中随机选择一个法术ID
|
||||
$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;
|
||||
// 使用 Item::createSpell 创建法术物品
|
||||
return \Game\Entities\Item::createSpell($spellId, $enemy->level);
|
||||
}
|
||||
|
||||
private function playerAttack($out): bool
|
||||
|
|
@ -881,17 +868,13 @@ class Battle
|
|||
}
|
||||
}
|
||||
|
||||
// 掉落法术资源书
|
||||
$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;
|
||||
}
|
||||
// 掉落法术物品 (新法术系统)
|
||||
$spellDropChance = 15; // 15% 概率掉落法术
|
||||
if (rand(1, 100) <= $spellDropChance && !empty($this->spellsData)) {
|
||||
$spellItem = $this->generateSpellDrop($enemy);
|
||||
if ($spellItem) {
|
||||
$this->player->addItem($spellItem);
|
||||
$allDrops[] = $spellItem;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,10 +6,11 @@ use Game\Core\ItemDisplay;
|
|||
use Game\Core\Screen;
|
||||
use Game\Core\Colors;
|
||||
use Game\Services\EquipmentEnhancer;
|
||||
use Game\Entities\Partner;
|
||||
|
||||
/**
|
||||
* 装备强化面板
|
||||
* 允许玩家快速强化库存或装备栏中的任何装备
|
||||
* 允许玩家和队友强化装备
|
||||
*/
|
||||
class EquipmentEnhancePanel
|
||||
{
|
||||
|
|
@ -46,6 +47,62 @@ class EquipmentEnhancePanel
|
|||
$out->writeln("{$this->cyan}║{$this->reset} {$this->white}灵石: {$this->yellow}{$player->spiritStones}{$this->reset}");
|
||||
$out->writeln("{$this->cyan}╠════════════════════════════════════╣{$this->reset}");
|
||||
|
||||
// 选择强化对象
|
||||
$out->writeln("{$this->cyan}║{$this->reset} {$this->bold}选择强化对象:{$this->reset}");
|
||||
$out->writeln("{$this->cyan}║{$this->reset} [1] 强化玩家装备");
|
||||
|
||||
if (!empty($player->partners)) {
|
||||
$idx = 2;
|
||||
foreach ($player->partners as $partner) {
|
||||
$out->writeln("{$this->cyan}║{$this->reset} [{$idx}] 强化 {$partner->name} 的装备");
|
||||
$idx++;
|
||||
}
|
||||
}
|
||||
|
||||
$out->writeln("{$this->cyan}║{$this->reset} [0] 返回");
|
||||
$out->writeln("{$this->cyan}╚════════════════════════════════════╝{$this->reset}");
|
||||
|
||||
$choice = Screen::input($out, "选择: ");
|
||||
|
||||
if ($choice === '0') {
|
||||
$this->game->state = Game::MENU;
|
||||
return;
|
||||
}
|
||||
|
||||
if ($choice === '1') {
|
||||
$this->showPlayerEquipment();
|
||||
continue;
|
||||
}
|
||||
|
||||
// 处理队友选择
|
||||
$choiceNum = (int)$choice;
|
||||
$partnerIdx = $choiceNum - 2;
|
||||
|
||||
$partnerList = array_values($player->partners);
|
||||
if (isset($partnerList[$partnerIdx])) {
|
||||
$this->showPartnerEquipment($partnerList[$partnerIdx]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 显示玩家装备强化界面
|
||||
*/
|
||||
private function showPlayerEquipment()
|
||||
{
|
||||
while (true) {
|
||||
Screen::clear($this->game->output);
|
||||
|
||||
$out = $this->game->output;
|
||||
$player = $this->game->player;
|
||||
|
||||
$out->writeln("");
|
||||
$out->writeln("{$this->cyan}╔════════════════════════════════════╗{$this->reset}");
|
||||
$out->writeln("{$this->cyan}║{$this->reset} {$this->bold}玩家装备强化{$this->reset} {$this->cyan}║{$this->reset}");
|
||||
$out->writeln("{$this->cyan}╠════════════════════════════════════╣{$this->reset}");
|
||||
$out->writeln("{$this->cyan}║{$this->reset} {$this->white}灵石: {$this->yellow}{$player->spiritStones}{$this->reset}");
|
||||
$out->writeln("{$this->cyan}╠════════════════════════════════════╣{$this->reset}");
|
||||
|
||||
// 显示已装备物品
|
||||
$out->writeln("{$this->cyan}║{$this->reset} {$this->bold}当前装备:{$this->reset}");
|
||||
|
||||
|
|
@ -71,7 +128,6 @@ class EquipmentEnhancePanel
|
|||
$out->writeln("{$this->cyan}║{$this->reset} {$this->bold}库存物品:{$this->reset}");
|
||||
|
||||
// 显示库存中可强化的物品
|
||||
$inventoryStart = $slotIndex;
|
||||
$inventoryItems = [];
|
||||
|
||||
foreach ($player->inventory as $invIndex => $item) {
|
||||
|
|
@ -97,7 +153,6 @@ class EquipmentEnhancePanel
|
|||
|
||||
// 处理返回
|
||||
if ($choice === '0') {
|
||||
$this->game->state = Game::MENU;
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -140,6 +195,85 @@ class EquipmentEnhancePanel
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 显示队友装备强化界面
|
||||
*/
|
||||
private function showPartnerEquipment(Partner $partner)
|
||||
{
|
||||
while (true) {
|
||||
Screen::clear($this->game->output);
|
||||
|
||||
$out = $this->game->output;
|
||||
$player = $this->game->player;
|
||||
|
||||
$out->writeln("");
|
||||
$out->writeln("{$this->cyan}╔════════════════════════════════════╗{$this->reset}");
|
||||
$out->writeln("{$this->cyan}║{$this->reset} {$this->bold}{$partner->name} 的装备强化{$this->reset} {$this->cyan}║{$this->reset}");
|
||||
$out->writeln("{$this->cyan}╠════════════════════════════════════╣{$this->reset}");
|
||||
$out->writeln("{$this->cyan}║{$this->reset} {$this->white}灵石: {$this->yellow}{$player->spiritStones}{$this->reset}");
|
||||
$out->writeln("{$this->cyan}╠════════════════════════════════════╣{$this->reset}");
|
||||
|
||||
// 显示队友已装备的物品
|
||||
$out->writeln("{$this->cyan}║{$this->reset} {$this->bold}当前装备:{$this->reset}");
|
||||
|
||||
$slots = ['weapon' => '武器', 'armor' => '护甲', 'boots' => '靴子', 'ring' => '戒指', 'necklace' => '项链'];
|
||||
$equipped = [];
|
||||
$slotIndex = 1;
|
||||
|
||||
foreach ($slots as $slot => $slotName) {
|
||||
$item = $partner->equip[$slot] ?? null;
|
||||
|
||||
if ($item) {
|
||||
$str = ItemDisplay::renderListItem($item);
|
||||
$out->writeln("{$this->cyan}║[$slotIndex]$str");
|
||||
$equipped[$slotIndex] = ['slot' => $slot, 'item' => $item];
|
||||
} else {
|
||||
$out->writeln("{$this->cyan}║{$this->reset} [{$slotIndex}] {$slotName}: {$this->red}未装备{$this->reset}");
|
||||
}
|
||||
|
||||
$slotIndex++;
|
||||
}
|
||||
|
||||
$out->writeln("{$this->cyan}╠════════════════════════════════════╣{$this->reset}");
|
||||
$out->writeln("{$this->cyan}║{$this->reset}");
|
||||
$out->writeln("{$this->cyan}║{$this->reset} 选择要强化的装备编号或 [0] 返回");
|
||||
$out->writeln("{$this->cyan}║{$this->reset} 输入 [info 编号] 查看强化信息");
|
||||
$out->writeln("{$this->cyan}╚════════════════════════════════════╝{$this->reset}");
|
||||
|
||||
$choice = Screen::input($out, "选择: ");
|
||||
|
||||
// 处理返回
|
||||
if ($choice === '0') {
|
||||
return;
|
||||
}
|
||||
|
||||
// 处理查看信息
|
||||
if (strpos($choice, 'info') === 0) {
|
||||
$parts = explode(' ', $choice);
|
||||
if (isset($parts[1])) {
|
||||
$itemIndex = (int)$parts[1];
|
||||
$this->showPartnerEnhanceInfo($equipped, $itemIndex);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
$itemIndex = (int)$choice;
|
||||
|
||||
// 从装备中强化
|
||||
if (isset($equipped[$itemIndex])) {
|
||||
$this->selectPartnerTargetLevelAndEnhance(
|
||||
$partner,
|
||||
$equipped[$itemIndex]['slot'],
|
||||
$equipped[$itemIndex]['item']
|
||||
);
|
||||
continue;
|
||||
}
|
||||
|
||||
$out->writeln("{$this->red}无效选择{$this->reset}");
|
||||
Screen::sleep(1);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 选择目标强化等级并进行强化
|
||||
*/
|
||||
|
|
@ -216,6 +350,73 @@ class EquipmentEnhancePanel
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 为队友选择目标强化等级并进行强化
|
||||
*/
|
||||
private function selectPartnerTargetLevelAndEnhance(
|
||||
Partner $partner,
|
||||
string $slot,
|
||||
array &$item
|
||||
) {
|
||||
$out = $this->game->output;
|
||||
$player = $this->game->player;
|
||||
$currentLevel = $item['enhanceLevel'] ?? 0;
|
||||
|
||||
Screen::clear($out);
|
||||
|
||||
$out->writeln("");
|
||||
$out->writeln("{$this->cyan}╔════════════════════════════════════╗{$this->reset}");
|
||||
$out->writeln("{$this->cyan}║{$this->reset} {$this->bold}强 化 等 级 选 择{$this->reset} {$this->cyan}║{$this->reset}");
|
||||
$out->writeln("{$this->cyan}╠════════════════════════════════════╣{$this->reset}");
|
||||
|
||||
$out->writeln("{$this->cyan}║{$this->reset} 装备: {$item['name']}");
|
||||
$out->writeln("{$this->cyan}║{$this->reset} 当前等级: {$this->yellow}+{$currentLevel}{$this->reset}");
|
||||
$out->writeln("{$this->cyan}║{$this->reset}");
|
||||
$out->writeln("{$this->cyan}║{$this->reset} 可选等级:");
|
||||
$out->writeln("{$this->cyan}║{$this->reset}");
|
||||
|
||||
// 显示可选择的目标等级
|
||||
for ($target = $currentLevel + 1; $target <= EquipmentEnhancer::MAX_ENHANCE_LEVEL; $target++) {
|
||||
$cost = EquipmentEnhancer::getTotalCost($currentLevel, $target);
|
||||
$canAfford = $player->spiritStones >= $cost ? "{$this->green}✓{$this->reset}" : "{$this->red}✗{$this->reset}";
|
||||
$out->writeln("{$this->cyan}║{$this->reset} [{$target}] +{$target} (需要: {$cost} 灵石) {$canAfford}");
|
||||
}
|
||||
|
||||
$out->writeln("{$this->cyan}║{$this->reset} [0] 手动强化 (一次一级)");
|
||||
$out->writeln("{$this->cyan}║{$this->reset}");
|
||||
$out->writeln("{$this->cyan}║{$this->reset} 当前灵石: {$this->yellow}{$player->spiritStones}{$this->reset}");
|
||||
$out->writeln("{$this->cyan}╚════════════════════════════════════╝{$this->reset}");
|
||||
|
||||
$choice = Screen::input($out, "选择目标等级: ");
|
||||
|
||||
if ($choice === '0') {
|
||||
// 手动强化一次
|
||||
$this->enhancePartnerEquipment($partner, $slot, $item);
|
||||
return;
|
||||
}
|
||||
|
||||
$targetLevel = (int)$choice;
|
||||
|
||||
// 验证选择有效性
|
||||
if ($targetLevel <= $currentLevel || $targetLevel > EquipmentEnhancer::MAX_ENHANCE_LEVEL) {
|
||||
$out->writeln("{$this->red}无效的目标等级{$this->reset}");
|
||||
Screen::sleep(1);
|
||||
return;
|
||||
}
|
||||
|
||||
// 检查灵石是否足够
|
||||
$totalCost = EquipmentEnhancer::getTotalCost($currentLevel, $targetLevel);
|
||||
if ($player->spiritStones < $totalCost) {
|
||||
$lack = $totalCost - $player->spiritStones;
|
||||
$out->writeln("{$this->red}灵石不足!还需要 {$lack} 灵石{$this->reset}");
|
||||
Screen::sleep(1);
|
||||
return;
|
||||
}
|
||||
|
||||
// 执行自动强化
|
||||
$this->enhancePartnerToLevel($partner, $slot, $item, $targetLevel);
|
||||
}
|
||||
|
||||
/**
|
||||
* 自动强化装备到目标等级
|
||||
*/
|
||||
|
|
@ -319,6 +520,104 @@ class EquipmentEnhancePanel
|
|||
Screen::pause($out);
|
||||
}
|
||||
|
||||
/**
|
||||
* 为队友自动强化装备到目标等级
|
||||
*/
|
||||
private function enhancePartnerToLevel(
|
||||
Partner $partner,
|
||||
string $slot,
|
||||
array &$item,
|
||||
int $targetLevel
|
||||
) {
|
||||
$out = $this->game->output;
|
||||
$player = $this->game->player;
|
||||
$currentLevel = $item['enhanceLevel'] ?? 0;
|
||||
$item['enhanceLevel'] = $item['enhanceLevel'] ?? 0;
|
||||
Screen::clear($out);
|
||||
|
||||
$out->writeln("");
|
||||
$out->writeln("{$this->cyan}╔════════════════════════════════════╗{$this->reset}");
|
||||
$out->writeln("{$this->cyan}║{$this->reset} {$this->bold}自 动 强 化 中{$this->reset} {$this->cyan}║{$this->reset}");
|
||||
$out->writeln("{$this->cyan}╠════════════════════════════════════╣{$this->reset}");
|
||||
|
||||
$out->writeln("{$this->cyan}║{$this->reset} 装备: {$item['name']}");
|
||||
$out->writeln("{$this->cyan}║{$this->reset} 目标: +{$targetLevel}");
|
||||
$out->writeln("{$this->cyan}║{$this->reset}");
|
||||
|
||||
$totalAttempts = 0;
|
||||
$successCount = 0;
|
||||
$failureCount = 0;
|
||||
$downgrades = 0;
|
||||
$startingSpirits = $player->spiritStones;
|
||||
|
||||
// 循环强化直到达到目标等级或灵石用完
|
||||
while ($item['enhanceLevel'] < $targetLevel && $player->spiritStones > 0) {
|
||||
// 检查下一级是否可以强化(灵石足够)
|
||||
$config = EquipmentEnhancer::getConfig($item['enhanceLevel']);
|
||||
if (!$config || $player->spiritStones < $config['cost']) {
|
||||
break;
|
||||
}
|
||||
|
||||
$totalAttempts++;
|
||||
$oldLevel = $item['enhanceLevel'];
|
||||
|
||||
// 执行强化
|
||||
$result = EquipmentEnhancer::enhance($item, $player);
|
||||
|
||||
if ($result['success']) {
|
||||
$successCount++;
|
||||
$out->writeln("{$this->cyan}║{$this->reset} 第 {$totalAttempts} 次: {$this->green}✓ 成功{$this->reset} +{$oldLevel} → +{$result['newLevel']}");
|
||||
} else {
|
||||
$failureCount++;
|
||||
if ($result['downgraded']) {
|
||||
$downgrades++;
|
||||
$out->writeln("{$this->cyan}║{$this->reset} 第 {$totalAttempts} 次: {$this->red}✗ 失败并降级{$this->reset} +{$oldLevel} → +{$result['newLevel']}");
|
||||
} else {
|
||||
$out->writeln("{$this->cyan}║{$this->reset} 第 {$totalAttempts} 次: {$this->yellow}✗ 失败{$this->reset} 等级保持 +{$oldLevel}");
|
||||
}
|
||||
}
|
||||
|
||||
// 更新队友装备
|
||||
$partner->equip[$slot]['enhanceLevel'] = $item['enhanceLevel'];
|
||||
}
|
||||
|
||||
// 显示最终结果
|
||||
$out->writeln("{$this->cyan}║{$this->reset}");
|
||||
$out->writeln("{$this->cyan}╠════════════════════════════════════╣{$this->reset}");
|
||||
$out->writeln("{$this->cyan}║{$this->reset} {$this->bold}强 化 完 成{$this->reset}");
|
||||
$out->writeln("{$this->cyan}╠════════════════════════════════════╣{$this->reset}");
|
||||
|
||||
$finalLevel = $item['enhanceLevel'];
|
||||
$spiritUsed = $startingSpirits - $player->spiritStones;
|
||||
|
||||
$out->writeln("{$this->cyan}║{$this->reset} 装备等级: {$this->yellow}+{$currentLevel}{$this->reset} → {$this->yellow}+{$finalLevel}{$this->reset}");
|
||||
$out->writeln("{$this->cyan}║{$this->reset} 强化尝试: {$this->yellow}{$totalAttempts}{$this->reset}");
|
||||
$out->writeln("{$this->cyan}║{$this->reset} 成功次数: {$this->green}{$successCount}{$this->reset}");
|
||||
$out->writeln("{$this->cyan}║{$this->reset} 失败次数: {$this->red}{$failureCount}{$this->reset}");
|
||||
if ($downgrades > 0) {
|
||||
$out->writeln("{$this->cyan}║{$this->reset} 降级次数: {$this->red}{$downgrades}{$this->reset}");
|
||||
}
|
||||
$out->writeln("{$this->cyan}║{$this->reset} 灵石消耗: {$this->yellow}{$spiritUsed}{$this->reset}");
|
||||
$out->writeln("{$this->cyan}║{$this->reset} 剩余灵石: {$this->yellow}{$player->spiritStones}{$this->reset}");
|
||||
|
||||
if ($finalLevel >= $targetLevel) {
|
||||
$out->writeln("{$this->cyan}║{$this->reset}");
|
||||
$out->writeln("{$this->cyan}║{$this->reset} {$this->green}✓ 已达到目标等级!{$this->reset}");
|
||||
} elseif ($player->spiritStones <= 0) {
|
||||
$out->writeln("{$this->cyan}║{$this->reset}");
|
||||
$out->writeln("{$this->cyan}║{$this->reset} {$this->yellow}灵石已用完{$this->reset}");
|
||||
} else {
|
||||
$lack = EquipmentEnhancer::getTotalCost($finalLevel, $targetLevel);
|
||||
$out->writeln("{$this->cyan}║{$this->reset}");
|
||||
$out->writeln("{$this->cyan}║{$this->reset} {$this->yellow}灵石不足,还需 {$lack} 灵石{$this->reset}");
|
||||
}
|
||||
|
||||
$out->writeln("{$this->cyan}╚════════════════════════════════════╝{$this->reset}");
|
||||
|
||||
$this->game->saveState();
|
||||
Screen::pause($out);
|
||||
}
|
||||
|
||||
/**
|
||||
* 强化已装备的物品(手动一次一级)
|
||||
*/
|
||||
|
|
@ -371,6 +670,51 @@ class EquipmentEnhancePanel
|
|||
Screen::pause($out);
|
||||
}
|
||||
|
||||
/**
|
||||
* 强化队友已装备的物品
|
||||
*/
|
||||
private function enhancePartnerEquipment(Partner $partner, string $slot, array &$item)
|
||||
{
|
||||
$out = $this->game->output;
|
||||
$player = $this->game->player;
|
||||
|
||||
// 使用 EquipmentEnhancer 执行强化
|
||||
$result = EquipmentEnhancer::enhance($item, $player);
|
||||
|
||||
// 更新队友装备
|
||||
$partner->equip[$slot]['enhanceLevel'] = $item['enhanceLevel'];
|
||||
|
||||
Screen::clear($out);
|
||||
$out->writeln("");
|
||||
$out->writeln("{$this->cyan}╔════════════════════════════════════╗{$this->reset}");
|
||||
$out->writeln("{$this->cyan}║{$this->reset} {$this->bold}强 化 结 果{$this->reset} {$this->cyan}║{$this->reset}");
|
||||
$out->writeln("{$this->cyan}╚════════════════════════════════════╝{$this->reset}");
|
||||
$out->writeln("");
|
||||
|
||||
if ($result['success']) {
|
||||
$out->writeln("{$this->green}★ 强化成功!{$this->reset}");
|
||||
$out->writeln("{$item['name']} 强化等级提升至 {$this->yellow}+{$result['newLevel']}{$this->reset}");
|
||||
} else {
|
||||
if ($result['cost'] === 0) {
|
||||
$out->writeln("{$this->red}✗ {$result['message']}{$this->reset}");
|
||||
} else {
|
||||
$out->writeln("{$this->red}✗ 强化失败!{$this->reset}");
|
||||
if ($result['downgraded']) {
|
||||
$out->writeln("{$this->red}{$item['name']} 强化等级下降至 +{$result['newLevel']}{$this->reset}");
|
||||
} else {
|
||||
$out->writeln("强化等级保持不变");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$out->writeln("");
|
||||
$out->writeln("花费灵石: {$result['cost']}");
|
||||
$out->writeln("当前灵石: {$player->spiritStones}");
|
||||
|
||||
$this->game->saveState();
|
||||
Screen::pause($out);
|
||||
}
|
||||
|
||||
/**
|
||||
* 强化库存中的物品
|
||||
*/
|
||||
|
|
@ -470,4 +814,57 @@ class EquipmentEnhancePanel
|
|||
|
||||
Screen::pause($out);
|
||||
}
|
||||
|
||||
/**
|
||||
* 显示队友装备的强化信息
|
||||
*/
|
||||
private function showPartnerEnhanceInfo(array $equipped, int $itemIndex)
|
||||
{
|
||||
$out = $this->game->output;
|
||||
$player = $this->game->player;
|
||||
|
||||
$item = null;
|
||||
if (isset($equipped[$itemIndex])) {
|
||||
$item = $equipped[$itemIndex]['item'];
|
||||
}
|
||||
|
||||
if (!$item) {
|
||||
return;
|
||||
}
|
||||
|
||||
Screen::clear($out);
|
||||
|
||||
$currentLevel = $item['enhanceLevel'] ?? 0;
|
||||
$config = EquipmentEnhancer::getConfig($currentLevel);
|
||||
|
||||
$out->writeln("");
|
||||
$out->writeln("{$this->cyan}╔════════════════════════════════════╗{$this->reset}");
|
||||
$out->writeln("{$this->cyan}║{$this->reset} {$this->bold}强化信息{$this->reset} {$this->cyan}║{$this->reset}");
|
||||
$out->writeln("{$this->cyan}╠════════════════════════════════════╣{$this->reset}");
|
||||
|
||||
$out->writeln("{$this->cyan}║{$this->reset} 装备: {$item['name']}");
|
||||
$out->writeln("{$this->cyan}║{$this->reset} 当前强化: {$this->yellow}+{$currentLevel}{$this->reset}");
|
||||
|
||||
if ($config) {
|
||||
$nextBonus = EquipmentEnhancer::getEnhanceBonusPercent($currentLevel + 1);
|
||||
$out->writeln("{$this->cyan}║{$this->reset}");
|
||||
$out->writeln("{$this->cyan}║{$this->reset} 下一级信息:");
|
||||
$out->writeln("{$this->cyan}║{$this->reset} 成功率: {$this->yellow}{$config['rate']}%{$this->reset}");
|
||||
$out->writeln("{$this->cyan}║{$this->reset} 花费: {$config['cost']} 灵石");
|
||||
if ($config['downgrade'] > 0) {
|
||||
$out->writeln("{$this->cyan}║{$this->reset} 降级率: {$this->red}{$config['downgrade']}%{$this->reset}");
|
||||
}
|
||||
$out->writeln("{$this->cyan}║{$this->reset} 属性加成: {$nextBonus}%");
|
||||
} else {
|
||||
$out->writeln("{$this->cyan}║{$this->reset} {$this->green}✓ 已达最大强化等级{$this->reset}");
|
||||
}
|
||||
|
||||
$out->writeln("{$this->cyan}╠════════════════════════════════════╣{$this->reset}");
|
||||
$out->writeln("{$this->cyan}║{$this->reset}");
|
||||
$out->writeln("{$this->cyan}║{$this->reset} 当前灵石: {$this->yellow}{$player->spiritStones}{$this->reset}");
|
||||
$out->writeln("{$this->cyan}║{$this->reset}");
|
||||
$out->writeln("{$this->cyan}╚════════════════════════════════════╝{$this->reset}");
|
||||
|
||||
Screen::pause($out);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -19,10 +19,8 @@ class Menu
|
|||
$out->writeln("[2] 属性面板");
|
||||
$out->writeln("[3] 背包");
|
||||
$out->writeln("[4] 拜访故人");
|
||||
$out->writeln("[5] 同伴管理");
|
||||
$out->writeln("[6] 天赋系统");
|
||||
$out->writeln("[7] 装备强化");
|
||||
$out->writeln("[8] 法术系统");
|
||||
$out->writeln("[5] 天赋系统");
|
||||
$out->writeln("[6] 装备强化");
|
||||
$out->writeln("[0] 退出");
|
||||
$out->writeln("=========================");
|
||||
|
||||
|
|
@ -41,17 +39,11 @@ class Menu
|
|||
$this->game->state = Game::NPC;
|
||||
|
||||
} elseif ($choice == 5) {
|
||||
$this->game->state = Game::PARTNER;
|
||||
|
||||
} elseif ($choice == 6) {
|
||||
$this->game->state = Game::TALENT;
|
||||
|
||||
} elseif ($choice == 7) {
|
||||
} elseif ($choice == 6) {
|
||||
$this->game->state = Game::EQUIPMENT_ENHANCE;
|
||||
|
||||
} elseif ($choice == 8) {
|
||||
$this->game->state = Game::SPELL;
|
||||
|
||||
} elseif ($choice == 0) {
|
||||
$this->game->state = Game::EXIT;
|
||||
|
||||
|
|
|
|||
|
|
@ -114,7 +114,7 @@ class NpcPanel
|
|||
$this->game->output->writeln("[0] 离开");
|
||||
|
||||
$choice = Input::ask($this->game->output, "请选择: ");
|
||||
|
||||
$choice = is_numeric($choice) ? $choice : 0;
|
||||
if ($choice == 0) return;
|
||||
|
||||
if (isset($actionKeys[$choice - 1])) {
|
||||
|
|
|
|||
|
|
@ -1,288 +0,0 @@
|
|||
<?php
|
||||
namespace Game\Modules;
|
||||
|
||||
use Game\Core\Colors;
|
||||
use Game\Core\Game;
|
||||
use Game\Core\Screen;
|
||||
use Game\Core\Input;
|
||||
use Game\Core\ItemDisplay;
|
||||
use Game\Entities\Partner;
|
||||
|
||||
class PartnerPanel
|
||||
{
|
||||
private string $reset;
|
||||
private string $cyan;
|
||||
private string $green;
|
||||
private string $yellow;
|
||||
private string $red;
|
||||
private string $magenta;
|
||||
private string $white;
|
||||
|
||||
public function __construct(public Game $game)
|
||||
{
|
||||
$this->reset = Colors::RESET;
|
||||
$this->cyan = Colors::CYAN;
|
||||
$this->green = Colors::GREEN;
|
||||
$this->yellow = Colors::YELLOW;
|
||||
$this->red = Colors::RED;
|
||||
$this->magenta = Colors::MAGENTA;
|
||||
$this->white = Colors::WHITE;
|
||||
}
|
||||
|
||||
public function show()
|
||||
{
|
||||
while (true) {
|
||||
Screen::clear($this->game->output);
|
||||
$this->game->output->writeln("{$this->cyan}========== 同伴管理 =========={$this->reset}");
|
||||
$this->game->output->writeln("");
|
||||
|
||||
$partners = $this->game->player->partners;
|
||||
|
||||
if (empty($partners)) {
|
||||
$this->game->output->writeln("{$this->white}暂无同伴,可通过拜访故人招募。{$this->reset}");
|
||||
} else {
|
||||
$idx = 1;
|
||||
foreach ($partners as $partner) {
|
||||
$stats = $partner->getStats();
|
||||
$this->game->output->writeln("[{$idx}] {$this->magenta}{$partner->name}{$this->reset} Lv.{$partner->level}");
|
||||
$this->game->output->writeln(" HP:{$stats['maxHp']} 物攻:{$stats['patk']} 魔攻:{$stats['matk']} 暴击:{$stats['crit']}%");
|
||||
$idx++;
|
||||
}
|
||||
}
|
||||
|
||||
$this->game->output->writeln("");
|
||||
$this->game->output->writeln("{$this->cyan}=============================={$this->reset}");
|
||||
$this->game->output->writeln("输入编号查看详情");
|
||||
$this->game->output->writeln("[0] 返回");
|
||||
|
||||
$choice = Input::ask($this->game->output, "请选择: ");
|
||||
|
||||
if ($choice == 0) {
|
||||
$this->game->state = Game::MENU;
|
||||
return;
|
||||
}
|
||||
|
||||
$partnerList = array_values($partners);
|
||||
if (isset($partnerList[$choice-1])) {
|
||||
$this->showPartnerDetail($partnerList[$choice-1]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private function showPartnerDetail(Partner $partner)
|
||||
{
|
||||
while (true) {
|
||||
Screen::clear($this->game->output);
|
||||
$stats = $partner->getStats();
|
||||
|
||||
$this->game->output->writeln("{$this->cyan}========== {$partner->name} =========={$this->reset}");
|
||||
$this->game->output->writeln("");
|
||||
$this->game->output->writeln("等级: {$this->yellow}Lv.{$partner->level}{$this->reset}");
|
||||
$this->game->output->writeln("经验: {$this->cyan}{$partner->exp}/{$partner->maxExp}{$this->reset}");
|
||||
$this->game->output->writeln("");
|
||||
$this->game->output->writeln("{$this->white}--- 属性 ---{$this->reset}");
|
||||
$this->game->output->writeln("生命值: {$this->red}{$stats['hp']}{$this->reset}/{$stats['maxHp']}");
|
||||
$this->game->output->writeln("物理攻击: {$this->red}{$stats['patk']}{$this->reset}");
|
||||
$this->game->output->writeln("魔法攻击: {$this->cyan}{$stats['matk']}{$this->reset}");
|
||||
$this->game->output->writeln("物理防御: {$this->red}{$stats['pdef']}{$this->reset}");
|
||||
$this->game->output->writeln("魔法防御: {$this->cyan}{$stats['mdef']}{$this->reset}");
|
||||
$this->game->output->writeln("暴击率: {$this->yellow}{$stats['crit']}%{$this->reset}");
|
||||
$this->game->output->writeln("暴击伤害: {$this->yellow}{$stats['critdmg']}%{$this->reset}");
|
||||
$this->game->output->writeln("");
|
||||
|
||||
// 显示天赋(自动分配)
|
||||
$this->game->output->writeln("{$this->white}--- 天赋 (自动) ---{$this->reset}");
|
||||
$totalTalentPoints = $partner->getTotalTalentPoints();
|
||||
$talentNames = ['hp' => '生命', 'patk' => '物攻', 'matk' => '魔攻', 'pdef' => '物防', 'mdef' => '魔防', 'crit' => '暴击', 'critdmg' => '暴伤'];
|
||||
$talentParts = [];
|
||||
foreach ($talentNames as $key => $name) {
|
||||
if (isset($partner->talents[$key]) && $partner->talents[$key] > 0) {
|
||||
$talentParts[] = "{$name}:{$partner->talents[$key]}";
|
||||
}
|
||||
}
|
||||
if (!empty($talentParts)) {
|
||||
$this->game->output->writeln(implode(" | ", $talentParts));
|
||||
} else {
|
||||
$this->game->output->writeln("{$this->cyan}(尚未分配){$this->reset}");
|
||||
}
|
||||
$this->game->output->writeln("");
|
||||
|
||||
// 显示装备
|
||||
$this->game->output->writeln("{$this->white}--- 装备 ---{$this->reset}");
|
||||
$slots = ['weapon' => '武器', 'armor' => '护甲', 'ring' => '戒指', 'boots' => '靴子', 'necklace' => '项链'];
|
||||
foreach ($slots as $slot => $name) {
|
||||
$item = $partner->equip[$slot] ?? null;
|
||||
$slotLines = ItemDisplay::renderSlot($name, $item, "");
|
||||
foreach ($slotLines as $line) {
|
||||
$this->game->output->writeln($line);
|
||||
}
|
||||
}
|
||||
|
||||
$this->game->output->writeln("");
|
||||
$this->game->output->writeln("{$this->cyan}=============================={$this->reset}");
|
||||
$this->game->output->writeln("[1] 装备物品");
|
||||
$this->game->output->writeln("[2] 卸下装备");
|
||||
$this->game->output->writeln("[3] {$this->red}解散同伴{$this->reset}");
|
||||
$this->game->output->writeln("[0] 返回");
|
||||
|
||||
$choice = Input::ask($this->game->output, "请选择: ");
|
||||
|
||||
switch ($choice) {
|
||||
case '1':
|
||||
$this->equipItem($partner);
|
||||
break;
|
||||
case '2':
|
||||
$this->unequipItem($partner);
|
||||
break;
|
||||
case '3':
|
||||
if ($this->dismissPartner($partner)) {
|
||||
return;
|
||||
}
|
||||
break;
|
||||
case '0':
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private function equipItem(Partner $partner)
|
||||
{
|
||||
Screen::clear($this->game->output);
|
||||
$this->game->output->writeln("{$this->cyan}========== 装备物品 =========={$this->reset}");
|
||||
|
||||
// 筛选可装备物品
|
||||
$equipableItems = [];
|
||||
foreach ($this->game->player->inventory as $idx => $item) {
|
||||
$type = $item['type'] ?? '';
|
||||
if (in_array($type, ['weapon', 'armor', 'ring', 'boots', 'necklace'])) {
|
||||
$equipableItems[$idx] = $item;
|
||||
}
|
||||
}
|
||||
|
||||
if (empty($equipableItems)) {
|
||||
$this->game->output->writeln("{$this->white}没有可装备的物品{$this->reset}");
|
||||
Screen::pause($this->game->output);
|
||||
return;
|
||||
}
|
||||
|
||||
$displayIdx = 1;
|
||||
$idxMap = [];
|
||||
foreach ($equipableItems as $realIdx => $item) {
|
||||
$displayStr = ItemDisplay::renderListItem($item, true, false);
|
||||
$this->game->output->writeln("[{$displayIdx}] {$displayStr}");
|
||||
$idxMap[$displayIdx] = $realIdx;
|
||||
$displayIdx++;
|
||||
}
|
||||
|
||||
$this->game->output->writeln("[0] 取消");
|
||||
|
||||
$choice = Input::ask($this->game->output, "选择装备: ");
|
||||
|
||||
if ($choice == 0) return;
|
||||
|
||||
if (!isset($idxMap[$choice])) {
|
||||
$this->game->output->writeln("无效选择");
|
||||
Screen::sleep(1);
|
||||
return;
|
||||
}
|
||||
|
||||
$realIdx = $idxMap[$choice];
|
||||
$item = $this->game->player->inventory[$realIdx];
|
||||
$slot = $item['type'];
|
||||
|
||||
// 如果该槽位已有装备,先卸下
|
||||
if (!empty($partner->equip[$slot])) {
|
||||
$oldItem = $partner->equip[$slot];
|
||||
$this->game->player->addItem($oldItem);
|
||||
}
|
||||
|
||||
// 装备新物品
|
||||
$partner->equip[$slot] = $item;
|
||||
|
||||
// 从背包移除
|
||||
unset($this->game->player->inventory[$realIdx]);
|
||||
$this->game->player->inventory = array_values($this->game->player->inventory);
|
||||
|
||||
$this->game->saveState();
|
||||
$this->game->output->writeln("{$this->green}已装备 {$item['name']}!{$this->reset}");
|
||||
Screen::pause($this->game->output);
|
||||
}
|
||||
|
||||
private function unequipItem(Partner $partner)
|
||||
{
|
||||
Screen::clear($this->game->output);
|
||||
$this->game->output->writeln("{$this->cyan}========== 卸下装备 =========={$this->reset}");
|
||||
|
||||
$slots = ['weapon' => '武器', 'armor' => '护甲', 'ring' => '戒指', 'boots' => '靴子', 'necklace' => '项链'];
|
||||
$equipped = [];
|
||||
$idx = 0;
|
||||
|
||||
foreach ($slots as $slot => $name) {
|
||||
if (!empty($partner->equip[$slot])) {
|
||||
$item = $partner->equip[$slot];
|
||||
$this->game->output->writeln("[{$idx}] {$name}: " . ItemDisplay::formatName($item));
|
||||
$equipped[$idx] = $slot;
|
||||
$idx++;
|
||||
}
|
||||
}
|
||||
|
||||
if (empty($equipped)) {
|
||||
$this->game->output->writeln("{$this->white}没有已装备的物品{$this->reset}");
|
||||
Screen::pause($this->game->output);
|
||||
return;
|
||||
}
|
||||
|
||||
$this->game->output->writeln("[0] 取消");
|
||||
|
||||
$choice = Input::ask($this->game->output, "选择卸下: ");
|
||||
|
||||
if ($choice == 0) return;
|
||||
|
||||
if (!isset($equipped[$choice])) {
|
||||
$this->game->output->writeln("无效选择");
|
||||
Screen::sleep(1);
|
||||
return;
|
||||
}
|
||||
|
||||
$slot = $equipped[$choice];
|
||||
$item = $partner->equip[$slot];
|
||||
|
||||
// 放回背包
|
||||
$this->game->player->addItem($item);
|
||||
$partner->equip[$slot] = null;
|
||||
|
||||
$this->game->saveState();
|
||||
$this->game->output->writeln("{$this->green}已卸下 {$item['name']}!{$this->reset}");
|
||||
Screen::pause($this->game->output);
|
||||
}
|
||||
|
||||
private function dismissPartner(Partner $partner): bool
|
||||
{
|
||||
$this->game->output->writeln("");
|
||||
$this->game->output->writeln("{$this->red}确定要解散 {$partner->name} 吗?{$this->reset}");
|
||||
$this->game->output->writeln("{$this->white}解散后装备将返还背包{$this->reset}");
|
||||
|
||||
$confirm = Input::ask($this->game->output, "确认解散?[y/n]: ");
|
||||
|
||||
if (strtolower($confirm) !== 'y') {
|
||||
$this->game->output->writeln("取消解散");
|
||||
Screen::sleep(1);
|
||||
return false;
|
||||
}
|
||||
|
||||
// 返还装备
|
||||
foreach ($partner->equip as $item) {
|
||||
if (!empty($item)) {
|
||||
$this->game->player->addItem($item);
|
||||
}
|
||||
}
|
||||
|
||||
// 解散同伴
|
||||
$this->game->player->dismissPartner($partner->id);
|
||||
$this->game->saveState();
|
||||
|
||||
$this->game->output->writeln("{$this->yellow}{$partner->name} 离开了队伍{$this->reset}");
|
||||
Screen::pause($this->game->output);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
|
@ -4,8 +4,11 @@ namespace Game\Modules;
|
|||
use Game\Core\Colors;
|
||||
use Game\Core\Game;
|
||||
use Game\Core\Screen;
|
||||
use Game\Core\Input;
|
||||
use Game\Core\ItemDisplay;
|
||||
use Game\Services\EquipmentEnhancer;
|
||||
use Game\Entities\Actor;
|
||||
use Game\Entities\Partner;
|
||||
|
||||
class StatsPanel
|
||||
{
|
||||
|
|
@ -50,6 +53,10 @@ class StatsPanel
|
|||
'legendary' => '传说',
|
||||
];
|
||||
|
||||
// 当前查看的角色
|
||||
private ?Actor $currentActor = null;
|
||||
private bool $isPlayer = true;
|
||||
|
||||
public function __construct(public Game $game) {
|
||||
$this->cyan = Colors::CYAN;
|
||||
$this->yellow = Colors::YELLOW;
|
||||
|
|
@ -70,101 +77,437 @@ class StatsPanel
|
|||
|
||||
public function show()
|
||||
{
|
||||
$out = $this->game->output;
|
||||
Screen::clear($out);
|
||||
// 默认显示玩家
|
||||
$this->currentActor = $this->game->player;
|
||||
$this->isPlayer = true;
|
||||
|
||||
$player = $this->game->player;
|
||||
return $this->showCharacterSelect();
|
||||
}
|
||||
|
||||
// Calculate total stats using the new method
|
||||
$totalStats = $player->getStats();
|
||||
$maxHp = $totalStats['maxHp'];
|
||||
$totalPatk = $totalStats['patk'];
|
||||
$totalMatk = $totalStats['matk'];
|
||||
$totalPdef = $totalStats['pdef'];
|
||||
$totalMdef = $totalStats['mdef'];
|
||||
$totalCrit = $totalStats['crit'];
|
||||
$totalCritdmg = $totalStats['critdmg'];
|
||||
/**
|
||||
* 显示角色选择界面
|
||||
*/
|
||||
private function showCharacterSelect()
|
||||
{
|
||||
while (true) {
|
||||
Screen::clear($this->game->output);
|
||||
$out = $this->game->output;
|
||||
|
||||
$currentHp = $player->hp;
|
||||
$name = "玩家";
|
||||
$level = $player->level;
|
||||
$exp = $player->exp;
|
||||
$maxExp = $player->maxExp;
|
||||
$out->writeln("{$this->cyan}========== 角色管理 =========={$this->reset}");
|
||||
$out->writeln("");
|
||||
|
||||
$out->writeln("╔════════════════════════════════════╗");
|
||||
$out->writeln("║ {$this->cyan}角色属性面板{$this->reset} ║");
|
||||
$out->writeln("╠════════════════════════════════════╣");
|
||||
// 显示玩家
|
||||
$playerStats = $this->game->player->getStats();
|
||||
$mark = $this->isPlayer && $this->currentActor === $this->game->player ? "{$this->green}►{$this->reset} " : " ";
|
||||
$out->writeln("{$mark}[1] {$this->yellow}玩家{$this->reset} Lv.{$this->game->player->level}");
|
||||
$out->writeln(" HP:{$playerStats['maxHp']} 物攻:{$playerStats['patk']} 魔攻:{$playerStats['matk']} 暴击:{$playerStats['crit']}%");
|
||||
|
||||
$out->writeln("║ 名称: {$this->yellow}{$name}{$this->reset}");
|
||||
$out->writeln("║ 等级: {$level} (XP: {$exp}/{$maxExp})");
|
||||
$out->writeln("║ 💰灵石: {$this->green}{$player->spiritStones}{$this->reset}");
|
||||
$out->writeln("║ HP: {$this->red}{$currentHp}{$this->reset}/{$maxHp}");
|
||||
$out->writeln("║ 物攻: {$this->yellow}{$totalPatk}{$this->reset}");
|
||||
$out->writeln("║ 魔攻: {$this->blue}{$totalMatk}{$this->reset}");
|
||||
$out->writeln("║ 物防: {$this->yellow}{$totalPdef}{$this->reset}");
|
||||
$out->writeln("║ 魔防: {$this->blue}{$totalMdef}{$this->reset}");
|
||||
$out->writeln("║ 暴击: {$totalCrit} %");
|
||||
$out->writeln("║ 爆伤: {$totalCritdmg} %");
|
||||
$out->writeln("╠════════════════════════════════════╣");
|
||||
$out->writeln("║ {$this->magenta}装备栏{$this->reset}");
|
||||
$out->writeln("╠════════════════════════════════════╣");
|
||||
// 显示队友
|
||||
$partners = $this->game->player->partners;
|
||||
if (!empty($partners)) {
|
||||
$out->writeln("");
|
||||
$out->writeln("{$this->white}--- 队友 ---{$this->reset}");
|
||||
$idx = 2;
|
||||
foreach ($partners as $partner) {
|
||||
$stats = $partner->getStats();
|
||||
$mark = !$this->isPlayer && $this->currentActor === $partner ? "{$this->green}►{$this->reset} " : " ";
|
||||
$out->writeln("{$mark}[{$idx}] {$this->magenta}{$partner->name}{$this->reset} Lv.{$partner->level}");
|
||||
$out->writeln(" HP:{$stats['maxHp']} 物攻:{$stats['patk']} 魔攻:{$stats['matk']} 暴击:{$stats['crit']}%");
|
||||
$idx++;
|
||||
}
|
||||
}
|
||||
|
||||
$slotIndex = 1;
|
||||
$slots = ['weapon', 'armor', 'boots', 'ring', 'necklace'];
|
||||
$slotNames = [
|
||||
'weapon' => '武器',
|
||||
'armor' => '护甲',
|
||||
'boots' => '鞋子',
|
||||
'ring' => '戒指',
|
||||
'necklace' => '项链',
|
||||
];
|
||||
$out->writeln("");
|
||||
$out->writeln("{$this->cyan}=============================={$this->reset}");
|
||||
$out->writeln("输入编号查看详情");
|
||||
$out->writeln("[1] 查看玩家 | [0] 返回主菜单");
|
||||
|
||||
foreach ($slots as $slot) {
|
||||
$item = $player->equip[$slot] ?? null;
|
||||
$slotName = $slotNames[$slot];
|
||||
$choice = Input::ask($out, "请选择: ");
|
||||
|
||||
if ($item) {
|
||||
$slotLines = ItemDisplay::renderSlot($slotName, $item, "║ [{$slotIndex}] ");
|
||||
foreach ($slotLines as $i => $line) {
|
||||
if ($i === 0) {
|
||||
$out->writeln($line);
|
||||
} else {
|
||||
$out->writeln("║ " . substr($line, 6)); // 缩进对齐
|
||||
// Handle 'b' for back to menu
|
||||
if (strtolower($choice) == 0) {
|
||||
$this->game->state = Game::MENU;
|
||||
return;
|
||||
}
|
||||
|
||||
$choice = is_numeric($choice) ? (int)$choice : 1;
|
||||
|
||||
if ($choice === 1) {
|
||||
// 选择0总是选择玩家
|
||||
$this->currentActor = $this->game->player;
|
||||
$this->isPlayer = true;
|
||||
return $this->showActorStats();
|
||||
} elseif ($choice > 0) {
|
||||
// 选择队友
|
||||
$partnerList = array_values($partners);
|
||||
if (isset($partnerList[$choice - 2])) {
|
||||
$this->currentActor = $partnerList[$choice - 2];
|
||||
$this->isPlayer = false;
|
||||
return $this->showActorStats();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 显示角色详细属性
|
||||
*/
|
||||
private function showActorStats()
|
||||
{
|
||||
while (true) {
|
||||
Screen::clear($this->game->output);
|
||||
$out = $this->game->output;
|
||||
|
||||
$actor = $this->currentActor;
|
||||
$stats = $actor->getStats();
|
||||
|
||||
$name = $this->isPlayer ? "玩家" : $actor->name;
|
||||
$nameColor = $this->isPlayer ? $this->yellow : $this->magenta;
|
||||
|
||||
$out->writeln("╔════════════════════════════════════╗");
|
||||
$out->writeln("║ {$this->cyan}角色属性面板{$this->reset} ║");
|
||||
$out->writeln("╠════════════════════════════════════╣");
|
||||
|
||||
$out->writeln("║ 名称: {$nameColor}{$name}{$this->reset}");
|
||||
$out->writeln("║ 等级: {$actor->level} (XP: {$actor->exp}/{$actor->maxExp})");
|
||||
|
||||
if ($this->isPlayer) {
|
||||
$out->writeln("║ 💰灵石: {$this->green}{$this->game->player->spiritStones}{$this->reset}");
|
||||
}
|
||||
|
||||
$out->writeln("║ HP: {$this->red}{$stats['hp']}{$this->reset}/{$stats['maxHp']}");
|
||||
$out->writeln("║ 物攻: {$this->yellow}{$stats['patk']}{$this->reset}");
|
||||
$out->writeln("║ 魔攻: {$this->blue}{$stats['matk']}{$this->reset}");
|
||||
$out->writeln("║ 物防: {$this->yellow}{$stats['pdef']}{$this->reset}");
|
||||
$out->writeln("║ 魔防: {$this->blue}{$stats['mdef']}{$this->reset}");
|
||||
$out->writeln("║ 暴击: {$stats['crit']} %");
|
||||
$out->writeln("║ 爆伤: {$stats['critdmg']} %");
|
||||
|
||||
// 显示天赋信息
|
||||
if (!$this->isPlayer) {
|
||||
$out->writeln("║");
|
||||
$out->writeln("║ {$this->white}天赋(自动分配){$this->reset}");
|
||||
$talentNames = ['hp' => '生命', 'patk' => '物攻', 'matk' => '魔攻', 'pdef' => '物防', 'mdef' => '魔防', 'crit' => '暴击', 'critdmg' => '暴伤'];
|
||||
$talentParts = [];
|
||||
foreach ($talentNames as $key => $tname) {
|
||||
if (isset($actor->talents[$key]) && $actor->talents[$key] > 0) {
|
||||
$talentParts[] = "{$tname}:{$actor->talents[$key]}";
|
||||
}
|
||||
}
|
||||
} else {
|
||||
$out->writeln("║ [{$slotIndex}] {$this->cyan}{$slotName}{$this->reset}: " .
|
||||
Colors::GRAY . '(空)' . Colors::RESET);
|
||||
if (!empty($talentParts)) {
|
||||
$talentStr = implode(" ", $talentParts);
|
||||
$out->writeln("║ {$talentStr}");
|
||||
}
|
||||
}
|
||||
|
||||
$out->writeln("╠════════════════════════════════════╣");
|
||||
$out->writeln("║ {$this->magenta}装备栏{$this->reset}");
|
||||
$out->writeln("╠════════════════════════════════════╣");
|
||||
|
||||
$slotIndex = 1;
|
||||
$slots = ['weapon', 'armor', 'boots', 'ring', 'necklace'];
|
||||
$slotNames = [
|
||||
'weapon' => '武器',
|
||||
'armor' => '护甲',
|
||||
'boots' => '鞋子',
|
||||
'ring' => '戒指',
|
||||
'necklace' => '项链',
|
||||
];
|
||||
|
||||
foreach ($slots as $slot) {
|
||||
$item = $actor->equip[$slot] ?? null;
|
||||
$slotName = $slotNames[$slot];
|
||||
|
||||
if ($item) {
|
||||
$slotLines = ItemDisplay::renderSlot($slotName, $item, "║ [{$slotIndex}] ");
|
||||
foreach ($slotLines as $i => $line) {
|
||||
if ($i === 0) {
|
||||
$out->writeln($line);
|
||||
} else {
|
||||
$out->writeln("║ " . substr($line, 6)); // 缩进对齐
|
||||
}
|
||||
}
|
||||
} else {
|
||||
$out->writeln("║ [{$slotIndex}] {$this->cyan}{$slotName}{$this->reset}: " .
|
||||
Colors::GRAY . '(空)' . Colors::RESET);
|
||||
}
|
||||
$slotIndex++;
|
||||
}
|
||||
|
||||
$out->writeln("╚════════════════════════════════════╝");
|
||||
$out->writeln("");
|
||||
|
||||
// 显示技能槽位
|
||||
$out->writeln("╔════════════════════════════════════╗");
|
||||
$out->writeln("║ {$this->magenta}技能栏{$this->reset}");
|
||||
$out->writeln("╠════════════════════════════════════╣");
|
||||
|
||||
$skillSlots = ['skill1', 'skill2', 'skill3', 'skill4'];
|
||||
$skillIndex = 1;
|
||||
foreach ($skillSlots as $slot) {
|
||||
$spell = $actor->skillSlots[$slot] ?? null;
|
||||
if ($spell) {
|
||||
$slotLines = ItemDisplay::renderSlot("技能{$skillIndex}", $spell, "║ [{$skillIndex}] ");
|
||||
foreach ($slotLines as $i => $line) {
|
||||
if ($i === 0) {
|
||||
$out->writeln($line);
|
||||
} else {
|
||||
$out->writeln("║ " . substr($line, 6));
|
||||
}
|
||||
}
|
||||
} else {
|
||||
$out->writeln("║ [{$skillIndex}] {$this->cyan}技能{$skillIndex}{$this->reset}: " . Colors::GRAY . '(空)' . Colors::RESET);
|
||||
}
|
||||
$skillIndex++;
|
||||
}
|
||||
$out->writeln("╚════════════════════════════════════╝");
|
||||
$out->writeln("");
|
||||
|
||||
// 显示操作选项 - 统一玩家和队友的操作
|
||||
$out->writeln("[1] 装备物品 | [2] 卸下装备 | [3] 强化装备");
|
||||
$out->writeln("[5] 装备技能 | [6] 卸下技能 | [7] 强化技能");
|
||||
|
||||
if ($this->isPlayer) {
|
||||
$out->writeln("[s] 切换角色 | [0] 返回");
|
||||
} else {
|
||||
$out->writeln("[4] {$this->red}解散队友{$this->reset} | [s] 切换角色 | [0] 返回");
|
||||
}
|
||||
|
||||
$choice = Screen::input($out, "选择操作:");
|
||||
|
||||
if ($choice == 0 || strtolower($choice) === 's') {
|
||||
return $this->showCharacterSelect();
|
||||
}
|
||||
|
||||
// 统一处理操作
|
||||
switch ($choice) {
|
||||
case '1':
|
||||
$this->equipItem($actor);
|
||||
break;
|
||||
case '2':
|
||||
$this->unequipItem($actor);
|
||||
break;
|
||||
case '3':
|
||||
$this->enhanceEquipment($actor);
|
||||
break;
|
||||
case '4':
|
||||
if (!$this->isPlayer) {
|
||||
$this->dismissPartner($actor);
|
||||
return $this->showCharacterSelect(); // 解散后返回选择界面
|
||||
}
|
||||
break;
|
||||
case '5':
|
||||
$this->equipSkill($actor);
|
||||
break;
|
||||
case '6':
|
||||
$this->unequipSkill($actor);
|
||||
break;
|
||||
case '7':
|
||||
$this->enhanceSkill($actor);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 为角色装备物品(统一处理玩家和队友)
|
||||
*/
|
||||
private function equipItem(Actor $actor)
|
||||
{
|
||||
Screen::clear($this->game->output);
|
||||
$this->game->output->writeln("{$this->cyan}========== 装备物品 =========={$this->reset}");
|
||||
|
||||
// 筛选可装备物品
|
||||
$equipableItems = [];
|
||||
foreach ($this->game->player->inventory as $idx => $item) {
|
||||
$type = $item['type'] ?? '';
|
||||
if (in_array($type, ['weapon', 'armor', 'ring', 'boots', 'necklace'])) {
|
||||
$equipableItems[$idx] = $item;
|
||||
}
|
||||
$slotIndex++;
|
||||
}
|
||||
|
||||
$out->writeln("╚════════════════════════════════════╝");
|
||||
$out->writeln("");
|
||||
$out->writeln("[1-5] 强化装备 | [0] 返回");
|
||||
|
||||
$choice = Screen::input($out, "选择操作:");
|
||||
|
||||
if ($choice == 0) {
|
||||
$this->game->state = Game::MENU;
|
||||
if (empty($equipableItems)) {
|
||||
$this->game->output->writeln("{$this->white}没有可装备的物品{$this->reset}");
|
||||
Screen::pause($this->game->output);
|
||||
return;
|
||||
}
|
||||
|
||||
$choiceInt = intval($choice);
|
||||
if ($choiceInt >= 1 && $choiceInt <= 5) {
|
||||
$selectedSlot = $slots[$choiceInt - 1];
|
||||
$this->showEnhancePanel($selectedSlot);
|
||||
$displayIdx = 1;
|
||||
$idxMap = [];
|
||||
foreach ($equipableItems as $realIdx => $item) {
|
||||
$displayStr = ItemDisplay::renderListItem($item, true, false);
|
||||
$this->game->output->writeln("[{$displayIdx}] {$displayStr}");
|
||||
$idxMap[$displayIdx] = $realIdx;
|
||||
$displayIdx++;
|
||||
}
|
||||
|
||||
return $this->show();
|
||||
$this->game->output->writeln("[0] 取消");
|
||||
|
||||
$choice = Input::ask($this->game->output, "选择装备: ");
|
||||
|
||||
if ($choice == 0) return;
|
||||
|
||||
if (!isset($idxMap[$choice])) {
|
||||
$this->game->output->writeln("无效选择");
|
||||
Screen::sleep(1);
|
||||
return;
|
||||
}
|
||||
|
||||
$realIdx = $idxMap[$choice];
|
||||
$item = $this->game->player->inventory[$realIdx];
|
||||
$slot = $item['type'];
|
||||
|
||||
// 如果该槽位已有装备,先卸下
|
||||
if (!empty($actor->equip[$slot])) {
|
||||
$oldItem = $actor->equip[$slot];
|
||||
$this->game->player->addItem($oldItem);
|
||||
}
|
||||
|
||||
// 装备新物品
|
||||
$actor->equip[$slot] = $item;
|
||||
|
||||
// 从背包移除
|
||||
unset($this->game->player->inventory[$realIdx]);
|
||||
$this->game->player->inventory = array_values($this->game->player->inventory);
|
||||
|
||||
$this->game->saveState();
|
||||
$this->game->output->writeln("{$this->green}已装备 {$item['name']}!{$this->reset}");
|
||||
Screen::pause($this->game->output);
|
||||
}
|
||||
|
||||
private function showEnhancePanel(string $slot)
|
||||
/**
|
||||
* 为角色卸下装备(统一处理玩家和队友)
|
||||
*/
|
||||
private function unequipItem(Actor $actor)
|
||||
{
|
||||
Screen::clear($this->game->output);
|
||||
$this->game->output->writeln("{$this->cyan}========== 卸下装备 =========={$this->reset}");
|
||||
|
||||
$slots = ['weapon' => '武器', 'armor' => '护甲', 'ring' => '戒指', 'boots' => '靴子', 'necklace' => '项链'];
|
||||
$equipped = [];
|
||||
$idx = 1;
|
||||
|
||||
foreach ($slots as $slot => $name) {
|
||||
if (!empty($actor->equip[$slot])) {
|
||||
$item = $actor->equip[$slot];
|
||||
$this->game->output->writeln("[{$idx}] {$name}: " . ItemDisplay::formatName($item));
|
||||
$equipped[$idx] = $slot;
|
||||
$idx++;
|
||||
}
|
||||
}
|
||||
|
||||
if (empty($equipped)) {
|
||||
$this->game->output->writeln("{$this->white}没有已装备的物品{$this->reset}");
|
||||
Screen::pause($this->game->output);
|
||||
return;
|
||||
}
|
||||
|
||||
$this->game->output->writeln("[0] 取消");
|
||||
|
||||
$choice = Input::ask($this->game->output, "选择卸下: ");
|
||||
|
||||
if ($choice == 0) return;
|
||||
|
||||
if (!isset($equipped[$choice])) {
|
||||
$this->game->output->writeln("无效选择");
|
||||
Screen::sleep(1);
|
||||
return;
|
||||
}
|
||||
|
||||
$slot = $equipped[$choice];
|
||||
$item = $actor->equip[$slot];
|
||||
|
||||
// 放回背包
|
||||
$this->game->player->addItem($item);
|
||||
$actor->equip[$slot] = null;
|
||||
|
||||
$this->game->saveState();
|
||||
$this->game->output->writeln("{$this->green}已卸下 {$item['name']}!{$this->reset}");
|
||||
Screen::pause($this->game->output);
|
||||
}
|
||||
|
||||
/**
|
||||
* 解散队友
|
||||
*/
|
||||
private function dismissPartner(Partner $partner): bool
|
||||
{
|
||||
$this->game->output->writeln("");
|
||||
$this->game->output->writeln("{$this->red}确定要解散 {$partner->name} 吗?{$this->reset}");
|
||||
$this->game->output->writeln("{$this->white}解散后装备将返还背包{$this->reset}");
|
||||
|
||||
$confirm = Input::ask($this->game->output, "确认解散?[y/n]: ");
|
||||
|
||||
if (strtolower($confirm) !== 'y') {
|
||||
$this->game->output->writeln("取消解散");
|
||||
Screen::sleep(1);
|
||||
return false;
|
||||
}
|
||||
|
||||
// 返还装备
|
||||
foreach ($partner->equip as $item) {
|
||||
if (!empty($item)) {
|
||||
$this->game->player->addItem($item);
|
||||
}
|
||||
}
|
||||
|
||||
// 解散同伴
|
||||
$this->game->player->dismissPartner($partner->id);
|
||||
$this->game->saveState();
|
||||
|
||||
$this->game->output->writeln("{$this->yellow}{$partner->name} 离开了队伍{$this->reset}");
|
||||
Screen::pause($this->game->output);
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 为角色强化装备(统一处理玩家和队友)
|
||||
*/
|
||||
private function enhanceEquipment(Actor $actor)
|
||||
{
|
||||
Screen::clear($this->game->output);
|
||||
$this->game->output->writeln("{$this->cyan}========== 装备强化 =========={$this->reset}");
|
||||
|
||||
$slots = ['weapon' => '武器', 'armor' => '护甲', 'ring' => '戒指', 'boots' => '靴子', 'necklace' => '项链'];
|
||||
$equipped = [];
|
||||
$idx = 1;
|
||||
|
||||
foreach ($slots as $slot => $name) {
|
||||
if (!empty($actor->equip[$slot])) {
|
||||
$item = $actor->equip[$slot];
|
||||
$enhanceLevel = $item['enhanceLevel'] ?? 0;
|
||||
$this->game->output->writeln("[{$idx}] {$name}: " . ItemDisplay::formatName($item) . " {$this->yellow}+{$enhanceLevel}{$this->reset}");
|
||||
$equipped[$idx] = $slot;
|
||||
$idx++;
|
||||
}
|
||||
}
|
||||
|
||||
if (empty($equipped)) {
|
||||
$this->game->output->writeln("{$this->white}没有已装备的物品{$this->reset}");
|
||||
Screen::pause($this->game->output);
|
||||
return;
|
||||
}
|
||||
|
||||
$this->game->output->writeln("[0] 取消");
|
||||
|
||||
$choice = Input::ask($this->game->output, "选择要强化的装备: ");
|
||||
|
||||
if ($choice == 0) return;
|
||||
|
||||
if (!isset($equipped[$choice])) {
|
||||
$this->game->output->writeln("无效选择");
|
||||
Screen::sleep(1);
|
||||
return;
|
||||
}
|
||||
|
||||
$slot = $equipped[$choice];
|
||||
$this->showEnhancePanel($slot, $actor);
|
||||
}
|
||||
|
||||
private function showEnhancePanel(string $slot, Actor $actor)
|
||||
{
|
||||
$out = $this->game->output;
|
||||
$player = $this->game->player;
|
||||
|
||||
$item = $player->equip[$slot] ?? null;
|
||||
$item = $actor->equip[$slot] ?? null;
|
||||
|
||||
if (!$item) {
|
||||
$out->writeln("该位置没有装备!");
|
||||
|
|
@ -248,7 +591,7 @@ class StatsPanel
|
|||
}
|
||||
$out->writeln("║ 费用: {$this->yellow}{$cost}{$this->reset} 灵石");
|
||||
$out->writeln("║");
|
||||
$out->writeln("║ 你的灵石: {$this->green}{$player->spiritStones}{$this->reset}");
|
||||
$out->writeln("║ 你的灵石: {$this->green}{$this->game->player->spiritStones}{$this->reset}");
|
||||
$out->writeln("╚════════════════════════════════════╝");
|
||||
$out->writeln("");
|
||||
$out->writeln("[1] 强化 | [0] 返回");
|
||||
|
|
@ -256,20 +599,19 @@ class StatsPanel
|
|||
$choice = Screen::input($out, "选择操作:");
|
||||
|
||||
if ($choice == 1) {
|
||||
$this->doEnhance($slot);
|
||||
$this->doEnhance($slot, $actor);
|
||||
}
|
||||
}
|
||||
|
||||
private function doEnhance(string $slot)
|
||||
private function doEnhance(string $slot, Actor $actor)
|
||||
{
|
||||
$out = $this->game->output;
|
||||
$player = $this->game->player;
|
||||
|
||||
$item = &$player->equip[$slot];
|
||||
$item = &$actor->equip[$slot];
|
||||
if (!$item) return;
|
||||
|
||||
// 使用 EquipmentEnhancer 模块执行强化
|
||||
$result = EquipmentEnhancer::enhance($item, $player);
|
||||
$result = EquipmentEnhancer::enhance($item, $this->game->player);
|
||||
|
||||
// 显示强化结果
|
||||
$out->writeln("");
|
||||
|
|
@ -306,4 +648,270 @@ class StatsPanel
|
|||
// 每级增加 5% 属性加成
|
||||
return $level * 5;
|
||||
}
|
||||
|
||||
/**
|
||||
* 装备技能
|
||||
*/
|
||||
private function equipSkill(Actor $actor)
|
||||
{
|
||||
$out = $this->game->output;
|
||||
Screen::clear($out);
|
||||
|
||||
// 1. 获取背包中的法术
|
||||
$spells = [];
|
||||
foreach ($this->game->player->inventory as $index => $item) {
|
||||
if (($item['type'] ?? '') === 'spell') {
|
||||
$spells[$index] = $item;
|
||||
}
|
||||
}
|
||||
|
||||
if (empty($spells)) {
|
||||
$out->writeln("背包中没有法术!");
|
||||
Screen::sleep(1);
|
||||
return;
|
||||
}
|
||||
|
||||
// 2. 显示法术列表
|
||||
$out->writeln("{$this->cyan}选择要装备的法术:{$this->reset}");
|
||||
$spellIndices = [];
|
||||
$i = 1;
|
||||
foreach ($spells as $index => $item) {
|
||||
$out->writeln("[{$i}] " . ItemDisplay::renderListItem($item));
|
||||
$spellIndices[$i] = $index;
|
||||
$i++;
|
||||
}
|
||||
$out->writeln("[0] 返回");
|
||||
|
||||
$choice = Screen::input($out, "请选择: ");
|
||||
if ($choice == 0 || !isset($spellIndices[$choice])) return;
|
||||
|
||||
$inventoryIndex = $spellIndices[$choice];
|
||||
$selectedSpell = $spells[$inventoryIndex];
|
||||
|
||||
// 3. 选择技能槽位
|
||||
Screen::clear($out);
|
||||
$out->writeln("{$this->cyan}选择技能槽位:{$this->reset}");
|
||||
$skillSlots = ['skill1', 'skill2', 'skill3', 'skill4'];
|
||||
$i = 1;
|
||||
foreach ($skillSlots as $slot) {
|
||||
$currentSpell = $actor->skillSlots[$slot] ?? null;
|
||||
if ($currentSpell) {
|
||||
$out->writeln("[{$i}] 技能{$i}: " . ItemDisplay::formatName($currentSpell));
|
||||
} else {
|
||||
$out->writeln("[{$i}] 技能{$i}: (空)");
|
||||
}
|
||||
$i++;
|
||||
}
|
||||
$out->writeln("[0] 返回");
|
||||
|
||||
$slotChoice = Screen::input($out, "请选择槽位: ");
|
||||
if ($slotChoice < 1 || $slotChoice > 4) return;
|
||||
|
||||
$targetSlot = $skillSlots[$slotChoice - 1];
|
||||
|
||||
// 4. 执行装备
|
||||
// 如果槽位已有技能,先卸下
|
||||
if (isset($actor->skillSlots[$targetSlot])) {
|
||||
$this->game->player->addItem($actor->skillSlots[$targetSlot]);
|
||||
}
|
||||
|
||||
// 装备新技能
|
||||
$actor->skillSlots[$targetSlot] = $selectedSpell;
|
||||
|
||||
// 从背包移除
|
||||
unset($this->game->player->inventory[$inventoryIndex]);
|
||||
$this->game->player->inventory = array_values($this->game->player->inventory);
|
||||
|
||||
$out->writeln("{$this->green}装备成功!{$this->reset}");
|
||||
$this->game->saveState();
|
||||
Screen::sleep(1);
|
||||
}
|
||||
|
||||
/**
|
||||
* 卸下技能
|
||||
*/
|
||||
private function unequipSkill(Actor $actor)
|
||||
{
|
||||
$out = $this->game->output;
|
||||
Screen::clear($out);
|
||||
|
||||
$out->writeln("{$this->cyan}选择要卸下的技能:{$this->reset}");
|
||||
$skillSlots = ['skill1', 'skill2', 'skill3', 'skill4'];
|
||||
$hasSkill = false;
|
||||
$i = 1;
|
||||
foreach ($skillSlots as $slot) {
|
||||
$currentSpell = $actor->skillSlots[$slot] ?? null;
|
||||
if ($currentSpell) {
|
||||
$out->writeln("[{$i}] 技能{$i}: " . ItemDisplay::formatName($currentSpell));
|
||||
$hasSkill = true;
|
||||
} else {
|
||||
$out->writeln("[{$i}] 技能{$i}: (空)");
|
||||
}
|
||||
$i++;
|
||||
}
|
||||
$out->writeln("[0] 返回");
|
||||
|
||||
if (!$hasSkill) {
|
||||
$out->writeln("没有装备任何技能!");
|
||||
Screen::sleep(1);
|
||||
return;
|
||||
}
|
||||
|
||||
$slotChoice = Screen::input($out, "请选择槽位: ");
|
||||
if ($slotChoice < 1 || $slotChoice > 4) return;
|
||||
|
||||
$targetSlot = $skillSlots[$slotChoice - 1];
|
||||
$spell = $actor->skillSlots[$targetSlot] ?? null;
|
||||
|
||||
if (!$spell) {
|
||||
$out->writeln("该槽位没有技能!");
|
||||
Screen::sleep(1);
|
||||
return;
|
||||
}
|
||||
|
||||
// 卸下到背包
|
||||
$this->game->player->addItem($spell);
|
||||
$actor->skillSlots[$targetSlot] = null;
|
||||
|
||||
$out->writeln("{$this->green}卸下成功!{$this->reset}");
|
||||
$this->game->saveState();
|
||||
Screen::sleep(1);
|
||||
}
|
||||
|
||||
/**
|
||||
* 强化技能
|
||||
*/
|
||||
private function enhanceSkill(Actor $actor)
|
||||
{
|
||||
$out = $this->game->output;
|
||||
Screen::clear($out);
|
||||
|
||||
$out->writeln("{$this->cyan}选择要强化的技能:{$this->reset}");
|
||||
$skillSlots = ['skill1', 'skill2', 'skill3', 'skill4'];
|
||||
$equipped = [];
|
||||
$hasSkill = false;
|
||||
$i = 1;
|
||||
foreach ($skillSlots as $slot) {
|
||||
$spell = $actor->skillSlots[$slot] ?? null;
|
||||
if ($spell) {
|
||||
$out->writeln("[{$i}] 技能{$i}: " . ItemDisplay::formatName($spell));
|
||||
$equipped[$i] = $slot;
|
||||
$hasSkill = true;
|
||||
} else {
|
||||
$out->writeln("[{$i}] 技能{$i}: (空)");
|
||||
}
|
||||
$i++;
|
||||
}
|
||||
$out->writeln("[0] 返回");
|
||||
|
||||
if (!$hasSkill) {
|
||||
$out->writeln("没有装备任何技能!");
|
||||
Screen::sleep(1);
|
||||
return;
|
||||
}
|
||||
|
||||
$choice = Screen::input($out, "请选择: ");
|
||||
if ($choice == 0 || !isset($equipped[$choice])) return;
|
||||
|
||||
$slot = $equipped[$choice];
|
||||
|
||||
// 复用 showEnhancePanel,但需要修改 doEnhance 支持技能
|
||||
// 为了简单,我们创建一个专门的 showSkillEnhancePanel
|
||||
$this->showSkillEnhancePanel($slot, $actor);
|
||||
}
|
||||
|
||||
private function showSkillEnhancePanel(string $slot, Actor $actor)
|
||||
{
|
||||
$out = $this->game->output;
|
||||
$item = $actor->skillSlots[$slot] ?? null;
|
||||
|
||||
if (!$item) return;
|
||||
|
||||
Screen::clear($out);
|
||||
|
||||
$enhanceLevel = $item['enhanceLevel'] ?? 0;
|
||||
$maxLevel = 15;
|
||||
|
||||
if ($enhanceLevel >= $maxLevel) {
|
||||
$out->writeln("该技能已达到最高强化等级!");
|
||||
Screen::sleep(1);
|
||||
return;
|
||||
}
|
||||
|
||||
// 使用与装备相同的强化配置
|
||||
$config = $this->enhanceConfig[$enhanceLevel] ?? $this->enhanceConfig[14];
|
||||
$successRate = $config['rate'];
|
||||
$cost = $config['cost'];
|
||||
$downgradeChance = $config['downgrade'];
|
||||
|
||||
$out->writeln("╔════════════════════════════════════╗");
|
||||
$out->writeln("║ {$this->cyan}技能强化{$this->reset} ║");
|
||||
$out->writeln("╠════════════════════════════════════╣");
|
||||
$out->writeln("║ 技能: " . ItemDisplay::formatName($item));
|
||||
$out->writeln("║");
|
||||
|
||||
// 显示属性变化
|
||||
$out->writeln("║ {$this->white}--- 属性预览 ---{$this->reset}");
|
||||
$statLines = ItemDisplay::formatStatsDetailed($item, "║ ");
|
||||
foreach ($statLines as $line) {
|
||||
$out->writeln($line);
|
||||
}
|
||||
|
||||
$out->writeln("║");
|
||||
$out->writeln("╠════════════════════════════════════╣");
|
||||
$out->writeln("║");
|
||||
$out->writeln("║ 当前等级: {$this->green}+{$enhanceLevel}{$this->reset}");
|
||||
$out->writeln("║");
|
||||
$out->writeln("║ ➜ 强化至 {$this->yellow}+".($enhanceLevel + 1)."{$this->reset}");
|
||||
$out->writeln("║");
|
||||
$out->writeln("║ 成功率: {$this->green}{$successRate}%{$this->reset}");
|
||||
if ($downgradeChance > 0) {
|
||||
$out->writeln("║ 失败掉级概率: {$this->red}{$downgradeChance}%{$this->reset}");
|
||||
}
|
||||
$out->writeln("║ 费用: {$this->yellow}{$cost}{$this->reset} 灵石");
|
||||
$out->writeln("║");
|
||||
$out->writeln("║ 你的灵石: {$this->green}{$this->game->player->spiritStones}{$this->reset}");
|
||||
$out->writeln("╚════════════════════════════════════╝");
|
||||
$out->writeln("");
|
||||
$out->writeln("[1] 强化 | [0] 返回");
|
||||
|
||||
$choice = Screen::input($out, "选择操作:");
|
||||
|
||||
if ($choice == 1) {
|
||||
$this->doEnhanceSkill($slot, $actor);
|
||||
}
|
||||
}
|
||||
|
||||
private function doEnhanceSkill(string $slot, Actor $actor)
|
||||
{
|
||||
$out = $this->game->output;
|
||||
$item = &$actor->skillSlots[$slot];
|
||||
|
||||
if (!$item) return;
|
||||
|
||||
// 使用 EquipmentEnhancer 模块执行强化 (它应该能处理任何带有 enhanceLevel 的物品)
|
||||
// 注意: EquipmentEnhancer 可能需要 Item 对象或数组,这里我们传递的是数组引用
|
||||
$result = EquipmentEnhancer::enhance($item, $this->game->player);
|
||||
|
||||
$out->writeln("");
|
||||
|
||||
if ($result['success']) {
|
||||
$out->writeln("{$this->green}★ 强化成功!{$this->reset}");
|
||||
$out->writeln("技能强化等级提升至 {$this->yellow}+{$result['newLevel']}{$this->reset}");
|
||||
} else {
|
||||
if ($result['cost'] === 0) {
|
||||
$out->writeln("{$this->red}✗ {$result['message']}{$this->reset}");
|
||||
} else {
|
||||
$out->writeln("{$this->red}✗ 强化失败!{$this->reset}");
|
||||
if ($result['downgraded']) {
|
||||
$out->writeln("{$this->red}技能强化等级下降至 +{$result['newLevel']}{$this->reset}");
|
||||
} else {
|
||||
$out->writeln("强化等级保持不变");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$this->game->saveState();
|
||||
Screen::sleep(1);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,43 +1,6 @@
|
|||
<?php
|
||||
require __DIR__ . '/../vendor/autoload.php';
|
||||
|
||||
$play = new \Game\Entities\Partner(json_decode(' {
|
||||
"id": "li_feiyu",
|
||||
"name": "厉飞雨",
|
||||
"level": 10,
|
||||
"exp": 0,
|
||||
"maxExp": 100,
|
||||
"equip": [],
|
||||
"talents": {
|
||||
"hp": 0,
|
||||
"patk": 0,
|
||||
"matk": 0,
|
||||
"pdef": 0,
|
||||
"mdef": 0,
|
||||
"crit": 0,
|
||||
"critdmg": 0
|
||||
},
|
||||
"talentWeights": {
|
||||
"hp": 1,
|
||||
"patk": 1,
|
||||
"matk": 1,
|
||||
"pdef": 1,
|
||||
"mdef": 1,
|
||||
"crit": 1,
|
||||
"critdmg": 1
|
||||
},
|
||||
"mana": 0,
|
||||
"maxMana": 100,
|
||||
"spells": [],
|
||||
"spellBooks": [],
|
||||
"hp": 100,
|
||||
"maxHp": 100,
|
||||
"patk": 15,
|
||||
"matk": 5,
|
||||
"pdef": 5,
|
||||
"mdef": 3,
|
||||
"crit": 10,
|
||||
"critdmg": 130
|
||||
}',true));
|
||||
$monster = \Game\Entities\Monster::createGroup(2);
|
||||
|
||||
var_dump($play->getStats());
|
||||
dd(is_numeric('0'));
|
||||
Loading…
Reference in New Issue
Block a user