mirror of
https://github.com/goatcorp/Dalamud.git
synced 2025-12-30 20:33:40 +01:00
StyleCop: everything else
This commit is contained in:
parent
f64c9b8321
commit
595fd3f1e4
134 changed files with 16346 additions and 6202 deletions
|
|
@ -1,170 +1,224 @@
|
|||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
using Dalamud.Game.ClientState.Actors.Types;
|
||||
using Dalamud.Game.ClientState.Actors.Types.NonPlayer;
|
||||
using JetBrains.Annotations;
|
||||
using Serilog;
|
||||
|
||||
namespace Dalamud.Game.ClientState.Actors {
|
||||
namespace Dalamud.Game.ClientState.Actors
|
||||
{
|
||||
/// <summary>
|
||||
/// This collection represents the currently spawned FFXIV actors.
|
||||
/// This collection represents the currently spawned FFXIV actors.
|
||||
/// </summary>
|
||||
public class ActorTable : IReadOnlyCollection<Actor>, ICollection, IDisposable {
|
||||
|
||||
public sealed partial class ActorTable : IReadOnlyCollection<Actor>, ICollection, IDisposable
|
||||
{
|
||||
private const int ActorTableLength = 424;
|
||||
|
||||
#region Actor Table Cache
|
||||
#region ReadProcessMemory Hack
|
||||
private static readonly int ActorMemSize = Marshal.SizeOf(typeof(Structs.Actor));
|
||||
private static readonly IntPtr ActorMem = Marshal.AllocHGlobal(ActorMemSize);
|
||||
private static readonly IntPtr CurrentProcessHandle = new(-1);
|
||||
#endregion
|
||||
|
||||
private Dalamud dalamud;
|
||||
private ClientStateAddressResolver address;
|
||||
private List<Actor> actorsCache;
|
||||
|
||||
private List<Actor> ActorsCache {
|
||||
get {
|
||||
if (this.actorsCache != null) return this.actorsCache;
|
||||
this.actorsCache = GetActorTable();
|
||||
return this.actorsCache;
|
||||
}
|
||||
}
|
||||
|
||||
private void ResetCache() => actorsCache = null;
|
||||
#endregion
|
||||
|
||||
#region ReadProcessMemory Hack
|
||||
|
||||
[DllImport("kernel32.dll", SetLastError = true)]
|
||||
static extern bool ReadProcessMemory(
|
||||
IntPtr hProcess,
|
||||
IntPtr lpBaseAddress,
|
||||
IntPtr lpBuffer,
|
||||
int dwSize,
|
||||
out IntPtr lpNumberOfBytesRead);
|
||||
|
||||
private static readonly int ActorMemSize = Marshal.SizeOf(typeof(Structs.Actor));
|
||||
private IntPtr actorMem = Marshal.AllocHGlobal(ActorMemSize);
|
||||
private IntPtr currentProcessHandle = new IntPtr(-1);
|
||||
|
||||
#endregion
|
||||
|
||||
private ClientStateAddressResolver Address { get; }
|
||||
private Dalamud dalamud;
|
||||
|
||||
/// <summary>
|
||||
/// Set up the actor table collection.
|
||||
/// Initializes a new instance of the <see cref="ActorTable"/> class.
|
||||
/// Set up the actor table collection.
|
||||
/// </summary>
|
||||
/// <param name="addressResolver">Client state address resolver.</param>
|
||||
public ActorTable(Dalamud dalamud, ClientStateAddressResolver addressResolver) {
|
||||
Address = addressResolver;
|
||||
/// <param name="dalamud">The Dalamud instance.</param>
|
||||
/// <param name="addressResolver">The ClientStateAddressResolver instance.</param>
|
||||
public ActorTable(Dalamud dalamud, ClientStateAddressResolver addressResolver)
|
||||
{
|
||||
this.address = addressResolver;
|
||||
this.dalamud = dalamud;
|
||||
|
||||
dalamud.Framework.OnUpdateEvent += Framework_OnUpdateEvent;
|
||||
dalamud.Framework.OnUpdateEvent += this.Framework_OnUpdateEvent;
|
||||
|
||||
Log.Verbose("Actor table address {ActorTable}", Address.ActorTable);
|
||||
}
|
||||
|
||||
private void Framework_OnUpdateEvent(Internal.Framework framework) {
|
||||
this.ResetCache();
|
||||
Log.Verbose("Actor table address {ActorTable}", this.address.ActorTable);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get an actor at the specified spawn index.
|
||||
/// Gets the amount of currently spawned actors.
|
||||
/// </summary>
|
||||
public int Length => this.ActorsCache.Count;
|
||||
|
||||
private List<Actor> ActorsCache => this.actorsCache ??= this.GetActorTable();
|
||||
|
||||
/// <summary>
|
||||
/// Get an actor at the specified spawn index.
|
||||
/// </summary>
|
||||
/// <param name="index">Spawn index.</param>
|
||||
/// <returns><see cref="Actor" /> at the specified spawn index.</returns>
|
||||
[CanBeNull]
|
||||
public Actor this[int index] {
|
||||
get => ActorsCache[index];
|
||||
}
|
||||
public Actor this[int index] => this.ActorsCache[index];
|
||||
|
||||
/// <summary>
|
||||
/// Read an actor struct from memory and create the appropriate <see cref="ObjectKind"/> type of actor.
|
||||
/// </summary>
|
||||
/// <param name="offset">Offset of the actor in the actor table.</param>
|
||||
/// <returns>An instantiated actor.</returns>
|
||||
internal Actor ReadActorFromMemory(IntPtr offset)
|
||||
{
|
||||
try {
|
||||
try
|
||||
{
|
||||
// FIXME: hack workaround for trying to access the player on logout, after the main object has been deleted
|
||||
if (!ReadProcessMemory(this.currentProcessHandle, offset, this.actorMem, ActorMemSize, out _))
|
||||
if (!NativeFunctions.ReadProcessMemory(CurrentProcessHandle, offset, ActorMem, ActorMemSize, out _))
|
||||
{
|
||||
Log.Debug("ActorTable - ReadProcessMemory failed: likely player deletion during logout");
|
||||
return null;
|
||||
}
|
||||
|
||||
var actorStruct = Marshal.PtrToStructure<Structs.Actor>(this.actorMem);
|
||||
var actorStruct = Marshal.PtrToStructure<Structs.Actor>(ActorMem);
|
||||
|
||||
return actorStruct.ObjectKind switch {
|
||||
return actorStruct.ObjectKind switch
|
||||
{
|
||||
ObjectKind.Player => new PlayerCharacter(offset, actorStruct, this.dalamud),
|
||||
ObjectKind.BattleNpc => new BattleNpc(offset, actorStruct, this.dalamud),
|
||||
ObjectKind.EventObj => new EventObj(offset, actorStruct, this.dalamud),
|
||||
ObjectKind.Companion => new Npc(offset, actorStruct, this.dalamud),
|
||||
_ => new Actor(offset, actorStruct, this.dalamud)
|
||||
_ => new Actor(offset, actorStruct, this.dalamud),
|
||||
};
|
||||
}
|
||||
catch (Exception e) {
|
||||
catch (Exception e)
|
||||
{
|
||||
Log.Error(e, "Could not read actor from memory.");
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private IntPtr[] GetPointerTable() {
|
||||
private void ResetCache() => this.actorsCache = null;
|
||||
|
||||
private void Framework_OnUpdateEvent(Internal.Framework framework)
|
||||
{
|
||||
this.ResetCache();
|
||||
}
|
||||
|
||||
private IntPtr[] GetPointerTable()
|
||||
{
|
||||
var ret = new IntPtr[ActorTableLength];
|
||||
Marshal.Copy(Address.ActorTable, ret, 0, ActorTableLength);
|
||||
Marshal.Copy(this.address.ActorTable, ret, 0, ActorTableLength);
|
||||
return ret;
|
||||
}
|
||||
|
||||
private List<Actor> GetActorTable() {
|
||||
private List<Actor> GetActorTable()
|
||||
{
|
||||
var actors = new List<Actor>();
|
||||
var ptrTable = GetPointerTable();
|
||||
for (var i = 0; i < ActorTableLength; i++) {
|
||||
actors.Add(ptrTable[i] != IntPtr.Zero ? ReadActorFromMemory(ptrTable[i]) : null);
|
||||
var ptrTable = this.GetPointerTable();
|
||||
for (var i = 0; i < ActorTableLength; i++)
|
||||
{
|
||||
actors.Add(ptrTable[i] != IntPtr.Zero ? this.ReadActorFromMemory(ptrTable[i]) : null);
|
||||
}
|
||||
|
||||
return actors;
|
||||
}
|
||||
}
|
||||
|
||||
public IEnumerator<Actor> GetEnumerator() {
|
||||
return ActorsCache.Where(a => a != null).GetEnumerator();
|
||||
}
|
||||
|
||||
IEnumerator IEnumerable.GetEnumerator() {
|
||||
return GetEnumerator();
|
||||
}
|
||||
/// <summary>
|
||||
/// Implementing IDisposable.
|
||||
/// </summary>
|
||||
public sealed partial class ActorTable : IDisposable
|
||||
{
|
||||
private bool disposed = false;
|
||||
|
||||
/// <summary>
|
||||
/// The amount of currently spawned actors.
|
||||
/// Finalizes an instance of the <see cref="ActorTable"/> class.
|
||||
/// </summary>
|
||||
public int Length => ActorsCache.Count;
|
||||
~ActorTable() => this.Dispose(false);
|
||||
|
||||
int IReadOnlyCollection<Actor>.Count => Length;
|
||||
/// <summary>
|
||||
/// Disposes of managed and unmanaged resources.
|
||||
/// </summary>
|
||||
public void Dispose()
|
||||
{
|
||||
this.Dispose(true);
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
|
||||
int ICollection.Count => Length;
|
||||
private void Dispose(bool disposing)
|
||||
{
|
||||
if (this.disposed)
|
||||
return;
|
||||
|
||||
if (disposing)
|
||||
{
|
||||
this.dalamud.Framework.OnUpdateEvent -= this.Framework_OnUpdateEvent;
|
||||
Marshal.FreeHGlobal(ActorMem);
|
||||
}
|
||||
|
||||
this.disposed = true;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Implementing IReadOnlyCollection, IEnumerable, and Enumerable.
|
||||
/// </summary>
|
||||
public sealed partial class ActorTable : IReadOnlyCollection<Actor>
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the number of elements in the collection.
|
||||
/// </summary>
|
||||
/// <returns>The number of elements in the collection.</returns>
|
||||
int IReadOnlyCollection<Actor>.Count => this.Length;
|
||||
|
||||
/// <summary>
|
||||
/// Gets an enumerator capable of iterating through the actor table.
|
||||
/// </summary>
|
||||
/// <returns>An actor enumerable.</returns>
|
||||
public IEnumerator<Actor> GetEnumerator() => this.ActorsCache.Where(a => a != null).GetEnumerator();
|
||||
|
||||
/// <summary>
|
||||
/// Gets an enumerator capable of iterating through the actor table.
|
||||
/// </summary>
|
||||
/// <returns>An actor enumerable.</returns>
|
||||
IEnumerator IEnumerable.GetEnumerator() => this.GetEnumerator();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Implementing ICollection.
|
||||
/// </summary>
|
||||
public sealed partial class ActorTable : ICollection
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the number of elements in the collection.
|
||||
/// </summary>
|
||||
/// <returns>The number of elements in the collection.</returns>
|
||||
int ICollection.Count => this.Length;
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether access to the collection is synchronized (thread safe).
|
||||
/// </summary>
|
||||
/// <returns>Whether access is synchronized (thread safe) or not.</returns>
|
||||
bool ICollection.IsSynchronized => false;
|
||||
|
||||
/// <summary>
|
||||
/// Gets an object that can be used to synchronize access to the collection.
|
||||
/// </summary>
|
||||
/// <returns>An object that can be used to synchronize access to the collection.</returns>
|
||||
object ICollection.SyncRoot => this;
|
||||
|
||||
void ICollection.CopyTo(Array array, int index) {
|
||||
for (var i = 0; i < Length; i++) {
|
||||
/// <summary>
|
||||
/// Copies the elements of the collection to an array, starting at a particular index.
|
||||
/// </summary>
|
||||
/// <param name="array">
|
||||
/// The one-dimensional array that is the destination of the elements copied from the collection. The array must have zero-based indexing.
|
||||
/// </param>
|
||||
/// <param name="index">
|
||||
/// The zero-based index in array at which copying begins.
|
||||
/// </param>
|
||||
void ICollection.CopyTo(Array array, int index)
|
||||
{
|
||||
for (var i = 0; i < this.Length; i++)
|
||||
{
|
||||
array.SetValue(this[i], index);
|
||||
index++;
|
||||
}
|
||||
}
|
||||
|
||||
#region IDisposable Pattern
|
||||
private bool disposed = false;
|
||||
|
||||
private void Dispose(bool disposing)
|
||||
{
|
||||
if (this.disposed) return;
|
||||
this.dalamud.Framework.OnUpdateEvent -= Framework_OnUpdateEvent;
|
||||
Marshal.FreeHGlobal(this.actorMem);
|
||||
this.disposed = true;
|
||||
}
|
||||
|
||||
public void Dispose() {
|
||||
Dispose(true);
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
|
||||
~ActorTable() {
|
||||
Dispose(false);
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,16 +1,11 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Dalamud.Game.ClientState.Actors
|
||||
{
|
||||
/// <summary>
|
||||
/// This enum describes the indices of the Customize array.
|
||||
/// </summary>
|
||||
// TODO: This may need some rework since it may not be entirely accurate (stolen from Sapphire)
|
||||
public enum CustomizeIndex {
|
||||
public enum CustomizeIndex
|
||||
{
|
||||
/// <summary>
|
||||
/// The race of the character.
|
||||
/// </summary>
|
||||
|
|
@ -35,12 +30,12 @@ namespace Dalamud.Game.ClientState.Actors
|
|||
/// The model type of the character.
|
||||
/// </summary>
|
||||
ModelType = 0x02, // Au Ra: changes horns/tails, everything else: seems to drastically change appearance (flip between two sets, odd/even numbers). sometimes retains hairstyle and other features
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// The face type of the character.
|
||||
/// </summary>
|
||||
FaceType = 0x05,
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// The hair of the character.
|
||||
/// </summary>
|
||||
|
|
@ -50,12 +45,12 @@ namespace Dalamud.Game.ClientState.Actors
|
|||
/// Whether or not the character has hair highlights.
|
||||
/// </summary>
|
||||
HasHighlights = 0x07, // negative to enable, positive to disable
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// The skin color of the character.
|
||||
/// </summary>
|
||||
SkinColor = 0x08,
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// The eye color of the character.
|
||||
/// </summary>
|
||||
|
|
@ -125,17 +120,17 @@ namespace Dalamud.Game.ClientState.Actors
|
|||
/// The race feature type of the character.
|
||||
/// </summary>
|
||||
RaceFeatureType = 0x16, // negative or out of range tail shapes for race result in no tail (e.g. Au Ra has max of 4 tail shapes), incorrect value can crash client
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// The bust size of the character.
|
||||
/// </summary>
|
||||
BustSize = 0x17, // char creator allows up to max of 100, i set to 127 cause who wouldnt but no visible difference
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// The face paint of the character.
|
||||
/// </summary>
|
||||
Facepaint = 0x18,
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// The face paint color of the character.
|
||||
/// </summary>
|
||||
|
|
|
|||
|
|
@ -1,69 +1,83 @@
|
|||
namespace Dalamud.Game.ClientState.Actors {
|
||||
namespace Dalamud.Game.ClientState.Actors
|
||||
{
|
||||
/// <summary>
|
||||
/// Enum describing possible entity kinds.
|
||||
/// Enum describing possible entity kinds.
|
||||
/// </summary>
|
||||
public enum ObjectKind : byte {
|
||||
public enum ObjectKind : byte
|
||||
{
|
||||
/// <summary>
|
||||
/// Invalid actor.
|
||||
/// Invalid actor.
|
||||
/// </summary>
|
||||
None = 0x00,
|
||||
|
||||
/// <summary>
|
||||
/// Objects representing player characters.
|
||||
/// Objects representing player characters.
|
||||
/// </summary>
|
||||
Player = 0x01,
|
||||
|
||||
/// <summary>
|
||||
/// Objects representing battle NPCs.
|
||||
/// Objects representing battle NPCs.
|
||||
/// </summary>
|
||||
BattleNpc = 0x02,
|
||||
|
||||
/// <summary>
|
||||
/// Objects representing event NPCs.
|
||||
/// Objects representing event NPCs.
|
||||
/// </summary>
|
||||
EventNpc = 0x03,
|
||||
|
||||
/// <summary>
|
||||
/// Objects representing treasures.
|
||||
/// Objects representing treasures.
|
||||
/// </summary>
|
||||
Treasure = 0x04,
|
||||
|
||||
/// <summary>
|
||||
/// Objects representing aetherytes.
|
||||
/// Objects representing aetherytes.
|
||||
/// </summary>
|
||||
Aetheryte = 0x05,
|
||||
|
||||
/// <summary>
|
||||
/// Objects representing gathering points.
|
||||
/// Objects representing gathering points.
|
||||
/// </summary>
|
||||
GatheringPoint = 0x06,
|
||||
|
||||
/// <summary>
|
||||
/// Objects representing event objects.
|
||||
/// Objects representing event objects.
|
||||
/// </summary>
|
||||
EventObj = 0x07,
|
||||
|
||||
/// <summary>
|
||||
/// Objects representing mounts.
|
||||
/// Objects representing mounts.
|
||||
/// </summary>
|
||||
MountType = 0x08,
|
||||
|
||||
/// <summary>
|
||||
/// Objects representing minions.
|
||||
/// Objects representing minions.
|
||||
/// </summary>
|
||||
Companion = 0x09, // Minion
|
||||
|
||||
/// <summary>
|
||||
/// Objects representing retainers.
|
||||
/// Objects representing retainers.
|
||||
/// </summary>
|
||||
Retainer = 0x0A,
|
||||
|
||||
/// <summary>
|
||||
/// Objects representing area objects.
|
||||
/// </summary>
|
||||
Area = 0x0B,
|
||||
|
||||
/// <summary>
|
||||
/// Objects representing housing objects.
|
||||
/// Objects representing housing objects.
|
||||
/// </summary>
|
||||
Housing = 0x0C,
|
||||
|
||||
/// <summary>
|
||||
/// Objects representing cutscene objects.
|
||||
/// </summary>
|
||||
Cutscene = 0x0D,
|
||||
CardStand = 0x0E
|
||||
|
||||
/// <summary>
|
||||
/// Objects representing card stand objects.
|
||||
/// </summary>
|
||||
CardStand = 0x0E,
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,22 +1,38 @@
|
|||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace Dalamud.Game.ClientState.Actors {
|
||||
namespace Dalamud.Game.ClientState.Actors
|
||||
{
|
||||
/// <summary>
|
||||
/// A game native equivalent of a Vector3.
|
||||
/// </summary>
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
public struct Position3 {
|
||||
public struct Position3
|
||||
{
|
||||
/// <summary>
|
||||
/// The X of (X,Z,Y).
|
||||
/// </summary>
|
||||
public float X;
|
||||
|
||||
/// <summary>
|
||||
/// The Z of (X,Z,Y).
|
||||
/// </summary>
|
||||
public float Z;
|
||||
|
||||
/// <summary>
|
||||
/// The Y of (X,Z,Y).
|
||||
/// </summary>
|
||||
public float Y;
|
||||
|
||||
/// <summary>
|
||||
/// Convert this Position3 to a System.Numerics.Vector3
|
||||
/// Convert this Position3 to a System.Numerics.Vector3.
|
||||
/// </summary>
|
||||
/// <param name="pos">Position to convert.</param>
|
||||
public static implicit operator System.Numerics.Vector3(Position3 pos) => new System.Numerics.Vector3(pos.X, pos.Y, pos.Z);
|
||||
public static implicit operator System.Numerics.Vector3(Position3 pos) => new(pos.X, pos.Y, pos.Z);
|
||||
|
||||
/// <summary>
|
||||
/// Convert this Position3 to a SharpDX.Vector3
|
||||
/// Convert this Position3 to a SharpDX.Vector3.
|
||||
/// </summary>
|
||||
/// <param name="pos">Position to convert.</param>
|
||||
public static implicit operator SharpDX.Vector3(Position3 pos) => new SharpDX.Vector3(pos.X, pos.Z, pos.Y);
|
||||
public static implicit operator SharpDX.Vector3(Position3 pos) => new(pos.X, pos.Z, pos.Y);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,17 +1,24 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Dalamud.Game.ClientState.Actors.Resolvers
|
||||
{
|
||||
public abstract class BaseResolver {
|
||||
protected Dalamud dalamud;
|
||||
/// <summary>
|
||||
/// Base object resolver.
|
||||
/// </summary>
|
||||
public abstract class BaseResolver
|
||||
{
|
||||
private Dalamud dalamud;
|
||||
|
||||
public BaseResolver(Dalamud dalamud) {
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="BaseResolver"/> class.
|
||||
/// </summary>
|
||||
/// <param name="dalamud">The Dalamud instance.</param>
|
||||
public BaseResolver(Dalamud dalamud)
|
||||
{
|
||||
this.dalamud = dalamud;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the Dalamud instance.
|
||||
/// </summary>
|
||||
protected Dalamud Dalamud => this.dalamud;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,32 +1,31 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Dalamud.Game.ClientState.Actors.Resolvers
|
||||
{
|
||||
/// <summary>
|
||||
/// This object represents a class or job.
|
||||
/// </summary>
|
||||
public class ClassJob : BaseResolver {
|
||||
public class ClassJob : BaseResolver
|
||||
{
|
||||
/// <summary>
|
||||
/// ID of the ClassJob.
|
||||
/// </summary>
|
||||
public readonly uint Id;
|
||||
|
||||
/// <summary>
|
||||
/// GameData linked to this ClassJob.
|
||||
/// </summary>
|
||||
public Lumina.Excel.GeneratedSheets.ClassJob GameData =>
|
||||
this.dalamud.Data.GetExcelSheet<Lumina.Excel.GeneratedSheets.ClassJob>().GetRow(this.Id);
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="ClassJob"/> class.
|
||||
/// Set up the ClassJob resolver with the provided ID.
|
||||
/// </summary>
|
||||
/// <param name="id">The ID of the world.</param>
|
||||
public ClassJob(byte id, Dalamud dalamud) : base(dalamud) {
|
||||
/// <param name="id">The ID of the classJob.</param>
|
||||
/// <param name="dalamud">The Dalamud instance.</param>
|
||||
public ClassJob(byte id, Dalamud dalamud)
|
||||
: base(dalamud)
|
||||
{
|
||||
this.Id = id;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets GameData linked to this ClassJob.
|
||||
/// </summary>
|
||||
public Lumina.Excel.GeneratedSheets.ClassJob GameData =>
|
||||
this.Dalamud.Data.GetExcelSheet<Lumina.Excel.GeneratedSheets.ClassJob>().GetRow(this.Id);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,32 +1,31 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Dalamud.Game.ClientState.Actors.Resolvers
|
||||
{
|
||||
/// <summary>
|
||||
/// This object represents a world a character can reside on.
|
||||
/// </summary>
|
||||
public class World : BaseResolver {
|
||||
public class World : BaseResolver
|
||||
{
|
||||
/// <summary>
|
||||
/// ID of the world.
|
||||
/// </summary>
|
||||
public readonly uint Id;
|
||||
|
||||
/// <summary>
|
||||
/// GameData linked to this world.
|
||||
/// </summary>
|
||||
public Lumina.Excel.GeneratedSheets.World GameData =>
|
||||
this.dalamud.Data.GetExcelSheet<Lumina.Excel.GeneratedSheets.World>().GetRow(this.Id);
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="World"/> class.
|
||||
/// Set up the world resolver with the provided ID.
|
||||
/// </summary>
|
||||
/// <param name="id">The ID of the world.</param>
|
||||
public World(ushort id, Dalamud dalamud) : base(dalamud) {
|
||||
/// <param name="dalamud">The Dalamud instance.</param>
|
||||
public World(ushort id, Dalamud dalamud)
|
||||
: base(dalamud)
|
||||
{
|
||||
this.Id = id;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets GameData linked to this world.
|
||||
/// </summary>
|
||||
public Lumina.Excel.GeneratedSheets.World GameData =>
|
||||
this.Dalamud.Data.GetExcelSheet<Lumina.Excel.GeneratedSheets.World>().GetRow(this.Id);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,50 +1,118 @@
|
|||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
using Dalamud.Game.ClientState.Actors.Types;
|
||||
|
||||
namespace Dalamud.Game.ClientState.Actors {
|
||||
public static class TargetOffsets {
|
||||
public const int CurrentTarget = 0x80;
|
||||
public const int MouseOverTarget = 0xD0;
|
||||
public const int FocusTarget = 0xF8;
|
||||
public const int PreviousTarget = 0x110;
|
||||
public const int SoftTarget = 0x88;
|
||||
}
|
||||
|
||||
public sealed class Targets {
|
||||
private ClientStateAddressResolver Address { get; }
|
||||
namespace Dalamud.Game.ClientState.Actors
|
||||
{
|
||||
/// <summary>
|
||||
/// Get and set various kinds of targets for the player.
|
||||
/// </summary>
|
||||
public sealed class Targets
|
||||
{
|
||||
private Dalamud dalamud;
|
||||
private ClientStateAddressResolver address;
|
||||
|
||||
public Actor CurrentTarget => GetActorByOffset(TargetOffsets.CurrentTarget);
|
||||
public Actor MouseOverTarget => GetActorByOffset(TargetOffsets.MouseOverTarget);
|
||||
public Actor FocusTarget => GetActorByOffset(TargetOffsets.FocusTarget);
|
||||
public Actor PreviousTarget => GetActorByOffset(TargetOffsets.PreviousTarget);
|
||||
public Actor SoftTarget => GetActorByOffset(TargetOffsets.SoftTarget);
|
||||
|
||||
internal Targets(Dalamud dalamud, ClientStateAddressResolver addressResolver) {
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="Targets"/> class.
|
||||
/// </summary>
|
||||
/// <param name="dalamud">The Dalamud instance.</param>
|
||||
/// <param name="addressResolver">The ClientStateAddressResolver instance.</param>
|
||||
internal Targets(Dalamud dalamud, ClientStateAddressResolver addressResolver)
|
||||
{
|
||||
this.dalamud = dalamud;
|
||||
Address = addressResolver;
|
||||
this.address = addressResolver;
|
||||
}
|
||||
|
||||
public void SetCurrentTarget(Actor actor) => SetTarget(actor?.Address ?? IntPtr.Zero, TargetOffsets.CurrentTarget);
|
||||
public void SetCurrentTarget(IntPtr actorAddress) => SetTarget(actorAddress, TargetOffsets.CurrentTarget);
|
||||
/// <summary>
|
||||
/// Gets the current target.
|
||||
/// </summary>
|
||||
public Actor CurrentTarget => this.GetActorByOffset(TargetOffsets.CurrentTarget);
|
||||
|
||||
public void SetFocusTarget(Actor actor) => SetTarget(actor?.Address ?? IntPtr.Zero, TargetOffsets.FocusTarget);
|
||||
public void SetFocusTarget(IntPtr actorAddress) => SetTarget(actorAddress, TargetOffsets.FocusTarget);
|
||||
/// <summary>
|
||||
/// Gets the mouseover target.
|
||||
/// </summary>
|
||||
public Actor MouseOverTarget => this.GetActorByOffset(TargetOffsets.MouseOverTarget);
|
||||
|
||||
public void ClearCurrentTarget() => SetCurrentTarget(IntPtr.Zero);
|
||||
public void ClearFocusTarget() => SetFocusTarget(IntPtr.Zero);
|
||||
/// <summary>
|
||||
/// Gets the focus target.
|
||||
/// </summary>
|
||||
public Actor FocusTarget => this.GetActorByOffset(TargetOffsets.FocusTarget);
|
||||
|
||||
private void SetTarget(IntPtr actorAddress, int offset) {
|
||||
if (Address.TargetManager == IntPtr.Zero) return;
|
||||
Marshal.WriteIntPtr(Address.TargetManager, offset, actorAddress);
|
||||
/// <summary>
|
||||
/// Gets the previous target.
|
||||
/// </summary>
|
||||
public Actor PreviousTarget => this.GetActorByOffset(TargetOffsets.PreviousTarget);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the soft target.
|
||||
/// </summary>
|
||||
public Actor SoftTarget => this.GetActorByOffset(TargetOffsets.SoftTarget);
|
||||
|
||||
/// <summary>
|
||||
/// Sets the current target.
|
||||
/// </summary>
|
||||
/// <param name="actor">Actor to target.</param>
|
||||
public void SetCurrentTarget(Actor actor) => this.SetTarget(actor?.Address ?? IntPtr.Zero, TargetOffsets.CurrentTarget);
|
||||
|
||||
/// <summary>
|
||||
/// Sets the current target.
|
||||
/// </summary>
|
||||
/// <param name="actorAddress">Actor (address) to target.</param>
|
||||
public void SetCurrentTarget(IntPtr actorAddress) => this.SetTarget(actorAddress, TargetOffsets.CurrentTarget);
|
||||
|
||||
/// <summary>
|
||||
/// Sets the focus target.
|
||||
/// </summary>
|
||||
/// <param name="actor">Actor to focus.</param>
|
||||
public void SetFocusTarget(Actor actor) => this.SetTarget(actor?.Address ?? IntPtr.Zero, TargetOffsets.FocusTarget);
|
||||
|
||||
/// <summary>
|
||||
/// Sets the focus target.
|
||||
/// </summary>
|
||||
/// <param name="actorAddress">Actor (address) to focus.</param>
|
||||
public void SetFocusTarget(IntPtr actorAddress) => this.SetTarget(actorAddress, TargetOffsets.FocusTarget);
|
||||
|
||||
/// <summary>
|
||||
/// Clears the current target.
|
||||
/// </summary>
|
||||
public void ClearCurrentTarget() => this.SetCurrentTarget(IntPtr.Zero);
|
||||
|
||||
/// <summary>
|
||||
/// Clears the focus target.
|
||||
/// </summary>
|
||||
public void ClearFocusTarget() => this.SetFocusTarget(IntPtr.Zero);
|
||||
|
||||
private void SetTarget(IntPtr actorAddress, int offset)
|
||||
{
|
||||
if (this.address.TargetManager == IntPtr.Zero)
|
||||
return;
|
||||
|
||||
Marshal.WriteIntPtr(this.address.TargetManager, offset, actorAddress);
|
||||
}
|
||||
|
||||
private Actor GetActorByOffset(int offset) {
|
||||
if (Address.TargetManager == IntPtr.Zero) return null;
|
||||
var actorAddress = Marshal.ReadIntPtr(Address.TargetManager + offset);
|
||||
if (actorAddress == IntPtr.Zero) return null;
|
||||
|
||||
private Actor GetActorByOffset(int offset)
|
||||
{
|
||||
if (this.address.TargetManager == IntPtr.Zero)
|
||||
return null;
|
||||
|
||||
var actorAddress = Marshal.ReadIntPtr(this.address.TargetManager + offset);
|
||||
if (actorAddress == IntPtr.Zero)
|
||||
return null;
|
||||
|
||||
return this.dalamud.ClientState.Actors.ReadActorFromMemory(actorAddress);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Memory offsets for the <see cref="Targets"/> type.
|
||||
/// </summary>
|
||||
public static class TargetOffsets
|
||||
{
|
||||
public const int CurrentTarget = 0x80;
|
||||
public const int SoftTarget = 0x88;
|
||||
public const int MouseOverTarget = 0xD0;
|
||||
public const int FocusTarget = 0xF8;
|
||||
public const int PreviousTarget = 0x110;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,13 +5,11 @@ using Dalamud.Game.ClientState.Structs;
|
|||
namespace Dalamud.Game.ClientState.Actors.Types
|
||||
{
|
||||
/// <summary>
|
||||
/// This class represents a basic FFXIV actor.
|
||||
/// This class represents a basic FFXIV actor.
|
||||
/// </summary>
|
||||
public class Actor : IEquatable<Actor>
|
||||
{
|
||||
private readonly Structs.Actor actorStruct;
|
||||
// This is a breaking change. StyleCop demands it.
|
||||
// private readonly IntPtr address;
|
||||
private readonly Dalamud dalamud;
|
||||
|
||||
/// <summary>
|
||||
|
|
@ -83,8 +81,6 @@ namespace Dalamud.Game.ClientState.Actors.Types
|
|||
/// <summary>
|
||||
/// Gets the address of this actor in memory.
|
||||
/// </summary>
|
||||
// TODO: This is a breaking change, StyleCop demands it.
|
||||
// public IntPtr Address => this.address;
|
||||
public readonly IntPtr Address;
|
||||
|
||||
/// <summary>
|
||||
|
|
|
|||
|
|
@ -29,7 +29,7 @@ namespace Dalamud.Game.ClientState.Actors.Types
|
|||
/// <summary>
|
||||
/// Gets the ClassJob of this Chara.
|
||||
/// </summary>
|
||||
public ClassJob ClassJob => new ClassJob(this.ActorStruct.ClassJob, this.Dalamud);
|
||||
public ClassJob ClassJob => new(this.ActorStruct.ClassJob, this.Dalamud);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the current HP of this Chara.
|
||||
|
|
|
|||
|
|
@ -2,27 +2,51 @@ using System.Runtime.InteropServices;
|
|||
|
||||
namespace Dalamud.Game.ClientState.Actors.Types
|
||||
{
|
||||
/// <summary>
|
||||
/// This class represents a party member.
|
||||
/// </summary>
|
||||
public class PartyMember
|
||||
{
|
||||
/// <summary>
|
||||
/// The name of the character.
|
||||
/// </summary>
|
||||
public string CharacterName;
|
||||
|
||||
/// <summary>
|
||||
/// Unknown.
|
||||
/// </summary>
|
||||
public long Unknown;
|
||||
|
||||
/// <summary>
|
||||
/// The actor object that corresponds to this party member.
|
||||
/// </summary>
|
||||
public Actor Actor;
|
||||
|
||||
/// <summary>
|
||||
/// The kind or type of actor.
|
||||
/// </summary>
|
||||
public ObjectKind ObjectKind;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="PartyMember"/> class.
|
||||
/// </summary>
|
||||
/// <param name="table">The ActorTable instance.</param>
|
||||
/// <param name="rawData">The interop data struct.</param>
|
||||
public PartyMember(ActorTable table, Structs.PartyMember rawData)
|
||||
{
|
||||
CharacterName = Marshal.PtrToStringAnsi(rawData.namePtr);
|
||||
Unknown = rawData.unknown;
|
||||
Actor = null;
|
||||
this.CharacterName = Marshal.PtrToStringAnsi(rawData.namePtr);
|
||||
this.Unknown = rawData.unknown;
|
||||
this.Actor = null;
|
||||
for (var i = 0; i < table.Length; i++)
|
||||
{
|
||||
if (table[i] != null && table[i].ActorId == rawData.actorId)
|
||||
{
|
||||
Actor = table[i];
|
||||
this.Actor = table[i];
|
||||
break;
|
||||
}
|
||||
}
|
||||
ObjectKind = rawData.objectKind;
|
||||
|
||||
this.ObjectKind = rawData.objectKind;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -31,12 +31,12 @@ namespace Dalamud.Game.ClientState.Actors.Types
|
|||
/// <summary>
|
||||
/// Gets the current <see cref="World">world</see> of the character.
|
||||
/// </summary>
|
||||
public World CurrentWorld => new World(this.ActorStruct.CurrentWorld, this.Dalamud);
|
||||
public World CurrentWorld => new(this.ActorStruct.CurrentWorld, this.Dalamud);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the home <see cref="World">world</see> of the character.
|
||||
/// </summary>
|
||||
public World HomeWorld => new World(this.ActorStruct.HomeWorld, this.Dalamud);
|
||||
public World HomeWorld => new(this.ActorStruct.HomeWorld, this.Dalamud);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the Free Company tag of this player.
|
||||
|
|
|
|||
|
|
@ -1,10 +1,10 @@
|
|||
using System;
|
||||
using System.ComponentModel;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
using Dalamud.Game.ClientState.Actors;
|
||||
using Dalamud.Game.ClientState.Actors.Types;
|
||||
using Dalamud.Game.Internal;
|
||||
using Dalamud.Game.Internal.Network;
|
||||
using Dalamud.Hooking;
|
||||
using JetBrains.Annotations;
|
||||
using Lumina.Excel.GeneratedSheets;
|
||||
|
|
@ -15,41 +15,17 @@ namespace Dalamud.Game.ClientState
|
|||
/// <summary>
|
||||
/// This class represents the state of the game client at the time of access.
|
||||
/// </summary>
|
||||
public class ClientState : INotifyPropertyChanged, IDisposable {
|
||||
private readonly Dalamud dalamud;
|
||||
public event PropertyChangedEventHandler PropertyChanged;
|
||||
|
||||
private ClientStateAddressResolver Address { get; }
|
||||
|
||||
public readonly ClientLanguage ClientLanguage;
|
||||
|
||||
public class ClientState : INotifyPropertyChanged, IDisposable
|
||||
{
|
||||
/// <summary>
|
||||
/// The table of all present actors.
|
||||
/// </summary>
|
||||
public readonly ActorTable Actors;
|
||||
|
||||
/// <summary>
|
||||
/// The local player character, if one is present.
|
||||
/// Gets the language of the client.
|
||||
/// </summary>
|
||||
[CanBeNull]
|
||||
public PlayerCharacter LocalPlayer {
|
||||
get {
|
||||
var actor = this.Actors[0];
|
||||
|
||||
if (actor is PlayerCharacter pc)
|
||||
return pc;
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
#region TerritoryType
|
||||
|
||||
// TODO: The hooking logic for this should go into a separate class.
|
||||
[UnmanagedFunctionPointer(CallingConvention.ThisCall)]
|
||||
private delegate IntPtr SetupTerritoryTypeDelegate(IntPtr manager, ushort terriType);
|
||||
|
||||
private readonly Hook<SetupTerritoryTypeDelegate> setupTerritoryTypeHook;
|
||||
public readonly ClientLanguage ClientLanguage;
|
||||
|
||||
/// <summary>
|
||||
/// The current Territory the player resides in.
|
||||
|
|
@ -57,39 +33,12 @@ namespace Dalamud.Game.ClientState
|
|||
public ushort TerritoryType;
|
||||
|
||||
/// <summary>
|
||||
/// Event that gets fired when the current Territory changes.
|
||||
/// </summary>
|
||||
public EventHandler<ushort> TerritoryChanged;
|
||||
|
||||
/// <summary>
|
||||
/// Event that gets fired when a duty is ready.
|
||||
/// </summary>
|
||||
public event EventHandler<ContentFinderCondition> CfPop;
|
||||
|
||||
private IntPtr SetupTerritoryTypeDetour(IntPtr manager, ushort terriType)
|
||||
{
|
||||
this.TerritoryType = terriType;
|
||||
this.TerritoryChanged?.Invoke(this, terriType);
|
||||
|
||||
Log.Debug("TerritoryType changed: {0}", terriType);
|
||||
|
||||
return this.setupTerritoryTypeHook.Original(manager, terriType);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
/// <summary>
|
||||
/// The content ID of the local character.
|
||||
/// </summary>
|
||||
public ulong LocalContentId => (ulong) Marshal.ReadInt64(Address.LocalContentId);
|
||||
|
||||
/// <summary>
|
||||
/// The class facilitating Job Gauge data access
|
||||
/// The class facilitating Job Gauge data access.
|
||||
/// </summary>
|
||||
public JobGauges JobGauges;
|
||||
|
||||
/// <summary>
|
||||
/// The class facilitating party list data access
|
||||
/// The class facilitating party list data access.
|
||||
/// </summary>
|
||||
public PartyList PartyList;
|
||||
|
||||
|
|
@ -102,77 +51,76 @@ namespace Dalamud.Game.ClientState
|
|||
/// Provides access to the button state of gamepad buttons in game.
|
||||
/// </summary>
|
||||
public GamepadState GamepadState;
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Provides access to client conditions/player state. Allows you to check if a player is in a duty, mounted, etc.
|
||||
/// </summary>
|
||||
public Condition Condition;
|
||||
|
||||
/// <summary>
|
||||
/// The class facilitating target data access
|
||||
/// The class facilitating target data access.
|
||||
/// </summary>
|
||||
public Targets Targets;
|
||||
|
||||
/// <summary>
|
||||
/// Event that gets fired when the current Territory changes.
|
||||
/// </summary>
|
||||
public EventHandler<ushort> TerritoryChanged;
|
||||
|
||||
private readonly Dalamud dalamud;
|
||||
private readonly ClientStateAddressResolver address;
|
||||
private readonly Hook<SetupTerritoryTypeDelegate> setupTerritoryTypeHook;
|
||||
|
||||
private bool lastConditionNone = true;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="ClientState"/> class.
|
||||
/// Set up client state access.
|
||||
/// </summary>
|
||||
/// <param name="dalamud">Dalamud instance</param>
|
||||
/// /// <param name="startInfo">StartInfo of the current Dalamud launch</param>
|
||||
/// <param name="scanner">Sig scanner</param>
|
||||
public ClientState(Dalamud dalamud, DalamudStartInfo startInfo, SigScanner scanner) {
|
||||
/// <param name="dalamud">Dalamud instance.</param>
|
||||
/// <param name="startInfo">StartInfo of the current Dalamud launch.</param>
|
||||
/// <param name="scanner">Sig scanner.</param>
|
||||
public ClientState(Dalamud dalamud, DalamudStartInfo startInfo, SigScanner scanner)
|
||||
{
|
||||
this.dalamud = dalamud;
|
||||
Address = new ClientStateAddressResolver();
|
||||
Address.Setup(scanner);
|
||||
this.address = new ClientStateAddressResolver();
|
||||
this.address.Setup(scanner);
|
||||
|
||||
Log.Verbose("===== C L I E N T S T A T E =====");
|
||||
|
||||
this.ClientLanguage = startInfo.Language;
|
||||
|
||||
this.Actors = new ActorTable(dalamud, Address);
|
||||
this.Actors = new ActorTable(dalamud, this.address);
|
||||
|
||||
this.PartyList = new PartyList(dalamud, Address);
|
||||
this.PartyList = new PartyList(dalamud, this.address);
|
||||
|
||||
this.JobGauges = new JobGauges(Address);
|
||||
this.JobGauges = new JobGauges(this.address);
|
||||
|
||||
this.KeyState = new KeyState(Address, scanner.Module.BaseAddress);
|
||||
this.KeyState = new KeyState(this.address, scanner.Module.BaseAddress);
|
||||
|
||||
this.GamepadState = new GamepadState(this.Address);
|
||||
this.GamepadState = new GamepadState(this.address);
|
||||
|
||||
this.Condition = new Condition( Address );
|
||||
this.Condition = new Condition(this.address);
|
||||
|
||||
this.Targets = new Targets(dalamud, Address);
|
||||
this.Targets = new Targets(dalamud, this.address);
|
||||
|
||||
Log.Verbose("SetupTerritoryType address {SetupTerritoryType}", Address.SetupTerritoryType);
|
||||
Log.Verbose("SetupTerritoryType address {SetupTerritoryType}", this.address.SetupTerritoryType);
|
||||
|
||||
this.setupTerritoryTypeHook = new Hook<SetupTerritoryTypeDelegate>(Address.SetupTerritoryType,
|
||||
new SetupTerritoryTypeDelegate(SetupTerritoryTypeDetour),
|
||||
this);
|
||||
this.setupTerritoryTypeHook = new Hook<SetupTerritoryTypeDelegate>(this.address.SetupTerritoryType, new SetupTerritoryTypeDelegate(this.SetupTerritoryTypeDetour), this);
|
||||
|
||||
dalamud.Framework.OnUpdateEvent += FrameworkOnOnUpdateEvent;
|
||||
dalamud.NetworkHandlers.CfPop += NetworkHandlersOnCfPop;
|
||||
dalamud.Framework.OnUpdateEvent += this.FrameworkOnOnUpdateEvent;
|
||||
dalamud.NetworkHandlers.CfPop += this.NetworkHandlersOnCfPop;
|
||||
}
|
||||
|
||||
private void NetworkHandlersOnCfPop(object sender, ContentFinderCondition e) {
|
||||
CfPop?.Invoke(this, e);
|
||||
}
|
||||
[UnmanagedFunctionPointer(CallingConvention.ThisCall)]
|
||||
private delegate IntPtr SetupTerritoryTypeDelegate(IntPtr manager, ushort terriType);
|
||||
|
||||
public void Enable() {
|
||||
this.GamepadState.Enable();
|
||||
this.PartyList.Enable();
|
||||
this.setupTerritoryTypeHook.Enable();
|
||||
}
|
||||
|
||||
public void Dispose() {
|
||||
this.PartyList.Dispose();
|
||||
this.setupTerritoryTypeHook.Dispose();
|
||||
this.Actors.Dispose();
|
||||
this.GamepadState.Dispose();
|
||||
|
||||
this.dalamud.Framework.OnUpdateEvent -= FrameworkOnOnUpdateEvent;
|
||||
this.dalamud.NetworkHandlers.CfPop += NetworkHandlersOnCfPop;
|
||||
}
|
||||
|
||||
private bool lastConditionNone = true;
|
||||
/// <summary>
|
||||
/// Event that fires when a property changes.
|
||||
/// </summary>
|
||||
#pragma warning disable CS0067
|
||||
public event PropertyChangedEventHandler PropertyChanged;
|
||||
#pragma warning restore
|
||||
|
||||
/// <summary>
|
||||
/// Event that fires when a character is logging in.
|
||||
|
|
@ -184,24 +132,93 @@ namespace Dalamud.Game.ClientState
|
|||
/// </summary>
|
||||
public event EventHandler OnLogout;
|
||||
|
||||
/// <summary>
|
||||
/// Event that gets fired when a duty is ready.
|
||||
/// </summary>
|
||||
public event EventHandler<ContentFinderCondition> CfPop;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the local player character, if one is present.
|
||||
/// </summary>
|
||||
[CanBeNull]
|
||||
public PlayerCharacter LocalPlayer
|
||||
{
|
||||
get
|
||||
{
|
||||
var actor = this.Actors[0];
|
||||
|
||||
if (actor is PlayerCharacter pc)
|
||||
return pc;
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the content ID of the local character.
|
||||
/// </summary>
|
||||
public ulong LocalContentId => (ulong)Marshal.ReadInt64(this.address.LocalContentId);
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether a character is logged in.
|
||||
/// </summary>
|
||||
public bool IsLoggedIn { get; private set; }
|
||||
|
||||
private void FrameworkOnOnUpdateEvent(Framework framework) {
|
||||
if (this.Condition.Any() && this.lastConditionNone == true) {
|
||||
/// <summary>
|
||||
/// Enable this module.
|
||||
/// </summary>
|
||||
public void Enable()
|
||||
{
|
||||
this.GamepadState.Enable();
|
||||
this.PartyList.Enable();
|
||||
this.setupTerritoryTypeHook.Enable();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Dispose of managed and unmanaged resources.
|
||||
/// </summary>
|
||||
public void Dispose()
|
||||
{
|
||||
this.PartyList.Dispose();
|
||||
this.setupTerritoryTypeHook.Dispose();
|
||||
this.Actors.Dispose();
|
||||
this.GamepadState.Dispose();
|
||||
|
||||
this.dalamud.Framework.OnUpdateEvent -= this.FrameworkOnOnUpdateEvent;
|
||||
this.dalamud.NetworkHandlers.CfPop += this.NetworkHandlersOnCfPop;
|
||||
}
|
||||
|
||||
private IntPtr SetupTerritoryTypeDetour(IntPtr manager, ushort terriType)
|
||||
{
|
||||
this.TerritoryType = terriType;
|
||||
this.TerritoryChanged?.Invoke(this, terriType);
|
||||
|
||||
Log.Debug("TerritoryType changed: {0}", terriType);
|
||||
|
||||
return this.setupTerritoryTypeHook.Original(manager, terriType);
|
||||
}
|
||||
|
||||
private void NetworkHandlersOnCfPop(object sender, ContentFinderCondition e)
|
||||
{
|
||||
this.CfPop?.Invoke(this, e);
|
||||
}
|
||||
|
||||
private void FrameworkOnOnUpdateEvent(Framework framework)
|
||||
{
|
||||
if (this.Condition.Any() && this.lastConditionNone == true)
|
||||
{
|
||||
Log.Debug("Is login");
|
||||
this.lastConditionNone = false;
|
||||
this.IsLoggedIn = true;
|
||||
OnLogin?.Invoke(this, null);
|
||||
this.OnLogin?.Invoke(this, null);
|
||||
}
|
||||
|
||||
if (!this.Condition.Any() && this.lastConditionNone == false) {
|
||||
|
||||
if (!this.Condition.Any() && this.lastConditionNone == false)
|
||||
{
|
||||
Log.Debug("Is logout");
|
||||
this.lastConditionNone = true;
|
||||
this.IsLoggedIn = false;
|
||||
OnLogout?.Invoke(this, null);
|
||||
this.OnLogout?.Invoke(this, null);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,50 +1,88 @@
|
|||
using System;
|
||||
|
||||
using Dalamud.Game.Internal;
|
||||
|
||||
namespace Dalamud.Game.ClientState
|
||||
{
|
||||
public sealed class ClientStateAddressResolver : BaseAddressResolver {
|
||||
/// <summary>
|
||||
/// Client state memory address resolver.
|
||||
/// </summary>
|
||||
public sealed class ClientStateAddressResolver : BaseAddressResolver
|
||||
{
|
||||
// Static offsets
|
||||
public IntPtr ActorTable { get; private set; }
|
||||
//public IntPtr ViewportActorTable { get; private set; }
|
||||
public IntPtr LocalContentId { get; private set; }
|
||||
public IntPtr JobGaugeData { get; private set; }
|
||||
public IntPtr KeyboardState { get; private set; }
|
||||
public IntPtr TargetManager { get; private set; }
|
||||
|
||||
// Functions
|
||||
public IntPtr SetupTerritoryType { get; private set; }
|
||||
//public IntPtr SomeActorTableAccess { get; private set; }
|
||||
//public IntPtr PartyListUpdate { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Game function which polls the gamepads for data.
|
||||
///
|
||||
/// Gets the address of the actor table.
|
||||
/// </summary>
|
||||
public IntPtr ActorTable { get; private set; }
|
||||
|
||||
// public IntPtr ViewportActorTable { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the address of the local content id.
|
||||
/// </summary>
|
||||
public IntPtr LocalContentId { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the address of job gauge data.
|
||||
/// </summary>
|
||||
public IntPtr JobGaugeData { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the address of the keyboard state.
|
||||
/// </summary>
|
||||
public IntPtr KeyboardState { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the address of the target manager.
|
||||
/// </summary>
|
||||
public IntPtr TargetManager { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the address of the condition flag array.
|
||||
/// </summary>
|
||||
public IntPtr ConditionFlags { get; private set; }
|
||||
|
||||
// Functions
|
||||
|
||||
/// <summary>
|
||||
/// Gets the address of the method which sets the territory type.
|
||||
/// </summary>
|
||||
public IntPtr SetupTerritoryType { get; private set; }
|
||||
|
||||
// public IntPtr SomeActorTableAccess { get; private set; }
|
||||
// public IntPtr PartyListUpdate { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the address of the method which polls the gamepads for data.
|
||||
/// Called every frame, even when `Enable Gamepad` is off in the settings.
|
||||
/// </summary>
|
||||
public IntPtr GamepadPoll { get; private set; }
|
||||
|
||||
public IntPtr ConditionFlags { get; private set; }
|
||||
|
||||
protected override void Setup64Bit(SigScanner sig) {
|
||||
/// <summary>
|
||||
/// Scan for and setup any configured address pointers.
|
||||
/// </summary>
|
||||
/// <param name="sig">The signature scanner to facilitate setup.</param>
|
||||
protected override void Setup64Bit(SigScanner sig)
|
||||
{
|
||||
// We don't need those anymore, but maybe someone else will - let's leave them here for good measure
|
||||
//ViewportActorTable = sig.GetStaticAddressFromSig("48 8D 0D ?? ?? ?? ?? 85 ED", 0) + 0x148;
|
||||
//SomeActorTableAccess = sig.ScanText("E8 ?? ?? ?? ?? 48 8D 55 A0 48 8D 8E ?? ?? ?? ??");
|
||||
ActorTable = sig.GetStaticAddressFromSig("48 8D 0D ?? ?? ?? ?? E8 ?? ?? ?? ?? 44 0F B6 83");
|
||||
// ViewportActorTable = sig.GetStaticAddressFromSig("48 8D 0D ?? ?? ?? ?? 85 ED", 0) + 0x148;
|
||||
// SomeActorTableAccess = sig.ScanText("E8 ?? ?? ?? ?? 48 8D 55 A0 48 8D 8E ?? ?? ?? ??");
|
||||
this.ActorTable = sig.GetStaticAddressFromSig("48 8D 0D ?? ?? ?? ?? E8 ?? ?? ?? ?? 44 0F B6 83");
|
||||
|
||||
LocalContentId = sig.GetStaticAddressFromSig("48 0F 44 05 ?? ?? ?? ?? 48 39 07");
|
||||
JobGaugeData = sig.GetStaticAddressFromSig("E8 ?? ?? ?? ?? FF C6 48 8D 5B 0C", 0xB9) + 0x10;
|
||||
this.LocalContentId = sig.GetStaticAddressFromSig("48 0F 44 05 ?? ?? ?? ?? 48 39 07");
|
||||
this.JobGaugeData = sig.GetStaticAddressFromSig("E8 ?? ?? ?? ?? FF C6 48 8D 5B 0C", 0xB9) + 0x10;
|
||||
|
||||
SetupTerritoryType = sig.ScanText("48 89 5C 24 ?? 48 89 74 24 ?? 57 48 83 EC 20 48 8B F9 66 89 91 ?? ?? ?? ??");
|
||||
this.SetupTerritoryType = sig.ScanText("48 89 5C 24 ?? 48 89 74 24 ?? 57 48 83 EC 20 48 8B F9 66 89 91 ?? ?? ?? ??");
|
||||
|
||||
// This resolves to a fixed offset only, without the base address added in, so GetStaticAddressFromSig() can't be used
|
||||
KeyboardState = sig.ScanText("48 8D 0C 85 ?? ?? ?? ?? 8B 04 31 85 C2 0F 85") + 0x4;
|
||||
this.KeyboardState = sig.ScanText("48 8D 0C 85 ?? ?? ?? ?? 8B 04 31 85 C2 0F 85") + 0x4;
|
||||
|
||||
//PartyListUpdate = sig.ScanText("E8 ?? ?? ?? ?? 49 8B D7 4C 8D 86 ?? ?? ?? ??");
|
||||
// PartyListUpdate = sig.ScanText("E8 ?? ?? ?? ?? 49 8B D7 4C 8D 86 ?? ?? ?? ??");
|
||||
|
||||
ConditionFlags = sig.GetStaticAddressFromSig("48 8D 0D ?? ?? ?? ?? BA ?? ?? ?? ?? E8 ?? ?? ?? ?? B0 01 48 83 C4 30");
|
||||
this.ConditionFlags = sig.GetStaticAddressFromSig("48 8D 0D ?? ?? ?? ?? BA ?? ?? ?? ?? E8 ?? ?? ?? ?? B0 01 48 83 C4 30");
|
||||
|
||||
TargetManager = sig.GetStaticAddressFromSig("48 8B 05 ?? ?? ?? ?? 48 8D 0D ?? ?? ?? ?? FF 50 ?? 48 85 DB", 3);
|
||||
this.TargetManager = sig.GetStaticAddressFromSig("48 8B 05 ?? ?? ?? ?? 48 8D 0D ?? ?? ?? ?? FF 50 ?? 48 85 DB", 3);
|
||||
|
||||
this.GamepadPoll = sig.ScanText("40 ?? 57 41 ?? 48 81 EC ?? ?? ?? ?? 44 0F ?? ?? ?? ?? ?? ?? ?? 48 8B");
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,7 +1,4 @@
|
|||
using System;
|
||||
using System.Runtime.CompilerServices;
|
||||
using Dalamud.Hooking;
|
||||
using Serilog;
|
||||
|
||||
namespace Dalamud.Game.ClientState
|
||||
{
|
||||
|
|
@ -10,36 +7,49 @@ namespace Dalamud.Game.ClientState
|
|||
/// </summary>
|
||||
public class Condition
|
||||
{
|
||||
internal readonly IntPtr conditionArrayBase;
|
||||
|
||||
/// <summary>
|
||||
/// The current max number of conditions. You can get this just by looking at the condition sheet and how many rows it has.
|
||||
/// </summary>
|
||||
public const int MaxConditionEntries = 100;
|
||||
|
||||
internal Condition( ClientStateAddressResolver resolver )
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="Condition"/> class.
|
||||
/// </summary>
|
||||
/// <param name="resolver">The ClientStateAddressResolver instance.</param>
|
||||
internal Condition(ClientStateAddressResolver resolver)
|
||||
{
|
||||
this.conditionArrayBase = resolver.ConditionFlags;
|
||||
this.ConditionArrayBase = resolver.ConditionFlags;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the condition array base pointer.
|
||||
/// Would typically be private but is used in /xldata windows.
|
||||
/// </summary>
|
||||
internal IntPtr ConditionArrayBase { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Check the value of a specific condition/state flag.
|
||||
/// </summary>
|
||||
/// <param name="flag">The condition flag to check</param>
|
||||
public unsafe bool this[ ConditionFlag flag ]
|
||||
/// <param name="flag">The condition flag to check.</param>
|
||||
public unsafe bool this[ConditionFlag flag]
|
||||
{
|
||||
get
|
||||
{
|
||||
var idx = ( int )flag;
|
||||
|
||||
if( idx > MaxConditionEntries || idx < 0 )
|
||||
var idx = (int)flag;
|
||||
|
||||
if (idx > MaxConditionEntries || idx < 0)
|
||||
return false;
|
||||
|
||||
return *( bool* )( this.conditionArrayBase + idx );
|
||||
|
||||
return *(bool*)(this.ConditionArrayBase + idx);
|
||||
}
|
||||
}
|
||||
|
||||
public bool Any() {
|
||||
/// <summary>
|
||||
/// Check if any condition flags are set.
|
||||
/// </summary>
|
||||
/// <returns>Whether any single flag is set.</returns>
|
||||
public bool Any()
|
||||
{
|
||||
for (var i = 0; i < MaxConditionEntries; i++)
|
||||
{
|
||||
var typedCondition = (ConditionFlag)i;
|
||||
|
|
|
|||
|
|
@ -6,7 +6,8 @@ namespace Dalamud.Game.ClientState
|
|||
/// These come from LogMessage (somewhere) and directly map to each state field managed by the client. As of 5.25, it maps to
|
||||
/// LogMessage row 7700 and onwards, which can be checked by looking at the Condition sheet and looking at what column 2 maps to.
|
||||
/// </summary>
|
||||
public enum ConditionFlag {
|
||||
public enum ConditionFlag
|
||||
{
|
||||
/// <summary>
|
||||
/// Unused.
|
||||
/// </summary>
|
||||
|
|
@ -27,9 +28,6 @@ namespace Dalamud.Game.ClientState
|
|||
/// </summary>
|
||||
Emoting = 3,
|
||||
|
||||
/// <summary>
|
||||
/// Unable to execute command while mounted.
|
||||
/// </summary>
|
||||
/// <summary>
|
||||
/// Unable to execute command while mounted.
|
||||
/// </summary>
|
||||
|
|
@ -94,15 +92,15 @@ namespace Dalamud.Game.ClientState
|
|||
/// Unable to execute command while performing.
|
||||
/// </summary>
|
||||
Performing = 16,
|
||||
|
||||
//Unknown17 = 17,
|
||||
//Unknown18 = 18,
|
||||
//Unknown19 = 19,
|
||||
//Unknown20 = 20,
|
||||
//Unknown21 = 21,
|
||||
//Unknown22 = 22,
|
||||
//Unknown23 = 23,
|
||||
//Unknown24 = 24,
|
||||
|
||||
// Unknown17 = 17,
|
||||
// Unknown18 = 18,
|
||||
// Unknown19 = 19,
|
||||
// Unknown20 = 20,
|
||||
// Unknown21 = 21,
|
||||
// Unknown22 = 22,
|
||||
// Unknown23 = 23,
|
||||
// Unknown24 = 24,
|
||||
|
||||
/// <summary>
|
||||
/// Unable to execute command while occupied.
|
||||
|
|
@ -199,8 +197,8 @@ namespace Dalamud.Game.ClientState
|
|||
/// Unable to execute command while fishing.
|
||||
/// </summary>
|
||||
Fishing = 43,
|
||||
|
||||
//Unknown44 = 44,
|
||||
|
||||
// Unknown44 = 44,
|
||||
|
||||
/// <summary>
|
||||
/// Unable to execute command while between areas.
|
||||
|
|
@ -211,8 +209,8 @@ namespace Dalamud.Game.ClientState
|
|||
/// Unable to execute command while stealthed.
|
||||
/// </summary>
|
||||
Stealthed = 46,
|
||||
|
||||
//Unknown47 = 47,
|
||||
|
||||
// Unknown47 = 47,
|
||||
|
||||
/// <summary>
|
||||
/// Unable to execute command while jumping.
|
||||
|
|
@ -399,8 +397,8 @@ namespace Dalamud.Game.ClientState
|
|||
/// Unable to execute command while participating in a cross-world party or alliance.
|
||||
/// </summary>
|
||||
ParticipatingInCrossWorldPartyOrAlliance = 84,
|
||||
|
||||
//Unknown85 = 85,
|
||||
|
||||
// Unknown85 = 85,
|
||||
|
||||
/// <summary>
|
||||
/// Unable to execute command while playing duty record.
|
||||
|
|
|
|||
|
|
@ -126,7 +126,7 @@ namespace Dalamud.Game.ClientState
|
|||
/// Gets or sets a value indicating whether detour should block gamepad input for game.
|
||||
///
|
||||
/// Ideally, we would use
|
||||
/// (ImGui.GetIO().ConfigFlags & ImGuiConfigFlags.NavEnableGamepad) > 0
|
||||
/// (ImGui.GetIO().ConfigFlags & ImGuiConfigFlags.NavEnableGamepad) > 0
|
||||
/// but this has a race condition during load with the detour which sets up ImGui
|
||||
/// and throws if our detour gets called before the other.
|
||||
/// </summary>
|
||||
|
|
|
|||
|
|
@ -1,22 +1,26 @@
|
|||
using Serilog;
|
||||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
using Serilog;
|
||||
|
||||
namespace Dalamud.Game.ClientState
|
||||
{
|
||||
/// <summary>
|
||||
/// Wrapper around the game keystate buffer, which contains the pressed state for
|
||||
/// all keyboard keys, indexed by virtual vkCode
|
||||
/// Wrapper around the game keystate buffer, which contains the pressed state for all keyboard keys, indexed by virtual vkCode.
|
||||
/// </summary>
|
||||
public class KeyState
|
||||
{
|
||||
private IntPtr bufferBase;
|
||||
|
||||
// The array is accessed in a way that this limit doesn't appear to exist
|
||||
// but there is other state data past this point, and keys beyond here aren't
|
||||
// generally valid for most things anyway
|
||||
private const int MaxKeyCodeIndex = 0xA0;
|
||||
private IntPtr bufferBase;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="KeyState"/> class.
|
||||
/// </summary>
|
||||
/// <param name="addressResolver">The ClientStateAddressResolver instance.</param>
|
||||
/// <param name="moduleBaseAddress">The base address of the main process module.</param>
|
||||
public KeyState(ClientStateAddressResolver addressResolver, IntPtr moduleBaseAddress)
|
||||
{
|
||||
this.bufferBase = moduleBaseAddress + Marshal.ReadInt32(addressResolver.KeyboardState);
|
||||
|
|
@ -33,10 +37,10 @@ namespace Dalamud.Game.ClientState
|
|||
{
|
||||
get
|
||||
{
|
||||
if (vkCode< 0 || vkCode > MaxKeyCodeIndex)
|
||||
if (vkCode < 0 || vkCode > MaxKeyCodeIndex)
|
||||
throw new ArgumentException($"Keycode state only appears to be valid up to {MaxKeyCodeIndex}");
|
||||
|
||||
return (Marshal.ReadInt32(this.bufferBase + (4 * vkCode)) != 0);
|
||||
return Marshal.ReadInt32(this.bufferBase + (4 * vkCode)) != 0;
|
||||
}
|
||||
|
||||
set
|
||||
|
|
|
|||
|
|
@ -1,96 +1,139 @@
|
|||
using Dalamud.Game.ClientState.Actors.Types;
|
||||
using Dalamud.Hooking;
|
||||
using Dalamud.Plugin;
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
using Dalamud.Game.ClientState.Actors.Types;
|
||||
// using Dalamud.Hooking;
|
||||
|
||||
namespace Dalamud.Game.ClientState
|
||||
{
|
||||
public class PartyList : IReadOnlyCollection<PartyMember>, ICollection, IDisposable
|
||||
/// <summary>
|
||||
/// This class represents the members of your party.
|
||||
/// </summary>
|
||||
public sealed partial class PartyList
|
||||
{
|
||||
private ClientStateAddressResolver Address { get; }
|
||||
private Dalamud dalamud;
|
||||
private readonly Dalamud dalamud;
|
||||
private readonly ClientStateAddressResolver address;
|
||||
|
||||
// private bool isReady = false;
|
||||
// private IntPtr partyListBegin;
|
||||
// private Hook<PartyListUpdateDelegate> partyListUpdateHook;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="PartyList"/> class.
|
||||
/// </summary>
|
||||
/// <param name="dalamud">The Dalamud instance.</param>
|
||||
/// <param name="addressResolver">The ClientStateAddressResolver instance.</param>
|
||||
public PartyList(Dalamud dalamud, ClientStateAddressResolver addressResolver)
|
||||
{
|
||||
this.address = addressResolver;
|
||||
this.dalamud = dalamud;
|
||||
// this.partyListUpdateHook = new Hook<PartyListUpdateDelegate>(Address.PartyListUpdate, new PartyListUpdateDelegate(PartyListUpdateDetour), this);
|
||||
}
|
||||
|
||||
private delegate long PartyListUpdateDelegate(IntPtr structBegin, long param2, char param3);
|
||||
|
||||
private Hook<PartyListUpdateDelegate> partyListUpdateHook;
|
||||
private IntPtr partyListBegin;
|
||||
private bool isReady = false;
|
||||
/// <summary>
|
||||
/// Gets the length of the PartyList.
|
||||
/// </summary>
|
||||
public int Length => 0; // !this.isReady ? 0 : Marshal.ReadByte(this.partyListBegin + 0xF0);
|
||||
|
||||
public PartyList(Dalamud dalamud, ClientStateAddressResolver addressResolver)
|
||||
/// <summary>
|
||||
/// Get the nth party member.
|
||||
/// </summary>
|
||||
/// <param name="index">Index of the party member.</param>
|
||||
/// <returns>The party member.</returns>
|
||||
public PartyMember this[int index]
|
||||
{
|
||||
Address = addressResolver;
|
||||
this.dalamud = dalamud;
|
||||
//this.partyListUpdateHook = new Hook<PartyListUpdateDelegate>(Address.PartyListUpdate, new PartyListUpdateDelegate(PartyListUpdateDetour), this);
|
||||
get
|
||||
{
|
||||
return null;
|
||||
// if (!this.isReady)
|
||||
// return null;
|
||||
// if (index >= this.Length)
|
||||
// return null;
|
||||
// var tblIndex = this.partyListBegin + (index * 24);
|
||||
// var memberStruct = Marshal.PtrToStructure<Structs.PartyMember>(tblIndex);
|
||||
// return new PartyMember(this.dalamud.ClientState.Actors, memberStruct);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Enable this module.
|
||||
/// </summary>
|
||||
public void Enable()
|
||||
{
|
||||
// TODO Fix for 5.3
|
||||
//this.partyListUpdateHook.Enable();
|
||||
// this.partyListUpdateHook.Enable();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Dispose of managed and unmanaged resources.
|
||||
/// </summary>
|
||||
public void Dispose()
|
||||
{
|
||||
//if (!this.isReady)
|
||||
// this.partyListUpdateHook.Dispose();
|
||||
this.isReady = false;
|
||||
// if (!this.isReady)
|
||||
// this.partyListUpdateHook.Dispose();
|
||||
// this.isReady = false;
|
||||
}
|
||||
|
||||
private long PartyListUpdateDetour(IntPtr structBegin, long param2, char param3)
|
||||
{
|
||||
var result = this.partyListUpdateHook.Original(structBegin, param2, param3);
|
||||
this.partyListBegin = structBegin + 0xB48;
|
||||
this.partyListUpdateHook.Dispose();
|
||||
this.isReady = true;
|
||||
return result;
|
||||
}
|
||||
// private long PartyListUpdateDetour(IntPtr structBegin, long param2, char param3)
|
||||
// {
|
||||
// var result = this.partyListUpdateHook.Original(structBegin, param2, param3);
|
||||
// this.partyListBegin = structBegin + 0xB48;
|
||||
// this.partyListUpdateHook.Dispose();
|
||||
// this.isReady = true;
|
||||
// return result;
|
||||
// }
|
||||
}
|
||||
|
||||
public PartyMember this[int index]
|
||||
{
|
||||
get {
|
||||
if (!this.isReady)
|
||||
return null;
|
||||
if (index >= Length)
|
||||
return null;
|
||||
var tblIndex = partyListBegin + index * 24;
|
||||
var memberStruct = Marshal.PtrToStructure<Structs.PartyMember>(tblIndex);
|
||||
return new PartyMember(this.dalamud.ClientState.Actors, memberStruct);
|
||||
}
|
||||
}
|
||||
/// <summary>
|
||||
/// Implements IReadOnlyCollection, IEnumerable.
|
||||
/// </summary>
|
||||
public sealed partial class PartyList : IReadOnlyCollection<PartyMember>
|
||||
{
|
||||
/// <inheritdoc/>
|
||||
int IReadOnlyCollection<PartyMember>.Count => this.Length;
|
||||
|
||||
public void CopyTo(Array array, int index)
|
||||
/// <inheritdoc/>
|
||||
public IEnumerator<PartyMember> GetEnumerator()
|
||||
{
|
||||
for (var i = 0; i < Length; i++)
|
||||
for (var i = 0; i < this.Length; i++)
|
||||
{
|
||||
array.SetValue(this[i], index);
|
||||
index++;
|
||||
}
|
||||
}
|
||||
|
||||
public IEnumerator<PartyMember> GetEnumerator() {
|
||||
for (var i = 0; i < Length; i++) {
|
||||
if (this[i] != null) {
|
||||
if (this[i] != null)
|
||||
{
|
||||
yield return this[i];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
|
||||
/// <inheritdoc/>
|
||||
IEnumerator IEnumerable.GetEnumerator() => this.GetEnumerator();
|
||||
}
|
||||
|
||||
public int Length => !this.isReady ? 0 : Marshal.ReadByte(partyListBegin + 0xF0);
|
||||
|
||||
int IReadOnlyCollection<PartyMember>.Count => Length;
|
||||
|
||||
public int Count => Length;
|
||||
/// <summary>
|
||||
/// Implements ICollection.
|
||||
/// </summary>
|
||||
public sealed partial class PartyList : ICollection
|
||||
{
|
||||
/// <inheritdoc/>
|
||||
public int Count => this.Length;
|
||||
|
||||
/// <inheritdoc/>
|
||||
public object SyncRoot => this;
|
||||
|
||||
/// <inheritdoc/>
|
||||
public bool IsSynchronized => false;
|
||||
|
||||
/// <inheritdoc/>
|
||||
public void CopyTo(Array array, int index)
|
||||
{
|
||||
for (var i = 0; i < this.Length; i++)
|
||||
{
|
||||
array.SetValue(this[i], index);
|
||||
index++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,7 +4,245 @@ using Dalamud.Game.ClientState.Actors;
|
|||
|
||||
namespace Dalamud.Game.ClientState.Structs
|
||||
{
|
||||
public class ActorOffsets
|
||||
/// <summary>
|
||||
/// Native memory representation of an FFXIV actor.
|
||||
/// </summary>
|
||||
[StructLayout(LayoutKind.Explicit, Pack = 2)]
|
||||
public struct Actor
|
||||
{
|
||||
/// <summary>
|
||||
/// The actor name.
|
||||
/// </summary>
|
||||
[FieldOffset(ActorOffsets.Name)]
|
||||
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 30)]
|
||||
public string Name;
|
||||
|
||||
/// <summary>
|
||||
/// The actor's internal id.
|
||||
/// </summary>
|
||||
[FieldOffset(ActorOffsets.ActorId)]
|
||||
public int ActorId;
|
||||
|
||||
/// <summary>
|
||||
/// The actor's data id.
|
||||
/// </summary>
|
||||
[FieldOffset(ActorOffsets.DataId)]
|
||||
public int DataId;
|
||||
|
||||
/// <summary>
|
||||
/// The actor's owner id. This is useful for pets, summons, and the like.
|
||||
/// </summary>
|
||||
[FieldOffset(ActorOffsets.OwnerId)]
|
||||
public int OwnerId;
|
||||
|
||||
/// <summary>
|
||||
/// The type or kind of actor.
|
||||
/// </summary>
|
||||
[FieldOffset(ActorOffsets.ObjectKind)]
|
||||
public ObjectKind ObjectKind;
|
||||
|
||||
/// <summary>
|
||||
/// The sub-type or sub-kind of actor.
|
||||
/// </summary>
|
||||
[FieldOffset(ActorOffsets.SubKind)]
|
||||
public byte SubKind;
|
||||
|
||||
/// <summary>
|
||||
/// Whether the actor is friendly.
|
||||
/// </summary>
|
||||
[FieldOffset(ActorOffsets.IsFriendly)]
|
||||
public bool IsFriendly;
|
||||
|
||||
/// <summary>
|
||||
/// The horizontal distance in game units from the player.
|
||||
/// </summary>
|
||||
[FieldOffset(ActorOffsets.YalmDistanceFromPlayerX)]
|
||||
public byte YalmDistanceFromPlayerX;
|
||||
|
||||
/// <summary>
|
||||
/// The player target status.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// This is some kind of enum.
|
||||
/// </remarks>
|
||||
[FieldOffset(ActorOffsets.PlayerTargetStatus)]
|
||||
public byte PlayerTargetStatus;
|
||||
|
||||
/// <summary>
|
||||
/// The vertical distance in game units from the player.
|
||||
/// </summary>
|
||||
[FieldOffset(ActorOffsets.YalmDistanceFromPlayerY)]
|
||||
public byte YalmDistanceFromPlayerY;
|
||||
|
||||
/// <summary>
|
||||
/// The (X,Z,Y) position of the actor.
|
||||
/// </summary>
|
||||
[FieldOffset(ActorOffsets.Position)]
|
||||
public Position3 Position;
|
||||
|
||||
/// <summary>
|
||||
/// The rotation of the actor.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// The rotation is around the vertical axis (yaw), from -pi to pi radians.
|
||||
/// </remarks>
|
||||
[FieldOffset(ActorOffsets.Rotation)]
|
||||
public float Rotation;
|
||||
|
||||
/// <summary>
|
||||
/// The hitbox radius of the actor.
|
||||
/// </summary>
|
||||
[FieldOffset(ActorOffsets.HitboxRadius)]
|
||||
public float HitboxRadius;
|
||||
|
||||
/// <summary>
|
||||
/// The current HP of the actor.
|
||||
/// </summary>
|
||||
[FieldOffset(ActorOffsets.CurrentHp)]
|
||||
public int CurrentHp;
|
||||
|
||||
/// <summary>
|
||||
/// The max HP of the actor.
|
||||
/// </summary>
|
||||
[FieldOffset(ActorOffsets.MaxHp)]
|
||||
public int MaxHp;
|
||||
|
||||
/// <summary>
|
||||
/// The current MP of the actor.
|
||||
/// </summary>
|
||||
[FieldOffset(ActorOffsets.CurrentMp)]
|
||||
public int CurrentMp;
|
||||
|
||||
/// <summary>
|
||||
/// The max MP of the actor.
|
||||
/// </summary>
|
||||
[FieldOffset(ActorOffsets.MaxMp)]
|
||||
public short MaxMp;
|
||||
|
||||
/// <summary>
|
||||
/// The current GP of the actor.
|
||||
/// </summary>
|
||||
[FieldOffset(ActorOffsets.CurrentGp)]
|
||||
public short CurrentGp;
|
||||
|
||||
/// <summary>
|
||||
/// The max GP of the actor.
|
||||
/// </summary>
|
||||
[FieldOffset(ActorOffsets.MaxGp)]
|
||||
public short MaxGp;
|
||||
|
||||
/// <summary>
|
||||
/// The current CP of the actor.
|
||||
/// </summary>
|
||||
[FieldOffset(ActorOffsets.CurrentCp)]
|
||||
public short CurrentCp;
|
||||
|
||||
/// <summary>
|
||||
/// The max CP of the actor.
|
||||
/// </summary>
|
||||
[FieldOffset(ActorOffsets.MaxCp)]
|
||||
public short MaxCp;
|
||||
|
||||
/// <summary>
|
||||
/// The class-job of the actor.
|
||||
/// </summary>
|
||||
[FieldOffset(ActorOffsets.ClassJob)]
|
||||
public byte ClassJob;
|
||||
|
||||
/// <summary>
|
||||
/// The level of the actor.
|
||||
/// </summary>
|
||||
[FieldOffset(ActorOffsets.Level)]
|
||||
public byte Level;
|
||||
|
||||
/// <summary>
|
||||
/// The (player character) actor ID being targeted by the actor.
|
||||
/// </summary>
|
||||
[FieldOffset(ActorOffsets.PlayerCharacterTargetActorId)]
|
||||
public int PlayerCharacterTargetActorId;
|
||||
|
||||
/// <summary>
|
||||
/// The customization byte/bitfield of the actor.
|
||||
/// </summary>
|
||||
[FieldOffset(ActorOffsets.Customize)]
|
||||
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 28)]
|
||||
public byte[] Customize;
|
||||
|
||||
// Normally pack=2 should work, but ByTVal or Injection breaks this.
|
||||
// [FieldOffset(ActorOffsets.CompanyTag)] [MarshalAs(UnmanagedType.ByValArray, SizeConst = 6)] public string CompanyTag;
|
||||
|
||||
/// <summary>
|
||||
/// The (battle npc) actor ID being targeted by the actor.
|
||||
/// </summary>
|
||||
[FieldOffset(ActorOffsets.BattleNpcTargetActorId)]
|
||||
public int BattleNpcTargetActorId;
|
||||
|
||||
/// <summary>
|
||||
/// The name ID of the actor.
|
||||
/// </summary>
|
||||
[FieldOffset(ActorOffsets.NameId)]
|
||||
public int NameId;
|
||||
|
||||
/// <summary>
|
||||
/// The current world ID of the actor.
|
||||
/// </summary>
|
||||
[FieldOffset(ActorOffsets.CurrentWorld)]
|
||||
public ushort CurrentWorld;
|
||||
|
||||
/// <summary>
|
||||
/// The home world ID of the actor.
|
||||
/// </summary>
|
||||
[FieldOffset(ActorOffsets.HomeWorld)]
|
||||
public ushort HomeWorld;
|
||||
|
||||
/// <summary>
|
||||
/// Whether the actor is currently casting.
|
||||
/// </summary>
|
||||
[FieldOffset(ActorOffsets.IsCasting)]
|
||||
public bool IsCasting;
|
||||
|
||||
/// <summary>
|
||||
/// Whether the actor is currently casting (dup?).
|
||||
/// </summary>
|
||||
[FieldOffset(ActorOffsets.IsCasting2)]
|
||||
public bool IsCasting2;
|
||||
|
||||
/// <summary>
|
||||
/// The spell action ID currently being cast by the actor.
|
||||
/// </summary>
|
||||
[FieldOffset(ActorOffsets.CurrentCastSpellActionId)]
|
||||
public uint CurrentCastSpellActionId;
|
||||
|
||||
/// <summary>
|
||||
/// The actor ID of the target currently being cast at by the actor.
|
||||
/// </summary>
|
||||
[FieldOffset(ActorOffsets.CurrentCastTargetActorId)]
|
||||
public uint CurrentCastTargetActorId;
|
||||
|
||||
/// <summary>
|
||||
/// The current casting time of the spell being cast by the actor.
|
||||
/// </summary>
|
||||
[FieldOffset(ActorOffsets.CurrentCastTime)]
|
||||
public float CurrentCastTime;
|
||||
|
||||
/// <summary>
|
||||
/// The total casting time of the spell being cast by the actor.
|
||||
/// </summary>
|
||||
[FieldOffset(ActorOffsets.TotalCastTime)]
|
||||
public float TotalCastTime;
|
||||
|
||||
/// <summary>
|
||||
/// The array of status effects that the actor is currently affected by.
|
||||
/// </summary>
|
||||
[FieldOffset(ActorOffsets.UIStatusEffects)]
|
||||
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 20)]
|
||||
public StatusEffect[] UIStatusEffects;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Memory offsets for the <see cref="Actor"/> type.
|
||||
/// </summary>
|
||||
public static class ActorOffsets
|
||||
{
|
||||
// Reference https://github.com/FFXIVAPP/sharlayan-resources/blob/master/structures/5.4/x64.json for more
|
||||
public const int Name = 48; // 0x0030
|
||||
|
|
@ -48,51 +286,4 @@ namespace Dalamud.Game.ClientState.Structs
|
|||
public const int TotalCastTime = 0x1BB8;
|
||||
public const int UIStatusEffects = 0x19F8;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Native memory representation of a FFXIV actor.
|
||||
/// </summary>
|
||||
[StructLayout(LayoutKind.Explicit, Pack = 2)]
|
||||
public struct Actor
|
||||
{
|
||||
[FieldOffset(ActorOffsets.Name)] [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 30)] public string Name;
|
||||
[FieldOffset(ActorOffsets.ActorId)] public int ActorId;
|
||||
[FieldOffset(ActorOffsets.DataId)] public int DataId;
|
||||
[FieldOffset(ActorOffsets.OwnerId)] public int OwnerId;
|
||||
[FieldOffset(ActorOffsets.ObjectKind)] public ObjectKind ObjectKind;
|
||||
[FieldOffset(ActorOffsets.SubKind)] public byte SubKind;
|
||||
[FieldOffset(ActorOffsets.IsFriendly)] public bool IsFriendly;
|
||||
[FieldOffset(ActorOffsets.YalmDistanceFromPlayerX)] public byte YalmDistanceFromPlayerX; // Demo says one of these is x distance
|
||||
[FieldOffset(ActorOffsets.PlayerTargetStatus)] public byte PlayerTargetStatus; // This is some kind of enum
|
||||
[FieldOffset(ActorOffsets.YalmDistanceFromPlayerY)] public byte YalmDistanceFromPlayerY; // and the other is z distance
|
||||
[FieldOffset(ActorOffsets.Position)] public Position3 Position;
|
||||
[FieldOffset(ActorOffsets.Rotation)] public float Rotation; // Rotation around the vertical axis (yaw), from -pi to pi radians
|
||||
[FieldOffset(ActorOffsets.HitboxRadius)] public float HitboxRadius;
|
||||
[FieldOffset(ActorOffsets.CurrentHp)] public int CurrentHp;
|
||||
[FieldOffset(ActorOffsets.MaxHp)] public int MaxHp;
|
||||
[FieldOffset(ActorOffsets.CurrentMp)] public int CurrentMp;
|
||||
[FieldOffset(ActorOffsets.MaxMp)] public short MaxMp;
|
||||
[FieldOffset(ActorOffsets.CurrentGp)] public short CurrentGp;
|
||||
[FieldOffset(ActorOffsets.MaxGp)] public short MaxGp;
|
||||
[FieldOffset(ActorOffsets.CurrentCp)] public short CurrentCp;
|
||||
[FieldOffset(ActorOffsets.MaxCp)] public short MaxCp;
|
||||
[FieldOffset(ActorOffsets.ClassJob)] public byte ClassJob;
|
||||
[FieldOffset(ActorOffsets.Level)] public byte Level;
|
||||
[FieldOffset(ActorOffsets.PlayerCharacterTargetActorId)] public int PlayerCharacterTargetActorId;
|
||||
[FieldOffset(ActorOffsets.Customize)] [MarshalAs(UnmanagedType.ByValArray, SizeConst = 28)] public byte[] Customize;
|
||||
|
||||
// Normally pack=2 should work, but ByTVal or Injection breaks this.
|
||||
// [FieldOffset(ActorOffsets.CompanyTag)] [MarshalAs(UnmanagedType.ByValArray, SizeConst = 6)] public string CompanyTag;
|
||||
[FieldOffset(ActorOffsets.BattleNpcTargetActorId)] public int BattleNpcTargetActorId;
|
||||
[FieldOffset(ActorOffsets.NameId)] public int NameId;
|
||||
[FieldOffset(ActorOffsets.CurrentWorld)] public ushort CurrentWorld;
|
||||
[FieldOffset(ActorOffsets.HomeWorld)] public ushort HomeWorld;
|
||||
[FieldOffset(ActorOffsets.IsCasting)] public bool IsCasting;
|
||||
[FieldOffset(ActorOffsets.IsCasting2)] public bool IsCasting2;
|
||||
[FieldOffset(ActorOffsets.CurrentCastSpellActionId)] public uint CurrentCastSpellActionId;
|
||||
[FieldOffset(ActorOffsets.CurrentCastTargetActorId)] public uint CurrentCastTargetActorId;
|
||||
[FieldOffset(ActorOffsets.CurrentCastTime)] public float CurrentCastTime;
|
||||
[FieldOffset(ActorOffsets.TotalCastTime)] public float TotalCastTime;
|
||||
[FieldOffset(ActorOffsets.UIStatusEffects)] [MarshalAs(UnmanagedType.ByValArray, SizeConst = 20)] public StatusEffect[] UIStatusEffects;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,7 @@
|
|||
using System;
|
||||
|
||||
#pragma warning disable SA1402 // File may only contain a single type
|
||||
|
||||
namespace Dalamud.Game.ClientState.Structs.JobGauge
|
||||
{
|
||||
#region AST
|
||||
|
|
@ -270,3 +272,5 @@ namespace Dalamud.Game.ClientState.Structs.JobGauge
|
|||
|
||||
#endregion
|
||||
}
|
||||
|
||||
#pragma warning restore SA1402 // File may only contain a single type
|
||||
|
|
|
|||
|
|
@ -1,19 +1,26 @@
|
|||
using Dalamud.Game.ClientState.Actors;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
using Dalamud.Game.ClientState.Actors;
|
||||
|
||||
namespace Dalamud.Game.ClientState.Structs
|
||||
{
|
||||
/// <summary>
|
||||
/// This represents a native PartyMember class in memory.
|
||||
/// </summary>
|
||||
[StructLayout(LayoutKind.Explicit)]
|
||||
public struct PartyMember
|
||||
{
|
||||
[FieldOffset(0x0)] public IntPtr namePtr;
|
||||
[FieldOffset(0x8)] public long unknown;
|
||||
[FieldOffset(0x10)] public int actorId;
|
||||
[FieldOffset(0x14)] public ObjectKind objectKind;
|
||||
[FieldOffset(0x0)]
|
||||
public IntPtr namePtr;
|
||||
|
||||
[FieldOffset(0x8)]
|
||||
public long unknown;
|
||||
|
||||
[FieldOffset(0x10)]
|
||||
public int actorId;
|
||||
|
||||
[FieldOffset(0x14)]
|
||||
public ObjectKind objectKind;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,3 @@
|
|||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace Dalamud.Game.ClientState.Structs
|
||||
|
|
@ -9,10 +8,29 @@ namespace Dalamud.Game.ClientState.Structs
|
|||
[StructLayout(LayoutKind.Sequential)]
|
||||
public struct StatusEffect
|
||||
{
|
||||
/// <summary>
|
||||
/// The effect ID.
|
||||
/// </summary>
|
||||
public short EffectId;
|
||||
|
||||
/// <summary>
|
||||
/// How many stacks are present.
|
||||
/// </summary>
|
||||
public byte StackCount;
|
||||
|
||||
/// <summary>
|
||||
/// Additional parameters.
|
||||
/// </summary>
|
||||
public byte Param;
|
||||
|
||||
/// <summary>
|
||||
/// The duration remaining.
|
||||
/// </summary>
|
||||
public float Duration;
|
||||
|
||||
/// <summary>
|
||||
/// The ID of the actor that caused this effect.
|
||||
/// </summary>
|
||||
public int OwnerId;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue