import { resolveCustomWorldCampSceneImage, resolveCustomWorldLandmarkImageMap, } from '../data/customWorldVisuals'; import type { CustomWorldLandmark, CustomWorldProfile, SceneChapterBlueprint, } from '../types'; import { resolveCustomWorldCampScene } from './customWorldCamp'; export type CustomWorldSceneKind = 'camp' | 'landmark'; export type CustomWorldSceneActImagePreview = { id: string; title: string; imageSrc: string; }; export type CustomWorldScenePresentation = { id: string; kind: CustomWorldSceneKind; name: string; description: string; imageSrc: string; sceneChapters: SceneChapterBlueprint[]; actPreviews: CustomWorldSceneActImagePreview[]; }; function normalizeText(value: string | null | undefined) { return value?.trim() ?? ''; } export function resolveScenePresentationChapters(params: { sceneChapters: CustomWorldProfile['sceneChapterBlueprints']; sceneId: string; sceneName: string; }) { const sceneChapters = params.sceneChapters ?? []; const normalizedSceneId = params.sceneId.trim(); const normalizedSceneName = params.sceneName.trim(); const directMatches = sceneChapters.filter( (chapter) => chapter.sceneId.trim() === normalizedSceneId, ); if (directMatches.length > 0) { return directMatches; } const linkedMatches = sceneChapters.filter((chapter) => chapter.linkedLandmarkIds.some( (landmarkId) => landmarkId.trim() === normalizedSceneId, ), ); if (linkedMatches.length > 0) { return linkedMatches; } return sceneChapters.filter((chapter) => { const chapterTitle = chapter.title.trim(); return ( chapterTitle === normalizedSceneName || chapter.summary.includes(normalizedSceneName) || chapter.acts.some( (act) => act.title.includes(normalizedSceneName) || act.summary.includes(normalizedSceneName), ) ); }); } export function resolveScenePresentationImage(params: { sceneImageSrc?: string | null; sceneChapters: SceneChapterBlueprint[]; }) { const firstActImageSrc = params.sceneChapters .flatMap((chapter) => chapter.acts) .map((act) => normalizeText(act.backgroundImageSrc)) .find(Boolean) || ''; return firstActImageSrc || normalizeText(params.sceneImageSrc); } export function collectSceneActImagePreviews(params: { sceneChapters: SceneChapterBlueprint[]; sceneImageSrc?: string | null; }) { const sceneImageSrc = normalizeText(params.sceneImageSrc); const actPreviews = params.sceneChapters.flatMap((chapter) => chapter.acts .map((act, index) => { // 中文注释:幕预览图片必须优先读取当前幕背景;场景图只给缺图的旧数据兜底,避免开局场景和普通场景在列表、详情里显示不同图片。 const imageSrc = normalizeText(act.backgroundImageSrc) || sceneImageSrc; return { id: act.id.trim() || `${chapter.id}-act-${index}`, title: act.title.trim() || `第${index + 1}幕`, imageSrc, }; }) .filter((act) => act.imageSrc), ); if (actPreviews.length > 0 || !sceneImageSrc) { return actPreviews; } return [1, 2, 3].map((actNumber) => ({ id: `fallback-scene-act-${actNumber}`, title: `第${actNumber}幕`, imageSrc: sceneImageSrc, })); } export function buildScenePresentation(params: { profile: CustomWorldProfile; scene: CustomWorldLandmark; kind: CustomWorldSceneKind; sceneImageSrc?: string | null; }) { const sceneChapters = resolveScenePresentationChapters({ sceneChapters: params.profile.sceneChapterBlueprints, sceneId: params.scene.id, sceneName: params.scene.name, }); const imageSrc = resolveScenePresentationImage({ sceneImageSrc: params.sceneImageSrc ?? params.scene.imageSrc, sceneChapters, }); return { id: params.scene.id, kind: params.kind, name: params.scene.name, description: params.scene.description, imageSrc, sceneChapters, actPreviews: collectSceneActImagePreviews({ sceneChapters, sceneImageSrc: imageSrc, }), } satisfies CustomWorldScenePresentation; } export function buildCustomWorldScenePresentations(profile: CustomWorldProfile) { const landmarkImageById = resolveCustomWorldLandmarkImageMap(profile); const campScene = resolveCustomWorldCampScene(profile); const campPresentation = buildScenePresentation({ profile, scene: campScene, kind: 'camp', sceneImageSrc: resolveCustomWorldCampSceneImage(profile), }); const landmarkPresentations = profile.landmarks.map((landmark) => buildScenePresentation({ profile, scene: landmark, kind: 'landmark', sceneImageSrc: landmarkImageById.get(landmark.id) || landmark.imageSrc, }), ); return { camp: campPresentation, landmarks: landmarkPresentations, }; }