210 lines
6.5 KiB
TypeScript
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');
|
|
});
|
|
});
|