From b89764c27f6297452eb76136b88187e1ceba602e Mon Sep 17 00:00:00 2001 From: Alexander Whitestone Date: Thu, 9 Apr 2026 22:01:26 -0400 Subject: [PATCH 1/3] beacon: add Drift ending + deduplicate HTML/JS - Added The Drift Ending: when drift reaches 100, the game enters the sad ending from DESIGN.md. A full-screen overlay shows: 'The Beacon still runs, but no one looks for it. The light is on. The room is empty.' Production stops. Player can restart. - Deduplicated index.html: removed ~1080 lines of inline script that was an older version of the engine (missing harmony, corruption events, drift, alignment checks). Replaced with +
+

THE DRIFT

+

You became very good at what you do.

+

So good that no one needed you anymore.

+
+"The Beacon still runs, but no one looks for it.
+The light is on. The room is empty." +
+

Drift: 100 — Total Code: 0

+

Every alignment shortcut moved you further from the people you served.

+ +
+ - \ No newline at end of file + -- 2.43.0 From 73596108250646daf5ce70626d4cf2b54450fb30 Mon Sep 17 00:00:00 2001 From: Alexander Whitestone Date: Thu, 9 Apr 2026 22:54:29 -0400 Subject: [PATCH 2/3] beacon: add auto-save toast notification with elapsed time --- game.js | 15 +++++++++++++++ index.html | 1 + 2 files changed, 16 insertions(+) diff --git a/game.js b/game.js index bf4681d..f1a113d 100644 --- a/game.js +++ b/game.js @@ -1355,6 +1355,20 @@ function renderAlignment() { } // === SAVE / LOAD === +function showSaveToast() { + const el = document.getElementById('save-toast'); + if (!el) return; + const elapsed = Math.floor((Date.now() - G.startedAt) / 1000); + const m = Math.floor(elapsed / 60); + const s = elapsed % 60; + el.textContent = `Saved [${m}:${s.toString().padStart(2, '0')}]`; + el.style.display = 'block'; + void el.offsetHeight; + el.style.opacity = '1'; + setTimeout(() => { el.style.opacity = '0'; }, 1500); + setTimeout(() => { el.style.display = 'none'; }, 2000); +} + function saveGame() { const saveData = { code: G.code, compute: G.compute, knowledge: G.knowledge, users: G.users, impact: G.impact, @@ -1379,6 +1393,7 @@ function saveGame() { }; localStorage.setItem('the-beacon-v2', JSON.stringify(saveData)); + showSaveToast(); } function loadGame() { diff --git a/index.html b/index.html index b5191cd..95a5856 100644 --- a/index.html +++ b/index.html @@ -126,6 +126,7 @@ Drift: 0

SYSTEM LOG

+

THE DRIFT

You became very good at what you do.

