Compare commits
3 Commits
fix/192-re
...
feat/sessi
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
18eae67ff9 | ||
| d5645fea58 | |||
|
|
db08f9a478 |
16
GENOME.md
16
GENOME.md
@@ -8,24 +8,32 @@ The Beacon is a browser-based idle/incremental game inspired by Universal Paperc
|
||||
|
||||
Static HTML/JS — no build step, no dependencies, no framework. Open `index.html` in any browser.
|
||||
|
||||
**5,128 lines of JavaScript** across 10 files. **1 HTML file** with embedded CSS (~300 lines). **1 Python test file** for reckoning projects.
|
||||
**6,033 lines of JavaScript** across 11 files. **1 HTML file** with embedded CSS (~300 lines). **3 test files** (2 Node.js, 1 Python).
|
||||
|
||||
## Architecture
|
||||
|
||||
```
|
||||
index.html (UI + embedded CSS)
|
||||
index.html (UI + embedded CSS + inline JS ~5000L)
|
||||
|
|
||||
+-- js/engine.js (1590L) Core game loop, tick, resources, buildings, projects, events
|
||||
+-- js/data.js (944L) Building definitions, project trees, event tables, phase data
|
||||
+-- js/render.js (390L) DOM rendering, UI updates, resource displays
|
||||
+-- js/combat.js (359L) Boss encounters, combat mechanics
|
||||
+-- js/combat.js (359L) Canvas boid-flocking combat visualization
|
||||
+-- js/sound.js (401L) Web Audio API ambient drone, phase-aware sound
|
||||
+-- js/dismantle.js (570L) The Dismantle sequence (late-game narrative)
|
||||
+-- js/main.js (223L) Initialization, game loop start, auto-save, help overlay
|
||||
+-- js/utils.js (314L) Formatting, save/load, export/import, DOM helpers
|
||||
+-- js/tutorial.js (251L) New player tutorial, step-by-step guidance
|
||||
+-- js/strategy.js (68L) NPC strategy logic for combat
|
||||
+-- game/npc-logic.js (18L) NPC behavior stub
|
||||
+-- js/emergent-mechanics.js Emergent game mechanics from player behavior
|
||||
|
||||
CI scripts (not browser runtime):
|
||||
+-- scripts/guardrails.sh Static analysis guardrails for game logic
|
||||
+-- scripts/smoke.mjs Playwright smoke tests
|
||||
|
||||
Reference prototypes (NOT loaded by runtime):
|
||||
+-- docs/reference/npc-logic-prototype.js NPC state machine prototype
|
||||
+-- docs/reference/guardrails-prototype.js Stat validation prototype
|
||||
```
|
||||
|
||||
## Entry Points
|
||||
|
||||
@@ -3,20 +3,15 @@ _2026-04-12, Perplexity QA_
|
||||
|
||||
## Findings
|
||||
|
||||
### Potentially Unimported Files
|
||||
### Dead Code — Resolved (2026-04-15, Issue #192)
|
||||
|
||||
The following files were added by recent PRs but may not be imported
|
||||
by the main game runtime (`js/main.js` → `js/engine.js`):
|
||||
The following files were confirmed dead code — never imported by any runtime module.
|
||||
They have been moved to `docs/reference/` as prototype reference code.
|
||||
|
||||
| File | Added By | Lines | Status |
|
||||
|------|----------|-------|--------|
|
||||
| `game/npc-logic.js` | PR #79 (GOFAI NPC State Machine) | ~150 | **Verify import** |
|
||||
| `scripts/guardrails.js` | PR #80 (GOFAI Symbolic Guardrails) | ~120 | **Verify import** |
|
||||
|
||||
**Action:** Check if `js/main.js` or `js/engine.js` imports from `game/` or `scripts/`.
|
||||
If not, these files are dead code and should either be:
|
||||
1. Imported and wired into the game loop, or
|
||||
2. Moved to `docs/` as reference implementations
|
||||
| File | Original | Resolution |
|
||||
|------|----------|------------|
|
||||
| `game/npc-logic.js` | PR #79 (GOFAI NPC State Machine) | **Moved to `docs/reference/npc-logic-prototype.js`** — ES module using `export default`, incompatible with the global-script loading pattern. Concept (NPC state machine) is sound but not wired into any game system. |
|
||||
| `scripts/guardrails.js` | PR #80 (GOFAI Symbolic Guardrails) | **Moved to `docs/reference/guardrails-prototype.js`** — validates HP/MP/stats concepts that don't exist in The Beacon's resource system. The `scripts/guardrails.sh` (bash CI script) remains active. |
|
||||
|
||||
### game.js Bloat (PR #76)
|
||||
|
||||
|
||||
@@ -205,6 +205,8 @@ Events Resolved: <span id="st-resolved">0</span><br>
|
||||
<span id="emergent-stats" style="color:#b388ff;display:none">✦ Emergent Events: <span id="st-emergent">0</span> | Patterns: <span id="st-patterns">0</span> | Strategy: <span id="st-strategy">—</span></span>
|
||||
</div>
|
||||
<div id="production-breakdown" style="display:none;margin-top:12px;padding-top:10px;border-top:1px solid var(--border)"></div>
|
||||
<h2 style="margin-top:12px">SESSION</h2>
|
||||
<div id="session-stats"></div>
|
||||
</div>
|
||||
</div>
|
||||
<div id="edu-panel" role="region" aria-label="Educational Content">
|
||||
|
||||
@@ -160,6 +160,12 @@ const G = {
|
||||
startTime: 0,
|
||||
flags: {},
|
||||
|
||||
// Session stats (reset on page reload, not persisted)
|
||||
sessionCode: 0,
|
||||
sessionBuildings: 0,
|
||||
sessionEvents: 0,
|
||||
bestCombo: 0,
|
||||
|
||||
// Endgame sequence
|
||||
beaconEnding: false,
|
||||
dismantleTriggered: false,
|
||||
|
||||
@@ -407,6 +407,7 @@ function buyBuilding(id) {
|
||||
G[resource] -= amount;
|
||||
}
|
||||
G.buildings[id] = (G.buildings[id] || 0) + qty;
|
||||
G.sessionBuildings += qty;
|
||||
updateRates();
|
||||
// Emergent mechanics: track building purchase
|
||||
if (typeof EmergentMechanics !== 'undefined' && window._emergent) {
|
||||
@@ -781,6 +782,7 @@ function resolveEvent(debuffId) {
|
||||
G[resource] -= amount;
|
||||
G.activeDebuffs.splice(idx, 1);
|
||||
G.totalEventsResolved = (G.totalEventsResolved || 0) + 1;
|
||||
G.sessionEvents = (G.sessionEvents || 0) + 1;
|
||||
log(`Resolved: ${debuff.title}. Problem fixed.`, true);
|
||||
// Refund partial trust for resolution effort
|
||||
G.trust += 3;
|
||||
@@ -797,13 +799,15 @@ function writeCode() {
|
||||
const amount = getClickPower() * comboMult;
|
||||
G.code += amount;
|
||||
G.totalCode += amount;
|
||||
G.totalAutoClicks++;
|
||||
G.sessionCode += amount;
|
||||
G.totalClicks++;
|
||||
// Emergent mechanics: track click
|
||||
if (typeof EmergentMechanics !== 'undefined' && window._emergent) {
|
||||
window._emergent.track('click', { resource: 'code', delta: amount });
|
||||
}
|
||||
// Combo: each consecutive click within 2s adds 0.2x multiplier, max 5x
|
||||
G.comboCount++;
|
||||
G.bestCombo = Math.max(G.bestCombo, G.comboCount);
|
||||
G.comboTimer = G.comboDecay;
|
||||
// Combo milestone bonuses: sustained clicking earns ops and knowledge
|
||||
if (G.comboCount === 10) {
|
||||
|
||||
25
js/render.js
25
js/render.js
@@ -4,6 +4,7 @@ function render() {
|
||||
renderBuildings();
|
||||
renderProjects();
|
||||
renderStats();
|
||||
renderSessionStats();
|
||||
updateEducation();
|
||||
renderAlignment();
|
||||
renderProgress();
|
||||
@@ -388,3 +389,27 @@ function loadGame() {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// === SESSION STATS ===
|
||||
function renderSessionStats() {
|
||||
const el = document.getElementById('session-stats');
|
||||
if (!el) return;
|
||||
|
||||
const elapsed = Math.floor(G.playTime);
|
||||
const m = Math.floor(elapsed / 60);
|
||||
const s = elapsed % 60;
|
||||
const timeStr = m > 0 ? `${m}m ${s}s` : `${s}s`;
|
||||
|
||||
const clicksPerSec = elapsed > 0 ? (G.totalClicks / elapsed).toFixed(1) : '0.0';
|
||||
|
||||
el.innerHTML = `
|
||||
<div style="font-size:9px;color:#555;line-height:1.8">
|
||||
Session Code: <span style="color:#4a9eff">${fmt(G.sessionCode || 0)}</span><br>
|
||||
Buildings Built: <span style="color:#4caf50">${fmt(G.sessionBuildings || 0)}</span><br>
|
||||
Best Combo: <span style="color:#ffd700">${G.bestCombo || 0}x</span><br>
|
||||
Events Resolved: <span style="color:#b388ff">${G.sessionEvents || 0}</span><br>
|
||||
Clicks: <span style="color:#888">${fmt(G.totalClicks || 0)} (${clicksPerSec}/s)</span><br>
|
||||
Session Time: <span style="color:#888">${timeStr}</span>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user