270 lines
10 KiB
PHP
270 lines
10 KiB
PHP
<?php
|
||
namespace Game\Modules;
|
||
|
||
use Game\Core\Game;
|
||
use Game\Core\Screen;
|
||
use Game\Core\Input;
|
||
use Game\Entities\Item;
|
||
use Game\Entities\Partner;
|
||
|
||
class NpcPanel
|
||
{
|
||
private array $npcs;
|
||
|
||
public function __construct(public Game $game)
|
||
{
|
||
$this->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']} 加入了队伍!");
|
||
}
|
||
}
|