Colors::WHITE, 'uncommon' => Colors::GREEN, 'rare' => Colors::BLUE, 'epic' => Colors::MAGENTA, 'legendary' => Colors::YELLOW, ]; private string $reset = Colors::RESET; private int $perPage = 8; private array $categories = [ 'all' => '全部', 'weapon' => '武器', 'armor' => '护甲', 'boots' => '靴子', 'ring' => '戒指', 'necklace' => '项链', 'equipment' => '装备', 'consume' => '消耗品', 'quest_item' => '任务物品', ]; public function __construct(public Game $game) {} public function show(int $page = 1, string $category = 'equipment') { $out = $this->game->output; Screen::clear($out); $player = $this->game->player; // Display compact stats panel $this->renderCompactStats(); $allItems = array_values($player->inventory); // Filter items by category if ($category == 'equipment'){ $items = array_values(array_filter($allItems, fn($item) => $item['type'] !== 'consume' && $item['type'] !== 'quest_item')); }else{ $items = $category === 'all' ? $allItems : array_values(array_filter($allItems, fn($item) => $item['type'] === $category)); } $total = count($items); $totalPages = max(1, ceil($total / $this->perPage)); $page = max(1, min($page, $totalPages)); $start = ($page - 1) * $this->perPage; $pageItems = array_slice($items, $start, $this->perPage); $categoryName = $this->categories[$category] ?? '全部'; $out->writeln(""); $out->writeln("╔════════════════════════════╗"); $out->writeln("║ 背 包 [{$categoryName}] ║"); $out->writeln("║ 第 {$page} / {$totalPages} 页 ║"); $out->writeln("╠════════════════════════════╣"); $out->writeln("║ 🟢 小绿瓶: {$player->potionPool} ║"); $out->writeln("╚════════════════════════════╝"); if (empty($items)) { $out->writeln("该分类下没有物品..."); $out->writeln(""); $out->writeln("[h] 使用小绿瓶回血"); $out->writeln("[c] 切换分类 | [0] 返回"); $choice = Screen::input($out, "选择操作:"); if ($choice === 'h'){ $this->autoHeal(); return $this->show($page, $category); } if ($choice === "c" || $choice === "C") { return $this->showCategoryMenu($page, $category); } $this->game->state = Game::MENU; return; } foreach ($pageItems as $i => $item) { $index = $start + $i + 1; // 装备使用 ItemDisplay 显示 $displayStr = Item::show($item); $out->writeln("[{$index}] {$displayStr}"); } $out->writeln(""); $out->writeln("[n] 下一页 | [p] 上一页 | [c] 切换分类 | [s] 批量出售"); $out->writeln("[h] 一键回血 | [0] 返回"); $choice = Screen::input($out, "选择编号或操作:"); if ($choice === "n") return $this->show($page + 1, $category); if ($choice === "p") return $this->show($page - 1, $category); if ($choice === "c" || $choice === "C") return $this->showCategoryMenu($page, $category); if ($choice === "s" || $choice === "S") { $this->batchSell(); return $this->show($page, $category); } if ($choice === "h" || $choice === "H") { $this->autoHeal(); return $this->show($page, $category); } if ($choice == 0) { $this->game->state = Game::MENU; return; } $choice = intval($choice); if (!isset($items[$choice - 1])) { $out->writeln("无效编号"); Screen::sleep(1); return $this->show($page, $category); } // Find the actual inventory index $selectedItem = $items[$choice - 1]; $inventoryIndex = array_search($selectedItem, $allItems, true); $this->showItemDetail($selectedItem, $inventoryIndex, $page, $category); } private function showItemDetail(array $item, int $inventoryIndex, int $page, string $category = 'all') { $out = $this->game->output; Screen::clear($out); $quantity = $item['quantity'] ?? 1; $qtyText = $quantity > 1 ? " (拥有: {$quantity})" : ""; $out->writeln("╔════════════════════════════════════════╗"); // 使用统一的详细显示 $detailLines = Item::show($item); $out->writeln($detailLines); if ($quantity > 1) { $out->writeln("║"); $out->writeln("║ 拥有数量: {$quantity}"); } // 任务物品不显示售价 if ($item['type'] !== 'quest_item') { // 计算售价 $sellPrice = Equipment::calculateSellPrice($item); $out->writeln("║"); $out->writeln("║ 💰 售价: {$sellPrice} 灵石"); } $out->writeln("╚════════════════════════════════════════╝"); $out->writeln(""); $isEquip = in_array($item['type'], ['weapon', 'armor', 'boots', 'ring', 'necklace']); $isQuestItem = $item['type'] === 'quest_item'; if ($isQuestItem) { // 任务物品无法操作 $out->writeln("这是一个关键任务物品,无法进行任何操作。"); $out->writeln(""); $out->writeln("[0] 返回"); } else { if ($isEquip) { $out->writeln("[1] 装备"); } elseif ($item['type'] === 'consume') { $out->writeln("[1] 使用"); } $out->writeln("[2] 出售"); $out->writeln("[3] 丢弃"); $out->writeln("[0] 返回"); } $choice = Screen::input($out, "选择操作:"); if (!$isQuestItem) { if ($choice == 1) $this->useItem($item, $inventoryIndex); if ($choice == 2) $this->sellItem($item, $inventoryIndex); if ($choice == 3) $this->dropItem($item, $inventoryIndex); } return $this->show($page, $category); } private function useItem(array $item, int $index) { $out = $this->game->output; $player = $this->game->player; if ($item['type'] === 'consume') { $stats = $player->getStats(); $maxHp = $stats['maxHp']; // 检查玩家血量 $playerNeedsHeal = $player->hp < $maxHp; // 检查队友血量 $partnersNeedHeal = []; foreach ($player->partners as $partner) { $partnerStats = $partner->getStats(); $partnerMaxHp = $partnerStats['maxHp']; if ($partner->hp < $partnerMaxHp) { $partnersNeedHeal[] = $partner; } } // 如果都不需要恢复 if (!$playerNeedsHeal && empty($partnersNeedHeal)) { $out->writeln("你和队友的生命值都已满,无需使用!"); Screen::sleep(1); return; } // 选择恢复目标 $target = null; if ($playerNeedsHeal && !empty($partnersNeedHeal)) { // 需要询问玩家选择 $out->writeln(""); $out->writeln("选择恢复目标:"); $out->writeln("[1] 恢复自己"); foreach ($partnersNeedHeal as $i => $partner) { $out->writeln("[" . ($i + 2) . "] 恢复 {$partner->name}"); } $choice = Screen::input($out, "选择:"); $choiceInt = (int)$choice; if ($choiceInt === 1) { $target = 'player'; } elseif ($choiceInt >= 2 && $choiceInt - 2 < count($partnersNeedHeal)) { $target = $partnersNeedHeal[$choiceInt - 2]; } else { $out->writeln("无效选择"); Screen::sleep(1); return; } } elseif ($playerNeedsHeal) { $target = 'player'; } elseif (!empty($partnersNeedHeal)) { $target = $partnersNeedHeal[0]; } // 使用消耗品进行恢复 if ($target === 'player') { // 从小绿瓶池获取实际恢复量 $healAmount = $item['heal']; $actualPotionHeal = $player->consumePotionPool($healAmount); if ($actualPotionHeal == 0){ $out->writeln("小绿瓶已用尽"); Screen::sleep(1); return; } $actualHeal = $player->heal($actualPotionHeal); $out->writeln("你使用了 {$item['name']},从小绿瓶中恢复了 {$actualHeal} HP!(当前: {$player->hp}/{$maxHp})"); $out->writeln("小绿瓶剩余: {$player->potionPool}"); } else { // 恢复队友 $partnerStats = $target->getStats(); $partnerMaxHp = $partnerStats['maxHp']; // 从小绿瓶池获取实际恢复量 $healAmount = $item['heal']; $actualPotionHeal = $player->consumePotionPool($healAmount); if ($actualPotionHeal == 0){ $out->writeln("小绿瓶已用尽"); Screen::sleep(1); return; } $actualHeal = $target->heal($actualPotionHeal); $out->writeln("你使用了 {$item['name']} 来恢复 {$target->name},从小绿瓶中恢复了 {$actualHeal} HP!(当前: {$target->hp}/{$partnerMaxHp})"); $out->writeln("小绿瓶剩余: {$player->potionPool}"); } // Decrease quantity or remove if (($player->inventory[$index]['quantity'] ?? 1) > 1) { $player->inventory[$index]['quantity']--; $out->writeln("剩余数量: {$player->inventory[$index]['quantity']}"); } else { unset($player->inventory[$index]); $player->inventory = array_values($player->inventory); } Screen::sleep(1); return; } $slot = $item['type']; $player->equip[$slot] = $item; // Remove from inventory unset($player->inventory[$index]); $player->inventory = array_values($player->inventory); $out->writeln("你装备了:{$item['name']}"); Screen::sleep(1); } private function dropItem(array $item, int $index) { $out = $this->game->output; // Decrease quantity or remove if (($this->game->player->inventory[$index]['quantity'] ?? 1) > 1) { $this->game->player->inventory[$index]['quantity']--; $out->writeln("你丢弃了 1 个 {$item['name']},剩余: {$this->game->player->inventory[$index]['quantity']}"); } else { unset($this->game->player->inventory[$index]); $this->game->player->inventory = array_values($this->game->player->inventory); $out->writeln("你丢弃了 {$item['name']}"); } Screen::sleep(1); } private function sellItem(array $item, int $index) { $out = $this->game->output; $player = $this->game->player; // 计算售价 $sellPrice = Item::calcPrice($item); // 消耗品可能有多个,卖一个 $quantity = $item['quantity'] ?? 1; if ($quantity > 1) { // 卖一个 $player->inventory[$index]['quantity']--; $player->addSpiritStones($sellPrice); $out->writeln("你出售了 1 个 {$item['name']},获得 {$sellPrice} 灵石"); $out->writeln("剩余数量: {$player->inventory[$index]['quantity']}"); } else { // 卖掉整个物品 unset($player->inventory[$index]); $player->inventory = array_values($player->inventory); $player->addSpiritStones($sellPrice); $out->writeln("你出售了 {$item['name']},获得 {$sellPrice} 灵石"); } $out->writeln("当前灵石: {$player->spiritStones}"); Screen::sleep(1); } /** * 一键使用消耗品回复生命值至满(包括队友) */ private function autoHeal() { $out = $this->game->output; $player = $this->game->player; // 检查玩家和队友谁需要治疗 $playerStats = $player->getStats(); $playerMaxHp = $playerStats['maxHp']; $playerNeedsHeal = $player->hp < $playerMaxHp; // 收集需要治疗的队友 $partnersNeedHeal = []; foreach ($player->partners as $partner) { $partnerStats = $partner->getStats(); $partnerMaxHp = $partnerStats['maxHp']; if ($partner->hp < $partnerMaxHp) { $partnersNeedHeal[] = [ 'partner' => $partner, 'maxHp' => $partnerMaxHp, 'needHeal' => $partnerMaxHp - $partner->hp, ]; } } // 如果都不需要治疗 if (!$playerNeedsHeal && empty($partnersNeedHeal)) { $out->writeln("你和队友的生命值都已满!"); Screen::sleep(1); return; } $totalHealed = 0; $healLog = []; // 回复玩家 while ($playerNeedsHeal && $player->hp < $playerMaxHp) { // 计算需要恢复的量 $needHeal = $playerMaxHp - $player->hp; // 使用药品,从小绿瓶池里扣除 $actualPotionHeal = $player->consumePotionPool($needHeal); if ($actualPotionHeal == 0){ $out->writeln("小绿瓶已用尽"); Screen::sleep(1); return; } $actualHeal = $player->heal($actualPotionHeal); $totalHealed += $actualHeal; $healLog[] = "玩家: +{$actualHeal} HP"; } // 回复队友(按需要程度优先恢复) if (!empty($partnersNeedHeal)) { // 按需要治疗量从大到小排序(优先治疗伤势最重的) usort($partnersNeedHeal, fn($a, $b) => $b['needHeal'] <=> $a['needHeal']); foreach ($partnersNeedHeal as $healData) { $partner = $healData['partner']; $partnerMaxHp = $healData['maxHp']; while ($partner->hp < $partnerMaxHp) { // 计算需要恢复的量 $needHeal = $partnerMaxHp - $partner->hp; // 使用药品回复队友,从小绿瓶池里扣除 $actualPotionHeal = $player->consumePotionPool($needHeal); if ($actualPotionHeal == 0){ $out->writeln("小绿瓶已用尽"); Screen::sleep(1); return; } $actualHeal = $partner->heal($actualPotionHeal); $totalHealed += $actualHeal; $healLog[] = "{$partner->name}: +{$actualHeal} HP"; } } } $this->game->saveState(); if ($totalHealed > 0) { $out->writeln(""); $out->writeln("╔════════════════════════════════════╗"); $out->writeln("║ 一键回血结果 ║"); $out->writeln("╚════════════════════════════════════╝"); $out->writeln("共恢复 {$totalHealed} HP!"); $out->writeln(""); $out->writeln("治疗详情:"); foreach ($healLog as $log) { $out->writeln(" " . $log); } $out->writeln(""); $out->writeln("当前状态:"); $out->writeln(" 玩家: {$player->hp}/{$playerMaxHp}"); foreach ($player->partners as $partner) { $partnerStats = $partner->getStats(); $out->writeln(" {$partner->name}: {$partner->hp}/{$partnerStats['maxHp']}"); } } else { $out->writeln("没有使用任何药品。"); } Screen::pause($out); } private function batchSell() { $out = $this->game->output; $player = $this->game->player; Screen::clear($out); $out->writeln("╔════════════════════════════════════╗"); $out->writeln("║ 批量出售装备 ║"); $out->writeln("╚════════════════════════════════════╝"); $out->writeln(""); $out->writeln("选择要出售的品质:"); $out->writeln("[1] 仅 Common (普通)"); $out->writeln("[2] Common + Rare (普通+稀有)"); $out->writeln("[3] Common + Rare + Epic (普通+稀有+史诗)"); $out->writeln("[0] 取消"); $out->writeln(""); $choice = Screen::input($out, "请选择:"); if ($choice == 0) { return; } // 确定要出售的品质范围 $qualitiesToSell = []; switch ($choice) { case 1: $qualitiesToSell = ['common']; break; case 2: $qualitiesToSell = ['common', 'rare']; break; case 3: $qualitiesToSell = ['common', 'rare', 'epic']; break; default: $out->writeln("无效选择"); Screen::sleep(1); return; } // 统计符合条件的装备(排除消耗品) $itemsToSell = []; $totalValue = 0; foreach ($player->inventory as $index => $item) { $quality = $item['quality'] ?? $item['rarity'] ?? 'common'; $isEquipment = in_array($item['type'], ['weapon', 'armor', 'ring', 'necklace', 'boots','spell']); if ($isEquipment && in_array($quality, $qualitiesToSell)) { $price = Item::calcPrice($item); $itemsToSell[] = [ 'index' => $index, 'item' => $item, 'price' => $price ]; $totalValue += $price; } } if (empty($itemsToSell)) { $out->writeln("\n没有符合条件的装备可以出售。"); Screen::sleep(2); return; } // 显示将要出售的物品 Screen::clear($out); $out->writeln("╔════════════════════════════════════╗"); $out->writeln("║ 确认批量出售 ║"); $out->writeln("╚════════════════════════════════════╝"); $out->writeln(""); $out->writeln("将要出售以下装备:"); $out->writeln(""); $maxDisplay = 10; $displayCount = min(count($itemsToSell), $maxDisplay); for ($i = 0; $i < $displayCount; $i++) { $data = $itemsToSell[$i]; $item = $data['item']; $price = $data['price']; $quality = $item['quality'] ?? 'common'; $color = $this->rarityColors[$quality] ?? ''; $reset = $this->reset; $out->writeln(" {$color}[{$quality}]{$reset} {$item['name']} - {$price} 灵石"); } if (count($itemsToSell) > $maxDisplay) { $remaining = count($itemsToSell) - $maxDisplay; $out->writeln(" ... 还有 {$remaining} 件装备"); } $out->writeln(""); $out->writeln("总计: {$totalValue} 灵石 (共 " . count($itemsToSell) . " 件装备)"); $out->writeln("当前灵石: {$player->spiritStones}"); $out->writeln(""); $confirm = Screen::input($out, "确认出售? [y/n]:"); if (strtolower($confirm) !== 'y') { $out->writeln("已取消"); Screen::sleep(1); return; } // 执行批量出售(从后往前删除,避免索引问题) $soldCount = 0; usort($itemsToSell, function($a, $b) { return $b['index'] - $a['index']; }); foreach ($itemsToSell as $data) { $index = $data['index']; $price = $data['price']; unset($player->inventory[$index]); $player->addSpiritStones($price); $soldCount++; } // 重建索引 $player->inventory = array_values($player->inventory); $out->writeln(""); $out->writeln("✅ 成功出售 {$soldCount} 件装备!"); $out->writeln("💰 获得灵石: {$totalValue}"); $out->writeln("💰 当前灵石: {$player->spiritStones}"); Screen::sleep(2); } private function showCategoryMenu(int $page, string $currentCategory) { $out = $this->game->output; Screen::clear($out); $out->writeln("╔════════════════════════════════════╗"); $out->writeln("║ 选择物品分类 ║"); $out->writeln("╚════════════════════════════════════╝"); $out->writeln(""); $index = 1; $categoryKeys = []; foreach ($this->categories as $key => $name) { $marker = ($key === $currentCategory) ? " ★" : ""; $out->writeln("[{$index}] {$name}{$marker}"); $categoryKeys[$index] = $key; $index++; } $out->writeln(""); $out->writeln("[0] 返回"); $out->writeln(""); $choice = Screen::input($out, "请选择分类:"); if ($choice == 0) { return $this->show($page, $currentCategory); } $choice = intval($choice); if (!isset($categoryKeys[$choice])) { $out->writeln("无效选择"); Screen::sleep(1); return $this->showCategoryMenu($page, $currentCategory); } $selectedCategory = $categoryKeys[$choice]; return $this->show(1, $selectedCategory); } private function renderCompactStats(): void { $out = $this->game->output; $player = $this->game->player; // Calculate total stats $stats = $player->getStats(); // Colors $cyan = Colors::CYAN; $yellow = Colors::YELLOW; $green = Colors::GREEN; $red = Colors::RED; $reset = Colors::RESET; $out->writeln("╔════════════════════════════════════════════════════════╗"); $out->writeln("║ {$cyan}角色属性{$reset} Lv.{$player->level} | {$green}💰 {$player->spiritStones}{$reset} 灵石 | 🟢 {$player->potionPool} ║"); $out->writeln("╠════════════════════════════════════════════════════════╣"); // First row: HP (current/max), patk, matk $hpDisplay = "{$player->hp}/{$stats['maxHp']}"; $out->writeln(sprintf( "║ {$red}HP{$reset}: %-10s {$yellow}物攻{$reset}: %-5s {$cyan}魔攻{$reset}: %-5s ║", $hpDisplay, $stats['patk'], $stats['matk'] )); // Second row: pdef, mdef $out->writeln(sprintf( "║ {$yellow}物防{$reset}: %-5s {$cyan}魔防{$reset}: %-5s ║", $stats['pdef'], $stats['mdef'] )); // Third row: Crit, CritDmg $out->writeln(sprintf( "║ {$cyan}暴击{$reset}: %-4s%% {$cyan}暴伤{$reset}: %-4s%% ║", $stats['crit'], $stats['critdmg'] )); $out->writeln("╠════════════════════════════════════════════════════════╣"); $out->writeln("║ {$yellow}装备{$reset} ║"); $out->writeln("╠════════════════════════════════════════════════════════╣"); // Equipment slots $slots = [ 'weapon' => '武器', 'armor' => '护甲', 'boots' => '鞋子', 'ring' => '戒指', 'necklace' => '项链', ]; foreach ($slots as $slot => $slotName) { $item = $player->equip[$slot] ?? null; if ($item){ $slotLines = Item::show($item); $out->writeln($slotLines); } } $out->writeln("╚════════════════════════════════════════════════════════╝"); } }