Skip to content

Commit

Permalink
Enhance Sage and Paladin rotations with new logic
Browse files Browse the repository at this point in the history
Updated SGE_Default to manage Eukrasia actions with new methods
(ChoiceEukrasia, DoEukrasia) and streamlined existing logic. Added
DisplayStatus method for status information.

Enhanced PLD_Beta with new Clemency configurations, adjusted
GeneralAbility and AttackAbility methods, and improved HealSingleGCD
logic. Updated AoE and single-target logic for Holy Circle and Holy
Spirit actions.

Modified PaladinRotation to prevent Clemency targeting units with
tank stance status.

Minor update to SageRotation: removed AddersgallTimerRaw display
and streamlined ModifyEukrasianDiagnosisPvE method.
  • Loading branch information
LTS-FFXIV committed Jan 5, 2025
1 parent c70dda4 commit 392e094
Show file tree
Hide file tree
Showing 4 changed files with 137 additions and 135 deletions.
215 changes: 94 additions & 121 deletions BasicRotations/Healer/SGE_Default.cs
Original file line number Diff line number Diff line change
Expand Up @@ -171,7 +171,6 @@ protected override bool HealSingleAbility(IAction nextGCD, out IAction? act)

if (SoteriaPvE.CanUse(out act) && PartyMembers.Any(b => b.HasStatus(true, StatusID.Kardion) && b.GetHealthRatio() < SoteriaHeal)) return true;


var tank = PartyMembers.GetJobCategory(JobRole.Tank);
if (Addersgall < 1 && (tank.Any(t => t.GetHealthRatio() < OGCDTankHeal) || PartyMembers.Any(b => b.GetHealthRatio() < OGCDHeal)))
{
Expand Down Expand Up @@ -201,7 +200,6 @@ protected override bool HealSingleAbility(IAction nextGCD, out IAction? act)
}

[RotationDesc(ActionID.EukrasianPrognosisPvE, ActionID.EukrasianPrognosisIiPvE)]

protected override bool GeneralAbility(IAction nextGCD, out IAction? act)
{
// If not in combat and lacking the Kardia status, attempt to use KardiaPvE
Expand All @@ -219,69 +217,113 @@ protected override bool GeneralAbility(IAction nextGCD, out IAction? act)
}
#endregion

#region GCD Logic
protected override bool DefenseAreaGCD(out IAction? act)
#region Eukrasia Logic
private IBaseAction? _EukrasiaActionAim = null;

// Sets the target Eukrasia action to be performed next.
// If the action is null, it exits early.
// If the current action aim is not null and the last action matches certain conditions, it exits early.
// Finally, updates the current Eukrasia action aim if it's different from the incoming action.
private void SetEukrasia(IBaseAction act)
{
if (act == null) return;

if (_EukrasiaActionAim != null && IsLastGCD(false, _EukrasiaActionAim)) return;

if (_EukrasiaActionAim != act)
{
_EukrasiaActionAim = act;
}
}

// Clears the Eukrasia action aim, effectively resetting any planned Eukrasia action.
private void ClearEukrasia()
{
if (_EukrasiaActionAim != null)
{
_EukrasiaActionAim = null;
}
}

