Files
Genarrative/src/components/game-shell/GameShellMainContent.tsx
高物 c49c64896a
Some checks failed
CI / verify (push) Has been cancelled
初始仓库迁移
2026-04-04 23:57:06 +08:00

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>
);
}