This commit is contained in:
33333-33333 2026-05-28 00:30:09 +09:00
commit b17be0e0d2
21 changed files with 8034 additions and 2737 deletions

77
app.js
View file

@ -24,6 +24,7 @@ const state = {
};
const canvas = document.getElementById("mapCanvas");
const canvasShell = document.querySelector(".canvas-shell");
const seedInput = document.getElementById("seed");
const randomSeedButton = document.getElementById("randomSeed");
const showFeaturesInput = document.getElementById("showFeatures");
@ -38,6 +39,69 @@ let generationStartedAt = 0;
let generationCurrentStage = "";
let generationTimer = null;
const panState = { keys: new Set(), raf: null, lastTime: 0, speedPxPerSecond: 520 };
function mapClientToCell(event) {
if (!state.map) return null;
const rect = canvas.getBoundingClientRect();
if (!rect.width || !rect.height) return null;
const relX = (event.clientX - rect.left) / rect.width;
const relY = (event.clientY - rect.top) / rect.height;
return {
x: Math.floor(relX * state.map.width),
y: Math.floor(relY * state.map.height),
};
}
function isEditableTarget(target) {
if (!target) return false;
const tag = target.tagName?.toLowerCase?.();
return tag === "input" || tag === "textarea" || tag === "select" || target.isContentEditable;
}
function panFrame(time) {
if (!canvasShell || panState.keys.size === 0) {
panState.raf = null;
panState.lastTime = 0;
return;
}
const dt = panState.lastTime ? Math.min(0.05, (time - panState.lastTime) / 1000) : 0;
panState.lastTime = time;
let dx = 0;
let dy = 0;
if (panState.keys.has("a")) dx -= 1;
if (panState.keys.has("d")) dx += 1;
if (panState.keys.has("w")) dy -= 1;
if (panState.keys.has("s")) dy += 1;
if (dx || dy) {
const normalizer = dx && dy ? Math.SQRT1_2 : 1;
const amount = panState.speedPxPerSecond * dt;
canvasShell.scrollLeft += dx * normalizer * amount;
canvasShell.scrollTop += dy * normalizer * amount;
tooltipEl?.classList.remove("visible");
}
panState.raf = requestAnimationFrame(panFrame);
}
function startKeyboardPan() {
if (panState.raf == null) panState.raf = requestAnimationFrame(panFrame);
}
function handlePanKeyDown(event) {
const key = event.key?.toLowerCase?.();
if (!key || !"wasd".includes(key) || isEditableTarget(event.target)) return;
panState.keys.add(key);
startKeyboardPan();
event.preventDefault();
}
function handlePanKeyUp(event) {
const key = event.key?.toLowerCase?.();
if (!key || !"wasd".includes(key)) return;
panState.keys.delete(key);
event.preventDefault();
}
function parseSeed(seedText) {
const numeric = Number.parseInt(seedText, 10);
if (Number.isFinite(numeric)) return numeric >>> 0;
@ -121,6 +185,7 @@ function getStats(map) {
["Map Type", map.terrainTemplate?.terrainTypeLabel || map.terrainDebug?.terrainTypeLabel || "-"],
["Terrain ID", map.terrainTemplate?.terrainType || map.terrainDebug?.terrainType || "-"],
["Generation Total", map.generationTotalMs ? formatMs(map.generationTotalMs) : "-"],
["Geography Basis", map.geographyDebug?.version ? `${map.geographyDebug.version} / ${map.geographyDebug.stage || "-"}` : "-"],
...(map.generationTimings || []).map((row) => [`Time: ${row.label}`, formatMs(row.ms)]),
["Villages", countText(map.villages)],
["Market Towns", countText(map.markets)],
@ -218,8 +283,9 @@ function prefectureNameForCell(map, i) {
function updateTooltip(event) {
if (!state.map || !tooltipEl) return;
const rect = canvas.getBoundingClientRect();
const x = Math.floor((event.clientX - rect.left) / rect.width * state.map.width);
const y = Math.floor((event.clientY - rect.top) / rect.height * state.map.height);
const cell = mapClientToCell(event);
if (!cell) return;
const { x, y } = cell;
if (x < 0 || y < 0 || x >= state.map.width || y >= state.map.height) {
tooltipEl.classList.remove("visible");
return;
@ -318,8 +384,13 @@ function init() {
redraw();
});
canvasShell?.setAttribute("tabindex", "0");
window.addEventListener("keydown", handlePanKeyDown);
window.addEventListener("keyup", handlePanKeyUp);
canvas.addEventListener("mousemove", updateTooltip);
canvas.addEventListener("mouseleave", () => tooltipEl?.classList.remove("visible"));
canvas.addEventListener("mouseleave", () => {
tooltipEl?.classList.remove("visible");
});
regenerate();
}