private bool ChoiceEukrasia(out IAction? act)
{
act = null;

if (HasSwift && SwiftLogic && EgeiroPvE.CanUse(out _)) return false;
if (!EukrasiaPvE.CanUse(out _)) return false;
// Checks for Eukrasia status.
// Attempts to set correct Eurkrasia action based on availablity and MergedStatus.
if (EukrasianPrognosisIiPvE.CanUse(out _) && EukrasianPrognosisIiPvE.EnoughLevel && MergedStatus.HasFlag(AutoStatus.DefenseArea))
{
SetEukrasia(EukrasianPrognosisIiPvE);
return false;
}

if (EukrasianPrognosisIiPvE.CanUse(out act))
if (EukrasianPrognosisPvE.CanUse(out _) && EukrasianPrognosisPvE.EnoughLevel && MergedStatus.HasFlag(AutoStatus.DefenseArea))
{
if (EukrasianPrognosisIiPvE.Target.Target?.HasStatus(false,
StatusID.EukrasianDiagnosis,
StatusID.EukrasianPrognosis,
StatusID.Galvanize
) ?? false) return false;
SetEukrasia(EukrasianPrognosisPvE);
return false;
}

if (EukrasiaPvE.CanUse(out act)) return true;
if (EukrasianDiagnosisPvE.CanUse(out _) && EukrasianDiagnosisPvE.EnoughLevel && MergedStatus.HasFlag(AutoStatus.DefenseSingle))
{
SetEukrasia(EukrasianDiagnosisPvE);
return false;
}

act = EukrasianPrognosisIiPvE;
return true;
if (EukrasianDyskrasiaPvE.CanUse(out _) && EukrasianDyskrasiaPvE.EnoughLevel && (!MergedStatus.HasFlag(AutoStatus.DefenseSingle) || !MergedStatus.HasFlag(AutoStatus.DefenseSingle)))
{
SetEukrasia(EukrasianDyskrasiaPvE);
return false;
}

if (!EukrasianPrognosisIiPvE.EnoughLevel && EukrasianPrognosisPvE.CanUse(out act))
if (EukrasianDosisIiiPvE.CanUse(out _) && EukrasianDosisIiiPvE.EnoughLevel && (!MergedStatus.HasFlag(AutoStatus.DefenseSingle) || !MergedStatus.HasFlag(AutoStatus.DefenseSingle)))
{
if (EukrasianPrognosisPvE.Target.Target?.HasStatus(false,
StatusID.EukrasianDiagnosis,
StatusID.EukrasianPrognosis,
StatusID.Galvanize
) ?? false) return false;
SetEukrasia(EukrasianDosisIiiPvE);
return false;
}

if (EukrasiaPvE.CanUse(out act)) return true;
if (EukrasianDosisIiPvE.CanUse(out _) && EukrasianDosisIiPvE.EnoughLevel && (!MergedStatus.HasFlag(AutoStatus.DefenseSingle) || !MergedStatus.HasFlag(AutoStatus.DefenseSingle)))
{
SetEukrasia(EukrasianDosisIiPvE);
return false;
}

act = EukrasianPrognosisPvE;
return true;
if (EukrasianDosisPvE.CanUse(out _) && EukrasianDosisPvE.EnoughLevel && (!MergedStatus.HasFlag(AutoStatus.DefenseSingle) || !MergedStatus.HasFlag(AutoStatus.DefenseSingle)))
{
SetEukrasia(EukrasianDosisPvE);
return false;
}

return base.DefenseAreaGCD(out act);
// If the last action performed matches any of a list of specific actions, it clears the Eukrasia aim.
// This serves as a reset/cleanup mechanism to ensure the decision logic starts fresh for the next cycle.
if (IsLastGCD(false, EukrasianPrognosisIiPvE, EukrasianPrognosisPvE,
EukrasianDiagnosisPvE, EukrasianDyskrasiaPvE, EukrasianDosisIiiPvE, EukrasianDosisIiPvE,
EukrasianDosisPvE) || !InCombat)
{
ClearEukrasia();
}
return false; // Indicates that no specific Eukrasia action was chosen in this cycle.
}
#endregion

[RotationDesc(ActionID.EukrasianDiagnosisPvE)]
protected override bool DefenseSingleGCD(out IAction? act)
#region Eukrasia Execution
// Attempts to perform a Eukrasia action, based on the current game state and conditions.
private bool DoEukrasia(out IAction? act)
{
act = null;

if (HasSwift && SwiftLogic && EgeiroPvE.CanUse(out _)) return false;

if (EukrasianDiagnosisPvE.CanUse(out act))
if (_EukrasiaActionAim != null && _EukrasiaActionAim.CanUse(out act))
{
if (EukrasianDiagnosisPvE.Target.Target?.HasStatus(true,
StatusID.EukrasianDiagnosis,
StatusID.DifferentialDiagnosis,
StatusID.EukrasianPrognosis,
StatusID.Galvanize
) ?? false) return false;

if (EukrasiaPvE.CanUse(out act)) return true;

act = EukrasianDiagnosisPvE;
act = _EukrasiaActionAim;
return true;
}

return base.DefenseSingleGCD(out act);
return false;
}
#endregion

