104 lines
4.1 KiB
TypeScript
104 lines
4.1 KiB
TypeScript
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<string>();
|
|
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,
|
|
};
|
|
}
|