From 3a29603def9ce1b6a6c188edacd25d36679f2b48 Mon Sep 17 00:00:00 2001 From: Raymond Date: Tue, 10 Aug 2021 18:15:33 -0400 Subject: [PATCH] Actors and objects --- Dalamud/Game/ClientState/Actors/ActorTable.cs | 149 ----------- Dalamud/Game/ClientState/Actors/Targets.cs | 125 ---------- .../Game/ClientState/Actors/Types/Actor.cs | 165 ------------- .../ClientState/Actors/Types/ActorOffsets.cs | 61 ----- .../Game/ClientState/Actors/Types/Chara.cs | 124 ---------- .../Actors/Types/NonPlayer/EventObj.cs | 26 -- .../ClientState/Actors/Types/PartyMember.cs | 54 ---- Dalamud/Game/ClientState/Buddy/BuddyList.cs | 188 ++++++++++++++ Dalamud/Game/ClientState/Buddy/BuddyMember.cs | 78 ++++++ Dalamud/Game/ClientState/ClientState.cs | 22 +- .../ClientState/ClientStateAddressResolver.cs | 29 ++- .../Enums}/BattleNpcSubKind.cs | 2 +- .../Types => Objects/Enums}/CustomizeIndex.cs | 2 +- .../Types => Objects/Enums}/ObjectKind.cs | 4 +- .../Types => Objects/Enums}/StatusFlags.cs | 18 +- .../Game/ClientState/Objects/ObjectTable.cs | 146 +++++++++++ .../SubKinds}/BattleNpc.cs | 13 +- .../ClientState/Objects/SubKinds/EventObj.cs | 23 ++ .../NonPlayer => Objects/SubKinds}/Npc.cs | 13 +- .../SubKinds}/PlayerCharacter.cs | 18 +- Dalamud/Game/ClientState/Objects/Targets.cs | 169 +++++++++++++ .../ClientState/Objects/Types/BattleChara.cs | 68 ++++++ .../ClientState/Objects/Types/Character.cs | 103 ++++++++ .../ClientState/Objects/Types/GameObject.cs | 168 +++++++++++++ Dalamud/Game/ClientState/Party/PartyList.cs | 183 ++++++++++++++ Dalamud/Game/ClientState/Party/PartyMember.cs | 117 +++++++++ Dalamud/Game/ClientState/PartyList.cs | 139 ----------- Dalamud/Game/ClientState/Statuses/Status.cs | 73 ++++++ .../Game/ClientState/Statuses/StatusList.cs | 161 ++++++++++++ .../Game/ClientState/Structs/PartyMember.cs | 26 -- .../Interface/Internal/Windows/DataWindow.cs | 231 ++++++++++++------ Dalamud/Interface/UiBuilder.cs | 1 - 32 files changed, 1700 insertions(+), 999 deletions(-) delete mode 100644 Dalamud/Game/ClientState/Actors/ActorTable.cs delete mode 100644 Dalamud/Game/ClientState/Actors/Targets.cs delete mode 100644 Dalamud/Game/ClientState/Actors/Types/Actor.cs delete mode 100644 Dalamud/Game/ClientState/Actors/Types/ActorOffsets.cs delete mode 100644 Dalamud/Game/ClientState/Actors/Types/Chara.cs delete mode 100644 Dalamud/Game/ClientState/Actors/Types/NonPlayer/EventObj.cs delete mode 100644 Dalamud/Game/ClientState/Actors/Types/PartyMember.cs create mode 100644 Dalamud/Game/ClientState/Buddy/BuddyList.cs create mode 100644 Dalamud/Game/ClientState/Buddy/BuddyMember.cs rename Dalamud/Game/ClientState/{Actors/Types/NonPlayer => Objects/Enums}/BattleNpcSubKind.cs (88%) rename Dalamud/Game/ClientState/{Actors/Types => Objects/Enums}/CustomizeIndex.cs (98%) rename Dalamud/Game/ClientState/{Actors/Types => Objects/Enums}/ObjectKind.cs (95%) rename Dalamud/Game/ClientState/{Actors/Types => Objects/Enums}/StatusFlags.cs (70%) create mode 100644 Dalamud/Game/ClientState/Objects/ObjectTable.cs rename Dalamud/Game/ClientState/{Actors/Types/NonPlayer => Objects/SubKinds}/BattleNpc.cs (64%) create mode 100644 Dalamud/Game/ClientState/Objects/SubKinds/EventObj.cs rename Dalamud/Game/ClientState/{Actors/Types/NonPlayer => Objects/SubKinds}/Npc.cs (65%) rename Dalamud/Game/ClientState/{Actors/Types => Objects/SubKinds}/PlayerCharacter.cs (63%) create mode 100644 Dalamud/Game/ClientState/Objects/Targets.cs create mode 100644 Dalamud/Game/ClientState/Objects/Types/BattleChara.cs create mode 100644 Dalamud/Game/ClientState/Objects/Types/Character.cs create mode 100644 Dalamud/Game/ClientState/Objects/Types/GameObject.cs create mode 100644 Dalamud/Game/ClientState/Party/PartyList.cs create mode 100644 Dalamud/Game/ClientState/Party/PartyMember.cs delete mode 100644 Dalamud/Game/ClientState/PartyList.cs create mode 100644 Dalamud/Game/ClientState/Statuses/Status.cs create mode 100644 Dalamud/Game/ClientState/Statuses/StatusList.cs delete mode 100644 Dalamud/Game/ClientState/Structs/PartyMember.cs diff --git a/Dalamud/Game/ClientState/Actors/ActorTable.cs b/Dalamud/Game/ClientState/Actors/ActorTable.cs deleted file mode 100644 index a139f8b48..000000000 --- a/Dalamud/Game/ClientState/Actors/ActorTable.cs +++ /dev/null @@ -1,149 +0,0 @@ -using System; -using System.Collections; -using System.Collections.Generic; - -using Dalamud.Game.ClientState.Actors.Types; -using Dalamud.Game.ClientState.Actors.Types.NonPlayer; -using JetBrains.Annotations; -using Serilog; - -namespace Dalamud.Game.ClientState.Actors -{ - /// - /// This collection represents the currently spawned FFXIV actors. - /// - public sealed partial class ActorTable - { - private const int ActorTableLength = 424; - - private readonly Dalamud dalamud; - private readonly ClientStateAddressResolver address; - - /// - /// Initializes a new instance of the class. - /// - /// The instance. - /// Client state address resolver. - internal ActorTable(Dalamud dalamud, ClientStateAddressResolver addressResolver) - { - this.dalamud = dalamud; - this.address = addressResolver; - - Log.Verbose($"Actor table address 0x{this.address.ActorTable.ToInt64():X}"); - } - - /// - /// Gets the amount of currently spawned actors. - /// - public int Length - { - get - { - var count = 0; - for (var i = 0; i < ActorTableLength; i++) - { - var ptr = this.GetActorAddress(i); - if (ptr != IntPtr.Zero) - { - count++; - } - } - - return count; - } - } - - /// - /// Get an actor at the specified spawn index. - /// - /// Spawn index. - /// An at the specified spawn index. - [CanBeNull] - public Actor this[int index] - { - get - { - var address = this.GetActorAddress(index); - return this.CreateActorReference(address); - } - } - - /// - /// Gets the address of the actor at the specified index of the actor table. - /// - /// The index of the actor. - /// The memory address of the actor. - public unsafe IntPtr GetActorAddress(int index) - { - if (index >= ActorTableLength) - return IntPtr.Zero; - - return *(IntPtr*)(this.address.ActorTable + (8 * index)); - } - - /// - /// Create a reference to a FFXIV actor. - /// - /// The address of the actor in memory. - /// object or inheritor containing requested data. - [CanBeNull] - public unsafe Actor CreateActorReference(IntPtr address) - { - if (this.dalamud.ClientState.LocalContentId == 0) - return null; - - if (address == IntPtr.Zero) - return null; - - var objKind = *(ObjectKind*)(address + ActorOffsets.ObjectKind); - return objKind switch - { - ObjectKind.Player => new PlayerCharacter(address, this.dalamud), - ObjectKind.BattleNpc => new BattleNpc(address, this.dalamud), - ObjectKind.EventObj => new EventObj(address, this.dalamud), - ObjectKind.Companion => new Npc(address, this.dalamud), - _ => new Actor(address, this.dalamud), - }; - } - } - - /// - /// This collection represents the currently spawned FFXIV actors. - /// - public sealed partial class ActorTable : IReadOnlyCollection, ICollection - { - /// - int IReadOnlyCollection.Count => this.Length; - - /// - int ICollection.Count => this.Length; - - /// - bool ICollection.IsSynchronized => false; - - /// - object ICollection.SyncRoot => this; - - /// - public IEnumerator GetEnumerator() - { - for (var i = 0; i < ActorTableLength; i++) - { - yield return this[i]; - } - } - - /// - IEnumerator IEnumerable.GetEnumerator() => this.GetEnumerator(); - - /// - void ICollection.CopyTo(Array array, int index) - { - for (var i = 0; i < this.Length; i++) - { - array.SetValue(this[i], index); - index++; - } - } - } -} diff --git a/Dalamud/Game/ClientState/Actors/Targets.cs b/Dalamud/Game/ClientState/Actors/Targets.cs deleted file mode 100644 index 154aa446d..000000000 --- a/Dalamud/Game/ClientState/Actors/Targets.cs +++ /dev/null @@ -1,125 +0,0 @@ -using System; -using System.Runtime.InteropServices; - -using Dalamud.Game.ClientState.Actors.Types; -using JetBrains.Annotations; - -namespace Dalamud.Game.ClientState.Actors -{ - /// - /// Get and set various kinds of targets for the player. - /// - public sealed class Targets - { - private readonly Dalamud dalamud; - private readonly ClientStateAddressResolver address; - - /// - /// Initializes a new instance of the class. - /// - /// The Dalamud instance. - /// The ClientStateAddressResolver instance. - internal Targets(Dalamud dalamud, ClientStateAddressResolver addressResolver) - { - this.dalamud = dalamud; - this.address = addressResolver; - } - - /// - /// Gets the current target. - /// - [CanBeNull] - public Actor CurrentTarget => this.GetActorByOffset(TargetOffsets.CurrentTarget); - - /// - /// Gets the mouseover target. - /// - [CanBeNull] - public Actor MouseOverTarget => this.GetActorByOffset(TargetOffsets.MouseOverTarget); - - /// - /// Gets the focus target. - /// - [CanBeNull] - public Actor FocusTarget => this.GetActorByOffset(TargetOffsets.FocusTarget); - - /// - /// Gets the previous target. - /// - [CanBeNull] - public Actor PreviousTarget => this.GetActorByOffset(TargetOffsets.PreviousTarget); - - /// - /// Gets the soft target. - /// - [CanBeNull] - public Actor SoftTarget => this.GetActorByOffset(TargetOffsets.SoftTarget); - - /// - /// Sets the current target. - /// - /// Actor to target. - public void SetCurrentTarget(Actor actor) => this.SetTarget(actor?.Address ?? IntPtr.Zero, TargetOffsets.CurrentTarget); - - /// - /// Sets the current target. - /// - /// Actor (address) to target. - public void SetCurrentTarget(IntPtr actorAddress) => this.SetTarget(actorAddress, TargetOffsets.CurrentTarget); - - /// - /// Sets the focus target. - /// - /// Actor to focus. - public void SetFocusTarget(Actor actor) => this.SetTarget(actor?.Address ?? IntPtr.Zero, TargetOffsets.FocusTarget); - - /// - /// Sets the focus target. - /// - /// Actor (address) to focus. - public void SetFocusTarget(IntPtr actorAddress) => this.SetTarget(actorAddress, TargetOffsets.FocusTarget); - - /// - /// Clears the current target. - /// - public void ClearCurrentTarget() => this.SetCurrentTarget(IntPtr.Zero); - - /// - /// Clears the focus target. - /// - public void ClearFocusTarget() => this.SetFocusTarget(IntPtr.Zero); - - private void SetTarget(IntPtr actorAddress, int offset) - { - if (this.address.TargetManager == IntPtr.Zero) - return; - - Marshal.WriteIntPtr(this.address.TargetManager, offset, actorAddress); - } - - [CanBeNull] - private Actor GetActorByOffset(int offset) - { - if (this.address.TargetManager == IntPtr.Zero) - return null; - - var actorAddress = Marshal.ReadIntPtr(this.address.TargetManager + offset); - if (actorAddress == IntPtr.Zero) - return null; - - return this.dalamud.ClientState.Actors.CreateActorReference(actorAddress); - } - } - - /// - /// Memory offsets for the type. - /// - public static class TargetOffsets - { - public const int CurrentTarget = 0x80; - public const int SoftTarget = 0x88; - public const int MouseOverTarget = 0xD0; - public const int FocusTarget = 0xF8; - public const int PreviousTarget = 0x110; - } -} diff --git a/Dalamud/Game/ClientState/Actors/Types/Actor.cs b/Dalamud/Game/ClientState/Actors/Types/Actor.cs deleted file mode 100644 index f98d8932e..000000000 --- a/Dalamud/Game/ClientState/Actors/Types/Actor.cs +++ /dev/null @@ -1,165 +0,0 @@ -using System; -using System.Runtime.InteropServices; - -using Dalamud.Game.ClientState.Structs; -using Dalamud.Game.Text.SeStringHandling; -using Dalamud.Memory; - -namespace Dalamud.Game.ClientState.Actors.Types -{ - /// - /// This class represents a basic actor (GameObject) in FFXIV. - /// - public unsafe partial class Actor : IEquatable - { - /// - /// Initializes a new instance of the class. - /// - /// The address of this actor in memory. - /// A dalamud reference needed to access game data in Resolvers. - internal Actor(IntPtr address, Dalamud dalamud) - { - this.Dalamud = dalamud; - this.Address = address; - } - - /// - /// Gets the address of the actor in memory. - /// - public IntPtr Address { get; } - - /// - /// Gets Dalamud itself. - /// - private protected Dalamud Dalamud { get; } - - /// - /// This allows you to if (actor) {...} to check for validity. - /// - /// The actor to check. - /// True or false. - public static implicit operator bool(Actor actor) => IsValid(actor); - - public static bool operator ==(Actor actor1, Actor actor2) - { - if (actor1 is null || actor2 is null) - return Equals(actor1, actor2); - - return actor1.Equals(actor2); - } - - public static bool operator !=(Actor actor1, Actor actor2) => !(actor1 == actor2); - - /// - /// Gets a value indicating whether this actor is still valid in memory. - /// - /// The actor to check. - /// True or false. - public static bool IsValid(Actor actor) - { - if (actor == null) - return false; - - if (actor.Dalamud.ClientState.LocalContentId == 0) - return false; - - return true; - } - - /// - /// Gets a value indicating whether this actor is still valid in memory. - /// - /// True or false. - public bool IsValid() => IsValid(this); - - /// - bool IEquatable.Equals(Actor other) => this.ActorId == other?.ActorId; - - /// - public override bool Equals(object obj) => ((IEquatable)this).Equals(obj as Actor); - - /// - public override int GetHashCode() => this.ActorId.GetHashCode(); - } - - /// - /// This class represents a basic actor (GameObject) in FFXIV. - /// - public unsafe partial class Actor - { - /// - /// Gets the displayname of this . - /// - public SeString Name => MemoryHelper.ReadSeString(this.Address + ActorOffsets.Name, 32); - - /// - /// Gets the actor ID of this . - /// - public uint ActorId => *(uint*)(this.Address + ActorOffsets.ActorId); - - /// - /// Gets the data ID for linking to other respective game data. - /// - public uint DataId => *(uint*)(this.Address + ActorOffsets.DataId); - - /// - /// Gets the ID of this GameObject's owner. - /// - public uint OwnerId => *(uint*)(this.Address + ActorOffsets.OwnerId); - - /// - /// Gets the entity kind of this . - /// See the ObjectKind enum for possible values. - /// - public ObjectKind ObjectKind => *(ObjectKind*)(this.Address + ActorOffsets.ObjectKind); - - /// - /// Gets the sub kind of this Actor. - /// - public byte SubKind => *(byte*)(this.Address + ActorOffsets.SubKind); - - /// - /// Gets a value indicating whether the actor is friendly. - /// - public bool IsFriendly => *(int*)(this.Address + ActorOffsets.IsFriendly) > 0; - - /// - /// Gets the X distance from the local player in yalms. - /// - public byte YalmDistanceX => *(byte*)(this.Address + ActorOffsets.YalmDistanceFromObjectX); - - /// - /// Gets the target status. - /// - /// - /// This is some kind of enum. It may be . - /// - public byte TargetStatus => *(byte*)(this.Address + ActorOffsets.TargetStatus); - - /// - /// Gets the Y distance from the local player in yalms. - /// - public byte YalmDistanceY => *(byte*)(this.Address + ActorOffsets.YalmDistanceFromObjectY); - - /// - /// Gets the position of this . - /// - public Position3 Position => *(Position3*)(this.Address + ActorOffsets.Position); - - /// - /// Gets the rotation of this . - /// This ranges from -pi to pi radians. - /// - public float Rotation => *(float*)(this.Address + ActorOffsets.Rotation); - - /// - /// Gets the hitbox radius of this . - /// - public float HitboxRadius => *(float*)(this.Address + ActorOffsets.HitboxRadius); - - /// - /// Gets the current target of the Actor. - /// - public virtual uint TargetActorID => 0; - } -} diff --git a/Dalamud/Game/ClientState/Actors/Types/ActorOffsets.cs b/Dalamud/Game/ClientState/Actors/Types/ActorOffsets.cs deleted file mode 100644 index e3ef26a5c..000000000 --- a/Dalamud/Game/ClientState/Actors/Types/ActorOffsets.cs +++ /dev/null @@ -1,61 +0,0 @@ -using System.Diagnostics.CodeAnalysis; - -namespace Dalamud.Game.ClientState.Actors.Types -{ - /// - /// Memory offsets for the type and all that inherit from it. - /// - [SuppressMessage("StyleCop.CSharp.DocumentationRules", "SA1600:Elements should be documented", Justification = "Document the offset usage instead.")] - public static class ActorOffsets - { - // GameObject(Actor) - // GameObject :: Character - // GameObject :: Character :: BattleChara - // GameObject :: Character :: Companion - - public const int Name = 0x30; - public const int ActorId = 0x74; - public const int DataId = 0x80; - public const int OwnerId = 0x84; - public const int ObjectKind = 0x8C; - public const int SubKind = 0x8D; - public const int IsFriendly = 0x8E; - public const int YalmDistanceFromObjectX = 0x90; - public const int TargetStatus = 0x91; - public const int YalmDistanceFromObjectY = 0x92; - public const int Position = 0xA0; - public const int Rotation = 0xB0; - public const int HitboxRadius = 0xC0; - // End GameObject 0x1A0 - - public const int CurrentHp = 0x1C4; - public const int MaxHp = 0x1C8; - public const int CurrentMp = 0x1CC; - public const int MaxMp = 0x1D0; - public const int CurrentGp = 0x1D4; - public const int MaxGp = 0x1D6; - public const int CurrentCp = 0x1D8; - public const int MaxCp = 0x1DA; - public const int ClassJob = 0x1E2; - public const int Level = 0x1E3; - public const int PlayerCharacterTargetActorId = 0x230; - public const int Customize = 0x1898; - public const int CompanyTag = 0x18B2; - public const int BattleNpcTargetActorId = 0x18D8; - public const int NameId = 0x1940; - public const int CurrentWorld = 0x195C; - public const int HomeWorld = 0x195E; - public const int StatusFlags = 0x19A0; - // End Character 0x19B0 - // End Companion 0x19C0 - - public const int UIStatusEffects = 0x19F8; - public const int IsCasting = 0x1B80; - public const int IsCasting2 = 0x1B82; - public const int CurrentCastSpellActionId = 0x1B84; - public const int CurrentCastTargetActorId = 0x1B90; - public const int CurrentCastTime = 0x1BB4; - public const int TotalCastTime = 0x1BB8; - // End BattleChara 0x2C00 - } -} diff --git a/Dalamud/Game/ClientState/Actors/Types/Chara.cs b/Dalamud/Game/ClientState/Actors/Types/Chara.cs deleted file mode 100644 index 5c8dca228..000000000 --- a/Dalamud/Game/ClientState/Actors/Types/Chara.cs +++ /dev/null @@ -1,124 +0,0 @@ -using System; - -using Dalamud.Game.ClientState.Resolvers; -using Dalamud.Game.ClientState.Structs; -using Dalamud.Memory; - -namespace Dalamud.Game.ClientState.Actors.Types -{ - /// - /// This class represents the base for non-static entities. - /// - public unsafe class Chara : Actor - { - /// - /// Initializes a new instance of the class. - /// This represents a non-static entity. - /// - /// The address of this actor in memory. - /// A dalamud reference needed to access game data in Resolvers. - internal Chara(IntPtr address, Dalamud dalamud) - : base(address, dalamud) - { - } - - /// - /// Gets the current HP of this Chara. - /// - public uint CurrentHp => *(uint*)(this.Address + ActorOffsets.CurrentHp); - - /// - /// Gets the maximum HP of this Chara. - /// - public uint MaxHp => *(uint*)(this.Address + ActorOffsets.MaxHp); - - /// - /// Gets the current MP of this Chara. - /// - public uint CurrentMp => *(uint*)(this.Address + ActorOffsets.CurrentMp); - - /// - /// Gets the maximum MP of this Chara. - /// - public uint MaxMp => *(uint*)(this.Address + ActorOffsets.MaxMp); - - /// - /// Gets the current GP of this Chara. - /// - public uint CurrentGp => *(uint*)(this.Address + ActorOffsets.CurrentGp); - - /// - /// Gets the maximum GP of this Chara. - /// - public uint MaxGp => *(uint*)(this.Address + ActorOffsets.MaxGp); - - /// - /// Gets the current CP of this Chara. - /// - public uint CurrentCp => *(uint*)(this.Address + ActorOffsets.CurrentCp); - - /// - /// Gets the maximum CP of this Chara. - /// - public uint MaxCp => *(uint*)(this.Address + ActorOffsets.MaxCp); - - /// - /// Gets the ClassJob of this Chara. - /// - public ExcelResolver ClassJob => new(*(byte*)(this.Address + ActorOffsets.ClassJob), this.Dalamud); - - /// - /// Gets the level of this Chara. - /// - public byte Level => *(byte*)(this.Address + ActorOffsets.Level); - - /// - /// Gets a byte array describing the visual appearance of this Chara. - /// Indexed by . - /// - public byte[] Customize => MemoryHelper.Read(this.Address + ActorOffsets.Customize, 28); - - /// - /// Gets the status flags. - /// - public StatusFlags StatusFlags => *(StatusFlags*)(this.Address + ActorOffsets.StatusFlags); - - /// - /// Gets the current status effects. - /// - /// - /// This copies every time it is invoked, so make sure to only grab it once. - /// - public StatusEffect[] StatusEffects => MemoryHelper.Read(this.Address + ActorOffsets.UIStatusEffects, 30, true); - - /// - /// Gets a value indicating whether the actor is currently casting. - /// - public bool IsCasting => *(int*)(this.Address + ActorOffsets.IsCasting) > 0; - - /// - /// Gets a value indicating whether the actor is currently casting (again?). - /// - public bool IsCasting2 => *(int*)(this.Address + ActorOffsets.IsCasting2) > 0; - - /// - /// Gets the spell action ID currently being cast by the actor. - /// - public uint CurrentCastSpellActionId => *(uint*)(this.Address + ActorOffsets.CurrentCastSpellActionId); - - /// - /// Gets the actor ID of the target currently being cast at by the actor. - /// - public uint CurrentCastTargetActorId => *(uint*)(this.Address + ActorOffsets.CurrentCastTargetActorId); - - /// - /// Gets the current casting time of the spell being cast by the actor. - /// - public float CurrentCastTime => *(float*)(this.Address + ActorOffsets.CurrentCastTime); - - /// - /// Gets the total casting time of the spell being cast by the actor. - /// - public float TotalCastTime => *(float*)(this.Address + ActorOffsets.TotalCastTime); - } -} diff --git a/Dalamud/Game/ClientState/Actors/Types/NonPlayer/EventObj.cs b/Dalamud/Game/ClientState/Actors/Types/NonPlayer/EventObj.cs deleted file mode 100644 index 7d9dce443..000000000 --- a/Dalamud/Game/ClientState/Actors/Types/NonPlayer/EventObj.cs +++ /dev/null @@ -1,26 +0,0 @@ -using System; - -namespace Dalamud.Game.ClientState.Actors.Types.NonPlayer -{ - /// - /// This class represents an EventObj. - /// - public unsafe class EventObj : Actor - { - /// - /// Initializes a new instance of the class. - /// Set up a new EventObj with the provided memory representation. - /// - /// The address of this actor in memory. - /// A dalamud reference needed to access game data in Resolvers. - internal EventObj(IntPtr address, Dalamud dalamud) - : base(address, dalamud) - { - } - - /// - /// Gets the event object ID of the linking to their respective game data. - /// - public uint EventObjectId => *(uint*)(this.Address + ActorOffsets.DataId); - } -} diff --git a/Dalamud/Game/ClientState/Actors/Types/PartyMember.cs b/Dalamud/Game/ClientState/Actors/Types/PartyMember.cs deleted file mode 100644 index 2e2d67f54..000000000 --- a/Dalamud/Game/ClientState/Actors/Types/PartyMember.cs +++ /dev/null @@ -1,54 +0,0 @@ -using Dalamud.Game.Text.SeStringHandling; -using Dalamud.Memory; - -namespace Dalamud.Game.ClientState.Actors.Types -{ - /// - /// This class represents a party member. - /// - public class PartyMember - { - /// - /// Initializes a new instance of the class. - /// - /// The ActorTable instance. - /// The interop data struct. - public PartyMember(ActorTable table, Structs.PartyMember rawData) - { - this.CharacterName = MemoryHelper.ReadSeString(rawData.namePtr); - this.Unknown = rawData.unknown; - this.Actor = null; - - for (var i = 0; i < table.Length; i++) - { - if (table[i] != null && table[i].ActorId == rawData.actorId) - { - this.Actor = table[i]; - break; - } - } - - this.ObjectKind = rawData.objectKind; - } - - /// - /// Gets the name of the character. - /// - public SeString CharacterName { get; } - - /// - /// Gets something unknown. - /// - public long Unknown { get; } - - /// - /// Gets the actor object that corresponds to this party member. - /// - public Actor Actor { get; } - - /// - /// Gets the kind or type of actor. - /// - public ObjectKind ObjectKind { get; } - } -} diff --git a/Dalamud/Game/ClientState/Buddy/BuddyList.cs b/Dalamud/Game/ClientState/Buddy/BuddyList.cs new file mode 100644 index 000000000..3080283a5 --- /dev/null +++ b/Dalamud/Game/ClientState/Buddy/BuddyList.cs @@ -0,0 +1,188 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using System.Runtime.InteropServices; + +using JetBrains.Annotations; +using Serilog; + +namespace Dalamud.Game.ClientState.Buddy +{ + /// + /// This collection represents the buddies present in your squadron or trust party. + /// It does not include the local player. + /// + public sealed partial class BuddyList + { + private const uint InvalidObjectID = 0xE0000000; + + private readonly Dalamud dalamud; + private readonly ClientStateAddressResolver address; + + /// + /// Initializes a new instance of the class. + /// + /// The instance. + /// Client state address resolver. + internal BuddyList(Dalamud dalamud, ClientStateAddressResolver addressResolver) + { + this.dalamud = dalamud; + this.address = addressResolver; + + Log.Verbose($"Buddy list address 0x{this.address.BuddyList.ToInt64():X}"); + } + + /// + /// Gets the amount of battle buddies the local player has. + /// + public int Length + { + get + { + var i = 0; + for (; i < 3; i++) + { + var addr = this.GetBattleBuddyMemberAddress(i); + var member = this.CreateBuddyMemberReference(addr); + if (member == null) + break; + } + + return i; + } + } + + /// + /// Gets a value indicating whether the local player's companion is present. + /// + public bool CompanionBuddyPresent => this.CompanionBuddy != null; + + /// + /// Gets a value indicating whether the local player's pet is present. + /// + public bool PetBuddyPresent => this.PetBuddy != null; + + /// + /// Gets the active companion buddy. + /// + [CanBeNull] + public BuddyMember CompanionBuddy + { + get + { + var addr = this.GetCompanionBuddyMemberAddress(); + return this.CreateBuddyMemberReference(addr); + } + } + + /// + /// Gets the active pet buddy. + /// + [CanBeNull] + public BuddyMember PetBuddy + { + get + { + var addr = this.GetPetBuddyMemberAddress(); + return this.CreateBuddyMemberReference(addr); + } + } + + /// + /// Gets the address of the buddy list. + /// + internal IntPtr BuddyListAddress => this.address.BuddyList; + + private static int BuddyMemberSize { get; } = Marshal.SizeOf(); + + private unsafe FFXIVClientStructs.FFXIV.Client.Game.UI.Buddy* BuddyListStruct => (FFXIVClientStructs.FFXIV.Client.Game.UI.Buddy*)this.BuddyListAddress; + + /// + /// Gets a battle buddy at the specified spawn index. + /// + /// Spawn index. + /// A at the specified spawn index. + [CanBeNull] + public BuddyMember this[int index] + { + get + { + var address = this.GetBattleBuddyMemberAddress(index); + return this.CreateBuddyMemberReference(address); + } + } + + /// + /// Gets the address of the companion buddy. + /// + /// The memory address of the companion buddy. + public unsafe IntPtr GetCompanionBuddyMemberAddress() + { + return (IntPtr)(&this.BuddyListStruct->Companion); + } + + /// + /// Gets the address of the pet buddy. + /// + /// The memory address of the pet buddy. + public unsafe IntPtr GetPetBuddyMemberAddress() + { + return (IntPtr)(&this.BuddyListStruct->Pet); + } + + /// + /// Gets the address of the battle buddy at the specified index of the buddy list. + /// + /// The index of the battle buddy. + /// The memory address of the battle buddy. + public unsafe IntPtr GetBattleBuddyMemberAddress(int index) + { + if (index < 0 || index >= 3) + return IntPtr.Zero; + + return (IntPtr)(this.BuddyListStruct->BattleBuddies + (index * BuddyMemberSize)); + } + + /// + /// Create a reference to a buddy. + /// + /// The address of the buddy in memory. + /// object containing the requested data. + [CanBeNull] + public BuddyMember CreateBuddyMemberReference(IntPtr address) + { + if (this.dalamud.ClientState.LocalContentId == 0) + return null; + + if (address == IntPtr.Zero) + return null; + + var buddy = new BuddyMember(address, this.dalamud); + if (buddy.ObjectId == InvalidObjectID) + return null; + + return buddy; + } + } + + /// + /// This collection represents the buddies present in your squadron or trust party. + /// + public sealed partial class BuddyList : IReadOnlyCollection + { + /// + int IReadOnlyCollection.Count => this.Length; + + /// + public IEnumerator GetEnumerator() + { + for (var i = 0; i < this.Length; i++) + { + yield return this[i]; + } + } + + /// + IEnumerator IEnumerable.GetEnumerator() => this.GetEnumerator(); + } +} diff --git a/Dalamud/Game/ClientState/Buddy/BuddyMember.cs b/Dalamud/Game/ClientState/Buddy/BuddyMember.cs new file mode 100644 index 000000000..1cb40f39f --- /dev/null +++ b/Dalamud/Game/ClientState/Buddy/BuddyMember.cs @@ -0,0 +1,78 @@ +using System; + +using Dalamud.Game.ClientState.Objects.Types; +using Dalamud.Game.ClientState.Resolvers; +using JetBrains.Annotations; + +namespace Dalamud.Game.ClientState.Buddy +{ + /// + /// This class represents a buddy such as the chocobo companion, summoned pets, squadron groups and trust parties. + /// + public unsafe class BuddyMember + { + private Dalamud dalamud; + + /// + /// Initializes a new instance of the class. + /// + /// Buddy address. + /// Dalamud instance. + internal BuddyMember(IntPtr address, Dalamud dalamud) + { + this.dalamud = dalamud; + this.Address = address; + } + + /// + /// Gets the address of the buddy in memory. + /// + public IntPtr Address { get; } + + /// + /// Gets the object ID of this buddy. + /// + public uint ObjectId => this.Struct->ObjectID; + + /// + /// Gets the actor associated with this buddy. + /// + /// + /// This iterates the actor table, it should be used with care. + /// + [CanBeNull] + public GameObject Actor => this.dalamud.ClientState.Objects.SearchByID(this.ObjectId); + + /// + /// Gets the current health of this buddy. + /// + public uint CurrentHP => this.Struct->CurrentHealth; + + /// + /// Gets the maximum health of this buddy. + /// + public uint MaxHP => this.Struct->MaxHealth; + + /// + /// Gets the data ID of this buddy. + /// + public uint DataID => this.Struct->DataID; + + /// + /// Gets the Mount data related to this buddy. It should only be used with companion buddies. + /// + public ExcelResolver MountData => new(this.DataID, this.dalamud); + + /// + /// Gets the Pet data related to this buddy. It should only be used with pet buddies. + /// + public ExcelResolver PetData => new(this.DataID, this.dalamud); + + /// + /// Gets the Trust data related to this buddy. It should only be used with battle buddies. + /// + public ExcelResolver TrustData => new(this.DataID, this.dalamud); + + private FFXIVClientStructs.FFXIV.Client.Game.UI.Buddy.BuddyMember* Struct => (FFXIVClientStructs.FFXIV.Client.Game.UI.Buddy.BuddyMember*)this.Address; + } +} diff --git a/Dalamud/Game/ClientState/ClientState.cs b/Dalamud/Game/ClientState/ClientState.cs index 2da9eddac..4a82b96d5 100644 --- a/Dalamud/Game/ClientState/ClientState.cs +++ b/Dalamud/Game/ClientState/ClientState.cs @@ -3,14 +3,15 @@ using System.ComponentModel; using System.Linq; using System.Runtime.InteropServices; -using Dalamud.Game.ClientState.Actors; -using Dalamud.Game.ClientState.Actors.Types; +using Dalamud.Game.ClientState.Buddy; using Dalamud.Game.ClientState.Conditions; using Dalamud.Game.ClientState.Fates; using Dalamud.Game.ClientState.GamePad; using Dalamud.Game.ClientState.JobGauge; using Dalamud.Game.ClientState.Keys; -using Dalamud.Game.Internal; +using Dalamud.Game.ClientState.Objects; +using Dalamud.Game.ClientState.Objects.SubKinds; +using Dalamud.Game.ClientState.Party; using Dalamud.Hooking; using JetBrains.Annotations; using Serilog; @@ -45,12 +46,14 @@ namespace Dalamud.Game.ClientState this.ClientLanguage = startInfo.Language; - this.Actors = new ActorTable(dalamud, this.address); + this.Objects = new ObjectTable(dalamud, this.address); this.Fates = new FateTable(dalamud, this.address); this.PartyList = new PartyList(dalamud, this.address); + this.BuddyList = new BuddyList(dalamud, this.address); + this.JobGauges = new JobGauges(this.address); this.KeyState = new KeyState(this.address, scanner.Module.BaseAddress); @@ -102,7 +105,7 @@ namespace Dalamud.Game.ClientState /// /// Gets the table of all present actors. /// - public ActorTable Actors { get; } + public ObjectTable Objects { get; } /// /// Gets the table of all present fates. @@ -124,6 +127,11 @@ namespace Dalamud.Game.ClientState /// public PartyList PartyList { get; } + /// + /// Gets the class facilitating buddy list data access. + /// + public BuddyList BuddyList { get; } + /// /// Gets access to the keypress state of keyboard keys in game. /// @@ -153,7 +161,7 @@ namespace Dalamud.Game.ClientState /// Gets the local player character, if one is present. /// [CanBeNull] - public PlayerCharacter LocalPlayer => this.Actors[0] as PlayerCharacter; + public PlayerCharacter LocalPlayer => this.Objects[0] as PlayerCharacter; /// /// Gets the content ID of the local character. @@ -171,7 +179,6 @@ namespace Dalamud.Game.ClientState public void Enable() { this.GamepadState.Enable(); - this.PartyList.Enable(); this.setupTerritoryTypeHook.Enable(); } @@ -180,7 +187,6 @@ namespace Dalamud.Game.ClientState /// public void Dispose() { - this.PartyList.Dispose(); this.setupTerritoryTypeHook.Dispose(); this.GamepadState.Dispose(); diff --git a/Dalamud/Game/ClientState/ClientStateAddressResolver.cs b/Dalamud/Game/ClientState/ClientStateAddressResolver.cs index 1b6a86454..334bf0198 100644 --- a/Dalamud/Game/ClientState/ClientStateAddressResolver.cs +++ b/Dalamud/Game/ClientState/ClientStateAddressResolver.cs @@ -1,7 +1,4 @@ using System; -using System.Runtime.InteropServices; - -using Dalamud.Game.Internal; namespace Dalamud.Game.ClientState { @@ -15,17 +12,25 @@ namespace Dalamud.Game.ClientState /// /// Gets the address of the actor table. /// - public IntPtr ActorTable { get; private set; } + public IntPtr ObjectTable { get; private set; } /// - /// Gets the address of the fate table pointer. + /// Gets the address of the buddy list. + /// + public IntPtr BuddyList { get; private set; } + + /// + /// Gets the address of a pointer to the fate table. /// /// /// This is a static address to a pointer, not the address of the table itself. /// public IntPtr FateTablePtr { get; private set; } - // public IntPtr ViewportActorTable { get; private set; } + /// + /// Gets the address of the Group Manager. + /// + public IntPtr GroupManager { get; private set; } /// /// Gets the address of the local content id. @@ -75,12 +80,16 @@ namespace Dalamud.Game.ClientState // ViewportActorTable = sig.GetStaticAddressFromSig("48 8D 0D ?? ?? ?? ?? 85 ED", 0) + 0x148; // SomeActorTableAccess = sig.ScanText("E8 ?? ?? ?? ?? 48 8D 55 A0 48 8D 8E ?? ?? ?? ??"); - this.ActorTable = sig.GetStaticAddressFromSig("48 8D 0D ?? ?? ?? ?? E8 ?? ?? ?? ?? 44 0F B6 83"); + this.ObjectTable = sig.GetStaticAddressFromSig("48 8D 0D ?? ?? ?? ?? E8 ?? ?? ?? ?? 44 0F B6 83"); + + this.BuddyList = sig.GetStaticAddressFromSig("48 8D 0D ?? ?? ?? ?? E8 ?? ?? ?? ?? 45 84 E4 75 1A F6 45 12 04"); this.FateTablePtr = sig.GetStaticAddressFromSig("48 8B 15 ?? ?? ?? ?? 48 8B F9 44 0F B7 41 ??"); + this.GroupManager = sig.GetStaticAddressFromSig("48 8D 0D ?? ?? ?? ?? E8 ?? ?? ?? ?? 80 B8 ?? ?? ?? ?? ?? 76 50"); + this.LocalContentId = sig.GetStaticAddressFromSig("48 0F 44 05 ?? ?? ?? ?? 48 39 07"); - this.JobGaugeData = sig.GetStaticAddressFromSig("48 8B 0D ?? ?? ?? ?? 48 85 C9 74 43") + 0x10; + this.JobGaugeData = sig.GetStaticAddressFromSig("48 8B 0D ?? ?? ?? ?? 48 85 C9 74 43") + 0x8; this.SetupTerritoryType = sig.ScanText("48 89 5C 24 ?? 48 89 74 24 ?? 57 48 83 EC 20 48 8B F9 66 89 91 ?? ?? ?? ??"); @@ -88,11 +97,9 @@ namespace Dalamud.Game.ClientState // so GetStaticAddressFromSig() can't be used. lea rcx, ds:1DB9F74h[rax*4] this.KeyboardState = sig.ScanText("48 8D 0C 85 ?? ?? ?? ?? 8B 04 31 85 C2 0F 85") + 0x4; - // PartyListUpdate = sig.ScanText("E8 ?? ?? ?? ?? 49 8B D7 4C 8D 86 ?? ?? ?? ??"); - this.ConditionFlags = sig.GetStaticAddressFromSig("48 8D 0D ?? ?? ?? ?? BA ?? ?? ?? ?? E8 ?? ?? ?? ?? B0 01 48 83 C4 30"); - this.TargetManager = sig.GetStaticAddressFromSig("48 8B 05 ?? ?? ?? ?? 48 8D 0D ?? ?? ?? ?? FF 50 ?? 48 85 DB", 3); + this.TargetManager = sig.GetStaticAddressFromSig("48 8B 05 ?? ?? ?? ?? 48 8D 0D ?? ?? ?? ?? FF 50 ?? 48 85 DB"); this.GamepadPoll = sig.ScanText("40 ?? 57 41 ?? 48 81 EC ?? ?? ?? ?? 44 0F ?? ?? ?? ?? ?? ?? ?? 48 8B"); } diff --git a/Dalamud/Game/ClientState/Actors/Types/NonPlayer/BattleNpcSubKind.cs b/Dalamud/Game/ClientState/Objects/Enums/BattleNpcSubKind.cs similarity index 88% rename from Dalamud/Game/ClientState/Actors/Types/NonPlayer/BattleNpcSubKind.cs rename to Dalamud/Game/ClientState/Objects/Enums/BattleNpcSubKind.cs index 1f437b315..c10cc6cfc 100644 --- a/Dalamud/Game/ClientState/Actors/Types/NonPlayer/BattleNpcSubKind.cs +++ b/Dalamud/Game/ClientState/Objects/Enums/BattleNpcSubKind.cs @@ -1,4 +1,4 @@ -namespace Dalamud.Game.ClientState.Actors.Types.NonPlayer +namespace Dalamud.Game.ClientState.Objects.Enums { /// /// An Enum describing possible BattleNpc kinds. diff --git a/Dalamud/Game/ClientState/Actors/Types/CustomizeIndex.cs b/Dalamud/Game/ClientState/Objects/Enums/CustomizeIndex.cs similarity index 98% rename from Dalamud/Game/ClientState/Actors/Types/CustomizeIndex.cs rename to Dalamud/Game/ClientState/Objects/Enums/CustomizeIndex.cs index 23618ae24..b1827c0c5 100644 --- a/Dalamud/Game/ClientState/Actors/Types/CustomizeIndex.cs +++ b/Dalamud/Game/ClientState/Objects/Enums/CustomizeIndex.cs @@ -1,4 +1,4 @@ -namespace Dalamud.Game.ClientState.Actors.Types +namespace Dalamud.Game.ClientState.Objects.Enums { /// /// This enum describes the indices of the Customize array. diff --git a/Dalamud/Game/ClientState/Actors/Types/ObjectKind.cs b/Dalamud/Game/ClientState/Objects/Enums/ObjectKind.cs similarity index 95% rename from Dalamud/Game/ClientState/Actors/Types/ObjectKind.cs rename to Dalamud/Game/ClientState/Objects/Enums/ObjectKind.cs index b79adb784..038bce143 100644 --- a/Dalamud/Game/ClientState/Actors/Types/ObjectKind.cs +++ b/Dalamud/Game/ClientState/Objects/Enums/ObjectKind.cs @@ -1,4 +1,4 @@ -namespace Dalamud.Game.ClientState.Actors.Types +namespace Dalamud.Game.ClientState.Objects.Enums { /// /// Enum describing possible entity kinds. @@ -6,7 +6,7 @@ namespace Dalamud.Game.ClientState.Actors.Types public enum ObjectKind : byte { /// - /// Invalid actor. + /// Invalid character. /// None = 0x00, diff --git a/Dalamud/Game/ClientState/Actors/Types/StatusFlags.cs b/Dalamud/Game/ClientState/Objects/Enums/StatusFlags.cs similarity index 70% rename from Dalamud/Game/ClientState/Actors/Types/StatusFlags.cs rename to Dalamud/Game/ClientState/Objects/Enums/StatusFlags.cs index a1101ceca..43587c66b 100644 --- a/Dalamud/Game/ClientState/Actors/Types/StatusFlags.cs +++ b/Dalamud/Game/ClientState/Objects/Enums/StatusFlags.cs @@ -1,6 +1,6 @@ using System; -namespace Dalamud.Game.ClientState.Actors.Types +namespace Dalamud.Game.ClientState.Objects.Enums { /// /// Enum describing possible status flags. @@ -14,42 +14,42 @@ namespace Dalamud.Game.ClientState.Actors.Types None = 0, /// - /// Hostile actor. + /// Hostile character. /// Hostile = 1, /// - /// Actor in combat. + /// Character in combat. /// InCombat = 2, /// - /// Actor weapon is out. + /// Character weapon is out. /// WeaponOut = 4, /// - /// Actor offhand is out. + /// Character offhand is out. /// OffhandOut = 8, /// - /// Actor is a party member. + /// Character is a party member. /// PartyMember = 16, /// - /// Actor is a alliance member. + /// Character is a alliance member. /// AllianceMember = 32, /// - /// Actor is in friend list. + /// Character is in friend list. /// Friend = 64, /// - /// Actor is casting. + /// Character is casting. /// IsCasting = 128, } diff --git a/Dalamud/Game/ClientState/Objects/ObjectTable.cs b/Dalamud/Game/ClientState/Objects/ObjectTable.cs new file mode 100644 index 000000000..7e11d8e6f --- /dev/null +++ b/Dalamud/Game/ClientState/Objects/ObjectTable.cs @@ -0,0 +1,146 @@ +using System; +using System.Collections; +using System.Collections.Generic; + +using Dalamud.Game.ClientState.Objects.Enums; +using Dalamud.Game.ClientState.Objects.SubKinds; +using Dalamud.Game.ClientState.Objects.Types; +using JetBrains.Annotations; +using Serilog; + +namespace Dalamud.Game.ClientState.Objects +{ + /// + /// This collection represents the currently spawned FFXIV game objects. + /// + public sealed partial class ObjectTable + { + private const int ObjectTableLength = 424; + + private readonly Dalamud dalamud; + private readonly ClientStateAddressResolver address; + + /// + /// Initializes a new instance of the class. + /// + /// The instance. + /// Client state address resolver. + internal ObjectTable(Dalamud dalamud, ClientStateAddressResolver addressResolver) + { + this.dalamud = dalamud; + this.address = addressResolver; + + Log.Verbose($"Object table address 0x{this.address.ObjectTable.ToInt64():X}"); + } + + /// + /// Gets the address of the object table. + /// + public IntPtr Address => this.address.ObjectTable; + + /// + /// Gets the length of the object table. + /// + public int Length => ObjectTableLength; + + /// + /// Get an object at the specified spawn index. + /// + /// Spawn index. + /// An at the specified spawn index. + [CanBeNull] + public GameObject this[int index] + { + get + { + var address = this.GetObjectAddress(index); + return this.CreateObjectReference(address); + } + } + + /// + /// Search for a game object by their Object ID. + /// + /// Object ID to find. + /// A game object or null. + [CanBeNull] + public GameObject SearchByID(uint objectID) + { + foreach (var obj in this) + { + if (obj == null) + continue; + + if (obj.ObjectId == objectID) + return obj; + } + + return null; + } + + /// + /// Gets the address of the game object at the specified index of the object table. + /// + /// The index of the object. + /// The memory address of the object. + public unsafe IntPtr GetObjectAddress(int index) + { + if (index < 0 || index >= ObjectTableLength) + return IntPtr.Zero; + + return *(IntPtr*)(this.address.ObjectTable + (8 * index)); + } + + /// + /// Create a reference to an FFXIV game object. + /// + /// The address of the object in memory. + /// object or inheritor containing the requested data. + [CanBeNull] + public unsafe GameObject CreateObjectReference(IntPtr address) + { + if (this.dalamud.ClientState.LocalContentId == 0) + return null; + + if (address == IntPtr.Zero) + return null; + + var obj = (FFXIVClientStructs.FFXIV.Client.Game.Object.GameObject*)address; + var objKind = (ObjectKind)obj->ObjectKind; + return objKind switch + { + ObjectKind.Player => new PlayerCharacter(address, this.dalamud), + ObjectKind.BattleNpc => new BattleNpc(address, this.dalamud), + ObjectKind.EventObj => new EventObj(address, this.dalamud), + ObjectKind.Companion => new Npc(address, this.dalamud), + _ => new GameObject(address, this.dalamud), + }; + } + } + + /// + /// This collection represents the currently spawned FFXIV game objects. + /// + public sealed partial class ObjectTable : IReadOnlyCollection + { + /// + int IReadOnlyCollection.Count => this.Length; + + /// + public IEnumerator GetEnumerator() + { + for (var i = 0; i < ObjectTableLength; i++) + { + var obj = this[i]; + + if (obj == null) + continue; + + yield return obj; + } + } + + /// + IEnumerator IEnumerable.GetEnumerator() => this.GetEnumerator(); + } +} diff --git a/Dalamud/Game/ClientState/Actors/Types/NonPlayer/BattleNpc.cs b/Dalamud/Game/ClientState/Objects/SubKinds/BattleNpc.cs similarity index 64% rename from Dalamud/Game/ClientState/Actors/Types/NonPlayer/BattleNpc.cs rename to Dalamud/Game/ClientState/Objects/SubKinds/BattleNpc.cs index 3fa8f0c28..ea5fc64fa 100644 --- a/Dalamud/Game/ClientState/Actors/Types/NonPlayer/BattleNpc.cs +++ b/Dalamud/Game/ClientState/Objects/SubKinds/BattleNpc.cs @@ -1,11 +1,13 @@ using System; -namespace Dalamud.Game.ClientState.Actors.Types.NonPlayer +using Dalamud.Game.ClientState.Objects.Enums; + +namespace Dalamud.Game.ClientState.Objects.Types { /// /// This class represents a battle NPC. /// - public unsafe class BattleNpc : Npc + public unsafe class BattleNpc : BattleChara { /// /// Initializes a new instance of the class. @@ -21,11 +23,6 @@ namespace Dalamud.Game.ClientState.Actors.Types.NonPlayer /// /// Gets the BattleNpc of this BattleNpc. /// - public BattleNpcSubKind BattleNpcKind => *(BattleNpcSubKind*)(this.Address + ActorOffsets.SubKind); - - /// - /// Gets the target of the Battle NPC. - /// - public override uint TargetActorID => *(uint*)(this.Address + ActorOffsets.BattleNpcTargetActorId); + public BattleNpcSubKind BattleNpcKind => (BattleNpcSubKind)this.Struct->Character.GameObject.SubKind; } } diff --git a/Dalamud/Game/ClientState/Objects/SubKinds/EventObj.cs b/Dalamud/Game/ClientState/Objects/SubKinds/EventObj.cs new file mode 100644 index 000000000..55e0b3f97 --- /dev/null +++ b/Dalamud/Game/ClientState/Objects/SubKinds/EventObj.cs @@ -0,0 +1,23 @@ +using System; + +using Dalamud.Game.ClientState.Objects.Types; + +namespace Dalamud.Game.ClientState.Objects.SubKinds +{ + /// + /// This class represents an EventObj. + /// + public unsafe class EventObj : GameObject + { + /// + /// Initializes a new instance of the class. + /// Set up a new EventObj with the provided memory representation. + /// + /// The address of this event object in memory. + /// A dalamud reference. + internal EventObj(IntPtr address, Dalamud dalamud) + : base(address, dalamud) + { + } + } +} diff --git a/Dalamud/Game/ClientState/Actors/Types/NonPlayer/Npc.cs b/Dalamud/Game/ClientState/Objects/SubKinds/Npc.cs similarity index 65% rename from Dalamud/Game/ClientState/Actors/Types/NonPlayer/Npc.cs rename to Dalamud/Game/ClientState/Objects/SubKinds/Npc.cs index a3597570c..cf582a11f 100644 --- a/Dalamud/Game/ClientState/Actors/Types/NonPlayer/Npc.cs +++ b/Dalamud/Game/ClientState/Objects/SubKinds/Npc.cs @@ -1,11 +1,13 @@ using System; -namespace Dalamud.Game.ClientState.Actors.Types.NonPlayer +using Dalamud.Game.ClientState.Objects.Types; + +namespace Dalamud.Game.ClientState.Objects.SubKinds { /// /// This class represents a NPC. /// - public unsafe class Npc : Chara + public unsafe class Npc : Character { /// /// Initializes a new instance of the class. @@ -18,14 +20,9 @@ namespace Dalamud.Game.ClientState.Actors.Types.NonPlayer { } - /// - /// Gets the data ID of the NPC linking to their assoicated BNpcBase data. - /// - public uint BaseId => *(uint*)(this.Address + ActorOffsets.DataId); - /// /// Gets the name ID of the NPC linking to their respective game data. /// - public uint NameId => *(uint*)(this.Address + ActorOffsets.NameId); + public uint NameId => this.Struct->NameID; } } diff --git a/Dalamud/Game/ClientState/Actors/Types/PlayerCharacter.cs b/Dalamud/Game/ClientState/Objects/SubKinds/PlayerCharacter.cs similarity index 63% rename from Dalamud/Game/ClientState/Actors/Types/PlayerCharacter.cs rename to Dalamud/Game/ClientState/Objects/SubKinds/PlayerCharacter.cs index e04c36d32..9b33e6ce5 100644 --- a/Dalamud/Game/ClientState/Actors/Types/PlayerCharacter.cs +++ b/Dalamud/Game/ClientState/Objects/SubKinds/PlayerCharacter.cs @@ -1,15 +1,14 @@ using System; +using Dalamud.Game.ClientState.Objects.Types; using Dalamud.Game.ClientState.Resolvers; -using Dalamud.Game.Text.SeStringHandling; -using Dalamud.Memory; -namespace Dalamud.Game.ClientState.Actors.Types +namespace Dalamud.Game.ClientState.Objects.SubKinds { /// /// This class represents a player character. /// - public unsafe class PlayerCharacter : Chara + public unsafe class PlayerCharacter : BattleChara { /// /// Initializes a new instance of the class. @@ -25,21 +24,16 @@ namespace Dalamud.Game.ClientState.Actors.Types /// /// Gets the current world of the character. /// - public ExcelResolver CurrentWorld => new(*(ushort*)(this.Address + ActorOffsets.CurrentWorld), this.Dalamud); + public ExcelResolver CurrentWorld => new(this.Struct->Character.CurrentWorld, this.Dalamud); /// /// Gets the home world of the character. /// - public ExcelResolver HomeWorld => new(*(ushort*)(this.Address + ActorOffsets.HomeWorld), this.Dalamud); - - /// - /// Gets the Free Company tag of this player. - /// - public SeString CompanyTag => MemoryHelper.ReadSeString(this.Address + ActorOffsets.CompanyTag, 6); + public ExcelResolver HomeWorld => new(this.Struct->Character.HomeWorld, this.Dalamud); /// /// Gets the target actor ID of the PlayerCharacter. /// - public override uint TargetActorID => *(uint*)(this.Address + ActorOffsets.PlayerCharacterTargetActorId); + public override uint TargetObjectId => this.Struct->Character.GameObject.TargetObjectID; } } diff --git a/Dalamud/Game/ClientState/Objects/Targets.cs b/Dalamud/Game/ClientState/Objects/Targets.cs new file mode 100644 index 000000000..1c9527221 --- /dev/null +++ b/Dalamud/Game/ClientState/Objects/Targets.cs @@ -0,0 +1,169 @@ +using System; + +using Dalamud.Game.ClientState.Objects.Types; +using JetBrains.Annotations; + +namespace Dalamud.Game.ClientState.Objects +{ + /// + /// Get and set various kinds of targets for the player. + /// + public sealed unsafe class Targets + { + private readonly Dalamud dalamud; + private readonly ClientStateAddressResolver address; + + /// + /// Initializes a new instance of the class. + /// + /// The Dalamud instance. + /// The ClientStateAddressResolver instance. + internal Targets(Dalamud dalamud, ClientStateAddressResolver addressResolver) + { + this.dalamud = dalamud; + this.address = addressResolver; + } + + /// + /// Gets the address of the target manager. + /// + public IntPtr Address => this.address.TargetManager; + + /// + /// Gets or sets the current target. + /// + [CanBeNull] + public GameObject Target + { + get => this.dalamud.ClientState.Objects.CreateObjectReference((IntPtr)Struct->Target); + set => this.SetTarget(value); + } + + /// + /// Gets or sets the mouseover target. + /// + [CanBeNull] + public GameObject MouseOverTarget + { + get => this.dalamud.ClientState.Objects.CreateObjectReference((IntPtr)Struct->MouseOverTarget); + set => this.SetMouseOverTarget(value); + } + + /// + /// Gets or sets the focus target. + /// + [CanBeNull] + public GameObject FocusTarget + { + get => this.dalamud.ClientState.Objects.CreateObjectReference((IntPtr)Struct->FocusTarget); + set => this.SetFocusTarget(value); + } + + /// + /// Gets or sets the previous target. + /// + [CanBeNull] + public GameObject PreviousTarget + { + get => this.dalamud.ClientState.Objects.CreateObjectReference((IntPtr)Struct->PreviousTarget); + set => this.SetPreviousTarget(value); + } + + /// + /// Gets or sets the soft target. + /// + [CanBeNull] + public GameObject SoftTarget + { + get => this.dalamud.ClientState.Objects.CreateObjectReference((IntPtr)Struct->SoftTarget); + set => this.SetSoftTarget(value); + } + + private FFXIVClientStructs.FFXIV.Client.Game.Control.TargetSystem* Struct => (FFXIVClientStructs.FFXIV.Client.Game.Control.TargetSystem*)this.Address; + + /// + /// Sets the current target. + /// + /// Actor to target. + public void SetTarget(GameObject actor) => this.SetTarget(actor?.Address ?? IntPtr.Zero); + + /// + /// Sets the mouseover target. + /// + /// Actor to target. + public void SetMouseOverTarget(GameObject actor) => this.SetTarget(actor?.Address ?? IntPtr.Zero); + + /// + /// Sets the focus target. + /// + /// Actor to target. + public void SetFocusTarget(GameObject actor) => this.SetTarget(actor?.Address ?? IntPtr.Zero); + + /// + /// Sets the previous target. + /// + /// Actor to target. + public void SetPreviousTarget(GameObject actor) => this.SetTarget(actor?.Address ?? IntPtr.Zero); + + /// + /// Sets the soft target. + /// + /// Actor to target. + public void SetSoftTarget(GameObject actor) => this.SetTarget(actor?.Address ?? IntPtr.Zero); + + /// + /// Sets the current target. + /// + /// Actor (address) to target. + public void SetTarget(IntPtr actorAddress) => Struct->Target = (FFXIVClientStructs.FFXIV.Client.Game.Object.GameObject*)actorAddress; + + /// + /// Sets the mouseover target. + /// + /// Actor (address) to target. + public void SetMouseOverTarget(IntPtr actorAddress) => Struct->MouseOverTarget = (FFXIVClientStructs.FFXIV.Client.Game.Object.GameObject*)actorAddress; + + /// + /// Sets the focus target. + /// + /// Actor (address) to target. + public void SetFocusTarget(IntPtr actorAddress) => Struct->FocusTarget = (FFXIVClientStructs.FFXIV.Client.Game.Object.GameObject*)actorAddress; + + /// + /// Sets the previous target. + /// + /// Actor (address) to target. + public void SetPreviousTarget(IntPtr actorAddress) => Struct->PreviousTarget = (FFXIVClientStructs.FFXIV.Client.Game.Object.GameObject*)actorAddress; + + /// + /// Sets the soft target. + /// + /// Actor (address) to target. + public void SetSoftTarget(IntPtr actorAddress) => Struct->SoftTarget = (FFXIVClientStructs.FFXIV.Client.Game.Object.GameObject*)actorAddress; + + /// + /// Clears the current target. + /// + public void ClearTarget() => this.SetTarget(IntPtr.Zero); + + /// + /// Clears the mouseover target. + /// + public void ClearMouseOverTarget() => this.SetMouseOverTarget(IntPtr.Zero); + + /// + /// Clears the focus target. + /// + public void ClearFocusTarget() => this.SetFocusTarget(IntPtr.Zero); + + /// + /// Clears the previous target. + /// + public void ClearPreviousTarget() => this.SetPreviousTarget(IntPtr.Zero); + + /// + /// Clears the soft target. + /// + public void ClearSoftTarget() => this.SetSoftTarget(IntPtr.Zero); + } +} diff --git a/Dalamud/Game/ClientState/Objects/Types/BattleChara.cs b/Dalamud/Game/ClientState/Objects/Types/BattleChara.cs new file mode 100644 index 000000000..5a553b215 --- /dev/null +++ b/Dalamud/Game/ClientState/Objects/Types/BattleChara.cs @@ -0,0 +1,68 @@ +using System; + +using Dalamud.Game.ClientState.Statuses; + +namespace Dalamud.Game.ClientState.Objects.Types +{ + /// + /// This class represents the battle characters. + /// + public unsafe class BattleChara : Character + { + /// + /// Initializes a new instance of the class. + /// This represents a battle character. + /// + /// The address of this character in memory. + /// Dalamud itself. + internal BattleChara(IntPtr address, Dalamud dalamud) + : base(address, dalamud) + { + } + + /// + /// Gets the current status effects. + /// + public StatusList StatusList => new(&this.Struct->StatusManager, this.Dalamud); + + /// + /// Gets a value indicating whether the chara is currently casting. + /// + public bool IsCasting => this.Struct->SpellCastInfo.IsCasting > 0; + + /// + /// Gets a value indicating whether the cast is interruptible. + /// + public bool IsCastInterruptible => this.Struct->SpellCastInfo.Interruptible > 0; + + /// + /// Gets the spell action type of the spell being cast by the actor. + /// + public byte CastActionType => (byte)this.Struct->SpellCastInfo.ActionType; + + /// + /// Gets the spell action ID of the spell being cast by the actor. + /// + public uint CastActionId => this.Struct->SpellCastInfo.ActionID; + + /// + /// Gets the object ID of the target currently being cast at by the chara. + /// + public uint CastTargetObjectId => this.Struct->SpellCastInfo.CastTargetID; + + /// + /// Gets the current casting time of the spell being cast by the chara. + /// + public float CurrentCastTime => this.Struct->SpellCastInfo.CurrentCastTime; + + /// + /// Gets the total casting time of the spell being cast by the chara. + /// + public float TotalCastTime => this.Struct->SpellCastInfo.TotalCastTime; + + /// + /// Gets the underlying structure. + /// + private protected new FFXIVClientStructs.FFXIV.Client.Game.Character.BattleChara* Struct => (FFXIVClientStructs.FFXIV.Client.Game.Character.BattleChara*)this.Address; + } +} diff --git a/Dalamud/Game/ClientState/Objects/Types/Character.cs b/Dalamud/Game/ClientState/Objects/Types/Character.cs new file mode 100644 index 000000000..2ebcbd1c5 --- /dev/null +++ b/Dalamud/Game/ClientState/Objects/Types/Character.cs @@ -0,0 +1,103 @@ +using System; + +using Dalamud.Game.ClientState.Objects.Enums; +using Dalamud.Game.ClientState.Resolvers; +using Dalamud.Game.Text.SeStringHandling; +using Dalamud.Memory; +using JetBrains.Annotations; + +namespace Dalamud.Game.ClientState.Objects.Types +{ + /// + /// This class represents the base for non-static entities. + /// + public unsafe class Character : GameObject + { + /// + /// Initializes a new instance of the class. + /// This represents a non-static entity. + /// + /// The address of this character in memory. + /// Dalamud itself. + internal Character(IntPtr address, Dalamud dalamud) + : base(address, dalamud) + { + } + + /// + /// Gets the current HP of this Chara. + /// + public uint CurrentHp => this.Struct->Health; + + /// + /// Gets the maximum HP of this Chara. + /// + public uint MaxHp => this.Struct->MaxHealth; + + /// + /// Gets the current MP of this Chara. + /// + public uint CurrentMp => this.Struct->Mana; + + /// + /// Gets the maximum MP of this Chara. + /// + public uint MaxMp => this.Struct->MaxMana; + + /// + /// Gets the current GP of this Chara. + /// + public uint CurrentGp => this.Struct->GatheringPoints; + + /// + /// Gets the maximum GP of this Chara. + /// + public uint MaxGp => this.Struct->MaxGatheringPoints; + + /// + /// Gets the current CP of this Chara. + /// + public uint CurrentCp => this.Struct->CraftingPoints; + + /// + /// Gets the maximum CP of this Chara. + /// + public uint MaxCp => this.Struct->MaxCraftingPoints; + + /// + /// Gets the ClassJob of this Chara. + /// + public ExcelResolver ClassJob => new(this.Struct->ClassJob, this.Dalamud); + + /// + /// Gets the level of this Chara. + /// + public byte Level => this.Struct->Level; + + /// + /// Gets a byte array describing the visual appearance of this Chara. + /// Indexed by . + /// + public byte[] Customize => MemoryHelper.Read((IntPtr)this.Struct->CustomizeData, 28); + + /// + /// Gets the Free Company tag of this chara. + /// + public SeString CompanyTag => MemoryHelper.ReadSeString((IntPtr)this.Struct->FreeCompanyTag, 6); + + /// + /// Gets the target object ID of the character. + /// + public override uint TargetObjectId => this.Struct->GameObject.TargetObjectID; + + /// + /// Gets the status flags. + /// + public StatusFlags StatusFlags => (StatusFlags)this.Struct->StatusFlags; + + /// + /// Gets the underlying structure. + /// + private protected new FFXIVClientStructs.FFXIV.Client.Game.Character.Character* Struct => (FFXIVClientStructs.FFXIV.Client.Game.Character.Character*)this.Address; + } +} diff --git a/Dalamud/Game/ClientState/Objects/Types/GameObject.cs b/Dalamud/Game/ClientState/Objects/Types/GameObject.cs new file mode 100644 index 000000000..68e41ef3f --- /dev/null +++ b/Dalamud/Game/ClientState/Objects/Types/GameObject.cs @@ -0,0 +1,168 @@ +using System; +using System.Numerics; + +using Dalamud.Game.ClientState.Objects.Enums; +using Dalamud.Game.Text.SeStringHandling; +using Dalamud.Memory; +using JetBrains.Annotations; + +namespace Dalamud.Game.ClientState.Objects.Types +{ + /// + /// This class represents a GameObject in FFXIV. + /// + public unsafe partial class GameObject : IEquatable + { + /// + /// Initializes a new instance of the class. + /// + /// The address of this game object in memory. + /// Dalamud itself. + internal GameObject(IntPtr address, Dalamud dalamud) + { + this.Address = address; + this.Dalamud = dalamud; + } + + /// + /// Gets the address of the game object in memory. + /// + public IntPtr Address { get; } + + /// + /// Gets the Dalamud instance. + /// + private protected Dalamud Dalamud { get; } + + /// + /// This allows you to if (obj) {...} to check for validity. + /// + /// The actor to check. + /// True or false. + public static implicit operator bool(GameObject? gameObject) => IsValid(gameObject); + + public static bool operator ==(GameObject? gameObject1, GameObject? gameObject2) + { + // Using == results in a stack overflow. + if (gameObject1 is null || gameObject2 is null) + return Equals(gameObject1, gameObject2); + + return gameObject1.Equals(gameObject2); + } + + public static bool operator !=(GameObject? actor1, GameObject? actor2) => !(actor1 == actor2); + + /// + /// Gets a value indicating whether this actor is still valid in memory. + /// + /// The actor to check. + /// True or false. + public static bool IsValid(GameObject? actor) + { + if (actor is null) + return false; + + if (actor.Dalamud.ClientState.LocalContentId == 0) + return false; + + return true; + } + + /// + /// Gets a value indicating whether this actor is still valid in memory. + /// + /// True or false. + public bool IsValid() => IsValid(this); + + /// + bool IEquatable.Equals(GameObject other) => this.ObjectId == other?.ObjectId; + + /// + public override bool Equals(object obj) => ((IEquatable)this).Equals(obj as GameObject); + + /// + public override int GetHashCode() => this.ObjectId.GetHashCode(); + } + + /// + /// This class represents a basic actor (GameObject) in FFXIV. + /// + public unsafe partial class GameObject + { + /// + /// Gets the name of this . + /// + public SeString Name => MemoryHelper.ReadSeString((IntPtr)this.Struct->Name, 32); + + /// + /// Gets the object ID of this . + /// + public uint ObjectId => this.Struct->ObjectID; + + /// + /// Gets the data ID for linking to other respective game data. + /// + public uint DataId => this.Struct->DataID; + + /// + /// Gets the ID of this GameObject's owner. + /// + public uint OwnerId => this.Struct->OwnerID; + + /// + /// Gets the entity kind of this . + /// See the ObjectKind enum for possible values. + /// + public ObjectKind ObjectKind => (ObjectKind)this.Struct->ObjectKind; + + /// + /// Gets the sub kind of this Actor. + /// + public byte SubKind => this.Struct->SubKind; + + /// + /// Gets the X distance from the local player in yalms. + /// + public byte YalmDistanceX => this.Struct->YalmDistanceFromPlayerX; + + /// + /// Gets the Y distance from the local player in yalms. + /// + public byte YalmDistanceZ => this.Struct->YalmDistanceFromPlayerZ; + + /// + /// Gets the position of this . + /// + public Vector3 Position => new(this.Struct->Position.X, this.Struct->Position.Y, this.Struct->Position.Z); + + /// + /// Gets the rotation of this . + /// This ranges from -pi to pi radians. + /// + public float Rotation => this.Struct->Rotation; + + /// + /// Gets the hitbox radius of this . + /// + public float HitboxRadius => this.Struct->HitboxRadius; + + /// + /// Gets the current target of the game object. + /// + public virtual uint TargetObjectId => 0; + + /// + /// Gets the target object of the game object. + /// + /// + /// This iterates the actor table, it should be used with care. + /// + [CanBeNull] + public virtual GameObject TargetObject => this.Dalamud.ClientState.Objects.SearchByID(this.TargetObjectId); + + /// + /// Gets the underlying structure. + /// + private protected FFXIVClientStructs.FFXIV.Client.Game.Object.GameObject* Struct => (FFXIVClientStructs.FFXIV.Client.Game.Object.GameObject*)this.Address; + } +} diff --git a/Dalamud/Game/ClientState/Party/PartyList.cs b/Dalamud/Game/ClientState/Party/PartyList.cs new file mode 100644 index 000000000..8de1ad1ea --- /dev/null +++ b/Dalamud/Game/ClientState/Party/PartyList.cs @@ -0,0 +1,183 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using System.Runtime.InteropServices; + +using JetBrains.Annotations; +using Serilog; + +namespace Dalamud.Game.ClientState.Party +{ + /// + /// This collection represents the actors present in your party or alliance. + /// + public sealed unsafe partial class PartyList + { + private const int GroupLength = 8; + private const int AllianceLength = 20; + + private readonly Dalamud dalamud; + private readonly ClientStateAddressResolver address; + + /// + /// Initializes a new instance of the class. + /// + /// The instance. + /// Client state address resolver. + internal PartyList(Dalamud dalamud, ClientStateAddressResolver addressResolver) + { + this.dalamud = dalamud; + this.address = addressResolver; + + Log.Verbose($"Group manager address 0x{this.address.GroupManager.ToInt64():X}"); + } + + /// + /// Gets the amount of party members the local player has. + /// + public int Length => this.GroupManagerStruct->MemberCount; + + /// + /// Gets the index of the party leader. + /// + public uint PartyLeaderIndex => this.GroupManagerStruct->PartyLeaderIndex; + + /// + /// Gets a value indicating whether this group is an alliance. + /// + public bool IsAlliance => this.GroupManagerStruct->IsAlliance; + + /// + /// Gets the address of the Group Manager. + /// + public IntPtr GroupManagerAddress => this.address.GroupManager; + + /// + /// Gets the address of the party list within the group manager. + /// + public IntPtr GroupListAddress => (IntPtr)GroupManagerStruct->PartyMembers; + + /// + /// Gets the address of the alliance member list within the group manager. + /// + public IntPtr AllianceListAddress => (IntPtr)this.GroupManagerStruct->AllianceMembers; + + private static int PartyMemberSize { get; } = Marshal.SizeOf(); + + private FFXIVClientStructs.FFXIV.Client.Game.Group.GroupManager* GroupManagerStruct => (FFXIVClientStructs.FFXIV.Client.Game.Group.GroupManager*)this.GroupManagerAddress; + + /// + /// Get a party member at the specified spawn index. + /// + /// Spawn index. + /// A at the specified spawn index. + [CanBeNull] + public PartyMember this[int index] + { + get + { + // Normally using Length results in a recursion crash, however we know the party size via ptr. + if (index < 0 || index >= this.Length) + return null; + + if (this.Length > GroupLength) + { + var addr = this.GetAllianceMemberAddress(index); + return this.CreateAllianceMemberReference(addr); + } + else + { + var addr = this.GetPartyMemberAddress(index); + return this.CreatePartyMemberReference(addr); + } + } + } + + /// + /// Gets the address of the party member at the specified index of the party list. + /// + /// The index of the party member. + /// The memory address of the party member. + public IntPtr GetPartyMemberAddress(int index) + { + if (index < 0 || index >= GroupLength) + return IntPtr.Zero; + + return this.GroupListAddress + (index * PartyMemberSize); + } + + /// + /// Create a reference to an FFXIV party member. + /// + /// The address of the party member in memory. + /// The party member object containing the requested data. + [CanBeNull] + public PartyMember CreatePartyMemberReference(IntPtr address) + { + if (this.dalamud.ClientState.LocalContentId == 0) + return null; + + if (address == IntPtr.Zero) + return null; + + return new PartyMember(address, this.dalamud); + } + + /// + /// Gets the address of the alliance member at the specified index of the alliance list. + /// + /// The index of the alliance member. + /// The memory address of the alliance member. + public IntPtr GetAllianceMemberAddress(int index) + { + if (index < 0 || index >= AllianceLength) + return IntPtr.Zero; + + return this.AllianceListAddress + (index * PartyMemberSize); + } + + /// + /// Create a reference to an FFXIV alliance member. + /// + /// The address of the alliance member in memory. + /// The party member object containing the requested data. + [CanBeNull] + public PartyMember CreateAllianceMemberReference(IntPtr address) + { + if (this.dalamud.ClientState.LocalContentId == 0) + return null; + + if (address == IntPtr.Zero) + return null; + + return new PartyMember(address, this.dalamud); + } + } + + /// + /// This collection represents the party members present in your party or alliance. + /// + public sealed partial class PartyList : IReadOnlyCollection + { + /// + int IReadOnlyCollection.Count => this.Length; + + /// + public IEnumerator GetEnumerator() + { + // Normally using Length results in a recursion crash, however we know the party size via ptr. + for (var i = 0; i < this.Length; i++) + { + var member = this[i]; + + if (member == null) + break; + + yield return member; + } + } + + /// + IEnumerator IEnumerable.GetEnumerator() => this.GetEnumerator(); + } +} diff --git a/Dalamud/Game/ClientState/Party/PartyMember.cs b/Dalamud/Game/ClientState/Party/PartyMember.cs new file mode 100644 index 000000000..8cbd2dd84 --- /dev/null +++ b/Dalamud/Game/ClientState/Party/PartyMember.cs @@ -0,0 +1,117 @@ +using System; +using System.Numerics; + +using Dalamud.Game.ClientState.Objects.Types; +using Dalamud.Game.ClientState.Resolvers; +using Dalamud.Game.ClientState.Statuses; +using Dalamud.Game.Text.SeStringHandling; +using Dalamud.Memory; +using JetBrains.Annotations; + +namespace Dalamud.Game.ClientState.Party +{ + /// + /// This class represents a party member in the group manager. + /// + public unsafe class PartyMember + { + private Dalamud dalamud; + + /// + /// Initializes a new instance of the class. + /// + /// Address of the party member. + /// Dalamud itself. + internal PartyMember(IntPtr address, Dalamud dalamud) + { + this.Address = address; + this.dalamud = dalamud; + } + + /// + /// Gets the address of this party member in memory. + /// + public IntPtr Address { get; } + + /// + /// Gets a list of buffs or debuffs applied to this party member. + /// + public StatusList Statuses => new(&this.Struct->StatusManager, this.dalamud); + + /// + /// Gets the position of the party member. + /// + public Vector3 Position => new(this.Struct->X, this.Struct->Y, this.Struct->Z); + + /// + /// Gets the content ID of the party member. + /// + public long ContentId => this.Struct->ContentID; + + /// + /// Gets the actor ID of this party member. + /// + public uint ObjectId => this.Struct->ObjectID; + + /// + /// Gets the actor associated with this buddy. + /// + /// + /// This iterates the actor table, it should be used with care. + /// + [CanBeNull] + public GameObject GameObject => this.dalamud.ClientState.Objects.SearchByID(this.ObjectId); + + /// + /// Gets the current HP of this party member. + /// + public uint CurrentHP => this.Struct->CurrentHP; + + /// + /// Gets the maximum HP of this party member. + /// + public uint MaxHP => this.Struct->MaxHP; + + /// + /// Gets the current MP of this party member. + /// + public ushort CurrentMP => this.Struct->CurrentMP; + + /// + /// Gets the maximum MP of this party member. + /// + public ushort MaxMP => this.Struct->MaxMP; + + /// + /// Gets the territory this party member is located in. + /// + public ExcelResolver Territory => new(this.Struct->TerritoryType, this.dalamud); + + /// + /// Gets the World this party member resides in. + /// + public ExcelResolver World => new(this.Struct->HomeWorld, this.dalamud); + + /// + /// Gets the displayname of this party member. + /// + public SeString Name => MemoryHelper.ReadSeString((IntPtr)Struct->Name, 0x40); + + /// + /// Gets the sex of this party member. + /// + public byte Sex => this.Struct->Sex; + + /// + /// Gets the classjob of this party member. + /// + public ExcelResolver ClassJob => new(this.Struct->ClassJob, this.dalamud); + + /// + /// Gets the level of this party member. + /// + public byte Level => this.Struct->Level; + + private FFXIVClientStructs.FFXIV.Client.Game.Group.PartyMember* Struct => (FFXIVClientStructs.FFXIV.Client.Game.Group.PartyMember*)this.Address; + } +} diff --git a/Dalamud/Game/ClientState/PartyList.cs b/Dalamud/Game/ClientState/PartyList.cs deleted file mode 100644 index 2aa4b95ed..000000000 --- a/Dalamud/Game/ClientState/PartyList.cs +++ /dev/null @@ -1,139 +0,0 @@ -using System; -using System.Collections; -using System.Collections.Generic; -using System.Runtime.InteropServices; - -using Dalamud.Game.ClientState.Actors.Types; -// using Dalamud.Hooking; - -namespace Dalamud.Game.ClientState -{ - /// - /// This class represents the members of your party. - /// - public sealed partial class PartyList - { - private readonly Dalamud dalamud; - private readonly ClientStateAddressResolver address; - - // private bool isReady = false; - // private IntPtr partyListBegin; - // private Hook partyListUpdateHook; - - /// - /// Initializes a new instance of the class. - /// - /// The Dalamud instance. - /// The ClientStateAddressResolver instance. - internal PartyList(Dalamud dalamud, ClientStateAddressResolver addressResolver) - { - this.address = addressResolver; - this.dalamud = dalamud; - // this.partyListUpdateHook = new Hook(Address.PartyListUpdate, new PartyListUpdateDelegate(PartyListUpdateDetour), this); - } - - private delegate long PartyListUpdateDelegate(IntPtr structBegin, long param2, char param3); - - /// - /// Gets the length of the PartyList. - /// - public int Length => 0; // !this.isReady ? 0 : Marshal.ReadByte(this.partyListBegin + 0xF0); - - /// - /// Get the nth party member. - /// - /// Index of the party member. - /// The party member. - public PartyMember this[int index] - { - get - { - return null; - // if (!this.isReady) - // return null; - // if (index >= this.Length) - // return null; - // var tblIndex = this.partyListBegin + (index * 24); - // var memberStruct = Marshal.PtrToStructure(tblIndex); - // return new PartyMember(this.dalamud.ClientState.Actors, memberStruct); - } - } - - /// - /// Enable this module. - /// - public void Enable() - { - // TODO Fix for 5.3 - // this.partyListUpdateHook.Enable(); - } - - /// - /// Dispose of managed and unmanaged resources. - /// - public void Dispose() - { - // if (!this.isReady) - // this.partyListUpdateHook.Dispose(); - // this.isReady = false; - } - - // private long PartyListUpdateDetour(IntPtr structBegin, long param2, char param3) - // { - // var result = this.partyListUpdateHook.Original(structBegin, param2, param3); - // this.partyListBegin = structBegin + 0xB48; - // this.partyListUpdateHook.Dispose(); - // this.isReady = true; - // return result; - // } - } - - /// - /// Implements IReadOnlyCollection, IEnumerable. - /// - public sealed partial class PartyList : IReadOnlyCollection - { - /// - int IReadOnlyCollection.Count => this.Length; - - /// - public IEnumerator GetEnumerator() - { - for (var i = 0; i < this.Length; i++) - { - if (this[i] != null) - { - yield return this[i]; - } - } - } - - /// - IEnumerator IEnumerable.GetEnumerator() => this.GetEnumerator(); - } - - /// - /// Implements ICollection. - /// - public sealed partial class PartyList : ICollection - { - /// - public int Count => this.Length; - - /// - public object SyncRoot => this; - - /// - public bool IsSynchronized => false; - - /// - public void CopyTo(Array array, int index) - { - for (var i = 0; i < this.Length; i++) - { - array.SetValue(this[i], index); - index++; - } - } - } -} diff --git a/Dalamud/Game/ClientState/Statuses/Status.cs b/Dalamud/Game/ClientState/Statuses/Status.cs new file mode 100644 index 000000000..9570c79a9 --- /dev/null +++ b/Dalamud/Game/ClientState/Statuses/Status.cs @@ -0,0 +1,73 @@ +using System; + +using Dalamud.Game.ClientState.Objects.Types; +using Dalamud.Game.ClientState.Resolvers; +using JetBrains.Annotations; + +namespace Dalamud.Game.ClientState.Statuses +{ + /// + /// This class represents a status effect an actor is afflicted by. + /// + public unsafe class Status + { + private Dalamud dalamud; + + /// + /// Initializes a new instance of the class. + /// + /// Status address. + /// Dalamud instance. + internal Status(IntPtr address, Dalamud dalamud) + { + this.dalamud = dalamud; + this.Address = address; + } + + /// + /// Gets the address of the status in memory. + /// + public IntPtr Address { get; } + + /// + /// Gets the status ID of this status. + /// + public uint StatusID => this.Struct->StatusID; + + /// + /// Gets the GameData associated with this status. + /// + public Lumina.Excel.GeneratedSheets.Status GameData => new ExcelResolver(this.Struct->StatusID, this.dalamud).GameData; + + /// + /// Gets the parameter value of the status. + /// + public byte Param => this.Struct->Param; + + /// + /// Gets the stack count of this status. + /// + public byte StackCount => this.Struct->StackCount; + + /// + /// Gets the time remaining of this status. + /// + public float RemainingTime => this.Struct->RemainingTime; + + /// + /// Gets the source ID of this status. + /// + public uint SourceID => this.Struct->SourceID; + + /// + /// Gets the source actor associated with this status. + /// + /// + /// This iterates the actor table, it should be used with care. + /// + [CanBeNull] + public GameObject SourceActor => this.dalamud.ClientState.Objects.SearchByID(this.SourceID); + + private FFXIVClientStructs.FFXIV.Client.Game.Status* Struct => (FFXIVClientStructs.FFXIV.Client.Game.Status*)this.Address; + } +} diff --git a/Dalamud/Game/ClientState/Statuses/StatusList.cs b/Dalamud/Game/ClientState/Statuses/StatusList.cs new file mode 100644 index 000000000..a617d900d --- /dev/null +++ b/Dalamud/Game/ClientState/Statuses/StatusList.cs @@ -0,0 +1,161 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using System.Runtime.InteropServices; + +using JetBrains.Annotations; + +namespace Dalamud.Game.ClientState.Statuses +{ + /// + /// This collection represents the status effects an actor is afflicted by. + /// + public sealed unsafe partial class StatusList + { + private const int StatusListLength = 30; + + private readonly Dalamud dalamud; + + /// + /// Initializes a new instance of the class. + /// + /// Address of the status list. + /// The instance. + internal StatusList(IntPtr address, Dalamud dalamud) + { + this.Address = address; + this.dalamud = dalamud; + } + + /// + /// Initializes a new instance of the class. + /// + /// Pointer to the status list. + /// The instance. + internal unsafe StatusList(void* pointer, Dalamud dalamud) + : this((IntPtr)pointer, dalamud) + { + } + + /// + /// Gets the address of the status list in memory. + /// + public IntPtr Address { get; } + + /// + /// Gets the amount of status effects the actor has. + /// + public int Length + { + get + { + var i = 0; + for (; i < StatusListLength; i++) + { + var status = this[i]; + if (status == null || status.StatusID == 0) + break; + } + + return i; + } + } + + private static int StatusSize { get; } = Marshal.SizeOf(); + + private FFXIVClientStructs.FFXIV.Client.Game.StatusManager* Struct => (FFXIVClientStructs.FFXIV.Client.Game.StatusManager*)this.Address; + + /// + /// Get a status effect at the specified index. + /// + /// Status Index. + /// The status at the specified index. + [CanBeNull] + public Status this[int index] + { + get + { + if (index < 0 || index > StatusListLength) + return null; + + var addr = this.GetStatusAddress(index); + return this.CreateStatusReference(addr); + } + } + + /// + /// Gets the address of the party member at the specified index of the party list. + /// + /// The index of the party member. + /// The memory address of the party member. + public IntPtr GetStatusAddress(int index) + { + if (index < 0 || index >= StatusListLength) + return IntPtr.Zero; + + return (IntPtr)(this.Struct->Status + (index * StatusSize)); + } + + /// + /// Create a reference to an FFXIV actor status. + /// + /// The address of the status effect in memory. + /// The status object containing the requested data. + [CanBeNull] + public Status CreateStatusReference(IntPtr address) + { + if (this.dalamud.ClientState.LocalContentId == 0) + return null; + + if (address == IntPtr.Zero) + return null; + + return new Status(address, this.dalamud); + } + } + + /// + /// This collection represents the status effects an actor is afflicted by. + /// + public sealed partial class StatusList : IReadOnlyCollection, ICollection + { + /// + int IReadOnlyCollection.Count => this.Length; + + /// + int ICollection.Count => this.Length; + + /// + bool ICollection.IsSynchronized => false; + + /// + object ICollection.SyncRoot => this; + + /// + public IEnumerator GetEnumerator() + { + for (var i = 0; i < StatusListLength; i++) + { + var status = this[i]; + + if (status == null || status.StatusID == 0) + continue; + + yield return status; + } + } + + /// + IEnumerator IEnumerable.GetEnumerator() => this.GetEnumerator(); + + /// + void ICollection.CopyTo(Array array, int index) + { + for (var i = 0; i < this.Length; i++) + { + array.SetValue(this[i], index); + index++; + } + } + } +} diff --git a/Dalamud/Game/ClientState/Structs/PartyMember.cs b/Dalamud/Game/ClientState/Structs/PartyMember.cs deleted file mode 100644 index 926730e54..000000000 --- a/Dalamud/Game/ClientState/Structs/PartyMember.cs +++ /dev/null @@ -1,26 +0,0 @@ -using System; -using System.Runtime.InteropServices; - -using Dalamud.Game.ClientState.Actors.Types; - -namespace Dalamud.Game.ClientState.Structs -{ - /// - /// This represents a native PartyMember class in memory. - /// - [StructLayout(LayoutKind.Explicit)] - public struct PartyMember - { - [FieldOffset(0x0)] - public IntPtr namePtr; - - [FieldOffset(0x8)] - public long unknown; - - [FieldOffset(0x10)] - public int actorId; - - [FieldOffset(0x14)] - public ObjectKind objectKind; - } -} diff --git a/Dalamud/Interface/Internal/Windows/DataWindow.cs b/Dalamud/Interface/Internal/Windows/DataWindow.cs index 96473d879..96c5f11e3 100644 --- a/Dalamud/Interface/Internal/Windows/DataWindow.cs +++ b/Dalamud/Interface/Internal/Windows/DataWindow.cs @@ -5,13 +5,12 @@ using System.Linq; using System.Numerics; using Dalamud.Game; -using Dalamud.Game.ClientState; -using Dalamud.Game.ClientState.Actors.Types; -using Dalamud.Game.ClientState.Actors.Types.NonPlayer; using Dalamud.Game.ClientState.Conditions; using Dalamud.Game.ClientState.GamePad; using Dalamud.Game.ClientState.JobGauge.Enums; using Dalamud.Game.ClientState.JobGauge.Types; +using Dalamud.Game.ClientState.Objects.SubKinds; +using Dalamud.Game.ClientState.Objects.Types; using Dalamud.Game.Gui.Addons; using Dalamud.Game.Gui.Toast; using Dalamud.Game.Text; @@ -37,8 +36,8 @@ namespace Dalamud.Interface.Internal.Windows private string serverOpString; private DataKind currentKind; - private bool drawActors = false; - private float maxActorDrawDistance = 20; + private bool drawCharas = false; + private float maxCharaDrawDistance = 20; private string inputSig = string.Empty; private IntPtr sigResult = IntPtr.Zero; @@ -50,6 +49,7 @@ namespace Dalamud.Interface.Internal.Windows private IntPtr findAgentInterfacePtr; private bool resolveGameData = false; + private bool resolveObjects = false; private UIDebug addonInspector = null; @@ -89,10 +89,11 @@ namespace Dalamud.Interface.Internal.Windows { Server_OpCode, Address, - Actor_Table, + Object_Table, Fate_Table, Font_Test, Party_List, + Buddy_List, Plugin_IPC, Condition, Gauge, @@ -127,18 +128,23 @@ namespace Dalamud.Interface.Internal.Windows if (string.IsNullOrEmpty(dataKind)) return; - if (dataKind == "ai") - dataKind = "Addon Inspector"; + dataKind = dataKind switch + { + "ai" => "Addon Inspector", + "at" => "Object Table", // Actor Table + "ot" => "Object Table", + _ => dataKind, + }; dataKind = dataKind.Replace(" ", string.Empty).ToLower(); - var dataKinds = Enum.GetValues(typeof(DataKind)) + var matched = Enum.GetValues(typeof(DataKind)) .Cast() - .Where(k => nameof(k).Replace("_", string.Empty).ToLower() == dataKind) - .ToList(); + .Where(k => Enum.GetName(typeof(DataKind), k).Replace("_", string.Empty).ToLower() == dataKind) + .FirstOrDefault(); - if (dataKinds.Count > 0) + if (matched != default) { - this.currentKind = dataKinds.First(); + this.currentKind = matched; } else { @@ -189,8 +195,8 @@ namespace Dalamud.Interface.Internal.Windows this.DrawAddress(); break; - case DataKind.Actor_Table: - this.DrawActorTable(); + case DataKind.Object_Table: + this.DrawObjectTable(); break; case DataKind.Fate_Table: @@ -205,6 +211,10 @@ namespace Dalamud.Interface.Internal.Windows this.DrawPartyList(); break; + case DataKind.Buddy_List: + this.DrawBuddyList(); + break; + case DataKind.Plugin_IPC: this.DrawPluginIPC(); break; @@ -309,24 +319,18 @@ namespace Dalamud.Interface.Internal.Windows } } - private void DrawActorTable() + private void DrawObjectTable() { var stateString = string.Empty; - // LocalPlayer is null in a number of situations (at least with the current visible-actors list) - // which would crash here. - if (this.dalamud.ClientState.Actors.Length == 0) - { - ImGui.TextUnformatted("Data not ready."); - } - else if (this.dalamud.ClientState.LocalPlayer == null) + if (this.dalamud.ClientState.LocalPlayer == null) { ImGui.TextUnformatted("LocalPlayer null."); } else { stateString += $"FrameworkBase: {this.dalamud.Framework.Address.BaseAddress.ToInt64():X}\n"; - stateString += $"ActorTableLen: {this.dalamud.ClientState.Actors.Length}\n"; + stateString += $"ObjectTableLen: {this.dalamud.ClientState.Objects.Length}\n"; stateString += $"LocalPlayerName: {this.dalamud.ClientState.LocalPlayer.Name}\n"; stateString += $"CurrentWorldName: {(this.resolveGameData ? this.dalamud.ClientState.LocalPlayer.CurrentWorld.GameData.Name : this.dalamud.ClientState.LocalPlayer.CurrentWorld.Id.ToString())}\n"; stateString += $"HomeWorldName: {(this.resolveGameData ? this.dalamud.ClientState.LocalPlayer.HomeWorld.GameData.Name : this.dalamud.ClientState.LocalPlayer.HomeWorld.Id.ToString())}\n"; @@ -336,29 +340,29 @@ namespace Dalamud.Interface.Internal.Windows ImGui.TextUnformatted(stateString); - ImGui.Checkbox("Draw actors on screen", ref this.drawActors); - ImGui.SliderFloat("Draw Distance", ref this.maxActorDrawDistance, 2f, 40f); + ImGui.Checkbox("Draw characters on screen", ref this.drawCharas); + ImGui.SliderFloat("Draw Distance", ref this.maxCharaDrawDistance, 2f, 40f); - for (var i = 0; i < this.dalamud.ClientState.Actors.Length; i++) + for (var i = 0; i < this.dalamud.ClientState.Objects.Length; i++) { - var actor = this.dalamud.ClientState.Actors[i]; + var obj = this.dalamud.ClientState.Objects[i]; - if (actor == null) + if (obj == null) continue; - this.PrintActor(actor, i.ToString()); + this.PrintGameObject(obj, i.ToString()); - if (this.drawActors && this.dalamud.Framework.Gui.WorldToScreen(actor.Position, out var screenCoords)) + if (this.drawCharas && this.dalamud.Framework.Gui.WorldToScreen(obj.Position, out var screenCoords)) { // So, while WorldToScreen will return false if the point is off of game client screen, to // to avoid performance issues, we have to manually determine if creating a window would // produce a new viewport, and skip rendering it if so - var actorText = $"{actor.Address.ToInt64():X}:{actor.ActorId:X}[{i}] - {actor.ObjectKind} - {actor.Name}"; + var objectText = $"{obj.Address.ToInt64():X}:{obj.ObjectId:X}[{i}] - {obj.ObjectKind} - {obj.Name}"; var screenPos = ImGui.GetMainViewport().Pos; var screenSize = ImGui.GetMainViewport().Size; - var windowSize = ImGui.CalcTextSize(actorText); + var windowSize = ImGui.CalcTextSize(objectText); // Add some extra safety padding windowSize.X += ImGui.GetStyle().WindowPadding.X + 10; @@ -368,12 +372,12 @@ namespace Dalamud.Interface.Internal.Windows screenCoords.Y + windowSize.Y > screenPos.Y + screenSize.Y) continue; - if (actor.YalmDistanceX > this.maxActorDrawDistance) + if (obj.YalmDistanceX > this.maxCharaDrawDistance) continue; ImGui.SetNextWindowPos(new Vector2(screenCoords.X, screenCoords.Y)); - ImGui.SetNextWindowBgAlpha(Math.Max(1f - (actor.YalmDistanceX / this.maxActorDrawDistance), 0.2f)); + ImGui.SetNextWindowBgAlpha(Math.Max(1f - (obj.YalmDistanceX / this.maxCharaDrawDistance), 0.2f)); if (ImGui.Begin( $"Actor{i}##ActorWindow{i}", ImGuiWindowFlags.NoDecoration | @@ -384,7 +388,7 @@ namespace Dalamud.Interface.Internal.Windows ImGuiWindowFlags.NoDocking | ImGuiWindowFlags.NoFocusOnAppearing | ImGuiWindowFlags.NoNav)) - ImGui.Text(actorText); + ImGui.Text(objectText); ImGui.End(); } } @@ -453,30 +457,118 @@ namespace Dalamud.Interface.Internal.Windows private void DrawPartyList() { - var partyString = string.Empty; + ImGui.Checkbox("Resolve Actors", ref this.resolveObjects); - if (this.dalamud.ClientState.PartyList.Length == 0) + ImGui.Text($"GroupManager: {this.dalamud.ClientState.PartyList.GroupManagerAddress.ToInt64():X}"); + ImGui.Text($"GroupList: {this.dalamud.ClientState.PartyList.GroupListAddress.ToInt64():X}"); + ImGui.Text($"AllianceList: {this.dalamud.ClientState.PartyList.AllianceListAddress.ToInt64():X}"); + + ImGui.Text($"{this.dalamud.ClientState.PartyList.Length} Members"); + + for (var i = 0; i < this.dalamud.ClientState.PartyList.Length; i++) { - ImGui.TextUnformatted("Data not ready."); - } - else - { - partyString += $"{this.dalamud.ClientState.PartyList.Count} Members\n"; - for (var i = 0; i < this.dalamud.ClientState.PartyList.Count; i++) + var member = this.dalamud.ClientState.PartyList[i]; + if (member == null) { - var member = this.dalamud.ClientState.PartyList[i]; - if (member == null) - { - partyString += - $"[{i}] was null\n"; - continue; - } - - partyString += - $"[{i}] {member.CharacterName} - {member.ObjectKind} - {member.Actor.ActorId}\n"; + ImGui.Text($"[{i}] was null"); + continue; } - ImGui.TextUnformatted(partyString); + ImGui.Text($"[{i}] {member.Address.ToInt64():X} - {member.Name} - {member.GameObject.ObjectId}"); + if (this.resolveObjects) + { + var actor = member.GameObject; + if (actor == null) + { + ImGui.Text("Actor was null"); + } + else + { + this.PrintGameObject(actor, "-"); + } + } + } + } + + private void DrawBuddyList() + { + ImGui.Checkbox("Resolve Actors", ref this.resolveObjects); + + ImGui.Text($"BuddyList: {this.dalamud.ClientState.BuddyList.BuddyListAddress.ToInt64():X}"); + { + var member = this.dalamud.ClientState.BuddyList.CompanionBuddy; + if (member == null) + { + ImGui.Text("[Companion] null"); + } + else + { + ImGui.Text($"[Companion] {member.Address.ToInt64():X} - {member.ObjectId} - {member.DataID}"); + if (this.resolveObjects) + { + var actor = member.Actor; + if (actor == null) + { + ImGui.Text("Actor was null"); + } + else + { + this.PrintGameObject(actor, "-"); + } + } + } + } + + { + var member = this.dalamud.ClientState.BuddyList.PetBuddy; + if (member == null) + { + ImGui.Text("[Pet] null"); + } + else + { + ImGui.Text($"[Pet] {member.Address.ToInt64():X} - {member.ObjectId} - {member.DataID}"); + if (this.resolveObjects) + { + var actor = member.Actor; + if (actor == null) + { + ImGui.Text("Actor was null"); + } + else + { + this.PrintGameObject(actor, "-"); + } + } + } + } + + { + var count = this.dalamud.ClientState.BuddyList.Length; + if (count == 0) + { + ImGui.Text("[BattleBuddy] None present"); + } + else + { + for (var i = 0; i < count; i++) + { + var member = this.dalamud.ClientState.BuddyList[i]; + ImGui.Text($"[BattleBuddy] [{i}] {member.Address.ToInt64():X} - {member.ObjectId} - {member.DataID}"); + if (this.resolveObjects) + { + var actor = member.Actor; + if (actor == null) + { + ImGui.Text("Actor was null"); + } + else + { + this.PrintGameObject(actor, "-"); + } + } + } + } } } @@ -783,26 +875,26 @@ namespace Dalamud.Interface.Internal.Windows { var targetMgr = this.dalamud.ClientState.Targets; - if (targetMgr.CurrentTarget != null) + if (targetMgr.Target != null) { - this.PrintActor(targetMgr.CurrentTarget, "CurrentTarget"); - Util.ShowObject(targetMgr.CurrentTarget); + this.PrintGameObject(targetMgr.Target, "CurrentTarget"); + Util.ShowObject(targetMgr.Target); } if (targetMgr.FocusTarget != null) - this.PrintActor(targetMgr.FocusTarget, "FocusTarget"); + this.PrintGameObject(targetMgr.FocusTarget, "FocusTarget"); if (targetMgr.MouseOverTarget != null) - this.PrintActor(targetMgr.MouseOverTarget, "MouseOverTarget"); + this.PrintGameObject(targetMgr.MouseOverTarget, "MouseOverTarget"); if (targetMgr.PreviousTarget != null) - this.PrintActor(targetMgr.PreviousTarget, "PreviousTarget"); + this.PrintGameObject(targetMgr.PreviousTarget, "PreviousTarget"); if (targetMgr.SoftTarget != null) - this.PrintActor(targetMgr.SoftTarget, "SoftTarget"); + this.PrintGameObject(targetMgr.SoftTarget, "SoftTarget"); if (ImGui.Button("Clear CT")) - targetMgr.ClearCurrentTarget(); + targetMgr.ClearTarget(); if (ImGui.Button("Clear FT")) targetMgr.ClearFocusTarget(); @@ -812,7 +904,7 @@ namespace Dalamud.Interface.Internal.Windows if (localPlayer != null) { if (ImGui.Button("Set CT")) - targetMgr.SetCurrentTarget(localPlayer); + targetMgr.SetTarget(localPlayer); if (ImGui.Button("Set FT")) targetMgr.SetFocusTarget(localPlayer); @@ -924,9 +1016,10 @@ namespace Dalamud.Interface.Internal.Windows $"L3 {resolve(GamepadButtons.L3)} " + $"R3 {resolve(GamepadButtons.R3)} "); } -#if DEBUG + ImGui.Text($"GamepadInput 0x{this.dalamud.ClientState.GamepadState.GamepadInputAddress.ToInt64():X}"); +#if DEBUG if (ImGui.IsItemHovered()) ImGui.SetMouseCursor(ImGuiMouseCursor.Hand); @@ -969,15 +1062,15 @@ namespace Dalamud.Interface.Internal.Windows } } - private void PrintActor(Actor actor, string tag) + private void PrintGameObject(GameObject actor, string tag) { var actorString = - $"{actor.Address.ToInt64():X}:{actor.ActorId:X}[{tag}] - {actor.ObjectKind} - {actor.Name} - X{actor.Position.X} Y{actor.Position.Y} Z{actor.Position.Z} D{actor.YalmDistanceX} R{actor.Rotation} - Target: {actor.TargetActorID:X}\n"; + $"{actor.Address.ToInt64():X}:{actor.ObjectId:X}[{tag}] - {actor.ObjectKind} - {actor.Name} - X{actor.Position.X} Y{actor.Position.Y} Z{actor.Position.Z} D{actor.YalmDistanceX} R{actor.Rotation} - Target: {actor.TargetObjectId:X}\n"; if (actor is Npc npc) - actorString += $" DataId: {npc.BaseId} NameId:{npc.NameId}\n"; + actorString += $" DataId: {npc.DataId} NameId:{npc.NameId}\n"; - if (actor is Chara chara) + if (actor is Character chara) { actorString += $" Level: {chara.Level} ClassJob: {(this.resolveGameData ? chara.ClassJob.GameData.Name : chara.ClassJob.Id.ToString())} CHP: {chara.CurrentHp} MHP: {chara.MaxHp} CMP: {chara.CurrentMp} MMP: {chara.MaxMp}\n Customize: {BitConverter.ToString(chara.Customize).Replace("-", " ")} StatusFlags: {chara.StatusFlags}\n"; diff --git a/Dalamud/Interface/UiBuilder.cs b/Dalamud/Interface/UiBuilder.cs index 71ce244f7..adf404978 100644 --- a/Dalamud/Interface/UiBuilder.cs +++ b/Dalamud/Interface/UiBuilder.cs @@ -2,7 +2,6 @@ using System; using System.Collections.Generic; using System.Diagnostics; -using Dalamud.Game.ClientState; using Dalamud.Game.ClientState.Conditions; using Dalamud.Interface.Internal; using ImGuiNET;