From 911c9f4a8bd15e7191b93062543fb5a937b98e9d Mon Sep 17 00:00:00 2001
From: LTS-FFXIV <127939494+LTS-FFXIV@users.noreply.github.com>
Date: Mon, 13 Jan 2025 16:41:30 -0600
Subject: [PATCH 1/2] Set Paradox rotation at level 100 only, properly removed
old PLD default, fixed teaching mode bugs, and resolved issue with Auto off
class switch feautre turning off during area transitions.
---
BasicRotations/Magical/BLM_Beta.cs | 6 +-
BasicRotations/RebornRotations.csproj | 1 -
BasicRotations/Tank/PLD_Default.cs | 2 +-
BasicRotations/Tank/PLD_Default_Old | 271 ------------------
RotationSolver/Commands/RSCommands_Actions.cs | 2 +-
.../DrawingHighlightHotbar.cs | 36 ++-
RotationSolver/UI/ImGuiHelper.cs | 12 +-
7 files changed, 23 insertions(+), 307 deletions(-)
delete mode 100644 BasicRotations/Tank/PLD_Default_Old
diff --git a/BasicRotations/Magical/BLM_Beta.cs b/BasicRotations/Magical/BLM_Beta.cs
index 9db1fb1b3..3ba68d0d4 100644
--- a/BasicRotations/Magical/BLM_Beta.cs
+++ b/BasicRotations/Magical/BLM_Beta.cs
@@ -7,7 +7,7 @@
public sealed class BLM_Beta : BlackMageRotation
{
#region Config Options
- [RotationConfig(CombatType.PvE, Name = "Use Infinite Paradox Rotation (Requires Level 90)")]
+ [RotationConfig(CombatType.PvE, Name = "Use Infinite Paradox Rotation when at level 100)")]
public bool Infinity { get; set; } = false;
[RotationConfig(CombatType.PvE, Name = "(Standard Rotation) Use Leylines in combat when standing still")]
@@ -140,7 +140,7 @@ protected override bool AttackAbility(IAction nextGCD, out IAction? act)
if (AmplifierPvE.CanUse(out act)) return true;
}
- if (ParadoxPvE.EnoughLevel && Infinity)
+ if (FlareStarPvE.EnoughLevel && Infinity)
{
if (UseMedicine && UseBurstMedicine(out act)) return true;
@@ -199,7 +199,7 @@ protected override bool GeneralGCD(out IAction? act)
if (MaintainStatus(out act)) return true;
}
- if (ParadoxPvE.EnoughLevel && Infinity)
+ if (FlareStarPvE.EnoughLevel && Infinity)
{
if (InAstralFire && ElementTime < 4 && Player.HasStatus(true, StatusID.Firestarter))
diff --git a/BasicRotations/RebornRotations.csproj b/BasicRotations/RebornRotations.csproj
index e1c2d2739..46b699e02 100644
--- a/BasicRotations/RebornRotations.csproj
+++ b/BasicRotations/RebornRotations.csproj
@@ -26,7 +26,6 @@
-
diff --git a/BasicRotations/Tank/PLD_Default.cs b/BasicRotations/Tank/PLD_Default.cs
index 29ded4c1b..fb739a77d 100644
--- a/BasicRotations/Tank/PLD_Default.cs
+++ b/BasicRotations/Tank/PLD_Default.cs
@@ -1,6 +1,6 @@
namespace RebornRotations.Tank;
-[Rotation("Beta", CombatType.PvE, GameVersion = "7.15")]
+[Rotation("Default", CombatType.PvE, GameVersion = "7.15")]
[SourceCode(Path = "main/BasicRotations/Tank/PLD_Default.cs")]
[Api(4)]
diff --git a/BasicRotations/Tank/PLD_Default_Old b/BasicRotations/Tank/PLD_Default_Old
deleted file mode 100644
index 022277d70..000000000
--- a/BasicRotations/Tank/PLD_Default_Old
+++ /dev/null
@@ -1,271 +0,0 @@
-namespace DefaultRotations.Tank;
-
-[Rotation("Default", CombatType.PvE, GameVersion = "7.15")]
-[SourceCode(Path = "main/BasicRotations/Tank/PLD_Default.cs")]
-[Api(4)]
-public class PLD_Default_Old : PaladinRotation
-{
- #region Config Options
-
- [RotationConfig(CombatType.PvE, Name = "Prevent actions while you have Passage of Arms up")]
- public bool PassageProtec { get; set; } = false;
-
- [RotationConfig(CombatType.PvE, Name = "Use Hallowed Ground with Cover")]
- private bool HallowedWithCover { get; set; } = true;
-
- [Range(1, 8, ConfigUnitType.Pixels)]
- [RotationConfig(CombatType.PvE, Name = "How many GCDs to delay burst by (Assumes you open with Holy Spirit, 2 is best for melee opening) ")]
- private int AdjustedBurst { get; set; } = 3;
-
- [RotationConfig(CombatType.PvE, Name = "Prioritize Atonement Combo During Fight or Flight outside of Opener (Might not good for Dungeons Packs)")]
- private bool PrioritizeAtonementCombo { get; set; } = false;
-
- [RotationConfig(CombatType.PvE, Name = "Use Holy Spirit First (For if you want to MinMax it)")]
- private bool MinMaxHolySpirit { get; set; } = false;
-
- [RotationConfig(CombatType.PvE, Name = "Use Divine Veil at 15 seconds remaining on Countdown")]
- private bool UseDivineVeilPre { get; set; } = false;
-
- [RotationConfig(CombatType.PvE, Name = "Use Holy Circle or Holy Spirit when out of melee range")]
- private bool UseHolyWhenAway { get; set; } = false;
-
- [RotationConfig(CombatType.PvE, Name = "Use Shield Bash when Low Blow is cooling down")]
- private bool UseShieldBash { get; set; } = true;
-
- [RotationConfig(CombatType.PvE, Name = "Allow the Use of Shield Lob")]
- private bool UseShieldLob { get; set; } = true;
-
- [RotationConfig(CombatType.PvE, Name = "Maximize Damage if Target if considered dying")]
- private bool BurstTargetIfConsideredDying { get; set; } = false;
-
- [Range(0, 100, ConfigUnitType.Pixels)]
- [RotationConfig(CombatType.PvE, Name = "Use Sheltron at minimum X Oath to prevent over cap (Set to 0 to disable)")]
- private int WhenToSheltron { get; set; } = 100;
-
- [Range(0, 1, ConfigUnitType.Percent)]
- [RotationConfig(CombatType.PvE, Name = "Health threshold for Intervention (Set to 0 to disable)")]
- private float InterventionRatio { get; set; } = 0.6f;
-
- [Range(0, 1, ConfigUnitType.Percent)]
- [RotationConfig(CombatType.PvE, Name = "Health threshold for Cover (Set to 0 to disable)")]
- private float CoverRatio { get; set; } = 0.3f;
-
- private bool HasSupplicationReady => Player.HasStatus(true, StatusID.SupplicationReady);
- private bool HasSepulchreReady => Player.HasStatus(true, StatusID.SepulchreReady);
- private bool HasHonorReady => Player.HasStatus(true, StatusID.BladeOfHonorReady);
- private bool TargetIsDying => (HostileTarget?.IsDying() ?? false) && BurstTargetIfConsideredDying;
-
- private bool HolySpiritFirst(out IAction? act)
- {
- act = null;
- if (MinMaxHolySpirit && HasDivineMight && HolySpiritPvE.CanUse(out act)) return true;
- return false;
- }
-
- private const ActionID ConfiteorPvEActionId = (ActionID)16459;
- private new readonly IBaseAction ConfiteorPvE = new BaseAction(ConfiteorPvEActionId);
- #endregion
-
- #region Countdown Logic
- protected override IAction? CountDownAction(float remainTime)
- {
- if (remainTime < HolySpiritPvE.Info.CastTime + CountDownAhead
- && HolySpiritPvE.CanUse(out var act)) return act;
-
- if (remainTime < 15 && UseDivineVeilPre
- && DivineVeilPvE.CanUse(out act)) return act;
-
- return base.CountDownAction(remainTime);
- }
- #endregion
-
- #region oGCD Logic
- protected override bool EmergencyAbility(IAction nextGCD, out IAction? act)
- {
- act = null;
- if (PassageProtec && Player.HasStatus(true, StatusID.PassageOfArms)) return false;
-
- if (Player.HasStatus(true, StatusID.Cover) && HallowedWithCover && HallowedGroundPvE.CanUse(out act)) return true;
-
- if (HallowedGroundPvE.CanUse(out act)
- && Player.GetHealthRatio() <= HealthForDyingTanks) return true;
-
- if ((Player.HasStatus(true, StatusID.Rampart) || Player.HasStatus(true, StatusID.Sentinel)) &&
- InterventionPvE.CanUse(out act) &&
- InterventionPvE.Target.Target?.GetHealthRatio() < 0.6) return true;
-
- if (CoverPvE.CanUse(out act) && CoverPvE.Target.Target?.DistanceToPlayer() < 10 &&
- CoverPvE.Target.Target?.GetHealthRatio() < CoverRatio) return true;
-
- return base.EmergencyAbility(nextGCD, out act);
- }
-
- [RotationDesc(ActionID.IntervenePvE)]
- protected override bool MoveForwardAbility(IAction nextGCD, out IAction? act)
- {
- act = null;
- if (PassageProtec && Player.HasStatus(true, StatusID.PassageOfArms)) return false;
- if (IntervenePvE.CanUse(out act)) return true;
- return base.MoveForwardAbility(nextGCD, out act);
- }
-
- [RotationDesc(ActionID.SentinelPvE, ActionID.RampartPvE, ActionID.BulwarkPvE, ActionID.SheltronPvE, ActionID.ReprisalPvE)]
- protected override bool DefenseSingleAbility(IAction nextGCD, out IAction? act)
- {
- act = null;
- if (PassageProtec && Player.HasStatus(true, StatusID.PassageOfArms)) return false;
-
- // If the player has the Hallowed Ground status, don't use any abilities.
- if (!Player.HasStatus(true, StatusID.HallowedGround))
- {
- // If Bulwark can be used, use it and return true.
- if (BulwarkPvE.CanUse(out act, skipAoeCheck: true)) return true;
-
- // If Oath can be used, use it and return true.
- if (UseOath(out act)) return true;
-
- // If Rampart is not cooling down or has been cooling down for more than 60 seconds, and Sentinel can be used, use Sentinel and return true.
- if ((!RampartPvE.Cooldown.IsCoolingDown || RampartPvE.Cooldown.ElapsedAfter(60)) && SentinelPvE.CanUse(out act)) return true;
-
- // If Sentinel is at an enough level and is cooling down for more than 60 seconds, or if Sentinel is not at an enough level, and Rampart can be used, use Rampart and return true.
- if ((SentinelPvE.EnoughLevel && SentinelPvE.Cooldown.IsCoolingDown && SentinelPvE.Cooldown.ElapsedAfter(60) || !SentinelPvE.EnoughLevel) && RampartPvE.CanUse(out act)) return true;
-
- // If Reprisal can be used, use it and return true.
- if (ReprisalPvE.CanUse(out act, skipAoeCheck: true)) return true;
-
- }
- return base.DefenseSingleAbility(nextGCD, out act);
- }
-
- [RotationDesc(ActionID.DivineVeilPvE, ActionID.PassageOfArmsPvE)]
- protected override bool DefenseAreaAbility(IAction nextGCD, out IAction? act)
- {
- act = null;
- if (PassageProtec && Player.HasStatus(true, StatusID.PassageOfArms)) return false;
-
- if (DivineVeilPvE.CanUse(out act)) return true;
-
- if (PassageOfArmsPvE.CanUse(out act)) return true;
-
- return base.DefenseAreaAbility(nextGCD, out act);
- }
-
- protected override bool AttackAbility(IAction nextGCD, out IAction? act)
- {
- act = null;
- if (PassageProtec && Player.HasStatus(true, StatusID.PassageOfArms)) return false;
-
- if (WeaponRemain > 0.42f)
- {
- act = null;
-
- if (HasHonorReady && BladeOfHonorPvE.CanUse(out act, skipAoeCheck: true)) return true;
-
- if ((InCombat && !CombatElapsedLessGCD(AdjustedBurst)))
- {
- if (FightOrFlightPvE.CanUse(out act)) return true;
- if (RequiescatPvE.CanUse(out act, skipAoeCheck: true, usedUp: HasFightOrFlight)) return true;
- if (OathGauge >= WhenToSheltron && WhenToSheltron > 0 && UseOath(out act)) return true;
- }
-
- if (CombatElapsedLessGCD(AdjustedBurst + 1)) return false;
-
- if (FightOrFlightPvE.CanUse(out act)) return true;
- if (RequiescatPvE.CanUse(out act, skipAoeCheck: true, usedUp: HasFightOrFlight)) return true;
- if (OathGauge >= WhenToSheltron && WhenToSheltron > 0 && UseOath(out act)) return true;
-
- if (CircleOfScornPvE.CanUse(out act, skipAoeCheck: true)) return true;
- if (SpiritsWithinPvE.CanUse(out act, skipAoeCheck: true)) return true;
-
- if (!IsMoving && IntervenePvE.CanUse(out act, skipAoeCheck: true, usedUp: HasFightOrFlight)) return true;
- }
-
- return base.AttackAbility(nextGCD, out act);
- }
- #endregion
-
- #region GCD Logic
- protected override bool GeneralGCD(out IAction? act)
- {
- act = null;
- if (PassageProtec && Player.HasStatus(true, StatusID.PassageOfArms)) return false;
-
- //Minimizes Accidents in EX and Savage Hopefully
- if (IsInHighEndDuty && !InCombat) { act = null; return false; }
-
- if (Player.HasStatus(true, StatusID.Requiescat))
- {
- if ((Player.Level >= 90) && (Player.StatusStack(true, StatusID.Requiescat) < 4))
- {
- if (!TargetIsDying && PrioritizeAtonementCombo && !CombatElapsedLess(30) && (Player.StatusTime(true, StatusID.FightOrFlight) > 12) && AtonementCombo(out act)) return true;
- if (ConfiteorPvE.CanUse(out act, skipAoeCheck: true)) return true;
- }
- if ((Player.Level >= 80) && (Player.StatusStack(true, StatusID.Requiescat) > 3))
- {
- if (!TargetIsDying && PrioritizeAtonementCombo && !CombatElapsedLess(30) && (Player.StatusTime(true, StatusID.FightOrFlight) > 12) && AtonementCombo(out act)) return true;
- if (ConfiteorPvE.CanUse(out act, skipAoeCheck: true)) return true;
- }
- if (HolyCirclePvE.CanUse(out act)) return true;
- if (HolySpiritPvE.CanUse(out act)) return true;
- }
-
- //AOE
- if (HasDivineMight && HolyCirclePvE.CanUse(out act)) return true;
- if (ProminencePvE.CanUse(out act)) return true;
- if (TotalEclipsePvE.CanUse(out act)) return true;
-
- //Single
- if (UseShieldBash && ShieldBashPvE.CanUse(out act)) return true;
-
- if (Player.HasStatus(true, StatusID.FightOrFlight) && AtonementCombo(out act)) return true;
- if (TargetIsDying && AtonementCombo(out act)) return true;
-
- //Helps ensure Atonement combo is ready for FoF in cases of unfortunate downtime
- if (((!HasAtonementReady && (HasSepulchreReady || HasSupplicationReady || HasDivineMight)) ||
- (HasAtonementReady && !HasDivineMight)) &&
- !Player.HasStatus(true, StatusID.Medicated) && !RageOfHalonePvE.CanUse(out act, skipComboCheck: false))
- {
- if (RiotBladePvE.CanUse(out act) || FastBladePvE.CanUse(out act)) return true;
- }
-
- if (!(HasSupplicationReady || HasSepulchreReady || HasDivineMight || HasAtonementReady) && RageOfHalonePvE.CanUse(out act)) return true;
-
- if (AtonementCombo(out act)) return true;
-
- if (RiotBladePvE.CanUse(out act) || FastBladePvE.CanUse(out act)) return true;
-
- //Range
- if (UseHolyWhenAway && Player.CurrentMp > 3000)
- {
- if (HolyCirclePvE.CanUse(out act) || HolySpiritPvE.CanUse(out act))
- return true;
- }
-
- if (UseShieldLob && ShieldLobPvE.CanUse(out act)) return true;
-
- return base.GeneralGCD(out act);
- }
-
- [RotationDesc(ActionID.ClemencyPvE)]
- protected override bool HealSingleGCD(out IAction? act)
- {
- act = null;
- if (PassageProtec && Player.HasStatus(true, StatusID.PassageOfArms)) return false;
- if (ClemencyPvE.CanUse(out act)) return true;
- return base.HealSingleGCD(out act);
- }
- #endregion
-
- #region Extra Methods
-
- private bool AtonementCombo(out IAction? act) => HolySpiritFirst(out act) || GoringBladePvE.CanUse(out act) || AtonementPvE.CanUse(out act) || SupplicationPvE.CanUse(out act) || SepulchrePvE.CanUse(out act) || HasDivineMight && HolyCirclePvE.CanUse(out act) || HasDivineMight && HolySpiritPvE.CanUse(out act);
-
- private bool UseOath(out IAction? act)
- {
- act = null;
- if ((InterventionPvE.Target.Target?.GetHealthRatio() <= InterventionRatio) && InterventionPvE.CanUse(out act)) return true;
- if (SheltronPvE.CanUse(out act)) return true;
- return false;
- }
- #endregion
-}
diff --git a/RotationSolver/Commands/RSCommands_Actions.cs b/RotationSolver/Commands/RSCommands_Actions.cs
index af677013b..52dd1b70a 100644
--- a/RotationSolver/Commands/RSCommands_Actions.cs
+++ b/RotationSolver/Commands/RSCommands_Actions.cs
@@ -190,7 +190,7 @@ internal static void UpdateRotationState()
(DataCenter.RightSet.SwitchCancelConditionSet?.IsTrue(DataCenter.RightNowRotation) ?? false))
{
CancelState();
- if (Player.Job != _previousJob) _previousJob = Player.Job;
+ if (Player.Job != null && Player.Job != _previousJob) _previousJob = Player.Job;
if (ActionUpdater.AutoCancelTime != DateTime.MinValue) ActionUpdater.AutoCancelTime = DateTime.MinValue;
}
else if ((Service.Config.AutoOnPvPMatchStart && Svc.Condition[ConditionFlag.BetweenAreas] &&
diff --git a/RotationSolver/UI/HighlightTeachingMode/DrawingHighlightHotbar.cs b/RotationSolver/UI/HighlightTeachingMode/DrawingHighlightHotbar.cs
index 394c2ba7e..26237b601 100644
--- a/RotationSolver/UI/HighlightTeachingMode/DrawingHighlightHotbar.cs
+++ b/RotationSolver/UI/HighlightTeachingMode/DrawingHighlightHotbar.cs
@@ -58,7 +58,10 @@ private protected override unsafe IEnumerable To2D()
var result = new List();
var hotBarIndex = 0;
- foreach (var intPtr in GetAddons())
+ foreach (var intPtr in GetAddons()
+ .Union(GetAddons())
+ .Union(GetAddons())
+ .Union(GetAddons()))
{
var actionBar = (AddonActionBarBase*)intPtr;
if (actionBar != null && IsVisible(actionBar->AtkUnitBase))
@@ -139,16 +142,9 @@ private static unsafe IEnumerable GetAddons() where T : struct
{
if (typeof(T).GetCustomAttribute() is not AddonAttribute on) return Array.Empty();
- var result = new List();
- foreach (var str in on.AddonIdentifiers)
- {
- var ptr = Svc.GameGui.GetAddonByName(str, 1);
- if (ptr != nint.Zero)
- {
- result.Add(ptr);
- }
- }
- return result;
+ return on.AddonIdentifiers
+ .Select(str => Svc.GameGui.GetAddonByName(str, 1))
+ .Where(ptr => ptr != nint.Zero);
}
private static unsafe bool IsVisible(AtkUnitBase unit)
@@ -243,6 +239,12 @@ public interface IDrawing2D
void Draw();
}
+/// Drawing the image.
+///
+///
+///
+///
+///
public readonly struct ImageDrawing(IDalamudTextureWrap texture, Vector2 pt1, Vector2 pt2, uint col = uint.MaxValue) : IDrawing2D
{
///
@@ -260,16 +262,10 @@ public ImageDrawing(IDalamudTextureWrap texture, Vector2 pt1, Vector2 pt2,
_uv2 = uv2;
}
- public unsafe void Draw()
+ /// Draw on the
+ public void Draw()
{
- if (_texture == null)
- return;
-
- var drawList = ImGui.GetWindowDrawList();
- if (drawList.NativePtr == null)
- return;
-
- drawList.AddImage(_texture.ImGuiHandle, _pt1, _pt2, _uv1, _uv2, _col);
+ ImGui.GetWindowDrawList().AddImage(_texture.ImGuiHandle, _pt1, _pt2, _uv1, _uv2, _col);
}
private readonly uint _col = col;
diff --git a/RotationSolver/UI/ImGuiHelper.cs b/RotationSolver/UI/ImGuiHelper.cs
index 255b97cb6..072fefa07 100644
--- a/RotationSolver/UI/ImGuiHelper.cs
+++ b/RotationSolver/UI/ImGuiHelper.cs
@@ -470,7 +470,7 @@ private static void CopyCommand(string command)
Notify.Success($"\"{command}\" copied to clipboard.");
}
- private static readonly Dictionary _lastChecked = new();
+ private static readonly SortedList _lastChecked = [];
private static void ExecuteHotKeys(Action action, params VirtualKey[] keys)
{
if (action == null) return;
@@ -479,15 +479,7 @@ private static void ExecuteHotKeys(Action action, params VirtualKey[] keys)
var name = string.Join(' ', keys);
if (!_lastChecked.TryGetValue(name, out var last)) last = false;
- var now = true;
- foreach (var key in keys)
- {
- if (!Svc.KeyState[key])
- {
- now = false;
- break;
- }
- }
+ var now = keys.All(k => Svc.KeyState[k]);
_lastChecked[name] = now;
if (!last && now) action();
From cf2f4cbbb1349ba858c57a6836eb55e9a9a56a9f Mon Sep 17 00:00:00 2001
From: LTS-FFXIV <127939494+LTS-FFXIV@users.noreply.github.com>
Date: Mon, 13 Jan 2025 17:07:22 -0600
Subject: [PATCH 2/2] Fix for cover usage and more granular configs
---
BasicRotations/Tank/PLD_Default.cs | 7 +++++--
1 file changed, 5 insertions(+), 2 deletions(-)
diff --git a/BasicRotations/Tank/PLD_Default.cs b/BasicRotations/Tank/PLD_Default.cs
index fb739a77d..5d9322319 100644
--- a/BasicRotations/Tank/PLD_Default.cs
+++ b/BasicRotations/Tank/PLD_Default.cs
@@ -32,6 +32,9 @@ public sealed class PLD_Default : PaladinRotation
[RotationConfig(CombatType.PvE, Name = "Use Holy Spirit when out of melee range")]
private bool UseHolyWhenAway { get; set; } = false;
+ [RotationConfig(CombatType.PvE, Name = "Use Clemency with Requiescat")]
+ private bool RequiescatHealBot { get; set; } = true;
+
[Range(0, 1, ConfigUnitType.Percent)]
[RotationConfig(CombatType.PvE, Name = "Minimum HP threshold party member needs to be to use Clemency with Requiescat")]
public float ClemencyRequi { get; set; } = 0.2f;
@@ -186,8 +189,8 @@ protected override bool HealSingleGCD(out IAction? act)
{
act = null;
if (PassageProtec && Player.HasStatus(true, StatusID.PassageOfArms)) return false;
- if (ClemencyPvE.Target.Target?.GetHealthRatio() < ClemencyRequi && RequiescatStacks > 0 && ClemencyPvE.CanUse(out act, skipCastingCheck: true)) return true;
- if (HealBot && ClemencyPvE.Target.Target?.GetHealthRatio() < ClemencyNoRequi && ClemencyPvE.CanUse(out act)) return true;
+ if (RequiescatHealBot && RequiescatStacks > 0 && ClemencyPvE.CanUse(out act, skipCastingCheck: true) && ClemencyPvE.Target.Target?.GetHealthRatio() < ClemencyRequi) return true;
+ if (HealBot && ClemencyPvE.CanUse(out act) && ClemencyPvE.Target.Target?.GetHealthRatio() < ClemencyNoRequi) return true;
return base.HealSingleGCD(out act);
}