import type { MapEntry, ProjectFile, ProjectManifest } from '../project/types'; import { decodeMapDefinition } from './mapSchema'; import { resolveProjectAssetPath } from './mapPaths'; import type { MapSelection, ResolvedProjectMap, VisualMapAsset } from './types'; import { decodeEditableMapDocument } from './editor/editorSchema'; function projectFile(manifest: ProjectManifest, path: string): ProjectFile { const file = manifest.files.find((candidate) => candidate.path === path); if (!file) throw new Error(`地图引用的资源不存在:${path}`); return file; } export function discoverMapEntries(files: ProjectFile[]): MapEntry[] { const paths = new Set(files.map((file) => file.path)); const filesByPath = new Map(files.map((file) => [file.path, file])); const ids = new Set(); return files .filter((file) => /(^|\/)map\.json$/i.test(file.path)) .map((file) => { const definition = decodeMapDefinition(file.data); if (ids.has(definition.id)) throw new Error(`地图 id 重复:${definition.id}`); ids.add(definition.id); const physicsPath = definition.physics ? resolveProjectAssetPath(file.path, definition.physics.source) : undefined; const visualPath = definition.visual ? resolveProjectAssetPath(file.path, definition.visual.source) : undefined; const authoringPath = definition.authoring ? resolveProjectAssetPath(file.path, definition.authoring.source) : undefined; for (const resolved of [physicsPath, visualPath, authoringPath]) { if (resolved && !paths.has(resolved)) throw new Error(`地图 ${definition.name} 引用的资源不存在:${resolved}`); } if (authoringPath) { const authoring = filesByPath.get(authoringPath); if (!authoring) throw new Error(`地图 ${definition.name} 的创作层不存在`); const editable = decodeEditableMapDocument(authoring.data); if (editable.mapId !== definition.id) throw new Error(`地图 ${definition.name} 的创作层 mapId 必须为 ${definition.id}`); } return { descriptorPath: file.path, schemaVersion: definition.schemaVersion, id: definition.id, name: definition.name, physicsPath, visualPath, ...(authoringPath ? { authoringPath } : {}), spawnPoints: definition.spawnPoints.map(({ id, name }) => ({ id, name })), }; }); } export function resolveProjectMap( manifest: ProjectManifest, descriptorPath: string, ): ResolvedProjectMap { const descriptor = projectFile(manifest, descriptorPath); const definition = decodeMapDefinition(descriptor.data); const physicsPath = definition.physics ? resolveProjectAssetPath(descriptorPath, definition.physics.source) : undefined; const visualPath = definition.visual ? resolveProjectAssetPath(descriptorPath, definition.visual.source) : undefined; const authoringPath = definition.authoring ? resolveProjectAssetPath(descriptorPath, definition.authoring.source) : undefined; if (physicsPath) projectFile(manifest, physicsPath); if (visualPath) projectFile(manifest, visualPath); if (authoringPath) { const editable = decodeEditableMapDocument(projectFile(manifest, authoringPath).data); if (editable.mapId !== definition.id) throw new Error(`地图 ${definition.name} 的创作层 mapId 必须为 ${definition.id}`); } return { definition, descriptorPath, physicsPath, visualPath, authoringPath, }; } export function visualMapAsset( manifest: ProjectManifest, selection: MapSelection, ): VisualMapAsset | null { if (selection.kind !== 'project') return null; const resolved = resolveProjectMap(manifest, selection.descriptorPath); if (!resolved.visualPath || !resolved.definition.visual) return null; const visual = projectFile(manifest, resolved.visualPath); return { id: resolved.definition.id, name: resolved.definition.name, path: resolved.visualPath, data: visual.data, castShadow: resolved.definition.visual.castShadow ?? true, receiveShadow: resolved.definition.visual.receiveShadow ?? true, }; }