179 lines
6.3 KiB
PHP
179 lines
6.3 KiB
PHP
<?php
|
||
namespace Game\System;
|
||
|
||
use Game\Database\EnemyRepository;
|
||
use Game\Database\ItemRepository;
|
||
use Game\Event\Event;
|
||
use Game\Event\EventListenerInterface;
|
||
use Game\Event\EventDispatcher;
|
||
use Game\Model\Enemy;
|
||
use Game\Model\Item;
|
||
|
||
/**
|
||
* LootService: 负责处理物品掉落、背包管理和物品获取。
|
||
*/
|
||
class LootService implements EventListenerInterface {
|
||
|
||
private EventDispatcher $dispatcher;
|
||
private StateManager $stateManager;
|
||
// ... 现有属性 ...
|
||
private ItemRepository $itemRepository;
|
||
private EnemyRepository $enemyRepository;
|
||
// ⭐ 修正构造函数:注入 Repositories
|
||
public function __construct(EventDispatcher $dispatcher, StateManager $stateManager, ItemRepository $itemRepository, EnemyRepository $enemyRepository) {
|
||
$this->dispatcher = $dispatcher;
|
||
$this->stateManager = $stateManager;
|
||
$this->itemRepository = $itemRepository;
|
||
$this->enemyRepository = $enemyRepository;
|
||
}
|
||
|
||
public function createEnemy(string $id): ?Enemy {
|
||
// ⭐ 使用 Repository 获取数据
|
||
$data = $this->enemyRepository->find($id);
|
||
if (!$data) {
|
||
$this->dispatcher->dispatch(new Event('SystemMessage', ['message' => "❌ 错误:敌人ID '{$id}' 不存在。"]));
|
||
return null;
|
||
}
|
||
|
||
// ⭐ 修正实例化 Enemy:传入 loot_table
|
||
return new Enemy(
|
||
$data['id'],
|
||
$data['name'],
|
||
$data['health'],
|
||
$data['attack'],
|
||
$data['defense'],
|
||
$data['xp'],
|
||
$data['loot'] ?? [] // 传递 loot_table
|
||
);
|
||
}
|
||
|
||
public function handleEvent(Event $event): void {
|
||
switch ($event->getType()) {
|
||
case 'LootDropEvent':
|
||
$enemyId = $event->getPayload()['enemyId'];
|
||
// LootDropEvent 应该由 BattleService 在敌人死亡时触发
|
||
$this->handleLootDrop($enemyId);
|
||
break;
|
||
case 'LootFoundEvent': // 响应 MapSystem 探索时发现的宝箱
|
||
$lootId = $event->getPayload()['lootId'];
|
||
$this->handleLootFound($lootId);
|
||
break;
|
||
case 'ShopPurchaseEvent': // ⭐ 响应商店购买
|
||
$itemId = $event->getPayload()['itemId'];
|
||
$this->giveItemToPlayer($itemId);
|
||
break;
|
||
// ⭐ 响应遇敌事件
|
||
case 'EncounterEnemyEvent':
|
||
$enemyId = $event->getPayload()['enemyId'];
|
||
$newEnemy = $this->createEnemy($enemyId);
|
||
|
||
if ($newEnemy) {
|
||
// 通知 BattleService 启动战斗
|
||
$this->dispatcher->dispatch(new Event('StartBattleEvent', ['enemy' => $newEnemy]));
|
||
}
|
||
break;
|
||
}
|
||
}
|
||
|
||
/**
|
||
* ⭐ 修正方法:处理敌人死亡时的掉落逻辑(从配置中加载数据)
|
||
*/
|
||
private function handleLootDrop(string $enemyId): void {
|
||
|
||
// 1. 获取敌人配置数据
|
||
$enemyData = $this->enemyRepository->find($enemyId);
|
||
|
||
if (!$enemyData) {
|
||
$this->dispatcher->dispatch(new Event('SystemMessage', ['message' => "❌ 错误:无法找到敌人 {$enemyId} 的掉落配置。"]));
|
||
return;
|
||
}
|
||
|
||
// --- 2. 掉落金币 (从配置中获取范围) ---
|
||
$minGold = $enemyData['min_gold'] ?? 5; // 使用配置,默认 5
|
||
$maxGold = $enemyData['max_gold'] ?? 15; // 使用配置,默认 15
|
||
|
||
$goldAmount = rand($minGold, $maxGold);
|
||
$player = $this->stateManager->getPlayer();
|
||
$player->gainGold($goldAmount);
|
||
|
||
$this->dispatcher->dispatch(new Event('SystemMessage', [
|
||
'message' => "💰 获得了 <fg=yellow>{$goldAmount}</> 金币。"
|
||
]));
|
||
|
||
// --- 3. 掉落物品 (从配置的 loot_table 中获取) ---
|
||
$lootTable = $enemyData['loot_table'] ?? [];
|
||
|
||
if (!empty($lootTable)) {
|
||
$droppedItemId = $this->getRandomItemIdFromLootTable($lootTable);
|
||
|
||
if ($droppedItemId !== null) { // 成功掉落物品
|
||
$this->giveItemToPlayer($droppedItemId);
|
||
} else {
|
||
$this->dispatcher->dispatch(new Event('SystemMessage', [
|
||
'message' => "战利品很少,只找到了一些零钱。"
|
||
]));
|
||
}
|
||
} else {
|
||
$this->dispatcher->dispatch(new Event('SystemMessage', [
|
||
'message' => "战利品很少,只找到了一些零钱。"
|
||
]));
|
||
}
|
||
}
|
||
|
||
/**
|
||
* ⭐ 新增方法:根据权重随机选择物品 ID
|
||
*/
|
||
private function getRandomItemIdFromLootTable(array $lootTable): ?int {
|
||
$totalChance = array_sum(array_column($lootTable, 'chance'));
|
||
|
||
// 随机数范围是 1 到 100
|
||
$randValue = rand(1, 100);
|
||
|
||
// 如果随机值大于总几率,则表示未掉落任何物品
|
||
// 例如:总几率为 80,随机数是 85,则不掉落
|
||
if ($randValue > $totalChance) {
|
||
return null;
|
||
}
|
||
|
||
$currentChance = 0;
|
||
foreach ($lootTable as $item) {
|
||
$currentChance += $item['chance'];
|
||
if ($randValue <= $currentChance) {
|
||
// 确保 item_id 是整数类型
|
||
return (int)$item['item_id'];
|
||
}
|
||
}
|
||
|
||
return null;
|
||
}
|
||
|
||
/**
|
||
* 处理探索时发现的宝箱/固定物品 (保持不变)
|
||
*/
|
||
private function handleLootFound($lootId): void {
|
||
|
||
}
|
||
|
||
/**
|
||
* 核心逻辑:创建物品实例并添加到玩家背包 (保持不变)
|
||
*/
|
||
private function giveItemToPlayer($itemId): void {
|
||
// ...
|
||
$item = $this->itemRepository->createItem($itemId);
|
||
|
||
if (!$item) {
|
||
$this->dispatcher->dispatch(new Event('SystemMessage', ['message' => "❌ 物品ID '{$itemId}' 不存在,无法获得。"]));
|
||
return;
|
||
}
|
||
|
||
$player = $this->stateManager->getPlayer();
|
||
$player->addItem($item);
|
||
|
||
$this->dispatcher->dispatch(new Event('SystemMessage', [
|
||
'message' => "➕ 获得了物品:<fg=green>{$item->name}</>!"
|
||
]));
|
||
|
||
// 触发 InventoryUpdateEvent (未来用于 UI 实时更新)
|
||
$this->dispatcher->dispatch(new Event('InventoryUpdateEvent', ['playerInventory' => $player->getInventory()]));
|
||
}
|
||
} |