From 2fb075e5c956fadff1478665b06cfb5b07782128 Mon Sep 17 00:00:00 2001 From: AceAkechi123 Date: Sun, 12 Jan 2025 05:46:03 -0800 Subject: [PATCH 01/18] BLM WIP --- BossMod/Autorotation/akechi/AkechiBLM.cs | 469 +++++++++++++++++++++++ 1 file changed, 469 insertions(+) create mode 100644 BossMod/Autorotation/akechi/AkechiBLM.cs diff --git a/BossMod/Autorotation/akechi/AkechiBLM.cs b/BossMod/Autorotation/akechi/AkechiBLM.cs new file mode 100644 index 0000000000..40db812deb --- /dev/null +++ b/BossMod/Autorotation/akechi/AkechiBLM.cs @@ -0,0 +1,469 @@ +using BossMod.AST; +using FFXIVClientStructs.FFXIV.Client.Game.Gauge; +using AID = BossMod.BLM.AID; +using SID = BossMod.BLM.SID; + +namespace BossMod.Autorotation.akechi; +//Contribution by Akechi +//Discord: @akechdz or 'Akechi' on Puni.sh for maintenance + +public sealed class AkechiBLM(RotationModuleManager manager, Actor player) : RotationModule(manager, player) +{ + #region Enums: Abilities / Strategies + public enum Track + { + AOE, //ST&AOE rotations tracking + Thunder, //Thunder tracking + Polyglot, //Polyglot tracking + Manafont, //Manafont tracking + Potion, //Potion item tracking + Transpose, //Transpose tracking + Triplecast, //Triplecast tracking + LeyLines, //Ley Lines tracking + Amplifier, //Amplifier tracking + } + public enum AOEStrategy + { + Auto, //Automatically decide when to use ST or AOE rotation based on targets nearby + ForceST, //Force ST rotation only + ForceAOE, //Force AOE rotation only + } + public enum ThunderStrategy + { + Thunder3, //Force use of Thunder if target has 3s or less remaining on DOT effect + Thunder6, //Force use of Thunder if target has 6s or less remaining on DOT effect + Thunder9, //Force use of Thunder if target has 9s or less remaining on DOT effect + Thunder0, //Force use of Thunder if target has does not have DOT effect + Force, //Force use of Thunder regardless of DOT effect + Delay //Delay the use of Thunder for manual or strategic usage + } + public enum PolyglotStrategy + { + Automatic, //Automatically decide when to use Polyglot based on targets nearby + OnlyXeno, //Automatically use Xenoglossy optimal spender, regardless of targets nearby + OnlyFoul, //Automatically use Foul optimal spender, regardless of targets nearby + ForceXeno, //Force use of Xenoglossy + ForceFoul, //Force use of Foul + Delay //Delay the use of Polyglot abilities for manual or strategic usage + } + public enum ManafontStrategy + { + Automatic, //Automatically decide when to use Manafont + Force, //Force the use of Manafont (180s CD), regardless of weaving conditions + ForceWeave, //Force the use of Manafont (180s CD) in any next possible weave slot + ForceEX, //Force the use of Manafont (100s CD), regardless of weaving conditions + ForceWeaveEX, //Force the use of Manafont (100s CD) in any next possible weave slot + Delay //Delay the use of Manafont for strategic reasons + } + public enum PotionStrategy + { + Manual, //Manual potion usage + AlignWithRaidBuffs, //Align potion usage with raid buffs + Immediate //Use potions immediately when available + } + public enum OffensiveStrategy + { + Automatic, //Automatically decide when to use off-global offensive abilities + Force, //Force the use of off-global offensive abilities, regardless of weaving conditions + AnyWeave, //Force the use of off-global offensive abilities in any next possible weave slot + EarlyWeave, //Force the use of off-global offensive abilities in very next FIRST weave slot only + LateWeave, //Force the use of off-global offensive abilities in very next LAST weave slot only + Delay //Delay the use of offensive abilities for strategic reasons + } + #endregion + + public static RotationModuleDefinition Definition() + { + var res = new RotationModuleDefinition("Akechi BLM", //Title + "Standard Rotation Module", //Description + "Standard rotation (Akechi)", //Category + "Akechi", //Contributor + RotationModuleQuality.Ok, //Quality + BitMask.Build((int)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.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.Thunder).As("Damage Over Time", "Thunder", 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.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) + .AddOption(PolyglotStrategy.Automatic, "Auto", "Automatically decide when to use Polyglot based on targets nearby", 0, 0, ActionTargets.Hostile, 70) + .AddOption(PolyglotStrategy.OnlyXeno, "Only Xenoglossy", "Automatically use Xenoglossy optimal spender, regardless of targets nearby", 0, 0, ActionTargets.Hostile, 80) + .AddOption(PolyglotStrategy.OnlyFoul, "Only Foul", "Automatically use Foul optimal spender, regardless of targets nearby", 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, 30, ActionTargets.Hostile, 70) + .AddOption(PolyglotStrategy.Delay, "Delay", "Delay the use of Polyglot abilities for manual or strategic usage", 0, 0, ActionTargets.Hostile, 2) + .AddAssociatedActions(AID.Xenoglossy, AID.Foul); + res.Define(Track.Manafont).As("Manafont", "Manafont", uiPriority: 170) + .AddOption(ManafontStrategy.Automatic, "Auto", "Automatically decide when to use Manafont", 0, 0, ActionTargets.Self, 30) + .AddOption(ManafontStrategy.Force, "Force", "Force the use of Manafont (180s CD), regardless of weaving conditions", 180, 0, ActionTargets.Self, 30, 83) + .AddOption(ManafontStrategy.ForceWeave, "ForceWeave", "Force the use of Manafont (180s CD) in any next possible weave slot", 180, 0, ActionTargets.Self, 30, 83) + .AddOption(ManafontStrategy.ForceEX, "ForceEX", "Force the use of Manafont (100s CD), regardless of weaving conditions", 100, 0, ActionTargets.Self, 84) + .AddOption(ManafontStrategy.ForceWeaveEX, "ForceWeaveEX", "Force the use of Manafont (100s CD) in any next possible weave slot", 100, 0, ActionTargets.Self, 84) + .AddOption(ManafontStrategy.Delay, "Delay", "Delay the use of Manafont for strategic reasons", 0, 0, ActionTargets.Self, 30) + .AddAssociatedActions(AID.Manafont); + res.Define(Track.Potion).As("Potion", uiPriority: 180) + .AddOption(PotionStrategy.Manual, "Manual", "Do not use automatically") + .AddOption(PotionStrategy.AlignWithRaidBuffs, "AlignWithRaidBuffs", "Align with No Mercy & Bloodfest together (to ensure use on 2-minute windows)", 270, 30, ActionTargets.Self) + .AddOption(PotionStrategy.Immediate, "Immediate", "Use ASAP, regardless of any buffs", 270, 30, ActionTargets.Self) + .AddAssociatedAction(ActionDefinitions.IDPotionInt); + #endregion + + #region Offensive Strategies + res.Define(Track.Transpose).As("Transpose", uiPriority: 170) + .AddOption(OffensiveStrategy.Automatic, "Auto", "Automatically decide when to use Transpose", 5, 0, ActionTargets.Self, 4) + .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.Delay, "Delay", "Delay the use of Transpose", 0, 0, ActionTargets.Self, 4) + .AddAssociatedActions(AID.Transpose); + res.Define(Track.Triplecast).As("Triplecast", uiPriority: 170) + .AddOption(OffensiveStrategy.Automatic, "Auto", "Automatically decide when to use Triplecast", 60, 0, ActionTargets.Self, 4) + .AddOption(OffensiveStrategy.Force, "Force", "Force the use of Triplecast, regardless of weaving conditions", 60, 0, ActionTargets.Self, 4) + .AddOption(OffensiveStrategy.AnyWeave, "AnyWeave", "Force the use of Triplecast in any next possible weave slot", 60, 0, ActionTargets.Self, 4) + .AddOption(OffensiveStrategy.EarlyWeave, "EarlyWeave", "Force the use of Triplecast in very next FIRST weave slot only", 60, 0, ActionTargets.Self, 4) + .AddOption(OffensiveStrategy.LateWeave, "LateWeave", "Force the use of Triplecast in very next LAST weave slot only", 60, 0, ActionTargets.Self, 4) + .AddOption(OffensiveStrategy.Delay, "Delay", "Delay the use of Triplecast", 0, 0, ActionTargets.Self, 4) + .AddAssociatedActions(AID.Triplecast); + res.Define(Track.LeyLines).As("LeyLines", uiPriority: 170) + .AddOption(OffensiveStrategy.Automatic, "Auto", "Automatically decide when to use Ley Lines", 90, 0, ActionTargets.Self, 4) + .AddOption(OffensiveStrategy.Force, "Force", "Force the use of Ley Lines, regardless of weaving conditions", 90, 0, ActionTargets.Self, 4) + .AddOption(OffensiveStrategy.AnyWeave, "AnyWeave", "Force the use of Ley Lines in any next possible weave slot", 90, 0, ActionTargets.Self, 4) + .AddOption(OffensiveStrategy.EarlyWeave, "EarlyWeave", "Force the use of Ley Lines in very next FIRST weave slot only", 90, 0, ActionTargets.Self, 4) + .AddOption(OffensiveStrategy.LateWeave, "LateWeave", "Force the use of Ley Lines in very next LAST weave slot only", 90, 0, ActionTargets.Self, 4) + .AddOption(OffensiveStrategy.Delay, "Delay", "Delay the use of Ley Lines", 0, 0, ActionTargets.Self, 4) + .AddAssociatedActions(AID.LeyLines); + res.Define(Track.Amplifier).As("Amplifier", uiPriority: 170) + .AddOption(OffensiveStrategy.Automatic, "Auto", "Automatically decide when to use Amplifier", 30, 0, ActionTargets.Self, 4) + .AddOption(OffensiveStrategy.Force, "Force", "Force the use of Amplifier, regardless of weaving conditions", 30, 0, ActionTargets.Self, 4) + .AddOption(OffensiveStrategy.AnyWeave, "AnyWeave", "Force the use of Amplifier in any next possible weave slot", 30, 0, ActionTargets.Self, 4) + .AddOption(OffensiveStrategy.EarlyWeave, "EarlyWeave", "Force the use of Amplifier in very next FIRST weave slot only", 30, 0, ActionTargets.Self, 4) + .AddOption(OffensiveStrategy.LateWeave, "LateWeave", "Force the use of Amplifier in very next LAST weave slot only", 30, 0, ActionTargets.Self, 4) + .AddOption(OffensiveStrategy.Delay, "Delay", "Delay the use of Amplifier", 0, 0, ActionTargets.Self, 4) + .AddAssociatedActions(AID.Amplifier); + + #endregion + + return res; + } + + #region Priorities + public enum GCDPriority //priorities for GCDs (higher number = higher priority) + { + None = 0, //default + Standard = 300, //standard abilities + DOT = 400, //damage-over-time abilities + ForcedGCD = 900, //Forced GCDs + } + public enum OGCDPriority //priorities for oGCDs (higher number = higher priority) + { + None = 0, //default + Potion = 800, //Potion + ForcedOGCD = 900, //Forced oGCDs + } + #endregion + + #region Placeholders for Variables + private float ThunderLeft; //Time left on DOT effect (30s base) + private bool ShouldUseAOE; //Checks if AOE should be used + public bool canWeaveIn; //Can weave in oGCDs + public bool canWeaveEarly; //Can early weave oGCDs + public bool canWeaveLate; //Can late weave oGCDs + public bool quarterWeave; //Can last second weave oGCDs + public float PotionLeft; //Time left on potion buff (30s base) + public float RaidBuffsLeft; //Time left on raid-wide buffs (typically 20s-22s) + public float RaidBuffsIn; //Time until raid-wide buffs are applied again (typically 20s-22s) + public float SpS; //Current GCD length, adjusted by spell speed/haste (2.5s baseline) + public float BurstWindowLeft; //Time left in current burst window (typically 20s-22s) + public float BurstWindowIn; //Time until next burst window (typically 20s-22s) + public AID NextGCD; //Next global cooldown action to be used + #endregion + + #region Module Helpers + private bool Unlocked(AID aid) => ActionUnlocked(ActionID.MakeSpell(aid)); //Check if the desired ability is unlocked + private bool Unlocked(TraitID tid) => TraitUnlocked((uint)tid); //Check if the desired trait is unlocked + private float CD(AID aid) => World.Client.Cooldowns[ActionDefinitions.Instance.Spell(aid)!.MainCooldownGroup].Remaining; //Get remaining cooldown time for the specified action + 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 ready (cooldown less than 0.6 seconds) + private bool IsFirstGCD() => !Player.InCombat || (World.CurrentTime - Manager.CombatStart).TotalSeconds < 0.1f; //Check if this is the first GCD in combat + private int TargetsInAOERange() => Hints.NumPriorityTargetsInAOECircle(Player.Position, 5); //Returns the number of targets hit by AOE within a 5-yalm radius around the player + public bool PlayerHasEffect(SID sid, float duration) => SelfStatusLeft(sid, duration) > 0; //Checks if Status effect is on self + #endregion + + #region Upgrade Paths + private AID BestThunderST + => Unlocked(AID.HighThunder) ? AID.HighThunder + : Unlocked(AID.Thunder3) ? AID.Thunder3 + : AID.Thunder1; + private AID BestThunderAOE + => Unlocked(AID.HighThunder2) ? AID.HighThunder2 + : Unlocked(AID.Thunder4) ? AID.Thunder4 + : AID.Thunder2; + private AID BestThunder + => ShouldUseAOE ? BestThunderAOE : BestThunderST; + private AID BestPolyglot + => ShouldUseAOE ? AID.Foul : BestXenoglossy; + private AID BestXenoglossy + => Unlocked(AID.Xenoglossy) ? AID.Xenoglossy : AID.Foul; + #endregion + + public override void Execute(StrategyValues strategy, Actor? primaryTarget, float estimatedAnimLockDelay, bool isMoving) //Executes our actions + { + #region Variables + var gauge = World.Client.GetGauge(); //Retrieve BLMolar gauge + canWeaveIn = GCD is <= 2.5f and >= 0.1f; //Can weave in oGCDs + canWeaveEarly = GCD is <= 2.5f and >= 1.25f; //Can weave in oGCDs early + 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 + PotionLeft = PotionStatusLeft(); //Remaining time for potion buff (30s) + ShouldUseAOE = TargetsInAOERange() > 1; //otherwise, use AOE if 2+ targets would be hit + + #region Strategy Definitions + var AOE = strategy.Option(Track.AOE); //AOE track + var AOEStrategy = AOE.As(); //AOE strategy + var Thunder = strategy.Option(Track.Thunder); //Thunder track + var ThunderStrategy = Thunder.As(); //Thunder strategy + var potion = strategy.Option(Track.Potion).As(); //Potion strategy + + + #endregion + + #endregion + + #region Force Execution + if (AOEStrategy is AOEStrategy.Auto) + QueueGCD(BestRotation(), ResolveTargetOverride(AOE.Value) ?? primaryTarget, GCDPriority.Standard); + if (AOEStrategy is AOEStrategy.ForceST) + QueueGCD(BestST(), ResolveTargetOverride(AOE.Value) ?? primaryTarget, GCDPriority.ForcedGCD); + if (AOEStrategy is AOEStrategy.ForceAOE) + QueueGCD(BestAOE(), Player, GCDPriority.ForcedGCD); + #endregion + + #region Standard Execution + if (AOEStrategy == AOEStrategy.Auto) + { + var STtarget = ResolveTargetOverride(AOE.Value) ?? primaryTarget; + if (In25y(STtarget)) + { + if (ShouldUseAOE) + QueueGCD(BestAOE(), Player, GCDPriority.Standard); + if (In25y(STtarget) && + (!ShouldUseAOE || IsFirstGCD())) + QueueGCD(BestST(), STtarget, GCDPriority.Standard); + + } + } + + if (potion is PotionStrategy.AlignWithRaidBuffs && CD(AID.LeyLines) < 5 || + potion is PotionStrategy.Immediate) + Hints.ActionsToExecute.Push(ActionDefinitions.IDPotionMnd, Player, ActionQueue.Priority.VeryHigh + (int)OGCDPriority.Potion, 0, GCD - 0.9f); + #endregion + } + + #region Core Execution Helpers + public void QueueGCD

(AID aid, Actor? target, P priority, float delay = 0) where P : Enum + => QueueGCD(aid, target, (int)(object)priority, delay); + + public void QueueGCD(AID aid, Actor? target, int priority = 8, float delay = 0) + { + var NextGCDPrio = 0; + + if (priority == 0) + return; + + if (QueueAction(aid, target, ActionQueue.Priority.High + priority, delay) && priority > NextGCDPrio) + { + NextGCD = aid; + } + } + + public void QueueOGCD

