import type { Dispatch, SetStateAction } from 'react'; import type { Character, Encounter, GameState, StoryOption } from '../../types'; import type { EscapePlaybackSync as ResolvedChoicePlaybackSync } from '../combat/escapeFlow'; import type { ResolvedChoiceState } from '../combat/resolvedChoice'; import { createStoryInteractionCoordinatorConfig } from './storyInteractionCoordinator'; import { sanitizeStoryOptions } from './storyPresentation'; import type { StoryRuntimeSupport } from './storyRuntimeSupport'; import { useRpgRuntimeInteractionFlow } from './useRpgRuntimeInteractionFlow'; import type { RpgRuntimeStoryControllerResult } from './useRpgRuntimeStoryController'; import { useRpgRuntimeStoryState } from './useRpgRuntimeStoryState'; import { useStoryGoalOptionCoordinator } from './useStoryGoalOptionCoordinator'; type RpgRuntimeStoryFlowParams = { gameState: GameState; setGameState: Dispatch>; sceneTransitionPhase?: 'idle' | 'exiting' | 'entering'; buildResolvedChoiceState: ( state: GameState, option: StoryOption, character: Character, ) => ResolvedChoiceState; playResolvedChoice: ( state: GameState, option: StoryOption, character: Character, resolvedChoice: ResolvedChoiceState, sync?: ResolvedChoicePlaybackSync, ) => Promise; getStoryGenerationHostileNpcs: ( state: GameState, ) => GameState['sceneHostileNpcs']; getResolvedSceneHostileNpcs: ( state: GameState, ) => GameState['sceneHostileNpcs']; runtimeController: RpgRuntimeStoryControllerResult; runtimeSupport: StoryRuntimeSupport; sortOptions: (options: StoryOption[]) => StoryOption[]; buildContinueAdventureOption: () => StoryOption; resolveNpcInteractionDecision: ( state: GameState, option: StoryOption, ) => { kind: string }; clearCharacterChatModal: () => void; isContinueAdventureOption: (option: StoryOption) => boolean; isCampTravelHomeOption: (option: StoryOption) => boolean; isRegularNpcEncounter: ( encounter: GameState['currentEncounter'], ) => encounter is Encounter; isNpcEncounter: ( encounter: GameState['currentEncounter'], ) => encounter is Encounter; npcPreviewTalkFunctionId: string; fallbackCompanionName: string; turnVisualMs: number; }; /** * RPG runtime story 主编排层。 * 这里把 option 展示、正式交互分发和 story/session 状态动作收束成稳定出口。 */ export function useRpgRuntimeStoryFlow({ gameState, setGameState, sceneTransitionPhase = 'idle', buildResolvedChoiceState, playResolvedChoice, getStoryGenerationHostileNpcs, getResolvedSceneHostileNpcs, runtimeController, runtimeSupport, sortOptions, buildContinueAdventureOption, resolveNpcInteractionDecision, clearCharacterChatModal, isContinueAdventureOption, isCampTravelHomeOption, isRegularNpcEncounter, isNpcEncounter, npcPreviewTalkFunctionId, fallbackCompanionName, turnVisualMs, }: RpgRuntimeStoryFlowParams) { const { currentStory, setCurrentStory, setAiError, setIsLoading, isLoading, buildStoryContextFromState, buildFallbackStoryForState, buildDialogueStoryMoment, generateStoryForState, getAvailableOptionsForState, getTypewriterDelay, commitGeneratedState, commitGeneratedStateWithEncounterEntry, appendHistory, buildOpeningCampChatContext, resetPreparedOpeningAdventure, } = runtimeController; // 中文注释:interactionConfig 是“剧情交互协调器”的配置快照; // 后续选项刷新、动作提交、fallback 叙事都会共用这套上下文。 const interactionConfig = createStoryInteractionCoordinatorConfig({ gameState, setGameState, setCurrentStory, setAiError, setIsLoading, currentStory, buildStoryContextFromState, buildFallbackStoryForState, buildDialogueStoryMoment, generateStoryForState, getAvailableOptionsForState, getStoryGenerationHostileNpcs, getTypewriterDelay, runtimeSupport, commitGeneratedState, commitGeneratedStateWithEncounterEntry, appendHistory, buildOpeningCampChatContext, sortOptions, buildContinueAdventureOption, sanitizeOptions: sanitizeStoryOptions, resolveNpcInteractionDecision, }); const { displayedOptions, canRefreshOptions, handleRefreshOptions, goalUi, clearStoryGoalOptionUi, } = useStoryGoalOptionCoordinator({ gameState, currentStory, }); // 中文注释:这一层把“战斗/NPC/背包/地图旅行”等具体交互入口分发到对应流程, // 保证冒险面板只调用统一的 handleChoice / handleNpcChatInput 等接口。 const { handleChoice, battleRewardUi, npcUi, inventoryUi, clearStoryInteractionUi, handleNpcChatInput, refreshNpcChatOptions, exitNpcChat, npcChatQuestOfferUi, } = useRpgRuntimeInteractionFlow({ gameState, isLoading, sceneTransitionPhase, interactionConfig, runtimeSupport, buildResolvedChoiceState, playResolvedChoice, buildStoryFromResponse: runtimeController.buildStoryFromResponse, getResolvedSceneHostileNpcs, getCampCompanionTravelScene: runtimeController.getCampCompanionTravelScene, isContinueAdventureOption, isCampTravelHomeOption, isRegularNpcEncounter, isNpcEncounter, npcPreviewTalkFunctionId, fallbackCompanionName, turnVisualMs, }); const { questUi, resetStoryState, hydrateStoryState, travelToSceneFromMap } = useRpgRuntimeStoryState({ gameState, isLoading, setGameState, setCurrentStory, setAiError, setIsLoading, commitGeneratedState, buildFallbackStoryForState, resetPreparedOpeningAdventure, clearStoryGoalOptionUi, clearStoryInteractionUi, clearCharacterChatModal, }); // 中文注释:最终返回的是已经过目标选项协调、交互分发和 story state 收束后的稳定输出。 return { displayedOptions, canRefreshOptions, handleRefreshOptions, handleChoice, resetStoryState, hydrateStoryState, travelToSceneFromMap, battleRewardUi, questUi, goalUi, npcUi, inventoryUi, handleNpcChatInput, refreshNpcChatOptions, exitNpcChat, npcChatQuestOfferUi, }; } export type UseRpgRuntimeStoryFlowParams = Parameters< typeof useRpgRuntimeStoryFlow >[0]; export type RpgRuntimeStoryFlowResult = ReturnType< typeof useRpgRuntimeStoryFlow >;