// ============================================================ // WORLD — derived geometry from the hex map (adjacency, centroids) // ============================================================ import { MAP_ROWS, HEX_R, hexToWorld, PROVINCES } from "./data.js"; export const HEX_W = Math.sqrt(3) * HEX_R; function nbrs(c, r) { const odd = r % 2; return [[c - 1, r], [c + 1, r], [c + odd - 1, r - 1], [c + odd, r - 1], [c + odd - 1, r + 1], [c + odd, r + 1]]; } const rows = MAP_ROWS; const H = rows.length; const W = Math.max(...rows.map(r => r.length)); const at = (c, r) => (r < 0 || r >= H || c < 0 || c >= (rows[r]?.length ?? 0)) ? "." : rows[r][c]; export const hexes = []; for (let r = 0; r < H; r++) { for (let c = 0; c < rows[r].length; c++) { const ch = rows[r][c]; if (ch === ".") continue; const [x, z] = hexToWorld(c, r); hexes.push({ col: c, row: r, ch, prov: ch.toLowerCase(), mountain: ch !== ch.toLowerCase(), x, z }); } } // province adjacency export const PROV_ADJ = {}; for (const p of Object.keys(PROVINCES)) PROV_ADJ[p] = new Set(); const key = (c, r) => c + "," + r; const provAt = (c, r) => { const ch = at(c, r); return ch === "." ? null : ch.toLowerCase(); }; for (const h of hexes) { for (const [nc, nr] of nbrs(h.col, h.row)) { const q = provAt(nc, nr); if (q && q !== h.prov) { PROV_ADJ[h.prov].add(q); PROV_ADJ[q].add(h.prov); } } } export const provNeighbors = p => [...PROV_ADJ[p]]; // centroids (average of hexes, biased toward cities) export const PROV_CENTROID = {}; for (const p of Object.keys(PROVINCES)) { let sx = 0, sz = 0, n = 0; for (const h of hexes) if (h.prov === p) { sx += h.x; sz += h.z; n++; } PROV_CENTROID[p] = n ? [sx / n, sz / n] : [0, 0]; } // pull centroid toward cities so markers sit near population import { CITY_DEFS } from "./data.js"; for (const p of Object.keys(PROVINCES)) { const cs = CITY_DEFS.filter(c => c[3] === p); if (!cs.length) continue; const [cx, cz] = PROV_CENTROID[p]; let bx = 0, bz = 0; for (const [, , , , col, row] of cs) { const [x, z] = hexToWorld(col, row); bx += x; bz += z; } bx /= cs.length; bz /= cs.length; PROV_CENTROID[p] = [cx * 0.45 + bx * 0.55, cz * 0.45 + bz * 0.55]; } // distance between provinces (for AI threat estimates) export function provDist(a, b, depth = 0, seen = new Set()) { if (a === b) return 0; if (PROV_ADJ[a]?.has(b)) return 1; seen.add(a); let best = Infinity; for (const n of PROV_ADJ[a] || []) { if (seen.has(n)) continue; best = Math.min(best, 1 + provDist(n, b, depth + 1, seen)); if (depth > 6) break; } return best; } export function adjacentProvinces(a, b) { return !!PROV_ADJ[a]?.has(b); }