124 lines
3.8 KiB
TypeScript
124 lines
3.8 KiB
TypeScript
import { test } from "node:test";
|
|
import assert from "node:assert/strict";
|
|
import { surfacePixels } from "../renderer/surface-map.mjs";
|
|
import type { TerrainSnapshot } from "../shared/protocol";
|
|
|
|
function pixel(map: ReturnType<typeof surfacePixels>, x: number, z: number) {
|
|
return [
|
|
...map.data.subarray((z * map.width + x) * 4, (z * map.width + x + 1) * 4),
|
|
];
|
|
}
|
|
|
|
test("surface map keeps north up, negative coordinates, rectangular bounds and uncovered parchment", () => {
|
|
const map = surfacePixels({
|
|
bounds: { minX: -2, maxX: 2, minZ: -3, maxZ: -1 },
|
|
coverage: [],
|
|
blocks: [
|
|
[-2, 64, -3, "minecraft:snow_block"],
|
|
[1, 64, -2, "minecraft:grass_block"],
|
|
[2, 90, -2, "minecraft:snow_block"], // half-open boundary
|
|
[-2, 90, -1, "minecraft:snow_block"],
|
|
],
|
|
});
|
|
assert.equal(map.width, 4);
|
|
assert.equal(map.height, 2);
|
|
assert.equal(map.columns, 2);
|
|
const north = pixel(map, 0, 0);
|
|
assert.equal(north[0], north[1]);
|
|
assert.equal(north[1], north[2]);
|
|
const south = pixel(map, 3, 1);
|
|
assert.ok(south[1] > south[0] && south[0] > south[2]);
|
|
const blank = pixel(map, 3, 0);
|
|
assert.ok(blank[0] > blank[1] && blank[1] > blank[2]);
|
|
assert.equal(blank[3], 255);
|
|
});
|
|
|
|
test("the highest mapped block wins regardless of export order and glass does not obscure terrain", () => {
|
|
const terrain: TerrainSnapshot = {
|
|
bounds: { minX: 0, maxX: 1, minZ: 0, maxZ: 1 },
|
|
coverage: [],
|
|
blocks: [
|
|
[0, 63, 0, "minecraft:dirt"],
|
|
[0, 64, 0, "minecraft:grass_block[snowy=false]"],
|
|
[0, 70, 0, "minecraft:glass"],
|
|
[0, 71, 0, "minecraft:short_grass"],
|
|
],
|
|
};
|
|
const first = surfacePixels(terrain);
|
|
const reversed = surfacePixels({
|
|
...terrain,
|
|
blocks: [...terrain.blocks].reverse(),
|
|
});
|
|
assert.deepEqual(first.data, reversed.data);
|
|
const rgb = pixel(first, 0, 0);
|
|
assert.ok(rgb[1] > rgb[0] && rgb[0] > rgb[2]);
|
|
});
|
|
|
|
test("depth shading separates shallow, deep and unknown-bed water without texture speckle", () => {
|
|
const map = surfacePixels({
|
|
bounds: { minX: 0, maxX: 3, minZ: 0, maxZ: 1 },
|
|
coverage: [],
|
|
blocks: [
|
|
[0, 64, 0, "minecraft:water[level=0]"],
|
|
[0, 63, 0, "minecraft:sand"],
|
|
[1, 64, 0, "minecraft:water[level=0]"],
|
|
[1, 52, 0, "minecraft:stone"],
|
|
[2, 64, 0, "minecraft:water[level=0]"],
|
|
],
|
|
});
|
|
const shallow = pixel(map, 0, 0),
|
|
deep = pixel(map, 1, 0);
|
|
assert.ok(shallow[2] > shallow[1] && shallow[1] > shallow[0]);
|
|
assert.ok(shallow[2] > deep[2]);
|
|
assert.deepEqual(deep, pixel(map, 2, 0));
|
|
});
|
|
|
|
test("height relief shades slopes while flat ground stays uniform", () => {
|
|
const blocks: TerrainSnapshot["blocks"] = [64, 64, 66, 64].map((y, z) => [
|
|
0,
|
|
y,
|
|
z,
|
|
"minecraft:stone",
|
|
]);
|
|
const map = surfacePixels({
|
|
bounds: { minX: 0, maxX: 1, minZ: 0, maxZ: 4 },
|
|
coverage: [],
|
|
blocks,
|
|
});
|
|
assert.deepEqual(pixel(map, 0, 0), pixel(map, 0, 1));
|
|
assert.ok(pixel(map, 0, 2)[0] > pixel(map, 0, 1)[0]);
|
|
assert.ok(pixel(map, 0, 3)[0] < pixel(map, 0, 1)[0]);
|
|
});
|
|
|
|
test("map cells use the dominant surface material instead of a textured average", () => {
|
|
const blocks: TerrainSnapshot["blocks"] = [];
|
|
for (let x = 0; x < 4; x++)
|
|
for (let z = 0; z < 4; z++)
|
|
blocks.push([
|
|
x,
|
|
64,
|
|
z,
|
|
x === 0 ? "minecraft:stone" : "minecraft:grass_block",
|
|
]);
|
|
const map = surfacePixels(
|
|
{ bounds: { minX: 0, maxX: 4, minZ: 0, maxZ: 4 }, coverage: [], blocks },
|
|
{ resolution: 1 },
|
|
);
|
|
assert.equal(map.width, 1);
|
|
assert.equal(map.height, 1);
|
|
const rgb = pixel(map, 0, 0);
|
|
assert.ok(rgb[1] > rgb[0] && rgb[0] > rgb[2]);
|
|
});
|
|
|
|
test("empty exports fail instead of producing a playable blank map", () => {
|
|
assert.throws(
|
|
() =>
|
|
surfacePixels({
|
|
bounds: { minX: 0, maxX: 16, minZ: 0, maxZ: 16 },
|
|
coverage: [],
|
|
blocks: [],
|
|
}),
|
|
/no mapped terrain/,
|
|
);
|
|
});
|