using System; using System.Collections; using System.Collections.Generic; using Dalamud.Game; using Dalamud.Game.ClientState; using Dalamud.Game.ClientState.Objects; using Glamourer.Services; using Penumbra.GameData.Actors; namespace Glamourer.Interop; public readonly struct ActorData { public readonly List Objects; public readonly string Label; public bool Valid => Objects.Count > 0; public ActorData(Actor actor, string label) { Objects = new List { actor }; Label = label; } public static readonly ActorData Invalid = new(false); private ActorData(bool _) { Objects = new List(0); Label = string.Empty; } } public class ObjectManager : IReadOnlyDictionary { private readonly Framework _framework; private readonly ClientState _clientState; private readonly ObjectTable _objects; private readonly ActorService _actors; public ObjectManager(Framework framework, ClientState clientState, ObjectTable objects, ActorService actors) { _framework = framework; _clientState = clientState; _objects = objects; _actors = actors; } public DateTime LastUpdate { get; private set; } public bool IsInGPose { get; private set; } public ushort World { get; private set; } private readonly Dictionary _identifiers = new(200); private void HandleIdentifier(ActorIdentifier identifier, Actor character) { if (!character.DrawObject || !identifier.IsValid) return; if (!_identifiers.TryGetValue(identifier, out var data)) { data = new ActorData(character, identifier.ToString()); _identifiers[identifier] = data; } else { data.Objects.Add(character); } } public void Update() { var lastUpdate = _framework.LastUpdate; if (lastUpdate <= LastUpdate) return; LastUpdate = lastUpdate; World = (ushort)(_clientState.LocalPlayer?.CurrentWorld.Id ?? 0u); _identifiers.Clear(); for (var i = 0; i < (int)ScreenActor.CutsceneStart; ++i) { Actor character = _objects.GetObjectAddress(i); if (character.Identifier(_actors.AwaitedService, out var identifier)) HandleIdentifier(identifier, character); } for (var i = (int)ScreenActor.CutsceneStart; i < (int)ScreenActor.CutsceneEnd; ++i) { Actor character = _objects.GetObjectAddress(i); if (!character.Identifier(_actors.AwaitedService, out var identifier)) break; HandleIdentifier(identifier, character); } void AddSpecial(ScreenActor idx, string label) { Actor actor = _objects.GetObjectAddress((int)idx); if (actor.Identifier(_actors.AwaitedService, out var ident)) { var data = new ActorData(actor, label); _identifiers.Add(ident, data); } } AddSpecial(ScreenActor.CharacterScreen, "Character Screen Actor"); AddSpecial(ScreenActor.ExamineScreen, "Examine Screen Actor"); AddSpecial(ScreenActor.FittingRoom, "Fitting Room Actor"); AddSpecial(ScreenActor.DyePreview, "Dye Preview Actor"); AddSpecial(ScreenActor.Portrait, "Portrait Actor"); AddSpecial(ScreenActor.Card6, "Card Actor 6"); AddSpecial(ScreenActor.Card7, "Card Actor 7"); AddSpecial(ScreenActor.Card8, "Card Actor 8"); for (var i = (int)ScreenActor.ScreenEnd; i < _objects.Length; ++i) { Actor character = _objects.GetObjectAddress(i); if (character.Identifier(_actors.AwaitedService, out var identifier)) HandleIdentifier(identifier, character); } var gPose = GPosePlayer; IsInGPose = gPose && gPose.Utf8Name.Length > 0; } public Actor GPosePlayer => _objects.GetObjectAddress((int)ScreenActor.GPosePlayer); public Actor Player => _objects.GetObjectAddress(0); public IEnumerator> GetEnumerator() => _identifiers.GetEnumerator(); IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); public int Count => _identifiers.Count; public bool ContainsKey(ActorIdentifier key) => _identifiers.ContainsKey(key); public bool TryGetValue(ActorIdentifier key, out ActorData value) => _identifiers.TryGetValue(key, out value); public ActorData this[ActorIdentifier key] => _identifiers[key]; public IEnumerable Keys => _identifiers.Keys; public IEnumerable Values => _identifiers.Values; }