Files
Genarrative/src/hooks/rpg-runtime-story/useRpgRuntimeStoryFlow.ts
kdletters cbc27bad4a
Some checks failed
CI / verify (push) Has been cancelled
init with react+axum+spacetimedb
2026-04-26 18:06:23 +08:00

204 lines
5.8 KiB
TypeScript

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<SetStateAction<GameState>>;
buildResolvedChoiceState: (
state: GameState,
option: StoryOption,
character: Character,
) => ResolvedChoiceState;
playResolvedChoice: (
state: GameState,
option: StoryOption,
character: Character,
resolvedChoice: ResolvedChoiceState,
sync?: ResolvedChoicePlaybackSync,
) => Promise<GameState>;
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
>;