Skip to content

Commit

Permalink
Merge pull request #566 from FFXIV-CombatReborn/beegSGE
Browse files Browse the repository at this point in the history
Refactor and enhance party and alliance member handling
  • Loading branch information
LTS-FFXIV authored Jan 13, 2025
2 parents 550c728 + 7453c6b commit 1156b97
Show file tree
Hide file tree
Showing 16 changed files with 288 additions and 128 deletions.
56 changes: 35 additions & 21 deletions BasicRotations/Healer/SGE_Default.cs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
using static FFXIVClientStructs.FFXIV.Client.UI.Misc.DataCenterHelper;

namespace RebornRotations.Healer;

[Rotation("Default", CombatType.PvE, GameVersion = "7.15")]
Expand Down Expand Up @@ -62,6 +64,10 @@ public sealed class SGE_Default : SageRotation

#endregion

#region Tracking Properties
private IBaseAction? _lastEukrasiaActionAim = null;
#endregion

#region Countdown Logic
protected override IAction? CountDownAction(float remainTime)
{
Expand All @@ -84,7 +90,24 @@ protected override bool AttackAbility(IAction nextGCD, out IAction? act)

protected override bool EmergencyAbility(IAction nextGCD, out IAction? act)
{
if (base.EmergencyAbility(nextGCD, out act)) return true;
if ((_EukrasiaActionAim == EukrasianDosisIiiPvE || _EukrasiaActionAim == EukrasianDosisIiPvE || _EukrasiaActionAim == EukrasianDosisPvE)
&& (DyskrasiaPvE.CanUse(out _) || DyskrasiaIiPvE.CanUse(out _) || EukrasianDyskrasiaPvE.CanUse(out _)))
{
ClearEukrasia();
}

// 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();
}

if (ChoiceEukrasia(out act)) return true;

//if (base.EmergencyAbility(nextGCD, out act)) return true;

if (nextGCD.IsTheSameTo(false, PneumaPvE, EukrasianPrognosisPvE, EukrasianPrognosisIiPvE))
{
Expand Down Expand Up @@ -230,7 +253,7 @@ private void SetEukrasia(IBaseAction act)
{
if (act == null) return;

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

if (_EukrasiaActionAim != act)
{
Expand All @@ -243,6 +266,7 @@ private void ClearEukrasia()
{
if (_EukrasiaActionAim != null)
{
_lastEukrasiaActionAim = _EukrasiaActionAim;
_EukrasiaActionAim = null;
}
}
Expand All @@ -251,16 +275,8 @@ private bool ChoiceEukrasia(out IAction? act)
{
act = null;

// 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(true, EukrasianPrognosisIiPvE, EukrasianPrognosisPvE,
EukrasianDiagnosisPvE, EukrasianDyskrasiaPvE, EukrasianDosisIiiPvE, EukrasianDosisIiPvE,
EukrasianDosisPvE) || !InCombat)
{
ClearEukrasia();
}

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))
Expand All @@ -281,25 +297,25 @@ private bool ChoiceEukrasia(out IAction? act)
return false;
}

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

if (EukrasianDosisIiiPvE.CanUse(out _) && EukrasianDosisIiiPvE.EnoughLevel && (!MergedStatus.HasFlag(AutoStatus.DefenseSingle) || !MergedStatus.HasFlag(AutoStatus.DefenseSingle)))
if ((!EukrasianDyskrasiaPvE.CanUse(out _) || !DyskrasiaPvE.CanUse(out _)) && EukrasianDosisIiiPvE.CanUse(out _) && EukrasianDosisIiiPvE.EnoughLevel && (!MergedStatus.HasFlag(AutoStatus.DefenseSingle) || !MergedStatus.HasFlag(AutoStatus.DefenseArea)))
{
SetEukrasia(EukrasianDosisIiiPvE);
return false;
}

