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:
@@ -48,7 +48,14 @@ enum Mode { NORMAL, SPRINT, CROUCH }
|
||||
## last element. Empty while inactive.
|
||||
var _path: Array[Vector3i] = []
|
||||
var _goal: Vector3i = Vector3i.ZERO
|
||||
## T-1088 follow-facing: two server ticks (50 ms each) after an emitted step —
|
||||
## past the tick that applies the move (whose facing_from_delta would win) but
|
||||
## as soon after as the wire allows.
|
||||
const FACING_REASSERT_DELAY_MS := 100
|
||||
|
||||
var _active: bool = false
|
||||
## 0 = nothing pending; else the msec deadline for the post-step facing re-assert.
|
||||
var _reassert_at_ms: int = 0
|
||||
|
||||
var _mode: int = Mode.NORMAL
|
||||
## SPRINT only: the pre-sprint stance (kept for readability/debug) and the exact
|
||||
@@ -120,6 +127,13 @@ func cancel() -> void:
|
||||
func tick(current_tile: Vector3i, is_floor: Callable) -> void:
|
||||
if not _active:
|
||||
return
|
||||
# T-1088 follow-facing: the server overwrote Facing from this follow's last
|
||||
# accepted step — re-assert the mouse octant once the overwrite tick has
|
||||
# passed (~2 server ticks; a same-tick re-assert loses, movement wins within
|
||||
# a tick). Keeps the vision cone on the mouse while the body walks the path.
|
||||
if _reassert_at_ms > 0 and Time.get_ticks_msec() >= _reassert_at_ms:
|
||||
_reassert_at_ms = 0
|
||||
InputMapper.reassert_facing()
|
||||
# Cancel (no stance change): input suppression — dialogue / free camera.
|
||||
if GameState.dialogue_active or GameState.free_camera_mode:
|
||||
cancel()
|
||||
@@ -147,7 +161,8 @@ func tick(current_tile: Vector3i, is_floor: Callable) -> void:
|
||||
# the 200 ms cadence automatically — the character accelerates into the sprint).
|
||||
var next_tile: Vector3i = _path[idx + 1]
|
||||
var delta := Vector2i(next_tile.x - current_tile.x, next_tile.y - current_tile.y)
|
||||
InputMapper.queue_move_step(delta)
|
||||
if InputMapper.queue_move_step(delta):
|
||||
_reassert_at_ms = Time.get_ticks_msec() + FACING_REASSERT_DELAY_MS
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
Reference in New Issue
Block a user