npcs = require __DIR__ . '/../../src/Data/npcs.php'; } public function show() { while (true) { Screen::clear($this->game->output); $this->game->output->writeln("========== 拜访故人 =========="); // Filter NPCs based on level $availableNpcs = []; foreach ($this->npcs as $npc) { if ($this->game->player->level >= $npc['min_level']) { $availableNpcs[] = $npc; } } if (empty($availableNpcs)) { $this->game->output->writeln("目前没有故人可以拜访。"); } else { foreach ($availableNpcs as $index => $npc) { $idx = $index+1; $this->game->output->writeln("[{$idx}] {$npc['name']} <{$npc['title']}>"); } } $this->game->output->writeln("=============================="); $this->game->output->writeln("[0] 返回"); $choice = Input::ask($this->game->output, "请选择故人: "); if ($choice == 0) { $this->game->state = Game::MENU; return; } if (isset($availableNpcs[$choice-1])) { $this->interact($availableNpcs[$choice-1]); } else { $this->game->output->writeln("无效选择"); Screen::sleep(1); } } } private function interact(array $npc) { while (true) { Screen::clear($this->game->output); $this->game->output->writeln("========== {$npc['name']} =========="); $this->game->output->writeln($npc['desc']); $this->game->output->writeln("------------------------------"); $actions = $npc['actions'] ?? []; $actionKeys = array_keys($actions); foreach ($actionKeys as $idx => $key) { $label = match($key) { 'talk' => '交谈', 'gift' => '领取赠礼', 'trade' => '交易', 'heal' => '疗伤', 'buff' => '强化', 'recruit' => '邀请入队', default => ucfirst($key) }; // 如果已招募,显示不同文本 if ($key === 'recruit' && isset($this->game->player->partners[$npc['id']])) { $label = '已入队 ✓'; } $this->game->output->writeln("[{$idx}] {$label}"); } $this->game->output->writeln("[0] 离开"); $choice = Input::ask($this->game->output, "请选择: "); if ($choice == 0) return; if (isset($actionKeys[$choice])) { $actionType = $actionKeys[$choice]; $actionData = $actions[$actionType]; $this->handleAction($actionType, $actionData, $npc); Screen::pause($this->game->output); } } } private function handleAction(string $type, array $data, array $npc) { switch ($type) { case 'talk': $this->game->output->writeln("{$npc['name']}: \"{$data['text']}\""); break; case 'recruit': $this->handleRecruit($data, $npc); break; case 'heal': $this->game->output->writeln("{$npc['name']}: \"{$data['text']}\""); $healAmount = $this->game->player->maxHp - $this->game->player->hp; if ($healAmount > 0) { $this->game->player->hp = $this->game->player->maxHp; $this->game->output->writeln("你的生命值已恢复满!"); } else { $this->game->output->writeln("你状态很好,无需治疗。"); } break; case 'gift': // Check if already received (need storage for this state, maybe in player save data?) // For simplicity, let's just give it for now, or check inventory? // Real implementation should save received gifts in player data. // Let's assume we can give it repeatedly for now or add a simple check if I can modify Player class. // I'll modify Player class to store 'npc_flags'. $flag = "gift_{$npc['id']}"; if (isset($this->game->player->npcFlags[$flag])) { $this->game->output->writeln("{$npc['name']}: \"东西已经给你了,别太贪心。\""); return; } $this->game->output->writeln("{$npc['name']}: \"{$data['text']}\""); if (isset($data['item_spec'])) { $item = Item::createFromSpec($data['item_spec'], $this->game->player->level); $this->game->player->addItem($item); $this->game->output->writeln("获得了 [{$item['name']}]!"); $this->game->player->npcFlags[$flag] = true; } break; case 'trade': $this->game->output->writeln("{$npc['name']}: \"{$data['text']}\""); $this->trade($data['items']); break; } } private function trade(array $items) { // Simple trade implementation $index = 1; foreach ($items as $idx => $itemInfo) { $index++; $this->game->output->writeln("[{$index}] {$itemInfo['name']} - {$itemInfo['price']} 灵石"); } $this->game->output->writeln("[0] 取消"); $choice = Input::ask($this->game->output, "购买: "); if (isset($items[$choice-1])) { $itemInfo = $items[$choice]; if ($this->game->player->spiritStones >= $itemInfo['price']) { $this->game->player->spiritStones -= $itemInfo['price']; // Create item. Since trade items in config are simple, we might need randomItem or createFromSpec // For now assuming simple items, use randomItem logic or createFromSpec if detailed // The config has 'type' and 'name'. $spec = ['type' => $itemInfo['type'], 'name' => $itemInfo['name']]; $item = Item::createFromSpec($spec, $this->game->player->level); $this->game->player->addItem($item); $this->game->output->writeln("购买了 [{$item['name']}]!"); } else { $this->game->output->writeln("灵石不足!"); } } } /** * 处理招募同伴 */ private function handleRecruit(array $data, array $npc) { $player = $this->game->player; // 检查是否已招募 if (isset($player->partners[$npc['id']])) { $this->game->output->writeln("{$npc['name']}: \"我已经在队伍中了!\""); return; } // 检查是否有base_stats(可招募的NPC必须有) if (!isset($npc['base_stats'])) { $this->game->output->writeln("{$npc['name']}: \"我还有其他事要忙,不能与你同行。\""); return; } // 检查队伍是否已满 if (count($player->partners) >= $player->maxPartners) { $this->game->output->writeln("队伍已满!最多只能携带 {$player->maxPartners} 名同伴。"); return; } // 检查招募费用 $cost = $data['cost'] ?? 0; if ($cost > 0 && $player->spiritStones < $cost) { $this->game->output->writeln("灵石不足!需要 {$cost} 灵石。"); return; } // 显示招募对话 $this->game->output->writeln("{$npc['name']}: \"{$data['text']}\""); $this->game->output->writeln(""); // 显示同伴属性预览 $stats = $npc['base_stats']; $this->game->output->writeln("========== 同伴属性 =========="); $patk = $stats['patk'] ?? $stats['atk'] ?? 10; $matk = $stats['matk'] ?? 5; $pdef = $stats['pdef'] ?? $stats['def'] ?? 5; $mdef = $stats['mdef'] ?? 3; $this->game->output->writeln("生命: {$stats['hp']} 物攻: {$patk} 魔攻: {$matk}"); $this->game->output->writeln("物防: {$pdef} 魔防: {$mdef}"); $this->game->output->writeln("暴击: {$stats['crit']}% 暴伤: {$stats['critdmg']}%"); $this->game->output->writeln("成长: {$stats['growth']}x"); $this->game->output->writeln("=============================="); if ($cost > 0) { $this->game->output->writeln("招募费用: {$cost} 灵石"); } $this->game->output->writeln(""); $confirm = Input::ask($this->game->output, "确认招募?[y/n]: "); if (strtolower($confirm) !== 'y') { $this->game->output->writeln("取消招募。"); return; } // 扣除费用 if ($cost > 0) { $player->spendSpiritStones($cost); } // 创建同伴并添加到队伍 $partner = new Partner([ 'id' => $npc['id'], 'name' => $npc['name'], 'level' => 1, 'exp' => 0, 'baseStats' => $npc['base_stats'], 'equip' => [], 'talentWeights' => $npc['talent_weights'] ?? [ 'hp' => 1, 'patk' => 1, 'matk' => 1, 'pdef' => 1, 'mdef' => 1, 'crit' => 1, 'critdmg' => 1 ], ]); $player->recruitPartner($partner); $this->game->saveState(); $this->game->output->writeln(""); $this->game->output->writeln("★ {$npc['name']} 加入了队伍!"); } }