feat(client): mouse-relative facing and movement (#526, D-054)

Mouse position now determines facing direction as a client-side float.
WASD remapped: W=toward cursor, S=away, A/D=strafe. Facing octant
derived from mouse angle and sent to server via SET_FACING action only
when it changes. EntityRenderer facing indicator uses continuous angle
for smooth rotation. SimBridge test mode updated to handle SetFacing.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-02-18 12:53:19 +01:00
co-authored by Claude Opus 4.6
parent 80d5a57b5d
commit 97cb69e6a4
3 changed files with 124 additions and 15 deletions
+9 -1
View File
@@ -258,6 +258,8 @@ static func _action_enum_to_wire(action: int) -> String:
return "" # Client-only action, not part of wire protocol
InputMapper.Action.BUG_REPORT:
return "" # Client-only action (#495), not part of wire protocol
InputMapper.Action.SET_FACING:
return "SetFacing" # D-054: facing octant update (no movement)
_:
push_warning("SimBridge: unknown action enum %s" % action)
return ""
@@ -280,12 +282,18 @@ func _test_snapshot() -> Dictionary:
if dist <= 2 and _test_has_los(_test_player_pos, npc_pos):
_test_in_dialogue = true
continue
if action_name == "SetFacing":
# D-054: facing update handled separately — octant comes from InputMapper
_test_facing = InputMapper.facing_octant
continue
var delta := _action_to_delta(action_name)
var new_pos := _test_player_pos + delta
if _test_is_walkable(new_pos):
_test_player_pos = new_pos
if delta != Vector2i.ZERO:
_test_facing = _delta_to_facing(delta)
# D-054: Facing is now mouse-driven, not movement-driven.
# Use InputMapper's octant instead of deriving from movement delta.
_test_facing = InputMapper.facing_octant
# Walk-away dismisses dialogue (D-064)
if _test_in_dialogue:
_test_in_dialogue = false