Worldshaper/src/worldChunking.ts

160 lines
4.4 KiB
TypeScript
Raw Permalink Normal View History

2026-06-26 18:18:14 -04:00
import type { JsonObject } from "./editorCore";
export const WORLD_INDEX_SCHEMA_VERSION = 1;
export const WORLD_SCHEMA_VERSION = 1;
export const WORLD_CHUNK_SCHEMA_VERSION = 1;
export const WORLD_BOOKMARKS_SCHEMA_VERSION = 1;
export const DEFAULT_WORLD_CHUNK_SIZE = 32;
export const DEFAULT_WORLD_TILE_SIZE = 32;
export type WorldIndexEntry = {
id: string;
name: string;
worldDir: string;
};
export type WorldIndexPayload = {
schemaVersion: number;
worlds: WorldIndexEntry[];
};
export type WorldBookmark = {
id: string;
label: string;
x: number;
y: number;
};
export type WorldBookmarksPayload = {
schemaVersion: number;
worldId: string;
bookmarks: WorldBookmark[];
};
export type WorldDefinition = {
schemaVersion: number;
id: string;
name: string;
chunkWidth: number;
chunkHeight: number;
tileSize: number;
defaultBackgroundTileId: string;
spawn: { x: number; y: number };
editor?: {
defaultZoom?: number;
gridVisible?: boolean;
};
};
export type WorldChunkLayer = {
layer: number;
name?: string;
rows: string[];
instanceIds: string[];
};
export type WorldHeightPatch = {
id: string;
name?: string;
z: number;
x: number;
y: number;
rows: string[];
};
export type WorldChunkInstance = {
id: string;
templateId?: string;
layer: number;
x: number;
y: number;
record: JsonObject;
};
export type WorldChunk = {
schemaVersion: number;
worldId: string;
chunkX: number;
chunkY: number;
width: number;
height: number;
backgroundTileId: string;
roomLayers: WorldChunkLayer[];
heightLayers: WorldHeightPatch[];
instances: WorldChunkInstance[];
};
export function normalizeChunkDimension(value: unknown, fallback = DEFAULT_WORLD_CHUNK_SIZE): number {
return Math.max(1, Math.floor(Number(value) || fallback));
}
export function buildChunkKey(chunkX: number, chunkY: number): string {
return `${Math.floor(chunkX)}:${Math.floor(chunkY)}`;
}
export function buildChunkFileName(chunkX: number, chunkY: number): string {
return `${Math.floor(chunkX)}_${Math.floor(chunkY)}.json`;
}
export function worldToChunkCoord(worldCoord: number, chunkSize: number): number {
const safeChunkSize = Math.max(1, Math.floor(Number(chunkSize) || DEFAULT_WORLD_CHUNK_SIZE));
return Math.floor(Number(worldCoord) / safeChunkSize);
}
export function worldToLocalCoord(worldCoord: number, chunkSize: number): number {
const safeChunkSize = Math.max(1, Math.floor(Number(chunkSize) || DEFAULT_WORLD_CHUNK_SIZE));
const chunkCoord = worldToChunkCoord(worldCoord, safeChunkSize);
return Math.floor(Number(worldCoord) - (chunkCoord * safeChunkSize));
}
export function localToWorldCoord(chunkCoord: number, localCoord: number, chunkSize: number): number {
const safeChunkSize = Math.max(1, Math.floor(Number(chunkSize) || DEFAULT_WORLD_CHUNK_SIZE));
return (Math.floor(Number(chunkCoord) || 0) * safeChunkSize) + Math.floor(Number(localCoord) || 0);
}
export function resolveWorldChunkAddress(worldX: number, worldY: number, chunkWidth: number, chunkHeight: number) {
const safeChunkWidth = normalizeChunkDimension(chunkWidth);
const safeChunkHeight = normalizeChunkDimension(chunkHeight);
const chunkX = worldToChunkCoord(worldX, safeChunkWidth);
const chunkY = worldToChunkCoord(worldY, safeChunkHeight);
const localX = worldToLocalCoord(worldX, safeChunkWidth);
const localY = worldToLocalCoord(worldY, safeChunkHeight);
return {
chunkX,
chunkY,
localX,
localY,
chunkKey: buildChunkKey(chunkX, chunkY),
fileName: buildChunkFileName(chunkX, chunkY),
};
}
export function createEmptyChunk(worldId: string, chunkX: number, chunkY: number, backgroundTileId = "", chunkWidth = DEFAULT_WORLD_CHUNK_SIZE, chunkHeight = DEFAULT_WORLD_CHUNK_SIZE): WorldChunk {
const width = normalizeChunkDimension(chunkWidth);
const height = normalizeChunkDimension(chunkHeight);
return {
schemaVersion: WORLD_CHUNK_SCHEMA_VERSION,
worldId: String(worldId || "").trim(),
chunkX: Math.floor(Number(chunkX) || 0),
chunkY: Math.floor(Number(chunkY) || 0),
width,
height,
backgroundTileId: String(backgroundTileId || "").trim(),
roomLayers: [
{
layer: 0,
rows: Array.from({ length: height }, () => ".".repeat(width)),
instanceIds: [],
},
{
layer: 1,
rows: Array.from({ length: height }, () => " ".repeat(width)),
instanceIds: [],
},
],
heightLayers: [],
instances: [],
};
}