feat(client): follow-facing — body commits to path, mouse drives layered head/torso look-at (T-1088)
During an RMB path-follow the body yaw locks to the leg direction (rig commit_body_to_motion, from interpolated velocity — immune to SetFacing interleaving between throttled steps). The mouse instead drives a layered look-at: LookAtModifier3D pair on Head (±70°) + spine_02 (±30° torso twist for looking far lateral/behind — past their sum the character physically cannot look further without turning). Forward axis measured from the armature rest pose (+Z), not guessed. Influence fades in/out on follow start/end. Pure client presentation per D-249; SetFacing still rides the wire, so the server vision cone follows the mouse while walking — the character looks where the player points. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -62,6 +62,10 @@ var step_window_ms_provider: Callable = Callable()
|
||||
## unset (NPCs) to collapse the facing split to pure wire facing — single code
|
||||
## path by construction.
|
||||
var idle_facing_provider: Callable = Callable()
|
||||
## T-1088 follow-facing: while a client-initiated path-follow is active the
|
||||
## body yaw commits to the leg direction (mouse -> head_look, not body).
|
||||
## Set/cleared by the sandbox root from PathFollower.is_active().
|
||||
var commit_body_to_motion: bool = false
|
||||
## () -> bool, true while input is suppressed. The player adapter returns
|
||||
## GameState.dialogue_active or GameState.free_camera_mode — the exact condition
|
||||
## under which InputMapper computes but does not send octants (input_mapper.gd:71).
|
||||
@@ -197,10 +201,18 @@ func _update_facing(delta: float) -> void:
|
||||
var suppressed := false
|
||||
if suppression_provider.is_valid():
|
||||
suppressed = bool(suppression_provider.call())
|
||||
var idle_octant := ""
|
||||
if idle_facing_provider.is_valid():
|
||||
idle_octant = str(idle_facing_provider.call())
|
||||
yaw_target = select_yaw_target(is_moving, wire_octant, idle_octant, suppressed, yaw_target)
|
||||
if commit_body_to_motion and is_moving and velocity.length_squared() > 0.0001:
|
||||
# Path-follow (T-1088 follow-facing refinement): the body is committed to
|
||||
# the leg direction — the mouse drives the head/torso look-at instead
|
||||
# (head_look.gd), and SetFacing still steers the server vision cone.
|
||||
# Leg direction in WorldRoot-local space -> yaw via the same convention
|
||||
# as SandboxSpace (local +X = sim East, +Z = sim South).
|
||||
yaw_target = SandboxSpace.sim_angle_to_yaw(atan2(velocity.z, velocity.x))
|
||||
else:
|
||||
var idle_octant := ""
|
||||
if idle_facing_provider.is_valid():
|
||||
idle_octant = str(idle_facing_provider.call())
|
||||
yaw_target = select_yaw_target(is_moving, wire_octant, idle_octant, suppressed, yaw_target)
|
||||
if model_root == null:
|
||||
return
|
||||
var budget_key := stance if is_moving else "Idle"
|
||||
|
||||
Reference in New Issue
Block a user