diff --git a/Peeping Tom.Ipc/IpcInfo.cs b/Peeping Tom.Ipc/IpcInfo.cs index b67c65a..2ccb3af 100755 --- a/Peeping Tom.Ipc/IpcInfo.cs +++ b/Peeping Tom.Ipc/IpcInfo.cs @@ -8,11 +8,11 @@ namespace PeepingTom.Ipc { public const string FromRegistrationName = "PeepingTom.From"; public const string ToRegistrationName = "PeepingTom.To"; - public static ICallGateProvider GetProvider(DalamudPluginInterface @interface) { + public static ICallGateProvider GetProvider(IDalamudPluginInterface @interface) { return @interface.GetIpcProvider(ToRegistrationName); } - public static ICallGateSubscriber GetSubscriber(DalamudPluginInterface @interface) { + public static ICallGateSubscriber GetSubscriber(IDalamudPluginInterface @interface) { return @interface.GetIpcSubscriber(FromRegistrationName); } } diff --git a/Peeping Tom.Ipc/Targeter.cs b/Peeping Tom.Ipc/Targeter.cs index ff93b8f..db688d5 100755 --- a/Peeping Tom.Ipc/Targeter.cs +++ b/Peeping Tom.Ipc/Targeter.cs @@ -10,27 +10,31 @@ namespace PeepingTom.Ipc { public class Targeter { [JsonConverter(typeof(SeStringConverter))] public SeString Name { get; } + public uint HomeWorldId { get; } - public uint ObjectId { get; } + public ulong GameObjectId { get; } + public uint EntityId { get; } public DateTime When { get; } - public Targeter(PlayerCharacter character) { + public Targeter(IPlayerCharacter character) { this.Name = character.Name; this.HomeWorldId = character.HomeWorld.Id; - this.ObjectId = character.ObjectId; + this.EntityId = character.EntityId; + this.GameObjectId = character.GameObjectId; this.When = DateTime.UtcNow; } [JsonConstructor] - public Targeter(SeString name, uint homeWorldId, uint objectId, DateTime when) { + public Targeter(SeString name, uint homeWorldId, uint entityId, ulong gameObjectId, DateTime when) { this.Name = name; this.HomeWorldId = homeWorldId; - this.ObjectId = objectId; + this.EntityId = entityId; + this.GameObjectId = gameObjectId; this.When = when; } - public PlayerCharacter? GetPlayerCharacter(IObjectTable objectTable) { - return objectTable.FirstOrDefault(actor => actor.ObjectId == this.ObjectId && actor is PlayerCharacter) as PlayerCharacter; + public IPlayerCharacter? GetPlayerCharacter(IObjectTable objectTable) { + return objectTable.FirstOrDefault(actor => actor.GameObjectId == this.GameObjectId && actor is IPlayerCharacter) as IPlayerCharacter; } } } diff --git a/Peeping Tom/Configuration.cs b/Peeping Tom/Configuration.cs index 5aa8e70..77aed21 100644 --- a/Peeping Tom/Configuration.cs +++ b/Peeping Tom/Configuration.cs @@ -8,7 +8,7 @@ namespace PeepingTom { internal class Configuration : IPluginConfiguration { public int Version { get; set; } = 1; - private DalamudPluginInterface Interface { get; set; } = null!; + private IDalamudPluginInterface Interface { get; set; } = null!; public bool MarkTargeted { get; set; } @@ -52,7 +52,7 @@ namespace PeepingTom { public int PollFrequency { get; set; } = 100; - public void Initialize(DalamudPluginInterface pluginInterface) { + public void Initialize(IDalamudPluginInterface pluginInterface) { this.Interface = pluginInterface; } diff --git a/Peeping Tom/Peeping Tom.csproj b/Peeping Tom/Peeping Tom.csproj index 9254bb3..5c1a96e 100755 --- a/Peeping Tom/Peeping Tom.csproj +++ b/Peeping Tom/Peeping Tom.csproj @@ -37,6 +37,10 @@ $(DalamudLibPath)\ImGui.NET.dll False + + $(DalamudLibPath)\FFXIVClientStructs.dll + False + $(DalamudLibPath)\Lumina.dll False @@ -51,12 +55,11 @@ - - + + - diff --git a/Peeping Tom/Plugin.cs b/Peeping Tom/Plugin.cs index 4208ef5..a66cfd8 100644 --- a/Peeping Tom/Plugin.cs +++ b/Peeping Tom/Plugin.cs @@ -7,7 +7,6 @@ using Dalamud.IoC; using Dalamud.Plugin.Services; using Lumina.Excel.GeneratedSheets; using PeepingTom.Resources; -using XivCommon; namespace PeepingTom { // ReSharper disable once ClassNeverInstantiated.Global @@ -18,7 +17,7 @@ namespace PeepingTom { internal static IPluginLog Log { get; private set; } = null!; [PluginService] - internal DalamudPluginInterface Interface { get; init; } = null!; + internal IDalamudPluginInterface Interface { get; init; } = null!; [PluginService] internal IChatGui ChatGui { get; init; } = null!; @@ -53,13 +52,11 @@ namespace PeepingTom { internal Configuration Config { get; } internal PluginUi Ui { get; } internal TargetWatcher Watcher { get; } - internal XivCommonBase Common { get; } internal IpcManager IpcManager { get; } internal bool InPvp { get; private set; } public Plugin() { - this.Common = new XivCommonBase(this.Interface); this.Config = this.Interface.GetPluginConfig() as Configuration ?? new Configuration(); this.Config.Initialize(this.Interface); this.Watcher = new TargetWatcher(this); @@ -99,7 +96,6 @@ namespace PeepingTom { this.IpcManager.Dispose(); this.Ui.Dispose(); this.Watcher.Dispose(); - this.Common.Dispose(); } private static void OnLanguageChange(string langCode) { diff --git a/Peeping Tom/PluginUi.cs b/Peeping Tom/PluginUi.cs index 2550f7a..af63859 100644 --- a/Peeping Tom/PluginUi.cs +++ b/Peeping Tom/PluginUi.cs @@ -16,12 +16,13 @@ using PeepingTom.Ipc; using PeepingTom.Resources; using Dalamud.Interface.ImGuiFileDialog; using Dalamud.Interface; +using FFXIVClientStructs.FFXIV.Client.UI.Agent; namespace PeepingTom { internal class PluginUi : IDisposable { private Plugin Plugin { get; } - private uint? PreviousFocus { get; set; } = new(); + private ulong? PreviousFocus { get; set; } = new(); private bool _wantsOpen; @@ -115,9 +116,9 @@ namespace PeepingTom { } var targeting = this.Plugin.Watcher.CurrentTargeters - .Select(targeter => this.Plugin.ObjectTable.FirstOrDefault(obj => obj.ObjectId == targeter.ObjectId)) - .Where(targeter => targeter is PlayerCharacter) - .Cast() + .Select(targeter => this.Plugin.ObjectTable.FirstOrDefault(obj => obj.GameObjectId == targeter.GameObjectId)) + .Where(targeter => targeter is IPlayerCharacter) + .Cast() .ToArray(); foreach (var targeter in targeting) { this.MarkPlayer(targeter, this.Plugin.Config.TargetingColour, this.Plugin.Config.TargetingSize); @@ -408,15 +409,15 @@ namespace PeepingTom { // to prevent looping over a subset of the actors repeatedly when multiple people are targeting, // create a dictionary for O(1) lookups by actor id - Dictionary? objects = null; + Dictionary? objects = null; if (targeting.Count + (previousTargeters?.Count ?? 0) > 1) { - var dict = new Dictionary(); + var dict = new Dictionary(); foreach (var obj in this.Plugin.ObjectTable) { - if (dict.ContainsKey(obj.ObjectId) || obj.ObjectKind != ObjectKind.Player) { + if (dict.ContainsKey(obj.GameObjectId) || obj.ObjectKind != ObjectKind.Player) { continue; } - dict.Add(obj.ObjectId, obj); + dict.Add(obj.GameObjectId, obj); } objects = dict; @@ -459,20 +460,20 @@ namespace PeepingTom { // } foreach (var targeter in targeting) { - GameObject? obj = null; - objects?.TryGetValue(targeter.ObjectId, out obj); + IGameObject? obj = null; + objects?.TryGetValue(targeter.GameObjectId, out obj); this.AddEntry(targeter, obj, ref anyHovered); } if (this.Plugin.Config.KeepHistory) { // get a list of the previous targeters that aren't currently targeting var previous = (previousTargeters ?? new List()) - .Where(old => targeting.All(actor => actor.ObjectId != old.ObjectId)) + .Where(old => targeting.All(actor => actor.GameObjectId != old.GameObjectId)) .Take(this.Plugin.Config.NumHistory); // add previous targeters to the list foreach (var oldTargeter in previous) { - GameObject? obj = null; - objects?.TryGetValue(oldTargeter.ObjectId, out obj); + IGameObject? obj = null; + objects?.TryGetValue(oldTargeter.GameObjectId, out obj); this.AddEntry(oldTargeter, obj, ref anyHovered, ImGuiSelectableFlags.Disabled); } } @@ -485,7 +486,7 @@ namespace PeepingTom { if (previousFocus == uint.MaxValue) { this.Plugin.TargetManager.FocusTarget = null; } else { - var actor = this.Plugin.ObjectTable.FirstOrDefault(a => a.ObjectId == previousFocus); + var actor = this.Plugin.ObjectTable.FirstOrDefault(a => a.GameObjectId == previousFocus); // either target the actor if still present or target nothing this.Plugin.TargetManager.FocusTarget = actor; } @@ -510,7 +511,7 @@ namespace PeepingTom { ImGui.EndTooltip(); } - private void AddEntry(Targeter targeter, GameObject? obj, ref bool anyHovered, ImGuiSelectableFlags flags = ImGuiSelectableFlags.None) { + private void AddEntry(Targeter targeter, IGameObject? obj, ref bool anyHovered, ImGuiSelectableFlags flags = ImGuiSelectableFlags.None) { ImGui.BeginGroup(); ImGui.Selectable(targeter.Name.TextValue, false, flags); @@ -539,7 +540,7 @@ namespace PeepingTom { var left = hover && ImGui.IsMouseClicked(ImGuiMouseButton.Left); var right = hover && ImGui.IsMouseClicked(ImGuiMouseButton.Right); - obj ??= this.Plugin.ObjectTable.FirstOrDefault(a => a.ObjectId == targeter.ObjectId); + obj ??= this.Plugin.ObjectTable.FirstOrDefault(a => a.GameObjectId == targeter.GameObjectId); // don't count as hovered if the actor isn't here (clears focus target when hovering missing actors) if (obj != null) { @@ -547,21 +548,23 @@ namespace PeepingTom { } if (this.Plugin.Config.FocusTargetOnHover && hover && obj != null) { - this.PreviousFocus ??= this.Plugin.TargetManager.FocusTarget?.ObjectId ?? uint.MaxValue; + this.PreviousFocus ??= this.Plugin.TargetManager.FocusTarget?.GameObjectId ?? uint.MaxValue; this.Plugin.TargetManager.FocusTarget = obj; } if (left) { if (this.Plugin.Config.OpenExamine && ImGui.GetIO().KeyAlt) { if (obj != null) { - this.Plugin.Common.Functions.Examine.OpenExamineWindow(obj); + unsafe { + AgentInspect.Instance()->ExamineCharacter(obj.EntityId); + } } else { var error = string.Format(Language.ExamineErrorToast, targeter.Name); this.Plugin.ToastGui.ShowError(error); } } else { var payload = new PlayerPayload(targeter.Name.TextValue, targeter.HomeWorldId); - Payload[] payloads = { payload }; + Payload[] payloads = [payload]; this.Plugin.ChatGui.Print(new XivChatEntry { Message = new SeString(payloads), }); @@ -571,7 +574,7 @@ namespace PeepingTom { } } - private void MarkPlayer(GameObject? player, Vector4 colour, float size) { + private void MarkPlayer(IGameObject? player, Vector4 colour, float size) { if (player == null) { return; } @@ -592,7 +595,7 @@ namespace PeepingTom { ImGui.PopClipRect(); } - private PlayerCharacter? GetCurrentTarget() { + private IPlayerCharacter? GetCurrentTarget() { var player = this.Plugin.ClientState.LocalPlayer; if (player == null) { return null; @@ -604,8 +607,8 @@ namespace PeepingTom { } return this.Plugin.ObjectTable - .Where(actor => actor.ObjectId == targetId && actor is PlayerCharacter) - .Select(actor => actor as PlayerCharacter) + .Where(actor => actor.GameObjectId == targetId && actor is IPlayerCharacter) + .Select(actor => actor as IPlayerCharacter) .FirstOrDefault(); } } diff --git a/Peeping Tom/TargetWatcher.cs b/Peeping Tom/TargetWatcher.cs index 259dc4c..666dee8 100644 --- a/Peeping Tom/TargetWatcher.cs +++ b/Peeping Tom/TargetWatcher.cs @@ -21,11 +21,11 @@ namespace PeepingTom { private Stopwatch? SoundWatch { get; set; } private int LastTargetAmount { get; set; } - private Targeter[] Current { get; set; } = Array.Empty(); + private Targeter[] Current { get; set; } = []; public IReadOnlyCollection CurrentTargeters => this.Current; - private List Previous { get; } = new(); + private List Previous { get; } = []; public IReadOnlyCollection PreviousTargeters => this.Previous; @@ -63,7 +63,7 @@ namespace PeepingTom { // get targeters and set a copy so we can release the mutex faster var newCurrent = this.GetTargeting(this.Plugin.ObjectTable, player); - foreach (var newTargeter in newCurrent.Where(t => this.Current.All(c => c.ObjectId != t.ObjectId))) { + foreach (var newTargeter in newCurrent.Where(t => this.Current.All(c => c.GameObjectId != t.GameObjectId))) { try { this.Plugin.IpcManager.SendNewTargeter(newTargeter); } catch (Exception ex) { @@ -71,7 +71,7 @@ namespace PeepingTom { } } - foreach (var stopped in this.Current.Where(t => newCurrent.All(c => c.ObjectId != t.ObjectId))) { + foreach (var stopped in this.Current.Where(t => newCurrent.All(c => c.GameObjectId != t.GameObjectId))) { try { this.Plugin.IpcManager.SendStoppedTargeting(stopped); } catch (Exception ex) { @@ -99,42 +99,42 @@ namespace PeepingTom { foreach (var targeter in targeting) { // add the targeter to the previous list - if (this.Previous.Any(old => old.ObjectId == targeter.ObjectId)) { - this.Previous.RemoveAll(old => old.ObjectId == targeter.ObjectId); + if (this.Previous.Any(old => old.GameObjectId == targeter.GameObjectId)) { + this.Previous.RemoveAll(old => old.GameObjectId == targeter.GameObjectId); } this.Previous.Insert(0, targeter); } // only keep the configured number of previous targeters (ignoring ones that are currently targeting) - while (this.Previous.Count(old => targeting.All(actor => actor.ObjectId != old.ObjectId)) > this.Plugin.Config.NumHistory) { + while (this.Previous.Count(old => targeting.All(actor => actor.GameObjectId != old.GameObjectId)) > this.Plugin.Config.NumHistory) { this.Previous.RemoveAt(this.Previous.Count - 1); } } - private Targeter[] GetTargeting(IEnumerable objects, GameObject player) { + private Targeter[] GetTargeting(IEnumerable objects, IGameObject player) { return objects - .Where(obj => obj.TargetObjectId == player.ObjectId && obj is PlayerCharacter) + .Where(obj => obj.TargetObjectId == player.GameObjectId && obj is IPlayerCharacter) // .Where(obj => Marshal.ReadByte(obj.Address + ActorOffsets.PlayerCharacterTargetActorId + 4) == 0) - .Cast() + .Cast() .Where(actor => this.Plugin.Config.LogParty || !InParty(actor)) .Where(actor => this.Plugin.Config.LogAlliance || !InAlliance(actor)) .Where(actor => this.Plugin.Config.LogInCombat || !InCombat(actor)) - .Where(actor => this.Plugin.Config.LogSelf || actor.ObjectId != player.ObjectId) + .Where(actor => this.Plugin.Config.LogSelf || actor.GameObjectId != player.GameObjectId) .Select(actor => new Targeter(actor)) .ToArray(); } - private static byte GetStatus(GameObject actor) { + private static byte GetStatus(IGameObject actor) { var statusPtr = actor.Address + 0x1980; // updated 5.4 return Marshal.ReadByte(statusPtr); } - private static bool InCombat(GameObject actor) => (GetStatus(actor) & 2) > 0; + private static bool InCombat(IGameObject actor) => (GetStatus(actor) & 2) > 0; - private static bool InParty(GameObject actor) => (GetStatus(actor) & 16) > 0; + private static bool InParty(IGameObject actor) => (GetStatus(actor) & 16) > 0; - private static bool InAlliance(GameObject actor) => (GetStatus(actor) & 32) > 0; + private static bool InAlliance(IGameObject actor) => (GetStatus(actor) & 32) > 0; private bool CanPlaySound() { if (!this.Plugin.Config.PlaySoundOnTarget) { @@ -178,10 +178,9 @@ namespace PeepingTom { return; } - using var channel = new WaveChannel32(reader) { - Volume = this.Plugin.Config.SoundVolume, - PadWithZeroes = false, - }; + using var channel = new WaveChannel32(reader); + channel.Volume = this.Plugin.Config.SoundVolume; + channel.PadWithZeroes = false; using (reader) { using var output = new DirectSoundOut(soundDevice.Guid); diff --git a/Peeping Tom/packages.lock.json b/Peeping Tom/packages.lock.json old mode 100644 new mode 100755 index aef3ef7..61fe157 --- a/Peeping Tom/packages.lock.json +++ b/Peeping Tom/packages.lock.json @@ -4,15 +4,15 @@ "net8.0-windows7.0": { "DalamudPackager": { "type": "Direct", - "requested": "[2.1.12, )", - "resolved": "2.1.12", - "contentHash": "Sc0PVxvgg4NQjcI8n10/VfUQBAS4O+Fw2pZrAqBdRMbthYGeogzu5+xmIGCGmsEZ/ukMOBuAqiNiB5qA3MRalg==" + "requested": "[2.1.13, )", + "resolved": "2.1.13", + "contentHash": "rMN1omGe8536f4xLMvx9NwfvpAc9YFFfeXJ1t4P4PE6Gu8WCIoFliR1sh07hM+bfODmesk/dvMbji7vNI+B/pQ==" }, "Fody": { "type": "Direct", - "requested": "[6.8.0, )", - "resolved": "6.8.0", - "contentHash": "hfZ/f8Mezt8aTkgv9nsvFdYoQ809/AqwsJlOGOPYIfBcG2aAIG3v3ex9d8ZqQuFYyMoucjRg4eKy3VleeGodKQ==" + "requested": "[6.8.1, )", + "resolved": "6.8.1", + "contentHash": "pwk2/No1kL1ft+zMT2y0MvzB6W5cpkdOTj+0+T2u7LGJMTw37qtlnHzCSCyvwRiZVoJ/V/DE78eQ/WEcutUVDw==" }, "NAudio.Core": { "type": "Direct", @@ -38,12 +38,6 @@ "Fody": "6.6.4" } }, - "XivCommon": { - "type": "Direct", - "requested": "[9.0.0, )", - "resolved": "9.0.0", - "contentHash": "avaBp3FmSCi/PiQhntCeBDYOHejdyTWmFtz4pRBVQQ8vHkmRx+YTk1la9dkYBMlXxRXKckEdH1iI1Fu61JlE7w==" - }, "PeepingTom.Ipc": { "type": "Project" }