Files
Genarrative/src/data/runtimeItemDirector.test.ts
kdletters cbc27bad4a
Some checks failed
CI / verify (push) Has been cancelled
init with react+axum+spacetimedb
2026-04-26 18:06:23 +08:00

124 lines
4.1 KiB
TypeScript

import {describe, expect, it} from 'vitest';
import {WorldType} from '../types';
import {addInventoryItems} from './npcInteractions';
import {
buildLooseRuntimeItemGenerationContext,
buildQuestRuntimeItemGenerationContext,
} from './runtimeItemContext';
import {buildDirectedRuntimeReward, buildRuntimeInventoryStock} from './runtimeItemDirector';
describe('runtime item director', () => {
it('builds treasure rewards with runtime metadata and relation anchors', () => {
const context = buildLooseRuntimeItemGenerationContext({
worldType: WorldType.WUXIA,
scene: {
id: 'scene-ruins',
name: '断碑古道',
description: '碎碑与旧誓散落在路旁。',
treasureHints: ['残匣', '旧祭火'],
},
encounter: {
id: 'treasure-altar',
kind: 'treasure',
npcName: '断誓秘匣',
npcDescription: '匣盖上留着未熄的旧印。',
npcAvatar: '',
context: '古道祭坛',
},
playerCharacterId: 'hero',
playerBuildTags: ['快剑', '追击'],
generationChannel: 'treasure',
});
const reward = buildDirectedRuntimeReward(context, {
seedKey: 'test:treasure',
fixedKinds: ['relic', 'consumable'],
fixedPermanence: ['permanent', 'timed'],
itemCount: 2,
});
expect(reward.primaryItem?.runtimeMetadata?.generationChannel).toBe('treasure');
expect(reward.primaryItem?.runtimeMetadata?.relationAnchor?.type).toBe('npc');
expect(reward.primaryItem?.name).not.toBe('未命名秘物');
expect(reward.primaryItem?.runtimeMetadata?.storyFingerprint?.visibleClue).toBeTruthy();
expect(reward.primaryItem?.runtimeMetadata?.storyFingerprint?.unresolvedQuestion).toBeTruthy();
expect(reward.primaryItem?.description).toContain('适合当前局势里的临场构筑调整');
});
it('keeps identity-sensitive runtime items separate when adding inventory', () => {
const baseItem = buildRuntimeInventoryStock(
buildLooseRuntimeItemGenerationContext({
worldType: WorldType.WUXIA,
encounter: {
id: 'npc-blackmarket',
kind: 'npc',
npcName: '黑市牙人',
npcDescription: '在阴影里兜售消息与暗器。',
npcAvatar: '',
context: '黑市',
},
playerCharacterId: 'hero',
playerBuildTags: ['快袭', '风行'],
generationChannel: 'npc_trade',
}),
{
seedKey: 'test:stock',
itemCount: 1,
fixedKinds: ['relic'],
fixedPermanence: ['permanent'],
},
)[0];
const secondItem = {
...baseItem,
id: `${baseItem.id}:variant`,
runtimeMetadata: baseItem.runtimeMetadata
? {
...baseItem.runtimeMetadata,
seedKey: `${baseItem.runtimeMetadata.seedKey}:variant`,
}
: null,
};
const merged = addInventoryItems([], [baseItem, secondItem]);
expect(merged).toHaveLength(2);
});
it('can build quest-flavored runtime rewards from quest context', () => {
const context = buildQuestRuntimeItemGenerationContext({
context: {
worldType: WorldType.XIANXIA,
currentSceneId: 'scene-cloud',
currentSceneName: '云阙旧渡',
currentSceneDescription: '旧渡口残留着灵潮和巡守痕迹。',
issuerNpcId: 'npc-issuer',
issuerNpcName: '巡守使',
issuerNpcContext: '巡守',
issuerAffinity: 24,
recentStoryMoments: [],
playerCharacter: null,
},
issuerNpcId: 'npc-issuer',
issuerNpcName: '巡守使',
roleText: '巡守',
scene: {
id: 'scene-cloud',
name: '云阙旧渡',
description: '旧渡口残留着灵潮和巡守痕迹。',
treasureHints: ['旧印'],
},
});
const reward = buildDirectedRuntimeReward(context, {
seedKey: 'test:quest',
fixedKinds: ['equipment', 'consumable'],
fixedPermanence: ['permanent', 'timed'],
itemCount: 2,
});
expect(reward.primaryItem?.runtimeMetadata?.generationChannel).toBe('quest_reward');
expect(reward.supportItems.length).toBeGreaterThanOrEqual(1);
});
});