mirror of
https://github.com/goatcorp/Dalamud.git
synced 2025-12-15 13:14:17 +01:00
228 lines
6 KiB
C#
228 lines
6 KiB
C#
using System.Collections.Generic;
|
|
using System.Linq;
|
|
|
|
using Dalamud.IoC;
|
|
using Dalamud.IoC.Internal;
|
|
using Dalamud.Plugin.Services;
|
|
|
|
using Serilog;
|
|
|
|
namespace Dalamud.Game.ClientState.Conditions;
|
|
|
|
/// <summary>
|
|
/// Provides access to conditions (generally player state). You can check whether a player is in combat, mounted, etc.
|
|
/// </summary>
|
|
[InterfaceVersion("1.0")]
|
|
[ServiceManager.EarlyLoadedService]
|
|
internal sealed class Condition : IInternalDisposableService, ICondition
|
|
{
|
|
/// <summary>
|
|
/// Gets the current max number of conditions. You can get this just by looking at the condition sheet and how many rows it has.
|
|
/// </summary>
|
|
internal const int MaxConditionEntries = 104;
|
|
|
|
[ServiceManager.ServiceDependency]
|
|
private readonly Framework framework = Service<Framework>.Get();
|
|
|
|
private readonly bool[] cache = new bool[MaxConditionEntries];
|
|
|
|
private bool isDisposed;
|
|
|
|
[ServiceManager.ServiceConstructor]
|
|
private Condition(ClientState clientState)
|
|
{
|
|
var resolver = clientState.AddressResolver;
|
|
this.Address = resolver.ConditionFlags;
|
|
|
|
// Initialization
|
|
for (var i = 0; i < MaxConditionEntries; i++)
|
|
this.cache[i] = this[i];
|
|
|
|
this.framework.Update += this.FrameworkUpdate;
|
|
}
|
|
|
|
/// <summary>Finalizes an instance of the <see cref="Condition" /> class.</summary>
|
|
~Condition() => this.Dispose(false);
|
|
|
|
/// <inheritdoc/>
|
|
public event ICondition.ConditionChangeDelegate? ConditionChange;
|
|
|
|
/// <inheritdoc/>
|
|
public int MaxEntries => MaxConditionEntries;
|
|
|
|
/// <inheritdoc/>
|
|
public IntPtr Address { get; private set; }
|
|
|
|
/// <inheritdoc/>
|
|
public unsafe bool this[int flag]
|
|
{
|
|
get
|
|
{
|
|
if (flag < 0 || flag >= MaxConditionEntries)
|
|
return false;
|
|
|
|
return *(bool*)(this.Address + flag);
|
|
}
|
|
}
|
|
|
|
/// <inheritdoc/>
|
|
public bool this[ConditionFlag flag]
|
|
=> this[(int)flag];
|
|
|
|
/// <inheritdoc/>
|
|
void IInternalDisposableService.DisposeService() => this.Dispose(true);
|
|
|
|
/// <inheritdoc/>
|
|
public bool Any()
|
|
{
|
|
for (var i = 0; i < MaxConditionEntries; i++)
|
|
{
|
|
var cond = this[i];
|
|
|
|
if (cond)
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
/// <inheritdoc/>
|
|
public bool Any(params ConditionFlag[] flags)
|
|
{
|
|
foreach (var flag in flags)
|
|
{
|
|
// this[i] performs range checking, so no need to check here
|
|
if (this[flag])
|
|
{
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
/// <inheritdoc/>
|
|
public bool OnlyAny(params ConditionFlag[] other)
|
|
{
|
|
var resultSet = this.AsReadOnlySet();
|
|
return !resultSet.Except(other).Any();
|
|
}
|
|
|
|
/// <inheritdoc/>
|
|
public bool OnlyAll(params ConditionFlag[] other)
|
|
{
|
|
var resultSet = this.AsReadOnlySet();
|
|
return resultSet.SetEquals(other);
|
|
}
|
|
|
|
/// <inheritdoc/>
|
|
public IReadOnlySet<ConditionFlag> AsReadOnlySet()
|
|
{
|
|
var result = new HashSet<ConditionFlag>();
|
|
|
|
for (var i = 0; i < MaxConditionEntries; i++)
|
|
{
|
|
if (this[i])
|
|
{
|
|
result.Add((ConditionFlag)i);
|
|
}
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
private void Dispose(bool disposing)
|
|
{
|
|
if (this.isDisposed)
|
|
return;
|
|
|
|
if (disposing)
|
|
{
|
|
this.framework.Update -= this.FrameworkUpdate;
|
|
}
|
|
|
|
this.isDisposed = true;
|
|
}
|
|
|
|
private void FrameworkUpdate(IFramework unused)
|
|
{
|
|
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>
|
|
/// Plugin-scoped version of a Condition service.
|
|
/// </summary>
|
|
[PluginInterface]
|
|
[InterfaceVersion("1.0")]
|
|
[ServiceManager.ScopedService]
|
|
#pragma warning disable SA1015
|
|
[ResolveVia<ICondition>]
|
|
#pragma warning restore SA1015
|
|
internal class ConditionPluginScoped : IInternalDisposableService, ICondition
|
|
{
|
|
[ServiceManager.ServiceDependency]
|
|
private readonly Condition conditionService = Service<Condition>.Get();
|
|
|
|
/// <summary>
|
|
/// Initializes a new instance of the <see cref="ConditionPluginScoped"/> class.
|
|
/// </summary>
|
|
internal ConditionPluginScoped()
|
|
{
|
|
this.conditionService.ConditionChange += this.ConditionChangedForward;
|
|
}
|
|
|
|
/// <inheritdoc/>
|
|
public event ICondition.ConditionChangeDelegate? ConditionChange;
|
|
|
|
/// <inheritdoc/>
|
|
public int MaxEntries => this.conditionService.MaxEntries;
|
|
|
|
/// <inheritdoc/>
|
|
public IntPtr Address => this.conditionService.Address;
|
|
|
|
/// <inheritdoc/>
|
|
public bool this[int flag] => this.conditionService[flag];
|
|
|
|
/// <inheritdoc/>
|
|
void IInternalDisposableService.DisposeService()
|
|
{
|
|
this.conditionService.ConditionChange -= this.ConditionChangedForward;
|
|
|
|
this.ConditionChange = null;
|
|
}
|
|
|
|
/// <inheritdoc/>
|
|
public IReadOnlySet<ConditionFlag> AsReadOnlySet() => this.conditionService.AsReadOnlySet();
|
|
|
|
/// <inheritdoc/>
|
|
public bool Any() => this.conditionService.Any();
|
|
|
|
/// <inheritdoc/>
|
|
public bool Any(params ConditionFlag[] flags) => this.conditionService.Any(flags);
|
|
|
|
/// <inheritdoc/>
|
|
public bool OnlyAny(params ConditionFlag[] other) => this.conditionService.OnlyAny(other);
|
|
|
|
/// <inheritdoc/>
|
|
public bool OnlyAll(params ConditionFlag[] other) => this.conditionService.OnlyAll(other);
|
|
|
|
private void ConditionChangedForward(ConditionFlag flag, bool value) => this.ConditionChange?.Invoke(flag, value);
|
|
}
|