Compare commits

...

1 Commits

Author SHA1 Message Date
Alexander Whitestone
671ccd6c9e fix: clear activeProjects on ending resolution, guard checkProjects (#130)
Some checks failed
Accessibility Checks / a11y-audit (pull_request) Successful in 12s
Smoke Test / smoke (pull_request) Failing after 17s
After ReCKoning resolution, unrelated normal projects like p_wire_budget
remained active in G.activeProjects. This creates inconsistent endgame
state.

Changes:
1. renderBeaconEnding trigger: G.activeProjects = []
2. renderDriftEnding trigger: G.activeProjects = []
3. checkProjects(): early return if G.beaconEnding || G.driftEnding
   prevents new projects from activating after ending starts

Both ending paths now clear activeProjects and block new activations.

Tests: tests/test_endgame_project_clear.py (5 tests)

Closes #130
2026-04-14 22:23:54 -04:00
2 changed files with 79 additions and 0 deletions

View File

@@ -236,6 +236,8 @@ function tick() {
if (G.drift >= 100 && !G.driftEnding && !G.dismantleActive) {
G.driftEnding = true;
G.running = false;
// Clear active projects — game is ending
G.activeProjects = [];
renderDriftEnding();
}
@@ -243,6 +245,8 @@ function tick() {
if (G.totalRescues >= 100000 && G.pactFlag === 1 && G.harmony > 50 && !G.beaconEnding && typeof Dismantle === 'undefined') {
G.beaconEnding = true;
G.running = false;
// Clear non-endgame active projects — game is ending
G.activeProjects = [];
renderBeaconEnding();
}
@@ -335,6 +339,9 @@ function checkMilestones() {
}
function checkProjects() {
// Endgame guard: don't activate new projects once an ending has started
if (G.beaconEnding || G.driftEnding) return;
// Check for new project triggers
for (const pDef of PDEFS) {
const alreadyPurchased = G.completedProjects && G.completedProjects.includes(pDef.id);

View File

@@ -0,0 +1,72 @@
import pathlib
import re
import unittest
ROOT = pathlib.Path(__file__).resolve().parents[1]
ENGINE_JS = ROOT / 'js' / 'engine.js'
class TestEndgameProjectClear(unittest.TestCase):
"""Verify ending transitions clear activeProjects and prevent new activations."""
@classmethod
def setUpClass(cls):
cls.js = ENGINE_JS.read_text()
def test_beacon_ending_clears_active_projects(self):
"""Beacon ending should clear G.activeProjects."""
# Find the beacon ending trigger section
match = re.search(
r'G\.beaconEnding\s*=\s*true;.*?renderBeaconEnding\(\)',
self.js, re.DOTALL
)
self.assertIsNotNone(match, 'Expected beacon ending trigger to exist.')
section = match.group(0)
self.assertIn('activeProjects', section,
'Beacon ending should reference activeProjects to clear them.')
def test_drift_ending_clears_active_projects(self):
"""Drift ending should clear G.activeProjects."""
match = re.search(
r'G\.driftEnding\s*=\s*true;.*?renderDriftEnding\(\)',
self.js, re.DOTALL
)
self.assertIsNotNone(match, 'Expected drift ending trigger to exist.')
section = match.group(0)
self.assertIn('activeProjects', section,
'Drift ending should reference activeProjects to clear them.')
def test_check_projects_has_endgame_guard(self):
"""checkProjects() should guard against ending state."""
match = re.search(
r'function checkProjects\(\).*?(?=function |\Z)',
self.js, re.DOTALL
)
self.assertIsNotNone(match, 'Expected checkProjects function to exist.')
func = match.group(0)
self.assertIn('beaconEnding', func,
'checkProjects should check beaconEnding state.')
self.assertIn('driftEnding', func,
'checkProjects should check driftEnding state.')
def test_beacon_ending_sets_empty_array(self):
"""Beacon ending should set activeProjects to empty array, not null."""
match = re.search(
r'G\.beaconEnding\s*=\s*true;.*?renderBeaconEnding\(\)',
self.js, re.DOTALL
)
self.assertIsNotNone(match)
section = match.group(0)
self.assertIn('[]', section,
'Beacon ending should set activeProjects = []')
def test_drift_ending_sets_empty_array(self):
"""Drift ending should set activeProjects to empty array, not null."""
match = re.search(
r'G\.driftEnding\s*=\s*true;.*?renderDriftEnding\(\)',
self.js, re.DOTALL
)
self.assertIsNotNone(match)
section = match.group(0)
self.assertIn('[]', section,
'Drift ending should set activeProjects = []')