(AID aid, Actor? target, P priority, float delay = 0) where P : Enum + => QueueOGCD(aid, target, (int)(object)priority, delay); + + public void QueueOGCD(AID aid, Actor? target, int priority = 4, float delay = 0) + { + if (priority == 0) + return; + + QueueAction(aid, target, ActionQueue.Priority.Medium + priority, delay); + } + + public bool QueueAction(AID aid, Actor? target, float priority, float delay) + { + if ((uint)(object)aid == 0) + return false; + + var def = ActionDefinitions.Instance.Spell(aid); + if (def == null) + return false; + + if (def.Range != 0 && target == null) + { + return false; + } + + Vector3 targetPos = default; + + if (def.AllowedTargets.HasFlag(ActionTargets.Area)) + { + if (def.Range == 0) + targetPos = Player.PosRot.XYZ(); + else if (target != null) + targetPos = target.PosRot.XYZ(); + } + + Hints.ActionsToExecute.Push(ActionID.MakeSpell(aid), target, priority, delay: delay, targetPos: targetPos); + return true; + } + #endregion + private void STLv1toLv34() + { + // TODO: Implement single-target rotation for level 1-34 + } + private void STLv35toLv59() + { + // TODO: Implement single-target rotation for level 35-59 + } + private void STLv60toLv71() + { + // TODO: Implement single-target rotation for level 60-71 + } + private void STLv72toLv89() + { + // TODO: Implement single-target rotation for level 72-89 + } + private void STLv90toLv99() + { + // TODO: Implement single-target rotation for level 90-99 + } + private void STLv100() + { + // TODO: Implement single-target rotation for level 100 + } + private void BestST() + { + if (Player.Level is >= 1 and <= 34) + STLv1toLv34(); + if (Player.Level is >= 35 and <= 59) + STLv35toLv59(); + if (Player.Level is >= 60 and <= 71) + STLv60toLv71(); + if (Player.Level is >= 72 and <= 89) + STLv72toLv89(); + if (Player.Level is >= 90 and <= 99) + STLv90toLv99(); + if (Player.Level is 100) + STLv100(); + } + private void AOELv12toLv34() + { + // TODO: Implement AOE rotation for level 12-34 + } + private void AOELv35toLv39() + { + // TODO: Implement AOE rotation for level 35-39 + } + private void AOELv40toLv49() + { + // TODO: Implement AOE rotation for level 40-49 + } + private void AOELv50toLv57() + { + // TODO: Implement AOE rotation for level 50-57 + } + private void AOELv58toLv81() + { + // TODO: Implement AOE rotation for level 58-81 + } + private void AOELv82toLv99() + { + // TODO: Implement AOE rotation for level 82-99 + } + private void AOELv100() + { + // TODO: Implement AOE rotation for level 100 + } + private void BestAOE() + { + if (Player.Level is >= 12 and <= 34) + AOELv12toLv34(); + if (Player.Level is >= 35 and <= 39) + AOELv35toLv39(); + if (Player.Level is >= 40 and <= 49) + AOELv40toLv49(); + if (Player.Level is >= 50 and <= 57) + AOELv50toLv57(); + if (Player.Level is >= 58 and <= 81) + AOELv58toLv81(); + if (Player.Level is >= 82 and <= 99) + AOELv82toLv99(); + if (Player.Level is 100) + AOELv100(); + } + + private void BestRotation() + { + if (ShouldUseAOE) + { + BestAOE(); + } + if (!ShouldUseAOE) + { + BestST(); + } + } + + #region Cooldown Helpers + private bool ShouldUseThunder(Actor? target, ThunderStrategy strategy) => strategy switch + { + ThunderStrategy.Thunder3 => Player.InCombat && target != null && hasThunderhead && ThunderLeft <= 3 && In25y(target), + ThunderStrategy.Thunder6 => Player.InCombat && target != null && hasThunderhead && ThunderLeft <= 6 && In25y(target), + ThunderStrategy.Thunder9 => Player.InCombat && target != null && hasThunderhead && ThunderLeft <= 9 && In25y(target), + ThunderStrategy.Thunder0 => Player.InCombat && target != null && hasThunderhead && ThunderLeft is 0 && In25y(target), + ThunderStrategy.Force => hasThunderhead, + ThunderStrategy.Delay => false, + _ => false + }; + private bool ShouldUseLeyLines(Actor? target, OffensiveStrategy strategy) => strategy switch + { + OffensiveStrategy.Automatic => Player.InCombat && target != null && canLL && canWeaveIn, + OffensiveStrategy.Force => canLL, + OffensiveStrategy.AnyWeave => canLL && canWeaveIn, + OffensiveStrategy.EarlyWeave => canLL && canWeaveEarly, + OffensiveStrategy.LateWeave => canLL && canWeaveLate, + OffensiveStrategy.Delay => false, + _ => false + }; + private bool ShouldUseAmplifier(Actor? target, OffensiveStrategy strategy) => strategy switch + { + OffensiveStrategy.Automatic => Player.InCombat && target != null && canAmp && canWeaveIn, + OffensiveStrategy.Force => canAmp, + OffensiveStrategy.AnyWeave => canAmp && canWeaveIn, + OffensiveStrategy.EarlyWeave => canAmp && canWeaveEarly, + OffensiveStrategy.LateWeave => canAmp && canWeaveLate, + OffensiveStrategy.Delay => false, + _ => false + }; + private bool ShouldUseManafont(Actor? target, ManafontStrategy strategy) => strategy switch + { + ManafontStrategy.Automatic => Player.InCombat && target != null && canMF && canWeaveIn, + ManafontStrategy.Force => canMF, + ManafontStrategy.ForceWeave => canMF && canWeaveIn, + ManafontStrategy.ForceEX => canMF, + ManafontStrategy.ForceWeaveEX => canMF && canWeaveIn, + ManafontStrategy.Delay => false, + _ => false + }; + #endregion +} From 8e2ec3a096cb7d1e8332da9bec172a6e6bc2ec3b Mon Sep 17 00:00:00 2001 From: ace Date: Tue, 14 Jan 2025 00:11:35 -0800 Subject: [PATCH 02/18] more BLM WIP --- BossMod/Autorotation/akechi/AkechiBLM.cs | 434 +++++++++++++++++++---- 1 file changed, 367 insertions(+), 67 deletions(-) diff --git a/BossMod/Autorotation/akechi/AkechiBLM.cs b/BossMod/Autorotation/akechi/AkechiBLM.cs index 40db812deb..99b27c05c9 100644 --- a/BossMod/Autorotation/akechi/AkechiBLM.cs +++ b/BossMod/Autorotation/akechi/AkechiBLM.cs @@ -1,7 +1,7 @@ -using BossMod.AST; -using FFXIVClientStructs.FFXIV.Client.Game.Gauge; +using FFXIVClientStructs.FFXIV.Client.Game.Gauge; using AID = BossMod.BLM.AID; using SID = BossMod.BLM.SID; +using TraitID = BossMod.BLM.TraitID; namespace BossMod.Autorotation.akechi; //Contribution by Akechi @@ -79,7 +79,7 @@ public static RotationModuleDefinition Definition() "Standard rotation (Akechi)", //Category "Akechi", //Contributor RotationModuleQuality.Ok, //Quality - BitMask.Build((int)Class.BLM), //Job + BitMask.Build(Class.THM, Class.BLM), //Job 100); //Level supported #region Custom strategies @@ -162,18 +162,59 @@ public static RotationModuleDefinition Definition() { None = 0, //default Standard = 300, //standard abilities - DOT = 400, //damage-over-time abilities + DOT = 350, //damage-over-time abilities + Despair = 400, //Despair + F3P = 450, //Fire III proc + NeedB3 = 460, //Need to use Blizzard III + Polyglot = 475, //Polyglots + Paradox = 500, //Paradox + + 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 = 700, //Moving ForcedGCD = 900, //Forced GCDs } public enum OGCDPriority //priorities for oGCDs (higher number = higher priority) { None = 0, //default + Transpose = 400, //Transpose + Manafont = 450, //Manafont + LeyLines = 500, //Ley Lines + Amplifier = 550, //Amplifier + Triplecast = 600, //Triplecast Potion = 800, //Potion ForcedOGCD = 900, //Forced oGCDs } #endregion #region Placeholders for Variables + private uint MP; //Current MP + private bool NoStance; //No stance + private bool InAstralFire; //In Astral Fire + private bool InUmbralIce; //In Umbral Ice + private sbyte ElementStance; //Elemental Stance + private byte Polyglots; //Polyglot Stacks + private int PolyglotTimer; //Polyglot timer + private int MaxPolyglots; // + private byte UmbralHearts; //Umbral Hearts + private int MaxUmbralHearts; //Max Umbral Hearts + private int UmbralStacks; //Umbral Ice Stacks + private int AstralStacks; //Astral Fire Stacks + private int AstralSoulStacks; //Stacks for Flare Star (Lv100) + private int EnochianTimer; //Enochian timer + private float ElementTimer; //Time remaining on Enochian + private bool EnochianActive; //Enochian is active + private bool ParadoxActive; //Paradox is active + private bool canFoul; //Can use Foul + private bool canXeno; //Can use Xenoglossy + private bool canLL; //Can use Ley Lines + private bool canAmp; //Can use Amplifier + private bool canTC; //Can use Triplecast + private bool canMF; //Can use Manafont + private bool hasFirestarter; //Has Firestarter buff + private bool hasThunderhead; //Has Thunderhead buff private float ThunderLeft; //Time left on DOT effect (30s base) private bool ShouldUseAOE; //Checks if AOE should be used public bool canWeaveIn; //Can weave in oGCDs @@ -196,11 +237,59 @@ public static RotationModuleDefinition Definition() 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 ready (cooldown less than 0.6 seconds) private bool IsFirstGCD() => !Player.InCombat || (World.CurrentTime - Manager.CombatStart).TotalSeconds < 0.1f; //Check if this is the first GCD in combat - private int TargetsInAOERange() => Hints.NumPriorityTargetsInAOECircle(Player.Position, 5); //Returns the number of targets hit by AOE within a 5-yalm radius around the player - public bool PlayerHasEffect(SID sid, float duration) => SelfStatusLeft(sid, duration) > 0; //Checks if Status effect is on self + private int TargetsInRange() => Hints.NumPriorityTargetsInAOECircle(Player.Position, 25); //Returns the number of targets hit by AOE within a 25-yalm radius around the player + public bool PlayerHasEffect(SID sid, float duration) => SelfStatusLeft(sid, duration) > GCD; //Checks if Status effect is on self + public bool JustUsed(AID aid, float variance) + { + if (Manager?.LastCast == null) + return false; + + return Manager.LastCast.Data?.IsSpell(aid) == true + && (World.CurrentTime - Manager.LastCast.Time).TotalSeconds <= variance; + } + private float GetCastTime(AID aid) => ActionDefinitions.Instance.Spell(aid)!.CastTime * SpS / 2.5f; + private float CastTime(AID aid) + { + if (PlayerHasEffect(SID.Triplecast, 15) || + PlayerHasEffect(SID.Swiftcast, 10)) + return 0; + + var aspect = ActionDefinitions.Instance.Spell(aid)!.Aspect; + + if (aid == AID.Fire3 && hasFirestarter + || aid == AID.Foul && Unlocked(TraitID.EnhancedFoul) + || aspect == ActionAspect.Thunder && hasThunderhead) + return 0; + + var castTime = GetCastTime(aid); + if (castTime == 0) + return 0; + + if (ElementStance == -3 && aspect == ActionAspect.Fire || ElementStance == 3 && aspect == ActionAspect.Ice) + castTime *= 0.5f; + + return castTime; + + } + private int TargetsInPlayerAOE(Actor primary) => Hints.NumPriorityTargetsInAOERect( //Use Hints to count number of targets in AOE rectangle + Player.Position, //from Player's position + (primary.Position - Player.Position) //direction of AOE rectangle + .Normalized(), //normalized direction + 25, //AOE rectangle length + 5); //AOE rectangle width + public Actor? TargetChoice(StrategyValues.OptionRef strategy) => ResolveTargetOverride(strategy.Value); //Resolves the target choice based on the strategy #endregion #region Upgrade Paths + private AID BestFire1 + => Unlocked(AID.Paradox) ? AID.Paradox + : AID.Fire1; + private AID BestBlizzard1 + => Unlocked(AID.Blizzard4) ? AID.Blizzard4 + : AID.Blizzard1; + private AID swiftcastB3 + => Player.InCombat && ActionReady(AID.Swiftcast) ? AID.Swiftcast + : AID.Blizzard3; private AID BestThunderST => Unlocked(AID.HighThunder) ? AID.HighThunder : Unlocked(AID.Thunder3) ? AID.Thunder3 @@ -220,54 +309,112 @@ private AID BestXenoglossy public override void Execute(StrategyValues strategy, Actor? primaryTarget, float estimatedAnimLockDelay, bool isMoving) //Executes our actions { #region Variables - var gauge = World.Client.GetGauge(); //Retrieve BLMolar gauge + var gauge = World.Client.GetGauge(); //Retrieve BLM gauge + NoStance = ElementStance is 0; //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 + Polyglots = gauge.PolyglotStacks; //Polyglot Stacks + UmbralHearts = gauge.UmbralHearts; //Umbral Hearts + MaxUmbralHearts = Unlocked(TraitID.UmbralHeart) ? 3 : 0; + UmbralStacks = gauge.UmbralStacks; //Umbral Ice Stacks + AstralStacks = gauge.AstralStacks; //Astral Fire Stacks + AstralSoulStacks = gauge.AstralSoulStacks; //Stacks for Flare Star (Lv100) + EnochianActive = gauge.EnochianActive; //Enochian is active + ParadoxActive = gauge.ParadoxActive; //Paradox is active + EnochianTimer = gauge.EnochianTimer; //Enochian timer + ElementTimer = gauge.ElementTimeRemaining / 1000f; //Time remaining on current element + PolyglotTimer = Math.Max(0, ((MaxPolyglots - Polyglots) * 30000) + (EnochianTimer - 30000)); + MaxPolyglots = Unlocked(TraitID.EnhancedPolyglotII) ? 3 : Unlocked(TraitID.EnhancedPolyglot) ? 2 : 1; + canFoul = Unlocked(AID.Foul) && Polyglots > 0; //Can use Foul + canXeno = Unlocked(AID.Xenoglossy) && Polyglots > 0; //Can use Xenoglossy + canLL = Unlocked(AID.LeyLines) && CD(AID.LeyLines) <= 120 && SelfStatusLeft(SID.LeyLines, 30) == 0; //Can use Ley Lines + canAmp = ActionReady(AID.Amplifier); //Can use Amplifier + canTC = Unlocked(AID.Triplecast) && CD(AID.Triplecast) <= 60 && SelfStatusLeft(SID.Triplecast) == 0; //Can use Triplecast + canMF = ActionReady(AID.Manafont); //Can use Manafont + hasFirestarter = PlayerHasEffect(SID.Firestarter, 15); //Has Firestarter buff + hasThunderhead = PlayerHasEffect(SID.Thunderhead, 30); //Has Thunderhead buff + ThunderLeft = Utils.MaxAll( + StatusDetails(primaryTarget, SID.Thunder, Player.InstanceID, 24).Left, + StatusDetails(primaryTarget, SID.ThunderII, Player.InstanceID, 18).Left, + StatusDetails(primaryTarget, SID.ThunderIII, Player.InstanceID, 27).Left, + StatusDetails(primaryTarget, SID.ThunderIV, Player.InstanceID, 21).Left, + StatusDetails(primaryTarget, SID.HighThunder, Player.InstanceID, 30).Left, + StatusDetails(primaryTarget, SID.HighThunderII, Player.InstanceID, 24).Left); + MP = Player.HPMP.CurMP; //Current MP canWeaveIn = GCD is <= 2.5f and >= 0.1f; //Can weave in oGCDs canWeaveEarly = GCD is <= 2.5f and >= 1.25f; //Can weave in oGCDs early 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 PotionLeft = PotionStatusLeft(); //Remaining time for potion buff (30s) - ShouldUseAOE = TargetsInAOERange() > 1; //otherwise, use AOE if 2+ targets would be hit + ShouldUseAOE = TargetsInRange() > 2; //otherwise, use AOE if 2+ targets would be hit #region Strategy Definitions var AOE = strategy.Option(Track.AOE); //AOE track var AOEStrategy = AOE.As(); //AOE strategy - var Thunder = strategy.Option(Track.Thunder); //Thunder track - var ThunderStrategy = Thunder.As(); //Thunder strategy + var thunder = strategy.Option(Track.Thunder); //Thunder track + var thunderStrat = thunder.As(); //Thunder strategy + var polyglot = strategy.Option(Track.Polyglot); //Polyglot track + var polyglotStrat = polyglot.As(); //Polyglot strategy + var mf = strategy.Option(Track.Manafont); //Manafont track + var mfStrat = mf.As(); //Manafont strategy + var tc = strategy.Option(Track.Triplecast); //Triplecast track + var tcStrat = tc.As(); //Triplecast strategy + var ll = strategy.Option(Track.LeyLines); //Ley Lines track + var llStrat = ll.As(); //Ley Lines strategy + var amp = strategy.Option(Track.Amplifier); //Amplifier track + var ampStrat = amp.As(); //Amplifier strategy var potion = strategy.Option(Track.Potion).As(); //Potion strategy - - #endregion #endregion #region Force Execution if (AOEStrategy is AOEStrategy.Auto) - QueueGCD(BestRotation(), ResolveTargetOverride(AOE.Value) ?? primaryTarget, GCDPriority.Standard); + STLv72toLv89(TargetChoice(AOE) ?? primaryTarget); if (AOEStrategy is AOEStrategy.ForceST) - QueueGCD(BestST(), ResolveTargetOverride(AOE.Value) ?? primaryTarget, GCDPriority.ForcedGCD); + BestST(TargetChoice(AOE) ?? primaryTarget); if (AOEStrategy is AOEStrategy.ForceAOE) - QueueGCD(BestAOE(), Player, GCDPriority.ForcedGCD); + BestAOE(TargetChoice(AOE) ?? primaryTarget); #endregion #region Standard Execution - if (AOEStrategy == AOEStrategy.Auto) + //Out of combat + if (Unlocked(AID.UmbralSoul)) { - var STtarget = ResolveTargetOverride(AOE.Value) ?? primaryTarget; - if (In25y(STtarget)) + if (primaryTarget == null && + (!Player.InCombat || Player.InCombat && TargetsInRange() is 0)) { - if (ShouldUseAOE) - QueueGCD(BestAOE(), Player, GCDPriority.Standard); - if (In25y(STtarget) && - (!ShouldUseAOE || IsFirstGCD())) - QueueGCD(BestST(), STtarget, GCDPriority.Standard); - + if (InAstralFire) + QueueOGCD(AID.Transpose, Player, OGCDPriority.Transpose); + if (InUmbralIce && + (ElementTimer <= 14 || UmbralStacks < 3 || UmbralHearts != MaxUmbralHearts)) + QueueGCD(AID.UmbralSoul, Player, GCDPriority.Standard); } } - + //Thunder + if (ShouldUseThunder(primaryTarget, thunderStrat)) //if Thunder should be used based on strategy + QueueGCD(BestThunder, TargetChoice(thunder) ?? primaryTarget, ThunderLeft < 3 ? GCDPriority.NeedDOT : GCDPriority.DOT); //Queue Thunder + //Polyglots + if (ShouldUsePolyglot(primaryTarget, polyglotStrat)) //if Polyglot should be used based on strategy + QueueGCD(BestPolyglot, TargetChoice(polyglot) ?? primaryTarget, polyglotStrat is PolyglotStrategy.ForceFoul or PolyglotStrategy.ForceXeno ? GCDPriority.ForcedGCD : EnochianTimer < 5000 ? GCDPriority.NeedPolyglot : GCDPriority.Paradox); //Queue Polyglot + //LeyLines + if (ShouldUseLeyLines(primaryTarget, llStrat)) + QueueOGCD(AID.LeyLines, Player, OGCDPriority.LeyLines); + //Triplecast + if (ShouldUseTriplecast(primaryTarget, tcStrat)) + QueueOGCD(AID.Triplecast, Player, OGCDPriority.Triplecast); + //Amplifier + if (ShouldUseAmplifier(primaryTarget, ampStrat)) + QueueOGCD(AID.Amplifier, Player, OGCDPriority.Amplifier); + //Manafont + if (ShouldUseManafont(primaryTarget, mfStrat)) + QueueOGCD(AID.Manafont, Player, OGCDPriority.Manafont); + //Potion if (potion is PotionStrategy.AlignWithRaidBuffs && CD(AID.LeyLines) < 5 || potion is PotionStrategy.Immediate) - Hints.ActionsToExecute.Push(ActionDefinitions.IDPotionMnd, Player, ActionQueue.Priority.VeryHigh + (int)OGCDPriority.Potion, 0, GCD - 0.9f); + Hints.ActionsToExecute.Push(ActionDefinitions.IDPotionInt, Player, ActionQueue.Priority.VeryHigh + (int)OGCDPriority.Potion, 0, GCD - 0.9f); #endregion } @@ -327,21 +474,80 @@ public bool QueueAction(AID aid, Actor? target, float priority, float delay) return true; } #endregion - private void STLv1toLv34() - { - // TODO: Implement single-target rotation for level 1-34 + private void STLv1toLv34(Actor? target) + { + //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 + if (InUmbralIce && MP != 10000) //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() - { - // TODO: Implement single-target rotation for level 35-59 + private void STLv35toLv59(Actor? target) + { + //Ice + if (Unlocked(AID.Blizzard3) && //if Blizzard III is unlocked + (NoStance && MP == 10000 || //and if no stance is active and MP is max (opener) + (NoStance && MP < 10000 && Player.InCombat) || //or if in combat and no stance is active and MP is less than max (died or stopped attacking) + InAstralFire && MP < 1600)) //or if Astral Fire is active and MP is less than 1600 + QueueGCD(AID.Blizzard3, target, GCDPriority.Standard); + if (UmbralStacks >= 1) + QueueGCD(BestBlizzard1, target, GCDPriority.Standard); + //Fire + if (CastTime(AID.Fire3) < 2.5f && + (hasFirestarter || JustUsed(BestBlizzard1, 5))) + QueueGCD(AID.Fire3, target, GCDPriority.F3P); + if (InAstralFire && MP >= 1600) + QueueGCD(AID.Fire1, target, GCDPriority.Standard); } - private void STLv60toLv71() - { - // TODO: Implement single-target rotation for level 60-71 + private void STLv60toLv71(Actor? target) + { + //Ice + if (Unlocked(AID.Blizzard3) && //if Blizzard III is unlocked + (NoStance && MP == 10000 || //and if no stance is active and MP is max (opener) + (NoStance && MP < 10000 && Player.InCombat) || //or if in combat and no stance is active and MP is less than max (died or stopped attacking) + InAstralFire && MP < 1600)) //or if Astral Fire is active and MP is less than 1600 + QueueGCD(AID.Blizzard3, target, GCDPriority.Standard); + if (UmbralStacks >= 1 || + Unlocked(AID.Blizzard4) && UmbralHearts != MaxUmbralHearts) + QueueGCD(BestBlizzard1, target, GCDPriority.Standard); + //Fire + if (CastTime(AID.Fire3) < 2.5f && + (hasFirestarter || JustUsed(BestBlizzard1, 5))) + QueueGCD(AID.Fire3, target, GCDPriority.F3P); + if (InAstralFire && MP >= 1600) + QueueGCD(AID.Fire4, target, GCDPriority.Standard); + if (InAstralFire && ElementTimer <= 6) + QueueGCD(BestFire1, target, GCDPriority.Paradox); } - private void STLv72toLv89() - { - // TODO: Implement single-target rotation for level 72-89 + + private void STLv72toLv89(Actor? target) + { + //Ice + if (Unlocked(AID.Blizzard3) && //if Blizzard III is unlocked + (NoStance && MP == 10000 || //and if no stance is active and MP is max (opener) + (NoStance && MP < 10000 && Player.InCombat) || //or if in combat and no stance is active and MP is less than max (died or stopped attacking) + InAstralFire && MP < 1600)) //or if Astral Fire is active and MP is less than 400 + QueueGCD(AID.Blizzard3, target, InAstralFire && MP is 0 ? GCDPriority.NeedB3 : GCDPriority.Standard); + if (InUmbralIce && UmbralHearts != MaxUmbralHearts) + QueueGCD(BestBlizzard1, target, GCDPriority.Standard); + //Fire + if (CastTime(AID.Fire3) < SpS && !JustUsed(AID.Fire3, 5) && + (hasFirestarter || UmbralHearts == MaxUmbralHearts && UmbralStacks == 3)) + QueueGCD(AID.Fire3, target, UmbralHearts == MaxUmbralHearts && UmbralStacks == 3 ? GCDPriority.NeedF3P : GCDPriority.F3P); + if (InAstralFire && MP >= 1600) + QueueGCD(AID.Fire4, target, GCDPriority.Standard); + if (InAstralFire && ElementTimer <= 6 && MP >= 2800) + QueueGCD(BestFire1, target, GCDPriority.Paradox); + if (InAstralFire && MP is < 1600 and >= 800) + QueueGCD(AID.Despair, target, ElementTimer <= 4 && MP <= 2800 ? GCDPriority.NeedDespair : GCDPriority.Despair); } private void STLv90toLv99() { @@ -351,20 +557,32 @@ private void STLv100() { // TODO: Implement single-target rotation for level 100 } - private void BestST() + private void BestST(Actor? target) { if (Player.Level is >= 1 and <= 34) - STLv1toLv34(); + { + STLv1toLv34(target); + } if (Player.Level is >= 35 and <= 59) - STLv35toLv59(); + { + STLv35toLv59(target); + } if (Player.Level is >= 60 and <= 71) - STLv60toLv71(); + { + STLv60toLv71(target); + } if (Player.Level is >= 72 and <= 89) - STLv72toLv89(); + { + //STLv72toLv89(target); + } if (Player.Level is >= 90 and <= 99) - STLv90toLv99(); + { + //STLv90toLv99(target); + } if (Player.Level is 100) - STLv100(); + { + //STLv100(target); + } } private void AOELv12toLv34() { @@ -394,33 +612,50 @@ private void AOELv100() { // TODO: Implement AOE rotation for level 100 } - private void BestAOE() - { - if (Player.Level is >= 12 and <= 34) - AOELv12toLv34(); - if (Player.Level is >= 35 and <= 39) - AOELv35toLv39(); - if (Player.Level is >= 40 and <= 49) - AOELv40toLv49(); - if (Player.Level is >= 50 and <= 57) - AOELv50toLv57(); - if (Player.Level is >= 58 and <= 81) - AOELv58toLv81(); - if (Player.Level is >= 82 and <= 99) - AOELv82toLv99(); - if (Player.Level is 100) - AOELv100(); + private void BestAOE(Actor? target) + { + if (In25y(target)) + { + if (Player.Level is >= 12 and <= 34) + { + AOELv12toLv34(); + } + if (Player.Level is >= 35 and <= 39) + { + AOELv35toLv39(); + } + if (Player.Level is >= 40 and <= 49) + { + AOELv40toLv49(); + } + if (Player.Level is >= 50 and <= 57) + { + AOELv50toLv57(); + } + if (Player.Level is >= 58 and <= 81) + { + AOELv58toLv81(); + } + if (Player.Level is >= 82 and <= 99) + { + AOELv82toLv99(); + } + if (Player.Level is 100) + { + AOELv100(); + } + } } - private void BestRotation() + private void BestRotation(Actor? target) { if (ShouldUseAOE) { - BestAOE(); + BestAOE(target); } if (!ShouldUseAOE) { - BestST(); + BestST(target); } } @@ -435,9 +670,50 @@ private void BestRotation() ThunderStrategy.Delay => false, _ => false }; + private bool ShouldUsePolyglot(Actor? target, PolyglotStrategy strategy) => strategy switch + { + PolyglotStrategy.Automatic + => Player.InCombat && + target != null && + Polyglots > 0 && + ((CD(AID.Triplecast) <= 60 || CD(AID.LeyLines) <= 120) || (CD(AID.Manafont) < 0.6f && JustUsed(AID.Despair, 5) && MP < 1600) || + (Polyglots == MaxPolyglots && PolyglotTimer <= 5000)), //Overcap + PolyglotStrategy.OnlyXeno => ShouldUseXenoglossy(target, PolyglotStrategy.Automatic), + PolyglotStrategy.OnlyFoul => ShouldUseFoul(target, PolyglotStrategy.Automatic), + PolyglotStrategy.ForceXeno => canXeno, + PolyglotStrategy.ForceFoul => canFoul, + PolyglotStrategy.Delay => false, + _ => false + }; + private bool ShouldUseXenoglossy(Actor? target, PolyglotStrategy strategy) => strategy switch + { + PolyglotStrategy.Automatic + => Player.InCombat && + target != null && + canXeno && + Polyglots > 0 && + ((CD(AID.Triplecast) <= 60 || CD(AID.LeyLines) <= 120) || (CD(AID.Manafont) < 0.6f && JustUsed(AID.Despair, 5) && MP < 1600) || + (Polyglots == MaxPolyglots && PolyglotTimer <= 5000)), //Overcap + _ => false + }; + private bool ShouldUseFoul(Actor? target, PolyglotStrategy strategy) => strategy switch + { + PolyglotStrategy.Automatic + => Player.InCombat && + target != null && + canFoul && + Polyglots > 0 && + ((CD(AID.Triplecast) <= 60 || CD(AID.LeyLines) <= 120) || (CD(AID.Manafont) < 0.6f && JustUsed(AID.Despair, 5) && MP < 1600) || + (Polyglots == MaxPolyglots && PolyglotTimer <= 5000)), //Overcap + _ => false + }; private bool ShouldUseLeyLines(Actor? target, OffensiveStrategy strategy) => strategy switch { - OffensiveStrategy.Automatic => Player.InCombat && target != null && canLL && canWeaveIn, + OffensiveStrategy.Automatic + => Player.InCombat && + target != null && + canLL && + canWeaveIn, OffensiveStrategy.Force => canLL, OffensiveStrategy.AnyWeave => canLL && canWeaveIn, OffensiveStrategy.EarlyWeave => canLL && canWeaveEarly, @@ -447,7 +723,12 @@ private void BestRotation() }; private bool ShouldUseAmplifier(Actor? target, OffensiveStrategy strategy) => strategy switch { - OffensiveStrategy.Automatic => Player.InCombat && target != null && canAmp && canWeaveIn, + OffensiveStrategy.Automatic + => Player.InCombat && + target != null && + canAmp && + canWeaveIn && + Polyglots != MaxPolyglots, OffensiveStrategy.Force => canAmp, OffensiveStrategy.AnyWeave => canAmp && canWeaveIn, OffensiveStrategy.EarlyWeave => canAmp && canWeaveEarly, @@ -457,7 +738,12 @@ private void BestRotation() }; private bool ShouldUseManafont(Actor? target, ManafontStrategy strategy) => strategy switch { - ManafontStrategy.Automatic => Player.InCombat && target != null && canMF && canWeaveIn, + ManafontStrategy.Automatic + => Player.InCombat && + target != null && + canMF && + InAstralFire && + (JustUsed(BestXenoglossy, 5) && MP < 1600), ManafontStrategy.Force => canMF, ManafontStrategy.ForceWeave => canMF && canWeaveIn, ManafontStrategy.ForceEX => canMF, @@ -465,5 +751,19 @@ private void BestRotation() ManafontStrategy.Delay => false, _ => false }; + private bool ShouldUseTriplecast(Actor? target, OffensiveStrategy strategy) => strategy switch + { + OffensiveStrategy.Automatic + => Player.InCombat && + target != null && + canTC && + canWeaveIn, + OffensiveStrategy.Force => canWeaveIn, + OffensiveStrategy.AnyWeave => canWeaveIn, + OffensiveStrategy.EarlyWeave => canWeaveEarly, + OffensiveStrategy.LateWeave => canWeaveLate, + OffensiveStrategy.Delay => false, + _ => false + }; #endregion } From b170801d750daf54e7680f70c90a5f4f093f3e7d Mon Sep 17 00:00:00 2001 From: AceAkechi123 Date: Thu, 16 Jan 2025 05:50:05 -0800 Subject: [PATCH 03/18] even more WIP, getting there --- BossMod/Autorotation/akechi/AkechiBLM.cs | 274 +++++++++++++++++------ 1 file changed, 207 insertions(+), 67 deletions(-) diff --git a/BossMod/Autorotation/akechi/AkechiBLM.cs b/BossMod/Autorotation/akechi/AkechiBLM.cs index 99b27c05c9..c7ce3cb95e 100644 --- a/BossMod/Autorotation/akechi/AkechiBLM.cs +++ b/BossMod/Autorotation/akechi/AkechiBLM.cs @@ -16,11 +16,13 @@ public enum Track Thunder, //Thunder tracking Polyglot, //Polyglot tracking Manafont, //Manafont tracking - Potion, //Potion item tracking - Transpose, //Transpose tracking Triplecast, //Triplecast tracking LeyLines, //Ley Lines tracking + Potion, //Potion item tracking + Transpose, //Transpose tracking Amplifier, //Amplifier tracking + Retrace, //Retrace tracking + BTL, //Between the Lines tracking } public enum AOEStrategy { @@ -55,6 +57,24 @@ public enum ManafontStrategy ForceWeaveEX, //Force the use of Manafont (100s CD) in any next possible weave slot Delay //Delay the use of Manafont for strategic reasons } + public enum TriplecastStrategy + { + Automatic, //Automatically decide when to use Triplecast + Force, //Force the use of Triplecast; use all charges + Force1, //Force the use of Triplecast; holds one charge for manual usage + ForceWeave, //Force the use of Triplecast in any next possible weave slot + ForceWeave1, //Force the use of Triplecast in any next possible weave slot; holds one charge for manual usage + Delay //Delay the use of Triplecast + } + public enum LeyLinesStrategy + { + Automatic, //Automatically decide when to use Ley Lines + Force, //Force the use of Ley Lines, regardless of weaving conditions + Force1, //Force the use of Ley Lines; holds one charge for manual usage + ForceWeave, //Force the use of Ley Lines in any next possible weave slot + ForceWeave1, //Force the use of Ley Lines in any next possible weave slot; holds one charge for manual usage + Delay //Delay the use of Ley Lines + } public enum PotionStrategy { Manual, //Manual potion usage @@ -111,9 +131,25 @@ public static RotationModuleDefinition Definition() .AddOption(ManafontStrategy.ForceWeaveEX, "ForceWeaveEX", "Force the use of Manafont (100s CD) in any next possible weave slot", 100, 0, ActionTargets.Self, 84) .AddOption(ManafontStrategy.Delay, "Delay", "Delay the use of Manafont for strategic reasons", 0, 0, ActionTargets.Self, 30) .AddAssociatedActions(AID.Manafont); + res.Define(Track.Triplecast).As("Triplecast", uiPriority: 160) + .AddOption(TriplecastStrategy.Automatic, "Auto", "Automatically decide when to use Triplecast", 60, 0, ActionTargets.Self, 66) + .AddOption(TriplecastStrategy.Force, "Force", "Force the use of Triplecast; use 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) + .AddAssociatedActions(AID.Triplecast); + res.Define(Track.LeyLines).As("Ley Lines", uiPriority: 150) + .AddOption(LeyLinesStrategy.Automatic, "Auto", "Automatically decide when to use Ley Lines", 120, 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) + .AddAssociatedActions(AID.LeyLines); res.Define(Track.Potion).As("Potion", uiPriority: 180) .AddOption(PotionStrategy.Manual, "Manual", "Do not use automatically") - .AddOption(PotionStrategy.AlignWithRaidBuffs, "AlignWithRaidBuffs", "Align with No Mercy & Bloodfest together (to ensure use on 2-minute windows)", 270, 30, ActionTargets.Self) + .AddOption(PotionStrategy.AlignWithRaidBuffs, "AlignWithRaidBuffs", "Align with buffs (to ensure use on 2-minute windows)", 270, 30, ActionTargets.Self) .AddOption(PotionStrategy.Immediate, "Immediate", "Use ASAP, regardless of any buffs", 270, 30, ActionTargets.Self) .AddAssociatedAction(ActionDefinitions.IDPotionInt); #endregion @@ -127,30 +163,30 @@ public static RotationModuleDefinition Definition() .AddOption(OffensiveStrategy.LateWeave, "LateWeave", "Force the use of Transpose in very next LAST weave slot only", 0, 0, ActionTargets.Self, 4) .AddOption(OffensiveStrategy.Delay, "Delay", "Delay the use of Transpose", 0, 0, ActionTargets.Self, 4) .AddAssociatedActions(AID.Transpose); - res.Define(Track.Triplecast).As("Triplecast", uiPriority: 170) - .AddOption(OffensiveStrategy.Automatic, "Auto", "Automatically decide when to use Triplecast", 60, 0, ActionTargets.Self, 4) - .AddOption(OffensiveStrategy.Force, "Force", "Force the use of Triplecast, regardless of weaving conditions", 60, 0, ActionTargets.Self, 4) - .AddOption(OffensiveStrategy.AnyWeave, "AnyWeave", "Force the use of Triplecast in any next possible weave slot", 60, 0, ActionTargets.Self, 4) - .AddOption(OffensiveStrategy.EarlyWeave, "EarlyWeave", "Force the use of Triplecast in very next FIRST weave slot only", 60, 0, ActionTargets.Self, 4) - .AddOption(OffensiveStrategy.LateWeave, "LateWeave", "Force the use of Triplecast in very next LAST weave slot only", 60, 0, ActionTargets.Self, 4) - .AddOption(OffensiveStrategy.Delay, "Delay", "Delay the use of Triplecast", 0, 0, ActionTargets.Self, 4) - .AddAssociatedActions(AID.Triplecast); - res.Define(Track.LeyLines).As("LeyLines", uiPriority: 170) - .AddOption(OffensiveStrategy.Automatic, "Auto", "Automatically decide when to use Ley Lines", 90, 0, ActionTargets.Self, 4) - .AddOption(OffensiveStrategy.Force, "Force", "Force the use of Ley Lines, regardless of weaving conditions", 90, 0, ActionTargets.Self, 4) - .AddOption(OffensiveStrategy.AnyWeave, "AnyWeave", "Force the use of Ley Lines in any next possible weave slot", 90, 0, ActionTargets.Self, 4) - .AddOption(OffensiveStrategy.EarlyWeave, "EarlyWeave", "Force the use of Ley Lines in very next FIRST weave slot only", 90, 0, ActionTargets.Self, 4) - .AddOption(OffensiveStrategy.LateWeave, "LateWeave", "Force the use of Ley Lines in very next LAST weave slot only", 90, 0, ActionTargets.Self, 4) - .AddOption(OffensiveStrategy.Delay, "Delay", "Delay the use of Ley Lines", 0, 0, ActionTargets.Self, 4) - .AddAssociatedActions(AID.LeyLines); res.Define(Track.Amplifier).As("Amplifier", uiPriority: 170) - .AddOption(OffensiveStrategy.Automatic, "Auto", "Automatically decide when to use Amplifier", 30, 0, ActionTargets.Self, 4) - .AddOption(OffensiveStrategy.Force, "Force", "Force the use of Amplifier, regardless of weaving conditions", 30, 0, ActionTargets.Self, 4) - .AddOption(OffensiveStrategy.AnyWeave, "AnyWeave", "Force the use of Amplifier in any next possible weave slot", 30, 0, ActionTargets.Self, 4) - .AddOption(OffensiveStrategy.EarlyWeave, "EarlyWeave", "Force the use of Amplifier in very next FIRST weave slot only", 30, 0, ActionTargets.Self, 4) - .AddOption(OffensiveStrategy.LateWeave, "LateWeave", "Force the use of Amplifier in very next LAST weave slot only", 30, 0, ActionTargets.Self, 4) - .AddOption(OffensiveStrategy.Delay, "Delay", "Delay the use of Amplifier", 0, 0, ActionTargets.Self, 4) + .AddOption(OffensiveStrategy.Automatic, "Auto", "Automatically decide when to use Amplifier", 120, 0, ActionTargets.Self, 86) + .AddOption(OffensiveStrategy.Force, "Force", "Force the use of Amplifier, regardless of weaving conditions", 120, 0, ActionTargets.Self, 86) + .AddOption(OffensiveStrategy.AnyWeave, "AnyWeave", "Force the use of Amplifier in any next possible weave slot", 120, 0, ActionTargets.Self, 86) + .AddOption(OffensiveStrategy.EarlyWeave, "EarlyWeave", "Force the use of Amplifier in very next FIRST weave slot only", 120, 0, ActionTargets.Self, 86) + .AddOption(OffensiveStrategy.LateWeave, "LateWeave", "Force the use of Amplifier in very next LAST weave slot only", 120, 0, ActionTargets.Self, 86) + .AddOption(OffensiveStrategy.Delay, "Delay", "Delay the use of Amplifier", 0, 0, ActionTargets.Self, 86) .AddAssociatedActions(AID.Amplifier); + res.Define(Track.Retrace).As("Retrace", uiPriority: 170) + .AddOption(OffensiveStrategy.Automatic, "Auto", "Automatically decide when to use Retrace", 40, 0, ActionTargets.Self, 96) + .AddOption(OffensiveStrategy.Force, "Force", "Force the use of Retrace, regardless of weaving conditions", 40, 0, ActionTargets.Self, 96) + .AddOption(OffensiveStrategy.AnyWeave, "AnyWeave", "Force the use of Retrace in any next possible weave slot", 40, 0, ActionTargets.Self, 96) + .AddOption(OffensiveStrategy.EarlyWeave, "EarlyWeave", "Force the use of Retrace in very next FIRST weave slot only", 40, 0, ActionTargets.Self, 96) + .AddOption(OffensiveStrategy.LateWeave, "LateWeave", "Force the use of Retrace in very next LAST weave slot only", 40, 0, ActionTargets.Self, 96) + .AddOption(OffensiveStrategy.Delay, "Delay", "Delay the use of Retrace", 0, 0, ActionTargets.Self, 96) + .AddAssociatedActions(AID.Retrace); + res.Define(Track.BTL).As("Between The Lines", "BTL", uiPriority: 170) + .AddOption(OffensiveStrategy.Automatic, "Auto", "Automatically decide when to use Between The Lines", 3, 0, ActionTargets.Self, 62) + .AddOption(OffensiveStrategy.Force, "Force", "Force the use of Between The Lines, regardless of weaving conditions", 3, 0, ActionTargets.Self, 62) + .AddOption(OffensiveStrategy.AnyWeave, "AnyWeave", "Force the use of Between The Lines in any next possible weave slot", 3, 0, ActionTargets.Self, 62) + .AddOption(OffensiveStrategy.EarlyWeave, "EarlyWeave", "Force the use of Between The Lines in very next FIRST weave slot only", 3, 0, ActionTargets.Self, 62) + .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 @@ -161,14 +197,24 @@ public static RotationModuleDefinition Definition() 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 Standard = 300, //standard abilities DOT = 350, //damage-over-time abilities + FlareStar = 375, //Flare Star Despair = 400, //Despair F3P = 450, //Fire III proc NeedB3 = 460, //Need to use Blizzard III Polyglot = 475, //Polyglots Paradox = 500, //Paradox - NeedDOT = 600, //Need to apply DOTs NeedF3P = 625, //Need to use Fire III proc NeedDespair = 640, //Need to use Despair @@ -209,10 +255,13 @@ public static RotationModuleDefinition Definition() private bool ParadoxActive; //Paradox is active private bool canFoul; //Can use Foul private bool canXeno; //Can use Xenoglossy + private bool canParadox; //Can use Paradox private bool canLL; //Can use Ley Lines private bool canAmp; //Can use Amplifier private bool canTC; //Can use Triplecast private bool canMF; //Can use Manafont + private bool canRetrace; //Can use Retrace + private bool canBTL; //Can use Between the Lines private bool hasFirestarter; //Has Firestarter buff private bool hasThunderhead; //Has Thunderhead buff private float ThunderLeft; //Time left on DOT effect (30s base) @@ -238,8 +287,9 @@ public static RotationModuleDefinition Definition() private bool ActionReady(AID aid) => Unlocked(aid) && CD(aid) < 0.6f; //Check if the desired action is ready (cooldown less than 0.6 seconds) private bool IsFirstGCD() => !Player.InCombat || (World.CurrentTime - Manager.CombatStart).TotalSeconds < 0.1f; //Check if this is the first GCD in combat private int TargetsInRange() => Hints.NumPriorityTargetsInAOECircle(Player.Position, 25); //Returns the number of targets hit by AOE within a 25-yalm radius around the player - public bool PlayerHasEffect(SID sid, float duration) => SelfStatusLeft(sid, duration) > GCD; //Checks if Status effect is on self - public bool JustUsed(AID aid, float variance) + private bool PlayerHasEffect(SID sid, float duration) => SelfStatusLeft(sid, duration) > GCD; //Checks if Status effect is on self + private float GCDsInTimer => (ElementTimer / 2.5f) + 1f; //Calculates the number of GCDs in the Enochian timer + private bool JustUsed(AID aid, float variance) { if (Manager?.LastCast == null) return false; @@ -250,26 +300,24 @@ public bool JustUsed(AID aid, float variance) private float GetCastTime(AID aid) => ActionDefinitions.Instance.Spell(aid)!.CastTime * SpS / 2.5f; private float CastTime(AID aid) { + var aspect = ActionDefinitions.Instance.Spell(aid)!.Aspect; + var castTime = GetCastTime(aid); if (PlayerHasEffect(SID.Triplecast, 15) || PlayerHasEffect(SID.Swiftcast, 10)) return 0; - - var aspect = ActionDefinitions.Instance.Spell(aid)!.Aspect; - if (aid == AID.Fire3 && hasFirestarter || aid == AID.Foul && Unlocked(TraitID.EnhancedFoul) - || aspect == ActionAspect.Thunder && hasThunderhead) + || aspect == ActionAspect.Thunder && hasThunderhead + || aid == AID.Despair & Unlocked(TraitID.EnhancedAstralFire)) return 0; - - var castTime = GetCastTime(aid); if (castTime == 0) return 0; - - if (ElementStance == -3 && aspect == ActionAspect.Fire || ElementStance == 3 && aspect == ActionAspect.Ice) + if (ElementStance == -3 && + aspect == ActionAspect.Fire || + ElementStance == 3 && + aspect == ActionAspect.Ice) castTime *= 0.5f; - return castTime; - } private int TargetsInPlayerAOE(Actor primary) => Hints.NumPriorityTargetsInAOERect( //Use Hints to count number of targets in AOE rectangle Player.Position, //from Player's position @@ -277,12 +325,14 @@ private int TargetsInPlayerAOE(Actor primary) => Hints.NumPriorityTargetsInAOERe .Normalized(), //normalized direction 25, //AOE rectangle length 5); //AOE rectangle width - public Actor? TargetChoice(StrategyValues.OptionRef strategy) => ResolveTargetOverride(strategy.Value); //Resolves the target choice based on the strategy + private Actor? TargetChoice(StrategyValues.OptionRef strategy) => ResolveTargetOverride(strategy.Value); //Resolves the target choice based on the strategy #endregion #region Upgrade Paths + private AID FireAspect => Unlocked(AID.Fire4) ? AID.Fire4 : Unlocked(AID.Fire3) ? AID.Fire3 : Unlocked(AID.Fire2) ? AID.Fire2 : AID.Fire1; + private AID IceAspect => Unlocked(AID.Blizzard4) ? AID.Blizzard4 : Unlocked(AID.Freeze) ? AID.Freeze : Unlocked(AID.Blizzard3) ? AID.Blizzard3 : Unlocked(AID.Blizzard2) ? AID.Blizzard2 : AID.Blizzard1; private AID BestFire1 - => Unlocked(AID.Paradox) ? AID.Paradox + => Unlocked(AID.Paradox) && ParadoxActive ? AID.Paradox : AID.Fire1; private AID BestBlizzard1 => Unlocked(AID.Blizzard4) ? AID.Blizzard4 @@ -328,10 +378,13 @@ public override void Execute(StrategyValues strategy, Actor? primaryTarget, floa MaxPolyglots = Unlocked(TraitID.EnhancedPolyglotII) ? 3 : Unlocked(TraitID.EnhancedPolyglot) ? 2 : 1; canFoul = Unlocked(AID.Foul) && Polyglots > 0; //Can use Foul canXeno = Unlocked(AID.Xenoglossy) && Polyglots > 0; //Can use Xenoglossy + canParadox = Unlocked(AID.Paradox) && gauge.ParadoxActive; //Can use Paradox canLL = Unlocked(AID.LeyLines) && CD(AID.LeyLines) <= 120 && SelfStatusLeft(SID.LeyLines, 30) == 0; //Can use Ley Lines canAmp = ActionReady(AID.Amplifier); //Can use Amplifier canTC = Unlocked(AID.Triplecast) && CD(AID.Triplecast) <= 60 && SelfStatusLeft(SID.Triplecast) == 0; //Can use Triplecast canMF = ActionReady(AID.Manafont); //Can use Manafont + canRetrace = ActionReady(AID.Retrace) && PlayerHasEffect(SID.LeyLines, 30); //Can use Retrace + canBTL = ActionReady(AID.BetweenTheLines) && PlayerHasEffect(SID.LeyLines, 30); //Can use Between the Lines hasFirestarter = PlayerHasEffect(SID.Firestarter, 15); //Has Firestarter buff hasThunderhead = PlayerHasEffect(SID.Thunderhead, 30); //Has Thunderhead buff ThunderLeft = Utils.MaxAll( @@ -360,9 +413,9 @@ public override void Execute(StrategyValues strategy, Actor? primaryTarget, floa var mf = strategy.Option(Track.Manafont); //Manafont track var mfStrat = mf.As(); //Manafont strategy var tc = strategy.Option(Track.Triplecast); //Triplecast track - var tcStrat = tc.As(); //Triplecast strategy + var tcStrat = tc.As(); //Triplecast strategy var ll = strategy.Option(Track.LeyLines); //Ley Lines track - var llStrat = ll.As(); //Ley Lines strategy + var llStrat = ll.As(); //Ley Lines strategy var amp = strategy.Option(Track.Amplifier); //Amplifier track var ampStrat = amp.As(); //Amplifier strategy var potion = strategy.Option(Track.Potion).As(); //Potion strategy @@ -370,9 +423,9 @@ public override void Execute(StrategyValues strategy, Actor? primaryTarget, floa #endregion - #region Force Execution + #region Force Execution if (AOEStrategy is AOEStrategy.Auto) - STLv72toLv89(TargetChoice(AOE) ?? primaryTarget); + STAce(TargetChoice(AOE) ?? primaryTarget); if (AOEStrategy is AOEStrategy.ForceST) BestST(TargetChoice(AOE) ?? primaryTarget); if (AOEStrategy is AOEStrategy.ForceAOE) @@ -528,6 +581,55 @@ private void STLv60toLv71(Actor? target) QueueGCD(BestFire1, target, GCDPriority.Paradox); } + private void STAce(Actor? target) + { + if (NoStance) + { + if (MP >= 10000) //if no stance is active and MP is max (opener) + QueueGCD(AID.Blizzard3, target, GCDPriority.NeedB3); + 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(swiftcastB3, target, GCDPriority.NeedB3); + } + if (InUmbralIce) + { + //Step 1 - max stacks in UI + if (Unlocked(AID.Blizzard4) && + JustUsed(AID.Blizzard3, 5) || UmbralHearts != MaxUmbralHearts) + QueueGCD(AID.Blizzard4, target, GCDPriority.Step3); + //Step 2 - Ice Paradox + if (canParadox && + JustUsed(AID.Blizzard4, 5)) + QueueGCD(AID.Paradox, target, GCDPriority.Step2); + //Step 3 - swap from UI to AF + if (Unlocked(AID.Fire3)) + QueueGCD(AID.Fire3, target, GCDPriority.Step1); + } + if (InAstralFire) + { + //Step 1 to 3/4, 4/5 to 7 - F4 or F1 if no F4 yet + if (GCDsInTimer >= 2 && + AstralSoulStacks != 6 && + MP >= 1600) + QueueGCD(Unlocked(AID.Fire4) ? AID.Fire4 : AID.Fire1, target, GCDPriority.Step10); + //Step 4 - Paradox / Fire I + if (GCDsInTimer < 3 && + InAstralFire ? MP >= 1600 : MP >= 800) + QueueGCD(BestFire1, target, ElementTimer <= 3 ? GCDPriority.Paradox : GCDPriority.Step5); + //Step 5 - F3P + if (hasFirestarter) + QueueGCD(AID.Fire3, target, GCDPriority.Step4); + //Step 8 - Despair + if (MP is < 1600 and not 0 && + Unlocked(AID.Despair)) + QueueGCD(AID.Despair, target, GCDPriority.Step3); + //Step 9 - Flare Star + if (AstralSoulStacks == 6) + QueueGCD(AID.FlareStar, target, GCDPriority.Step2); + //Step 10 - swap from AF to UI + QueueGCD(AID.Blizzard3, target, GCDPriority.Step1); + } + } + private void STLv72toLv89(Actor? target) { //Ice @@ -539,15 +641,22 @@ private void STLv72toLv89(Actor? target) if (InUmbralIce && UmbralHearts != MaxUmbralHearts) QueueGCD(BestBlizzard1, target, GCDPriority.Standard); //Fire - if (CastTime(AID.Fire3) < SpS && !JustUsed(AID.Fire3, 5) && - (hasFirestarter || UmbralHearts == MaxUmbralHearts && UmbralStacks == 3)) - QueueGCD(AID.Fire3, target, UmbralHearts == MaxUmbralHearts && UmbralStacks == 3 ? GCDPriority.NeedF3P : GCDPriority.F3P); - if (InAstralFire && MP >= 1600) - QueueGCD(AID.Fire4, target, GCDPriority.Standard); - if (InAstralFire && ElementTimer <= 6 && MP >= 2800) - QueueGCD(BestFire1, target, GCDPriority.Paradox); - if (InAstralFire && MP is < 1600 and >= 800) - QueueGCD(AID.Despair, target, ElementTimer <= 4 && MP <= 2800 ? GCDPriority.NeedDespair : GCDPriority.Despair); + if (CastTime(AID.Fire3) < SpS && !JustUsed(AID.Fire3, 5)) + { + if (SelfStatusLeft(SID.Firestarter) < 25) + QueueGCD(AID.Fire3, target, GCDPriority.F3P); + if (UmbralHearts == MaxUmbralHearts && UmbralStacks == 3) + QueueGCD(AID.Fire3, target, GCDPriority.NeedF3P); + } + if (InAstralFire) + { + if (MP >= 1600) + QueueGCD(AID.Fire4, target, GCDPriority.Standard); + if (ElementTimer <= 6 && MP >= 2800) + QueueGCD(BestFire1, target, GCDPriority.Paradox); + if (MP is < 1600 and >= 800) + QueueGCD(AID.Despair, target, ElementTimer <= 4 && MP <= 2800 ? GCDPriority.NeedDespair : GCDPriority.Despair); + } } private void STLv90toLv99() { @@ -707,18 +816,18 @@ private void BestRotation(Actor? target) (Polyglots == MaxPolyglots && PolyglotTimer <= 5000)), //Overcap _ => false }; - private bool ShouldUseLeyLines(Actor? target, OffensiveStrategy strategy) => strategy switch + private bool ShouldUseLeyLines(Actor? target, LeyLinesStrategy strategy) => strategy switch { - OffensiveStrategy.Automatic + LeyLinesStrategy.Automatic => Player.InCombat && target != null && canLL && canWeaveIn, - OffensiveStrategy.Force => canLL, - OffensiveStrategy.AnyWeave => canLL && canWeaveIn, - OffensiveStrategy.EarlyWeave => canLL && canWeaveEarly, - OffensiveStrategy.LateWeave => canLL && canWeaveLate, - OffensiveStrategy.Delay => false, + LeyLinesStrategy.Force => canLL, + LeyLinesStrategy.Force1 => canLL && CD(AID.LeyLines) < 6, + LeyLinesStrategy.ForceWeave => canLL && canWeaveIn, + LeyLinesStrategy.ForceWeave1 => canLL && canWeaveIn && CD(AID.LeyLines) < 6, + LeyLinesStrategy.Delay => false, _ => false }; private bool ShouldUseAmplifier(Actor? target, OffensiveStrategy strategy) => strategy switch @@ -736,6 +845,37 @@ private void BestRotation(Actor? target) OffensiveStrategy.Delay => false, _ => false }; + private bool ShouldUseRetrace(Actor? target, OffensiveStrategy strategy) => strategy switch + { + OffensiveStrategy.Automatic + => Player.InCombat && + target != null && + canWeaveIn && + canRetrace, + OffensiveStrategy.Force => canRetrace, + OffensiveStrategy.AnyWeave => canRetrace && canWeaveIn, + OffensiveStrategy.EarlyWeave => canRetrace && canWeaveEarly, + OffensiveStrategy.LateWeave => canRetrace && canWeaveLate, + OffensiveStrategy.Delay => false, + _ => false + + }; + private bool ShouldUseBTL(Actor? target, OffensiveStrategy strategy) => strategy switch + { + OffensiveStrategy.Automatic + => Player.InCombat && + target != null && + canWeaveIn && + canBTL, + OffensiveStrategy.Force => canBTL, + OffensiveStrategy.AnyWeave => canBTL && canWeaveIn, + OffensiveStrategy.EarlyWeave => canBTL && canWeaveEarly, + OffensiveStrategy.LateWeave => canBTL && canWeaveLate, + OffensiveStrategy.Delay => false, + _ => false + + }; + private bool ShouldUseManafont(Actor? target, ManafontStrategy strategy) => strategy switch { ManafontStrategy.Automatic @@ -751,18 +891,18 @@ private void BestRotation(Actor? target) ManafontStrategy.Delay => false, _ => false }; - private bool ShouldUseTriplecast(Actor? target, OffensiveStrategy strategy) => strategy switch + private bool ShouldUseTriplecast(Actor? target, TriplecastStrategy strategy) => strategy switch { - OffensiveStrategy.Automatic + TriplecastStrategy.Automatic => Player.InCombat && target != null && canTC && canWeaveIn, - OffensiveStrategy.Force => canWeaveIn, - OffensiveStrategy.AnyWeave => canWeaveIn, - OffensiveStrategy.EarlyWeave => canWeaveEarly, - OffensiveStrategy.LateWeave => canWeaveLate, - OffensiveStrategy.Delay => false, + TriplecastStrategy.Force => canTC, + TriplecastStrategy.Force1 => canTC && CD(AID.Triplecast) < 6, + TriplecastStrategy.ForceWeave => canTC && canWeaveIn, + TriplecastStrategy.ForceWeave1 => canTC && canWeaveIn && CD(AID.Triplecast) < 6, + TriplecastStrategy.Delay => false, _ => false }; #endregion From 97d04b2a9a1de3f5fdc6c351b7bc6981f94162cb Mon Sep 17 00:00:00 2001 From: ace Date: Thu, 16 Jan 2025 09:46:46 -0800 Subject: [PATCH 04/18] Lv100 ST working smooth wow --- BossMod/Autorotation/akechi/AkechiBLM.cs | 199 ++++++++++++++--------- 1 file changed, 121 insertions(+), 78 deletions(-) diff --git a/BossMod/Autorotation/akechi/AkechiBLM.cs b/BossMod/Autorotation/akechi/AkechiBLM.cs index c7ce3cb95e..05518f82f4 100644 --- a/BossMod/Autorotation/akechi/AkechiBLM.cs +++ b/BossMod/Autorotation/akechi/AkechiBLM.cs @@ -41,9 +41,15 @@ public enum ThunderStrategy } public enum PolyglotStrategy { - Automatic, //Automatically decide when to use Polyglot based on targets nearby - OnlyXeno, //Automatically use Xenoglossy optimal spender, regardless of targets nearby - OnlyFoul, //Automatically use Foul optimal spender, regardless of targets nearby + AutoAll, //Spend all Polyglots as soon as possible + Auto2, //Spend 2 Polyglots; holds one for manual usage + Auto1, //Spend 1 Polyglot; holds two for manual usage + OnlyXenoAll, //Use Xenoglossy as optimal spender, regardless of targets nearby; spends all Polyglots + OnlyXeno2, //Use Xenoglossy as optimal spender, regardless of targets nearby; holds one Polyglot for manual usage + OnlyXeno1, //Use Xenoglossy as optimal spender, regardless of targets nearby; holds two Polyglots for manual usage + OnlyFoulAll, //Use Foul as optimal spender, regardless of targets nearby + OnlyFoul2, //Use Foul as optimal spender, regardless of targets nearby; holds one Polyglot for manual usage + OnlyFoul1, //Use Foul as optimal spender, regardless of targets nearby; holds two Polyglots for manual usage ForceXeno, //Force use of Xenoglossy ForceFoul, //Force use of Foul Delay //Delay the use of Polyglot abilities for manual or strategic usage @@ -116,12 +122,18 @@ public static RotationModuleDefinition Definition() .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) - .AddOption(PolyglotStrategy.Automatic, "Auto", "Automatically decide when to use Polyglot based on targets nearby", 0, 0, ActionTargets.Hostile, 70) - .AddOption(PolyglotStrategy.OnlyXeno, "Only Xenoglossy", "Automatically use Xenoglossy optimal spender, regardless of targets nearby", 0, 0, ActionTargets.Hostile, 80) - .AddOption(PolyglotStrategy.OnlyFoul, "Only Foul", "Automatically use Foul optimal spender, regardless of targets nearby", 0, 0, ActionTargets.Hostile, 70) + .AddOption(PolyglotStrategy.AutoAll, "AutoAll", "Spend all Polyglots as soon as possible", 0, 0, ActionTargets.Hostile, 70) + .AddOption(PolyglotStrategy.Auto2, "Auto2", "Spend 2 Polyglots; holds one for manual usage", 0, 0, ActionTargets.Hostile, 70) + .AddOption(PolyglotStrategy.Auto1, "Auto1", "Spend 1 Polyglot; holds two for manual usage", 0, 0, ActionTargets.Hostile, 70) + .AddOption(PolyglotStrategy.OnlyXenoAll, "OnlyXenoAll", "Use Xenoglossy as optimal spender, regardless of targets nearby; spends all Polyglots", 0, 0, ActionTargets.Hostile, 80) + .AddOption(PolyglotStrategy.OnlyXeno2, "OnlyXeno2", "Use Xenoglossy as optimal spender, regardless of targets nearby; holds one Polyglot for manual usage", 0, 0, ActionTargets.Hostile, 80) + .AddOption(PolyglotStrategy.OnlyXeno1, "OnlyXeno1", "Use Xenoglossy as optimal spender, regardless of targets nearby; holds two Polyglots for manual usage", 0, 0, ActionTargets.Hostile, 80) + .AddOption(PolyglotStrategy.OnlyFoulAll, "OnlyFoulAll", "Use Foul as optimal spender, regardless of targets nearby", 0, 0, ActionTargets.Hostile, 70) + .AddOption(PolyglotStrategy.OnlyFoul2, "OnlyFoul2", "Use Foul as optimal spender, regardless of targets nearby; holds one Polyglot for manual usage", 0, 0, ActionTargets.Hostile, 70) + .AddOption(PolyglotStrategy.OnlyFoul1, "OnlyFoul1", "Use Foul as optimal spender, regardless of targets nearby; holds two Polyglots for manual usage", 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, 30, ActionTargets.Hostile, 70) - .AddOption(PolyglotStrategy.Delay, "Delay", "Delay the use of Polyglot abilities for manual or strategic usage", 0, 0, ActionTargets.Hostile, 2) + .AddOption(PolyglotStrategy.Delay, "Delay", "Delay the use of Polyglot abilities for manual or strategic usage", 0, 0, ActionTargets.Hostile, 70) .AddAssociatedActions(AID.Xenoglossy, AID.Foul); res.Define(Track.Manafont).As("Manafont", "Manafont", uiPriority: 170) .AddOption(ManafontStrategy.Automatic, "Auto", "Automatically decide when to use Manafont", 0, 0, ActionTargets.Self, 30) @@ -132,8 +144,8 @@ public static RotationModuleDefinition Definition() .AddOption(ManafontStrategy.Delay, "Delay", "Delay the use of Manafont for strategic reasons", 0, 0, ActionTargets.Self, 30) .AddAssociatedActions(AID.Manafont); res.Define(Track.Triplecast).As("Triplecast", uiPriority: 160) - .AddOption(TriplecastStrategy.Automatic, "Auto", "Automatically decide when to use Triplecast", 60, 0, ActionTargets.Self, 66) - .AddOption(TriplecastStrategy.Force, "Force", "Force the use of Triplecast; use all charges", 60, 0, ActionTargets.Self, 66) + .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)", 60, 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) @@ -329,16 +341,19 @@ private int TargetsInPlayerAOE(Actor primary) => Hints.NumPriorityTargetsInAOERe #endregion #region Upgrade Paths - private AID FireAspect => Unlocked(AID.Fire4) ? AID.Fire4 : Unlocked(AID.Fire3) ? AID.Fire3 : Unlocked(AID.Fire2) ? AID.Fire2 : AID.Fire1; - private AID IceAspect => Unlocked(AID.Blizzard4) ? AID.Blizzard4 : Unlocked(AID.Freeze) ? AID.Freeze : Unlocked(AID.Blizzard3) ? AID.Blizzard3 : Unlocked(AID.Blizzard2) ? AID.Blizzard2 : AID.Blizzard1; - private AID BestFire1 - => Unlocked(AID.Paradox) && ParadoxActive ? AID.Paradox + private AID BestParadox + => Unlocked(AID.Paradox) + && ParadoxActive + ? AID.Paradox : AID.Fire1; private AID BestBlizzard1 - => Unlocked(AID.Blizzard4) ? AID.Blizzard4 + => Unlocked(AID.Blizzard4) + ? AID.Blizzard4 : AID.Blizzard1; - private AID swiftcastB3 - => Player.InCombat && ActionReady(AID.Swiftcast) ? AID.Swiftcast + private AID SwiftcastB3 + => Player.InCombat + && ActionReady(AID.Swiftcast) + ? AID.Swiftcast : AID.Blizzard3; private AID BestThunderST => Unlocked(AID.HighThunder) ? AID.HighThunder @@ -378,7 +393,7 @@ public override void Execute(StrategyValues strategy, Actor? primaryTarget, floa MaxPolyglots = Unlocked(TraitID.EnhancedPolyglotII) ? 3 : Unlocked(TraitID.EnhancedPolyglot) ? 2 : 1; canFoul = Unlocked(AID.Foul) && Polyglots > 0; //Can use Foul canXeno = Unlocked(AID.Xenoglossy) && Polyglots > 0; //Can use Xenoglossy - canParadox = Unlocked(AID.Paradox) && gauge.ParadoxActive; //Can use Paradox + canParadox = Unlocked(AID.Paradox) && ParadoxActive; //Can use Paradox canLL = Unlocked(AID.LeyLines) && CD(AID.LeyLines) <= 120 && SelfStatusLeft(SID.LeyLines, 30) == 0; //Can use Ley Lines canAmp = ActionReady(AID.Amplifier); //Can use Amplifier canTC = Unlocked(AID.Triplecast) && CD(AID.Triplecast) <= 60 && SelfStatusLeft(SID.Triplecast) == 0; //Can use Triplecast @@ -418,6 +433,10 @@ public override void Execute(StrategyValues strategy, Actor? primaryTarget, floa var llStrat = ll.As(); //Ley Lines strategy var amp = strategy.Option(Track.Amplifier); //Amplifier track var ampStrat = amp.As(); //Amplifier strategy + var retrace = strategy.Option(Track.Retrace); //Retrace track + var retraceStrat = retrace.As(); //Retrace strategy + var btl = strategy.Option(Track.BTL); //Between the Lines track + var btlStrat = btl.As(); //Between the Lines strategy var potion = strategy.Option(Track.Potion).As(); //Potion strategy #endregion @@ -451,7 +470,14 @@ public override void Execute(StrategyValues strategy, Actor? primaryTarget, floa QueueGCD(BestThunder, TargetChoice(thunder) ?? primaryTarget, ThunderLeft < 3 ? GCDPriority.NeedDOT : GCDPriority.DOT); //Queue Thunder //Polyglots if (ShouldUsePolyglot(primaryTarget, polyglotStrat)) //if Polyglot should be used based on strategy - QueueGCD(BestPolyglot, TargetChoice(polyglot) ?? primaryTarget, polyglotStrat is PolyglotStrategy.ForceFoul or PolyglotStrategy.ForceXeno ? GCDPriority.ForcedGCD : EnochianTimer < 5000 ? GCDPriority.NeedPolyglot : GCDPriority.Paradox); //Queue Polyglot + { + if (polyglotStrat is PolyglotStrategy.AutoAll or PolyglotStrategy.Auto2 or PolyglotStrategy.Auto1) + QueueGCD(BestPolyglot, TargetChoice(polyglot) ?? primaryTarget, polyglotStrat is PolyglotStrategy.ForceXeno ? GCDPriority.ForcedGCD : Polyglots == MaxPolyglots && EnochianTimer < 5000 ? GCDPriority.NeedPolyglot : GCDPriority.Paradox); //Queue Polyglot + if (polyglotStrat is PolyglotStrategy.OnlyXenoAll or PolyglotStrategy.OnlyXeno2 or PolyglotStrategy.OnlyXeno1) + QueueGCD(BestXenoglossy, TargetChoice(polyglot) ?? primaryTarget, polyglotStrat is PolyglotStrategy.ForceXeno ? GCDPriority.ForcedGCD : Polyglots == MaxPolyglots && EnochianTimer < 5000 ? GCDPriority.NeedPolyglot : GCDPriority.Paradox); //Queue Xenoglossy + if (polyglotStrat is PolyglotStrategy.OnlyFoulAll or PolyglotStrategy.OnlyFoul2 or PolyglotStrategy.OnlyFoul1) + QueueGCD(AID.Foul, TargetChoice(polyglot) ?? primaryTarget, polyglotStrat is PolyglotStrategy.ForceFoul ? GCDPriority.ForcedGCD : Polyglots == MaxPolyglots && EnochianTimer < 5000 ? GCDPriority.NeedPolyglot : GCDPriority.Paradox); //Queue Foul + } //LeyLines if (ShouldUseLeyLines(primaryTarget, llStrat)) QueueOGCD(AID.LeyLines, Player, OGCDPriority.LeyLines); @@ -464,6 +490,12 @@ public override void Execute(StrategyValues strategy, Actor? primaryTarget, floa //Manafont if (ShouldUseManafont(primaryTarget, mfStrat)) QueueOGCD(AID.Manafont, Player, OGCDPriority.Manafont); + //Retrace + if (ShouldUseRetrace(retraceStrat)) + QueueOGCD(AID.Retrace, Player, OGCDPriority.ForcedOGCD); + //Between the Lines + if (ShouldUseBTL(btlStrat)) + QueueOGCD(AID.BetweenTheLines, Player, OGCDPriority.ForcedOGCD); //Potion if (potion is PotionStrategy.AlignWithRaidBuffs && CD(AID.LeyLines) < 5 || potion is PotionStrategy.Immediate) @@ -578,7 +610,7 @@ private void STLv60toLv71(Actor? target) if (InAstralFire && MP >= 1600) QueueGCD(AID.Fire4, target, GCDPriority.Standard); if (InAstralFire && ElementTimer <= 6) - QueueGCD(BestFire1, target, GCDPriority.Paradox); + QueueGCD(BestParadox, target, GCDPriority.Paradox); } private void STAce(Actor? target) @@ -588,7 +620,7 @@ private void STAce(Actor? target) if (MP >= 10000) //if no stance is active and MP is max (opener) QueueGCD(AID.Blizzard3, target, GCDPriority.NeedB3); 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(swiftcastB3, target, GCDPriority.NeedB3); + QueueGCD(SwiftcastB3, target, GCDPriority.NeedB3); } if (InUmbralIce) { @@ -601,32 +633,43 @@ private void STAce(Actor? target) JustUsed(AID.Blizzard4, 5)) QueueGCD(AID.Paradox, target, GCDPriority.Step2); //Step 3 - swap from UI to AF - if (Unlocked(AID.Fire3)) + if (Unlocked(AID.Fire3) && + UmbralStacks == 3 && + Unlocked(TraitID.UmbralHeart) ? UmbralHearts == MaxUmbralHearts : UmbralHearts == 0) QueueGCD(AID.Fire3, target, GCDPriority.Step1); } if (InAstralFire) { - //Step 1 to 3/4, 4/5 to 7 - F4 or F1 if no F4 yet - if (GCDsInTimer >= 2 && + //Step 1 to 3/4, 5/6 to 7 - F4 or F1 if no F4 yet + if (ElementTimer >= (SpS * 3) && AstralSoulStacks != 6 && MP >= 1600) QueueGCD(Unlocked(AID.Fire4) ? AID.Fire4 : AID.Fire1, target, GCDPriority.Step10); - //Step 4 - Paradox / Fire I - if (GCDsInTimer < 3 && - InAstralFire ? MP >= 1600 : MP >= 800) - QueueGCD(BestFire1, target, ElementTimer <= 3 ? GCDPriority.Paradox : GCDPriority.Step5); + //Step 4 - Paradox + if (ParadoxActive && + ElementTimer < (SpS * 3) && + MP >= 1600) + QueueGCD(AID.Paradox, target, ElementTimer <= 3 ? GCDPriority.Paradox : GCDPriority.Step5); //Step 5 - F3P - if (hasFirestarter) + if (SelfStatusLeft(SID.Firestarter, 30) is < 25 and not 0) QueueGCD(AID.Fire3, target, GCDPriority.Step4); //Step 8 - Despair if (MP is < 1600 and not 0 && Unlocked(AID.Despair)) QueueGCD(AID.Despair, target, GCDPriority.Step3); - //Step 9 - Flare Star + //Step 9A - Flare Star if (AstralSoulStacks == 6) QueueGCD(AID.FlareStar, target, GCDPriority.Step2); + //Step 9B - skip Flare Star if we cant use it (cryge) + if (Unlocked(AID.Blizzard3) && + MP <= 400 && + AstralSoulStacks is < 6 and > 0) + QueueGCD(AID.Blizzard3, target, GCDPriority.Step1); //Step 10 - swap from AF to UI - QueueGCD(AID.Blizzard3, target, GCDPriority.Step1); + if (Unlocked(AID.Blizzard3) && + MP <= 400 && + AstralSoulStacks == 0) + QueueGCD(AID.Blizzard3, target, GCDPriority.Step1); } } @@ -653,7 +696,7 @@ private void STLv72toLv89(Actor? target) if (MP >= 1600) QueueGCD(AID.Fire4, target, GCDPriority.Standard); if (ElementTimer <= 6 && MP >= 2800) - QueueGCD(BestFire1, target, GCDPriority.Paradox); + QueueGCD(BestParadox, target, GCDPriority.Paradox); if (MP is < 1600 and >= 800) QueueGCD(AID.Despair, target, ElementTimer <= 4 && MP <= 2800 ? GCDPriority.NeedDespair : GCDPriority.Despair); } @@ -779,43 +822,50 @@ private void BestRotation(Actor? target) ThunderStrategy.Delay => false, _ => false }; + private bool ShouldSpendPolyglot(Actor? target, PolyglotStrategy strategy) => strategy switch + { + PolyglotStrategy.AutoAll + => Player.InCombat && + target != null && + Polyglots > 0 && + (CD(AID.Triplecast) <= 60 || + CD(AID.LeyLines) <= 120 || + (CD(AID.Manafont) < 0.6f && MP < 1600) || + (Polyglots == MaxPolyglots && PolyglotTimer <= 5000)), //Overcap + PolyglotStrategy.Auto2 + => Player.InCombat && + target != null && + Polyglots > 1 && + (CD(AID.Triplecast) <= 60 || + CD(AID.LeyLines) <= 120 || + (CD(AID.Manafont) < 0.6f && MP < 1600) || + (Polyglots == MaxPolyglots && PolyglotTimer <= 5000)), //Overcap + PolyglotStrategy.Auto1 + => Player.InCombat && + target != null && + Polyglots > 2 && + (CD(AID.Triplecast) <= 60 || + CD(AID.LeyLines) <= 120 || + (CD(AID.Manafont) < 0.6f && MP < 1600) || + (Polyglots == MaxPolyglots && PolyglotTimer <= 5000)), //Overcap + _ => false + }; private bool ShouldUsePolyglot(Actor? target, PolyglotStrategy strategy) => strategy switch { - PolyglotStrategy.Automatic - => Player.InCombat && - target != null && - Polyglots > 0 && - ((CD(AID.Triplecast) <= 60 || CD(AID.LeyLines) <= 120) || (CD(AID.Manafont) < 0.6f && JustUsed(AID.Despair, 5) && MP < 1600) || - (Polyglots == MaxPolyglots && PolyglotTimer <= 5000)), //Overcap - PolyglotStrategy.OnlyXeno => ShouldUseXenoglossy(target, PolyglotStrategy.Automatic), - PolyglotStrategy.OnlyFoul => ShouldUseFoul(target, PolyglotStrategy.Automatic), + PolyglotStrategy.AutoAll => ShouldSpendPolyglot(target, PolyglotStrategy.AutoAll), + PolyglotStrategy.Auto2 => ShouldSpendPolyglot(target, PolyglotStrategy.Auto2), + PolyglotStrategy.Auto1 => ShouldSpendPolyglot(target, PolyglotStrategy.Auto1), + PolyglotStrategy.OnlyXenoAll => ShouldSpendPolyglot(target, PolyglotStrategy.AutoAll), + PolyglotStrategy.OnlyXeno2 => ShouldSpendPolyglot(target, PolyglotStrategy.Auto2), + PolyglotStrategy.OnlyXeno1 => ShouldSpendPolyglot(target, PolyglotStrategy.Auto1), + PolyglotStrategy.OnlyFoulAll => ShouldSpendPolyglot(target, PolyglotStrategy.AutoAll), + PolyglotStrategy.OnlyFoul2 => ShouldSpendPolyglot(target, PolyglotStrategy.Auto2), + PolyglotStrategy.OnlyFoul1 => ShouldSpendPolyglot(target, PolyglotStrategy.Auto1), PolyglotStrategy.ForceXeno => canXeno, PolyglotStrategy.ForceFoul => canFoul, PolyglotStrategy.Delay => false, _ => false }; - private bool ShouldUseXenoglossy(Actor? target, PolyglotStrategy strategy) => strategy switch - { - PolyglotStrategy.Automatic - => Player.InCombat && - target != null && - canXeno && - Polyglots > 0 && - ((CD(AID.Triplecast) <= 60 || CD(AID.LeyLines) <= 120) || (CD(AID.Manafont) < 0.6f && JustUsed(AID.Despair, 5) && MP < 1600) || - (Polyglots == MaxPolyglots && PolyglotTimer <= 5000)), //Overcap - _ => false - }; - private bool ShouldUseFoul(Actor? target, PolyglotStrategy strategy) => strategy switch - { - PolyglotStrategy.Automatic - => Player.InCombat && - target != null && - canFoul && - Polyglots > 0 && - ((CD(AID.Triplecast) <= 60 || CD(AID.LeyLines) <= 120) || (CD(AID.Manafont) < 0.6f && JustUsed(AID.Despair, 5) && MP < 1600) || - (Polyglots == MaxPolyglots && PolyglotTimer <= 5000)), //Overcap - _ => false - }; private bool ShouldUseLeyLines(Actor? target, LeyLinesStrategy strategy) => strategy switch { LeyLinesStrategy.Automatic @@ -824,9 +874,9 @@ private void BestRotation(Actor? target) canLL && canWeaveIn, LeyLinesStrategy.Force => canLL, - LeyLinesStrategy.Force1 => canLL && CD(AID.LeyLines) < 6, + LeyLinesStrategy.Force1 => canLL && CD(AID.LeyLines) < (SpS * 2), LeyLinesStrategy.ForceWeave => canLL && canWeaveIn, - LeyLinesStrategy.ForceWeave1 => canLL && canWeaveIn && CD(AID.LeyLines) < 6, + LeyLinesStrategy.ForceWeave1 => canLL && canWeaveIn && CD(AID.LeyLines) < (SpS * 2), LeyLinesStrategy.Delay => false, _ => false }; @@ -845,13 +895,9 @@ private void BestRotation(Actor? target) OffensiveStrategy.Delay => false, _ => false }; - private bool ShouldUseRetrace(Actor? target, OffensiveStrategy strategy) => strategy switch + private bool ShouldUseRetrace(OffensiveStrategy strategy) => strategy switch { - OffensiveStrategy.Automatic - => Player.InCombat && - target != null && - canWeaveIn && - canRetrace, + OffensiveStrategy.Automatic => false, OffensiveStrategy.Force => canRetrace, OffensiveStrategy.AnyWeave => canRetrace && canWeaveIn, OffensiveStrategy.EarlyWeave => canRetrace && canWeaveEarly, @@ -860,13 +906,9 @@ private void BestRotation(Actor? target) _ => false }; - private bool ShouldUseBTL(Actor? target, OffensiveStrategy strategy) => strategy switch + private bool ShouldUseBTL(OffensiveStrategy strategy) => strategy switch { - OffensiveStrategy.Automatic - => Player.InCombat && - target != null && - canWeaveIn && - canBTL, + OffensiveStrategy.Automatic => false, OffensiveStrategy.Force => canBTL, OffensiveStrategy.AnyWeave => canBTL && canWeaveIn, OffensiveStrategy.EarlyWeave => canBTL && canWeaveEarly, @@ -897,11 +939,12 @@ private void BestRotation(Actor? target) => Player.InCombat && target != null && canTC && - canWeaveIn, + canWeaveIn && + PlayerHasEffect(SID.LeyLines, 30), //Overcap TriplecastStrategy.Force => canTC, - TriplecastStrategy.Force1 => canTC && CD(AID.Triplecast) < 6, + TriplecastStrategy.Force1 => canTC && CD(AID.Triplecast) < (SpS * 2), TriplecastStrategy.ForceWeave => canTC && canWeaveIn, - TriplecastStrategy.ForceWeave1 => canTC && canWeaveIn && CD(AID.Triplecast) < 6, + TriplecastStrategy.ForceWeave1 => canTC && canWeaveIn && CD(AID.Triplecast) < (SpS * 2), TriplecastStrategy.Delay => false, _ => false }; From f8b82116796610854471d0bcf0d5e4dcd9041556 Mon Sep 17 00:00:00 2001 From: ace Date: Thu, 16 Jan 2025 14:35:38 -0800 Subject: [PATCH 05/18] ST is done yay --- BossMod/Autorotation/akechi/AkechiBLM.cs | 369 ++++++++++++++--------- 1 file changed, 233 insertions(+), 136 deletions(-) diff --git a/BossMod/Autorotation/akechi/AkechiBLM.cs b/BossMod/Autorotation/akechi/AkechiBLM.cs index 05518f82f4..50c0b4ca98 100644 --- a/BossMod/Autorotation/akechi/AkechiBLM.cs +++ b/BossMod/Autorotation/akechi/AkechiBLM.cs @@ -1,4 +1,5 @@ using FFXIVClientStructs.FFXIV.Client.Game.Gauge; +using static BossMod.ActorCastEvent; using AID = BossMod.BLM.AID; using SID = BossMod.BLM.SID; using TraitID = BossMod.BLM.TraitID; @@ -254,7 +255,6 @@ public static RotationModuleDefinition Definition() private bool InUmbralIce; //In Umbral Ice private sbyte ElementStance; //Elemental Stance private byte Polyglots; //Polyglot Stacks - private int PolyglotTimer; //Polyglot timer private int MaxPolyglots; // private byte UmbralHearts; //Umbral Hearts private int MaxUmbralHearts; //Max Umbral Hearts @@ -263,7 +263,6 @@ public static RotationModuleDefinition Definition() private int AstralSoulStacks; //Stacks for Flare Star (Lv100) private int EnochianTimer; //Enochian timer private float ElementTimer; //Time remaining on Enochian - private bool EnochianActive; //Enochian is active private bool ParadoxActive; //Paradox is active private bool canFoul; //Can use Foul private bool canXeno; //Can use Xenoglossy @@ -341,15 +340,6 @@ private int TargetsInPlayerAOE(Actor primary) => Hints.NumPriorityTargetsInAOERe #endregion #region Upgrade Paths - private AID BestParadox - => Unlocked(AID.Paradox) - && ParadoxActive - ? AID.Paradox - : AID.Fire1; - private AID BestBlizzard1 - => Unlocked(AID.Blizzard4) - ? AID.Blizzard4 - : AID.Blizzard1; private AID SwiftcastB3 => Player.InCombat && ActionReady(AID.Swiftcast) @@ -385,11 +375,9 @@ public override void Execute(StrategyValues strategy, Actor? primaryTarget, floa UmbralStacks = gauge.UmbralStacks; //Umbral Ice Stacks AstralStacks = gauge.AstralStacks; //Astral Fire Stacks AstralSoulStacks = gauge.AstralSoulStacks; //Stacks for Flare Star (Lv100) - EnochianActive = gauge.EnochianActive; //Enochian is active ParadoxActive = gauge.ParadoxActive; //Paradox is active EnochianTimer = gauge.EnochianTimer; //Enochian timer ElementTimer = gauge.ElementTimeRemaining / 1000f; //Time remaining on current element - PolyglotTimer = Math.Max(0, ((MaxPolyglots - Polyglots) * 30000) + (EnochianTimer - 30000)); MaxPolyglots = Unlocked(TraitID.EnhancedPolyglotII) ? 3 : Unlocked(TraitID.EnhancedPolyglot) ? 2 : 1; canFoul = Unlocked(AID.Foul) && Polyglots > 0; //Can use Foul canXeno = Unlocked(AID.Xenoglossy) && Polyglots > 0; //Can use Xenoglossy @@ -444,7 +432,7 @@ public override void Execute(StrategyValues strategy, Actor? primaryTarget, floa #region Force Execution if (AOEStrategy is AOEStrategy.Auto) - STAce(TargetChoice(AOE) ?? primaryTarget); + BestRotation(TargetChoice(AOE) ?? primaryTarget); if (AOEStrategy is AOEStrategy.ForceST) BestST(TargetChoice(AOE) ?? primaryTarget); if (AOEStrategy is AOEStrategy.ForceAOE) @@ -559,7 +547,7 @@ public bool QueueAction(AID aid, Actor? target, float priority, float delay) return true; } #endregion - private void STLv1toLv34(Actor? target) + private void STLv1toLv34(Actor? target) //Level 1-34 single-target rotation { //Fire if (Unlocked(AID.Fire1) && //if Fire is unlocked @@ -575,139 +563,248 @@ private void STLv1toLv34(Actor? target) 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) + private void STLv35toLv59(Actor? target) //Level 35-59 single-target rotation { - //Ice - if (Unlocked(AID.Blizzard3) && //if Blizzard III is unlocked - (NoStance && MP == 10000 || //and if no stance is active and MP is max (opener) - (NoStance && MP < 10000 && Player.InCombat) || //or if in combat and no stance is active and MP is less than max (died or stopped attacking) - InAstralFire && MP < 1600)) //or if Astral Fire is active and MP is less than 1600 - QueueGCD(AID.Blizzard3, target, GCDPriority.Standard); - if (UmbralStacks >= 1) - QueueGCD(BestBlizzard1, target, GCDPriority.Standard); - //Fire - if (CastTime(AID.Fire3) < 2.5f && - (hasFirestarter || JustUsed(BestBlizzard1, 5))) - QueueGCD(AID.Fire3, target, GCDPriority.F3P); - if (InAstralFire && MP >= 1600) - QueueGCD(AID.Fire1, target, GCDPriority.Standard); - } - private void STLv60toLv71(Actor? target) - { - //Ice - if (Unlocked(AID.Blizzard3) && //if Blizzard III is unlocked - (NoStance && MP == 10000 || //and if no stance is active and MP is max (opener) - (NoStance && MP < 10000 && Player.InCombat) || //or if in combat and no stance is active and MP is less than max (died or stopped attacking) - InAstralFire && MP < 1600)) //or if Astral Fire is active and MP is less than 1600 - QueueGCD(AID.Blizzard3, target, GCDPriority.Standard); - if (UmbralStacks >= 1 || - Unlocked(AID.Blizzard4) && UmbralHearts != MaxUmbralHearts) - QueueGCD(BestBlizzard1, target, GCDPriority.Standard); - //Fire - if (CastTime(AID.Fire3) < 2.5f && - (hasFirestarter || JustUsed(BestBlizzard1, 5))) - QueueGCD(AID.Fire3, target, GCDPriority.F3P); - if (InAstralFire && MP >= 1600) - QueueGCD(AID.Fire4, target, GCDPriority.Standard); - if (InAstralFire && ElementTimer <= 6) - QueueGCD(BestParadox, target, GCDPriority.Paradox); - } + 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(SwiftcastB3, target, GCDPriority.NeedB3); //Queue Swiftcast->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.Blizzard4) && UmbralStacks != 3) //if Blizzard IV is not unlocked and Umbral Ice stacks are not 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 - private void STAce(Actor? target) + } + //Step 2 - swap from UI to AF + if (Unlocked(AID.Fire3) && //if Fire III is unlocked + UmbralStacks == 3 && //if Fire III is unlocked and Umbral Ice stacks are max + Unlocked(TraitID.UmbralHeart) ? UmbralHearts == MaxUmbralHearts : UmbralHearts == 0) //if Fire III is unlocked and Umbral Ice stacks are max and Umbral Hearts are max or 0 + QueueGCD(AID.Fire3, target, GCDPriority.Step1); //Queue Fire III + } + 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 2 - F3P + if (SelfStatusLeft(SID.Firestarter, 30) is < 25 and not 0) //if Firestarter buff is active and has less than 25s left + QueueGCD(AID.Fire3, target, GCDPriority.Step2); //Queue Fire III + //Step 3 - 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 STLv60toLv71(Actor? target) //Level 60-71 single-target rotation { - if (NoStance) + 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); - 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(SwiftcastB3, target, GCDPriority.NeedB3); + 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(SwiftcastB3, target, GCDPriority.NeedB3); //Queue Swiftcast->Blizzard III + } } - if (InUmbralIce) + if (InUmbralIce) //if Umbral Ice is active { //Step 1 - max stacks in UI - if (Unlocked(AID.Blizzard4) && - JustUsed(AID.Blizzard3, 5) || UmbralHearts != MaxUmbralHearts) - QueueGCD(AID.Blizzard4, target, GCDPriority.Step3); - //Step 2 - Ice Paradox - if (canParadox && - JustUsed(AID.Blizzard4, 5)) - QueueGCD(AID.Paradox, target, GCDPriority.Step2); - //Step 3 - swap from UI to AF - if (Unlocked(AID.Fire3) && - UmbralStacks == 3 && - Unlocked(TraitID.UmbralHeart) ? UmbralHearts == MaxUmbralHearts : UmbralHearts == 0) - QueueGCD(AID.Fire3, target, GCDPriority.Step1); + 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 (InAstralFire) //if Astral Fire is active { - //Step 1 to 3/4, 5/6 to 7 - F4 or F1 if no F4 yet - if (ElementTimer >= (SpS * 3) && - AstralSoulStacks != 6 && - MP >= 1600) - QueueGCD(Unlocked(AID.Fire4) ? AID.Fire4 : AID.Fire1, target, GCDPriority.Step10); - //Step 4 - Paradox - if (ParadoxActive && - ElementTimer < (SpS * 3) && - MP >= 1600) - QueueGCD(AID.Paradox, target, ElementTimer <= 3 ? GCDPriority.Paradox : GCDPriority.Step5); - //Step 5 - F3P - if (SelfStatusLeft(SID.Firestarter, 30) is < 25 and not 0) - QueueGCD(AID.Fire3, target, GCDPriority.Step4); - //Step 8 - Despair - if (MP is < 1600 and not 0 && - Unlocked(AID.Despair)) - QueueGCD(AID.Despair, target, GCDPriority.Step3); - //Step 9A - Flare Star - if (AstralSoulStacks == 6) - QueueGCD(AID.FlareStar, target, GCDPriority.Step2); - //Step 9B - skip Flare Star if we cant use it (cryge) - if (Unlocked(AID.Blizzard3) && - MP <= 400 && - AstralSoulStacks is < 6 and > 0) - QueueGCD(AID.Blizzard3, target, GCDPriority.Step1); - //Step 10 - swap from AF to UI - if (Unlocked(AID.Blizzard3) && - MP <= 400 && - AstralSoulStacks == 0) - QueueGCD(AID.Blizzard3, target, GCDPriority.Step1); + //Step 1-3, 5-7 - Fire IV + if (ElementTimer >= (SpS * 3) && //if time remaining on current element is greater than or equal to 3x GCDs + 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 <= 3 && 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 has less than 25s left + QueueGCD(AID.Fire3, target, GCDPriority.Step3); //Queue Fire III + //Step 8 - 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 STLv72toLv89(Actor? target) + private void STLv72toLv89(Actor? target) //Level 72-89 single-target rotation { - //Ice - if (Unlocked(AID.Blizzard3) && //if Blizzard III is unlocked - (NoStance && MP == 10000 || //and if no stance is active and MP is max (opener) - (NoStance && MP < 10000 && Player.InCombat) || //or if in combat and no stance is active and MP is less than max (died or stopped attacking) - InAstralFire && MP < 1600)) //or if Astral Fire is active and MP is less than 400 - QueueGCD(AID.Blizzard3, target, InAstralFire && MP is 0 ? GCDPriority.NeedB3 : GCDPriority.Standard); - if (InUmbralIce && UmbralHearts != MaxUmbralHearts) - QueueGCD(BestBlizzard1, target, GCDPriority.Standard); - //Fire - if (CastTime(AID.Fire3) < SpS && !JustUsed(AID.Fire3, 5)) + 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(SwiftcastB3, target, GCDPriority.NeedB3); //Queue Swiftcast->Blizzard III + } + } + if (InUmbralIce) //if Umbral Ice is active { - if (SelfStatusLeft(SID.Firestarter) < 25) - QueueGCD(AID.Fire3, target, GCDPriority.F3P); - if (UmbralHearts == MaxUmbralHearts && UmbralStacks == 3) - QueueGCD(AID.Fire3, target, GCDPriority.NeedF3P); + //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 (InAstralFire) //if Astral Fire is active { - if (MP >= 1600) - QueueGCD(AID.Fire4, target, GCDPriority.Standard); - if (ElementTimer <= 6 && MP >= 2800) - QueueGCD(BestParadox, target, GCDPriority.Paradox); - if (MP is < 1600 and >= 800) - QueueGCD(AID.Despair, target, ElementTimer <= 4 && MP <= 2800 ? GCDPriority.NeedDespair : GCDPriority.Despair); + //Step 1-3, 5-7 - Fire IV + if (ElementTimer >= (SpS * 3) && //if time remaining on current element is greater than or equal to 3x GCDs + 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 <= 3 && 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 has less than 25s left + QueueGCD(AID.Fire3, target, GCDPriority.Step3); //Queue Fire III + //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.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() + private void STLv90toLv99(Actor? target) //Level 90-99 single-target rotation { - // TODO: Implement single-target rotation for level 90-99 + 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(SwiftcastB3, target, GCDPriority.NeedB3); //Queue Swiftcast->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 (ElementTimer >= (SpS * 3) && //if time remaining on current element is greater than or equal to 3x GCDs + 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 5B - F3P + if (SelfStatusLeft(SID.Firestarter, 30) is < 25 and not 0) //if Firestarter buff is active and has less than 25s left + QueueGCD(AID.Fire3, target, GCDPriority.Step3); //Queue Fire III + //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.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() + private void STLv100(Actor? target) //Level 100 single-target rotation { - // TODO: Implement single-target rotation for level 100 + 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(SwiftcastB3, target, GCDPriority.NeedB3); //Queue Swiftcast->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 (ElementTimer >= (SpS * 3) && //if time remaining on current element is greater than or equal to 3x GCDs + 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 5B - F3P + if (SelfStatusLeft(SID.Firestarter, 30) is < 25 and not 0) //if Firestarter buff is active and has less than 25s left + QueueGCD(AID.Fire3, target, GCDPriority.Step4); //Queue Fire III + //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 + 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) { @@ -725,17 +822,18 @@ private void BestST(Actor? target) } if (Player.Level is >= 72 and <= 89) { - //STLv72toLv89(target); + STLv72toLv89(target); } if (Player.Level is >= 90 and <= 99) { - //STLv90toLv99(target); + STLv90toLv99(target); } if (Player.Level is 100) { - //STLv100(target); + STLv100(target); } } + private void AOELv12toLv34() { // TODO: Implement AOE rotation for level 12-34 @@ -798,7 +896,6 @@ private void BestAOE(Actor? target) } } } - private void BestRotation(Actor? target) { if (ShouldUseAOE) @@ -828,10 +925,10 @@ private void BestRotation(Actor? target) => Player.InCombat && target != null && Polyglots > 0 && - (CD(AID.Triplecast) <= 60 || + ((CD(AID.Triplecast) <= 60 && PlayerHasEffect(SID.LeyLines, 30)) || CD(AID.LeyLines) <= 120 || (CD(AID.Manafont) < 0.6f && MP < 1600) || - (Polyglots == MaxPolyglots && PolyglotTimer <= 5000)), //Overcap + (Polyglots == MaxPolyglots && EnochianTimer <= 5000)), //Overcap PolyglotStrategy.Auto2 => Player.InCombat && target != null && @@ -839,7 +936,7 @@ private void BestRotation(Actor? target) (CD(AID.Triplecast) <= 60 || CD(AID.LeyLines) <= 120 || (CD(AID.Manafont) < 0.6f && MP < 1600) || - (Polyglots == MaxPolyglots && PolyglotTimer <= 5000)), //Overcap + (Polyglots == MaxPolyglots && EnochianTimer <= 5000)), //Overcap PolyglotStrategy.Auto1 => Player.InCombat && target != null && @@ -847,7 +944,7 @@ private void BestRotation(Actor? target) (CD(AID.Triplecast) <= 60 || CD(AID.LeyLines) <= 120 || (CD(AID.Manafont) < 0.6f && MP < 1600) || - (Polyglots == MaxPolyglots && PolyglotTimer <= 5000)), //Overcap + (Polyglots == MaxPolyglots && EnochianTimer <= 5000)), //Overcap _ => false }; private bool ShouldUsePolyglot(Actor? target, PolyglotStrategy strategy) => strategy switch From 7cdc3aac33b93170aca4c84d61e0de074abe1485 Mon Sep 17 00:00:00 2001 From: ace Date: Thu, 16 Jan 2025 15:25:07 -0800 Subject: [PATCH 06/18] AOE complete --- BossMod/Autorotation/akechi/AkechiBLM.cs | 348 ++++++++++++++++++++--- 1 file changed, 307 insertions(+), 41 deletions(-) diff --git a/BossMod/Autorotation/akechi/AkechiBLM.cs b/BossMod/Autorotation/akechi/AkechiBLM.cs index 50c0b4ca98..69342a03b6 100644 --- a/BossMod/Autorotation/akechi/AkechiBLM.cs +++ b/BossMod/Autorotation/akechi/AkechiBLM.cs @@ -345,6 +345,13 @@ private AID SwiftcastB3 && ActionReady(AID.Swiftcast) ? AID.Swiftcast : AID.Blizzard3; + private AID SwiftcastB2 + => Player.InCombat + && ActionReady(AID.Swiftcast) + ? AID.Swiftcast + : Unlocked(AID.HighBlizzard2) + ? AID.HighBlizzard2 + : AID.Blizzard2; private AID BestThunderST => Unlocked(AID.HighThunder) ? AID.HighThunder : Unlocked(AID.Thunder3) ? AID.Thunder3 @@ -547,6 +554,19 @@ public bool QueueAction(AID aid, Actor? target, float priority, float delay) return true; } #endregion + private void BestRotation(Actor? target) + { + if (ShouldUseAOE) + { + BestAOE(target); + } + if (!ShouldUseAOE) + { + BestST(target); + } + } + + #region Single-Target Helpers private void STLv1toLv34(Actor? target) //Level 1-34 single-target rotation { //Fire @@ -580,17 +600,17 @@ private void STLv35toLv59(Actor? target) //Level 35-59 single-target rotation //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 not max + 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 - } //Step 2 - swap from UI to AF if (Unlocked(AID.Fire3) && //if Fire III is unlocked - UmbralStacks == 3 && //if Fire III is unlocked and Umbral Ice stacks are max - Unlocked(TraitID.UmbralHeart) ? UmbralHearts == MaxUmbralHearts : UmbralHearts == 0) //if Fire III is unlocked and Umbral Ice stacks are max and Umbral Hearts are max or 0 - QueueGCD(AID.Fire3, target, GCDPriority.Step1); //Queue Fire III + 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 { @@ -598,11 +618,11 @@ private void STLv35toLv59(Actor? target) //Level 35-59 single-target rotation if (MP >= 1600) //if MP is 1600 or more QueueGCD(AID.Fire1, target, GCDPriority.Step3); //Queue Fire I //Step 2 - F3P - if (SelfStatusLeft(SID.Firestarter, 30) is < 25 and not 0) //if Firestarter buff is active and has less than 25s left + if (hasFirestarter && AstralStacks == 3) //if Firestarter buff is active and Astral Fire stacks are max QueueGCD(AID.Fire3, target, GCDPriority.Step2); //Queue Fire III //Step 3 - swap from AF to UI if (Unlocked(AID.Blizzard3) && //if Blizzard III is unlocked - MP <= 400) //and MP is less than 400 + MP < 1600) //and MP is less than 400 QueueGCD(AID.Blizzard3, target, GCDPriority.Step1); //Queue Blizzard III } } @@ -640,11 +660,11 @@ private void STLv60toLv71(Actor? target) //Level 60-71 single-target rotation MP >= 4000) //and MP is 4000 or more QueueGCD(AID.Fire1, target, ElementTimer <= 3 && 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 has less than 25s left + if (hasFirestarter && AstralStacks == 3) //if Firestarter buff is active and Astral Fire stacks are max QueueGCD(AID.Fire3, target, GCDPriority.Step3); //Queue Fire III //Step 8 - swap from AF to UI if (Unlocked(AID.Blizzard3) && //if Blizzard III is unlocked - MP <= 400) //and MP is less than 400 + MP < 1600) //and MP is less than 400 QueueGCD(AID.Blizzard3, target, GCDPriority.Step1); //Queue Blizzard III } } @@ -833,34 +853,290 @@ private void BestST(Actor? target) STLv100(target); } } + #endregion - private void AOELv12toLv34() + #region AOE Helpers + private void AOELv12toLv34(Actor? target) { - // TODO: Implement AOE rotation for level 12-34 + //Fire + if (Unlocked(AID.Fire2) && //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.Fire2, target, GCDPriority.Standard); //Queue Fire + //Ice + if (InUmbralIce && MP != 10000) //if Umbral Ice is active and MP is not max + QueueGCD(AID.Blizzard2, 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 AOELv35toLv39() + private void AOELv35toLv39(Actor? target) { - // TODO: Implement AOE rotation for level 35-39 + if (NoStance) + { + if (Unlocked(AID.Blizzard2)) + { + if (MP >= 10000) + QueueGCD(AID.Blizzard2, target, GCDPriority.NeedB3); + if (MP < 10000 && Player.InCombat) + QueueGCD(SwiftcastB2, target, GCDPriority.NeedB3); + } + } + if (InUmbralIce) + { + if (JustUsed(AID.Fire2, 5)) + { + if (Unlocked(AID.Blizzard2) && MP != 10000) + QueueGCD(AID.Blizzard2, target, GCDPriority.Step2); + } + if (Unlocked(AID.Fire2) && + MP >= 10000 && + UmbralStacks == 3) + QueueGCD(AID.Fire2, target, GCDPriority.Step1); + } + if (InAstralFire) + { + if (MP >= 1600) + QueueGCD(AID.Fire2, target, GCDPriority.Step2); + if (Unlocked(AID.Blizzard2) && + MP < 1600) + QueueGCD(AID.Blizzard2, target, GCDPriority.Step1); + } } - private void AOELv40toLv49() + private void AOELv40toLv49(Actor? target) { - // TODO: Implement AOE rotation for level 40-49 + if (NoStance) + { + if (Unlocked(AID.Blizzard2)) + { + if (MP >= 10000) + QueueGCD(AID.Blizzard2, target, GCDPriority.NeedB3); + if (MP < 10000 && Player.InCombat) + QueueGCD(SwiftcastB2, target, GCDPriority.NeedB3); + } + } + if (InUmbralIce) + { + //Step 1 - max stacks in UI + if (JustUsed(AID.Fire2, 5) && + Unlocked(AID.Blizzard2) && + UmbralStacks != 3) + QueueGCD(AID.Blizzard2, target, GCDPriority.Step2); + //Step 2 - Freeze + if (Unlocked(AID.Freeze) && + JustUsed(AID.Blizzard2, 5) || UmbralStacks == 3) + QueueGCD(AID.Freeze, target, JustUsed(AID.Blizzard2, 5) ? GCDPriority.Step10 : GCDPriority.Step1); + //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 >= 1600) + QueueGCD(AID.Fire2, target, GCDPriority.Step2); + if (Unlocked(AID.Blizzard2) && + MP < 1600) + QueueGCD(AID.Blizzard2, target, GCDPriority.Step1); + } } - private void AOELv50toLv57() + private void AOELv50toLv57(Actor? target) { - // TODO: Implement AOE rotation for level 50-57 + if (NoStance) + { + if (Unlocked(AID.Blizzard2)) + { + if (MP >= 10000) + QueueGCD(AID.Blizzard2, target, GCDPriority.NeedB3); + if (MP < 10000 && Player.InCombat) + QueueGCD(SwiftcastB2, target, GCDPriority.NeedB3); + } + } + if (InUmbralIce) + { + //Step 1 - max stacks in UI + if (JustUsed(AID.Fire2, 5) && + Unlocked(AID.Blizzard2) && + UmbralStacks != 3) + QueueGCD(AID.Blizzard2, target, GCDPriority.Step2); + //Step 2 - Freeze + if (Unlocked(AID.Freeze) && + JustUsed(AID.Blizzard2, 5) || UmbralStacks == 3) + QueueGCD(AID.Freeze, target, JustUsed(AID.Blizzard2, 5) ? GCDPriority.Step10 : GCDPriority.Step1); + //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 >= 1600) + QueueGCD(AID.Fire2, target, GCDPriority.Step3); + //Step 2 - Flare + if (Unlocked(AID.Flare) && + MP < 1600) + QueueGCD(AID.Flare, target, GCDPriority.Step2); + //Step 3 - swap from AF to UI + if (Unlocked(AID.Blizzard2) && + (!Unlocked(AID.Flare) && MP < 1600) || + (Unlocked(AID.Flare) && MP < 400)) + QueueGCD(AID.Blizzard2, target, GCDPriority.Step1); + } } - private void AOELv58toLv81() + private void AOELv58toLv81(Actor? target) { - // TODO: Implement AOE rotation for level 58-81 + if (NoStance) + { + if (Unlocked(AID.Blizzard2)) + { + if (MP >= 10000) + QueueGCD(AID.Blizzard2, target, GCDPriority.NeedB3); + if (MP < 10000 && Player.InCombat) + QueueGCD(SwiftcastB2, target, GCDPriority.NeedB3); + } + } + if (InUmbralIce) + { + //Step 1 - max stacks in UI + if (JustUsed(AID.Fire2, 5) && + Unlocked(AID.Blizzard2) && + UmbralStacks != 3) + QueueGCD(AID.Blizzard2, target, GCDPriority.Step2); + //Step 2 - Freeze + if (Unlocked(AID.Freeze) && + JustUsed(AID.Blizzard2, 5) || UmbralStacks == 3) + QueueGCD(AID.Freeze, target, JustUsed(AID.Blizzard2, 5) ? GCDPriority.Step10 : GCDPriority.Step1); + //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 > 5500) + 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 (MP < 2500 && JustUsed(AID.Flare, 5f)) + 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() + private void AOELv82toLv99(Actor? target) { - // TODO: Implement AOE rotation for level 82-99 + if (NoStance) + { + if (Unlocked(AID.Blizzard2)) + { + if (MP >= 10000) + QueueGCD(AID.Blizzard2, target, GCDPriority.NeedB3); + if (MP < 10000 && Player.InCombat) + QueueGCD(SwiftcastB2, target, GCDPriority.NeedB3); + } + } + if (InUmbralIce) + { + //Step 1 - max stacks in UI + if (JustUsed(AID.HighFire2, 5) && + Unlocked(AID.HighBlizzard2) && + UmbralStacks != 3) + QueueGCD(AID.HighBlizzard2, target, GCDPriority.Step2); + //Step 2 - Freeze + if (Unlocked(AID.Freeze) && + JustUsed(AID.HighBlizzard2, 5) || UmbralStacks == 3) + QueueGCD(AID.Freeze, target, JustUsed(AID.Blizzard2, 5) ? GCDPriority.Step10 : GCDPriority.Step1); + //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)) + { + //first cast + if (UmbralHearts == 1) + QueueGCD(AID.Flare, target, GCDPriority.Step3); + //second cast + if (MP < 2500 && JustUsed(AID.Flare, 5f)) + QueueGCD(AID.Flare, target, GCDPriority.Step2); + } + //Step 3 - swap from AF to UI + if (Unlocked(AID.HighBlizzard2) && + MP < 400) + QueueGCD(AID.HighBlizzard2, target, GCDPriority.Step1); + } } - private void AOELv100() + private void AOELv100(Actor? target) { - // TODO: Implement AOE rotation for level 100 + if (NoStance) + { + if (Unlocked(AID.HighBlizzard2)) + { + if (MP >= 10000) + QueueGCD(AID.HighBlizzard2, target, GCDPriority.NeedB3); + if (MP < 10000 && Player.InCombat) + QueueGCD(SwiftcastB2, target, GCDPriority.NeedB3); + } + } + if (InUmbralIce) + { + //Step 1 - max stacks in UI + if (JustUsed(AID.HighFire2, 5) && + Unlocked(AID.HighBlizzard2) && + UmbralStacks != 3) + QueueGCD(AID.HighBlizzard2, target, GCDPriority.Step2); + //Step 2 - Freeze + if (Unlocked(AID.Freeze) && + JustUsed(AID.HighBlizzard2, 5) || UmbralStacks == 3) + QueueGCD(AID.Freeze, target, JustUsed(AID.Blizzard2, 5) ? GCDPriority.Step10 : GCDPriority.Step1); + //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 == 3) + QueueGCD(AID.Flare, target, GCDPriority.Step3); + //second cast + if (MP < 2500 && JustUsed(AID.Flare, 5f)) + 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) { @@ -868,45 +1144,35 @@ private void BestAOE(Actor? target) { if (Player.Level is >= 12 and <= 34) { - AOELv12toLv34(); + AOELv12toLv34(target); } if (Player.Level is >= 35 and <= 39) { - AOELv35toLv39(); + AOELv35toLv39(target); } if (Player.Level is >= 40 and <= 49) { - AOELv40toLv49(); + AOELv40toLv49(target); } if (Player.Level is >= 50 and <= 57) { - AOELv50toLv57(); + AOELv50toLv57(target); } if (Player.Level is >= 58 and <= 81) { - AOELv58toLv81(); + AOELv58toLv81(target); } if (Player.Level is >= 82 and <= 99) { - AOELv82toLv99(); + AOELv82toLv99(target); } if (Player.Level is 100) { - AOELv100(); + AOELv100(target); } } } - private void BestRotation(Actor? target) - { - if (ShouldUseAOE) - { - BestAOE(target); - } - if (!ShouldUseAOE) - { - BestST(target); - } - } + #endregion #region Cooldown Helpers private bool ShouldUseThunder(Actor? target, ThunderStrategy strategy) => strategy switch From ee85fbcb2d4e252a48074e7503f4ba4d527ef1fe Mon Sep 17 00:00:00 2001 From: ace Date: Thu, 16 Jan 2025 15:41:28 -0800 Subject: [PATCH 07/18] damn, I think I'm done? --- BossMod/Autorotation/akechi/AkechiBLM.cs | 197 +++++++++++++---------- 1 file changed, 113 insertions(+), 84 deletions(-) diff --git a/BossMod/Autorotation/akechi/AkechiBLM.cs b/BossMod/Autorotation/akechi/AkechiBLM.cs index 69342a03b6..94251a9ddd 100644 --- a/BossMod/Autorotation/akechi/AkechiBLM.cs +++ b/BossMod/Autorotation/akechi/AkechiBLM.cs @@ -1,5 +1,4 @@ using FFXIVClientStructs.FFXIV.Client.Game.Gauge; -using static BossMod.ActorCastEvent; using AID = BossMod.BLM.AID; using SID = BossMod.BLM.SID; using TraitID = BossMod.BLM.TraitID; @@ -99,13 +98,14 @@ public enum OffensiveStrategy } #endregion + #region Module & Strategy Definitions public static RotationModuleDefinition Definition() { var res = new RotationModuleDefinition("Akechi BLM", //Title "Standard Rotation Module", //Description "Standard rotation (Akechi)", //Category "Akechi", //Contributor - RotationModuleQuality.Ok, //Quality + RotationModuleQuality.Basic, //Quality BitMask.Build(Class.THM, Class.BLM), //Job 100); //Level supported @@ -205,6 +205,7 @@ public static RotationModuleDefinition Definition() return res; } + #endregion #region Priorities public enum GCDPriority //priorities for GCDs (higher number = higher priority) @@ -248,6 +249,35 @@ public static RotationModuleDefinition Definition() } #endregion + #region Upgrade Paths + private AID SwiftcastB3 + => Player.InCombat + && ActionReady(AID.Swiftcast) + ? AID.Swiftcast + : AID.Blizzard3; + private AID SwiftcastB2 + => Player.InCombat + && ActionReady(AID.Swiftcast) + ? AID.Swiftcast + : Unlocked(AID.HighBlizzard2) + ? AID.HighBlizzard2 + : AID.Blizzard2; + private AID BestThunderST + => Unlocked(AID.HighThunder) ? AID.HighThunder + : Unlocked(AID.Thunder3) ? AID.Thunder3 + : AID.Thunder1; + private AID BestThunderAOE + => Unlocked(AID.HighThunder2) ? AID.HighThunder2 + : Unlocked(AID.Thunder4) ? AID.Thunder4 + : AID.Thunder2; + private AID BestThunder + => ShouldUseAOE ? BestThunderAOE : BestThunderST; + private AID BestPolyglot + => ShouldUseAOE ? AID.Foul : BestXenoglossy; + private AID BestXenoglossy + => Unlocked(AID.Xenoglossy) ? AID.Xenoglossy : AID.Foul; + #endregion + #region Placeholders for Variables private uint MP; //Current MP private bool NoStance; //No stance @@ -296,10 +326,8 @@ public static RotationModuleDefinition Definition() private float CD(AID aid) => World.Client.Cooldowns[ActionDefinitions.Instance.Spell(aid)!.MainCooldownGroup].Remaining; //Get remaining cooldown time for the specified action 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 ready (cooldown less than 0.6 seconds) - private bool IsFirstGCD() => !Player.InCombat || (World.CurrentTime - Manager.CombatStart).TotalSeconds < 0.1f; //Check if this is the first GCD in combat 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 bool PlayerHasEffect(SID sid, float duration) => SelfStatusLeft(sid, duration) > GCD; //Checks if Status effect is on self - private float GCDsInTimer => (ElementTimer / 2.5f) + 1f; //Calculates the number of GCDs in the Enochian timer private bool JustUsed(AID aid, float variance) { if (Manager?.LastCast == null) @@ -308,66 +336,9 @@ private bool JustUsed(AID aid, float variance) return Manager.LastCast.Data?.IsSpell(aid) == true && (World.CurrentTime - Manager.LastCast.Time).TotalSeconds <= variance; } - private float GetCastTime(AID aid) => ActionDefinitions.Instance.Spell(aid)!.CastTime * SpS / 2.5f; - private float CastTime(AID aid) - { - var aspect = ActionDefinitions.Instance.Spell(aid)!.Aspect; - var castTime = GetCastTime(aid); - if (PlayerHasEffect(SID.Triplecast, 15) || - PlayerHasEffect(SID.Swiftcast, 10)) - return 0; - if (aid == AID.Fire3 && hasFirestarter - || aid == AID.Foul && Unlocked(TraitID.EnhancedFoul) - || aspect == ActionAspect.Thunder && hasThunderhead - || aid == AID.Despair & Unlocked(TraitID.EnhancedAstralFire)) - return 0; - if (castTime == 0) - return 0; - if (ElementStance == -3 && - aspect == ActionAspect.Fire || - ElementStance == 3 && - aspect == ActionAspect.Ice) - castTime *= 0.5f; - return castTime; - } - private int TargetsInPlayerAOE(Actor primary) => Hints.NumPriorityTargetsInAOERect( //Use Hints to count number of targets in AOE rectangle - Player.Position, //from Player's position - (primary.Position - Player.Position) //direction of AOE rectangle - .Normalized(), //normalized direction - 25, //AOE rectangle length - 5); //AOE rectangle width private Actor? TargetChoice(StrategyValues.OptionRef strategy) => ResolveTargetOverride(strategy.Value); //Resolves the target choice based on the strategy #endregion - #region Upgrade Paths - private AID SwiftcastB3 - => Player.InCombat - && ActionReady(AID.Swiftcast) - ? AID.Swiftcast - : AID.Blizzard3; - private AID SwiftcastB2 - => Player.InCombat - && ActionReady(AID.Swiftcast) - ? AID.Swiftcast - : Unlocked(AID.HighBlizzard2) - ? AID.HighBlizzard2 - : AID.Blizzard2; - private AID BestThunderST - => Unlocked(AID.HighThunder) ? AID.HighThunder - : Unlocked(AID.Thunder3) ? AID.Thunder3 - : AID.Thunder1; - private AID BestThunderAOE - => Unlocked(AID.HighThunder2) ? AID.HighThunder2 - : Unlocked(AID.Thunder4) ? AID.Thunder4 - : AID.Thunder2; - private AID BestThunder - => ShouldUseAOE ? BestThunderAOE : BestThunderST; - private AID BestPolyglot - => ShouldUseAOE ? AID.Foul : BestXenoglossy; - private AID BestXenoglossy - => Unlocked(AID.Xenoglossy) ? AID.Xenoglossy : AID.Foul; - #endregion - public override void Execute(StrategyValues strategy, Actor? primaryTarget, float estimatedAnimLockDelay, bool isMoving) //Executes our actions { #region Variables @@ -437,60 +408,118 @@ public override void Execute(StrategyValues strategy, Actor? primaryTarget, floa #endregion - #region Force Execution + #region Rotation Execution + //Rotations if (AOEStrategy is AOEStrategy.Auto) BestRotation(TargetChoice(AOE) ?? primaryTarget); if (AOEStrategy is AOEStrategy.ForceST) BestST(TargetChoice(AOE) ?? primaryTarget); if (AOEStrategy is AOEStrategy.ForceAOE) BestAOE(TargetChoice(AOE) ?? primaryTarget); - #endregion - - #region Standard Execution //Out of combat - if (Unlocked(AID.UmbralSoul)) + if (Unlocked(AID.Transpose)) { - if (primaryTarget == null && - (!Player.InCombat || Player.InCombat && TargetsInRange() is 0)) + if (!Unlocked(AID.UmbralSoul)) + { + if (primaryTarget == null && + (!Player.InCombat || Player.InCombat && TargetsInRange() is 0)) + { + if (CD(AID.Transpose) < 0.6f && + (InAstralFire || InUmbralIce)) + QueueOGCD(AID.Transpose, Player, OGCDPriority.Transpose); + } + } + if (Unlocked(AID.UmbralSoul)) { - if (InAstralFire) - QueueOGCD(AID.Transpose, Player, OGCDPriority.Transpose); - if (InUmbralIce && - (ElementTimer <= 14 || UmbralStacks < 3 || UmbralHearts != MaxUmbralHearts)) - QueueGCD(AID.UmbralSoul, Player, GCDPriority.Standard); + if (primaryTarget == null && + (!Player.InCombat || Player.InCombat && TargetsInRange() is 0)) + { + if (InAstralFire) + QueueOGCD(AID.Transpose, Player, OGCDPriority.Transpose); + if (InUmbralIce && + (ElementTimer <= 14 || UmbralStacks < 3 || UmbralHearts != MaxUmbralHearts)) + QueueGCD(AID.UmbralSoul, Player, GCDPriority.Standard); + } } } //Thunder if (ShouldUseThunder(primaryTarget, thunderStrat)) //if Thunder should be used based on strategy - QueueGCD(BestThunder, TargetChoice(thunder) ?? primaryTarget, ThunderLeft < 3 ? GCDPriority.NeedDOT : GCDPriority.DOT); //Queue Thunder + QueueGCD(BestThunder, + TargetChoice(thunder) ?? primaryTarget, + ThunderLeft < 3 ? GCDPriority.NeedDOT : + GCDPriority.DOT); //Polyglots if (ShouldUsePolyglot(primaryTarget, polyglotStrat)) //if Polyglot should be used based on strategy { - if (polyglotStrat is PolyglotStrategy.AutoAll or PolyglotStrategy.Auto2 or PolyglotStrategy.Auto1) - QueueGCD(BestPolyglot, TargetChoice(polyglot) ?? primaryTarget, polyglotStrat is PolyglotStrategy.ForceXeno ? GCDPriority.ForcedGCD : Polyglots == MaxPolyglots && EnochianTimer < 5000 ? GCDPriority.NeedPolyglot : GCDPriority.Paradox); //Queue Polyglot - if (polyglotStrat is PolyglotStrategy.OnlyXenoAll or PolyglotStrategy.OnlyXeno2 or PolyglotStrategy.OnlyXeno1) - QueueGCD(BestXenoglossy, TargetChoice(polyglot) ?? primaryTarget, polyglotStrat is PolyglotStrategy.ForceXeno ? GCDPriority.ForcedGCD : Polyglots == MaxPolyglots && EnochianTimer < 5000 ? GCDPriority.NeedPolyglot : GCDPriority.Paradox); //Queue Xenoglossy + if (polyglotStrat is PolyglotStrategy.AutoAll + or PolyglotStrategy.Auto2 + or PolyglotStrategy.Auto1) + QueueGCD(BestPolyglot, + TargetChoice(polyglot) ?? primaryTarget, + polyglotStrat is PolyglotStrategy.ForceXeno ? GCDPriority.ForcedGCD + : Polyglots == MaxPolyglots && EnochianTimer < 5000 ? GCDPriority.NeedPolyglot + : GCDPriority.Paradox); + if (polyglotStrat is PolyglotStrategy.OnlyXenoAll + or PolyglotStrategy.OnlyXeno2 + or PolyglotStrategy.OnlyXeno1) + QueueGCD(BestXenoglossy, + TargetChoice(polyglot) ?? primaryTarget, + polyglotStrat is PolyglotStrategy.ForceXeno ? GCDPriority.ForcedGCD + : Polyglots == MaxPolyglots && EnochianTimer < 5000 ? GCDPriority.NeedPolyglot + : GCDPriority.Paradox); if (polyglotStrat is PolyglotStrategy.OnlyFoulAll or PolyglotStrategy.OnlyFoul2 or PolyglotStrategy.OnlyFoul1) QueueGCD(AID.Foul, TargetChoice(polyglot) ?? primaryTarget, polyglotStrat is PolyglotStrategy.ForceFoul ? GCDPriority.ForcedGCD : Polyglots == MaxPolyglots && EnochianTimer < 5000 ? GCDPriority.NeedPolyglot : GCDPriority.Paradox); //Queue Foul } //LeyLines if (ShouldUseLeyLines(primaryTarget, llStrat)) - QueueOGCD(AID.LeyLines, Player, OGCDPriority.LeyLines); + QueueOGCD(AID.LeyLines, + Player, + llStrat is LeyLinesStrategy.Force + or LeyLinesStrategy.Force1 + or LeyLinesStrategy.ForceWeave1 + or LeyLinesStrategy.ForceWeave1 + ? OGCDPriority.ForcedOGCD + : OGCDPriority.LeyLines); //Triplecast if (ShouldUseTriplecast(primaryTarget, tcStrat)) - QueueOGCD(AID.Triplecast, Player, OGCDPriority.Triplecast); + QueueOGCD(AID.Triplecast, + Player, + tcStrat is TriplecastStrategy.Force + or TriplecastStrategy.Force1 + or TriplecastStrategy.ForceWeave + or TriplecastStrategy.ForceWeave1 + ? OGCDPriority.ForcedOGCD + : OGCDPriority.Triplecast); //Amplifier if (ShouldUseAmplifier(primaryTarget, ampStrat)) - QueueOGCD(AID.Amplifier, Player, OGCDPriority.Amplifier); + QueueOGCD(AID.Amplifier, + Player, + ampStrat is OffensiveStrategy.Force + or OffensiveStrategy.AnyWeave + or OffensiveStrategy.EarlyWeave + or OffensiveStrategy.LateWeave + ? OGCDPriority.ForcedOGCD + : OGCDPriority.Amplifier); //Manafont if (ShouldUseManafont(primaryTarget, mfStrat)) - QueueOGCD(AID.Manafont, Player, OGCDPriority.Manafont); + QueueOGCD(AID.Manafont, + Player, + mfStrat is ManafontStrategy.Force + or ManafontStrategy.ForceWeave + or ManafontStrategy.ForceEX + or ManafontStrategy.ForceWeaveEX + ? OGCDPriority.ForcedOGCD + : OGCDPriority.Manafont); //Retrace if (ShouldUseRetrace(retraceStrat)) - QueueOGCD(AID.Retrace, Player, OGCDPriority.ForcedOGCD); + QueueOGCD(AID.Retrace, + Player, + OGCDPriority.ForcedOGCD); //Between the Lines if (ShouldUseBTL(btlStrat)) - QueueOGCD(AID.BetweenTheLines, Player, OGCDPriority.ForcedOGCD); + QueueOGCD(AID.BetweenTheLines, + Player, + OGCDPriority.ForcedOGCD); //Potion if (potion is PotionStrategy.AlignWithRaidBuffs && CD(AID.LeyLines) < 5 || potion is PotionStrategy.Immediate) From 7415d252c06066b7907992c7a66bfb3b33200819 Mon Sep 17 00:00:00 2001 From: ace Date: Thu, 16 Jan 2025 17:09:01 -0800 Subject: [PATCH 08/18] new stuff, some opti.. fix later --- BossMod/Autorotation/akechi/AkechiBLM.cs | 121 +++++++++++++---------- 1 file changed, 68 insertions(+), 53 deletions(-) diff --git a/BossMod/Autorotation/akechi/AkechiBLM.cs b/BossMod/Autorotation/akechi/AkechiBLM.cs index 94251a9ddd..a7aee27b28 100644 --- a/BossMod/Autorotation/akechi/AkechiBLM.cs +++ b/BossMod/Autorotation/akechi/AkechiBLM.cs @@ -41,9 +41,9 @@ public enum ThunderStrategy } public enum PolyglotStrategy { - AutoAll, //Spend all Polyglots as soon as possible - Auto2, //Spend 2 Polyglots; holds one for manual usage - Auto1, //Spend 1 Polyglot; holds two for manual usage + AutoSpendAll, //Spend all Polyglots as soon as possible + AutoHold1, //Spend 2 Polyglots; holds one for manual usage + AutoHold2, //Spend 1 Polyglot; holds two for manual usage OnlyXenoAll, //Use Xenoglossy as optimal spender, regardless of targets nearby; spends all Polyglots OnlyXeno2, //Use Xenoglossy as optimal spender, regardless of targets nearby; holds one Polyglot for manual usage OnlyXeno1, //Use Xenoglossy as optimal spender, regardless of targets nearby; holds two Polyglots for manual usage @@ -123,9 +123,9 @@ public static RotationModuleDefinition Definition() .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) - .AddOption(PolyglotStrategy.AutoAll, "AutoAll", "Spend all Polyglots as soon as possible", 0, 0, ActionTargets.Hostile, 70) - .AddOption(PolyglotStrategy.Auto2, "Auto2", "Spend 2 Polyglots; holds one for manual usage", 0, 0, ActionTargets.Hostile, 70) - .AddOption(PolyglotStrategy.Auto1, "Auto1", "Spend 1 Polyglot; holds two for manual usage", 0, 0, ActionTargets.Hostile, 70) + .AddOption(PolyglotStrategy.AutoSpendAll, "AutoSpendAll", "Spend all Polyglots as soon as possible", 0, 0, ActionTargets.Hostile, 70) + .AddOption(PolyglotStrategy.AutoHold1, "AutoHold1", "Spend 2 Polyglots; holds one for manual usage", 0, 0, ActionTargets.Hostile, 70) + .AddOption(PolyglotStrategy.AutoHold2, "AutoHold2", "Spend 1 Polyglot; holds two for manual usage", 0, 0, ActionTargets.Hostile, 70) .AddOption(PolyglotStrategy.OnlyXenoAll, "OnlyXenoAll", "Use Xenoglossy as optimal spender, regardless of targets nearby; spends all Polyglots", 0, 0, ActionTargets.Hostile, 80) .AddOption(PolyglotStrategy.OnlyXeno2, "OnlyXeno2", "Use Xenoglossy as optimal spender, regardless of targets nearby; holds one Polyglot for manual usage", 0, 0, ActionTargets.Hostile, 80) .AddOption(PolyglotStrategy.OnlyXeno1, "OnlyXeno1", "Use Xenoglossy as optimal spender, regardless of targets nearby; holds two Polyglots for manual usage", 0, 0, ActionTargets.Hostile, 80) @@ -303,7 +303,6 @@ private AID BestXenoglossy private bool canMF; //Can use Manafont private bool canRetrace; //Can use Retrace private bool canBTL; //Can use Between the Lines - private bool hasFirestarter; //Has Firestarter buff private bool hasThunderhead; //Has Thunderhead buff private float ThunderLeft; //Time left on DOT effect (30s base) private bool ShouldUseAOE; //Checks if AOE should be used @@ -366,7 +365,6 @@ public override void Execute(StrategyValues strategy, Actor? primaryTarget, floa canMF = ActionReady(AID.Manafont); //Can use Manafont canRetrace = ActionReady(AID.Retrace) && PlayerHasEffect(SID.LeyLines, 30); //Can use Retrace canBTL = ActionReady(AID.BetweenTheLines) && PlayerHasEffect(SID.LeyLines, 30); //Can use Between the Lines - hasFirestarter = PlayerHasEffect(SID.Firestarter, 15); //Has Firestarter buff hasThunderhead = PlayerHasEffect(SID.Thunderhead, 30); //Has Thunderhead buff ThunderLeft = Utils.MaxAll( StatusDetails(primaryTarget, SID.Thunder, Player.InstanceID, 24).Left, @@ -451,9 +449,9 @@ public override void Execute(StrategyValues strategy, Actor? primaryTarget, floa //Polyglots if (ShouldUsePolyglot(primaryTarget, polyglotStrat)) //if Polyglot should be used based on strategy { - if (polyglotStrat is PolyglotStrategy.AutoAll - or PolyglotStrategy.Auto2 - or PolyglotStrategy.Auto1) + if (polyglotStrat is PolyglotStrategy.AutoSpendAll + or PolyglotStrategy.AutoHold1 + or PolyglotStrategy.AutoHold2) QueueGCD(BestPolyglot, TargetChoice(polyglot) ?? primaryTarget, polyglotStrat is PolyglotStrategy.ForceXeno ? GCDPriority.ForcedGCD @@ -646,9 +644,10 @@ private void STLv35toLv59(Actor? target) //Level 35-59 single-target rotation //Step 1 - Fire 1 if (MP >= 1600) //if MP is 1600 or more QueueGCD(AID.Fire1, target, GCDPriority.Step3); //Queue Fire I - //Step 2 - F3P - if (hasFirestarter && AstralStacks == 3) //if Firestarter buff is active and Astral Fire stacks are max - QueueGCD(AID.Fire3, target, GCDPriority.Step2); //Queue Fire III + //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 @@ -688,9 +687,10 @@ private void STLv60toLv71(Actor? target) //Level 60-71 single-target rotation 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 <= 3 && MP >= 4000 ? GCDPriority.Paradox : GCDPriority.Step4); //Queue Fire I, increase priority if less than 3s left on element - //Step 4B - F3P - if (hasFirestarter && AstralStacks == 3) //if Firestarter buff is active and Astral Fire stacks are max - QueueGCD(AID.Fire3, target, GCDPriority.Step3); //Queue Fire III + //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 @@ -731,8 +731,9 @@ private void STLv72toLv89(Actor? target) //Level 72-89 single-target rotation MP >= 4000) //and MP is 4000 or more QueueGCD(AID.Fire1, target, ElementTimer <= 3 && 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 has less than 25s left - QueueGCD(AID.Fire3, target, GCDPriority.Step3); //Queue Fire III + 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 @@ -781,9 +782,10 @@ private void STLv90toLv99(Actor? target) //Level 90-99 single-target rotation 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 5B - F3P - if (SelfStatusLeft(SID.Firestarter, 30) is < 25 and not 0) //if Firestarter buff is active and has less than 25s left - QueueGCD(AID.Fire3, target, GCDPriority.Step3); //Queue Fire III + //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 @@ -833,16 +835,21 @@ private void STLv100(Actor? target) //Level 100 single-target rotation 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 5B - F3P - if (SelfStatusLeft(SID.Firestarter, 30) is < 25 and not 0) //if Firestarter buff is active and has less than 25s left - QueueGCD(AID.Fire3, target, GCDPriority.Step4); //Queue Fire III + //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 (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 @@ -1214,45 +1221,52 @@ private void BestAOE(Actor? target) ThunderStrategy.Delay => false, _ => false }; + private bool ShouldSpendPolyglot(Actor? target, PolyglotStrategy strategy) => strategy switch { - PolyglotStrategy.AutoAll + PolyglotStrategy.AutoSpendAll + => Player.InCombat && + target != null && + + Polyglots > 0 && //Spend 3 + (((CD(AID.Triplecast) < 5 || CD(AID.Triplecast) == 0 || (CD(AID.Triplecast) >= 59 && CD(AID.Triplecast) <= 65)) && PlayerHasEffect(SID.LeyLines, 30)) || //Triplecast prep + (CD(AID.LeyLines) < 5 || CD(AID.LeyLines) == 0 || CD(AID.LeyLines) <= 125 && CD(AID.LeyLines) >= 119) || //Ley Lines prep + CD(AID.Amplifier) < 0.6f || //Amplifier prep + (CD(AID.Manafont) < 0.6f && MP < 1600)), //Manafont prep + PolyglotStrategy.AutoHold1 => Player.InCombat && target != null && - Polyglots > 0 && - ((CD(AID.Triplecast) <= 60 && PlayerHasEffect(SID.LeyLines, 30)) || - CD(AID.LeyLines) <= 120 || - (CD(AID.Manafont) < 0.6f && MP < 1600) || - (Polyglots == MaxPolyglots && EnochianTimer <= 5000)), //Overcap - PolyglotStrategy.Auto2 + Polyglots > 1 && //Spend 2 + (((CD(AID.Triplecast) < 5 || CD(AID.Triplecast) == 0 || (CD(AID.Triplecast) >= 59 && CD(AID.Triplecast) <= 65)) && PlayerHasEffect(SID.LeyLines, 30)) || //Triplecast prep + (CD(AID.LeyLines) < 5 || CD(AID.LeyLines) == 0 || CD(AID.LeyLines) <= 125 && CD(AID.LeyLines) >= 119) || //Ley Lines prep + CD(AID.Amplifier) < 0.6f || //Amplifier prep + (CD(AID.Manafont) < 0.6f && MP < 1600)), //Manafont prep + PolyglotStrategy.AutoHold2 => Player.InCombat && target != null && - Polyglots > 1 && - (CD(AID.Triplecast) <= 60 || - CD(AID.LeyLines) <= 120 || - (CD(AID.Manafont) < 0.6f && MP < 1600) || - (Polyglots == MaxPolyglots && EnochianTimer <= 5000)), //Overcap - PolyglotStrategy.Auto1 + Polyglots > 2 && //Spend 1 + (((CD(AID.Triplecast) < 5 || (CD(AID.Triplecast) <= 60 && CD(AID.Triplecast) >= 65)) && PlayerHasEffect(SID.LeyLines, 30)) || //Triplecast prep + (CD(AID.LeyLines) < 5 || CD(AID.LeyLines) <= 120 && CD(AID.LeyLines) >= 110 || //Ley Lines prep + CD(AID.Amplifier) < 0.6f || //Amplifier prep + (CD(AID.Manafont) < 0.6f && MP < 1600)), //Manafont prep + PolyglotStrategy.AutoHold3 => Player.InCombat && target != null && - Polyglots > 2 && - (CD(AID.Triplecast) <= 60 || - CD(AID.LeyLines) <= 120 || - (CD(AID.Manafont) < 0.6f && MP < 1600) || - (Polyglots == MaxPolyglots && EnochianTimer <= 5000)), //Overcap + Polyglots == 3 && //if max Polyglots + EnochianTimer <= 5000, //Enochian is 5s away from adding a Polyglot _ => false }; private bool ShouldUsePolyglot(Actor? target, PolyglotStrategy strategy) => strategy switch { - PolyglotStrategy.AutoAll => ShouldSpendPolyglot(target, PolyglotStrategy.AutoAll), - PolyglotStrategy.Auto2 => ShouldSpendPolyglot(target, PolyglotStrategy.Auto2), - PolyglotStrategy.Auto1 => ShouldSpendPolyglot(target, PolyglotStrategy.Auto1), - PolyglotStrategy.OnlyXenoAll => ShouldSpendPolyglot(target, PolyglotStrategy.AutoAll), - PolyglotStrategy.OnlyXeno2 => ShouldSpendPolyglot(target, PolyglotStrategy.Auto2), - PolyglotStrategy.OnlyXeno1 => ShouldSpendPolyglot(target, PolyglotStrategy.Auto1), - PolyglotStrategy.OnlyFoulAll => ShouldSpendPolyglot(target, PolyglotStrategy.AutoAll), - PolyglotStrategy.OnlyFoul2 => ShouldSpendPolyglot(target, PolyglotStrategy.Auto2), - PolyglotStrategy.OnlyFoul1 => ShouldSpendPolyglot(target, PolyglotStrategy.Auto1), + PolyglotStrategy.AutoSpendAll => ShouldSpendPolyglot(target, PolyglotStrategy.AutoSpendAll), + PolyglotStrategy.AutoHold1 => ShouldSpendPolyglot(target, PolyglotStrategy.AutoHold1), + PolyglotStrategy.AutoHold2 => ShouldSpendPolyglot(target, PolyglotStrategy.AutoHold2), + PolyglotStrategy.OnlyXenoAll => ShouldSpendPolyglot(target, PolyglotStrategy.AutoSpendAll), + PolyglotStrategy.OnlyXeno2 => ShouldSpendPolyglot(target, PolyglotStrategy.AutoHold1), + PolyglotStrategy.OnlyXeno1 => ShouldSpendPolyglot(target, PolyglotStrategy.AutoHold2), + PolyglotStrategy.OnlyFoulAll => ShouldSpendPolyglot(target, PolyglotStrategy.AutoSpendAll), + PolyglotStrategy.OnlyFoul2 => ShouldSpendPolyglot(target, PolyglotStrategy.AutoHold1), + PolyglotStrategy.OnlyFoul1 => ShouldSpendPolyglot(target, PolyglotStrategy.AutoHold2), PolyglotStrategy.ForceXeno => canXeno, PolyglotStrategy.ForceFoul => canFoul, PolyglotStrategy.Delay => false, @@ -1332,7 +1346,8 @@ private void BestAOE(Actor? target) target != null && canTC && canWeaveIn && - PlayerHasEffect(SID.LeyLines, 30), //Overcap + InAstralFire && + PlayerHasEffect(SID.LeyLines, 30), TriplecastStrategy.Force => canTC, TriplecastStrategy.Force1 => canTC && CD(AID.Triplecast) < (SpS * 2), TriplecastStrategy.ForceWeave => canTC && canWeaveIn, From 2c9659baf360e1961713abf4d62b23af43745437 Mon Sep 17 00:00:00 2001 From: AceAkechi123 Date: Thu, 16 Jan 2025 22:54:59 -0800 Subject: [PATCH 09/18] xeno opti --- BossMod/Autorotation/akechi/AkechiBLM.cs | 132 +++++++++++++++++------ 1 file changed, 102 insertions(+), 30 deletions(-) diff --git a/BossMod/Autorotation/akechi/AkechiBLM.cs b/BossMod/Autorotation/akechi/AkechiBLM.cs index a7aee27b28..6c862e8049 100644 --- a/BossMod/Autorotation/akechi/AkechiBLM.cs +++ b/BossMod/Autorotation/akechi/AkechiBLM.cs @@ -41,15 +41,18 @@ public enum ThunderStrategy } public enum PolyglotStrategy { - AutoSpendAll, //Spend all Polyglots as soon as possible - AutoHold1, //Spend 2 Polyglots; holds one for manual usage - AutoHold2, //Spend 1 Polyglot; holds two for manual usage - OnlyXenoAll, //Use Xenoglossy as optimal spender, regardless of targets nearby; spends all Polyglots - OnlyXeno2, //Use Xenoglossy as optimal spender, regardless of targets nearby; holds one Polyglot for manual usage - OnlyXeno1, //Use Xenoglossy as optimal spender, regardless of targets nearby; holds two Polyglots for manual usage - OnlyFoulAll, //Use Foul as optimal spender, regardless of targets nearby - OnlyFoul2, //Use Foul as optimal spender, regardless of targets nearby; holds one Polyglot for manual usage - OnlyFoul1, //Use Foul as optimal spender, regardless of targets nearby; holds two Polyglots for manual usage + AutoSpendAll, //Spend all Polyglots as soon as possible + AutoHold1, //Spend 2 Polyglots; holds one for manual usage + AutoHold2, //Spend 1 Polyglot; holds two for manual usage + AutoHold3, //Holds all Polyglots for as long as possible + XenoSpendAll, //Use Xenoglossy as optimal spender, regardless of targets nearby; spends all Polyglots + XenoHold1, //Use Xenoglossy as optimal spender, regardless of targets nearby; holds one Polyglot for manual usage + XenoHold2, //Use Xenoglossy as optimal spender, regardless of targets nearby; holds two Polyglots for manual usage + XenoHold3, //Holds all Polyglots for as long as possible + FoulSpendAll, //Use Foul as optimal spender, regardless of targets nearby + FoulHold1, //Use Foul as optimal spender, regardless of targets nearby; holds one Polyglot for manual usage + FoulHold2, //Use Foul as optimal spender, regardless of targets nearby; holds two Polyglots for manual usage + FoulHold3, //Holds all Polyglots for as long as possible ForceXeno, //Force use of Xenoglossy ForceFoul, //Force use of Foul Delay //Delay the use of Polyglot abilities for manual or strategic usage @@ -126,12 +129,15 @@ public static RotationModuleDefinition Definition() .AddOption(PolyglotStrategy.AutoSpendAll, "AutoSpendAll", "Spend all Polyglots as soon as possible", 0, 0, ActionTargets.Hostile, 70) .AddOption(PolyglotStrategy.AutoHold1, "AutoHold1", "Spend 2 Polyglots; holds one for manual usage", 0, 0, ActionTargets.Hostile, 70) .AddOption(PolyglotStrategy.AutoHold2, "AutoHold2", "Spend 1 Polyglot; holds two for manual usage", 0, 0, ActionTargets.Hostile, 70) - .AddOption(PolyglotStrategy.OnlyXenoAll, "OnlyXenoAll", "Use Xenoglossy as optimal spender, regardless of targets nearby; spends all Polyglots", 0, 0, ActionTargets.Hostile, 80) - .AddOption(PolyglotStrategy.OnlyXeno2, "OnlyXeno2", "Use Xenoglossy as optimal spender, regardless of targets nearby; holds one Polyglot for manual usage", 0, 0, ActionTargets.Hostile, 80) - .AddOption(PolyglotStrategy.OnlyXeno1, "OnlyXeno1", "Use Xenoglossy as optimal spender, regardless of targets nearby; holds two Polyglots for manual usage", 0, 0, ActionTargets.Hostile, 80) - .AddOption(PolyglotStrategy.OnlyFoulAll, "OnlyFoulAll", "Use Foul as optimal spender, regardless of targets nearby", 0, 0, ActionTargets.Hostile, 70) - .AddOption(PolyglotStrategy.OnlyFoul2, "OnlyFoul2", "Use Foul as optimal spender, regardless of targets nearby; holds one Polyglot for manual usage", 0, 0, ActionTargets.Hostile, 70) - .AddOption(PolyglotStrategy.OnlyFoul1, "OnlyFoul1", "Use Foul as optimal spender, regardless of targets nearby; holds two Polyglots for manual usage", 0, 0, ActionTargets.Hostile, 70) + .AddOption(PolyglotStrategy.AutoHold3, "AutoHold3", "Holds all Polyglots for as long as possible", 0, 0, ActionTargets.Hostile, 70) + .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.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.ForceXeno, "Force Xenoglossy", "Force use of Xenoglossy", 0, 0, ActionTargets.Hostile, 80) .AddOption(PolyglotStrategy.ForceFoul, "Force Foul", "Force use of Foul", 0, 30, ActionTargets.Hostile, 70) .AddOption(PolyglotStrategy.Delay, "Delay", "Delay the use of Polyglot abilities for manual or strategic usage", 0, 0, ActionTargets.Hostile, 70) @@ -305,7 +311,6 @@ private AID BestXenoglossy private bool canBTL; //Can use Between the Lines private bool hasThunderhead; //Has Thunderhead buff private float ThunderLeft; //Time left on DOT effect (30s base) - private bool ShouldUseAOE; //Checks if AOE should be used public bool canWeaveIn; //Can weave in oGCDs public bool canWeaveEarly; //Can early weave oGCDs public bool canWeaveLate; //Can late weave oGCDs @@ -336,6 +341,72 @@ private bool JustUsed(AID aid, float variance) && (World.CurrentTime - Manager.LastCast.Time).TotalSeconds <= variance; } private Actor? TargetChoice(StrategyValues.OptionRef strategy) => ResolveTargetOverride(strategy.Value); //Resolves the target choice based on the strategy + 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 + 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 (_, bestPrio) = FindBetterTargetBy(null, 25f, splashPriorityFunc); + + return bestPrio >= minimumTargetsForAOE; + } + + return false; + } + } + private Actor? BestAOETarget => FindBestSplashTarget(); // Find the best target for splash attack + + 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; + } #endregion public override void Execute(StrategyValues strategy, Actor? primaryTarget, float estimatedAnimLockDelay, bool isMoving) //Executes our actions @@ -380,7 +451,6 @@ public override void Execute(StrategyValues strategy, Actor? primaryTarget, floa 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 PotionLeft = PotionStatusLeft(); //Remaining time for potion buff (30s) - ShouldUseAOE = TargetsInRange() > 2; //otherwise, use AOE if 2+ targets would be hit #region Strategy Definitions var AOE = strategy.Option(Track.AOE); //AOE track @@ -457,15 +527,15 @@ or PolyglotStrategy.AutoHold1 polyglotStrat is PolyglotStrategy.ForceXeno ? GCDPriority.ForcedGCD : Polyglots == MaxPolyglots && EnochianTimer < 5000 ? GCDPriority.NeedPolyglot : GCDPriority.Paradox); - if (polyglotStrat is PolyglotStrategy.OnlyXenoAll - or PolyglotStrategy.OnlyXeno2 - or PolyglotStrategy.OnlyXeno1) + if (polyglotStrat is PolyglotStrategy.XenoSpendAll + or PolyglotStrategy.XenoHold1 + or PolyglotStrategy.XenoHold2) QueueGCD(BestXenoglossy, TargetChoice(polyglot) ?? primaryTarget, polyglotStrat is PolyglotStrategy.ForceXeno ? GCDPriority.ForcedGCD : Polyglots == MaxPolyglots && EnochianTimer < 5000 ? GCDPriority.NeedPolyglot : GCDPriority.Paradox); - if (polyglotStrat is PolyglotStrategy.OnlyFoulAll or PolyglotStrategy.OnlyFoul2 or PolyglotStrategy.OnlyFoul1) + if (polyglotStrat is PolyglotStrategy.FoulSpendAll or PolyglotStrategy.FoulHold1 or PolyglotStrategy.FoulHold2) QueueGCD(AID.Foul, TargetChoice(polyglot) ?? primaryTarget, polyglotStrat is PolyglotStrategy.ForceFoul ? GCDPriority.ForcedGCD : Polyglots == MaxPolyglots && EnochianTimer < 5000 ? GCDPriority.NeedPolyglot : GCDPriority.Paradox); //Queue Foul } //LeyLines @@ -580,7 +650,6 @@ 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 private void BestRotation(Actor? target) { if (ShouldUseAOE) @@ -592,6 +661,7 @@ private void BestRotation(Actor? target) BestST(target); } } + #endregion #region Single-Target Helpers private void STLv1toLv34(Actor? target) //Level 1-34 single-target rotation @@ -1227,7 +1297,6 @@ private void BestAOE(Actor? target) PolyglotStrategy.AutoSpendAll => Player.InCombat && target != null && - Polyglots > 0 && //Spend 3 (((CD(AID.Triplecast) < 5 || CD(AID.Triplecast) == 0 || (CD(AID.Triplecast) >= 59 && CD(AID.Triplecast) <= 65)) && PlayerHasEffect(SID.LeyLines, 30)) || //Triplecast prep (CD(AID.LeyLines) < 5 || CD(AID.LeyLines) == 0 || CD(AID.LeyLines) <= 125 && CD(AID.LeyLines) >= 119) || //Ley Lines prep @@ -1248,7 +1317,7 @@ private void BestAOE(Actor? target) (((CD(AID.Triplecast) < 5 || (CD(AID.Triplecast) <= 60 && CD(AID.Triplecast) >= 65)) && PlayerHasEffect(SID.LeyLines, 30)) || //Triplecast prep (CD(AID.LeyLines) < 5 || CD(AID.LeyLines) <= 120 && CD(AID.LeyLines) >= 110 || //Ley Lines prep CD(AID.Amplifier) < 0.6f || //Amplifier prep - (CD(AID.Manafont) < 0.6f && MP < 1600)), //Manafont prep + (CD(AID.Manafont) < 0.6f && MP < 1600))), //Manafont prep PolyglotStrategy.AutoHold3 => Player.InCombat && target != null && @@ -1261,12 +1330,15 @@ private void BestAOE(Actor? target) PolyglotStrategy.AutoSpendAll => ShouldSpendPolyglot(target, PolyglotStrategy.AutoSpendAll), PolyglotStrategy.AutoHold1 => ShouldSpendPolyglot(target, PolyglotStrategy.AutoHold1), PolyglotStrategy.AutoHold2 => ShouldSpendPolyglot(target, PolyglotStrategy.AutoHold2), - PolyglotStrategy.OnlyXenoAll => ShouldSpendPolyglot(target, PolyglotStrategy.AutoSpendAll), - PolyglotStrategy.OnlyXeno2 => ShouldSpendPolyglot(target, PolyglotStrategy.AutoHold1), - PolyglotStrategy.OnlyXeno1 => ShouldSpendPolyglot(target, PolyglotStrategy.AutoHold2), - PolyglotStrategy.OnlyFoulAll => ShouldSpendPolyglot(target, PolyglotStrategy.AutoSpendAll), - PolyglotStrategy.OnlyFoul2 => ShouldSpendPolyglot(target, PolyglotStrategy.AutoHold1), - PolyglotStrategy.OnlyFoul1 => ShouldSpendPolyglot(target, PolyglotStrategy.AutoHold2), + PolyglotStrategy.AutoHold3 => ShouldSpendPolyglot(target, PolyglotStrategy.AutoHold3), + PolyglotStrategy.XenoSpendAll => ShouldSpendPolyglot(target, PolyglotStrategy.AutoSpendAll), + PolyglotStrategy.XenoHold1 => ShouldSpendPolyglot(target, PolyglotStrategy.AutoHold1), + PolyglotStrategy.XenoHold2 => ShouldSpendPolyglot(target, PolyglotStrategy.AutoHold2), + PolyglotStrategy.XenoHold3 => ShouldSpendPolyglot(target, PolyglotStrategy.AutoHold3), + PolyglotStrategy.FoulSpendAll => ShouldSpendPolyglot(target, PolyglotStrategy.AutoSpendAll), + PolyglotStrategy.FoulHold1 => ShouldSpendPolyglot(target, PolyglotStrategy.AutoHold1), + PolyglotStrategy.FoulHold2 => ShouldSpendPolyglot(target, PolyglotStrategy.AutoHold2), + PolyglotStrategy.FoulHold3 => ShouldSpendPolyglot(target, PolyglotStrategy.AutoHold3), PolyglotStrategy.ForceXeno => canXeno, PolyglotStrategy.ForceFoul => canFoul, PolyglotStrategy.Delay => false, From 8db920bfedd9fd636ec23d8cb47908885f112c7c Mon Sep 17 00:00:00 2001 From: AceAkechi123 Date: Thu, 16 Jan 2025 23:32:39 -0800 Subject: [PATCH 10/18] now to test --- BossMod/Autorotation/akechi/AkechiBLM.cs | 47 +++++++++++------------- 1 file changed, 22 insertions(+), 25 deletions(-) diff --git a/BossMod/Autorotation/akechi/AkechiBLM.cs b/BossMod/Autorotation/akechi/AkechiBLM.cs index 6c862e8049..f46e93762e 100644 --- a/BossMod/Autorotation/akechi/AkechiBLM.cs +++ b/BossMod/Autorotation/akechi/AkechiBLM.cs @@ -650,7 +650,7 @@ 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; } - private void BestRotation(Actor? target) + private void BestRotation(Actor? target) //Best rotation based on targets nearby { if (ShouldUseAOE) { @@ -750,13 +750,12 @@ private void STLv60toLv71(Actor? target) //Level 60-71 single-target rotation if (InAstralFire) //if Astral Fire is active { //Step 1-3, 5-7 - Fire IV - if (ElementTimer >= (SpS * 3) && //if time remaining on current element is greater than or equal to 3x GCDs - MP >= 1600) //and MP is 1600 or more + 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 <= 3 && MP >= 4000 ? GCDPriority.Paradox : GCDPriority.Step4); //Queue Fire I, increase priority if less than 3s left on element + 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 @@ -793,13 +792,12 @@ private void STLv72toLv89(Actor? target) //Level 72-89 single-target rotation if (InAstralFire) //if Astral Fire is active { //Step 1-3, 5-7 - Fire IV - if (ElementTimer >= (SpS * 3) && //if time remaining on current element is greater than or equal to 3x GCDs - MP >= 1600) //and MP is 1600 or more + 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 <= 3 && MP >= 4000 ? GCDPriority.Paradox : GCDPriority.Step4); //Queue Fire I, increase priority if less than 3s left on element + 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 @@ -844,8 +842,7 @@ private void STLv90toLv99(Actor? target) //Level 90-99 single-target rotation if (InAstralFire) //if Astral Fire is active { //Step 1-4, 6 & 7 - Fire IV - if (ElementTimer >= (SpS * 3) && //if time remaining on current element is greater than or equal to 3x GCDs - MP >= 1600) //and MP is 1600 or more + 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 @@ -896,8 +893,7 @@ private void STLv100(Actor? target) //Level 100 single-target rotation if (InAstralFire) //if Astral Fire is active { //Step 1-4, 6 & 7 - Fire IV - if (ElementTimer >= (SpS * 3) && //if time remaining on current element is greater than or equal to 3x GCDs - AstralSoulStacks != 6 && //and Astral Soul stacks are not max + 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 @@ -932,7 +928,7 @@ private void STLv100(Actor? target) //Level 100 single-target rotation QueueGCD(AID.Blizzard3, target, GCDPriority.Step1); //Queue Blizzard III } } - private void BestST(Actor? target) + private void BestST(Actor? target) //Single-target rotation based on level { if (Player.Level is >= 1 and <= 34) { @@ -962,7 +958,7 @@ private void BestST(Actor? target) #endregion #region AOE Helpers - private void AOELv12toLv34(Actor? target) + private void AOELv12toLv34(Actor? target) //Level 12-34 AOE rotation { //Fire if (Unlocked(AID.Fire2) && //if Fire is unlocked @@ -978,7 +974,7 @@ private void AOELv12toLv34(Actor? target) InUmbralIce && MP == 10000) //or if Umbral Ice is active and MP is max QueueOGCD(AID.Transpose, Player, OGCDPriority.Transpose); //Queue Transpose } - private void AOELv35toLv39(Actor? target) + private void AOELv35toLv39(Actor? target) //Level 35-39 AOE rotation { if (NoStance) { @@ -1011,7 +1007,7 @@ private void AOELv35toLv39(Actor? target) QueueGCD(AID.Blizzard2, target, GCDPriority.Step1); } } - private void AOELv40toLv49(Actor? target) + private void AOELv40toLv49(Actor? target) //Level 40-49 AOE rotation { if (NoStance) { @@ -1049,7 +1045,7 @@ private void AOELv40toLv49(Actor? target) QueueGCD(AID.Blizzard2, target, GCDPriority.Step1); } } - private void AOELv50toLv57(Actor? target) + private void AOELv50toLv57(Actor? target) //Level 50-57 AOE rotation { if (NoStance) { @@ -1094,7 +1090,7 @@ private void AOELv50toLv57(Actor? target) QueueGCD(AID.Blizzard2, target, GCDPriority.Step1); } } - private void AOELv58toLv81(Actor? target) + private void AOELv58toLv81(Actor? target) //Level 58-81 AOE rotation { if (NoStance) { @@ -1112,27 +1108,28 @@ private void AOELv58toLv81(Actor? target) if (JustUsed(AID.Fire2, 5) && Unlocked(AID.Blizzard2) && UmbralStacks != 3) - QueueGCD(AID.Blizzard2, target, GCDPriority.Step2); + QueueGCD(AID.Blizzard2, target, GCDPriority.Step3); //Step 2 - Freeze if (Unlocked(AID.Freeze) && JustUsed(AID.Blizzard2, 5) || UmbralStacks == 3) - QueueGCD(AID.Freeze, target, JustUsed(AID.Blizzard2, 5) ? GCDPriority.Step10 : GCDPriority.Step1); + QueueGCD(AID.Freeze, target, JustUsed(AID.Blizzard2, 5) ? GCDPriority.Step10 : GCDPriority.Step2); //Step 3 - swap from UI to AF if (Unlocked(AID.Fire2) && MP >= 10000 && UmbralStacks == 3) - QueueGCD(AID.Fire2, target, GCDPriority.Step1); + QueueGCD(AID.Fire2, target, JustUsed(AID.Freeze, 5f) ? GCDPriority.Step10 : GCDPriority.Step1); } if (InAstralFire) { //Step 1 - spam Fire 2 - if (MP > 5500) + if (MP > 7000) QueueGCD(AID.Fire2, target, GCDPriority.Step4); //Step 2 - Flare if (Unlocked(AID.Flare)) { //first cast - if (UmbralHearts == 1) + if (MP <= 7000 && + UmbralHearts == 1) QueueGCD(AID.Flare, target, GCDPriority.Step3); //second cast if (MP < 2500 && JustUsed(AID.Flare, 5f)) @@ -1144,7 +1141,7 @@ private void AOELv58toLv81(Actor? target) QueueGCD(AID.Blizzard2, target, GCDPriority.Step1); } } - private void AOELv82toLv99(Actor? target) + private void AOELv82toLv99(Actor? target) //Level 82-99 AOE rotation { if (NoStance) { @@ -1194,7 +1191,7 @@ private void AOELv82toLv99(Actor? target) QueueGCD(AID.HighBlizzard2, target, GCDPriority.Step1); } } - private void AOELv100(Actor? target) + private void AOELv100(Actor? target) //Level 100 AOE rotation { if (NoStance) { @@ -1244,7 +1241,7 @@ private void AOELv100(Actor? target) QueueGCD(AID.HighBlizzard2, target, GCDPriority.Step1); } } - private void BestAOE(Actor? target) + private void BestAOE(Actor? target) //AOE rotation based on level { if (In25y(target)) { From 0c076f27e4df3df6cf33252fe5b4896492c6f53a Mon Sep 17 00:00:00 2001 From: AceAkechi123 Date: Fri, 17 Jan 2025 05:15:27 -0800 Subject: [PATCH 11/18] okay fuck, everything is working now --- BossMod/Autorotation/akechi/AkechiBLM.cs | 361 +++++++++++++++-------- 1 file changed, 231 insertions(+), 130 deletions(-) diff --git a/BossMod/Autorotation/akechi/AkechiBLM.cs b/BossMod/Autorotation/akechi/AkechiBLM.cs index f46e93762e..bbdc8d6cdb 100644 --- a/BossMod/Autorotation/akechi/AkechiBLM.cs +++ b/BossMod/Autorotation/akechi/AkechiBLM.cs @@ -1,4 +1,6 @@ -using FFXIVClientStructs.FFXIV.Client.Game.Gauge; +using BossMod.Global.MaskedCarnivale.Stage26.Act2; +using FFXIVClientStructs.FFXIV.Client.Game.Gauge; +using System.Runtime.CompilerServices; using AID = BossMod.BLM.AID; using SID = BossMod.BLM.SID; using TraitID = BossMod.BLM.TraitID; @@ -139,7 +141,7 @@ public static RotationModuleDefinition Definition() .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.ForceXeno, "Force Xenoglossy", "Force use of Xenoglossy", 0, 0, ActionTargets.Hostile, 80) - .AddOption(PolyglotStrategy.ForceFoul, "Force Foul", "Force use of Foul", 0, 30, ActionTargets.Hostile, 70) + .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) .AddAssociatedActions(AID.Xenoglossy, AID.Foul); res.Define(Track.Manafont).As("Manafont", "Manafont", uiPriority: 170) @@ -256,18 +258,6 @@ public static RotationModuleDefinition Definition() #endregion #region Upgrade Paths - private AID SwiftcastB3 - => Player.InCombat - && ActionReady(AID.Swiftcast) - ? AID.Swiftcast - : AID.Blizzard3; - private AID SwiftcastB2 - => Player.InCombat - && ActionReady(AID.Swiftcast) - ? AID.Swiftcast - : Unlocked(AID.HighBlizzard2) - ? AID.HighBlizzard2 - : AID.Blizzard2; private AID BestThunderST => Unlocked(AID.HighThunder) ? AID.HighThunder : Unlocked(AID.Thunder3) ? AID.Thunder3 @@ -322,6 +312,7 @@ private AID BestXenoglossy public float BurstWindowLeft; //Time left in current burst window (typically 20s-22s) public float BurstWindowIn; //Time until next burst window (typically 20s-22s) public AID NextGCD; //Next global cooldown action to be used + public float AnimLockDelay; //Estimated animation lock delay #endregion #region Module Helpers @@ -329,18 +320,62 @@ private AID BestXenoglossy private bool Unlocked(TraitID tid) => TraitUnlocked((uint)tid); //Check if the desired trait is unlocked private float CD(AID aid) => World.Client.Cooldowns[ActionDefinitions.Instance.Spell(aid)!.MainCooldownGroup].Remaining; //Get remaining cooldown time for the specified action 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 ready (cooldown less than 0.6 seconds) + 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 int TargetsInRange() => Hints.NumPriorityTargetsInAOECircle(Player.Position, 25); //Returns the number of targets hit by AOE within a 25-yalm radius around the player private bool PlayerHasEffect(SID sid, float duration) => SelfStatusLeft(sid, duration) > GCD; //Checks if Status effect is on self private bool JustUsed(AID aid, float variance) { - if (Manager?.LastCast == null) - return false; - - return Manager.LastCast.Data?.IsSpell(aid) == true - && (World.CurrentTime - Manager.LastCast.Time).TotalSeconds <= variance; + var used = Manager.LastCast.Data?.IsSpell(aid) == true; + var within = (World.CurrentTime - Manager.LastCast.Time).TotalSeconds <= variance; + return used && within; + } + public float GetActualCastTime(AID aid) => ActionDefinitions.Instance.Spell(aid)!.CastTime * SpS / 2.5f; + public float GetCastTime(AID aid) + { + var aspect = ActionDefinitions.Instance.Spell(aid)!.Aspect; + var castTime = GetActualCastTime(aid); + if (PlayerHasEffect(SID.Triplecast, 15f) || PlayerHasEffect(SID.Swiftcast, 10f)) + return 0f; + if (aid == AID.Fire3 && PlayerHasEffect(SID.Firestarter, 30f) + || aid == AID.Foul && Unlocked(TraitID.EnhancedFoul) + || aspect == ActionAspect.Thunder && PlayerHasEffect(SID.Thunderhead, 30f) + || aid == AID.Despair && Unlocked(TraitID.EnhancedAstralFire)) + return 0; + if (castTime == 0) + return 0; + if (ElementStance == -3 && aspect == ActionAspect.Fire || + ElementStance == 3 && aspect == ActionAspect.Ice) + castTime *= 0.5f; + return castTime; } + 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 @@ -380,33 +415,7 @@ float splashPriorityFunc(Actor actor) return false; } } - private Actor? BestAOETarget => FindBestSplashTarget(); // Find the best target for splash attack - 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; - } #endregion public override void Execute(StrategyValues strategy, Actor? primaryTarget, float estimatedAnimLockDelay, bool isMoving) //Executes our actions @@ -450,6 +459,7 @@ 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 + AnimLockDelay = estimatedAnimLockDelay; //Estimated animation lock delay PotionLeft = PotionStatusLeft(); //Remaining time for potion buff (30s) #region Strategy Definitions @@ -479,11 +489,11 @@ public override void Execute(StrategyValues strategy, Actor? primaryTarget, floa #region Rotation Execution //Rotations if (AOEStrategy is AOEStrategy.Auto) - BestRotation(TargetChoice(AOE) ?? primaryTarget); + BestRotation(TargetChoice(AOE) ?? BestAOETarget ?? primaryTarget); if (AOEStrategy is AOEStrategy.ForceST) BestST(TargetChoice(AOE) ?? primaryTarget); if (AOEStrategy is AOEStrategy.ForceAOE) - BestAOE(TargetChoice(AOE) ?? primaryTarget); + BestAOE(TargetChoice(AOE) ?? BestAOETarget ?? primaryTarget); //Out of combat if (Unlocked(AID.Transpose)) { @@ -512,10 +522,23 @@ public override void Execute(StrategyValues strategy, Actor? primaryTarget, floa } //Thunder if (ShouldUseThunder(primaryTarget, thunderStrat)) //if Thunder should be used based on strategy - QueueGCD(BestThunder, - TargetChoice(thunder) ?? primaryTarget, - ThunderLeft < 3 ? GCDPriority.NeedDOT : - GCDPriority.DOT); + { + if (AOEStrategy is AOEStrategy.Auto) + QueueGCD(BestThunder, + TargetChoice(thunder) ?? BestAOETarget ?? primaryTarget, + ThunderLeft < 3 ? GCDPriority.NeedDOT : + GCDPriority.DOT); + if (AOEStrategy is AOEStrategy.ForceST) + QueueGCD(BestThunderST, + TargetChoice(thunder) ?? primaryTarget, + ThunderLeft < 3 ? GCDPriority.NeedDOT : + GCDPriority.DOT); + if (AOEStrategy is AOEStrategy.ForceAOE) + QueueGCD(BestThunderAOE, + TargetChoice(thunder) ?? BestAOETarget ?? primaryTarget, + ThunderLeft < 3 ? GCDPriority.NeedDOT : + GCDPriority.DOT); + } //Polyglots if (ShouldUsePolyglot(primaryTarget, polyglotStrat)) //if Polyglot should be used based on strategy { @@ -523,7 +546,7 @@ public override void Execute(StrategyValues strategy, Actor? primaryTarget, floa or PolyglotStrategy.AutoHold1 or PolyglotStrategy.AutoHold2) QueueGCD(BestPolyglot, - TargetChoice(polyglot) ?? primaryTarget, + TargetChoice(polyglot) ?? BestAOETarget ?? primaryTarget, polyglotStrat is PolyglotStrategy.ForceXeno ? GCDPriority.ForcedGCD : Polyglots == MaxPolyglots && EnochianTimer < 5000 ? GCDPriority.NeedPolyglot : GCDPriority.Paradox); @@ -536,7 +559,7 @@ polyglotStrat is PolyglotStrategy.ForceXeno ? GCDPriority.ForcedGCD : Polyglots == MaxPolyglots && EnochianTimer < 5000 ? GCDPriority.NeedPolyglot : GCDPriority.Paradox); if (polyglotStrat is PolyglotStrategy.FoulSpendAll or PolyglotStrategy.FoulHold1 or PolyglotStrategy.FoulHold2) - QueueGCD(AID.Foul, TargetChoice(polyglot) ?? primaryTarget, polyglotStrat is PolyglotStrategy.ForceFoul ? GCDPriority.ForcedGCD : Polyglots == MaxPolyglots && EnochianTimer < 5000 ? GCDPriority.NeedPolyglot : GCDPriority.Paradox); //Queue Foul + QueueGCD(AID.Foul, TargetChoice(polyglot) ?? BestAOETarget ?? primaryTarget, polyglotStrat is PolyglotStrategy.ForceFoul ? GCDPriority.ForcedGCD : Polyglots == MaxPolyglots && EnochianTimer < 5000 ? GCDPriority.NeedPolyglot : GCDPriority.Paradox); //Queue Foul } //LeyLines if (ShouldUseLeyLines(primaryTarget, llStrat)) @@ -672,7 +695,8 @@ private void STLv1toLv34(Actor? target) //Level 1-34 single-target rotation InAstralFire && MP >= 1600) //or if Astral Fire is active and MP is 1600 or more QueueGCD(AID.Fire1, target, GCDPriority.Standard); //Queue Fire //Ice - if (InUmbralIce && MP != 10000) //if Umbral Ice is active and MP is not max + //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 @@ -689,7 +713,12 @@ private void STLv35toLv59(Actor? target) //Level 35-59 single-target rotation 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(SwiftcastB3, target, GCDPriority.NeedB3); //Queue Swiftcast->Blizzard III + { + if (ActionReady(AID.Swiftcast)) + QueueGCD(AID.Swiftcast, target, GCDPriority.NeedB3); //Queue Swiftcast->Blizzard III + else + QueueGCD(AID.Blizzard3, target, GCDPriority.NeedB3); //Queue Blizzard III + } } } if (InUmbralIce) //if Umbral Ice is active @@ -733,7 +762,12 @@ private void STLv60toLv71(Actor? target) //Level 60-71 single-target rotation 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(SwiftcastB3, target, GCDPriority.NeedB3); //Queue Swiftcast->Blizzard III + { + if (ActionReady(AID.Swiftcast)) + QueueGCD(AID.Swiftcast, target, GCDPriority.NeedB3); //Queue Swiftcast->Blizzard III + else + QueueGCD(AID.Blizzard3, target, GCDPriority.NeedB3); //Queue Blizzard III + } } } if (InUmbralIce) //if Umbral Ice is active @@ -775,7 +809,12 @@ private void STLv72toLv89(Actor? target) //Level 72-89 single-target rotation 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(SwiftcastB3, target, GCDPriority.NeedB3); //Queue Swiftcast->Blizzard III + { + if (ActionReady(AID.Swiftcast)) + QueueGCD(AID.Swiftcast, target, GCDPriority.NeedB3); //Queue Swiftcast->Blizzard III + else + QueueGCD(AID.Blizzard3, target, GCDPriority.NeedB3); //Queue Blizzard III + } } } if (InUmbralIce) //if Umbral Ice is active @@ -805,7 +844,12 @@ private void STLv72toLv89(Actor? target) //Level 72-89 single-target rotation //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.Step2); //Queue Despair + { + 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 @@ -821,7 +865,12 @@ private void STLv90toLv99(Actor? target) //Level 90-99 single-target rotation 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(SwiftcastB3, target, GCDPriority.NeedB3); //Queue Swiftcast->Blizzard III + { + if (ActionReady(AID.Swiftcast)) + QueueGCD(AID.Swiftcast, target, GCDPriority.NeedB3); //Queue Swiftcast->Blizzard III + else + QueueGCD(AID.Blizzard3, target, GCDPriority.NeedB3); //Queue Blizzard III + } } } if (InUmbralIce) //if Umbral Ice is active @@ -853,10 +902,15 @@ private void STLv90toLv99(Actor? target) //Level 90-99 single-target rotation 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 + //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.Step2); //Queue Despair + { + 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 @@ -872,7 +926,12 @@ private void STLv100(Actor? target) //Level 100 single-target rotation 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(SwiftcastB3, target, GCDPriority.NeedB3); //Queue Swiftcast->Blizzard III + { + if (ActionReady(AID.Swiftcast)) + QueueGCD(AID.Swiftcast, target, GCDPriority.NeedB3); //Queue Swiftcast->Blizzard III + else + QueueGCD(AID.Blizzard3, target, GCDPriority.NeedB3); //Queue Blizzard III + } } } if (InUmbralIce) //if Umbral Ice is active @@ -960,18 +1019,34 @@ private void BestST(Actor? target) //Single-target rotation based on level #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 (ActionReady(AID.Swiftcast)) + QueueGCD(AID.Swiftcast, target, GCDPriority.NeedB3); + else + QueueGCD(AID.Blizzard2, target, GCDPriority.NeedB3); + } + } + } //Fire if (Unlocked(AID.Fire2) && //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.Fire2, target, GCDPriority.Standard); //Queue Fire + 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 - if (InUmbralIce && MP != 10000) //if Umbral Ice is active and MP is not max - QueueGCD(AID.Blizzard2, target, GCDPriority.Standard); //Queue Blizzard + //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 < 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 + (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 @@ -983,27 +1058,33 @@ private void AOELv35toLv39(Actor? target) //Level 35-39 AOE rotation if (MP >= 10000) QueueGCD(AID.Blizzard2, target, GCDPriority.NeedB3); if (MP < 10000 && Player.InCombat) - QueueGCD(SwiftcastB2, target, GCDPriority.NeedB3); + { + if (ActionReady(AID.Swiftcast)) + QueueGCD(AID.Swiftcast, target, GCDPriority.NeedB3); + else + QueueGCD(AID.Blizzard2, target, GCDPriority.NeedB3); + } } } if (InUmbralIce) { - if (JustUsed(AID.Fire2, 5)) - { - if (Unlocked(AID.Blizzard2) && MP != 10000) - QueueGCD(AID.Blizzard2, target, GCDPriority.Step2); - } + //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 >= 10000 && + MP >= 9600 && UmbralStacks == 3) QueueGCD(AID.Fire2, target, GCDPriority.Step1); } if (InAstralFire) { - if (MP >= 1600) + if (MP >= 3000) QueueGCD(AID.Fire2, target, GCDPriority.Step2); if (Unlocked(AID.Blizzard2) && - MP < 1600) + MP < 3000) QueueGCD(AID.Blizzard2, target, GCDPriority.Step1); } } @@ -1016,20 +1097,24 @@ private void AOELv40toLv49(Actor? target) //Level 40-49 AOE rotation if (MP >= 10000) QueueGCD(AID.Blizzard2, target, GCDPriority.NeedB3); if (MP < 10000 && Player.InCombat) - QueueGCD(SwiftcastB2, target, GCDPriority.NeedB3); + { + if (ActionReady(AID.Swiftcast)) + QueueGCD(AID.Swiftcast, target, GCDPriority.NeedB3); + else + QueueGCD(AID.Blizzard2, target, GCDPriority.NeedB3); + } } } if (InUmbralIce) { //Step 1 - max stacks in UI - if (JustUsed(AID.Fire2, 5) && - Unlocked(AID.Blizzard2) && - UmbralStacks != 3) - QueueGCD(AID.Blizzard2, target, GCDPriority.Step2); + if (Unlocked(AID.Blizzard2) && + UmbralStacks < 3) + QueueGCD(AID.Blizzard2, target, GCDPriority.Step3); //Step 2 - Freeze - if (Unlocked(AID.Freeze) && - JustUsed(AID.Blizzard2, 5) || UmbralStacks == 3) - QueueGCD(AID.Freeze, target, JustUsed(AID.Blizzard2, 5) ? GCDPriority.Step10 : GCDPriority.Step1); + 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 && @@ -1038,10 +1123,10 @@ private void AOELv40toLv49(Actor? target) //Level 40-49 AOE rotation } if (InAstralFire) { - if (MP >= 1600) + if (MP >= 3000) QueueGCD(AID.Fire2, target, GCDPriority.Step2); if (Unlocked(AID.Blizzard2) && - MP < 1600) + MP < 3000) QueueGCD(AID.Blizzard2, target, GCDPriority.Step1); } } @@ -1054,20 +1139,24 @@ private void AOELv50toLv57(Actor? target) //Level 50-57 AOE rotation if (MP >= 10000) QueueGCD(AID.Blizzard2, target, GCDPriority.NeedB3); if (MP < 10000 && Player.InCombat) - QueueGCD(SwiftcastB2, target, GCDPriority.NeedB3); + { + if (ActionReady(AID.Swiftcast)) + QueueGCD(AID.Swiftcast, target, GCDPriority.NeedB3); + else + QueueGCD(AID.Blizzard2, target, GCDPriority.NeedB3); + } } } if (InUmbralIce) { //Step 1 - max stacks in UI - if (JustUsed(AID.Fire2, 5) && - Unlocked(AID.Blizzard2) && - UmbralStacks != 3) - QueueGCD(AID.Blizzard2, target, GCDPriority.Step2); + if (Unlocked(AID.Blizzard2) && + UmbralStacks < 3) + QueueGCD(AID.Blizzard2, target, GCDPriority.Step3); //Step 2 - Freeze - if (Unlocked(AID.Freeze) && - JustUsed(AID.Blizzard2, 5) || UmbralStacks == 3) - QueueGCD(AID.Freeze, target, JustUsed(AID.Blizzard2, 5) ? GCDPriority.Step10 : GCDPriority.Step1); + 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 && @@ -1077,17 +1166,17 @@ private void AOELv50toLv57(Actor? target) //Level 50-57 AOE rotation if (InAstralFire) { //Step 1 - spam Fire 2 - if (MP >= 1600) + if (MP >= 3000) QueueGCD(AID.Fire2, target, GCDPriority.Step3); //Step 2 - Flare if (Unlocked(AID.Flare) && - MP < 1600) + MP < 3000) QueueGCD(AID.Flare, target, GCDPriority.Step2); //Step 3 - swap from AF to UI if (Unlocked(AID.Blizzard2) && - (!Unlocked(AID.Flare) && MP < 1600) || + (!Unlocked(AID.Flare) && MP < 3000) || //do your job quests, fool (Unlocked(AID.Flare) && MP < 400)) - QueueGCD(AID.Blizzard2, target, GCDPriority.Step1); + QueueGCD(AID.Blizzard2, target, MP < 400 ? GCDPriority.Step10 : GCDPriority.Step1); } } private void AOELv58toLv81(Actor? target) //Level 58-81 AOE rotation @@ -1099,25 +1188,29 @@ private void AOELv58toLv81(Actor? target) //Level 58-81 AOE rotation if (MP >= 10000) QueueGCD(AID.Blizzard2, target, GCDPriority.NeedB3); if (MP < 10000 && Player.InCombat) - QueueGCD(SwiftcastB2, target, GCDPriority.NeedB3); + { + if (ActionReady(AID.Swiftcast)) + QueueGCD(AID.Swiftcast, target, GCDPriority.NeedB3); + else + QueueGCD(AID.Blizzard2, target, GCDPriority.NeedB3); + } } } if (InUmbralIce) { //Step 1 - max stacks in UI - if (JustUsed(AID.Fire2, 5) && - Unlocked(AID.Blizzard2) && - UmbralStacks != 3) + if (Unlocked(AID.Blizzard2) && + UmbralStacks < 3) QueueGCD(AID.Blizzard2, target, GCDPriority.Step3); //Step 2 - Freeze - if (Unlocked(AID.Freeze) && - JustUsed(AID.Blizzard2, 5) || UmbralStacks == 3) - QueueGCD(AID.Freeze, target, JustUsed(AID.Blizzard2, 5) ? GCDPriority.Step10 : GCDPriority.Step2); + 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, JustUsed(AID.Freeze, 5f) ? GCDPriority.Step10 : GCDPriority.Step1); + QueueGCD(AID.Fire2, target, GCDPriority.Step1); } if (InAstralFire) { @@ -1132,7 +1225,7 @@ private void AOELv58toLv81(Actor? target) //Level 58-81 AOE rotation UmbralHearts == 1) QueueGCD(AID.Flare, target, GCDPriority.Step3); //second cast - if (MP < 2500 && JustUsed(AID.Flare, 5f)) + if (MP is < 2500 and >= 800 && JustUsed(AID.Flare, 5f)) QueueGCD(AID.Flare, target, GCDPriority.Step2); } //Step 3 - swap from AF to UI @@ -1145,25 +1238,29 @@ private void AOELv82toLv99(Actor? target) //Level 82-99 AOE rotation { if (NoStance) { - if (Unlocked(AID.Blizzard2)) + if (Unlocked(AID.HighBlizzard2)) { if (MP >= 10000) - QueueGCD(AID.Blizzard2, target, GCDPriority.NeedB3); + QueueGCD(AID.HighBlizzard2, target, GCDPriority.NeedB3); if (MP < 10000 && Player.InCombat) - QueueGCD(SwiftcastB2, target, GCDPriority.NeedB3); + { + if (ActionReady(AID.Swiftcast)) + QueueGCD(AID.Swiftcast, target, GCDPriority.NeedB3); + else + QueueGCD(AID.HighBlizzard2, target, GCDPriority.NeedB3); + } } } if (InUmbralIce) { //Step 1 - max stacks in UI - if (JustUsed(AID.HighFire2, 5) && - Unlocked(AID.HighBlizzard2) && - UmbralStacks != 3) - QueueGCD(AID.HighBlizzard2, target, GCDPriority.Step2); + if (Unlocked(AID.HighBlizzard2) && + UmbralStacks < 3) + QueueGCD(AID.HighBlizzard2, target, GCDPriority.Step3); //Step 2 - Freeze - if (Unlocked(AID.Freeze) && - JustUsed(AID.HighBlizzard2, 5) || UmbralStacks == 3) - QueueGCD(AID.Freeze, target, JustUsed(AID.Blizzard2, 5) ? GCDPriority.Step10 : GCDPriority.Step1); + 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 && @@ -1182,7 +1279,7 @@ private void AOELv82toLv99(Actor? target) //Level 82-99 AOE rotation if (UmbralHearts == 1) QueueGCD(AID.Flare, target, GCDPriority.Step3); //second cast - if (MP < 2500 && JustUsed(AID.Flare, 5f)) + if (MP is < 2500 and >= 800 && JustUsed(AID.Flare, 5f)) QueueGCD(AID.Flare, target, GCDPriority.Step2); } //Step 3 - swap from AF to UI @@ -1200,20 +1297,24 @@ private void AOELv100(Actor? target) //Level 100 AOE rotation if (MP >= 10000) QueueGCD(AID.HighBlizzard2, target, GCDPriority.NeedB3); if (MP < 10000 && Player.InCombat) - QueueGCD(SwiftcastB2, target, GCDPriority.NeedB3); + { + if (ActionReady(AID.Swiftcast)) + QueueGCD(AID.Swiftcast, target, GCDPriority.NeedB3); + else + QueueGCD(AID.HighBlizzard2, target, GCDPriority.NeedB3); + } } } if (InUmbralIce) { //Step 1 - max stacks in UI - if (JustUsed(AID.HighFire2, 5) && - Unlocked(AID.HighBlizzard2) && - UmbralStacks != 3) - QueueGCD(AID.HighBlizzard2, target, GCDPriority.Step2); + if (Unlocked(AID.HighBlizzard2) && + UmbralStacks < 3) + QueueGCD(AID.HighBlizzard2, target, GCDPriority.Step3); //Step 2 - Freeze - if (Unlocked(AID.Freeze) && - JustUsed(AID.HighBlizzard2, 5) || UmbralStacks == 3) - QueueGCD(AID.Freeze, target, JustUsed(AID.Blizzard2, 5) ? GCDPriority.Step10 : GCDPriority.Step1); + 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 && @@ -1229,7 +1330,7 @@ private void AOELv100(Actor? target) //Level 100 AOE rotation if (UmbralHearts == 3) QueueGCD(AID.Flare, target, GCDPriority.Step3); //second cast - if (MP < 2500 && JustUsed(AID.Flare, 5f)) + if (MP is < 2500 and >= 800 && JustUsed(AID.Flare, 5f)) QueueGCD(AID.Flare, target, GCDPriority.Step2); } //Step 2 - Flare Star From 18d19d230554b97c046ada4dbb19d548c01447bc Mon Sep 17 00:00:00 2001 From: AceAkechi123 Date: Fri, 17 Jan 2025 05:48:01 -0800 Subject: [PATCH 12/18] finishing touches for now --- BossMod/Autorotation/akechi/AkechiBLM.cs | 80 ++++++++++++++---------- 1 file changed, 48 insertions(+), 32 deletions(-) diff --git a/BossMod/Autorotation/akechi/AkechiBLM.cs b/BossMod/Autorotation/akechi/AkechiBLM.cs index bbdc8d6cdb..1509f8490b 100644 --- a/BossMod/Autorotation/akechi/AkechiBLM.cs +++ b/BossMod/Autorotation/akechi/AkechiBLM.cs @@ -1,6 +1,4 @@ -using BossMod.Global.MaskedCarnivale.Stage26.Act2; -using FFXIVClientStructs.FFXIV.Client.Game.Gauge; -using System.Runtime.CompilerServices; +using FFXIVClientStructs.FFXIV.Client.Game.Gauge; using AID = BossMod.BLM.AID; using SID = BossMod.BLM.SID; using TraitID = BossMod.BLM.TraitID; @@ -21,6 +19,7 @@ public enum Track Triplecast, //Triplecast tracking LeyLines, //Ley Lines tracking Potion, //Potion item tracking + TPUS, //Transpose&UmbralSoul combo tracking Transpose, //Transpose tracking Amplifier, //Amplifier tracking Retrace, //Retrace tracking @@ -92,6 +91,13 @@ public enum PotionStrategy AlignWithRaidBuffs, //Align potion usage with raid buffs Immediate //Use potions immediately when available } + public enum TPUSStrategy + { + Allow, //Allow Transpose & Umbral Soul combo whenever available + OOConly, //Only use Transpose & Umbral Soul combo when fully out of combat + Forbid //Forbid Transpose & Umbral Soul combo + } + public enum OffensiveStrategy { Automatic, //Automatically decide when to use off-global offensive abilities @@ -119,7 +125,7 @@ public static RotationModuleDefinition Definition() .AddOption(AOEStrategy.Auto, "Auto", "Automatically decide when to use ST or AOE abilities") .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.Thunder).As("Damage Over Time", "Thunder", uiPriority: 190) + res.Define(Track.Thunder).As("Damage Over Time", "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) @@ -144,7 +150,7 @@ public static RotationModuleDefinition Definition() .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) .AddAssociatedActions(AID.Xenoglossy, AID.Foul); - res.Define(Track.Manafont).As("Manafont", "Manafont", uiPriority: 170) + res.Define(Track.Manafont).As("Manafont", "M.font", uiPriority: 165) .AddOption(ManafontStrategy.Automatic, "Auto", "Automatically decide when to use Manafont", 0, 0, ActionTargets.Self, 30) .AddOption(ManafontStrategy.Force, "Force", "Force the use of Manafont (180s CD), regardless of weaving conditions", 180, 0, ActionTargets.Self, 30, 83) .AddOption(ManafontStrategy.ForceWeave, "ForceWeave", "Force the use of Manafont (180s CD) in any next possible weave slot", 180, 0, ActionTargets.Self, 30, 83) @@ -152,7 +158,7 @@ public static RotationModuleDefinition Definition() .AddOption(ManafontStrategy.ForceWeaveEX, "ForceWeaveEX", "Force the use of Manafont (100s CD) in any next possible weave slot", 100, 0, ActionTargets.Self, 84) .AddOption(ManafontStrategy.Delay, "Delay", "Delay the use of Manafont for strategic reasons", 0, 0, ActionTargets.Self, 30) .AddAssociatedActions(AID.Manafont); - res.Define(Track.Triplecast).As("Triplecast", uiPriority: 160) + 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)", 60, 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) @@ -160,7 +166,7 @@ public static RotationModuleDefinition Definition() .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) .AddAssociatedActions(AID.Triplecast); - res.Define(Track.LeyLines).As("Ley Lines", uiPriority: 150) + res.Define(Track.LeyLines).As("L.Lines", uiPriority: 170) .AddOption(LeyLinesStrategy.Automatic, "Auto", "Automatically decide when to use Ley Lines", 120, 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) @@ -168,15 +174,20 @@ public static RotationModuleDefinition Definition() .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) .AddAssociatedActions(AID.LeyLines); - res.Define(Track.Potion).As("Potion", uiPriority: 180) + res.Define(Track.Potion).As("Potion", uiPriority: 160) .AddOption(PotionStrategy.Manual, "Manual", "Do not use automatically") .AddOption(PotionStrategy.AlignWithRaidBuffs, "AlignWithRaidBuffs", "Align with buffs (to ensure use on 2-minute windows)", 270, 30, ActionTargets.Self) .AddOption(PotionStrategy.Immediate, "Immediate", "Use ASAP, regardless of any buffs", 270, 30, ActionTargets.Self) .AddAssociatedAction(ActionDefinitions.IDPotionInt); + res.Define(Track.TPUS).As("Transpose & Umbral Soul", "TP/US", uiPriority: 160) + .AddOption(TPUSStrategy.Allow, "Allow", "Allow Transpose & Umbral Soul combo when out of combat or no targetable enemy is nearby", 0, 0, ActionTargets.Self, 35) + .AddOption(TPUSStrategy.OOConly, "OOConly", "Only use Transpose & Umbral Soul combo when fully out of combat", 0, 0, ActionTargets.Self, 35) + .AddOption(TPUSStrategy.Forbid, "Forbid", "Forbid Transpose & Umbral Soul combo", 0, 0, ActionTargets.Self, 35) + .AddAssociatedActions(AID.Transpose, AID.UmbralSoul); #endregion #region Offensive Strategies - res.Define(Track.Transpose).As("Transpose", uiPriority: 170) + res.Define(Track.Transpose).As("Transpose", uiPriority: 125) .AddOption(OffensiveStrategy.Automatic, "Auto", "Automatically decide when to use Transpose", 5, 0, ActionTargets.Self, 4) .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) @@ -192,7 +203,7 @@ public static RotationModuleDefinition Definition() .AddOption(OffensiveStrategy.LateWeave, "LateWeave", "Force the use of Amplifier in very next LAST weave slot only", 120, 0, ActionTargets.Self, 86) .AddOption(OffensiveStrategy.Delay, "Delay", "Delay the use of Amplifier", 0, 0, ActionTargets.Self, 86) .AddAssociatedActions(AID.Amplifier); - res.Define(Track.Retrace).As("Retrace", uiPriority: 170) + res.Define(Track.Retrace).As("Retrace", uiPriority: 155) .AddOption(OffensiveStrategy.Automatic, "Auto", "Automatically decide when to use Retrace", 40, 0, ActionTargets.Self, 96) .AddOption(OffensiveStrategy.Force, "Force", "Force the use of Retrace, regardless of weaving conditions", 40, 0, ActionTargets.Self, 96) .AddOption(OffensiveStrategy.AnyWeave, "AnyWeave", "Force the use of Retrace in any next possible weave slot", 40, 0, ActionTargets.Self, 96) @@ -200,7 +211,7 @@ public static RotationModuleDefinition Definition() .AddOption(OffensiveStrategy.LateWeave, "LateWeave", "Force the use of Retrace in very next LAST weave slot only", 40, 0, ActionTargets.Self, 96) .AddOption(OffensiveStrategy.Delay, "Delay", "Delay the use of Retrace", 0, 0, ActionTargets.Self, 96) .AddAssociatedActions(AID.Retrace); - res.Define(Track.BTL).As("Between The Lines", "BTL", uiPriority: 170) + res.Define(Track.BTL).As("Between The Lines", "BTL", uiPriority: 150) .AddOption(OffensiveStrategy.Automatic, "Auto", "Automatically decide when to use Between The Lines", 3, 0, ActionTargets.Self, 62) .AddOption(OffensiveStrategy.Force, "Force", "Force the use of Between The Lines, regardless of weaving conditions", 3, 0, ActionTargets.Self, 62) .AddOption(OffensiveStrategy.AnyWeave, "AnyWeave", "Force the use of Between The Lines in any next possible weave slot", 3, 0, ActionTargets.Self, 62) @@ -415,7 +426,6 @@ float splashPriorityFunc(Actor actor) return false; } } - #endregion public override void Execute(StrategyValues strategy, Actor? primaryTarget, float estimatedAnimLockDelay, bool isMoving) //Executes our actions @@ -481,7 +491,8 @@ public override void Execute(StrategyValues strategy, Actor? primaryTarget, floa var retraceStrat = retrace.As(); //Retrace strategy var btl = strategy.Option(Track.BTL); //Between the Lines track var btlStrat = btl.As(); //Between the Lines strategy - var potion = strategy.Option(Track.Potion).As(); //Potion strategy + var potionStrat = strategy.Option(Track.Potion).As(); //Potion strategy + var tpusStrat = strategy.Option(Track.TPUS).As(); //Transpose/Umbral Soul strategy #endregion #endregion @@ -495,28 +506,33 @@ public override void Execute(StrategyValues strategy, Actor? primaryTarget, floa if (AOEStrategy is AOEStrategy.ForceAOE) BestAOE(TargetChoice(AOE) ?? BestAOETarget ?? primaryTarget); //Out of combat - if (Unlocked(AID.Transpose)) + if (tpusStrat != TPUSStrategy.Forbid) { - if (!Unlocked(AID.UmbralSoul)) + if (Unlocked(AID.Transpose)) { - if (primaryTarget == null && - (!Player.InCombat || Player.InCombat && TargetsInRange() is 0)) + if (!Unlocked(AID.UmbralSoul)) { - if (CD(AID.Transpose) < 0.6f && - (InAstralFire || InUmbralIce)) - QueueOGCD(AID.Transpose, Player, OGCDPriority.Transpose); + if (primaryTarget == null && + (tpusStrat != TPUSStrategy.Allow && (!Player.InCombat || Player.InCombat && TargetsInRange() is 0)) || + (tpusStrat != TPUSStrategy.OOConly && !Player.InCombat)) + { + if (CD(AID.Transpose) < 0.6f && + (InAstralFire || InUmbralIce)) + QueueOGCD(AID.Transpose, Player, OGCDPriority.Transpose); + } } - } - if (Unlocked(AID.UmbralSoul)) - { - if (primaryTarget == null && - (!Player.InCombat || Player.InCombat && TargetsInRange() is 0)) + if (Unlocked(AID.UmbralSoul)) { - if (InAstralFire) - QueueOGCD(AID.Transpose, Player, OGCDPriority.Transpose); - if (InUmbralIce && - (ElementTimer <= 14 || UmbralStacks < 3 || UmbralHearts != MaxUmbralHearts)) - QueueGCD(AID.UmbralSoul, Player, GCDPriority.Standard); + 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); + } } } } @@ -612,8 +628,8 @@ or ManafontStrategy.ForceWeaveEX Player, OGCDPriority.ForcedOGCD); //Potion - if (potion is PotionStrategy.AlignWithRaidBuffs && CD(AID.LeyLines) < 5 || - potion is PotionStrategy.Immediate) + if (potionStrat is PotionStrategy.AlignWithRaidBuffs && CD(AID.LeyLines) < 5 || + potionStrat is PotionStrategy.Immediate) Hints.ActionsToExecute.Push(ActionDefinitions.IDPotionInt, Player, ActionQueue.Priority.VeryHigh + (int)OGCDPriority.Potion, 0, GCD - 0.9f); #endregion } From f846509c27ee11316ce99092542c21d26d056990 Mon Sep 17 00:00:00 2001 From: ace Date: Fri, 17 Jan 2025 14:50:38 -0800 Subject: [PATCH 13/18] Movement options --- BossMod/Autorotation/akechi/AkechiBLM.cs | 107 ++++++++++++++++++++--- 1 file changed, 94 insertions(+), 13 deletions(-) diff --git a/BossMod/Autorotation/akechi/AkechiBLM.cs b/BossMod/Autorotation/akechi/AkechiBLM.cs index 1509f8490b..d6894cfaaf 100644 --- a/BossMod/Autorotation/akechi/AkechiBLM.cs +++ b/BossMod/Autorotation/akechi/AkechiBLM.cs @@ -13,6 +13,7 @@ public sealed class AkechiBLM(RotationModuleManager manager, Actor player) : Rot public enum Track { AOE, //ST&AOE rotations tracking + Movement, //Movement strategy tracking Thunder, //Thunder tracking Polyglot, //Polyglot tracking Manafont, //Manafont tracking @@ -31,6 +32,14 @@ public enum AOEStrategy ForceST, //Force ST rotation only ForceAOE, //Force AOE rotation only } + 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 + 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 + Forbid //Forbid the use of any abilities for movement + } public enum ThunderStrategy { Thunder3, //Force use of Thunder if target has 3s or less remaining on DOT effect @@ -46,11 +55,11 @@ public enum PolyglotStrategy AutoHold1, //Spend 2 Polyglots; holds one for manual usage AutoHold2, //Spend 1 Polyglot; holds two for manual usage AutoHold3, //Holds all Polyglots for as long as possible - XenoSpendAll, //Use Xenoglossy as optimal spender, regardless of targets nearby; spends all Polyglots + XenoSpendAll, //Use Xenoglossy as optimal spender, regardless of targets nearby; spends all Polyglots XenoHold1, //Use Xenoglossy as optimal spender, regardless of targets nearby; holds one Polyglot for manual usage XenoHold2, //Use Xenoglossy as optimal spender, regardless of targets nearby; holds two Polyglots for manual usage XenoHold3, //Holds all Polyglots for as long as possible - FoulSpendAll, //Use Foul as optimal spender, regardless of targets nearby + FoulSpendAll, //Use Foul as optimal spender, regardless of targets nearby FoulHold1, //Use Foul as optimal spender, regardless of targets nearby; holds one Polyglot for manual usage FoulHold2, //Use Foul as optimal spender, regardless of targets nearby; holds two Polyglots for manual usage FoulHold3, //Holds all Polyglots for as long as possible @@ -97,7 +106,6 @@ public enum TPUSStrategy OOConly, //Only use Transpose & Umbral Soul combo when fully out of combat Forbid //Forbid Transpose & Umbral Soul combo } - public enum OffensiveStrategy { Automatic, //Automatically decide when to use off-global offensive abilities @@ -125,7 +133,13 @@ public static RotationModuleDefinition Definition() .AddOption(AOEStrategy.Auto, "Auto", "Automatically decide when to use ST or AOE abilities") .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.Thunder).As("Damage Over Time", "DOT", uiPriority: 190) + res.Define(Track.Movement).As("Movement", uiPriority: 195) + .AddOption(MovementStrategy.Allow, "Allow", "Allow the use of all appropriate abilities for movement", 0, 0, ActionTargets.Self, 35) + .AddOption(MovementStrategy.OnlyGCDs, "OnlyGCDs", "Only use instant cast GCDs for movement; Polyglots->Firestarter->Thunder->Scathe if nothing left", 0, 0, ActionTargets.Self, 35) + .AddOption(MovementStrategy.OnlyOGCDs, "OnlyOGCDs", "Only use OGCDs for movement; Swiftcast->Triplecast", 0, 0, ActionTargets.Self, 35) + .AddOption(MovementStrategy.OnlyScathe, "OnlyScathe", "Only use Scathe for movement", 0, 0, ActionTargets.Self, 35) + .AddOption(MovementStrategy.Forbid, "Forbid", "Forbid the use of any abilities for movement", 0, 0, ActionTargets.Self, 35); + 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) @@ -227,6 +241,7 @@ public static RotationModuleDefinition Definition() #endregion #region Priorities + //TODO: Fix this shit, looks crazy public enum GCDPriority //priorities for GCDs (higher number = higher priority) { None = 0, //default @@ -252,7 +267,9 @@ public static RotationModuleDefinition Definition() NeedF3P = 625, //Need to use Fire III proc NeedDespair = 640, //Need to use Despair NeedPolyglot = 650, //Need to use Polyglots - Moving = 700, //Moving + Moving3 = 700, //Moving (3rd priority) + Moving2 = 710, //Moving (2nd priority) + Moving1 = 720, //Moving (1st priority) ForcedGCD = 900, //Forced GCDs } public enum OGCDPriority //priorities for oGCDs (higher number = higher priority) @@ -475,6 +492,7 @@ public override void Execute(StrategyValues strategy, Actor? primaryTarget, floa #region Strategy Definitions var AOE = strategy.Option(Track.AOE); //AOE track var AOEStrategy = AOE.As(); //AOE strategy + var movementStrat = strategy.Option(Track.Movement).As(); var thunder = strategy.Option(Track.Thunder); //Thunder track var thunderStrat = thunder.As(); //Thunder strategy var polyglot = strategy.Option(Track.Polyglot); //Polyglot track @@ -505,6 +523,64 @@ public override void Execute(StrategyValues strategy, Actor? primaryTarget, floa BestST(TargetChoice(AOE) ?? primaryTarget); if (AOEStrategy is AOEStrategy.ForceAOE) BestAOE(TargetChoice(AOE) ?? BestAOETarget ?? primaryTarget); + //Movement + if (isMoving) + { + if (movementStrat is MovementStrategy.Allow) + { + //GCDs + if (!PlayerHasEffect(SID.Swiftcast, 10) || + !PlayerHasEffect(SID.Triplecast, 15)) + QueueGCD( + Polyglots > 0 ? BestPolyglot + : PlayerHasEffect(SID.Firestarter, 30) ? AID.Fire3 + : hasThunderhead ? BestThunder + : AID.Scathe, + Polyglots > 0 ? TargetChoice(polyglot) ?? BestAOETarget ?? primaryTarget + : PlayerHasEffect(SID.Firestarter, 30) ? TargetChoice(AOE) ?? primaryTarget + : hasThunderhead ? TargetChoice(thunder) ?? BestAOETarget ?? primaryTarget + : primaryTarget, + GCDPriority.Moving1); + //OGCDs + if (ActionReady(AID.Swiftcast) && + !PlayerHasEffect(SID.Triplecast, 15)) + QueueOGCD(AID.Swiftcast, Player, GCDPriority.Moving2); + if (Unlocked(AID.Triplecast) && + CD(AID.Triplecast) <= 60 && + !PlayerHasEffect(SID.Triplecast, 15) && + !PlayerHasEffect(SID.Swiftcast, 10)) + QueueOGCD(AID.Triplecast, Player, GCDPriority.Moving3); + } + if (movementStrat is MovementStrategy.OnlyGCDs) + { + //GCDs + QueueGCD( + Polyglots > 0 ? BestPolyglot + : PlayerHasEffect(SID.Firestarter, 30) ? AID.Fire3 + : hasThunderhead ? BestThunder + : AID.Scathe, + Polyglots > 0 ? TargetChoice(polyglot) ?? BestAOETarget ?? primaryTarget + : PlayerHasEffect(SID.Firestarter, 30) ? TargetChoice(AOE) ?? primaryTarget + : hasThunderhead ? TargetChoice(thunder) ?? BestAOETarget ?? primaryTarget + : primaryTarget, + GCDPriority.Moving1); + } + if (movementStrat is MovementStrategy.OnlyOGCDs) + { + //OGCDs + if (ActionReady(AID.Swiftcast) && + !PlayerHasEffect(SID.Triplecast, 15)) + QueueOGCD(AID.Swiftcast, Player, GCDPriority.Moving2); + if (canTC && + !PlayerHasEffect(SID.Swiftcast, 10)) + QueueOGCD(AID.Triplecast, Player, GCDPriority.Moving3); + } + if (movementStrat is MovementStrategy.OnlyScathe) + { + QueueGCD(AID.Scathe, primaryTarget, GCDPriority.Moving1); + } + } + //Out of combat if (tpusStrat != TPUSStrategy.Forbid) { @@ -513,8 +589,8 @@ public override void Execute(StrategyValues strategy, Actor? primaryTarget, floa if (!Unlocked(AID.UmbralSoul)) { if (primaryTarget == null && - (tpusStrat != TPUSStrategy.Allow && (!Player.InCombat || Player.InCombat && TargetsInRange() is 0)) || - (tpusStrat != TPUSStrategy.OOConly && !Player.InCombat)) + (tpusStrat == TPUSStrategy.Allow && (!Player.InCombat || Player.InCombat && TargetsInRange() is 0)) || + (tpusStrat == TPUSStrategy.OOConly && !Player.InCombat)) { if (CD(AID.Transpose) < 0.6f && (InAstralFire || InUmbralIce)) @@ -524,8 +600,8 @@ public override void Execute(StrategyValues strategy, Actor? primaryTarget, floa if (Unlocked(AID.UmbralSoul)) { if (primaryTarget == null && - (tpusStrat != TPUSStrategy.Allow && (!Player.InCombat || Player.InCombat && TargetsInRange() is 0)) || - (tpusStrat != TPUSStrategy.OOConly && !Player.InCombat)) + (tpusStrat == TPUSStrategy.Allow && (!Player.InCombat || Player.InCombat && TargetsInRange() is 0)) || + (tpusStrat == TPUSStrategy.OOConly && !Player.InCombat)) { if (InAstralFire) QueueOGCD(AID.Transpose, Player, OGCDPriority.Transpose); @@ -560,7 +636,8 @@ public override void Execute(StrategyValues strategy, Actor? primaryTarget, floa { if (polyglotStrat is PolyglotStrategy.AutoSpendAll or PolyglotStrategy.AutoHold1 - or PolyglotStrategy.AutoHold2) + or PolyglotStrategy.AutoHold2 + or PolyglotStrategy.AutoHold3) QueueGCD(BestPolyglot, TargetChoice(polyglot) ?? BestAOETarget ?? primaryTarget, polyglotStrat is PolyglotStrategy.ForceXeno ? GCDPriority.ForcedGCD @@ -568,13 +645,17 @@ polyglotStrat is PolyglotStrategy.ForceXeno ? GCDPriority.ForcedGCD : GCDPriority.Paradox); if (polyglotStrat is PolyglotStrategy.XenoSpendAll or PolyglotStrategy.XenoHold1 - or PolyglotStrategy.XenoHold2) + or PolyglotStrategy.XenoHold2 + or PolyglotStrategy.XenoHold3) QueueGCD(BestXenoglossy, TargetChoice(polyglot) ?? primaryTarget, polyglotStrat is PolyglotStrategy.ForceXeno ? GCDPriority.ForcedGCD : Polyglots == MaxPolyglots && EnochianTimer < 5000 ? GCDPriority.NeedPolyglot : GCDPriority.Paradox); - if (polyglotStrat is PolyglotStrategy.FoulSpendAll or PolyglotStrategy.FoulHold1 or PolyglotStrategy.FoulHold2) + 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 } //LeyLines @@ -1436,7 +1517,7 @@ private void BestAOE(Actor? target) //AOE rotation based on level => Player.InCombat && target != null && Polyglots == 3 && //if max Polyglots - EnochianTimer <= 5000, //Enochian is 5s away from adding a Polyglot + EnochianTimer <= 10000f, //Enochian is 5s away from adding a Polyglot _ => false }; private bool ShouldUsePolyglot(Actor? target, PolyglotStrategy strategy) => strategy switch From 9944f4834cb903446efa7f1dbb06c09264d412ed Mon Sep 17 00:00:00 2001 From: ace Date: Fri, 17 Jan 2025 15:07:14 -0800 Subject: [PATCH 14/18] cleanup --- BossMod/Autorotation/akechi/AkechiBLM.cs | 72 ++++++++++++------------ 1 file changed, 35 insertions(+), 37 deletions(-) diff --git a/BossMod/Autorotation/akechi/AkechiBLM.cs b/BossMod/Autorotation/akechi/AkechiBLM.cs index d6894cfaaf..0cbcb2974b 100644 --- a/BossMod/Autorotation/akechi/AkechiBLM.cs +++ b/BossMod/Autorotation/akechi/AkechiBLM.cs @@ -102,9 +102,9 @@ public enum PotionStrategy } public enum TPUSStrategy { - Allow, //Allow Transpose & Umbral Soul combo whenever available - OOConly, //Only use Transpose & Umbral Soul combo when fully out of combat - Forbid //Forbid Transpose & Umbral Soul combo + Allow, //Allow Transpose & Umbral Soul combo whenever available + OOConly, //Only use Transpose & Umbral Soul combo when fully out of combat + Forbid //Forbid Transpose & Umbral Soul combo } public enum OffensiveStrategy { @@ -134,11 +134,11 @@ public static RotationModuleDefinition Definition() .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) - .AddOption(MovementStrategy.Allow, "Allow", "Allow the use of all appropriate abilities for movement", 0, 0, ActionTargets.Self, 35) - .AddOption(MovementStrategy.OnlyGCDs, "OnlyGCDs", "Only use instant cast GCDs for movement; Polyglots->Firestarter->Thunder->Scathe if nothing left", 0, 0, ActionTargets.Self, 35) - .AddOption(MovementStrategy.OnlyOGCDs, "OnlyOGCDs", "Only use OGCDs for movement; Swiftcast->Triplecast", 0, 0, ActionTargets.Self, 35) - .AddOption(MovementStrategy.OnlyScathe, "OnlyScathe", "Only use Scathe for movement", 0, 0, ActionTargets.Self, 35) - .AddOption(MovementStrategy.Forbid, "Forbid", "Forbid the use of any abilities for movement", 0, 0, ActionTargets.Self, 35); + .AddOption(MovementStrategy.Allow, "Allow", "Allow the use of all appropriate abilities for movement") + .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") + .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) @@ -173,7 +173,7 @@ public static RotationModuleDefinition Definition() .AddOption(ManafontStrategy.Delay, "Delay", "Delay the use of Manafont for strategic reasons", 0, 0, ActionTargets.Self, 30) .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)", 60, 0, ActionTargets.Self, 66) + .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) @@ -181,7 +181,7 @@ public static RotationModuleDefinition Definition() .AddOption(TriplecastStrategy.Delay, "Delay", "Delay the use of Triplecast", 60, 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", 120, 0, ActionTargets.Self, 52) + .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) @@ -202,7 +202,7 @@ public static RotationModuleDefinition Definition() #region Offensive Strategies res.Define(Track.Transpose).As("Transpose", uiPriority: 125) - .AddOption(OffensiveStrategy.Automatic, "Auto", "Automatically decide when to use Transpose", 5, 0, ActionTargets.Self, 4) + .AddOption(OffensiveStrategy.Automatic, "Auto", "Automatically decide when to use Transpose", 0, 0, ActionTargets.Self, 4) .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) @@ -210,7 +210,7 @@ public static RotationModuleDefinition Definition() .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) - .AddOption(OffensiveStrategy.Automatic, "Auto", "Automatically decide when to use Amplifier", 120, 0, ActionTargets.Self, 86) + .AddOption(OffensiveStrategy.Automatic, "Auto", "Automatically decide when to use Amplifier", 0, 0, ActionTargets.Self, 86) .AddOption(OffensiveStrategy.Force, "Force", "Force the use of Amplifier, regardless of weaving conditions", 120, 0, ActionTargets.Self, 86) .AddOption(OffensiveStrategy.AnyWeave, "AnyWeave", "Force the use of Amplifier in any next possible weave slot", 120, 0, ActionTargets.Self, 86) .AddOption(OffensiveStrategy.EarlyWeave, "EarlyWeave", "Force the use of Amplifier in very next FIRST weave slot only", 120, 0, ActionTargets.Self, 86) @@ -218,7 +218,7 @@ public static RotationModuleDefinition Definition() .AddOption(OffensiveStrategy.Delay, "Delay", "Delay the use of Amplifier", 0, 0, ActionTargets.Self, 86) .AddAssociatedActions(AID.Amplifier); res.Define(Track.Retrace).As("Retrace", uiPriority: 155) - .AddOption(OffensiveStrategy.Automatic, "Auto", "Automatically decide when to use Retrace", 40, 0, ActionTargets.Self, 96) + .AddOption(OffensiveStrategy.Automatic, "Auto", "Automatically decide when to use Retrace", 0, 0, ActionTargets.Self, 96) .AddOption(OffensiveStrategy.Force, "Force", "Force the use of Retrace, regardless of weaving conditions", 40, 0, ActionTargets.Self, 96) .AddOption(OffensiveStrategy.AnyWeave, "AnyWeave", "Force the use of Retrace in any next possible weave slot", 40, 0, ActionTargets.Self, 96) .AddOption(OffensiveStrategy.EarlyWeave, "EarlyWeave", "Force the use of Retrace in very next FIRST weave slot only", 40, 0, ActionTargets.Self, 96) @@ -226,7 +226,7 @@ public static RotationModuleDefinition Definition() .AddOption(OffensiveStrategy.Delay, "Delay", "Delay the use of Retrace", 0, 0, ActionTargets.Self, 96) .AddAssociatedActions(AID.Retrace); res.Define(Track.BTL).As("Between The Lines", "BTL", uiPriority: 150) - .AddOption(OffensiveStrategy.Automatic, "Auto", "Automatically decide when to use Between The Lines", 3, 0, ActionTargets.Self, 62) + .AddOption(OffensiveStrategy.Automatic, "Auto", "Automatically decide when to use Between The Lines", 0, 0, ActionTargets.Self, 62) .AddOption(OffensiveStrategy.Force, "Force", "Force the use of Between The Lines, regardless of weaving conditions", 3, 0, ActionTargets.Self, 62) .AddOption(OffensiveStrategy.AnyWeave, "AnyWeave", "Force the use of Between The Lines in any next possible weave slot", 3, 0, ActionTargets.Self, 62) .AddOption(OffensiveStrategy.EarlyWeave, "EarlyWeave", "Force the use of Between The Lines in very next FIRST weave slot only", 3, 0, ActionTargets.Self, 62) @@ -241,7 +241,7 @@ public static RotationModuleDefinition Definition() #endregion #region Priorities - //TODO: Fix this shit, looks crazy + //TODO: Fix this shit later, looks crazy public enum GCDPriority //priorities for GCDs (higher number = higher priority) { None = 0, //default @@ -332,15 +332,8 @@ private AID BestXenoglossy public bool canWeaveIn; //Can weave in oGCDs public bool canWeaveEarly; //Can early weave oGCDs public bool canWeaveLate; //Can late weave oGCDs - public bool quarterWeave; //Can last second weave oGCDs - public float PotionLeft; //Time left on potion buff (30s base) - public float RaidBuffsLeft; //Time left on raid-wide buffs (typically 20s-22s) - public float RaidBuffsIn; //Time until raid-wide buffs are applied again (typically 20s-22s) public float SpS; //Current GCD length, adjusted by spell speed/haste (2.5s baseline) - public float BurstWindowLeft; //Time left in current burst window (typically 20s-22s) - public float BurstWindowIn; //Time until next burst window (typically 20s-22s) public AID NextGCD; //Next global cooldown action to be used - public float AnimLockDelay; //Estimated animation lock delay #endregion #region Module Helpers @@ -349,14 +342,7 @@ private AID BestXenoglossy private float CD(AID aid) => World.Client.Cooldowns[ActionDefinitions.Instance.Spell(aid)!.MainCooldownGroup].Remaining; //Get remaining cooldown time for the specified action 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 int TargetsInRange() => Hints.NumPriorityTargetsInAOECircle(Player.Position, 25); //Returns the number of targets hit by AOE within a 25-yalm radius around the player private bool PlayerHasEffect(SID sid, float duration) => SelfStatusLeft(sid, duration) > GCD; //Checks if Status effect is on self - private bool JustUsed(AID aid, float variance) - { - var used = Manager.LastCast.Data?.IsSpell(aid) == true; - var within = (World.CurrentTime - Manager.LastCast.Time).TotalSeconds <= variance; - return used && within; - } public float GetActualCastTime(AID aid) => ActionDefinitions.Instance.Spell(aid)!.CastTime * SpS / 2.5f; public float GetCastTime(AID aid) { @@ -376,7 +362,15 @@ public float GetCastTime(AID aid) castTime *= 0.5f; return castTime; } + private bool JustUsed(AID aid, float variance) + { + var used = Manager.LastCast.Data?.IsSpell(aid) == true; + var within = (World.CurrentTime - Manager.LastCast.Time).TotalSeconds <= variance; + return used && within; + } + #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() { @@ -445,6 +439,8 @@ float splashPriorityFunc(Actor actor) } #endregion + #endregion + public override void Execute(StrategyValues strategy, Actor? primaryTarget, float estimatedAnimLockDelay, bool isMoving) //Executes our actions { #region Variables @@ -473,7 +469,7 @@ public override void Execute(StrategyValues strategy, Actor? primaryTarget, floa canRetrace = ActionReady(AID.Retrace) && PlayerHasEffect(SID.LeyLines, 30); //Can use Retrace canBTL = ActionReady(AID.BetweenTheLines) && PlayerHasEffect(SID.LeyLines, 30); //Can use Between the Lines hasThunderhead = PlayerHasEffect(SID.Thunderhead, 30); //Has Thunderhead buff - ThunderLeft = Utils.MaxAll( + ThunderLeft = Utils.MaxAll( //Time left on DOT effect StatusDetails(primaryTarget, SID.Thunder, Player.InstanceID, 24).Left, StatusDetails(primaryTarget, SID.ThunderII, Player.InstanceID, 18).Left, StatusDetails(primaryTarget, SID.ThunderIII, Player.InstanceID, 27).Left, @@ -486,8 +482,6 @@ 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 - AnimLockDelay = estimatedAnimLockDelay; //Estimated animation lock delay - PotionLeft = PotionStatusLeft(); //Remaining time for potion buff (30s) #region Strategy Definitions var AOE = strategy.Option(Track.AOE); //AOE track @@ -516,14 +510,17 @@ public override void Execute(StrategyValues strategy, Actor? primaryTarget, floa #endregion #region Rotation Execution - //Rotations + + #region ST / AOE 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); - //Movement + #endregion + + #region Movement if (isMoving) { if (movementStrat is MovementStrategy.Allow) @@ -580,8 +577,9 @@ public override void Execute(StrategyValues strategy, Actor? primaryTarget, floa QueueGCD(AID.Scathe, primaryTarget, GCDPriority.Moving1); } } + #endregion - //Out of combat + #region Out of combat if (tpusStrat != TPUSStrategy.Forbid) { if (Unlocked(AID.Transpose)) @@ -612,6 +610,8 @@ public override void Execute(StrategyValues strategy, Actor? primaryTarget, floa } } } + #endregion + //Thunder if (ShouldUseThunder(primaryTarget, thunderStrat)) //if Thunder should be used based on strategy { @@ -1486,7 +1486,6 @@ private void BestAOE(Actor? target) //AOE rotation based on level ThunderStrategy.Delay => false, _ => false }; - private bool ShouldSpendPolyglot(Actor? target, PolyglotStrategy strategy) => strategy switch { PolyglotStrategy.AutoSpendAll @@ -1590,7 +1589,6 @@ private void BestAOE(Actor? target) //AOE rotation based on level _ => false }; - private bool ShouldUseManafont(Actor? target, ManafontStrategy strategy) => strategy switch { ManafontStrategy.Automatic From f38415ec77f0173473f0004aed4dc3ab0e83bcc0 Mon Sep 17 00:00:00 2001 From: ace Date: Fri, 17 Jan 2025 15:12:21 -0800 Subject: [PATCH 15/18] . --- BossMod/Autorotation/akechi/AkechiBLM.cs | 24 +++++++++++++----------- 1 file changed, 13 insertions(+), 11 deletions(-) diff --git a/BossMod/Autorotation/akechi/AkechiBLM.cs b/BossMod/Autorotation/akechi/AkechiBLM.cs index 0cbcb2974b..f0337ce237 100644 --- a/BossMod/Autorotation/akechi/AkechiBLM.cs +++ b/BossMod/Autorotation/akechi/AkechiBLM.cs @@ -529,7 +529,7 @@ public override void Execute(StrategyValues strategy, Actor? primaryTarget, floa if (!PlayerHasEffect(SID.Swiftcast, 10) || !PlayerHasEffect(SID.Triplecast, 15)) QueueGCD( - Polyglots > 0 ? BestPolyglot + Unlocked(TraitID.EnhancedPolyglot) && Polyglots > 0 ? BestPolyglot : PlayerHasEffect(SID.Firestarter, 30) ? AID.Fire3 : hasThunderhead ? BestThunder : AID.Scathe, @@ -551,16 +551,18 @@ public override void Execute(StrategyValues strategy, Actor? primaryTarget, floa if (movementStrat is MovementStrategy.OnlyGCDs) { //GCDs - QueueGCD( - Polyglots > 0 ? BestPolyglot - : PlayerHasEffect(SID.Firestarter, 30) ? AID.Fire3 - : hasThunderhead ? BestThunder - : AID.Scathe, - Polyglots > 0 ? TargetChoice(polyglot) ?? BestAOETarget ?? primaryTarget - : PlayerHasEffect(SID.Firestarter, 30) ? TargetChoice(AOE) ?? primaryTarget - : hasThunderhead ? TargetChoice(thunder) ?? BestAOETarget ?? primaryTarget - : primaryTarget, - GCDPriority.Moving1); + if (!PlayerHasEffect(SID.Swiftcast, 10) || + !PlayerHasEffect(SID.Triplecast, 15)) + QueueGCD( + Unlocked(TraitID.EnhancedPolyglot) && Polyglots > 0 ? BestPolyglot + : PlayerHasEffect(SID.Firestarter, 30) ? AID.Fire3 + : hasThunderhead ? BestThunder + : AID.Scathe, + Polyglots > 0 ? TargetChoice(polyglot) ?? BestAOETarget ?? primaryTarget + : PlayerHasEffect(SID.Firestarter, 30) ? TargetChoice(AOE) ?? primaryTarget + : hasThunderhead ? TargetChoice(thunder) ?? BestAOETarget ?? primaryTarget + : primaryTarget, + GCDPriority.Moving1); } if (movementStrat is MovementStrategy.OnlyOGCDs) { From d78b1ee1df1d6f00af9867f1f2ddd0ba995fd786 Mon Sep 17 00:00:00 2001 From: ace Date: Fri, 17 Jan 2025 15:30:51 -0800 Subject: [PATCH 16/18] ok fr this time --- BossMod/Autorotation/akechi/AkechiBLM.cs | 30 +++++++++++++----------- 1 file changed, 16 insertions(+), 14 deletions(-) diff --git a/BossMod/Autorotation/akechi/AkechiBLM.cs b/BossMod/Autorotation/akechi/AkechiBLM.cs index f0337ce237..fdda11320e 100644 --- a/BossMod/Autorotation/akechi/AkechiBLM.cs +++ b/BossMod/Autorotation/akechi/AkechiBLM.cs @@ -521,7 +521,9 @@ public override void Execute(StrategyValues strategy, Actor? primaryTarget, floa #endregion #region Movement - if (isMoving) + if (Player.InCombat && + primaryTarget != null && + isMoving) { if (movementStrat is MovementStrategy.Allow) { @@ -1314,17 +1316,17 @@ private void AOELv58toLv81(Actor? target) //Level 58-81 AOE rotation if (InAstralFire) { //Step 1 - spam Fire 2 - if (MP > 7000) + if (UmbralHearts > 1) QueueGCD(AID.Fire2, target, GCDPriority.Step4); //Step 2 - Flare if (Unlocked(AID.Flare)) { //first cast - if (MP <= 7000 && - UmbralHearts == 1) + if (UmbralHearts == 1) QueueGCD(AID.Flare, target, GCDPriority.Step3); //second cast - if (MP is < 2500 and >= 800 && JustUsed(AID.Flare, 5f)) + if (UmbralHearts == 0 && + MP >= 800) QueueGCD(AID.Flare, target, GCDPriority.Step2); } //Step 3 - swap from AF to UI @@ -1517,7 +1519,7 @@ private void BestAOE(Actor? target) //AOE rotation based on level PolyglotStrategy.AutoHold3 => Player.InCombat && target != null && - Polyglots == 3 && //if max Polyglots + Polyglots == MaxPolyglots && //if max Polyglots EnochianTimer <= 10000f, //Enochian is 5s away from adding a Polyglot _ => false }; @@ -1547,10 +1549,10 @@ private void BestAOE(Actor? target) //AOE rotation based on level target != null && canLL && canWeaveIn, - LeyLinesStrategy.Force => canLL, - LeyLinesStrategy.Force1 => canLL && CD(AID.LeyLines) < (SpS * 2), - LeyLinesStrategy.ForceWeave => canLL && canWeaveIn, - LeyLinesStrategy.ForceWeave1 => canLL && canWeaveIn && CD(AID.LeyLines) < (SpS * 2), + LeyLinesStrategy.Force => Player.InCombat && canLL, + LeyLinesStrategy.Force1 => Player.InCombat && canLL && CD(AID.LeyLines) < (SpS * 2), + LeyLinesStrategy.ForceWeave => Player.InCombat && canLL && canWeaveIn, + LeyLinesStrategy.ForceWeave1 => Player.InCombat && canLL && canWeaveIn && CD(AID.LeyLines) < (SpS * 2), LeyLinesStrategy.Delay => false, _ => false }; @@ -1615,10 +1617,10 @@ private void BestAOE(Actor? target) //AOE rotation based on level canWeaveIn && InAstralFire && PlayerHasEffect(SID.LeyLines, 30), - TriplecastStrategy.Force => canTC, - TriplecastStrategy.Force1 => canTC && CD(AID.Triplecast) < (SpS * 2), - TriplecastStrategy.ForceWeave => canTC && canWeaveIn, - TriplecastStrategy.ForceWeave1 => canTC && canWeaveIn && CD(AID.Triplecast) < (SpS * 2), + TriplecastStrategy.Force => Player.InCombat && canTC, + TriplecastStrategy.Force1 => Player.InCombat && canTC && CD(AID.Triplecast) < (SpS * 2), + TriplecastStrategy.ForceWeave => Player.InCombat && canTC && canWeaveIn, + TriplecastStrategy.ForceWeave1 => Player.InCombat && canTC && canWeaveIn && CD(AID.Triplecast) < (SpS * 2), TriplecastStrategy.Delay => false, _ => false }; From 4e342daff308809f1fbaa49f756cc39b9ba0de4b Mon Sep 17 00:00:00 2001 From: ace Date: Fri, 17 Jan 2025 15:38:29 -0800 Subject: [PATCH 17/18] >_> --- BossMod/Autorotation/akechi/AkechiBLM.cs | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/BossMod/Autorotation/akechi/AkechiBLM.cs b/BossMod/Autorotation/akechi/AkechiBLM.cs index fdda11320e..ba4a9eb01c 100644 --- a/BossMod/Autorotation/akechi/AkechiBLM.cs +++ b/BossMod/Autorotation/akechi/AkechiBLM.cs @@ -1380,7 +1380,8 @@ private void AOELv82toLv99(Actor? target) //Level 82-99 AOE rotation if (UmbralHearts == 1) QueueGCD(AID.Flare, target, GCDPriority.Step3); //second cast - if (MP is < 2500 and >= 800 && JustUsed(AID.Flare, 5f)) + if (UmbralHearts == 0 && + MP >= 800) QueueGCD(AID.Flare, target, GCDPriority.Step2); } //Step 3 - swap from AF to UI @@ -1428,10 +1429,11 @@ private void AOELv100(Actor? target) //Level 100 AOE rotation if (Unlocked(AID.Flare)) { //first cast - if (UmbralHearts == 3) + if (UmbralHearts == 1) QueueGCD(AID.Flare, target, GCDPriority.Step3); //second cast - if (MP is < 2500 and >= 800 && JustUsed(AID.Flare, 5f)) + if (UmbralHearts == 0 && + MP >= 800) QueueGCD(AID.Flare, target, GCDPriority.Step2); } //Step 2 - Flare Star From ecc268f9256c631d17bd319ae80720f90edb6b1c Mon Sep 17 00:00:00 2001 From: AceAkechi123 Date: Fri, 17 Jan 2025 23:23:25 -0800 Subject: [PATCH 18/18] Casting while Moving options --- BossMod/Autorotation/akechi/AkechiBLM.cs | 36 +++++++++++++++++++----- 1 file changed, 29 insertions(+), 7 deletions(-) diff --git a/BossMod/Autorotation/akechi/AkechiBLM.cs b/BossMod/Autorotation/akechi/AkechiBLM.cs index ba4a9eb01c..d677562dea 100644 --- a/BossMod/Autorotation/akechi/AkechiBLM.cs +++ b/BossMod/Autorotation/akechi/AkechiBLM.cs @@ -21,6 +21,7 @@ public enum Track LeyLines, //Ley Lines tracking Potion, //Potion item tracking TPUS, //Transpose&UmbralSoul combo tracking + Casting, //Cast while Moving option tracking Transpose, //Transpose tracking Amplifier, //Amplifier tracking Retrace, //Retrace tracking @@ -106,6 +107,11 @@ public enum TPUSStrategy OOConly, //Only use Transpose & Umbral Soul combo when fully out of combat Forbid //Forbid Transpose & Umbral Soul combo } + public enum CastingOption + { + Allow, //Allow casting while moving + Forbid //Forbid casting while moving + } public enum OffensiveStrategy { Automatic, //Automatically decide when to use off-global offensive abilities @@ -198,6 +204,9 @@ public static RotationModuleDefinition Definition() .AddOption(TPUSStrategy.OOConly, "OOConly", "Only use Transpose & Umbral Soul combo when fully out of combat", 0, 0, ActionTargets.Self, 35) .AddOption(TPUSStrategy.Forbid, "Forbid", "Forbid Transpose & Umbral Soul combo", 0, 0, ActionTargets.Self, 35) .AddAssociatedActions(AID.Transpose, AID.UmbralSoul); + res.Define(Track.Casting).As("Casting", uiPriority: 155) + .AddOption(CastingOption.Allow, "Allow", "Allow casting while Casting") + .AddOption(CastingOption.Forbid, "Forbid", "Forbid casting while moving"); #endregion #region Offensive Strategies @@ -271,6 +280,7 @@ public static RotationModuleDefinition Definition() Moving2 = 710, //Moving (2nd priority) Moving1 = 720, //Moving (1st priority) ForcedGCD = 900, //Forced GCDs + BlockAll = 2000, //Block all GCDs } public enum OGCDPriority //priorities for oGCDs (higher number = higher priority) { @@ -505,6 +515,7 @@ public override void Execute(StrategyValues strategy, Actor? primaryTarget, floa var btlStrat = btl.As(); //Between the Lines strategy 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 #endregion #endregion @@ -512,12 +523,22 @@ public override void Execute(StrategyValues strategy, Actor? primaryTarget, floa #region Rotation Execution #region ST / AOE - 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); + if (movingOption is CastingOption.Allow || + movingOption is CastingOption.Forbid && + (!isMoving || //if not moving + (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 + 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); + } #endregion #region Movement @@ -578,7 +599,8 @@ public override void Execute(StrategyValues strategy, Actor? primaryTarget, floa } if (movementStrat is MovementStrategy.OnlyScathe) { - QueueGCD(AID.Scathe, primaryTarget, GCDPriority.Moving1); + if (MP >= 800) + QueueGCD(AID.Scathe, primaryTarget, GCDPriority.Moving1); } } #endregion