feat(phys): UpdatePosition handles multi-part entities

Multi-part entities cached via RegisterMultiPart's _entityShapes now
recompose all part transforms on UpdatePosition (called when the server
broadcasts UpdatePosition (0xF748) for a moving entity). Legacy
single-shape path preserved unchanged for tests + entities that never
went through RegisterMultiPart.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
Erik 2026-05-24 15:21:35 +02:00
parent fca0a13217
commit d5ffb0331b
2 changed files with 63 additions and 5 deletions

View file

@ -219,8 +219,36 @@ public sealed class ShadowObjectRegistry
public void UpdatePosition(uint entityId, Vector3 worldPos, Quaternion rotation,
float worldOffsetX, float worldOffsetY, uint landblockId)
{
// Find the existing entry (any cell holds a copy with the same
// entity-scoped state + flags + shape).
// A6.P4 door fix (2026-05-24): if the entity was registered via
// RegisterMultiPart, we have its full shape list cached. Use that
// to recompose all part transforms instead of finding one template entry.
if (_entityShapes.TryGetValue(entityId, out var shapes))
{
// Pull the entity-scoped state + flags from the first matching entry
// (they're shared across all parts of a logical entity).
uint state = 0u;
EntityCollisionFlags flags = EntityCollisionFlags.None;
if (_entityToCells.TryGetValue(entityId, out var existingCells)
&& existingCells.Count > 0
&& _cells.TryGetValue(existingCells[0], out var firstList))
{
foreach (var e in firstList)
{
if (e.EntityId == entityId)
{
state = e.State;
flags = e.Flags;
break;
}
}
}
RegisterMultiPart(entityId, worldPos, rotation, shapes,
state, flags, worldOffsetX, worldOffsetY, landblockId);
return;
}
// Single-shape path (legacy compat for tests + entities that never
// went through RegisterMultiPart).
if (!_entityToCells.TryGetValue(entityId, out var oldCells) || oldCells.Count == 0)
return;
@ -240,10 +268,8 @@ public sealed class ShadowObjectRegistry
}
if (template is not null) break;
}
if (template is null)
return;
if (template is null) return;
// Preserve everything except position + rotation.
var t = template.Value;
Register(entityId, t.GfxObjId, worldPos, rotation, t.Radius,
worldOffsetX, worldOffsetY, landblockId,