132 lines
4.3 KiB
TypeScript
132 lines
4.3 KiB
TypeScript
import { describe, expect, it } from 'vitest';
|
|
|
|
import {
|
|
type InventoryItem,
|
|
type RuntimeItemAiIntent,
|
|
type RuntimeItemGenerationContext,
|
|
type RuntimeItemPlan,
|
|
WorldType,
|
|
} from '../../types';
|
|
import {
|
|
buildCarrierNarrativeDescription,
|
|
buildCarrierNarrativeName,
|
|
buildRuntimeItemStoryFingerprint,
|
|
} from './carrierNarrativeCompiler';
|
|
|
|
function createContext(): RuntimeItemGenerationContext {
|
|
return {
|
|
worldType: WorldType.CUSTOM,
|
|
customWorldProfile: null,
|
|
sceneId: 'scene-1',
|
|
sceneName: '断桥旧哨',
|
|
sceneDescription: '旧哨火和断桥一起守着边城北口。',
|
|
sceneTags: ['断桥', '旧哨火'],
|
|
treasureHints: [],
|
|
encounter: null,
|
|
encounterNpcId: 'npc-1',
|
|
encounterNpcName: '梁砺',
|
|
encounterContextText: '巡守',
|
|
relatedNpcState: null,
|
|
relatedNpcNarrativeProfile: {
|
|
publicMask: '他只承认自己还在守桥。',
|
|
firstContactMask: '先别问旧案,桥口还不能放开。',
|
|
visibleLine: '他把全部注意力都压在桥口和来路上。',
|
|
hiddenLine: '他真正盯着的是封桥旧令背后的名字。',
|
|
contradiction: '嘴上只说守桥,提到名单时却会明显收紧语气。',
|
|
debtOrBurden: '封桥那夜的后果还压在他身上。',
|
|
taboo: '封桥令',
|
|
immediatePressure: '裂潮再逼桥口,他不敢轻易放人过去。',
|
|
relatedThreadIds: ['thread-1'],
|
|
relatedScarIds: ['scar-1'],
|
|
reactionHooks: ['名单', '旧哨火'],
|
|
},
|
|
relatedScene: {
|
|
id: 'scene-1',
|
|
name: '断桥旧哨',
|
|
description: '旧哨火和断桥一起守着边城北口。',
|
|
treasureHints: [],
|
|
},
|
|
recentStorySummary: '桥口的风声越来越不对。',
|
|
recentActions: ['你刚和巡守对上了第一轮试探。'],
|
|
activeThreadIds: ['thread-1'],
|
|
playerCharacterId: 'hero',
|
|
playerBuildTags: ['守御', '追击'],
|
|
playerBuildGaps: ['mana_gap'],
|
|
playerEquipmentTags: ['weapon'],
|
|
generationChannel: 'quest_reward',
|
|
};
|
|
}
|
|
|
|
describe('carrierNarrativeCompiler', () => {
|
|
it('builds fingerprint, patterned name, and layered description', () => {
|
|
const item: InventoryItem = {
|
|
id: 'runtime:test-item',
|
|
category: '稀有品',
|
|
name: '未命名秘物',
|
|
quantity: 1,
|
|
rarity: 'rare',
|
|
tags: ['relic'],
|
|
equipmentSlotId: 'relic',
|
|
runtimeMetadata: {
|
|
origin: 'ai_compiled',
|
|
generationChannel: 'quest_reward',
|
|
seedKey: 'seed',
|
|
sourceReason: '桥口的旧事把它重新推到了你眼前。',
|
|
},
|
|
};
|
|
const plan: RuntimeItemPlan = {
|
|
slot: 'primary',
|
|
itemKind: 'quest',
|
|
permanence: 'permanent',
|
|
narrativeWeight: 'heavy',
|
|
targetBuildDirection: ['守御', '追击'],
|
|
relationAnchor: {
|
|
type: 'npc',
|
|
npcId: 'npc-1',
|
|
npcName: '梁砺',
|
|
roleText: '巡守',
|
|
},
|
|
};
|
|
const intent: RuntimeItemAiIntent = {
|
|
shortNameSeed: '断桥',
|
|
sourcePhrase: '梁砺',
|
|
reasonToAppear: '桥口的旧事把它重新推到了你眼前。',
|
|
relationHooks: ['旧哨火'],
|
|
desiredBuildTags: ['守御', '追击'],
|
|
desiredFunctionalBias: ['guard'],
|
|
tone: 'ritual',
|
|
visibleClue: '桥口旧铜味一直留在它的边角。',
|
|
witnessMark: '它曾被人攥着在封桥夜里来回传递。',
|
|
unfinishedBusiness: '它为什么会在封桥之后一直没有被交回去?',
|
|
hiddenHook: '有人故意让它留在旧哨火旁。',
|
|
reactionHooks: ['名单', '封桥令'],
|
|
namingPattern: 'forbidden_object',
|
|
};
|
|
|
|
const fingerprint = buildRuntimeItemStoryFingerprint({
|
|
context: createContext(),
|
|
plan,
|
|
intent,
|
|
});
|
|
const name = buildCarrierNarrativeName({
|
|
item,
|
|
context: createContext(),
|
|
plan,
|
|
intent,
|
|
});
|
|
const description = buildCarrierNarrativeDescription({
|
|
item,
|
|
context: createContext(),
|
|
plan,
|
|
intent,
|
|
});
|
|
|
|
expect(fingerprint.visibleClue).toContain('桥口');
|
|
expect(name).toContain('封桥令'.slice(0, 2));
|
|
expect(name).toContain('封痕');
|
|
expect(description).toContain('它和');
|
|
expect(description).toContain('如今它会在断桥旧哨出现');
|
|
expect(description).toContain('适合当前局势里的临场构筑调整');
|
|
});
|
|
});
|