import { useEffect } from 'react'; import { DEFAULT_MUSIC_VOLUME } from '../../../packages/shared/src/contracts/runtime'; import { useAuthUi } from '../../components/auth/AuthUiContext'; import type { RpgRuntimeShellProps } from '../../components/rpg-runtime-shell'; import { activateRosterCompanion, benchActiveCompanion } from '../../data/companionRoster'; import { syncGameStatePlayTime } from '../../data/runtimeStats'; import type { HydratedSavedGameSnapshot } from '../../persistence/runtimeSnapshotTypes'; import { useBackgroundMusic } from '../useBackgroundMusic'; import { useCombatFlow } from '../useCombatFlow'; import { useNpcInteractionFlow } from '../useNpcInteractionFlow'; import { useRpgRuntimeStory } from '../rpg-runtime-story'; import { useRpgSessionBootstrap } from './useRpgSessionBootstrap'; import { useRpgSessionPersistence } from './useRpgSessionPersistence'; /** * RPG 主运行态装配器真实实现。 * 工作包 C 起主链改为组合 `rpg-session` 下的 bootstrap / persistence 新入口。 */ export function useRpgRuntimeSession(): RpgRuntimeShellProps { const authUi = useAuthUi(); const { gameState, setGameState, bottomTab, setBottomTab, isMapOpen, setIsMapOpen, resetGame, handleCustomWorldSelect: selectCustomWorld, handleBackToWorldSelect: backToWorldSelect, handleCharacterSelect: selectCharacter, } = useRpgSessionBootstrap(); const combatFlow = useCombatFlow({ setGameState, }); const storyFlow = useRpgRuntimeStory({ gameState, setGameState, buildResolvedChoiceState: combatFlow.buildResolvedChoiceState, playResolvedChoice: combatFlow.playResolvedChoice, }); const { companionRenderStates, buildCompanionRenderStates } = useNpcInteractionFlow(gameState); const persistence = useRpgSessionPersistence({ authenticatedUserId: authUi?.user?.id ?? null, gameState, bottomTab, currentStory: storyFlow.currentStory, isLoading: storyFlow.isLoading, setGameState, setBottomTab, hydrateStoryState: storyFlow.hydrateStoryState, resetStoryState: storyFlow.resetStoryState, }); useBackgroundMusic({ active: Boolean( gameState.playerCharacter && gameState.currentScene === 'Story', ), volume: authUi?.musicVolume ?? DEFAULT_MUSIC_VOLUME, }); useEffect(() => { if (!gameState.playerCharacter || gameState.currentScene !== 'Story') { return; } const intervalId = window.setInterval(() => { setGameState((currentState) => { if ( !currentState.playerCharacter || currentState.currentScene !== 'Story' ) { return currentState; } return syncGameStatePlayTime(currentState); }); }, 15000); return () => window.clearInterval(intervalId); }, [gameState.currentScene, gameState.playerCharacter, setGameState]); const handleCustomWorldSelect = ( customWorldProfile: Parameters[0], ) => { storyFlow.resetStoryState(); selectCustomWorld(customWorldProfile); }; const handleCharacterSelect = ( character: Parameters[0], ) => { storyFlow.resetStoryState(); selectCharacter(character); }; const handleBackToWorldSelect = () => { storyFlow.resetStoryState(); backToWorldSelect(); }; const handleContinueGame = (snapshot?: HydratedSavedGameSnapshot | null) => { void persistence.continueSavedGame(snapshot); }; const handleStartNewGame = () => { void persistence.clearSavedGame(); storyFlow.resetStoryState(); resetGame(); }; const handleSaveAndExit = () => { const syncedGameState = syncGameStatePlayTime(gameState); void persistence.saveCurrentGame({ gameState: syncedGameState, bottomTab, currentStory: storyFlow.currentStory, }); storyFlow.resetStoryState(); resetGame(); }; const handleBenchCompanion = (npcId: string) => { setGameState((currentState) => benchActiveCompanion(currentState, npcId)); }; const handleActivateRosterCompanion = ( npcId: string, swapNpcId?: string | null, ) => { setGameState((currentState) => activateRosterCompanion(currentState, npcId, swapNpcId), ); }; return { session: { gameState, currentStory: storyFlow.currentStory, isLoading: storyFlow.isLoading, aiError: storyFlow.aiError, bottomTab, setBottomTab, isMapOpen, setIsMapOpen, }, story: { displayedOptions: storyFlow.displayedOptions, canRefreshOptions: storyFlow.canRefreshOptions, handleRefreshOptions: storyFlow.handleRefreshOptions, handleChoice: storyFlow.handleChoice, handleNpcChatInput: storyFlow.handleNpcChatInput, exitNpcChat: storyFlow.exitNpcChat, handleMapTravelToScene: storyFlow.travelToSceneFromMap, npcUi: storyFlow.npcUi, characterChatUi: storyFlow.characterChatUi, inventoryUi: storyFlow.inventoryUi, battleRewardUi: storyFlow.battleRewardUi, questUi: storyFlow.questUi, npcChatQuestOfferUi: storyFlow.npcChatQuestOfferUi, goalUi: storyFlow.goalUi, }, entry: { hasSavedGame: persistence.hasSavedGame, savedSnapshot: persistence.savedSnapshot, handleContinueGame, handleStartNewGame, handleSaveAndExit, handleCustomWorldSelect, handleBackToWorldSelect, handleCharacterSelect, }, companions: { companionRenderStates, buildCompanionRenderStates, onBenchCompanion: handleBenchCompanion, onActivateRosterCompanion: handleActivateRosterCompanion, }, audio: { musicVolume: authUi?.musicVolume ?? DEFAULT_MUSIC_VOLUME, onMusicVolumeChange: authUi?.setMusicVolume ?? (() => {}), }, }; } export type RpgRuntimeSessionResult = ReturnType;