Spaces:
Paused
Paused
Commit History
-
Tier 3f: split the selection panel's derived state out of its DOMβ
Claude +1: Claude Opus 5Unverified signaturehudSelection.js was 2242 lines. The natural seam is not "which panel" but "derive vs render": panelSignature is the panel's entire reactivity model β it decides whether renderSelectionPanel rebuilds AT ALL, so a panel whose own state contributes no term to it silently never redraws, which is exactly what left the Freight Lane and Colony Policy controls inert. A model that load-bearing should be readable without scrolling past 800 lines of button construction. hudPanelSignature.js takes panelSignature and the derivations it shares with the renderers β factoryStatus, loadableComs, countByType, summaryRowLabel, counterInfo and the hoisted cost/counter tables β because both sides call them and the two must never drift. It imports no DOM and no hud.js, so unlike the panel itself it is a genuine leaf: drivable from a test, outside the hud/hudSelection import cycle. hudSelection.js drops to 1915 lines with the panel's output byte-identical, which the golden lock added in the previous commit β¦97f3bdf -
Tier 2a: collapse the cross-module duplicates, pin the tie-breaksβ
Claude +1: Claude Opus 5Unverified signatureradiusOf existed THREE times in engine/ and bomb.js's comment asserted the copies were "the same". They were not: only bomb.js's handled a building, by reading the instance radius state.js stamps at construction; movement.js and formation.js returned the magic constant 9 for any building β while movement.js is a // @ts-check'ed file whose JSDoc advertises @param {Unit|Building}. The type layer was actively telling callers a Building was supported on a path that silently under-sized it, which is the exact class of bug MAX_UNIT_RADIUS exists to prevent and which no determinism test can see, being wrong-but-stable on every seed. One shared helper now lives in colliders.js, which already reasoned about def.radius and b.radius. Consolidating it surfaced a real subtlety the plan didn't predict: keying on `kind` alone broke formation.js, whose callers are frequently unit-shaped literals carrying no `kind` field at all. The helper resolves by TYPE first, with a comment recording why. clamp aβ¦e847942
-
Widen idle and formation unit spacing by 20%β
ClaudeUnverified signatureTwo independent levers, kept deliberately separate so they don't compound into each other: - engine/formation.js: COARSENESS (the existing formation-spacing multiplier, introduced by a prior "units sat too close together" pass at 1.15x) goes to 1.38 β a further +20% on top of that. It already cascades correctly through every formation shape (grid, line, wedge, circle) and nested formation-of-formations spacing with no other change needed. - engine/separation.js: idle/general collision separation had no padding at all before this β minDist was the bare sum of two units' radii, so same-owner units at rest settled shoulder to shoulder. Added a standalone SEPARATION_PAD_MULT (1.2), applied only to minDist here, NOT to entities.js's own radius fields β touching radius directly would have also fed formation.js's own spacing calculation (which reads that same field), compounding the two changes to 44% instead of two clean, independent 20% increases. Combat packing isβ¦873d661
-
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
-
Let a player promote any selected unit to formation leader with Ctrl+clickβ
ClaudeUnverified signatureThe formation leader is always selection[0], and the only way to choose it deliberately was to click the intended leader alone first, then Ctrl+click/drag to add the rest β a single one-shot box-select over a whole group left the leader to fall out of Map iteration (effectively spawn) order instead. Ctrl+clicking a unit that's already in the current selection now promotes it to the front instead of being a same-position no-op, so any squad member can be handed the lead after the fact without rebuilding the selection around it.16bdd2a -
Make formation spacing 15% more coarseβ
ClaudeUnverified signatureUnits were sitting too close together in formations. Introduce a single COARSENESS multiplier (1.15x) applied to both GRID_SPACING (the default grid formation) and spacingFor (line/wedge/circle and nested formations), so every formation shape gets uniformly more breathing room.bd7ba17
-
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