/** * RPG runtime story 状态与响应共享契约。 * 该文件只负责 view model、presentation、patch 与 snapshot 回包结构。 */ import type { JsonObject } from './common'; import type { SavedGameSnapshot, SavedGameSnapshotInput } from './runtime'; import type { RuntimeActionRequest, RuntimeActionResponse, RuntimeStoryChoiceAction, RuntimeStoryChoicePayload, RuntimeStoryOptionInteraction, Task5RuntimeOptionScope, } from './rpgRuntimeStoryAction'; export type RuntimeStoryOptionView = { functionId: string; actionText: string; detailText?: string; scope: Task5RuntimeOptionScope; interaction?: RuntimeStoryOptionInteraction; payload?: RuntimeStoryChoicePayload; disabled?: boolean; reason?: string; }; export type RuntimeStoryPlayerViewModel = { hp: number; maxHp: number; mana: number; maxMana: number; }; export type RuntimeStoryCompanionViewModel = { npcId: string; characterId?: string; joinedAtAffinity: number; }; export type RuntimeStoryEncounterViewModel = { id: string; kind: 'npc' | 'treasure'; npcName: string; hostile: boolean; affinity?: number; recruited?: boolean; interactionActive: boolean; battleMode?: 'fight' | 'spar' | null; }; export type RuntimeStoryStatusViewModel = { inBattle: boolean; npcInteractionActive: boolean; currentNpcBattleMode: 'fight' | 'spar' | null; currentNpcBattleOutcome: 'fight_victory' | 'spar_complete' | null; }; export type RuntimeBattlePresentation = { targetId?: string; targetName?: string; damageDealt?: number; damageTaken?: number; outcome?: 'ongoing' | 'victory' | 'spar_complete' | 'escaped'; }; export type RuntimeStoryViewModel = { player: RuntimeStoryPlayerViewModel; encounter: RuntimeStoryEncounterViewModel | null; companions: RuntimeStoryCompanionViewModel[]; availableOptions: RuntimeStoryOptionView[]; status: RuntimeStoryStatusViewModel; }; export type RuntimeStoryPresentation = { actionText: string; resultText: string; storyText: string; options: RuntimeStoryOptionView[]; toast?: string | null; battle?: RuntimeBattlePresentation | null; }; export type RuntimeStoryPatch = | { type: 'story_history_append'; actionText: string; resultText: string; } | { type: 'npc_affinity_changed'; npcId: string; previousAffinity: number; nextAffinity: number; } | { type: 'battle_resolved'; functionId: string; targetId?: string; damageDealt?: number; damageTaken?: number; outcome: 'ongoing' | 'victory' | 'spar_complete' | 'escaped'; } | { type: 'status_changed'; inBattle: boolean; npcInteractionActive: boolean; currentNpcBattleMode: 'fight' | 'spar' | null; currentNpcBattleOutcome: 'fight_victory' | 'spar_complete' | null; } | { type: 'encounter_changed'; encounterId: string | null; }; export type RuntimeStoryActionRequest = RuntimeActionRequest & { snapshot?: SavedGameSnapshotInput; }; export type RuntimeStoryStateRequest< TSnapshotGameState = JsonObject, TSnapshotCurrentStory = JsonObject, > = { sessionId: string; clientVersion?: number; snapshot?: SavedGameSnapshotInput< TSnapshotGameState, string, TSnapshotCurrentStory >; }; export type RuntimeStoryActionResponse< TSnapshotGameState = JsonObject, TSnapshotCurrentStory = JsonObject, > = RuntimeActionResponse< RuntimeStoryViewModel, RuntimeStoryPresentation, RuntimeStoryPatch > & { snapshot: SavedGameSnapshot< TSnapshotGameState, string, TSnapshotCurrentStory >; };