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;
}