feat: condition changed event, expose condition address

This commit is contained in:
Raymond 2021-09-07 11:14:17 -04:00
parent b0f2486215
commit 37fc6ffc73
4 changed files with 116 additions and 26 deletions

View file

@ -153,6 +153,19 @@ namespace Dalamud
Service<NetworkHandlers>.Set();
Log.Information("[T2] NH OK!");
try
{
Service<DataManager>.Set().Initialize(this.AssetDirectory.FullName);
}
catch (Exception e)
{
Log.Error(e, "Could not initialize DataManager.");
this.Unload();
return;
}
Log.Information("[T2] Data OK!");
var clientState = Service<ClientState>.Set();
Log.Information("[T2] CS OK!");
@ -192,19 +205,6 @@ namespace Dalamud
Log.Information(e, "Could not init IME.");
}
try
{
Service<DataManager>.Set().Initialize(this.AssetDirectory.FullName);
}
catch (Exception e)
{
Log.Error(e, "Could not initialize DataManager.");
this.Unload();
return;
}
Log.Information("[T2] Data OK!");
#pragma warning disable CS0618 // Type or member is obsolete
Service<SeStringManager>.Set();
#pragma warning restore CS0618 // Type or member is obsolete

View file

@ -125,6 +125,7 @@ namespace Dalamud.Game.ClientState
/// </summary>
public void Enable()
{
Service<Condition>.Get().Enable();
Service<GamepadState>.Get().Enable();
this.setupTerritoryTypeHook.Enable();
}
@ -135,6 +136,7 @@ namespace Dalamud.Game.ClientState
public void Dispose()
{
this.setupTerritoryTypeHook.Dispose();
Service<Condition>.Get().Dispose();
Service<GamepadState>.Get().Dispose();
Service<Framework>.Get().Update -= this.FrameworkOnOnUpdateEvent;
Service<NetworkHandlers>.Get().CfPop -= this.NetworkHandlersOnCfPop;

View file

@ -2,6 +2,7 @@ using System;
using Dalamud.IoC;
using Dalamud.IoC.Internal;
using Serilog;
namespace Dalamud.Game.ClientState.Conditions
{
@ -10,45 +11,61 @@ namespace Dalamud.Game.ClientState.Conditions
/// </summary>
[PluginInterface]
[InterfaceVersion("1.0")]
public class Condition
public sealed partial class Condition
{
/// <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;
private readonly bool[] cache = new bool[MaxConditionEntries];
/// <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.Address = resolver.ConditionFlags;
}
/// <summary>
/// Gets the condition array base pointer.
/// Would typically be private but is used in /xldata windows.
/// A delegate type used with the <see cref="ConditionChange"/> event.
/// </summary>
internal IntPtr ConditionArrayBase { get; private set; }
/// <param name="flag">The changed condition.</param>
/// <param name="value">The value the condition is set to.</param>
public delegate void ConditionChangeDelegate(ConditionFlag flag, bool value);
/// <summary>
/// Event that gets fired when a condition is set.
/// Should only get fired for actual changes, so the previous value will always be !value.
/// </summary>
public event ConditionChangeDelegate? ConditionChange;
/// <summary>
/// Gets the condition array base pointer.
/// </summary>
public IntPtr Address { 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]
public unsafe bool this[int flag]
{
get
{
var idx = (int)flag;
if (idx < 0 || idx >= MaxConditionEntries)
if (flag < 0 || flag >= MaxConditionEntries)
return false;
return *(bool*)(this.ConditionArrayBase + idx);
return *(bool*)(this.Address + flag);
}
}
/// <inheritdoc cref="this[int]"/>
public unsafe bool this[ConditionFlag flag]
=> this[(int)flag];
/// <summary>
/// Check if any condition flags are set.
/// </summary>
@ -57,8 +74,7 @@ namespace Dalamud.Game.ClientState.Conditions
{
for (var i = 0; i < MaxConditionEntries; i++)
{
var typedCondition = (ConditionFlag)i;
var cond = this[typedCondition];
var cond = this[i];
if (cond)
return true;
@ -66,5 +82,77 @@ namespace Dalamud.Game.ClientState.Conditions
return false;
}
/// <summary>
/// Enables the hooks of the Condition class function.
/// </summary>
public void Enable()
{
// Initialization
for (var i = 0; i < MaxConditionEntries; i++)
this.cache[i] = this[i];
Service<Framework>.Get().Update += this.FrameworkUpdate;
}
private void FrameworkUpdate(Framework framework)
{
for (var i = 0; i < MaxConditionEntries; i++)
{
var value = this[i];
if (value != this.cache[i])
{
this.cache[i] = value;
try
{
this.ConditionChange?.Invoke((ConditionFlag)i, value);
}
catch (Exception ex)
{
Log.Error(ex, $"While invoking {nameof(this.ConditionChange)}, an exception was thrown.");
}
}
}
}
}
/// <summary>
/// Provides access to conditions (generally player state). You can check whether a player is in combat, mounted, etc.
/// </summary>
public sealed partial class Condition : IDisposable
{
private bool isDisposed;
/// <summary>
/// Finalizes an instance of the <see cref="Condition" /> class.
/// </summary>
~Condition()
{
this.Dispose(false);
}
/// <summary>
/// Disposes this instance, alongside its hooks.
/// </summary>
public void Dispose()
{
GC.SuppressFinalize(this);
this.Dispose(true);
}
private void Dispose(bool disposing)
{
if (this.isDisposed)
return;
if (disposing)
{
Service<Framework>.Get().Update -= this.FrameworkUpdate;
}
this.isDisposed = true;
}
}
}

View file

@ -674,7 +674,7 @@ namespace Dalamud.Interface.Internal.Windows
var condition = Service<Condition>.Get();
#if DEBUG
ImGui.Text($"ptr: 0x{condition.ConditionArrayBase.ToInt64():X}");
ImGui.Text($"ptr: 0x{condition.Address.ToInt64():X}");
#endif
ImGui.Text("Current Conditions:");