#region GCD Logic
[RotationDesc(ActionID.PneumaPvE, ActionID.PrognosisPvE, ActionID.EukrasianPrognosisPvE, ActionID.EukrasianPrognosisIiPvE)]
protected override bool HealAreaGCD(out IAction? act)
{
Expand Down Expand Up @@ -339,49 +381,8 @@ protected override bool GeneralGCD(out IAction? act)

if (HasSwift && SwiftLogic && EgeiroPvE.CanUse(out _)) return false;

if (EukrasianDiagnosisPvE.CanUse(out act) && MergedStatus.HasFlag(AutoStatus.DefenseSingle))
{
if (EukrasianDiagnosisPvE.Target.Target?.HasStatus(true,
StatusID.EukrasianDiagnosis,
StatusID.DifferentialDiagnosis,
StatusID.EukrasianPrognosis,
StatusID.Galvanize
) ?? false) return false;

if (EukrasiaPvE.CanUse(out act)) return true;

act = EukrasianDiagnosisPvE;
return true;
}

if (!InCombat && !Player.HasStatus(true, StatusID.Eukrasia) && EukrasiaPvE.CanUse(out act)) return true;

if (HostileTarget?.IsBossFromTTK() ?? false)
{
if (EukrasianDyskrasiaPvE.CanUse(out _, skipCastingCheck: true))
{
if (EukrasiaPvE.CanUse(out act, skipCastingCheck: true)) return true;
if (EukrasianDyskrasiaPvE.CanUse(out act))
{
DosisPvE.Target = EukrasianDyskrasiaPvE.Target;
return true;
}
}
}

if (HostileTarget?.IsBossFromTTK() ?? false)
{
if (EukrasianDosisPvE.CanUse(out _, skipCastingCheck: true))
{
if (EukrasiaPvE.CanUse(out act, skipCastingCheck: true)) return true;
if (DosisPvE.CanUse(out act))
{
DosisPvE.Target = EukrasianDosisPvE.Target;
return true;
}
}
}

if (PhlegmaIiiPvE.CanUse(out act, usedUp: IsMoving)) return true;
if (PhlegmaIiPvE.CanUse(out act, usedUp: IsMoving)) return true;
if (PhlegmaPvE.CanUse(out act, usedUp: IsMoving)) return true;
Expand All @@ -393,49 +394,12 @@ protected override bool GeneralGCD(out IAction? act)

if (IsMoving && ToxikonPvE.CanUse(out act)) return true;

if (EukrasianDyskrasiaPvE.CanUse(out _, skipCastingCheck: true))
{
if (EukrasiaPvE.CanUse(out act, skipCastingCheck: true)) return true;
if (DyskrasiaPvE.CanUse(out act))
{
DyskrasiaPvE.Target = EukrasianDyskrasiaPvE.Target;
return true;
}
}
if (ChoiceEukrasia(out act)) return true;
if (DoEukrasia(out act)) return true;

if (DyskrasiaPvE.CanUse(out _))
{
if (EukrasianDyskrasiaPvE.EnoughLevel && (EukrasianDyskrasiaPvE.Target.Target?.WillStatusEnd(3, true, EukrasianDyskrasiaPvE.Setting.TargetStatusProvide ?? []) ?? false) && InCombat && (!MergedStatus.HasFlag(AutoStatus.DefenseArea) || !MergedStatus.HasFlag(AutoStatus.DefenseSingle)))
{
StatusHelper.StatusOff(StatusID.Eukrasia);
}
if (DyskrasiaPvE.CanUse(out act))
{
return true;
}
}
if (DyskrasiaPvE.CanUse(out act)) return true;

if (EukrasianDosisPvE.CanUse(out _, skipCastingCheck: true))
{
if (EukrasiaPvE.CanUse(out act, skipCastingCheck: true)) return true;
if (DosisPvE.CanUse(out act))
{
DosisPvE.Target = EukrasianDosisPvE.Target;
return true;
}
}

if (DosisPvE.CanUse(out _))
{
if ((EukrasianDosisPvE.Target.Target?.WillStatusEnd(3, true, EukrasianDosisPvE.Setting.TargetStatusProvide ?? []) ?? false) && InCombat && (!MergedStatus.HasFlag(AutoStatus.DefenseArea) || !MergedStatus.HasFlag(AutoStatus.DefenseSingle)))
{
StatusHelper.StatusOff(StatusID.Eukrasia);
}
if (DosisPvE.CanUse(out act))
{
return true;
}
}
if (DosisPvE.CanUse(out act)) return true;

return base.GeneralGCD(out act);
}
Expand All @@ -444,5 +408,14 @@ protected override bool GeneralGCD(out IAction? act)
#region Extra Methods
public override bool CanHealSingleSpell => base.CanHealSingleSpell && (GCDHeal || PartyMembers.GetJobCategory(JobRole.Healer).Count() < 2);
public override bool CanHealAreaSpell => base.CanHealAreaSpell && (GCDHeal || PartyMembers.GetJobCategory(JobRole.Healer).Count() < 2);

