feat: New Condition APIs (#1842)

* feat: New Condition APIs

- New methods for `ICondition`:
  - `AnyExcept` ensures the listed conditions are *not* present.
  - `OnlyAny` ensures that *only* the listed conditions are met.
  - `EqualTo` ensures that the condition state matches the listed set.
- New `IsGameIdle` method in `IClientState` can be used to check if the player is not in any "active" game state.
This commit is contained in:
KazWolfe 2024-06-15 15:44:47 -07:00 committed by GitHub
parent 8f36641f36
commit 1c03242aa9
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 88 additions and 29 deletions

View file

@ -1,6 +1,8 @@
using System.Linq;
using System.Runtime.InteropServices;
using Dalamud.Data;
using Dalamud.Game.ClientState.Conditions;
using Dalamud.Game.ClientState.Objects;
using Dalamud.Game.ClientState.Objects.SubKinds;
using Dalamud.Game.Gui;
@ -123,6 +125,27 @@ internal sealed class ClientState : IInternalDisposableService, IClientState
/// Gets client state address resolver.
/// </summary>
internal ClientStateAddressResolver AddressResolver => this.address;
/// <inheritdoc/>
public bool IsClientIdle(out ConditionFlag blockingFlag)
{
blockingFlag = 0;
if (this.LocalPlayer is null) return true;
var condition = Service<Conditions.Condition>.GetNullable();
var blockingConditions = condition.AsReadOnlySet().Except([
ConditionFlag.NormalConditions,
ConditionFlag.Jumping,
ConditionFlag.Mounted,
ConditionFlag.UsingParasol]);
blockingFlag = blockingConditions.FirstOrDefault();
return blockingFlag == 0;
}
/// <inheritdoc/>
public bool IsClientIdle() => this.IsClientIdle(out _);
/// <summary>
/// Dispose of managed and unmanaged resources.
@ -271,6 +294,12 @@ internal class ClientStatePluginScoped : IInternalDisposableService, IClientStat
/// <inheritdoc/>
public bool IsGPosing => this.clientStateService.IsGPosing;
/// <inheritdoc/>
public bool IsClientIdle(out ConditionFlag blockingFlag) => this.clientStateService.IsClientIdle(out blockingFlag);
/// <inheritdoc/>
public bool IsClientIdle() => this.clientStateService.IsClientIdle();
/// <inheritdoc/>
void IInternalDisposableService.DisposeService()
{

View file

@ -71,6 +71,22 @@ internal sealed class Condition : IInternalDisposableService, ICondition
/// <inheritdoc/>
void IInternalDisposableService.DisposeService() => this.Dispose(true);
/// <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;
}
/// <inheritdoc/>
public bool Any()
@ -100,37 +116,26 @@ internal sealed class Condition : IInternalDisposableService, ICondition
return false;
}
/// <inheritdoc/>
public bool AnyExcept(params ConditionFlag[] excluded)
{
return !this.AsReadOnlySet().Intersect(excluded).Any();
}
/// <inheritdoc/>
public bool OnlyAny(params ConditionFlag[] other)
{
var resultSet = this.AsReadOnlySet();
return !resultSet.Except(other).Any();
return !this.AsReadOnlySet().Except(other).Any();
}
/// <inheritdoc/>
public bool OnlyAll(params ConditionFlag[] other)
public bool EqualTo(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)
@ -217,12 +222,15 @@ internal class ConditionPluginScoped : IInternalDisposableService, ICondition
/// <inheritdoc/>
public bool Any(params ConditionFlag[] flags) => this.conditionService.Any(flags);
/// <inheritdoc/>
public bool AnyExcept(params ConditionFlag[] except) => this.conditionService.AnyExcept(except);
/// <inheritdoc/>
public bool OnlyAny(params ConditionFlag[] other) => this.conditionService.OnlyAny(other);
/// <inheritdoc/>
public bool OnlyAll(params ConditionFlag[] other) => this.conditionService.OnlyAll(other);
public bool EqualTo(params ConditionFlag[] other) => this.conditionService.EqualTo(other);
private void ConditionChangedForward(ConditionFlag flag, bool value) => this.ConditionChange?.Invoke(flag, value);
}

View file

@ -1,3 +1,4 @@
using Dalamud.Game.ClientState.Conditions;
using Dalamud.Game.ClientState.Objects.SubKinds;
namespace Dalamud.Plugin.Services;
@ -81,4 +82,19 @@ public interface IClientState
/// Gets a value indicating whether the client is currently in Group Pose (GPose) mode.
/// </summary>
public bool IsGPosing { get; }
/// <summary>
/// Check whether the client is currently "idle". This means a player is not logged in, or is notctively in combat
/// or doing anything that we may not want to disrupt.
/// </summary>
/// <param name="blockingFlag">An outvar containing the first observed condition blocking the "idle" state. 0 if idle.</param>
/// <returns>Returns true if the client is idle, false otherwise.</returns>
public bool IsClientIdle(out ConditionFlag blockingFlag);
/// <summary>
/// Check whether the client is currently "idle". This means a player is not logged in, or is notctively in combat
/// or doing anything that we may not want to disrupt.
/// </summary>
/// <returns>Returns true if the client is idle, false otherwise.</returns>
public bool IsClientIdle() => this.IsClientIdle(out _);
}

View file

@ -40,6 +40,12 @@ public interface ICondition
/// <inheritdoc cref="this[int]"/>
public bool this[ConditionFlag flag] => this[(int)flag];
/// <summary>
/// Convert the conditions array to a set of all set condition flags.
/// </summary>
/// <returns>Returns a set.</returns>
public IReadOnlySet<ConditionFlag> AsReadOnlySet();
/// <summary>
/// Check if any condition flags are set.
@ -55,8 +61,14 @@ public interface ICondition
public bool Any(params ConditionFlag[] flags);
/// <summary>
/// Check that *only* any of the condition flags specified are set. Useful to test if the client is in one of any
/// of a few specific condiiton states.
/// Check that the specified condition flags are *not* present in the current conditions.
/// </summary>
/// <param name="except">The array of flags to check.</param>
/// <returns>Returns false if any of the listed conditions are present, true otherwise.</returns>
public bool AnyExcept(params ConditionFlag[] except);
/// <summary>
/// Check that *only* any of the condition flags specified are set.
/// </summary>
/// <param name="other">The array of flags to check.</param>
/// <returns>Returns a bool.</returns>
@ -68,11 +80,5 @@ public interface ICondition
/// </summary>
/// <param name="other">The array of flags to check.</param>
/// <returns>Returns a bool.</returns>
public bool OnlyAll(params ConditionFlag[] other);
/// <summary>
/// Convert the conditions array to a set of all set condition flags.
/// </summary>
/// <returns>Returns a set.</returns>
public IReadOnlySet<ConditionFlag> AsReadOnlySet();
public bool EqualTo(params ConditionFlag[] other);
}