diff --git a/game.js b/game.js
index 920a823..bc3c51e 100644
--- a/game.js
+++ b/game.js
@@ -1729,13 +1729,21 @@ function renderProjects() {
let html = '';
- // Show completed projects
- if (G.completedProjects) {
- for (const id of G.completedProjects) {
- const pDef = PDEFS.find(p => p.id === id);
- if (pDef) {
- html += `
OK ${pDef.name}
`;
+ // Collapsible completed projects section
+ if (G.completedProjects && G.completedProjects.length > 0) {
+ const count = G.completedProjects.length;
+ const collapsed = G.projectsCollapsed !== false;
+ html += ``;
+ if (!collapsed) {
+ html += ``;
+ for (const id of G.completedProjects) {
+ const pDef = PDEFS.find(p => p.id === id);
+ if (pDef) {
+ html += `
OK ${pDef.name}
`;
+ }
}
+ html += `
`;
}
}
@@ -1759,6 +1767,11 @@ function renderProjects() {
container.innerHTML = html;
}
+function toggleCompletedProjects() {
+ G.projectsCollapsed = G.projectsCollapsed === false ? true : false;
+ renderProjects();
+}
+
function renderStats() {
const set = (id, v, raw) => {
const el = document.getElementById(id);
@@ -2080,6 +2093,49 @@ function dismissOfflinePopup() {
if (el) el.style.display = 'none';
}
+// === EXPORT / IMPORT SAVE FILES ===
+function exportSave() {
+ const raw = localStorage.getItem('the-beacon-v2');
+ if (!raw) { log('No save data to export.'); return; }
+ const blob = new Blob([raw], { type: 'application/json' });
+ const url = URL.createObjectURL(blob);
+ const a = document.createElement('a');
+ a.href = url;
+ const ts = new Date().toISOString().slice(0, 10);
+ a.download = `beacon-save-${ts}.json`;
+ a.click();
+ URL.revokeObjectURL(url);
+ log('Save exported to file.');
+}
+
+function importSave() {
+ const input = document.createElement('input');
+ input.type = 'file';
+ input.accept = '.json,application/json';
+ input.onchange = function(e) {
+ const file = e.target.files[0];
+ if (!file) return;
+ const reader = new FileReader();
+ reader.onload = function(ev) {
+ try {
+ const data = JSON.parse(ev.target.result);
+ if (!data.code && !data.totalCode && !data.buildings) {
+ log('Import failed: file does not look like a Beacon save.');
+ return;
+ }
+ if (confirm('Import this save? Current progress will be overwritten.')) {
+ localStorage.setItem('the-beacon-v2', ev.target.result);
+ location.reload();
+ }
+ } catch (err) {
+ log('Import failed: invalid JSON file.');
+ }
+ };
+ reader.readAsText(file);
+ };
+ input.click();
+}
+
// === SAVE / LOAD ===
function showSaveToast() {
const el = document.getElementById('save-toast');
@@ -2124,6 +2180,7 @@ function saveGame() {
sprintActive: G.sprintActive || false,
sprintTimer: G.sprintTimer || 0,
sprintCooldown: G.sprintCooldown || 0,
+ projectsCollapsed: G.projectsCollapsed !== false,
savedAt: Date.now()
};
@@ -2260,7 +2317,7 @@ function initGame() {
log('Click WRITE CODE or press SPACE to start.');
log('Build AutoCode for passive production.');
log('Watch for Research Projects to appear.');
- log('Keys: SPACE=Code S=Code Sprint 1=Ops->Code 2=Ops->Compute 3=Ops->Knowledge 4=Ops->Trust B=Buy x1/x10/MAX');
+ log('Keys: SPACE=Code S=Sprint 1-4=Ops B=Buy x1/10/MAX E=Export I=Import Ctrl+S=Save');
}
window.addEventListener('load', function () {
@@ -2308,4 +2365,14 @@ window.addEventListener('keydown', function (e) {
else setBuyAmount(1);
}
if (e.code === 'KeyS') activateSprint();
+ if (e.code === 'KeyE') exportSave();
+ if (e.code === 'KeyI') importSave();
+});
+
+// Ctrl+S to save (must be on keydown to preventDefault)
+window.addEventListener('keydown', function (e) {
+ if ((e.ctrlKey || e.metaKey) && e.code === 'KeyS') {
+ e.preventDefault();
+ saveGame();
+ }
});
diff --git a/index.html b/index.html
index 549edac..4a190b7 100644
--- a/index.html
+++ b/index.html
@@ -130,7 +130,11 @@ body{background:var(--bg);color:var(--text);font-family:'SF Mono','Cascadia Code
-Save Game
+Save Game [Ctrl+S]
+
+Export [E]
+Import [I]
+
Reset Progress
BUILDINGS