ui/phase-20: pick-first Send + lock after Modernize/Dismantle/Transfer

Send no longer carries a destination control inside the form: a
click on the action drops the inspector straight into map-pick
mode, and the form (ship count + confirm) only mounts after the
player chooses a destination. Cancelling the picker leaves no
form behind.

A queued Modernize / Dismantle / Transfer for a given group
locks every action button on its inspector and surfaces a banner
that points the player at the order list. Cancelling the queued
entry from the order tab releases the lock on the next render —
the derivation watches draft.commands directly. Send / Load /
Unload / Split / Join Fleet do not lock; Send is naturally
followed by an out-of-orbit state at turn cutoff, the rest can
stack legitimately.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
Ilia Denisov
2026-05-10 17:20:48 +02:00
parent de824dfc9a
commit ac14eaff10
7 changed files with 332 additions and 77 deletions
+31 -6
View File
@@ -2160,12 +2160,14 @@ Artifacts:
payload carries the *target* group UUID (the source group, or
the freshly-minted `newGroupId` when an implicit split precedes
the action)
- `Send` action picks destination through a planet picker filtered
by the group's reach (`localPlayerDrive * 40`, computed inline
via the existing `torusShortestDelta` from
`cargo-routes.svelte`); the player's tech levels are already on
`GameReport.localPlayer*` from Phase 18, no extra plumbing
needed
- `Send` action drops the inspector straight into map-pick mode
on click and only mounts the form (ship count + confirm) after
the player chooses a destination — there is no destination
control inside the form. The picker is filtered by the group's
reach (`localPlayerDrive * 40`, computed inline via the existing
`torusShortestDelta` from `cargo-routes.svelte`); the player's
tech levels are already on `GameReport.localPlayer*` from
Phase 18, no extra plumbing needed
- `Modernize` cost preview through `core.blockUpgradeCost`
(Phase 20 bridge), summed over the four ship-class blocks for
the targeted ship count; preview hides when `Core` is not yet
@@ -2175,6 +2177,14 @@ Artifacts:
planet with colonists onboard (engine reference
`controller/ship_group.go:177-179` — `UnloadColonists` is not
called over a foreign planet, so the cargo is lost)
- destructive-command lock: a `Modernize` / `Dismantle` /
`Transfer` order in the draft for a given group disables every
action button on that group's inspector and surfaces a banner
pointing to the order list. Cancelling the queued command in
the order tab releases the lock. Other commands (Send / Load /
Unload / Split / JoinFleet) do not lock — Send is naturally
followed by an out-of-orbit state at turn cutoff and the
remaining four can stack legitimately
- `pkg/calc/ship.go.BlockUpgradeCost` (migrated from
`game/internal/controller/ship_group_upgrade.go`) — the bridge
rule says `ui/core/calc/` only wraps `pkg/calc/` formulas, so
@@ -2262,6 +2272,21 @@ Decisions during stage:
at `newId`. JoinFleet and Split do not get a counter (JoinFleet
is whole-group atomically per the engine; Split *is* the break
command).
6. **Send is pick-first, form-second**. Click → enter map-pick
mode immediately. The form (ship count + confirm) only appears
after a destination is chosen; cancelling the picker leaves no
form behind. Removing the destination control from the form
keeps the surface to one editable field at any time.
7. **Destructive-command lock**. Any `upgradeShipGroup`,
`dismantleShipGroup`, or `transferShipGroup` in the draft for a
given group id disables every action button on that group's
inspector with a "command pending" tooltip and renders a
banner pointing the player at the order list. Cancellation
from the order tab releases the lock. The three commands all
change the group's engine-side state at turn cutoff
(`StateUpgrade` / removal / `StateTransfer`), so any second
action would race the engine's pre-condition check anyway —
the lock surfaces that commitment up-front.
## Phase 21. Sciences — CRUD List + Designer