From 6ed66f7407cc12d6f0a7e383712bda3e9f607201 Mon Sep 17 00:00:00 2001 From: LTS-FFXIV <127939494+LTS-FFXIV@users.noreply.github.com> Date: Mon, 13 Jan 2025 22:49:54 -0600 Subject: [PATCH] Add null checks, status conditions, and logging - Updated `CheckResistance` in `ActionTargetInfo.cs` to handle null `gameObject` and added exception handling with logging. - Modified solo target condition in `ObjectHelper.cs` to `DataCenter.PartyMembers.Count == 1`. - Included `ECommons.DalamudServices` and added exception handling with logging in `StatusHelper.cs`. - Added `Mudra` status check in `NinjaRotation.cs` before using `FeintPvE`. - Required `EukrasianPrognosis` status for `PepsisPvE` in `SageRotation.cs`. - Added `Mudra` status check in `CustomRotation_Ability.cs` before using various abilities. - Added pragma directive in `RSCommands_Actions.cs` to suppress a null check warning and restored the previous job if it changes. --- .../Actions/ActionTargetInfo.cs | 38 ++++++++++++------- RotationSolver.Basic/Helpers/ObjectHelper.cs | 8 ++-- RotationSolver.Basic/Helpers/StatusHelper.cs | 27 ++++++++++--- .../Rotations/Basic/NinjaRotation.cs | 2 +- .../Rotations/Basic/SageRotation.cs | 1 + .../Rotations/CustomRotation_Ability.cs | 14 +++---- RotationSolver/Commands/RSCommands_Actions.cs | 2 + 7 files changed, 62 insertions(+), 30 deletions(-) diff --git a/RotationSolver.Basic/Actions/ActionTargetInfo.cs b/RotationSolver.Basic/Actions/ActionTargetInfo.cs index cac1a8e1c..f7ffda9bd 100644 --- a/RotationSolver.Basic/Actions/ActionTargetInfo.cs +++ b/RotationSolver.Basic/Actions/ActionTargetInfo.cs @@ -254,27 +254,39 @@ private bool CheckStatus(IGameObject gameObject, bool skipStatusProvideCheck) /// private bool CheckResistance(IGameObject gameObject) { - if (action.Info.AttackType == AttackType.Magic) + if (gameObject == null) return false; + + try { - if (gameObject.HasStatus(false, StatusHelper.MagicResistance)) + if (action.Info.AttackType == AttackType.Magic) { - return false; + if (gameObject.HasStatus(false, StatusHelper.MagicResistance)) + { + return false; + } } - } - else if (action.Info.Aspect != Aspect.Piercing) // Physical - { - if (gameObject.HasStatus(false, StatusHelper.PhysicalResistance)) + else if (action.Info.Aspect != Aspect.Piercing) // Physical { - return false; + if (gameObject.HasStatus(false, StatusHelper.PhysicalResistance)) + { + return false; + } } - } - if (Range >= 20) // Range - { - if (gameObject.HasStatus(false, StatusID.RangedResistance, StatusID.EnergyField)) + if (Range >= 20) // Range { - return false; + if (gameObject.HasStatus(false, StatusID.RangedResistance, StatusID.EnergyField)) + { + return false; + } } } + catch (Exception ex) + { + // Log the exception for debugging purposes + Svc.Log.Error($"Error checking resistance: {ex.Message}"); + return false; + } + return true; } diff --git a/RotationSolver.Basic/Helpers/ObjectHelper.cs b/RotationSolver.Basic/Helpers/ObjectHelper.cs index 26e955dc9..11e27567a 100644 --- a/RotationSolver.Basic/Helpers/ObjectHelper.cs +++ b/RotationSolver.Basic/Helpers/ObjectHelper.cs @@ -132,12 +132,12 @@ internal static bool IsAttackable(this IBattleChara battleChara) { TargetHostileType.AllTargetsCanAttack => true, TargetHostileType.TargetsHaveTarget => battleChara.TargetObject is IBattleChara, - TargetHostileType.AllTargetsWhenSolo => DataCenter.PartyMembers.Count < 2 || battleChara.TargetObject is IBattleChara, - TargetHostileType.AllTargetsWhenSoloInDuty => (DataCenter.PartyMembers.Count < 2 && (Svc.Condition[ConditionFlag.BoundByDuty] || Svc.Condition[ConditionFlag.BoundByDuty56])) + TargetHostileType.AllTargetsWhenSolo => DataCenter.PartyMembers.Count == 1 || battleChara.TargetObject is IBattleChara, + TargetHostileType.AllTargetsWhenSoloInDuty => (DataCenter.PartyMembers.Count == 1 && (Svc.Condition[ConditionFlag.BoundByDuty] || Svc.Condition[ConditionFlag.BoundByDuty56])) || battleChara.TargetObject is IBattleChara, TargetHostileType.TargetIsInEnemiesList => battleChara.TargetObject is IBattleChara target && target.IsInEnemiesList(), - TargetHostileType.AllTargetsWhenSoloTargetIsInEnemiesList => (DataCenter.PartyMembers.Count < 2 && (Svc.Condition[ConditionFlag.BoundByDuty] || Svc.Condition[ConditionFlag.BoundByDuty56])) || battleChara.TargetObject is IBattleChara target && target.IsInEnemiesList(), - TargetHostileType.AllTargetsWhenSoloInDutyTargetIsInEnemiesList => DataCenter.PartyMembers.Count < 2 || battleChara.TargetObject is IBattleChara target && target.IsInEnemiesList(), + TargetHostileType.AllTargetsWhenSoloTargetIsInEnemiesList => (DataCenter.PartyMembers.Count == 1 && (Svc.Condition[ConditionFlag.BoundByDuty] || Svc.Condition[ConditionFlag.BoundByDuty56])) || battleChara.TargetObject is IBattleChara target && target.IsInEnemiesList(), + TargetHostileType.AllTargetsWhenSoloInDutyTargetIsInEnemiesList => DataCenter.PartyMembers.Count == 1 || battleChara.TargetObject is IBattleChara target && target.IsInEnemiesList(), _ => true, }; } diff --git a/RotationSolver.Basic/Helpers/StatusHelper.cs b/RotationSolver.Basic/Helpers/StatusHelper.cs index bb9f21c90..4c1ab3e9f 100644 --- a/RotationSolver.Basic/Helpers/StatusHelper.cs +++ b/RotationSolver.Basic/Helpers/StatusHelper.cs @@ -1,5 +1,6 @@ using Dalamud.Game.ClientState.Statuses; using ECommons.Automation; +using ECommons.DalamudServices; using ECommons.GameHelpers; using ECommons.Logging; using RotationSolver.Basic.Configuration; @@ -339,11 +340,27 @@ private static IEnumerable GetAllStatus(this IGameObject obj, bool isFro if (obj is not IBattleChara b) return Enumerable.Empty(); var playerId = Player.Object?.GameObjectId ?? 0; - // Ensure b.StatusList is not null - return b.StatusList?.Where(status => !isFromSelf - || status.SourceId == playerId - || status.SourceObject?.OwnerId == playerId) - ?? Enumerable.Empty(); + + try + { + // Ensure b.StatusList is not null + if (b.StatusList == null) + { + PluginLog.Error("StatusList is null. Cannot get statuses."); + return Enumerable.Empty(); + } + + return b.StatusList.Where(status => !isFromSelf + || status.SourceId == playerId + || status.SourceObject?.OwnerId == playerId) + ?? Enumerable.Empty(); + } + catch (Exception ex) + { + // Log the exception + Svc.Log.Error($"Failed to get statuses: {ex.Message}"); + return Enumerable.Empty(); + } } /// diff --git a/RotationSolver.Basic/Rotations/Basic/NinjaRotation.cs b/RotationSolver.Basic/Rotations/Basic/NinjaRotation.cs index e293d1e67..9a1cd0ff0 100644 --- a/RotationSolver.Basic/Rotations/Basic/NinjaRotation.cs +++ b/RotationSolver.Basic/Rotations/Basic/NinjaRotation.cs @@ -528,7 +528,7 @@ protected sealed override bool MoveForwardAbility(IAction nextGCD, out IAction? [RotationDesc(ActionID.FeintPvE)] protected sealed override bool DefenseAreaAbility(IAction nextGCD, out IAction? act) { - if (FeintPvE.CanUse(out act)) return true; + if (FeintPvE.CanUse(out act) && !Player.HasStatus(true, StatusID.Mudra)) return true; return base.DefenseAreaAbility(nextGCD, out act); } diff --git a/RotationSolver.Basic/Rotations/Basic/SageRotation.cs b/RotationSolver.Basic/Rotations/Basic/SageRotation.cs index 02ca79acc..b8bae7e88 100644 --- a/RotationSolver.Basic/Rotations/Basic/SageRotation.cs +++ b/RotationSolver.Basic/Rotations/Basic/SageRotation.cs @@ -199,6 +199,7 @@ static partial void ModifyZoePvE(ref ActionSetting setting) static partial void ModifyPepsisPvE(ref ActionSetting setting) { + setting.StatusNeed = [StatusID.EukrasianPrognosis]; setting.CreateConfig = () => new ActionConfig() { AoeCount = 1, diff --git a/RotationSolver.Basic/Rotations/CustomRotation_Ability.cs b/RotationSolver.Basic/Rotations/CustomRotation_Ability.cs index a48c7e35c..55e853131 100644 --- a/RotationSolver.Basic/Rotations/CustomRotation_Ability.cs +++ b/RotationSolver.Basic/Rotations/CustomRotation_Ability.cs @@ -40,7 +40,7 @@ private bool Ability(IAction nextGCD, out IAction? act) var role = DataCenter.Role; IBaseAction.TargetOverride = TargetType.Interrupt; - if (DataCenter.MergedStatus.HasFlag(AutoStatus.Interrupt) && MyInterruptAbility(role, nextGCD, out act)) + if (DataCenter.MergedStatus.HasFlag(AutoStatus.Interrupt) && !Player.HasStatus(true, StatusID.Mudra) && MyInterruptAbility(role, nextGCD, out act)) { return true; } @@ -71,7 +71,7 @@ private bool Ability(IAction nextGCD, out IAction? act) { IBaseAction.ShouldEndSpecial = true; } - if (DataCenter.MergedStatus.HasFlag(AutoStatus.AntiKnockback) && AntiKnockback(role, nextGCD, out act)) + if (DataCenter.MergedStatus.HasFlag(AutoStatus.AntiKnockback) && !Player.HasStatus(true, StatusID.Mudra) && AntiKnockback(role, nextGCD, out act)) { return true; } @@ -81,7 +81,7 @@ private bool Ability(IAction nextGCD, out IAction? act) { IBaseAction.ShouldEndSpecial = true; } - if (DataCenter.MergedStatus.HasFlag(AutoStatus.Positional) && TrueNorthPvE.Cooldown.CurrentCharges > 0 && !IsLastAbility(true, TrueNorthPvE) && TrueNorthPvE.CanUse(out act, skipComboCheck: true, usedUp: true)) + if (DataCenter.MergedStatus.HasFlag(AutoStatus.Positional) && !Player.HasStatus(true, StatusID.Mudra) && TrueNorthPvE.Cooldown.CurrentCharges > 0 && !IsLastAbility(true, TrueNorthPvE) && TrueNorthPvE.CanUse(out act, skipComboCheck: true, usedUp: true)) { return true; } @@ -271,7 +271,7 @@ private bool MyInterruptAbility(JobRole role, IAction nextGCD, out IAction? act) break; case JobRole.Melee: - if (LegSweepPvE.CanUse(out act)) return true; + if (LegSweepPvE.CanUse(out act) && !Player.HasStatus(true, StatusID.Mudra)) return true; break; case JobRole.RangedPhysical: @@ -312,7 +312,7 @@ private bool AntiKnockback(JobRole role, IAction nextGCD, out IAction? act) { case JobRole.Tank: case JobRole.Melee: - if (ArmsLengthPvE.CanUse(out act)) return true; + if (ArmsLengthPvE.CanUse(out act) && !Player.HasStatus(true, StatusID.Mudra)) return true; break; case JobRole.Healer: case JobRole.RangedMagical: @@ -372,8 +372,8 @@ private bool GeneralUsingAbility(JobRole role, IAction nextGCD, out IAction? act break; case JobRole.Melee: - if (SecondWindPvE.CanUse(out act)) return true; - if (BloodbathPvE.CanUse(out act)) return true; + if (SecondWindPvE.CanUse(out act) && !Player.HasStatus(true, StatusID.Mudra)) return true; + if (BloodbathPvE.CanUse(out act) && !Player.HasStatus(true, StatusID.Mudra)) return true; break; case JobRole.Healer: diff --git a/RotationSolver/Commands/RSCommands_Actions.cs b/RotationSolver/Commands/RSCommands_Actions.cs index 52dd1b70a..0daf43610 100644 --- a/RotationSolver/Commands/RSCommands_Actions.cs +++ b/RotationSolver/Commands/RSCommands_Actions.cs @@ -190,7 +190,9 @@ internal static void UpdateRotationState() (DataCenter.RightSet.SwitchCancelConditionSet?.IsTrue(DataCenter.RightNowRotation) ?? false)) { CancelState(); +#pragma warning disable CS0472 // The result of the expression is always the same since a value of this type is never equal to 'null' if (Player.Job != null && Player.Job != _previousJob) _previousJob = Player.Job; +#pragma warning restore CS0472 // The result of the expression is always the same since a value of this type is never equal to 'null' if (ActionUpdater.AutoCancelTime != DateTime.MinValue) ActionUpdater.AutoCancelTime = DateTime.MinValue; } else if ((Service.Config.AutoOnPvPMatchStart && Svc.Condition[ConditionFlag.BetweenAreas] &&