if (EukrasianDosisIiPvE.CanUse(out _) && EukrasianDosisIiPvE.EnoughLevel && (!MergedStatus.HasFlag(AutoStatus.DefenseSingle) || !MergedStatus.HasFlag(AutoStatus.DefenseSingle)))
if ((!EukrasianDyskrasiaPvE.CanUse(out _) || !DyskrasiaPvE.CanUse(out _)) && EukrasianDosisIiPvE.CanUse(out _) && EukrasianDosisIiPvE.EnoughLevel && (!MergedStatus.HasFlag(AutoStatus.DefenseSingle) || !MergedStatus.HasFlag(AutoStatus.DefenseArea)))
{
SetEukrasia(EukrasianDosisIiPvE);
return false;
}

if (EukrasianDosisPvE.CanUse(out _) && EukrasianDosisPvE.EnoughLevel && (!MergedStatus.HasFlag(AutoStatus.DefenseSingle) || !MergedStatus.HasFlag(AutoStatus.DefenseSingle)))
if ((!EukrasianDyskrasiaPvE.CanUse(out _) || !DyskrasiaPvE.CanUse(out _)) && EukrasianDosisPvE.CanUse(out _) && EukrasianDosisPvE.EnoughLevel && (!MergedStatus.HasFlag(AutoStatus.DefenseSingle) || !MergedStatus.HasFlag(AutoStatus.DefenseArea)))
{
SetEukrasia(EukrasianDosisPvE);
return false;
Expand Down Expand Up @@ -364,11 +380,8 @@ protected override bool HealSingleGCD(out IAction? act)
protected override bool GeneralGCD(out IAction? act)
{
act = null;

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

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;

if (PartyMembers.Any(b => b.GetHealthRatio() < PneumaSTPartyHeal && !b.IsDead) || PartyMembers.GetJobCategory(JobRole.Tank).Any(t => t.GetHealthRatio() < PneumaSTTankHeal && !t.IsDead))
Expand All @@ -378,10 +391,10 @@ protected override bool GeneralGCD(out IAction? act)

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

if (ChoiceEukrasia(out act)) return true;
if (DoEukrasia(out act)) return true;
if ((_EukrasiaActionAim != EukrasianDiagnosisPvE || _EukrasiaActionAim != EukrasianPrognosisPvE || _EukrasiaActionAim != EukrasianPrognosisIiPvE || _EukrasiaActionAim != EukrasianDyskrasiaPvE)
&& DyskrasiaPvE.CanUse(out act)) return true;

if (DyskrasiaPvE.CanUse(out act)) return true;
if (DoEukrasia(out act)) return true;

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

Expand All @@ -398,6 +411,7 @@ protected override bool GeneralGCD(out IAction? act)
public override void DisplayStatus()
{
ImGui.Text($"Eukrasian Action: {_EukrasiaActionAim}");
ImGui.Text($"Last Eukrasian Action: {_lastEukrasiaActionAim}");
ImGui.Text("HasEukrasia: " + HasEukrasia.ToString());
ImGui.Text("Addersgall: " + Addersgall.ToString());
ImGui.Text("Addersting: " + Addersting.ToString());
Expand Down
8 changes: 1 addition & 7 deletions BasicRotations/Magical/RDM_Default.cs
Original file line number Diff line number Diff line change
Expand Up @@ -106,13 +106,7 @@ protected override bool AttackAbility(IAction nextGCD, out IAction? act)
ActionID.EnchantedMoulinetPvE,
ActionID.MoulinetPvE
//I dont know at this point if nextGCD.IsTheSameTo even working, but stil gonna left it in here.
}) && !nextGCD.IsTheSameTo(new[]
{
ActionID.RipostePvE,
ActionID.EnchantedRipostePvE,
ActionID.MoulinetPvE,
ActionID.EnchantedMoulinetPvE
});
}) && !nextGCD.IsTheSameTo(true, ActionID.RipostePvE, ActionID.EnchantedRipostePvE, ActionID.MoulinetPvE, ActionID.EnchantedMoulinetPvE);

