1
This commit is contained in:
191
src/hooks/rpg-session/useRpgRuntimeSession.ts
Normal file
191
src/hooks/rpg-session/useRpgRuntimeSession.ts
Normal file
@@ -0,0 +1,191 @@
|
||||
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<typeof selectCustomWorld>[0],
|
||||
) => {
|
||||
storyFlow.resetStoryState();
|
||||
selectCustomWorld(customWorldProfile);
|
||||
};
|
||||
|
||||
const handleCharacterSelect = (
|
||||
character: Parameters<typeof selectCharacter>[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<typeof useRpgRuntimeSession>;
|
||||
Reference in New Issue
Block a user