/**
* Organic cave renderer: smoothed contour loops -> SVG.
*
* The look: rocky floor inside flowing walls, thick rock walls with a cast
* shadow + inner highlight, scattered floor debris, optional grid clipped to
* floor. Pure + deterministic.
*/
import { Rng } from "./rng.mjs";
const THEMES = {
cave: {
outside: "#0f0d0b", floorBase: "#7d7164", floorSpeck: "#8d8173",
floorEdge: "#5e5349", wall: "#1d1813", wallHi: "#5a5042",
grid: "#3a342e", label: "#f3e9d4", speckLight: "#9a8e80"
},
crypt: {
outside: "#0c0e12", floorBase: "#6a6f7c", floorSpeck: "#7a8090",
floorEdge: "#50545f", wall: "#171a20", wallHi: "#566173",
grid: "#33363d", label: "#eef3fb", speckLight: "#8990a0"
},
sewer: {
outside: "#0c100e", floorBase: "#61705f", floorSpeck: "#6e7e69",
floorEdge: "#4a564a", wall: "#161d18", wallHi: "#566355",
grid: "#2f3733", label: "#e7f5ec", speckLight: "#7e8e78"
}
};
const PX = (v, gs) => +(v * gs).toFixed(1);
export function renderCaveSVG(args) {
const model = args.model;
const gs = args.gridSize ?? 70;
const theme = THEMES[args.theme] ?? THEMES.cave;
const showGrid = args.showGrid !== false;
const showLabels = args.showLabels !== false;
const rng = new Rng(`${args.seed ?? "cave"}::caverender`);
const W = model.cols * gs;
const H = model.rows * gs;
const out = [];
const pathFor = (pts) => {
let d = `M${PX(pts[0].x, gs)},${PX(pts[0].y, gs)}`;
for (let i = 1; i < pts.length; i += 1) d += `L${PX(pts[i].x, gs)},${PX(pts[i].y, gs)}`;
return d + "Z";
};
const floorPath = model.loops.map((lp) => pathFor(lp.smooth)).join(" ");
out.push(``);
return out.join("");
}
function rockTexture(W, H, theme, gs, rng) {
// Sparse rock cracks/blotches outside, very subtle.
const out = [``];
const n = Math.round((W * H) / (gs * gs) / 6);
for (let i = 0; i < n; i += 1) {
const x = rng.float() * W, y = rng.float() * H;
const len = gs * (0.3 + rng.float() * 0.5);
const ang = rng.float() * Math.PI;
out.push(``);
}
out.push(``);
return out.join("");
}
function floorDebris(model, gs, theme, rng) {
const out = [``];
const n = Math.round(model.cols * model.rows * 0.6);
for (let i = 0; i < n; i += 1) {
const x = rng.float() * model.cols, y = rng.float() * model.rows;
const r = gs * (0.02 + rng.float() * 0.06);
const col = rng.float() < 0.5 ? theme.floorSpeck : theme.speckLight;
out.push(``);
}
out.push(``);
return out.join("");
}
function caveGrid(model, gs, color) {
const out = [``];
for (let x = 0; x <= model.cols; x += 1) out.push(``);
for (let y = 0; y <= model.rows; y += 1) out.push(``);
out.push(``);
return out.join("");
}
function escapeXml(str) {
return String(str).replace(/[<>&'"]/g, (c) => ({ "<": "<", ">": ">", "&": "&", "'": "'", '"': """ }[c]));
}