fix(client): re-assert mouse facing after each follow step — vision-cone flap mitigation (T-1088)

Live finding: during a path-follow the cone snapped to path-forward on every
accepted step (server facing_from_delta overwrites Facing; the change-gated
SetFacing never re-sends an unchanged octant) and stayed there until the
mouse crossed an octant boundary. The follower now re-asserts the current
mouse octant 100ms (~2 ticks) after each emitted step — same-tick re-asserts
lose, movement wins within a tick — shrinking the flap to a ~100ms blip.
Full stability needs server-side aim-lock: filed T-1093 (Q-084's walk-vs-aim
split, now with play evidence); wire semantics untouched per the sidequest's
presentation-layer constraint.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
2026-07-06 20:44:52 +02:00
co-authored by Claude Fable 5
parent cb6b39331d
commit 6453f71220
4 changed files with 38 additions and 1 deletions
+20
View File
@@ -208,6 +208,26 @@ func queue_move_step(tile_delta: Vector2i) -> bool:
# so no throttle — the server's handle_toggle_stance has no cooldown, so the sandbox
# follower can burst the exact ladder distance and trust it (server validates). No-op
# while input is suppressed (dialogue / free camera), matching movement.
## T-1088 follow-facing: unconditionally re-send the CURRENT facing octant.
## The server overwrites Facing from the move delta on every accepted step
## (movement.rs facing_from_delta), and the change-gated send above never
## re-asserts an unchanged octant — during a path-follow the vision cone would
## stick path-forward until the mouse crossed an octant boundary. The follower
## calls this ~2 server ticks after each emitted step (same-tick re-asserts
## lose: movement wins within a tick). Suppression-guarded like all sends.
func reassert_facing() -> void:
if GameState.dialogue_active or GameState.free_camera_mode:
return
_last_sent_octant = facing_octant
input_queue.append(
{
"action": Action.SET_FACING,
"timestamp_msec": Time.get_ticks_msec(),
"action_data": {"facing": facing_octant},
}
)
func queue_stance_toggle(up: bool) -> void:
if GameState.dialogue_active or GameState.free_camera_mode:
return