diff --git a/RotationSolver.Basic/Actions/ActionBasicInfo.cs b/RotationSolver.Basic/Actions/ActionBasicInfo.cs index 9c4102e3e..aa9ff11bb 100644 --- a/RotationSolver.Basic/Actions/ActionBasicInfo.cs +++ b/RotationSolver.Basic/Actions/ActionBasicInfo.cs @@ -166,7 +166,7 @@ internal readonly bool BasicCheck(bool skipStatusProvideCheck, bool skipComboChe private bool IsActionEnabled() => _action.Config.IsEnabled; - private bool IsActionDisabled() => DataCenter.DisabledActionSequencer?.Contains(ID) ?? false; + private bool IsActionDisabled() => !IBaseAction.ForceEnable && (DataCenter.DisabledActionSequencer?.Contains(ID) ?? false); private bool HasEnoughMP() => DataCenter.CurrentMp >= MPNeed; diff --git a/RotationSolver.Basic/Configuration/Conditions/ActionCondition.cs b/RotationSolver.Basic/Configuration/Conditions/ActionCondition.cs index 8d8c17db5..baedd6883 100644 --- a/RotationSolver.Basic/Configuration/Conditions/ActionCondition.cs +++ b/RotationSolver.Basic/Configuration/Conditions/ActionCondition.cs @@ -65,6 +65,10 @@ protected override bool IsTrueInside(ICustomRotation rotation) return _action.Cooldown.MaxCharges == Param1; } break; + case ActionConditionType.CanUse: + return _action.CanUse(out var act); + break; + } return false; } diff --git a/RotationSolver.Basic/Configuration/Conditions/TargetCondition.cs b/RotationSolver.Basic/Configuration/Conditions/TargetCondition.cs index 24146cc94..2674de3c6 100644 --- a/RotationSolver.Basic/Configuration/Conditions/TargetCondition.cs +++ b/RotationSolver.Basic/Configuration/Conditions/TargetCondition.cs @@ -1,4 +1,5 @@ using ECommons.DalamudServices; +using ECommons.GameFunctions; using ECommons.GameHelpers; using Lumina.Excel.GeneratedSheets; @@ -24,6 +25,8 @@ internal class TargetCondition : DelayCondition public string CastingActionName = string.Empty; + public string CombatRole = string.Empty; + /// /// Checks if the condition is true inside the rotation. /// @@ -63,7 +66,8 @@ protected override bool IsTrueInside(ICustomRotation rotation) TargetConditionType.HPRatio => CheckHPRatio(tar), TargetConditionType.MP => CheckMP(tar), TargetConditionType.TargetName => CheckTargetName(tar), - _ => false, + TargetConditionType.TargetRole => CheckTargetRole(tar), + _ => false, }; } @@ -158,6 +162,14 @@ private bool CheckTargetName(IBattleChara tar) } return tar.Name.TextValue == CastingActionName; } + private bool CheckTargetRole(IBattleChara tar) + { + if (string.IsNullOrEmpty(CombatRole)) + { + return false; + } + return tar.GetRole().ToString() == CombatRole; + } } internal enum TargetType : byte @@ -221,4 +233,7 @@ internal enum TargetConditionType : byte [Description("Target Name")] TargetName, -} \ No newline at end of file + + [Description("Target Role")] + TargetRole, +} diff --git a/RotationSolver.Basic/Configuration/Configs.cs b/RotationSolver.Basic/Configuration/Configs.cs index 8d5d76369..849564be4 100644 --- a/RotationSolver.Basic/Configuration/Configs.cs +++ b/RotationSolver.Basic/Configuration/Configs.cs @@ -111,7 +111,11 @@ public const string [ConditionBool, UI("Automatically use MP Potions", Description = "Experimental.", Filter = AutoActionUsage)] private static readonly bool _useMpPotions = false; - + + [JobConfig, UI("MP threshold under which to use Lucid Dreaming", Filter = AutoActionUsage)] + [Range(0, 10000, ConfigUnitType.None)] + public int LucidDreamingMpThreshold { get; set; } = 6000; + [ConditionBool, UI("Prioritize mob/object targets with attack markers", Filter = TargetConfig)] private static readonly bool _chooseAttackMark = true; @@ -493,11 +497,6 @@ public const string [Range(0, 5, ConfigUnitType.Seconds, 0.05f)] public Vector2 HealWhenNothingTodoDelay { get; set; } = new(0.5f, 1); - [UI("Auto Heal delay range", - Parent = nameof(AutoHeal))] - [Range(0, 3, ConfigUnitType.Seconds, 0.002f)] - public Vector2 HealDelay { get; set; } = new(0.5f, 1); - [UI("How soon before countdown is finished to start casting or attacking.", Filter = BasicTimer, Section = 1, PvPFilter = JobFilterType.NoJob)] [Range(0, 0.7f, ConfigUnitType.Seconds, 0.002f)] diff --git a/RotationSolver.Basic/Rotations/Basic/PictomancerRotation.cs b/RotationSolver.Basic/Rotations/Basic/PictomancerRotation.cs index 019f4e7b4..188bf750c 100644 --- a/RotationSolver.Basic/Rotations/Basic/PictomancerRotation.cs +++ b/RotationSolver.Basic/Rotations/Basic/PictomancerRotation.cs @@ -49,22 +49,22 @@ public partial class PictomancerRotation /// /// Is Pom Motif ready /// - public static bool isPomMotifReady => ((byte)JobGauge.CreatureFlags & 32) == 32 || ((byte)JobGauge.CreatureFlags & 0) == 0; + public static bool isPomMotifReady => ((byte)JobGauge.CreatureFlags) == 32 || ((byte)JobGauge.CreatureFlags) == 0; /// /// Is Wing Motif ready /// - public static bool isWingMotifReady => ((byte)JobGauge.CreatureFlags & 33) == 33 || ((byte)JobGauge.CreatureFlags & 1) == 1; + public static bool isWingMotifReady => ((byte)JobGauge.CreatureFlags) == 33 || ((byte)JobGauge.CreatureFlags) == 1; /// /// Is Claw Motif ready /// - public static bool isClawMotifReady => ((byte)JobGauge.CreatureFlags & 19) == 19 || ((byte)JobGauge.CreatureFlags & 3) == 3; + public static bool isClawMotifReady => ((byte)JobGauge.CreatureFlags) == 19 || ((byte)JobGauge.CreatureFlags) == 3; /// /// Indicates that the player is not in a Dread Combo. /// - public static bool isMawMotifReady => ((byte)JobGauge.CreatureFlags & 23) == 23 || ((byte)JobGauge.CreatureFlags & 7) == 7; + public static bool isMawMotifReady => ((byte)JobGauge.CreatureFlags) == 23 || ((byte)JobGauge.CreatureFlags) == 7; /// diff --git a/RotationSolver.Basic/Rotations/CustomRotation_Ability.cs b/RotationSolver.Basic/Rotations/CustomRotation_Ability.cs index 790679974..e38d44268 100644 --- a/RotationSolver.Basic/Rotations/CustomRotation_Ability.cs +++ b/RotationSolver.Basic/Rotations/CustomRotation_Ability.cs @@ -44,39 +44,59 @@ private bool Ability(IAction nextGCD, out IAction? act) } IBaseAction.TargetOverride = TargetType.Tank; - IBaseAction.ShouldEndSpecial = true; + if (DataCenter.CommandStatus.HasFlag(AutoStatus.Shirk)) + { + IBaseAction.ShouldEndSpecial = true; + } if (DataCenter.MergedStatus.HasFlag(AutoStatus.Shirk) && ShirkPvE.CanUse(out act)) { return true; } + IBaseAction.ShouldEndSpecial = false; IBaseAction.TargetOverride = null; + if (DataCenter.CommandStatus.HasFlag(AutoStatus.TankStance)) + { + IBaseAction.ShouldEndSpecial = true; + } if (DataCenter.MergedStatus.HasFlag(AutoStatus.TankStance) && (TankStance?.CanUse(out act) ?? false)) { return true; } + IBaseAction.ShouldEndSpecial = false; + if (DataCenter.CommandStatus.HasFlag(AutoStatus.AntiKnockback)) + { + IBaseAction.ShouldEndSpecial = true; + } if (DataCenter.MergedStatus.HasFlag(AutoStatus.AntiKnockback) && AntiKnockback(role, nextGCD, out act)) { return true; } + IBaseAction.ShouldEndSpecial = false; + if (DataCenter.CommandStatus.HasFlag(AutoStatus.Positional)) + { + IBaseAction.ShouldEndSpecial = true; + } if (DataCenter.MergedStatus.HasFlag(AutoStatus.Positional) && TrueNorthPvE.Cooldown.CurrentCharges > 0 && !IsLastAbility(true, TrueNorthPvE) && TrueNorthPvE.CanUse(out act, skipComboCheck: true, usedUp: true)) { return true; } + IBaseAction.ShouldEndSpecial = false; IBaseAction.TargetOverride = TargetType.Heal; - IBaseAction.ShouldEndSpecial = false; if (DataCenter.CommandStatus.HasFlag(AutoStatus.HealAreaAbility)) { IBaseAction.AllEmpty = true; + IBaseAction.ShouldEndSpecial = true; if (HealAreaAbility(nextGCD, out act)) { return true; } IBaseAction.AllEmpty = false; + IBaseAction.ShouldEndSpecial = false; } if (DataCenter.AutoStatus.HasFlag(AutoStatus.HealAreaAbility) && CanHealAreaAbility) @@ -92,11 +112,13 @@ private bool Ability(IAction nextGCD, out IAction? act) if (DataCenter.CommandStatus.HasFlag(AutoStatus.HealSingleAbility)) { IBaseAction.AllEmpty = true; + IBaseAction.ShouldEndSpecial = true; if (HealSingleAbility(nextGCD, out act)) { return true; } IBaseAction.AllEmpty = false; + IBaseAction.ShouldEndSpecial = false; } if (DataCenter.AutoStatus.HasFlag(AutoStatus.HealSingleAbility) && CanHealSingleAbility) @@ -110,13 +132,21 @@ private bool Ability(IAction nextGCD, out IAction? act) } IBaseAction.TargetOverride = null; - IBaseAction.ShouldEndSpecial = true; + if (DataCenter.CommandStatus.HasFlag(AutoStatus.Speed)) + { + IBaseAction.ShouldEndSpecial = true; + } if (DataCenter.CommandStatus.HasFlag(AutoStatus.Speed) && SpeedAbility(nextGCD, out act)) { return true; } + IBaseAction.ShouldEndSpecial = false; + if (DataCenter.CommandStatus.HasFlag(AutoStatus.Provoke)) + { + IBaseAction.ShouldEndSpecial = true; + } if (DataCenter.MergedStatus.HasFlag(AutoStatus.Provoke)) { if (!HasTankStance && (TankStance?.CanUse(out act) ?? false)) @@ -130,9 +160,14 @@ private bool Ability(IAction nextGCD, out IAction? act) return true; } } + IBaseAction.ShouldEndSpecial = false; IBaseAction.TargetOverride = TargetType.BeAttacked; + if (DataCenter.CommandStatus.HasFlag(AutoStatus.DefenseArea)) + { + IBaseAction.ShouldEndSpecial = true; + } if (DataCenter.MergedStatus.HasFlag(AutoStatus.DefenseArea)) { if (DefenseAreaAbility(nextGCD, out act) || (role is JobRole.Melee or JobRole.RangedPhysical or JobRole.RangedMagical && DefenseSingleAbility(nextGCD, out act))) @@ -140,7 +175,12 @@ private bool Ability(IAction nextGCD, out IAction? act) return true; } } + IBaseAction.ShouldEndSpecial = false; + if (DataCenter.CommandStatus.HasFlag(AutoStatus.DefenseSingle)) + { + IBaseAction.ShouldEndSpecial = true; + } if (DataCenter.MergedStatus.HasFlag(AutoStatus.DefenseSingle)) { if (DefenseSingleAbility(nextGCD, out act) || (!DataCenter.IsHostileCastingToTank && ArmsLengthPvE.CanUse(out act))) @@ -148,21 +188,36 @@ private bool Ability(IAction nextGCD, out IAction? act) return true; } } + IBaseAction.ShouldEndSpecial = false; IBaseAction.TargetOverride = null; + if (DataCenter.CommandStatus.HasFlag(AutoStatus.MoveForward)) + { + IBaseAction.ShouldEndSpecial = true; + } IBaseAction.AllEmpty = true; if (DataCenter.MergedStatus.HasFlag(AutoStatus.MoveForward) && Player != null && !Player.HasStatus(true, StatusID.Bind) && MoveForwardAbility(nextGCD, out act)) { return true; } + IBaseAction.ShouldEndSpecial = false; + if (DataCenter.CommandStatus.HasFlag(AutoStatus.MoveBack)) + { + IBaseAction.ShouldEndSpecial = true; + } if (DataCenter.MergedStatus.HasFlag(AutoStatus.MoveBack) && MoveBackAbility(nextGCD, out act)) { return true; } + IBaseAction.ShouldEndSpecial = false; IBaseAction.AllEmpty = false; + if (DataCenter.CommandStatus.HasFlag(AutoStatus.HealSingleAbility)) + { + IBaseAction.ShouldEndSpecial = true; + } if (DataCenter.MergedStatus.HasFlag(AutoStatus.HealSingleAbility) && UseHpPotion(nextGCD, out act)) { return true; diff --git a/RotationSolver.Basic/Rotations/CustomRotation_Actions.cs b/RotationSolver.Basic/Rotations/CustomRotation_Actions.cs index 36b1e67db..a75560796 100644 --- a/RotationSolver.Basic/Rotations/CustomRotation_Actions.cs +++ b/RotationSolver.Basic/Rotations/CustomRotation_Actions.cs @@ -47,7 +47,7 @@ static partial void ModifyEsunaPvE(ref ActionSetting setting) static partial void ModifyLucidDreamingPvE(ref ActionSetting setting) { - setting.ActionCheck = () => Player.CurrentMp < 6000 && InCombat; + setting.ActionCheck = () => Player.CurrentMp < Service.Config.LucidDreamingMpThreshold && InCombat; } static partial void ModifySecondWindPvE(ref ActionSetting setting) diff --git a/RotationSolver.Basic/Rotations/CustomRotation_GCD.cs b/RotationSolver.Basic/Rotations/CustomRotation_GCD.cs index f4d5746ed..7e2e06b08 100644 --- a/RotationSolver.Basic/Rotations/CustomRotation_GCD.cs +++ b/RotationSolver.Basic/Rotations/CustomRotation_GCD.cs @@ -25,8 +25,6 @@ partial class CustomRotation if (EmergencyGCD(out act)) return act; - IBaseAction.ShouldEndSpecial = true; - IBaseAction.TargetOverride = TargetType.Death; if (RaiseSpell(out act, false)) return act; @@ -147,8 +145,10 @@ private bool RaiseSpell(out IAction? act, bool mustUse) if (DataCenter.CommandStatus.HasFlag(AutoStatus.Raise)) { + IBaseAction.ShouldEndSpecial = true; if (RaiseGCD(out act) || RaiseAction(out act, false)) return true; } + IBaseAction.ShouldEndSpecial = false; if (!DataCenter.AutoStatus.HasFlag(AutoStatus.Raise)) return false; @@ -187,7 +187,12 @@ bool RaiseAction(out IAction act, bool ignoreCastingCheck) /// True if the action can be used; otherwise, false. protected virtual bool RaiseGCD(out IAction? act) { + if (DataCenter.CommandStatus.HasFlag(AutoStatus.Raise)) + { + IBaseAction.ShouldEndSpecial = true; + } if (DataCenter.RightNowDutyRotation?.RaiseGCD(out act) ?? false) return true; + IBaseAction.ShouldEndSpecial = false; act = null; return false; } @@ -200,9 +205,13 @@ protected virtual bool DispelGCD(out IAction? act) { act = null; if (ShouldSkipAction()) return false; - + if (DataCenter.CommandStatus.HasFlag(AutoStatus.Dispel)) + { + IBaseAction.ShouldEndSpecial = true; + } if (!HasSwift && EsunaPvE.CanUse(out act)) return true; if (DataCenter.RightNowDutyRotation?.DispelGCD(out act) ?? false) return true; + IBaseAction.ShouldEndSpecial = false; return false; } @@ -237,8 +246,13 @@ protected virtual bool MoveForwardGCD(out IAction? act) { act = null; if (ShouldSkipAction()) return false; + if (DataCenter.CommandStatus.HasFlag(AutoStatus.MoveForward)) + { + IBaseAction.ShouldEndSpecial = true; + } if (DataCenter.RightNowDutyRotation?.MoveForwardGCD(out act) ?? false) return true; + IBaseAction.ShouldEndSpecial = false; act = null; return false; } @@ -250,7 +264,13 @@ protected virtual bool MoveForwardGCD(out IAction? act) [RotationDesc(DescType.HealSingleGCD)] protected virtual bool HealSingleGCD(out IAction? act) { + if (DataCenter.CommandStatus.HasFlag(AutoStatus.HealSingleSpell)) + { + IBaseAction.ShouldEndSpecial = true; + } + if (DataCenter.RightNowDutyRotation?.HealSingleGCD(out act) ?? false) return true; + IBaseAction.ShouldEndSpecial = false; act = null; return false; } @@ -264,8 +284,13 @@ protected virtual bool HealAreaGCD(out IAction? act) { act = null; if (ShouldSkipAction()) return false; + if (DataCenter.CommandStatus.HasFlag(AutoStatus.HealAreaSpell)) + { + IBaseAction.ShouldEndSpecial = true; + } if (DataCenter.RightNowDutyRotation?.HealAreaGCD(out act) ?? false) return true; + IBaseAction.ShouldEndSpecial = false; act = null!; return false; } @@ -279,8 +304,13 @@ protected virtual bool DefenseSingleGCD(out IAction? act) { act = null; if (ShouldSkipAction()) return false; + if (DataCenter.CommandStatus.HasFlag(AutoStatus.DefenseSingle)) + { + IBaseAction.ShouldEndSpecial = true; + } if (DataCenter.RightNowDutyRotation?.DefenseSingleGCD(out act) ?? false) return true; + IBaseAction.ShouldEndSpecial = false; act = null!; return false; } @@ -294,8 +324,13 @@ protected virtual bool DefenseAreaGCD(out IAction? act) { act = null; if (ShouldSkipAction()) return false; + if (DataCenter.CommandStatus.HasFlag(AutoStatus.DefenseArea)) + { + IBaseAction.ShouldEndSpecial = true; + } if (DataCenter.RightNowDutyRotation?.DefenseAreaGCD(out act) ?? false) return true; + IBaseAction.ShouldEndSpecial = false; act = null; return false; } @@ -317,4 +352,4 @@ private bool ShouldSkipAction() { return DataCenter.CommandStatus.HasFlag(AutoStatus.Raise) && Role is JobRole.Healer && HasSwift; } -} \ No newline at end of file +} diff --git a/RotationSolver/UI/ConditionDrawer.cs b/RotationSolver/UI/ConditionDrawer.cs index d79e719ea..f0622b687 100644 --- a/RotationSolver/UI/ConditionDrawer.cs +++ b/RotationSolver/UI/ConditionDrawer.cs @@ -869,6 +869,11 @@ void DrawStatusIcon() ImGuiHelper.SetNextWidthWithName(targetCondition.CastingActionName); ImGui.InputText($"Name##TargetName{targetCondition.GetHashCode()}", ref targetCondition.CastingActionName, 128); break; + case TargetConditionType.TargetRole: + ImGui.SameLine(); + ImGuiHelper.SetNextWidthWithName(targetCondition.CombatRole.ToString()); + ImGui.InputText($"Name##TargetRole{targetCondition.GetHashCode()}", ref targetCondition.CombatRole, 128); + break; } if (targetCondition._action == null && targetCondition.TargetType == TargetType.Target) diff --git a/RotationSolver/Updaters/StateUpdater.cs b/RotationSolver/Updaters/StateUpdater.cs index 9b7af5016..a9108d958 100644 --- a/RotationSolver/Updaters/StateUpdater.cs +++ b/RotationSolver/Updaters/StateUpdater.cs @@ -20,12 +20,6 @@ public static void UpdateState() DataCenter.AutoStatus = StatusFromAutomatic(); } - static RandomDelay - _healDelay1 = new(() => Service.Config.HealDelay), - _healDelay2 = new(() => Service.Config.HealDelay), - _healDelay3 = new(() => Service.Config.HealDelay), - _healDelay4 = new(() => Service.Config.HealDelay); - private static AutoStatus StatusFromAutomatic() { AutoStatus status = AutoStatus.None; @@ -83,19 +77,19 @@ private static AutoStatus StatusFromAutomatic() canHealAreaSpell = DataCenter.PartyMembersDifferHP < Service.Config.HealthDifference && DataCenter.PartyMembersAverHP < Lerp(Service.Config.HealthAreaSpell, Service.Config.HealthAreaSpellHot, ratio); } - if (_healDelay1.Delay(canHealAreaAbility)) + if (canHealAreaAbility) { status |= AutoStatus.HealAreaAbility; } - if (_healDelay2.Delay(canHealAreaSpell)) + if (canHealAreaSpell) { status |= AutoStatus.HealAreaSpell; } - if (_healDelay3.Delay(canHealSingleAbility)) + if (canHealSingleAbility) { status |= AutoStatus.HealSingleAbility; } - if (_healDelay4.Delay(canHealSingleSpell)) + if (canHealSingleSpell) { status |= AutoStatus.HealSingleSpell; }