import type { RpgCreationEditorTarget, RpgCreationEntityEditorModalProps, } from './RpgCreationEntityEditorModalImpl'; import type { CustomWorldLandmark, CustomWorldNpc, CustomWorldPlayableNpc, CustomWorldProfile, } from '../../types'; /** * 工作包 C 第一轮先把编辑器目标分发需要的 profile 变更收口到 mapper。 * 后续继续拆 section 表单时,提交 patch 和字段清洗会逐步下沉到这里。 */ function slugify(value: string) { const normalized = value .trim() .toLowerCase() .replace(/[^a-z0-9\u4e00-\u9fa5]+/g, '-') .replace(/^-+|-+$/g, ''); return normalized || 'entry'; } function createEntryId(prefix: string, label: string, seed: number) { return `${prefix}-${slugify(label || `${prefix}-${seed}`)}-${seed.toString(36)}`; } const BACKSTORY_UNLOCK_AFFINITY_EASED = 6; const BACKSTORY_UNLOCK_AFFINITY_FRIENDLY = 12; const BACKSTORY_UNLOCK_AFFINITY_TRUSTED = 18; const BACKSTORY_UNLOCK_AFFINITY_CLOSE = 24; export function createPlayableNpcDraft( profile: CustomWorldProfile, ): CustomWorldPlayableNpc { const seed = Date.now() + profile.playableNpcs.length; return { id: createEntryId( 'playable-npc', `角色-${profile.playableNpcs.length + 1}`, seed, ), name: `自定义角色${profile.playableNpcs.length + 1}`, title: '自定义身份', role: '世界中的行动者', description: '', backstory: '', personality: '', motivation: '', combatStyle: '', initialAffinity: 18, relationshipHooks: ['首次接触', '合作空间'], relations: [], tags: ['自定义'], backstoryReveal: { publicSummary: '', chapters: [ { id: 'surface', title: '表层来意', affinityRequired: BACKSTORY_UNLOCK_AFFINITY_EASED, teaser: '', content: '', contextSnippet: '', }, { id: 'scar', title: '旧事裂痕', affinityRequired: BACKSTORY_UNLOCK_AFFINITY_FRIENDLY, teaser: '', content: '', contextSnippet: '', }, { id: 'hidden', title: '隐藏执念', affinityRequired: BACKSTORY_UNLOCK_AFFINITY_TRUSTED, teaser: '', content: '', contextSnippet: '', }, { id: 'final', title: '最终底牌', affinityRequired: BACKSTORY_UNLOCK_AFFINITY_CLOSE, teaser: '', content: '', contextSnippet: '', }, ], }, skills: [ { id: 'skill-1', name: '基础起手', summary: '', style: '起手压制' }, { id: 'skill-2', name: '常用变招', summary: '', style: '机动周旋' }, { id: 'skill-3', name: '压箱底牌', summary: '', style: '爆发终结' }, ], initialItems: [ { id: 'item-1', name: '随身武具', category: '武器', quantity: 1, rarity: 'rare', description: '', tags: ['自定义'], }, { id: 'item-2', name: '补给包', category: '消耗品', quantity: 2, rarity: 'uncommon', description: '', tags: ['自定义'], }, { id: 'item-3', name: '私人物件', category: '专属物品', quantity: 1, rarity: 'rare', description: '', tags: ['自定义'], }, ], templateCharacterId: profile.playableNpcs[0]?.templateCharacterId, }; } export function createStoryNpcDraft( profile: Pick, ): CustomWorldNpc { const seed = Date.now() + profile.storyNpcs.length; return { id: createEntryId( 'story-npc', `场景角色-${profile.storyNpcs.length + 1}`, seed, ), name: `自定义场景角色${profile.storyNpcs.length + 1}`, title: '自定义头衔', role: '自定义身份', description: '', backstory: '', personality: '', motivation: '', combatStyle: '', initialAffinity: 6, relationshipHooks: ['合作', '互动'], relations: [], tags: ['自定义'], backstoryReveal: { publicSummary: '', chapters: [ { id: 'surface', title: '表层来意', affinityRequired: BACKSTORY_UNLOCK_AFFINITY_EASED, teaser: '', content: '', contextSnippet: '', }, { id: 'scar', title: '旧事裂痕', affinityRequired: BACKSTORY_UNLOCK_AFFINITY_FRIENDLY, teaser: '', content: '', contextSnippet: '', }, { id: 'hidden', title: '隐藏执念', affinityRequired: BACKSTORY_UNLOCK_AFFINITY_TRUSTED, teaser: '', content: '', contextSnippet: '', }, { id: 'final', title: '最终底牌', affinityRequired: BACKSTORY_UNLOCK_AFFINITY_CLOSE, teaser: '', content: '', contextSnippet: '', }, ], }, skills: [ { id: 'skill-1', name: '基础起手', summary: '', style: '起手压制' }, { id: 'skill-2', name: '常用变招', summary: '', style: '机动周旋' }, { id: 'skill-3', name: '压箱底牌', summary: '', style: '爆发终结' }, ], initialItems: [ { id: 'item-1', name: '随身武具', category: '武器', quantity: 1, rarity: 'rare', description: '', tags: ['自定义'], }, { id: 'item-2', name: '补给包', category: '消耗品', quantity: 2, rarity: 'uncommon', description: '', tags: ['自定义'], }, { id: 'item-3', name: '私人物件', category: '专属物品', quantity: 1, rarity: 'rare', description: '', tags: ['自定义'], }, ], } satisfies CustomWorldNpc; } export function createLandmarkDraft( profile: CustomWorldProfile, ): CustomWorldLandmark { const seed = Date.now() + profile.landmarks.length; const previousLandmark = profile.landmarks[profile.landmarks.length - 1]; return { id: createEntryId( 'landmark', `scene-${profile.landmarks.length + 1}`, seed, ), name: `自定义场景${profile.landmarks.length + 1}`, description: '', dangerLevel: '中', imageSrc: undefined, sceneNpcIds: profile.storyNpcs.slice(0, 3).map((npc) => npc.id), connections: previousLandmark ? [ { targetLandmarkId: previousLandmark.id, relativePosition: 'south', summary: `南侧可回到${previousLandmark.name}`, }, ] : [], }; } export function buildEditorTargetRendererParams( props: RpgCreationEntityEditorModalProps, ) { const { profile, target, onClose, onProfileChange } = props; return { onClose, onProfileChange, profile, target, }; } export function resolveEditablePlayableNpc( profile: CustomWorldProfile, target: Extract, ) { if (target.mode === 'create') { return createPlayableNpcDraft(profile); } return profile.playableNpcs.find((item) => item.id === target.id) ?? null; } export function resolveEditableStoryNpc( profile: CustomWorldProfile, target: Extract, ) { if (target.mode === 'create') { return createStoryNpcDraft(profile); } return profile.storyNpcs.find((item) => item.id === target.id) ?? null; } export function resolveEditableLandmark( profile: CustomWorldProfile, target: Extract, ) { if (target.mode === 'create') { return createLandmarkDraft(profile); } return profile.landmarks.find((item) => item.id === target.id) ?? null; }