import type { CampaignPack, CustomWorldProfile, ScenarioPack, } from '../../types'; import { buildActPlan } from './actPlanner'; import { resolveCampaignState } from './campaignDirector'; function slugify(value: string) { const ascii = value .trim() .toLowerCase() .replace(/[^a-z0-9\u4e00-\u9fa5]+/giu, '-') .replace(/^-+|-+$/g, ''); return ascii || 'campaign'; } export function buildCampaignPack(params: { scenarioPackId: string; profile: CustomWorldProfile; authoringStyle?: string; }) { const { scenarioPackId, profile, authoringStyle = 'classic_story_rpg' } = params; const campaignStateSeed = resolveCampaignState({ state: { customWorldProfile: profile, chapterState: null, storyEngineMemory: { activeThreadIds: profile.storyGraph?.visibleThreads.slice(0, 3).map((thread) => thread.id) ?? [], }, } as never, }); const actTemplates = buildActPlan({ state: { customWorldProfile: profile, storyEngineMemory: { activeThreadIds: profile.storyGraph?.visibleThreads.slice(0, 3).map((thread) => thread.id) ?? [], }, } as never, }); return { id: profile.campaignPackId ?? `campaign-pack:${slugify(profile.name)}`, scenarioPackId, title: `${profile.name} 主战役`, authoringStyle, campaignStateSeed, actTemplates, requiredCompanionIds: profile.playableNpcs.slice(0, 2).map((npc) => npc.id), } satisfies CampaignPack; } export function compileCampaignFromWorldProfile(params: { profile: CustomWorldProfile; }) { const scenarioPack: ScenarioPack = { id: params.profile.scenarioPackId ?? `scenario-pack:${slugify(params.profile.name)}`, title: `${params.profile.name} Scenario Pack`, version: '0.1.0', worldPackIds: [params.profile.id], campaignIds: [], sharedConstraintPackIds: [], }; const campaignPack = buildCampaignPack({ scenarioPackId: scenarioPack.id, profile: params.profile, }); return { scenarioPack: { ...scenarioPack, campaignIds: [campaignPack.id], sharedConstraintPackIds: [campaignPack.id], }, campaignPack, }; }