215 lines
8.2 KiB
JavaScript
215 lines
8.2 KiB
JavaScript
"use strict";
|
|
|
|
function escapeHtml(value) { return window.TarinaiUIHelpers.htmlEscape(value); }
|
|
|
|
function resizeCanvas() {
|
|
const rect = canvas.getBoundingClientRect();
|
|
const perfScale = window.TarinaiPerf.dprScale();
|
|
const dpr = Math.max(0.5, Math.min((window.devicePixelRatio || 1) * perfScale, 2));
|
|
const nextW = Math.max(1, Math.floor(rect.width * dpr));
|
|
const nextH = Math.max(1, Math.floor(rect.height * dpr));
|
|
if (canvas.width === nextW && canvas.height === nextH && uiCache.canvasDpr === dpr) return;
|
|
uiCache.canvasDpr = dpr;
|
|
canvas.width = nextW;
|
|
canvas.height = nextH;
|
|
ctx.setTransform(dpr, 0, 0, dpr, 0, 0);
|
|
ctx.fillStyle = "#cfe3d3";
|
|
ctx.fillRect(0, 0, Math.max(1, rect.width), Math.max(1, rect.height));
|
|
world.setViewportSize(Math.max(1, rect.width), Math.max(1, rect.height));
|
|
uiCache.lastChartDraw = "";
|
|
if (typeof render === "function") render();
|
|
}
|
|
|
|
function applyFieldLayout(fieldType = world.fieldType || "garden") {
|
|
const field = FIELD_TYPES?.[fieldType] || FIELD_TYPES.garden;
|
|
const layout = FIELD_LAYOUT;
|
|
const root = document.documentElement;
|
|
root.style.setProperty("--field-height", layout.height);
|
|
root.style.setProperty("--field-min-height", layout.minHeight);
|
|
root.style.setProperty("--field-mobile-height", layout.mobileHeight);
|
|
root.style.setProperty("--field-mobile-min-height", layout.mobileMinHeight);
|
|
world.fieldType = field.id;
|
|
const rect = canvas.getBoundingClientRect();
|
|
world.setViewportSize(rect.width, rect.height);
|
|
resizeCanvas();
|
|
}
|
|
|
|
function normalizeResetPresetId(presetId = "default") {
|
|
if (window.TarinaiResetPresets?.normalize) return window.TarinaiResetPresets.normalize(presetId);
|
|
return new Set(["empty", "default", "athletic", "crowded", "family", "war", "happy", "hair_trigger"]).has(String(presetId || "")) ? String(presetId) : "default";
|
|
}
|
|
|
|
function syncFieldDialogChoices() {
|
|
const fieldType = FIELD_TYPES?.[uiCache.resetFieldType]?.id || world?.fieldType || "garden";
|
|
const presetId = normalizeResetPresetId(uiCache.resetPreset || "default");
|
|
uiCache.resetFieldType = fieldType;
|
|
uiCache.resetPreset = presetId;
|
|
ui.fieldDialog?.querySelectorAll("[data-field-type]").forEach((btn) => {
|
|
const active = btn.dataset.fieldType === fieldType;
|
|
btn.classList.toggle("active", active);
|
|
btn.setAttribute("aria-pressed", active ? "true" : "false");
|
|
});
|
|
ui.fieldDialog?.querySelectorAll("[data-reset-preset]").forEach((btn) => {
|
|
const active = btn.dataset.resetPreset === presetId;
|
|
btn.classList.toggle("active", active);
|
|
btn.setAttribute("aria-pressed", active ? "true" : "false");
|
|
});
|
|
}
|
|
|
|
function selectResetFieldType(fieldType = "garden") {
|
|
uiCache.resetFieldType = FIELD_TYPES?.[fieldType]?.id || "garden";
|
|
syncFieldDialogChoices();
|
|
}
|
|
|
|
function selectResetPreset(presetId = "default") {
|
|
uiCache.resetPreset = normalizeResetPresetId(presetId);
|
|
syncFieldDialogChoices();
|
|
}
|
|
|
|
function openFieldDialog() {
|
|
uiCache.resetFieldType = FIELD_TYPES?.[world?.fieldType]?.id || uiCache.resetFieldType || "garden";
|
|
uiCache.resetPreset = normalizeResetPresetId(uiCache.resetPreset || "default");
|
|
syncFieldDialogChoices();
|
|
ui.fieldDialog?.classList.remove("hidden");
|
|
}
|
|
|
|
function closeFieldDialog() {
|
|
ui.fieldDialog?.classList.add("hidden");
|
|
}
|
|
|
|
function hydrateLazyImages(root) {
|
|
const scope = root || document;
|
|
for (const img of scope.querySelectorAll("img[data-src]")) {
|
|
if (!img.getAttribute("src")) img.setAttribute("src", img.dataset.src || "");
|
|
}
|
|
}
|
|
|
|
function renderEcologyCards() {
|
|
if (!ui.ecologyGrid || ui.ecologyGrid.dataset.ready === "1") return;
|
|
const cards = window.TEXT_CATALOG?.ecologyCards || [];
|
|
ui.ecologyGrid.innerHTML = cards.map(card => `
|
|
<section class="ecology-card">
|
|
<img data-src="${escapeHtml(card.image || "assets/sprites/tarinai_01_smile.webp")}" loading="lazy" decoding="async" alt="">
|
|
<div><strong>${escapeHtml(card.title || "")}</strong><p>${escapeHtml(card.text || "")}</p></div>
|
|
</section>`).join("");
|
|
ui.ecologyGrid.dataset.ready = "1";
|
|
}
|
|
|
|
function openEcologyDialog() {
|
|
renderEcologyCards();
|
|
hydrateLazyImages(ui.ecologyDialog);
|
|
ui.ecologyDialog?.classList.remove("hidden");
|
|
}
|
|
|
|
function closeEcologyDialog() {
|
|
ui.ecologyDialog?.classList.add("hidden");
|
|
}
|
|
|
|
function openArchiveResetDialog() {
|
|
ui.archiveResetDialog?.classList.remove("hidden");
|
|
}
|
|
|
|
function closeArchiveResetDialog() {
|
|
ui.archiveResetDialog?.classList.add("hidden");
|
|
}
|
|
|
|
function resetWithField(fieldType, presetId = uiCache.resetPreset || "default") {
|
|
const field = FIELD_TYPES?.[fieldType] || FIELD_TYPES.garden;
|
|
const preset = normalizeResetPresetId(presetId);
|
|
uiCache.resetFieldType = field.id;
|
|
uiCache.resetPreset = preset;
|
|
closeFieldDialog();
|
|
applyFieldLayout(field.id);
|
|
world.reset(null, field.id, preset);
|
|
uiCache.archiveVersion = "";
|
|
renderLog(world.logs);
|
|
renderArchive();
|
|
renderStats();
|
|
render();
|
|
}
|
|
|
|
|
|
function resizeFieldFromWheel(e) {
|
|
const itemType = toolItemType(world?.tool || "");
|
|
if ((e.shiftKey || e.altKey) && itemType && isRotatableItemType(itemType) && world?.rotateToolAngle) {
|
|
e.preventDefault();
|
|
const step = (e.altKey ? 1 : 5) * Math.PI / 180;
|
|
world.rotateToolAngle(itemType, (e.deltaY > 0 ? 1 : -1) * step);
|
|
render();
|
|
return;
|
|
}
|
|
if (!world.setFieldZoom) return;
|
|
e.preventDefault();
|
|
const rect = canvas.getBoundingClientRect();
|
|
const sx = e.clientX - rect.left;
|
|
const sy = e.clientY - rect.top;
|
|
const focus = world.screenToWorld ? world.screenToWorld(sx, sy) : { x: sx, y: sy };
|
|
const factor = e.deltaY > 0 ? 1 / 1.08 : 1.08;
|
|
if (!world.setFieldZoom((world.fieldZoom || 1) * factor)) return;
|
|
const off = world.fieldScreenOffset ? world.fieldScreenOffset() : { x: 0, y: 0 };
|
|
const scale = world.viewScale ? world.viewScale() : 1;
|
|
world.cameraX = focus.x - (sx - off.x) / scale;
|
|
world.cameraY = focus.y - (sy - off.y) / scale;
|
|
world.clampCamera?.();
|
|
render();
|
|
}
|
|
|
|
function selectTool(tool) {
|
|
const wasCopyArmed = world.tool === "copy" && tool === "copy" && Boolean(world.copyBuffer);
|
|
const result = window.TarinaiCommands.dispatch(world, { type: "tool.select", toolId: tool });
|
|
const activeTool = result?.toolId || world.tool || tool;
|
|
if (!["rope", "rod", "spring", "wire", "insulated_wire"].includes(activeTool)) world.pendingLinkEndpoint = null;
|
|
if (activeTool !== "copy") world.copyBuffer = null;
|
|
else if (wasCopyArmed) {
|
|
world.copyBuffer = null;
|
|
showToast("\u30b3\u30d4\u30fc\u3092\u89e3\u9664\u3057\u307e\u3057\u305f\u3002");
|
|
}
|
|
for (const btn of uiCache.toolButtons) {
|
|
btn.classList.toggle("selected", btn.dataset.tool === activeTool);
|
|
}
|
|
syncToolSizeBadges();
|
|
canvas.style.cursor = window.TarinaiUIInputShared.cursorForTool(activeTool) || (activeTool === "observe" ? "default" : (activeTool === "pinch" ? "grab" : (activeTool === "poke" || activeTool === "delete" ? "pointer" : "crosshair")));
|
|
}
|
|
|
|
function showToast(text) {
|
|
ui.toast.textContent = text;
|
|
ui.toast.classList.remove("hidden");
|
|
clearTimeout(showToast._timer);
|
|
showToast._timer = setTimeout(() => ui.toast.classList.add("hidden"), 1400);
|
|
}
|
|
|
|
function syncTopButtons() {
|
|
ui.pauseBtn.textContent = world.paused ? "\u518d\u958b" : "\u4e00\u6642\u505c\u6b62";
|
|
ui.speedBtn.textContent = `\u901f\u5ea6 x${world.speed}`;
|
|
syncAudioControls();
|
|
syncArchiveUpdateButton();
|
|
}
|
|
|
|
function archiveAutoUpdateEnabled() {
|
|
return Boolean(uiCache.archiveUpdateEnabled);
|
|
}
|
|
|
|
function syncArchiveUpdateButton() {
|
|
const btn = ui.archiveUpdateToggleBtn;
|
|
if (!btn) return;
|
|
const on = archiveAutoUpdateEnabled();
|
|
btn.textContent = on ? "\u66f4\u65b0 ON" : "\u66f4\u65b0 OFF";
|
|
btn.classList.toggle("active", on);
|
|
btn.setAttribute("aria-pressed", on ? "true" : "false");
|
|
}
|
|
|
|
function setArchiveUpdateEnabled(on) {
|
|
uiCache.archiveUpdateEnabled = Boolean(on);
|
|
syncArchiveUpdateButton();
|
|
if (uiCache.archiveUpdateEnabled) {
|
|
uiCache.archiveVersion = "";
|
|
uiCache.archiveFamilyVersion = null;
|
|
world.familyTreeDirty = true;
|
|
scheduleArchiveWindowRender?.();
|
|
showToast("\u5bb6\u7cfb\u56f3\u306e\u66f4\u65b0\u3092ON\u306b\u3057\u307e\u3057\u305f\u3002");
|
|
} else {
|
|
if (uiCache.archiveRenderTimer) { clearTimeout(uiCache.archiveRenderTimer); uiCache.archiveRenderTimer = 0; }
|
|
uiCache.archiveScheduled = false;
|
|
showToast("\u5bb6\u7cfb\u56f3\u306e\u66f4\u65b0\u3092OFF\u306b\u3057\u307e\u3057\u305f\u3002");
|
|
}
|
|
}
|