This commit is contained in:
229
src/services/customWorldFoundationEntries.ts
Normal file
229
src/services/customWorldFoundationEntries.ts
Normal file
@@ -0,0 +1,229 @@
|
||||
import type { EightAnchorContent } from '../../packages/shared/src/contracts/customWorldAgent';
|
||||
import { normalizeCustomWorldCreatorIntent } from './customWorldCreatorIntent';
|
||||
import type { CustomWorldProfile } from '../types';
|
||||
|
||||
export type CustomWorldFoundationEntryId =
|
||||
| 'world-promise'
|
||||
| 'player-fantasy'
|
||||
| 'theme-boundary'
|
||||
| 'player-entry-point'
|
||||
| 'core-conflict'
|
||||
| 'key-relationships'
|
||||
| 'hidden-lines'
|
||||
| 'iconic-elements';
|
||||
|
||||
export type CustomWorldFoundationEntry = {
|
||||
id: CustomWorldFoundationEntryId;
|
||||
label: string;
|
||||
value: string;
|
||||
};
|
||||
|
||||
export function compactFoundationTextList(
|
||||
values: Array<string | null | undefined>,
|
||||
) {
|
||||
return values.map((value) => value?.trim()).filter(Boolean) as string[];
|
||||
}
|
||||
|
||||
export function parseFoundationTagText(value: string) {
|
||||
return value
|
||||
.split(/[;;]/u)
|
||||
.map((item) => item.trim())
|
||||
.filter(Boolean);
|
||||
}
|
||||
|
||||
function toText(value: unknown) {
|
||||
return typeof value === 'string' ? value.trim() : '';
|
||||
}
|
||||
|
||||
function toRecord(value: unknown) {
|
||||
return value && typeof value === 'object' && !Array.isArray(value)
|
||||
? (value as Record<string, unknown>)
|
||||
: null;
|
||||
}
|
||||
|
||||
function compactAnchorValue(value: unknown): string | null {
|
||||
const text = toText(value);
|
||||
if (text) {
|
||||
return text;
|
||||
}
|
||||
if (Array.isArray(value)) {
|
||||
const compacted = compactFoundationTextList(
|
||||
value.map((item) => compactAnchorValue(item)),
|
||||
).join(';');
|
||||
return compacted || null;
|
||||
}
|
||||
const record = toRecord(value);
|
||||
if (record) {
|
||||
const compacted = compactFoundationTextList(
|
||||
Object.values(record).map((item) => compactAnchorValue(item)),
|
||||
).join(';');
|
||||
return compacted || null;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
function buildRelationshipSeedText(value: unknown) {
|
||||
const record = toRecord(value);
|
||||
if (!record) {
|
||||
return '';
|
||||
}
|
||||
|
||||
return compactFoundationTextList([
|
||||
toText(record.name),
|
||||
toText(record.role),
|
||||
toText(record.relationToPlayer)
|
||||
? `与玩家:${toText(record.relationToPlayer)}`
|
||||
: '',
|
||||
toText(record.hiddenHook) ? `代价/暗线:${toText(record.hiddenHook)}` : '',
|
||||
]).join(';');
|
||||
}
|
||||
|
||||
function buildAnchorContentFromProfileFallback(
|
||||
profile: CustomWorldProfile,
|
||||
): EightAnchorContent {
|
||||
const creatorIntent = normalizeCustomWorldCreatorIntent(profile.creatorIntent);
|
||||
const relationshipSeed = creatorIntent?.keyCharacters[0] ?? null;
|
||||
|
||||
return {
|
||||
worldPromise: compactFoundationTextList([
|
||||
creatorIntent?.worldHook ||
|
||||
profile.anchorPack?.worldSummary ||
|
||||
profile.summary,
|
||||
profile.subtitle || profile.settingText,
|
||||
creatorIntent?.toneDirectives.join('、') || profile.tone,
|
||||
]).join(';') || null,
|
||||
playerFantasy: compactFoundationTextList([
|
||||
creatorIntent?.playerPremise || profile.playerGoal,
|
||||
profile.playerGoal,
|
||||
relationshipSeed?.hiddenHook ||
|
||||
creatorIntent?.coreConflicts[0] ||
|
||||
profile.coreConflicts[0] ||
|
||||
'',
|
||||
]).join(';') || null,
|
||||
themeBoundary: compactFoundationTextList([
|
||||
creatorIntent?.themeKeywords.join('、') || '',
|
||||
creatorIntent?.toneDirectives.join('、') || '',
|
||||
profile.tone,
|
||||
profile.subtitle,
|
||||
creatorIntent?.forbiddenDirectives.length
|
||||
? `避免:${creatorIntent.forbiddenDirectives.join('、')}`
|
||||
: '',
|
||||
]).join(';') || null,
|
||||
playerEntryPoint: compactFoundationTextList([
|
||||
creatorIntent?.playerPremise || '',
|
||||
creatorIntent?.openingSituation || profile.coreConflicts[0] || '',
|
||||
profile.playerGoal,
|
||||
]).join(';') || null,
|
||||
coreConflict: compactFoundationTextList([
|
||||
(creatorIntent?.coreConflicts.length
|
||||
? creatorIntent.coreConflicts
|
||||
: profile.coreConflicts
|
||||
).join('、'),
|
||||
relationshipSeed?.hiddenHook || profile.summary || profile.settingText,
|
||||
creatorIntent?.openingSituation || profile.coreConflicts[0] || profile.playerGoal,
|
||||
]).join(';') || null,
|
||||
keyRelationships: relationshipSeed
|
||||
? compactFoundationTextList([
|
||||
relationshipSeed.name,
|
||||
relationshipSeed.role,
|
||||
relationshipSeed.relationToPlayer,
|
||||
relationshipSeed.hiddenHook ? `代价/秘密:${relationshipSeed.hiddenHook}` : '',
|
||||
]).join(';')
|
||||
: null,
|
||||
hiddenLines: compactFoundationTextList([
|
||||
relationshipSeed?.hiddenHook || '',
|
||||
profile.summary,
|
||||
profile.subtitle,
|
||||
profile.majorFactions[0] || '',
|
||||
creatorIntent?.openingSituation || profile.coreConflicts[0] || profile.playerGoal,
|
||||
]).join(';') || null,
|
||||
iconicElements: compactFoundationTextList([
|
||||
(creatorIntent?.iconicElements.length
|
||||
? creatorIntent.iconicElements
|
||||
: [
|
||||
profile.anchorPack?.motifDirectives.join('、') || '',
|
||||
profile.landmarks[0]?.name || '',
|
||||
]
|
||||
).join('、'),
|
||||
profile.camp?.name || '',
|
||||
profile.majorFactions[0] || '',
|
||||
profile.playerGoal,
|
||||
profile.coreConflicts[0] || '',
|
||||
]).join(';') || null,
|
||||
} satisfies EightAnchorContent;
|
||||
}
|
||||
|
||||
export function getCustomWorldFoundationAnchorContent(
|
||||
profile: CustomWorldProfile,
|
||||
) {
|
||||
const anchorContentRecord = profile.anchorContent;
|
||||
if (!anchorContentRecord) {
|
||||
return buildAnchorContentFromProfileFallback(profile);
|
||||
}
|
||||
|
||||
return {
|
||||
worldPromise: compactAnchorValue(anchorContentRecord.worldPromise),
|
||||
playerFantasy: compactAnchorValue(anchorContentRecord.playerFantasy),
|
||||
themeBoundary: compactAnchorValue(anchorContentRecord.themeBoundary),
|
||||
playerEntryPoint: compactAnchorValue(anchorContentRecord.playerEntryPoint),
|
||||
coreConflict: compactAnchorValue(anchorContentRecord.coreConflict),
|
||||
keyRelationships: compactAnchorValue(anchorContentRecord.keyRelationships),
|
||||
hiddenLines: compactAnchorValue(anchorContentRecord.hiddenLines),
|
||||
iconicElements: compactAnchorValue(anchorContentRecord.iconicElements),
|
||||
} satisfies EightAnchorContent;
|
||||
}
|
||||
|
||||
export function buildCustomWorldFoundationEntries(
|
||||
profile: CustomWorldProfile,
|
||||
): CustomWorldFoundationEntry[] {
|
||||
const creatorIntent = normalizeCustomWorldCreatorIntent(profile.creatorIntent);
|
||||
const anchorContent = getCustomWorldFoundationAnchorContent(profile);
|
||||
const fallbackRelationshipText =
|
||||
buildRelationshipSeedText(creatorIntent?.keyCharacters[0]) ||
|
||||
profile.playableNpcs[0]?.relationshipHooks.join(';') ||
|
||||
profile.storyNpcs[0]?.relationshipHooks.join(';') ||
|
||||
'';
|
||||
|
||||
return [
|
||||
{
|
||||
id: 'world-promise',
|
||||
label: '世界承诺',
|
||||
value: anchorContent.worldPromise || '',
|
||||
},
|
||||
{
|
||||
id: 'player-fantasy',
|
||||
label: '玩家幻想',
|
||||
value: anchorContent.playerFantasy || '',
|
||||
},
|
||||
{
|
||||
id: 'theme-boundary',
|
||||
label: '主题边界',
|
||||
value: anchorContent.themeBoundary || '',
|
||||
},
|
||||
{
|
||||
id: 'player-entry-point',
|
||||
label: '玩家切入口',
|
||||
value: anchorContent.playerEntryPoint || '',
|
||||
},
|
||||
{
|
||||
id: 'core-conflict',
|
||||
label: '核心冲突',
|
||||
value: anchorContent.coreConflict || '',
|
||||
},
|
||||
{
|
||||
id: 'key-relationships',
|
||||
label: '关键关系',
|
||||
value: anchorContent.keyRelationships || fallbackRelationshipText,
|
||||
},
|
||||
{
|
||||
id: 'hidden-lines',
|
||||
label: '暗线与揭示',
|
||||
value: anchorContent.hiddenLines || '',
|
||||
},
|
||||
{
|
||||
id: 'iconic-elements',
|
||||
label: '标志元素',
|
||||
value: anchorContent.iconicElements || '',
|
||||
},
|
||||
];
|
||||
}
|
||||
Reference in New Issue
Block a user