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:
Erik 2026-04-11 20:33:26 +02:00
parent 4752b8a528
commit a71db90310
12 changed files with 675 additions and 45 deletions

View file

@ -5,6 +5,18 @@ out vec4 fragColor;
uniform sampler2D uDiffuse;
// Phase 9.1: translucency kind — matches TranslucencyKind C# enum.
// 0 = Opaque — depth write+test, no blend; shader never discards
// 1 = ClipMap — alpha-key discard (doors, windows, vegetation)
// 2 = AlphaBlend — GL blending handles compositing; do NOT discard
// 3 = Additive — GL additive blending; do NOT discard
// 4 = InvAlpha — GL inverted-alpha blending; do NOT discard
//
// Only ClipMap uses the alpha-discard path. AlphaBlend/Additive/InvAlpha
// rely entirely on the GL blend stage — discarding low-alpha fragments
// would make semi-transparent surfaces (portals, glows) fully invisible.
uniform int uTranslucencyKind;
// Phase 3a: simple directional lighting. A single sun direction + ambient term
// gives scenery and building faces enough differentiation to read as 3D instead
// of looking like paper cutouts. Hardcoded for now; a later phase can route
@ -19,8 +31,12 @@ const float DIFFUSE = 0.75;
void main() {
vec4 sampled = texture(uDiffuse, vTex);
// Alpha cutout for doors, windows, vegetation, and other alpha-keyed textures.
if (sampled.a < 0.5) discard;
// Alpha cutout only for clip-map surfaces (doors, windows, vegetation).
// Blended surface types (AlphaBlend, Additive, InvAlpha) must NOT
// discard here — that would make every semi-transparent pixel invisible
// before the blend stage even runs.
if (uTranslucencyKind == 1 && sampled.a < 0.5) discard;
vec3 N = normalize(vWorldNormal);
float ndotl = max(dot(N, SUN_DIR), 0.0);