Choose a destination by tapping, not by hovering #36

Merged
icub3d merged 1 commit from 23-touch-movement-preview into main 2026-09-13 18:05:05 +00:00
Owner

Implements ADR 0003.

The preview was built on a state a touchscreen does not have. MovementPreview held "the
route to whatever the cursor is over", so on a tablet the tile was never hovered, the
route was never drawn, and the half of the overlay that answers where am I going simply
did not exist.

Two-stage commit

Tap a unit, then tap where it should go. The destination stays chosen with nothing held
down — which is the point: a preview that lives only while a finger is down is one the
player cannot read, because their hand is on top of it.

Choice is one resource with three states (Idle, Unit, Destination) rather than a
selection and a destination side by side. A destination without a unit is not a state this
game has, and two resources would let it be written down.

decide is the whole state machine as a pure function, so every press is testable without
an App — the same reason path and plan have no ECS in them. Seven tests cover it
directly.

Every press either advances the choice or puts it down, and none of them strand the
player:

Tap Result
the unit in hand put it down
another unit switch to it
a tile it can reach choose that destination
ground out of range cancel — it must not silently look like a destination
off the map cancel — empty sky is the biggest "not that after all" target there is

Input

A Pointer system param reads touch first and mouse second, so a device with both behaves
as a tablet rather than as a desktop that happens to have a screen. The tile comes from the
press position. The hover cue still follows a mouse, but it only says where the cursor
is
— no part of a move is legible through it alone, which is the rule ADR 0003 sets.

Also

  • A held destination that stops being reachable — somebody moved onto it — demotes to the
    unit rather than cancelling. The player has not changed their mind about the unit, only
    about where it can get to.
  • The overlay grows a fourth shape, not a fourth colour: a larger diamond around the
    chosen destination. The reticle, not the arrowhead, says the choice is being held — it
    survives the finger lifting, where an arrow alone would read as a hover.

Verification

cargo fmt --check, cargo clippy --all-targets -- -D warnings (and with --features perf), and cargo test — 122 passing, up from 114.

Driven on a Pixel Tablet by touch: tapping a unit offers its range, tapping a tile draws
the route and holds it across seconds with no input, and tapping the unit again clears it.
Screenshots at each stage; the held frame was captured 6 s after the tap.

Not here

  • A labelled cancel control. ADR 0003 asks for a visible affordance; that is a HUD
    element and belongs with #10. What exists now is tap-to-cancel, which is always in reach
    but unlabelled.
  • Escape-to-cancel. It would have to layer under leave_mission_on_escape, which is a
    placeholder #12 replaces — and ui does not depend on world, so wiring it now would
    invent a coupling for something about to be rewritten.

Closes #23

🤖 Generated with Claude Code

https://claude.ai/code/session_01TA4hJHkRSU3XBxZYtMKXdh

