"use strict";
(function (global) {
const Snapshot = global.TarinaiSnapshot;
const Codec = global.TarinaiSaveCodec;
const Storage = global.TarinaiSaveStorage;
const Restore = global.TarinaiRestoreCoordinator;
if (!Snapshot || !Codec || !Storage || !Restore) throw new Error("save_system.js dependencies are not available");
const SLOT_COUNT = Storage.SLOT_COUNT;
const htmlEscape = global.TarinaiUIHelpers.htmlEscape;
function createSnapshot(worldRef = global.world, options = {}) {
return Snapshot.createSnapshot(worldRef, options);
}
function restoreSnapshot(snapshot, worldRef = global.world, options = {}) {
return Restore.restoreSnapshot(snapshot, worldRef, { syncUi: options.syncUi });
}
async function encodeSnapshot(snapshot = createSnapshot(global.world)) {
return Codec.encodeSnapshot(snapshot);
}
async function decodeSnapshot(text) {
return Codec.decodeSnapshot(text);
}
async function saveSlot(slot) {
const snapshot = createSnapshot(global.world);
const hash = await encodeSnapshot(snapshot);
return Storage.writeSlot(slot, hash, { m: snapshot.m });
}
async function loadSlot(slot) {
const hash = Storage.requireSlot(slot);
const snapshot = await decodeSnapshot(hash);
return restoreSnapshot(snapshot, global.world);
}
function deleteSlot(slot) { Storage.deleteSlot(slot); }
function readSlot(slot) { return Storage.readSlot(slot); }
function formatSlotSummary(entry) {
if (!entry) return "\u7a7a\u304d";
const meta = entry.m || entry;
const m = meta.m || [];
const date = meta.savedAt ? new Date(meta.savedAt).toLocaleString("ja-JP", { month: "2-digit", day: "2-digit", hour: "2-digit", minute: "2-digit" }) : "\u65e5\u6642\u4e0d\u660e";
const day = m[0] || 1;
const population = m[1] || 0;
const fieldType = m[2] || "\u5ead";
const hashLen = entry.hash ? ` / ${entry.hash.length}\u5b57` : "";
return `${date} / ${fieldType} / ${day}\u65e5\u76ee / ${population}\u5339${hashLen}`;
}
function requestConfirmation(options = {}) {
return global.TarinaiGameDialogs?.confirm?.(options) || Promise.resolve(false);
}
function ensureDialog() {
let dialog = document.getElementById("saveDialog");
if (dialog) return dialog;
dialog = document.createElement("div");
dialog.id = "saveDialog";
dialog.className = "field-dialog hidden";
dialog.setAttribute("role", "dialog");
dialog.setAttribute("aria-modal", "true");
dialog.innerHTML = `
\u30bb\u30fc\u30d6 / \u30ed\u30fc\u30c9
\u30bb\u30fc\u30d6\u306f\u5b9f\u9a13\u7684\u6a5f\u80fd\u3067\u3042\u308a\u4fdd\u8a3c\u5bfe\u8c61\u5916\u3067\u3059\u3002
`;
document.body.appendChild(dialog);
return dialog;
}
function renderSlotList() {
const dialog = ensureDialog();
const list = dialog.querySelector("#saveSlotList");
if (!list) return;
list.innerHTML = "";
for (let i = 1; i <= SLOT_COUNT; i++) {
const snapshot = readSlot(i);
const row = document.createElement("div");
row.className = "save-slot-row";
row.innerHTML = `
\u30b9\u30ed\u30c3\u30c8 ${i}
${htmlEscape(formatSlotSummary(snapshot))}
`;
list.appendChild(row);
}
}
function openDialog() {
const dialog = ensureDialog();
renderSlotList();
dialog.classList.remove("hidden");
}
function closeDialog() {
document.getElementById("saveDialog")?.classList.add("hidden");
}
function bindSaveSystem() {
const btn = document.getElementById("saveBtn");
if (!btn || btn.dataset.saveBound === "1") return;
btn.dataset.saveBound = "1";
btn.addEventListener("click", () => {
global.audio?.uiClick?.();
openDialog();
});
const dialog = ensureDialog();
if (dialog.dataset.saveDialogBound === "1") return;
dialog.dataset.saveDialogBound = "1";
dialog.addEventListener("click", async (e) => {
if (e.target === dialog) { closeDialog(); return; }
const saveBtn = e.target.closest("[data-save-slot]");
const loadBtn = e.target.closest("[data-load-slot]");
const deleteBtn = e.target.closest("[data-delete-slot]");
if (saveBtn) {
const slot = Number(saveBtn.dataset.saveSlot);
try {
await saveSlot(slot);
renderSlotList();
global.audio?.notify?.();
global.showToast?.(`\u30b9\u30ed\u30c3\u30c8${slot}\u306b\u30bb\u30fc\u30d6\u3057\u307e\u3057\u305f\u3002`);
} catch (error) {
console.warn(error);
global.showToast?.("\u30bb\u30fc\u30d6\u306b\u5931\u6557\u3057\u307e\u3057\u305f\u3002\u7a7a\u304d\u5bb9\u91cf\u3092\u78ba\u8a8d\u3057\u3066\u304f\u3060\u3055\u3044\u3002");
}
return;
}
if (loadBtn) {
const slot = Number(loadBtn.dataset.loadSlot);
if (!await requestConfirmation({
title: "\u30ed\u30fc\u30c9\u524d\u306e\u78ba\u8a8d",
message: `\u30b9\u30ed\u30c3\u30c8${slot}\u3092\u30ed\u30fc\u30c9\u3057\u307e\u3059\u3002\u73fe\u5728\u306e\u72b6\u614b\u306f\u4e0a\u66f8\u304d\u3055\u308c\u307e\u3059\u3002`,
confirmText: "\u30ed\u30fc\u30c9\u3059\u308b",
})) return;
try {
await loadSlot(slot);
closeDialog();
global.audio?.notify?.();
global.showToast?.(`\u30b9\u30ed\u30c3\u30c8${slot}\u3092\u30ed\u30fc\u30c9\u3057\u307e\u3057\u305f\u3002`);
} catch (error) {
console.warn(error);
global.showToast?.("\u30ed\u30fc\u30c9\u306b\u5931\u6557\u3057\u307e\u3057\u305f\u3002");
}
return;
}
if (deleteBtn) {
const slot = Number(deleteBtn.dataset.deleteSlot);
if (!await requestConfirmation({
title: "\u30bb\u30fc\u30d6\u30c7\u30fc\u30bf\u306e\u524a\u9664",
message: `\u30b9\u30ed\u30c3\u30c8${slot}\u306e\u30bb\u30fc\u30d6\u30c7\u30fc\u30bf\u3092\u524a\u9664\u3057\u307e\u3059\u3002`,
confirmText: "\u524a\u9664\u3059\u308b",
danger: true,
})) return;
deleteSlot(slot);
renderSlotList();
global.audio?.delete?.();
global.showToast?.(`\u30b9\u30ed\u30c3\u30c8${slot}\u3092\u6d88\u3057\u307e\u3057\u305f\u3002`);
return;
}
if (e.target.closest("#saveCloseBtn")) closeDialog();
if (e.target.closest("#saveExportBtn")) {
const exportBtn = dialog.querySelector("#saveExportBtn");
const area = dialog.querySelector("#saveHashText");
if (exportBtn?.dataset.busy === "1") return;
if (exportBtn) {
exportBtn.dataset.busy = "1";
exportBtn.disabled = true;
exportBtn.textContent = "\u751f\u6210\u4e2d...";
}
try {
const hash = await encodeSnapshot(createSnapshot(global.world, { includeAchievements: true }));
if (!hash || typeof hash !== "string") throw new Error("save text was not generated");
if (area) {
area.value = hash;
area.focus();
area.select();
}
global.showToast?.("\u546a\u6587\u3092\u30bb\u30fc\u30d6\u3057\u307e\u3057\u305f\u3002");
} catch (error) {
console.warn("save text generation failed", error);
global.showToast?.("\u546a\u6587\u306e\u751f\u6210\u306b\u5931\u6557\u3057\u307e\u3057\u305f\u3002");
} finally {
if (exportBtn) {
exportBtn.dataset.busy = "0";
exportBtn.disabled = false;
exportBtn.textContent = "\u546a\u6587\u3092\u30bb\u30fc\u30d6";
}
}
return;
}
if (e.target.closest("#saveImportBtn")) {
const area = dialog.querySelector("#saveHashText");
const text = area?.value || "";
if (!text.trim()) { global.showToast?.("\u30ed\u30fc\u30c9\u3059\u308b\u546a\u6587\u3092\u8cbc\u308a\u4ed8\u3051\u3066\u304f\u3060\u3055\u3044\u3002"); return; }
if (!await requestConfirmation({
title: "\u546a\u6587\u306e\u30ed\u30fc\u30c9",
message: "\u8cbc\u308a\u4ed8\u3051\u305f\u546a\u6587\u3092\u30ed\u30fc\u30c9\u3057\u307e\u3059\u3002\u73fe\u5728\u306e\u30d5\u30a3\u30fc\u30eb\u30c9\u306f\u4e0a\u66f8\u304d\u3055\u308c\u307e\u3059\u3002",
confirmText: "\u30ed\u30fc\u30c9\u3059\u308b",
})) return;
try {
const result = restoreSnapshot(await decodeSnapshot(text), global.world);
closeDialog();
global.showToast?.("\u546a\u6587\u304b\u3089\u30ed\u30fc\u30c9\u3057\u307e\u3057\u305f\u3002");
} catch (error) {
console.warn(error);
global.showToast?.("\u30ed\u30fc\u30c9\u306b\u5931\u6557\u3057\u307e\u3057\u305f\u3002\u546a\u6587\u3092\u78ba\u8a8d\u3057\u3066\u304f\u3060\u3055\u3044\u3002");
}
}
});
}
global.TarinaiSaveSystem = { bindSaveSystem };
})(typeof window !== "undefined" ? window : globalThis);