Spaces:
Paused
Paused
Commit History
-
Tier 3b: rank formation slots per cluster, not globally
Claude +1: Claude Opus 5Unverified signatureDESIGN QUESTION: should range-layering rank per cluster or globally? PER CLUSTER. formationSlots deliberately gives each spatial cluster its own sub-formation — "spaced by each cluster's own footprint so sub-formations can't overlap" — and clusterUnits promises it "preserves each unit's relative position". rankSlotsByRange then re-paired GLOBALLY: it sorted ALL followers by weapon range against ALL slots by forwardness, with no cluster awareness in either half. Two armies far apart therefore cross-swapped sub-formations and each walked the extra distance to the other's slots. The resulting shape was still correct, which is why this reads as pathing jank rather than as two features disagreeing. Ranking per cluster honours both at once: each cluster keeps its members, and each is internally range-layered, which is the entire point of the layering. Two things the work corrected in me rather than in the code: Clustering the FOLLOWERS was wrong — excluding the leader first splits its own…cdae5f9 -
Tier 1, step 4a: five reproduced engine defects (A1-A4, A11)
Claude +1: Claude Opus 5Unverified signatureEvery fix was preceded by a test verified red against the real behaviour, and the two A1 tests were additionally re-run with the fix reverted to prove they bite rather than passing incidentally. A1 — keepFollowingLeader decided its leader was gone by hp alone. A follow-leader order holds a live object REFERENCE, and two removal paths delete a unit that is still at full hp: deployColonyShip and updateUnitRecycle. Escorting a colony ship to a site and deploying it is the canonical Odyssey play, and it left both escorts permanently pinned to a ship no longer in state.units, orbiting its frozen last position. They still accept new direct orders, so it read as "my units randomly stopped obeying the formation". The check now asks the state, not the field. (escort orders were always immune — escortSlot resolves through state.units.get every tick.) A2 — the squad-release loop spliced the array it was iterating. A for...of iterator is index-based, so every splice skipped the next element: dro…0da037f
-
Fix the Mender formation-rank fallback: rear, not front
Claude +1: Claude Sonnet 5Unverified signaturerankSlotsByRange sorted units by UNITS[type].range ?? -1 ascending, so an unarmed unit (no range field at all — the Mender, role:"support") evaluated to -1: the single lowest key in the whole group, landing it on the MOST forward slot of a shaped formation. The proposal's own Problem/Change text is explicit that support and unarmed units belong "innermost/rear" — a healer leading the charge is exactly backwards from the point of the feature. The missing-range fallback is now Infinity, so any unarmed unit sorts last and lands on the rearmost slot instead. Caught during the integration pass, flagged clearly by the implementing team as a "Where"-text-vs-prose-summary discrepancy rather than shipped silently. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>22c31e3 -
Range-layered formation ranks and per-building logistics priority
Claude +1: Claude Sonnet 5Unverified signatureTwo P3-Combat-Logistics proposals sharing engine/commands.js, landed together per the roadmap's own grouping. Range-layered formation ranks (brawlers screen, artillery trails): dispatchFormation now re-pairs a shaped (line/wedge/circle) formation's followers with their slots by weapon range instead of raw selection-array order — shortest-ranged units (Bastion, Skiff) take the most forward slots, longest-ranged (Lancer, Breacher, Colossus) the rearmost, via a new rankSlotsByRange helper. Ranking uses each slot's forwardness (projection onto the formation's own travel heading), computed by a new resolveHeading export in engine/formation.js — factored out of formationSlots' existing heading-resolution logic (including its near-zero-heading fallback to facing +x, which matters for issueHoldFormation's origin===destination case) so the two never drift apart. The legacy grid shape and the AI's plain (no-formation) path are both untouched — reordering only ever runs inside the player leader/…b383918 -
Patrol: looping attack-move waypoints
Claude +1: Claude Sonnet 5Unverified signatureAdds issuePatrol(units, points) to engine/commands.js: builds an ordinary attack-move waypoint chain (engine/combat.js needs nothing - a patrol leg auto-engages exactly like any other attack-move) with every leg flagged patrol:true, gated to combat/scout roles like issueHold. engine/sim.js's pull-next-queued-order step requeues a patrol leg onto the tail the instant it's pulled off the front, so the chain cycles forever instead of draining to idle once the last point is reached; issuePatrol gives the very first leg the same trailing copy by hand, since it lands directly in `order` rather than passing through that queue-pull step. Also adds a "attack-move" case to sim.js's generic order switch (shared with "move"): patrol is gated to combat/scout roles, but before this only a combat-role unit ever actually acted on an attack-move order (routed through combat.js's updateCombat) - a scout given one would have hit the switch's `default: unit.order = null` and never moved a single pixel. A…9f7bfe7
-
Cap a formation leader's pace 5% below its slowest member's speed
ClaudeUnverified signatureAn exact tie between the leader's speedCap and the slowest member's own top speed leaves a straggler (knocked off its slot by avoidance steering, a longer way around an obstacle, etc.) with no way to ever close the gap and reform, since it can never out-pace a leader already moving at its own maximum. groupSpeedCap now trims the cap to 95% of the slowest member's speed, giving stragglers room to catch back up — applies uniformly to move/attack-move/hold-formation and a formation-leading scout order, since all three share this helper.7836482 -
Make scout mode lead a formation instead of abandoning it
ClaudeUnverified signatureReverses the previous fix: a Ranger leading a formation should stay with the group as a protective scout, not break away solo. issueScout now leaves its followers on their existing follow-leader order (instead of releasing them) and caps the scout order's speed to the group's slowest member via a new groupSpeedCap helper shared with dispatchFormation's existing speed-cap logic. scout.js now runs that cap through orderedSpeed instead of always using the unit's own full speed. A solo Ranger (no squadFollowers) is unaffected — it still scouts at its own full speed. Verified end-to-end via the real sim tick loop: a Ranger leading a Worker, once put into scout mode, travels together with it at the Worker's pace instead of pulling away.f71d721 -
Release a formation leader's squad when it enters scout mode
ClaudeUnverified signatureissueScout only cleared the Ranger's own squadLeader link, not its squadFollowers list. A Ranger breaking off from a formation to scout autonomously left its old followers on a live follow-leader order, trailing it indefinitely across the whole map at their own slower pace instead of being released — the formation stuck dragging behind a unit no longer acting as its leader. Now the Ranger's followers are stopped and unlinked, the same way dispatchFormation already drops a follower that falls out of a redefined squad. Verified the Ranger's own scout order was never actually speed-capped (scout.js always uses the unit's own full speed, never orderedSpeed) — added a test locking that in alongside the squad-release fix.5485eb0 -
Add player-assigned home base override and worker repair of wounded units
Claude +1: Claude Sonnet 5Unverified signatureCloses the two items flagged as future work in the previous zone-affinity commit: a home-base override, and repairing mobile units. engine/gather.js zoneFirst now takes an optional homeId that, when it resolves to a live owned Command Center, wins over the usual nearest-CC distance guess. Right-clicking a completed own CC with eligible units selected (worker, Mender, freighter) pins unit.homeCC to it (issueSetHomeBase) — a passive assignment that only changes the unit's next idle job search, never its current order. A stale override (its CC destroyed) is ignored and falls back to the distance guess. Threaded through every zone-first call site in haul.js and repair.js's pickRepairTarget, so it covers haul/service/ferry job search and both the worker repair job and the Mender's own autoRepairRoam. assignRepair no longer restricts itself to buildings: a worker can now be assigned to chase down and patch a wounded mobile unit the same way it patches a turret, walking toward the target's …06de929 -
Add Command Center zone affinity for haulers, plus a worker repair job
Claude +1: Claude Sonnet 5Unverified signatureHaulers/servers/ferriers used to pick jobs by raw nearest-distance across the whole empire, with no notion of "which base do I belong to." Once a busy base's own ≤2-per-target caps filled up, idle labour got routinely poached across the map by another base's job. engine/gather.js zoneFirst now restricts each search to the worker's own Command Center zone first, only widening to the whole empire when the home zone has nothing to offer (byte-identical to before on any single-CC game). The auto-repair Mender's roam target-pick adopts the same zone-first logic via a shared pickRepairTarget helper (engine/repair.js). Buildings could previously only be healed passively by a Mender in range. Workers can now be assigned a repair job (auto, zone-first, after ferry/haul/service in priority; or manual via right-click, issueRepairBuilding), patching a damaged own building at a flat rate, capped per building like the existing haul/service caps. Both changes are pure functions of state and reused …32d2559
-
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 -
Split the generalist Worker into category-owning specialists (#28)
Unverified signatureThe single Worker did everything — mining, founding every building type, hauling, servicing factories, ferrying freighters — across ~22 building types, making its job list unmanageable. Adds three new specialist unit types, each owning construction (and, for Technician, ongoing operation) end-to-end for a building category, mirroring the existing Mender precedent (a proven specialist split with its own tech gate): - Miner: gather-only, no tech gate, cheaper and faster-mining than Worker. - Engineer: builds Infrastructure + Military buildings (Command Center, Habitat, Spaceport, Datacenter, Antimatter Gate; Barracks, Turret, Star Dock) — the buildings with no ongoing haul/service loop. - Technician: builds + hauls/services/ferries Industrial buildings (Refinery, Foundry, Arsenal, the factory chain, power stations, Plasma Rig), gated behind Foundry like Mender. Foundry/Arsenal land here (not Military) because both are forward drop-offs that already need this logistics capabi…340ed0f
-
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 -
Let workers use a freighter as a collection point, with self-shuttling when full
Claude +1: Claude Sonnet 5Unverified signatureWorkers can already ferry cargo to/from a landed freighter. This adds a Collection Point toggle (no research needed): once a freighter's hold is full, it drives itself to the nearest Command Center, banks its whole hold, and returns to the spot it was anchored at to keep collecting -- so a freighter parked at a remote claim can act as a self-emptying forward depot instead of needing a worker to carry its load home too. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01AdsYZQyGk32n8x3UBiPGup7a847ac -
Let workers load/unload freighters and add an AI-logistics freighter mode
Claude +1: Claude Sonnet 5Unverified signatureWorkers can now physically ferry cargo to and from a landed freighter (right-click it, like assigning a worker to service a building) — the freighter becomes a mobile collection point for nearby production instead of every load detouring through the Command Center. Also adds a new Datacenter research, Autonomous Freight AI, that lets a freighter join the local haul/service logistics chain on its own, like a worker but at its own much larger cargo capacity. Running autonomously burns AI Cores from the treasury while active; out of AI Cores it just pauses in place instead of losing its job or cargo. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01AdsYZQyGk32n8x3UBiPGup6f39bc5 -
Redefine formation leader as first-selected unit, add persistent follow, speed cap, and drag-to-face
Claude +1: Claude Sonnet 5Unverified signatureReworks the formation system per feedback on PR #9: - Leader is now whichever unit the player selected first (the one others get added to), not a stat-based pick. pickLeader() and layout() in engine/formation.js simply use array order instead of scanning for highest maxHp. - Selecting and moving just the leader now drags along everyone previously grouped with it, with no need to reselect the group. A new persistent "follow-leader" order (engine/commands.js, engine/movement.js, engine/sim.js, engine/combat.js) mirrors the existing escort-order pattern: followers re-seek the leader's live position every tick via object references (unit.squadLeader / leader.squadFollowers), set up by dispatchFormation() and torn down by any direct order that bypasses it (stop, hold, scout, build). - A formation's travel speed is capped to its slowest member so a fast leader can't stretch the group out of shape. dispatchFormation() computes the cap once at issue time and stores i…6648f62 -
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
-
Round-trip worker logistics + manual worker→building assignment
Claude +1: Claude Opus 4.8Unverified signatureWorkers no longer run empty legs, and the player can put a worker on a specific building. The old separate supply/haul-for-factories jobs become one SERVICE round trip: in a single loop a worker carries a needed INPUT from the treasury to a factory AND carries its finished OUTPUT back — neither leg wasted. Pure producers (the Rig, the forward drop-offs) keep the one-way HAUL. - haul.js: rewritten around updateService/assignService (round trip) + updateHaul/ assignHaul (pure producers only, recipe buildings excluded). Adaptive legs — with no input to fetch it goes straight for the output; with no output it just delivers. - commands.js: issueServiceBuilding — assign workers a standing `manual` service order. - input.js: right-clicking a completed friendly factory / Rig / drop-off with selected workers assigns them to service it (they keep serving it until re-ordered). - sim.js: idle worker → haul (pure producers) else service (factories); dispatch service. - persist.js/types.js: `…d443e60
-
Tier 1 review fixes: save-load hardening, engine guards, UX bugs
Claude +1: Claude Opus 4.8Unverified signatureAddresses the Tier-1 (correctness & safety) findings from the codebase review. Save/load safety (engine/persist.js, saveload.js): - Validate values on load, not just JSON shape: drop entities of unknown type (an undefined def threw on the first tick, freezing the game AFTER load's try/catch returned), coerce numeric fields to finite/in-range (a string "time" concatenated; a NaN coord poisoned the spatial hash), and recompute the entity-id counter past every loaded id so a low/missing nextEntityId can't mint a colliding id. All identity for a valid save, so determinism is untouched. - Persist aiWaveCount — omitting it reset the AI economy-raid cadence on every reload, breaking continue-identically for Tactical games. - deserializeGalaxy now throws on structural nonsense (no worlds/planets, or an activeId with no planet) BEFORE the caller tears down the live game, and filters the world roster to real world ids. - autoSave returns success; "Save & Exit" falls back to a …a4bbde9
-
Escort order: right-click a friendly ship to guard and follow it
Claude +1: Claude Opus 4.8Unverified signatureRight-clicking a friendly ship with a group selected now puts the group into a protective escort formation: each unit takes a stable slot on a ring around the target and follows it wherever it's ordered, holding station until given another order. A combat escort still auto-acquires threats that come near the target, then reforms — so it actually protects the ship, not just trails it. - movement.js: escortSlot() — the deterministic per-unit point on the ring around the guarded ship (the ring expands to seat the whole group). Null when the target is gone, so callers drop the order. - commands.js: issueEscort() assigns each unit an {type:"escort", targetId, slot, slots} order (stable slots → deterministic formation). - combat.js / sim.js: handle "escort" for combat, worker, and support units — a persistent follow (never cleared on arrival). Combat escorts fall through the normal auto-acquire first, so they defend then reform. - input.js: a right-click on a friendly UNIT (not bu…b1fda0e
-
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 -
Ranger scout unit + AI hunts down hidden Command Centers
Claude +1: Claude Fable 5Unverified signatureTwo related recon features. New unit — the Ranger (engine/entities.js, scout.js, render.js): - Trained at the Command Center like a Worker; cheap (45 ore) and fragile (50 hp) with only a token bite for self-defence. Its own role "scout", so it never auto-acquires and is never swept into the combat army. - Its three edges are a scout's: far sight (340, more than any combat unit), all-terrain movement (the existing `allTerrain` flag — ice fields and rough ground never slow it), and an autonomous SCOUT MODE (order "scout", the E key / a panel button). In scout mode it heads for the nearest unexplored ground on its own, its sight reveals it, it re-picks — a frontier that expands from home until the map is charted, then it patrols to keep vision fresh. Persistent and deterministic (a row-major fog scan, no rng), so it's safe in the sim. - Draws as a light recon craft with a sensor ring, distinct from the Skiff. A generic-unit fallback in the renderer keeps the "every enti…c310233 -
Content: tech tree — a Foundry gate for the Tier-2 units
Claude +1: Claude Fable 5Unverified signatureTurns "all four combat units from turn one" into a real build-order fork. - entities.js: new Foundry building (ore-only, requires a Barracks) and a requires field gating Lancer and Breacher behind it. prereqsMet() is the shared source of truth (a building token needs a COMPLETED building; an upgrade token needs it researched). RPS triangle and stats untouched — only WHEN each unit is reachable changes. - production.js / commands.js: queueProduction and issueBuild reject a locked unit/building (prereq checked before affordability, so "locked" outranks "too expensive"). - ai.js: effectiveMix drops tech-locked units so the cycle runs Skiff/Bastion until the Foundry is up and can never stall on a locked entry. The AI builds the Foundry as priority infrastructure and reserves its ore from the unit stream while banking — otherwise it spends every spare 100 on a Skiff and never techs. Rushers drop Lancer from their mix (pure Tier-1, no tech delay); economist/balanced re…e3fdeef -
Tier 7: bring controls & UX up to RTS standard
Claude +1: Claude Fable 5Unverified signatureControls (input.js / commands.js): - Control groups: Shift+1–9 bind, 1–9 recall (Ctrl+digit is the browser's own tab-switch shortcut, so Shift is the conflict-free bind modifier; matched on physical key code so it works with Shift held). - Stop (X) drops orders + queued waypoints; Select Army (Q); Idle Worker (`) cycles to and centres on the next idle gatherer. - Ctrl+drag/click adds to the current selection; double-click selects every on-screen unit of that type. - Edge scroll: cursor at a canvas edge pans the camera. - Right-click the minimap to order the selection there (move / attack-move) without scrolling the main view first. HUD & readability (main.js / render.js / style.css): - Unaffordable produce/build/research buttons grey out and play the denied buzz on click instead of failing silently; buttons carry stat tooltips. - Selected units/buildings keep their health bar even at full HP, and the bar now has three colour bands (green/amber/red) rather than a green/re…4e300be -
Use Ctrl instead of Shift as the waypoint-queue modifier
Claude +1: Claude Fable 5Unverified signatureRebind the queue-a-waypoint gesture from Shift+right-click to Ctrl+right-click, and update the in-game hint, README controls, and engine comments to match. The command layer stays modifier-agnostic (it takes a queue boolean); only the input layer names the key. Ctrl also sidesteps the Firefox quirk that let Shift+right-click bypass the page's context-menu suppression, so the window-level preventDefault now holds across every browser — dropped that stale caveat from the comment. Verified in headless Chromium: Ctrl+right-click chains waypoints and the dashed path renders; 162 tests pass. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01AEQUVetLk31Jj7niDQAsXL3f927e2
-
Waypoint queueing: shift+right-click chains orders into a path
Claude +1: Claude Fable 5Unverified signatureUnits now carry an order queue. A plain right-click issues its order immediately and clears the queue (unchanged feel); holding Shift appends the order as a waypoint instead, so a sequence of Shift+right-clicks lays down a path the units run through in order. The queued order is context-sensitive, exactly like a normal right-click — move/attack-move on open ground (combat units keep their aggressive-advance flavor, workers a plain move), attack on an enemy, gather on a node, assist on a construction site — so a chain can mix destinations, targets, and jobs. A Shift command to a fully idle unit still takes effect immediately, so the first waypoint of a chain is never a dead step. The sim pulls the next queued order in the moment the active one finishes (arrival, target killed, node drained), and a combat unit only falls back to auto-acquiring once its whole chain is exhausted. render.js draws the remaining path — a dashed line through each stop with a marker at each — for any selected …845de25 -
Merge Tier 4 into main alongside Tier 3
Claude +1: Claude Fable 5Unverified signaturemain had advanced with Tier 3 (repeated AI attack waves, counter-building, minimap, under-attack alert, combat tracers/death flashes, build-ghost preview, production-queue display + cancel, panel aggregation) while Tier 4 (buildable expansion Command Center, Sentinel Turret + Breacher siege unit, supply cap + Habitat, 9-world roster with per-planet modifiers, AI parity) was built in parallel. This reconciles the two feature sets. Notable resolutions: - Placement validation: the two tiers independently shipped engine/placement.js (isValidPlacement) and engine/colliders.js (canPlaceBuilding + findPlacement). Consolidated on colliders.js — it's a superset the AI's expansion needs — and repointed the build-ghost preview and issueBuild at it; removed placement.js and its now-redundant test. - Combat: kept Tier 4's shared performAttack fire path and folded Tier 3's tracer data (fromX/fromY/unitType) back into its attackHit event, so both the siege heavy-hit sound and the attack …c769ac5 -
Tier 4 foundation: placement validation, buildable expansion CC, guaranteed ore
Claude +1: Claude Fable 5Unverified signatureAdd engine/colliders.js as the single source of truth for building placement: canPlaceBuilding rejects overlap with buildings, live resource nodes, and map bounds (units are deliberately ignored — they move, and separation already untangles them), and findPlacement does a deterministic outward ring search so the AI can slide a fouled build spot to nearby valid ground. issueBuild now validates placement before charging, so a rejected spot never leaks resources. The AI routes its barracks/refinery spots through findPlacement in the same change, so validation can't stall its build order. Make the Command Center buildable (400 ore, 30s) instead of free and instant; the starting CC is still seeded complete. Multi-CC dropoff and all-CCs victory already worked, so an expansion is a real second base with no further plumbing. generateMap guarantees a lean mirrored ore cluster near each base on any world whose deposit table yields no ore (distance-triggered, so ore-bearing maps stay byte-iden…1c88bda -
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 -
Multiple workers can assist a build; fix skiffs getting stuck fighting
Claude +1: Claude Sonnet 5Unverified signatureTwo independent fixes, both about giving the player more control: 1. Multiple workers can now speed up the same construction. Right- click a worker onto an already-founded (constructing) friendly building to send it in as an extra builder — no new cost, it's not founding a second building. updateBuildingConstruction now scales its rate by how many same-owner workers are actually standing on site (capped at 4, so massing a crowd on one site has a limit), while a lone founding worker still builds at the original, unchanged pace. issueAssistBuild (commands.js) is the new player-facing command; BUILD_REACH is now shared between production.js and sim.js so "counts as on-site" and "close enough to stop walking" agree. 2. Skiffs (and any future combat unit) could not be redirected once an enemy wandered into aggro range: updateCombat called acquireTarget unconditionally whenever there wasn't an explicit 'attack' order, so a plain 'move' order got silently…e735d2f -
Fix skiff visibility and group-move stacking
Claude +1: Claude Sonnet 5Unverified signatureThe triangle shape from the last commit was there but nearly invisible: its outline color (#05070f) was literally identical to the canvas background, so anti-aliasing blurred its corners into what read as just another circle. Outline is now a light color that actually contrasts, and the triangle draws bigger (radius x2). Separately, any multi-unit move or attack-move sent every unit to the exact same point, so a group converged into one visually stacked pile mid-transit — including the AI's own attack wave, which set each skiff's order directly rather than going through the shared command helpers. issueMove/issueAttackMove now spread a group across a small grid centered on the clicked point, and ai.js routes its attack order through issueAttackMove instead of hand- rolling it. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01BDmJ8vc9jxc6TUegK9F2YJ3ea9b20 -
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