Files
Genarrative/src/services/customWorldCamp.ts
kdletters cbc27bad4a
Some checks failed
CI / verify (push) Has been cancelled
init with react+axum+spacetimedb
2026-04-26 18:06:23 +08:00

121 lines
3.9 KiB
TypeScript

import {
type CustomWorldCampScene,
type CustomWorldProfile,
} from '../types';
import { detectCustomWorldThemeMode } from './customWorldTheme';
type CampProfileSeed = Pick<
CustomWorldProfile,
'name' | 'summary' | 'tone' | 'playerGoal' | 'settingText' | 'templateWorldType'
> & {
camp?: Pick<
CustomWorldCampScene,
| 'id'
| 'name'
| 'description'
| 'visualDescription'
| 'imageSrc'
| 'sceneNpcIds'
| 'connections'
| 'narrativeResidues'
> | null;
};
function clampText(value: string, maxLength: number) {
const normalized = value.trim().replace(/\s+/g, ' ');
if (!normalized) {
return '';
}
if (normalized.length <= maxLength) {
return normalized;
}
return `${normalized.slice(0, Math.max(0, maxLength - 1)).trim()}`;
}
function sanitizeCampSeed(name: string) {
const normalized = name.trim().replace(/\s+/g, '');
if (!normalized) {
return '';
}
const stripped = normalized.replace(
/(|||||||||)$/u,
'',
);
const seed = stripped || normalized;
return seed.slice(0, Math.min(seed.length, 4));
}
function buildFallbackCampName(profile: CampProfileSeed) {
const seed =
sanitizeCampSeed(profile.name) ||
'归途';
const themeMode = detectCustomWorldThemeMode(profile);
const suffixByMode = {
mythic: '归舍',
martial: '归舍',
arcane: '栖居',
machina: '整备居',
tide: '潮居',
rift: '界隙居所',
} as const;
return `${seed}${suffixByMode[themeMode]}`;
}
function buildFallbackCampDescription(profile: CampProfileSeed, campName: string) {
const summaryLead = clampText(profile.summary, 24) || '前路尚未明朗';
const goalLead = clampText(profile.playerGoal, 24) || '继续整理线索';
const themeMode = detectCustomWorldThemeMode(profile);
const descriptionByMode = {
mythic: `${campName}是你在${profile.name}暂时安顿的归处,能整备行装、交换判断,并围绕“${goalLead}”继续启程。`,
martial: `${campName}是你在${profile.name}暂时落脚的归处,能整备行装、收拢同伴,并朝“${goalLead}”继续动身。`,
arcane: `${campName}是你在${profile.name}暂时栖身的居所,适合调息、整理法器,并围绕“${summaryLead}”交换判断。`,
machina: `${campName}是你在${profile.name}的临时居所,能检修装备、补齐物资,并为下一段行动重新校准节奏。`,
tide: `${campName}是你在${profile.name}靠潮歇息的住处,能安顿队伍、整理补给,并顺着“${goalLead}”继续前探。`,
rift: `${campName}是你在${profile.name}勉强守住的一处居所,既能短暂停步,也能围绕“${summaryLead}”商量下一步去向。`,
} as const;
return descriptionByMode[themeMode];
}
export function buildFallbackCustomWorldCampScene(
profile: CampProfileSeed,
): CustomWorldCampScene {
const fallbackName = buildFallbackCampName(profile);
return {
id: 'custom-scene-camp',
name: fallbackName,
description: buildFallbackCampDescription(profile, fallbackName),
sceneNpcIds: [],
connections: [],
narrativeResidues: null,
};
}
export function resolveCustomWorldCampScene(
profile: CampProfileSeed,
): CustomWorldCampScene {
const fallback = buildFallbackCustomWorldCampScene(profile);
const camp = profile.camp;
return {
id: camp?.id?.trim() || fallback.id,
name: camp?.name?.trim() || fallback.name,
description: camp?.description?.trim() || fallback.description,
visualDescription: camp?.visualDescription?.trim() || undefined,
imageSrc: camp?.imageSrc?.trim() || undefined,
sceneNpcIds: Array.isArray(camp?.sceneNpcIds)
? [...new Set(camp.sceneNpcIds.map((entry) => entry.trim()).filter(Boolean))]
: fallback.sceneNpcIds,
connections: Array.isArray(camp?.connections)
? camp.connections
: fallback.connections,
narrativeResidues: camp?.narrativeResidues ?? fallback.narrativeResidues,
};
}