181 lines
5.1 KiB
TypeScript
181 lines
5.1 KiB
TypeScript
import { useEffect } from 'react';
|
|
|
|
import { GameShellRuntime } from './components/game-shell/GameShellRuntime.tsx';
|
|
import { activateRosterCompanion, benchActiveCompanion } from './data/companionRoster';
|
|
import { syncGameStatePlayTime } from './data/runtimeStats';
|
|
import { useBackgroundMusic } from './hooks/useBackgroundMusic';
|
|
import { useCombatFlow } from './hooks/useCombatFlow';
|
|
import { useGameFlow } from './hooks/useGameFlow';
|
|
import { useGamePersistence } from './hooks/useGamePersistence';
|
|
import { useGameSettings } from './hooks/useGameSettings';
|
|
import { useNpcInteractionFlow } from './hooks/useNpcInteractionFlow';
|
|
import { useStoryGeneration } from './hooks/useStoryGeneration';
|
|
|
|
export default function App() {
|
|
const {
|
|
gameState,
|
|
setGameState,
|
|
bottomTab,
|
|
setBottomTab,
|
|
isMapOpen,
|
|
setIsMapOpen,
|
|
resetGame,
|
|
handleCustomWorldSelect: selectCustomWorld,
|
|
handleBackToWorldSelect: backToWorldSelect,
|
|
handleCharacterSelect: selectCharacter,
|
|
} = useGameFlow();
|
|
|
|
const combatFlow = useCombatFlow({
|
|
setGameState,
|
|
});
|
|
|
|
const storyFlow = useStoryGeneration({
|
|
gameState,
|
|
setGameState,
|
|
buildResolvedChoiceState: combatFlow.buildResolvedChoiceState,
|
|
playResolvedChoice: combatFlow.playResolvedChoice,
|
|
});
|
|
|
|
const { companionRenderStates, buildCompanionRenderStates } = useNpcInteractionFlow(gameState);
|
|
const settings = useGameSettings();
|
|
|
|
const persistence = useGamePersistence({
|
|
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: settings.musicVolume,
|
|
});
|
|
|
|
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 = () => {
|
|
persistence.continueSavedGame();
|
|
};
|
|
|
|
const handleStartNewGame = () => {
|
|
persistence.clearSavedGame();
|
|
storyFlow.resetStoryState();
|
|
resetGame();
|
|
};
|
|
|
|
const handleSaveAndExit = () => {
|
|
const syncedGameState = syncGameStatePlayTime(gameState);
|
|
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));
|
|
};
|
|
|
|
const gameShellSession = {
|
|
gameState,
|
|
currentStory: storyFlow.currentStory,
|
|
isLoading: storyFlow.isLoading,
|
|
aiError: storyFlow.aiError,
|
|
bottomTab,
|
|
setBottomTab,
|
|
isMapOpen,
|
|
setIsMapOpen,
|
|
};
|
|
|
|
const gameShellStory = {
|
|
displayedOptions: storyFlow.displayedOptions,
|
|
canRefreshOptions: storyFlow.canRefreshOptions,
|
|
handleRefreshOptions: storyFlow.handleRefreshOptions,
|
|
handleChoice: storyFlow.handleChoice,
|
|
handleMapTravelToScene: storyFlow.travelToSceneFromMap,
|
|
npcUi: storyFlow.npcUi,
|
|
characterChatUi: storyFlow.characterChatUi,
|
|
inventoryUi: storyFlow.inventoryUi,
|
|
battleRewardUi: storyFlow.battleRewardUi,
|
|
questUi: storyFlow.questUi,
|
|
goalUi: storyFlow.goalUi,
|
|
};
|
|
|
|
const gameShellEntry = {
|
|
hasSavedGame: persistence.hasSavedGame,
|
|
handleContinueGame,
|
|
handleStartNewGame,
|
|
handleSaveAndExit,
|
|
handleCustomWorldSelect,
|
|
handleBackToWorldSelect,
|
|
handleCharacterSelect,
|
|
};
|
|
|
|
const gameShellCompanions = {
|
|
companionRenderStates,
|
|
buildCompanionRenderStates,
|
|
onBenchCompanion: handleBenchCompanion,
|
|
onActivateRosterCompanion: handleActivateRosterCompanion,
|
|
};
|
|
|
|
const gameShellAudio = {
|
|
musicVolume: settings.musicVolume,
|
|
onMusicVolumeChange: settings.setMusicVolume,
|
|
};
|
|
|
|
return (
|
|
<GameShellRuntime
|
|
session={gameShellSession}
|
|
story={gameShellStory}
|
|
entry={gameShellEntry}
|
|
companions={gameShellCompanions}
|
|
audio={gameShellAudio}
|
|
/>
|
|
);
|
|
}
|