164 lines
5.4 KiB
PHP
164 lines
5.4 KiB
PHP
<?php
|
|
namespace Game\System;
|
|
|
|
use Game\Event\Event;
|
|
use Game\Event\EventDispatcher;
|
|
use Game\Model\DialogueNode;
|
|
use Symfony\Component\Console\Input\InputInterface;
|
|
use Symfony\Component\Console\Output\OutputInterface;
|
|
use Symfony\Component\Console\Helper\QuestionHelper;
|
|
use Symfony\Component\Console\Question\Question;
|
|
|
|
use Game\Event\EventListenerInterface; // ⭐ Add import
|
|
|
|
/**
|
|
* DialogueService: 负责处理对话树的流程逻辑和交互。
|
|
*/
|
|
class DialogueService implements EventListenerInterface {
|
|
|
|
private EventDispatcher $dispatcher;
|
|
private StateManager $stateManager; // ⭐ Add StateManager
|
|
private InputInterface $input;
|
|
private OutputInterface $output;
|
|
private QuestionHelper $helper;
|
|
|
|
private array $currentDialogueTree = [];
|
|
private string $currentNodeId = '';
|
|
|
|
public function __construct(EventDispatcher $dispatcher, StateManager $stateManager, InputInterface $input, OutputInterface $output, QuestionHelper $helper) {
|
|
$this->dispatcher = $dispatcher;
|
|
$this->stateManager = $stateManager;
|
|
$this->input = $input;
|
|
$this->output = $output;
|
|
$this->helper = $helper;
|
|
}
|
|
|
|
public function handleEvent(Event $event): void {
|
|
switch ($event->getType()) {
|
|
case 'StartDialogueEvent':
|
|
$dialogue = $event->getPayload()['dialogue'];
|
|
$this->startDialogue($dialogue);
|
|
break;
|
|
case 'DialogueChoice': // ⭐ Listen for choice event
|
|
$choice = $event->getPayload()['choice'];
|
|
$this->handleChoice($choice);
|
|
break;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 开始一段对话 (非阻塞,初始化状态)
|
|
*/
|
|
public function startDialogue(array $dialogueTree): void {
|
|
$this->currentDialogueTree = $dialogueTree;
|
|
$this->currentNodeId = 'root';
|
|
|
|
// 切换游戏模式
|
|
$this->stateManager->setMode(StateManager::MODE_DIALOGUE);
|
|
|
|
// 显示第一个节点
|
|
$this->displayCurrentNode();
|
|
}
|
|
|
|
private function displayCurrentNode(): void {
|
|
if (!isset($this->currentDialogueTree[$this->currentNodeId])) {
|
|
$this->endDialogue();
|
|
return;
|
|
}
|
|
|
|
$nodeData = $this->currentDialogueTree[$this->currentNodeId];
|
|
$node = DialogueNode::fromArray($this->currentNodeId, $nodeData);
|
|
|
|
$this->output->writeln("\n<fg=cyan>🗣️ 对话</>");
|
|
$this->output->writeln("<fg=white>" . str_repeat("-", 40) . "</>");
|
|
$lines = explode("\n", $node->text);
|
|
foreach ($lines as $line) {
|
|
$this->output->writeln(" " . trim($line));
|
|
}
|
|
$this->output->writeln("<fg=white>" . str_repeat("-", 40) . "</>\n");
|
|
|
|
if (empty($node->options)) {
|
|
$this->output->writeln(" [按回车键或输入任意值结束]");
|
|
} else {
|
|
foreach ($node->options as $index => $option) {
|
|
$this->output->writeln(" [<fg=yellow>{$index}</>] {$option['text']}");
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 处理玩家选择
|
|
*/
|
|
private function handleChoice(string $input): void {
|
|
if (!isset($this->currentDialogueTree[$this->currentNodeId])) {
|
|
$this->endDialogue();
|
|
return;
|
|
}
|
|
|
|
$nodeData = $this->currentDialogueTree[$this->currentNodeId];
|
|
$node = DialogueNode::fromArray($this->currentNodeId, $nodeData);
|
|
|
|
// 如果没有选项,任意输入都结束对话 (或者跳转 next)
|
|
if (empty($node->options)) {
|
|
$this->endDialogue();
|
|
return;
|
|
}
|
|
|
|
if (!is_numeric($input)) {
|
|
$this->output->writeln("<fg=red>输入无效,请输入选项数字。</>");
|
|
// 这里不需要循环,直接返回,等待下一次输入事件
|
|
return;
|
|
}
|
|
|
|
$index = (int)$input;
|
|
if (!isset($node->options[$index])) {
|
|
$this->output->writeln("<fg=red>选项不存在。</>");
|
|
return;
|
|
}
|
|
|
|
// 处理选中项
|
|
$selectedOption = $node->options[$index];
|
|
|
|
// 1. 执行动作
|
|
if (!empty($selectedOption['action'])) {
|
|
$this->handleAction($selectedOption['action']);
|
|
}
|
|
|
|
// 2. 跳转
|
|
if (empty($selectedOption['next'])) {
|
|
$this->endDialogue();
|
|
} else {
|
|
$this->currentNodeId = $selectedOption['next'];
|
|
$this->displayCurrentNode();
|
|
}
|
|
}
|
|
|
|
private function endDialogue(): void {
|
|
$this->currentDialogueTree = [];
|
|
$this->currentNodeId = '';
|
|
$this->output->writeln("\n[对话结束]");
|
|
|
|
// 恢复地图模式 (或者之前的模式,稍微简化处理)
|
|
$this->stateManager->setMode(StateManager::MODE_MAP);
|
|
}
|
|
|
|
private function handleAction(string $actionString): void {
|
|
$parts = explode(':', $actionString, 2);
|
|
$actionType = $parts[0];
|
|
$payload = $parts[1] ?? null;
|
|
|
|
switch ($actionType) {
|
|
case 'accept_quest':
|
|
if ($payload) {
|
|
$this->dispatcher->dispatch(new Event('QuestAcceptRequest', ['questId' => $payload]));
|
|
}
|
|
break;
|
|
case 'open_shop':
|
|
if ($payload) {
|
|
$this->dispatcher->dispatch(new Event('OpenShopEvent', ['npcId' => $payload]));
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|