Implements [ADR 0003](../src/branch/main/docs/adr/0003-touch-first-input.md). The preview was built on a state a touchscreen does not have. `MovementPreview` held "the route to whatever the cursor is over", so on a tablet the tile was never hovered, the route was never drawn, and the half of the overlay that answers *where am I going* simply did not exist. ## Two-stage commit Tap a unit, then tap where it should go. The destination stays chosen with nothing held down — which is the point: a preview that lives only while a finger is down is one the player cannot read, because their hand is on top of it. `Choice` is one resource with three states (`Idle`, `Unit`, `Destination`) rather than a selection and a destination side by side. A destination without a unit is not a state this game has, and two resources would let it be written down. `decide` is the whole state machine as a pure function, so every press is testable without an `App` — the same reason `path` and `plan` have no ECS in them. Seven tests cover it directly. Every press either advances the choice or puts it down, and none of them strand the player: | Tap | Result | | --- | --- | | the unit in hand | put it down | | another unit | switch to it | | a tile it can reach | choose that destination | | ground out of range | cancel — it must not silently look like a destination | | off the map | cancel — empty sky is the biggest "not that after all" target there is | ## Input A `Pointer` system param reads touch first and mouse second, so a device with both behaves as a tablet rather than as a desktop that happens to have a screen. The tile comes from the press position. The hover cue still follows a mouse, but it only says *where the cursor is* — no part of a move is legible through it alone, which is the rule ADR 0003 sets. ## Also - A held destination that stops being reachable — somebody moved onto it — demotes to the unit rather than cancelling. The player has not changed their mind about the unit, only about where it can get to. - The overlay grows a fourth **shape**, not a fourth colour: a larger diamond around the chosen destination. The reticle, not the arrowhead, says the choice is being *held* — it survives the finger lifting, where an arrow alone would read as a hover. ## Verification `cargo fmt --check`, `cargo clippy --all-targets -- -D warnings` (and with `--features perf`), and `cargo test` — **122 passing**, up from 114. Driven on a Pixel Tablet by touch: tapping a unit offers its range, tapping a tile draws the route and holds it across seconds with no input, and tapping the unit again clears it. Screenshots at each stage; the held frame was captured 6 s after the tap. ## Not here - **A labelled cancel control.** ADR 0003 asks for a visible affordance; that is a HUD element and belongs with #10. What exists now is tap-to-cancel, which is always in reach but unlabelled. - **Escape-to-cancel.** It would have to layer under `leave_mission_on_escape`, which is a placeholder #12 replaces — and `ui` does not depend on `world`, so wiring it now would invent a coupling for something about to be rewritten. Closes #23 🤖 Generated with [Claude Code](https://claude.com/claude-code) https://claude.ai/code/session_01TA4hJHkRSU3XBxZYtMKXdh
Implements ADR 0003. The preview was built on a state a touchscreen does not
have: `MovementPreview` held "the route to whatever the cursor is over", so on a
tablet the tile was never hovered, the route was never drawn, and the half of
the overlay that answers *where am I going* simply did not exist.

Choosing is now a two-stage commit. Tap a unit, then tap where it should go, and
the destination stays chosen with nothing held down — which is the point, since
a preview that lives only while a finger is down is one the player cannot read
with their hand on top of it.

`Choice` is one resource with three states rather than a selection and a
destination side by side. A destination without a unit is not a state this game
has, and two resources would let it be written down.

`decide` is the whole state machine as a pure function, so every press is
testable without an `App` — the same reason `path` and `plan` have no ECS in
them. Every press either advances the choice or puts it down, and none of them
strand the player: tapping the unit in hand puts it down, tapping another unit
switches, tapping ground it cannot reach cancels rather than quietly doing
nothing, and a press that misses the map entirely is a cancel too, because empty
sky is the largest "not that after all" target on the screen.

Presses come from a `Pointer` that reads touch first and mouse second, so a
device with both behaves as a tablet rather than as a desktop that happens to
have a screen. The tile comes from the press position. The hover cue still
follows a mouse, but it only says where the cursor is; no part of a move is
legible through it alone.

A held destination that stops being reachable — somebody moved onto it —
demotes to the unit rather than cancelling. The player has not changed their
mind about the unit, only about where it can get to.

The overlay grows a fourth shape, not a fourth colour: a larger diamond around
the chosen destination. The reticle, not the arrowhead, is what says the choice
is being held — it survives the finger lifting, and an arrow alone would read as
a hover.

Verified on a Pixel Tablet: tapping a unit offers its range, tapping a tile
draws the route and holds it across seconds of no input, and tapping the unit
again clears it.

Not here: a labelled cancel control, which is a HUD element and belongs with
#10; and Escape-to-cancel, which would have to layer under
`leave_mission_on_escape` — a placeholder #12 replaces.

Closes #23

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TA4hJHkRSU3XBxZYtMKXdh
icub3d merged commit 02a7406685 into main 2026-09-13 18:05:05 +00:00
icub3d deleted branch 23-touch-movement-preview 2026-09-13 18:05:05 +00:00
Sign in to join this conversation.
No description provided.