Spaces:
Paused
Paused
Commit History
-
Boot the game in a real browser in CI — the one thing 2502 tests cannot see
Claude +1: Claude Opus 4.8Unverified signatureThere is no build step, so the files in the repo are the files the browser loads. The suite is headless and dependency-free, which is the point of it, and it means nothing in it can catch a page that parses, imports cleanly, and then throws on load. static-integrity.test.js catches STATIC breakage — a syntax error, an import pointing at a moved file, a getElementById for an id that does not exist — and stops exactly there. The failure it cannot reach is a blank white screen, and the way that has been found here before is a human clicking. Measured rather than asserted. Adding a bad draw call to renderUnits.js — one line, valid syntax, imports fine — leaves all 2502 tests passing and takes the game down on its first frame. tools/smoke.js catches it and exits 1 with the stack. A second mutation (a runtime throw in boot.js) is caught by BOTH this and the existing "imports cleanly under Node" guard, which is why the render one is the honest demonstration of what is new here. tools/smoke.…778e159
-
Phase 5 (spectate): watch an AI-vs-AI match live, at 1x-8x
Claude +1: Claude Opus 4.8Unverified signaturedocs/competitions-and-elo.md Phase 5, the spectate half. Replay-from-the-ledger and Season stay open. Observer Mode was one `if` away from supporting this, and that is all it took: enterObserverMode's `if (!game.galaxy) return` became `if (!game.galaxy && !game.spectateMatch) return`, with spectateId falling back to the state's own planetId. The camera path, input.js's seven delegation points, observedState()'s existing game.state fallback and boot.js's render swap were already general and are untouched — the Odyssey observer tests are the safety net that the galaxy path did not move. Where Observer Mode is OFFERED stays exactly two places: an Odyssey, and a match the human is not playing. An ordinary skirmish is still refused; revealing its fog would just be a cheat. A WATCHED MATCH IS EXHIBITION ONLY, deliberately. Every rated result in this system is a pairing — side-swapped, multi-seed, replicate-parity swapAsym — because a single unswapped match is confounded by the seat edge se…450f6c1
-
Make the right-side selection panel foldable on desktop/landscape
ClaudeUnverified signatureThe panel could only be collapsed in portrait mode (the bottom-sheet .sheet-toggle handle) — on desktop/landscape it was permanently visible with no way to reclaim that width for the view. Added a second, parallel toggle (#panelToggle / .panel-toggle) for the normal side-column layout: a small handle riding the seam between #view and #selectionPanel (vertically centered, right edge of #view), visible everywhere the portrait bottom-sheet mode isn't. Clicking it hides #selectionPanel via a new `panel-collapsed` body class, and #view's existing flex:1 automatically reclaims the freed width — the canvas refit comes for free from main.js's existing ResizeObserver on #field, same as the portrait toggle already relies on. Each toggle's click handler clears the other's collapsed class, so resizing/rotating across the portrait/landscape breakpoint can never leave one toggle's glyph out of sync with the panel's actual visibility. Verified in-browser: folding grows the canvas from 996px to 125…e6ecfb1 -
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
-
Persistent Antimatter Gate charge strip in the HUD
Claude +1: Claude Sonnet 5Unverified signatureThe Gate's multi-minute charge is the Odyssey finale's whole clock, and a charging Gate provokes every neighbour for as long as it charges (per CHANGELOG) — but until now the percentage was only visible in boot.js's 25%-milestone toasts or by keeping the Gate selected in the side panel. While the player is off defending the waves the charge attracts, the value they're defending for was off-screen, and a stall (starved feed goods or throttled Power) was completely silent. renderHUD now finds the player's wonder-flagged building (gated on game.galaxy only, matching the existing Odyssey idiom the power readout uses) and renders a slim persistent chip: "Gate NN% - provoking neighbours", color warming from accent to warn to bad as charge climbs, and flipping to a dedicated stalled look when charge hasn't advanced since the last tick observed it (tracked as {id, charge} on session.js's game object, next to the other per-frame UI comparison state, reset by hud.js's own resetPanelSignature() …3d6d01c -
Tech & Industry Chart overlay: make the unlock ladder visible
Claude +1: Claude Sonnet 5Unverified signatureThe ladder (Foundry -> Arsenal, the Datacenter's TECHS -> Chip Fab/ Machine Works -> the Strategic tier -> the Antimatter Gate) couldn't be seen anywhere in-game: the Odyssey build menu deliberately hides locked buildings, the Datacenter panel is a flat list with one-level "Requires X" tips, and docs/player-handbook.html — the one place the ladder is actually drawn — was linked from nowhere in the app. New techChart.js, a leaf UI module modeled on starmap.js: its own full-screen overlay element, pauseLoop('techchart')/resumeLoop, Esc/hotkey T (guarded against firing while a text control is focused, input.js's own pattern), a topbar button, and backdrop- click-to-close. It builds one node per building, gated unit, and tech/doctrine node (BUILDINGS/UNITS/UPGRADES from engine/entities.js, TECHS from engine/techtree.js), derives a tier via a depth-first walk of each def's own `requires` array, and lays them out as tier columns. Each node is coloured by live state — built/researched, build…a9143d2 -
HUD alerts + selection UX: minimap attack pings, base cycling, idle-production chip, Alt-subtract
Claude +1: Claude Sonnet 5Unverified signatureThree related input.js proposals landed as one coherent pass, per the roadmap's Phase 3 UX grouping (docs/improvement-proposals.md lines 351-372, 563-572): - Attack pings on the minimap plus a jump-to-last-alert key: effects.js exports activePings() (the same under-attack pings addUnderAttackPing already feeds, kept alive far longer via a new MINIMAP_PING_LIFETIME_MS so the minimap can still show them once the world-space ping and the banner have faded); minimap.js draws pulsing red rings for them; boot.js threads activePings() into its drawMinimap call and moves the remembered attack site onto game.lastAttackAt (session.js, next to supplyBlockedUntil, which already crosses this exact module boundary) so input.js's new Backspace binding can jump to it too, not just the banner's own click handler. - Space cycles bases and an idle-production topbar chip: centerOnBase() used to always land on the first completed Command Center a .find() happened to hit; repeated presses wi…18ec8e7 -
Reactive opening checklist + an honest, configurable endgame clock
Claude +1: Claude Sonnet 5Unverified signatureReactive opening checklist (replaces the static objectives strip): overlays.js's showObjectives() used to print one static sentence that auto-dismissed after 30s regardless of what the player had actually done. renderObjectives(state, galaxy) now walks a small per-mode predicate table (5 items for skirmish, 4 for Odyssey) and paints a live checklist; items check off with a flash as their predicate is satisfied. updateObjectives(game), called from hud.js's renderHUD every tick behind a cheap signature guard, keeps it reactive and retires the strip (remembering the completion via a per-mode localStorage flag) once every item is done, instead of a blind 30-second timeout. The strip is suppressed for a scripted scenario, whose own scenario bar already states that mode's objective. Make the clock endgame visible, honest, and configurable: - engine/victory.js's finish() now records state.winReason ("elimination" | "mutual-wipe-score" | "timeout-score") at each of checkWinCondition's …5bffdf0
-
Topbar: pin controls to a fixed second row so they never jump
Claude +1: Claude Opus 4.8Unverified signatureThe topbar was one wrapping flex row, so as the resource readout grew or shrank (more/fewer commodities on an Odyssey world) the control buttons — seed, volume, mute, Pause, Galaxy, Save, Load, Home, ? — flipped between line 1 and line 2, jumping up and down and dragging selections with them. Now it's a two-row column: - Line 1 (info): title, version, the resource readout, clock, idle-workers, faction. It stays a SINGLE line — the resource readout scrolls sideways instead of wrapping, and every other item is pinned (flex: 0 0 auto; white-space: nowrap) so only the readout absorbs the squeeze (previously the title text itself wrapped to 3 lines and grew the row). - Line 2 (controls): the buttons, always pinned below — they never move. Result: the controls sit at a constant position and the topbar keeps a constant height no matter how long the commodity list gets (verified: buttons at the same Y and the bar the same height for a 4- vs 24-item readout). This also settles the old…e95934b
-
Tier 3 review fixes: 5 defects from the adversarial pass
Claude +1: Claude Opus 4.8Unverified signatureAn adversarial review of the Tier-3 diff surfaced five real UI defects the happy-path browser probe couldn't catch: - Manual pause was keyboard-only (medium): added a topbar ⏸ Pause / ▶ Resume button (togglePause), visible in every mode, so touch players can pause too; the PAUSED banner text is now mode-neutral ("⏸ PAUSED"). - Group chip captured the input controller at build time (low): after an Odyssey jump with an unchanged chip signature the row isn't rebuilt, so a captured handler would recall on the destroyed pre-jump controller. Now reads game.input live in the handler. - togglePause ignored game-over (low): pressing P on the finished-match screen painted a spurious PAUSED banner. Now guarded with `game.state.over`. - Space's global preventDefault swallowed keyboard activation of focused controls (low): a focused HUD/modal button couldn't be Space-activated during play. The keydown handler now skips the Space/production hotkeys when a focusable control (button/inp…a3867b9 -
Tier 3 UX: pause, toast stack, control groups, hotkeys, touch & colour cues
Claude +1: Claude Opus 4.8Unverified signatureSeven UI/controls fixes from the review (all UI-side — no engine changes, so replay determinism is untouched and the 549-test engine suite is unaffected). - Pause (boot.js/overlays.js/saveload.js/style.css): a refcounted pause gates the update() callback (render keeps drawing the frozen frame). Opening Help or the Home-confirm modal pauses the sim; a P key toggles a manual pause with a centered PAUSED banner. The accumulator self-bounds (loop's backlog-drop), so resume never spirals and the fixed-dt sequence is intact. - Toast stack (overlays.js/style.css/index.html): #galaxyToast is now a column of up to 3 toasts, each with its own timer/onclick — a routine "Researched…" can no longer overwrite a one-shot clickable "colony has fallen — click to retake"; a plain toast never evicts a clickable one. Same showGalaxyToast signature. - Control groups (session.js/input.js/hud.js/dom.js/index.html/style.css): moved onto game.groups keyed per planet, so a group survives an Ody…1ad681c
-
Add app versioning, auto-update check, save-compat signal, load sanitization
Claude +1: Claude Opus 4.8Unverified signature- Versioning: version.js is the source of truth (APP_VERSION semver, bump per release/merge) plus pure, tested helpers (parseVersion/compareVersions/isNewer/ saveImpact). version.json is the deployed release manifest. A version chip shows in the topbar and the setup menu. - Auto-update check (update.js): a long-lived tab can outlast a deploy, so it fetches version.json (cache-busted) on load + every 30 min and, when the deployed release is newer than this build, raises a dismissible banner with Reload / Later. Network/parse errors are silent. - Backward-compat signal: the banner tells the truth about save data — comparing the manifest's minimum readable save-format versions against the player's stored saves (saveImpact), it says whether they carry over ("✓ Backward compatible") or would be orphaned ("⚠ won't load — export to a file first"), nudging a file Save. - Load sanitization (engine/persist.js sanitizeSave, run at the top of both deserializers): a save from a …755b556 -
Save/Load to file; autosave + resume via localStorage
Claude +1: Claude Opus 4.8Unverified signatureSplit the two save channels so each does one job well: - The topbar Save / Load buttons now move a save to and from a FILE. Save downloads the current game as stellar-frontier-<mode>-<stamp>.json (galaxy or skirmish); Load opens a file picker and imports one, auto-detecting the mode by shape (a galaxy carries `planets`). Explicit backups you can keep and transfer. - localStorage is now the automatic checkpoint. autoSave() serializes the current game (skipping a finished game or a non-savable scenario) on a 12s timer, when the tab is hidden, and on unload — no manual step. The map-select "Continue" buttons resume straight from it, and Home's "Save & Exit" checkpoints on the way out. loadGame/loadOdyssey (the resume path) still read localStorage. setup.js: the resume buttons are relabelled "Continue — resume autosave". index: the Save/Load buttons get ⭳/⭱ glyphs and file-oriented tooltips. Engine/persist untouched (493 tests still pass). Browser-verified: autoSave writes…f80d3ea
-
Add a Home button that confirms saving before returning to the menu
Claude +1: Claude Opus 4.8Unverified signatureA topbar ⌂ Home button (beside Save/Load) returns to the setup screen, first asking whether to save. A skirmish/Odyssey gets a "Return to the menu?" modal with Save & Exit / Exit without Saving / Cancel; a scenario (which can't be resumed) gets a plain "Leave the mission?" with Leave / Cancel. Cancel, the backdrop, and Esc all dismiss it. - index.html/dom.js: the button. - boot.js: export restartToMapSelect and stop the loop on the way out (idempotent — game over already stopped it), so the game freezes behind the menu. - saveload.js: goHome() builds the confirm modal, routing Save & Exit through the existing mode-aware save() (galaxy vs single-state). - style.css: modal + .btn primary/ghost variants. UI only; 468 tests pass. Browser-verified all paths: Cancel keeps playing, Exit writes no save, Save & Exit writes one, and a scenario offers no save option. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01AEQUVetLk31Jj7niDQA…cf3fc9b
-
Odyssey Phase 5: the starmap — the galaxy's capstone view
Claude +1: Claude Opus 4.8Unverified signatureA full-screen galaxy map ties the Odyssey together. Every world sits on a ring, coloured by your relationship to it: your active capital, a colony you hold (with its neighbour's stance), or somewhere still unexplored (shown with its AI doctrine). If your capital has a Spaceport and the credits for fuel, clicking a world jumps there straight from the map; otherwise the header prompts you to build one. A visited-worlds count echoes the original game's "see every world" thread. - starmap.js (new): the overlay, self-wired to a topbar "Galaxy" button and the M key (Esc / backdrop closes). Pure view over a new galaxy.js galaxyStatus() snapshot (per-world status + stance + the visited/total counts). - hud.js: the Galaxy button shows only in Odyssey; the skirmish Save/Load buttons hide there (galaxy persistence isn't supported, so a skirmish save would mislead). - index.html/dom.js/style.css: the button, the overlay, and its styles — including the per-component `.hidden` rules thi…842eae1 -
Odyssey Phase 4: living galaxy — scarcity diplomacy + colony alerts
Claude +1: Claude Opus 4.8Unverified signatureThe neighbours are no longer just obstacles: a world can be shared in harmony and then turn on you as its ground runs out, and the colonies you leave behind report in when they come under fire. Engine: - diplomacy.js (new): each Odyssey world's AI holds a stance (-1 hostile .. +1 allied) that drifts toward a target set by how depleted the world's deposits are — cordial while rich, hostile once mined out. - ai.js: the offense commit is gated on the stance — at peace the neighbour keeps building economy and army and still DEFENDS itself, it just launches no attacks on you until scarcity turns it hostile. A skirmish has no diplomacy, so the gate is a no-op there (the resolves-to-a-winner guarantee is untouched). - galaxy.js: every world gets a diplomacy stance; sweepColonies() watches the background colonies — reporting one coming under attack or being lost — and is the single place their sim events are drained (a colony isn't rendered, so nothing else consumes them). -…11037b3 -
Add Convoy Escort scenario: shepherd freighters across a raided route
Claude +1: Claude Opus 4.8Unverified signatureIntroduces a scenario layer over the existing skirmish engine. The Convoy Escort scenario tasks the player with shepherding four freighters along a multi-leg route through pirate territory to a destination gate, against a global mission clock. Scenario framework (engine/scenarios.js, new): - setupEscort() reuses createGameState for map/fog/players, then clears units+buildings and lays out an evenly-spaced left-to-right route of stations (one leg per configured risk value), the four freighters, and a difficulty-scaled escort fleet. - updateScenario() drives a prep -> travel -> docked phase machine: freighters advance leg by leg, pirate waves spawn per-leg by risk and steer at the lead freighter, and the player docks between legs to repair from a budget. Win on delivering >=1 freighter before the clock; loss on losing them all or the clock expiring undelivered. - Determinism preserved: scenario randomness flows through a seeded mulberry32 on state.scenario.rng, decoupled…dcbdeac -
Portrait mobile layout — a collapsible bottom sheet for one-handed play
Claude +1: Claude Opus 4.8Unverified signatureFinishes the mobile story: held upright on a phone, the game now reshapes from the desktop "canvas + right-hand panel" into a full-width battlefield with the selection panel as a BOTTOM SHEET — the standard one-handed mobile shape. Layout (pure CSS, scoped to `@media (orientation: portrait) and (max-width:820px)`): - #main stacks into a column: the battlefield spans the full width up top, the panel becomes a full-width sheet along the bottom. - The sheet is height-adaptive (as tall as its content, capped at 42vh so the battlefield keeps most of the screen), scrolls when it overflows, and pairs its buttons two-per-row so it shows more at a glance. - `min-height: 0` on #view lets the flex column shrink the canvas so the sheet actually gets its share — without it the canvas's intrinsic height shoves the sheet off the bottom of the screen. - The minimap is repositioned/shrunk for the portrait view. Collapse handle: a floating ▾/▴ button (a sibling of the panel, so panel rebuil…d088d6d -
Faction asymmetry — pick a side with its own passive identity
Claude +1: Claude Opus 4.8Unverified signatureThree playable factions, each a small bundle of passive trait multipliers — so a faction changes how a side plays without touching the shared roster, the rock-paper-scissors triangle, the tech tree, or the AI's build logic: • Frontier — mobility & vision (speed +8%, sight +6%): strikes first, holds the map. • Miners — industry (haul +15%, build/train −10%): out-scales in the long game. • Syndicate — firepower on a lean economy (damage +10%, haul −8%): win the fight, fast. Distinct axes with a real opportunity cost (Syndicate pays economy for its punch), so the pick is a strategic fork, not power creep. How it plugs in (engine/factions.js): A faction's traits are keyed on the exact side-modifier names the engine already reads, so they fold straight into map.js's sideMod — one seam, and every existing consumer (movement speed, fog/aggro sight, gather yield, build/train time) picks up the faction edge for free, stacking multiplicatively with a world's own per-side mod…ec7c7c8 -
Tier 3: accessibility — foe marker on enemy buildings + click-to-jump alert
Claude +1: Claude Fable 5Unverified signatureTwo colour-independent cues so the game reads without relying on the cyan-vs-red hue. - Enemy buildings now carry the same downward foe ▼ marker that enemy units do (render.js, reusing drawEnemyPip). Friend/foe becomes a SHAPE cue, not colour alone — a colourblind player can tell an enemy base from their own. - The "Under Attack" banner is now clickable and jumps the camera to the attack (main.js tracks the last hit even while the alert is throttled; the banner hints "▸" and a title). A raid on the far side of a Gigantic map is one click away instead of a frantic scroll. Render/UI only — no engine change, 297 tests pass. Browser-verified the enemy building shows the foe marker and the friendly one does not. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01AEQUVetLk31Jj7niDQAsXL406718a -
Tier 3: save / load — resume a game exactly where you left off
Claude +1: Claude Fable 5Unverified signatureThe payoff of Tier 1's seeding. Because the sim is deterministic and seed-driven, a save doesn't need the whole world — the map (terrain, node positions) regenerates from the seed, so we persist only the seed + the DYNAMIC state: entities, economies, fog memory, and the AI's bookkeeping. - engine/persist.js: serializeGame(state) -> a detached, JSON-safe snapshot; deserializeGame(save) rebuilds a state that ticks on from exactly where it was saved. visible fog is recomputed on load (only permanent `explored` memory is stored); the entity-id counter is snapshotted/restored (new state.js peekEntityId/restoreEntityId) so a loaded game keeps minting non-colliding ids. - state.js records planetId/sizeMult/resourceMult so the map can regenerate. - main.js: Save/Load buttons in the topbar and a "Resume saved game" button on the splash; startGame's boot path is factored into a shared bootState() that both fresh and loaded games funnel through. localStorage-backed. Additive — it ne…dfed415 -
Tier 2 UX: Hold-position stance + a topbar idle-worker indicator
Claude +1: Claude Fable 5Unverified signatureTwo safe quality-of-life additions while balance items await playtesting. Hold position (H / a panel button): a combat unit stands its ground and fires on anything that enters weapon range but never CHASES an auto-acquired target out of position — the standard defensive stance for holding a line or a choke. combat.js honours a new unit.hold; any move/attack order or Stop clears it. Player-only — the AI never sets it — so determinism and the resolve guarantee are untouched. Idle-worker indicator: a lost worker on a big map is easy to miss, so the topbar now shows a "N idle" badge (hidden at zero) that jumps to the next idle worker on click, mirroring the ` key. Both surfaced in the help overlay and the controls legend. Tests (+2, 279 total): a holding unit fires an in-range enemy but won't chase an out-of-range one; issueHold sets the stance on combat units only and a move/Stop clears it. Browser-verified: the badge shows "3 idle" at start and the help lists Hold; clean boot. Co-Au…fea25dd -
Tier 1: seed the sim, attack-move/hotkeys, onboarding, rally-to-mine, tiebreak
Claude +1: Claude Fable 5Unverified signatureSix high-leverage quick wins from the improvement review. Seed the sim (engine/rng.js, state.js, main.js): production now draws a seed at match start and flows it through a shared mulberry32, so a match is reproducible — the seed shows on a topbar chip (click to copy) and the game-over screen, and can be re-entered on the setup screen to replay the exact same world. This is the keystone that later unlocks replays/save-load. Engine determinism is now guarded by a test that scans engine/ for Math.random/Date/performance.now (the two sanctioned defaults + the render clock are marked deterministic-exempt). Standard army verbs (input.js): A-then-click issues attack-move (the missing genre default — plain right-click still moves and ignores enemies); the waypoint queue moves from Ctrl to Shift so Ctrl is add-to-selection only; Esc cancels a pending attack-move or build placement. A crosshair cursor shows when armed. A is no longer a pan key (pan left with arrows/edge-scroll). Onboarding (…4f1cb54 -
Tier 9: polish & accessibility
Claude +1: Claude Fable 5Unverified signatureAudio (sound.js / main.js / input.js): - Order-acknowledgement and selection cues — the most-missed feedback; a quiet click confirms a move/attack/gather/rally order landed and a blip confirms a selection. - Stereo panning by on-screen position, so you can hear which flank a fight is on; per-play pitch jitter so repeated hits/spawns aren't mechanically identical; a master-gain bus and a top-bar volume slider (separate from mute). Accessibility: - Enemy units carry a small downward pip — a SHAPE cue, so friend-vs-foe no longer rests on the cyan-vs-red colour alone (three-band health bars from Tier 7 help here too). - prefers-reduced-motion: the under-attack banner holds steady and the world/minimap pings de-animate to a static ring instead of pulsing. Visual juice: - A brief impact spark where each shot lands, so a hit reads as connecting rather than a line drawn through the target. Docs: README updated for the new controls (groups, Q/X/`, additive select, edge/minima…4f2ab31
-
Tier 3: repeated AI waves + counter-building, combat feedback, minimap, build validation
Claude +1: Claude Sonnet 5Unverified signatureAI: - Replace the one-shot attack flag with a home-army/committed-army split, so the AI launches repeated waves for the rest of the match instead of attacking exactly once and then sitting on an ever-growing army. - Every third unit built reacts to whatever combat type the player fields most, building its hard counter (derived from the bonusVs tables, not hardcoded) instead of blindly following the archetype's mix -- the Skiff/Bastion/Lancer triangle now actually matters to the AI's play. Combat feedback: - attackHit/entityKilled sim events now carry enough info for a new effects.js (transient, client-side) to drive tracer lines and death flashes, drained each render frame the same way sound events already were. Situational awareness: - A minimap (fog, deposits, units/buildings, camera viewport) that doubles as a click-to-jump camera shortcut. - An "Under Attack" banner + alarm sound + world/minimap ping when the AI lands a hit on the player, throttled so a siege …cae3dc6 -
Tier 2: rally points, Refinery upgrades, AI archetypes, and sound
Claude +1: Claude Sonnet 5Unverified signatureFour features, in dependency order (smallest/most self-contained first): ## Rally-point UI (engine/commands.js, input.js, render.js) Right-click a single selected production building (Command Center or Barracks) to set where it sends newly produced units — the engine already stored an arbitrary rally point per building and read it fresh at spawn time, there was just no way to change it. Shows a dashed line + marker for the currently selected building only, to avoid cluttering the view with every building's rally line at once. ## Refinery + upgrades (engine/entities.js, production.js, combat.js) A new building (built by a Worker like the Barracks) researches two one-time, player-wide purchases: Reinforced Plating (crystals, -15% damage taken) and Overcharged Weapons (radioactives, +20% damage dealt). Both are live multipliers applied in combat.js's damage calculation rather than baked into unit stats at spawn, so they affect a player's whole army immediately, not just future product…9eded26 -
Fix worker stacking on a shared node; add a battlefield picker
Claude +1: Claude Sonnet 5Unverified signatureWorkers sent to the same node all walked to its exact center and rendered fully stacked. gather.js now sends each one to a stable per-unit point on a small ring around the node instead, so a group visibly spreads out around what it's mining. Also replaced the hardcoded single map with a pre-game picker (three ore-bearing worlds pulled straight from data.js: Ferros Prime, Korrath, Vesper), each with a genuinely different resource layout since node cluster count scales with the planet's own deposit yields. The post-game overlay now offers "Choose another battlefield" to start a fresh skirmish on a different world without reloading the page. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01BDmJ8vc9jxc6TUegK9F2YJ6fe5e5b -
First playable slice: 1v1 skirmish vs scripted AI on Ferros Prime
Claude +1: Claude Sonnet 5Unverified signatureGather ore/crystals/radioactives with Workers, build a Barracks, produce Skiffs, fight, win by destroying the enemy Command Center. engine/ holds the sim: a fixed-timestep loop decoupled from render rate, movement, the gather/deposit loop, combat with auto-acquire, building construction + unit production queues, a scripted AI opponent, and the win condition. render.js/input.js are the canvas view and mouse controls (drag-select, right-click context orders, build placement); main.js wires it to a DOM HUD. Converted data.js's top-level tables to ES module exports so the engine can import them directly. Covered by 23 node:test cases including a full simulated skirmish that plays to a winner, and manually playtested end-to-end in a real browser (gather -> build -> produce -> victory/defeat UI) via Playwright. One real bug caught in that process: constructed buildings never got a `radius` copied onto the instance, which went on to poison unit spawn points and building hit-testing with Na…17641e3 -
Seed repo: shared universe data + real-time loop bootstrap
Claude +1: Claude Sonnet 5Unverified signatureCarries over data.js verbatim from the original Stellar Frontier repo (pure static data: factions, commodities, recipes, the 20 charted worlds) plus the style.css color palette, so this reads as the same universe. Everything else is new — index.html/main.js is a minimal canvas + requestAnimationFrame loop proving out the real-time tick, with no game state or mechanics yet. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01BDmJ8vc9jxc6TUegK9F2YJ7a2b109