You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

72 lines
3.8 KiB
JavaScript

// core/grid.js; the ONE rotation convention + facing tables + ground/water probes.
import { safeGetBlock } from "./mc.js";
export const RIGHT = { south: [1, 0, 0], north: [-1, 0, 0], east: [0, 0, -1], west: [0, 0, 1] };
export const FWD = { south: [0, 0, 1], north: [0, 0, -1], east: [1, 0, 0], west: [-1, 0, 0] };
// world position of a south-frame offset g given the anchor + facing. rotating=false -> raw offset (symmetric canopies um/bu/bt).
export function cellWorld(g, ax, ay, az, dir, rotating = true) {
if (!rotating) return { x: ax + g[0], y: ay + g[1], z: az + g[2] };
const R = RIGHT[dir] || RIGHT.south, F = FWD[dir] || FWD.south;
return { x: ax + g[0] * R[0] + g[2] * F[0], y: ay + g[1], z: az + g[0] * R[2] + g[2] * F[2] };
}
// inverse of cellWorld: back compute the anchor from a cell's world position + facing.
export function anchorFrom(g, x, y, z, dir, rotating = true) {
if (!rotating) return { x: x - g[0], y: y - g[1], z: z - g[2] };
const R = RIGHT[dir] || RIGHT.south, F = FWD[dir] || FWD.south;
return { x: x - g[0] * R[0] - g[2] * F[0], y: y - g[1], z: z - g[0] * R[2] - g[2] * F[2] };
}
// Bundle a cell map into ready-to-use cellWorld/anchorFrom closures (used by every multiblock family).
export function grid(cells, rotating = true) {
return {
cells,
cellWorld: (cell, ax, ay, az, dir) => cellWorld(cells[cell], ax, ay, az, dir, rotating),
anchorFrom: (cell, x, y, z, dir) => anchorFrom(cells[cell], x, y, z, dir, rotating),
};
}
// Facing / yaw tables
export const DIR_YAW = { north: 0, south: 180, east: 90, west: 270 }; // seat: player faces OUT of the seat
export const DIR_TROT = { south: 0, north: 180, east: 90, west: -90 }; // seat: block geometry transformation angle
export const DISPLAY_DIR_YAW = { north: 0, east: 90, south: 180, west: -90 }; // DecoCraft display-entity summon yaw
export const TUBE_DIR_YAW = { south: 0, west: 90, north: 180, east: -90 }; // rideable block-facing -> entity body yaw
export const displayYawToDir = (yaw) => { const y = ((yaw % 360) + 360) % 360; if (y < 45 || y >= 315) return "north"; if (y < 135) return "east"; if (y < 225) return "south"; return "west"; };
export const tubeYawToDir = (yaw) => { const y = ((yaw % 360) + 360) % 360; if (y < 45 || y >= 315) return "south"; if (y < 135) return "west"; if (y < 225) return "north"; return "east"; };
export function fcCanReplace(nb) {
if (!nb) return false;
if (nb.isAir) return true;
const t = nb.typeId || "";
if (/grass_block|dirt|podzol|mycelium|mud|farmland|clay|gravel|sand|stone|terracotta|concrete|planks|log|wool/.test(t)) return false;
return /short_grass|tall_grass|tallgrass|fern|flower|sapling|snow_layer|dead_bush|azalea|deadbush|seagrass|\bvine|leaf_litter/.test(t);
}
export function tubeWaterTop(dim, x, z, nearY) {
const isW = (b) => { try { return b.typeId.endsWith("water"); } catch { return false; } };
for (let y = Math.floor(nearY) + 3; y >= Math.floor(nearY) - 6; y--) { const b = safeGetBlock(dim, { x, y, z }); if (isW(b)) return y; }
return null;
}
export function tubeOverWater(block) {
try { if (block.isWaterlogged) return true; } catch {}
const dim = block.dimension;
for (const [dx, dy, dz] of [[0, -1, 0], [1, 0, 0], [-1, 0, 0], [0, 0, 1], [0, 0, -1]]) {
const b = safeGetBlock(dim, { x: block.x + dx, y: block.y + dy, z: block.z + dz });
try { if (b && (b.typeId.endsWith("water") || b.isWaterlogged)) return true; } catch {}
}
return false;
}
export function loungerSupportY(dim, x, z, fromY, noFloat, float = 0) {
for (let y = Math.floor(fromY) + 1; y >= Math.floor(fromY) - 48; y--) {
const b = safeGetBlock(dim, { x, y, z });
if (!b) continue;
const id = b.typeId || "";
if (id.endsWith("water")) { if (noFloat) continue; return y + 1 + float; }
let air = id === "minecraft:air"; try { air = b.isAir; } catch {}
if (!air && !/water|lava/.test(id)) return y + 1;
}
return null;
}