193 lines
6.4 KiB
TypeScript
193 lines
6.4 KiB
TypeScript
import { AnimatePresence, motion } from 'motion/react';
|
|
|
|
import type { BottomTab } from '../../hooks/useGameFlow';
|
|
import type {
|
|
BattleRewardUi,
|
|
CharacterChatUi,
|
|
InventoryFlowUi,
|
|
QuestFlowUi,
|
|
} from '../../hooks/useStoryGeneration';
|
|
import type {
|
|
CompanionRenderState,
|
|
GameState,
|
|
StoryMoment,
|
|
StoryOption,
|
|
WorldType,
|
|
} from '../../types';
|
|
import { UI_CHROME } from '../../uiAssets';
|
|
import type { GameCanvasEntitySelection } from '../GameCanvas';
|
|
import { CharacterSelectionFlow } from './CharacterSelectionFlow';
|
|
import { GameShellStoryPanels } from './GameShellStoryPanels';
|
|
import { PreGameSelectionFlow, type SelectionStage } from './PreGameSelectionFlow';
|
|
|
|
type AdventureStatistics = {
|
|
playTimeMs: number;
|
|
hostileNpcsDefeated: number;
|
|
questsAccepted: number;
|
|
questsCompleted: number;
|
|
questsTurnedIn: number;
|
|
itemsUsed: number;
|
|
scenesTraveled: number;
|
|
currentSceneName: string;
|
|
playerCurrency: number;
|
|
inventoryItemCount: number;
|
|
inventoryStackCount: number;
|
|
activeCompanionCount: number;
|
|
rosterCompanionCount: number;
|
|
};
|
|
|
|
export function GameShellMainContent({
|
|
gameState,
|
|
visibleGameState,
|
|
visibleCurrentStory,
|
|
isLoading,
|
|
aiError,
|
|
bottomTab,
|
|
setBottomTab,
|
|
selectionStage,
|
|
setSelectionStage,
|
|
isCharacterSelectionStage,
|
|
hasSavedGame,
|
|
handleContinueGame,
|
|
handleStartNewGame,
|
|
handleWorldSelect,
|
|
handleBackToWorldSelect,
|
|
handleCharacterSelect,
|
|
displayedOptions,
|
|
hideStoryOptions,
|
|
canRefreshOptions,
|
|
handleRefreshOptions,
|
|
handleSceneTransitionChoice,
|
|
characterChatUi,
|
|
inventoryUi,
|
|
battleRewardUi,
|
|
questUi,
|
|
companionRenderStates,
|
|
characterChatSummaries,
|
|
openOverlayPanel,
|
|
openCampModal,
|
|
openPartyMemberDetails,
|
|
adventureStatistics,
|
|
musicVolume,
|
|
onMusicVolumeChange,
|
|
resetForSaveAndExit,
|
|
handleSaveAndExit,
|
|
}: {
|
|
gameState: GameState;
|
|
visibleGameState: GameState;
|
|
visibleCurrentStory: StoryMoment | null;
|
|
isLoading: boolean;
|
|
aiError: string | null;
|
|
bottomTab: BottomTab;
|
|
setBottomTab: (tab: BottomTab) => void;
|
|
selectionStage: SelectionStage;
|
|
setSelectionStage: (stage: SelectionStage) => void;
|
|
isCharacterSelectionStage: boolean;
|
|
hasSavedGame: boolean;
|
|
handleContinueGame: () => void;
|
|
handleStartNewGame: () => void;
|
|
handleWorldSelect: (type: WorldType, customWorldProfile?: GameState['customWorldProfile']) => void;
|
|
handleBackToWorldSelect: () => void;
|
|
handleCharacterSelect: (character: NonNullable<GameState['playerCharacter']>) => void;
|
|
displayedOptions: StoryOption[];
|
|
hideStoryOptions: boolean;
|
|
canRefreshOptions: boolean;
|
|
handleRefreshOptions: () => void;
|
|
handleSceneTransitionChoice: (option: StoryOption) => void;
|
|
characterChatUi: CharacterChatUi;
|
|
inventoryUi: InventoryFlowUi;
|
|
battleRewardUi: BattleRewardUi;
|
|
questUi: QuestFlowUi;
|
|
companionRenderStates: CompanionRenderState[];
|
|
characterChatSummaries: Record<string, string>;
|
|
openOverlayPanel: (panel: 'character' | 'inventory') => void;
|
|
openCampModal: () => void;
|
|
openPartyMemberDetails: (selection: GameCanvasEntitySelection) => void;
|
|
adventureStatistics: AdventureStatistics;
|
|
musicVolume: number;
|
|
onMusicVolumeChange: (value: number) => void;
|
|
resetForSaveAndExit: () => void;
|
|
handleSaveAndExit: () => void;
|
|
}) {
|
|
return (
|
|
<div
|
|
className={`pixel-app-shell flex min-h-0 flex-1 flex-col ${isCharacterSelectionStage ? 'justify-center p-4 sm:p-5' : 'p-3 sm:p-4'}`}
|
|
style={{
|
|
background: isCharacterSelectionStage
|
|
? '#0d1016'
|
|
: `linear-gradient(rgba(10, 12, 18, 0.55), rgba(10, 12, 18, 0.55)), url("${UI_CHROME.appBackground}")`,
|
|
backgroundPosition: isCharacterSelectionStage ? undefined : 'center',
|
|
backgroundRepeat: isCharacterSelectionStage ? undefined : 'repeat',
|
|
}}
|
|
>
|
|
<AnimatePresence mode="wait">
|
|
{!gameState.worldType && (
|
|
<PreGameSelectionFlow
|
|
selectionStage={selectionStage}
|
|
setSelectionStage={setSelectionStage}
|
|
gameState={gameState}
|
|
hasSavedGame={hasSavedGame}
|
|
handleContinueGame={handleContinueGame}
|
|
handleStartNewGame={handleStartNewGame}
|
|
handleWorldSelect={handleWorldSelect}
|
|
/>
|
|
)}
|
|
|
|
{gameState.worldType && !gameState.playerCharacter && (
|
|
<motion.div
|
|
key="character-select-shell"
|
|
initial={{ opacity: 0, y: 12 }}
|
|
animate={{ opacity: 1, y: 0 }}
|
|
exit={{ opacity: 0, y: -12 }}
|
|
className="flex h-full min-h-0 flex-col"
|
|
>
|
|
<CharacterSelectionFlow
|
|
worldType={gameState.worldType}
|
|
customWorldProfile={gameState.customWorldProfile}
|
|
onBack={() => {
|
|
handleBackToWorldSelect();
|
|
setSelectionStage('world');
|
|
}}
|
|
onConfirm={handleCharacterSelect}
|
|
/>
|
|
</motion.div>
|
|
)}
|
|
|
|
{visibleGameState.playerCharacter && visibleCurrentStory && (
|
|
<motion.div key="story-flow" initial={{ opacity: 0 }} animate={{ opacity: 1 }} className="flex h-full min-h-0 flex-col">
|
|
<GameShellStoryPanels
|
|
visibleGameState={visibleGameState}
|
|
visibleCurrentStory={visibleCurrentStory}
|
|
isLoading={isLoading}
|
|
aiError={aiError}
|
|
bottomTab={bottomTab}
|
|
setBottomTab={setBottomTab}
|
|
displayedOptions={displayedOptions}
|
|
hideStoryOptions={hideStoryOptions}
|
|
canRefreshOptions={canRefreshOptions}
|
|
handleRefreshOptions={handleRefreshOptions}
|
|
handleSceneTransitionChoice={handleSceneTransitionChoice}
|
|
characterChatUi={characterChatUi}
|
|
inventoryUi={inventoryUi}
|
|
battleRewardUi={battleRewardUi}
|
|
questUi={questUi}
|
|
companionRenderStates={companionRenderStates}
|
|
characterChatSummaries={characterChatSummaries}
|
|
openOverlayPanel={openOverlayPanel}
|
|
openCampModal={openCampModal}
|
|
openPartyMemberDetails={openPartyMemberDetails}
|
|
adventureStatistics={adventureStatistics}
|
|
musicVolume={musicVolume}
|
|
onMusicVolumeChange={onMusicVolumeChange}
|
|
onSaveAndExit={() => {
|
|
resetForSaveAndExit();
|
|
handleSaveAndExit();
|
|
}}
|
|
/>
|
|
</motion.div>
|
|
)}
|
|
</AnimatePresence>
|
|
</div>
|
|
);
|
|
}
|