Mapwright v0.5.0 — procedural battle map generator for Foundry VTT
Buildings (modern/fantasy, multi-floor, footprint shapes), caves, outdoor biomes, and town/village + city-block settlements. Auto-places Foundry walls, doors, windows, lighting; furniture as locked tiles; live preview. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,65 @@
|
||||
/**
|
||||
* Build an HTML harness that renders one generated SVG two ways:
|
||||
* left = inline in the DOM (what the preview does — lenient HTML parser)
|
||||
* right = rasterised to a <canvas> via Image (what scene.mjs does)
|
||||
* Used with Playwright to reproduce the "missing content in scene" bug.
|
||||
*/
|
||||
import { writeFileSync } from "node:fs";
|
||||
import { fileURLToPath } from "node:url";
|
||||
import { dirname, join } from "node:path";
|
||||
|
||||
import { Rng } from "../module/lib/rng.mjs";
|
||||
import { generateBuilding } from "../module/lib/building.mjs";
|
||||
import { buildWalls } from "../module/lib/walls.mjs";
|
||||
import { renderSVG } from "../module/lib/render-vector.mjs";
|
||||
|
||||
const here = dirname(fileURLToPath(import.meta.url));
|
||||
|
||||
const layout = generateBuilding({ cols: 26, rows: 18, targetRooms: 11, subtype: "modern", seed: "mire-680", shape: "rectangle" });
|
||||
const walls = buildWalls(layout, { rng: new Rng("mire-680::walls::0") });
|
||||
const svg = renderSVG({ layout, walls, gridSize: 100, subtype: "modern", seed: "mire-680::0" });
|
||||
const W = layout.cols * 100, H = layout.rows * 100;
|
||||
|
||||
const html = `<!doctype html><html><head><meta charset="utf-8"><style>
|
||||
body{margin:0;background:#444;font-family:sans-serif;color:#fff}
|
||||
.row{display:flex;gap:8px;padding:8px}
|
||||
.cell{flex:1}.cell h3{margin:4px}
|
||||
svg,canvas{width:100%;height:auto;border:1px solid #000;background:#888}
|
||||
</style></head><body>
|
||||
<div class="row">
|
||||
<div class="cell"><h3>A: inline SVG (preview)</h3><div id="inline">${svg}</div></div>
|
||||
<div class="cell"><h3 id="status">B: SVG→canvas</h3><canvas id="cv" width="${W}" height="${H}"></canvas></div>
|
||||
<div class="cell"><h3 id="status2">C: →PNG blob→reload (final scene bytes)</h3><canvas id="cv2" width="${W}" height="${H}"></canvas></div>
|
||||
</div>
|
||||
<script>
|
||||
const svgText = ${JSON.stringify(svg)};
|
||||
const W=${W}, H=${H};
|
||||
const blob = new Blob([svgText], { type: "image/svg+xml;charset=utf-8" });
|
||||
const url = URL.createObjectURL(blob);
|
||||
const img = new Image(); img.width=W; img.height=H;
|
||||
img.onload = async () => {
|
||||
try { if (img.decode) await img.decode(); } catch(e){}
|
||||
const cv = document.getElementById("cv");
|
||||
cv.getContext("2d").drawImage(img, 0, 0, W, H);
|
||||
document.getElementById("status").textContent = "B: SVG→canvas — OK";
|
||||
// C: exactly what scene.mjs uploads — toBlob PNG, then reload it
|
||||
cv.toBlob((png) => {
|
||||
if (!png) { document.getElementById("status2").textContent = "C: toBlob NULL"; window.__done = true; return; }
|
||||
const purl = URL.createObjectURL(png);
|
||||
const img2 = new Image();
|
||||
img2.onload = () => {
|
||||
document.getElementById("cv2").getContext("2d").drawImage(img2, 0, 0, W, H);
|
||||
document.getElementById("status2").textContent = "C: PNG " + Math.round(png.size/1024) + "KB — OK";
|
||||
window.__done = true;
|
||||
};
|
||||
img2.onerror = () => { document.getElementById("status2").textContent = "C: PNG RELOAD ERROR"; window.__done = true; };
|
||||
img2.src = purl;
|
||||
}, "image/png");
|
||||
};
|
||||
img.onerror = (e) => { document.getElementById("status").textContent = "B: IMAGE ERROR"; window.__done = true; };
|
||||
img.src = url;
|
||||
</script>
|
||||
</body></html>`;
|
||||
|
||||
writeFileSync(join(here, "raster-test.html"), html);
|
||||
console.log("wrote", join(here, "raster-test.html"), "size", svg.length);
|
||||
Reference in New Issue
Block a user