phase(N.4) Adjustment 6: add PartOverrides + HiddenPartsMask to WorldEntity

Resolves Adjustment 4 (Option A): WorldEntity now carries the server-
sent AnimPartChange data as PartOverrides and a HiddenPartsMask bitmask.
EntitySpawnAdapter.OnCreate populates AnimatedEntityState from these
fields at spawn time. GameWindow's CreateObject handler converts the
network-layer AnimPartChange records into lightweight PartOverride
structs.

This unblocks Task 22: the WbDrawDispatcher can now resolve per-part
GfxObj overrides and hidden-part suppression from entity state.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Erik 2026-05-08 15:10:22 +02:00
parent 16a36dda8f
commit 5b4fd4b61d
4 changed files with 70 additions and 14 deletions

View file

@ -2407,6 +2407,19 @@ public sealed class GameWindow : IDisposable
SubPalettes: ranges);
}
AcDream.Core.World.PartOverride[] entityPartOverrides;
if (animPartChanges.Count == 0)
{
entityPartOverrides = Array.Empty<AcDream.Core.World.PartOverride>();
}
else
{
entityPartOverrides = new AcDream.Core.World.PartOverride[animPartChanges.Count];
for (int i = 0; i < animPartChanges.Count; i++)
entityPartOverrides[i] = new AcDream.Core.World.PartOverride(
animPartChanges[i].PartIndex, animPartChanges[i].NewModelId);
}
var entity = new AcDream.Core.World.WorldEntity
{
Id = _liveEntityIdCounter++,
@ -2416,6 +2429,7 @@ public sealed class GameWindow : IDisposable
Rotation = rot,
MeshRefs = meshRefs,
PaletteOverride = paletteOverride,
PartOverrides = entityPartOverrides,
};
var snapshot = new AcDream.Plugin.Abstractions.WorldEntitySnapshot(

View file

@ -39,15 +39,10 @@ namespace AcDream.App.Rendering.Wb;
/// </para>
///
/// <para>
/// <b>Adjustment 4</b>: <see cref="WorldEntity"/> does not currently expose
/// <c>HiddenPartsMask</c> or <c>AnimPartChanges</c> as direct fields (those
/// live on the network-layer spawn record and are consumed upstream before
/// the <see cref="WorldEntity"/> is built). When those fields are promoted to
/// <see cref="WorldEntity"/>, <see cref="OnCreate"/> should call
/// <see cref="AnimatedEntityState.HideParts"/> and
/// <see cref="AnimatedEntityState.SetPartOverride"/> here. For now the mask
/// stays at 0 (no parts hidden) and no part overrides are set — the draw
/// dispatcher falls through to Setup defaults for every part.
/// <b>Adjustment 6</b> (resolved Adjustment 4): <see cref="WorldEntity"/> now
/// carries <see cref="WorldEntity.PartOverrides"/> and
/// <see cref="WorldEntity.HiddenPartsMask"/>. <see cref="OnCreate"/> applies
/// both to the created <see cref="AnimatedEntityState"/>.
/// </para>
/// </summary>
public sealed class EntitySpawnAdapter
@ -125,11 +120,10 @@ public sealed class EntitySpawnAdapter
var sequencer = _sequencerFactory(entity);
var state = new AnimatedEntityState(sequencer);
// Adjustment 4 placeholder: when WorldEntity gains HiddenPartsMask +
// AnimPartChanges fields, apply them here:
// state.HideParts(entity.HiddenPartsMask);
// foreach (var apc in entity.AnimPartChanges)
// state.SetPartOverride(apc.PartIndex, apc.NewModelId);
// Adjustment 6: WorldEntity now carries PartOverrides + HiddenPartsMask.
state.HideParts(entity.HiddenPartsMask);
foreach (var po in entity.PartOverrides)
state.SetPartOverride(po.PartIndex, po.GfxObjId);
_stateByGuid[entity.ServerGuid] = state;
return state;