(function (root) { "use strict"; const Data = root.PrincessLimaData; const Systems = root.PrincessLimaSystems; function create(container, actions) { const overlay = container.querySelector("[data-lima-overlay]"); const panel = container.querySelector("[data-lima-panel]"); const title = container.querySelector("[data-lima-panel-title]"); const body = container.querySelector("[data-lima-panel-body]"); const closeButton = container.querySelector("[data-lima-panel-close]"); let returnFocus = null; let dialogue = null; let dialogueIndex = 0; function lock(value) { actions.onLock(value); overlay.hidden = !value; container.classList.toggle("is-overlay-open", value); } function show(kind, heading, html, closable) { returnFocus = document.activeElement; panel.dataset.kind = kind; title.textContent = heading; body.innerHTML = html; closeButton.hidden = closable === false; lock(true); const target = body.querySelector("button, input, select") || closeButton; target.focus(); } function close() { if (dialogue) return advanceDialogue(); lock(false); body.innerHTML = ""; if (returnFocus && document.contains(returnFocus)) returnFocus.focus(); else container.querySelector("[data-lima-game]").focus(); } function openDialogue(id, done) { const npc = Data.NPCS[id]; if (!npc) return; dialogue = { id, lines: npc.dialogue.slice(), done }; dialogueIndex = 0; renderDialogue(); } function renderDialogue() { const npc = Data.NPCS[dialogue.id]; const line = dialogue.lines[dialogueIndex]; show("dialogue", npc.name, `

${escapeHtml(line)}

`, false); body.querySelector("[data-lima-advance]").addEventListener("click", advanceDialogue, { once: true }); } function advanceDialogue() { if (!dialogue) return; dialogueIndex += 1; if (dialogueIndex < dialogue.lines.length) return renderDialogue(); const done = dialogue.done; dialogue = null; lock(false); body.innerHTML = ""; container.querySelector("[data-lima-game]").focus(); if (done) done(); } function openPanel(kind, state) { if (kind === "inventory") { const entries = state.inventory.length ? state.inventory.map((entry) => { const item = Data.ITEMS[entry.id]; const equipped = Object.values(state.equipment).includes(entry.id) ? " · Equipped" : ""; return `
  • ${escapeHtml(item.name)}×${entry.quantity}${equipped}

    ${escapeHtml(item.description)}

  • `; }).join("") : "
  • No items yet.
  • "; show("inventory", "Inventory", ``); const use = body.querySelector("[data-lima-use-tonic]"); use.addEventListener("click", () => { actions.onUseTonic(); openPanel("inventory", actions.getState()); }); } else if (kind === "quests") { const quests = Data.QUEST_IDS.filter((id) => state.quests[id].status !== "locked").map((id) => { const quest = Data.QUESTS[id]; const progress = state.quests[id]; return `
  • ${quest.main ? "Main · " : "Optional · "}${escapeHtml(quest.title)} ${progress.status === "complete" ? "Complete" : `${progress.count} / ${quest.target}`}

    ${escapeHtml(quest.description)} · ${escapeHtml(quest.region)}

  • `; }).join(""); show("quests", "Quest Log", `

    ${escapeHtml(Systems.currentObjective(state))}

    `); } else if (kind === "settings") { show("settings", "Settings", ` `); body.querySelectorAll("[data-setting]").forEach((control) => control.addEventListener("change", () => { const value = control.type === "checkbox" ? control.checked : control.type === "range" ? Number(control.value) : control.value; actions.onSetting(control.dataset.setting, value); })); } else if (kind === "pause") { show("pause", "Paused", `

    ${escapeHtml(Systems.currentObjective(state))}

    Exit to Website
    `); body.querySelectorAll("[data-panel]").forEach((button) => button.addEventListener("click", () => openPanel(button.dataset.panel, actions.getState()))); body.querySelector("[data-lima-replay-intro-panel]").addEventListener("click", actions.onReplayIntro); body.querySelector("[data-lima-fullscreen-panel]").addEventListener("click", actions.onFullscreen); body.querySelector("[data-lima-reset-request]").addEventListener("click", confirmReset); } } function confirmReset() { show("confirm", "Delete this adventure?", `

    This permanently removes the Princess Lima save on this device.

    `, false); body.querySelector("[data-confirm-reset]").addEventListener("click", actions.onReset, { once: true }); body.querySelector("[data-cancel-reset]").addEventListener("click", () => openPanel("pause", actions.getState()), { once: true }); } function gameOver(state) { show("defeat", "The road is not finished", `

    You awaken at the latest safe checkpoint with your quests and important items intact.

    `, false); body.querySelector("[data-respawn]").addEventListener("click", actions.onRespawn, { once: true }); } function ending(state) { show("ending", "Princess Lima Rescued", `

    At dawn, the roads reopen. The villages ring their bells, the forest paths quiet, and the mountain fires become beacons instead of warnings.

    ${escapeHtml(state.player.name)} is offered a place at the royal table—and chooses first to walk the repaired road home with Lima.

    The kingdom remains explorable from your final save.

    Exit to Website `, false); body.querySelector("[data-ending-continue]").addEventListener("click", close, { once: true }); } closeButton.addEventListener("click", close); overlay.addEventListener("click", (event) => { if (event.target === overlay && !dialogue) close(); }); document.addEventListener("keydown", (event) => { if (event.key === "Escape" && !overlay.hidden && !dialogue) { event.preventDefault(); close(); } if ((event.key === "Enter" || event.key === " ") && dialogue && !overlay.hidden) { event.preventDefault(); advanceDialogue(); } if (!overlay.hidden && !dialogue && ["ArrowDown", "ArrowUp"].includes(event.key)) { const controls = Array.from(panel.querySelectorAll("button:not([hidden]), a[href], input, select")) .filter((control) => !control.disabled && control.offsetParent !== null); if (!controls.length) return; event.preventDefault(); const current = controls.indexOf(document.activeElement); const change = event.key === "ArrowDown" ? 1 : -1; controls[(current + change + controls.length) % controls.length].focus(); } }); return Object.freeze({ show, close, openDialogue, openPanel, gameOver, ending, confirmReset }); } function escapeHtml(value) { return String(value).replace(/[&<>"']/g, (character) => ({ "&": "&", "<": "<", ">": ">", '"': """, "'": "'" }[character])); } root.PrincessLimaUI = Object.freeze({ create }); }(typeof globalThis !== "undefined" ? globalThis : this));