import { afterEach, describe, expect, it } from 'vitest'; import { buildExpandedCustomWorldProfile } from '../services/customWorldBuilder'; import { WorldType } from '../types'; import { setRuntimeCustomWorldProfile } from './customWorldRuntime'; import { buildEncounterFromSceneNpc, getScenePresetsByWorld } from './scenePresets'; function createPlayableNpc(index: number) { return { name: `可扮演角色${index + 1}`, title: `可扮演头衔${index + 1}`, role: `可扮演身份${index + 1}`, description: `可扮演角色描述${index + 1}`, backstory: `可扮演角色背景${index + 1}`, personality: `可扮演角色性格${index + 1}`, motivation: `可扮演角色动机${index + 1}`, combatStyle: `可扮演角色战斗风格${index + 1}`, initialAffinity: 18, relationshipHooks: [`切入口${index + 1}`], tags: [`标签${index + 1}`], backstoryReveal: { publicSummary: `公开背景${index + 1}`, chapters: [ { id: `surface-${index + 1}`, title: '表层来意', affinityRequired: 10, teaser: `提示${index + 1}-1`, content: `内容${index + 1}-1`, contextSnippet: `摘要${index + 1}-1`, }, { id: `scar-${index + 1}`, title: '旧事裂痕', affinityRequired: 30, teaser: `提示${index + 1}-2`, content: `内容${index + 1}-2`, contextSnippet: `摘要${index + 1}-2`, }, { id: `hidden-${index + 1}`, title: '隐藏执念', affinityRequired: 55, teaser: `提示${index + 1}-3`, content: `内容${index + 1}-3`, contextSnippet: `摘要${index + 1}-3`, }, { id: `final-${index + 1}`, title: '最终底牌', affinityRequired: 80, teaser: `提示${index + 1}-4`, content: `内容${index + 1}-4`, contextSnippet: `摘要${index + 1}-4`, }, ], }, skills: [ { name: `技能${index + 1}-1`, summary: '技能摘要1', style: '起手压制' }, { name: `技能${index + 1}-2`, summary: '技能摘要2', style: '机动周旋' }, { name: `技能${index + 1}-3`, summary: '技能摘要3', style: '爆发终结' }, ], initialItems: [ { name: `物品${index + 1}-1`, category: '武器', quantity: 1, rarity: 'rare', description: '物品描述1', tags: ['物品标签1'], }, { name: `物品${index + 1}-2`, category: '消耗品', quantity: 2, rarity: 'uncommon', description: '物品描述2', tags: ['物品标签2'], }, { name: `物品${index + 1}-3`, category: '专属物品', quantity: 1, rarity: 'rare', description: '物品描述3', tags: ['物品标签3'], }, ], }; } describe('scenePresets custom world npc mapping', () => { afterEach(() => { setRuntimeCustomWorldProfile(null); }); it('preserves custom world npc dossier fields into scene npcs and encounters', () => { const profile = buildExpandedCustomWorldProfile( { name: '雾潮世界', subtitle: '潮雾未散', summary: '一座围绕码头、断桥和旧潮路展开的自定义世界。', tone: '克制、潮湿、危险', playerGoal: '查清雾潮里失踪的人和桥下的旧案。', templateWorldType: 'WUXIA', playableNpcs: Array.from({ length: 5 }, (_, index) => createPlayableNpc(index), ), storyNpcs: [ { ...createPlayableNpc(10), name: '沈雾', title: '潮路领航人', role: '码头向导', description: '熟悉潮路和暗栈的旧向导。', backstory: '少年时曾在断桥坠潮夜里失去整队同伴。', personality: '谨慎冷静,先观察再表态。', motivation: '想把雾潮深处那条失踪航线重新找出来。', combatStyle: '短刀试探后再借地形逼近。', relationshipHooks: ['潮路', '断桥旧案'], tags: ['码头', '旧潮路'], imageSrc: '/custom/npcs/shenwu.png', }, { ...createPlayableNpc(11), name: '陆沉', title: '断桥守更', role: '守桥人', description: '夜里守着断桥口的旧灯火。', }, { ...createPlayableNpc(12), name: '顾潮', title: '潮册记录员', role: '记录员', description: '在潮账房里整理各路失踪名单。', }, ], landmarks: [ { name: '雾潮码头', description: '旧船桩和潮雾把视线切成断续的几段。', sceneNpcNames: ['沈雾', '陆沉', '顾潮'], connections: [ { targetLandmarkName: '断桥旧道', relativePosition: 'north', summary: '顺着潮路向北可抵断桥。', }, ], }, { name: '断桥旧道', description: '半塌的桥面上还挂着旧索和残旗。', sceneNpcNames: ['沈雾', '陆沉', '顾潮'], connections: [ { targetLandmarkName: '雾潮码头', relativePosition: 'south', summary: '沿旧潮路南返能回码头。', }, ], }, ], }, '玩家想要一个围绕雾潮码头与断桥旧案展开的世界。', ); setRuntimeCustomWorldProfile(profile); const scene = getScenePresetsByWorld(WorldType.CUSTOM).find( (entry) => entry.name === '雾潮码头', ); const npc = scene?.npcs?.find((entry) => entry.name === '沈雾'); expect(scene).toBeTruthy(); expect(npc).toBeTruthy(); expect(npc?.characterId).toBe(npc?.id); expect(npc?.title).toBe('潮路领航人'); expect(npc?.backstory).toContain('断桥坠潮夜'); expect(npc?.personality).toContain('谨慎冷静'); expect(npc?.motivation).toContain('失踪航线'); expect(npc?.skills).toHaveLength(3); expect(npc?.initialItems).toHaveLength(3); expect(npc?.avatar).toBe('/custom/npcs/shenwu.png'); const encounter = buildEncounterFromSceneNpc(npc!); expect(encounter.title).toBe('潮路领航人'); expect(encounter.backstoryReveal?.publicSummary).toBe('公开背景11'); expect(encounter.skills?.[0]?.name).toBe('技能11-1'); expect(encounter.initialItems?.[0]?.name).toBe('物品11-1'); expect(encounter.imageSrc).toBe('/custom/npcs/shenwu.png'); }); });