141 lines
4.0 KiB
TypeScript
141 lines
4.0 KiB
TypeScript
import type {
|
|
RuntimeStoryActionRequest,
|
|
RuntimeStoryPatch,
|
|
} from '../../../../packages/shared/src/contracts/rpgRuntimeStoryState.js';
|
|
import { conflict, invalidRequest } from '../../errors.js';
|
|
import {
|
|
addInventoryItems,
|
|
appendStoryEngineCarrierMemory,
|
|
} from '../../bridges/legacyNpcTask6Bridge.js';
|
|
import {
|
|
buildTreasureResultText,
|
|
resolveTreasureReward,
|
|
} from '../../bridges/legacyTreasureRuntimeBridge.js';
|
|
import { buildBuildToast } from '../inventory/inventoryStoryActionService.js';
|
|
import {
|
|
replaceRuntimeSessionRawGameState,
|
|
} from '../rpg-runtime-story/RpgRuntimeSnapshotSync.js';
|
|
import type { RuntimeSession } from '../rpg-runtime-story/RpgRuntimeSessionLoader.js';
|
|
|
|
const SUPPORTED_TREASURE_STORY_FUNCTION_IDS = new Set<string>([
|
|
'treasure_inspect',
|
|
'treasure_leave',
|
|
'treasure_secure',
|
|
]);
|
|
|
|
type TreasureStoryResolution = {
|
|
actionText: string;
|
|
resultText: string;
|
|
patches: RuntimeStoryPatch[];
|
|
toast?: string | null;
|
|
};
|
|
|
|
type JsonRecord = Record<string, unknown>;
|
|
type RuntimeGameState = Parameters<typeof resolveTreasureReward>[0];
|
|
type RuntimeEncounter = Parameters<typeof resolveTreasureReward>[1];
|
|
|
|
function resolveTreasureAction(functionId: string) {
|
|
switch (functionId) {
|
|
case 'treasure_secure':
|
|
return 'secure';
|
|
case 'treasure_inspect':
|
|
return 'inspect';
|
|
case 'treasure_leave':
|
|
return 'leave';
|
|
default:
|
|
throw invalidRequest(`暂不支持的 Treasure 动作:${functionId}`);
|
|
}
|
|
}
|
|
|
|
function getTreasureEncounter(
|
|
session: RuntimeSession,
|
|
state: RuntimeGameState,
|
|
): RuntimeEncounter | null {
|
|
const rawEncounter = state.currentEncounter;
|
|
if (!rawEncounter || rawEncounter.kind !== 'treasure') {
|
|
return null;
|
|
}
|
|
|
|
return {
|
|
npcAvatar: '',
|
|
hostile: false,
|
|
...rawEncounter,
|
|
id: rawEncounter.id ?? session.currentEncounter?.id ?? rawEncounter.npcName,
|
|
} satisfies RuntimeEncounter;
|
|
}
|
|
|
|
export function isSupportedTreasureStoryFunctionId(functionId: string) {
|
|
return SUPPORTED_TREASURE_STORY_FUNCTION_IDS.has(functionId);
|
|
}
|
|
|
|
export function resolveTreasureStoryAction(
|
|
session: RuntimeSession,
|
|
request: RuntimeStoryActionRequest,
|
|
): TreasureStoryResolution {
|
|
const state = session.rawGameState as unknown as RuntimeGameState;
|
|
const encounter = getTreasureEncounter(session, state);
|
|
if (!encounter) {
|
|
throw conflict('当前没有可结算的宝藏遭遇。');
|
|
}
|
|
|
|
const action = resolveTreasureAction(request.action.functionId);
|
|
const reward =
|
|
action === 'leave' ? null : resolveTreasureReward(state, encounter, action);
|
|
|
|
let nextState = {
|
|
...state,
|
|
currentEncounter: null,
|
|
npcInteractionActive: false,
|
|
sceneHostileNpcs: [],
|
|
playerX: 0,
|
|
playerFacing: 'right' as const,
|
|
animationState: state.animationState,
|
|
scrollWorld: false,
|
|
inBattle: false,
|
|
playerHp: reward
|
|
? Math.min(state.playerMaxHp, state.playerHp + reward.hp)
|
|
: state.playerHp,
|
|
playerMana: reward
|
|
? Math.min(state.playerMaxMana, state.playerMana + reward.mana)
|
|
: state.playerMana,
|
|
playerCurrency: reward
|
|
? state.playerCurrency + reward.currency
|
|
: state.playerCurrency,
|
|
playerInventory: reward
|
|
? addInventoryItems(state.playerInventory, reward.items)
|
|
: state.playerInventory,
|
|
currentBattleNpcId: null,
|
|
currentNpcBattleMode: null,
|
|
currentNpcBattleOutcome: null,
|
|
sparReturnEncounter: null,
|
|
sparPlayerHpBefore: null,
|
|
sparPlayerMaxHpBefore: null,
|
|
sparStoryHistoryBefore: null,
|
|
} satisfies RuntimeGameState;
|
|
if (reward) {
|
|
nextState = appendStoryEngineCarrierMemory(nextState, reward.items);
|
|
}
|
|
|
|
replaceRuntimeSessionRawGameState(
|
|
session,
|
|
nextState as unknown as JsonRecord,
|
|
);
|
|
|
|
return {
|
|
actionText:
|
|
action === 'leave'
|
|
? '先记下位置'
|
|
: action === 'inspect'
|
|
? '仔细检查'
|
|
: '直接收取',
|
|
resultText: buildTreasureResultText(
|
|
encounter,
|
|
action,
|
|
reward ?? undefined,
|
|
state.worldType,
|
|
),
|
|
patches: [],
|
|
toast: reward ? buildBuildToast(nextState) : null,
|
|
};
|
|
}
|