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>; 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, 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; 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, }); const { handleChoice, battleRewardUi, npcUi, inventoryUi, clearStoryInteractionUi, handleNpcChatInput, refreshNpcChatOptions, exitNpcChat, npcChatQuestOfferUi, } = useRpgRuntimeInteractionFlow({ gameState, isLoading, 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, }); 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 >;