Spaces:
Paused
Paused
Commit History
-
Odyssey: bound the living galaxy to 3 seed-drawn background worlds
Claude +1: Claude Opus 5Unverified signaturecreateGalaxy instantiated and simulated all 11 worlds from turn one. It now brings up the start seat plus a pseudo-random draw of BACKGROUND_WORLDS (3) others — backgroundWorldIds, keyed on the galaxy seed and the start world from its own distinctly-seeded stream, returned in fixed roster order so the background scheduler and every galaxy-wide scan read exactly as before. Which neighbours are alive is now part of what a seed means; same seed still replays the same galaxy. The rest of the roster stays DORMANT: still on the starmap, still jumpable, still a full skirmish map — built in on arrival (jumpCapital already did this for a never-visited destination) and simulating from then on like any world you leave behind. addPlanet now honours a standing claim when it builds a world in, so a dormant world the faction-spread sweep claimed flies those colours when the player finally lands on it. Three paths assumed every world's state existed, and are fixed here: - initiateJump treated a mis…b88cc9c
-
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 -
Phase 4 (part 2): the playable Gauntlet — play a real match, rate it, resume it
Claude +1: Claude Opus 4.8Unverified signatureThe human-inclusive half of docs/competitions-and-elo.md Phase 4, on top of part 1's persisted ledger support: a fifth Competition tab where you play ONE live match against each AI entrant in a field, in order, and every result lands on the same bracketed ladder the AI entrants are rated on. One live match per opponent is the format's defining decision, not an economy measure: an AI-vs-AI pairing is worlds x seeds x 2 sides of simulated matches (seconds of compute), but a person cannot play 40 real matches. That is why Gauntlet is the human-inclusive format that ships first, and why the config screen prices a run in HOURS OF REAL PLAY before Start is clickable ("4 live matches — one per opponent — up to about 1 h 20 min"), defaulting to Quick (20 min), since the same field at Standard is over three hours. - boot.js startCompetitionMatch(fixture): a near-copy of startGame, deliberately — same resolveSeed/difficultyDials/createGameState/bootState chain, same loop, same HUD. A gaunt…1ff6f70
-
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 -
Starmap live colony ledger: alert badges + garrison line
Claude +1: Claude Sonnet 5Unverified signatureMulti-colony Odyssey play routed all trouble through transient toasts (boot.js notifyColony, 5-9s, max 3 on screen); the starmap itself showed only status/income/stance/industry, so a world under attack, recently fallen, or undefended looked identical to a healthy one. - boot.js's notifyColony now records every attack/hostile/lost notification onto the session as game.colonyAlerts[planetId] = {type, at} (session.js), in addition to its existing toast. Written unconditionally for all three types, independent of the toast's own 9s throttle (which still only compares a world's own prior ATTACKED timestamp, unchanged). focusActivePlanet clears exactly the one world's entry when the player actually arrives there; bootState resets the whole ledger for a fresh game. Transient session state, like lastAttackAt/groups next to it — never persisted. - starmap.js's renderStarmap reads the ledger per world when building each node: a CSS badge class + sub-label reading "under atta…25e82b6 -
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
-
Redesign panel collapsibility: per-category, and generalized to every long list
Unverified signatureThe 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 from the set means "expanded" — the old field's "starts open" default, generalized, …bb92539 -
Make the Build submenu collapsible for Worker/Engineer/Technician (#30)
Unverified signatureSelecting a generalist Worker (every building category) or a mixed selection could show a long wall of "Build X" buttons — the original "very long list of jobs" complaint the worker-specialist split was meant to shrink, still visible on any one builder's own panel. Adds a "Build (N)" header, toggled open/closed like the existing formation-choice UI preference (game.formation, session.js): game.buildMenuOpen persists the choice across every later selection until toggled back, defaulting to open so behavior is unchanged until a player opts in to collapsing it. The header's count is derived from the exact same visibility filter the list itself renders with (category-eligible AND, in Odyssey, prereqs met or in the always-shown set) rather than a separately-tracked list, so "Build (N)" never promises more than expanding actually reveals. Verified in a live browser session (skirmish Worker and an Odyssey Engineer): the header shows the right count, collapsing hides the list, and the collap…96134e3
-
Formation shapes, leader positioning, and nested formations for grouped units
ClaudeUnverified signatureMulti-unit move/attack-move used to just spread the group across a flat square grid (engine/commands.js formationSpots) with no leader concept and no way to hold that shape once stationary. This adds: - Four selectable formation shapes (engine/formation.js): Grid (the original spread, kept byte-identical for the default case), Line (abreast the direction of travel, leader nudged front or back), Wedge (a spearhead with the leader at the tip, or a protective chevron with the leader shielded at the rear and flanks leading), and Circle (a ring around the leader at its center — "protection of a formation," the group-scale sibling of the existing single-target Escort). - A deterministic "leader": the unit with the most maxHp, ties broken by id — never selection-order-dependent. - Spacing derived from the group's own hull sizes, not a flat constant, for every shape except the untouched default grid path. - Nested formations: a selection already spread into distinct spatial …4c8f450
-
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 Odyssey open-world mode (Phase 1): endless single-planet foundation
Claude +1: Claude Opus 4.8Unverified signatureThe first slice of the "Space Exploration" meta-game — the open-world campaign the project is named for. Phase 1 stands up the galaxy scaffold and a playable, endless single-planet sandbox with one relocatable Command Center; the multi-planet jump, market, and diplomacy build on this in later phases. Engine: - state.js: new `endless` flag on a game state. - victory.js: checkEndlessLoss() — an Odyssey never ends by conquest or clock; it only ends in defeat when the player loses their single Command Center (their capital). Razing a neighbour's CC just leaves that world running. - sim.js: tick() routes the terminal check three ways now — scenarios settle themselves, endless uses checkEndlessLoss, a normal match uses checkWinCondition. - engine/galaxy.js (new): the meta-layer. createGalaxy() picks a random start world from the galaxy seed and builds its planet state (a normal createGameState flagged endless); holds universal `credits`, the active planet id, the world roste…cae0952 -
Refactor: split the main.js god-file into focused modules
Claude +1: Claude Opus 4.8Unverified signaturePure code-health, behavior-preserving: main.js goes from 1001 lines to a 117-line composition root, with its concerns extracted into cohesive modules: session.js — the live { state, input, supplyBlockedUntil } holder that boot reassigns and every UI module reads (was the main.js closure vars). dom.js — every getElementById handle + MINIMAP_W/H + isTouchMode(), once. setup.js — the splash / map-select screen and the shared `setup` config. boot.js — startGame, bootState, the fixed-timestep loop, the frame-event sound/effects pump, and the under-attack alert. hud.js — renderHUD, the selection panel (rebuild + live patch), the button / tooltip factories and formatters. overlays.js — seed + faction chips, the objectives strip, the help overlay, and the game-over screen. saveload.js — localStorage save / load. main.js — imports the graph (each module self-wires its own listeners), owns …d32ddf2