diff --git a/src/AcDream.App/Rendering/GameWindow.cs b/src/AcDream.App/Rendering/GameWindow.cs index e5611c85..5991c83b 100644 --- a/src/AcDream.App/Rendering/GameWindow.cs +++ b/src/AcDream.App/Rendering/GameWindow.cs @@ -837,7 +837,6 @@ public sealed class GameWindow : IDisposable /// keys the render list; this parallel dictionary keys by server guid. /// private readonly Dictionary _entitiesByServerGuid = new(); - private readonly Dictionary _liveEntityInfoByGuid = new(); /// /// Latest for each /// guid. Captured at the end of so @@ -854,9 +853,6 @@ public sealed class GameWindow : IDisposable // far-range sends fire the wire packet immediately at SendUse/SendPickUp // time. Cleared before the deferred send fires — single-fire, no retry. private (uint Guid, bool IsPickup)? _pendingPostArrivalAction; - private readonly record struct LiveEntityInfo( - string? Name, - AcDream.Core.Items.ItemType ItemType); private static bool IsPlayerGuid(uint guid) => (guid & 0xFF000000u) == 0x50000000u; private const double ServerControlledVelocityStaleSeconds = 0.60; private int _liveSpawnReceived; // diagnostics @@ -1302,7 +1298,7 @@ public sealed class GameWindow : IDisposable // live state from this GameWindow instance every frame: // - selected guid → _selectedGuid (set by PickAndStoreSelection) // - entity resolver → position from _entitiesByServerGuid + - // itemType / PWD bits from cached LiveEntityInfo + last spawn + // itemType from ClientObjectTable (Objects) + last spawn // - camera → _cameraController.Active or (zero) when not // yet ready, in which case the panel bails on viewport==0. _targetIndicator = new AcDream.App.UI.TargetIndicatorPanel( @@ -1311,9 +1307,7 @@ public sealed class GameWindow : IDisposable { if (!_entitiesByServerGuid.TryGetValue(guid, out var entity)) return null; - uint rawItemType = 0; - if (_liveEntityInfoByGuid.TryGetValue(guid, out var info)) - rawItemType = (uint)info.ItemType; + uint rawItemType = (uint)LiveItemType(guid); uint pwdBits = 0; uint? useability = null; if (_lastSpawnByGuid.TryGetValue(guid, out var spawn)) @@ -2706,12 +2700,6 @@ public sealed class GameWindow : IDisposable $"itemType={itemTypeStr} animParts={animPartCount} texChanges={texChangeCount} subPalettes={subPalCount}"); } - _liveEntityInfoByGuid[spawn.Guid] = new LiveEntityInfo( - spawn.Name, - spawn.ItemType is { } rawItemType - ? (AcDream.Core.Items.ItemType)rawItemType - : AcDream.Core.Items.ItemType.None); - // Target the statue specifically for full diagnostic dump: Name match // is cheap and gives us exactly one entity's worth of log regardless // of arrival order. @@ -3717,7 +3705,6 @@ public sealed class GameWindow : IDisposable // clear using the same guid the next spawn/update would use. _remoteDeadReckon.Remove(serverGuid); _remoteLastMove.Remove(serverGuid); - _liveEntityInfoByGuid.Remove(serverGuid); _entitiesByServerGuid.Remove(serverGuid); _lastSpawnByGuid.Remove(serverGuid); if (_selectedGuid == serverGuid) @@ -3809,8 +3796,7 @@ public sealed class GameWindow : IDisposable // Per-Door UM dispatch trail; grep [door-cycle] in launch.log to verify door animation. if (AcDream.Core.Physics.PhysicsDiagnostics.ProbeBuildingEnabled - && _liveEntityInfoByGuid.TryGetValue(update.Guid, out var doorInfo) - && IsDoorName(doorInfo.Name)) + && IsDoorName(LiveName(update.Guid))) { Console.WriteLine(System.FormattableString.Invariant( $"[door-cycle] guid=0x{update.Guid:X8} stance=0x{stance:X4} cmd=0x{(command ?? 0u):X4}")); @@ -11590,9 +11576,7 @@ public sealed class GameWindow : IDisposable // RadarBlipColor are produced for the just-picked entity. // Helps verify whether a "green NPC" really is flagged as // Vendor server-side or whether our lookup is wrong. - uint rawItemType = 0; - if (_liveEntityInfoByGuid.TryGetValue(guid, out var info)) - rawItemType = (uint)info.ItemType; + uint rawItemType = (uint)LiveItemType(guid); uint pwdBits = 0; uint? pickUseability = null; float? pickUseRadius = null; @@ -11649,8 +11633,7 @@ public sealed class GameWindow : IDisposable // Retail string at acclient_2013_pseudo_c.txt:1033115 // (data_7e2a70): "The %s cannot be used". - bool isCreature = _liveEntityInfoByGuid.TryGetValue(sel, out var info) - && (info.ItemType & AcDream.Core.Items.ItemType.Creature) != 0; + bool isCreature = (LiveItemType(sel) & AcDream.Core.Items.ItemType.Creature) != 0; if (isCreature) { @@ -11891,8 +11874,7 @@ public sealed class GameWindow : IDisposable // Mirror InstallSpeculativeTurnToTarget's per-type radius heuristic. float useRadius = 0.6f; - if (_liveEntityInfoByGuid.TryGetValue(targetGuid, out var info) - && (info.ItemType & AcDream.Core.Items.ItemType.Creature) != 0) + if ((LiveItemType(targetGuid) & AcDream.Core.Items.ItemType.Creature) != 0) { useRadius = 3.0f; } @@ -11919,8 +11901,7 @@ public sealed class GameWindow : IDisposable // Per-type use radius — same heuristic as the picker's // radiusForGuid callback. float useRadius = 0.6f; - if (_liveEntityInfoByGuid.TryGetValue(targetGuid, out var info) - && (info.ItemType & AcDream.Core.Items.ItemType.Creature) != 0) + if ((LiveItemType(targetGuid) & AcDream.Core.Items.ItemType.Creature) != 0) { useRadius = 3.0f; } @@ -12001,10 +11982,8 @@ public sealed class GameWindow : IDisposable return false; if (!_entitiesByServerGuid.ContainsKey(guid)) return false; - if (!_liveEntityInfoByGuid.TryGetValue(guid, out var info)) - return false; - return (info.ItemType & AcDream.Core.Items.ItemType.Creature) != 0; + return (LiveItemType(guid) & AcDream.Core.Items.ItemType.Creature) != 0; } @@ -12171,8 +12150,7 @@ public sealed class GameWindow : IDisposable // `ItemUseable = null`; without the fallback the M1 "click NPC" // flow regresses. The diagnostic line below lets us measure // how often this branch fires in real play. - if (_liveEntityInfoByGuid.TryGetValue(guid, out var info) - && (info.ItemType & AcDream.Core.Items.ItemType.Creature) != 0) + if ((LiveItemType(guid) & AcDream.Core.Items.ItemType.Creature) != 0) { if (AcDream.Core.Physics.PhysicsDiagnostics.ProbeUseabilityFallbackEnabled) Console.WriteLine(System.FormattableString.Invariant( @@ -12280,11 +12258,15 @@ public sealed class GameWindow : IDisposable return (it & SmallItemMask) != 0u; } + private AcDream.Core.Items.ItemType LiveItemType(uint guid) => + Objects.Get(guid)?.Type ?? AcDream.Core.Items.ItemType.None; + + private string? LiveName(uint guid) => Objects.Get(guid)?.Name; + private string DescribeLiveEntity(uint guid) { - if (_liveEntityInfoByGuid.TryGetValue(guid, out var info) - && !string.IsNullOrWhiteSpace(info.Name)) - return info.Name!; + var name = LiveName(guid); + if (!string.IsNullOrWhiteSpace(name)) return name!; return $"0x{guid:X8}"; }