/** * RPG runtime story 状态与响应共享契约。 * 该文件只负责 view model、presentation、patch 与 snapshot 回包结构。 */ import type { JsonObject } from './common'; import type { 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' | 'fight_defeat' | 'spar_complete' | null; }; export type RuntimeStoryInventoryActionView = { functionId: string; actionText: string; payload?: JsonObject; enabled: boolean; reason?: string | null; }; export type RuntimeStoryInventoryItemActionsView = { use: RuntimeStoryInventoryActionView; equip: RuntimeStoryInventoryActionView; dismantle: RuntimeStoryInventoryActionView; reforge: RuntimeStoryInventoryActionView; }; export type RuntimeStoryInventoryItemView = { item: JsonObject; actions: RuntimeStoryInventoryItemActionsView; }; export type RuntimeStoryEquipmentSlotView = { slotId: string; label: string; item?: JsonObject | null; unequip: RuntimeStoryInventoryActionView; }; export type RuntimeStoryForgeRequirementView = { id: string; label: string; quantity: number; owned: number; }; export type RuntimeStoryForgeRecipeView = { id: string; name: string; kind: string; description: string; resultLabel: string; currencyCost: number; currencyText: string; requirements: RuntimeStoryForgeRequirementView[]; canCraft: boolean; disabledReason?: string | null; action: RuntimeStoryInventoryActionView; }; export type RuntimeStoryInventoryViewModel = { playerCurrency: number; currencyText: string; inBattle: boolean; backpackItems: RuntimeStoryInventoryItemView[]; equipmentSlots: RuntimeStoryEquipmentSlotView[]; forgeRecipes: RuntimeStoryForgeRecipeView[]; }; export type RuntimeNpcTradeMode = 'buy' | 'sell'; export type RuntimeNpcTradeItemView = { itemId: string; item: JsonObject; mode: RuntimeNpcTradeMode; unitPrice: number; maxQuantity: number; canSubmit: boolean; reason?: string | null; }; export type RuntimeNpcGiftItemView = { itemId: string; item: JsonObject; affinityGain: number; canSubmit: boolean; reason?: string | null; }; export type RuntimeNpcInteractionView = { npcId: string; npcName: string; playerCurrency: number; currencyName: string; trade: { buyItems: RuntimeNpcTradeItemView[]; sellItems: RuntimeNpcTradeItemView[]; }; gift: { items: RuntimeNpcGiftItemView[]; }; }; export type RuntimeBattlePresentation = { targetId?: string; targetName?: string; damageDealt?: number; damageTaken?: number; outcome?: 'ongoing' | 'victory' | 'spar_complete' | 'defeat' | 'escaped'; }; export type RuntimeStoryViewModel = { player: RuntimeStoryPlayerViewModel; encounter: RuntimeStoryEncounterViewModel | null; companions: RuntimeStoryCompanionViewModel[]; inventory: RuntimeStoryInventoryViewModel; availableOptions: RuntimeStoryOptionView[]; status: RuntimeStoryStatusViewModel; npcInteraction?: RuntimeNpcInteractionView | null; }; 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' | 'defeat' | 'escaped'; } | { type: 'status_changed'; inBattle: boolean; npcInteractionActive: boolean; currentNpcBattleMode: 'fight' | 'spar' | null; currentNpcBattleOutcome: 'fight_victory' | 'fight_defeat' | 'spar_complete' | null; } | { type: 'encounter_changed'; encounterId: string | null; }; export type RuntimeStoryActionRequest = { sessionId: string; clientVersion?: number; action: RuntimeStoryChoiceAction; }; export type RuntimeStoryAiRequestOptions = { availableOptions?: JsonObject[]; optionCatalog?: JsonObject[]; }; export type RuntimeStoryAiRequest = { sessionId: string; clientVersion?: number; choice?: string; lastFunctionId?: string | null; observeSignsRequested?: boolean; recentActionResult?: string | null; requestOptions?: RuntimeStoryAiRequestOptions; };