fix(motion): preserve signed TurnSpeed for remote turn animations
The wire-arrival animCycle picker in OnLiveMotionUpdated was passing MathF.Abs(turnSpeed) to the sequencer, stripping the sign that ACE uses to encode TurnLeft. Confirmed via live wire trace 2026-05-03: TurnLeft input from a retail-driven character arrives as turnCmd16=0x000D (TurnRight), TurnSpeed=-1.500 — mirroring retail's adjust_motion convention on the wire. With Abs, both directions collapsed onto motion=TurnRight + speedMod=+1.5, and the synthesize- omega path computed -2.25 (CW = right) for both. Visible symptom: TurnLeft animated as TurnRight then blipped to the correct facing on the next UpdatePosition. Pass the signed speed through unchanged. The sequencer's negative- speed path (EnqueueMotionData multiplies MotionData.Omega by speedMod; the synthesize-omega fallback uses -(pi/2)*adjustedSpeed) produces the correct CCW omega for TurnLeft now that the sign survives. Also adds a TURN_WIRE diagnostic gated on ACDREAM_REMOTE_VEL_DIAG=1 that prints every wire-arrived TurnCommand with reconstructed enum and signed speed, plus splits the OMEGA_DIAG throttle off LastVelDiagLogTime onto its own LastOmegaDiagLogTime so the two diagnostics don't starve each other. Verified with the same trace: TURN_WIRE speed=-1.500 -> OMEGA_DIAG Z=+2.250 (CCW = TurnLeft), TURN_WIRE speed=+1.500 -> OMEGA_DIAG Z=-2.250 (CW = TurnRight). Both directions now have correct sign. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
parent
0997f96078
commit
9960ce3bce
1 changed files with 37 additions and 1 deletions
|
|
@ -376,6 +376,7 @@ public sealed class GameWindow : IDisposable
|
|||
public System.Numerics.Vector3 PrevServerPos;
|
||||
public double PrevServerPosTime;
|
||||
public double LastVelDiagLogTime;
|
||||
public double LastOmegaDiagLogTime;
|
||||
|
||||
public RemoteMotion()
|
||||
{
|
||||
|
|
@ -2862,7 +2863,17 @@ public sealed class GameWindow : IDisposable
|
|||
.ReconstructFullCommand(turnForAnim);
|
||||
if (turnFullForAnim == 0) turnFullForAnim = 0x65000000u | turnForAnim;
|
||||
animCycle = turnFullForAnim;
|
||||
animSpeed = MathF.Abs(update.MotionState.TurnSpeed ?? 1f);
|
||||
// SIGNED — do NOT MathF.Abs. ACE encodes TurnLeft on the
|
||||
// wire as (TurnCommand=TurnRight, TurnSpeed=NEGATIVE),
|
||||
// mirroring retail's adjust_motion convention. The
|
||||
// sequencer's negative-speed path (EnqueueMotionData
|
||||
// multiplies MotionData.Omega by speedMod, the
|
||||
// synthesize-omega fallback flips zomega via
|
||||
// -(pi/2)*adjustedSpeed) only produces the correct
|
||||
// CCW rotation when the sign is preserved here.
|
||||
// Confirmed by live wire trace 2026-05-03: TurnLeft
|
||||
// input arrives as turnCmd16=0x000D, speed=-1.500.
|
||||
animSpeed = update.MotionState.TurnSpeed ?? 1f;
|
||||
}
|
||||
}
|
||||
// K-fix17 (2026-04-26): preserve the Falling cycle while
|
||||
|
|
@ -2993,6 +3004,14 @@ public sealed class GameWindow : IDisposable
|
|||
.ReconstructFullCommand(turnCmd16);
|
||||
if (turnFull == 0) turnFull = 0x65000000u | turnCmd16;
|
||||
float turnSpd = update.MotionState.TurnSpeed ?? 1f;
|
||||
if (System.Environment.GetEnvironmentVariable("ACDREAM_REMOTE_VEL_DIAG") == "1")
|
||||
{
|
||||
System.Console.WriteLine(
|
||||
$"[TURN_WIRE] guid={update.Guid:X8} turnCmd16=0x{turnCmd16:X4} "
|
||||
+ $"turnFull=0x{turnFull:X8} low=0x{turnFull & 0xFFu:X2} "
|
||||
+ $"({(((turnFull & 0xFFu) == 0x0D) ? "TurnRight" : ((turnFull & 0xFFu) == 0x0E) ? "TurnLeft" : "OTHER")}) "
|
||||
+ $"speed={turnSpd:F3}");
|
||||
}
|
||||
remoteMot.Motion.DoInterpretedMotion(
|
||||
turnFull, turnSpd, modifyInterpretedState: true);
|
||||
// Seed ObservedOmega with formula so rotation starts
|
||||
|
|
@ -5874,6 +5893,23 @@ public sealed class GameWindow : IDisposable
|
|||
var rot = System.Numerics.Quaternion.CreateFromAxisAngle(axis, angleDelta);
|
||||
rm.Body.Orientation = System.Numerics.Quaternion.Normalize(
|
||||
System.Numerics.Quaternion.Concatenate(rm.Body.Orientation, rot));
|
||||
|
||||
// Diagnostic (ACDREAM_REMOTE_VEL_DIAG=1): print seqOmega direction
|
||||
// once per remote per ~1 second so we can confirm whether the omega
|
||||
// sign actually being applied matches the retail-observed turn
|
||||
// direction. Z>0 = CCW (TurnLeft); Z<0 = CW (TurnRight).
|
||||
if (System.Environment.GetEnvironmentVariable("ACDREAM_REMOTE_VEL_DIAG") == "1")
|
||||
{
|
||||
double nowSec = (System.DateTime.UtcNow - System.DateTime.UnixEpoch).TotalSeconds;
|
||||
if (nowSec - rm.LastOmegaDiagLogTime > 0.5)
|
||||
{
|
||||
uint seqMotion = ae.Sequencer?.CurrentMotion ?? 0;
|
||||
System.Console.WriteLine(
|
||||
$"[OMEGA_DIAG] guid={serverGuid:X8} motion=0x{seqMotion:X8} "
|
||||
+ $"seqOmega.Z={seqOmega.Z:F3} (Z>0=CCW=TurnLeft, Z<0=CW=TurnRight)");
|
||||
rm.LastOmegaDiagLogTime = nowSec;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Step 3: calc_acceleration sets body.Acceleration from the Gravity flag
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue