# ADR-0011: Aim-free target selection, precision walk, and focus clearing - **Status:** Accepted - **Date:** 2026-10-31 ## Context ADR-0007 and ADR-0008 split input into locomotion, look, and a focus cone. A pass over the resulting controls found four problems in play: 1. **The cone ignored what the player sees.** `NpcFocus.pick_index` returned the first head inside the cone in group order, not the one nearest the crosshair. Two people in the cone gave an arbitrary pick. 2. **`Tab` cycled in spawn order.** `GameManager._cycle_nearby_target` walked the `npcs` array, so "next" meant "next spawned", not "next nearest". 3. **A cycled target was dropped within a few frames.** The cone re-evaluated every physics frame and cleared a target that was not under the crosshair after the 8-frame miss grace. Aim-free selection therefore only worked while the player also aimed. 4. **There was no way to dismiss someone.** `Esc` pauses the shift and right click only cancelled modals. The movement pass also left one gap at the door: the only walking speed is 3.2 m/s, which overshoots when the player is trying to stand at a register or read an artifact at conversation distance. ## Decision **Rank the cone by angle, then distance.** `NpcFocus.rank_indices` scores every head by its angle from the camera forward vector and breaks ties by distance; `pick_index` is the first ranked entry. The cone therefore picks the person closest to the crosshair, which is what the player is looking at. **Make aim-free selection authoritative.** `GuardController.cycle_target(direction)` walks `nearby_candidates()` — everyone within 12 m, nearest first — starting from the current target and wrapping in both directions. The chosen avatar is *pinned*: while a pinned target is alive the cone does not re-evaluate, so `Tab` and the mouse wheel produce a stable selection the player can then act on. **Bind the mouse wheel to the same ring.** `cycle_target_next` (wheel down) and `cycle_target_prev` (wheel up) are new input actions handled in `GameManager._handle_mouse_button`. `Tab` remains the keyboard path. The HUD prints how many people are in the ring (`hud.nearby`), so the player knows the control exists before they need it. **Right click clears focus, with a short grace.** With no modal open, right click clears the target and suppresses cone acquisition for `FOCUS_SUPPRESS_S = 0.75 s`. Without the grace the cone re-acquired the same person on the next frame, which made the control feel broken. Any explicit pick (cone or cycle) cancels the suppression. **Add a precision walk.** Holding `Shift` (`walk_slow`) multiplies the walk speed by `SLOW_WALK_FACTOR = 0.45`. This is a fine-positioning aid, not a sprint: PLAN.md deliberately excludes sprint and crouch, and this does not add a faster move. Escape keeps its current meaning (close the top modal, otherwise pause). Selection is not overloaded onto the same key. ## Consequences ### Positive - The focused person is predictable: the one nearest the crosshair, or the one the player explicitly cycled to. - Aim-free play works: cycle, act, cycle again, without fighting the cone. - Right click has an unambiguous "never mind" meaning while remaining a modal cancel. - Standing on a mark while reading an artifact is possible. ### Negative - Two more input actions and one more piece of guard state (`target_pinned`, `focus_suppress_s`). - A cycled target stays selected until the player clears it, acts, or the avatar is freed; it does not expire on its own. - The 0.75 s suppression is a feel constant that may need tuning against play testing. ## Follow-up - The action wheel (ADR-0009) and inspection panels still take `A`/`D`; the wheel owns those keys while it is open, and the guard is frozen then, so no conflict is observable.