Spaces:
Paused
Paused
Commit History
-
Tier 3g: name the three render rules that were copied instead
Claude +1: Claude Opus 5Unverified signatureThree idioms were spread across the render modules by hand, and in each case the copying had already caused or hidden a bug. centeredText. textAlign/textBaseline are canvas-WIDE state, not per-call arguments, so code that centers text and walks away shifts every later draw in the frame — in a different file, where nobody would look. Two sites shipped exactly that, invisible only because drawFrame's outer save/restore swallowed it once per frame; Tier 1 fixed both by hand. Making the pair un-splittable is the fix that stays fixed. drawLabelChip. The two placement-ghost annotations had independently hand-rolled the same measureText -> fillRect(x - w/2 - 5, y - 15, w + 10, 17) arithmetic. The plate has to be measured rather than constant, because the labels range from "Tier 2 · draw ×1.4" to "⛏ blind spot — no surface to read (a gamble)", and a plate sized by a stale constant is a legible label on an illegible background. hiddenByFog. Four draw passes each carried their own copy of `ow…b870df3 -
Tier 2e: one roster, distinct silhouettes, and a perf claim that didn't survive measurement
Claude +1: Claude Opus 5Unverified signatureThe HUD carried three hand-maintained copies of engine/entities.js's `produces` lists — two identical literals plus a pair of unrolled blocks — while the engine already owns and ENFORCES the roster (queueProduction refuses anything not in it). Adding a unit therefore made it buildable by the engine and by the AI while staying invisible in the UI, with nothing failing. The Odyssey gate was duplicated differently on top of that: the engine gates per-unit on `def.odysseyOnly && !state.endless`, the HUD hand-split the array on game.galaxy. All three panels now derive from BUILDINGS[type].produces through one helper that applies the engine's own two gates. The test came first and immediately taught me something: my first version asserted a button for EVERY produced unit and went red on the Barracks, because a unit whose cost commodity this world can neither mine nor stock is deliberately hidden rather than shown forever-greyed. That is an engine rule (commodityAvailable), not a HUD opinion…505ae6b -
Tier 1, step 4b: stop one bad entity killing the view (A7, A8)
Claude +1: Claude Opus 5Unverified signatureA7 — drawFrame opened a ctx.save() it could only close on the happy path. engine/loop.js deliberately SWALLOWS a throwing render so a bad entity can't brick the session; without a finally that guarantee was inverted. A throw left the camera transform unpopped, so every later frame drew on top of it — including the backdrop fillRect, which is why the screen stopped clearing at all. The loop kept running while the view stayed dead forever, which to a player is a freeze, and it is exactly the symptom test/renderEffects.test.js already records as having shipped once. Reproduced with a save-depth-tracking ctx: depth stays at 1 for the rest of the session. The live trigger is fixed alongside it. drawRallyPoint destructured building.rally unguarded, and `rally` is optional with cleanEntity never defaulting it — so an older or hand-edited save threw a TypeError from a single entity and took the whole frame with it. Two latent instances of the same class are closed too: renderBuildings.js (two…a53030d
-
Add Observer Mode: a free-look spectator over the Odyssey galaxy
Claude +1: Claude Sonnet 5Unverified signatureLets the player watch how AI archetypes develop and interact, like a scientist running the sim and just watching, instead of needing a CLI tool (tools/ailab.js) to get that view. O (or the topbar Observe button) toggles it: fog is revealed on whichever world is being spectated, the Galaxy map becomes a free camera jump to any world (discovered or not, no fuel/Spaceport), and Space cycles every base on that world regardless of owner. A stats panel surfaces the spectated world's archetype, strategy, diplomatic stance/hostility, army composition, buildings/development, supply, and resources. Orders are disabled while observing. Deliberately never touches game.state, galaxy.activeId, or the real camera - observer.js only changes what's drawn (every world already ticks regardless, via stepGalaxy) - so normal play resumes exactly where it was left on exit. Fog bypass is a pure render-time flag on drawFrame/drawUnits/drawBuildings/drawNodes/drawMinimap, never a mutation of state.fog. First…6bd2c72
-
Static-defense ladder: Bastille, Aegis Bastion, Plasma Torpedo Battery
Claude +1: Claude Sonnet 5Unverified signatureShips as one rationalized tier per docs/improvement-roadmap.md Phase 4, not three unrelated additions: all three new structures are tuned against a single shared invariant (every siege unit — Breacher 150, Colossus 185, Leviathan 200 — must still strictly outrange every static-defense structure added here, with one deliberate, documented exception at the top of the ladder), and land together so that invariant is verified once, holistically, rather than three separate times. - Bastille (engine/entities.js): a second, Foundry-gated static-defense tier — hp 600, attack 32, cooldown 1.4, range 115 (deliberately under even the plain turret's 130: a tankier, shorter-ranged brawler, not a sniper). Same combat stat field names the turret uses, so combat.js's updateBuildingCombat needed zero changes to run it. Wired into hudSelection.js's skirmish build list and the Odyssey GROUPS/alwaysShow sets alongside the turret, and into aiEconomy.js's fortify block: the AI's second fortifi…45b37a3 -
Bespoke power-plant hulls and a deterministic work pulse for factories
Claude +1: Claude Sonnet 5Unverified signatureReactor, Combustion Generator, and Biomass Reactor previously fell through to the same generic hex + BUILDING_GLYPH emoji every recipe factory uses, and a healthy running factory looked identical to a dead one (drawConcernBadge is deliberately silent when things are fine). Two changes in renderBuildings.js: - New drawReactor/drawCombustor/drawBiomassReactor cases in drawBuildingShape give the three power buildings bespoke hulls (a cooling-tower core, a flame-stack, a green vat) at the Foundry/ Arsenal art level, each reading its own `powered` state directly (same on/off tones electrifiedLight already uses) instead of a static emoji disc. Removed from BUILDING_GLYPH accordingly. - drawFactory now draws a subtle work-pulse glow behind a recipe factory's glyph disc when it's genuinely running (completed, not paused, buildingConcern reads clean) — phased by hashStr(b.id) and driven by state.time, matching electrifiedLight's own deterministic- presentation idiom (no wall…524c178 -
P3 T2 industry/tech depth: Grid Substation, scoped Heavy Alloys, standing
Claude +1: Claude Sonnet 5Unverified signatureFoundry/Arsenal bonuses, cancelable research queue Four proposals sharing engine/entities.js/industry.js/techtree.js, landed as one coherent pass per the roadmap's own grouping: Grid Substation (engine/entities.js, engine/industry.js): a cheap odysseyOnly BUILDINGS.substation (60 ore + 30 crystals; no energyGrants, no fuel) that acts as a passive one-hop power-grid relay. bestGridDist splits its old body into bestSourceDist (real sources only) plus a new relay pass: a Substation counts as a virtual source point (its own shorter powerRange ~0.8) only while it stands within an active real source's own 'linked' band (checked via bestSourceDist, never via another relay, so a relay can never chain through a second relay). powerDraw adds the relay's own flat draw (the ELECTRIFY_POWER idiom); powerCap is untouched (a relay never grants capacity, only reach). Verified hudSelection.js's gridEfficiencyRow and the render-side ghost-cue label pick this up for free via the existing powerEfficienc…af15726
-
Give the Market its own building art
Claude +1: Claude Sonnet 5Unverified signatureSelecting/placing a Market previously fell through to the generic hex silhouette every unhandled building type gets. Adds a bespoke drawMarket: an open-air trading stall — a scalloped, striped awning on support poles over a counter of goods (in the same gold the trade panel prices credits with), topped with a small trade pennant, the building's one distinguishing accent (same idiom as the Command Center's antenna or the Barracks' dish). Wired into drawBuildingShape so both the map render and the HUD build-button icon (which shares the same dispatch) pick it up. Verified visually: a standalone large-scale render of the shape next to its siblings, the in-game build-menu icon, and the placed/selected building in a live browser session. npm test (939/939) is green (pure render code, no behavioral test surface). Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01DFowPSNmgMwngQBpunfHkr57456d0 -
Add a map-level badge for a paused, stalled, or throttled producer
ClaudeUnverified signaturePreviously the only way to learn a factory/Rig/power station was paused, out of Power or fuel, starved of an input, backed up, or running under-rate was to select it and read its panel text — nothing on the map itself showed it. Add engine/industry.js buildingConcern, a pure read that mirrors updateProduction/updatePlasmaRig/ updateCombustors' own gating exactly, and a small badge in renderBuildings.js (muted pause bars, red "!", or gold lightning) next to any own producer that isn't running clean, so a player scanning the whole base spots the problem without opening every building's panel.df7e459
-
Have power stations refuel from a worker-hauled larder, and add the Biomass Reactor
ClaudeUnverified signatureThe Combustion Generator used to burn gas or biomass straight out of the treasury, automatically, with no worker involvement at all — unlike every other producer in the Odyssey economy, which draws from a finite local buffer a worker has to physically haul in. Gave it (and its new sibling) the same treatment: fuel now lives in the building's own `input` larder, filled by a worker SERVICE run (engine/haul.js) exactly like a factory's input, and updateCombustors burns from that larder instead of the treasury directly. Split the fuel roster while at it: the Combustion Generator now burns gas (Helium-3) or radioactives; the new Biomass Reactor is its sibling for a biomass-rich claim, same stats, burning biomass alone. Both are ordinary `combust` buildings, so entities.js's per-commodity larder-splitting (inputCapOf) already gives each accepted fuel its own guaranteed slice, same guarantee a multi-input factory recipe gets. Also fixes a bug this surfaced: countLogistics only ever reset th…a7b559b -
Let the player recycle a completed building or unit for part of its cost back
ClaudeUnverified signatureSelect any owned, completed building or unit and Recycle it: it stands down in place over roughly half its build time (freely cancellable, no penalty) and refunds 40% of its cost, or 50% for pricier industrial/capital assets (a factory, the Plasma Rig, a Reactor/Generator, a freighter, a Mender) — boosted to 70%/80% (capped) by a new research node in either tech tree (Field Recycling, Logistics doctrine tier 3 in skirmish; Reclamation Engineering at the Datacenter in Odyssey). A Command Center can't be recycled. Persists across save/load, with save-hardening against a tampered file.79c7af7 -
Add a power-status light to every electrifiable building
Claude +1: Claude Sonnet 5Unverified signatureCommand Center, Barracks, Habitat and Star Dock (engine/entities.js isElectrifiable) each get a small lit cue when wired into the power grid: steady gold while the grid can carry the full +30% boost, flickering whenever it can't (a partial or dead grid) -- the same throttle that scales the boost itself, so the light is an honest at-a-glance read of whether it's actually getting the bonus right now. No light at all for a building that isn't electrified. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01AdsYZQyGk32n8x3UBiPGup6f3b37b -
Show a small yellow dot in an electrified Habitat's dome
Claude +1: Claude Sonnet 5Unverified signatureGives the Habitat's power-grid toggle a visual cue on the map itself, matching the small lit-detail style already used elsewhere (the Foundry's ember, the Spaceport's tier pips) — no need to select the building and open its panel to tell it's electrified. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01AdsYZQyGk32n8x3UBiPGup27d00a7
-
Fix game-freezing crash when placing a factory, the Plasma Rig, or the Gate
Claude +1: Claude Sonnet 5Unverified signaturerenderBuildings.js's POWER_TIER_COLOR and drawReactorBands were never exported - a leftover gap from the earlier "split render.js into cohesive modules" refactor - but renderEffects.js's drawGhostPowerCue referenced both anyway. The moment a player armed build mode for any power-consuming building (Smelter, Assembly Plant, Chip Fab, Machine Works, Antimatter Forge, AI Foundry, Torpedo Works, the Plasma Rig, or the Antimatter Gate), every single frame's render() threw a ReferenceError while the ghost followed the cursor. loop.js's try/catch (added in an earlier fix) kept the loop itself from dying, but the error re-fired on literally every frame for as long as build mode stayed armed - which is what made the game look and feel completely frozen, reported live by a player placing a Smelter. Exported both from renderBuildings.js and imported them in renderEffects.js. Verified live: reproduced the exact ReferenceError from the reported stack trace, confirmed the fix resolves it, and confi…d86e12b -
Fix rendering waste: redundant Set allocs, stale DPR icon cache, swallowed errors
Claude +1: Claude Sonnet 5Unverified signature- drawUnits/drawBuildingBars/drawJumpStaging/drawPowerGrid each independently allocated new Set(state.selection) - four allocations of the same set, 60 times a second. render.js's drawFrame now computes it once and threads it down to all four. - spriteIcon's cache key didn't include the device-pixel-ratio scale it rasterizes at, so dragging the window to a different-DPR display served a permanently stale icon. scale is now folded into the key. - Icon render failures were swallowed with no logging, and only successful renders were cached - a genuinely broken icon type redid the full allocate-draw-throw cycle on every call, forever. Canvas/context setup failures (environment problems) are now logged and NOT cached, so they can retry; per-type draw failures (deterministic for that kind/type) are logged AND cached as empty, so they fail once instead of forever. The working-icon path is unchanged. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session:…71c607d -
Fix the scout-a-building crash: unexported drawEnemyPip + harden loop.js
Claude +1: Claude Sonnet 5Unverified signaturerenderBuildings.js called drawEnemyPip for every non-player building without importing it — it was a private function in renderUnits.js. The first fog-visible enemy building threw a ReferenceError, and because engine/loop.js had no try/catch around its render() call, the throw killed the requestAnimationFrame chain permanently: sim and render both stopped for the rest of the session. Export drawEnemyPip and import it where it's used. Wrap only the render() call in loop.js's frame() in try/catch (update() stays unguarded — a throwing sim update is a different problem) so one bad draw degrades a frame instead of bricking the game. Add a direct regression test (verified it fails with the exact ReferenceError against the pre-fix code) plus a loop.js test proving a throwing render doesn't stop later frames. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Dmxcvm178TEt2wVXrX5Cj1693efa9 -
Split render.js god file into cohesive render modules
Claude +1: Claude Opus 4.8Unverified signaturerender.js was a 1867-line god object holding every canvas draw path. Split it into a shared leaf plus per-cluster draw modules along an acyclic import graph, leaving render.js a 192-line orchestrator that keeps its public exports (drawFrame, spriteIcon, snapshotPositions, resetFacing) so boot.js and the HUD are unchanged: - renderShared.js — the toolkit every draw path needs, in one leaf so the cluster modules share it without a cycle: DETAIL, the facing map + frame-to-frame interpolation state (snapshotPositions/lerpXY/updateFacing/pruneFacing/ resetFacing), the geometry+colour primitives, inView culling, and the drawHealthBar bar primitive. - renderNodes.js — resource-deposit silhouettes. - renderBuildings.js — building hulls, health/store bars, jump-staging + power-grid cues. - renderUnits.js — unit hulls (two-pass) + overlays. - renderEffects.js — tracers…1aaa2ca