public override void DisplayStatus()
{
ImGui.Text($"Eukrasian Action: {_EukrasiaActionAim}");
ImGui.Text("HasEukrasia: " + HasEukrasia.ToString());
ImGui.Text("Addersgall: " + Addersgall.ToString());
ImGui.Text("Addersting: " + Addersting.ToString());
ImGui.Text("AddersgallTime: " + AddersgallTime.ToString());
}
#endregion
}
49 changes: 37 additions & 12 deletions BasicRotations/Tank/PLD_Beta.cs
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,17 @@ public sealed class PLD_Beta : PaladinRotation

[RotationConfig(CombatType.PvE, Name = "Use Holy Spirit when out of melee range")]
private bool UseHolyWhenAway { get; set; } = false;

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

[RotationConfig(CombatType.PvE, Name = "Use Clemency without Requiescat")]
private bool HealBot { get; set; } = true;

[Range(0, 1, ConfigUnitType.Percent)]
[RotationConfig(CombatType.PvE, Name = "Minimum HP threshold party member needs to be to use Clemency without Requiescat")]
public float ClemencyNoRequi { get; set; } = 0.4f;
#endregion

private const ActionID ConfiteorPvEActionId = (ActionID)16459;
Expand Down Expand Up @@ -128,7 +139,7 @@ protected override bool DefenseSingleAbility(IAction nextGCD, out IAction? act)

#region oGCD Logic

[RotationDesc(ActionID.FightOrFlightPvE)]
[RotationDesc(ActionID.SheltronPvE, ActionID.HolySheltronPvE)]
protected override bool GeneralAbility(IAction nextGCD, out IAction? act)
{
act = null;
Expand All @@ -137,22 +148,35 @@ protected override bool GeneralAbility(IAction nextGCD, out IAction? act)
if (InCombat && OathGauge >= WhenToSheltron && WhenToSheltron > 0 && UseOath(out act)) return true;
return base.GeneralAbility(nextGCD, out act);
}
[RotationDesc(ActionID.SpiritsWithinPvE)]

[RotationDesc(ActionID.IntervenePvE, ActionID.SpiritsWithinPvE, ActionID.ExpiacionPvE, ActionID.CircleOfScornPvE, ActionID.RequiescatPvE, ActionID.ImperatorPvE, ActionID.FightOrFlightPvE)]
protected override bool AttackAbility(IAction nextGCD, out IAction? act)
{
act = null;
if (PassageProtec && Player.HasStatus(true, StatusID.PassageOfArms)) return false;

if (BladeOfHonorPvE.CanUse(out act, skipAoeCheck: true)) return true;

if (!RiotBladePvE.EnoughLevel && FightOrFlightPvE.CanUse(out act)) return true;
if (!RageOfHalonePvE.EnoughLevel && nextGCD.IsTheSameTo(true, RiotBladePvE) && FightOrFlightPvE.CanUse(out act)) return true;
if (!SwordOathTrait.EnoughLevel && nextGCD.IsTheSameTo(true, RoyalAuthorityPvE) && FightOrFlightPvE.CanUse(out act)) return true;
if (SwordOathTrait.EnoughLevel && Player.HasStatus(true, StatusID.AtonementReady) && FightOrFlightPvE.CanUse(out act)) return true;
if (!RiotBladePvE.EnoughLevel && nextGCD.IsTheSameTo(true, FastBladePvE) && FightOrFlightPvE.CanUse(out act)) return true;
if (!RageOfHalonePvE.EnoughLevel && nextGCD.IsTheSameTo(true, RiotBladePvE, TotalEclipsePvE) && FightOrFlightPvE.CanUse(out act)) return true;
if (!ProminencePvE.EnoughLevel && nextGCD.IsTheSameTo(true, RageOfHalonePvE, TotalEclipsePvE) && FightOrFlightPvE.CanUse(out act)) return true;
if (!AtonementPvE.EnoughLevel && nextGCD.IsTheSameTo(true, RoyalAuthorityPvE, ProminencePvE) && FightOrFlightPvE.CanUse(out act)) return true;
if (AtonementPvE.EnoughLevel && (Player.HasStatus(true, StatusID.AtonementReady) || nextGCD.IsTheSameTo(true, ProminencePvE)) && FightOrFlightPvE.CanUse(out act)) return true;

// if requiscat is able to proc confiteor, use it immediately after Fight or Flight
if (RequiescatMasteryTrait.EnoughLevel)
{
if (IsLastAbility(true, FightOrFlightPvE) && ImperatorPvE.CanUse(out act, skipAoeCheck: true, usedUp: true)) return true;
if (IsLastAbility(true, FightOrFlightPvE) && RequiescatPvE.CanUse(out act, skipAoeCheck: true, usedUp: true)) return true;
}

// if requiscat is not able to proc confiteor, use it as AOE tool if able, otherwise as Single Target
if (!RequiescatMasteryTrait.EnoughLevel)
{
if (HolyCirclePvE.EnoughLevel && NumberOfHostilesInRange > 1 && RequiescatPvE.CanUse(out act, skipAoeCheck: true, usedUp: true)) return true;
if (!HolyCirclePvE.EnoughLevel && (NumberOfHostilesInRange == 1 || (RequiescatPvE.Target.Target?.IsBossFromIcon() ?? false)) && RequiescatPvE.CanUse(out act, skipAoeCheck: true, usedUp: true)) return true;
}

if (IsLastAbility(true, FightOrFlightPvE) && ImperatorPvE.CanUse(out act, skipAoeCheck: true, usedUp: true)) return true;
if (IsLastAbility(true, FightOrFlightPvE) && RequiescatPvE.CanUse(out act, skipAoeCheck: true, usedUp: true)) return true;

if (FightOrFlightPvE.Cooldown.IsCoolingDown && CircleOfScornPvE.CanUse(out act, skipAoeCheck: true)) return true;
if (FightOrFlightPvE.Cooldown.IsCoolingDown && ExpiacionPvE.CanUse(out act, skipAoeCheck: true)) return true;
if (FightOrFlightPvE.Cooldown.IsCoolingDown && SpiritsWithinPvE.CanUse(out act)) return true;
Expand All @@ -168,7 +192,8 @@ 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;
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;
return base.HealSingleGCD(out act);
}

Expand Down Expand Up @@ -200,12 +225,12 @@ protected override bool GeneralGCD(out IAction? act)
if (AtonementPvE.CanUse(out act)) return true;

//AoE
if (Player.HasStatus(true, StatusID.DivineMight) && HolyCirclePvE.CanUse(out act)) return true;
if ((Player.HasStatus(true, StatusID.DivineMight) || RequiescatStacks > 0) && HolyCirclePvE.CanUse(out act, skipCastingCheck: true)) return true;
if (ProminencePvE.CanUse(out act)) return true;
if (TotalEclipsePvE.CanUse(out act)) return true;

//Single Target
if (Player.HasStatus(true, StatusID.DivineMight) && HolySpiritPvE.CanUse(out act)) return true;
if ((Player.HasStatus(true, StatusID.DivineMight) || RequiescatStacks > 0) && HolySpiritPvE.CanUse(out act, skipCastingCheck: true)) return true;

if (RoyalAuthorityPvE.CanUse(out act)) return true;
if (RageOfHalonePvE.CanUse(out act)) return true;
Expand Down
5 changes: 5 additions & 0 deletions RotationSolver.Basic/Rotations/Basic/PaladinRotation.cs
Original file line number Diff line number Diff line change
Expand Up @@ -227,6 +227,11 @@ static partial void ModifyDivineVeilPvE(ref ActionSetting setting)
static partial void ModifyClemencyPvE(ref ActionSetting setting)
{
setting.UnlockedByQuestID = 67572;
setting.CanTarget = t =>
{
if (t.HasStatus(false, StatusHelper.TankStanceStatus)) return false;
return true;
};
}

static partial void ModifyRoyalAuthorityPvE(ref ActionSetting setting)
Expand Down
Loading

0 comments on commit 392e094

Please sign in to comment.