From f0b894a2b67a7d45dbc8485482f8a4ff8142928e Mon Sep 17 00:00:00 2001 From: Alexander Whitestone Date: Sat, 11 Apr 2026 01:24:32 -0400 Subject: [PATCH] =?UTF-8?q?wip:=20fix=20debuff=20corruption=20bug=20?= =?UTF-8?q?=E2=80=94=20debuffs=20no=20longer=20degrade=20boost=20multiplie?= =?UTF-8?q?rs=20on=20each=20updateRates()=20call;=20persist=20playTime=20a?= =?UTF-8?q?cross=20sessions?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- game.js | 44 +++++++++++++++++++++++++++++++------------- 1 file changed, 31 insertions(+), 13 deletions(-) diff --git a/game.js b/game.js index 79079c1..6a3bc89 100644 --- a/game.js +++ b/game.js @@ -1017,17 +1017,23 @@ function updateRates() { G.userRate = 0; G.impactRate = 0; G.rescuesRate = 0; G.opsRate = 0; G.trustRate = 0; G.creativityRate = 0; G.harmonyRate = 0; - // Apply building rates + // Snapshot base boosts BEFORE debuffs modify them + // Without this, debuffs permanently degrade boost multipliers on each updateRates() call + let _codeBoost = G.codeBoost, _computeBoost = G.computeBoost; + let _knowledgeBoost = G.knowledgeBoost, _userBoost = G.userBoost; + let _impactBoost = G.impactBoost; + + // Apply building rates using snapshot boosts (immune to debuff mutation) for (const def of BDEF) { const count = G.buildings[def.id] || 0; if (count > 0 && def.rates) { for (const [resource, baseRate] of Object.entries(def.rates)) { - if (resource === 'code') G.codeRate += baseRate * count * G.codeBoost; - else if (resource === 'compute') G.computeRate += baseRate * count * G.computeBoost; - 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; + if (resource === 'code') G.codeRate += baseRate * count * _codeBoost; + else if (resource === 'compute') G.computeRate += baseRate * count * _computeBoost; + else if (resource === 'knowledge') G.knowledgeRate += baseRate * count * _knowledgeBoost; + else if (resource === 'user') G.userRate += baseRate * count * _userBoost; + else if (resource === 'impact') G.impactRate += baseRate * count * _impactBoost; + else if (resource === 'rescues') G.rescuesRate += baseRate * count * _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; @@ -1108,15 +1114,24 @@ function updateRates() { // Swarm Protocol: buildings auto-code based on click power if (G.swarmFlag === 1) { const totalBuildings = Object.values(G.buildings).reduce((a, b) => a + b, 0); - const clickPower = getClickPower(); - G.swarmRate = totalBuildings * clickPower; + // Compute click power using snapshot boost to avoid debuff mutation + const _clickPower = (1 + Math.floor(G.buildings.autocoder * 0.5) + Math.max(0, (G.phase - 1)) * 2) * _codeBoost; + G.swarmRate = totalBuildings * _clickPower; G.codeRate += G.swarmRate; } - // Apply persistent debuffs from active events + // Apply persistent debuffs to rates (NOT to global boost fields — prevents corruption) if (G.activeDebuffs && G.activeDebuffs.length > 0) { for (const debuff of G.activeDebuffs) { - if (debuff.applyFn) debuff.applyFn(); + switch (debuff.id) { + case 'runner_stuck': G.codeRate *= 0.5; break; + case 'ezra_offline': G.userRate *= 0.3; break; + case 'unreviewed_merge': G.trustRate -= 2; break; + case 'api_rate_limit': G.computeRate *= 0.5; break; + case 'bilbo_vanished': G.creativityRate = 0; break; + case 'memory_leak': G.computeRate *= 0.7; G.opsRate -= 10; break; + case 'community_drama': G.harmonyRate -= 0.5; G.codeRate *= 0.7; break; + } } } } @@ -1182,6 +1197,7 @@ function tick() { } G.tick += dt; + G.playTime += dt; // Sprint ability tickSprint(dt); @@ -2047,7 +2063,7 @@ function renderStats() { set('st-drift', (G.drift || 0).toString()); set('st-resolved', (G.totalEventsResolved || 0).toString()); - const elapsed = Math.floor((Date.now() - G.startedAt) / 1000); + const elapsed = Math.floor(G.playTime || (Date.now() - G.startedAt) / 1000); const m = Math.floor(elapsed / 60); const s = elapsed % 60; set('st-time', `${m}:${s.toString().padStart(2, '0')}`); @@ -2685,6 +2701,7 @@ function saveGame() { swarmRate: G.swarmRate || 0, strategicFlag: G.strategicFlag || 0, projectsCollapsed: G.projectsCollapsed !== false, + playTime: G.playTime || 0, savedAt: Date.now() }; @@ -2716,7 +2733,8 @@ function loadGame() { 'drift', 'driftEnding', 'beaconEnding', 'pendingAlignment', 'lastEventAt', 'totalEventsResolved', 'buyAmount', 'sprintActive', 'sprintTimer', 'sprintCooldown', - 'swarmFlag', 'swarmRate', 'strategicFlag', 'projectsCollapsed' + 'swarmFlag', 'swarmRate', 'strategicFlag', 'projectsCollapsed', + 'playTime' ]; G.isLoading = true;