//i really hate this.
bool ambatumelee = Player.HasStatus(true, StatusID.Manafication, StatusID.MagickedSwordplay);
Expand Down
3 changes: 2 additions & 1 deletion BasicRotations/Melee/NIN_Default.cs
Original file line number Diff line number Diff line change
Expand Up @@ -317,6 +317,8 @@ protected override bool EmergencyAbility(IAction nextGCD, out IAction? act)
// Initializes the action to null, indicating no action has been chosen yet.
act = null;

if ((InCombat || (CommbatMudra && HasHostilesInMaxRange)) && ChoiceNinjutsu(out act)) return true;

// If Ninjutsu is available or not in combat, defers to the base class's emergency ability logic.
if (!NoNinjutsu || !InCombat) return base.EmergencyAbility(nextGCD, out act);

Expand Down Expand Up @@ -413,7 +415,6 @@ protected override bool GeneralGCD(out IAction? act)
if (ForkedUse && ForkedRaijuPvE.CanUse(out act)) return true;
}

if ((InCombat || (CommbatMudra && HasHostilesInMaxRange)) && ChoiceNinjutsu(out act)) return true;
if (((!InCombat && CommbatMudra && HasHostilesInMaxRange) || !CombatElapsedLess(7)) && DoNinjutsu(out act)) return true;

//No Ninjutsu
Expand Down
2 changes: 1 addition & 1 deletion ECommons
10 changes: 10 additions & 0 deletions RotationSolver.Basic/Actions/ActionBasicInfo.cs
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,16 @@ public readonly struct ActionBasicInfo
/// </summary>
public readonly uint ID => _action.Action.RowId;

/// <summary>
/// The Range of the action.
/// </summary>
public readonly sbyte Range => _action.Action.Range;

/// <summary>
/// The EffectRange of the action.
/// </summary>
public readonly byte EffectRange => _action.Action.EffectRange;

/// <summary>
/// The icon of the action.
/// </summary>
Expand Down
2 changes: 1 addition & 1 deletion RotationSolver.Basic/Actions/ActionTargetInfo.cs
Original file line number Diff line number Diff line change
Expand Up @@ -467,7 +467,7 @@ private bool CheckTimeToKill(IGameObject gameObject)
{
if (DataCenter.Territory?.ContentType == TerritoryContentType.Trials ||
(DataCenter.Territory?.ContentType == TerritoryContentType.Raids &&
DataCenter.AllianceMembers.Count(p => p is IPlayerCharacter) == 8))
DataCenter.AllianceMembers.Count(p => p is IPlayerCharacter) >= 8))
{
var fallbackPoints = new[] { Vector3.Zero, new Vector3(100, 0, 100) };
var closestFallback = fallbackPoints.MinBy(p => Vector3.Distance(player.Position, p));
Expand Down
6 changes: 3 additions & 3 deletions RotationSolver.Basic/Configuration/Configs.cs
Original file line number Diff line number Diff line change
Expand Up @@ -466,13 +466,13 @@ public const string
Filter = HealingActionCondition, Section = 3)]
private static readonly bool _healOutOfCombat = false;

[ConditionBool, UI("Heal solo instance NPCs", Description = "Experimental.",
[ConditionBool, UI("Heal solo instance NPCs (Only enable as needed)", Description = "Experimental.",
Filter = HealingActionCondition, Section = 3)]
private static readonly bool _friendlyBattleNPCHeal = false;

[ConditionBool, UI("Heal and raise Party NPCs.", Description = "Experimental, only enable as needed.",
[ConditionBool, UI("Heal and raise Party NPCs.",
Filter = HealingActionCondition, Section = 3)]
private static readonly bool _friendlyPartyNPCHealRaise2 = false;
private static readonly bool _friendlyPartyNPCHealRaise2 = true;

[ConditionBool, UI("Heal/Dance partner your chocobo", Description = "Experimental.",
Filter = HealingActionCondition, Section = 3)]
Expand Down
14 changes: 10 additions & 4 deletions RotationSolver.Basic/Data/RaiseType.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,14 +12,20 @@ public enum RaiseType : byte
PartyOnly,

/// <summary>
/// Raise party and alliance members.
/// Raise party members and alliance supports.
/// </summary>
[Description("Raise all.")]
PartyAndAlliance,
[Description("Raise party members and alliance supports.")]
PartyAndAllianceSupports,

/// <summary>
/// Raise party members and alliance healers.
/// </summary>
[Description("Raise party members and non-party healers.")]
[Description("Raise party members and alliance healers.")]
PartyAndAllianceHealers,

/// <summary>
/// Raise all.
/// </summary>
[Description("Raise All.")]
All,
}
16 changes: 9 additions & 7 deletions RotationSolver.Basic/DataCenter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -804,7 +804,10 @@ internal static void AddDamageRec(float damageRatio)

public static bool IsCastingTankVfx()
{
return IsCastingVfx(s =>
// Create a copy of the VfxDataQueue to avoid modification during enumeration
var vfxDataQueueCopy = VfxDataQueue.ToList();

return IsCastingVfx(vfxDataQueueCopy, s =>
{
if (!s.Path.StartsWith("vfx/lockon/eff/tank_lockon")) return false;
if (!Player.Available) return false;
Expand All @@ -816,20 +819,19 @@ public static bool IsCastingTankVfx()

public static bool IsCastingAreaVfx()
{
return IsCastingVfx(s => s.Path.StartsWith("vfx/lockon/eff/coshare"));
var vfxDataQueueCopy = VfxDataQueue.ToArray();
return IsCastingVfx([.. vfxDataQueueCopy], s => s.Path.StartsWith("vfx/lockon/eff/coshare"));
}

public static bool IsCastingVfx(Func<VfxNewData, bool> isVfx)
public static bool IsCastingVfx(List<VfxNewData> vfxDataQueueCopy, Func<VfxNewData, bool> isVfx)
{
// Ensure the list is not empty
if (VfxDataQueue.Count == 0)
if (vfxDataQueueCopy.Count == 0)
{
return false;
}

// Create a copy of the VfxDataQueue to avoid modification during enumeration
var vfxDataQueueCopy = VfxDataQueue.ToList();

// Iterate over the copied list
foreach (var vfx in vfxDataQueueCopy)
{
if (isVfx(vfx))
Expand Down
7 changes: 4 additions & 3 deletions RotationSolver.Basic/Helpers/IActionHelper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -112,18 +112,19 @@ public static bool IsLastActionAbility()
/// <returns>True if the action is the same as any of the provided actions, otherwise false.</returns>
public static bool IsTheSameTo(this IAction action, bool isAdjust, params IAction[] actions)
{
return actions != null && action.IsTheSameTo(GetIDFromActions(isAdjust, actions));
return actions != null && action.IsTheSameTo(isAdjust, GetIDFromActions(isAdjust, actions));
}

/// <summary>
/// Determines if the action is the same as any of the provided action IDs.
/// </summary>
/// <param name="action">The action to check.</param>
/// <param name="isAdjust">Whether to use the adjusted ID.</param>
/// <param name="actions">The action IDs to check against.</param>
/// <returns>True if the action is the same as any of the provided action IDs, otherwise false.</returns>
public static bool IsTheSameTo(this IAction action, params ActionID[] actions)
public static bool IsTheSameTo(this IAction action, bool isAdjust, params ActionID[] actions)
{
return action != null && actions != null && IsActionID((ActionID)action.AdjustedID, actions);
return action != null && actions != null && IsActionID(isAdjust ? (ActionID)action.AdjustedID : (ActionID)action.ID, actions);
}

/// <summary>
Expand Down
Loading

0 comments on commit 1156b97

Please sign in to comment.