diff --git a/src/AcDream.App/Rendering/GameWindow.cs b/src/AcDream.App/Rendering/GameWindow.cs index cf30c64..bbb5140 100644 --- a/src/AcDream.App/Rendering/GameWindow.cs +++ b/src/AcDream.App/Rendering/GameWindow.cs @@ -542,6 +542,7 @@ public sealed class GameWindow : IDisposable if (isStatue) { Console.WriteLine($"live: [STATUE] objScale={spawn.ObjScale?.ToString("F3") ?? "null"}"); + Console.WriteLine($"live: [STATUE] mtable=0x{(spawn.MotionTableId ?? 0):X8} stance=0x{(spawn.MotionState?.Stance ?? 0):X4} cmd=0x{(spawn.MotionState?.ForwardCommand ?? 0):X4}"); if (spawn.TextureChanges is { } tcs) { foreach (var tc in tcs) @@ -577,7 +578,6 @@ public sealed class GameWindow : IDisposable int subCount = pgfx?.Surfaces.Count ?? -1; Console.WriteLine($"live: [STATUE] part[{pi}] gfxObj=0x{partGfxId:X8} surfaces={subCount}"); } - // The placement frame the existing flatten logic uses. Console.WriteLine($"live: [STATUE] placementFrames count={baseSetup.PlacementFrames.Count}"); } } @@ -633,8 +633,14 @@ public sealed class GameWindow : IDisposable // resolving the cycle key from those gives the aggressive crouch. ushort? stanceOverride = spawn.MotionState?.Stance; ushort? commandOverride = spawn.MotionState?.ForwardCommand; + // Critical for entities like the Foundry's drudge statue: their + // base Setup has DefaultMotionTable=0, but the server tells us + // which motion table to use via PhysicsDescriptionFlag.MTable. + // Without this override the resolver returns null and we fall + // back to PlacementFrames[Default] which renders the wrong pose. var idleFrame = AcDream.Core.Meshing.MotionResolver.GetIdleFrame( - setup, _dats, motionTableIdOverride: null, + setup, _dats, + motionTableIdOverride: spawn.MotionTableId, stanceOverride: stanceOverride, commandOverride: commandOverride); var flat = AcDream.Core.Meshing.SetupMesh.Flatten(setup, idleFrame); diff --git a/src/AcDream.Core.Net/Messages/CreateObject.cs b/src/AcDream.Core.Net/Messages/CreateObject.cs index 43b7eba..4d6691d 100644 --- a/src/AcDream.Core.Net/Messages/CreateObject.cs +++ b/src/AcDream.Core.Net/Messages/CreateObject.cs @@ -91,7 +91,8 @@ public static class CreateObject uint? BasePaletteId, float? ObjScale, string? Name, - ServerMotionState? MotionState); + ServerMotionState? MotionState, + uint? MotionTableId); /// /// The relevant subset of the server-sent MovementData / @@ -156,6 +157,7 @@ public static class CreateObject uint? setupTableId = null; float? objScale = null; ServerMotionState? motionState = null; + uint? motionTableId = null; try { @@ -274,6 +276,7 @@ public static class CreateObject if ((physicsFlags & PhysicsDescriptionFlag.MTable) != 0) { if (body.Length - pos < 4) return null; + motionTableId = BinaryPrimitives.ReadUInt32LittleEndian(body.Slice(pos)); pos += 4; } @@ -346,13 +349,13 @@ public static class CreateObject } return new Parsed(guid, position, setupTableId, animParts, - textureChanges, subPalettes, basePaletteId, objScale, name, motionState); + textureChanges, subPalettes, basePaletteId, objScale, name, motionState, motionTableId); // Local helper: if we ran out of fields past PhysicsData, still // return the useful prefix (guid/position/setup/animParts/textures/palettes/scale/motion). Parsed PartialResult() => new( guid, position, setupTableId, animParts, - textureChanges, subPalettes, basePaletteId, objScale, null, motionState); + textureChanges, subPalettes, basePaletteId, objScale, null, motionState, motionTableId); } catch { diff --git a/src/AcDream.Core.Net/WorldSession.cs b/src/AcDream.Core.Net/WorldSession.cs index fc958c9..5121a05 100644 --- a/src/AcDream.Core.Net/WorldSession.cs +++ b/src/AcDream.Core.Net/WorldSession.cs @@ -52,7 +52,8 @@ public sealed class WorldSession : IDisposable uint? BasePaletteId, float? ObjScale, string? Name, - CreateObject.ServerMotionState? MotionState); + CreateObject.ServerMotionState? MotionState, + uint? MotionTableId); /// Fires when the session finishes parsing a CreateObject. public event Action? EntitySpawned; @@ -238,7 +239,8 @@ public sealed class WorldSession : IDisposable parsed.Value.BasePaletteId, parsed.Value.ObjScale, parsed.Value.Name, - parsed.Value.MotionState)); + parsed.Value.MotionState, + parsed.Value.MotionTableId)); } } }