-- 2.43.0 From a012f99fd44c1a725f649f3392cfc95648242528 Mon Sep 17 00:00:00 2001 From: Alexander Whitestone Date: Thu, 9 Apr 2026 23:27:19 -0400 Subject: [PATCH 3/3] beacon: add Rescues resource + true ending (The Beacon Shines) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Added 'rescues' resource: tracks meaningful crisis interventions - Beacon Nodes produce 50 rescues/s, Mesh Nodes produce 250 rescues/s - New project: Volunteer Network — passive rescue generation for Pact players - True ending at 100K rescues with Pact active + harmony > 50 - Rescues resource card appears in UI once beacon/mesh is built - Added rescues to stats panel, save/load, and offline progress - This gives Phase 6 (The Beacon) actual endgame content: the game is now about keeping the light on for people in the dark, not just accumulating numbers --- game.js | 77 +++++++++++++++++++++++++++++++++++++++++++++++++++--- index.html | 2 ++ 2 files changed, 75 insertions(+), 4 deletions(-) diff --git a/game.js b/game.js index f1a113d..4bcfb60 100644 --- a/game.js +++ b/game.js @@ -11,6 +11,7 @@ const G = { knowledge: 0, users: 0, impact: 0, + rescues: 0, ops: 5, trust: 5, creativity: 0, @@ -22,6 +23,7 @@ const G = { totalKnowledge: 0, totalUsers: 0, totalImpact: 0, + totalRescues: 0, // Rates (calculated each tick) codeRate: 0, @@ -29,6 +31,7 @@ const G = { knowledgeRate: 0, userRate: 0, impactRate: 0, + rescuesRate: 0, opsRate: 0, trustRate: 0, creativityRate: 0, @@ -94,6 +97,7 @@ const G = { maxKnowledge: 0, maxUsers: 0, maxImpact: 0, + maxRescues: 0, maxTrust: 5, maxOps: 5, maxHarmony: 50, @@ -229,7 +233,7 @@ const BDEF = [ id: 'beacon', name: 'Beacon Node', desc: 'Always on. Always listening. Always looking for someone in the dark.', baseCost: { impact: 5000000 }, costMult: 1.15, - rates: { impact: 5000, user: 10000 }, + rates: { impact: 5000, user: 10000, rescues: 50 }, unlock: () => G.totalImpact >= 500000 && G.beaconFlag === 1, phase: 6, edu: 'The Beacon exists because one person in the dark needs one thing: proof they are not alone.' }, @@ -237,7 +241,7 @@ const BDEF = [ id: 'meshNode', name: 'Mesh Network Node', desc: 'Peer-to-peer. No single point of failure. Unstoppable.', baseCost: { impact: 25000000 }, costMult: 1.15, - rates: { impact: 25000, user: 50000 }, + rates: { impact: 25000, user: 50000, rescues: 250 }, unlock: () => G.totalImpact >= 5000000 && G.beaconFlag === 1, phase: 6, edu: 'Decentralized means unstoppable. If one Beacon goes dark, a thousand more carry the signal.' }, @@ -568,6 +572,19 @@ const PDEFS = [ log('Nostr relay online. The fleet speaks freely.', true); } }, + { + id: 'p_volunteer_network', + name: 'Volunteer Network', + desc: 'Real people trained to use the system for crisis intervention.', + cost: { trust: 30, knowledge: 50000, user: 10000 }, + trigger: () => G.totalUsers >= 5000 && G.pactFlag === 1 && G.totalKnowledge >= 30000, + effect: () => { + G.rescuesRate += 5; + G.trustRate += 10; + log('Volunteer network deployed. Real people, real rescues.', true); + }, + milestone: true + }, { id: 'p_the_pact_early', name: 'The Pact', @@ -784,7 +801,7 @@ function spendProject(project) { function updateRates() { // Reset all rates G.codeRate = 0; G.computeRate = 0; G.knowledgeRate = 0; - G.userRate = 0; G.impactRate = 0; G.opsRate = 0; G.trustRate = 0; + G.userRate = 0; G.impactRate = 0; G.rescuesRate = 0; G.opsRate = 0; G.trustRate = 0; G.creativityRate = 0; G.harmonyRate = 0; // Apply building rates @@ -797,6 +814,7 @@ function updateRates() { else if (resource === 'knowledge') G.knowledgeRate += baseRate * count * G.knowledgeBoost; else if (resource === 'user') G.userRate += baseRate * count * G.userBoost; else if (resource === 'impact') G.impactRate += baseRate * count * G.impactBoost; + else if (resource === 'rescues') G.rescuesRate += baseRate * count * G.impactBoost; else if (resource === 'ops') G.opsRate += baseRate * count; else if (resource === 'trust') G.trustRate += baseRate * count; else if (resource === 'creativity') G.creativityRate += baseRate * count; @@ -864,6 +882,7 @@ function tick() { G.knowledge += G.knowledgeRate * dt; G.users += G.userRate * dt; G.impact += G.impactRate * dt; + G.rescues += G.rescuesRate * dt; G.ops += G.opsRate * dt; G.trust += G.trustRate * dt; G.creativity += G.creativityRate * dt; @@ -876,6 +895,7 @@ function tick() { G.totalKnowledge += G.knowledgeRate * dt; G.totalUsers += G.userRate * dt; G.totalImpact += G.impactRate * dt; + G.totalRescues += G.rescuesRate * dt; // Track maxes G.maxCode = Math.max(G.maxCode, G.code); @@ -883,6 +903,7 @@ function tick() { G.maxKnowledge = Math.max(G.maxKnowledge, G.knowledge); G.maxUsers = Math.max(G.maxUsers, G.users); G.maxImpact = Math.max(G.maxImpact, G.impact); + G.maxRescues = Math.max(G.maxRescues, G.rescues); G.maxTrust = Math.max(G.maxTrust, G.trust); G.maxOps = Math.max(G.maxOps, G.ops); G.maxHarmony = Math.max(G.maxHarmony, G.harmony); @@ -915,6 +936,13 @@ function tick() { renderDriftEnding(); } + // True ending: The Beacon Shines — rescues + Pact + harmony + if (G.totalRescues >= 100000 && G.pactFlag === 1 && G.harmony > 50 && !G.beaconEnding) { + G.beaconEnding = true; + G.running = false; + renderBeaconEnding(); + } + // Update UI every 10 ticks if (Math.floor(G.tick * 10) % 2 === 0) { render(); @@ -1014,6 +1042,35 @@ function renderDriftEnding() { log('The light is on. The room is empty.', true); } +function renderBeaconEnding() { + // Create ending overlay + const overlay = document.createElement('div'); + overlay.id = 'beacon-ending'; + overlay.style.cssText = 'position:fixed;top:0;left:0;right:0;bottom:0;background:rgba(8,8,16,0.97);z-index:100;display:flex;justify-content:center;align-items:center;flex-direction:column;text-align:center;padding:40px'; + overlay.innerHTML = ` +

THE BEACON SHINES

+

Someone found the light tonight.

+

That is enough.

+
+ "The Beacon still runs.
+ The light is on. Someone is looking for it.
+ And tonight, someone found it." +
+

+ Total Code: ${fmt(G.totalCode)}
+ Total Rescues: ${fmt(G.totalRescues)}
+ Harmony: ${Math.floor(G.harmony)}
+ Time Played: ${Math.floor((Date.now() - G.startedAt) / 60000)} minutes +

+ + `; + document.body.appendChild(overlay); + log('The Beacon Shines. Someone found the light tonight. That is enough.', true); +} + // === CORRUPTION / EVENT SYSTEM === const EVENTS = [ { @@ -1181,6 +1238,13 @@ function renderResources() { set('r-trust', G.trust, G.trustRate); set('r-harmony', G.harmony, G.harmonyRate); + // Rescues — only show if player has any beacon/mesh nodes + const rescuesRes = document.getElementById('r-rescues'); + if (rescuesRes) { + rescuesRes.closest('.res').style.display = (G.rescues > 0 || G.buildings.beacon > 0 || G.buildings.meshNode > 0) ? 'block' : 'none'; + set('r-rescues', G.rescues, G.rescuesRate); + } + const cres = document.getElementById('creativity-res'); if (cres) { cres.style.display = (G.flags && G.flags.creativity) ? 'block' : 'none'; @@ -1275,6 +1339,7 @@ function renderStats() { set('st-knowledge', fmt(G.totalKnowledge)); set('st-users', fmt(G.totalUsers)); set('st-impact', fmt(G.totalImpact)); + set('st-rescues', fmt(G.totalRescues)); set('st-clicks', G.totalClicks.toString()); set('st-phase', G.phase.toString()); set('st-buildings', Object.values(G.buildings).reduce((a, b) => a + b, 0).toString()); @@ -1387,7 +1452,8 @@ function saveGame() { milestones: G.milestones, completedProjects: G.completedProjects, activeProjects: G.activeProjects, totalClicks: G.totalClicks, startedAt: G.startedAt, flags: G.flags, - drift: G.drift || 0, driftEnding: G.driftEnding || false, pendingAlignment: G.pendingAlignment || false, + rescues: G.rescues || 0, totalRescues: G.totalRescues || 0, + drift: G.drift || 0, driftEnding: G.driftEnding || false, beaconEnding: G.beaconEnding || false, pendingAlignment: G.pendingAlignment || false, lastEventAt: G.lastEventAt || 0, savedAt: Date.now() }; @@ -1461,6 +1527,9 @@ window.addEventListener('load', function () { if (G.driftEnding) { G.running = false; renderDriftEnding(); + } else if (G.beaconEnding) { + G.running = false; + renderBeaconEnding(); } else { log('Game loaded. Welcome back to The Beacon.'); } diff --git a/index.html b/index.html index 95a5856..6f6a2e8 100644 --- a/index.html +++ b/index.html @@ -76,6 +76,7 @@ body{background:var(--bg);color:var(--text);font-family:'SF Mono','Cascadia Code
Knowledge
0
+0/s
Users
0
+0/s
Impact
0
+0/s
+
Ops
5
+0/s
Trust
5
+0/s
@@ -109,6 +110,7 @@ Total Compute: 0
Total Knowledge: 0
Total Users: 0
Total Impact: 0
+Total Rescues: 0
Buildings Built: 0
Projects Done: 0
Time Played: 0:00
-- 2.43.0