Skip to content

Commit

Permalink
feat: New Condition APIs (#1842)
Browse files Browse the repository at this point in the history
* 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.
  • Loading branch information
KazWolfe authored Jun 15, 2024
1 parent 8f36641 commit 1c03242
Show file tree
Hide file tree
Showing 4 changed files with 88 additions and 29 deletions.
29 changes: 29 additions & 0 deletions Dalamud/Game/ClientState/ClientState.cs
Original file line number Diff line number Diff line change
@@ -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;
Expand Down Expand Up @@ -123,6 +125,27 @@ public unsafe uint MapId
/// 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.
Expand Down Expand Up @@ -271,6 +294,12 @@ internal ClientStatePluginScoped()
/// <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()
{
Expand Down
48 changes: 28 additions & 20 deletions Dalamud/Game/ClientState/Conditions/Condition.cs
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,22 @@ public bool this[ConditionFlag flag]

/// <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()
Expand Down Expand Up @@ -100,35 +116,24 @@ public bool Any(params ConditionFlag[] flags)

return false;
}

/// <inheritdoc/>
public bool OnlyAny(params ConditionFlag[] other)
public bool AnyExcept(params ConditionFlag[] excluded)
{
var resultSet = this.AsReadOnlySet();
return !resultSet.Except(other).Any();
return !this.AsReadOnlySet().Intersect(excluded).Any();
}

/// <inheritdoc/>
public bool OnlyAll(params ConditionFlag[] other)
public bool OnlyAny(params ConditionFlag[] other)
{
var resultSet = this.AsReadOnlySet();
return resultSet.SetEquals(other);
return !this.AsReadOnlySet().Except(other).Any();
}

/// <inheritdoc/>
public IReadOnlySet<ConditionFlag> AsReadOnlySet()
public bool EqualTo(params ConditionFlag[] other)
{
var result = new HashSet<ConditionFlag>();

for (var i = 0; i < MaxConditionEntries; i++)
{
if (this[i])
{
result.Add((ConditionFlag)i);
}
}

return result;
var resultSet = this.AsReadOnlySet();
return resultSet.SetEquals(other);
}

private void Dispose(bool disposing)
Expand Down Expand Up @@ -217,12 +222,15 @@ void IInternalDisposableService.DisposeService()

/// <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);
}
16 changes: 16 additions & 0 deletions Dalamud/Plugin/Services/IClientState.cs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
using Dalamud.Game.ClientState.Conditions;
using Dalamud.Game.ClientState.Objects.SubKinds;

namespace Dalamud.Plugin.Services;
Expand Down Expand Up @@ -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 _);
}
24 changes: 15 additions & 9 deletions Dalamud/Plugin/Services/ICondition.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand All @@ -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>
Expand All @@ -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);
}

0 comments on commit 1c03242

Please sign in to comment.