166 lines
5.0 KiB
TypeScript
166 lines
5.0 KiB
TypeScript
import {
|
|
AnimationState,
|
|
type Encounter,
|
|
type GameState,
|
|
type StoryMoment,
|
|
type StoryOption,
|
|
type TreasureInteractionAction,
|
|
} from '../types';
|
|
import { formatCurrency } from './economy';
|
|
import {
|
|
TREASURE_INSPECT_FUNCTION,
|
|
TREASURE_LEAVE_FUNCTION,
|
|
TREASURE_SECURE_FUNCTION,
|
|
} from './functionCatalog';
|
|
import { buildRuntimeItemGenerationContext } from './runtimeItemContext';
|
|
import { buildDirectedRuntimeReward } from './runtimeItemDirector';
|
|
import { flattenDirectedRuntimeRewardItems } from './runtimeItemNarrative';
|
|
|
|
export type TreasureReward = {
|
|
items: ReturnType<typeof flattenDirectedRuntimeRewardItems>;
|
|
hp: number;
|
|
mana: number;
|
|
currency: number;
|
|
storyHint?: string;
|
|
};
|
|
|
|
function buildTreasureOption(
|
|
functionId: string,
|
|
actionText: string,
|
|
detailText: string,
|
|
action: TreasureInteractionAction,
|
|
): StoryOption {
|
|
return {
|
|
functionId,
|
|
actionText,
|
|
detailText,
|
|
visuals: {
|
|
playerAnimation: AnimationState.IDLE,
|
|
playerMoveMeters: 0,
|
|
playerOffsetY: 0,
|
|
playerFacing: 'right',
|
|
scrollWorld: false,
|
|
monsterChanges: [],
|
|
},
|
|
interaction: {
|
|
kind: 'treasure',
|
|
action,
|
|
},
|
|
} as StoryOption;
|
|
}
|
|
|
|
function buildTreasureReward(
|
|
state: GameState,
|
|
encounter: Encounter,
|
|
action: TreasureInteractionAction,
|
|
): TreasureReward {
|
|
const context = buildRuntimeItemGenerationContext({
|
|
state,
|
|
generationChannel: 'treasure',
|
|
encounter,
|
|
});
|
|
const directed = buildDirectedRuntimeReward(context, {
|
|
seedKey: `treasure:${encounter.id ?? encounter.npcName}:${action}`,
|
|
variant: action,
|
|
itemCount: action === 'inspect' ? 2 : 2,
|
|
fixedKinds:
|
|
action === 'inspect' ? ['relic', 'consumable'] : ['relic', 'material'],
|
|
fixedPermanence:
|
|
action === 'inspect' ? ['permanent', 'timed'] : ['permanent', 'resource'],
|
|
baseHp: action === 'inspect' ? 10 : 0,
|
|
baseMana: action === 'inspect' ? 12 : 0,
|
|
baseCurrency:
|
|
action === 'inspect'
|
|
? state.worldType === 'XIANXIA'
|
|
? 34
|
|
: 48
|
|
: state.worldType === 'XIANXIA'
|
|
? 22
|
|
: 30,
|
|
storyHint: `${encounter.npcName}里藏着与你当前构筑和现场线索贴合的战利品。`,
|
|
});
|
|
|
|
return {
|
|
items: flattenDirectedRuntimeRewardItems(directed),
|
|
hp: directed.hp ?? 0,
|
|
mana: directed.mana ?? 0,
|
|
currency: directed.currency ?? 0,
|
|
storyHint: directed.storyHint,
|
|
};
|
|
}
|
|
|
|
export function buildTreasureEncounterStoryMoment({
|
|
state,
|
|
encounter,
|
|
overrideText,
|
|
}: {
|
|
state: GameState;
|
|
encounter: Encounter;
|
|
overrideText?: string;
|
|
}): StoryMoment {
|
|
const secureReward = buildTreasureReward(state, encounter, 'secure');
|
|
const inspectReward = buildTreasureReward(state, encounter, 'inspect');
|
|
|
|
return {
|
|
text:
|
|
overrideText ??
|
|
`你在 ${encounter.npcName} 前停下脚步。${encounter.npcDescription} 它看起来并非随意遗落,而像是被刻意留在这里。`,
|
|
options: [
|
|
buildTreasureOption(
|
|
TREASURE_SECURE_FUNCTION.id,
|
|
TREASURE_SECURE_FUNCTION.title,
|
|
`直接带走 ${secureReward.items.map((item) => item.name).join('、')},并获得 ${formatCurrency(secureReward.currency, state.worldType)}。`,
|
|
'secure',
|
|
),
|
|
buildTreasureOption(
|
|
TREASURE_INSPECT_FUNCTION.id,
|
|
TREASURE_INSPECT_FUNCTION.title,
|
|
`多花些时间搜查,可额外拿到 ${inspectReward.items.map((item) => item.name).join('、')}、${formatCurrency(inspectReward.currency, state.worldType)} 与恢复收益。`,
|
|
'inspect',
|
|
),
|
|
buildTreasureOption(
|
|
TREASURE_LEAVE_FUNCTION.id,
|
|
TREASURE_LEAVE_FUNCTION.title,
|
|
`先把 ${encounter.npcName} 的位置和迹象记住,暂时不动它。`,
|
|
'leave',
|
|
),
|
|
],
|
|
};
|
|
}
|
|
|
|
export function resolveTreasureReward(
|
|
state: GameState,
|
|
encounter: Encounter,
|
|
action: TreasureInteractionAction,
|
|
) {
|
|
return buildTreasureReward(state, encounter, action);
|
|
}
|
|
|
|
export function buildTreasureResultText(
|
|
encounter: Encounter,
|
|
action: TreasureInteractionAction,
|
|
reward?: TreasureReward,
|
|
) {
|
|
if (action === 'leave') {
|
|
return `你暂时没有触碰 ${encounter.npcName},只是把它的异常位置和痕迹牢牢记下。`;
|
|
}
|
|
|
|
const itemText = reward?.items.length
|
|
? reward.items.map((item) => item.name).join('、')
|
|
: '零散战利品';
|
|
const restoreParts = [
|
|
(reward?.hp ?? 0) > 0 ? `气血 +${reward?.hp ?? 0}` : null,
|
|
(reward?.mana ?? 0) > 0 ? `灵力 +${reward?.mana ?? 0}` : null,
|
|
].filter(Boolean);
|
|
const restoreText =
|
|
restoreParts.length > 0 ? `,并恢复 ${restoreParts.join('、')}` : '';
|
|
const currencyText = reward ? `,另得 ${reward.currency} 钱币` : '';
|
|
const storyHint = reward?.storyHint ? ` ${reward.storyHint}` : '';
|
|
|
|
if (action === 'inspect') {
|
|
return `你仔细检查了 ${encounter.npcName},顺着现场痕迹拆开机关与伪装,最终收回 ${itemText}${currencyText}${restoreText}。${storyHint}`;
|
|
}
|
|
|
|
return `你迅速收下了 ${encounter.npcName} 中最关键的收获:${itemText}${currencyText}。${storyHint}`;
|
|
}
|