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 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
-
P8 Combat Depth: veterancy chevrons + tiered destruction visuals and audio
Claude +1: Claude Sonnet 5Unverified signaturePresentation half of the veterancy/tiered-destruction proposals (the sim half landed in the previous commit): - renderUnits.js's drawUnits overlay pass stamps a small chevron stack above the health bar for any unit at rankMults(unit).rank > 0 — either owner (an enemy veteran's rank is intel, same as its hostile pip), and independent of drawHealthBar's own "hide at full hp" rule since a rank is a permanent trait, not a damage state. - effects.js's addDeathFlash now carries the killed entity's radius/kind (defaulting to a small-hull baseline for back-compat), and scales the death flash's own lifetime by both: a bigger hull lingers longer (clamped 0.7x-2.2x), and a building's slower collapse gets an extra 1.6x on top. renderEffects.js's death-ring draw scales its radius/width off the same baseline, flings a few outward debris shards for a hull at or above the debris threshold (radius 10 — Breacher and up), and draws a building as a slower two-ring collapse (a laggi…d213ade -
Give the Leviathan a real capital-ship hull
Claude +1: Claude Sonnet 5Unverified signatureUNITS.leviathan (900hp, radius 14, Star Dock-gated, paid in Strategic goods) had no case in renderUnits.js's drawUnitShape, so the endgame flagship drew via drawGenericUnit — a featureless diamond that read a tier BELOW the Dreadnought's detailed hull, on the map and on its HUD production button alike (spriteIcon shares the dispatch). The file's own comment on drawGenericUnit — "Nothing on the roster falls through to it today" — was stale; it's corrected here too. Adds drawLeviathan, dispatched from drawUnitShape: a long twin-spine super-capital silhouette, broader and longer than the Dreadnought (whose single-spine/4-battery design it deliberately outdoes), built from the existing toWorld/pathOriented helpers in renderShared.js — the hull tapers aft into two trailing spine prongs around a stern notch, with a three-point engine-glow array nested in the notch (a cue unique to this hull), twin spinal cannon lines flanking the centreline, six battery pods (three a side, up from the Dread…1ccad3e
-
Remove Miner, Engineer, and Technician — Worker builds everything again
Claude +1: ClaudeUnverified signatureThe three-way specialist split made the worker roster harder to manage, not easier: three unit types to remember and train instead of one, for a "which building categories can this thing build" distinction the Build panel's own collapsible per-category sections already handle. Reverts to just Worker as the one economy unit, trainable on ore or biomass (altCost, unchanged) and able to build/gather/haul every category, same as before the split. - engine/entities.js: delete the miner/engineer/technician UNITS entries and drop them from the Command Center's produces list. canBuildCategory/ canGatherType/canLogisticsType stay — they're still the single source of truth issueBuild, issueGather, and the haul/service/ferry auto-assign read, now simply true only for Worker. - renderUnits.js/hudSelection.js/input.js/engine/aiMilitary.js/ engine/commands.js/engine/production.js: drop the now-dead sprite dispatch and produce-list entries, and reword comments that explained the specia…53ddc42 -
Give Miner, Engineer, and Technician their own unit sprites
Unverified signature* Redesign panel collapsibility: per-category, and generalized to every long list The Build submenu had one toggle for the whole thing — collapsing it hid Military along with Economy. Splits it into independent per-group toggles (Economy/Military/Endgame/Travel each fold on their own), and generalizes the mechanism to every other long, data-driven button list in the selection panel that was found to warrant it: the Command Center's own Produce list, the Market trade panel, Barracks' Produce list, the Refinery/Datacenter research lists, a freighter's Cargo list, and the Spaceport's Jump list. Short, fixed-size panels (Capital, Diplomacy, Stardock, Mender, Helium Bomb, Formation, …) were surveyed and deliberately left alone — collapsing a 1-2 button panel is a click tax, not a space saver. - session.js: game.buildMenuOpen (one boolean) -> game.collapsedSections (a Set of collapsed-section keys), so N independent sections scale without a dedicated field per section. A key absent fro…502b16c
-
Add the Helium Bomb: a mobile, arm-then-detonate doomsday unit
Claude +1: Claude Sonnet 5Unverified signatureA new Odyssey unit (engine/entities.js UNITS.heliumbomb), built at the Star Dock alongside the Leviathan, costed entirely in the three named materials: gas (Helium-3), antimatter, and plasmatorp (Plasma Torpedoes) - all three already-real commodities, no new economy pieces needed. Lifecycle: built UNARMED (safe to move anywhere, no different from any other unit) - the player ARMS it via a HUD toggle (hudSelection.js, same direct-mutation pattern as the Mender's auto-repair switch) - once armed, it detonates the instant any of three triggers fires: - it's attacked at all (combat.js's performAttack calls engine/bomb.js's detonateIfAttacked before applying normal damage - any hit sets it off instead of chipping its hp) - a live enemy unit or building comes within BOMB_DETECT_RANGE (checked once per tick from sim.js for every role:"bomb" unit) - the player clicks "Detonate Now" (calls detonateBomb directly) All three funnel through the one detonateBomb() function in the …53d4247 -
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