230 lines
7.2 KiB
TypeScript
230 lines
7.2 KiB
TypeScript
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 || '',
|
||
},
|
||
];
|
||
}
|