map/worldMap.js
2026-05-28 19:37:28 +09:00

100 lines
3.2 KiB
JavaScript

import { MAP_H, MAP_W, SIZE } from "./mapUtils.js";
export const DEFAULT_WORLD_PADDING_X = MAP_W;
export const DEFAULT_WORLD_PADDING_Y = MAP_H;
const NEGATIVE_ONE_FIELDS = new Set([
"adminId",
"prefectureRegionId",
"regionId",
"municipalityId",
]);
function isCellField(value) {
return ArrayBuffer.isView(value) && typeof value.length === "number" && value.length === SIZE;
}
function defaultForField(name, Constructor) {
if (name === "sea") return 1;
if (name === "elevation") return 0.08;
if (name === "seaLevel") return undefined;
if (NEGATIVE_ONE_FIELDS.has(name)) return -1;
if (Constructor === Float32Array || Constructor === Float64Array) return 0;
return 0;
}
function makeWorldField(name, source, worldWidth, worldHeight, originX, originY) {
const Constructor = source.constructor;
const out = new Constructor(worldWidth * worldHeight);
const fallback = defaultForField(name, Constructor);
if (fallback !== 0) out.fill(fallback);
for (let y = 0; y < MAP_H; y++) {
const srcRow = y * MAP_W;
const dstRow = (originY + y) * worldWidth + originX;
for (let x = 0; x < MAP_W; x++) out[dstRow + x] = source[srcRow + x];
}
return out;
}
export function createWorldMap(initialMap, options = {}) {
const paddingX = Number.isFinite(options.paddingX) ? Math.max(0, Math.floor(options.paddingX)) : DEFAULT_WORLD_PADDING_X;
const paddingY = Number.isFinite(options.paddingY) ? Math.max(0, Math.floor(options.paddingY)) : DEFAULT_WORLD_PADDING_Y;
const worldWidth = MAP_W + paddingX * 2;
const worldHeight = MAP_H + paddingY * 2;
const originX = paddingX;
const originY = paddingY;
const fields = {};
for (const [name, value] of Object.entries(initialMap || {})) {
if (isCellField(value)) fields[name] = makeWorldField(name, value, worldWidth, worldHeight, originX, originY);
}
if (!fields.sea) {
fields.sea = new Uint8Array(worldWidth * worldHeight);
fields.sea.fill(1);
}
if (!fields.elevation) {
fields.elevation = new Float32Array(worldWidth * worldHeight);
fields.elevation.fill(0.08);
}
return {
seed: initialMap?.seed ?? 0,
width: worldWidth,
height: worldHeight,
originX,
originY,
sourceWidth: initialMap?.width || MAP_W,
sourceHeight: initialMap?.height || MAP_H,
sourceMap: initialMap,
fields,
invalidatedRects: [],
humanPatchHistory: [],
generatedRects: [{
x0: originX,
y0: originY,
x1: originX + MAP_W,
y1: originY + MAP_H,
terrainType: initialMap?.terrainTemplate?.terrainType || initialMap?.terrainDebug?.terrainType || "auto",
label: initialMap?.terrainTemplate?.terrainTypeLabel || initialMap?.terrainDebug?.terrainTypeLabel || "Initial generation",
}],
};
}
export function createInitialCamera(world) {
return {
x: world?.originX || 0,
y: world?.originY || 0,
};
}
export function clampCameraToWorld(camera, world, viewWidth = MAP_W, viewHeight = MAP_H) {
if (!camera || !world) return { x: 0, y: 0 };
const maxX = Math.max(0, world.width - viewWidth);
const maxY = Math.max(0, world.height - viewHeight);
return {
x: Math.min(Math.max(Math.round(camera.x || 0), 0), maxX),
y: Math.min(Math.max(Math.round(camera.y || 0), 0), maxY),
};
}