1
This commit is contained in:
158
packages/shared/src/contracts/auth.ts
Normal file
158
packages/shared/src/contracts/auth.ts
Normal file
@@ -0,0 +1,158 @@
|
||||
export type AuthBindingStatus = 'active' | 'pending_bind_phone';
|
||||
export type AuthLoginMethod = 'password' | 'phone' | 'wechat';
|
||||
|
||||
export type AuthUser = {
|
||||
id: string;
|
||||
username: string;
|
||||
displayName: string;
|
||||
phoneNumberMasked: string | null;
|
||||
loginMethod: AuthLoginMethod;
|
||||
bindingStatus: AuthBindingStatus;
|
||||
wechatBound: boolean;
|
||||
};
|
||||
|
||||
export type AuthEntryRequest = {
|
||||
username: string;
|
||||
password: string;
|
||||
};
|
||||
|
||||
export type AuthEntryResponse = {
|
||||
token: string;
|
||||
user: AuthUser;
|
||||
};
|
||||
|
||||
export type AuthPhoneSendCodeRequest = {
|
||||
phone: string;
|
||||
scene?: 'login' | 'bind_phone' | 'change_phone';
|
||||
captchaChallengeId?: string;
|
||||
captchaAnswer?: string;
|
||||
};
|
||||
|
||||
export type AuthPhoneSendCodeResponse = {
|
||||
ok: true;
|
||||
cooldownSeconds: number;
|
||||
expiresInSeconds: number;
|
||||
providerRequestId: string | null;
|
||||
};
|
||||
|
||||
export type AuthPhoneLoginRequest = {
|
||||
phone: string;
|
||||
code: string;
|
||||
};
|
||||
|
||||
export type AuthPhoneLoginResponse = {
|
||||
token: string;
|
||||
user: AuthUser;
|
||||
};
|
||||
|
||||
export type AuthMeResponse = {
|
||||
user: AuthUser | null;
|
||||
availableLoginMethods: AuthLoginMethod[];
|
||||
};
|
||||
|
||||
export type AuthWechatStartResponse = {
|
||||
authorizationUrl: string;
|
||||
};
|
||||
|
||||
export type AuthWechatBindPhoneRequest = {
|
||||
phone: string;
|
||||
code: string;
|
||||
};
|
||||
|
||||
export type AuthWechatBindPhoneResponse = {
|
||||
token: string;
|
||||
user: AuthUser;
|
||||
};
|
||||
|
||||
export type AuthPhoneChangeRequest = {
|
||||
phone: string;
|
||||
code: string;
|
||||
};
|
||||
|
||||
export type AuthPhoneChangeResponse = {
|
||||
user: AuthUser;
|
||||
};
|
||||
|
||||
export type AuthRefreshResponse = {
|
||||
token: string;
|
||||
};
|
||||
|
||||
export type AuthSessionSummary = {
|
||||
sessionId: string;
|
||||
clientType: string;
|
||||
clientLabel: string;
|
||||
userAgent: string | null;
|
||||
ipMasked: string | null;
|
||||
isCurrent: boolean;
|
||||
createdAt: string;
|
||||
lastSeenAt: string;
|
||||
expiresAt: string;
|
||||
};
|
||||
|
||||
export type AuthSessionsResponse = {
|
||||
sessions: AuthSessionSummary[];
|
||||
};
|
||||
|
||||
export type AuthLogoutAllResponse = {
|
||||
ok: true;
|
||||
};
|
||||
|
||||
export type AuthRevokeSessionResponse = {
|
||||
ok: true;
|
||||
};
|
||||
|
||||
export type AuthAuditLogEventType =
|
||||
| 'password_login'
|
||||
| 'phone_login'
|
||||
| 'wechat_login'
|
||||
| 'wechat_bind_phone'
|
||||
| 'change_phone'
|
||||
| 'captcha_required'
|
||||
| 'logout'
|
||||
| 'logout_all'
|
||||
| 'revoke_session'
|
||||
| 'risk_block_phone'
|
||||
| 'risk_block_ip'
|
||||
| 'risk_unblock_phone'
|
||||
| 'risk_unblock_ip';
|
||||
|
||||
export type AuthAuditLogEntry = {
|
||||
id: string;
|
||||
eventType: AuthAuditLogEventType;
|
||||
title: string;
|
||||
detail: string;
|
||||
ipMasked: string | null;
|
||||
userAgent: string | null;
|
||||
createdAt: string;
|
||||
};
|
||||
|
||||
export type AuthAuditLogsResponse = {
|
||||
logs: AuthAuditLogEntry[];
|
||||
};
|
||||
|
||||
export type AuthCaptchaChallenge = {
|
||||
challengeId: string;
|
||||
promptText: string;
|
||||
imageDataUrl: string;
|
||||
expiresInSeconds: number;
|
||||
};
|
||||
|
||||
export type AuthRiskBlockSummary = {
|
||||
scopeType: 'phone' | 'ip';
|
||||
title: string;
|
||||
detail: string;
|
||||
expiresAt: string;
|
||||
remainingSeconds: number;
|
||||
};
|
||||
|
||||
export type AuthRiskBlocksResponse = {
|
||||
blocks: AuthRiskBlockSummary[];
|
||||
};
|
||||
|
||||
export type AuthLiftRiskBlockResponse = {
|
||||
ok: true;
|
||||
};
|
||||
|
||||
export type LogoutResponse = {
|
||||
ok: true;
|
||||
};
|
||||
3
packages/shared/src/contracts/common.ts
Normal file
3
packages/shared/src/contracts/common.ts
Normal file
@@ -0,0 +1,3 @@
|
||||
export type JsonObject = Record<string, unknown>;
|
||||
|
||||
export type JsonArray = unknown[];
|
||||
124
packages/shared/src/contracts/runtime.ts
Normal file
124
packages/shared/src/contracts/runtime.ts
Normal file
@@ -0,0 +1,124 @@
|
||||
import type { JsonObject } from './common';
|
||||
|
||||
export const SAVE_SNAPSHOT_VERSION = 2;
|
||||
export const DEFAULT_MUSIC_VOLUME = 0.42;
|
||||
|
||||
export type SavedGameSnapshot<
|
||||
TGameState = unknown,
|
||||
TBottomTab extends string = string,
|
||||
TCurrentStory = unknown,
|
||||
> = {
|
||||
version: number;
|
||||
savedAt: string;
|
||||
gameState: TGameState;
|
||||
bottomTab: TBottomTab;
|
||||
currentStory: TCurrentStory | null;
|
||||
};
|
||||
|
||||
export type SavedGameSnapshotInput<
|
||||
TGameState = unknown,
|
||||
TBottomTab extends string = string,
|
||||
TCurrentStory = unknown,
|
||||
> = Omit<
|
||||
SavedGameSnapshot<TGameState, TBottomTab, TCurrentStory>,
|
||||
'savedAt' | 'version'
|
||||
> & {
|
||||
savedAt?: string;
|
||||
};
|
||||
|
||||
export type RuntimeSettings = {
|
||||
musicVolume: number;
|
||||
};
|
||||
|
||||
export type BasicOkResult = {
|
||||
ok: true;
|
||||
};
|
||||
|
||||
export type CustomWorldProfileRecord = JsonObject & {
|
||||
id?: string;
|
||||
};
|
||||
|
||||
export type CustomWorldLibraryResponse<
|
||||
TProfile = CustomWorldProfileRecord,
|
||||
> = {
|
||||
profiles: TProfile[];
|
||||
};
|
||||
|
||||
export const CUSTOM_WORLD_GENERATION_MODES = ['fast', 'full'] as const;
|
||||
export type CustomWorldGenerationMode =
|
||||
(typeof CUSTOM_WORLD_GENERATION_MODES)[number];
|
||||
|
||||
export const CUSTOM_WORLD_SESSION_STATUSES = [
|
||||
'clarifying',
|
||||
'ready_to_generate',
|
||||
'generating',
|
||||
'completed',
|
||||
'generation_error',
|
||||
] as const;
|
||||
export type CustomWorldSessionStatus =
|
||||
(typeof CUSTOM_WORLD_SESSION_STATUSES)[number];
|
||||
|
||||
export type CustomWorldQuestion = {
|
||||
id: string;
|
||||
label: string;
|
||||
question: string;
|
||||
answer?: string;
|
||||
};
|
||||
|
||||
export type CustomWorldGenerationStep = {
|
||||
id: string;
|
||||
label: string;
|
||||
detail: string;
|
||||
completed: number;
|
||||
total: number;
|
||||
status: 'pending' | 'active' | 'completed';
|
||||
};
|
||||
|
||||
export type CustomWorldGenerationProgress = {
|
||||
phaseId: string;
|
||||
phaseLabel: string;
|
||||
phaseDetail: string;
|
||||
batchLabel?: string;
|
||||
overallProgress: number;
|
||||
completedWeight: number;
|
||||
totalWeight: number;
|
||||
elapsedMs: number;
|
||||
estimatedRemainingMs: number | null;
|
||||
activeStepIndex: number;
|
||||
steps: CustomWorldGenerationStep[];
|
||||
};
|
||||
|
||||
export type GenerateCustomWorldProfileOptions = {
|
||||
onProgress?: (progress: CustomWorldGenerationProgress) => void;
|
||||
signal?: AbortSignal;
|
||||
};
|
||||
|
||||
export type GenerateCustomWorldProfileInput = {
|
||||
settingText: string;
|
||||
creatorIntent?: JsonObject | null;
|
||||
generationMode?: CustomWorldGenerationMode;
|
||||
};
|
||||
|
||||
export type CreateCustomWorldSessionRequest = {
|
||||
settingText: string;
|
||||
creatorIntent?: JsonObject | null;
|
||||
generationMode: CustomWorldGenerationMode;
|
||||
};
|
||||
|
||||
export type AnswerCustomWorldSessionQuestionRequest = {
|
||||
questionId: string;
|
||||
answer: string;
|
||||
};
|
||||
|
||||
export type CustomWorldSessionSummary = {
|
||||
sessionId: string;
|
||||
status: CustomWorldSessionStatus;
|
||||
questions: CustomWorldQuestion[];
|
||||
};
|
||||
|
||||
export type CustomWorldSessionRecord = CustomWorldSessionSummary & {
|
||||
settingText: string;
|
||||
generationMode: CustomWorldGenerationMode;
|
||||
result?: JsonObject;
|
||||
lastError?: string;
|
||||
};
|
||||
408
packages/shared/src/contracts/story.ts
Normal file
408
packages/shared/src/contracts/story.ts
Normal file
@@ -0,0 +1,408 @@
|
||||
import type { JsonObject } from './common';
|
||||
import type { SavedGameSnapshot } from './runtime';
|
||||
|
||||
export const QUEST_NARRATIVE_TYPES = [
|
||||
'bounty',
|
||||
'escort',
|
||||
'investigation',
|
||||
'retrieval',
|
||||
'relationship',
|
||||
'trial',
|
||||
] as const;
|
||||
export type SharedQuestNarrativeType =
|
||||
(typeof QUEST_NARRATIVE_TYPES)[number];
|
||||
|
||||
export const QUEST_OBJECTIVE_KINDS = [
|
||||
'defeat_hostile_npc',
|
||||
'inspect_treasure',
|
||||
'spar_with_npc',
|
||||
'talk_to_npc',
|
||||
'reach_scene',
|
||||
'deliver_item',
|
||||
] as const;
|
||||
export type SharedQuestObjectiveKind =
|
||||
(typeof QUEST_OBJECTIVE_KINDS)[number];
|
||||
|
||||
export const QUEST_URGENCY_LEVELS = ['low', 'medium', 'high'] as const;
|
||||
export type SharedQuestUrgency = (typeof QUEST_URGENCY_LEVELS)[number];
|
||||
|
||||
export const QUEST_INTIMACY_LEVELS = [
|
||||
'transactional',
|
||||
'cooperative',
|
||||
'trust_based',
|
||||
] as const;
|
||||
export type SharedQuestIntimacy = (typeof QUEST_INTIMACY_LEVELS)[number];
|
||||
|
||||
export const QUEST_REWARD_THEMES = [
|
||||
'currency',
|
||||
'resource',
|
||||
'relationship',
|
||||
'intel',
|
||||
'rare_item',
|
||||
] as const;
|
||||
export type SharedQuestRewardTheme =
|
||||
(typeof QUEST_REWARD_THEMES)[number];
|
||||
|
||||
export const RUNTIME_ITEM_FUNCTIONAL_BIAS_VALUES = [
|
||||
'heal',
|
||||
'mana',
|
||||
'cooldown',
|
||||
'guard',
|
||||
'damage',
|
||||
] as const;
|
||||
export type SharedRuntimeItemFunctionalBias =
|
||||
(typeof RUNTIME_ITEM_FUNCTIONAL_BIAS_VALUES)[number];
|
||||
|
||||
export const RUNTIME_ITEM_TONE_VALUES = [
|
||||
'grim',
|
||||
'mysterious',
|
||||
'martial',
|
||||
'ritual',
|
||||
'survival',
|
||||
] as const;
|
||||
export type SharedRuntimeItemTone =
|
||||
(typeof RUNTIME_ITEM_TONE_VALUES)[number];
|
||||
|
||||
export type StoryRequestOptionsPayload = {
|
||||
availableOptions?: JsonObject[];
|
||||
optionCatalog?: JsonObject[];
|
||||
};
|
||||
|
||||
export type StoryRequestPayload<TWorldType extends string = string> = {
|
||||
worldType: TWorldType;
|
||||
character: JsonObject;
|
||||
monsters?: JsonObject[];
|
||||
history?: JsonObject[];
|
||||
choice?: string;
|
||||
context: JsonObject;
|
||||
requestOptions?: StoryRequestOptionsPayload;
|
||||
};
|
||||
|
||||
export type PlainTextPromptRequest = {
|
||||
systemPrompt: string;
|
||||
userPrompt: string;
|
||||
};
|
||||
|
||||
export type PlainTextResponse = {
|
||||
text: string;
|
||||
};
|
||||
|
||||
export type CharacterChatReplyRequest<
|
||||
TCharacter = unknown,
|
||||
TStoryMoment = unknown,
|
||||
TContext = unknown,
|
||||
TConversationTurn = unknown,
|
||||
TTargetStatus = unknown,
|
||||
> = {
|
||||
worldType: string;
|
||||
playerCharacter: TCharacter;
|
||||
targetCharacter: TCharacter;
|
||||
storyHistory: TStoryMoment[];
|
||||
context: TContext;
|
||||
conversationHistory: TConversationTurn[];
|
||||
conversationSummary: string;
|
||||
playerMessage: string;
|
||||
targetStatus: TTargetStatus;
|
||||
};
|
||||
|
||||
export type CharacterChatSuggestionsRequest<
|
||||
TCharacter = unknown,
|
||||
TStoryMoment = unknown,
|
||||
TContext = unknown,
|
||||
TConversationTurn = unknown,
|
||||
TTargetStatus = unknown,
|
||||
> = {
|
||||
worldType: string;
|
||||
playerCharacter: TCharacter;
|
||||
targetCharacter: TCharacter;
|
||||
storyHistory: TStoryMoment[];
|
||||
context: TContext;
|
||||
conversationHistory: TConversationTurn[];
|
||||
conversationSummary: string;
|
||||
targetStatus: TTargetStatus;
|
||||
};
|
||||
|
||||
export type CharacterChatSummaryRequest<
|
||||
TCharacter = unknown,
|
||||
TStoryMoment = unknown,
|
||||
TContext = unknown,
|
||||
TConversationTurn = unknown,
|
||||
TTargetStatus = unknown,
|
||||
> = {
|
||||
worldType: string;
|
||||
playerCharacter: TCharacter;
|
||||
targetCharacter: TCharacter;
|
||||
storyHistory: TStoryMoment[];
|
||||
context: TContext;
|
||||
conversationHistory: TConversationTurn[];
|
||||
previousSummary: string;
|
||||
targetStatus: TTargetStatus;
|
||||
};
|
||||
|
||||
export type NpcChatDialogueRequest<
|
||||
TCharacter = unknown,
|
||||
TEncounter = unknown,
|
||||
TMonster = unknown,
|
||||
TStoryMoment = unknown,
|
||||
TContext = unknown,
|
||||
> = {
|
||||
worldType: string;
|
||||
character: TCharacter;
|
||||
encounter: TEncounter;
|
||||
monsters: TMonster[];
|
||||
history: TStoryMoment[];
|
||||
context: TContext;
|
||||
topic: string;
|
||||
resultSummary: string;
|
||||
};
|
||||
|
||||
export type NpcRecruitDialogueRequest<
|
||||
TCharacter = unknown,
|
||||
TEncounter = unknown,
|
||||
TMonster = unknown,
|
||||
TStoryMoment = unknown,
|
||||
TContext = unknown,
|
||||
> = {
|
||||
worldType: string;
|
||||
character: TCharacter;
|
||||
encounter: TEncounter;
|
||||
monsters: TMonster[];
|
||||
history: TStoryMoment[];
|
||||
context: TContext;
|
||||
invitationText: string;
|
||||
recruitSummary: string;
|
||||
};
|
||||
|
||||
export type RuntimeItemIntentRequest<
|
||||
TContext = JsonObject,
|
||||
TPlan = JsonObject,
|
||||
> = {
|
||||
context: TContext;
|
||||
plans: TPlan[];
|
||||
};
|
||||
|
||||
export type RuntimeItemIntentResponse<TIntent = JsonObject> = {
|
||||
intents: TIntent[];
|
||||
};
|
||||
|
||||
export type QuestGenerationRequest<
|
||||
TState = JsonObject,
|
||||
TEncounter = JsonObject,
|
||||
> = {
|
||||
state: TState;
|
||||
encounter: TEncounter;
|
||||
};
|
||||
|
||||
export type RuntimeAction<
|
||||
TType extends string = string,
|
||||
TPayload = JsonObject,
|
||||
> = {
|
||||
type: TType;
|
||||
functionId?: string;
|
||||
targetId?: string;
|
||||
payload?: TPayload;
|
||||
};
|
||||
|
||||
export type RuntimeActionRequest<
|
||||
TAction extends RuntimeAction = RuntimeAction,
|
||||
> = {
|
||||
sessionId: string;
|
||||
clientVersion?: number;
|
||||
action: TAction;
|
||||
};
|
||||
|
||||
export type RuntimeActionResponse<
|
||||
TViewModel = JsonObject,
|
||||
TPresentation = JsonObject,
|
||||
TPatch = JsonObject,
|
||||
> = {
|
||||
sessionId: string;
|
||||
serverVersion: number;
|
||||
viewModel: TViewModel;
|
||||
presentation: TPresentation;
|
||||
patches: TPatch[];
|
||||
};
|
||||
|
||||
export const TASK5_RUNTIME_FUNCTION_IDS = [
|
||||
'story_continue_adventure',
|
||||
'story_opening_camp_dialogue',
|
||||
'camp_travel_home_scene',
|
||||
'idle_call_out',
|
||||
'idle_explore_forward',
|
||||
'idle_observe_signs',
|
||||
'idle_rest_focus',
|
||||
'idle_travel_next_scene',
|
||||
'battle_all_in_crush',
|
||||
'battle_escape_breakout',
|
||||
'battle_feint_step',
|
||||
'battle_finisher_window',
|
||||
'battle_guard_break',
|
||||
'battle_probe_pressure',
|
||||
'battle_recover_breath',
|
||||
'npc_chat',
|
||||
'npc_fight',
|
||||
'npc_help',
|
||||
'npc_leave',
|
||||
'npc_preview_talk',
|
||||
'npc_recruit',
|
||||
'npc_spar',
|
||||
] as const;
|
||||
export type Task5RuntimeFunctionId =
|
||||
(typeof TASK5_RUNTIME_FUNCTION_IDS)[number];
|
||||
|
||||
export const TASK6_RUNTIME_FUNCTION_IDS = [
|
||||
'equipment_equip',
|
||||
'equipment_unequip',
|
||||
'forge_craft',
|
||||
'forge_dismantle',
|
||||
'forge_reforge',
|
||||
'inventory_use',
|
||||
'npc_gift',
|
||||
'npc_quest_accept',
|
||||
'npc_quest_turn_in',
|
||||
'npc_trade',
|
||||
'treasure_inspect',
|
||||
'treasure_leave',
|
||||
'treasure_secure',
|
||||
] as const;
|
||||
export type Task6RuntimeFunctionId =
|
||||
(typeof TASK6_RUNTIME_FUNCTION_IDS)[number];
|
||||
|
||||
export const SERVER_RUNTIME_FUNCTION_IDS = [
|
||||
...TASK5_RUNTIME_FUNCTION_IDS,
|
||||
...TASK6_RUNTIME_FUNCTION_IDS,
|
||||
] as const;
|
||||
export type ServerRuntimeFunctionId =
|
||||
(typeof SERVER_RUNTIME_FUNCTION_IDS)[number];
|
||||
|
||||
export const TASK5_RUNTIME_OPTION_SCOPES = ['story', 'combat', 'npc'] as const;
|
||||
export type Task5RuntimeOptionScope =
|
||||
(typeof TASK5_RUNTIME_OPTION_SCOPES)[number];
|
||||
|
||||
export type RuntimeStoryChoicePayload = JsonObject & {
|
||||
optionText?: string;
|
||||
note?: string;
|
||||
};
|
||||
|
||||
export type RuntimeStoryChoiceAction = RuntimeAction<
|
||||
'story_choice',
|
||||
RuntimeStoryChoicePayload
|
||||
> & {
|
||||
functionId: string;
|
||||
targetId?: string;
|
||||
};
|
||||
|
||||
export type RuntimeStoryOptionView = {
|
||||
functionId: string;
|
||||
actionText: string;
|
||||
detailText?: string;
|
||||
scope: Task5RuntimeOptionScope;
|
||||
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<RuntimeStoryChoiceAction>;
|
||||
|
||||
export type RuntimeStoryActionResponse<
|
||||
TSnapshotGameState = JsonObject,
|
||||
TSnapshotCurrentStory = JsonObject,
|
||||
> = RuntimeActionResponse<
|
||||
RuntimeStoryViewModel,
|
||||
RuntimeStoryPresentation,
|
||||
RuntimeStoryPatch
|
||||
> & {
|
||||
snapshot: SavedGameSnapshot<
|
||||
TSnapshotGameState,
|
||||
string,
|
||||
TSnapshotCurrentStory
|
||||
>;
|
||||
};
|
||||
Reference in New Issue
Block a user