Files
Genarrative/src/services/customWorldReferenceSignals.test.ts

210 lines
6.5 KiB
TypeScript

import { describe, expect, it } from 'vitest';
import { type CustomWorldProfile, WorldType } from '../types';
import {
collectCreatureArchetypeSignals,
collectSceneBucketSignalKeywords,
resolveCreatureArchetypeForSource,
resolveRoleTemplateCharacterIdFromReferenceProfile,
resolveSceneBucketForLandmark,
} from './customWorldReferenceSignals';
function buildReferenceProfileHarness() {
return {
id: 'reference-harness',
settingText: '围绕裂界港区、断桥前线与工业旧站展开的世界。',
name: '裂桥港区',
subtitle: '前线潮压',
summary: '断桥、港区和旧站之间的战线不断回响。',
tone: '高压、潮湿、迟滞',
playerGoal: '查清断桥封锁与旧站事故背后的真相',
templateWorldType: WorldType.WUXIA,
majorFactions: [],
coreConflicts: [],
attributeSchema: {
id: 'schema:test',
worldId: 'custom:test',
schemaVersion: 1,
generatedFrom: {
worldType: WorldType.CUSTOM,
worldName: '裂桥港区',
settingSummary: '断桥前线',
tone: '高压',
conflictCore: '旧站事故',
},
slots: [],
},
playableNpcs: [],
storyNpcs: [],
items: [],
landmarks: [],
ownedSettingLayers: {
semanticAnchor: {
genreSignals: ['裂界边境'],
conflictForms: ['追查失线'],
institutionTypes: ['前哨'],
tabooTypes: ['封桥令'],
carrierTypes: ['界核'],
forceSystemTypes: ['裂界'],
atmosphereTags: ['高压'],
},
ruleProfile: {
attributeSchema: {
id: 'schema:test',
worldId: 'custom:test',
schemaVersion: 1,
generatedFrom: {
worldType: WorldType.CUSTOM,
worldName: '裂桥港区',
settingSummary: '断桥前线',
tone: '高压',
conflictCore: '旧站事故',
},
slots: [],
},
resourceLabels: {
hp: '界命',
mp: '裂能',
maxHp: '界命上限',
maxMp: '裂能上限',
damage: '界势',
guard: '稳界',
range: '界距',
cooldown: '复界',
manaCost: '裂能消耗',
currency: '边贸券',
},
economyProfile: {
initialCurrency: 160,
},
},
expressionProfile: {
themePack: {
id: 'theme:test',
displayName: '裂桥前线',
toneRange: ['高压'],
institutionLexicon: ['前哨'],
tabooLexicon: ['封桥令'],
artifactClasses: ['界核'],
actorArchetypes: ['边巡者'],
conflictForms: ['追查失线'],
clueForms: ['裂痕'],
namingPatterns: ['前哨+旧痕+器类'],
revealStyles: ['证词错位'],
},
presentationTone: ['高压'],
namingDirectives: ['前哨+旧痕+器类'],
clueDirectives: ['裂痕'],
revealDirectives: ['证词错位'],
},
referenceProfile: {
roleArchetypes: [
{
id: 'role-1',
label: '远程压制型',
combatFocus: '依靠弓与远程火力持续压制。',
narrativeFunction: '为队伍提供远程压制与侦查。',
sourceRoleIds: [],
sourceTemplateCharacterIds: [],
tags: ['远程', '射击'],
},
],
sceneBuckets: [
{
id: 'scene-1',
label: '工业热区',
moodTags: ['高压'],
keywords: ['旧站', '工坊'],
referenceLandmarkIds: ['landmark-industrial'],
},
{
id: 'scene-2',
label: '临水渡口区',
moodTags: ['潮湿'],
keywords: ['港区', '渡桥'],
referenceLandmarkIds: ['landmark-harbor'],
},
],
creatureArchetypes: [
{
id: 'creature-1',
label: '机关守卫体',
threatStyle: '围绕节点和装置进行守线压制。',
keywords: ['机关', '守卫', '旧站'],
},
{
id: 'creature-2',
label: '远程威胁者',
threatStyle: '依靠远程投射和凝视压制走位。',
keywords: ['远程', '压制', '索敌'],
},
],
},
compatibilityProfile: {
legacyTemplateWorldType: WorldType.WUXIA,
migrationVersion: 'test',
},
},
themePack: null,
storyGraph: null,
creatorIntent: null,
anchorPack: null,
lockState: null,
generationMode: 'full',
generationStatus: 'complete',
} satisfies CustomWorldProfile;
}
describe('customWorldReferenceSignals', () => {
it('resolves scene buckets by explicit landmark ownership', () => {
const profile = buildReferenceProfileHarness();
const bucket = resolveSceneBucketForLandmark(profile, {
id: 'landmark-industrial',
name: '旧站锅炉层',
description: '轨道和锅炉残响仍卡在热区深处。',
});
expect(bucket?.label).toBe('工业热区');
expect(collectSceneBucketSignalKeywords(bucket!).includes('工坊')).toBe(true);
});
it('resolves creature archetypes and exposes combat/habitat signal tags', () => {
const profile = buildReferenceProfileHarness();
const archetype = resolveCreatureArchetypeForSource(profile, {
name: '旧站守卫傀',
role: '节点守卫',
description: '围绕工坊旧站守线,遇敌后会启动压制炮座。',
combatStyle: '守住节点后用远程火力封锁通路。',
tags: ['机关', '旧站', '守卫'],
});
const signals = collectCreatureArchetypeSignals(archetype!);
expect(archetype?.label).toBe('机关守卫体');
expect(signals.combatTags).toContain('守御');
expect(signals.habitatTags).toContain('工场');
});
it('maps role archetypes back to suitable preset character templates', () => {
const profile = buildReferenceProfileHarness();
const templateCharacterId = resolveRoleTemplateCharacterIdFromReferenceProfile(
profile,
{
id: 'story-role-1',
name: '雾港狙巡手',
title: '岸线压制者',
role: '远程巡手',
description: '负责在港区高点做远程掩护与索敌压制。',
personality: '冷静,开火前总先确认潮向。',
combatStyle: '高点远程压制,必要时转为游击拉扯。',
tags: ['远程', '射击', '港区'],
},
);
expect(templateCharacterId).toBe('archer-hero');
});
});