import type { CustomWorldCoverProfile, CustomWorldPlayableNpc, CustomWorldProfile, } from '../types'; export type CustomWorldCoverRenderMode = 'image' | 'scene_with_roles'; export type CustomWorldCoverPresentation = { imageSrc: string | null; renderMode: CustomWorldCoverRenderMode; characterImageSrcs: string[]; sourceType: CustomWorldCoverProfile['sourceType']; }; function resolveOpeningSceneFirstActImageSrc(profile: CustomWorldProfile) { return profile.sceneChapterBlueprints?.[0]?.acts?.[0]?.backgroundImageSrc?.trim() || null; } function resolveOpeningSceneImageSrc(profile: CustomWorldProfile) { // 默认封面优先取开局场景第一幕图,避免草稿页与作品库继续沿用旧的营地兜底策略。 const firstActImageSrc = resolveOpeningSceneFirstActImageSrc(profile); if (firstActImageSrc) { return firstActImageSrc; } const campImageSrc = profile.camp?.imageSrc?.trim() || ''; if (campImageSrc) { return campImageSrc; } return ( profile.landmarks .map((landmark) => landmark.imageSrc?.trim() || '') .find(Boolean) || null ); } function resolvePlayableCoverImageSrc(role: CustomWorldPlayableNpc) { const explicitImageSrc = role.imageSrc?.trim() || ''; if (explicitImageSrc) { return explicitImageSrc; } return null; } function normalizeCoverCharacterRoleIds( profile: Pick, roleIds?: string[] | null, ) { const availableIds = new Set( profile.playableNpcs.map((role) => role.id.trim()).filter(Boolean), ); const selectedIds = Array.isArray(roleIds) ? [ ...new Set( roleIds .map((roleId) => roleId.trim()) .filter((roleId) => roleId && availableIds.has(roleId)), ), ].slice(0, 3) : []; if (selectedIds.length > 0) { return selectedIds; } return profile.playableNpcs .map((role) => role.id.trim()) .filter(Boolean) .slice(0, 3); } export function buildDefaultCustomWorldCoverProfile( profile: Pick, ): CustomWorldCoverProfile { return { sourceType: 'default', imageSrc: null, characterRoleIds: normalizeCoverCharacterRoleIds(profile), }; } export function resolveCustomWorldCoverPresentation( profile: CustomWorldProfile, ): CustomWorldCoverPresentation { const cover = profile.cover; const sourceType = cover?.sourceType === 'uploaded' || cover?.sourceType === 'generated' ? cover.sourceType : 'default'; const explicitImageSrc = cover?.imageSrc?.trim() || ''; if (sourceType !== 'default' && explicitImageSrc) { return { imageSrc: explicitImageSrc, renderMode: 'image', characterImageSrcs: [], sourceType, }; } const openingSceneImageSrc = resolveOpeningSceneImageSrc(profile); const roleById = new Map( profile.playableNpcs.map((role) => [role.id.trim(), role] as const), ); const characterImageSrcs = normalizeCoverCharacterRoleIds( profile, cover?.characterRoleIds, ) .map((roleId) => roleById.get(roleId)) .map((role) => (role ? resolvePlayableCoverImageSrc(role) : null)) .filter((imageSrc): imageSrc is string => Boolean(imageSrc)); const leadPlayableImageSrc = profile.playableNpcs .map((role) => resolvePlayableCoverImageSrc(role)) .find(Boolean) || null; return { imageSrc: openingSceneImageSrc || leadPlayableImageSrc, renderMode: openingSceneImageSrc && characterImageSrcs.length > 0 ? 'scene_with_roles' : 'image', characterImageSrcs: openingSceneImageSrc && characterImageSrcs.length > 0 ? characterImageSrcs : [], sourceType: 'default', }; }