feat(net): Phase 6.6 — parse UpdateMotion (0xF74C) into MotionUpdated event
Server sends UpdateMotion whenever an entity's motion state changes: NPCs starting a walk cycle, creatures switching to a combat stance, doors opening, a player waving, etc. Phase 6.1-6.4 already handles rendering different (stance, forward-command) pairs for the INITIAL CreateObject, but without this message NPCs freeze in whatever pose they spawned with and never transition to walking/fighting. Added UpdateMotion.TryParse with the same ServerMotionState the CreateObject path uses, reached via a slightly different outer layout (guid + instance seq + header'd MovementData; the MovementData starts with the 8-byte sequence/autonomous header this time rather than being preceded by a length field). Only the (stance, forward- command) pair is extracted — same subset CreateObject grabs. WorldSession dispatches MotionUpdated(guid, state) when a 0xF74C body parses successfully. The App-side wiring (guid→entity lookup and AnimatedEntity cycle swap) is intentionally deferred to a separate commit because it touches GameWindow which is currently being edited by the Phase 9.1 translucent-pass work. 89 Core.Net tests (was 83, +6 for UpdateMotion coverage). Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
4752b8a528
commit
a71db90310
12 changed files with 675 additions and 45 deletions
|
|
@ -1,5 +1,6 @@
|
|||
using System.Numerics;
|
||||
using AcDream.Core.Terrain;
|
||||
using DatReaderWriter;
|
||||
using DatReaderWriter.DBObjs;
|
||||
using DatReaderWriter.Types;
|
||||
|
||||
|
|
@ -20,7 +21,14 @@ public static class CellMesh
|
|||
/// per referenced Surface. Surfaces are resolved from <paramref name="envCell"/>.Surfaces
|
||||
/// (OR'd with 0x08000000 to form the full dat id). Polygons are triangulated as fans.
|
||||
/// </summary>
|
||||
public static IReadOnlyList<GfxObjSubMesh> Build(EnvCell envCell, CellStruct cellStruct)
|
||||
/// <param name="envCell">The EnvCell that owns the surface list.</param>
|
||||
/// <param name="cellStruct">The CellStruct containing the polygon + vertex geometry.</param>
|
||||
/// <param name="dats">
|
||||
/// Optional dat collection used to read Surface.Type flags and set
|
||||
/// <see cref="GfxObjSubMesh.Translucency"/>. When null (e.g. offline tests)
|
||||
/// all sub-meshes default to <see cref="TranslucencyKind.Opaque"/>.
|
||||
/// </param>
|
||||
public static IReadOnlyList<GfxObjSubMesh> Build(EnvCell envCell, CellStruct cellStruct, DatCollection? dats = null)
|
||||
{
|
||||
// Group output vertices and indices per surface dat id.
|
||||
var perSurface = new Dictionary<uint, (List<Vertex> Vertices, List<uint> Indices, Dictionary<(int pos, int uv), uint> Dedupe)>();
|
||||
|
|
@ -97,10 +105,23 @@ public static class CellMesh
|
|||
var result = new List<GfxObjSubMesh>(perSurface.Count);
|
||||
foreach (var kvp in perSurface)
|
||||
{
|
||||
// Resolve Surface.Type flags when a DatCollection is available so the
|
||||
// renderer can split the draw into opaque and translucent passes.
|
||||
var translucency = TranslucencyKind.Opaque;
|
||||
if (dats is not null)
|
||||
{
|
||||
var surface = dats.Get<Surface>(kvp.Key);
|
||||
if (surface is not null)
|
||||
translucency = TranslucencyKindExtensions.FromSurfaceType(surface.Type);
|
||||
}
|
||||
|
||||
result.Add(new GfxObjSubMesh(
|
||||
SurfaceId: kvp.Key,
|
||||
Vertices: kvp.Value.Vertices.ToArray(),
|
||||
Indices: kvp.Value.Indices.ToArray()));
|
||||
Indices: kvp.Value.Indices.ToArray())
|
||||
{
|
||||
Translucency = translucency,
|
||||
});
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue