fanren/src/System/ShopService.php
2025-12-22 18:07:05 +08:00

233 lines
9.0 KiB
PHP
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<?php
namespace Game\System;
use Game\Event\Event;
use Game\Event\EventListenerInterface;
use Game\Event\EventDispatcher;
use Game\Model\Item;
use Game\Model\Player;
use Game\Model\NPC; // ⭐ 新增
use Game\Database\ItemRepository; // ⭐ 新增
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Helper\QuestionHelper;
use Symfony\Component\Console\Question\Question;
/**
* ShopService: 负责处理商店的购买和出售交易。
*/
class ShopService implements EventListenerInterface {
private EventDispatcher $dispatcher;
private StateManager $stateManager;
private InputInterface $input;
private OutputInterface $output;
private QuestionHelper $helper;
private ItemRepository $itemRepository; // ⭐ 新增
// 商店的固定库存通常从DPC配置加载
private array $shopInventory;
private ?NPC $currentShopNpc = null; // ⭐ 当前商店NPC
public function __construct(
EventDispatcher $dispatcher,
StateManager $stateManager,
InputInterface $input,
OutputInterface $output,
QuestionHelper $helper,
ItemRepository $itemRepository // ⭐ 新增
) {
$this->dispatcher = $dispatcher;
$this->stateManager = $stateManager;
$this->input = $input;
$this->output = $output;
$this->helper = $helper;
$this->itemRepository = $itemRepository; // ⭐ 赋值
$this->loadShopInventory();
}
private function loadShopInventory(): void {
// 模拟商店出售的物品配置 (ID 对应 LootService::loadItemData)
$this->shopInventory = [
// 商店物品 ID => 价格倍数(如果价格不是 Item::value
1 => ['stock' => 10, 'price' => 10], // 小药水
3 => ['stock' => 5, 'price' => 100], // 新物品:高级药水
];
}
public function handleEvent(Event $event): void {
switch ($event->getType()) {
case 'OpenShopEvent': // 响应 InteractionSystem 的请求
$payload = $event->getPayload();
$npc = $payload['npc'] ?? null;
if ($npc && $npc->hasShop) {
$this->currentShopNpc = $npc;
$this->shopInventory = $npc->shopInventory;
$this->startShopping();
} else {
$this->startShopping(); // 使用默认库存
}
break;
}
}
/**
* 启动商店界面和循环
*/
private function startShopping(): void {
$npcName = $this->currentShopNpc ? $this->currentShopNpc->getName() : '商人';
$this->dispatcher->dispatch(new Event('SystemMessage', ['message' => "\n🛍️ 欢迎光临 <fg=yellow>{$npcName}</> 的商店!"]));
$running = true;
while ($running) {
$player = $this->stateManager->getPlayer();
$this->displayShopMenu($player);
$question = new Question("> 请选择操作 (B:购买 | S:出售 | X:退出)");
$choice = strtoupper($this->helper->ask($this->input, $this->output, $question) ?? '');
switch ($choice) {
case 'B':
$this->handlePurchase();
break;
case 'S':
$this->handleSelling();
break;
case 'X':
$running = false;
$this->dispatcher->dispatch(new Event('SystemMessage', ['message' => '下次再来!']));
// ⭐ 返回地图模式
$this->stateManager->setMode(StateManager::MODE_MAP);
$this->dispatcher->dispatch(new Event('ShowMenuEvent'));
break;
default:
$this->dispatcher->dispatch(new Event('SystemMessage', ['message' => '无效的指令。']));
}
}
}
// --- 购买逻辑 ---
private function handlePurchase(): void {
$this->displaySaleItems();
$player = $this->stateManager->getPlayer();
$question = new Question("> 输入要购买的物品编号 (或 X 退出)");
$choice = strtoupper($this->helper->ask($this->input, $this->output, $question) ?? '');
if ($choice === 'X') return;
if (is_numeric($choice)) {
$itemId = (int)$choice;
if (isset($this->shopInventory[$itemId])) {
$price = $this->shopInventory[$itemId]['price'];
if ($player->spendGold($price)) {
// ⭐ 触发事件请求 LootService 给予物品(重用 LootService 的逻辑)
$this->dispatcher->dispatch(new Event('ShopPurchaseEvent', ['itemId' => $itemId]));
$this->dispatcher->dispatch(new Event('SystemMessage', [
'message' => "交易成功!花费 {$price} 💰,剩余 {$player->getGold()} 💰。"
]));
} else {
$this->dispatcher->dispatch(new Event('SystemMessage', ['message' => "金币不足!你需要 {$price} 💰。"]));
}
} else {
$this->dispatcher->dispatch(new Event('SystemMessage', ['message' => '商店没有这个编号的物品。']));
}
}
}
// --- 出售逻辑 ---
private function handleSelling(): void {
$player = $this->stateManager->getPlayer();
if (empty($player->getInventory())) {
$this->dispatcher->dispatch(new Event('SystemMessage', ['message' => '你的背包是空的,没有什么可卖的。']));
return;
}
$this->displaySellableItems($player);
$question = new Question("> 输入要出售的物品编号 (背包索引 | 或 X 退出)");
$choice = strtoupper($this->helper->ask($this->input, $this->output, $question) ?? '');
if ($choice === 'X') return;
if (is_numeric($choice)) {
$index = (int)$choice;
$inventory = $player->getInventory();
if (isset($inventory[$index])) {
$item = $inventory[$index];
// 简化:出售价格为 Item::value 的一半
$sellPrice = floor($item->value / 2);
// 移除物品并获得金币
$player->removeItemByIndex($index);
$player->gainGold($sellPrice);
$this->dispatcher->dispatch(new Event('SystemMessage', [
'message' => "💰 出售了 <fg=white>{$item->name}</>,获得 {$sellPrice} 💰。剩余 {$player->getGold()} 💰。"
]));
// 触发 UI 更新
$this->dispatcher->dispatch(new Event('InventoryUpdateEvent'));
} else {
$this->dispatcher->dispatch(new Event('SystemMessage', ['message' => '无效的背包编号。']));
}
}
}
// --- UI 辅助方法 ---
private function displayShopMenu(Player $player): void {
$this->output->writeln("\n--- 商店主页 ---");
$this->output->writeln("你的金币: <fg=yellow>{$player->getGold()}</> 💰");
$this->output->writeln("--------------------------");
}
private function displaySaleItems(): void {
$this->output->writeln("\n--- 🛍️ 商店出售 ---");
if (empty($this->shopInventory)) {
$this->output->writeln(" <fg=gray>商店没有货物。</>");
$this->output->writeln("--------------------------");
return;
}
foreach ($this->shopInventory as $itemId => $data) {
// ⭐ 从 ItemRepository 获取真实数据
$itemData = $this->itemRepository->find($itemId);
if ($itemData) {
$name = $itemData['name'] ?? "未知物品";
$price = $data['price'] ?? $itemData['value'] ?? 0;
$type = $itemData['type'] ?? '';
$typeLabel = match($type) {
'potion' => '<fg=green>药水</>',
'weapon' => '<fg=red>武器</>',
'armor' => '<fg=blue>护甲</>',
default => $type
};
$this->output->writeln("[<fg=green>{$itemId}</>] <fg=white;options=bold>{$name}</> {$typeLabel} | 价格: <fg=yellow>{$price}</> 💰");
}
}
$this->output->writeln("--------------------------");
}
private function displaySellableItems(Player $player): void {
$this->output->writeln("\n--- 📦 出售你的物品 ---");
$inventory = $player->getInventory();
if (empty($inventory)) return;
foreach ($inventory as $index => $item) {
$sellPrice = floor($item->value / 2);
$this->output->writeln("[<fg=green>{$index}</>] <fg=white>{$item->name}</> | 售价: <fg=yellow>{$sellPrice}</> 💰");
}
$this->output->writeln("--------------------------");
}
}