126 lines
3.2 KiB
JavaScript
126 lines
3.2 KiB
JavaScript
'use strict'
|
|
|
|
/**
|
|
* Border / neighbor helpers for mesh regions (ADR-0008).
|
|
* Pure functions — no I/O. Used by CLI and future Squid plugin.
|
|
*/
|
|
|
|
/**
|
|
* Distance to nearest edge of bounds (positive inside, negative outside).
|
|
* @param {{ minX:number, maxX:number, minZ:number, maxZ:number }} bounds
|
|
* @param {number} x
|
|
* @param {number} z
|
|
*/
|
|
function distanceToEdge(bounds, x, z) {
|
|
const dx = Math.min(x - bounds.minX, bounds.maxX - x)
|
|
const dz = Math.min(z - bounds.minZ, bounds.maxZ - z)
|
|
return Math.min(dx, dz)
|
|
}
|
|
|
|
/**
|
|
* @param {object} bounds
|
|
* @param {number} x
|
|
* @param {number} z
|
|
* @param {number} [margin=16]
|
|
*/
|
|
function nearBorder(bounds, x, z, margin = 16) {
|
|
if (!bounds) return false
|
|
if (x < bounds.minX || x > bounds.maxX || z < bounds.minZ || z > bounds.maxZ) {
|
|
return true
|
|
}
|
|
return distanceToEdge(bounds, x, z) <= margin
|
|
}
|
|
|
|
/**
|
|
* Pick a neighboring region for a position leaving `current`.
|
|
* Prefers region containing (x,z); else nearest by center distance.
|
|
* @param {object[]} regions
|
|
* @param {object} current current region record
|
|
* @param {number} x
|
|
* @param {number} z
|
|
*/
|
|
function findNeighbor(regions, current, x, z) {
|
|
const others = regions.filter((r) => r.regionId !== current.regionId)
|
|
const containing = others.find(
|
|
(r) =>
|
|
r.bounds &&
|
|
x >= r.bounds.minX &&
|
|
x <= r.bounds.maxX &&
|
|
z >= r.bounds.minZ &&
|
|
z <= r.bounds.maxZ
|
|
)
|
|
if (containing) return containing
|
|
|
|
// Nearest center among regions
|
|
let best = null
|
|
let bestD = Infinity
|
|
for (const r of others) {
|
|
if (!r.bounds) continue
|
|
const cx = (r.bounds.minX + r.bounds.maxX) / 2
|
|
const cz = (r.bounds.minZ + r.bounds.maxZ) / 2
|
|
const d = (cx - x) * (cx - x) + (cz - z) * (cz - z)
|
|
if (d < bestD) {
|
|
bestD = d
|
|
best = r
|
|
}
|
|
}
|
|
return best
|
|
}
|
|
|
|
/**
|
|
* Map position into neighbor local space using offsets.
|
|
* @param {object} fromRegion
|
|
* @param {object} toRegion
|
|
* @param {number} x
|
|
* @param {number} y
|
|
* @param {number} z
|
|
*/
|
|
function mapPosition(fromRegion, toRegion, x, y, z) {
|
|
const fo = fromRegion.offset || { x: 0, z: 0 }
|
|
const to = toRegion.offset || { x: 0, z: 0 }
|
|
// Global ≈ local + offset; remap: localTo = global - to.offset
|
|
const gx = x + fo.x
|
|
const gz = z + fo.z
|
|
return {
|
|
x: gx - to.x,
|
|
y,
|
|
z: gz - to.z
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Suggest portal policy action.
|
|
* @returns {{ action: 'none'|'warn'|'migrate', neighbor: object|null, margin: number }}
|
|
*/
|
|
function borderAction(regions, current, x, z, margin = 16) {
|
|
if (!current || !current.bounds) {
|
|
return { action: 'none', neighbor: null, margin }
|
|
}
|
|
if (!nearBorder(current.bounds, x, z, margin)) {
|
|
return { action: 'none', neighbor: null, margin }
|
|
}
|
|
const outside =
|
|
x < current.bounds.minX ||
|
|
x > current.bounds.maxX ||
|
|
z < current.bounds.minZ ||
|
|
z > current.bounds.maxZ
|
|
const neighbor = findNeighbor(regions, current, x, z)
|
|
if (!neighbor) {
|
|
return { action: 'warn', neighbor: null, margin }
|
|
}
|
|
return {
|
|
action: outside ? 'migrate' : 'warn',
|
|
neighbor,
|
|
margin,
|
|
mapped: outside ? mapPosition(current, neighbor, x, 64, z) : null
|
|
}
|
|
}
|
|
|
|
module.exports = {
|
|
distanceToEdge,
|
|
nearBorder,
|
|
findNeighbor,
|
|
mapPosition,
|
|
borderAction
|
|
}
|