From 072607182dd0f840e69ba29fe49d88ea6e07ea7e Mon Sep 17 00:00:00 2001 From: AceAkechi123 Date: Sun, 19 Jan 2025 05:23:51 -0800 Subject: [PATCH 01/12] less spaghetti --- BossMod/Autorotation/akechi/AkechiBLM.cs | 1247 +++++++++------------- 1 file changed, 522 insertions(+), 725 deletions(-) diff --git a/BossMod/Autorotation/akechi/AkechiBLM.cs b/BossMod/Autorotation/akechi/AkechiBLM.cs index d677562de..dc0449f05 100644 --- a/BossMod/Autorotation/akechi/AkechiBLM.cs +++ b/BossMod/Autorotation/akechi/AkechiBLM.cs @@ -130,13 +130,13 @@ public static RotationModuleDefinition Definition() "Standard Rotation Module", //Description "Standard rotation (Akechi)", //Category "Akechi", //Contributor - RotationModuleQuality.Basic, //Quality + RotationModuleQuality.Ok, //Quality BitMask.Build(Class.THM, Class.BLM), //Job 100); //Level supported #region Custom strategies res.Define(Track.AOE).As("AOE", "AOE", uiPriority: 200) - .AddOption(AOEStrategy.Auto, "Auto", "Automatically decide when to use ST or AOE abilities") + .AddOption(AOEStrategy.Auto, "Auto", "Automatically decide when to use ST or AOE abilities", supportedTargets: ActionTargets.Hostile) .AddOption(AOEStrategy.ForceST, "Force ST", "Force use of ST abilities only", supportedTargets: ActionTargets.Hostile) .AddOption(AOEStrategy.ForceAOE, "Force AOE", "Force use of AOE abilities only", supportedTargets: ActionTargets.Hostile); res.Define(Track.Movement).As("Movement", uiPriority: 195) @@ -146,11 +146,11 @@ public static RotationModuleDefinition Definition() .AddOption(MovementStrategy.OnlyScathe, "OnlyScathe", "Only use Scathe for movement") .AddOption(MovementStrategy.Forbid, "Forbid", "Forbid the use of any abilities for movement"); res.Define(Track.Thunder).As("Thunder", "DOT", uiPriority: 190) - .AddOption(ThunderStrategy.Thunder3, "Thunder3", "Use Thunder if target has 3s or less remaining on DoT effect", 0, 30, ActionTargets.Hostile, 6) - .AddOption(ThunderStrategy.Thunder6, "Thunder6", "Use Thunder if target has 6s or less remaining on DoT effect", 0, 30, ActionTargets.Hostile, 6) - .AddOption(ThunderStrategy.Thunder9, "Thunder9", "Use Thunder if target has 9s or less remaining on DoT effect", 0, 30, ActionTargets.Hostile, 6) - .AddOption(ThunderStrategy.Thunder0, "Thunder0", "Use Thunder if target does not have DoT effect", 0, 30, ActionTargets.Hostile, 6) - .AddOption(ThunderStrategy.Force, "Force", "Force use of Thunder regardless of DoT effect", 0, 30, ActionTargets.Hostile, 6) + .AddOption(ThunderStrategy.Thunder3, "Thunder3", "Use Thunder if target has 3s or less remaining on DoT effect", 0, 27, ActionTargets.Hostile, 6) + .AddOption(ThunderStrategy.Thunder6, "Thunder6", "Use Thunder if target has 6s or less remaining on DoT effect", 0, 27, ActionTargets.Hostile, 6) + .AddOption(ThunderStrategy.Thunder9, "Thunder9", "Use Thunder if target has 9s or less remaining on DoT effect", 0, 27, ActionTargets.Hostile, 6) + .AddOption(ThunderStrategy.Thunder0, "Thunder0", "Use Thunder if target does not have DoT effect", 0, 27, ActionTargets.Hostile, 6) + .AddOption(ThunderStrategy.Force, "Force", "Force use of Thunder regardless of DoT effect", 0, 27, ActionTargets.Hostile, 6) .AddOption(ThunderStrategy.Delay, "Delay", "Delay the use of Thunder for manual or strategic usage", 0, 0, ActionTargets.Hostile, 6) .AddAssociatedActions(AID.Thunder1, AID.Thunder2, AID.Thunder3, AID.Thunder4, AID.HighThunder, AID.HighThunder2); res.Define(Track.Polyglot).As("Polyglot", "Polyglot", uiPriority: 180) @@ -161,11 +161,11 @@ public static RotationModuleDefinition Definition() .AddOption(PolyglotStrategy.XenoSpendAll, "XenoSpendAll", "Use Xenoglossy as optimal spender, regardless of targets nearby; spends all Polyglots", 0, 0, ActionTargets.Hostile, 80) .AddOption(PolyglotStrategy.XenoHold1, "XenoHold1", "Use Xenoglossy as optimal spender, regardless of targets nearby; holds one Polyglot for manual usage", 0, 0, ActionTargets.Hostile, 80) .AddOption(PolyglotStrategy.XenoHold2, "XenoHold2", "Use Xenoglossy as optimal spender, regardless of targets nearby; holds two Polyglots for manual usage", 0, 0, ActionTargets.Hostile, 80) - .AddOption(PolyglotStrategy.XenoHold3, "XenoHold3", "Holds all Polyglots for as long as possible", 0, 0, ActionTargets.Hostile, 80) + .AddOption(PolyglotStrategy.XenoHold3, "XenoHold3", "Use Xenoglossy as optimal spender; Holds all Polyglots for as long as possible", 0, 0, ActionTargets.Hostile, 80) .AddOption(PolyglotStrategy.FoulSpendAll, "FoulSpendAll", "Use Foul as optimal spender, regardless of targets nearby", 0, 0, ActionTargets.Hostile, 70) .AddOption(PolyglotStrategy.FoulHold1, "FoulHold1", "Use Foul as optimal spender, regardless of targets nearby; holds one Polyglot for manual usage", 0, 0, ActionTargets.Hostile, 70) .AddOption(PolyglotStrategy.FoulHold2, "FoulHold2", "Use Foul as optimal spender, regardless of targets nearby; holds two Polyglots for manual usage", 0, 0, ActionTargets.Hostile, 70) - .AddOption(PolyglotStrategy.FoulHold3, "FoulHold3", "Holds all Polyglots for as long as possible", 0, 0, ActionTargets.Hostile, 70) + .AddOption(PolyglotStrategy.FoulHold3, "FoulHold3", "Use Foul as optimal spender; Holds all Polyglots for as long as possible", 0, 0, ActionTargets.Hostile, 70) .AddOption(PolyglotStrategy.ForceXeno, "Force Xenoglossy", "Force use of Xenoglossy", 0, 0, ActionTargets.Hostile, 80) .AddOption(PolyglotStrategy.ForceFoul, "Force Foul", "Force use of Foul", 0, 0, ActionTargets.Hostile, 70) .AddOption(PolyglotStrategy.Delay, "Delay", "Delay the use of Polyglot abilities for manual or strategic usage", 0, 0, ActionTargets.Hostile, 70) @@ -180,19 +180,19 @@ public static RotationModuleDefinition Definition() .AddAssociatedActions(AID.Manafont); res.Define(Track.Triplecast).As("T.cast", uiPriority: 170) .AddOption(TriplecastStrategy.Automatic, "Auto", "Use any charges available during Ley Lines window or every 2 minutes (NOTE: does not take into account charge overcap, will wait for 2 minute windows to spend both)", 0, 0, ActionTargets.Self, 66) - .AddOption(TriplecastStrategy.Force, "Force", "Force the use of Triplecast; uses all charges", 60, 0, ActionTargets.Self, 66) - .AddOption(TriplecastStrategy.Force1, "Force1", "Force the use of Triplecast; holds one charge for manual usage", 60, 0, ActionTargets.Self, 66) - .AddOption(TriplecastStrategy.ForceWeave, "ForceWeave", "Force the use of Triplecast in any next possible weave slot", 60, 0, ActionTargets.Self, 66) - .AddOption(TriplecastStrategy.ForceWeave1, "ForceWeave1", "Force the use of Triplecast in any next possible weave slot; holds one charge for manual usage", 60, 0, ActionTargets.Self, 66) - .AddOption(TriplecastStrategy.Delay, "Delay", "Delay the use of Triplecast", 60, 0, ActionTargets.Self, 66) + .AddOption(TriplecastStrategy.Force, "Force", "Force the use of Triplecast; uses all charges", 60, 15, ActionTargets.Self, 66) + .AddOption(TriplecastStrategy.Force1, "Force1", "Force the use of Triplecast; holds one charge for manual usage", 60, 15, ActionTargets.Self, 66) + .AddOption(TriplecastStrategy.ForceWeave, "ForceWeave", "Force the use of Triplecast in any next possible weave slot", 60, 15, ActionTargets.Self, 66) + .AddOption(TriplecastStrategy.ForceWeave1, "ForceWeave1", "Force the use of Triplecast in any next possible weave slot; holds one charge for manual usage", 60, 15, ActionTargets.Self, 66) + .AddOption(TriplecastStrategy.Delay, "Delay", "Delay the use of Triplecast", 0, 0, ActionTargets.Self, 66) .AddAssociatedActions(AID.Triplecast); res.Define(Track.LeyLines).As("L.Lines", uiPriority: 170) .AddOption(LeyLinesStrategy.Automatic, "Auto", "Automatically decide when to use Ley Lines", 0, 0, ActionTargets.Self, 52) - .AddOption(LeyLinesStrategy.Force, "Force", "Force the use of Ley Lines, regardless of weaving conditions", 120, 0, ActionTargets.Self, 52) - .AddOption(LeyLinesStrategy.Force1, "Force1", "Force the use of Ley Lines; holds one charge for manual usage", 120, 0, ActionTargets.Self, 52) - .AddOption(LeyLinesStrategy.ForceWeave, "ForceWeave", "Force the use of Ley Lines in any next possible weave slot", 120, 0, ActionTargets.Self, 52) - .AddOption(LeyLinesStrategy.ForceWeave1, "ForceWeave1", "Force the use of Ley Lines in any next possible weave slot; holds one charge for manual usage", 120, 0, ActionTargets.Self, 52) - .AddOption(LeyLinesStrategy.Delay, "Delay", "Delay the use of Ley Lines", 120, 0, ActionTargets.Self, 52) + .AddOption(LeyLinesStrategy.Force, "Force", "Force the use of Ley Lines, regardless of weaving conditions", 120, 30, ActionTargets.Self, 52) + .AddOption(LeyLinesStrategy.Force1, "Force1", "Force the use of Ley Lines; holds one charge for manual usage", 120, 30, ActionTargets.Self, 52) + .AddOption(LeyLinesStrategy.ForceWeave, "ForceWeave", "Force the use of Ley Lines in any next possible weave slot", 120, 30, ActionTargets.Self, 52) + .AddOption(LeyLinesStrategy.ForceWeave1, "ForceWeave1", "Force the use of Ley Lines in any next possible weave slot; holds one charge for manual usage", 120, 30, ActionTargets.Self, 52) + .AddOption(LeyLinesStrategy.Delay, "Delay", "Delay the use of Ley Lines", 0, 0, ActionTargets.Self, 52) .AddAssociatedActions(AID.LeyLines); res.Define(Track.Potion).As("Potion", uiPriority: 160) .AddOption(PotionStrategy.Manual, "Manual", "Do not use automatically") @@ -215,7 +215,7 @@ public static RotationModuleDefinition Definition() .AddOption(OffensiveStrategy.Force, "Force", "Force the use of Transpose, regardless of weaving conditions", 5, 0, ActionTargets.Self, 4) .AddOption(OffensiveStrategy.AnyWeave, "AnyWeave", "Force the use of Transpose in any next possible weave slot", 5, 0, ActionTargets.Self, 4) .AddOption(OffensiveStrategy.EarlyWeave, "EarlyWeave", "Force the use of Transpose in very next FIRST weave slot only", 5, 0, ActionTargets.Self, 4) - .AddOption(OffensiveStrategy.LateWeave, "LateWeave", "Force the use of Transpose in very next LAST weave slot only", 0, 0, ActionTargets.Self, 4) + .AddOption(OffensiveStrategy.LateWeave, "LateWeave", "Force the use of Transpose in very next LAST weave slot only", 5, 0, ActionTargets.Self, 4) .AddOption(OffensiveStrategy.Delay, "Delay", "Delay the use of Transpose", 0, 0, ActionTargets.Self, 4) .AddAssociatedActions(AID.Transpose); res.Define(Track.Amplifier).As("Amplifier", uiPriority: 170) @@ -242,7 +242,6 @@ public static RotationModuleDefinition Definition() .AddOption(OffensiveStrategy.LateWeave, "LateWeave", "Force the use of Between The Lines in very next LAST weave slot only", 3, 0, ActionTargets.Self, 62) .AddOption(OffensiveStrategy.Delay, "Delay", "Delay the use of Between The Lines", 0, 0, ActionTargets.Self, 62) .AddAssociatedActions(AID.BetweenTheLines); - #endregion return res; @@ -250,20 +249,20 @@ public static RotationModuleDefinition Definition() #endregion #region Priorities - //TODO: Fix this shit later, looks crazy public enum GCDPriority //priorities for GCDs (higher number = higher priority) { - None = 0, //default - Step1 = 100, //Step 1 - Step2 = 110, //Step 2 - Step3 = 120, //Step 3 - Step4 = 130, //Step 4 - Step5 = 140, //Step 5 - Step6 = 150, //Step 6 - Step7 = 160, //Step 7 - Step8 = 170, //Step 8 - Step9 = 180, //Step 9 - Step10 = 190, //Step 10 + None = 0, + + //Rotation + SixthStep = 100, + FifthStep = 125, + FourthStep = 150, + ThirdStep = 175, + SecondStep = 200, + FirstStep = 250, + ForcedStep = 299, + + //GCDs Standard = 300, //standard abilities DOT = 350, //damage-over-time abilities FlareStar = 375, //Flare Star @@ -272,19 +271,23 @@ public static RotationModuleDefinition Definition() NeedB3 = 460, //Need to use Blizzard III Polyglot = 475, //Polyglots Paradox = 500, //Paradox + + //Necessities NeedDOT = 600, //Need to apply DOTs NeedF3P = 625, //Need to use Fire III proc - NeedDespair = 640, //Need to use Despair NeedPolyglot = 650, //Need to use Polyglots + + //Moving Moving3 = 700, //Moving (3rd priority) Moving2 = 710, //Moving (2nd priority) Moving1 = 720, //Moving (1st priority) + + //Forced ForcedGCD = 900, //Forced GCDs - BlockAll = 2000, //Block all GCDs } public enum OGCDPriority //priorities for oGCDs (higher number = higher priority) { - None = 0, //default + None = 0, Transpose = 400, //Transpose Manafont = 450, //Manafont LeyLines = 500, //Ley Lines @@ -303,7 +306,8 @@ private AID BestThunderST private AID BestThunderAOE => Unlocked(AID.HighThunder2) ? AID.HighThunder2 : Unlocked(AID.Thunder4) ? AID.Thunder4 - : AID.Thunder2; + : Unlocked(AID.Thunder2) ? AID.Thunder2 + : AID.Thunder1; private AID BestThunder => ShouldUseAOE ? BestThunderAOE : BestThunderST; private AID BestPolyglot @@ -380,50 +384,18 @@ private bool JustUsed(AID aid, float variance) } #region Targeting - private int TargetsInRange() => Hints.NumPriorityTargetsInAOECircle(Player.Position, 25); //Returns the number of targets hit by AOE within a 25-yalm radius around the player - private Actor? TargetChoice(StrategyValues.OptionRef strategy) => ResolveTargetOverride(strategy.Value); //Resolves the target choice based on the strategy - private Actor? FindBestSplashTarget() - { - float splashPriorityFunc(Actor actor) - { - var distanceToPlayer = actor.DistanceToHitbox(Player); - if (distanceToPlayer <= 24f) - { - var targetsInSplashRadius = 0; - foreach (var enemy in Hints.PriorityTargets) - { - var targetActor = enemy.Actor; - if (targetActor != actor && targetActor.Position.InCircle(actor.Position, 5f)) - { - targetsInSplashRadius++; - } - } - return targetsInSplashRadius; - } - return float.MinValue; - } - - var (bestTarget, bestPrio) = FindBetterTargetBy(null, 25f, splashPriorityFunc); - - return bestTarget; - } - private Actor? BestAOETarget => FindBestSplashTarget(); // Find the best target for splash attack private bool ShouldUseAOE { get { - // Check if there's a valid target for the AoE attack var bestTarget = BestAOETarget; - - // If there is a best target and it has a significant number of other targets in its splash radius, we can use AoE if (bestTarget != null) { - // We can define a threshold to require a minimum number of targets within the splash radius to make AoE worthwhile - var minimumTargetsForAOE = 2; // Example: At least 2 other enemies within the 5-yard splash radius + var minimumTargetsForAOE = 2; float splashPriorityFunc(Actor actor) { var distanceToPlayer = actor.DistanceToHitbox(Player); - if (distanceToPlayer <= 24f) + if (distanceToPlayer <= 24.99f) { var targetsInSplashRadius = 0; foreach (var enemy in Hints.PriorityTargets) @@ -447,6 +419,35 @@ float splashPriorityFunc(Actor actor) return false; } } + private int TargetsInRange() => Hints.NumPriorityTargetsInAOECircle(Player.Position, 25); //Returns the number of targets hit by AOE within a 25-yalm radius around the player + private Actor? TargetChoice(StrategyValues.OptionRef strategy) => ResolveTargetOverride(strategy.Value); //Resolves the target choice based on the strategy + private Actor? FindBestSplashTarget() + { + float splashPriorityFunc(Actor actor) + { + var distanceToPlayer = actor.DistanceToHitbox(Player); + if (distanceToPlayer <= 24f) + { + var targetsInSplashRadius = 0; + foreach (var enemy in Hints.PriorityTargets) + { + var targetActor = enemy.Actor; + if (targetActor != actor && targetActor.Position.InCircle(actor.Position, 5f)) + { + targetsInSplashRadius++; + } + } + return targetsInSplashRadius; + } + return float.MinValue; + } + + var (bestTarget, bestPrio) = FindBetterTargetBy(null, 25f, splashPriorityFunc); + + return bestTarget; + } + private Actor? BestAOETarget => FindBestSplashTarget(); // Find the best target for splash attack + //TODO: BestDOTTarget #endregion #endregion @@ -455,10 +456,10 @@ public override void Execute(StrategyValues strategy, Actor? primaryTarget, floa { #region Variables var gauge = World.Client.GetGauge(); //Retrieve BLM gauge - NoStance = ElementStance is 0; //No stance + NoStance = ElementStance is 0 and not (1 or 2 or 3 or -1 or -2 or -3); //No stance ElementStance = gauge.ElementStance; //Elemental Stance - InAstralFire = ElementStance is 1 or 2 or 3; //In Astral Fire - InUmbralIce = ElementStance is -1 or -2 or -3; //In Umbral Ice + InAstralFire = ElementStance is 1 or 2 or 3 and not (0 or -1 or -2 or -3); //In Astral Fire + InUmbralIce = ElementStance is -1 or -2 or -3 and not (0 or 1 or 2 or 3); //In Umbral Ice Polyglots = gauge.PolyglotStacks; //Polyglot Stacks UmbralHearts = gauge.UmbralHearts; //Umbral Hearts MaxUmbralHearts = Unlocked(TraitID.UmbralHeart) ? 3 : 0; @@ -516,6 +517,8 @@ public override void Execute(StrategyValues strategy, Actor? primaryTarget, floa var potionStrat = strategy.Option(Track.Potion).As(); //Potion strategy var tpusStrat = strategy.Option(Track.TPUS).As(); //Transpose/Umbral Soul strategy var movingOption = strategy.Option(Track.Casting).As(); //Casting while moving strategy + var forceST = AOEStrategy is AOEStrategy.ForceST; //Force single target + var forceAOE = AOEStrategy is AOEStrategy.ForceAOE; //Force AOE #endregion #endregion @@ -524,20 +527,20 @@ public override void Execute(StrategyValues strategy, Actor? primaryTarget, floa #region ST / AOE if (movingOption is CastingOption.Allow || - movingOption is CastingOption.Forbid && + (movingOption is CastingOption.Forbid && (!isMoving || //if not moving - (PlayerHasEffect(SID.Swiftcast, 10) || //or has Swiftcast + PlayerHasEffect(SID.Swiftcast, 10) || //or has Swiftcast PlayerHasEffect(SID.Triplecast, 15) || //or has Triplecast - (canParadox && ElementTimer < (SpS * 3) && MP >= 1600 || canParadox && JustUsed(AID.Blizzard4, 5)) || //or can use Paradox + (canParadox && (ElementTimer < (SpS * 3) && MP >= 1600) || JustUsed(AID.Blizzard4, 5)) || //or can use Paradox SelfStatusLeft(SID.Firestarter, 30) is < 25 and not 0 || //or can use F3P (Unlocked(TraitID.EnhancedAstralFire) && MP is < 1600 and not 0)))) //instant cast Despair { if (AOEStrategy is AOEStrategy.Auto) - BestRotation(TargetChoice(AOE) ?? BestAOETarget ?? primaryTarget); - if (AOEStrategy is AOEStrategy.ForceST) - BestST(TargetChoice(AOE) ?? primaryTarget); - if (AOEStrategy is AOEStrategy.ForceAOE) - BestAOE(TargetChoice(AOE) ?? BestAOETarget ?? primaryTarget); + BestRotation(TargetChoice(AOE) ?? primaryTarget ?? BestAOETarget); //target prio is user choice -> current target -> best AOE target + if (forceST) + BestST(TargetChoice(AOE) ?? primaryTarget); //target prio is user choice -> current target + if (forceAOE) + BestAOE(TargetChoice(AOE) ?? primaryTarget ?? BestAOETarget); //target prio is user choice -> best AOE target -> current target } #endregion @@ -552,9 +555,11 @@ movingOption is CastingOption.Forbid && if (!PlayerHasEffect(SID.Swiftcast, 10) || !PlayerHasEffect(SID.Triplecast, 15)) QueueGCD( - Unlocked(TraitID.EnhancedPolyglot) && Polyglots > 0 ? BestPolyglot + Unlocked(TraitID.EnhancedPolyglot) && Polyglots > 0 ? + (forceST ? BestXenoglossy : forceAOE ? AID.Foul : BestPolyglot) : PlayerHasEffect(SID.Firestarter, 30) ? AID.Fire3 - : hasThunderhead ? BestThunder + : hasThunderhead ? + (forceST ? BestThunderST : forceAOE ? BestThunderAOE : BestThunder) : AID.Scathe, Polyglots > 0 ? TargetChoice(polyglot) ?? BestAOETarget ?? primaryTarget : PlayerHasEffect(SID.Firestarter, 30) ? TargetChoice(AOE) ?? primaryTarget @@ -577,9 +582,11 @@ movingOption is CastingOption.Forbid && if (!PlayerHasEffect(SID.Swiftcast, 10) || !PlayerHasEffect(SID.Triplecast, 15)) QueueGCD( - Unlocked(TraitID.EnhancedPolyglot) && Polyglots > 0 ? BestPolyglot + Unlocked(TraitID.EnhancedPolyglot) && Polyglots > 0 ? + (forceST ? BestXenoglossy : forceAOE ? AID.Foul : BestPolyglot) : PlayerHasEffect(SID.Firestarter, 30) ? AID.Fire3 - : hasThunderhead ? BestThunder + : hasThunderhead ? + (forceST ? BestThunderST : forceAOE ? BestThunderAOE : BestThunder) : AID.Scathe, Polyglots > 0 ? TargetChoice(polyglot) ?? BestAOETarget ?? primaryTarget : PlayerHasEffect(SID.Firestarter, 30) ? TargetChoice(AOE) ?? primaryTarget @@ -599,7 +606,7 @@ movingOption is CastingOption.Forbid && } if (movementStrat is MovementStrategy.OnlyScathe) { - if (MP >= 800) + if (Unlocked(AID.Scathe) && MP >= 800) QueueGCD(AID.Scathe, primaryTarget, GCDPriority.Moving1); } } @@ -643,17 +650,17 @@ movingOption is CastingOption.Forbid && { if (AOEStrategy is AOEStrategy.Auto) QueueGCD(BestThunder, - TargetChoice(thunder) ?? BestAOETarget ?? primaryTarget, + TargetChoice(thunder) ?? primaryTarget ?? BestAOETarget, ThunderLeft < 3 ? GCDPriority.NeedDOT : GCDPriority.DOT); - if (AOEStrategy is AOEStrategy.ForceST) + if (forceST) QueueGCD(BestThunderST, TargetChoice(thunder) ?? primaryTarget, ThunderLeft < 3 ? GCDPriority.NeedDOT : GCDPriority.DOT); if (AOEStrategy is AOEStrategy.ForceAOE) QueueGCD(BestThunderAOE, - TargetChoice(thunder) ?? BestAOETarget ?? primaryTarget, + TargetChoice(thunder) ?? primaryTarget ?? BestAOETarget, ThunderLeft < 3 ? GCDPriority.NeedDOT : GCDPriority.DOT); } @@ -665,10 +672,10 @@ or PolyglotStrategy.AutoHold1 or PolyglotStrategy.AutoHold2 or PolyglotStrategy.AutoHold3) QueueGCD(BestPolyglot, - TargetChoice(polyglot) ?? BestAOETarget ?? primaryTarget, + TargetChoice(polyglot) ?? primaryTarget ?? BestAOETarget, polyglotStrat is PolyglotStrategy.ForceXeno ? GCDPriority.ForcedGCD - : Polyglots == MaxPolyglots && EnochianTimer < 5000 ? GCDPriority.NeedPolyglot - : GCDPriority.Paradox); + : Polyglots == MaxPolyglots && EnochianTimer <= 5000 ? GCDPriority.NeedPolyglot + : GCDPriority.Polyglot); if (polyglotStrat is PolyglotStrategy.XenoSpendAll or PolyglotStrategy.XenoHold1 or PolyglotStrategy.XenoHold2 @@ -676,13 +683,17 @@ or PolyglotStrategy.XenoHold2 QueueGCD(BestXenoglossy, TargetChoice(polyglot) ?? primaryTarget, polyglotStrat is PolyglotStrategy.ForceXeno ? GCDPriority.ForcedGCD - : Polyglots == MaxPolyglots && EnochianTimer < 5000 ? GCDPriority.NeedPolyglot - : GCDPriority.Paradox); + : Polyglots == MaxPolyglots && EnochianTimer <= 5000 ? GCDPriority.NeedPolyglot + : GCDPriority.Polyglot); if (polyglotStrat is PolyglotStrategy.FoulSpendAll or PolyglotStrategy.FoulHold1 or PolyglotStrategy.FoulHold2 or PolyglotStrategy.FoulHold3) - QueueGCD(AID.Foul, TargetChoice(polyglot) ?? BestAOETarget ?? primaryTarget, polyglotStrat is PolyglotStrategy.ForceFoul ? GCDPriority.ForcedGCD : Polyglots == MaxPolyglots && EnochianTimer < 5000 ? GCDPriority.NeedPolyglot : GCDPriority.Paradox); //Queue Foul + QueueGCD(AID.Foul, + TargetChoice(polyglot) ?? primaryTarget ?? BestAOETarget, + polyglotStrat is PolyglotStrategy.ForceFoul ? GCDPriority.ForcedGCD + : Polyglots == MaxPolyglots && EnochianTimer <= 5000 ? GCDPriority.NeedPolyglot + : GCDPriority.Polyglot); } //LeyLines if (ShouldUseLeyLines(primaryTarget, llStrat)) @@ -796,6 +807,9 @@ public bool QueueAction(AID aid, Actor? target, float priority, float delay) Hints.ActionsToExecute.Push(ActionID.MakeSpell(aid), target, priority, delay: delay, targetPos: targetPos); return true; } + #endregion + + #region Rotation Helpers private void BestRotation(Actor? target) //Best rotation based on targets nearby { if (ShouldUseAOE) @@ -807,697 +821,480 @@ private void BestRotation(Actor? target) //Best rotation based on targets nearby BestST(target); } } - #endregion - - #region Single-Target Helpers - private void STLv1toLv34(Actor? target) //Level 1-34 single-target rotation - { - //Fire - if (Unlocked(AID.Fire1) && //if Fire is unlocked - NoStance && MP >= 800 || //if no stance is active and MP is 800 or more - InAstralFire && MP >= 1600) //or if Astral Fire is active and MP is 1600 or more - QueueGCD(AID.Fire1, target, GCDPriority.Standard); //Queue Fire - //Ice - //TODO: Fix Blizzard I still casting once after at 10000MP due to MP tick not counting fast enough before next cast - if (InUmbralIce && MP < 9500) //if Umbral Ice is active and MP is not max - QueueGCD(AID.Blizzard1, target, GCDPriority.Standard); //Queue Blizzard - //Transpose - if (ActionReady(AID.Transpose) && //if Transpose is unlocked & off cooldown - InAstralFire && MP < 1600 || //if Astral Fire is active and MP is less than 1600 - InUmbralIce && MP == 10000) //or if Umbral Ice is active and MP is max - QueueOGCD(AID.Transpose, Player, OGCDPriority.Transpose); //Queue Transpose - } - private void STLv35toLv59(Actor? target) //Level 35-59 single-target rotation + private void BestST(Actor? target) //Single-target rotation based on level { - if (NoStance) //if no stance is active + if (In25y(target)) { - if (Unlocked(AID.Blizzard3)) //if Blizzard III is unlocked + if (NoStance) //if no stance is active { - if (MP >= 10000) //if no stance is active and MP is max (opener) - QueueGCD(AID.Blizzard3, target, GCDPriority.NeedB3); //Queue Blizzard III - if (MP < 10000 && Player.InCombat) //or if in combat and no stance is active and MP is less than max (died or stopped attacking) + if (Unlocked(AID.Blizzard3)) //if Blizzard III is unlocked { - if (ActionReady(AID.Swiftcast)) - QueueGCD(AID.Swiftcast, target, GCDPriority.NeedB3); //Queue Swiftcast->Blizzard III - else + if (MP >= 10000) //if no stance is active and MP is max (opener) + QueueGCD(AID.Blizzard3, target, GCDPriority.NeedB3); //Queue Blizzard III + if (MP < 10000 && Player.InCombat) //or if in combat and no stance is active and MP is less than max (died or stopped attacking) QueueGCD(AID.Blizzard3, target, GCDPriority.NeedB3); //Queue Blizzard III } } - } - if (InUmbralIce) //if Umbral Ice is active - { - //Step 1 - max stacks in UI - if (JustUsed(AID.Blizzard3, 5)) //if Blizzard III was just used + if (!Unlocked(AID.Blizzard3) || Player.Level is >= 1 and <= 34) { - if (!Unlocked(AID.Blizzard4) && UmbralStacks == 3) //if Blizzard IV is not unlocked and Umbral Ice stacks are max - QueueGCD(AID.Blizzard1, target, GCDPriority.Step2); //Queue Blizzard I - if (Unlocked(AID.Blizzard4) && UmbralHearts != MaxUmbralHearts) //if Blizzard IV is unlocked and Umbral Hearts are not max - QueueGCD(AID.Blizzard4, target, GCDPriority.Step2); //Queue Blizzard IV + //Fire + if (Unlocked(AID.Fire1) && //if Fire is unlocked + NoStance && MP >= 800 || //if no stance is active and MP is 800 or more + InAstralFire && MP >= 1600) //or if Astral Fire is active and MP is 1600 or more + QueueGCD(AID.Fire1, target, GCDPriority.Standard); //Queue Fire + //Ice + //TODO: Fix Blizzard I still casting once after at 10000MP due to MP tick not counting fast enough before next cast + if (InUmbralIce && MP < 9500) //if Umbral Ice is active and MP is not max + QueueGCD(AID.Blizzard1, target, GCDPriority.Standard); //Queue Blizzard + //Transpose + if (ActionReady(AID.Transpose) && //if Transpose is unlocked & off cooldown + InAstralFire && MP < 1600 || //if Astral Fire is active and MP is less than 1600 + InUmbralIce && MP == 10000) //or if Umbral Ice is active and MP is max + QueueOGCD(AID.Transpose, Player, OGCDPriority.Transpose); //Queue Transpose + } - //Step 2 - swap from UI to AF - if (Unlocked(AID.Fire3) && //if Fire III is unlocked - JustUsed(AID.Blizzard1, 5) && //and Blizzard I was just used - MP < 10000 && //and MP is less than max - Unlocked(TraitID.UmbralHeart) ? UmbralHearts == MaxUmbralHearts : UmbralHearts == 0) //and Umbral Hearts are max if unlocked, or 0 if not - QueueGCD(AID.Fire3, target, JustUsed(AID.Blizzard1, 5) ? GCDPriority.Step10 : GCDPriority.Step1); //Queue Fire III, increase priority if Blizzard I was just used - } - if (InAstralFire) //if Astral Fire is active - { - //Step 1 - Fire 1 - if (MP >= 1600) //if MP is 1600 or more - QueueGCD(AID.Fire1, target, GCDPriority.Step3); //Queue Fire I - //Step 2B - F3P - if (SelfStatusLeft(SID.Firestarter, 30) is < 25 and not 0 && //if Firestarter buff is active and not 0 - AstralStacks == 3) //and Umbral Hearts are 0 - QueueGCD(AID.Fire3, target, GCDPriority.Step10); //Queue Fire III (AF3 F3P) - //Step 3 - swap from AF to UI - if (Unlocked(AID.Blizzard3) && //if Blizzard III is unlocked - MP < 1600) //and MP is less than 400 - QueueGCD(AID.Blizzard3, target, GCDPriority.Step1); //Queue Blizzard III - } - } - private void STLv60toLv71(Actor? target) //Level 60-71 single-target rotation - { - if (NoStance) //if no stance is active - { - if (Unlocked(AID.Blizzard3)) //if Blizzard III is unlocked + if (!Unlocked(AID.Fire4) || Player.Level is >= 35 and <= 59) { - if (MP >= 10000) //if no stance is active and MP is max (opener) - QueueGCD(AID.Blizzard3, target, GCDPriority.NeedB3); //Queue Blizzard III - if (MP < 10000 && Player.InCombat) //or if in combat and no stance is active and MP is less than max (died or stopped attacking) + if (InUmbralIce) //if Umbral Ice is active { - if (ActionReady(AID.Swiftcast)) - QueueGCD(AID.Swiftcast, target, GCDPriority.NeedB3); //Queue Swiftcast->Blizzard III - else - QueueGCD(AID.Blizzard3, target, GCDPriority.NeedB3); //Queue Blizzard III + //Step 1 - max stacks in UI + if (JustUsed(AID.Blizzard3, 5)) //if Blizzard III was just used + { + if (!Unlocked(AID.Blizzard4) && UmbralStacks == 3) //if Blizzard IV is not unlocked and Umbral Ice stacks are max + QueueGCD(AID.Blizzard1, target, GCDPriority.FirstStep); //Queue Blizzard I + if (Unlocked(AID.Blizzard4) && UmbralHearts != MaxUmbralHearts) //if Blizzard IV is unlocked and Umbral Hearts are not max + QueueGCD(AID.Blizzard4, target, GCDPriority.FirstStep); //Queue Blizzard IV + } + //Step 2 - swap from UI to AF + if (Unlocked(AID.Fire3) && //if Fire III is unlocked + JustUsed(AID.Blizzard1, 5) && //and Blizzard I was just used + MP < 10000 && //and MP is less than max + Unlocked(TraitID.UmbralHeart) ? UmbralHearts == MaxUmbralHearts : UmbralHearts == 0) //and Umbral Hearts are max if unlocked, or 0 if not + QueueGCD(AID.Fire3, target, JustUsed(AID.Blizzard1, 5) ? GCDPriority.ForcedStep : GCDPriority.SecondStep); //Queue Fire III, increase priority if Blizzard I was just used } - } - } - if (InUmbralIce) //if Umbral Ice is active - { - //Step 1 - max stacks in UI - if (Unlocked(AID.Blizzard4) && //if Blizzard IV is unlocked - JustUsed(AID.Blizzard3, 5) || UmbralHearts != MaxUmbralHearts) //and Blizzard III was just used or Umbral Hearts are not max - QueueGCD(AID.Blizzard4, target, GCDPriority.Step2); //Queue Blizzard IV - //Step 2 - swap from UI to AF - if (Unlocked(AID.Fire3) && //if Fire III is unlocked - UmbralHearts == MaxUmbralHearts) //and Umbral Hearts are max - QueueGCD(AID.Fire3, target, GCDPriority.Step1); //Queue Fire III - } - if (InAstralFire) //if Astral Fire is active - { - //Step 1-3, 5-7 - Fire IV - if (MP >= 1600) //and MP is 1600 or more - QueueGCD(AID.Fire4, target, GCDPriority.Step5); //Queue Fire IV - //Step 4A - Fire 1 - if (ElementTimer <= (SpS * 3) && //if time remaining on current element is less than 3x GCDs - MP >= 4000) //and MP is 4000 or more - QueueGCD(AID.Fire1, target, ElementTimer <= 5 && MP >= 4000 ? GCDPriority.Paradox : GCDPriority.Step4); //Queue Fire I, increase priority if less than 3s left on element - //Step 4B - F3P - if (SelfStatusLeft(SID.Firestarter, 30) is < 25 and not 0 && //if Firestarter buff is active and not 0 - AstralStacks == 3) //and Umbral Hearts are 0 - QueueGCD(AID.Fire3, target, GCDPriority.Step10); //Queue Fire III (AF3 F3P) - //Step 8 - swap from AF to UI - if (Unlocked(AID.Blizzard3) && //if Blizzard III is unlocked - MP < 1600) //and MP is less than 400 - QueueGCD(AID.Blizzard3, target, GCDPriority.Step1); //Queue Blizzard III - } - } - private void STLv72toLv89(Actor? target) //Level 72-89 single-target rotation - { - if (NoStance) //if no stance is active - { - if (Unlocked(AID.Blizzard3)) //if Blizzard III is unlocked - { - if (MP >= 10000) //if no stance is active and MP is max (opener) - QueueGCD(AID.Blizzard3, target, GCDPriority.NeedB3); //Queue Blizzard III - if (MP < 10000 && Player.InCombat) //or if in combat and no stance is active and MP is less than max (died or stopped attacking) + if (InAstralFire) //if Astral Fire is active { - if (ActionReady(AID.Swiftcast)) - QueueGCD(AID.Swiftcast, target, GCDPriority.NeedB3); //Queue Swiftcast->Blizzard III - else - QueueGCD(AID.Blizzard3, target, GCDPriority.NeedB3); //Queue Blizzard III + //Step 1 - Fire 1 + if (MP >= 1600) //if MP is 1600 or more + QueueGCD(AID.Fire1, target, GCDPriority.FirstStep); //Queue Fire I + //Step 2B - F3P + if (SelfStatusLeft(SID.Firestarter, 30) is < 25 and not 0 && //if Firestarter buff is active and not 0 + AstralStacks == 3) //and Umbral Hearts are 0 + QueueGCD(AID.Fire3, target, GCDPriority.ForcedStep); //Queue Fire III (AF3 F3P) + //Step 3 - swap from AF to UI + if (Unlocked(AID.Blizzard3) && //if Blizzard III is unlocked + MP < 1600) //and MP is less than 400 + QueueGCD(AID.Blizzard3, target, GCDPriority.SecondStep); //Queue Blizzard III } } - } - if (InUmbralIce) //if Umbral Ice is active - { - //Step 1 - max stacks in UI - if (Unlocked(AID.Blizzard4) && //if Blizzard IV is unlocked - JustUsed(AID.Blizzard3, 5) || UmbralHearts != MaxUmbralHearts) //and Blizzard III was just used or Umbral Hearts are not max - QueueGCD(AID.Blizzard4, target, GCDPriority.Step2); //Queue Blizzard IV - //Step 2 - swap from UI to AF - if (Unlocked(AID.Fire3) && //if Fire III is unlocked - UmbralHearts == MaxUmbralHearts) //and Umbral Hearts are max - QueueGCD(AID.Fire3, target, GCDPriority.Step1); //Queue Fire III - } - if (InAstralFire) //if Astral Fire is active - { - //Step 1-3, 5-7 - Fire IV - if (MP >= 1600) //and MP is 1600 or more - QueueGCD(AID.Fire4, target, GCDPriority.Step5); //Queue Fire IV - //Step 4A - Fire 1 - if (ElementTimer <= (SpS * 3) && //if time remaining on current element is less than 3x GCDs - MP >= 4000) //and MP is 4000 or more - QueueGCD(AID.Fire1, target, ElementTimer <= 5 && MP >= 4000 ? GCDPriority.Paradox : GCDPriority.Step4); //Queue Fire I, increase priority if less than 3s left on element - //Step 4B - F3P - if (SelfStatusLeft(SID.Firestarter, 30) is < 25 and not 0 && //if Firestarter buff is active and not 0 - AstralStacks == 3) //and Umbral Hearts are 0 - QueueGCD(AID.Fire3, target, GCDPriority.Step10); //Queue Fire III (AF3 F3P) - //Step 8 - Despair - if (MP is < 1600 and not 0 && //if MP is less than 1600 and not 0 - Unlocked(AID.Despair)) //and Despair is unlocked - { - if (ActionReady(AID.Swiftcast) && ElementTimer < GetCastTime(AID.Despair)) - QueueGCD(AID.Swiftcast, target, GCDPriority.Step2); //Queue Swiftcast->Despair - else - QueueGCD(AID.Despair, target, GCDPriority.Step2); //Queue Despair - } - //Step 9 - swap from AF to UI - if (Unlocked(AID.Blizzard3) && //if Blizzard III is unlocked - MP <= 400) //and MP is less than 400 - QueueGCD(AID.Blizzard3, target, GCDPriority.Step1); //Queue Blizzard III - } - } - private void STLv90toLv99(Actor? target) //Level 90-99 single-target rotation - { - if (NoStance) //if no stance is active - { - if (Unlocked(AID.Blizzard3)) //if Blizzard III is unlocked + if (!Unlocked(AID.Despair) || Player.Level is >= 60 and <= 71) { - if (MP >= 10000) //if no stance is active and MP is max (opener) - QueueGCD(AID.Blizzard3, target, GCDPriority.NeedB3); //Queue Blizzard III - if (MP < 10000 && Player.InCombat) //or if in combat and no stance is active and MP is less than max (died or stopped attacking) + if (InUmbralIce) //if Umbral Ice is active { - if (ActionReady(AID.Swiftcast)) - QueueGCD(AID.Swiftcast, target, GCDPriority.NeedB3); //Queue Swiftcast->Blizzard III - else - QueueGCD(AID.Blizzard3, target, GCDPriority.NeedB3); //Queue Blizzard III + //Step 1 - max stacks in UI + if (Unlocked(AID.Blizzard4) && //if Blizzard IV is unlocked + JustUsed(AID.Blizzard3, 5) || UmbralHearts != MaxUmbralHearts) //and Blizzard III was just used or Umbral Hearts are not max + QueueGCD(AID.Blizzard4, target, GCDPriority.FirstStep); //Queue Blizzard IV + //Step 2 - swap from UI to AF + if (Unlocked(AID.Fire3) && //if Fire III is unlocked + UmbralHearts == MaxUmbralHearts) //and Umbral Hearts are max + QueueGCD(AID.Fire3, target, GCDPriority.SecondStep); //Queue Fire III } - } - } - if (InUmbralIce) //if Umbral Ice is active - { - //Step 1 - max stacks in UI - if (Unlocked(AID.Blizzard4) && //if Blizzard IV is unlocked - JustUsed(AID.Blizzard3, 5) || UmbralHearts != MaxUmbralHearts) //and Blizzard III was just used or Umbral Hearts are not max - QueueGCD(AID.Blizzard4, target, GCDPriority.Step3); //Queue Blizzard IV - //Step 2 - Ice Paradox - if (canParadox && //if Paradox is unlocked and Paradox is active - JustUsed(AID.Blizzard4, 5)) //and Blizzard IV was just used - QueueGCD(AID.Paradox, target, GCDPriority.Step2); //Queue Paradox - //Step 2 - swap from UI to AF - if (Unlocked(AID.Fire3) && //if Fire III is unlocked - UmbralHearts == MaxUmbralHearts) //and Umbral Hearts are max - QueueGCD(AID.Fire3, target, GCDPriority.Step1); //Queue Fire III - } - if (InAstralFire) //if Astral Fire is active - { - //Step 1-4, 6 & 7 - Fire IV - if (MP >= 1600) //and MP is 1600 or more - QueueGCD(AID.Fire4, target, GCDPriority.Step5); //Queue Fire IV - //Step 5A - Paradox - if (canParadox && //if Paradox is unlocked and Paradox is active - ElementTimer < (SpS * 3) && //and time remaining on current element is less than 3x GCDs - MP >= 1600) //and MP is 1600 or more - QueueGCD(AID.Paradox, target, ElementTimer <= 3 ? GCDPriority.Paradox : GCDPriority.Step4); //Queue Paradox, increase priority if less than 3s left on element - //Step 4B - F3P - if (SelfStatusLeft(SID.Firestarter, 30) is < 25 and not 0 && //if Firestarter buff is active and not 0 - AstralStacks == 3) //and Umbral Hearts are 0 - QueueGCD(AID.Fire3, target, GCDPriority.Step10); //Queue Fire III (AF3 F3P) - //Step 8 - Despair - if (MP is < 1600 and not 0 && //if MP is less than 1600 and not 0 - Unlocked(AID.Despair)) //and Despair is unlocked - { - if (ActionReady(AID.Swiftcast) && ElementTimer < GetCastTime(AID.Despair)) - QueueGCD(AID.Swiftcast, target, GCDPriority.Step2); //Queue Swiftcast->Despair - else - QueueGCD(AID.Despair, target, GCDPriority.Step2); //Queue Despair - } - //Step 9 - swap from AF to UI - if (Unlocked(AID.Blizzard3) && //if Blizzard III is unlocked - MP <= 400) //and MP is less than 400 - QueueGCD(AID.Blizzard3, target, GCDPriority.Step1); //Queue Blizzard III - } - } - private void STLv100(Actor? target) //Level 100 single-target rotation - { - if (NoStance) //if no stance is active - { - if (Unlocked(AID.Blizzard3)) //if Blizzard III is unlocked - { - if (MP >= 10000) //if no stance is active and MP is max (opener) - QueueGCD(AID.Blizzard3, target, GCDPriority.NeedB3); //Queue Blizzard III - if (MP < 10000 && Player.InCombat) //or if in combat and no stance is active and MP is less than max (died or stopped attacking) + if (InAstralFire) //if Astral Fire is active { - if (ActionReady(AID.Swiftcast)) - QueueGCD(AID.Swiftcast, target, GCDPriority.NeedB3); //Queue Swiftcast->Blizzard III - else - QueueGCD(AID.Blizzard3, target, GCDPriority.NeedB3); //Queue Blizzard III + //Step 1-3, 5-7 - Fire IV + if (MP >= 1600) //and MP is 1600 or more + QueueGCD(AID.Fire4, target, GCDPriority.FirstStep); //Queue Fire IV + //Step 4A - Fire 1 + if (ElementTimer <= (SpS * 3) && //if time remaining on current element is less than 3x GCDs + MP >= 4000) //and MP is 4000 or more + QueueGCD(AID.Fire1, target, ElementTimer <= 5 && MP >= 4000 ? GCDPriority.Paradox : GCDPriority.SecondStep); //Queue Fire I, increase priority if less than 3s left on element + //Step 4B - F3P + if (SelfStatusLeft(SID.Firestarter, 30) is < 25 and not 0 && //if Firestarter buff is active and not 0 + AstralStacks == 3) //and Umbral Hearts are 0 + QueueGCD(AID.Fire3, target, GCDPriority.ForcedStep); //Queue Fire III (AF3 F3P) + //Step 8 - swap from AF to UI + if (Unlocked(AID.Blizzard3) && //if Blizzard III is unlocked + MP < 1600) //and MP is less than 400 + QueueGCD(AID.Blizzard3, target, GCDPriority.ThirdStep); //Queue Blizzard III } } - } - if (InUmbralIce) //if Umbral Ice is active - { - //Step 1 - max stacks in UI - if (Unlocked(AID.Blizzard4) && //if Blizzard IV is unlocked - JustUsed(AID.Blizzard3, 5) || UmbralHearts != MaxUmbralHearts) //and Blizzard III was just used or Umbral Hearts are not max - QueueGCD(AID.Blizzard4, target, GCDPriority.Step3); //Queue Blizzard IV - //Step 2 - Ice Paradox - if (canParadox && //if Paradox is unlocked and Paradox is active - JustUsed(AID.Blizzard4, 5)) //and Blizzard IV was just used - QueueGCD(AID.Paradox, target, GCDPriority.Step2); //Queue Paradox - //Step 2 - swap from UI to AF - if (Unlocked(AID.Fire3) && //if Fire III is unlocked - UmbralHearts == MaxUmbralHearts) //and Umbral Hearts are max - QueueGCD(AID.Fire3, target, GCDPriority.Step1); //Queue Fire III - } - if (InAstralFire) //if Astral Fire is active - { - //Step 1-4, 6 & 7 - Fire IV - if (AstralSoulStacks != 6 && //and Astral Soul stacks are not max - MP >= 1600) //and MP is 1600 or more - QueueGCD(AID.Fire4, target, GCDPriority.Step6); //Queue Fire IV - //Step 5A - Paradox - if (ParadoxActive && //if Paradox is active - ElementTimer < (SpS * 3) && //and time remaining on current element is less than 3x GCDs - MP >= 1600) //and MP is 1600 or more - QueueGCD(AID.Paradox, target, ElementTimer <= 3 ? GCDPriority.Paradox : GCDPriority.Step5); //Queue Paradox, increase priority if less than 3s left on element - //Step 4B - F3P - if (SelfStatusLeft(SID.Firestarter, 30) is < 25 and not 0 && //if Firestarter buff is active and not 0 - AstralStacks == 3) //and Umbral Hearts are 0 - QueueGCD(AID.Fire3, target, GCDPriority.Step10); //Queue Fire III (AF3 F3P) - //Step 8 - Despair - if (MP is < 1600 and not 0 && //if MP is less than 1600 and not 0 - Unlocked(AID.Despair)) //and Despair is unlocked - QueueGCD(AID.Despair, target, GCDPriority.Step3); //Queue Despair - //Step 9 - Flare Star - if (AstralSoulStacks == 6) //if Astral Soul stacks are max + if (!Unlocked(AID.Paradox) || Player.Level is >= 72 and <= 89) { - if (JustUsed(AID.Despair, 5f) && ActionReady(AID.Swiftcast)) - QueueGCD(AID.Swiftcast, Player, GCDPriority.Step2); //Queue Swiftcast->Flare Star - QueueGCD(AID.FlareStar, target, GCDPriority.Step2); //Queue Flare Star - } - //Step 10A - skip Flare Star if we cant use it (cryge) - if (Unlocked(AID.Blizzard3) && //if Blizzard III is unlocked - MP <= 400 && //and MP is less than 400 - AstralSoulStacks is < 6 and > 0) //and Astral Soul stacks are less than 6 but greater than 0 - QueueGCD(AID.Blizzard3, target, GCDPriority.Step1); //Queue Blizzard III - //Step 10B - swap from AF to UI - if (Unlocked(AID.Blizzard3) && //if Blizzard III is unlocked - MP <= 400 && //and MP is less than 400 - AstralSoulStacks == 0) //and Astral Soul stacks are 0 - QueueGCD(AID.Blizzard3, target, GCDPriority.Step1); //Queue Blizzard III - } - } - private void BestST(Actor? target) //Single-target rotation based on level - { - if (Player.Level is >= 1 and <= 34) - { - STLv1toLv34(target); - } - if (Player.Level is >= 35 and <= 59) - { - STLv35toLv59(target); - } - if (Player.Level is >= 60 and <= 71) - { - STLv60toLv71(target); - } - if (Player.Level is >= 72 and <= 89) - { - STLv72toLv89(target); - } - if (Player.Level is >= 90 and <= 99) - { - STLv90toLv99(target); - } - if (Player.Level is 100) - { - STLv100(target); - } - } - #endregion - - #region AOE Helpers - private void AOELv12toLv34(Actor? target) //Level 12-34 AOE rotation - { - if (NoStance) - { - if (Unlocked(AID.Blizzard2)) - { - if (MP >= 10000) - QueueGCD(AID.Blizzard2, target, GCDPriority.NeedB3); - if (MP < 10000 && Player.InCombat) + if (InUmbralIce) //if Umbral Ice is active { - if (ActionReady(AID.Swiftcast)) - QueueGCD(AID.Swiftcast, target, GCDPriority.NeedB3); - else - QueueGCD(AID.Blizzard2, target, GCDPriority.NeedB3); + //Step 1 - max stacks in UI + if (Unlocked(AID.Blizzard4) && //if Blizzard IV is unlocked + JustUsed(AID.Blizzard3, 5) || UmbralHearts != MaxUmbralHearts) //and Blizzard III was just used or Umbral Hearts are not max + QueueGCD(AID.Blizzard4, target, GCDPriority.FirstStep); //Queue Blizzard IV + //Step 2 - swap from UI to AF + if (Unlocked(AID.Fire3) && //if Fire III is unlocked + UmbralHearts == MaxUmbralHearts) //and Umbral Hearts are max + QueueGCD(AID.Fire3, target, GCDPriority.SecondStep); //Queue Fire III } - } - } - //Fire - if (Unlocked(AID.Fire2) && //if Fire is unlocked - InAstralFire && MP >= 3000) //or if Astral Fire is active and MP is 1600 or more - QueueGCD(AID.Fire2, target, GCDPriority.Standard); //Queue Fire II - //Ice - //TODO: MP tick is not fast enough before next cast, this will cause an extra unnecessary cast - if (InUmbralIce && - MP <= 9600) - QueueGCD(AID.Blizzard2, target, GCDPriority.Standard); //Queue Blizzard II - //Transpose - if (ActionReady(AID.Transpose) && //if Transpose is unlocked & off cooldown - (InAstralFire && MP < 3000 || //if Astral Fire is active and MP is less than 1600 - InUmbralIce && MP > 9600)) //or if Umbral Ice is active and MP is max - QueueOGCD(AID.Transpose, Player, OGCDPriority.Transpose); //Queue Transpose - } - private void AOELv35toLv39(Actor? target) //Level 35-39 AOE rotation - { - if (NoStance) - { - if (Unlocked(AID.Blizzard2)) - { - if (MP >= 10000) - QueueGCD(AID.Blizzard2, target, GCDPriority.NeedB3); - if (MP < 10000 && Player.InCombat) + if (InAstralFire) //if Astral Fire is active { - if (ActionReady(AID.Swiftcast)) - QueueGCD(AID.Swiftcast, target, GCDPriority.NeedB3); - else - QueueGCD(AID.Blizzard2, target, GCDPriority.NeedB3); + //Step 1-3, 5-7 - Fire IV + if (MP >= 1600) //and MP is 1600 or more + QueueGCD(AID.Fire4, target, GCDPriority.FirstStep); //Queue Fire IV + //Step 4A - Fire 1 + if (ElementTimer <= (GetCastTime(AID.Fire1) * 3) && //if time remaining on current element is less than 3x GCDs + MP >= 4000) //and MP is 4000 or more + QueueGCD(AID.Fire1, target, ElementTimer <= (GetCastTime(AID.Fire1) * 3) && MP >= 4000 ? GCDPriority.Paradox : GCDPriority.SecondStep); //Queue Fire I, increase priority if less than 3s left on element + //Step 4B - F3P + if (SelfStatusLeft(SID.Firestarter, 30) is < 25 and not 0 && //if Firestarter buff is active and not 0 + AstralStacks == 3) //and Umbral Hearts are 0 + QueueGCD(AID.Fire3, target, GCDPriority.ForcedStep); //Queue Fire III (AF3 F3P) + //Step 8 - Despair + if (Unlocked(AID.Despair) && //if Despair is unlocked + ((MP is < 1600 and >= 800) || //if MP is less than 1600 and not 0 + (MP is <= 4000 and >= 800 && ElementTimer <= (GetCastTime(AID.Despair) * 2)))) //or if we dont have enough time for last F4s + QueueGCD(AID.Despair, target, ElementTimer <= (GetCastTime(AID.Despair) * 2) ? GCDPriority.ForcedGCD : GCDPriority.ThirdStep); //Queue Despair + //Step 9 - swap from AF to UI + if (MP <= 400) //and MP is less than 400 + QueueGCD(AID.Blizzard3, target, GCDPriority.FourthStep); //Queue Blizzard III } } - } - if (InUmbralIce) - { - //Step 1 - max stacks in UI - //TODO: MP tick is not fast enough before next cast, this will cause an extra unnecessary cast - if (Unlocked(AID.Blizzard2) && - MP < 9600) - QueueGCD(AID.Blizzard2, target, GCDPriority.Step2); - //Step 2 - swap from UI to AF - if (Unlocked(AID.Fire2) && - MP >= 9600 && - UmbralStacks == 3) - QueueGCD(AID.Fire2, target, GCDPriority.Step1); - } - if (InAstralFire) - { - if (MP >= 3000) - QueueGCD(AID.Fire2, target, GCDPriority.Step2); - if (Unlocked(AID.Blizzard2) && - MP < 3000) - QueueGCD(AID.Blizzard2, target, GCDPriority.Step1); - } - } - private void AOELv40toLv49(Actor? target) //Level 40-49 AOE rotation - { - if (NoStance) - { - if (Unlocked(AID.Blizzard2)) + if (!Unlocked(AID.FlareStar) || Player.Level is >= 90 and <= 99) { - if (MP >= 10000) - QueueGCD(AID.Blizzard2, target, GCDPriority.NeedB3); - if (MP < 10000 && Player.InCombat) + if (InUmbralIce) //if Umbral Ice is active { - if (ActionReady(AID.Swiftcast)) - QueueGCD(AID.Swiftcast, target, GCDPriority.NeedB3); - else - QueueGCD(AID.Blizzard2, target, GCDPriority.NeedB3); + //Step 1 - max stacks in UI + if (Unlocked(AID.Blizzard4) && //if Blizzard IV is unlocked + JustUsed(AID.Blizzard3, 5) || UmbralHearts != MaxUmbralHearts) //and Blizzard III was just used or Umbral Hearts are not max + QueueGCD(AID.Blizzard4, target, GCDPriority.FirstStep); //Queue Blizzard IV + //Step 2 - Ice Paradox + if (canParadox && //if Paradox is unlocked and Paradox is active + JustUsed(AID.Blizzard4, 5)) //and Blizzard IV was just used + QueueGCD(AID.Paradox, target, GCDPriority.SecondStep); //Queue Paradox + //Step 3 - swap from UI to AF + if (Unlocked(AID.Fire3) && //if Fire III is unlocked + UmbralHearts == MaxUmbralHearts) //and Umbral Hearts are max + QueueGCD(AID.Fire3, target, GCDPriority.ThirdStep); //Queue Fire III + } + if (InAstralFire) //if Astral Fire is active + { + //Step 1-4, 6 & 7 - Fire IV + if (MP >= 1600) //and MP is 1600 or more + QueueGCD(AID.Fire4, target, GCDPriority.FirstStep); //Queue Fire IV + //Step 5A - Paradox + if (canParadox && //if Paradox is unlocked and Paradox is active + ElementTimer < (SpS * 3) && //and time remaining on current element is less than 3x GCDs + MP >= 1600) //and MP is 1600 or more + QueueGCD(AID.Paradox, target, ElementTimer <= 3 ? GCDPriority.Paradox : GCDPriority.SecondStep); //Queue Paradox, increase priority if less than 3s left on element + //Step 4B - F3P + if (SelfStatusLeft(SID.Firestarter, 30) is < 25 and not 0 && //if Firestarter buff is active and not 0 + AstralStacks == 3) //and Umbral Hearts are 0 + QueueGCD(AID.Fire3, target, GCDPriority.ForcedStep); //Queue Fire III (AF3 F3P) + //Step 8 - Despair + if (Unlocked(AID.Despair) && //if Despair is unlocked + ((MP is < 1600 and >= 800) || //if MP is less than 1600 and not 0 + (MP is <= 4000 and >= 800 && ElementTimer <= (GetCastTime(AID.Despair) * 2)))) //or if we dont have enough time for last F4s + QueueGCD(AID.Despair, target, ElementTimer <= (GetCastTime(AID.Despair) * 2) ? GCDPriority.ForcedGCD : GCDPriority.ThirdStep); //Queue Despair + //Step 9 - swap from AF to UI + if (Unlocked(AID.Blizzard3) && //if Blizzard III is unlocked + MP <= 400) //and MP is less than 400 + QueueGCD(AID.Blizzard3, target, GCDPriority.FourthStep); //Queue Blizzard III } } - } - if (InUmbralIce) - { - //Step 1 - max stacks in UI - if (Unlocked(AID.Blizzard2) && - UmbralStacks < 3) - QueueGCD(AID.Blizzard2, target, GCDPriority.Step3); - //Step 2 - Freeze - if (Unlocked(AID.Freeze) && !JustUsed(AID.Freeze, 5f) && - (JustUsed(AID.Blizzard2, 5) || MP < 10000)) - QueueGCD(AID.Freeze, target, GCDPriority.Step2); - //Step 3 - swap from UI to AF - if (Unlocked(AID.Fire2) && - MP >= 10000 && - UmbralStacks == 3) - QueueGCD(AID.Fire2, target, GCDPriority.Step1); - } - if (InAstralFire) - { - if (MP >= 3000) - QueueGCD(AID.Fire2, target, GCDPriority.Step2); - if (Unlocked(AID.Blizzard2) && - MP < 3000) - QueueGCD(AID.Blizzard2, target, GCDPriority.Step1); - } - } - private void AOELv50toLv57(Actor? target) //Level 50-57 AOE rotation - { - if (NoStance) - { - if (Unlocked(AID.Blizzard2)) + if (!Unlocked(AID.FlareStar) || Player.Level is 100) { - if (MP >= 10000) - QueueGCD(AID.Blizzard2, target, GCDPriority.NeedB3); - if (MP < 10000 && Player.InCombat) + if (InUmbralIce) //if Umbral Ice is active { - if (ActionReady(AID.Swiftcast)) - QueueGCD(AID.Swiftcast, target, GCDPriority.NeedB3); - else - QueueGCD(AID.Blizzard2, target, GCDPriority.NeedB3); + //Step 1 - max stacks in UI + if (Unlocked(AID.Blizzard4) && //if Blizzard IV is unlocked + JustUsed(AID.Blizzard3, 5) || UmbralHearts != MaxUmbralHearts) //and Blizzard III was just used or Umbral Hearts are not max + QueueGCD(AID.Blizzard4, target, GCDPriority.FirstStep); //Queue Blizzard IV + //Step 2 - Ice Paradox + if (canParadox && //if Paradox is unlocked and Paradox is active + JustUsed(AID.Blizzard4, 5)) //and Blizzard IV was just used + QueueGCD(AID.Paradox, target, GCDPriority.SecondStep); //Queue Paradox + //Step 3 - swap from UI to AF + if (Unlocked(AID.Fire3) && //if Fire III is unlocked + UmbralHearts == MaxUmbralHearts) //and Umbral Hearts are max + QueueGCD(AID.Fire3, target, GCDPriority.ThirdStep); //Queue Fire III + } + if (InAstralFire) //if Astral Fire is active + { + //Step 1-4, 6 & 7 - Fire IV + if (AstralSoulStacks != 6 && //and Astral Soul stacks are not max + MP >= 1600) //and MP is 1600 or more + QueueGCD(AID.Fire4, target, GCDPriority.FirstStep); //Queue Fire IV + //Step 5A - Paradox + if (ParadoxActive && //if Paradox is active + ElementTimer < (SpS * 3) && //and time remaining on current element is less than 3x GCDs + MP >= 1600) //and MP is 1600 or more + QueueGCD(AID.Paradox, target, ElementTimer <= 3 ? GCDPriority.Paradox : GCDPriority.SecondStep); //Queue Paradox, increase priority if less than 3s left on element + //Step 4B - F3P + if (SelfStatusLeft(SID.Firestarter, 30) is < 25 and not 0 && //if Firestarter buff is active and not 0 + AstralStacks == 3) //and Umbral Hearts are 0 + QueueGCD(AID.Fire3, target, GCDPriority.ForcedStep); //Queue Fire III (AF3 F3P) + //Step 8 - Despair + if (MP is < 1600 and not 0 && //if MP is less than 1600 and not 0 + Unlocked(AID.Despair)) //and Despair is unlocked + QueueGCD(AID.Despair, target, GCDPriority.ThirdStep); //Queue Despair + //Step 9 - Flare Star + if (AstralSoulStacks == 6) //if Astral Soul stacks are max + QueueGCD(AID.FlareStar, target, GCDPriority.FourthStep); //Queue Flare Star + //Step 10A - skip Flare Star if we cant use it (cryge) + if (Unlocked(AID.Blizzard3) && //if Blizzard III is unlocked + MP <= 400 && //and MP is less than 400 + AstralSoulStacks is < 6 and > 0) //and Astral Soul stacks are less than 6 but greater than 0 + QueueGCD(AID.Blizzard3, target, GCDPriority.FifthStep); //Queue Blizzard III + //Step 10B - swap from AF to UI + if (Unlocked(AID.Blizzard3) && //if Blizzard III is unlocked + MP <= 400 && //and MP is less than 400 + AstralSoulStacks == 0) //and Astral Soul stacks are 0 + QueueGCD(AID.Blizzard3, target, GCDPriority.FifthStep); //Queue Blizzard III } } } - if (InUmbralIce) - { - //Step 1 - max stacks in UI - if (Unlocked(AID.Blizzard2) && - UmbralStacks < 3) - QueueGCD(AID.Blizzard2, target, GCDPriority.Step3); - //Step 2 - Freeze - if (Unlocked(AID.Freeze) && !JustUsed(AID.Freeze, 5f) && - (JustUsed(AID.Blizzard2, 5) || MP < 10000)) - QueueGCD(AID.Freeze, target, GCDPriority.Step2); - //Step 3 - swap from UI to AF - if (Unlocked(AID.Fire2) && - MP >= 10000 && - UmbralStacks == 3) - QueueGCD(AID.Fire2, target, GCDPriority.Step1); - } - if (InAstralFire) - { - //Step 1 - spam Fire 2 - if (MP >= 3000) - QueueGCD(AID.Fire2, target, GCDPriority.Step3); - //Step 2 - Flare - if (Unlocked(AID.Flare) && - MP < 3000) - QueueGCD(AID.Flare, target, GCDPriority.Step2); - //Step 3 - swap from AF to UI - if (Unlocked(AID.Blizzard2) && - (!Unlocked(AID.Flare) && MP < 3000) || //do your job quests, fool - (Unlocked(AID.Flare) && MP < 400)) - QueueGCD(AID.Blizzard2, target, MP < 400 ? GCDPriority.Step10 : GCDPriority.Step1); - } } - private void AOELv58toLv81(Actor? target) //Level 58-81 AOE rotation + private void BestAOE(Actor? target) //AOE rotation based on level { - if (NoStance) + if (In25y(target)) { - if (Unlocked(AID.Blizzard2)) + if (NoStance) { - if (MP >= 10000) - QueueGCD(AID.Blizzard2, target, GCDPriority.NeedB3); - if (MP < 10000 && Player.InCombat) + if (Unlocked(AID.Blizzard2) && !Unlocked(AID.HighBlizzard2)) { - if (ActionReady(AID.Swiftcast)) - QueueGCD(AID.Swiftcast, target, GCDPriority.NeedB3); - else + if (MP >= 10000) + QueueGCD(AID.Blizzard2, target, GCDPriority.NeedB3); + if (MP < 10000 && Player.InCombat) QueueGCD(AID.Blizzard2, target, GCDPriority.NeedB3); } - } - } - if (InUmbralIce) - { - //Step 1 - max stacks in UI - if (Unlocked(AID.Blizzard2) && - UmbralStacks < 3) - QueueGCD(AID.Blizzard2, target, GCDPriority.Step3); - //Step 2 - Freeze - if (Unlocked(AID.Freeze) && !JustUsed(AID.Freeze, 5f) && - (JustUsed(AID.Blizzard2, 5) || MP < 10000)) - QueueGCD(AID.Freeze, target, GCDPriority.Step2); - //Step 3 - swap from UI to AF - if (Unlocked(AID.Fire2) && - MP >= 10000 && - UmbralStacks == 3) - QueueGCD(AID.Fire2, target, GCDPriority.Step1); - } - if (InAstralFire) - { - //Step 1 - spam Fire 2 - if (UmbralHearts > 1) - QueueGCD(AID.Fire2, target, GCDPriority.Step4); - //Step 2 - Flare - if (Unlocked(AID.Flare)) - { - //first cast - if (UmbralHearts == 1) - QueueGCD(AID.Flare, target, GCDPriority.Step3); - //second cast - if (UmbralHearts == 0 && - MP >= 800) - QueueGCD(AID.Flare, target, GCDPriority.Step2); - } - //Step 3 - swap from AF to UI - if (Unlocked(AID.Blizzard2) && - MP < 400) - QueueGCD(AID.Blizzard2, target, GCDPriority.Step1); - } - } - private void AOELv82toLv99(Actor? target) //Level 82-99 AOE rotation - { - if (NoStance) - { - if (Unlocked(AID.HighBlizzard2)) - { - if (MP >= 10000) - QueueGCD(AID.HighBlizzard2, target, GCDPriority.NeedB3); - if (MP < 10000 && Player.InCombat) + if (Unlocked(AID.HighBlizzard2)) { - if (ActionReady(AID.Swiftcast)) - QueueGCD(AID.Swiftcast, target, GCDPriority.NeedB3); - else + if (MP >= 10000) + QueueGCD(AID.HighBlizzard2, target, GCDPriority.NeedB3); + if (MP < 10000 && Player.InCombat) QueueGCD(AID.HighBlizzard2, target, GCDPriority.NeedB3); } } - } - if (InUmbralIce) - { - //Step 1 - max stacks in UI - if (Unlocked(AID.HighBlizzard2) && - UmbralStacks < 3) - QueueGCD(AID.HighBlizzard2, target, GCDPriority.Step3); - //Step 2 - Freeze - if (Unlocked(AID.Freeze) && !JustUsed(AID.Freeze, 5f) && - (JustUsed(AID.HighBlizzard2, 5) || MP < 10000)) - QueueGCD(AID.Freeze, target, GCDPriority.Step2); - //Step 3 - swap from UI to AF - if (Unlocked(AID.HighFire2) && - MP >= 10000 && - UmbralStacks == 3) - QueueGCD(AID.HighFire2, target, GCDPriority.Step1); - } - if (InAstralFire) - { - //Step 1 - spam Fire 2 - if (MP > 5500) - QueueGCD(AID.HighFire2, target, GCDPriority.Step4); - //Step 2 - Flare - if (Unlocked(AID.Flare)) + if (!Unlocked(AID.Blizzard3) || Player.Level is >= 12 and <= 35) { - //first cast - if (UmbralHearts == 1) - QueueGCD(AID.Flare, target, GCDPriority.Step3); - //second cast - if (UmbralHearts == 0 && - MP >= 800) - QueueGCD(AID.Flare, target, GCDPriority.Step2); + //Fire + if (Unlocked(AID.Fire2) && //if Fire is unlocked + InAstralFire && MP >= 3000) //or if Astral Fire is active and MP is 1600 or more + QueueGCD(AID.Fire2, target, GCDPriority.Standard); //Queue Fire II + //Ice + //TODO: MP tick is not fast enough before next cast, this will cause an extra unnecessary cast + if (InUmbralIce && + MP <= 9600) + QueueGCD(AID.Blizzard2, target, GCDPriority.Standard); //Queue Blizzard II + //Transpose + if (ActionReady(AID.Transpose) && //if Transpose is unlocked & off cooldown + (InAstralFire && MP < 3000 || //if Astral Fire is active and MP is less than 1600 + InUmbralIce && MP > 9600)) //or if Umbral Ice is active and MP is max + QueueOGCD(AID.Transpose, Player, OGCDPriority.Transpose); //Queue Transpose + //if in AF but no F2 yet, TP back to UI for B2 spam + if (InAstralFire && !Unlocked(AID.Fire2)) + QueueOGCD(AID.Transpose, Player, OGCDPriority.Transpose); } - //Step 3 - swap from AF to UI - if (Unlocked(AID.HighBlizzard2) && - MP < 400) - QueueGCD(AID.HighBlizzard2, target, GCDPriority.Step1); - } - } - private void AOELv100(Actor? target) //Level 100 AOE rotation - { - if (NoStance) - { - if (Unlocked(AID.HighBlizzard2)) + if (!Unlocked(AID.Freeze) || Player.Level is >= 35 and <= 39) { - if (MP >= 10000) - QueueGCD(AID.HighBlizzard2, target, GCDPriority.NeedB3); - if (MP < 10000 && Player.InCombat) + if (InUmbralIce) { - if (ActionReady(AID.Swiftcast)) - QueueGCD(AID.Swiftcast, target, GCDPriority.NeedB3); - else - QueueGCD(AID.HighBlizzard2, target, GCDPriority.NeedB3); + //Step 1 - max stacks in UI + //TODO: MP tick is not fast enough before next cast, this will cause an extra unnecessary cast + if (Unlocked(AID.Blizzard2) && + MP < 9600) + QueueGCD(AID.Blizzard2, target, GCDPriority.FirstStep); + //Step 2 - swap from UI to AF + if (Unlocked(AID.Fire2) && + MP >= 9600 && + UmbralStacks == 3) + QueueGCD(AID.Fire2, target, GCDPriority.SecondStep); + } + if (InAstralFire) + { + if (MP >= 3000) + QueueGCD(AID.Fire2, target, GCDPriority.FirstStep); + if (Unlocked(AID.Blizzard2) && + MP < 3000) + QueueGCD(AID.Blizzard2, target, GCDPriority.SecondStep); } } - } - if (InUmbralIce) - { - //Step 1 - max stacks in UI - if (Unlocked(AID.HighBlizzard2) && - UmbralStacks < 3) - QueueGCD(AID.HighBlizzard2, target, GCDPriority.Step3); - //Step 2 - Freeze - if (Unlocked(AID.Freeze) && !JustUsed(AID.Freeze, 5f) && - (JustUsed(AID.HighBlizzard2, 5) || MP < 10000)) - QueueGCD(AID.Freeze, target, GCDPriority.Step2); - //Step 3 - swap from UI to AF - if (Unlocked(AID.HighFire2) && - MP >= 10000 && - UmbralStacks == 3) - QueueGCD(AID.HighFire2, target, GCDPriority.Step1); - } - if (InAstralFire) - { - //Step 1 - Flare - if (Unlocked(AID.Flare)) - { - //first cast - if (UmbralHearts == 1) - QueueGCD(AID.Flare, target, GCDPriority.Step3); - //second cast - if (UmbralHearts == 0 && - MP >= 800) - QueueGCD(AID.Flare, target, GCDPriority.Step2); - } - //Step 2 - Flare Star - if (AstralSoulStacks == 6) //if Astral Soul stacks are max - QueueGCD(AID.FlareStar, target, GCDPriority.Step2); //Queue Flare Star - //Step 3 - swap from AF to UI - if (Unlocked(AID.HighBlizzard2) && - MP < 400) - QueueGCD(AID.HighBlizzard2, target, GCDPriority.Step1); - } - } - private void BestAOE(Actor? target) //AOE rotation based on level - { - if (In25y(target)) - { - if (Player.Level is >= 12 and <= 34) - { - AOELv12toLv34(target); - } - if (Player.Level is >= 35 and <= 39) - { - AOELv35toLv39(target); - } - if (Player.Level is >= 40 and <= 49) + if (!Unlocked(AID.Flare) || Player.Level is >= 40 and <= 49) { - AOELv40toLv49(target); + if (InUmbralIce) + { + //Step 1 - max stacks in UI + if (Unlocked(AID.Blizzard2) && + UmbralStacks < 3) + QueueGCD(AID.Blizzard2, target, GCDPriority.FirstStep); + //Step 2 - Freeze + if (Unlocked(AID.Freeze) && !JustUsed(AID.Freeze, 5f) && + (JustUsed(AID.Blizzard2, 5) || MP < 10000)) + QueueGCD(AID.Freeze, target, GCDPriority.SecondStep); + //Step 3 - swap from UI to AF + if (Unlocked(AID.Fire2) && + MP >= 10000 && + UmbralStacks == 3) + QueueGCD(AID.Fire2, target, GCDPriority.ThirdStep); + } + if (InAstralFire) + { + if (MP >= 3000) + QueueGCD(AID.Fire2, target, GCDPriority.FirstStep); + if (Unlocked(AID.Blizzard2) && + MP < 3000) + QueueGCD(AID.Blizzard2, target, GCDPriority.SecondStep); + } } - if (Player.Level is >= 50 and <= 57) + if (!Unlocked(AID.Blizzard4) || Player.Level is >= 50 and <= 57) { - AOELv50toLv57(target); + if (InUmbralIce) + { + //Step 1 - max stacks in UI + if (Unlocked(AID.Blizzard2) && + UmbralStacks < 3) + QueueGCD(AID.Blizzard2, target, GCDPriority.FirstStep); + //Step 2 - Freeze + if (Unlocked(AID.Freeze) && !JustUsed(AID.Freeze, 5f) && + (JustUsed(AID.Blizzard2, 5) || MP < 10000)) + QueueGCD(AID.Freeze, target, GCDPriority.SecondStep); + //Step 3 - swap from UI to AF + if (Unlocked(AID.Fire2) && + MP >= 10000 && + UmbralStacks == 3) + QueueGCD(AID.Fire2, target, GCDPriority.ThirdStep); + } + if (InAstralFire) + { + //Step 1 - spam Fire 2 + if (MP >= 3000) + QueueGCD(AID.Fire2, target, GCDPriority.FirstStep); + //Step 2 - Flare + if (Unlocked(AID.Flare) && + MP < 3000) + QueueGCD(AID.Flare, target, GCDPriority.SecondStep); + //Step 3 - swap from AF to UI + if (Unlocked(AID.Blizzard2) && + (!Unlocked(AID.Flare) && MP < 3000) || //do your job quests, fool + (Unlocked(AID.Flare) && MP < 400)) + QueueGCD(AID.Blizzard2, target, MP < 400 ? GCDPriority.ForcedStep : GCDPriority.ThirdStep); + } } - if (Player.Level is >= 58 and <= 81) + if (!Unlocked(AID.HighBlizzard2) || Player.Level is >= 58 and <= 81) { - AOELv58toLv81(target); + if (InUmbralIce) + { + //Step 1 - max stacks in UI + if (Unlocked(AID.Blizzard2) && + UmbralStacks < 3) + QueueGCD(AID.Blizzard2, target, GCDPriority.FirstStep); + //Step 2 - Freeze + if (Unlocked(AID.Freeze) && !JustUsed(AID.Freeze, 5f) && + (JustUsed(AID.Blizzard2, 5) || MP < 10000)) + QueueGCD(AID.Freeze, target, GCDPriority.SecondStep); + //Step 3 - swap from UI to AF + if (Unlocked(AID.Fire2) && + MP >= 10000 && + UmbralStacks == 3) + QueueGCD(AID.Fire2, target, GCDPriority.ThirdStep); + } + if (InAstralFire) + { + //Step 1 - spam Fire 2 + if (UmbralHearts > 1) + QueueGCD(AID.Fire2, target, GCDPriority.FirstStep); + //Step 2 - Flare + if (Unlocked(AID.Flare)) + { + //first cast + if (UmbralHearts == 1) + QueueGCD(AID.Flare, target, GCDPriority.SecondStep); + //second cast + if (UmbralHearts == 0 && + MP >= 800) + QueueGCD(AID.Flare, target, GCDPriority.ThirdStep); + } + //Step 3 - swap from AF to UI + if (Unlocked(AID.Blizzard2) && + MP < 400) + QueueGCD(AID.Blizzard2, target, GCDPriority.FourthStep); + } } - if (Player.Level is >= 82 and <= 99) + if (!Unlocked(AID.FlareStar) || Player.Level is >= 82 and <= 99) { - AOELv82toLv99(target); + if (InUmbralIce) + { + //Step 1 - max stacks in UI + if (Unlocked(AID.HighBlizzard2) && + UmbralStacks < 3) + QueueGCD(AID.HighBlizzard2, target, GCDPriority.FirstStep); + //Step 2 - Freeze + if (Unlocked(AID.Freeze) && !JustUsed(AID.Freeze, 5f) && + (JustUsed(AID.HighBlizzard2, 5) || MP < 10000)) + QueueGCD(AID.Freeze, target, GCDPriority.SecondStep); + //Step 3 - swap from UI to AF + if (Unlocked(AID.HighFire2) && + MP >= 10000 && + UmbralStacks == 3) + QueueGCD(AID.HighFire2, target, GCDPriority.ThirdStep); + } + if (InAstralFire) + { + //Step 1 - spam Fire 2 + if (MP > 5500) + QueueGCD(AID.HighFire2, target, GCDPriority.FirstStep); + //Step 2 - Flare + if (Unlocked(AID.Flare)) + { + //first cast + if (UmbralHearts == 1) + QueueGCD(AID.Flare, target, GCDPriority.SecondStep); + //second cast + if (UmbralHearts == 0 && + MP >= 800) + QueueGCD(AID.Flare, target, GCDPriority.ThirdStep); + } + //Step 3 - swap from AF to UI + if (Unlocked(AID.HighBlizzard2) && + MP < 400) + QueueGCD(AID.HighBlizzard2, target, GCDPriority.ThirdStep); + } } - if (Player.Level is 100) + if (Unlocked(AID.FlareStar) || Player.Level is 100) { - AOELv100(target); + if (InUmbralIce) + { + //Step 1 - max stacks in UI + if (Unlocked(AID.HighBlizzard2) && + UmbralStacks < 3) + QueueGCD(AID.HighBlizzard2, target, GCDPriority.FirstStep); + //Step 2 - Freeze + if (Unlocked(AID.Freeze) && !JustUsed(AID.Freeze, 5f) && + (JustUsed(AID.HighBlizzard2, 5) || MP < 10000)) + QueueGCD(AID.Freeze, target, GCDPriority.SecondStep); + //Step 3 - swap from UI to AF + if (Unlocked(AID.HighFire2) && + MP >= 10000 && + UmbralStacks == 3) + QueueGCD(AID.HighFire2, target, GCDPriority.ThirdStep); + } + if (InAstralFire) + { + //Step 1 - Flare + if (Unlocked(AID.Flare)) + { + //first cast + if (UmbralHearts == 1) + QueueGCD(AID.Flare, target, GCDPriority.FirstStep); + //second cast + if (UmbralHearts == 0 && + MP >= 800) + QueueGCD(AID.Flare, target, GCDPriority.SecondStep); + } + //Step 2 - Flare Star + if (AstralSoulStacks == 6) //if Astral Soul stacks are max + QueueGCD(AID.FlareStar, target, GCDPriority.ThirdStep); //Queue Flare Star + //Step 3 - swap from AF to UI + if (Unlocked(AID.HighBlizzard2) && + MP < 400) + QueueGCD(AID.HighBlizzard2, target, GCDPriority.FourthStep); + } } } } From f7e711c9b7ccf924311e5c926a255e74ec1fff0c Mon Sep 17 00:00:00 2001 From: AceAkechi123 Date: Sun, 19 Jan 2025 05:25:36 -0800 Subject: [PATCH 02/12] forgot this --- BossMod/Autorotation/akechi/AkechiBLM.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/BossMod/Autorotation/akechi/AkechiBLM.cs b/BossMod/Autorotation/akechi/AkechiBLM.cs index dc0449f05..25c8f7ef8 100644 --- a/BossMod/Autorotation/akechi/AkechiBLM.cs +++ b/BossMod/Autorotation/akechi/AkechiBLM.cs @@ -658,7 +658,7 @@ public override void Execute(StrategyValues strategy, Actor? primaryTarget, floa TargetChoice(thunder) ?? primaryTarget, ThunderLeft < 3 ? GCDPriority.NeedDOT : GCDPriority.DOT); - if (AOEStrategy is AOEStrategy.ForceAOE) + if (forceAOE) QueueGCD(BestThunderAOE, TargetChoice(thunder) ?? primaryTarget ?? BestAOETarget, ThunderLeft < 3 ? GCDPriority.NeedDOT : From 104f9b115d074f3429b36dadc277ed05a9cbcdf3 Mon Sep 17 00:00:00 2001 From: AceAkechi123 Date: Sun, 19 Jan 2025 05:29:02 -0800 Subject: [PATCH 03/12] good to go for now, more opti later --- BossMod/Autorotation/akechi/AkechiBLM.cs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/BossMod/Autorotation/akechi/AkechiBLM.cs b/BossMod/Autorotation/akechi/AkechiBLM.cs index 25c8f7ef8..94f914d4e 100644 --- a/BossMod/Autorotation/akechi/AkechiBLM.cs +++ b/BossMod/Autorotation/akechi/AkechiBLM.cs @@ -655,7 +655,7 @@ public override void Execute(StrategyValues strategy, Actor? primaryTarget, floa GCDPriority.DOT); if (forceST) QueueGCD(BestThunderST, - TargetChoice(thunder) ?? primaryTarget, + TargetChoice(thunder) ?? primaryTarget ?? BestAOETarget, ThunderLeft < 3 ? GCDPriority.NeedDOT : GCDPriority.DOT); if (forceAOE) @@ -681,7 +681,7 @@ or PolyglotStrategy.XenoHold1 or PolyglotStrategy.XenoHold2 or PolyglotStrategy.XenoHold3) QueueGCD(BestXenoglossy, - TargetChoice(polyglot) ?? primaryTarget, + TargetChoice(polyglot) ?? primaryTarget ?? BestAOETarget, polyglotStrat is PolyglotStrategy.ForceXeno ? GCDPriority.ForcedGCD : Polyglots == MaxPolyglots && EnochianTimer <= 5000 ? GCDPriority.NeedPolyglot : GCDPriority.Polyglot); @@ -736,11 +736,13 @@ or ManafontStrategy.ForceWeaveEX ? OGCDPriority.ForcedOGCD : OGCDPriority.Manafont); //Retrace + //TODO: more options? if (ShouldUseRetrace(retraceStrat)) QueueOGCD(AID.Retrace, Player, OGCDPriority.ForcedOGCD); //Between the Lines + //TODO: Utility maybe? if (ShouldUseBTL(btlStrat)) QueueOGCD(AID.BetweenTheLines, Player, From 30c2105a5a625f78a58ea35479a5d2befd247cc6 Mon Sep 17 00:00:00 2001 From: AceAkechi123 Date: Sun, 19 Jan 2025 05:37:25 -0800 Subject: [PATCH 04/12] this caused issues oops --- BossMod/Autorotation/akechi/AkechiBLM.cs | 26 ++++++++++++------------ 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/BossMod/Autorotation/akechi/AkechiBLM.cs b/BossMod/Autorotation/akechi/AkechiBLM.cs index 94f914d4e..7ab3b5f59 100644 --- a/BossMod/Autorotation/akechi/AkechiBLM.cs +++ b/BossMod/Autorotation/akechi/AkechiBLM.cs @@ -837,7 +837,7 @@ private void BestST(Actor? target) //Single-target rotation based on level QueueGCD(AID.Blizzard3, target, GCDPriority.NeedB3); //Queue Blizzard III } } - if (!Unlocked(AID.Blizzard3) || Player.Level is >= 1 and <= 34) + if (Player.Level is >= 1 and <= 34) { //Fire if (Unlocked(AID.Fire1) && //if Fire is unlocked @@ -855,7 +855,7 @@ private void BestST(Actor? target) //Single-target rotation based on level QueueOGCD(AID.Transpose, Player, OGCDPriority.Transpose); //Queue Transpose } - if (!Unlocked(AID.Fire4) || Player.Level is >= 35 and <= 59) + if (Player.Level is >= 35 and <= 59) { if (InUmbralIce) //if Umbral Ice is active { @@ -889,7 +889,7 @@ private void BestST(Actor? target) //Single-target rotation based on level QueueGCD(AID.Blizzard3, target, GCDPriority.SecondStep); //Queue Blizzard III } } - if (!Unlocked(AID.Despair) || Player.Level is >= 60 and <= 71) + if (Player.Level is >= 60 and <= 71) { if (InUmbralIce) //if Umbral Ice is active { @@ -921,7 +921,7 @@ private void BestST(Actor? target) //Single-target rotation based on level QueueGCD(AID.Blizzard3, target, GCDPriority.ThirdStep); //Queue Blizzard III } } - if (!Unlocked(AID.Paradox) || Player.Level is >= 72 and <= 89) + if (Player.Level is >= 72 and <= 89) { if (InUmbralIce) //if Umbral Ice is active { @@ -957,7 +957,7 @@ private void BestST(Actor? target) //Single-target rotation based on level QueueGCD(AID.Blizzard3, target, GCDPriority.FourthStep); //Queue Blizzard III } } - if (!Unlocked(AID.FlareStar) || Player.Level is >= 90 and <= 99) + if (Player.Level is >= 90 and <= 99) { if (InUmbralIce) //if Umbral Ice is active { @@ -999,7 +999,7 @@ private void BestST(Actor? target) //Single-target rotation based on level QueueGCD(AID.Blizzard3, target, GCDPriority.FourthStep); //Queue Blizzard III } } - if (!Unlocked(AID.FlareStar) || Player.Level is 100) + if (Player.Level is 100) { if (InUmbralIce) //if Umbral Ice is active { @@ -1073,7 +1073,7 @@ private void BestAOE(Actor? target) //AOE rotation based on level QueueGCD(AID.HighBlizzard2, target, GCDPriority.NeedB3); } } - if (!Unlocked(AID.Blizzard3) || Player.Level is >= 12 and <= 35) + if (Player.Level is >= 12 and <= 35) { //Fire if (Unlocked(AID.Fire2) && //if Fire is unlocked @@ -1093,7 +1093,7 @@ private void BestAOE(Actor? target) //AOE rotation based on level if (InAstralFire && !Unlocked(AID.Fire2)) QueueOGCD(AID.Transpose, Player, OGCDPriority.Transpose); } - if (!Unlocked(AID.Freeze) || Player.Level is >= 35 and <= 39) + if (Player.Level is >= 35 and <= 39) { if (InUmbralIce) { @@ -1117,7 +1117,7 @@ private void BestAOE(Actor? target) //AOE rotation based on level QueueGCD(AID.Blizzard2, target, GCDPriority.SecondStep); } } - if (!Unlocked(AID.Flare) || Player.Level is >= 40 and <= 49) + if (Player.Level is >= 40 and <= 49) { if (InUmbralIce) { @@ -1144,7 +1144,7 @@ private void BestAOE(Actor? target) //AOE rotation based on level QueueGCD(AID.Blizzard2, target, GCDPriority.SecondStep); } } - if (!Unlocked(AID.Blizzard4) || Player.Level is >= 50 and <= 57) + if (Player.Level is >= 50 and <= 57) { if (InUmbralIce) { @@ -1178,7 +1178,7 @@ private void BestAOE(Actor? target) //AOE rotation based on level QueueGCD(AID.Blizzard2, target, MP < 400 ? GCDPriority.ForcedStep : GCDPriority.ThirdStep); } } - if (!Unlocked(AID.HighBlizzard2) || Player.Level is >= 58 and <= 81) + if (Player.Level is >= 58 and <= 81) { if (InUmbralIce) { @@ -1218,7 +1218,7 @@ private void BestAOE(Actor? target) //AOE rotation based on level QueueGCD(AID.Blizzard2, target, GCDPriority.FourthStep); } } - if (!Unlocked(AID.FlareStar) || Player.Level is >= 82 and <= 99) + if (Player.Level is >= 82 and <= 99) { if (InUmbralIce) { @@ -1258,7 +1258,7 @@ private void BestAOE(Actor? target) //AOE rotation based on level QueueGCD(AID.HighBlizzard2, target, GCDPriority.ThirdStep); } } - if (Unlocked(AID.FlareStar) || Player.Level is 100) + if (Player.Level is 100) { if (InUmbralIce) { From 1aaff43d7d3ed052890f1439eb0810f1ff4e52c8 Mon Sep 17 00:00:00 2001 From: AceAkechi123 Date: Sun, 19 Jan 2025 05:43:36 -0800 Subject: [PATCH 05/12] Scathe has too much prio here, adjusted this --- BossMod/Autorotation/akechi/AkechiBLM.cs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/BossMod/Autorotation/akechi/AkechiBLM.cs b/BossMod/Autorotation/akechi/AkechiBLM.cs index 7ab3b5f59..6b1d151e3 100644 --- a/BossMod/Autorotation/akechi/AkechiBLM.cs +++ b/BossMod/Autorotation/akechi/AkechiBLM.cs @@ -560,12 +560,15 @@ public override void Execute(StrategyValues strategy, Actor? primaryTarget, floa : PlayerHasEffect(SID.Firestarter, 30) ? AID.Fire3 : hasThunderhead ? (forceST ? BestThunderST : forceAOE ? BestThunderAOE : BestThunder) - : AID.Scathe, + : BestThunder, Polyglots > 0 ? TargetChoice(polyglot) ?? BestAOETarget ?? primaryTarget : PlayerHasEffect(SID.Firestarter, 30) ? TargetChoice(AOE) ?? primaryTarget : hasThunderhead ? TargetChoice(thunder) ?? BestAOETarget ?? primaryTarget : primaryTarget, GCDPriority.Moving1); + if (CD(AID.Swiftcast) > 2f && //if Swiftcast is on cooldown + CD(AID.Triplecast) > 62f) //and Triplecast is not active + QueueGCD(AID.Scathe, primaryTarget, GCDPriority.Moving1); //use Scathe //OGCDs if (ActionReady(AID.Swiftcast) && !PlayerHasEffect(SID.Triplecast, 15)) From de8e7020923980cb17370fcc5d3d13ac3c75ed4f Mon Sep 17 00:00:00 2001 From: AceAkechi123 Date: Sun, 19 Jan 2025 05:52:07 -0800 Subject: [PATCH 06/12] wait what? --- BossMod/Autorotation/akechi/AkechiBLM.cs | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/BossMod/Autorotation/akechi/AkechiBLM.cs b/BossMod/Autorotation/akechi/AkechiBLM.cs index 6b1d151e3..e8d9ea8b6 100644 --- a/BossMod/Autorotation/akechi/AkechiBLM.cs +++ b/BossMod/Autorotation/akechi/AkechiBLM.cs @@ -558,9 +558,10 @@ public override void Execute(StrategyValues strategy, Actor? primaryTarget, floa Unlocked(TraitID.EnhancedPolyglot) && Polyglots > 0 ? (forceST ? BestXenoglossy : forceAOE ? AID.Foul : BestPolyglot) : PlayerHasEffect(SID.Firestarter, 30) ? AID.Fire3 - : hasThunderhead ? - (forceST ? BestThunderST : forceAOE ? BestThunderAOE : BestThunder) - : BestThunder, + : hasThunderhead ? (forceST ? BestThunderST : forceAOE ? BestThunderAOE : BestThunder) + : ActionReady(AID.Swiftcast) && !PlayerHasEffect(SID.Triplecast, 15) ? AID.Swiftcast + : Unlocked(AID.Triplecast) && CD(AID.Triplecast) <= 60 && !PlayerHasEffect(SID.Triplecast, 15) && !PlayerHasEffect(SID.Swiftcast, 10) ? AID.Triplecast + : AID.Scathe, Polyglots > 0 ? TargetChoice(polyglot) ?? BestAOETarget ?? primaryTarget : PlayerHasEffect(SID.Firestarter, 30) ? TargetChoice(AOE) ?? primaryTarget : hasThunderhead ? TargetChoice(thunder) ?? BestAOETarget ?? primaryTarget From df275f4e005448fe34130fe0f5780dfd97c64210 Mon Sep 17 00:00:00 2001 From: ace Date: Sun, 19 Jan 2025 10:42:12 -0800 Subject: [PATCH 07/12] cooked --- BossMod/Autorotation/akechi/AkechiBLM.cs | 143 ++++++++++------------- 1 file changed, 60 insertions(+), 83 deletions(-) diff --git a/BossMod/Autorotation/akechi/AkechiBLM.cs b/BossMod/Autorotation/akechi/AkechiBLM.cs index e8d9ea8b6..4e06f8d6d 100644 --- a/BossMod/Autorotation/akechi/AkechiBLM.cs +++ b/BossMod/Autorotation/akechi/AkechiBLM.cs @@ -1,4 +1,9 @@ -using FFXIVClientStructs.FFXIV.Client.Game.Gauge; +using Dalamud.Game.ClientState.Objects.Types; +using Dalamud.Plugin.Services; +using FFXIVClientStructs.FFXIV.Client.Game.Gauge; +using System; +using static BossMod.ActorState; +using static FFXIVClientStructs.FFXIV.Client.UI.Misc.DataCenterHelper; using AID = BossMod.BLM.AID; using SID = BossMod.BLM.SID; using TraitID = BossMod.BLM.TraitID; @@ -357,7 +362,8 @@ private AID BestXenoglossy private bool In25y(Actor? target) => Player.DistanceToHitbox(target) <= 24.99f; //Check if the target is within 25 yalms private bool ActionReady(AID aid) => Unlocked(aid) && CD(aid) < 0.6f; //Check if the desired action is unlocked and is ready (cooldown less than 0.6 seconds) private bool PlayerHasEffect(SID sid, float duration) => SelfStatusLeft(sid, duration) > GCD; //Checks if Status effect is on self - public float GetActualCastTime(AID aid) => ActionDefinitions.Instance.Spell(aid)!.CastTime * SpS / 2.5f; + private float GetCurrentCastTime(AID aid) => ActionDefinitions.Instance.Spell(aid)!.CastTime; //Get the current cast time for the specified action + public float GetActualCastTime(AID aid) => GetCurrentCastTime(aid) * SpS / 2.5f; public float GetCastTime(AID aid) { var aspect = ActionDefinitions.Instance.Spell(aid)!.Aspect; @@ -384,49 +390,15 @@ private bool JustUsed(AID aid, float variance) } #region Targeting - private bool ShouldUseAOE - { - get - { - var bestTarget = BestAOETarget; - if (bestTarget != null) - { - var minimumTargetsForAOE = 2; - float splashPriorityFunc(Actor actor) - { - var distanceToPlayer = actor.DistanceToHitbox(Player); - if (distanceToPlayer <= 24.99f) - { - var targetsInSplashRadius = 0; - foreach (var enemy in Hints.PriorityTargets) - { - var targetActor = enemy.Actor; - if (targetActor != actor && targetActor.Position.InCircle(actor.Position, 5f)) - { - targetsInSplashRadius++; - } - } - return targetsInSplashRadius; - } - return float.MinValue; - } - - var (_, bestPrio) = FindBetterTargetBy(null, 25f, splashPriorityFunc); - - return bestPrio >= minimumTargetsForAOE; - } - - return false; - } - } - private int TargetsInRange() => Hints.NumPriorityTargetsInAOECircle(Player.Position, 25); //Returns the number of targets hit by AOE within a 25-yalm radius around the player + private int TargetsInRange() => Hints.NumPriorityTargetsInAOECircle(Player.Position, 26); //Returns the number of targets within 26-yalm radius around the player + private bool ShouldUseAOE => TargetsInRange() >= 3; //Check if we should use AOE private Actor? TargetChoice(StrategyValues.OptionRef strategy) => ResolveTargetOverride(strategy.Value); //Resolves the target choice based on the strategy - private Actor? FindBestSplashTarget() + private Actor? FindBestTarget() { - float splashPriorityFunc(Actor actor) + float AOEPriorityFunc(Actor actor) { var distanceToPlayer = actor.DistanceToHitbox(Player); - if (distanceToPlayer <= 24f) + if (distanceToPlayer <= 24.99f) { var targetsInSplashRadius = 0; foreach (var enemy in Hints.PriorityTargets) @@ -437,16 +409,17 @@ float splashPriorityFunc(Actor actor) targetsInSplashRadius++; } } - return targetsInSplashRadius; + return targetsInSplashRadius * 10 - actor.HPMP.CurHP * 0.01f; } return float.MinValue; } + float STPriorityFunc(Actor actor) => actor.HPMP.CurHP > 0 ? 1f / actor.HPMP.CurHP : float.MinValue; - var (bestTarget, bestPrio) = FindBetterTargetBy(null, 25f, splashPriorityFunc); - - return bestTarget; + var (bestAOETarget, bestAOEPrio) = FindBetterTargetBy(null, 25f, STPriorityFunc); + var (bestTarget, bestPrio) = FindBetterTargetBy(bestAOETarget, 25f, AOEPriorityFunc); + return ShouldUseAOE ? bestAOETarget : bestTarget; } - private Actor? BestAOETarget => FindBestSplashTarget(); // Find the best target for splash attack + private Actor? BestTarget => FindBestTarget(); // Find the best target for splash attack //TODO: BestDOTTarget #endregion @@ -536,11 +509,11 @@ public override void Execute(StrategyValues strategy, Actor? primaryTarget, floa (Unlocked(TraitID.EnhancedAstralFire) && MP is < 1600 and not 0)))) //instant cast Despair { if (AOEStrategy is AOEStrategy.Auto) - BestRotation(TargetChoice(AOE) ?? primaryTarget ?? BestAOETarget); //target prio is user choice -> current target -> best AOE target + BestRotation(TargetChoice(AOE) ?? BestTarget ?? primaryTarget); //target prio is user choice -> current target -> best AOE target if (forceST) BestST(TargetChoice(AOE) ?? primaryTarget); //target prio is user choice -> current target if (forceAOE) - BestAOE(TargetChoice(AOE) ?? primaryTarget ?? BestAOETarget); //target prio is user choice -> best AOE target -> current target + BestAOE(TargetChoice(AOE) ?? primaryTarget); //target prio is user choice -> best AOE target -> current target } #endregion @@ -551,34 +524,38 @@ public override void Execute(StrategyValues strategy, Actor? primaryTarget, floa { if (movementStrat is MovementStrategy.Allow) { - //GCDs - if (!PlayerHasEffect(SID.Swiftcast, 10) || - !PlayerHasEffect(SID.Triplecast, 15)) - QueueGCD( - Unlocked(TraitID.EnhancedPolyglot) && Polyglots > 0 ? - (forceST ? BestXenoglossy : forceAOE ? AID.Foul : BestPolyglot) - : PlayerHasEffect(SID.Firestarter, 30) ? AID.Fire3 - : hasThunderhead ? (forceST ? BestThunderST : forceAOE ? BestThunderAOE : BestThunder) - : ActionReady(AID.Swiftcast) && !PlayerHasEffect(SID.Triplecast, 15) ? AID.Swiftcast - : Unlocked(AID.Triplecast) && CD(AID.Triplecast) <= 60 && !PlayerHasEffect(SID.Triplecast, 15) && !PlayerHasEffect(SID.Swiftcast, 10) ? AID.Triplecast - : AID.Scathe, - Polyglots > 0 ? TargetChoice(polyglot) ?? BestAOETarget ?? primaryTarget - : PlayerHasEffect(SID.Firestarter, 30) ? TargetChoice(AOE) ?? primaryTarget - : hasThunderhead ? TargetChoice(thunder) ?? BestAOETarget ?? primaryTarget - : primaryTarget, - GCDPriority.Moving1); - if (CD(AID.Swiftcast) > 2f && //if Swiftcast is on cooldown - CD(AID.Triplecast) > 62f) //and Triplecast is not active - QueueGCD(AID.Scathe, primaryTarget, GCDPriority.Moving1); //use Scathe + // GCDs + if (!PlayerHasEffect(SID.Swiftcast, 10) || !PlayerHasEffect(SID.Triplecast, 15)) + { + if (Unlocked(TraitID.EnhancedPolyglot) && Polyglots > 0) + QueueGCD(forceST ? BestXenoglossy : forceAOE ? AID.Foul : BestPolyglot, + TargetChoice(polyglot) ?? primaryTarget ?? BestTarget, + GCDPriority.Moving1); + + if (PlayerHasEffect(SID.Firestarter, 30)) + QueueGCD(AID.Fire3, + TargetChoice(AOE) ?? primaryTarget ?? BestTarget, + GCDPriority.Moving1); + + if (hasThunderhead) + QueueGCD(forceST ? BestThunderST : forceAOE ? BestThunderAOE : BestThunder, + TargetChoice(thunder) ?? primaryTarget ?? BestTarget, + GCDPriority.Moving1); + + if (MP >= 800 && + CD(AID.Swiftcast) > 2f && //if Swiftcast is on cooldown + CD(AID.Triplecast) > 62f) //and Triplecast is not active + QueueGCD(AID.Scathe, TargetChoice(AOE) ?? primaryTarget ?? BestTarget, GCDPriority.SixthStep); //use Scathe + } //OGCDs if (ActionReady(AID.Swiftcast) && !PlayerHasEffect(SID.Triplecast, 15)) - QueueOGCD(AID.Swiftcast, Player, GCDPriority.Moving2); + QueueOGCD(AID.Swiftcast, Player, GCDPriority.Moving1); if (Unlocked(AID.Triplecast) && CD(AID.Triplecast) <= 60 && !PlayerHasEffect(SID.Triplecast, 15) && !PlayerHasEffect(SID.Swiftcast, 10)) - QueueOGCD(AID.Triplecast, Player, GCDPriority.Moving3); + QueueOGCD(AID.Triplecast, Player, GCDPriority.Moving1); } if (movementStrat is MovementStrategy.OnlyGCDs) { @@ -592,9 +569,9 @@ public override void Execute(StrategyValues strategy, Actor? primaryTarget, floa : hasThunderhead ? (forceST ? BestThunderST : forceAOE ? BestThunderAOE : BestThunder) : AID.Scathe, - Polyglots > 0 ? TargetChoice(polyglot) ?? BestAOETarget ?? primaryTarget - : PlayerHasEffect(SID.Firestarter, 30) ? TargetChoice(AOE) ?? primaryTarget - : hasThunderhead ? TargetChoice(thunder) ?? BestAOETarget ?? primaryTarget + Polyglots > 0 ? TargetChoice(polyglot) ?? primaryTarget ?? BestTarget + : PlayerHasEffect(SID.Firestarter, 30) ? TargetChoice(AOE) ?? primaryTarget ?? BestTarget + : hasThunderhead ? TargetChoice(thunder) ?? primaryTarget ?? BestTarget : primaryTarget, GCDPriority.Moving1); } @@ -611,7 +588,7 @@ public override void Execute(StrategyValues strategy, Actor? primaryTarget, floa if (movementStrat is MovementStrategy.OnlyScathe) { if (Unlocked(AID.Scathe) && MP >= 800) - QueueGCD(AID.Scathe, primaryTarget, GCDPriority.Moving1); + QueueGCD(AID.Scathe, TargetChoice(AOE) ?? primaryTarget ?? BestTarget, GCDPriority.Moving1); } } #endregion @@ -654,17 +631,17 @@ public override void Execute(StrategyValues strategy, Actor? primaryTarget, floa { if (AOEStrategy is AOEStrategy.Auto) QueueGCD(BestThunder, - TargetChoice(thunder) ?? primaryTarget ?? BestAOETarget, + TargetChoice(thunder) ?? primaryTarget ?? BestTarget, ThunderLeft < 3 ? GCDPriority.NeedDOT : GCDPriority.DOT); if (forceST) QueueGCD(BestThunderST, - TargetChoice(thunder) ?? primaryTarget ?? BestAOETarget, + TargetChoice(thunder) ?? primaryTarget ?? BestTarget, ThunderLeft < 3 ? GCDPriority.NeedDOT : GCDPriority.DOT); if (forceAOE) QueueGCD(BestThunderAOE, - TargetChoice(thunder) ?? primaryTarget ?? BestAOETarget, + TargetChoice(thunder) ?? primaryTarget ?? BestTarget, ThunderLeft < 3 ? GCDPriority.NeedDOT : GCDPriority.DOT); } @@ -676,7 +653,7 @@ or PolyglotStrategy.AutoHold1 or PolyglotStrategy.AutoHold2 or PolyglotStrategy.AutoHold3) QueueGCD(BestPolyglot, - TargetChoice(polyglot) ?? primaryTarget ?? BestAOETarget, + TargetChoice(polyglot) ?? primaryTarget ?? BestTarget, polyglotStrat is PolyglotStrategy.ForceXeno ? GCDPriority.ForcedGCD : Polyglots == MaxPolyglots && EnochianTimer <= 5000 ? GCDPriority.NeedPolyglot : GCDPriority.Polyglot); @@ -685,7 +662,7 @@ or PolyglotStrategy.XenoHold1 or PolyglotStrategy.XenoHold2 or PolyglotStrategy.XenoHold3) QueueGCD(BestXenoglossy, - TargetChoice(polyglot) ?? primaryTarget ?? BestAOETarget, + TargetChoice(polyglot) ?? primaryTarget ?? BestTarget, polyglotStrat is PolyglotStrategy.ForceXeno ? GCDPriority.ForcedGCD : Polyglots == MaxPolyglots && EnochianTimer <= 5000 ? GCDPriority.NeedPolyglot : GCDPriority.Polyglot); @@ -694,7 +671,7 @@ or PolyglotStrategy.FoulHold1 or PolyglotStrategy.FoulHold2 or PolyglotStrategy.FoulHold3) QueueGCD(AID.Foul, - TargetChoice(polyglot) ?? primaryTarget ?? BestAOETarget, + TargetChoice(polyglot) ?? primaryTarget ?? BestTarget, polyglotStrat is PolyglotStrategy.ForceFoul ? GCDPriority.ForcedGCD : Polyglots == MaxPolyglots && EnochianTimer <= 5000 ? GCDPriority.NeedPolyglot : GCDPriority.Polyglot); @@ -943,20 +920,20 @@ private void BestST(Actor? target) //Single-target rotation based on level //Step 1-3, 5-7 - Fire IV if (MP >= 1600) //and MP is 1600 or more QueueGCD(AID.Fire4, target, GCDPriority.FirstStep); //Queue Fire IV - //Step 4A - Fire 1 + //Step 4A - Fire 1 if (ElementTimer <= (GetCastTime(AID.Fire1) * 3) && //if time remaining on current element is less than 3x GCDs MP >= 4000) //and MP is 4000 or more QueueGCD(AID.Fire1, target, ElementTimer <= (GetCastTime(AID.Fire1) * 3) && MP >= 4000 ? GCDPriority.Paradox : GCDPriority.SecondStep); //Queue Fire I, increase priority if less than 3s left on element - //Step 4B - F3P + //Step 4B - F3P if (SelfStatusLeft(SID.Firestarter, 30) is < 25 and not 0 && //if Firestarter buff is active and not 0 AstralStacks == 3) //and Umbral Hearts are 0 QueueGCD(AID.Fire3, target, GCDPriority.ForcedStep); //Queue Fire III (AF3 F3P) - //Step 8 - Despair + //Step 8 - Despair if (Unlocked(AID.Despair) && //if Despair is unlocked ((MP is < 1600 and >= 800) || //if MP is less than 1600 and not 0 (MP is <= 4000 and >= 800 && ElementTimer <= (GetCastTime(AID.Despair) * 2)))) //or if we dont have enough time for last F4s QueueGCD(AID.Despair, target, ElementTimer <= (GetCastTime(AID.Despair) * 2) ? GCDPriority.ForcedGCD : GCDPriority.ThirdStep); //Queue Despair - //Step 9 - swap from AF to UI + //Step 9 - swap from AF to UI if (MP <= 400) //and MP is less than 400 QueueGCD(AID.Blizzard3, target, GCDPriority.FourthStep); //Queue Blizzard III } From fb8a08ac3481017f09c5cf315db56098435ee7ea Mon Sep 17 00:00:00 2001 From: ace Date: Sun, 19 Jan 2025 10:43:30 -0800 Subject: [PATCH 08/12] pesky little shits --- BossMod/Autorotation/akechi/AkechiBLM.cs | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/BossMod/Autorotation/akechi/AkechiBLM.cs b/BossMod/Autorotation/akechi/AkechiBLM.cs index 4e06f8d6d..4d1659c7f 100644 --- a/BossMod/Autorotation/akechi/AkechiBLM.cs +++ b/BossMod/Autorotation/akechi/AkechiBLM.cs @@ -1,9 +1,4 @@ -using Dalamud.Game.ClientState.Objects.Types; -using Dalamud.Plugin.Services; -using FFXIVClientStructs.FFXIV.Client.Game.Gauge; -using System; -using static BossMod.ActorState; -using static FFXIVClientStructs.FFXIV.Client.UI.Misc.DataCenterHelper; +using FFXIVClientStructs.FFXIV.Client.Game.Gauge; using AID = BossMod.BLM.AID; using SID = BossMod.BLM.SID; using TraitID = BossMod.BLM.TraitID; From 1b3ab0f5b778cf5d742b194313b91c4fd9c5c987 Mon Sep 17 00:00:00 2001 From: ace Date: Sun, 19 Jan 2025 15:13:59 -0800 Subject: [PATCH 09/12] some opti stuff, Fire III opener --- BossMod/Autorotation/akechi/AkechiBLM.cs | 38 ++++++++++++++---------- 1 file changed, 23 insertions(+), 15 deletions(-) diff --git a/BossMod/Autorotation/akechi/AkechiBLM.cs b/BossMod/Autorotation/akechi/AkechiBLM.cs index 4d1659c7f..61ce51109 100644 --- a/BossMod/Autorotation/akechi/AkechiBLM.cs +++ b/BossMod/Autorotation/akechi/AkechiBLM.cs @@ -284,6 +284,9 @@ public static RotationModuleDefinition Definition() //Forced ForcedGCD = 900, //Forced GCDs + + //Opener + Opener = 1000, //Opener } public enum OGCDPriority //priorities for oGCDs (higher number = higher priority) { @@ -348,6 +351,7 @@ private AID BestXenoglossy public bool canWeaveLate; //Can late weave oGCDs public float SpS; //Current GCD length, adjusted by spell speed/haste (2.5s baseline) public AID NextGCD; //Next global cooldown action to be used + public bool canOpen; //Can use opener #endregion #region Module Helpers @@ -461,7 +465,10 @@ public override void Execute(StrategyValues strategy, Actor? primaryTarget, floa canWeaveLate = GCD is <= 1.25f and >= 0.1f; //Can weave in oGCDs late SpS = ActionSpeed.GCDRounded(World.Client.PlayerStats.SpellSpeed, World.Client.PlayerStats.Haste, Player.Level); //GCD based on spell speed and haste NextGCD = AID.None; //Next global cooldown action to be used - + canOpen = CD(AID.LeyLines) <= 120 + && CD(AID.Triplecast) <= 0.1f + && CD(AID.Manafont) <= 0.1f + && CD(AID.Amplifier) <= 0.1f; #region Strategy Definitions var AOE = strategy.Option(Track.AOE); //AOE track var AOEStrategy = AOE.As(); //AOE strategy @@ -599,7 +606,8 @@ public override void Execute(StrategyValues strategy, Actor? primaryTarget, floa (tpusStrat == TPUSStrategy.Allow && (!Player.InCombat || Player.InCombat && TargetsInRange() is 0)) || (tpusStrat == TPUSStrategy.OOConly && !Player.InCombat)) { - if (CD(AID.Transpose) < 0.6f && + if (Player.Level < 35 && + CD(AID.Transpose) < 0.6f && (InAstralFire || InUmbralIce)) QueueOGCD(AID.Transpose, Player, OGCDPriority.Transpose); } @@ -788,7 +796,7 @@ public bool QueueAction(AID aid, Actor? target, float priority, float delay) #endregion #region Rotation Helpers - private void BestRotation(Actor? target) //Best rotation based on targets nearby + private void BestRotation(Actor? target) { if (ShouldUseAOE) { @@ -805,13 +813,13 @@ private void BestST(Actor? target) //Single-target rotation based on level { if (NoStance) //if no stance is active { - if (Unlocked(AID.Blizzard3)) //if Blizzard III is unlocked - { - if (MP >= 10000) //if no stance is active and MP is max (opener) - QueueGCD(AID.Blizzard3, target, GCDPriority.NeedB3); //Queue Blizzard III - if (MP < 10000 && Player.InCombat) //or if in combat and no stance is active and MP is less than max (died or stopped attacking) - QueueGCD(AID.Blizzard3, target, GCDPriority.NeedB3); //Queue Blizzard III - } + if (Unlocked(AID.Blizzard3) && + MP < 9600 && + Player.InCombat) //if Blizzard III is unlocked + QueueGCD(AID.Blizzard3, target, GCDPriority.NeedB3); //Queue Blizzard III + if (Unlocked(AID.Fire3) && + (CD(AID.Manafont) < 5 && MP >= 10000)) + QueueGCD(AID.Fire3, target, canOpen ? GCDPriority.Opener : GCDPriority.NeedB3); } if (Player.Level is >= 1 and <= 34) { @@ -1008,9 +1016,9 @@ private void BestST(Actor? target) //Single-target rotation based on level AstralStacks == 3) //and Umbral Hearts are 0 QueueGCD(AID.Fire3, target, GCDPriority.ForcedStep); //Queue Fire III (AF3 F3P) //Step 8 - Despair - if (MP is < 1600 and not 0 && //if MP is less than 1600 and not 0 - Unlocked(AID.Despair)) //and Despair is unlocked - QueueGCD(AID.Despair, target, GCDPriority.ThirdStep); //Queue Despair + if (Unlocked(AID.Despair) && + ((MP is < 1600 and not 0) || (MP <= 1600 && ElementTimer <= 4))) //if MP is less than 1600 and not 0 + QueueGCD(AID.Despair, target, (MP <= 1600 && ElementTimer <= 4) ? GCDPriority.NeedPolyglot : GCDPriority.ThirdStep); //Queue Despair //Step 9 - Flare Star if (AstralSoulStacks == 6) //if Astral Soul stacks are max QueueGCD(AID.FlareStar, target, GCDPriority.FourthStep); //Queue Flare Star @@ -1398,8 +1406,8 @@ private void BestAOE(Actor? target) //AOE rotation based on level => Player.InCombat && target != null && canMF && - InAstralFire && - (JustUsed(BestXenoglossy, 5) && MP < 1600), + canWeaveIn && + MP == 0, ManafontStrategy.Force => canMF, ManafontStrategy.ForceWeave => canMF && canWeaveIn, ManafontStrategy.ForceEX => canMF, From b72f6452633d9ae367802056db3627ee8fec183a Mon Sep 17 00:00:00 2001 From: ace Date: Sun, 19 Jan 2025 16:21:46 -0800 Subject: [PATCH 10/12] more de-spaghetting --- BossMod/Autorotation/akechi/AkechiBLM.cs | 87 ++++++++---------------- 1 file changed, 28 insertions(+), 59 deletions(-) diff --git a/BossMod/Autorotation/akechi/AkechiBLM.cs b/BossMod/Autorotation/akechi/AkechiBLM.cs index 61ce51109..607a9ce5f 100644 --- a/BossMod/Autorotation/akechi/AkechiBLM.cs +++ b/BossMod/Autorotation/akechi/AkechiBLM.cs @@ -36,6 +36,7 @@ public enum AOEStrategy public enum MovementStrategy { Allow, //Allow the use of all abilities for movement, regardless of any setting or condition set by the user in other options + AllowNoScathe, //Allow the use of all abilities for movement, except Scathe OnlyGCDs, //Only use instant cast GCDs for movement (Polyglots->Firestarter->Thunder->Scathe if nothing left), regardless of any setting or condition set by the user in other options OnlyOGCDs, //Only use OGCDs for movement, (Swiftcast->Triplecast) regardless of any setting or condition set by the user in other options OnlyScathe, //Only use Scathe for movement @@ -141,6 +142,7 @@ public static RotationModuleDefinition Definition() .AddOption(AOEStrategy.ForceAOE, "Force AOE", "Force use of AOE abilities only", supportedTargets: ActionTargets.Hostile); res.Define(Track.Movement).As("Movement", uiPriority: 195) .AddOption(MovementStrategy.Allow, "Allow", "Allow the use of all appropriate abilities for movement") + .AddOption(MovementStrategy.AllowNoScathe, "AllowNoScathe", "Allow the use of all appropriate abilities for movement except for Scathe") .AddOption(MovementStrategy.OnlyGCDs, "OnlyGCDs", "Only use instant cast GCDs for movement; Polyglots->Firestarter->Thunder->Scathe if nothing left") .AddOption(MovementStrategy.OnlyOGCDs, "OnlyOGCDs", "Only use OGCDs for movement; Swiftcast->Triplecast") .AddOption(MovementStrategy.OnlyScathe, "OnlyScathe", "Only use Scathe for movement") @@ -524,7 +526,9 @@ public override void Execute(StrategyValues strategy, Actor? primaryTarget, floa primaryTarget != null && isMoving) { - if (movementStrat is MovementStrategy.Allow) + if (movementStrat is MovementStrategy.Allow + or MovementStrategy.AllowNoScathe + or MovementStrategy.OnlyGCDs) { // GCDs if (!PlayerHasEffect(SID.Swiftcast, 10) || !PlayerHasEffect(SID.Triplecast, 15)) @@ -543,41 +547,11 @@ public override void Execute(StrategyValues strategy, Actor? primaryTarget, floa QueueGCD(forceST ? BestThunderST : forceAOE ? BestThunderAOE : BestThunder, TargetChoice(thunder) ?? primaryTarget ?? BestTarget, GCDPriority.Moving1); - - if (MP >= 800 && - CD(AID.Swiftcast) > 2f && //if Swiftcast is on cooldown - CD(AID.Triplecast) > 62f) //and Triplecast is not active - QueueGCD(AID.Scathe, TargetChoice(AOE) ?? primaryTarget ?? BestTarget, GCDPriority.SixthStep); //use Scathe } - //OGCDs - if (ActionReady(AID.Swiftcast) && - !PlayerHasEffect(SID.Triplecast, 15)) - QueueOGCD(AID.Swiftcast, Player, GCDPriority.Moving1); - if (Unlocked(AID.Triplecast) && - CD(AID.Triplecast) <= 60 && - !PlayerHasEffect(SID.Triplecast, 15) && - !PlayerHasEffect(SID.Swiftcast, 10)) - QueueOGCD(AID.Triplecast, Player, GCDPriority.Moving1); - } - if (movementStrat is MovementStrategy.OnlyGCDs) - { - //GCDs - if (!PlayerHasEffect(SID.Swiftcast, 10) || - !PlayerHasEffect(SID.Triplecast, 15)) - QueueGCD( - Unlocked(TraitID.EnhancedPolyglot) && Polyglots > 0 ? - (forceST ? BestXenoglossy : forceAOE ? AID.Foul : BestPolyglot) - : PlayerHasEffect(SID.Firestarter, 30) ? AID.Fire3 - : hasThunderhead ? - (forceST ? BestThunderST : forceAOE ? BestThunderAOE : BestThunder) - : AID.Scathe, - Polyglots > 0 ? TargetChoice(polyglot) ?? primaryTarget ?? BestTarget - : PlayerHasEffect(SID.Firestarter, 30) ? TargetChoice(AOE) ?? primaryTarget ?? BestTarget - : hasThunderhead ? TargetChoice(thunder) ?? primaryTarget ?? BestTarget - : primaryTarget, - GCDPriority.Moving1); } - if (movementStrat is MovementStrategy.OnlyOGCDs) + if (movementStrat is MovementStrategy.Allow + or MovementStrategy.AllowNoScathe + or MovementStrategy.OnlyOGCDs) { //OGCDs if (ActionReady(AID.Swiftcast) && @@ -587,7 +561,8 @@ public override void Execute(StrategyValues strategy, Actor? primaryTarget, floa !PlayerHasEffect(SID.Swiftcast, 10)) QueueOGCD(AID.Triplecast, Player, GCDPriority.Moving3); } - if (movementStrat is MovementStrategy.OnlyScathe) + if (movementStrat is MovementStrategy.Allow + or MovementStrategy.OnlyScathe) { if (Unlocked(AID.Scathe) && MP >= 800) QueueGCD(AID.Scathe, TargetChoice(AOE) ?? primaryTarget ?? BestTarget, GCDPriority.Moving1); @@ -596,56 +571,48 @@ public override void Execute(StrategyValues strategy, Actor? primaryTarget, floa #endregion #region Out of combat - if (tpusStrat != TPUSStrategy.Forbid) + if (primaryTarget == null && + (tpusStrat == TPUSStrategy.Allow && (!Player.InCombat || Player.InCombat && TargetsInRange() is 0)) || + (tpusStrat == TPUSStrategy.OOConly && !Player.InCombat)) { if (Unlocked(AID.Transpose)) { if (!Unlocked(AID.UmbralSoul)) { - if (primaryTarget == null && - (tpusStrat == TPUSStrategy.Allow && (!Player.InCombat || Player.InCombat && TargetsInRange() is 0)) || - (tpusStrat == TPUSStrategy.OOConly && !Player.InCombat)) - { - if (Player.Level < 35 && - CD(AID.Transpose) < 0.6f && - (InAstralFire || InUmbralIce)) - QueueOGCD(AID.Transpose, Player, OGCDPriority.Transpose); - } + if (CD(AID.Transpose) < 0.6f && + (InAstralFire || InUmbralIce)) + QueueOGCD(AID.Transpose, Player, OGCDPriority.Transpose); } if (Unlocked(AID.UmbralSoul)) { - if (primaryTarget == null && - (tpusStrat == TPUSStrategy.Allow && (!Player.InCombat || Player.InCombat && TargetsInRange() is 0)) || - (tpusStrat == TPUSStrategy.OOConly && !Player.InCombat)) - { - if (InAstralFire) - QueueOGCD(AID.Transpose, Player, OGCDPriority.Transpose); - if (InUmbralIce && - (ElementTimer <= 14 || UmbralStacks < 3 || UmbralHearts != MaxUmbralHearts)) - QueueGCD(AID.UmbralSoul, Player, GCDPriority.Standard); - } + if (InAstralFire) + QueueOGCD(AID.Transpose, Player, OGCDPriority.Transpose); + if (InUmbralIce && + (ElementTimer <= 14 || UmbralStacks < 3 || UmbralHearts != MaxUmbralHearts)) + QueueGCD(AID.UmbralSoul, Player, GCDPriority.Standard); } } } #endregion + #region Cooldowns //Thunder if (ShouldUseThunder(primaryTarget, thunderStrat)) //if Thunder should be used based on strategy { if (AOEStrategy is AOEStrategy.Auto) QueueGCD(BestThunder, TargetChoice(thunder) ?? primaryTarget ?? BestTarget, - ThunderLeft < 3 ? GCDPriority.NeedDOT : + ThunderLeft <= 3 ? GCDPriority.NeedDOT : GCDPriority.DOT); if (forceST) QueueGCD(BestThunderST, TargetChoice(thunder) ?? primaryTarget ?? BestTarget, - ThunderLeft < 3 ? GCDPriority.NeedDOT : + ThunderLeft <= 3 ? GCDPriority.NeedDOT : GCDPriority.DOT); if (forceAOE) QueueGCD(BestThunderAOE, TargetChoice(thunder) ?? primaryTarget ?? BestTarget, - ThunderLeft < 3 ? GCDPriority.NeedDOT : + ThunderLeft <= 3 ? GCDPriority.NeedDOT : GCDPriority.DOT); } //Polyglots @@ -736,6 +703,8 @@ or ManafontStrategy.ForceWeaveEX potionStrat is PotionStrategy.Immediate) Hints.ActionsToExecute.Push(ActionDefinitions.IDPotionInt, Player, ActionQueue.Priority.VeryHigh + (int)OGCDPriority.Potion, 0, GCD - 0.9f); #endregion + + #endregion } #region Core Execution Helpers @@ -818,7 +787,7 @@ private void BestST(Actor? target) //Single-target rotation based on level Player.InCombat) //if Blizzard III is unlocked QueueGCD(AID.Blizzard3, target, GCDPriority.NeedB3); //Queue Blizzard III if (Unlocked(AID.Fire3) && - (CD(AID.Manafont) < 5 && MP >= 10000)) + ((CD(AID.Manafont) < 5 && CD(AID.LeyLines) <= 121 && MP >= 10000)) || (!Player.InCombat && World.Client.CountdownRemaining <= 4)) //F3 opener QueueGCD(AID.Fire3, target, canOpen ? GCDPriority.Opener : GCDPriority.NeedB3); } if (Player.Level is >= 1 and <= 34) From 42594cc155dc749ae26a54e6074347669a4c0aa3 Mon Sep 17 00:00:00 2001 From: ace Date: Mon, 20 Jan 2025 10:16:19 -0800 Subject: [PATCH 11/12] targeting update --- BossMod/Autorotation/akechi/AkechiBLM.cs | 82 ++++++++++++++++++------ 1 file changed, 61 insertions(+), 21 deletions(-) diff --git a/BossMod/Autorotation/akechi/AkechiBLM.cs b/BossMod/Autorotation/akechi/AkechiBLM.cs index 607a9ce5f..bc314585a 100644 --- a/BossMod/Autorotation/akechi/AkechiBLM.cs +++ b/BossMod/Autorotation/akechi/AkechiBLM.cs @@ -391,10 +391,52 @@ private bool JustUsed(AID aid, float variance) } #region Targeting - private int TargetsInRange() => Hints.NumPriorityTargetsInAOECircle(Player.Position, 26); //Returns the number of targets within 26-yalm radius around the player - private bool ShouldUseAOE => TargetsInRange() >= 3; //Check if we should use AOE + private int TargetsInRange() => Hints.NumPriorityTargetsInAOECircle(Player.Position, 25); //Returns the number of targets within 26-yalm radius around the player + private bool ShouldUseAOE + { + get + { + var bestTarget = BestAOETarget; + if (bestTarget != null) + { + var minimumTargetsForAOE = 2; + + //Are there enough targets in the general area? + if (TargetsInRange() < minimumTargetsForAOE) + { + return false; + } + + float splashPriorityFunc(Actor actor) + { + var distanceToPlayer = actor.DistanceToHitbox(Player); + if (distanceToPlayer < 26f) + { + var targetsInSplashRadius = 0; + foreach (var enemy in Hints.PriorityTargets) + { + var targetActor = enemy.Actor; + if (targetActor != actor && targetActor.Position.InCircle(actor.Position, 5f)) + { + targetsInSplashRadius++; + } + } + return targetsInSplashRadius; + } + return float.MinValue; + } + + var (_, bestPrio) = FindBetterTargetBy(null, 25f, splashPriorityFunc); + + return bestPrio >= minimumTargetsForAOE; + } + + return false; + } + } + private Actor? TargetChoice(StrategyValues.OptionRef strategy) => ResolveTargetOverride(strategy.Value); //Resolves the target choice based on the strategy - private Actor? FindBestTarget() + private Actor? FindBestAOETarget() { float AOEPriorityFunc(Actor actor) { @@ -414,13 +456,11 @@ float AOEPriorityFunc(Actor actor) } return float.MinValue; } - float STPriorityFunc(Actor actor) => actor.HPMP.CurHP > 0 ? 1f / actor.HPMP.CurHP : float.MinValue; - var (bestAOETarget, bestAOEPrio) = FindBetterTargetBy(null, 25f, STPriorityFunc); - var (bestTarget, bestPrio) = FindBetterTargetBy(bestAOETarget, 25f, AOEPriorityFunc); - return ShouldUseAOE ? bestAOETarget : bestTarget; + var (BestAOETarget, bestPrio) = FindBetterTargetBy(null, 25f, AOEPriorityFunc); + return BestAOETarget; } - private Actor? BestTarget => FindBestTarget(); // Find the best target for splash attack + private Actor? BestAOETarget => FindBestAOETarget(); // Find the best target for splash attack //TODO: BestDOTTarget #endregion @@ -513,11 +553,11 @@ public override void Execute(StrategyValues strategy, Actor? primaryTarget, floa (Unlocked(TraitID.EnhancedAstralFire) && MP is < 1600 and not 0)))) //instant cast Despair { if (AOEStrategy is AOEStrategy.Auto) - BestRotation(TargetChoice(AOE) ?? BestTarget ?? primaryTarget); //target prio is user choice -> current target -> best AOE target + BestRotation(TargetChoice(AOE) ?? BestAOETarget ?? primaryTarget); if (forceST) - BestST(TargetChoice(AOE) ?? primaryTarget); //target prio is user choice -> current target + BestST(TargetChoice(AOE) ?? primaryTarget); if (forceAOE) - BestAOE(TargetChoice(AOE) ?? primaryTarget); //target prio is user choice -> best AOE target -> current target + BestAOE(TargetChoice(AOE) ?? primaryTarget); } #endregion @@ -535,17 +575,17 @@ or MovementStrategy.AllowNoScathe { if (Unlocked(TraitID.EnhancedPolyglot) && Polyglots > 0) QueueGCD(forceST ? BestXenoglossy : forceAOE ? AID.Foul : BestPolyglot, - TargetChoice(polyglot) ?? primaryTarget ?? BestTarget, + TargetChoice(polyglot) ?? primaryTarget ?? BestAOETarget, GCDPriority.Moving1); if (PlayerHasEffect(SID.Firestarter, 30)) QueueGCD(AID.Fire3, - TargetChoice(AOE) ?? primaryTarget ?? BestTarget, + TargetChoice(AOE) ?? primaryTarget ?? BestAOETarget, GCDPriority.Moving1); if (hasThunderhead) QueueGCD(forceST ? BestThunderST : forceAOE ? BestThunderAOE : BestThunder, - TargetChoice(thunder) ?? primaryTarget ?? BestTarget, + TargetChoice(thunder) ?? primaryTarget ?? BestAOETarget, GCDPriority.Moving1); } } @@ -565,7 +605,7 @@ or MovementStrategy.AllowNoScathe or MovementStrategy.OnlyScathe) { if (Unlocked(AID.Scathe) && MP >= 800) - QueueGCD(AID.Scathe, TargetChoice(AOE) ?? primaryTarget ?? BestTarget, GCDPriority.Moving1); + QueueGCD(AID.Scathe, TargetChoice(AOE) ?? primaryTarget ?? BestAOETarget, GCDPriority.Moving1); } } #endregion @@ -601,17 +641,17 @@ or MovementStrategy.AllowNoScathe { if (AOEStrategy is AOEStrategy.Auto) QueueGCD(BestThunder, - TargetChoice(thunder) ?? primaryTarget ?? BestTarget, + TargetChoice(thunder), ThunderLeft <= 3 ? GCDPriority.NeedDOT : GCDPriority.DOT); if (forceST) QueueGCD(BestThunderST, - TargetChoice(thunder) ?? primaryTarget ?? BestTarget, + TargetChoice(thunder) ?? primaryTarget, ThunderLeft <= 3 ? GCDPriority.NeedDOT : GCDPriority.DOT); if (forceAOE) QueueGCD(BestThunderAOE, - TargetChoice(thunder) ?? primaryTarget ?? BestTarget, + TargetChoice(thunder) ?? primaryTarget, ThunderLeft <= 3 ? GCDPriority.NeedDOT : GCDPriority.DOT); } @@ -623,7 +663,7 @@ or PolyglotStrategy.AutoHold1 or PolyglotStrategy.AutoHold2 or PolyglotStrategy.AutoHold3) QueueGCD(BestPolyglot, - TargetChoice(polyglot) ?? primaryTarget ?? BestTarget, + TargetChoice(polyglot) ?? BestAOETarget ?? primaryTarget, polyglotStrat is PolyglotStrategy.ForceXeno ? GCDPriority.ForcedGCD : Polyglots == MaxPolyglots && EnochianTimer <= 5000 ? GCDPriority.NeedPolyglot : GCDPriority.Polyglot); @@ -632,7 +672,7 @@ or PolyglotStrategy.XenoHold1 or PolyglotStrategy.XenoHold2 or PolyglotStrategy.XenoHold3) QueueGCD(BestXenoglossy, - TargetChoice(polyglot) ?? primaryTarget ?? BestTarget, + TargetChoice(polyglot) ?? primaryTarget, polyglotStrat is PolyglotStrategy.ForceXeno ? GCDPriority.ForcedGCD : Polyglots == MaxPolyglots && EnochianTimer <= 5000 ? GCDPriority.NeedPolyglot : GCDPriority.Polyglot); @@ -641,7 +681,7 @@ or PolyglotStrategy.FoulHold1 or PolyglotStrategy.FoulHold2 or PolyglotStrategy.FoulHold3) QueueGCD(AID.Foul, - TargetChoice(polyglot) ?? primaryTarget ?? BestTarget, + TargetChoice(polyglot) ?? primaryTarget, polyglotStrat is PolyglotStrategy.ForceFoul ? GCDPriority.ForcedGCD : Polyglots == MaxPolyglots && EnochianTimer <= 5000 ? GCDPriority.NeedPolyglot : GCDPriority.Polyglot); From b7a4f567458a0e9088570d5162c2253c839cfbd0 Mon Sep 17 00:00:00 2001 From: ace Date: Sat, 25 Jan 2025 15:02:51 -0800 Subject: [PATCH 12/12] opti --- BossMod/Autorotation/akechi/AkechiPLD.cs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/BossMod/Autorotation/akechi/AkechiPLD.cs b/BossMod/Autorotation/akechi/AkechiPLD.cs index 7dbcd1b5c..a78148285 100644 --- a/BossMod/Autorotation/akechi/AkechiPLD.cs +++ b/BossMod/Autorotation/akechi/AkechiPLD.cs @@ -325,6 +325,7 @@ public AID BestBlade public bool ShouldUseAOE; //Check if AOE rotation should be used public bool ShouldNormalHolyCircle; //Check if Holy Circle should be used public bool ShouldUseDMHolyCircle; //Check if Holy Circle should be used under Divine Might + public bool ShouldHoldDMandAC; //Check if Divine Might buff and Atonement combo should be held into Fight or Flight public AID NextGCD; //The next action to be executed during the global cooldown (for cartridge management) public bool canWeaveIn; //Can weave in oGCDs public bool canWeaveEarly; //Can early weave oGCDs @@ -405,6 +406,7 @@ public override void Execute(StrategyValues strategy, ref Actor? primaryTarget, ShouldUseAOE = TargetsHitByPlayerAOE() > 2; //Check if AOE rotation should be used ShouldNormalHolyCircle = !DivineMight.IsActive && TargetsHitByPlayerAOE() > 3; //Check if Holy Circle should be used (very niche) ShouldUseDMHolyCircle = DivineMight.IsActive && TargetsHitByPlayerAOE() > 2; //Check if Holy Circle should be used under Divine Might + ShouldHoldDMandAC = ComboLastMove is AID.RoyalAuthority ? FightOrFlight.CD < 5 : ComboLastMove is AID.FastBlade ? FightOrFlight.CD < 2.5 : ComboLastMove is AID.RiotBlade && FightOrFlight.CD < GCD; #region Strategy Options var AOE = strategy.Option(Track.AOE); //Retrieves AOE track @@ -805,7 +807,8 @@ public bool QueueAction(AID aid, Actor? target, float priority, float delay) Player.InCombat && //In combat target != null && //Target exists In3y(target) && //Target in range - Atonement.IsReady || Supplication.IsReady || Sepulchre.IsReady, //if any of the three are ready + !ShouldHoldDMandAC && + (Atonement.IsReady || Supplication.IsReady || Sepulchre.IsReady), //if any of the three are ready AtonementStrategy.ForceAtonement => Atonement.IsReady, //Force Atonement AtonementStrategy.ForceSupplication => Supplication.IsReady, //Force Supplication AtonementStrategy.ForceSepulchre => Sepulchre.IsReady, //Force Sepulchre @@ -830,6 +833,7 @@ public bool QueueAction(AID aid, Actor? target, float priority, float delay) target != null && //Target exists In25y(target) && //Target in range HolySpirit.IsReady && //can execute Holy Spirit + !ShouldHoldDMandAC && DivineMight.IsActive, //Divine Might is active _ => false };