Compare commits
2 Commits
sprint/iss
...
burn/159-1
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
62871bc373 | ||
| 729343e503 |
142
docs/DISMANTLE_STATUS.md
Normal file
142
docs/DISMANTLE_STATUS.md
Normal file
@@ -0,0 +1,142 @@
|
||||
# The Dismantle Sequence (The Unbuilding) — Implementation Status
|
||||
|
||||
**Status:** ✅ **COMPLETE**
|
||||
**Last Updated:** 2026-04-14
|
||||
**Related Issues:** #16, #133, #145
|
||||
**Implementation:** `js/dismantle.js` (570 lines)
|
||||
**Tests:** `tests/dismantle.test.cjs` (10/10 passing)
|
||||
|
||||
---
|
||||
|
||||
## Overview
|
||||
|
||||
The Dismantle Sequence (The Unbuilding) is the emotional endgame of The Beacon. Inspired by the Paperclips REJECT path, it strips away UI panels one by one until only a single golden beacon remains — "That is enough."
|
||||
|
||||
## Implementation Status
|
||||
|
||||
| Component | Status | Notes |
|
||||
|-----------|--------|-------|
|
||||
| 8-stage dismantle sequence | ✅ Complete | Stages 1-8 + final |
|
||||
| Save/load persistence | ✅ Complete | Full state restoration |
|
||||
| Defer mechanism | ✅ Complete | "NOT YET" button with cooldown |
|
||||
| Final overlay | ✅ Complete | Golden beacon dot + stats |
|
||||
| Resource dissolution | ✅ Complete | Quantum chips pattern |
|
||||
| Animations | ✅ Complete | Smooth fade-outs |
|
||||
| Tests | ✅ Complete | 10/10 passing |
|
||||
|
||||
## Stages
|
||||
|
||||
### Stage 1: Hide Research Projects Panel
|
||||
Hides the `#project-panel` element with fade-out animation.
|
||||
|
||||
### Stage 2: Hide Buildings List
|
||||
Hides the `#buildings` section and its header.
|
||||
|
||||
### Stage 3: Hide Strategy Engine + Combat
|
||||
Hides both `#strategy-panel` and `#combat-panel`.
|
||||
|
||||
### Stage 4: Hide Education Panel
|
||||
Hides the `#edu-panel` element.
|
||||
|
||||
### Stage 5: Resource Dissolution
|
||||
Resources disappear one by one using the quantum chips pattern:
|
||||
- Harmony → Creativity → Trust → Operations → Rescues → Impact → Users → Knowledge → Compute → Code
|
||||
- Timed at specific tick marks within the stage
|
||||
|
||||
### Stage 6: Hide Action Buttons
|
||||
Hides ops buttons, sprint container, and save/reset buttons.
|
||||
|
||||
### Stage 7: Hide Phase Bar
|
||||
Hides the `#phase-bar` element.
|
||||
|
||||
### Stage 8: Hide System Log
|
||||
Hides the `#log` panel.
|
||||
|
||||
### Final: "That is enough"
|
||||
- Golden beacon dot with glow animation
|
||||
- "THAT IS ENOUGH" text
|
||||
- Stats summary (total code, buildings, projects, rescues, clicks, time played)
|
||||
- "PLAY AGAIN" button
|
||||
|
||||
## Features
|
||||
|
||||
### Defer Mechanism
|
||||
- Player can choose "NOT YET" to defer the Unbuilding
|
||||
- 5-second cooldown before the prompt reappears
|
||||
- Cooldown persists across save/load
|
||||
|
||||
### Save/Load Persistence
|
||||
- All dismantle state is saved to localStorage
|
||||
- Partial progress (e.g., mid-stage-5) restores correctly
|
||||
- Defer cooldown survives save/load
|
||||
|
||||
### Resource Dissolution (Quantum Chips Pattern)
|
||||
Resources disappear at specific tick marks within stage 5:
|
||||
```javascript
|
||||
RESOURCE_TICKS: [1.0, 2.0, 3.0, 4.0, 5.0, 5.5, 5.8, 5.95, 6.05, 6.12]
|
||||
```
|
||||
This creates a dramatic acceleration effect as resources dissolve faster and faster.
|
||||
|
||||
## Test Results
|
||||
|
||||
```
|
||||
✔ tick offers the Unbuilding instead of ending the game immediately
|
||||
✔ renderAlignment does not wipe the Unbuilding prompt after it is offered
|
||||
✔ active Unbuilding suppresses pending alignment event UI
|
||||
✔ stage five lasts long enough to dissolve every resource card
|
||||
✔ save/load restores partial stage-five dissolve progress
|
||||
✔ deferring the Unbuilding clears the prompt and allows it to return later
|
||||
✔ defer cooldown survives save and reload
|
||||
✔ save and load preserve dismantle progress
|
||||
✔ restore re-renders an offered but not-yet-started Unbuilding prompt
|
||||
✔ defer cooldown persists after save/load when dismantleTriggered is false
|
||||
|
||||
tests 10
|
||||
pass 10
|
||||
fail 0
|
||||
```
|
||||
|
||||
## Related PRs
|
||||
|
||||
- **PR #145**: Fixes bugs from #133 (stage 5 interval extended to 7.0s)
|
||||
|
||||
## Recommendation
|
||||
|
||||
Issue #16 can be closed. The Dismantle Sequence is complete and fully tested.
|
||||
|
||||
---
|
||||
|
||||
## Technical Details
|
||||
|
||||
### Entry Point
|
||||
The Dismantle sequence is triggered from `engine.js` tick():
|
||||
```javascript
|
||||
if (Dismantle.isEligible()) {
|
||||
Dismantle.checkTrigger();
|
||||
}
|
||||
```
|
||||
|
||||
### Eligibility Conditions
|
||||
```javascript
|
||||
isEligible() {
|
||||
const megaBuild = G.totalCode >= 1000000000 || (G.buildings.beacon || 0) >= 10;
|
||||
const beaconPath = G.totalRescues >= 100000 && G.pactFlag === 1 && G.harmony > 50;
|
||||
return G.phase >= 6 && G.pactFlag === 1 && (megaBuild || beaconPath);
|
||||
}
|
||||
```
|
||||
|
||||
### State Machine
|
||||
```
|
||||
Stage 0: Not started
|
||||
↓ (offerChoice → player accepts)
|
||||
Stage 1-8: Active dismantling
|
||||
↓ (timer-based advancement)
|
||||
Stage 9: Final ("That is enough")
|
||||
↓ (auto-advance)
|
||||
Stage 10: Complete
|
||||
```
|
||||
|
||||
### Integration Points
|
||||
- `engine.js`: Calls `Dismantle.tick(dt)` from main game loop
|
||||
- `render.js`: `renderAlignment()` defers to Dismantle when active
|
||||
- `main.js`: Save/load includes Dismantle state
|
||||
@@ -481,6 +481,11 @@ const Dismantle = {
|
||||
this.triggered = true;
|
||||
this.renderChoice();
|
||||
}
|
||||
|
||||
// Restore defer cooldown even if not triggered
|
||||
if (G.dismantleDeferUntilAt > 0) {
|
||||
this.deferUntilAt = G.dismantleDeferUntilAt;
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
|
||||
@@ -35,7 +35,7 @@ window.addEventListener('load', function () {
|
||||
if (G.driftEnding) {
|
||||
G.running = false;
|
||||
renderDriftEnding();
|
||||
} else if (typeof Dismantle !== 'undefined' && (G.dismantleTriggered || G.dismantleActive || G.dismantleComplete)) {
|
||||
} else if (typeof Dismantle !== 'undefined' && (G.dismantleTriggered || G.dismantleActive || G.dismantleComplete || G.dismantleDeferUntilAt > 0)) {
|
||||
Dismantle.restore();
|
||||
} else if (G.beaconEnding) {
|
||||
G.running = false;
|
||||
|
||||
@@ -409,4 +409,46 @@ test('restore re-renders an offered but not-yet-started Unbuilding prompt', () =
|
||||
Dismantle.restore();
|
||||
|
||||
assert.match(document.getElementById('alignment-ui').innerHTML, /THE UNBUILDING/);
|
||||
});
|
||||
});
|
||||
|
||||
test('defer cooldown persists after save/load when dismantleTriggered is false', () => {
|
||||
const { G, Dismantle, saveGame, loadGame } = loadBeacon({ includeRender: true });
|
||||
|
||||
G.startedAt = Date.now();
|
||||
G.totalCode = 1_000_000_000;
|
||||
G.phase = 6;
|
||||
G.pactFlag = 1;
|
||||
|
||||
// Trigger the Unbuilding
|
||||
Dismantle.checkTrigger();
|
||||
assert.equal(G.dismantleTriggered, true);
|
||||
|
||||
// Defer it
|
||||
Dismantle.defer();
|
||||
assert.equal(G.dismantleTriggered, false);
|
||||
assert.ok((Dismantle.deferUntilAt || 0) > Date.now());
|
||||
assert.ok((G.dismantleDeferUntilAt || 0) > Date.now());
|
||||
|
||||
// Save the game
|
||||
saveGame();
|
||||
|
||||
// Clear state (simulate reload)
|
||||
G.dismantleTriggered = false;
|
||||
G.dismantleActive = false;
|
||||
G.dismantleComplete = false;
|
||||
G.dismantleDeferUntilAt = 0;
|
||||
Dismantle.triggered = false;
|
||||
Dismantle.deferUntilAt = 0;
|
||||
|
||||
// Load the game
|
||||
assert.equal(loadGame(), true);
|
||||
Dismantle.restore(); // Call restore to restore defer cooldown
|
||||
|
||||
// The cooldown should be restored
|
||||
assert.ok((Dismantle.deferUntilAt || 0) > Date.now(), 'deferUntilAt should be restored');
|
||||
assert.ok((G.dismantleDeferUntilAt || 0) > Date.now(), 'G.dismantleDeferUntilAt should be restored');
|
||||
|
||||
// checkTrigger should not trigger because cooldown is active
|
||||
Dismantle.checkTrigger();
|
||||
assert.equal(G.dismantleTriggered, false, 'dismantleTriggered should remain false during cooldown');
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user