diff --git a/Dalamud.CorePlugin/PluginImpl.cs b/Dalamud.CorePlugin/PluginImpl.cs index 52f54d140..15b5ac1e3 100644 --- a/Dalamud.CorePlugin/PluginImpl.cs +++ b/Dalamud.CorePlugin/PluginImpl.cs @@ -73,7 +73,7 @@ namespace Dalamud.CorePlugin Log.Information($"CorePlugin : DefaultFontHandle.ImFontChanged called {fc}"); }; - Service.Get().AddHandler("/coreplug", new(this.OnCommand) { HelpMessage = "Access the plugin." }); + Service.Get().AddHandler("/coreplug", new CommandInfo(this.OnCommand) { HelpMessage = "Access the plugin." }); log.Information("CorePlugin ctor!"); } diff --git a/Dalamud/Game/ClientState/Aetherytes/AetheryteEntry.cs b/Dalamud/Game/ClientState/Aetherytes/AetheryteEntry.cs index 1c169d37a..f723d7266 100644 --- a/Dalamud/Game/ClientState/Aetherytes/AetheryteEntry.cs +++ b/Dalamud/Game/ClientState/Aetherytes/AetheryteEntry.cs @@ -4,9 +4,9 @@ using FFXIVClientStructs.FFXIV.Client.Game.UI; namespace Dalamud.Game.ClientState.Aetherytes; /// -/// This class represents an entry in the Aetheryte list. +/// Class representing an aetheryte entry available to the game. /// -public sealed class AetheryteEntry +internal sealed class AetheryteEntry : IAetheryteEntry { private readonly TeleportInfo data; @@ -19,53 +19,90 @@ public sealed class AetheryteEntry this.data = data; } + /// + public uint AetheryteId => this.data.AetheryteId; + + /// + public uint TerritoryId => this.data.TerritoryId; + + /// + public byte SubIndex => this.data.SubIndex; + + /// + public byte Ward => this.data.Ward; + + /// + public byte Plot => this.data.Plot; + + /// + public uint GilCost => this.data.GilCost; + + /// + public bool IsFavourite => this.data.IsFavourite != 0; + + /// + public bool IsSharedHouse => this.data.IsSharedHouse; + + /// + public bool IsApartment => this.data.IsApartment; + + /// + public ExcelResolver AetheryteData => new(this.AetheryteId); +} + +/// +/// Interface representing an aetheryte entry available to the game. +/// +public interface IAetheryteEntry +{ /// /// Gets the Aetheryte ID. /// - public uint AetheryteId => this.data.AetheryteId; + uint AetheryteId { get; } /// /// Gets the Territory ID. /// - public uint TerritoryId => this.data.TerritoryId; + uint TerritoryId { get; } /// /// Gets the SubIndex used when there can be multiple Aetherytes with the same ID (Private/Shared Estates etc.). /// - public byte SubIndex => this.data.SubIndex; + byte SubIndex { get; } /// /// Gets the Ward. Zero if not a Shared Estate. /// - public byte Ward => this.data.Ward; + byte Ward { get; } /// /// Gets the Plot. Zero if not a Shared Estate. /// - public byte Plot => this.data.Plot; + byte Plot { get; } /// /// Gets the Cost in Gil to Teleport to this location. /// - public uint GilCost => this.data.GilCost; + uint GilCost { get; } /// /// Gets a value indicating whether the LocalPlayer has set this Aetheryte as Favorite or not. /// - public bool IsFavourite => this.data.IsFavourite != 0; + bool IsFavourite { get; } /// /// Gets a value indicating whether this Aetheryte is a Shared Estate or not. /// - public bool IsSharedHouse => this.data.IsSharedHouse; + bool IsSharedHouse { get; } /// /// Gets a value indicating whether this Aetheryte is an Apartment or not. /// - public bool IsApartment => this.data.IsApartment; + bool IsApartment { get; } /// /// Gets the Aetheryte data related to this aetheryte. /// - public ExcelResolver AetheryteData => new(this.AetheryteId); + ExcelResolver AetheryteData { get; } } + diff --git a/Dalamud/Game/ClientState/Aetherytes/AetheryteList.cs b/Dalamud/Game/ClientState/Aetherytes/AetheryteList.cs index eee4d0f19..3eadefd0f 100644 --- a/Dalamud/Game/ClientState/Aetherytes/AetheryteList.cs +++ b/Dalamud/Game/ClientState/Aetherytes/AetheryteList.cs @@ -49,7 +49,7 @@ internal sealed unsafe partial class AetheryteList : IServiceType, IAetheryteLis } /// - public AetheryteEntry? this[int index] + public IAetheryteEntry? this[int index] { get { @@ -84,7 +84,7 @@ internal sealed partial class AetheryteList public int Count => this.Length; /// - public IEnumerator GetEnumerator() + public IEnumerator GetEnumerator() { for (var i = 0; i < this.Length; i++) { diff --git a/Dalamud/Game/ClientState/Buddy/BuddyList.cs b/Dalamud/Game/ClientState/Buddy/BuddyList.cs index 004e23b2a..bafe9ded6 100644 --- a/Dalamud/Game/ClientState/Buddy/BuddyList.cs +++ b/Dalamud/Game/ClientState/Buddy/BuddyList.cs @@ -56,7 +56,7 @@ internal sealed partial class BuddyList : IServiceType, IBuddyList } /// - public BuddyMember? CompanionBuddy + public IBuddyMember? CompanionBuddy { get { @@ -66,7 +66,7 @@ internal sealed partial class BuddyList : IServiceType, IBuddyList } /// - public BuddyMember? PetBuddy + public IBuddyMember? PetBuddy { get { @@ -85,7 +85,7 @@ internal sealed partial class BuddyList : IServiceType, IBuddyList private unsafe FFXIVClientStructs.FFXIV.Client.Game.UI.Buddy* BuddyListStruct => (FFXIVClientStructs.FFXIV.Client.Game.UI.Buddy*)this.BuddyListAddress; /// - public BuddyMember? this[int index] + public IBuddyMember? this[int index] { get { @@ -116,7 +116,7 @@ internal sealed partial class BuddyList : IServiceType, IBuddyList } /// - public BuddyMember? CreateBuddyMemberReference(IntPtr address) + public IBuddyMember? CreateBuddyMemberReference(IntPtr address) { if (this.clientState.LocalContentId == 0) return null; @@ -138,10 +138,10 @@ internal sealed partial class BuddyList : IServiceType, IBuddyList internal sealed partial class BuddyList { /// - int IReadOnlyCollection.Count => this.Length; + int IReadOnlyCollection.Count => this.Length; /// - public IEnumerator GetEnumerator() + public IEnumerator GetEnumerator() { for (var i = 0; i < this.Length; i++) { diff --git a/Dalamud/Game/ClientState/Buddy/BuddyMember.cs b/Dalamud/Game/ClientState/Buddy/BuddyMember.cs index 3b5c2f910..1fdb794cc 100644 --- a/Dalamud/Game/ClientState/Buddy/BuddyMember.cs +++ b/Dalamud/Game/ClientState/Buddy/BuddyMember.cs @@ -7,7 +7,7 @@ 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 +internal unsafe class BuddyMember : IBuddyMember { [ServiceManager.ServiceDependency] private readonly ObjectTable objectTable = Service.Get(); @@ -21,15 +21,50 @@ public unsafe class BuddyMember this.Address = address; } + /// + public IntPtr Address { get; } + + /// + public uint ObjectId => this.Struct->EntityId; + + /// + public IGameObject? GameObject => this.objectTable.SearchById(this.ObjectId); + + /// + public uint CurrentHP => this.Struct->CurrentHealth; + + /// + public uint MaxHP => this.Struct->MaxHealth; + + /// + public uint DataID => this.Struct->DataId; + + /// + public ExcelResolver MountData => new(this.DataID); + + /// + public ExcelResolver PetData => new(this.DataID); + + /// + public ExcelResolver TrustData => new(this.DataID); + + private FFXIVClientStructs.FFXIV.Client.Game.UI.Buddy.BuddyMember* Struct => (FFXIVClientStructs.FFXIV.Client.Game.UI.Buddy.BuddyMember*)this.Address; +} + +/// +/// Interface representing represents a buddy such as the chocobo companion, summoned pets, squadron groups and trust parties. +/// +public interface IBuddyMember +{ /// /// Gets the address of the buddy in memory. /// - public IntPtr Address { get; } + IntPtr Address { get; } /// /// Gets the object ID of this buddy. /// - public uint ObjectId => this.Struct->EntityId; + unsafe uint ObjectId { get; } /// /// Gets the actor associated with this buddy. @@ -37,37 +72,35 @@ public unsafe class BuddyMember /// /// This iterates the actor table, it should be used with care. /// - public GameObject? GameObject => this.objectTable.SearchById(this.ObjectId); + IGameObject? GameObject { get; } /// /// Gets the current health of this buddy. /// - public uint CurrentHP => this.Struct->CurrentHealth; + unsafe uint CurrentHP { get; } /// /// Gets the maximum health of this buddy. /// - public uint MaxHP => this.Struct->MaxHealth; + unsafe uint MaxHP { get; } /// /// Gets the data ID of this buddy. /// - public uint DataID => this.Struct->DataId; + unsafe uint DataID { get; } /// /// Gets the Mount data related to this buddy. It should only be used with companion buddies. /// - public ExcelResolver MountData => new(this.DataID); + ExcelResolver MountData { get; } /// /// Gets the Pet data related to this buddy. It should only be used with pet buddies. /// - public ExcelResolver PetData => new(this.DataID); + ExcelResolver PetData { get; } /// /// Gets the Trust data related to this buddy. It should only be used with battle buddies. /// - public ExcelResolver TrustData => new(this.DataID); - - private FFXIVClientStructs.FFXIV.Client.Game.UI.Buddy.BuddyMember* Struct => (FFXIVClientStructs.FFXIV.Client.Game.UI.Buddy.BuddyMember*)this.Address; + ExcelResolver TrustData { get; } } diff --git a/Dalamud/Game/ClientState/ClientState.cs b/Dalamud/Game/ClientState/ClientState.cs index 4a6bee95c..d6fd8f37b 100644 --- a/Dalamud/Game/ClientState/ClientState.cs +++ b/Dalamud/Game/ClientState/ClientState.cs @@ -103,7 +103,7 @@ internal sealed class ClientState : IInternalDisposableService, IClientState } /// - public PlayerCharacter? LocalPlayer => Service.GetNullable()?[0] as PlayerCharacter; + public IPlayerCharacter? LocalPlayer => Service.GetNullable()?[0] as IPlayerCharacter; /// public ulong LocalContentId => (ulong)Marshal.ReadInt64(this.address.LocalContentId); @@ -275,7 +275,7 @@ internal class ClientStatePluginScoped : IInternalDisposableService, IClientStat public uint MapId => this.clientStateService.MapId; /// - public PlayerCharacter? LocalPlayer => this.clientStateService.LocalPlayer; + public IPlayerCharacter? LocalPlayer => this.clientStateService.LocalPlayer; /// public ulong LocalContentId => this.clientStateService.LocalContentId; diff --git a/Dalamud/Game/ClientState/Fates/Fate.cs b/Dalamud/Game/ClientState/Fates/Fate.cs index 47b32dce0..bb64c0023 100644 --- a/Dalamud/Game/ClientState/Fates/Fate.cs +++ b/Dalamud/Game/ClientState/Fates/Fate.cs @@ -10,7 +10,7 @@ namespace Dalamud.Game.ClientState.Fates; /// /// This class represents an FFXIV Fate. /// -public unsafe partial class Fate : IEquatable +internal unsafe partial class Fate { /// /// Initializes a new instance of the class. @@ -21,9 +21,7 @@ public unsafe partial class Fate : IEquatable this.Address = address; } - /// - /// Gets the address of this Fate in memory. - /// + /// public IntPtr Address { get; } private FFXIVClientStructs.FFXIV.Client.Game.Fate.FateContext* Struct => (FFXIVClientStructs.FFXIV.Client.Game.Fate.FateContext*)this.Address; @@ -63,10 +61,10 @@ public unsafe partial class Fate : IEquatable public bool IsValid() => IsValid(this); /// - bool IEquatable.Equals(Fate other) => this.FateId == other?.FateId; + bool IEquatable.Equals(IFate other) => this.FateId == other?.FateId; /// - public override bool Equals(object obj) => ((IEquatable)this).Equals(obj as Fate); + public override bool Equals(object obj) => ((IEquatable)this).Equals(obj as IFate); /// public override int GetHashCode() => this.FateId.GetHashCode(); @@ -75,60 +73,171 @@ public unsafe partial class Fate : IEquatable /// /// This class represents an FFXIV Fate. /// -public unsafe partial class Fate +internal unsafe partial class Fate : IFate { - /// - /// Gets the Fate ID of this . - /// + /// public ushort FateId => this.Struct->FateId; - /// - /// Gets game data linked to this Fate. - /// + /// public Lumina.Excel.GeneratedSheets.Fate GameData => Service.Get().GetExcelSheet().GetRow(this.FateId); - /// - /// Gets the time this started. - /// + /// public int StartTimeEpoch => this.Struct->StartTimeEpoch; - /// - /// Gets how long this will run. - /// + /// public short Duration => this.Struct->Duration; - /// - /// Gets the remaining time in seconds for this . - /// + /// public long TimeRemaining => this.StartTimeEpoch + this.Duration - DateTimeOffset.Now.ToUnixTimeSeconds(); - /// - /// Gets the displayname of this . - /// + /// public SeString Name => MemoryHelper.ReadSeString(&this.Struct->Name); - /// - /// Gets the state of this (Running, Ended, Failed, Preparation, WaitingForEnd). - /// + /// + public SeString Description => MemoryHelper.ReadSeString(&this.Struct->Description); + + /// + public SeString Objective => MemoryHelper.ReadSeString(&this.Struct->Objective); + + /// public FateState State => (FateState)this.Struct->State; - /// - /// Gets the progress amount of this . - /// + /// + public byte HandInCount => this.Struct->HandInCount; + + + /// public byte Progress => this.Struct->Progress; - /// - /// Gets the level of this . - /// + /// + public bool HasExpBonus => this.Struct->IsExpBonus; + + /// + public uint IconId => this.Struct->IconId; + + /// public byte Level => this.Struct->Level; - /// - /// Gets the position of this . - /// + /// + public byte MaxLevel => this.Struct->MaxLevel; + + /// public Vector3 Position => this.Struct->Location; + /// + public float Radius => this.Struct->Radius; + + /// + public uint MapIconId => this.Struct->MapIconId; + /// /// Gets the territory this is located in. /// public ExcelResolver TerritoryType => new(this.Struct->TerritoryId); } + +/// +/// Interface representing an fate entry that can be seen in the current area. +/// +public interface IFate : IEquatable +{ + /// + /// Gets the Fate ID of this . + /// + ushort FateId { get; } + + /// + /// Gets game data linked to this Fate. + /// + Lumina.Excel.GeneratedSheets.Fate GameData { get; } + + /// + /// Gets the time this started. + /// + int StartTimeEpoch { get; } + + /// + /// Gets how long this will run. + /// + short Duration { get; } + + /// + /// Gets the remaining time in seconds for this . + /// + long TimeRemaining { get; } + + /// + /// Gets the displayname of this . + /// + SeString Name { get; } + + /// + /// Gets the description of this . + /// + SeString Description { get; } + + /// + /// Gets the objective of this . + /// + SeString Objective { get; } + + /// + /// Gets the state of this (Running, Ended, Failed, Preparation, WaitingForEnd). + /// + FateState State { get; } + + /// + /// Gets the hand in count of this . + /// + byte HandInCount { get; } + + /// + /// Gets the progress amount of this . + /// + byte Progress { get; } + + /// + /// Gets a value indicating whether or not this has a EXP bonus. + /// + bool HasExpBonus { get; } + + /// + /// Gets the icon id of this . + /// + uint IconId { get; } + + /// + /// Gets the level of this . + /// + byte Level { get; } + + /// + /// Gets the max level level of this . + /// + byte MaxLevel { get; } + + /// + /// Gets the position of this . + /// + Vector3 Position { get; } + + /// + /// Gets the radius of this . + /// + float Radius { get; } + + /// + /// Gets the map icon id of this . + /// + uint MapIconId { get; } + + /// + /// Gets the territory this is located in. + /// + ExcelResolver TerritoryType { get; } + + /// + /// Gets the address of this Fate in memory. + /// + IntPtr Address { get; } +} diff --git a/Dalamud/Game/ClientState/Fates/FateTable.cs b/Dalamud/Game/ClientState/Fates/FateTable.cs index 76ae5302c..e28a8d113 100644 --- a/Dalamud/Game/ClientState/Fates/FateTable.cs +++ b/Dalamud/Game/ClientState/Fates/FateTable.cs @@ -69,7 +69,7 @@ internal sealed partial class FateTable : IServiceType, IFateTable private unsafe FFXIVClientStructs.FFXIV.Client.Game.Fate.FateManager* Struct => (FFXIVClientStructs.FFXIV.Client.Game.Fate.FateManager*)this.FateTableAddress; /// - public Fate? this[int index] + public IFate? this[int index] { get { @@ -78,6 +78,20 @@ internal sealed partial class FateTable : IServiceType, IFateTable } } + /// + public bool IsValid(IFate fate) + { + var clientState = Service.GetNullable(); + + if (fate == null || clientState == null) + return false; + + if (clientState.LocalContentId == 0) + return false; + + return true; + } + /// public unsafe IntPtr GetFateAddress(int index) { @@ -92,7 +106,7 @@ internal sealed partial class FateTable : IServiceType, IFateTable } /// - public Fate? CreateFateReference(IntPtr offset) + public IFate? CreateFateReference(IntPtr offset) { var clientState = Service.Get(); @@ -112,10 +126,10 @@ internal sealed partial class FateTable : IServiceType, IFateTable internal sealed partial class FateTable { /// - int IReadOnlyCollection.Count => this.Length; + int IReadOnlyCollection.Count => this.Length; /// - public IEnumerator GetEnumerator() + public IEnumerator GetEnumerator() { for (var i = 0; i < this.Length; i++) { diff --git a/Dalamud/Game/ClientState/Objects/ObjectTable.cs b/Dalamud/Game/ClientState/Objects/ObjectTable.cs index 34ebc9de2..a9bf057c5 100644 --- a/Dalamud/Game/ClientState/Objects/ObjectTable.cs +++ b/Dalamud/Game/ClientState/Objects/ObjectTable.cs @@ -71,7 +71,7 @@ internal sealed partial class ObjectTable : IServiceType, IObjectTable public int Length => ObjectTableLength; /// - public GameObject? this[int index] + public IGameObject? this[int index] { get { @@ -82,7 +82,7 @@ internal sealed partial class ObjectTable : IServiceType, IObjectTable } /// - public GameObject? SearchById(ulong gameObjectId) + public IGameObject? SearchById(ulong gameObjectId) { _ = this.WarnMultithreadedUsage(); @@ -107,7 +107,7 @@ internal sealed partial class ObjectTable : IServiceType, IObjectTable } /// - public unsafe GameObject? CreateObjectReference(nint address) + public unsafe IGameObject? CreateObjectReference(nint address) { _ = this.WarnMultithreadedUsage(); @@ -216,7 +216,7 @@ internal sealed partial class ObjectTable : IServiceType, IObjectTable internal sealed partial class ObjectTable { /// - public IEnumerator GetEnumerator() + public IEnumerator GetEnumerator() { // If something's trying to enumerate outside the framework thread, we use the ObjectPool. if (this.WarnMultithreadedUsage()) @@ -246,7 +246,7 @@ internal sealed partial class ObjectTable /// IEnumerator IEnumerable.GetEnumerator() => this.GetEnumerator(); - private sealed class Enumerator : IEnumerator, IResettable + private sealed class Enumerator : IEnumerator, IResettable { private readonly int slotId; private ObjectTable? owner; @@ -261,7 +261,7 @@ internal sealed partial class ObjectTable this.slotId = slotId; } - public GameObject Current { get; private set; } = null!; + public IGameObject Current { get; private set; } = null!; object IEnumerator.Current => this.Current; diff --git a/Dalamud/Game/ClientState/Objects/SubKinds/BattleNpc.cs b/Dalamud/Game/ClientState/Objects/SubKinds/BattleNpc.cs index add7a7f9f..a021a9a92 100644 --- a/Dalamud/Game/ClientState/Objects/SubKinds/BattleNpc.cs +++ b/Dalamud/Game/ClientState/Objects/SubKinds/BattleNpc.cs @@ -5,7 +5,7 @@ namespace Dalamud.Game.ClientState.Objects.Types; /// /// This class represents a battle NPC. /// -public unsafe class BattleNpc : BattleChara +internal unsafe class BattleNpc : BattleChara, IBattleNpc { /// /// Initializes a new instance of the class. @@ -25,3 +25,14 @@ public unsafe class BattleNpc : BattleChara /// public override ulong TargetObjectId => this.Struct->Character.TargetId; } + +/// +/// A interface that represents a battle NPC. +/// +internal interface IBattleNpc +{ + /// + /// Gets the BattleNpc of this BattleNpc. + /// + BattleNpcSubKind BattleNpcKind { get; } +} diff --git a/Dalamud/Game/ClientState/Objects/SubKinds/EventObj.cs b/Dalamud/Game/ClientState/Objects/SubKinds/EventObj.cs index ee2b62f0f..43ad8cfd4 100644 --- a/Dalamud/Game/ClientState/Objects/SubKinds/EventObj.cs +++ b/Dalamud/Game/ClientState/Objects/SubKinds/EventObj.cs @@ -5,7 +5,7 @@ namespace Dalamud.Game.ClientState.Objects.SubKinds; /// /// This class represents an EventObj. /// -public unsafe class EventObj : GameObject +internal unsafe class EventObj : GameObject { /// /// Initializes a new instance of the class. diff --git a/Dalamud/Game/ClientState/Objects/SubKinds/Npc.cs b/Dalamud/Game/ClientState/Objects/SubKinds/Npc.cs index ee4f350a0..be1ad333c 100644 --- a/Dalamud/Game/ClientState/Objects/SubKinds/Npc.cs +++ b/Dalamud/Game/ClientState/Objects/SubKinds/Npc.cs @@ -5,7 +5,7 @@ namespace Dalamud.Game.ClientState.Objects.SubKinds; /// /// This class represents a NPC. /// -public unsafe class Npc : Character +internal unsafe class Npc : Character { /// /// Initializes a new instance of the class. diff --git a/Dalamud/Game/ClientState/Objects/SubKinds/PlayerCharacter.cs b/Dalamud/Game/ClientState/Objects/SubKinds/PlayerCharacter.cs index 34ab1e19e..ed7bcc1df 100644 --- a/Dalamud/Game/ClientState/Objects/SubKinds/PlayerCharacter.cs +++ b/Dalamud/Game/ClientState/Objects/SubKinds/PlayerCharacter.cs @@ -1,12 +1,19 @@ +using System.Numerics; + +using Dalamud.Game.ClientState.Objects.Enums; using Dalamud.Game.ClientState.Objects.Types; using Dalamud.Game.ClientState.Resolvers; +using Dalamud.Game.ClientState.Statuses; +using Dalamud.Game.Text.SeStringHandling; + +using Lumina.Excel.GeneratedSheets; namespace Dalamud.Game.ClientState.Objects.SubKinds; /// /// This class represents a player character. /// -public unsafe class PlayerCharacter : BattleChara +internal unsafe class PlayerCharacter : BattleChara, IPlayerCharacter { /// /// Initializes a new instance of the class. @@ -18,14 +25,10 @@ public unsafe class PlayerCharacter : BattleChara { } - /// - /// Gets the current world of the character. - /// + /// public ExcelResolver CurrentWorld => new(this.Struct->CurrentWorld); - /// - /// Gets the home world of the character. - /// + /// public ExcelResolver HomeWorld => new(this.Struct->HomeWorld); /// @@ -33,3 +36,19 @@ public unsafe class PlayerCharacter : BattleChara /// public override ulong TargetObjectId => this.Struct->LookAt.Controller.Params[0].TargetParam.TargetId; } + +/// +/// Interface representing a player character. +/// +public interface IPlayerCharacter : IBattleChara +{ + /// + /// Gets the current world of the character. + /// + unsafe ExcelResolver CurrentWorld { get; } + + /// + /// Gets the home world of the character. + /// + unsafe ExcelResolver HomeWorld { get; } +} diff --git a/Dalamud/Game/ClientState/Objects/TargetManager.cs b/Dalamud/Game/ClientState/Objects/TargetManager.cs index 97a736566..5d7ae8945 100644 --- a/Dalamud/Game/ClientState/Objects/TargetManager.cs +++ b/Dalamud/Game/ClientState/Objects/TargetManager.cs @@ -27,49 +27,49 @@ internal sealed unsafe class TargetManager : IServiceType, ITargetManager } /// - public GameObject? Target + public IGameObject? Target { get => this.objectTable.CreateObjectReference((IntPtr)Struct->Target); set => Struct->Target = (FFXIVClientStructs.FFXIV.Client.Game.Object.GameObject*)value?.Address; } /// - public GameObject? MouseOverTarget + public IGameObject? MouseOverTarget { get => this.objectTable.CreateObjectReference((IntPtr)Struct->MouseOverTarget); set => Struct->MouseOverTarget = (FFXIVClientStructs.FFXIV.Client.Game.Object.GameObject*)value?.Address; } /// - public GameObject? FocusTarget + public IGameObject? FocusTarget { get => this.objectTable.CreateObjectReference((IntPtr)Struct->FocusTarget); set => Struct->FocusTarget = (FFXIVClientStructs.FFXIV.Client.Game.Object.GameObject*)value?.Address; } /// - public GameObject? PreviousTarget + public IGameObject? PreviousTarget { get => this.objectTable.CreateObjectReference((IntPtr)Struct->PreviousTarget); set => Struct->PreviousTarget = (FFXIVClientStructs.FFXIV.Client.Game.Object.GameObject*)value?.Address; } /// - public GameObject? SoftTarget + public IGameObject? SoftTarget { get => this.objectTable.CreateObjectReference((IntPtr)Struct->SoftTarget); set => Struct->SoftTarget = (FFXIVClientStructs.FFXIV.Client.Game.Object.GameObject*)value?.Address; } /// - public GameObject? GPoseTarget + public IGameObject? GPoseTarget { get => this.objectTable.CreateObjectReference((IntPtr)Struct->GPoseTarget); set => Struct->GPoseTarget = (FFXIVClientStructs.FFXIV.Client.Game.Object.GameObject*)value?.Address; } /// - public GameObject? MouseOverNameplateTarget + public IGameObject? MouseOverNameplateTarget { get => this.objectTable.CreateObjectReference((IntPtr)Struct->MouseOverNameplateTarget); set => Struct->MouseOverNameplateTarget = (FFXIVClientStructs.FFXIV.Client.Game.Object.GameObject*)value?.Address; diff --git a/Dalamud/Game/ClientState/Objects/Types/BattleChara.cs b/Dalamud/Game/ClientState/Objects/Types/BattleChara.cs index a54be132f..3d6707ddf 100644 --- a/Dalamud/Game/ClientState/Objects/Types/BattleChara.cs +++ b/Dalamud/Game/ClientState/Objects/Types/BattleChara.cs @@ -6,7 +6,7 @@ namespace Dalamud.Game.ClientState.Objects.Types; /// /// This class represents the battle characters. /// -public unsafe class BattleChara : Character +internal unsafe class BattleChara : Character, IBattleChara { /// /// Initializes a new instance of the class. @@ -18,57 +18,32 @@ public unsafe class BattleChara : Character { } - /// - /// Gets the current status effects. - /// + /// public StatusList StatusList => new(this.Struct->GetStatusManager()); - /// - /// Gets a value indicating whether the chara is currently casting. - /// + /// public bool IsCasting => this.Struct->GetCastInfo()->IsCasting > 0; - /// - /// Gets a value indicating whether the cast is interruptible. - /// + /// public bool IsCastInterruptible => this.Struct->GetCastInfo()->Interruptible > 0; - /// - /// Gets the spell action type of the spell being cast by the actor. - /// + /// public byte CastActionType => (byte)this.Struct->GetCastInfo()->ActionType; - /// - /// Gets the spell action ID of the spell being cast by the actor. - /// + /// public uint CastActionId => this.Struct->GetCastInfo()->ActionId; - /// - /// Gets the object ID of the target currently being cast at by the chara. - /// + /// public ulong CastTargetObjectId => this.Struct->GetCastInfo()->TargetId; - /// - /// Gets the current casting time of the spell being cast by the chara. - /// + /// public float CurrentCastTime => this.Struct->GetCastInfo()->CurrentCastTime; - /// - /// Gets the total casting time of the spell being cast by the chara. - /// - /// - /// This can only be a portion of the total cast for some actions. - /// Use AdjustedTotalCastTime if you always need the total cast time. - /// + /// [Api10ToDo("Rename so it is not confused with AdjustedTotalCastTime")] public float TotalCastTime => this.Struct->GetCastInfo()->TotalCastTime; - /// - /// Gets the plus any adjustments from the game, such as Action offset 2B. Used for display purposes. - /// - /// - /// This is the actual total cast time for all actions. - /// + /// [Api10ToDo("Rename so it is not confused with TotalCastTime")] public float AdjustedTotalCastTime => this.Struct->GetCastInfo()->AdjustedTotalCastTime; @@ -77,3 +52,61 @@ public unsafe class BattleChara : Character /// protected internal new FFXIVClientStructs.FFXIV.Client.Game.Character.BattleChara* Struct => (FFXIVClientStructs.FFXIV.Client.Game.Character.BattleChara*)this.Address; } + +/// +/// Interface representing a battle character. +/// +public interface IBattleChara : ICharacter +{ + /// + /// Gets the current status effects. + /// + public StatusList StatusList { get; } + + /// + /// Gets a value indicating whether the chara is currently casting. + /// + public bool IsCasting { get; } + + /// + /// Gets a value indicating whether the cast is interruptible. + /// + public bool IsCastInterruptible { get; } + + /// + /// Gets the spell action type of the spell being cast by the actor. + /// + public byte CastActionType { get; } + + /// + /// Gets the spell action ID of the spell being cast by the actor. + /// + public uint CastActionId { get; } + + /// + /// Gets the object ID of the target currently being cast at by the chara. + /// + public ulong CastTargetObjectId { get; } + + /// + /// Gets the current casting time of the spell being cast by the chara. + /// + public float CurrentCastTime { get; } + + /// + /// Gets the total casting time of the spell being cast by the chara. + /// + /// + /// This can only be a portion of the total cast for some actions. + /// Use AdjustedTotalCastTime if you always need the total cast time. + /// + public float TotalCastTime { get; } + + /// + /// Gets the plus any adjustments from the game, such as Action offset 2B. Used for display purposes. + /// + /// + /// This is the actual total cast time for all actions. + /// + public float AdjustedTotalCastTime { get; } +} diff --git a/Dalamud/Game/ClientState/Objects/Types/Character.cs b/Dalamud/Game/ClientState/Objects/Types/Character.cs index 5de803d15..7f025118c 100644 --- a/Dalamud/Game/ClientState/Objects/Types/Character.cs +++ b/Dalamud/Game/ClientState/Objects/Types/Character.cs @@ -11,7 +11,7 @@ namespace Dalamud.Game.ClientState.Objects.Types; /// /// This class represents the base for non-static entities. /// -public unsafe class Character : GameObject +internal unsafe class Character : GameObject, ICharacter { /// /// Initializes a new instance of the class. @@ -23,70 +23,43 @@ public unsafe class Character : GameObject { } - /// - /// Gets the current HP of this Chara. - /// + /// public uint CurrentHp => this.Struct->CharacterData.Health; - /// - /// Gets the maximum HP of this Chara. - /// + /// public uint MaxHp => this.Struct->CharacterData.MaxHealth; - /// - /// Gets the current MP of this Chara. - /// + /// public uint CurrentMp => this.Struct->CharacterData.Mana; - /// - /// Gets the maximum MP of this Chara. - /// + /// public uint MaxMp => this.Struct->CharacterData.MaxMana; - /// - /// Gets the current GP of this Chara. - /// + /// public uint CurrentGp => this.Struct->CharacterData.GatheringPoints; - /// - /// Gets the maximum GP of this Chara. - /// + /// public uint MaxGp => this.Struct->CharacterData.MaxGatheringPoints; - /// - /// Gets the current CP of this Chara. - /// + /// public uint CurrentCp => this.Struct->CharacterData.CraftingPoints; - /// - /// Gets the maximum CP of this Chara. - /// + /// public uint MaxCp => this.Struct->CharacterData.MaxCraftingPoints; - /// - /// Gets the shield percentage of this Chara. - /// + /// public byte ShieldPercentage => this.Struct->CharacterData.ShieldValue; - /// - /// Gets the ClassJob of this Chara. - /// + /// public ExcelResolver ClassJob => new(this.Struct->CharacterData.ClassJob); - /// - /// Gets the level of this Chara. - /// + /// public byte Level => this.Struct->CharacterData.Level; - /// - /// Gets a byte array describing the visual appearance of this Chara. - /// Indexed by . - /// + /// public byte[] Customize => this.Struct->DrawData.CustomizeData.Data.ToArray(); - /// - /// Gets the Free Company tag of this chara. - /// + /// public SeString CompanyTag => MemoryHelper.ReadSeString((nint)Unsafe.AsPointer(ref this.Struct->FreeCompanyTag[0]), 6); /// @@ -94,14 +67,10 @@ public unsafe class Character : GameObject /// public override ulong TargetObjectId => this.Struct->TargetId; - /// - /// Gets the name ID of the character. - /// + /// public uint NameId => this.Struct->NameId; - /// - /// Gets the current online status of the character. - /// + /// public ExcelResolver OnlineStatus => new(this.Struct->CharacterData.OnlineStatus); /// @@ -123,3 +92,90 @@ public unsafe class Character : GameObject protected internal new FFXIVClientStructs.FFXIV.Client.Game.Character.Character* Struct => (FFXIVClientStructs.FFXIV.Client.Game.Character.Character*)this.Address; } + +/// +/// Interface representing a character. +/// +public interface ICharacter : IGameObject +{ + /// + /// Gets the current HP of this Chara. + /// + public uint CurrentHp { get; } + + /// + /// Gets the maximum HP of this Chara. + /// + public uint MaxHp { get; } + + /// + /// Gets the current MP of this Chara. + /// + public uint CurrentMp { get; } + + /// + /// Gets the maximum MP of this Chara. + /// + public uint MaxMp { get; } + + /// + /// Gets the current GP of this Chara. + /// + public uint CurrentGp { get; } + + /// + /// Gets the maximum GP of this Chara. + /// + public uint MaxGp { get; } + + /// + /// Gets the current CP of this Chara. + /// + public uint CurrentCp { get; } + + /// + /// Gets the maximum CP of this Chara. + /// + public uint MaxCp { get; } + + /// + /// Gets the shield percentage of this Chara. + /// + public byte ShieldPercentage { get; } + + /// + /// Gets the ClassJob of this Chara. + /// + public ExcelResolver ClassJob { get; } + + /// + /// Gets the level of this Chara. + /// + public byte Level { get; } + + /// + /// Gets a byte array describing the visual appearance of this Chara. + /// Indexed by . + /// + public byte[] Customize { get; } + + /// + /// Gets the Free Company tag of this chara. + /// + public SeString CompanyTag { get; } + + /// + /// Gets the name ID of the character. + /// + public uint NameId { get; } + + /// + /// Gets the current online status of the character. + /// + public ExcelResolver OnlineStatus { get; } + + /// + /// Gets the status flags. + /// + public StatusFlags StatusFlags { get; } +} diff --git a/Dalamud/Game/ClientState/Objects/Types/GameObject.cs b/Dalamud/Game/ClientState/Objects/Types/GameObject.cs index 3503ee836..083fbbcc3 100644 --- a/Dalamud/Game/ClientState/Objects/Types/GameObject.cs +++ b/Dalamud/Game/ClientState/Objects/Types/GameObject.cs @@ -10,7 +10,7 @@ namespace Dalamud.Game.ClientState.Objects.Types; /// /// This class represents a GameObject in FFXIV. /// -public unsafe partial class GameObject : IEquatable +internal partial class GameObject { /// /// Initializes a new instance of the class. @@ -54,7 +54,7 @@ public unsafe partial class GameObject : IEquatable /// /// The actor to check. /// True or false. - public static bool IsValid(GameObject? actor) + public static bool IsValid(IGameObject? actor) { var clientState = Service.GetNullable(); @@ -74,10 +74,10 @@ public unsafe partial class GameObject : IEquatable public bool IsValid() => IsValid(this); /// - bool IEquatable.Equals(GameObject other) => this.GameObjectId == other?.GameObjectId; + bool IEquatable.Equals(IGameObject other) => this.GameObjectId == other?.GameObjectId; /// - public override bool Equals(object obj) => ((IEquatable)this).Equals(obj as GameObject); + public override bool Equals(object obj) => ((IEquatable)this).Equals(obj as IGameObject); /// public override int GetHashCode() => this.GameObjectId.GetHashCode(); @@ -86,103 +86,59 @@ public unsafe partial class GameObject : IEquatable /// /// This class represents a basic actor (GameObject) in FFXIV. /// -public unsafe partial class GameObject +internal unsafe partial class GameObject : IGameObject { - /// - /// Gets the name of this . - /// + /// public SeString Name => MemoryHelper.ReadSeString((nint)Unsafe.AsPointer(ref this.Struct->Name[0]), 64); - /// - /// Gets the GameObjectID for this GameObject. The Game Object ID is a globally unique identifier that points to - /// this specific object. This ID is used to reference specific objects on the local client (e.g. for targeting). - /// - /// Not to be confused with . - /// + /// public ulong GameObjectId => this.Struct->GetGameObjectId(); - /// - /// Gets the Entity ID for this GameObject. Entity IDs are assigned to networked GameObjects. - /// - /// A value of 0xE000_0000 indicates that this entity is not networked and has specific interactivity rules. - /// + /// public uint EntityId => this.Struct->EntityId; - - /// - /// Gets the data ID for linking to other respective game data. - /// + + /// public uint DataId => this.Struct->BaseId; - /// - /// Gets the ID of this GameObject's owner. - /// + /// public uint OwnerId => this.Struct->OwnerId; - /// - /// Gets the index of this object in the object table. - /// + /// public ushort ObjectIndex => this.Struct->ObjectIndex; - /// - /// 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 a value indicating whether the object is dead or alive. - /// + /// public bool IsDead => this.Struct->IsDead(); - /// - /// Gets a value indicating whether the object is targetable. - /// + /// public bool IsTargetable => this.Struct->GetIsTargetable(); - /// - /// 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 ulong TargetObjectId => 0; - /// - /// Gets the target object of the game object. - /// - /// - /// This iterates the actor table, it should be used with care. - /// + /// // TODO: Fix for non-networked GameObjects - public virtual GameObject? TargetObject => Service.Get().SearchById(this.TargetObjectId); + public virtual IGameObject? TargetObject => Service.Get().SearchById(this.TargetObjectId); /// /// Gets the underlying structure. @@ -192,3 +148,116 @@ public unsafe partial class GameObject /// public override string ToString() => $"{this.GameObjectId:X}({this.Name.TextValue} - {this.ObjectKind}) at {this.Address:X}"; } + +/// +/// Interface representing a game object. +/// +public interface IGameObject : IEquatable +{ + /// + /// Gets the name of this . + /// + public SeString Name { get; } + + /// + /// Gets the GameObjectID for this GameObject. The Game Object ID is a globally unique identifier that points to + /// this specific object. This ID is used to reference specific objects on the local client (e.g. for targeting). + /// + /// Not to be confused with . + /// + public ulong GameObjectId { get; } + + /// + /// Gets the Entity ID for this GameObject. Entity IDs are assigned to networked GameObjects. + /// + /// A value of 0xE000_0000 indicates that this entity is not networked and has specific interactivity rules. + /// + public uint EntityId { get; } + + /// + /// Gets the data ID for linking to other respective game data. + /// + public uint DataId { get; } + + /// + /// Gets the ID of this GameObject's owner. + /// + public uint OwnerId { get; } + + /// + /// Gets the index of this object in the object table. + /// + public ushort ObjectIndex { get; } + + /// + /// Gets the entity kind of this . + /// See the ObjectKind enum for possible values. + /// + public ObjectKind ObjectKind { get; } + + /// + /// Gets the sub kind of this Actor. + /// + public byte SubKind { get; } + + /// + /// Gets the X distance from the local player in yalms. + /// + public byte YalmDistanceX { get; } + + /// + /// Gets the Y distance from the local player in yalms. + /// + public byte YalmDistanceZ { get; } + + /// + /// Gets a value indicating whether the object is dead or alive. + /// + public bool IsDead { get; } + + /// + /// Gets a value indicating whether the object is targetable. + /// + public bool IsTargetable { get; } + + /// + /// Gets the position of this . + /// + public Vector3 Position { get; } + + /// + /// Gets the rotation of this . + /// This ranges from -pi to pi radians. + /// + public float Rotation { get; } + + /// + /// Gets the hitbox radius of this . + /// + public float HitboxRadius { get; } + + /// + /// Gets the current target of the game object. + /// + public ulong TargetObjectId { get; } + + /// + /// Gets the target object of the game object. + /// + /// + /// This iterates the actor table, it should be used with care. + /// + // TODO: Fix for non-networked GameObjects + public IGameObject? TargetObject { get; } + + /// + /// Gets the address of the game object in memory. + /// + public IntPtr Address { get; } + + /// + /// Gets a value indicating whether this actor is still valid in memory. + /// + /// True or false. + public bool IsValid(); +} diff --git a/Dalamud/Game/ClientState/Party/PartyList.cs b/Dalamud/Game/ClientState/Party/PartyList.cs index b808f0406..d6e235d30 100644 --- a/Dalamud/Game/ClientState/Party/PartyList.cs +++ b/Dalamud/Game/ClientState/Party/PartyList.cs @@ -63,7 +63,7 @@ internal sealed unsafe partial class PartyList : IServiceType, IPartyList private FFXIVClientStructs.FFXIV.Client.Game.Group.GroupManager* GroupManagerStruct => (FFXIVClientStructs.FFXIV.Client.Game.Group.GroupManager*)this.GroupManagerAddress; /// - public PartyMember? this[int index] + public IPartyMember? this[int index] { get { @@ -94,7 +94,7 @@ internal sealed unsafe partial class PartyList : IServiceType, IPartyList } /// - public PartyMember? CreatePartyMemberReference(IntPtr address) + public IPartyMember? CreatePartyMemberReference(IntPtr address) { if (this.clientState.LocalContentId == 0) return null; @@ -115,7 +115,7 @@ internal sealed unsafe partial class PartyList : IServiceType, IPartyList } /// - public PartyMember? CreateAllianceMemberReference(IntPtr address) + public IPartyMember? CreateAllianceMemberReference(IntPtr address) { if (this.clientState.LocalContentId == 0) return null; @@ -133,10 +133,10 @@ internal sealed unsafe partial class PartyList : IServiceType, IPartyList internal sealed partial class PartyList { /// - int IReadOnlyCollection.Count => this.Length; + int IReadOnlyCollection.Count => this.Length; /// - public IEnumerator GetEnumerator() + 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++) diff --git a/Dalamud/Game/ClientState/Party/PartyMember.cs b/Dalamud/Game/ClientState/Party/PartyMember.cs index 77539820a..86489d2ba 100644 --- a/Dalamud/Game/ClientState/Party/PartyMember.cs +++ b/Dalamud/Game/ClientState/Party/PartyMember.cs @@ -13,7 +13,7 @@ namespace Dalamud.Game.ClientState.Party; /// /// This class represents a party member in the group manager. /// -public unsafe class PartyMember +public unsafe class PartyMember : IPartyMember { /// /// Initializes a new instance of the class. @@ -55,7 +55,7 @@ public unsafe class PartyMember /// /// This iterates the actor table, it should be used with care. /// - public GameObject? GameObject => Service.Get().SearchById(this.ObjectId); + public IGameObject? GameObject => Service.Get().SearchById(this.ObjectId); /// /// Gets the current HP of this party member. @@ -109,3 +109,92 @@ public unsafe class PartyMember private FFXIVClientStructs.FFXIV.Client.Game.Group.PartyMember* Struct => (FFXIVClientStructs.FFXIV.Client.Game.Group.PartyMember*)this.Address; } + +/// +/// Interface representing a party member. +/// +public interface IPartyMember +{ + /// + /// Gets the address of this party member in memory. + /// + IntPtr Address { get; } + + /// + /// Gets a list of buffs or debuffs applied to this party member. + /// + StatusList Statuses { get; } + + /// + /// Gets the position of the party member. + /// + Vector3 Position { get; } + + /// + /// Gets the content ID of the party member. + /// + long ContentId { get; } + + /// + /// Gets the actor ID of this party member. + /// + uint ObjectId { get; } + + /// + /// Gets the actor associated with this buddy. + /// + /// + /// This iterates the actor table, it should be used with care. + /// + IGameObject? GameObject { get; } + + /// + /// Gets the current HP of this party member. + /// + uint CurrentHP { get; } + + /// + /// Gets the maximum HP of this party member. + /// + uint MaxHP { get; } + + /// + /// Gets the current MP of this party member. + /// + ushort CurrentMP { get; } + + /// + /// Gets the maximum MP of this party member. + /// + ushort MaxMP { get; } + + /// + /// Gets the territory this party member is located in. + /// + ExcelResolver Territory { get; } + + /// + /// Gets the World this party member resides in. + /// + ExcelResolver World { get; } + + /// + /// Gets the displayname of this party member. + /// + SeString Name { get; } + + /// + /// Gets the sex of this party member. + /// + byte Sex { get; } + + /// + /// Gets the classjob of this party member. + /// + ExcelResolver ClassJob { get; } + + /// + /// Gets the level of this party member. + /// + byte Level { get; } +} diff --git a/Dalamud/Game/ClientState/Statuses/Status.cs b/Dalamud/Game/ClientState/Statuses/Status.cs index 3c405601e..ad1a24b6a 100644 --- a/Dalamud/Game/ClientState/Statuses/Status.cs +++ b/Dalamud/Game/ClientState/Statuses/Status.cs @@ -59,7 +59,7 @@ public unsafe class Status /// /// This iterates the actor table, it should be used with care. /// - public GameObject? SourceObject => Service.Get().SearchById(this.SourceId); + public IGameObject? SourceObject => Service.Get().SearchById(this.SourceId); private FFXIVClientStructs.FFXIV.Client.Game.Status* Struct => (FFXIVClientStructs.FFXIV.Client.Game.Status*)this.Address; } diff --git a/Dalamud/Game/Command/CommandInfo.cs b/Dalamud/Game/Command/CommandInfo.cs index 13987f8ae..a8fad6e7a 100644 --- a/Dalamud/Game/Command/CommandInfo.cs +++ b/Dalamud/Game/Command/CommandInfo.cs @@ -3,42 +3,52 @@ namespace Dalamud.Game.Command; /// /// This class describes a registered command. /// -public sealed class CommandInfo +internal sealed class CommandInfo : ICommandInfo { /// /// Initializes a new instance of the class. /// Create a new CommandInfo with the provided handler. /// /// The method to call when the command is run. - public CommandInfo(HandlerDelegate handler) + public CommandInfo(ICommandInfo.HandlerDelegate handler) { this.Handler = handler; } + /// + public ICommandInfo.HandlerDelegate Handler { get; } + + /// + public string HelpMessage { get; set; } = string.Empty; + + /// + public bool ShowInHelp { get; set; } = true; +} + +/// +/// Interface representing a registered command. +/// +public interface ICommandInfo +{ /// /// The function to be executed when the command is dispatched. /// /// The command itself. /// The arguments supplied to the command, ready for parsing. public delegate void HandlerDelegate(string command, string arguments); - + /// /// Gets a which will be called when the command is dispatched. /// - public HandlerDelegate Handler { get; } + HandlerDelegate Handler { get; } /// /// Gets or sets the help message for this command. /// - public string HelpMessage { get; set; } = string.Empty; + string HelpMessage { get; set; } /// /// Gets or sets a value indicating whether if this command should be shown in the help output. /// - public bool ShowInHelp { get; set; } = true; - - /// - /// Gets or sets the name of the assembly responsible for this command. - /// - internal string LoaderAssemblyName { get; set; } = string.Empty; + bool ShowInHelp { get; set; } } diff --git a/Dalamud/Game/Command/CommandManager.cs b/Dalamud/Game/Command/CommandManager.cs index 72eae673f..c74f66d6e 100644 --- a/Dalamud/Game/Command/CommandManager.cs +++ b/Dalamud/Game/Command/CommandManager.cs @@ -1,6 +1,7 @@ using System.Collections.Concurrent; using System.Collections.Generic; using System.Collections.ObjectModel; +using System.Linq; using System.Text.RegularExpressions; using Dalamud.Console; @@ -23,7 +24,8 @@ internal sealed class CommandManager : IInternalDisposableService, ICommandManag { private static readonly ModuleLog Log = new("Command"); - private readonly ConcurrentDictionary commandMap = new(); + private readonly ConcurrentDictionary commandMap = new(); + private readonly ConcurrentDictionary<(string, ICommandInfo), string> commandAssemblyNameMap = new(); private readonly Regex commandRegexEn = new(@"^The command (?.+) does not exist\.$", RegexOptions.Compiled); private readonly Regex commandRegexJp = new(@"^そのコマンドはありません。: (?.+)$", RegexOptions.Compiled); private readonly Regex commandRegexDe = new(@"^„(?.+)“ existiert nicht als Textkommando\.$", RegexOptions.Compiled); @@ -54,7 +56,7 @@ internal sealed class CommandManager : IInternalDisposableService, ICommandManag } /// - public ReadOnlyDictionary Commands => new(this.commandMap); + public ReadOnlyDictionary Commands => new(this.commandMap); /// public bool ProcessCommand(string content) @@ -100,7 +102,7 @@ internal sealed class CommandManager : IInternalDisposableService, ICommandManag } /// - public void DispatchCommand(string command, string argument, CommandInfo info) + public void DispatchCommand(string command, string argument, ICommandInfo info) { try { @@ -111,9 +113,31 @@ internal sealed class CommandManager : IInternalDisposableService, ICommandManag Log.Error(ex, "Error while dispatching command {CommandName} (Argument: {Argument})", command, argument); } } + + /// + public bool AddHandler(string command, ICommandInfo info, string loaderAssemblyName = "") + { + if (info == null) + throw new ArgumentNullException(nameof(info), "Command handler is null."); + + if (!this.commandMap.TryAdd(command, info)) + { + Log.Error("Command {CommandName} is already registered.", command); + return false; + } + + if (!this.commandAssemblyNameMap.TryAdd((command, info), loaderAssemblyName)) + { + this.commandMap.Remove(command, out _); + Log.Error("Command {CommandName} is already registered in the assembly name map.", command); + return false; + } + + return true; + } /// - public bool AddHandler(string command, CommandInfo info) + public bool AddHandler(string command, ICommandInfo info) { if (info == null) throw new ArgumentNullException(nameof(info), "Command handler is null."); @@ -133,6 +157,32 @@ internal sealed class CommandManager : IInternalDisposableService, ICommandManag return this.commandMap.Remove(command, out _); } + /// + /// Returns the assembly name from which the command was added or blank if added internally. + /// + /// The command. + /// A ICommandInfo object. + /// The name of the assembly. + public string GetHandlerAssemblyName(string command, ICommandInfo commandInfo) + { + if (this.commandAssemblyNameMap.TryGetValue((command, commandInfo), out var assemblyName)) + { + return assemblyName; + } + + return string.Empty; + } + + /// + /// Returns a list of commands given a specified assembly name. + /// + /// The name of the assembly. + /// A list of commands and their associated activation string. + public List> GetHandlersByAssemblyName(string assemblyName) + { + return this.commandAssemblyNameMap.Where(c => c.Value == assemblyName).ToList(); + } + /// void IInternalDisposableService.DisposeService() { @@ -199,7 +249,7 @@ internal class CommandManagerPluginScoped : IInternalDisposableService, ICommand } /// - public ReadOnlyDictionary Commands => this.commandManagerService.Commands; + public ReadOnlyDictionary Commands => this.commandManagerService.Commands; /// void IInternalDisposableService.DisposeService() @@ -217,16 +267,15 @@ internal class CommandManagerPluginScoped : IInternalDisposableService, ICommand => this.commandManagerService.ProcessCommand(content); /// - public void DispatchCommand(string command, string argument, CommandInfo info) + public void DispatchCommand(string command, string argument, ICommandInfo info) => this.commandManagerService.DispatchCommand(command, argument, info); /// - public bool AddHandler(string command, CommandInfo info) + public bool AddHandler(string command, ICommandInfo info) { if (!this.pluginRegisteredCommands.Contains(command)) { - info.LoaderAssemblyName = this.pluginInfo.InternalName; - if (this.commandManagerService.AddHandler(command, info)) + if (this.commandManagerService.AddHandler(command, info, this.pluginInfo.InternalName)) { this.pluginRegisteredCommands.Add(command); return true; diff --git a/Dalamud/Game/Gui/ContextMenu/ContextMenu.cs b/Dalamud/Game/Gui/ContextMenu/ContextMenu.cs index cf4c6524e..55c0b178b 100644 --- a/Dalamud/Game/Gui/ContextMenu/ContextMenu.cs +++ b/Dalamud/Game/Gui/ContextMenu/ContextMenu.cs @@ -54,7 +54,7 @@ internal sealed unsafe class ContextMenu : IInternalDisposableService, IContextM /// public event IContextMenu.OnMenuOpenedDelegate? OnMenuOpened; - private Dictionary> MenuItems { get; } = new(); + private Dictionary> MenuItems { get; } = new(); private object MenuItemsLock { get; } = new(); @@ -62,7 +62,7 @@ internal sealed unsafe class ContextMenu : IInternalDisposableService, IContextM private ContextMenuType? SelectedMenuType { get; set; } - private List? SelectedItems { get; set; } + private List? SelectedItems { get; set; } private HashSet SelectedEventInterfaces { get; } = new(); @@ -72,7 +72,7 @@ internal sealed unsafe class ContextMenu : IInternalDisposableService, IContextM // 0 -> inf: selected items private List MenuCallbackIds { get; } = new(); - private IReadOnlyList? SubmenuItems { get; set; } + private IReadOnlyList? SubmenuItems { get; set; } /// void IInternalDisposableService.DisposeService() @@ -90,7 +90,7 @@ internal sealed unsafe class ContextMenu : IInternalDisposableService, IContextM } /// - public void AddMenuItem(ContextMenuType menuType, MenuItem item) + public void AddMenuItem(ContextMenuType menuType, IMenuItem item) { lock (this.MenuItemsLock) { @@ -101,7 +101,7 @@ internal sealed unsafe class ContextMenu : IInternalDisposableService, IContextM } /// - public bool RemoveMenuItem(ContextMenuType menuType, MenuItem item) + public bool RemoveMenuItem(ContextMenuType menuType, IMenuItem item) { lock (this.MenuItemsLock) { @@ -167,7 +167,7 @@ internal sealed unsafe class ContextMenu : IInternalDisposableService, IContextM return values; } - private void SetupGenericMenu(int headerCount, int sizeHeaderIdx, int returnHeaderIdx, int submenuHeaderIdx, IReadOnlyList items, ref int valueCount, ref AtkValue* values) + private void SetupGenericMenu(int headerCount, int sizeHeaderIdx, int returnHeaderIdx, int submenuHeaderIdx, IReadOnlyList items, ref int valueCount, ref AtkValue* values) { var itemsWithIdx = items.Select((item, idx) => (item, idx)).OrderBy(i => i.item.Priority).ToArray(); var prefixItems = itemsWithIdx.Where(i => i.item.Priority < 0).ToArray(); @@ -215,7 +215,7 @@ internal sealed unsafe class ContextMenu : IInternalDisposableService, IContextM returnMask <<= prefixMenuSize; submenuMask <<= prefixMenuSize; - void FillData(Span disabledData, Span nameData, int i, MenuItem item, int idx) + void FillData(Span disabledData, Span nameData, int i, IMenuItem item, int idx) { this.MenuCallbackIds.Add(idx); @@ -231,7 +231,7 @@ internal sealed unsafe class ContextMenu : IInternalDisposableService, IContextM submenuMask |= 1u << i; nameData[i].ChangeType(ValueType.String); - nameData[i].SetManagedString(item.PrefixedName.Encode().NullTerminate()); + nameData[i].SetManagedString(this.GetPrefixedName(item).Encode().NullTerminate()); } for (var i = 0; i < prefixMenuSize; ++i) @@ -253,8 +253,19 @@ internal sealed unsafe class ContextMenu : IInternalDisposableService, IContextM offsetData[sizeHeaderIdx].UInt += (uint)items.Count; } + + /// + /// Gets the name with the given prefix. + /// + internal SeString GetPrefixedName(IMenuItem menuItem) => + menuItem.Prefix is { } prefix + ? new SeStringBuilder() + .AddUiForeground($"{prefix.ToIconString()} ", menuItem.PrefixColor) + .Append(menuItem.Name) + .Build() + : menuItem.Name; - private void SetupContextMenu(IReadOnlyList items, ref int valueCount, ref AtkValue* values) + private void SetupContextMenu(IReadOnlyList items, ref int valueCount, ref AtkValue* values) { // 0: UInt = Item Count // 1: UInt = 0 (probably window name, just unused) @@ -268,8 +279,8 @@ internal sealed unsafe class ContextMenu : IInternalDisposableService, IContextM { if (!item.Prefix.HasValue) { - item.Prefix = MenuItem.DalamudDefaultPrefix; - item.PrefixColor = MenuItem.DalamudDefaultPrefixColor; + item.Prefix = IMenuItem.DalamudDefaultPrefix; + item.PrefixColor = IMenuItem.DalamudDefaultPrefixColor; if (!item.UseDefaultPrefix) { @@ -281,7 +292,7 @@ internal sealed unsafe class ContextMenu : IInternalDisposableService, IContextM this.SetupGenericMenu(7, 0, 2, 3, items, ref valueCount, ref values); } - private void SetupContextSubMenu(IReadOnlyList items, ref int valueCount, ref AtkValue* values) + private void SetupContextSubMenu(IReadOnlyList items, ref int valueCount, ref AtkValue* values) { // 0: UInt = ContextItemCount // 1: skipped? @@ -376,7 +387,7 @@ internal sealed unsafe class ContextMenu : IInternalDisposableService, IContextM return ret; } - private List FixupMenuList(List items, int nativeMenuSize) + private List FixupMenuList(List items, int nativeMenuSize) { // The in game menu actually supports 32 items, but the last item can't have a visible submenu arrow. // As such, we'll only work with 31 items. @@ -401,7 +412,7 @@ internal sealed unsafe class ContextMenu : IInternalDisposableService, IContextM return items; } - private void OpenSubmenu(SeString name, IReadOnlyList submenuItems, int posX, int posY) + private void OpenSubmenu(SeString name, IReadOnlyList submenuItems, int posX, int posY) { if (submenuItems.Count == 0) throw new ArgumentException("Submenu must not be empty", nameof(submenuItems)); @@ -513,7 +524,7 @@ internal class ContextMenuPluginScoped : IInternalDisposableService, IContextMen /// public event IContextMenu.OnMenuOpenedDelegate? OnMenuOpened; - private Dictionary> MenuItems { get; } = new(); + private Dictionary> MenuItems { get; } = new(); private object MenuItemsLock { get; } = new(); @@ -535,7 +546,7 @@ internal class ContextMenuPluginScoped : IInternalDisposableService, IContextMen } /// - public void AddMenuItem(ContextMenuType menuType, MenuItem item) + public void AddMenuItem(ContextMenuType menuType, IMenuItem item) { lock (this.MenuItemsLock) { @@ -548,7 +559,7 @@ internal class ContextMenuPluginScoped : IInternalDisposableService, IContextMen } /// - public bool RemoveMenuItem(ContextMenuType menuType, MenuItem item) + public bool RemoveMenuItem(ContextMenuType menuType, IMenuItem item) { lock (this.MenuItemsLock) { @@ -559,6 +570,6 @@ internal class ContextMenuPluginScoped : IInternalDisposableService, IContextMen return this.parentService.RemoveMenuItem(menuType, item); } - private void OnMenuOpenedForward(MenuOpenedArgs args) => + private void OnMenuOpenedForward(IMenuOpenedArgs args) => this.OnMenuOpened?.Invoke(args); } diff --git a/Dalamud/Game/Gui/ContextMenu/MenuArgs.cs b/Dalamud/Game/Gui/ContextMenu/MenuArgs.cs index 4691f9dd2..bd6d2ef68 100644 --- a/Dalamud/Game/Gui/ContextMenu/MenuArgs.cs +++ b/Dalamud/Game/Gui/ContextMenu/MenuArgs.cs @@ -11,7 +11,7 @@ namespace Dalamud.Game.Gui.ContextMenu; /// /// Base class for menu args. /// -public abstract unsafe class MenuArgs +internal abstract unsafe class MenuArgs : IMenuArgs { private IReadOnlySet? eventInterfaces; @@ -37,6 +37,51 @@ public abstract unsafe class MenuArgs }; } + /// + public string? AddonName { get; } + + /// + public nint AddonPtr { get; } + + /// + public nint AgentPtr { get; } + + /// + public ContextMenuType MenuType { get; } + + /// + public MenuTarget Target { get; } + + /// + public IReadOnlySet EventInterfaces + { + get + { + if (this.MenuType is ContextMenuType.Default) + { + return this.eventInterfaces ?? new HashSet(); + } + else + { + throw new InvalidOperationException("Not a default context menu"); + } + } + } +} + +/// +/// Interface representing a context menus args. +/// +public interface IMenuArgs +{ + /// + /// Gets a list of AtkEventInterface pointers associated with the context menu. + /// Only available with . + /// Almost always an agent pointer. You can use this to find out what type of context menu it is. + /// + /// Thrown when the context menu is not a . + public IReadOnlySet EventInterfaces { get; } + /// /// Gets the name of the addon that opened the context menu. /// @@ -63,25 +108,4 @@ public abstract unsafe class MenuArgs /// signifies a . /// public MenuTarget Target { get; } - - /// - /// Gets a list of AtkEventInterface pointers associated with the context menu. - /// Only available with . - /// Almost always an agent pointer. You can use this to find out what type of context menu it is. - /// - /// Thrown when the context menu is not a . - public IReadOnlySet EventInterfaces - { - get - { - if (this.MenuType is ContextMenuType.Default) - { - return this.eventInterfaces ?? new HashSet(); - } - else - { - throw new InvalidOperationException("Not a default context menu"); - } - } - } } diff --git a/Dalamud/Game/Gui/ContextMenu/MenuItem.cs b/Dalamud/Game/Gui/ContextMenu/MenuItem.cs index ec111f19e..22b7068f2 100644 --- a/Dalamud/Game/Gui/ContextMenu/MenuItem.cs +++ b/Dalamud/Game/Gui/ContextMenu/MenuItem.cs @@ -8,32 +8,15 @@ namespace Dalamud.Game.Gui.ContextMenu; /// /// A menu item that can be added to a context menu. /// -public sealed record MenuItem +public sealed record MenuItem : IMenuItem { - /// - /// The default prefix used if no specific preset is specified. - /// - public const SeIconChar DalamudDefaultPrefix = SeIconChar.BoxedLetterD; - - /// - /// The default prefix color used if no specific preset is specified. - /// - public const ushort DalamudDefaultPrefixColor = 539; - - /// - /// Gets or sets the display name of the menu item. - /// + /// public SeString Name { get; set; } = SeString.Empty; - /// - /// Gets or sets the prefix attached to the beginning of . - /// + /// public SeIconChar? Prefix { get; set; } - /// - /// Sets the character to prefix the with. Will be converted into a fancy boxed letter icon. Must be an uppercase letter. - /// - /// must be an uppercase letter. + /// public char? PrefixChar { set @@ -52,55 +35,97 @@ public sealed record MenuItem } } + /// + public ushort PrefixColor { get; set; } + + /// + public bool UseDefaultPrefix { get; set; } + + /// + public Action? OnClicked { get; set; } + + /// + public int Priority { get; set; } + + /// + public bool IsEnabled { get; set; } = true; + + /// + public bool IsSubmenu { get; set; } + + /// + public bool IsReturn { get; set; } +} + +/// +/// Interface representing a menu item to be added to a context menu. +/// +public interface IMenuItem +{ + /// + /// The default prefix used if no specific preset is specified. + /// + public const SeIconChar DalamudDefaultPrefix = SeIconChar.BoxedLetterD; + + /// + /// The default prefix color used if no specific preset is specified. + /// + public const ushort DalamudDefaultPrefixColor = 539; + + /// + /// Gets or sets the display name of the menu item. + /// + SeString Name { get; set; } + + /// + /// Gets or sets the prefix attached to the beginning of . + /// + SeIconChar? Prefix { get; set; } + + /// + /// Sets the character to prefix the with. Will be converted into a fancy boxed letter icon. Must be an uppercase letter. + /// + /// must be an uppercase letter. + char? PrefixChar { set; } + /// /// Gets or sets the color of the . Specifies a row id. /// - public ushort PrefixColor { get; set; } - + ushort PrefixColor { get; set; } + /// /// Gets or sets a value indicating whether the dev wishes to intentionally use the default prefix symbol and color. /// - public bool UseDefaultPrefix { get; set; } + bool UseDefaultPrefix { get; set; } /// /// Gets or sets the callback to be invoked when the menu item is clicked. /// - public Action? OnClicked { get; set; } + Action? OnClicked { get; set; } /// /// Gets or sets the priority (or order) with which the menu item should be displayed in descending order. /// Priorities below 0 will be displayed above the native menu items. /// Other priorities will be displayed below the native menu items. /// - public int Priority { get; set; } + int Priority { get; set; } /// /// Gets or sets a value indicating whether the menu item is enabled. /// Disabled items will be faded and cannot be clicked on. /// - public bool IsEnabled { get; set; } = true; + bool IsEnabled { get; set; } /// /// Gets or sets a value indicating whether the menu item is a submenu. /// This value is purely visual. Submenu items will have an arrow to its right. /// - public bool IsSubmenu { get; set; } + bool IsSubmenu { get; set; } /// /// Gets or sets a value indicating whether the menu item is a return item. /// This value is purely visual. Return items will have a back arrow to its left. /// If both and are true, the return arrow will take precedence. /// - public bool IsReturn { get; set; } - - /// - /// Gets the name with the given prefix. - /// - internal SeString PrefixedName => - this.Prefix is { } prefix - ? new SeStringBuilder() - .AddUiForeground($"{prefix.ToIconString()} ", this.PrefixColor) - .Append(this.Name) - .Build() - : this.Name; + bool IsReturn { get; set; } } diff --git a/Dalamud/Game/Gui/ContextMenu/MenuItemClickedArgs.cs b/Dalamud/Game/Gui/ContextMenu/MenuItemClickedArgs.cs index ded1475f0..b2542132a 100644 --- a/Dalamud/Game/Gui/ContextMenu/MenuItemClickedArgs.cs +++ b/Dalamud/Game/Gui/ContextMenu/MenuItemClickedArgs.cs @@ -10,7 +10,7 @@ namespace Dalamud.Game.Gui.ContextMenu; /// /// Callback args used when a menu item is clicked. /// -public sealed unsafe class MenuItemClickedArgs : MenuArgs +internal sealed unsafe class MenuItemClickedArgs : MenuArgs, IMenuItemClickedArgs { /// /// Initializes a new instance of the class. @@ -20,26 +20,73 @@ public sealed unsafe class MenuItemClickedArgs : MenuArgs /// Agent associated with the context menu. /// The type of context menu. /// List of AtkEventInterfaces associated with the context menu. - internal MenuItemClickedArgs(Action> openSubmenu, AtkUnitBase* addon, AgentInterface* agent, ContextMenuType type, IReadOnlySet eventInterfaces) + internal MenuItemClickedArgs(Action> openSubmenu, AtkUnitBase* addon, AgentInterface* agent, ContextMenuType type, IReadOnlySet eventInterfaces) : base(addon, agent, type, eventInterfaces) { this.OnOpenSubmenu = openSubmenu; } - private Action> OnOpenSubmenu { get; } + private Action> OnOpenSubmenu { get; } + /// + public void OpenSubmenu(SeString name, IReadOnlyList items) => + this.OnOpenSubmenu(name, items); + + /// + public void OpenSubmenu(IReadOnlyList items) => + this.OnOpenSubmenu(null, items); +} + +/// +/// An interface representing the callback args used when a menu item is clicked. +/// +public interface IMenuItemClickedArgs +{ /// /// Opens a submenu with the given name and items. /// /// The name of the submenu, displayed at the top. /// The items to display in the submenu. - public void OpenSubmenu(SeString name, IReadOnlyList items) => - this.OnOpenSubmenu(name, items); + void OpenSubmenu(SeString name, IReadOnlyList items); /// /// Opens a submenu with the given items. /// /// The items to display in the submenu. - public void OpenSubmenu(IReadOnlyList items) => - this.OnOpenSubmenu(null, items); + void OpenSubmenu(IReadOnlyList items); + + /// + /// Gets a list of AtkEventInterface pointers associated with the context menu. + /// Only available with . + /// Almost always an agent pointer. You can use this to find out what type of context menu it is. + /// + /// Thrown when the context menu is not a . + IReadOnlySet EventInterfaces { get; } + + /// + /// Gets the name of the addon that opened the context menu. + /// + string? AddonName { get; } + + /// + /// Gets the memory pointer of the addon that opened the context menu. + /// + nint AddonPtr { get; } + + /// + /// Gets the memory pointer of the agent that opened the context menu. + /// + nint AgentPtr { get; } + + /// + /// Gets the type of the context menu. + /// + ContextMenuType MenuType { get; } + + /// + /// Gets the target info of the context menu. The actual type depends on . + /// signifies a . + /// signifies a . + /// + MenuTarget Target { get; } } diff --git a/Dalamud/Game/Gui/ContextMenu/MenuOpenedArgs.cs b/Dalamud/Game/Gui/ContextMenu/MenuOpenedArgs.cs index c5e6a7ade..ca990cfd8 100644 --- a/Dalamud/Game/Gui/ContextMenu/MenuOpenedArgs.cs +++ b/Dalamud/Game/Gui/ContextMenu/MenuOpenedArgs.cs @@ -8,7 +8,7 @@ namespace Dalamud.Game.Gui.ContextMenu; /// /// Callback args used when a menu item is opened. /// -public sealed unsafe class MenuOpenedArgs : MenuArgs +internal sealed unsafe class MenuOpenedArgs : MenuArgs, IMenuOpenedArgs { /// /// Initializes a new instance of the class. @@ -26,10 +26,19 @@ public sealed unsafe class MenuOpenedArgs : MenuArgs private Action OnAddMenuItem { get; } + /// + public void AddMenuItem(MenuItem item) => + this.OnAddMenuItem(item); +} + +/// +/// An interface representing the callback args used when a menu item is opened. +/// +public interface IMenuOpenedArgs : IMenuArgs +{ /// /// Adds a custom menu item to the context menu. /// /// The menu item to add. - public void AddMenuItem(MenuItem item) => - this.OnAddMenuItem(item); + void AddMenuItem(MenuItem item); } diff --git a/Dalamud/Game/Gui/ContextMenu/MenuTargetDefault.cs b/Dalamud/Game/Gui/ContextMenu/MenuTargetDefault.cs index d87bc36b6..26365ab14 100644 --- a/Dalamud/Game/Gui/ContextMenu/MenuTargetDefault.cs +++ b/Dalamud/Game/Gui/ContextMenu/MenuTargetDefault.cs @@ -36,7 +36,7 @@ public sealed unsafe class MenuTargetDefault : MenuTarget /// /// Gets the target object. /// - public GameObject? TargetObject => Service.Get().SearchById(this.TargetObjectId); + public IGameObject? TargetObject => Service.Get().SearchById(this.TargetObjectId); /// /// Gets the content id of the target. diff --git a/Dalamud/Game/Gui/Dtr/DtrBar.cs b/Dalamud/Game/Gui/Dtr/DtrBar.cs index 30f2ab67a..81339b6aa 100644 --- a/Dalamud/Game/Gui/Dtr/DtrBar.cs +++ b/Dalamud/Game/Gui/Dtr/DtrBar.cs @@ -77,7 +77,7 @@ internal sealed unsafe class DtrBar : IInternalDisposableService, IDtrBar public IReadOnlyList Entries => this.entries; /// - public DtrBarEntry Get(string title, SeString? text = null) + public IDtrBarEntry Get(string title, SeString? text = null) { if (this.entries.Any(x => x.Title == title) || this.newEntries.Any(x => x.Title == title)) throw new ArgumentException("An entry with the same title already exists."); @@ -501,7 +501,7 @@ internal class DtrBarPluginScoped : IInternalDisposableService, IDtrBar [ServiceManager.ServiceDependency] private readonly DtrBar dtrBarService = Service.Get(); - private readonly Dictionary pluginEntries = new(); + private readonly Dictionary pluginEntries = new(); /// public IReadOnlyList Entries => this.dtrBarService.Entries; @@ -518,7 +518,7 @@ internal class DtrBarPluginScoped : IInternalDisposableService, IDtrBar } /// - public DtrBarEntry Get(string title, SeString? text = null) + public IDtrBarEntry Get(string title, SeString? text = null) { // If we already have a known entry for this plugin, return it. if (this.pluginEntries.TryGetValue(title, out var existingEntry)) return existingEntry; diff --git a/Dalamud/Game/Gui/PartyFinder/PartyFinderGui.cs b/Dalamud/Game/Gui/PartyFinder/PartyFinderGui.cs index 21520409e..19b4cecd0 100644 --- a/Dalamud/Game/Gui/PartyFinder/PartyFinderGui.cs +++ b/Dalamud/Game/Gui/PartyFinder/PartyFinderGui.cs @@ -154,5 +154,5 @@ internal class PartyFinderGuiPluginScoped : IInternalDisposableService, IPartyFi this.ReceiveListing = null; } - private void ReceiveListingForward(PartyFinderListing listing, PartyFinderListingEventArgs args) => this.ReceiveListing?.Invoke(listing, args); + private void ReceiveListingForward(IPartyFinderListing listing, IPartyFinderListingEventArgs args) => this.ReceiveListing?.Invoke(listing, args); } diff --git a/Dalamud/Game/Gui/PartyFinder/Types/PartyFinderListing.cs b/Dalamud/Game/Gui/PartyFinder/Types/PartyFinderListing.cs index 1a5b76f5b..abcc187fd 100644 --- a/Dalamud/Game/Gui/PartyFinder/Types/PartyFinderListing.cs +++ b/Dalamud/Game/Gui/PartyFinder/Types/PartyFinderListing.cs @@ -12,7 +12,7 @@ namespace Dalamud.Game.Gui.PartyFinder.Types; /// /// A single listing in party finder. /// -public class PartyFinderListing +internal class PartyFinderListing : IPartyFinderListing { private readonly byte objective; private readonly byte conditions; @@ -63,171 +63,267 @@ public class PartyFinderListing .ToArray(); } - /// - /// Gets the ID assigned to this listing by the game's server. - /// + /// public uint Id { get; } - /// - /// Gets the lower bits of the player's content ID. - /// + /// public uint ContentIdLower { get; } - /// - /// Gets the name of the player hosting this listing. - /// + /// public SeString Name { get; } - /// - /// Gets the description of this listing as set by the host. May be multiple lines. - /// + /// public SeString Description { get; } - /// - /// Gets the world that this listing was created on. - /// + /// public Lazy World { get; } - /// - /// Gets the home world of the listing's host. - /// + /// public Lazy HomeWorld { get; } - /// - /// Gets the current world of the listing's host. - /// + /// public Lazy CurrentWorld { get; } - /// - /// Gets the Party Finder category this listing is listed under. - /// + /// public DutyCategory Category { get; } - /// - /// Gets the row ID of the duty this listing is for. May be 0 for non-duty listings. - /// + /// public ushort RawDuty { get; } - /// - /// Gets the duty this listing is for. May be null for non-duty listings. - /// + /// public Lazy Duty { get; } - /// - /// Gets the type of duty this listing is for. - /// + /// public DutyType DutyType { get; } - /// - /// Gets a value indicating whether if this listing is beginner-friendly. Shown with a sprout icon in-game. - /// + /// public bool BeginnersWelcome { get; } - /// - /// Gets how many seconds this listing will continue to be available for. It may end before this time if the party - /// fills or the host ends it early. - /// + /// public ushort SecondsRemaining { get; } - /// - /// Gets the minimum item level required to join this listing. - /// + /// public ushort MinimumItemLevel { get; } - /// - /// Gets the number of parties this listing is recruiting for. - /// + /// public byte Parties { get; } - /// - /// Gets the number of player slots this listing is recruiting for. - /// + /// public byte SlotsAvailable { get; } - /// - /// Gets the time at which the server this listings is on last restarted for a patch/hotfix. - /// Probably. - /// + /// public uint LastPatchHotfixTimestamp { get; } - /// - /// Gets a list of player slots that the Party Finder is accepting. - /// + /// public IReadOnlyCollection Slots => this.slots; - /// - /// Gets the objective of this listing. - /// + /// public ObjectiveFlags Objective => (ObjectiveFlags)this.objective; - /// - /// Gets the conditions of this listing. - /// + /// public ConditionFlags Conditions => (ConditionFlags)this.conditions; - /// - /// Gets the Duty Finder settings that will be used for this listing. - /// + /// public DutyFinderSettingsFlags DutyFinderSettings => (DutyFinderSettingsFlags)this.dutyFinderSettings; - /// - /// Gets the loot rules that will be used for this listing. - /// + /// public LootRuleFlags LootRules => (LootRuleFlags)this.lootRules; - /// - /// Gets where this listing is searching. Note that this is also used for denoting alliance raid listings and one - /// player per job. - /// + /// public SearchAreaFlags SearchArea => (SearchAreaFlags)this.searchArea; - /// - /// Gets a list of the class/job IDs that are currently present in the party. - /// + /// public IReadOnlyCollection RawJobsPresent => this.jobsPresent; - /// - /// Gets a list of the classes/jobs that are currently present in the party. - /// + /// public IReadOnlyCollection> JobsPresent { get; } #region Indexers - /// - /// Check if the given flag is present. - /// - /// The flag to check for. - /// A value indicating whether the flag is present. + /// public bool this[ObjectiveFlags flag] => this.objective == 0 || (this.objective & (uint)flag) > 0; - /// - /// Check if the given flag is present. - /// - /// The flag to check for. - /// A value indicating whether the flag is present. + /// public bool this[ConditionFlags flag] => this.conditions == 0 || (this.conditions & (uint)flag) > 0; - /// - /// Check if the given flag is present. - /// - /// The flag to check for. - /// A value indicating whether the flag is present. + /// public bool this[DutyFinderSettingsFlags flag] => this.dutyFinderSettings == 0 || (this.dutyFinderSettings & (uint)flag) > 0; - /// - /// Check if the given flag is present. - /// - /// The flag to check for. - /// A value indicating whether the flag is present. + /// public bool this[LootRuleFlags flag] => this.lootRules == 0 || (this.lootRules & (uint)flag) > 0; - /// - /// Check if the given flag is present. - /// - /// The flag to check for. - /// A value indicating whether the flag is present. + /// public bool this[SearchAreaFlags flag] => this.searchArea == 0 || (this.searchArea & (uint)flag) > 0; #endregion } + +/// +/// A interface representing a single listing in party finder. +/// +public interface IPartyFinderListing +{ + /// + /// Gets the objective of this listing. + /// + ObjectiveFlags Objective { get; } + + /// + /// Gets the conditions of this listing. + /// + ConditionFlags Conditions { get; } + + /// + /// Gets the Duty Finder settings that will be used for this listing. + /// + DutyFinderSettingsFlags DutyFinderSettings { get; } + + /// + /// Gets the loot rules that will be used for this listing. + /// + LootRuleFlags LootRules { get; } + + /// + /// Gets where this listing is searching. Note that this is also used for denoting alliance raid listings and one + /// player per job. + /// + SearchAreaFlags SearchArea { get; } + + /// + /// Gets a list of player slots that the Party Finder is accepting. + /// + IReadOnlyCollection Slots { get; } + + /// + /// Gets a list of the classes/jobs that are currently present in the party. + /// + IReadOnlyCollection> JobsPresent { get; } + + /// + /// Gets the ID assigned to this listing by the game's server. + /// + uint Id { get; } + + /// + /// Gets the lower bits of the player's content ID. + /// + uint ContentIdLower { get; } + + /// + /// Gets the name of the player hosting this listing. + /// + SeString Name { get; } + + /// + /// Gets the description of this listing as set by the host. May be multiple lines. + /// + SeString Description { get; } + + /// + /// Gets the world that this listing was created on. + /// + Lazy World { get; } + + /// + /// Gets the home world of the listing's host. + /// + Lazy HomeWorld { get; } + + /// + /// Gets the current world of the listing's host. + /// + Lazy CurrentWorld { get; } + + /// + /// Gets the Party Finder category this listing is listed under. + /// + DutyCategory Category { get; } + + /// + /// Gets the row ID of the duty this listing is for. May be 0 for non-duty listings. + /// + ushort RawDuty { get; } + + /// + /// Gets the duty this listing is for. May be null for non-duty listings. + /// + Lazy Duty { get; } + + /// + /// Gets the type of duty this listing is for. + /// + DutyType DutyType { get; } + + /// + /// Gets a value indicating whether if this listing is beginner-friendly. Shown with a sprout icon in-game. + /// + bool BeginnersWelcome { get; } + + /// + /// Gets how many seconds this listing will continue to be available for. It may end before this time if the party + /// fills or the host ends it early. + /// + ushort SecondsRemaining { get; } + + /// + /// Gets the minimum item level required to join this listing. + /// + ushort MinimumItemLevel { get; } + + /// + /// Gets the number of parties this listing is recruiting for. + /// + byte Parties { get; } + + /// + /// Gets the number of player slots this listing is recruiting for. + /// + byte SlotsAvailable { get; } + + /// + /// Gets the time at which the server this listings is on last restarted for a patch/hotfix. + /// Probably. + /// + uint LastPatchHotfixTimestamp { get; } + + /// + /// Gets a list of the class/job IDs that are currently present in the party. + /// + IReadOnlyCollection RawJobsPresent { get; } + + /// + /// Check if the given flag is present. + /// + /// The flag to check for. + /// A value indicating whether the flag is present. + bool this[ObjectiveFlags flag] { get; } + + /// + /// Check if the given flag is present. + /// + /// The flag to check for. + /// A value indicating whether the flag is present. + bool this[ConditionFlags flag] { get; } + + /// + /// Check if the given flag is present. + /// + /// The flag to check for. + /// A value indicating whether the flag is present. + bool this[DutyFinderSettingsFlags flag] { get; } + + /// + /// Check if the given flag is present. + /// + /// The flag to check for. + /// A value indicating whether the flag is present. + bool this[LootRuleFlags flag] { get; } + + /// + /// Check if the given flag is present. + /// + /// The flag to check for. + /// A value indicating whether the flag is present. + bool this[SearchAreaFlags flag] { get; } +} diff --git a/Dalamud/Game/Gui/PartyFinder/Types/PartyFinderListingEventArgs.cs b/Dalamud/Game/Gui/PartyFinder/Types/PartyFinderListingEventArgs.cs index 4bc603d7a..b366c6105 100644 --- a/Dalamud/Game/Gui/PartyFinder/Types/PartyFinderListingEventArgs.cs +++ b/Dalamud/Game/Gui/PartyFinder/Types/PartyFinderListingEventArgs.cs @@ -3,7 +3,7 @@ namespace Dalamud.Game.Gui.PartyFinder.Types; /// /// This class represents additional arguments passed by the game. /// -public class PartyFinderListingEventArgs +internal class PartyFinderListingEventArgs : IPartyFinderListingEventArgs { /// /// Initializes a new instance of the class. @@ -14,13 +14,25 @@ public class PartyFinderListingEventArgs this.BatchNumber = batchNumber; } + /// + public int BatchNumber { get; } + + /// + public bool Visible { get; set; } = true; +} + +/// +/// A interface representing additional arguments passed by the game. +/// +public interface IPartyFinderListingEventArgs +{ /// /// Gets the batch number. /// - public int BatchNumber { get; } + int BatchNumber { get; } /// /// Gets or sets a value indicating whether the listing is visible. /// - public bool Visible { get; set; } = true; + bool Visible { get; set; } } diff --git a/Dalamud/Interface/Internal/Windows/Data/Widgets/CommandWidget.cs b/Dalamud/Interface/Internal/Windows/Data/Widgets/CommandWidget.cs index c4c74274a..07695c02a 100644 --- a/Dalamud/Interface/Internal/Windows/Data/Widgets/CommandWidget.cs +++ b/Dalamud/Interface/Internal/Windows/Data/Widgets/CommandWidget.cs @@ -56,8 +56,8 @@ internal class CommandWidget : IDataWindowWidget ? commands.OrderBy(kv => kv.Key).ToArray() : commands.OrderByDescending(kv => kv.Key).ToArray(), 1 => sortSpecs.Specs.SortDirection == ImGuiSortDirection.Ascending - ? commands.OrderBy(kv => kv.Value.LoaderAssemblyName).ToArray() - : commands.OrderByDescending(kv => kv.Value.LoaderAssemblyName).ToArray(), + ? commands.OrderBy(kv => commandManager.GetHandlerAssemblyName(kv.Key, kv.Value)).ToArray() + : commands.OrderByDescending(kv => commandManager.GetHandlerAssemblyName(kv.Key, kv.Value)).ToArray(), _ => commands, }; } @@ -70,7 +70,7 @@ internal class CommandWidget : IDataWindowWidget ImGui.Text(command.Key); ImGui.TableNextColumn(); - ImGui.Text(command.Value.LoaderAssemblyName); + ImGui.Text(commandManager.GetHandlerAssemblyName(command.Key, command.Value)); ImGui.TableNextColumn(); ImGui.TextWrapped(command.Value.HelpMessage); diff --git a/Dalamud/Interface/Internal/Windows/Data/Widgets/DtrBarWidget.cs b/Dalamud/Interface/Internal/Windows/Data/Widgets/DtrBarWidget.cs index dca5862c3..1f58541be 100644 --- a/Dalamud/Interface/Internal/Windows/Data/Widgets/DtrBarWidget.cs +++ b/Dalamud/Interface/Internal/Windows/Data/Widgets/DtrBarWidget.cs @@ -10,9 +10,9 @@ namespace Dalamud.Interface.Internal.Windows.Data.Widgets; /// internal class DtrBarWidget : IDataWindowWidget { - private DtrBarEntry? dtrTest1; - private DtrBarEntry? dtrTest2; - private DtrBarEntry? dtrTest3; + private IDtrBarEntry? dtrTest1; + private IDtrBarEntry? dtrTest2; + private IDtrBarEntry? dtrTest3; /// public string[]? CommandShortcuts { get; init; } = { "dtr", "dtrbar" }; @@ -51,7 +51,7 @@ internal class DtrBarWidget : IDataWindowWidget } } - private void DrawDtrTestEntry(ref DtrBarEntry? entry, string title) + private void DrawDtrTestEntry(ref IDtrBarEntry? entry, string title) { var dtrBar = Service.Get(); diff --git a/Dalamud/Interface/Internal/Windows/PluginInstaller/PluginInstallerWindow.cs b/Dalamud/Interface/Internal/Windows/PluginInstaller/PluginInstallerWindow.cs index c177f2933..438bb446e 100644 --- a/Dalamud/Interface/Internal/Windows/PluginInstaller/PluginInstallerWindow.cs +++ b/Dalamud/Interface/Internal/Windows/PluginInstaller/PluginInstallerWindow.cs @@ -2597,7 +2597,7 @@ internal class PluginInstallerWindow : Window, IDisposable var commands = commandManager.Commands .Where(cInfo => cInfo.Value is { ShowInHelp: true } && - cInfo.Value.LoaderAssemblyName == plugin.Manifest.InternalName) + commandManager.GetHandlerAssemblyName(cInfo.Key, cInfo.Value) == plugin.Manifest.InternalName) .ToArray(); if (commands.Any()) diff --git a/Dalamud/Interface/Internal/Windows/SelfTest/AgingSteps/ContextMenuAgingStep.cs b/Dalamud/Interface/Internal/Windows/SelfTest/AgingSteps/ContextMenuAgingStep.cs index 86ed420fd..3bc91088d 100644 --- a/Dalamud/Interface/Internal/Windows/SelfTest/AgingSteps/ContextMenuAgingStep.cs +++ b/Dalamud/Interface/Internal/Windows/SelfTest/AgingSteps/ContextMenuAgingStep.cs @@ -123,7 +123,7 @@ internal class ContextMenuAgingStep : IAgingStep this.targetCharacter = null; } - private void OnMenuOpened(MenuOpenedArgs args) + private void OnMenuOpened(IMenuOpenedArgs args) { this.LogMenuOpened(args); @@ -139,11 +139,11 @@ internal class ContextMenuAgingStep : IAgingStep PrefixColor = 56, Priority = -1, IsSubmenu = true, - OnClicked = (MenuItemClickedArgs a) => + OnClicked = (IMenuItemClickedArgs a) => { SeString name; uint count; - var targetItem = (a.Target as MenuTargetInventory).TargetItem; + var targetItem = (a.Target as MenuTargetInventory)!.TargetItem; if (targetItem is { } item) { name = (this.itemSheet.GetRow(item.ItemId)?.Name.ToDalamudString() ?? $"Unknown ({item.ItemId})") + (item.IsHq ? $" {SeIconChar.HighQuality.ToIconString()}" : string.Empty); @@ -186,7 +186,7 @@ internal class ContextMenuAgingStep : IAgingStep } } - private void LogMenuOpened(MenuOpenedArgs args) + private void LogMenuOpened(IMenuOpenedArgs args) { Log.Verbose($"Got {args.MenuType} context menu with addon 0x{args.AddonPtr:X8} ({args.AddonName}) and agent 0x{args.AgentPtr:X8}"); if (args.Target is MenuTargetDefault targetDefault) diff --git a/Dalamud/Interface/Internal/Windows/SelfTest/AgingSteps/PartyFinderAgingStep.cs b/Dalamud/Interface/Internal/Windows/SelfTest/AgingSteps/PartyFinderAgingStep.cs index f4236c4b6..d6f38092b 100644 --- a/Dalamud/Interface/Internal/Windows/SelfTest/AgingSteps/PartyFinderAgingStep.cs +++ b/Dalamud/Interface/Internal/Windows/SelfTest/AgingSteps/PartyFinderAgingStep.cs @@ -51,7 +51,7 @@ internal class PartyFinderAgingStep : IAgingStep } } - private void PartyFinderOnReceiveListing(PartyFinderListing listing, PartyFinderListingEventArgs args) + private void PartyFinderOnReceiveListing(IPartyFinderListing listing, IPartyFinderListingEventArgs args) { this.hasPassed = true; } diff --git a/Dalamud/Interface/Internal/Windows/TitleScreenMenuWindow.cs b/Dalamud/Interface/Internal/Windows/TitleScreenMenuWindow.cs index 00b8d0c39..bb94c0de8 100644 --- a/Dalamud/Interface/Internal/Windows/TitleScreenMenuWindow.cs +++ b/Dalamud/Interface/Internal/Windows/TitleScreenMenuWindow.cs @@ -147,7 +147,7 @@ internal class TitleScreenMenuWindow : Window, IDisposable return; var scale = ImGui.GetIO().FontGlobalScale; - var entries = this.titleScreenMenu.Entries; + var entries = this.titleScreenMenu.PluginEntries; var hovered = ImGui.IsWindowHovered( ImGuiHoveredFlags.RootAndChildWindows | @@ -283,7 +283,7 @@ internal class TitleScreenMenuWindow : Window, IDisposable } private bool DrawEntry( - TitleScreenMenuEntry entry, bool inhibitFadeout, bool showText, bool isFirst, bool overrideAlpha, bool interactable) + ITitleScreenMenuEntry entry, bool inhibitFadeout, bool showText, bool isFirst, bool overrideAlpha, bool interactable) { using var fontScopeDispose = this.myFontHandle.Value.Push(); diff --git a/Dalamud/Interface/TitleScreenMenu/TitleScreenMenu.cs b/Dalamud/Interface/TitleScreenMenu/TitleScreenMenu.cs index 1775ed9c3..6b2724587 100644 --- a/Dalamud/Interface/TitleScreenMenu/TitleScreenMenu.cs +++ b/Dalamud/Interface/TitleScreenMenu/TitleScreenMenu.cs @@ -36,7 +36,7 @@ internal class TitleScreenMenu : IServiceType, ITitleScreenMenu internal event Action? EntryListChange; /// - public IReadOnlyList Entries + public IReadOnlyList Entries { get { @@ -50,8 +50,32 @@ internal class TitleScreenMenu : IServiceType, ITitleScreenMenu } } - /// - public TitleScreenMenuEntry AddEntry(string text, IDalamudTextureWrap texture, Action onTriggered) + /// + /// Gets the list of entries in the title screen menu. + /// + public IReadOnlyList PluginEntries + { + get + { + lock (this.entries) + { + if (!this.entries.Any()) + return Array.Empty(); + + return this.entriesView ??= this.entries.OrderByDescending(x => x.IsInternal).ToArray(); + } + } + } + + /// + /// Adds a new entry to the title screen menu. + /// + /// The text to show. + /// The texture to show. + /// The action to execute when the option is selected. + /// A object that can be used to manage the entry. + /// Thrown when the texture provided does not match the required resolution(64x64). + public ITitleScreenMenuEntry AddPluginEntry(string text, IDalamudTextureWrap texture, Action onTriggered) { if (texture.Height != TextureSize || texture.Width != TextureSize) { @@ -78,7 +102,27 @@ internal class TitleScreenMenu : IServiceType, ITitleScreenMenu } /// - public TitleScreenMenuEntry AddEntry(ulong priority, string text, IDalamudTextureWrap texture, Action onTriggered) + public IReadOnlyTitleScreenMenuEntry AddEntry(string text, IDalamudTextureWrap texture, Action onTriggered) + { + return this.AddPluginEntry(text, texture, onTriggered); + } + + /// + public IReadOnlyTitleScreenMenuEntry AddEntry(ulong priority, string text, IDalamudTextureWrap texture, Action onTriggered) + { + return this.AddPluginEntry(priority, text, texture, onTriggered); + } + + /// + /// Adds a new entry to the title screen menu. + /// + /// Priority of the entry. + /// The text to show. + /// The texture to show. + /// The action to execute when the option is selected. + /// A object that can be used to manage the entry. + /// Thrown when the texture provided does not match the required resolution(64x64). + public ITitleScreenMenuEntry AddPluginEntry(ulong priority, string text, IDalamudTextureWrap texture, Action onTriggered) { if (texture.Height != TextureSize || texture.Width != TextureSize) { @@ -101,11 +145,11 @@ internal class TitleScreenMenu : IServiceType, ITitleScreenMenu } /// - public void RemoveEntry(TitleScreenMenuEntry entry) + public void RemoveEntry(IReadOnlyTitleScreenMenuEntry entry) { lock (this.entries) { - this.entries.Remove(entry); + this.entries.RemoveAll(pluginEntry => pluginEntry == entry); this.entriesView = null; } @@ -196,10 +240,10 @@ internal class TitleScreenMenuPluginScoped : IInternalDisposableService, ITitleS [ServiceManager.ServiceDependency] private readonly TitleScreenMenu titleScreenMenuService = Service.Get(); - private readonly List pluginEntries = new(); + private readonly List pluginEntries = new(); /// - public IReadOnlyList? Entries => this.titleScreenMenuService.Entries; + public IReadOnlyList? Entries => this.titleScreenMenuService.Entries; /// void IInternalDisposableService.DisposeService() @@ -211,25 +255,25 @@ internal class TitleScreenMenuPluginScoped : IInternalDisposableService, ITitleS } /// - public TitleScreenMenuEntry AddEntry(string text, IDalamudTextureWrap texture, Action onTriggered) + public IReadOnlyTitleScreenMenuEntry AddEntry(string text, IDalamudTextureWrap texture, Action onTriggered) { - var entry = this.titleScreenMenuService.AddEntry(text, texture, onTriggered); + var entry = this.titleScreenMenuService.AddPluginEntry(text, texture, onTriggered); this.pluginEntries.Add(entry); return entry; } /// - public TitleScreenMenuEntry AddEntry(ulong priority, string text, IDalamudTextureWrap texture, Action onTriggered) + public IReadOnlyTitleScreenMenuEntry AddEntry(ulong priority, string text, IDalamudTextureWrap texture, Action onTriggered) { - var entry = this.titleScreenMenuService.AddEntry(priority, text, texture, onTriggered); + var entry = this.titleScreenMenuService.AddPluginEntry(priority, text, texture, onTriggered); this.pluginEntries.Add(entry); return entry; } /// - public void RemoveEntry(TitleScreenMenuEntry entry) + public void RemoveEntry(IReadOnlyTitleScreenMenuEntry entry) { this.pluginEntries.Remove(entry); this.titleScreenMenuService.RemoveEntry(entry); diff --git a/Dalamud/Interface/TitleScreenMenu/TitleScreenMenuEntry.cs b/Dalamud/Interface/TitleScreenMenu/TitleScreenMenuEntry.cs index 8a400db7c..501f3e8db 100644 --- a/Dalamud/Interface/TitleScreenMenu/TitleScreenMenuEntry.cs +++ b/Dalamud/Interface/TitleScreenMenu/TitleScreenMenuEntry.cs @@ -11,7 +11,7 @@ namespace Dalamud.Interface; /// /// Class representing an entry in the title screen menu. /// -public class TitleScreenMenuEntry : IComparable +public class TitleScreenMenuEntry : ITitleScreenMenuEntry { private readonly Action onTriggered; @@ -40,40 +40,26 @@ public class TitleScreenMenuEntry : IComparable this.ShowConditionKeys = (showConditionKeys ?? Array.Empty()).ToImmutableSortedSet(); } - /// - /// Gets the priority of this entry. - /// + /// public ulong Priority { get; init; } - /// - /// Gets or sets the name of this entry. - /// + /// public string Name { get; set; } - /// - /// Gets or sets the texture of this entry. - /// + /// public IDalamudTextureWrap Texture { get; set; } - /// - /// Gets or sets a value indicating whether or not this entry is internal. - /// - internal bool IsInternal { get; set; } + /// + public bool IsInternal { get; set; } - /// - /// Gets the calling assembly of this entry. - /// - internal Assembly? CallingAssembly { get; init; } + /// + public Assembly? CallingAssembly { get; init; } - /// - /// Gets the internal ID of this entry. - /// - internal Guid Id { get; init; } = Guid.NewGuid(); + /// + public Guid Id { get; init; } = Guid.NewGuid(); - /// - /// Gets the keys that have to be pressed to show the menu. - /// - internal IReadOnlySet ShowConditionKeys { get; init; } + /// + public IReadOnlySet ShowConditionKeys { get; init; } /// public int CompareTo(TitleScreenMenuEntry? other) @@ -105,14 +91,72 @@ public class TitleScreenMenuEntry : IComparable /// Determines the displaying condition of this menu entry is met. /// /// True if met. - internal bool IsShowConditionSatisfied() => + public bool IsShowConditionSatisfied() => this.ShowConditionKeys.All(x => Service.GetNullable()?[x] is true); /// /// Trigger the action associated with this entry. /// - internal void Trigger() + public void Trigger() { this.onTriggered(); } } + +/// +/// A interface representing an entry in the title screen menu. +/// +public interface ITitleScreenMenuEntry : IReadOnlyTitleScreenMenuEntry, IComparable +{ + /// + /// Gets or sets a value indicating whether or not this entry is internal. + /// + bool IsInternal { get; set; } + + /// + /// Gets the calling assembly of this entry. + /// + Assembly? CallingAssembly { get; init; } + + /// + /// Gets the internal ID of this entry. + /// + Guid Id { get; init; } + + /// + /// Gets the keys that have to be pressed to show the menu. + /// + IReadOnlySet ShowConditionKeys { get; init; } + + /// + /// Determines the displaying condition of this menu entry is met. + /// + /// True if met. + bool IsShowConditionSatisfied(); + + /// + /// Trigger the action associated with this entry. + /// + void Trigger(); +} + +/// +/// A interface representing a read only entry in the title screen menu. +/// +public interface IReadOnlyTitleScreenMenuEntry +{ + /// + /// Gets the priority of this entry. + /// + ulong Priority { get; } + + /// + /// Gets the name of this entry. + /// + string Name { get; } + + /// + /// Gets the texture of this entry. + /// + IDalamudTextureWrap Texture { get; } +} diff --git a/Dalamud/Plugin/Internal/PluginValidator.cs b/Dalamud/Plugin/Internal/PluginValidator.cs index 134bb2a4c..2ea98ef9e 100644 --- a/Dalamud/Plugin/Internal/PluginValidator.cs +++ b/Dalamud/Plugin/Internal/PluginValidator.cs @@ -71,10 +71,11 @@ internal static class PluginValidator problems.Add(new NoMainUiProblem()); var cmdManager = Service.Get(); - foreach (var cmd in cmdManager.Commands.Where(x => x.Value.LoaderAssemblyName == plugin.InternalName && x.Value.ShowInHelp)) + + foreach (var cmd in cmdManager.GetHandlersByAssemblyName(plugin.InternalName).Where(c => c.Key.Item2.ShowInHelp)) { - if (string.IsNullOrEmpty(cmd.Value.HelpMessage)) - problems.Add(new CommandWithoutHelpTextProblem(cmd.Key)); + if (string.IsNullOrEmpty(cmd.Key.Item2.HelpMessage)) + problems.Add(new CommandWithoutHelpTextProblem(cmd.Value)); } if (plugin.Manifest.Tags == null || plugin.Manifest.Tags.Count == 0) diff --git a/Dalamud/Plugin/Services/IAetheryteList.cs b/Dalamud/Plugin/Services/IAetheryteList.cs index d98e846df..88c2ff616 100644 --- a/Dalamud/Plugin/Services/IAetheryteList.cs +++ b/Dalamud/Plugin/Services/IAetheryteList.cs @@ -7,7 +7,7 @@ namespace Dalamud.Plugin.Services; /// /// This collection represents the list of available Aetherytes in the Teleport window. /// -public interface IAetheryteList : IReadOnlyCollection +public interface IAetheryteList : IReadOnlyCollection { /// /// Gets the amount of Aetherytes the local player has unlocked. @@ -19,5 +19,5 @@ public interface IAetheryteList : IReadOnlyCollection /// /// Index. /// A at the specified index. - public AetheryteEntry? this[int index] { get; } + public IAetheryteEntry? this[int index] { get; } } diff --git a/Dalamud/Plugin/Services/IBuddyList.cs b/Dalamud/Plugin/Services/IBuddyList.cs index f5b9651a2..77c0b9c17 100644 --- a/Dalamud/Plugin/Services/IBuddyList.cs +++ b/Dalamud/Plugin/Services/IBuddyList.cs @@ -8,7 +8,7 @@ namespace Dalamud.Plugin.Services; /// This collection represents the buddies present in your squadron or trust party. /// It does not include the local player. /// -public interface IBuddyList : IReadOnlyCollection +public interface IBuddyList : IReadOnlyCollection { /// /// Gets the amount of battle buddies the local player has. @@ -18,19 +18,19 @@ public interface IBuddyList : IReadOnlyCollection /// /// Gets the active companion buddy. /// - public BuddyMember? CompanionBuddy { get; } + public IBuddyMember? CompanionBuddy { get; } /// /// Gets the active pet buddy. /// - public BuddyMember? PetBuddy { get; } + public IBuddyMember? PetBuddy { get; } /// /// Gets a battle buddy at the specified spawn index. /// /// Spawn index. /// A at the specified spawn index. - public BuddyMember? this[int index] { get; } + public IBuddyMember? this[int index] { get; } /// /// Gets the address of the companion buddy. @@ -56,5 +56,5 @@ public interface IBuddyList : IReadOnlyCollection /// /// The address of the buddy in memory. /// object containing the requested data. - public BuddyMember? CreateBuddyMemberReference(nint address); + public IBuddyMember? CreateBuddyMemberReference(nint address); } diff --git a/Dalamud/Plugin/Services/IClientState.cs b/Dalamud/Plugin/Services/IClientState.cs index 089a78d42..9f256b3eb 100644 --- a/Dalamud/Plugin/Services/IClientState.cs +++ b/Dalamud/Plugin/Services/IClientState.cs @@ -56,7 +56,7 @@ public interface IClientState /// /// Gets the local player character, if one is present. /// - public PlayerCharacter? LocalPlayer { get; } + public IPlayerCharacter? LocalPlayer { get; } /// /// Gets the content ID of the local character. diff --git a/Dalamud/Plugin/Services/ICommandManager.cs b/Dalamud/Plugin/Services/ICommandManager.cs index ead7723eb..6844ca7e0 100644 --- a/Dalamud/Plugin/Services/ICommandManager.cs +++ b/Dalamud/Plugin/Services/ICommandManager.cs @@ -12,7 +12,7 @@ public interface ICommandManager /// /// Gets a read-only list of all registered commands. /// - public ReadOnlyDictionary Commands { get; } + public ReadOnlyDictionary Commands { get; } /// /// Process a command in full. @@ -27,7 +27,7 @@ public interface ICommandManager /// The command to dispatch. /// The provided arguments. /// A object describing this command. - public void DispatchCommand(string command, string argument, CommandInfo info); + public void DispatchCommand(string command, string argument, ICommandInfo info); /// /// Add a command handler, which you can use to add your own custom commands to the in-game chat. @@ -35,7 +35,7 @@ public interface ICommandManager /// The command to register. /// A object describing the command. /// If adding was successful. - public bool AddHandler(string command, CommandInfo info); + public bool AddHandler(string command, ICommandInfo info); /// /// Remove a command from the command handlers. diff --git a/Dalamud/Plugin/Services/IContextMenu.cs b/Dalamud/Plugin/Services/IContextMenu.cs index a3bfa722a..02f773441 100644 --- a/Dalamud/Plugin/Services/IContextMenu.cs +++ b/Dalamud/Plugin/Services/IContextMenu.cs @@ -11,7 +11,7 @@ public interface IContextMenu /// A delegate type used for the event. /// /// Information about the currently opening menu. - public delegate void OnMenuOpenedDelegate(MenuOpenedArgs args); + public delegate void OnMenuOpenedDelegate(IMenuOpenedArgs args); /// /// Event that gets fired whenever any context menu is opened. @@ -25,7 +25,7 @@ public interface IContextMenu /// The type of context menu to add the item to. /// The item to add. /// Used to add a context menu entry to all context menus. - void AddMenuItem(ContextMenuType menuType, MenuItem item); + void AddMenuItem(ContextMenuType menuType, IMenuItem item); /// /// Removes a menu item from a context menu. @@ -34,5 +34,5 @@ public interface IContextMenu /// The item to add. /// Used to remove a context menu entry from all context menus. /// if the item was removed, if it was not found. - bool RemoveMenuItem(ContextMenuType menuType, MenuItem item); + bool RemoveMenuItem(ContextMenuType menuType, IMenuItem item); } diff --git a/Dalamud/Plugin/Services/IDtrBar.cs b/Dalamud/Plugin/Services/IDtrBar.cs index ae2fe560b..733a6d7e1 100644 --- a/Dalamud/Plugin/Services/IDtrBar.cs +++ b/Dalamud/Plugin/Services/IDtrBar.cs @@ -24,8 +24,7 @@ public interface IDtrBar /// The text the entry shows. /// The entry object used to update, hide and remove the entry. /// Thrown when an entry with the specified title exists. - [Api10ToDo("Return IDtrBarEntry instead of DtrBarEntry")] - public DtrBarEntry Get(string title, SeString? text = null); + public IDtrBarEntry Get(string title, SeString? text = null); /// /// Removes a DTR bar entry from the system. diff --git a/Dalamud/Plugin/Services/IFateTable.cs b/Dalamud/Plugin/Services/IFateTable.cs index ba243ec04..43bbbc9a1 100644 --- a/Dalamud/Plugin/Services/IFateTable.cs +++ b/Dalamud/Plugin/Services/IFateTable.cs @@ -7,7 +7,7 @@ namespace Dalamud.Plugin.Services; /// /// This collection represents the currently available Fate events. /// -public interface IFateTable : IReadOnlyCollection +public interface IFateTable : IReadOnlyCollection { /// /// Gets the address of the Fate table. @@ -18,13 +18,20 @@ public interface IFateTable : IReadOnlyCollection /// Gets the amount of currently active Fates. /// public int Length { get; } + + /// + /// Gets a value indicating whether this Fate is still valid in memory. + /// + /// The fate to check. + /// True or false. + public bool IsValid(IFate fate); /// /// Get an actor at the specified spawn index. /// /// Spawn index. /// A at the specified spawn index. - public Fate? this[int index] { get; } + public IFate? this[int index] { get; } /// /// Gets the address of the Fate at the specified index of the fate table. @@ -38,5 +45,5 @@ public interface IFateTable : IReadOnlyCollection /// /// The offset of the actor in memory. /// object containing requested data. - public Fate? CreateFateReference(nint offset); + public IFate? CreateFateReference(nint offset); } diff --git a/Dalamud/Plugin/Services/IObjectTable.cs b/Dalamud/Plugin/Services/IObjectTable.cs index bc6f9f811..a74e6db7a 100644 --- a/Dalamud/Plugin/Services/IObjectTable.cs +++ b/Dalamud/Plugin/Services/IObjectTable.cs @@ -7,7 +7,7 @@ namespace Dalamud.Plugin.Services; /// /// This collection represents the currently spawned FFXIV game objects. /// -public interface IObjectTable : IEnumerable +public interface IObjectTable : IEnumerable { /// /// Gets the address of the object table. @@ -24,14 +24,14 @@ public interface IObjectTable : IEnumerable /// /// Spawn index. /// An at the specified spawn index. - public GameObject? this[int index] { get; } + public IGameObject? this[int index] { get; } /// /// Search for a game object by their Object ID. /// /// Object ID to find. /// A game object or null. - public GameObject? SearchById(ulong gameObjectId); + public IGameObject? SearchById(ulong gameObjectId); /// /// Gets the address of the game object at the specified index of the object table. @@ -45,5 +45,5 @@ public interface IObjectTable : IEnumerable /// /// The address of the object in memory. /// object or inheritor containing the requested data. - public GameObject? CreateObjectReference(nint address); + public IGameObject? CreateObjectReference(nint address); } diff --git a/Dalamud/Plugin/Services/IPartyFinderGui.cs b/Dalamud/Plugin/Services/IPartyFinderGui.cs index f656963db..fb7a49acd 100644 --- a/Dalamud/Plugin/Services/IPartyFinderGui.cs +++ b/Dalamud/Plugin/Services/IPartyFinderGui.cs @@ -13,7 +13,7 @@ public interface IPartyFinderGui /// /// The listings received. /// Additional arguments passed by the game. - public delegate void PartyFinderListingEventDelegate(PartyFinderListing listing, PartyFinderListingEventArgs args); + public delegate void PartyFinderListingEventDelegate(IPartyFinderListing listing, IPartyFinderListingEventArgs args); /// /// Event fired each time the game receives an individual Party Finder listing. diff --git a/Dalamud/Plugin/Services/IPartyList.cs b/Dalamud/Plugin/Services/IPartyList.cs index fbf663a00..b046f36db 100644 --- a/Dalamud/Plugin/Services/IPartyList.cs +++ b/Dalamud/Plugin/Services/IPartyList.cs @@ -7,7 +7,7 @@ namespace Dalamud.Plugin.Services; /// /// This collection represents the actors present in your party or alliance. /// -public interface IPartyList : IReadOnlyCollection +public interface IPartyList : IReadOnlyCollection { /// /// Gets the amount of party members the local player has. @@ -49,7 +49,7 @@ public interface IPartyList : IReadOnlyCollection /// /// Spawn index. /// A at the specified spawn index. - public PartyMember? this[int index] { get; } + public IPartyMember? this[int index] { get; } /// /// Gets the address of the party member at the specified index of the party list. @@ -63,7 +63,7 @@ public interface IPartyList : IReadOnlyCollection /// /// The address of the party member in memory. /// The party member object containing the requested data. - public PartyMember? CreatePartyMemberReference(nint address); + public IPartyMember? CreatePartyMemberReference(nint address); /// /// Gets the address of the alliance member at the specified index of the alliance list. @@ -77,5 +77,5 @@ public interface IPartyList : IReadOnlyCollection /// /// The address of the alliance member in memory. /// The party member object containing the requested data. - public PartyMember? CreateAllianceMemberReference(nint address); + public IPartyMember? CreateAllianceMemberReference(nint address); } diff --git a/Dalamud/Plugin/Services/ITargetManager.cs b/Dalamud/Plugin/Services/ITargetManager.cs index 8b7d78262..5ba9f390e 100644 --- a/Dalamud/Plugin/Services/ITargetManager.cs +++ b/Dalamud/Plugin/Services/ITargetManager.cs @@ -11,41 +11,41 @@ public interface ITargetManager /// Gets or sets the current target. /// Set to null to clear the target. /// - public GameObject? Target { get; set; } + public IGameObject? Target { get; set; } /// /// Gets or sets the mouseover target. /// Set to null to clear the target. /// - public GameObject? MouseOverTarget { get; set; } + public IGameObject? MouseOverTarget { get; set; } /// /// Gets or sets the focus target. /// Set to null to clear the target. /// - public GameObject? FocusTarget { get; set; } + public IGameObject? FocusTarget { get; set; } /// /// Gets or sets the previous target. /// Set to null to clear the target. /// - public GameObject? PreviousTarget { get; set; } + public IGameObject? PreviousTarget { get; set; } /// /// Gets or sets the soft target. /// Set to null to clear the target. /// - public GameObject? SoftTarget { get; set; } + public IGameObject? SoftTarget { get; set; } /// /// Gets or sets the gpose target. /// Set to null to clear the target. /// - public GameObject? GPoseTarget { get; set; } + public IGameObject? GPoseTarget { get; set; } /// /// Gets or sets the mouseover nameplate target. /// Set to null to clear the target. /// - public GameObject? MouseOverNameplateTarget { get; set; } + public IGameObject? MouseOverNameplateTarget { get; set; } } diff --git a/Dalamud/Plugin/Services/ITitleScreenMenu.cs b/Dalamud/Plugin/Services/ITitleScreenMenu.cs index 9db8c70e4..e3b1eb993 100644 --- a/Dalamud/Plugin/Services/ITitleScreenMenu.cs +++ b/Dalamud/Plugin/Services/ITitleScreenMenu.cs @@ -11,9 +11,9 @@ namespace Dalamud.Plugin.Services; public interface ITitleScreenMenu { /// - /// Gets the list of entries in the title screen menu. + /// Gets the list of read only entries in the title screen menu. /// - public IReadOnlyList Entries { get; } + public IReadOnlyList Entries { get; } /// /// Adds a new entry to the title screen menu. @@ -21,9 +21,9 @@ public interface ITitleScreenMenu /// The text to show. /// The texture to show. /// The action to execute when the option is selected. - /// A object that can be used to manage the entry. + /// A object that can be reference the entry. /// Thrown when the texture provided does not match the required resolution(64x64). - public TitleScreenMenuEntry AddEntry(string text, IDalamudTextureWrap texture, Action onTriggered); + public IReadOnlyTitleScreenMenuEntry AddEntry(string text, IDalamudTextureWrap texture, Action onTriggered); /// /// Adds a new entry to the title screen menu. @@ -32,13 +32,13 @@ public interface ITitleScreenMenu /// The text to show. /// The texture to show. /// The action to execute when the option is selected. - /// A object that can be used to manage the entry. + /// A object that can be used to reference the entry. /// Thrown when the texture provided does not match the required resolution(64x64). - public TitleScreenMenuEntry AddEntry(ulong priority, string text, IDalamudTextureWrap texture, Action onTriggered); + public IReadOnlyTitleScreenMenuEntry AddEntry(ulong priority, string text, IDalamudTextureWrap texture, Action onTriggered); /// /// Remove an entry from the title screen menu. /// /// The entry to remove. - public void RemoveEntry(TitleScreenMenuEntry entry); + public void RemoveEntry(IReadOnlyTitleScreenMenuEntry entry); } diff --git a/Dalamud/Utility/MapUtil.cs b/Dalamud/Utility/MapUtil.cs index d71af9987..4eed22623 100644 --- a/Dalamud/Utility/MapUtil.cs +++ b/Dalamud/Utility/MapUtil.cs @@ -142,7 +142,7 @@ public static class MapUtil /// The GameObject to get the position for. /// Whether to "correct" a Z offset to sane values for maps that don't have one. /// A Vector3 that represents the X (east/west), Y (north/south), and Z (height) position of this object. - public static unsafe Vector3 GetMapCoordinates(this GameObject go, bool correctZOffset = false) + public static unsafe Vector3 GetMapCoordinates(this IGameObject go, bool correctZOffset = false) { var agentMap = AgentMap.Instance(); diff --git a/Dalamud/Utility/Util.cs b/Dalamud/Utility/Util.cs index 348efbac1..a9be33244 100644 --- a/Dalamud/Utility/Util.cs +++ b/Dalamud/Utility/Util.cs @@ -270,7 +270,7 @@ public static class Util /// /// The GameObject to show. /// Whether or not the struct should start as expanded. - public static unsafe void ShowGameObjectStruct(GameObject go, bool autoExpand = true) + public static unsafe void ShowGameObjectStruct(IGameObject go, bool autoExpand = true) { switch (go) { @@ -280,8 +280,8 @@ public static class Util case Character chara: ShowStruct(chara.Struct, autoExpand); break; - default: - ShowStruct(go.Struct, autoExpand); + case GameObject gameObject: + ShowStruct(gameObject.Struct, autoExpand); break; } } @@ -739,6 +739,40 @@ public static class Util // ignore } } + + /// + /// Print formatted IGameObject Information to ImGui. + /// + /// IGameObject to Display. + /// Display Tag. + /// If the IGameObjects data should be resolved. + internal static void PrintGameObject(IGameObject actor, string tag, bool resolveGameData) + { + var actorString = + $"{actor.Address.ToInt64():X}:{actor.GameObjectId: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.DataId} NameId:{npc.NameId}\n"; + + if (actor is ICharacter chara) + { + actorString += + $" Level: {chara.Level} ClassJob: {(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"; + } + + if (actor is IPlayerCharacter pc) + { + actorString += + $" HomeWorld: {(resolveGameData ? pc.HomeWorld.GameData?.Name : pc.HomeWorld.Id.ToString())} CurrentWorld: {(resolveGameData ? pc.CurrentWorld.GameData?.Name : pc.CurrentWorld.Id.ToString())} FC: {pc.CompanyTag}\n"; + } + + ImGui.TextUnformatted(actorString); + ImGui.SameLine(); + if (ImGui.Button($"C##{actor.Address.ToInt64()}")) + { + ImGui.SetClipboardText(actor.Address.ToInt64().ToString("X")); + } + } /// /// Print formatted GameObject Information to ImGui.