diff --git a/Dalamud/Game/ClientState/ClientState.cs b/Dalamud/Game/ClientState/ClientState.cs index 2e8d128c3..5bc49abc8 100644 --- a/Dalamud/Game/ClientState/ClientState.cs +++ b/Dalamud/Game/ClientState/ClientState.cs @@ -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. /// internal ClientStateAddressResolver AddressResolver => this.address; + + /// + public bool IsClientIdle(out ConditionFlag blockingFlag) + { + blockingFlag = 0; + if (this.LocalPlayer is null) return true; + + var condition = Service.GetNullable(); + + var blockingConditions = condition.AsReadOnlySet().Except([ + ConditionFlag.NormalConditions, + ConditionFlag.Jumping, + ConditionFlag.Mounted, + ConditionFlag.UsingParasol]); + + blockingFlag = blockingConditions.FirstOrDefault(); + return blockingFlag == 0; + } + + /// + public bool IsClientIdle() => this.IsClientIdle(out _); /// /// Dispose of managed and unmanaged resources. @@ -271,6 +294,12 @@ internal class ClientStatePluginScoped : IInternalDisposableService, IClientStat /// public bool IsGPosing => this.clientStateService.IsGPosing; + /// + public bool IsClientIdle(out ConditionFlag blockingFlag) => this.clientStateService.IsClientIdle(out blockingFlag); + + /// + public bool IsClientIdle() => this.clientStateService.IsClientIdle(); + /// void IInternalDisposableService.DisposeService() { diff --git a/Dalamud/Game/ClientState/Conditions/Condition.cs b/Dalamud/Game/ClientState/Conditions/Condition.cs index faafe05e0..b5cb77275 100644 --- a/Dalamud/Game/ClientState/Conditions/Condition.cs +++ b/Dalamud/Game/ClientState/Conditions/Condition.cs @@ -71,6 +71,22 @@ internal sealed class Condition : IInternalDisposableService, ICondition /// void IInternalDisposableService.DisposeService() => this.Dispose(true); + + /// + public IReadOnlySet AsReadOnlySet() + { + var result = new HashSet(); + + for (var i = 0; i < MaxConditionEntries; i++) + { + if (this[i]) + { + result.Add((ConditionFlag)i); + } + } + + return result; + } /// public bool Any() @@ -100,37 +116,26 @@ internal sealed class Condition : IInternalDisposableService, ICondition return false; } + + /// + public bool AnyExcept(params ConditionFlag[] excluded) + { + return !this.AsReadOnlySet().Intersect(excluded).Any(); + } /// public bool OnlyAny(params ConditionFlag[] other) { - var resultSet = this.AsReadOnlySet(); - return !resultSet.Except(other).Any(); + return !this.AsReadOnlySet().Except(other).Any(); } /// - public bool OnlyAll(params ConditionFlag[] other) + public bool EqualTo(params ConditionFlag[] other) { var resultSet = this.AsReadOnlySet(); return resultSet.SetEquals(other); } - /// - public IReadOnlySet AsReadOnlySet() - { - var result = new HashSet(); - - 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 /// public bool Any(params ConditionFlag[] flags) => this.conditionService.Any(flags); + + /// + public bool AnyExcept(params ConditionFlag[] except) => this.conditionService.AnyExcept(except); /// public bool OnlyAny(params ConditionFlag[] other) => this.conditionService.OnlyAny(other); /// - 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); } diff --git a/Dalamud/Plugin/Services/IClientState.cs b/Dalamud/Plugin/Services/IClientState.cs index 95d5259e6..089a78d42 100644 --- a/Dalamud/Plugin/Services/IClientState.cs +++ b/Dalamud/Plugin/Services/IClientState.cs @@ -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. /// public bool IsGPosing { get; } + + /// + /// 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. + /// + /// An outvar containing the first observed condition blocking the "idle" state. 0 if idle. + /// Returns true if the client is idle, false otherwise. + public bool IsClientIdle(out ConditionFlag blockingFlag); + + /// + /// 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. + /// + /// Returns true if the client is idle, false otherwise. + public bool IsClientIdle() => this.IsClientIdle(out _); } diff --git a/Dalamud/Plugin/Services/ICondition.cs b/Dalamud/Plugin/Services/ICondition.cs index 7215cdac6..4ea9e7f76 100644 --- a/Dalamud/Plugin/Services/ICondition.cs +++ b/Dalamud/Plugin/Services/ICondition.cs @@ -40,6 +40,12 @@ public interface ICondition /// public bool this[ConditionFlag flag] => this[(int)flag]; + + /// + /// Convert the conditions array to a set of all set condition flags. + /// + /// Returns a set. + public IReadOnlySet AsReadOnlySet(); /// /// Check if any condition flags are set. @@ -55,8 +61,14 @@ public interface ICondition public bool Any(params ConditionFlag[] flags); /// - /// 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. + /// + /// The array of flags to check. + /// Returns false if any of the listed conditions are present, true otherwise. + public bool AnyExcept(params ConditionFlag[] except); + + /// + /// Check that *only* any of the condition flags specified are set. /// /// The array of flags to check. /// Returns a bool. @@ -68,11 +80,5 @@ public interface ICondition /// /// The array of flags to check. /// Returns a bool. - public bool OnlyAll(params ConditionFlag[] other); - - /// - /// Convert the conditions array to a set of all set condition flags. - /// - /// Returns a set. - public IReadOnlySet AsReadOnlySet(); + public bool EqualTo(params ConditionFlag[] other); }