From 9b3f5aa855bed7b3a6e34cce33982ee800b9327f Mon Sep 17 00:00:00 2001 From: Akechi-kun <167795370+Akechi-kun@users.noreply.github.com> Date: Fri, 13 Dec 2024 17:26:32 -0800 Subject: [PATCH 01/29] tweaks --- BossMod/ActionQueue/Melee/DRG.cs | 1 + BossMod/Autorotation/akechi/AkechiDRG.cs | 146 +++++++++++++++++------ 2 files changed, 111 insertions(+), 36 deletions(-) diff --git a/BossMod/ActionQueue/Melee/DRG.cs b/BossMod/ActionQueue/Melee/DRG.cs index 20f87c0ca6..9d857284d7 100644 --- a/BossMod/ActionQueue/Melee/DRG.cs +++ b/BossMod/ActionQueue/Melee/DRG.cs @@ -91,6 +91,7 @@ public enum SID : uint NastrondReady = 3844, // applied by Geirskogul to self DragonsFlight = 3845, // applied by Dragonfire Dive to self StarcrossReady = 3846, // applied by Stardiver to self + EnhancedPiercingTalon = 1870, // applied by Elusive Jump to self //Shared Feint = ClassShared.SID.Feint, // applied by Feint to target diff --git a/BossMod/Autorotation/akechi/AkechiDRG.cs b/BossMod/Autorotation/akechi/AkechiDRG.cs index a372dd062d..b6ac775b5c 100644 --- a/BossMod/Autorotation/akechi/AkechiDRG.cs +++ b/BossMod/Autorotation/akechi/AkechiDRG.cs @@ -14,6 +14,7 @@ public sealed class AkechiDRG(RotationModuleManager manager, Actor player) : Rot public enum Track { AOE, //Area of Effect actions + Spears, //Spear actions Burst, //Burst actions Potion, //Potion usage LifeSurge, //Life Surge ability @@ -44,6 +45,12 @@ public enum AOEStrategy ForceAOE //Force Area of Effect abilities } + public enum SpearTargetingStrategy + { + AutoTargetHitPrimary, //Auto-target spear to hit primary target + AutoTargetHitMost, //Auto-target spear to hit most targets + } + //Burst strategy options public enum BurstStrategy { @@ -66,7 +73,9 @@ public enum SurgeStrategy { Automatic, //Automatically use Life Surge Force, //Force use of Life Surge - ForceEX, //Force use of Life Surge (EX) + ForceWeave, //Force use of Life Surge inside the next possible weave window + ForceNextOpti, //Force use of Life Surge in the next possible optimal window + ForceNextOptiWeave, //Force use of Life Surge optimally inside the next possible weave window Delay //Delay use of Life Surge } @@ -77,6 +86,7 @@ public enum JumpStrategy Force, //Force use of Jump ForceEX, //Force use of Jump EX) ForceEX2, //Force use of High Jump + ForceWeave, //Force use of Jump inside the Delay //Delay use of Jump } @@ -86,6 +96,7 @@ public enum DragonfireStrategy Automatic, //Automatically use Dragonfire Dive Force, //Force use of Dragonfire Dive ForceEX, //Force use of Dragonfire Dive (EX)) + ForceWeave, //Force use of Dragonfire Dive inside the next possible weave window Delay //Delay use of Dragonfire Dive } @@ -95,6 +106,7 @@ public enum GeirskogulStrategy Automatic, //Automatically use Geirskogul Force, //Force use of Geirskogul ForceEX, //Force use of Geirskogul (EX) + ForceWeave, //Force use of Geirskogul inside the next possible weave window Delay //Delay use of Geirskogul } @@ -104,6 +116,7 @@ public enum StardiverStrategy Automatic, //Automatically use Stardiver Force, //Force use of Stardiver ForceEX, //Force use of Stardiver (EX) + ForceWeave, //Force use of Stardiver inside the next possible weave window Delay //Delay use of Stardiver } @@ -112,13 +125,15 @@ public enum PiercingTalonStrategy { Forbid, //Forbid the use of Piercing Talon Allow, //Use Piercing Talon when appropriate + AllowEX, //Use Piercing Talon when Enhanced Force, //Force use of Piercing Talon + ForceEX, //Force use of Piercing Talon when Enhanced } //True North strategy public enum TrueNorthStrategy { - Automatic, //Late-Weave + Automatic, //Weave ASAP, //Use ASAP Rear, //Use only when in Rear Flank, //Use only when in Flank @@ -131,6 +146,7 @@ public enum OffensiveStrategy { Automatic, //Automatically use offensive abilities Force, //Force offensive abilities + ForceWeave, //Force offensive abilities only inside the next possible weave window Delay //Delay offensive abilities } @@ -153,6 +169,11 @@ public static RotationModuleDefinition Definition() .AddOption(AOEStrategy.ForceBuffsST, "Only 1-4-5 ST", "Force only ST 1-4-5 rotation (Buffs Only)") .AddOption(AOEStrategy.ForceAOE, "Force AOE", "Force AOE rotation, even if less than 3 targets"); + //Spear targeting strategy + res.Define(Track.Spears).As("Spear Targeting", "Spears", uiPriority: 195) + .AddOption(SpearTargetingStrategy.AutoTargetHitPrimary, "AutoTargetHitPrimary", "Selects best target within range that hits as many targets as possible whilst also hitting current target") + .AddOption(SpearTargetingStrategy.AutoTargetHitMost, "AutoTargetHitMost", "Selects best target within range that hits as many targets, regardless if current target is hit"); + //Burst strategy res.Define(Track.Burst).As("Burst", uiPriority: 190) .AddOption(BurstStrategy.Automatic, "Automatic", "Use Burst optimally based on situation") @@ -170,8 +191,10 @@ public static RotationModuleDefinition Definition() //Life Surge Strategy res.Define(Track.LifeSurge).As("Life Surge", "L. Surge", uiPriority: 160) .AddOption(SurgeStrategy.Automatic, "Automatic", "Use Life Surge normally") - .AddOption(SurgeStrategy.Force, "Force", "Force Life Surge usage", 40, 5, ActionTargets.Hostile, 6, 87) - .AddOption(SurgeStrategy.ForceEX, "ForceEX", "Force Life Surge (2 charges)", 40, 5, ActionTargets.Hostile, 88) //2 charges + .AddOption(SurgeStrategy.Force, "Force", "Force Life Surge usage", 40, 5, ActionTargets.Hostile, 6) + .AddOption(SurgeStrategy.ForceWeave, "Force Weave", "Force Life Surge usage inside the next possible weave window", 40, 5, ActionTargets.Hostile, 6) + .AddOption(SurgeStrategy.ForceNextOpti, "Force Optimally", "Force Life Surge usage in next possible optimal window", 40, 5, ActionTargets.Hostile, 6) + .AddOption(SurgeStrategy.ForceNextOptiWeave, "Force Weave Optimally", "Force Life Surge optimally inside the next possible weave window", 40, 5, ActionTargets.Hostile, 6) .AddOption(SurgeStrategy.Delay, "Delay", "Delay the use of Life Surge", 0, 0, ActionTargets.None, 6) .AddAssociatedActions(AID.LifeSurge); @@ -181,6 +204,7 @@ public static RotationModuleDefinition Definition() .AddOption(JumpStrategy.Force, "Force Jump", "Force Jump usage", 30, 0, ActionTargets.Self, 30, 67) .AddOption(JumpStrategy.ForceEX, "Force Jump (EX)", "Force Jump usage (Grants Dive Ready buff)", 30, 15, ActionTargets.Self, 68, 74) .AddOption(JumpStrategy.ForceEX2, "Force High Jump", "Force High Jump usage", 30, 15, ActionTargets.Self, 75) + .AddOption(JumpStrategy.ForceWeave, "Force Weave", "Force Jump usage inside the next possible weave window", 30, 15, ActionTargets.Hostile, 68) .AddOption(JumpStrategy.Delay, "Delay", "Delay Jump usage", 0, 0, ActionTargets.None, 30) .AddAssociatedActions(AID.Jump, AID.HighJump); @@ -189,6 +213,7 @@ public static RotationModuleDefinition Definition() .AddOption(DragonfireStrategy.Automatic, "Automatic", "Use Dragonfire Dive normally") .AddOption(DragonfireStrategy.Force, "Force", "Force Dragonfire Dive usage", 120, 0, ActionTargets.Hostile, 50, 91) .AddOption(DragonfireStrategy.ForceEX, "ForceEX", "Force Dragonfire Dive (Grants Dragon's Flight)", 120, 30, ActionTargets.Hostile, 92) + .AddOption(DragonfireStrategy.ForceWeave, "Force Weave", "Force Dragonfire Dive usage inside the next possible weave window", 120, 0, ActionTargets.Hostile, 68) .AddOption(DragonfireStrategy.Delay, "Delay", "Delay Dragonfire Dive usage", 0, 0, ActionTargets.None, 50) .AddAssociatedActions(AID.DragonfireDive); @@ -197,6 +222,7 @@ public static RotationModuleDefinition Definition() .AddOption(GeirskogulStrategy.Automatic, "Automatic", "Use Geirskogul normally") .AddOption(GeirskogulStrategy.Force, "Force", "Force Geirskogul usage", 60, 0, ActionTargets.Hostile, 60, 69) .AddOption(GeirskogulStrategy.ForceEX, "ForceEX", "Force Geirskogul (Grants Life of the Dragon & 3x Nastrond)", 60, 20, ActionTargets.Hostile, 70) + .AddOption(GeirskogulStrategy.ForceWeave, "Force Weave", "Force Geirskogul usage inside the next possible weave window", 60, 20, ActionTargets.Hostile, 70) .AddOption(GeirskogulStrategy.Delay, "Delay", "Delay Geirskogul usage", 0, 0, ActionTargets.None, 60) .AddAssociatedActions(AID.Geirskogul); @@ -205,6 +231,7 @@ public static RotationModuleDefinition Definition() .AddOption(StardiverStrategy.Automatic, "Automatic", "Use Stardiver normally") .AddOption(StardiverStrategy.Force, "Force", "Force Stardiver usage", 30, 0, ActionTargets.Hostile, 80, 99) .AddOption(StardiverStrategy.ForceEX, "ForceEX", "Force Stardiver (Grants Starcross Ready)", 30, 0, ActionTargets.Hostile, 100) + .AddOption(StardiverStrategy.ForceWeave, "Force Weave", "Force Stardiver usage inside the next possible weave window", 30, 0, ActionTargets.Hostile, 80) .AddOption(StardiverStrategy.Delay, "Delay", "Delay Stardiver usage", 0, 0, ActionTargets.None, 80) .AddAssociatedActions(AID.Stardiver); @@ -212,12 +239,14 @@ public static RotationModuleDefinition Definition() res.Define(Track.PiercingTalon).As("Piercing Talon", "Talon", uiPriority: 20) .AddOption(PiercingTalonStrategy.Forbid, "Forbid", "Forbid use of Piercing Talon") .AddOption(PiercingTalonStrategy.Allow, "Allow", "Allow use of Piercing Talon only if already in combat & outside melee range") + .AddOption(PiercingTalonStrategy.AllowEX, "AllowEX", "Allow use of Piercing Talon only if Enhanced") .AddOption(PiercingTalonStrategy.Force, "Force", "Force Piercing Talon usage ASAP (even in melee range)") + .AddOption(PiercingTalonStrategy.ForceEX, "ForceEX", "Force Piercing Talon usage ASAP when Enhanced") .AddAssociatedActions(AID.PiercingTalon); //True North strategy res.Define(Track.TrueNorth).As("True North", "T.North", uiPriority: 10) - .AddOption(TrueNorthStrategy.Automatic, "Automatic", "Late-weaves True North when out of positional") + .AddOption(TrueNorthStrategy.Automatic, "Automatic", "weaves True North when out of positional") .AddOption(TrueNorthStrategy.ASAP, "ASAP", "Use True North as soon as possible when out of positional", 45, 10, ActionTargets.Self, 50) .AddOption(TrueNorthStrategy.Rear, "Rear", "Use True North for rear positional only", 45, 10, ActionTargets.Self, 50) .AddOption(TrueNorthStrategy.Flank, "Flank", "Use True North for flank positional only", 45, 10, ActionTargets.Self, 50) @@ -232,6 +261,7 @@ public static RotationModuleDefinition Definition() res.Define(Track.LanceCharge).As("Lance Charge", "L.Charge", uiPriority: 165) .AddOption(OffensiveStrategy.Automatic, "Automatic", "Use Lance Charge normally") .AddOption(OffensiveStrategy.Force, "Force", "Force Lance Charge usage ASAP (even during downtime)", 60, 20, ActionTargets.Self, 30) + .AddOption(OffensiveStrategy.ForceWeave, "Force Weave", "Force Lance Charge usage inside the next possible weave window", 60, 20, ActionTargets.Self, 30) .AddOption(OffensiveStrategy.Delay, "Delay", "Delay Lance Charge usage", 0, 0, ActionTargets.None, 30) .AddAssociatedActions(AID.LanceCharge); @@ -239,6 +269,7 @@ public static RotationModuleDefinition Definition() res.Define(Track.BattleLitany).As("Battle Litany", "B.Litany", uiPriority: 170) .AddOption(OffensiveStrategy.Automatic, "Automatic", "Use Battle Litany normally") .AddOption(OffensiveStrategy.Force, "Force", "Force Battle Litany usage ASAP (even during downtime)", 120, 20, ActionTargets.Self, 52) + .AddOption(OffensiveStrategy.ForceWeave, "Force Weave", "Force Battle Litany usage inside the next possible weave window", 120, 20, ActionTargets.Self, 52) .AddOption(OffensiveStrategy.Delay, "Delay", "Delay Battle Litany usage", 0, 0, ActionTargets.None, 52) .AddAssociatedActions(AID.BattleLitany); @@ -246,6 +277,7 @@ public static RotationModuleDefinition Definition() res.Define(Track.MirageDive).As("Mirage Dive", "M.Dive", uiPriority: 105) .AddOption(OffensiveStrategy.Automatic, "Automatic", "Use Mirage Dive normally") .AddOption(OffensiveStrategy.Force, "Force", "Force Mirage Dive usage", 0, 0, ActionTargets.Hostile, 68) + .AddOption(OffensiveStrategy.ForceWeave, "Force Weave", "Force Mirage Dive usage inside the next possible weave window", 0, 0, ActionTargets.Hostile, 68) .AddOption(OffensiveStrategy.Delay, "Delay", "Delay Mirage Dive usage", 0, 0, ActionTargets.None, 68) .AddAssociatedActions(AID.MirageDive); @@ -253,6 +285,7 @@ public static RotationModuleDefinition Definition() res.Define(Track.Nastrond).As("Nastrond", "Nast.", uiPriority: 125) .AddOption(OffensiveStrategy.Automatic, "Automatic", "Use Nastrond normally") .AddOption(OffensiveStrategy.Force, "Force", "Force Nastrond usage", 0, 2, ActionTargets.Hostile, 70) + .AddOption(OffensiveStrategy.ForceWeave, "Force Weave", "Force Nastrond usage inside the next possible weave window", 0, 2, ActionTargets.Hostile, 70) .AddOption(OffensiveStrategy.Delay, "Delay", "Delay Nastrond usage", 0, 0, ActionTargets.None, 70) .AddAssociatedActions(AID.Nastrond); @@ -260,6 +293,7 @@ public static RotationModuleDefinition Definition() res.Define(Track.WyrmwindThrust).As("Wyrmwind Thrust", "W.Thrust", uiPriority: 120) .AddOption(OffensiveStrategy.Automatic, "Automatic", "Use Wyrmwind Thrust normally") .AddOption(OffensiveStrategy.Force, "Force", "Force Wyrmwind Thrust usage ASAP", 0, 10, ActionTargets.Hostile, 90) + .AddOption(OffensiveStrategy.ForceWeave, "Force Weave", "Force Nastrond usage inside the next possible weave window", 0, 10, ActionTargets.Hostile, 90) .AddOption(OffensiveStrategy.Delay, "Delay", "Delay Wyrmwind Thrust usage", 0, 0, ActionTargets.None, 90) .AddAssociatedActions(AID.WyrmwindThrust); @@ -267,13 +301,15 @@ public static RotationModuleDefinition Definition() res.Define(Track.RiseOfTheDragon).As("Rise Of The Dragon", "RotD", uiPriority: 145) .AddOption(OffensiveStrategy.Automatic, "Automatic", "Use Rise Of The Dragon normally") .AddOption(OffensiveStrategy.Force, "Force", "Force Rise Of The Dragon usage", 0, 0, ActionTargets.Hostile, 92) + .AddOption(OffensiveStrategy.ForceWeave, "Force Weave", "Force Rise Of The Dragon usage inside the next possible weave window", 0, 0, ActionTargets.Hostile, 92) .AddOption(OffensiveStrategy.Delay, "Delay", "Delay Rise Of The Dragon usage", 0, 0, ActionTargets.None, 92) .AddAssociatedActions(AID.RiseOfTheDragon); //Starcross strategy res.Define(Track.Starcross).As("Starcross", "S.cross", uiPriority: 135) .AddOption(OffensiveStrategy.Automatic, "Automatic", "Use Starcross normally") - .AddOption(OffensiveStrategy.Force, "Force", "Force Starcross usage", 0, 0, ActionTargets.Self, 100) + .AddOption(OffensiveStrategy.Force, "Force", "Force Starcross usage", 0, 0, ActionTargets.Hostile, 100) + .AddOption(OffensiveStrategy.ForceWeave, "Force Weave", "Force Starcross usage inside the next possible weave window", 0, 0, ActionTargets.Hostile, 100) .AddOption(OffensiveStrategy.Delay, "Delay", "Delay Starcross usage", 0, 0, ActionTargets.None, 100) .AddAssociatedActions(AID.Starcross); @@ -325,6 +361,8 @@ public enum OGCDPriority private float powerLeft; //Time remaining for Power Surge private float chaosLeft; //Remaining time for Chaotic Spring DoT + private bool canWeave; //Inside Weave window + private bool canWeaveInStardiver; //We can weave in Stardiver public float downtimeIn; //Duration of downtime in combat private float PotionLeft; //Remaining time for potion effect private float RaidBuffsLeft; //Time left for raid buffs @@ -361,31 +399,34 @@ public enum OGCDPriority #endregion #region Module Helpers - //Check if the desired ability is unlocked private bool Unlocked(AID aid) => ActionUnlocked(ActionID.MakeSpell(aid)); - //Get remaining cooldown time for the specified action - private float CD(AID aid) => World.Client.Cooldowns[ActionDefinitions.Instance.Spell(aid)!.MainCooldownGroup].Remaining; - //Get the last action used in the combo sequence private AID ComboLastMove => (AID)World.Client.ComboState.Action; + //Check if status effect is on self + public bool HasEffect(SID sid) => SelfStatusLeft(sid) > 0; + + //Check if the desired action is ready (cooldown < 0.6 seconds) + private bool ActionReady(AID aid) => World.Client.Cooldowns[ActionDefinitions.Instance.Spell(aid)!.MainCooldownGroup].Remaining < 0.6f; + + //Get remaining cooldown time for the specified action + private float CD(AID aid) => World.Client.Cooldowns[ActionDefinitions.Instance.Spell(aid)!.MainCooldownGroup].Remaining; + //Check if the target is within melee range (3 yalms) private bool In3y(Actor? target) => Player.DistanceToHitbox(target) <= 3; //Check if the target is within 15 yalms private bool In15y(Actor? target) => Player.DistanceToHitbox(target) <= 14.75; - //Check if the desired action is ready (cooldown < 0.6 seconds) - private bool ActionReady(AID aid) => World.Client.Cooldowns[ActionDefinitions.Instance.Spell(aid)!.MainCooldownGroup].Remaining < 0.6f; + //Checks if we can weave an ability + public bool CanWeave(AID aid, double weaveTime = 0.7) => CD(aid) > weaveTime; //Check if the potion should be used before raid buffs expire private bool IsPotionBeforeRaidbuffs() => RaidBuffsLeft == 0 && PotionLeft > RaidBuffsIn + 17.5f; - //Check if status effect is on self - public bool HasEffect(SID sid) where SID : Enum => Player.FindStatus((uint)(object)sid, Player.InstanceID) != null; - + #region Targeting Helpers //Count number of targets hit by AOE attack private int NumTargetsHitByAOE(Actor primary) => Hints.NumPriorityTargetsInAOECone(Player.Position, 10, (primary.Position - Player.Position).Normalized(), 45.Degrees()); @@ -408,6 +449,16 @@ public enum OGCDPriority _ => (null, 0) }; + private (Actor?, int) CheckSpearTargeting(SpearTargetingStrategy strategy, Actor? primaryTarget, float range, Func numTargets, Func check) => strategy switch + { + SpearTargetingStrategy.AutoTargetHitPrimary => FindBetterTargetBy(primaryTarget, range, t => primaryTarget == null || check(t, primaryTarget) ? numTargets(t) : 0), + SpearTargetingStrategy.AutoTargetHitMost => FindBetterTargetBy(primaryTarget, range, numTargets), + _ => (null, 0) + }; + + #endregion + + #region True North Helpers //Check which positional Player is on private Positional GetCurrentPositional(Actor target) => (Player.Position - target.Position).Normalized().Dot(target.Rotation.ToDirection()) switch { @@ -421,6 +472,7 @@ public enum OGCDPriority //Check if Player is on Flank (side) positional private bool IsOnFlank(Actor target) => GetCurrentPositional(target) == Positional.Flank; + #endregion #endregion @@ -467,6 +519,8 @@ public override void Execute(StrategyValues strategy, Actor? primaryTarget, floa #endregion #region Miscellaneous + canWeave = CanWeave(AID.TrueThrust); //Check if weaving is possible + canWeaveInStardiver = GCD is < 2.5f and > 1.49f; //Check if weaving Stardiver is possible downtimeIn = Manager.Planner?.EstimateTimeToNextDowntime().Item2 ?? float.MaxValue; //Estimate downtime until next action PotionLeft = PotionStatusLeft(); //Get remaining potion status (RaidBuffsLeft, RaidBuffsIn) = EstimateRaidBuffTimings(primaryTarget); //Estimate remaining raid buffs @@ -480,6 +534,7 @@ public override void Execute(StrategyValues strategy, Actor? primaryTarget, floa #region AOEStrategy 'Force' Execution var AOEStrategy = strategy.Option(Track.AOE).As(); //Retrieve the current AOE strategy + var SpearStrategy = strategy.Option(Track.Spears).As(); //Retrieve the current Spear targeting strategy //Force specific actions based on the AOE strategy selected if (AOEStrategy == AOEStrategy.ForceST) //If forced single target @@ -519,10 +574,9 @@ public override void Execute(StrategyValues strategy, Actor? primaryTarget, floa ? CheckAOETargeting(AOEStrategy, primaryTarget, 10, NumTargetsHitByAOE, IsHitByAOE) : (null, 0); //Set to null and count 0 if not unlocked - //Check if Geirskogul is unlocked; if so, determine the best spear target and count var (SpearBestTarget, SpearTargetCount) = Unlocked(AID.Geirskogul) - ? CheckAOETargeting(AOEStrategy, primaryTarget, 15, NumTargetsHitBySpear, IsHitBySpear) - : (null, 0); //Set to null and count 0 if not unlocked + ? CheckSpearTargeting(SpearStrategy, primaryTarget, 15, NumTargetsHitBySpear, IsHitBySpear) + : (null, 0); // Set to null and count 0 if not unlocked //Determine if using AOE is viable (at least 3 targets hit) var useAOE = AOETargetCount >= 3; @@ -545,67 +599,67 @@ public override void Execute(StrategyValues strategy, Actor? primaryTarget, floa //Execute Lance Charge if available var lcStrat = strategy.Option(Track.LanceCharge).As(); if (!hold && ShouldUseLanceCharge(lcStrat, primaryTarget)) - QueueOGCD(AID.LanceCharge, Player, lcStrat is OffensiveStrategy.Force ? OGCDPriority.ForcedOGCD : OGCDPriority.Buffs); + QueueOGCD(AID.LanceCharge, Player, lcStrat is OffensiveStrategy.Force or OffensiveStrategy.ForceWeave ? OGCDPriority.ForcedOGCD : OGCDPriority.Buffs); //Execute Battle Litany if available var blStrat = strategy.Option(Track.BattleLitany).As(); if (!hold && ShouldUseBattleLitany(blStrat, primaryTarget)) - QueueOGCD(AID.BattleLitany, Player, blStrat is OffensiveStrategy.Force ? OGCDPriority.ForcedOGCD : OGCDPriority.Buffs); + QueueOGCD(AID.BattleLitany, Player, blStrat is OffensiveStrategy.Force or OffensiveStrategy.ForceWeave ? OGCDPriority.ForcedOGCD : OGCDPriority.Buffs); //Execute Life Surge if conditions met var lsStrat = strategy.Option(Track.LifeSurge).As(); if (!hold && ShouldUseLifeSurge(lsStrat, primaryTarget)) - QueueOGCD(AID.LifeSurge, Player, lsStrat is SurgeStrategy.Force ? OGCDPriority.ForcedOGCD : OGCDPriority.Buffs); + QueueOGCD(AID.LifeSurge, Player, lsStrat is SurgeStrategy.Force or SurgeStrategy.ForceWeave or SurgeStrategy.ForceNextOpti or SurgeStrategy.ForceNextOptiWeave ? OGCDPriority.ForcedOGCD : OGCDPriority.Buffs); //Execute Jump ability if available var jumpStrat = strategy.Option(Track.Jump).As(); if (!hold && ShouldUseJump(jumpStrat, primaryTarget)) - QueueOGCD(Unlocked(AID.HighJump) ? AID.HighJump : AID.Jump, primaryTarget, jumpStrat == JumpStrategy.Force ? OGCDPriority.ForcedOGCD : OGCDPriority.Jump); + QueueOGCD(Unlocked(AID.HighJump) ? AID.HighJump : AID.Jump, primaryTarget, jumpStrat is JumpStrategy.Force or JumpStrategy.ForceEX or JumpStrategy.ForceEX2 or JumpStrategy.ForceWeave ? OGCDPriority.ForcedOGCD : OGCDPriority.Jump); //Execute Dragonfire Dive if available var ddStrat = strategy.Option(Track.DragonfireDive).As(); if (!hold && ShouldUseDragonfireDive(ddStrat, primaryTarget)) - QueueOGCD(AID.DragonfireDive, primaryTarget, ddStrat is DragonfireStrategy.Force ? OGCDPriority.ForcedOGCD : OGCDPriority.DragonfireDive); + QueueOGCD(AID.DragonfireDive, primaryTarget, ddStrat is DragonfireStrategy.Force or DragonfireStrategy.ForceWeave ? OGCDPriority.ForcedOGCD : OGCDPriority.DragonfireDive); //Execute Geirskogul if available var geirskogul = strategy.Option(Track.Geirskogul).As(); if (!hold && ShouldUseGeirskogul(geirskogul, primaryTarget)) - QueueOGCD(AID.Geirskogul, bestSpeartarget, geirskogul == GeirskogulStrategy.Force ? OGCDPriority.ForcedOGCD : OGCDPriority.Geirskogul); + QueueOGCD(AID.Geirskogul, bestSpeartarget, geirskogul is GeirskogulStrategy.Force or GeirskogulStrategy.ForceEX or GeirskogulStrategy.ForceWeave ? OGCDPriority.ForcedOGCD : OGCDPriority.Geirskogul); //Execute Mirage Dive if available var mirageStrat = strategy.Option(Track.MirageDive).As(); if (!hold && ShouldUseMirageDive(mirageStrat, primaryTarget)) - QueueOGCD(AID.MirageDive, primaryTarget, mirageStrat == OffensiveStrategy.Force ? OGCDPriority.ForcedOGCD : OGCDPriority.MirageDive); + QueueOGCD(AID.MirageDive, primaryTarget, mirageStrat is OffensiveStrategy.Force or OffensiveStrategy.ForceWeave ? OGCDPriority.ForcedOGCD : OGCDPriority.MirageDive); //Execute Nastrond if available var nastrondStrat = strategy.Option(Track.Nastrond).As(); if (!hold && ShouldUseNastrond(nastrondStrat, primaryTarget)) - QueueOGCD(AID.Nastrond, bestSpeartarget, nastrondStrat == OffensiveStrategy.Force ? OGCDPriority.ForcedOGCD : OGCDPriority.Nastrond); + QueueOGCD(AID.Nastrond, bestSpeartarget, nastrondStrat is OffensiveStrategy.Force or OffensiveStrategy.ForceWeave ? OGCDPriority.ForcedOGCD : OGCDPriority.Nastrond); //Execute Stardiver if available var sdStrat = strategy.Option(Track.Stardiver).As(); if (!hold && ShouldUseStardiver(sdStrat, primaryTarget)) - QueueOGCD(AID.Stardiver, primaryTarget, sdStrat == StardiverStrategy.Force ? OGCDPriority.ForcedOGCD : OGCDPriority.Stardiver); + QueueOGCD(AID.Stardiver, primaryTarget, sdStrat is StardiverStrategy.Force or StardiverStrategy.ForceEX or StardiverStrategy.ForceWeave ? OGCDPriority.ForcedOGCD : OGCDPriority.Stardiver); //Execute Wyrmwind Thrust if available var wtStrat = strategy.Option(Track.WyrmwindThrust).As(); if (!hold && ShouldUseWyrmwindThrust(wtStrat, primaryTarget)) - QueueOGCD(AID.WyrmwindThrust, bestSpeartarget, wtStrat == OffensiveStrategy.Force ? OGCDPriority.ForcedOGCD : HasEffect(SID.LanceCharge) ? OGCDPriority.WyrmwindThrustOpti : OGCDPriority.WyrmwindThrust); + QueueOGCD(AID.WyrmwindThrust, bestSpeartarget, wtStrat is OffensiveStrategy.Force or OffensiveStrategy.ForceWeave ? OGCDPriority.ForcedOGCD : HasEffect(SID.LanceCharge) ? OGCDPriority.WyrmwindThrustOpti : OGCDPriority.WyrmwindThrust); //Execute Rise of the Dragon if available var riseStrat = strategy.Option(Track.RiseOfTheDragon).As(); if (!hold && ShouldUseRiseOfTheDragon(riseStrat, primaryTarget)) - QueueOGCD(AID.RiseOfTheDragon, primaryTarget, riseStrat == OffensiveStrategy.Force ? OGCDPriority.ForcedOGCD : OGCDPriority.Buffs); + QueueOGCD(AID.RiseOfTheDragon, primaryTarget, riseStrat is OffensiveStrategy.Force or OffensiveStrategy.ForceWeave ? OGCDPriority.ForcedOGCD : OGCDPriority.Buffs); //Execute Starcross if available var crossStrat = strategy.Option(Track.Starcross).As(); if (!hold && ShouldUseStarcross(crossStrat, primaryTarget)) - QueueOGCD(AID.Starcross, primaryTarget, crossStrat == OffensiveStrategy.Force ? OGCDPriority.ForcedOGCD : OGCDPriority.Starcross); + QueueOGCD(AID.Starcross, primaryTarget, crossStrat is OffensiveStrategy.Force or OffensiveStrategy.ForceWeave ? OGCDPriority.ForcedOGCD : OGCDPriority.Starcross); //Execute Piercing Talon if available var ptStrat = strategy.Option(Track.PiercingTalon).As(); if (ShouldUsePiercingTalon(primaryTarget, ptStrat)) - QueueGCD(AID.PiercingTalon, primaryTarget, ptStrat == PiercingTalonStrategy.Force ? GCDPriority.ForcedGCD : GCDPriority.NormalGCD); + QueueGCD(AID.PiercingTalon, primaryTarget, ptStrat is PiercingTalonStrategy.Force or PiercingTalonStrategy.ForceEX ? GCDPriority.ForcedGCD : GCDPriority.NormalGCD); //Execute Potion if available if (ShouldUsePotion(strategy.Option(Track.Potion).As())) @@ -846,6 +900,7 @@ private void QueueOGCD(AID aid, Actor? target, OGCDPriority prio, float basePrio //Use Lance Charge automatically if the player is in combat, the target is valid, the action is ready, and there is power remaining Player.InCombat && target != null && canLC && powerLeft > 0, OffensiveStrategy.Force => canLC, //Always use if forced + OffensiveStrategy.ForceWeave => canLC && canWeave, //Always use if inside weave window OffensiveStrategy.Delay => false, //Delay usage if strategy is set to delay _ => false }; @@ -857,6 +912,7 @@ private void QueueOGCD(AID aid, Actor? target, OGCDPriority prio, float basePrio //Use Battle Litany automatically if the player is in combat, the target is valid, the action is ready, and there is power remaining Player.InCombat && target != null && canBL && powerLeft > 0, OffensiveStrategy.Force => canBL, //Always use if forced + OffensiveStrategy.ForceWeave => canBL && canWeave, //Always use if inside weave window OffensiveStrategy.Delay => false, //Delay usage if strategy is set to delay _ => false }; @@ -870,6 +926,13 @@ private void QueueOGCD(AID aid, Actor? target, OGCDPriority prio, float basePrio (ComboLastMove is AID.WheelingThrust or AID.FangAndClaw && Unlocked(AID.Drakesbane) || ComboLastMove is AID.VorpalThrust or AID.LanceBarrage && Unlocked(AID.FullThrust)), SurgeStrategy.Force => canLS, + SurgeStrategy.ForceWeave => canLS && canWeave, //Always use if inside weave window + SurgeStrategy.ForceNextOpti => canLS && + (ComboLastMove is AID.WheelingThrust or AID.FangAndClaw && Unlocked(AID.Drakesbane) || + ComboLastMove is AID.VorpalThrust or AID.LanceBarrage && Unlocked(AID.FullThrust)), + SurgeStrategy.ForceNextOptiWeave => canLS && canWeave && + (ComboLastMove is AID.WheelingThrust or AID.FangAndClaw && Unlocked(AID.Drakesbane) || + ComboLastMove is AID.VorpalThrust or AID.LanceBarrage && Unlocked(AID.FullThrust)), SurgeStrategy.Delay => false, _ => false }; @@ -882,6 +945,7 @@ private void QueueOGCD(AID aid, Actor? target, OGCDPriority prio, float basePrio Player.InCombat && target != null && canJump && (lcLeft > 0 || hasLC || lcCD is < 35 and > 17), JumpStrategy.ForceEX => canJump, //Always use in ForceEX strategy JumpStrategy.ForceEX2 => canJump, //Always use in ForceEX2 strategy + JumpStrategy.ForceWeave => canJump && canWeave, //Always use if inside weave window JumpStrategy.Delay => false, //Delay usage if strategy is set to delay _ => false }; @@ -894,6 +958,7 @@ private void QueueOGCD(AID aid, Actor? target, OGCDPriority prio, float basePrio Player.InCombat && target != null && In3y(target) && canDD && hasLC && hasBL, DragonfireStrategy.Force => canDD, //Always use if forced DragonfireStrategy.ForceEX => canDD, //Always use in ForceEX strategy + DragonfireStrategy.ForceWeave => canDD && canWeave, //Always use if inside weave window DragonfireStrategy.Delay => false, //Delay usage if strategy is set to delay _ => false }; @@ -906,6 +971,7 @@ private void QueueOGCD(AID aid, Actor? target, OGCDPriority prio, float basePrio Player.InCombat && In15y(target) && canGeirskogul && hasLC, GeirskogulStrategy.Force => canGeirskogul, //Always use if forced GeirskogulStrategy.ForceEX => canGeirskogul, //Always use if forced + GeirskogulStrategy.ForceWeave => canGeirskogul && canWeave, //Always use if inside weave window GeirskogulStrategy.Delay => false, //Delay usage if strategy is set to delay _ => false }; @@ -917,6 +983,7 @@ private void QueueOGCD(AID aid, Actor? target, OGCDPriority prio, float basePrio //Use Mirage Dive automatically if the player is in combat, the target is valid, and Dive Ready effect is active Player.InCombat && target != null && canMD, OffensiveStrategy.Force => canMD, //Always use if forced + OffensiveStrategy.ForceWeave => canMD && canWeave, //Always use if inside weave window OffensiveStrategy.Delay => false, //Delay usage if strategy is set to delay _ => false }; @@ -928,6 +995,7 @@ private void QueueOGCD(AID aid, Actor? target, OGCDPriority prio, float basePrio //Use Nastrond automatically if the player is in combat, has Nastrond ready, the target is within 15y, and Lance Charge is active Player.InCombat && In15y(target) && canNastrond, OffensiveStrategy.Force => canNastrond, //Always use if forced + OffensiveStrategy.ForceWeave => canNastrond && canWeave, //Always use if inside weave window OffensiveStrategy.Delay => false, //Delay usage if strategy is set to delay _ => false }; @@ -940,6 +1008,7 @@ private void QueueOGCD(AID aid, Actor? target, OGCDPriority prio, float basePrio Player.InCombat && target != null && In3y(target) && canSD && hasLOTD, StardiverStrategy.Force => canSD, //Always use if forced StardiverStrategy.ForceEX => canSD, //Always use if forced + StardiverStrategy.ForceWeave => canSD && canWeaveInStardiver, //Always use if inside weave window StardiverStrategy.Delay => false, //Delay usage if strategy is set to delay _ => false }; @@ -951,6 +1020,7 @@ private void QueueOGCD(AID aid, Actor? target, OGCDPriority prio, float basePrio //Use Wyrmwind Thrust automatically if the player is in combat, the target is within 15y, and focus count is exactly 2 Player.InCombat && target != null && In15y(target) && canWT && focusCount is 2 && lcCD > GCDLength * 3, OffensiveStrategy.Force => canWT, //Always use if forced + OffensiveStrategy.ForceWeave => canWT && canWeave, //Always use if inside weave window OffensiveStrategy.Delay => false, //Delay usage if strategy is set to delay _ => false }; @@ -962,6 +1032,7 @@ private void QueueOGCD(AID aid, Actor? target, OGCDPriority prio, float basePrio //Use Rise of the Dragon automatically if the player is in combat, the target is valid, and Dragon's Flight effect is active Player.InCombat && target != null && canROTD, OffensiveStrategy.Force => canROTD, //Always use if forced + OffensiveStrategy.ForceWeave => canROTD && canWeave, //Always use if inside weave window OffensiveStrategy.Delay => false, //Delay usage if strategy is set to delay _ => false }; @@ -973,6 +1044,7 @@ private void QueueOGCD(AID aid, Actor? target, OGCDPriority prio, float basePrio //Use Starcross automatically if the player is in combat, the target is valid, and Starcross Ready effect is active Player.InCombat && target != null && canSC, OffensiveStrategy.Force => canSC, //Always use if forced + OffensiveStrategy.ForceWeave => canSC && canWeave, //Always use if inside weave window OffensiveStrategy.Delay => false, //Delay usage if strategy is set to delay _ => false }; @@ -982,9 +1054,11 @@ private void QueueOGCD(AID aid, Actor? target, OGCDPriority prio, float basePrio { PiercingTalonStrategy.Forbid => false, //Never use if forbidden PiercingTalonStrategy.Allow => - //Use Piercing Talon if the target is not within 3y range and already in combat Player.InCombat && target != null && !In3y(target), + PiercingTalonStrategy.AllowEX => + Player.InCombat && target != null && !In3y(target) && HasEffect(SID.EnhancedPiercingTalon), PiercingTalonStrategy.Force => true, //Always use if forced + PiercingTalonStrategy.ForceEX => HasEffect(SID.EnhancedPiercingTalon), _ => false }; @@ -1004,7 +1078,7 @@ private void QueueOGCD(AID aid, Actor? target, OGCDPriority prio, float basePrio { TrueNorthStrategy.Automatic => target != null && Player.InCombat && - !HasEffect(ClassShared.SID.TrueNorth) && + !HasEffect(SID.TrueNorth) && GCD < 1.25f && (!IsOnRear(target) && //Side ComboLastMove is AID.Disembowel or AID.SpiralBlow @@ -1013,7 +1087,7 @@ or AID.ChaosThrust or AID.ChaoticSpring || ComboLastMove is AID.HeavensThrust or AID.FullThrust), TrueNorthStrategy.ASAP => target != null && Player.InCombat && - !HasEffect(ClassShared.SID.TrueNorth) && + !HasEffect(SID.TrueNorth) && (!IsOnRear(target) && //Side ComboLastMove is AID.Disembowel or AID.SpiralBlow or AID.ChaosThrust or AID.ChaoticSpring || @@ -1021,13 +1095,13 @@ or AID.ChaosThrust or AID.ChaoticSpring || ComboLastMove is AID.HeavensThrust or AID.FullThrust), TrueNorthStrategy.Flank => target != null && Player.InCombat && - !HasEffect(ClassShared.SID.TrueNorth) && + !HasEffect(SID.TrueNorth) && GCD < 1.25f && !IsOnFlank(target) && //Back ComboLastMove is AID.HeavensThrust or AID.FullThrust, TrueNorthStrategy.Rear => target != null && Player.InCombat && - !HasEffect(ClassShared.SID.TrueNorth) && + !HasEffect(SID.TrueNorth) && GCD < 1.25f && !IsOnRear(target) && //Side ComboLastMove is AID.Disembowel or AID.SpiralBlow From e38e75f7eff6b942a65cd59da0f9f582206f03ee Mon Sep 17 00:00:00 2001 From: Akechi-kun <167795370+Akechi-kun@users.noreply.github.com> Date: Fri, 13 Dec 2024 17:44:11 -0800 Subject: [PATCH 02/29] xdd --- BossMod/Autorotation/akechi/AkechiDRG.cs | 18 +++++++++--------- BossMod/Autorotation/akechi/AkechiGNB.cs | 4 +--- 2 files changed, 10 insertions(+), 12 deletions(-) diff --git a/BossMod/Autorotation/akechi/AkechiDRG.cs b/BossMod/Autorotation/akechi/AkechiDRG.cs index b6ac775b5c..14353430cc 100644 --- a/BossMod/Autorotation/akechi/AkechiDRG.cs +++ b/BossMod/Autorotation/akechi/AkechiDRG.cs @@ -86,7 +86,7 @@ public enum JumpStrategy Force, //Force use of Jump ForceEX, //Force use of Jump EX) ForceEX2, //Force use of High Jump - ForceWeave, //Force use of Jump inside the + ForceWeave, //Force use of Jump inside the next possible weave window Delay //Delay use of Jump } @@ -123,11 +123,11 @@ public enum StardiverStrategy //Piercing Talon strategy public enum PiercingTalonStrategy { - Forbid, //Forbid the use of Piercing Talon - Allow, //Use Piercing Talon when appropriate AllowEX, //Use Piercing Talon when Enhanced + Allow, //Use Piercing Talon when appropriate Force, //Force use of Piercing Talon ForceEX, //Force use of Piercing Talon when Enhanced + Forbid, //Forbid the use of Piercing Talon } //True North strategy @@ -237,11 +237,11 @@ public static RotationModuleDefinition Definition() //Piercing Talon strategy res.Define(Track.PiercingTalon).As("Piercing Talon", "Talon", uiPriority: 20) - .AddOption(PiercingTalonStrategy.Forbid, "Forbid", "Forbid use of Piercing Talon") - .AddOption(PiercingTalonStrategy.Allow, "Allow", "Allow use of Piercing Talon only if already in combat & outside melee range") - .AddOption(PiercingTalonStrategy.AllowEX, "AllowEX", "Allow use of Piercing Talon only if Enhanced") + .AddOption(PiercingTalonStrategy.AllowEX, "AllowEX", "Allow use of Piercing Talon if already in combat, outside melee range, & is Enhanced") + .AddOption(PiercingTalonStrategy.Allow, "Allow", "Allow use of Piercing Talon if already in combat & outside melee range") .AddOption(PiercingTalonStrategy.Force, "Force", "Force Piercing Talon usage ASAP (even in melee range)") .AddOption(PiercingTalonStrategy.ForceEX, "ForceEX", "Force Piercing Talon usage ASAP when Enhanced") + .AddOption(PiercingTalonStrategy.Forbid, "Forbid", "Forbid use of Piercing Talon") .AddAssociatedActions(AID.PiercingTalon); //True North strategy @@ -1052,13 +1052,13 @@ private void QueueOGCD(AID aid, Actor? target, OGCDPriority prio, float basePrio //Determines when to use Piercing Talon private bool ShouldUsePiercingTalon(Actor? target, PiercingTalonStrategy strategy) => strategy switch { - PiercingTalonStrategy.Forbid => false, //Never use if forbidden - PiercingTalonStrategy.Allow => - Player.InCombat && target != null && !In3y(target), PiercingTalonStrategy.AllowEX => Player.InCombat && target != null && !In3y(target) && HasEffect(SID.EnhancedPiercingTalon), + PiercingTalonStrategy.Allow => + Player.InCombat && target != null && !In3y(target), PiercingTalonStrategy.Force => true, //Always use if forced PiercingTalonStrategy.ForceEX => HasEffect(SID.EnhancedPiercingTalon), + PiercingTalonStrategy.Forbid => false, //Never use if forbidden _ => false }; diff --git a/BossMod/Autorotation/akechi/AkechiGNB.cs b/BossMod/Autorotation/akechi/AkechiGNB.cs index 70ffff2953..336ee2980f 100644 --- a/BossMod/Autorotation/akechi/AkechiGNB.cs +++ b/BossMod/Autorotation/akechi/AkechiGNB.cs @@ -5,7 +5,7 @@ namespace BossMod.Autorotation.akechi; //Contribution by Akechi -//Discord @akechdz or 'Akechi' on Puni.sh for maintenance +//Discord: @akechdz or 'Akechi' on Puni.sh for maintenance //This module supports <=2.47 SkS rotation as default (or 'Automatic') //With user adjustment, 'SlowGNB' or 'FastGNB' usage is achievable @@ -518,7 +518,6 @@ public override void Execute(StrategyValues strategy, Actor? primaryTarget, floa if (ShouldUseDoubleDown(ddStrat, primaryTarget)) QueueGCD(AID.DoubleDown, primaryTarget, ddStrat == OffensiveStrategy.Force || Ammo == 1 ? GCDPriority.ForcedGCD : GCDPriority.DoubleDown); - //Gnashing Fang Combo execution if (GunComboStep == 1) QueueGCD(AID.SavageClaw, primaryTarget, gfStrat == GnashingStrategy.ForceClaw ? GCDPriority.ForcedGCD : GCDPriority.GF23); @@ -543,7 +542,6 @@ public override void Execute(StrategyValues strategy, Actor? primaryTarget, floa if (Unlocked(AID.BurstStrike) && Unlocked(AID.Bloodfest) && ShouldUseBurstStrike(strikeStrat, primaryTarget)) QueueGCD(AID.BurstStrike, primaryTarget, strikeStrat == OffensiveStrategy.Force ? GCDPriority.ForcedGCD : nmCD < 1 ? GCDPriority.ForcedGCD : GCDPriority.BurstStrike); - //Fated Circle execution var fcStrat = strategy.Option(Track.FatedCircle).As(); if (ShouldUseFatedCircle(fcStrat, primaryTarget)) From 7255a3056bce1c20832671d0654af410db96c61e Mon Sep 17 00:00:00 2001 From: Akechi-kun <167795370+Akechi-kun@users.noreply.github.com> Date: Fri, 13 Dec 2024 17:47:38 -0800 Subject: [PATCH 03/29] xdd --- BossMod/Autorotation/akechi/AkechiDRG.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/BossMod/Autorotation/akechi/AkechiDRG.cs b/BossMod/Autorotation/akechi/AkechiDRG.cs index 14353430cc..6dfea7174a 100644 --- a/BossMod/Autorotation/akechi/AkechiDRG.cs +++ b/BossMod/Autorotation/akechi/AkechiDRG.cs @@ -246,7 +246,7 @@ public static RotationModuleDefinition Definition() //True North strategy res.Define(Track.TrueNorth).As("True North", "T.North", uiPriority: 10) - .AddOption(TrueNorthStrategy.Automatic, "Automatic", "weaves True North when out of positional") + .AddOption(TrueNorthStrategy.Automatic, "Automatic", "Late-weaves True North when out of positional") .AddOption(TrueNorthStrategy.ASAP, "ASAP", "Use True North as soon as possible when out of positional", 45, 10, ActionTargets.Self, 50) .AddOption(TrueNorthStrategy.Rear, "Rear", "Use True North for rear positional only", 45, 10, ActionTargets.Self, 50) .AddOption(TrueNorthStrategy.Flank, "Flank", "Use True North for flank positional only", 45, 10, ActionTargets.Self, 50) From de6ce0bb122c0b4f207b0ad2ccd6200f872895a6 Mon Sep 17 00:00:00 2001 From: Akechi-kun <167795370+Akechi-kun@users.noreply.github.com> Date: Fri, 13 Dec 2024 17:50:15 -0800 Subject: [PATCH 04/29] xdd --- BossMod/Autorotation/akechi/AkechiDRG.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/BossMod/Autorotation/akechi/AkechiDRG.cs b/BossMod/Autorotation/akechi/AkechiDRG.cs index 6dfea7174a..546db227ce 100644 --- a/BossMod/Autorotation/akechi/AkechiDRG.cs +++ b/BossMod/Autorotation/akechi/AkechiDRG.cs @@ -236,7 +236,7 @@ public static RotationModuleDefinition Definition() .AddAssociatedActions(AID.Stardiver); //Piercing Talon strategy - res.Define(Track.PiercingTalon).As("Piercing Talon", "Talon", uiPriority: 20) + res.Define(Track.PiercingTalon).As("Piercing Talon", "P.Talon", uiPriority: 20) .AddOption(PiercingTalonStrategy.AllowEX, "AllowEX", "Allow use of Piercing Talon if already in combat, outside melee range, & is Enhanced") .AddOption(PiercingTalonStrategy.Allow, "Allow", "Allow use of Piercing Talon if already in combat & outside melee range") .AddOption(PiercingTalonStrategy.Force, "Force", "Force Piercing Talon usage ASAP (even in melee range)") From b7bf556008e1aa846f4c05940e1265e6f2139bcc Mon Sep 17 00:00:00 2001 From: Akechi-kun <167795370+Akechi-kun@users.noreply.github.com> Date: Fri, 13 Dec 2024 17:57:54 -0800 Subject: [PATCH 05/29] fix Github error --- BossMod/Autorotation/akechi/AkechiDRG.cs | 28 ++++++++++++------------ 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/BossMod/Autorotation/akechi/AkechiDRG.cs b/BossMod/Autorotation/akechi/AkechiDRG.cs index 546db227ce..a91fd018b4 100644 --- a/BossMod/Autorotation/akechi/AkechiDRG.cs +++ b/BossMod/Autorotation/akechi/AkechiDRG.cs @@ -361,7 +361,7 @@ public enum OGCDPriority private float powerLeft; //Time remaining for Power Surge private float chaosLeft; //Remaining time for Chaotic Spring DoT - private bool canWeave; //Inside Weave window + private bool canWeaveIn; //Inside Weave window private bool canWeaveInStardiver; //We can weave in Stardiver public float downtimeIn; //Duration of downtime in combat private float PotionLeft; //Remaining time for potion effect @@ -519,7 +519,7 @@ public override void Execute(StrategyValues strategy, Actor? primaryTarget, floa #endregion #region Miscellaneous - canWeave = CanWeave(AID.TrueThrust); //Check if weaving is possible + canWeaveIn = CanWeave(AID.TrueThrust); //Check if weaving is possible canWeaveInStardiver = GCD is < 2.5f and > 1.49f; //Check if weaving Stardiver is possible downtimeIn = Manager.Planner?.EstimateTimeToNextDowntime().Item2 ?? float.MaxValue; //Estimate downtime until next action PotionLeft = PotionStatusLeft(); //Get remaining potion status @@ -900,7 +900,7 @@ private void QueueOGCD(AID aid, Actor? target, OGCDPriority prio, float basePrio //Use Lance Charge automatically if the player is in combat, the target is valid, the action is ready, and there is power remaining Player.InCombat && target != null && canLC && powerLeft > 0, OffensiveStrategy.Force => canLC, //Always use if forced - OffensiveStrategy.ForceWeave => canLC && canWeave, //Always use if inside weave window + OffensiveStrategy.ForceWeave => canLC && canWeaveIn, //Always use if inside weave window OffensiveStrategy.Delay => false, //Delay usage if strategy is set to delay _ => false }; @@ -912,7 +912,7 @@ private void QueueOGCD(AID aid, Actor? target, OGCDPriority prio, float basePrio //Use Battle Litany automatically if the player is in combat, the target is valid, the action is ready, and there is power remaining Player.InCombat && target != null && canBL && powerLeft > 0, OffensiveStrategy.Force => canBL, //Always use if forced - OffensiveStrategy.ForceWeave => canBL && canWeave, //Always use if inside weave window + OffensiveStrategy.ForceWeave => canBL && canWeaveIn, //Always use if inside weave window OffensiveStrategy.Delay => false, //Delay usage if strategy is set to delay _ => false }; @@ -926,11 +926,11 @@ private void QueueOGCD(AID aid, Actor? target, OGCDPriority prio, float basePrio (ComboLastMove is AID.WheelingThrust or AID.FangAndClaw && Unlocked(AID.Drakesbane) || ComboLastMove is AID.VorpalThrust or AID.LanceBarrage && Unlocked(AID.FullThrust)), SurgeStrategy.Force => canLS, - SurgeStrategy.ForceWeave => canLS && canWeave, //Always use if inside weave window + SurgeStrategy.ForceWeave => canLS && canWeaveIn, //Always use if inside weave window SurgeStrategy.ForceNextOpti => canLS && (ComboLastMove is AID.WheelingThrust or AID.FangAndClaw && Unlocked(AID.Drakesbane) || ComboLastMove is AID.VorpalThrust or AID.LanceBarrage && Unlocked(AID.FullThrust)), - SurgeStrategy.ForceNextOptiWeave => canLS && canWeave && + SurgeStrategy.ForceNextOptiWeave => canLS && canWeaveIn && (ComboLastMove is AID.WheelingThrust or AID.FangAndClaw && Unlocked(AID.Drakesbane) || ComboLastMove is AID.VorpalThrust or AID.LanceBarrage && Unlocked(AID.FullThrust)), SurgeStrategy.Delay => false, @@ -945,7 +945,7 @@ private void QueueOGCD(AID aid, Actor? target, OGCDPriority prio, float basePrio Player.InCombat && target != null && canJump && (lcLeft > 0 || hasLC || lcCD is < 35 and > 17), JumpStrategy.ForceEX => canJump, //Always use in ForceEX strategy JumpStrategy.ForceEX2 => canJump, //Always use in ForceEX2 strategy - JumpStrategy.ForceWeave => canJump && canWeave, //Always use if inside weave window + JumpStrategy.ForceWeave => canJump && canWeaveIn, //Always use if inside weave window JumpStrategy.Delay => false, //Delay usage if strategy is set to delay _ => false }; @@ -958,7 +958,7 @@ private void QueueOGCD(AID aid, Actor? target, OGCDPriority prio, float basePrio Player.InCombat && target != null && In3y(target) && canDD && hasLC && hasBL, DragonfireStrategy.Force => canDD, //Always use if forced DragonfireStrategy.ForceEX => canDD, //Always use in ForceEX strategy - DragonfireStrategy.ForceWeave => canDD && canWeave, //Always use if inside weave window + DragonfireStrategy.ForceWeave => canDD && canWeaveIn, //Always use if inside weave window DragonfireStrategy.Delay => false, //Delay usage if strategy is set to delay _ => false }; @@ -971,7 +971,7 @@ private void QueueOGCD(AID aid, Actor? target, OGCDPriority prio, float basePrio Player.InCombat && In15y(target) && canGeirskogul && hasLC, GeirskogulStrategy.Force => canGeirskogul, //Always use if forced GeirskogulStrategy.ForceEX => canGeirskogul, //Always use if forced - GeirskogulStrategy.ForceWeave => canGeirskogul && canWeave, //Always use if inside weave window + GeirskogulStrategy.ForceWeave => canGeirskogul && canWeaveIn, //Always use if inside weave window GeirskogulStrategy.Delay => false, //Delay usage if strategy is set to delay _ => false }; @@ -983,7 +983,7 @@ private void QueueOGCD(AID aid, Actor? target, OGCDPriority prio, float basePrio //Use Mirage Dive automatically if the player is in combat, the target is valid, and Dive Ready effect is active Player.InCombat && target != null && canMD, OffensiveStrategy.Force => canMD, //Always use if forced - OffensiveStrategy.ForceWeave => canMD && canWeave, //Always use if inside weave window + OffensiveStrategy.ForceWeave => canMD && canWeaveIn, //Always use if inside weave window OffensiveStrategy.Delay => false, //Delay usage if strategy is set to delay _ => false }; @@ -995,7 +995,7 @@ private void QueueOGCD(AID aid, Actor? target, OGCDPriority prio, float basePrio //Use Nastrond automatically if the player is in combat, has Nastrond ready, the target is within 15y, and Lance Charge is active Player.InCombat && In15y(target) && canNastrond, OffensiveStrategy.Force => canNastrond, //Always use if forced - OffensiveStrategy.ForceWeave => canNastrond && canWeave, //Always use if inside weave window + OffensiveStrategy.ForceWeave => canNastrond && canWeaveIn, //Always use if inside weave window OffensiveStrategy.Delay => false, //Delay usage if strategy is set to delay _ => false }; @@ -1020,7 +1020,7 @@ private void QueueOGCD(AID aid, Actor? target, OGCDPriority prio, float basePrio //Use Wyrmwind Thrust automatically if the player is in combat, the target is within 15y, and focus count is exactly 2 Player.InCombat && target != null && In15y(target) && canWT && focusCount is 2 && lcCD > GCDLength * 3, OffensiveStrategy.Force => canWT, //Always use if forced - OffensiveStrategy.ForceWeave => canWT && canWeave, //Always use if inside weave window + OffensiveStrategy.ForceWeave => canWT && canWeaveIn, //Always use if inside weave window OffensiveStrategy.Delay => false, //Delay usage if strategy is set to delay _ => false }; @@ -1032,7 +1032,7 @@ private void QueueOGCD(AID aid, Actor? target, OGCDPriority prio, float basePrio //Use Rise of the Dragon automatically if the player is in combat, the target is valid, and Dragon's Flight effect is active Player.InCombat && target != null && canROTD, OffensiveStrategy.Force => canROTD, //Always use if forced - OffensiveStrategy.ForceWeave => canROTD && canWeave, //Always use if inside weave window + OffensiveStrategy.ForceWeave => canROTD && canWeaveIn, //Always use if inside weave window OffensiveStrategy.Delay => false, //Delay usage if strategy is set to delay _ => false }; @@ -1044,7 +1044,7 @@ private void QueueOGCD(AID aid, Actor? target, OGCDPriority prio, float basePrio //Use Starcross automatically if the player is in combat, the target is valid, and Starcross Ready effect is active Player.InCombat && target != null && canSC, OffensiveStrategy.Force => canSC, //Always use if forced - OffensiveStrategy.ForceWeave => canSC && canWeave, //Always use if inside weave window + OffensiveStrategy.ForceWeave => canSC && canWeaveIn, //Always use if inside weave window OffensiveStrategy.Delay => false, //Delay usage if strategy is set to delay _ => false }; From 1a1d86777009761e09fa4e4bd75178af23129865 Mon Sep 17 00:00:00 2001 From: Akechi-kun <167795370+Akechi-kun@users.noreply.github.com> Date: Sat, 14 Dec 2024 02:11:37 -0800 Subject: [PATCH 06/29] DIves Strategy + major cleanup --- BossMod/Autorotation/akechi/AkechiDRG.cs | 382 +++++++++++++++-------- 1 file changed, 247 insertions(+), 135 deletions(-) diff --git a/BossMod/Autorotation/akechi/AkechiDRG.cs b/BossMod/Autorotation/akechi/AkechiDRG.cs index a91fd018b4..fb0899e67b 100644 --- a/BossMod/Autorotation/akechi/AkechiDRG.cs +++ b/BossMod/Autorotation/akechi/AkechiDRG.cs @@ -16,6 +16,7 @@ public enum Track AOE, //Area of Effect actions Spears, //Spear actions Burst, //Burst actions + Dives, //Dive actions Potion, //Potion usage LifeSurge, //Life Surge ability Jump, //Jump ability @@ -38,7 +39,7 @@ public enum AOEStrategy { AutoTargetHitPrimary, //Auto-target AOE actions to hit primary target as well AutoTargetHitMost, //Auto-target AOE actions to hit most targets - AutoOnPrimary, //Use AOE actions on primary target + AutoonPrimary, //Use AOE actions on primary target ForceST, //Force single-target abilities Force123ST, //Force single-target 123 combo ForceBuffsST, //Force single-target buffs combo @@ -60,6 +61,14 @@ public enum BurstStrategy UnderPotion //Burst when a potion is active } + public enum DivesStrategy + { + AllowMaxMelee, //Allow Dives only at max melee range + AllowCloseMelee, //Allow Dives only at close melee range + Allow, //Allow the use of Dives + Forbid, //Forbid the use of Dives + } + //Potion usage strategies public enum PotionStrategy { @@ -155,18 +164,24 @@ public enum OffensiveStrategy //Module Definitions public static RotationModuleDefinition Definition() { - //Module title & signature - var res = new RotationModuleDefinition("Akechi DRG", "Standard Rotation Module", "Standard rotation (Akechi)", "Akechi", RotationModuleQuality.Good, BitMask.Build(Class.LNC, Class.DRG), 100); + var res = new RotationModuleDefinition( + "Akechi DRG", //Name + "Standard Rotation Module", //Type + "Standard rotation (Akechi)", //Category + "Akechi", //Contributor of module + RotationModuleQuality.Good, //Quality + BitMask.Build(Class.LNC, Class.DRG), //Class and Job + 100); //Max Level supported #region Custom Strategies //Targeting strategy res.Define(Track.AOE).As("Combo Option", "AOE", uiPriority: 200) .AddOption(AOEStrategy.AutoTargetHitPrimary, "AutoTargetHitPrimary", "Use AOE actions if profitable, select best target that ensures primary target is hit") .AddOption(AOEStrategy.AutoTargetHitMost, "AutoTargetHitMost", "Use AOE actions if profitable, select a target that ensures maximal number of targets are hit") - .AddOption(AOEStrategy.AutoOnPrimary, "AutoOnPrimary", "Use AOE actions on primary target if profitable") + .AddOption(AOEStrategy.AutoonPrimary, "AutoonPrimary", "Use AOE actions on primary target if profitable") .AddOption(AOEStrategy.ForceST, "Force ST", "Force Single-Target rotation") - .AddOption(AOEStrategy.Force123ST, "Only 1-2-3 ST", "Force only ST 1-2-3 rotation (No Buffs)") - .AddOption(AOEStrategy.ForceBuffsST, "Only 1-4-5 ST", "Force only ST 1-4-5 rotation (Buffs Only)") + .AddOption(AOEStrategy.Force123ST, "only 1-2-3 ST", "Force only ST 1-2-3 rotation (No Buffs)") + .AddOption(AOEStrategy.ForceBuffsST, "only 1-4-5 ST", "Force only ST 1-4-5 rotation (Buffs only)") .AddOption(AOEStrategy.ForceAOE, "Force AOE", "Force AOE rotation, even if less than 3 targets"); //Spear targeting strategy @@ -177,10 +192,17 @@ public static RotationModuleDefinition Definition() //Burst strategy res.Define(Track.Burst).As("Burst", uiPriority: 190) .AddOption(BurstStrategy.Automatic, "Automatic", "Use Burst optimally based on situation") - .AddOption(BurstStrategy.Conserve, "Conserve", "Conserve resources until optimal burst timing") + .AddOption(BurstStrategy.Conserve, "Conserve", "Conserve all cooldowns") .AddOption(BurstStrategy.UnderRaidBuffs, "Under Raid Buffs", "Burst under raid buffs; conserve otherwise (ignores potion usage)") .AddOption(BurstStrategy.UnderPotion, "Under Potion", "Burst under potion, conserve otherwise (ignores raid buffs)"); + //Dives strategy + res.Define(Track.Dives).As("Dives", uiPriority: 185) + .AddOption(DivesStrategy.AllowMaxMelee, "Allow Max Melee", "Allow Jump, Stardiver, & Dragonfire Dive only at max melee range (within 3y)") + .AddOption(DivesStrategy.AllowCloseMelee, "Allow Close Melee", "Allow Jump, Stardiver, & Dragonfire Dive only at close melee range (within 1y)") + .AddOption(DivesStrategy.Allow, "Allow", "Allow the use of Jump, Stardiver, & Dragonfire Dive at any distance") + .AddOption(DivesStrategy.Forbid, "Forbid", "Forbid the use of Jump, Stardiver, & Dragonfire Dive"); + //Potion strategy res.Define(Track.Potion).As("Potion", uiPriority: 180) .AddOption(PotionStrategy.Manual, "Manual", "Do not use potions automatically") @@ -353,25 +375,8 @@ public enum OGCDPriority #endregion #region Placeholders for Variables - - private float GCDLength; //Length of the global cooldown - private float blCD; //Cooldown for Battle Litany - private float lcLeft; //Time remaining for Lance Charge - private float lcCD; //Cooldown for Lance Charge - private float powerLeft; //Time remaining for Power Surge - private float chaosLeft; //Remaining time for Chaotic Spring DoT - private bool canWeaveIn; //Inside Weave window private bool canWeaveInStardiver; //We can weave in Stardiver - public float downtimeIn; //Duration of downtime in combat - private float PotionLeft; //Remaining time for potion effect - private float RaidBuffsLeft; //Time left for raid buffs - private float RaidBuffsIn; //Time until next raid buffs are applied - public float BurstWindowLeft; //Time left in the burst window - public float BurstWindowIn; //Time until the next burst window starts - - private int focusCount; //Count of Firstmind's Focus gauge - private bool hasLOTD; //Flag for Life of the Dragon status private bool hasLC; //Flag for Lance Charge status private bool hasBL; //Flag for Battle Litany status @@ -379,7 +384,6 @@ public enum OGCDPriority private bool hasDF; //Flag for Dragon's Flight status private bool hasSC; //Flag for Starcross status private bool hasNastrond; //Flag for Nastrond status - private bool canLC; //Ability to use Lance Charge private bool canBL; //Ability to use Battle Litany private bool canLS; //Ability to use Life Surge @@ -392,38 +396,52 @@ public enum OGCDPriority private bool canWT; //Ability to use Wyrmwind Thrust private bool canROTD; //Ability to use Rise of the Dragon private bool canSC; //Ability to use Starcross - + private float GCDLength; //Length of the global cooldown + private float blCD; //Cooldown for Battle Litany + private float lcLeft; //Time remaining for Lance Charge + private float lcCD; //Cooldown for Lance Charge + private float powerLeft; //Time remaining for Power Surge + private float chaosLeft; //Remaining time for Chaotic Spring DoT + private float PotionLeft; //Remaining time for potion effect + private float RaidBuffsLeft; //Time left for raid buffs + private float RaidBuffsIn; //Time until next raid buffs are applied + public float downtimeIn; //Duration of downtime in combat + public float BurstWindowLeft; //Time left in the burst window + public float BurstWindowIn; //Time until the next burst window starts + private int focusCount; //Count of Firstmind's Focus gauge public AID NextGCD; //Next global cooldown action to be used private GCDPriority NextGCDPrio; //Priority of the next GCD for cooldown decision making - #endregion #region Module Helpers - //Check if the desired ability is unlocked + //Checks if the desired ability is unlocked private bool Unlocked(AID aid) => ActionUnlocked(ActionID.MakeSpell(aid)); - //Get the last action used in the combo sequence + //Gets the last action used in the combo sequence private AID ComboLastMove => (AID)World.Client.ComboState.Action; - //Check if status effect is on self + //Checks if status effect is on Self public bool HasEffect(SID sid) => SelfStatusLeft(sid) > 0; - //Check if the desired action is ready (cooldown < 0.6 seconds) + //Checks if the desired action is ready (cooldown < 0.6 seconds) private bool ActionReady(AID aid) => World.Client.Cooldowns[ActionDefinitions.Instance.Spell(aid)!.MainCooldownGroup].Remaining < 0.6f; //Get remaining cooldown time for the specified action private float CD(AID aid) => World.Client.Cooldowns[ActionDefinitions.Instance.Spell(aid)!.MainCooldownGroup].Remaining; - //Check if the target is within melee range (3 yalms) - private bool In3y(Actor? target) => Player.DistanceToHitbox(target) <= 3; + //Checks if the target is within max melee range (3 yalms) + private bool In3y(Actor? target) => Player.DistanceToHitbox(target) < 4; + + //Checks if the target is within close melee range (1 yalm) + private bool In1y(Actor? target) => Player.DistanceToHitbox(target) < 1; - //Check if the target is within 15 yalms - private bool In15y(Actor? target) => Player.DistanceToHitbox(target) <= 14.75; + //Checks if the target is within 15 yalms + private bool In15y(Actor? target) => Player.DistanceToHitbox(target) <= 14.9; //Checks if we can weave an ability public bool CanWeave(AID aid, double weaveTime = 0.7) => CD(aid) > weaveTime; - //Check if the potion should be used before raid buffs expire + //Checks if the potion should be used before raid buffs expire private bool IsPotionBeforeRaidbuffs() => RaidBuffsLeft == 0 && PotionLeft > RaidBuffsIn + 17.5f; #region Targeting Helpers @@ -444,11 +462,12 @@ public enum OGCDPriority { AOEStrategy.AutoTargetHitPrimary => FindBetterTargetBy(primaryTarget, range, t => primaryTarget == null || check(t, primaryTarget) ? numTargets(t) : 0), AOEStrategy.AutoTargetHitMost => FindBetterTargetBy(primaryTarget, range, numTargets), - AOEStrategy.AutoOnPrimary => (primaryTarget, primaryTarget != null ? numTargets(primaryTarget) : 0), + AOEStrategy.AutoonPrimary => (primaryTarget, primaryTarget != null ? numTargets(primaryTarget) : 0), AOEStrategy.ForceAOE => (primaryTarget, int.MaxValue), _ => (null, 0) }; + //Check targeting for Spear strategies private (Actor?, int) CheckSpearTargeting(SpearTargetingStrategy strategy, Actor? primaryTarget, float range, Func numTargets, Func check) => strategy switch { SpearTargetingStrategy.AutoTargetHitPrimary => FindBetterTargetBy(primaryTarget, range, t => primaryTarget == null || check(t, primaryTarget) ? numTargets(t) : 0), @@ -468,10 +487,10 @@ public enum OGCDPriority }; //Check if Player is on Rear (back) positional - private bool IsOnRear(Actor target) => GetCurrentPositional(target) == Positional.Rear; + private bool IsonRear(Actor target) => GetCurrentPositional(target) == Positional.Rear; //Check if Player is on Flank (side) positional - private bool IsOnFlank(Actor target) => GetCurrentPositional(target) == Positional.Flank; + private bool IsonFlank(Actor target) => GetCurrentPositional(target) == Positional.Flank; #endregion #endregion @@ -481,12 +500,10 @@ public override void Execute(StrategyValues strategy, Actor? primaryTarget, floa #region Variables #region Cooldown-related - //Gauge Management var gauge = World.Client.GetGauge(); //Retrieve Dragoon gauge data focusCount = gauge.FirstmindsFocusCount; //Update focus count from the gauge hasLOTD = gauge.LotdTimer > 0; //Check if Life of the Dragon (LOTD) is active - //Cooldown Checks lcCD = CD(AID.LanceCharge); //Get cooldown for Lance Charge lcLeft = SelfStatusLeft(SID.LanceCharge, 20); //Get remaining time for Lance Charge effect @@ -501,7 +518,6 @@ public override void Execute(StrategyValues strategy, Actor? primaryTarget, floa hasBL = blCD is >= 100 and <= 120; //Check if Battle Litany is within cooldown range hasDF = HasEffect(SID.DragonsFlight); //Check if Dragon's Flight effect is active hasSC = HasEffect(SID.StarcrossReady); //Check if Starcross is ready - //Minimum Conditions canLC = Unlocked(AID.LanceCharge) && ActionReady(AID.LanceCharge); //minimum condition(s) to execute Lance Charge canBL = Unlocked(AID.BattleLitany) && ActionReady(AID.BattleLitany); //minimum condition(s) to execute Battle Litany @@ -515,7 +531,6 @@ public override void Execute(StrategyValues strategy, Actor? primaryTarget, floa canWT = Unlocked(AID.WyrmwindThrust) && ActionReady(AID.WyrmwindThrust); //minimum condition(s) to execute Wyrmwind Thrust canROTD = Unlocked(AID.RiseOfTheDragon) && hasDF; //minimum condition(s) to execute Rise of the Dragon canSC = Unlocked(AID.Starcross) && hasSC; //minimum condition(s) to execute Starcross - #endregion #region Miscellaneous @@ -526,29 +541,26 @@ public override void Execute(StrategyValues strategy, Actor? primaryTarget, floa (RaidBuffsLeft, RaidBuffsIn) = EstimateRaidBuffTimings(primaryTarget); //Estimate remaining raid buffs NextGCD = AID.None; //Set next GCD ability NextGCDPrio = GCDPriority.None; //Set next GCD priority - #endregion #endregion #region AOEStrategy 'Force' Execution - var AOEStrategy = strategy.Option(Track.AOE).As(); //Retrieve the current AOE strategy var SpearStrategy = strategy.Option(Track.Spears).As(); //Retrieve the current Spear targeting strategy //Force specific actions based on the AOE strategy selected - if (AOEStrategy == AOEStrategy.ForceST) //If forced single target + if (AOEStrategy == AOEStrategy.ForceST) //if forced single target QueueGCD(NextFullST(), primaryTarget, GCDPriority.ForcedGCD); //Queue the next single target action - if (AOEStrategy == AOEStrategy.Force123ST) //If forced 123 combo - QueueGCD(UseOnly123ST(), primaryTarget, GCDPriority.ForcedGCD); //Queue the 123 combo action + if (AOEStrategy == AOEStrategy.Force123ST) //if forced 123 combo + QueueGCD(Useonly123ST(), primaryTarget, GCDPriority.ForcedGCD); //Queue the 123 combo action - if (AOEStrategy == AOEStrategy.ForceBuffsST) //If forced buffs combo - QueueGCD(UseOnly145ST(), primaryTarget, GCDPriority.ForcedGCD); //Queue the buffed 145 combo action + if (AOEStrategy == AOEStrategy.ForceBuffsST) //if forced buffs combo + QueueGCD(Useonly145ST(), primaryTarget, GCDPriority.ForcedGCD); //Queue the buffed 145 combo action - if (AOEStrategy == AOEStrategy.ForceAOE) //If forced AOE action + if (AOEStrategy == AOEStrategy.ForceAOE) //if forced AOE action QueueGCD(NextFullAOE(), primaryTarget, GCDPriority.ForcedGCD); //Queue the next AOE action - #endregion #region Burst Window Strategy @@ -559,16 +571,33 @@ public override void Execute(StrategyValues strategy, Actor? primaryTarget, floa //Set burst window timings based on the selected burst strategy (BurstWindowIn, BurstWindowLeft) = burstStrategy switch { - BurstStrategy.Automatic => (RaidBuffsIn, IsPotionBeforeRaidbuffs() ? 0 : Math.Max(PotionLeft, RaidBuffsLeft)), //Automatic mode: Set timings based on raid buffs and potion availability - BurstStrategy.UnderRaidBuffs => (RaidBuffsIn, RaidBuffsLeft), //Under Raid Buffs: Set timings directly from raid buffs - BurstStrategy.UnderPotion => (PotionCD, PotionLeft), //Under Potion: Use potion cooldown and remaining time - _ => (0, 0) //Default case: Set timings to zero + BurstStrategy.Automatic => (RaidBuffsIn, IsPotionBeforeRaidbuffs() ? 0 : Math.Max(PotionLeft, RaidBuffsLeft)), //Set timings based on raid buffs and potion availability + BurstStrategy.UnderRaidBuffs => (RaidBuffsIn, RaidBuffsLeft), //Set timings directly from raid buffs + BurstStrategy.UnderPotion => (PotionCD, PotionLeft), //Use potion cooldown and remaining time + _ => (0, 0) //Set timings to zero }; + #endregion + #region Dives Strategy + //Dive strategy + var dive = strategy.Option(Track.Dives).As(); //Retrieve the current dive strategy + var diveStrategy = dive switch + { + DivesStrategy.AllowMaxMelee => In3y(primaryTarget), //Only allow max melee if target is within 3 yalms + DivesStrategy.AllowCloseMelee => In1y(primaryTarget), //Only allow close melee if target is within 1 yalm + DivesStrategy.Allow => true, //Always allow dives + DivesStrategy.Forbid => false, //Never allow dives + _ => false, + }; + + var maxMelee = dive == DivesStrategy.AllowMaxMelee; //Check if max melee is allowed + var closeMelee = dive == DivesStrategy.AllowCloseMelee; //Check if close melee is allowed + var allowed = dive == DivesStrategy.Allow; //Check if dives are allowed + var forbidden = dive == DivesStrategy.Forbid; //Check if dives are forbidden + var divesGood = diveStrategy && (maxMelee || closeMelee || allowed) && !forbidden; //Check if dives are good to use #endregion #region Targeting - //Check if Doom Spike is unlocked; if so, determine the best AOE target and count var (AOEBestTarget, AOETargetCount) = Unlocked(AID.DoomSpike) ? CheckAOETargeting(AOEStrategy, primaryTarget, 10, NumTargetsHitByAOE, IsHitByAOE) @@ -589,104 +618,187 @@ public override void Execute(StrategyValues strategy, Actor? primaryTarget, floa //Select the best target for AOE based on whether it's viable, or default to primary target var bestAOEtarget = useAOE ? AOEBestTarget : primaryTarget; - #endregion #region Combo & Cooldown Execution - //Combo Action evecution QueueGCD(useAOE ? NextFullAOE() : NextFullST(), bestAOEtarget, GCDPriority.Combo123); //Execute Lance Charge if available - var lcStrat = strategy.Option(Track.LanceCharge).As(); - if (!hold && ShouldUseLanceCharge(lcStrat, primaryTarget)) - QueueOGCD(AID.LanceCharge, Player, lcStrat is OffensiveStrategy.Force or OffensiveStrategy.ForceWeave ? OGCDPriority.ForcedOGCD : OGCDPriority.Buffs); + var lcStrat = strategy.Option(Track.LanceCharge).As(); //Retrieve the Lance Charge strategy + if (!hold && //if not holding burst + ShouldUseLanceCharge(lcStrat, primaryTarget)) //if Lance Charge should be used + QueueOGCD(AID.LanceCharge, //Queue Lance Charge + Player, //on Self + lcStrat is OffensiveStrategy.Force //if strategy is Force + or OffensiveStrategy.ForceWeave //or Force Weave + ? OGCDPriority.ForcedOGCD //set priority to Forced oGCD + : OGCDPriority.Buffs); //otherwise, set priority to Buffs //Execute Battle Litany if available - var blStrat = strategy.Option(Track.BattleLitany).As(); - if (!hold && ShouldUseBattleLitany(blStrat, primaryTarget)) - QueueOGCD(AID.BattleLitany, Player, blStrat is OffensiveStrategy.Force or OffensiveStrategy.ForceWeave ? OGCDPriority.ForcedOGCD : OGCDPriority.Buffs); + var blStrat = strategy.Option(Track.BattleLitany).As(); //Retrieve the Battle Litany strategy + if (!hold && //if not holding burst + ShouldUseBattleLitany(blStrat, primaryTarget)) //if Battle Litany should be used + QueueOGCD(AID.BattleLitany, //Queue Battle Litany + Player, //on Self + blStrat is OffensiveStrategy.Force //if strategy is Force + or OffensiveStrategy.ForceWeave //or Force Weave + ? OGCDPriority.ForcedOGCD //set priority to Forced oGCD + : OGCDPriority.Buffs); //otherwise, set priority to Buffs //Execute Life Surge if conditions met - var lsStrat = strategy.Option(Track.LifeSurge).As(); - if (!hold && ShouldUseLifeSurge(lsStrat, primaryTarget)) - QueueOGCD(AID.LifeSurge, Player, lsStrat is SurgeStrategy.Force or SurgeStrategy.ForceWeave or SurgeStrategy.ForceNextOpti or SurgeStrategy.ForceNextOptiWeave ? OGCDPriority.ForcedOGCD : OGCDPriority.Buffs); + var lsStrat = strategy.Option(Track.LifeSurge).As(); //Retrieve the Life Surge strategy + if (!hold && //if not holding burst + ShouldUseLifeSurge(lsStrat, primaryTarget)) //if Life Surge should be used + QueueOGCD(AID.LifeSurge, //Queue Life Surge + Player, //on Self + lsStrat is SurgeStrategy.Force //if strategy is Force + or SurgeStrategy.ForceWeave //or Force Weave + or SurgeStrategy.ForceNextOpti //or Force Next Optimal Window + or SurgeStrategy.ForceNextOptiWeave //or Force Next Optimal Weave Window + ? OGCDPriority.ForcedOGCD //set priority to Forced oGCD + : OGCDPriority.Buffs); //otherwise, set priority to Buffs //Execute Jump ability if available - var jumpStrat = strategy.Option(Track.Jump).As(); - if (!hold && ShouldUseJump(jumpStrat, primaryTarget)) - QueueOGCD(Unlocked(AID.HighJump) ? AID.HighJump : AID.Jump, primaryTarget, jumpStrat is JumpStrategy.Force or JumpStrategy.ForceEX or JumpStrategy.ForceEX2 or JumpStrategy.ForceWeave ? OGCDPriority.ForcedOGCD : OGCDPriority.Jump); + var jumpStrat = strategy.Option(Track.Jump).As(); //Retrieve the Jump strategy + if (!hold && divesGood && //if not holding burst and dives are good + ShouldUseJump(jumpStrat, primaryTarget)) //if Jump should be used + QueueOGCD(Unlocked(AID.HighJump) ? AID.HighJump : AID.Jump, //Queue High Jump if unlocked, otherwise Jump + primaryTarget, //on the primary target + jumpStrat is JumpStrategy.Force //if strategy is Force + or JumpStrategy.ForceEX //or Force EX + or JumpStrategy.ForceEX2 //or Force EX2 + or JumpStrategy.ForceWeave //or Force Weave + ? OGCDPriority.ForcedOGCD //set priority to Forced oGCD + : OGCDPriority.Jump); //otherwise, set priority to Jump //Execute Dragonfire Dive if available - var ddStrat = strategy.Option(Track.DragonfireDive).As(); - if (!hold && ShouldUseDragonfireDive(ddStrat, primaryTarget)) - QueueOGCD(AID.DragonfireDive, primaryTarget, ddStrat is DragonfireStrategy.Force or DragonfireStrategy.ForceWeave ? OGCDPriority.ForcedOGCD : OGCDPriority.DragonfireDive); + var ddStrat = strategy.Option(Track.DragonfireDive).As(); //Retrieve the Dragonfire Dive strategy + if (!hold && divesGood && //if not holding burst and dives are good + ShouldUseDragonfireDive(ddStrat, primaryTarget)) //if Dragonfire Dive should be used + QueueOGCD(AID.DragonfireDive, //Queue Dragonfire Dive + primaryTarget, //on the primary target + ddStrat is DragonfireStrategy.Force //if strategy is Force + or DragonfireStrategy.ForceWeave //or Force Weave + ? OGCDPriority.ForcedOGCD //set priority to Forced oGCD + : OGCDPriority.DragonfireDive); //otherwise, set priority to Dragonfire Dive //Execute Geirskogul if available - var geirskogul = strategy.Option(Track.Geirskogul).As(); - if (!hold && ShouldUseGeirskogul(geirskogul, primaryTarget)) - QueueOGCD(AID.Geirskogul, bestSpeartarget, geirskogul is GeirskogulStrategy.Force or GeirskogulStrategy.ForceEX or GeirskogulStrategy.ForceWeave ? OGCDPriority.ForcedOGCD : OGCDPriority.Geirskogul); + var geirskogul = strategy.Option(Track.Geirskogul).As(); //Retrieve the Geirskogul strategy + if (!hold && //if not holding burst + ShouldUseGeirskogul(geirskogul, primaryTarget)) //if Geirskogul should be used + QueueOGCD(AID.Geirskogul, //Queue Geirskogul + bestSpeartarget, //on the best Spear target + geirskogul is GeirskogulStrategy.Force //if strategy is Force + or GeirskogulStrategy.ForceEX //or Force EX + or GeirskogulStrategy.ForceWeave //or Force Weave + ? OGCDPriority.ForcedOGCD //set priority to Forced oGCD + : OGCDPriority.Geirskogul); //otherwise, set priority to Geirskogul //Execute Mirage Dive if available - var mirageStrat = strategy.Option(Track.MirageDive).As(); - if (!hold && ShouldUseMirageDive(mirageStrat, primaryTarget)) - QueueOGCD(AID.MirageDive, primaryTarget, mirageStrat is OffensiveStrategy.Force or OffensiveStrategy.ForceWeave ? OGCDPriority.ForcedOGCD : OGCDPriority.MirageDive); + var mirageStrat = strategy.Option(Track.MirageDive).As(); //Retrieve the Mirage Dive strategy + if (!hold && //if not holding burst + ShouldUseMirageDive(mirageStrat, primaryTarget)) //if Mirage Dive should be used + QueueOGCD(AID.MirageDive, //Queue Mirage Dive + primaryTarget, //on the primary target + mirageStrat is OffensiveStrategy.Force //if strategy is Force + or OffensiveStrategy.ForceWeave //or Force Weave + ? OGCDPriority.ForcedOGCD //set priority to Forced oGCD + : OGCDPriority.MirageDive); //otherwise, set priority to Mirage Dive //Execute Nastrond if available - var nastrondStrat = strategy.Option(Track.Nastrond).As(); - if (!hold && ShouldUseNastrond(nastrondStrat, primaryTarget)) - QueueOGCD(AID.Nastrond, bestSpeartarget, nastrondStrat is OffensiveStrategy.Force or OffensiveStrategy.ForceWeave ? OGCDPriority.ForcedOGCD : OGCDPriority.Nastrond); + var nastrondStrat = strategy.Option(Track.Nastrond).As(); //Retrieve the Nastrond strategy + if (!hold && //if not holding burst + ShouldUseNastrond(nastrondStrat, primaryTarget)) //if Nastrond should be used + QueueOGCD(AID.Nastrond, //Queue Nastrond + bestSpeartarget, //on the best Spear target + nastrondStrat is OffensiveStrategy.Force //if strategy is Force + or OffensiveStrategy.ForceWeave //or Force Weave + ? OGCDPriority.ForcedOGCD //set priority to Forced oGCD + : OGCDPriority.Nastrond); //otherwise, set priority to Nastrond //Execute Stardiver if available - var sdStrat = strategy.Option(Track.Stardiver).As(); - if (!hold && ShouldUseStardiver(sdStrat, primaryTarget)) - QueueOGCD(AID.Stardiver, primaryTarget, sdStrat is StardiverStrategy.Force or StardiverStrategy.ForceEX or StardiverStrategy.ForceWeave ? OGCDPriority.ForcedOGCD : OGCDPriority.Stardiver); + var sdStrat = strategy.Option(Track.Stardiver).As(); //Retrieve the Stardiver strategy + if (!hold && divesGood && //if not holding burst and dives are good + ShouldUseStardiver(sdStrat, primaryTarget)) //if Stardiver should be used + QueueOGCD(AID.Stardiver, //Queue Stardiver + primaryTarget, //on the primary target + sdStrat is StardiverStrategy.Force //on the primary target + or StardiverStrategy.ForceEX //or Force EX + or StardiverStrategy.ForceWeave //or Force Weave + ? OGCDPriority.ForcedOGCD //set priority to Forced oGCD + : OGCDPriority.Stardiver); //otherwise, set priority to Stardiver //Execute Wyrmwind Thrust if available - var wtStrat = strategy.Option(Track.WyrmwindThrust).As(); - if (!hold && ShouldUseWyrmwindThrust(wtStrat, primaryTarget)) - QueueOGCD(AID.WyrmwindThrust, bestSpeartarget, wtStrat is OffensiveStrategy.Force or OffensiveStrategy.ForceWeave ? OGCDPriority.ForcedOGCD : HasEffect(SID.LanceCharge) ? OGCDPriority.WyrmwindThrustOpti : OGCDPriority.WyrmwindThrust); + var wtStrat = strategy.Option(Track.WyrmwindThrust).As(); //Retrieve the Wyrmwind Thrust strategy + if (!hold && //if not holding burst + ShouldUseWyrmwindThrust(wtStrat, primaryTarget)) //if Wyrmwind Thrust should be used + QueueOGCD(AID.WyrmwindThrust, //Queue Wyrmwind Thrust + bestSpeartarget, wtStrat is OffensiveStrategy.Force //on the best Spear target + or OffensiveStrategy.ForceWeave //or Force Weave + ? OGCDPriority.ForcedOGCD //set priority to Forced oGCD + : HasEffect(SID.LanceCharge) //if Lance Charge is active + ? OGCDPriority.WyrmwindThrustOpti //set priority to Optimal Wyrmwind Thrust + : OGCDPriority.WyrmwindThrust); //otherwise, set priority to Wyrmwind Thrust //Execute Rise of the Dragon if available - var riseStrat = strategy.Option(Track.RiseOfTheDragon).As(); - if (!hold && ShouldUseRiseOfTheDragon(riseStrat, primaryTarget)) - QueueOGCD(AID.RiseOfTheDragon, primaryTarget, riseStrat is OffensiveStrategy.Force or OffensiveStrategy.ForceWeave ? OGCDPriority.ForcedOGCD : OGCDPriority.Buffs); + var riseStrat = strategy.Option(Track.RiseOfTheDragon).As(); //Retrieve the Rise of the Dragon strategy + if (ShouldUseRiseOfTheDragon(riseStrat, primaryTarget)) //if Rise of the Dragon should be used + QueueOGCD(AID.RiseOfTheDragon, //Queue Rise of the Dragon + primaryTarget, //on the primary target + riseStrat is OffensiveStrategy.Force //if strategy is Force + or OffensiveStrategy.ForceWeave //or Force Weave + ? OGCDPriority.ForcedOGCD //set priority to Forced oGCD + : OGCDPriority.Buffs); //otherwise, set priority to Buffs //Execute Starcross if available - var crossStrat = strategy.Option(Track.Starcross).As(); - if (!hold && ShouldUseStarcross(crossStrat, primaryTarget)) - QueueOGCD(AID.Starcross, primaryTarget, crossStrat is OffensiveStrategy.Force or OffensiveStrategy.ForceWeave ? OGCDPriority.ForcedOGCD : OGCDPriority.Starcross); + var crossStrat = strategy.Option(Track.Starcross).As(); //Retrieve the Starcross strategy + if (ShouldUseStarcross(crossStrat, primaryTarget)) //if Starcross should be used + QueueOGCD(AID.Starcross, //Queue Starcross + primaryTarget, //on the primary target + crossStrat is OffensiveStrategy.Force //if strategy is Force + or OffensiveStrategy.ForceWeave //or Force Weave + ? OGCDPriority.ForcedOGCD //set priority to Forced oGCD + : OGCDPriority.Starcross); //otherwise, set priority to Starcross //Execute Piercing Talon if available - var ptStrat = strategy.Option(Track.PiercingTalon).As(); - if (ShouldUsePiercingTalon(primaryTarget, ptStrat)) - QueueGCD(AID.PiercingTalon, primaryTarget, ptStrat is PiercingTalonStrategy.Force or PiercingTalonStrategy.ForceEX ? GCDPriority.ForcedGCD : GCDPriority.NormalGCD); + var ptStrat = strategy.Option(Track.PiercingTalon).As(); //Retrieve the Piercing Talon strategy + if (ShouldUsePiercingTalon(primaryTarget, ptStrat)) //if Piercing Talon should be used + QueueGCD(AID.PiercingTalon, //Queue Piercing Talon + primaryTarget, //on the primary target + ptStrat is PiercingTalonStrategy.Force or //if strategy is Force + PiercingTalonStrategy.ForceEX //or Force EX + ? GCDPriority.ForcedGCD //set priority to Forced GCD + : GCDPriority.NormalGCD); //otherwise, set priority to Normal GCD //Execute Potion if available - if (ShouldUsePotion(strategy.Option(Track.Potion).As())) - Hints.ActionsToExecute.Push(ActionDefinitions.IDPotionStr, Player, ActionQueue.Priority.VeryHigh + (int)OGCDPriority.ForcedOGCD, 0, GCD - 0.9f); + if (ShouldUsePotion(strategy.Option(Track.Potion).As())) //if Potion should be used + Hints.ActionsToExecute.Push(ActionDefinitions.IDPotionStr, //Queue Potion + Player, //on Self + ActionQueue.Priority.VeryHigh + (int)OGCDPriority.ForcedOGCD, 0, GCD - 0.9f); //set priority to Forced oGCD //Execute True North if available - if (!hold && ShouldUseTrueNorth(strategy.Option(Track.TrueNorth).As(), primaryTarget)) - QueueOGCD(AID.TrueNorth, Player, OGCDPriority.TrueNorth); - + if (!hold && //if not holding burst + ShouldUseTrueNorth(strategy.Option(Track.TrueNorth).As(), primaryTarget)) //if True North should be used + QueueOGCD(AID.TrueNorth, //Queue True North + Player, //on Self + OGCDPriority.TrueNorth); //set priority to True North #endregion #region AI //AI hints for positioning - var goalST = primaryTarget != null ? Hints.GoalSingleTarget(primaryTarget, 3) : null; - var goalAOE = primaryTarget != null ? Hints.GoalAOECone(primaryTarget, 10, 45.Degrees()) : null; - var goal = AOEStrategy switch + var goalST = primaryTarget != null ? Hints.GoalSingleTarget(primaryTarget, 3) : null; //Set goal for single target + var goalAOE = primaryTarget != null ? Hints.GoalAOECone(primaryTarget, 10, 45.Degrees()) : null; //Set goal for AOE + var goal = AOEStrategy switch //Set goal based on AOE strategy { - AOEStrategy.ForceST => goalST, - AOEStrategy.Force123ST => goalST, - AOEStrategy.ForceBuffsST => goalST, - AOEStrategy.ForceAOE => goalAOE, - _ => goalST != null && goalAOE != null ? Hints.GoalCombined(goalST, goalAOE, 2) : goalAOE + AOEStrategy.ForceST => goalST, //if forced single target + AOEStrategy.Force123ST => goalST, //if forced 123 combo + AOEStrategy.ForceBuffsST => goalST, //if forced buffs combo + AOEStrategy.ForceAOE => goalAOE, //if forced AOE action + _ => goalST != null && goalAOE != null ? Hints.GoalCombined(goalST, goalAOE, 2) : goalAOE //otherwise, combine goals }; - if (goal != null) - Hints.GoalZones.Add(goal); + if (goal != null) //if goal is set + Hints.GoalZones.Add(goal); //add goal to zones #endregion - } #region Core Execution Helpers @@ -694,13 +806,13 @@ public override void Execute(StrategyValues strategy, Actor? primaryTarget, floa //Determine the effect application delay for specific abilities private float EffectApplicationDelay(AID aid) => aid switch { - AID.ChaoticSpring => 0.45f, //Chaotic Spring delay - AID.HighJump => 0.49f, //High Jump delay - AID.CoerthanTorment => 0.49f, //Coerthan Torment delay - AID.BattleLitany => 0.62f, //Battle Litany delay - AID.LanceBarrage => 0.62f, //Lance Barrage delay - AID.FangAndClaw => 0.62f, //Fang and Claw delay - AID.RaidenThrust => 0.62f, //Raiden Thrust delay + AID.ChaoticSpring => 0.45f, //Chaotic Spring delay + AID.HighJump => 0.49f, //High Jump delay + AID.CoerthanTorment => 0.49f, //Coerthan Torment delay + AID.BattleLitany => 0.62f, //Battle Litany delay + AID.LanceBarrage => 0.62f, //Lance Barrage delay + AID.FangAndClaw => 0.62f, //Fang and Claw delay + AID.RaidenThrust => 0.62f, //Raiden Thrust delay AID.Geirskogul => 0.67f, //Geirskogul delay AID.WheelingThrust => 0.67f, //Wheeling Thrust delay AID.HeavensThrust => 0.71f, //Heavens Thrust delay @@ -720,7 +832,7 @@ public override void Execute(StrategyValues strategy, Actor? primaryTarget, floa AID.SpiralBlow => 1.38f, //Spiral Blow delay AID.Disembowel => 1.65f, //Disembowel delay AID.DragonsongDive => 2.23f, //Dragonsong Dive delay - _ => 0 //Default case for unknown abilities + _ => 0 //Default case for unknown abilities }; //Queue a global cooldown (GCD) action @@ -771,7 +883,7 @@ private void QueueOGCD(AID aid, Actor? target, OGCDPriority prio, float basePrio { //Starting combo with TrueThrust or RaidenThrust AID.TrueThrust or AID.RaidenThrust => - //If Disembowel is Unlocked and power is low or Chaotic Spring is 0, use Disembowel or SpiralBlow, else VorpalThrust or LanceBarrage + //if Disembowel is Unlocked and power is low or Chaotic Spring is 0, use Disembowel or SpiralBlow, else VorpalThrust or LanceBarrage Unlocked(AID.Disembowel) && (powerLeft <= GCDLength * 6 || chaosLeft <= GCDLength * 4) ? Unlocked(AID.SpiralBlow) ? AID.SpiralBlow : AID.Disembowel : Unlocked(AID.LanceBarrage) ? AID.LanceBarrage : AID.VorpalThrust, @@ -803,13 +915,13 @@ private void QueueOGCD(AID aid, Actor? target, OGCDPriority prio, float basePrio Unlocked(AID.Drakesbane) ? AID.Drakesbane //Use Drakesbane if Unlocked : AID.TrueThrust, //Return to TrueThrust otherwise - //If no combo active and Draconian Fire buff is up, use RaidenThrust + //if no combo active and Draconian Fire buff is up, use RaidenThrust _ => HasEffect(SID.DraconianFire) ? AID.RaidenThrust //RaidenThrust if DraconianFire is active : AID.TrueThrust, //No combo, start with TrueThrust }; //Limits the combo sequence to just 1-2-3 ST skills, ignoring other Unlocked actions. - private AID UseOnly123ST() => ComboLastMove switch + private AID Useonly123ST() => ComboLastMove switch { //Start combo with TrueThrust AID.TrueThrust or AID.RaidenThrust => @@ -833,13 +945,13 @@ private void QueueOGCD(AID aid, Actor? target, OGCDPriority prio, float basePrio Unlocked(AID.Drakesbane) ? AID.Drakesbane //Drakesbane if Unlocked : AID.TrueThrust, //Else return to TrueThrust - //If Draconian Fire buff is up, use RaidenThrust + //if Draconian Fire buff is up, use RaidenThrust _ => HasEffect(SID.DraconianFire) ? AID.RaidenThrust //RaidenThrust if DraconianFire is active : AID.TrueThrust, //No combo, start with TrueThrust }; //Limits the combo sequence to 1-4-5 ST skills, focusing on Disembowel and Chaos/ChaoticSpring. - private AID UseOnly145ST() => ComboLastMove switch + private AID Useonly145ST() => ComboLastMove switch { //Start combo with TrueThrust AID.TrueThrust or AID.RaidenThrust => @@ -863,7 +975,7 @@ private void QueueOGCD(AID aid, Actor? target, OGCDPriority prio, float basePrio Unlocked(AID.Drakesbane) ? AID.Drakesbane //Drakesbane if Unlocked : AID.TrueThrust, //Else return to TrueThrust - //If Draconian Fire buff is up, use RaidenThrust + //if Draconian Fire buff is up, use RaidenThrust _ => HasEffect(SID.DraconianFire) ? AID.RaidenThrust //RaidenThrust if DraconianFire is active : AID.TrueThrust, //No combo, start with TrueThrust }; @@ -883,7 +995,7 @@ private void QueueOGCD(AID aid, Actor? target, OGCDPriority prio, float basePrio AID.SonicThrust => Unlocked(AID.CoerthanTorment) ? AID.CoerthanTorment : AID.DoomSpike, //CoerthanTorment if Unlocked, else DoomSpike - //If Draconian Fire buff is up, use DraconianFury + //if Draconian Fire buff is up, use DraconianFury _ => HasEffect(SID.DraconianFire) ? Unlocked(AID.DraconianFury) ? AID.DraconianFury : AID.DoomSpike //DraconianFury if Unlocked, else DoomSpike : AID.DoomSpike, //No DraconianFire active, default to DoomSpike @@ -1080,30 +1192,30 @@ private void QueueOGCD(AID aid, Actor? target, OGCDPriority prio, float basePrio target != null && Player.InCombat && !HasEffect(SID.TrueNorth) && GCD < 1.25f && - (!IsOnRear(target) && //Side + (!IsonRear(target) && //Side ComboLastMove is AID.Disembowel or AID.SpiralBlow or AID.ChaosThrust or AID.ChaoticSpring || - !IsOnFlank(target) && //Back + !IsonFlank(target) && //Back ComboLastMove is AID.HeavensThrust or AID.FullThrust), TrueNorthStrategy.ASAP => target != null && Player.InCombat && !HasEffect(SID.TrueNorth) && - (!IsOnRear(target) && //Side + (!IsonRear(target) && //Side ComboLastMove is AID.Disembowel or AID.SpiralBlow or AID.ChaosThrust or AID.ChaoticSpring || - !IsOnFlank(target) && //Back + !IsonFlank(target) && //Back ComboLastMove is AID.HeavensThrust or AID.FullThrust), TrueNorthStrategy.Flank => target != null && Player.InCombat && !HasEffect(SID.TrueNorth) && GCD < 1.25f && - !IsOnFlank(target) && //Back + !IsonFlank(target) && //Back ComboLastMove is AID.HeavensThrust or AID.FullThrust, TrueNorthStrategy.Rear => target != null && Player.InCombat && !HasEffect(SID.TrueNorth) && GCD < 1.25f && - !IsOnRear(target) && //Side + !IsonRear(target) && //Side ComboLastMove is AID.Disembowel or AID.SpiralBlow or AID.ChaosThrust or AID.ChaoticSpring, TrueNorthStrategy.Force => !HasEffect(SID.TrueNorth), From 5a6d8e5530bb3999d15bad553cb149fd652cd430 Mon Sep 17 00:00:00 2001 From: Akechi-kun <167795370+Akechi-kun@users.noreply.github.com> Date: Sat, 14 Dec 2024 02:15:30 -0800 Subject: [PATCH 07/29] xdd --- BossMod/Autorotation/akechi/AkechiDRG.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/BossMod/Autorotation/akechi/AkechiDRG.cs b/BossMod/Autorotation/akechi/AkechiDRG.cs index fb0899e67b..84cdc9934b 100644 --- a/BossMod/Autorotation/akechi/AkechiDRG.cs +++ b/BossMod/Autorotation/akechi/AkechiDRG.cs @@ -39,7 +39,7 @@ public enum AOEStrategy { AutoTargetHitPrimary, //Auto-target AOE actions to hit primary target as well AutoTargetHitMost, //Auto-target AOE actions to hit most targets - AutoonPrimary, //Use AOE actions on primary target + AutoOnPrimary, //Use AOE actions on primary target ForceST, //Force single-target abilities Force123ST, //Force single-target 123 combo ForceBuffsST, //Force single-target buffs combo @@ -178,7 +178,7 @@ public static RotationModuleDefinition Definition() res.Define(Track.AOE).As("Combo Option", "AOE", uiPriority: 200) .AddOption(AOEStrategy.AutoTargetHitPrimary, "AutoTargetHitPrimary", "Use AOE actions if profitable, select best target that ensures primary target is hit") .AddOption(AOEStrategy.AutoTargetHitMost, "AutoTargetHitMost", "Use AOE actions if profitable, select a target that ensures maximal number of targets are hit") - .AddOption(AOEStrategy.AutoonPrimary, "AutoonPrimary", "Use AOE actions on primary target if profitable") + .AddOption(AOEStrategy.AutoOnPrimary, "AutoOnPrimary", "Use AOE actions on primary target if profitable") .AddOption(AOEStrategy.ForceST, "Force ST", "Force Single-Target rotation") .AddOption(AOEStrategy.Force123ST, "only 1-2-3 ST", "Force only ST 1-2-3 rotation (No Buffs)") .AddOption(AOEStrategy.ForceBuffsST, "only 1-4-5 ST", "Force only ST 1-4-5 rotation (Buffs only)") @@ -462,7 +462,7 @@ public enum OGCDPriority { AOEStrategy.AutoTargetHitPrimary => FindBetterTargetBy(primaryTarget, range, t => primaryTarget == null || check(t, primaryTarget) ? numTargets(t) : 0), AOEStrategy.AutoTargetHitMost => FindBetterTargetBy(primaryTarget, range, numTargets), - AOEStrategy.AutoonPrimary => (primaryTarget, primaryTarget != null ? numTargets(primaryTarget) : 0), + AOEStrategy.AutoOnPrimary => (primaryTarget, primaryTarget != null ? numTargets(primaryTarget) : 0), AOEStrategy.ForceAOE => (primaryTarget, int.MaxValue), _ => (null, 0) }; From 15121cc1157ec73a0ce679c6caac0b460b8400b1 Mon Sep 17 00:00:00 2001 From: Akechi-kun <167795370+Akechi-kun@users.noreply.github.com> Date: Sat, 14 Dec 2024 02:19:02 -0800 Subject: [PATCH 08/29] xdd --- BossMod/Autorotation/akechi/AkechiDRG.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/BossMod/Autorotation/akechi/AkechiDRG.cs b/BossMod/Autorotation/akechi/AkechiDRG.cs index 84cdc9934b..5a2450ec0d 100644 --- a/BossMod/Autorotation/akechi/AkechiDRG.cs +++ b/BossMod/Autorotation/akechi/AkechiDRG.cs @@ -243,7 +243,7 @@ public static RotationModuleDefinition Definition() res.Define(Track.Geirskogul).As("Geirskogul", "Geirs.", uiPriority: 130) .AddOption(GeirskogulStrategy.Automatic, "Automatic", "Use Geirskogul normally") .AddOption(GeirskogulStrategy.Force, "Force", "Force Geirskogul usage", 60, 0, ActionTargets.Hostile, 60, 69) - .AddOption(GeirskogulStrategy.ForceEX, "ForceEX", "Force Geirskogul (Grants Life of the Dragon & 3x Nastrond)", 60, 20, ActionTargets.Hostile, 70) + .AddOption(GeirskogulStrategy.ForceEX, "ForceEX", "Force Geirskogul (Grants Life of the Dragon & Nastrond Ready)", 60, 20, ActionTargets.Hostile, 70) .AddOption(GeirskogulStrategy.ForceWeave, "Force Weave", "Force Geirskogul usage inside the next possible weave window", 60, 20, ActionTargets.Hostile, 70) .AddOption(GeirskogulStrategy.Delay, "Delay", "Delay Geirskogul usage", 0, 0, ActionTargets.None, 60) .AddAssociatedActions(AID.Geirskogul); From eab48dab7a90fc494cdf5ce69951a45da81958df Mon Sep 17 00:00:00 2001 From: Akechi-kun <167795370+Akechi-kun@users.noreply.github.com> Date: Sat, 14 Dec 2024 02:26:41 -0800 Subject: [PATCH 09/29] ok ok ok ok done --- BossMod/Autorotation/akechi/AkechiDRG.cs | 34 ++++++++++++------------ 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/BossMod/Autorotation/akechi/AkechiDRG.cs b/BossMod/Autorotation/akechi/AkechiDRG.cs index 5a2450ec0d..4f5c865517 100644 --- a/BossMod/Autorotation/akechi/AkechiDRG.cs +++ b/BossMod/Autorotation/akechi/AkechiDRG.cs @@ -48,8 +48,8 @@ public enum AOEStrategy public enum SpearTargetingStrategy { - AutoTargetHitPrimary, //Auto-target spear to hit primary target - AutoTargetHitMost, //Auto-target spear to hit most targets + AutoTargetHitPrimary, //Auto-target spear to hit primary target + AutoTargetHitMost, //Auto-target spear to hit most targets } //Burst strategy options @@ -63,9 +63,9 @@ public enum BurstStrategy public enum DivesStrategy { - AllowMaxMelee, //Allow Dives only at max melee range - AllowCloseMelee, //Allow Dives only at close melee range - Allow, //Allow the use of Dives + AllowMaxMelee, //Allow Dives only at max melee range + AllowCloseMelee, //Allow Dives only at close melee range + Allow, //Allow the use of Dives Forbid, //Forbid the use of Dives } @@ -142,21 +142,21 @@ public enum PiercingTalonStrategy //True North strategy public enum TrueNorthStrategy { - Automatic, //Weave - ASAP, //Use ASAP - Rear, //Use only when in Rear - Flank, //Use only when in Flank - Force, //Force - Delay //Delay + Automatic, //Weave + ASAP, //Use ASAP + Rear, //Use only when in Rear + Flank, //Use only when in Flank + Force, //Force + Delay //Delay } //Offensive strategies public enum OffensiveStrategy { - Automatic, //Automatically use offensive abilities - Force, //Force offensive abilities - ForceWeave, //Force offensive abilities only inside the next possible weave window - Delay //Delay offensive abilities + Automatic, //Automatically use offensive abilities + Force, //Force offensive abilities + ForceWeave, //Force offensive abilities only inside the next possible weave window + Delay //Delay offensive abilities } #endregion @@ -180,8 +180,8 @@ public static RotationModuleDefinition Definition() .AddOption(AOEStrategy.AutoTargetHitMost, "AutoTargetHitMost", "Use AOE actions if profitable, select a target that ensures maximal number of targets are hit") .AddOption(AOEStrategy.AutoOnPrimary, "AutoOnPrimary", "Use AOE actions on primary target if profitable") .AddOption(AOEStrategy.ForceST, "Force ST", "Force Single-Target rotation") - .AddOption(AOEStrategy.Force123ST, "only 1-2-3 ST", "Force only ST 1-2-3 rotation (No Buffs)") - .AddOption(AOEStrategy.ForceBuffsST, "only 1-4-5 ST", "Force only ST 1-4-5 rotation (Buffs only)") + .AddOption(AOEStrategy.Force123ST, "Only 1-2-3 ST", "Force only ST 1-2-3 rotation (No Buffs)") + .AddOption(AOEStrategy.ForceBuffsST, "Only 1-4-5 ST", "Force only ST 1-4-5 rotation (Buffs only)") .AddOption(AOEStrategy.ForceAOE, "Force AOE", "Force AOE rotation, even if less than 3 targets"); //Spear targeting strategy From f7200cec52e5a1c937e93ae48a1234241c408726 Mon Sep 17 00:00:00 2001 From: Akechi-kun <167795370+Akechi-kun@users.noreply.github.com> Date: Tue, 17 Dec 2024 20:23:44 -0800 Subject: [PATCH 10/29] hoo boy some big changes to my modules :) --- BossMod/Autorotation/akechi/AkechiDRG.cs | 251 +++-- BossMod/Autorotation/akechi/AkechiGNB.cs | 1247 +++++++++++++--------- 2 files changed, 854 insertions(+), 644 deletions(-) diff --git a/BossMod/Autorotation/akechi/AkechiDRG.cs b/BossMod/Autorotation/akechi/AkechiDRG.cs index 4f5c865517..29cd423201 100644 --- a/BossMod/Autorotation/akechi/AkechiDRG.cs +++ b/BossMod/Autorotation/akechi/AkechiDRG.cs @@ -15,7 +15,7 @@ public enum Track { AOE, //Area of Effect actions Spears, //Spear actions - Burst, //Burst actions + Cooldowns, //Cooldown actions Dives, //Dive actions Potion, //Potion usage LifeSurge, //Life Surge ability @@ -39,7 +39,6 @@ public enum AOEStrategy { AutoTargetHitPrimary, //Auto-target AOE actions to hit primary target as well AutoTargetHitMost, //Auto-target AOE actions to hit most targets - AutoOnPrimary, //Use AOE actions on primary target ForceST, //Force single-target abilities Force123ST, //Force single-target 123 combo ForceBuffsST, //Force single-target buffs combo @@ -52,13 +51,11 @@ public enum SpearTargetingStrategy AutoTargetHitMost, //Auto-target spear to hit most targets } - //Burst strategy options - public enum BurstStrategy + //Cooldowns strategy options + public enum CooldownStrategy { - Automatic, //Automatic burst under general conditions - Conserve, //Conserve burst for later - UnderRaidBuffs, //Burst during raid buffs - UnderPotion //Burst when a potion is active + Automatic, //Automatic Cooldowns under general conditions + Hold, //Hold Cooldowns for later } public enum DivesStrategy @@ -178,10 +175,9 @@ public static RotationModuleDefinition Definition() res.Define(Track.AOE).As("Combo Option", "AOE", uiPriority: 200) .AddOption(AOEStrategy.AutoTargetHitPrimary, "AutoTargetHitPrimary", "Use AOE actions if profitable, select best target that ensures primary target is hit") .AddOption(AOEStrategy.AutoTargetHitMost, "AutoTargetHitMost", "Use AOE actions if profitable, select a target that ensures maximal number of targets are hit") - .AddOption(AOEStrategy.AutoOnPrimary, "AutoOnPrimary", "Use AOE actions on primary target if profitable") .AddOption(AOEStrategy.ForceST, "Force ST", "Force Single-Target rotation") - .AddOption(AOEStrategy.Force123ST, "Only 1-2-3 ST", "Force only ST 1-2-3 rotation (No Buffs)") - .AddOption(AOEStrategy.ForceBuffsST, "Only 1-4-5 ST", "Force only ST 1-4-5 rotation (Buffs only)") + .AddOption(AOEStrategy.Force123ST, "Only 1-2-3 ST", "Force only ST 1-2-3 rotation (No Buff or DoT)") + .AddOption(AOEStrategy.ForceBuffsST, "Only 1-4-5 ST", "Force only ST 1-4-5 rotation (Buff & DoT only)") .AddOption(AOEStrategy.ForceAOE, "Force AOE", "Force AOE rotation, even if less than 3 targets"); //Spear targeting strategy @@ -189,22 +185,21 @@ public static RotationModuleDefinition Definition() .AddOption(SpearTargetingStrategy.AutoTargetHitPrimary, "AutoTargetHitPrimary", "Selects best target within range that hits as many targets as possible whilst also hitting current target") .AddOption(SpearTargetingStrategy.AutoTargetHitMost, "AutoTargetHitMost", "Selects best target within range that hits as many targets, regardless if current target is hit"); - //Burst strategy - res.Define(Track.Burst).As("Burst", uiPriority: 190) - .AddOption(BurstStrategy.Automatic, "Automatic", "Use Burst optimally based on situation") - .AddOption(BurstStrategy.Conserve, "Conserve", "Conserve all cooldowns") - .AddOption(BurstStrategy.UnderRaidBuffs, "Under Raid Buffs", "Burst under raid buffs; conserve otherwise (ignores potion usage)") - .AddOption(BurstStrategy.UnderPotion, "Under Potion", "Burst under potion, conserve otherwise (ignores raid buffs)"); + //Cooldowns strategy + res.Define(Track.Cooldowns).As("Cooldowns", "CDs", uiPriority: 190) + .AddOption(CooldownStrategy.Automatic, "Automatic", "Use Cooldowns optimally based on situation") + .AddOption(CooldownStrategy.Hold, "Hold", "Hold all cooldowns & resources"); //Dives strategy res.Define(Track.Dives).As("Dives", uiPriority: 185) .AddOption(DivesStrategy.AllowMaxMelee, "Allow Max Melee", "Allow Jump, Stardiver, & Dragonfire Dive only at max melee range (within 3y)") .AddOption(DivesStrategy.AllowCloseMelee, "Allow Close Melee", "Allow Jump, Stardiver, & Dragonfire Dive only at close melee range (within 1y)") .AddOption(DivesStrategy.Allow, "Allow", "Allow the use of Jump, Stardiver, & Dragonfire Dive at any distance") - .AddOption(DivesStrategy.Forbid, "Forbid", "Forbid the use of Jump, Stardiver, & Dragonfire Dive"); + .AddOption(DivesStrategy.Forbid, "Forbid", "Forbid the use of Jump, Stardiver, & Dragonfire Dive") + .AddAssociatedActions(AID.Jump, AID.HighJump, AID.DragonfireDive, AID.Stardiver); //Potion strategy - res.Define(Track.Potion).As("Potion", uiPriority: 180) + res.Define(Track.Potion).As("Potion", uiPriority: 20) .AddOption(PotionStrategy.Manual, "Manual", "Do not use potions automatically") .AddOption(PotionStrategy.AlignWithRaidBuffs, "Align With Raid Buffs", "Use potion in sync with 2-minute raid buffs (e.g., 0/6, 2/8)") .AddOption(PotionStrategy.Immediate, "Immediate", "Use potion as soon as possible, regardless of any buffs") @@ -258,7 +253,7 @@ public static RotationModuleDefinition Definition() .AddAssociatedActions(AID.Stardiver); //Piercing Talon strategy - res.Define(Track.PiercingTalon).As("Piercing Talon", "P.Talon", uiPriority: 20) + res.Define(Track.PiercingTalon).As("Piercing Talon", "P.Talon", uiPriority: 25) .AddOption(PiercingTalonStrategy.AllowEX, "AllowEX", "Allow use of Piercing Talon if already in combat, outside melee range, & is Enhanced") .AddOption(PiercingTalonStrategy.Allow, "Allow", "Allow use of Piercing Talon if already in combat & outside melee range") .AddOption(PiercingTalonStrategy.Force, "Force", "Force Piercing Talon usage ASAP (even in melee range)") @@ -342,8 +337,8 @@ public static RotationModuleDefinition Definition() #region Priorities - //Priorities for Global Cooldowns (GCDs) - public enum GCDPriority + public enum GCDPriority //Priorities for Global Cooldowns (GCDs) + { None = 0, //No priority Combo123 = 350, //Priority for the first three combo actions @@ -351,8 +346,8 @@ public enum GCDPriority ForcedGCD = 900, //High priority for forced GCD actions } - //Priorities for Off Global Cooldowns (oGCDs) - public enum OGCDPriority + public enum OGCDPriority //Priorities for Off Global Cooldowns (oGCDs) + { None = 0, //No priority //Flexible actions with varying priorities @@ -402,95 +397,109 @@ public enum OGCDPriority private float lcCD; //Cooldown for Lance Charge private float powerLeft; //Time remaining for Power Surge private float chaosLeft; //Remaining time for Chaotic Spring DoT - private float PotionLeft; //Remaining time for potion effect - private float RaidBuffsLeft; //Time left for raid buffs - private float RaidBuffsIn; //Time until next raid buffs are applied public float downtimeIn; //Duration of downtime in combat - public float BurstWindowLeft; //Time left in the burst window - public float BurstWindowIn; //Time until the next burst window starts private int focusCount; //Count of Firstmind's Focus gauge public AID NextGCD; //Next global cooldown action to be used private GCDPriority NextGCDPrio; //Priority of the next GCD for cooldown decision making #endregion #region Module Helpers - //Checks if the desired ability is unlocked - private bool Unlocked(AID aid) => ActionUnlocked(ActionID.MakeSpell(aid)); - - //Gets the last action used in the combo sequence - private AID ComboLastMove => (AID)World.Client.ComboState.Action; - - //Checks if status effect is on Self - public bool HasEffect(SID sid) => SelfStatusLeft(sid) > 0; - - //Checks if the desired action is ready (cooldown < 0.6 seconds) - private bool ActionReady(AID aid) => World.Client.Cooldowns[ActionDefinitions.Instance.Spell(aid)!.MainCooldownGroup].Remaining < 0.6f; - - //Get remaining cooldown time for the specified action - private float CD(AID aid) => World.Client.Cooldowns[ActionDefinitions.Instance.Spell(aid)!.MainCooldownGroup].Remaining; - - //Checks if the target is within max melee range (3 yalms) - private bool In3y(Actor? target) => Player.DistanceToHitbox(target) < 4; - - //Checks if the target is within close melee range (1 yalm) - private bool In1y(Actor? target) => Player.DistanceToHitbox(target) < 1; - - //Checks if the target is within 15 yalms - private bool In15y(Actor? target) => Player.DistanceToHitbox(target) <= 14.9; - - //Checks if we can weave an ability - public bool CanWeave(AID aid, double weaveTime = 0.7) => CD(aid) > weaveTime; - - //Checks if the potion should be used before raid buffs expire - private bool IsPotionBeforeRaidbuffs() => RaidBuffsLeft == 0 && PotionLeft > RaidBuffsIn + 17.5f; + private bool Unlocked(AID aid) => ActionUnlocked(ActionID.MakeSpell(aid)); //Checks if the desired ability is unlocked + private AID ComboLastMove => (AID)World.Client.ComboState.Action; //Gets the last action used in the combo sequence + public bool HasEffect(SID sid) => SelfStatusLeft(sid) > 0; //Checks if status effect is on Self + private bool ActionReady(AID aid) => World.Client.Cooldowns[ActionDefinitions.Instance.Spell(aid)!.MainCooldownGroup].Remaining < 0.6f; //Checks if the desired ability is ready + private float CD(AID aid) => World.Client.Cooldowns[ActionDefinitions.Instance.Spell(aid)!.MainCooldownGroup].Remaining; //Get remaining cooldown time for the specified ability + private bool In3y(Actor? target) => Player.DistanceToHitbox(target) < 4; //Checks if the target is within max melee range (3 yalms) + private bool In1y(Actor? target) => Player.DistanceToHitbox(target) < 1; //Checks if the target is within close melee range (1 yalm) + private bool In15y(Actor? target) => Player.DistanceToHitbox(target) <= 14.9; //Checks if the target is within 15 yalms + public bool CanWeave(AID aid, double weaveTime = 0.7) => CD(aid) > weaveTime; //Checks if we can weave an ability #region Targeting Helpers - //Count number of targets hit by AOE attack - private int NumTargetsHitByAOE(Actor primary) => Hints.NumPriorityTargetsInAOECone(Player.Position, 10, (primary.Position - Player.Position).Normalized(), 45.Degrees()); - - //Check if a target is hit by AOE - private bool IsHitByAOE(Actor primary, Actor check) => Hints.TargetInAOECone(check, Player.Position, 10, (primary.Position - Player.Position).Normalized(), 45.Degrees()); - - //Count number of targets hit by spear attack - private int NumTargetsHitBySpear(Actor primary) => Hints.NumPriorityTargetsInAOERect(Player.Position, (primary.Position - Player.Position).Normalized(), 15, 2); - - //Check if a target is hit by spear - private bool IsHitBySpear(Actor primary, Actor check) => Hints.TargetInAOERect(check, Player.Position, (primary.Position - Player.Position).Normalized(), 15, 2); + private int NumTargetsHitByAOE(Actor primary) //Count number of targets hit by AOE cone + => Hints.NumPriorityTargetsInAOECone( //Use Hints to count number of targets in AOE cone + Player.Position, //from Player's position + 10, //AOE cone radius + (primary.Position - Player.Position) //direction of AOE cone + .Normalized(), //normalized direction + 45.Degrees()); //cone angle + + private bool IsHitByAOE(Actor primary, Actor check) //Check if target has been hit by AOE cone + => Hints.TargetInAOECone( //Use Hints to check if target is in AOE cone + check, //check the target + Player.Position, //from Player's position + 10, //AOE cone radius + (primary.Position - Player.Position) //direction of AOE cone + .Normalized(), //normalized direction + 45.Degrees()); //cone angle + + private int NumTargetsHitBySpear(Actor primary) //Count number of targets hit by spear attack + => 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 + 15, //AOE rectangle length + 2); //AOE rectangle width + + private bool IsHitBySpear(Actor primary, Actor check) //Check if target has been hit by spear attack + => Hints.TargetInAOERect( //Use Hints to check if target is in AOE rectangle + check, //check the target + Player.Position, //from Player's position + (primary.Position - Player.Position) //direction of AOE rectangle + .Normalized(), //normalized direction + 15, //AOE rectangle length + 2); //AOE rectangle width //Check targeting for AOE strategies private (Actor?, int) CheckAOETargeting(AOEStrategy strategy, Actor? primaryTarget, float range, Func numTargets, Func check) => strategy switch { - AOEStrategy.AutoTargetHitPrimary => FindBetterTargetBy(primaryTarget, range, t => primaryTarget == null || check(t, primaryTarget) ? numTargets(t) : 0), - AOEStrategy.AutoTargetHitMost => FindBetterTargetBy(primaryTarget, range, numTargets), - AOEStrategy.AutoOnPrimary => (primaryTarget, primaryTarget != null ? numTargets(primaryTarget) : 0), - AOEStrategy.ForceAOE => (primaryTarget, int.MaxValue), + AOEStrategy.AutoTargetHitPrimary //if AutoTargetHitPrimary strategy is selected + => FindBetterTargetBy( //Find a better target based on conditions + primaryTarget, //primary target + range, //range of the action + t => primaryTarget == null //if primary target is null + || check(t, primaryTarget) //or check if target is hit by primary target + ? numTargets(t) //if true, count number of targets hit by AOE + : 0), //if false, return 0 + AOEStrategy.AutoTargetHitMost //if AutoTargetHitMost strategy is selected + => FindBetterTargetBy( //Find a better target based on conditions + primaryTarget, //primary target + range, //range of the action + numTargets), //count number of targets hit by AOE + AOEStrategy.ForceAOE //if ForceAOE strategy is selected + => (primaryTarget, //return primary target + int.MaxValue), //return maximum number of targets _ => (null, 0) }; //Check targeting for Spear strategies private (Actor?, int) CheckSpearTargeting(SpearTargetingStrategy strategy, Actor? primaryTarget, float range, Func numTargets, Func check) => strategy switch { - SpearTargetingStrategy.AutoTargetHitPrimary => FindBetterTargetBy(primaryTarget, range, t => primaryTarget == null || check(t, primaryTarget) ? numTargets(t) : 0), - SpearTargetingStrategy.AutoTargetHitMost => FindBetterTargetBy(primaryTarget, range, numTargets), + SpearTargetingStrategy.AutoTargetHitPrimary //if AutoTargetHitPrimary strategy is selected + => FindBetterTargetBy( //Find a better target based on conditions + primaryTarget, //on primary target + range, //range of the action + t => primaryTarget == null //if primary target is null + || check(t, primaryTarget) //or check if target is hit by primary target + ? numTargets(t) //if true, count number of targets hit by AOE + : 0), //if false, return 0 + SpearTargetingStrategy.AutoTargetHitMost //if AutoTargetHitMost strategy is selected + => FindBetterTargetBy( //Find a better target based on conditions + primaryTarget, //on primary target + range, //range of the action + numTargets), //count number of targets hit by AOE _ => (null, 0) }; - #endregion #region True North Helpers - //Check which positional Player is on - private Positional GetCurrentPositional(Actor target) => (Player.Position - target.Position).Normalized().Dot(target.Rotation.ToDirection()) switch + private Positional GetCurrentPositional(Actor target) => (Player.Position - target.Position).Normalized().Dot(target.Rotation.ToDirection()) switch //Check current positional based on target { - < -0.7071068f => Positional.Rear, - < 0.7071068f => Positional.Flank, - _ => Positional.Front + < -0.7071068f => Positional.Rear, //value for Rear positional + < 0.7071068f => Positional.Flank, //value for Flank positional + _ => Positional.Front //default to Front positional if not on Rear or Flank }; - - //Check if Player is on Rear (back) positional - private bool IsonRear(Actor target) => GetCurrentPositional(target) == Positional.Rear; - - //Check if Player is on Flank (side) positional - private bool IsonFlank(Actor target) => GetCurrentPositional(target) == Positional.Flank; + private bool IsOnRear(Actor target) => GetCurrentPositional(target) == Positional.Rear; //Check if target is on Rear positional + private bool IsOnFlank(Actor target) => GetCurrentPositional(target) == Positional.Flank; //Check if target is on Flank positional #endregion #endregion @@ -537,10 +546,9 @@ public override void Execute(StrategyValues strategy, Actor? primaryTarget, floa canWeaveIn = CanWeave(AID.TrueThrust); //Check if weaving is possible canWeaveInStardiver = GCD is < 2.5f and > 1.49f; //Check if weaving Stardiver is possible downtimeIn = Manager.Planner?.EstimateTimeToNextDowntime().Item2 ?? float.MaxValue; //Estimate downtime until next action - PotionLeft = PotionStatusLeft(); //Get remaining potion status - (RaidBuffsLeft, RaidBuffsIn) = EstimateRaidBuffTimings(primaryTarget); //Estimate remaining raid buffs NextGCD = AID.None; //Set next GCD ability NextGCDPrio = GCDPriority.None; //Set next GCD priority + var hold = strategy.Option(Track.Cooldowns).As() == CooldownStrategy.Hold; //Check if the Cooldowns should be held or delayed #endregion #endregion @@ -552,32 +560,14 @@ public override void Execute(StrategyValues strategy, Actor? primaryTarget, floa //Force specific actions based on the AOE strategy selected if (AOEStrategy == AOEStrategy.ForceST) //if forced single target QueueGCD(NextFullST(), primaryTarget, GCDPriority.ForcedGCD); //Queue the next single target action - if (AOEStrategy == AOEStrategy.Force123ST) //if forced 123 combo QueueGCD(Useonly123ST(), primaryTarget, GCDPriority.ForcedGCD); //Queue the 123 combo action - if (AOEStrategy == AOEStrategy.ForceBuffsST) //if forced buffs combo QueueGCD(Useonly145ST(), primaryTarget, GCDPriority.ForcedGCD); //Queue the buffed 145 combo action - if (AOEStrategy == AOEStrategy.ForceAOE) //if forced AOE action QueueGCD(NextFullAOE(), primaryTarget, GCDPriority.ForcedGCD); //Queue the next AOE action #endregion - #region Burst Window Strategy - var burst = strategy.Option(Track.Burst); //Retrieve the current burst strategy - var burstStrategy = burst.As(); //Cast to BurstStrategy type - var hold = burstStrategy == BurstStrategy.Conserve; //Check if the burst should be conserved - - //Set burst window timings based on the selected burst strategy - (BurstWindowIn, BurstWindowLeft) = burstStrategy switch - { - BurstStrategy.Automatic => (RaidBuffsIn, IsPotionBeforeRaidbuffs() ? 0 : Math.Max(PotionLeft, RaidBuffsLeft)), //Set timings based on raid buffs and potion availability - BurstStrategy.UnderRaidBuffs => (RaidBuffsIn, RaidBuffsLeft), //Set timings directly from raid buffs - BurstStrategy.UnderPotion => (PotionCD, PotionLeft), //Use potion cooldown and remaining time - _ => (0, 0) //Set timings to zero - }; - #endregion - #region Dives Strategy //Dive strategy var dive = strategy.Option(Track.Dives).As(); //Retrieve the current dive strategy @@ -589,7 +579,6 @@ public override void Execute(StrategyValues strategy, Actor? primaryTarget, floa DivesStrategy.Forbid => false, //Never allow dives _ => false, }; - var maxMelee = dive == DivesStrategy.AllowMaxMelee; //Check if max melee is allowed var closeMelee = dive == DivesStrategy.AllowCloseMelee; //Check if close melee is allowed var allowed = dive == DivesStrategy.Allow; //Check if dives are allowed @@ -605,7 +594,7 @@ public override void Execute(StrategyValues strategy, Actor? primaryTarget, floa var (SpearBestTarget, SpearTargetCount) = Unlocked(AID.Geirskogul) ? CheckSpearTargeting(SpearStrategy, primaryTarget, 15, NumTargetsHitBySpear, IsHitBySpear) - : (null, 0); // Set to null and count 0 if not unlocked + : (null, 0); //Set to null and count 0 if not unlocked //Determine if using AOE is viable (at least 3 targets hit) var useAOE = AOETargetCount >= 3; @@ -625,7 +614,7 @@ public override void Execute(StrategyValues strategy, Actor? primaryTarget, floa QueueGCD(useAOE ? NextFullAOE() : NextFullST(), bestAOEtarget, GCDPriority.Combo123); //Execute Lance Charge if available var lcStrat = strategy.Option(Track.LanceCharge).As(); //Retrieve the Lance Charge strategy - if (!hold && //if not holding burst + if (!hold && //if not holding Cooldowns ShouldUseLanceCharge(lcStrat, primaryTarget)) //if Lance Charge should be used QueueOGCD(AID.LanceCharge, //Queue Lance Charge Player, //on Self @@ -636,7 +625,7 @@ or OffensiveStrategy.ForceWeave //or Force Weave //Execute Battle Litany if available var blStrat = strategy.Option(Track.BattleLitany).As(); //Retrieve the Battle Litany strategy - if (!hold && //if not holding burst + if (!hold && //if not holding Cooldowns ShouldUseBattleLitany(blStrat, primaryTarget)) //if Battle Litany should be used QueueOGCD(AID.BattleLitany, //Queue Battle Litany Player, //on Self @@ -647,7 +636,7 @@ or OffensiveStrategy.ForceWeave //or Force Weave //Execute Life Surge if conditions met var lsStrat = strategy.Option(Track.LifeSurge).As(); //Retrieve the Life Surge strategy - if (!hold && //if not holding burst + if (!hold && //if not holding Cooldowns ShouldUseLifeSurge(lsStrat, primaryTarget)) //if Life Surge should be used QueueOGCD(AID.LifeSurge, //Queue Life Surge Player, //on Self @@ -660,7 +649,7 @@ or SurgeStrategy.ForceNextOptiWeave //or Force Next Optimal Weave Window //Execute Jump ability if available var jumpStrat = strategy.Option(Track.Jump).As(); //Retrieve the Jump strategy - if (!hold && divesGood && //if not holding burst and dives are good + if (!hold && divesGood && //if not holding Cooldowns and dives are good ShouldUseJump(jumpStrat, primaryTarget)) //if Jump should be used QueueOGCD(Unlocked(AID.HighJump) ? AID.HighJump : AID.Jump, //Queue High Jump if unlocked, otherwise Jump primaryTarget, //on the primary target @@ -673,7 +662,7 @@ or JumpStrategy.ForceWeave //or Force Weave //Execute Dragonfire Dive if available var ddStrat = strategy.Option(Track.DragonfireDive).As(); //Retrieve the Dragonfire Dive strategy - if (!hold && divesGood && //if not holding burst and dives are good + if (!hold && divesGood && //if not holding Cooldowns and dives are good ShouldUseDragonfireDive(ddStrat, primaryTarget)) //if Dragonfire Dive should be used QueueOGCD(AID.DragonfireDive, //Queue Dragonfire Dive primaryTarget, //on the primary target @@ -684,7 +673,7 @@ or DragonfireStrategy.ForceWeave //or Force Weave //Execute Geirskogul if available var geirskogul = strategy.Option(Track.Geirskogul).As(); //Retrieve the Geirskogul strategy - if (!hold && //if not holding burst + if (!hold && //if not holding Cooldowns ShouldUseGeirskogul(geirskogul, primaryTarget)) //if Geirskogul should be used QueueOGCD(AID.Geirskogul, //Queue Geirskogul bestSpeartarget, //on the best Spear target @@ -696,7 +685,7 @@ or GeirskogulStrategy.ForceWeave //or Force Weave //Execute Mirage Dive if available var mirageStrat = strategy.Option(Track.MirageDive).As(); //Retrieve the Mirage Dive strategy - if (!hold && //if not holding burst + if (!hold && //if not holding Cooldowns ShouldUseMirageDive(mirageStrat, primaryTarget)) //if Mirage Dive should be used QueueOGCD(AID.MirageDive, //Queue Mirage Dive primaryTarget, //on the primary target @@ -707,7 +696,7 @@ or OffensiveStrategy.ForceWeave //or Force Weave //Execute Nastrond if available var nastrondStrat = strategy.Option(Track.Nastrond).As(); //Retrieve the Nastrond strategy - if (!hold && //if not holding burst + if (!hold && //if not holding Cooldowns ShouldUseNastrond(nastrondStrat, primaryTarget)) //if Nastrond should be used QueueOGCD(AID.Nastrond, //Queue Nastrond bestSpeartarget, //on the best Spear target @@ -718,7 +707,7 @@ or OffensiveStrategy.ForceWeave //or Force Weave //Execute Stardiver if available var sdStrat = strategy.Option(Track.Stardiver).As(); //Retrieve the Stardiver strategy - if (!hold && divesGood && //if not holding burst and dives are good + if (!hold && divesGood && //if not holding Cooldowns and dives are good ShouldUseStardiver(sdStrat, primaryTarget)) //if Stardiver should be used QueueOGCD(AID.Stardiver, //Queue Stardiver primaryTarget, //on the primary target @@ -730,7 +719,7 @@ or StardiverStrategy.ForceWeave //or Force Weave //Execute Wyrmwind Thrust if available var wtStrat = strategy.Option(Track.WyrmwindThrust).As(); //Retrieve the Wyrmwind Thrust strategy - if (!hold && //if not holding burst + if (!hold && //if not holding Cooldowns ShouldUseWyrmwindThrust(wtStrat, primaryTarget)) //if Wyrmwind Thrust should be used QueueOGCD(AID.WyrmwindThrust, //Queue Wyrmwind Thrust bestSpeartarget, wtStrat is OffensiveStrategy.Force //on the best Spear target @@ -777,7 +766,7 @@ ptStrat is PiercingTalonStrategy.Force or //if strategy is Force ActionQueue.Priority.VeryHigh + (int)OGCDPriority.ForcedOGCD, 0, GCD - 0.9f); //set priority to Forced oGCD //Execute True North if available - if (!hold && //if not holding burst + if (!hold && //if not holding Cooldowns ShouldUseTrueNorth(strategy.Option(Track.TrueNorth).As(), primaryTarget)) //if True North should be used QueueOGCD(AID.TrueNorth, //Queue True North Player, //on Self @@ -1009,8 +998,9 @@ private void QueueOGCD(AID aid, Actor? target, OGCDPriority prio, float basePrio private bool ShouldUseLanceCharge(OffensiveStrategy strategy, Actor? target) => strategy switch { OffensiveStrategy.Automatic => - //Use Lance Charge automatically if the player is in combat, the target is valid, the action is ready, and there is power remaining - Player.InCombat && target != null && canLC && powerLeft > 0, + Player.InCombat && + target != null && + canLC && powerLeft > 0, OffensiveStrategy.Force => canLC, //Always use if forced OffensiveStrategy.ForceWeave => canLC && canWeaveIn, //Always use if inside weave window OffensiveStrategy.Delay => false, //Delay usage if strategy is set to delay @@ -1189,33 +1179,34 @@ private void QueueOGCD(AID aid, Actor? target, OGCDPriority prio, float basePrio private bool ShouldUseTrueNorth(TrueNorthStrategy strategy, Actor? target) => strategy switch { TrueNorthStrategy.Automatic => - target != null && Player.InCombat && - !HasEffect(SID.TrueNorth) && + target != null && //if has target + Player.InCombat && //if in combat + !HasEffect(SID.TrueNorth) && // GCD < 1.25f && - (!IsonRear(target) && //Side + (!IsOnRear(target) && //Side ComboLastMove is AID.Disembowel or AID.SpiralBlow or AID.ChaosThrust or AID.ChaoticSpring || - !IsonFlank(target) && //Back + !IsOnFlank(target) && //Back ComboLastMove is AID.HeavensThrust or AID.FullThrust), TrueNorthStrategy.ASAP => target != null && Player.InCombat && !HasEffect(SID.TrueNorth) && - (!IsonRear(target) && //Side + (!IsOnRear(target) && //Side ComboLastMove is AID.Disembowel or AID.SpiralBlow or AID.ChaosThrust or AID.ChaoticSpring || - !IsonFlank(target) && //Back + !IsOnFlank(target) && //Back ComboLastMove is AID.HeavensThrust or AID.FullThrust), TrueNorthStrategy.Flank => target != null && Player.InCombat && !HasEffect(SID.TrueNorth) && GCD < 1.25f && - !IsonFlank(target) && //Back + !IsOnFlank(target) && //Back ComboLastMove is AID.HeavensThrust or AID.FullThrust, TrueNorthStrategy.Rear => target != null && Player.InCombat && !HasEffect(SID.TrueNorth) && GCD < 1.25f && - !IsonRear(target) && //Side + !IsOnRear(target) && //Side ComboLastMove is AID.Disembowel or AID.SpiralBlow or AID.ChaosThrust or AID.ChaoticSpring, TrueNorthStrategy.Force => !HasEffect(SID.TrueNorth), diff --git a/BossMod/Autorotation/akechi/AkechiGNB.cs b/BossMod/Autorotation/akechi/AkechiGNB.cs index 336ee2980f..ca43d99024 100644 --- a/BossMod/Autorotation/akechi/AkechiGNB.cs +++ b/BossMod/Autorotation/akechi/AkechiGNB.cs @@ -7,48 +7,57 @@ namespace BossMod.Autorotation.akechi; //Contribution by Akechi //Discord: @akechdz or 'Akechi' on Puni.sh for maintenance //This module supports <=2.47 SkS rotation as default (or 'Automatic') -//With user adjustment, 'SlowGNB' or 'FastGNB' usage is achievable +//With user adjustment, 'SlowGNB' or 'FastGNB' usage is easily achievable public sealed class AkechiGNB(RotationModuleManager manager, Actor player) : RotationModule(manager, player) { #region Enums: Abilities / Strategies - //Actions tracked for Cooldown Planner execution + //Abilities tracked for Cooldown Planner & Autorotation execution public enum Track { - AoE, //ST&AoE actions - Burst, //Burst damage actions - Potion, //Potion usage tracking - LightningShot, //Ranged attack tracking - GnashingFang, //Gnashing Fang action tracking + AOE, //ST&AOE rotations tracking + Cooldowns, //Cooldown abilities tracking + Cartridges, //Cartridge abilities tracking + Potion, //Potion item tracking + LightningShot, //Ranged ability tracking NoMercy, //No Mercy ability tracking SonicBreak, //Sonic Break ability tracking + GnashingFang, //Gnashing Fang abilities tracking + Reign, //Reign abilities tracking Bloodfest, //Bloodfest ability tracking DoubleDown, //Double Down ability tracking - BurstStrike, //Burst Strike ability tracking - FatedCircle, //Fated Circle ability tracking - Zone, //Blasting Zone or Danger Zone tracking + Zone, //Blasting Zone or Danger Zone ability tracking BowShock, //Bow Shock ability tracking } - //Defines the strategy for using ST/AoE actions based on the current target selection and conditions + //Defines the strategy for using ST/AOE actions based on the current target selection and conditions public enum AOEStrategy { - SingleTarget, //Force single-target actions without exceeding cartridge cap - FocusSingleTarget, //Force single-target actions, regardless of cartridges - ForceAoE, //Force AoE actions without exceeding cartridge cap - FocusAoE, //Force AoE actions, regardless of cartridges - Auto, //Decide action based on target count; may break combo if needed AutoFinishCombo, //Decide action based on target count but finish current combo if possible - GenerateDowntime, //Generate cartridges before downtime + AutoBreakCombo, //Decide action based on target count; breaks combo if needed + ForceSTwithO, //Force single-target rotation with overcap protection on cartridges + ForceSTwithoutO, //Force single-target rotation without overcap protection on cartridges + ForceAOEwithO, //Force AOE rotation with overcap protection on cartridges + ForceAOEwithoutO, //Force AOE rotation without overcap protection on cartridges + GenerateDowntime //Generate cartridges before downtime } //Defines different strategies for executing burst damage actions based on cooldown and resource availability - public enum BurstStrategy + public enum CooldownStrategy { Automatic, //Automatically execute based on conditions - ConserveCarts, //Conserve cartridges for future use - UnderRaidBuffs, //Execute during raid buffs for maximum effect - UnderPotion //Execute while under the effects of a potion + Hold, //Hold all resources + } + + //Defines the strategy for using abilities that consume cartridges, allowing for different behaviors based on combat scenarios + public enum CartridgeStrategy + { + Automatic, //Automatically decide when to use Burst Strike & Fated Circle + BurstStrike, //Force the use of Burst Strike; consumes 1 cartridge + FatedCircle, //Force the use of Fated Circle; consumes 1 cartridge + GnashingFang, //Force the use of Gnashing Fang (cooldown only); consumes 1 cartridge + DoubleDown, //Force the use of Double Down; consumes 1 cartridge (yay) + Conserve //Conserves all cartridge-related abilities as much as possible } //Defines strategies for potion usage in combat, determining when and how to consume potions based on the situation @@ -62,33 +71,29 @@ public enum PotionStrategy //Defines strategies for using Lightning Shot during combat based on various conditions public enum LightningShotStrategy { - OpenerRanged, //Use Lightning Shot as part of the opener - Opener, //Use Lightning Shot at the start of combat - Force, //Always use Lightning Shot regardless of conditions - Ranged, //Use Lightning Shot when ranged attacks are necessary + OpenerFar, //Only use Lightning Shot in pre-pull & out of melee range + OpenerForce, //Force use Lightning Shot in pre-pull in any range + Force, //Force the use of Lightning Shot in any range + Allow, //Allow the use of Lightning Shot when out of melee range Forbid //Prohibit the use of Lightning Shot } - //Defines the strategy for using Gnashing Fang in combos, allowing for different behaviors based on combat scenarios - public enum GnashingStrategy - { - Automatic, //Automatically decide when to use Gnashing Fang - ForceGnash, //Force the use of Gnashing Fang regardless of conditions - ForceClaw, //Force the use of Savage Claw action when in combo - ForceTalon, //Force the use of Wicked Talon action when in combo - Delay //Delay the use of Gnashing Fang for strategic reasons - } - //Defines the strategy for using No Mercy, allowing for different behaviors based on combat scenarios public enum NoMercyStrategy { Automatic, //Automatically decide when to use No Mercy Force, //Force the use of No Mercy regardless of conditions - ForceLW, //Force the use of No Mercy in next possible 2nd oGCD slot + ForceW, //Force the use of No Mercy in next possible weave slot + ForceQW, //Force the use of No Mercy in next last possible second weave slot + Force1, //Force the use of No Mercy when 1 cartridge is available + Force1W, //Force the use of No Mercy when 1 cartridge is available in next possible weave slot + Force1QW, //Force the use of No Mercy when 1 cartridge is available in next last possible second weave slot Force2, //Force the use of No Mercy when 2 cartridges are available - Force2LW, //Force the use of No Mercy in next possible 2nd oGCD slot & 2 cartridges are available + Force2W, //Force the use of No Mercy when 2 cartridges are available in next possible weave slot + Force2QW, //Force the use of No Mercy when 2 cartridges are available in next last possible second weave slot Force3, //Force the use of No Mercy when 3 cartridges are available - Force3LW, //Force the use of No Mercy in next possible 2nd oGCD slot & 3 cartridges are available + Force3W, //Force the use of No Mercy when 3 cartridges are available in next possible weave slot + Force3QW, //Force the use of No Mercy when 3 cartridges are available in next last possible second weave slot Delay //Delay the use of No Mercy for strategic reasons } @@ -97,184 +102,233 @@ public enum SonicBreakStrategy { Automatic, //Automatically decide when to use Sonic Break Force, //Force the use of Sonic Break regardless of conditions - EarlySB, //Force the use of Sonic Break on the first GCD slot inside No Mercy window - LateSB, //Force the use of Sonic Break on the last GCD slot inside No Mercy window + Early, //Force the use of Sonic Break on the first GCD slot inside No Mercy window + Late, //Force the use of Sonic Break on the last GCD slot inside No Mercy window Delay //Delay the use of Sonic Break for strategic reasons } + //Defines the strategy for using Gnashing Fang in combos, allowing for different behaviors based on combat scenarios + public enum GnashingStrategy + { + Automatic, //Automatically decide when to use Gnashing Fang + ForceGnash, //Force the use of Gnashing Fang regardless of conditions + ForceClaw, //Force the use of Savage Claw action when in combo + ForceTalon, //Force the use of Wicked Talon action when in combo + Delay //Delay the use of Gnashing Fang for strategic reasons + } + + //Defines the strategy for using Reign of Beasts & it's combo chain, allowing for different behaviors based on combat scenarios + public enum ReignStrategy + { + Automatic, //Automatically decide when to use Reign of Beasts + ForceReign, //Force the use of Reign of Beasts when available + ForceNoble, //Force the use of Noble Blood when in available + ForceLion, //Force the use of Lion Heart when in available + Delay //Delay the use of Reign combo for strategic reasons + } + //Defines the strategy for using Bloodfest, allowing for different behaviors based on combat scenarios public enum BloodfestStrategy { Automatic, //Automatically decide when to use Bloodfest Force, //Force the use of Bloodfest regardless of conditions + ForceW, //Force the use of Bloodfest in next possible weave slot Force0, //Force the use of Bloodfest only when ammo is empty + Force0W, //Force the use of Bloodfest only when ammo is empty in next possible weave slot Delay //Delay the use of Sonic Break for strategic reasons } //Defines different offensive strategies that dictate how abilities and resources are used during combat - public enum OffensiveStrategy + public enum GCDStrategy //Global Cooldown Strategy { - Automatic, //Automatically decide when to use offensive abilities - Force, //Force the use of offensive abilities regardless of conditions - Delay //Delay the use of offensive abilities for strategic reasons + Automatic, //Automatically decide when to use global offensive abilities + Force, //Force the use of global offensive abilities regardless of conditions + Delay //Delay the use of global offensive abilities for strategic reasons + } + public enum OGCDStrategy //Off-Global Cooldown Strategy + { + 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 + //Module Definitions public static RotationModuleDefinition Definition() { - //Module title & signature - var res = new RotationModuleDefinition("Akechi GNB", "Standard Rotation Module", "Standard rotation (Akechi)", "Akechi", RotationModuleQuality.Good, BitMask.Build((int)Class.GNB), 100); + var res = new RotationModuleDefinition("Akechi GNB", //Title + "Standard Rotation Module", //Description + "Standard rotation (Akechi)", //Category + "Akechi", //Contributor + RotationModuleQuality.Good, //Quality + BitMask.Build((int)Class.GNB), //Job + 100); //Level supported #region Custom strategies - //Targeting strategy - res.Define(Track.AoE).As("Combo Option", "AoE", uiPriority: 200) - .AddOption(AOEStrategy.SingleTarget, "ST", "Use ST rotation (with overcap protection)") - .AddOption(AOEStrategy.FocusSingleTarget, "ST", "Use ST rotation (without overcap protection)") - .AddOption(AOEStrategy.ForceAoE, "AoE", "Use AoE rotation (with overcap protection)") - .AddOption(AOEStrategy.FocusAoE, "AoE", "Use AoE rotation (without overcap protection)") - .AddOption(AOEStrategy.Auto, "Auto", "Use AoE rotation if 3+ targets would be hit, otherwise use ST rotation; break combo if necessary") - .AddOption(AOEStrategy.AutoFinishCombo, "Auto Finish Combo", "Use AoE rotation if 3+ targets would be hit, otherwise use ST rotation; finish combo before switching") - .AddOption(AOEStrategy.GenerateDowntime, "Generate before Downtime", "Estimates time until disengagement & determines when to use ST or AoE combo to generate carts appropriately before downtime"); - - //Burst strategy - res.Define(Track.Burst).As("Burst", uiPriority: 190) - .AddOption(BurstStrategy.Automatic, "Automatic", "Spend cartridges optimally") - .AddOption(BurstStrategy.ConserveCarts, "Conserve", "Conserve everything (cartridges & GCDs)") - .AddOption(BurstStrategy.UnderRaidBuffs, "Under RaidBuffs", "Spend under raid buffs, otherwise conserve; ignores potion") - .AddOption(BurstStrategy.UnderPotion, "Under Potion", "Spend under potion, otherwise conserve; ignores raid buffs"); + //AOE strategy + res.Define(Track.AOE).As("AOE", uiPriority: 200) + .AddOption(AOEStrategy.AutoFinishCombo, "Auto (Finish Combo)", "Auto-selects best rotation dependant on targets; Finishes combo first") + .AddOption(AOEStrategy.AutoBreakCombo, "Auto (Break Combo)", "Auto-selects best rotation dependant on targets; Breaks combo if needed") + .AddOption(AOEStrategy.ForceSTwithO, "Force ST with Overcap", "Force single-target rotation with overcap protection") + .AddOption(AOEStrategy.ForceSTwithoutO, "Force ST without Overcap", "Force ST rotation without overcap protection") + .AddOption(AOEStrategy.ForceAOEwithO, "Force AOE with Overcap", "Force AOE rotation with overcap protection") + .AddOption(AOEStrategy.ForceAOEwithoutO, "Force AOE without Overcap", "Force AOE rotation without overcap protection") + .AddOption(AOEStrategy.GenerateDowntime, "Generate Downtime", "Generate cartridges before downtime"); + + //Cooldowns strategy + res.Define(Track.Cooldowns).As("Cooldowns", "CDs", uiPriority: 190) + .AddOption(CooldownStrategy.Automatic, "Automatic", "Automatically decides when to use cooldowns; will use them optimally") + .AddOption(CooldownStrategy.Hold, "Hold", "Prohibit use of all cooldown-related abilities; will not use any actions with a cooldown timer"); + + //Cartridges strategy + res.Define(Track.Cartridges).As("Cartridges", "Carts", uiPriority: 180) + .AddOption(CartridgeStrategy.Automatic, "Automatic", "Automatically decide when to use cartridges; uses them optimally") + .AddOption(CartridgeStrategy.BurstStrike, "Burst Strike", "Force the use of Burst Strike; consumes 1 cartridge") + .AddOption(CartridgeStrategy.FatedCircle, "Fated Circle", "Force the use of Fated Circle; consumes 1 cartridge") + .AddOption(CartridgeStrategy.GnashingFang, "Gnashing Fang", "Force the use of Gnashing Fang (cooldown only, no combo or CDs); consumes 1 cartridge") + .AddOption(CartridgeStrategy.DoubleDown, "Double Down", "Force the use of Double Down; consumes 1 cartridge") + .AddOption(CartridgeStrategy.Conserve, "Conserve", "Prohibit use of all cartridge-related abilities; will not use any of these actions listed above") + .AddAssociatedActions(AID.BurstStrike, AID.FatedCircle, AID.GnashingFang, AID.DoubleDown); //Potion strategy - res.Define(Track.Potion).As("Potion", uiPriority: 180) + res.Define(Track.Potion).As("Potion", uiPriority: 20) .AddOption(PotionStrategy.Manual, "Manual", "Do not use automatically") - .AddOption(PotionStrategy.AlignWithRaidBuffs, "AlignWithRaidBuffs", "Align with 2-minute raid buffs (0/6, 2/8, etc)", 270, 30, ActionTargets.Self) + .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.IDPotionStr); //LightningShot strategy - res.Define(Track.LightningShot).As("Lightning Shot", "L.Shot", uiPriority: 20) - .AddOption(LightningShotStrategy.OpenerRanged, "OpenerRanged", "Use as very first GCD and only if outside melee range") - .AddOption(LightningShotStrategy.Opener, "Opener", "Use as very first GCD regardless of range") - .AddOption(LightningShotStrategy.Force, "Force", "Force use ASAP (even in melee range)") - .AddOption(LightningShotStrategy.Ranged, "Ranged", "Use if outside melee range") - .AddOption(LightningShotStrategy.Forbid, "Forbid", "Do not use at all") + res.Define(Track.LightningShot).As("Lightning Shot", "L.Shot", uiPriority: 30) + .AddOption(LightningShotStrategy.OpenerFar, "Far (Opener)", "Use Lightning Shot in pre-pull & out of melee range") + .AddOption(LightningShotStrategy.OpenerForce, "Force (Opener)", "Force use Lightning Shot in pre-pull in any range") + .AddOption(LightningShotStrategy.Force, "Force", "Force use Lightning Shot in any range") + .AddOption(LightningShotStrategy.Allow, "Allow", "Allow use of Lightning Shot when out of melee range") + .AddOption(LightningShotStrategy.Forbid, "Forbid", "Prohibit use of Lightning Shot") .AddAssociatedActions(AID.LightningShot); - //GnashingFang strategy - res.Define(Track.GnashingFang).As("Gnashing Fang", "G.Fang", uiPriority: 160) - .AddOption(GnashingStrategy.Automatic, "Auto", "Normal use of Gnashing Fang") - .AddOption(GnashingStrategy.ForceGnash, "Force", "Force use of Gnashing Fang (Step 1)", 30, 0, ActionTargets.Hostile, 60) - .AddOption(GnashingStrategy.ForceClaw, "Force", "Force use of Savage Claw (Step 2)", 0, 0, ActionTargets.Hostile, 60) - .AddOption(GnashingStrategy.ForceTalon, "Force", "Force use of Wicked Talon (Step 3)", 0, 0, ActionTargets.Hostile, 60) - .AddOption(GnashingStrategy.Delay, "Delay", "Delay use of Gnashing Fang", 0, 0, ActionTargets.None, 60) - .AddAssociatedActions(AID.GnashingFang, AID.SavageClaw, AID.WickedTalon); - //NoMercy strategy res.Define(Track.NoMercy).As("No Mercy", "N.Mercy", uiPriority: 170) - .AddOption(NoMercyStrategy.Automatic, "Automatic", "Use normally") - .AddOption(NoMercyStrategy.Force, "Force", "Force use ASAP (even during downtime)", 60, 20, ActionTargets.Self, 2) - .AddOption(NoMercyStrategy.ForceLW, "Force Late-weave", "Uses as soon as in next possible late-weave slot", 60, 20, ActionTargets.Self, 2) - .AddOption(NoMercyStrategy.Force2, "Force (2+ carts)", "Use as soon as you have 2 (or more) cartridges (SlowGNB)", 60, 20, ActionTargets.Self, 30) - .AddOption(NoMercyStrategy.Force2LW, "Force Late-weave (2+ carts)", "Use as soon as you have 2+ cartridges & in next possible late-weave slot (FastGNB)", 60, 20, ActionTargets.Self, 30) - .AddOption(NoMercyStrategy.Force3, "Force (3 carts)", "Use as soon as you have 3 cartridges (SlowGNB)", 60, 20, ActionTargets.Self, 88) - .AddOption(NoMercyStrategy.Force3LW, "Force Late-weave (3 carts)", "Use as soon as you have 3 cartridges & in next possible late-weave slot (FastGNB)", 60, 20, ActionTargets.Self, 88) - .AddOption(NoMercyStrategy.Delay, "Delay", "Delay", 0, 0, ActionTargets.None, 2) + .AddOption(NoMercyStrategy.Automatic, "Auto", "Normal use of No Mercy") + .AddOption(NoMercyStrategy.Force, "Force", "Force use of No Mercy, regardless of weaving", 60, 20, ActionTargets.Self, 2) + .AddOption(NoMercyStrategy.ForceW, "Force (Weave)", "Force use of No Mercy in next possible weave slot", 60, 20, ActionTargets.Self, 2) + .AddOption(NoMercyStrategy.ForceQW, "Force (Q.Weave)", "Force use of No Mercy in next possible last second weave slot", 60, 20, ActionTargets.Self, 2) + .AddOption(NoMercyStrategy.Force1, "Force (1 cart)", "Force use of No Mercy when 1 cartridge is available, regardless of weaving", 60, 20, ActionTargets.Self, 2) + .AddOption(NoMercyStrategy.Force1W, "Force (1 cart, Weave)", "Force use of No Mercy when 1 cartridge is available & in next weave slot", 60, 20, ActionTargets.Self, 2) + .AddOption(NoMercyStrategy.Force1QW, "Force (1 cart, Q.Weave)", "Force use of No Mercy when 1 cartridge is available & in next possible last-second weave slot", 60, 20, ActionTargets.Self, 2) + .AddOption(NoMercyStrategy.Force2, "Force (2 carts)", "Force use of No Mercy when 2 cartridges are available, regardless of weaving", 60, 20, ActionTargets.Self, 2) + .AddOption(NoMercyStrategy.Force2W, "Force (2 carts, Weave)", "Force use of No Mercy when 2 cartridges are available & in next possible weave slot", 60, 20, ActionTargets.Self, 2) + .AddOption(NoMercyStrategy.Force2QW, "Force (2 carts, Q.Weave)", "Force use of No Mercy when 2 cartridges are available & in next possible last-second weave slot", 60, 20, ActionTargets.Self, 2) + .AddOption(NoMercyStrategy.Force3, "Force (3 carts)", "Force use of No Mercy when 3 cartridges are available, regardless of weaving", 60, 20, ActionTargets.Self, 2) + .AddOption(NoMercyStrategy.Force3W, "Force (3 carts, Weave)", "Force use of No Mercy when 3 cartridges are available & in next possible weave slot", 60, 20, ActionTargets.Self, 2) + .AddOption(NoMercyStrategy.Force3QW, "Force (3 carts, Q.Weave)", "Force use of No Mercy when 3 cartridges are available & in next possible last-second weave slot", 60, 20, ActionTargets.Self, 2) + .AddOption(NoMercyStrategy.Delay, "Delay", "Delay use of No Mercy", 0, 0, ActionTargets.None, 2) .AddAssociatedActions(AID.NoMercy); //SonicBreak strategy res.Define(Track.SonicBreak).As("Sonic Break", "S.Break", uiPriority: 150) .AddOption(SonicBreakStrategy.Automatic, "Auto", "Normal use of Sonic Break") .AddOption(SonicBreakStrategy.Force, "Force", "Force use of Sonic Break", 0, 30, ActionTargets.Hostile, 54) - .AddOption(SonicBreakStrategy.EarlySB, "Early Sonic Break", "Uses Sonic Break as the very first GCD when in No Mercy", 0, 30, ActionTargets.Hostile, 54) - .AddOption(SonicBreakStrategy.LateSB, "Late Sonic Break", "Uses Sonic Break as the very last GCD when in No Mercy", 0, 30, ActionTargets.Hostile, 54) + .AddOption(SonicBreakStrategy.Early, "Early Sonic Break", "Uses Sonic Break as the very first GCD when in No Mercy", 0, 30, ActionTargets.Hostile, 54) + .AddOption(SonicBreakStrategy.Late, "Late Sonic Break", "Uses Sonic Break as the very last GCD when in No Mercy", 0, 30, ActionTargets.Hostile, 54) .AddOption(SonicBreakStrategy.Delay, "Delay", "Delay use of Sonic Break", 0, 0, ActionTargets.None, 54) .AddAssociatedActions(AID.SonicBreak); + //GnashingFang strategy + res.Define(Track.GnashingFang).As("Gnashing Fang", "G.Fang", uiPriority: 160) + .AddOption(GnashingStrategy.Automatic, "Auto", "Normal use of Gnashing Fang") + .AddOption(GnashingStrategy.ForceGnash, "Force", "Force use of Gnashing Fang (Step 1)", 30, 0, ActionTargets.Hostile, 60) + .AddOption(GnashingStrategy.ForceClaw, "Force", "Force use of Savage Claw (Step 2)", 0, 0, ActionTargets.Hostile, 60) + .AddOption(GnashingStrategy.ForceTalon, "Force", "Force use of Wicked Talon (Step 3)", 0, 0, ActionTargets.Hostile, 60) + .AddOption(GnashingStrategy.Delay, "Delay", "Delay use of Gnashing Fang", 0, 0, ActionTargets.None, 60) + .AddAssociatedActions(AID.GnashingFang, AID.SavageClaw, AID.WickedTalon); + + //Reign strategy + res.Define(Track.Reign).As("Reign of Beasts", "Reign", uiPriority: 160) + .AddOption(ReignStrategy.Automatic, "Auto", "Normal use of Reign of Beasts") + .AddOption(ReignStrategy.ForceReign, "Force", "Force use of Reign of Beasts", 0, 0, ActionTargets.Hostile, 100) + .AddOption(ReignStrategy.ForceNoble, "Force", "Force use of Noble Blood", 0, 0, ActionTargets.Hostile, 100) + .AddOption(ReignStrategy.ForceLion, "Force", "Force use of Lion Heart", 0, 0, ActionTargets.Hostile, 100) + .AddOption(ReignStrategy.Delay, "Delay", "Delay use of Reign of Beasts", 0, 0, ActionTargets.None, 100) + .AddAssociatedActions(AID.ReignOfBeasts, AID.NobleBlood, AID.LionHeart); + //Bloodfest strategy res.Define(Track.Bloodfest).As("Bloodfest", "Fest", uiPriority: 170) .AddOption(BloodfestStrategy.Automatic, "Auto", "Normal use of Bloodfest") - .AddOption(BloodfestStrategy.Force, "Force", "Force use of Bloodfest, regardless of ammo count", 120, 0, ActionTargets.Hostile, 80) - .AddOption(BloodfestStrategy.Force0, "Force (0 cart)", "Force use of Bloodfest as soon as you have 0 carts", 120, 0, ActionTargets.Hostile, 80) + .AddOption(BloodfestStrategy.Force, "Force", "Force use of Bloodfest, regardless of ammo count & weaving", 120, 0, ActionTargets.Hostile, 80) + .AddOption(BloodfestStrategy.ForceW, "Force (Weave)", "Force use of Bloodfest in next possible weave slot, regardless of ammo count", 120, 0, ActionTargets.Hostile, 80) + .AddOption(BloodfestStrategy.Force0, "Force (0 cart)", "Force use of Bloodfest only if empty on cartridges", 120, 0, ActionTargets.Hostile, 80) + .AddOption(BloodfestStrategy.Force0W, "Force (0 cart, Weave)", "Force use of Bloodfest only if empty on cartridges & in next possible weave slot", 120, 0, ActionTargets.Hostile, 80) .AddOption(BloodfestStrategy.Delay, "Delay", "Delay use of Bloodfest", 0, 0, ActionTargets.None, 80) .AddAssociatedActions(AID.Bloodfest); + #endregion #region Offensive Strategies //DoubleDown strategy - res.Define(Track.DoubleDown).As("Double Down", "D.Down", uiPriority: 160) - .AddOption(OffensiveStrategy.Automatic, "Auto", "Normal use of Double Down") - .AddOption(OffensiveStrategy.Force, "Force", "Force use of Double Down", 60, 0, ActionTargets.Hostile, 90) - .AddOption(OffensiveStrategy.Delay, "Delay", "Delay use of Double Down", 0, 0, ActionTargets.None, 90) + res.Define(Track.DoubleDown).As("Double Down", "D.Down", uiPriority: 160) + .AddOption(GCDStrategy.Automatic, "Auto", "Normal use of Double Down") + .AddOption(GCDStrategy.Force, "Force", "Force use of Double Down", 60, 0, ActionTargets.Hostile, 90) + .AddOption(GCDStrategy.Delay, "Delay", "Delay use of Double Down", 0, 0, ActionTargets.None, 90) .AddAssociatedActions(AID.DoubleDown); - //BurstStrike strategy - res.Define(Track.BurstStrike).As("Burst Strike", "B.Strike", uiPriority: 140) - .AddOption(OffensiveStrategy.Automatic, "Auto", "Normal use of Burst Strike") - .AddOption(OffensiveStrategy.Force, "Force", "Force use of Burst Strike", 0, 0, ActionTargets.Hostile, 30) - .AddOption(OffensiveStrategy.Delay, "Delay", "Delay use of Burst Strike", 0, 0, ActionTargets.None, 30) - .AddAssociatedActions(AID.BurstStrike); - - //FatedCircle strategy - res.Define(Track.FatedCircle).As("Fated Circle", "F.Circle", uiPriority: 140) - .AddOption(OffensiveStrategy.Automatic, "Auto", "Normal use of Fated Circle") - .AddOption(OffensiveStrategy.Force, "Force", "Force use of Fated Circle", 0, 0, ActionTargets.Hostile, 72) - .AddOption(OffensiveStrategy.Delay, "Delay", "Delay use of Fated Circle", 0, 0, ActionTargets.None, 72) - .AddAssociatedActions(AID.FatedCircle); - //Zone strategy - res.Define(Track.Zone).As("Blasting Zone", "Zone", uiPriority: 150) - .AddOption(OffensiveStrategy.Automatic, "Automatic", "Use normally") - .AddOption(OffensiveStrategy.Force, "Force", "Force use ASAP", 30, 0, ActionTargets.Hostile, 18) - .AddOption(OffensiveStrategy.Delay, "Delay", "Delay", 0, 0, ActionTargets.None, 18) + res.Define(Track.Zone).As("Blasting Zone", "Zone", uiPriority: 150) + .AddOption(OGCDStrategy.Automatic, "Automatic", "Use normally") + .AddOption(OGCDStrategy.Force, "Force", "Force use ASAP", 30, 0, ActionTargets.Hostile, 18) + .AddOption(OGCDStrategy.AnyWeave, "Any Weave", "Use in any next possible weave slot", 30, 0, ActionTargets.Hostile, 18) + .AddOption(OGCDStrategy.EarlyWeave, "Early Weave", "Use in very next FIRST weave slot only", 30, 0, ActionTargets.Hostile, 18) + .AddOption(OGCDStrategy.LateWeave, "Late Weave", "Use in very next LAST weave slot only", 30, 0, ActionTargets.Hostile, 18) + .AddOption(OGCDStrategy.Delay, "Delay", "Delay", 0, 0, ActionTargets.None, 18) .AddAssociatedActions(AID.BlastingZone, AID.DangerZone); //BowShock strategy - res.Define(Track.BowShock).As("Bow Shock", "B.Shock", uiPriority: 150) - .AddOption(OffensiveStrategy.Automatic, "Auto", "Normal use of Bow Shock") - .AddOption(OffensiveStrategy.Force, "Force", "Force use of Bow Shock", 60, 15, ActionTargets.Self, 62) - .AddOption(OffensiveStrategy.Delay, "Delay", "Delay use of Bow Shock", 0, 0, ActionTargets.None, 62) + res.Define(Track.BowShock).As("Bow Shock", "B.Shock", uiPriority: 150) + .AddOption(OGCDStrategy.Automatic, "Auto", "Normal use of Bow Shock") + .AddOption(OGCDStrategy.Force, "Force", "Force use of Bow Shock", 60, 15, ActionTargets.Self, 62) + .AddOption(OGCDStrategy.AnyWeave, "Any Weave", "Force use of Bow Shock in any next possible weave slot", 60, 15, ActionTargets.Self, 62) + .AddOption(OGCDStrategy.EarlyWeave, "Early Weave", "Force use of Bow Shock in very next FIRST weave slot only", 60, 15, ActionTargets.Self, 62) + .AddOption(OGCDStrategy.LateWeave, "Late Weave", "Force use of Bow Shock in very next LAST weave slot only", 60, 15, ActionTargets.Self, 62) + .AddOption(OGCDStrategy.Delay, "Delay", "Delay use of Bow Shock", 0, 0, ActionTargets.None, 62) .AddAssociatedActions(AID.BowShock); #endregion return res; - } #region Priorities - //Priority for GCDs used - public enum GCDPriority - { - None = 0, - Combo123 = 350, - FatedCircle = 400, - BurstStrike = 500, - Reign = 525, - comboNeed = 550, - GF23 = 575, - SonicBreak = 600, - DoubleDown = 675, - GF1 = 700, - ForcedGCD = 900, + public enum GCDPriority //priorities for GCDs (higher number = higher priority) + { + None = 0, //default + Combo123 = 350, //combo actions + Gauge = 500, //cartridge spender actions + Reign = 525, //Reign of Beasts + comboNeed = 550, //combo actions that need to be used + GF23 = 575, //Gnashing combo chain + SonicBreak = 600, //Sonic Break + DoubleDown = 675, //Double Down + GF1 = 700, //Gnashing Fang + ForcedGCD = 900, //Forced GCDs } - //Priority for oGCDs used - public enum OGCDPriority - { - None = 0, - Continuation = 500, - Zone = 550, - BowShock = 600, - Continuation1 = 650, - Bloodfest = 700, - ContinuationNeed = 800, - NoMercy = 875, - Potion = 900, - ForcedOGCD = 900, + public enum OGCDPriority //priorities for oGCDs (higher number = higher priority) + { + None = 0, //default + Zone = 550, //Blasting Zone + BowShock = 600, //Bow Shock + Bloodfest = 700, //Bloodfest + Continuation = 800, //Continuation procs + NoMercy = 875, //No Mercy + Potion = 900, //Potion + ForcedOGCD = 900, //Forced oGCDs } #endregion #region Placeholders for Variables //Gauge public byte Ammo; //Range: 0-2 or 0-3 max; this counts current ammo count - public byte GunComboStep; //0 = Gnashing Fang & Reign of Beasts, 1 = Savage Claw, 2 = Wicked Talon, 4 = NobleBlood, 5 = LionHeart. + public byte GunComboStep; //0 = Gnashing Fang & Reign of Beasts, 1 = Savage Claw, 2 = Wicked Talon, 4 = NobleBlood, 5 = LionHeart public int MaxCartridges; //Maximum number of cartridges based on player level //Cooldown Related private float bfCD; //Time left on Bloodfest cooldown (120s base) @@ -288,18 +342,25 @@ public enum OGCDPriority private bool hasRip; //Checks self for Ready To Rip buff private bool hasTear; //Checks self for Ready To Tear buff private bool hasGouge; //Checks self for Ready To Gouge buff - private bool canBS; - private bool canGF; - private bool canFC; - private bool canDD; - private bool canBF; - private bool canZone; - private bool canBreak; - private bool canBow; - private bool canContinue; - private bool canReign; - + private bool canNM; //Checks if No Mercy is completely available + private bool canBS; //Checks if Burst Strike is completely available + private bool canGF; //Checks if Gnashing Fang & its combo chain are completely available + private bool canFC; //Checks if Fated Circle is completely available + private bool canDD; //Checks if Double Down is completely available + private bool canBF; //Checks if Bloodfest is completely available + private bool canZone; //Checks if Danger / Blasting Zone is completely available + private bool canBreak; //Checks if Sonic Break is completely available + private bool canBow; //Checks if Bow Shock is completely available + private bool canContinue; //Checks if Continuation is completely available + private bool canReign; //Checks if Reign of Beasts & its combo chain are completely available + private bool ShouldUseAOE; //Checks if AOE rotation should be used + private bool ShouldUseFC; //Checks if Fated Circle should be used //Misc + public bool inCombo; //Checks if player is already in a combo + 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) @@ -314,15 +375,40 @@ public enum OGCDPriority 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 CanFitGCD(float deadline, int extraGCDs = 0) => GCD + GCDLength * extraGCDs < deadline; //Check if we can fit an additional GCD within the provided deadline private AID ComboLastMove => (AID)World.Client.ComboState.Action; //Get the last action used in the combo sequence private bool In3y(Actor? target) => Player.DistanceToHitbox(target) <= 3; //Check if the target is within melee range (3 yalms) private bool In5y(Actor? target) => Player.DistanceToHitbox(target) <= 4.75; //Check if the target is within 5 yalms private bool ActionReady(AID aid) => World.Client.Cooldowns[ActionDefinitions.Instance.Spell(aid)!.MainCooldownGroup].Remaining < 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 NumTargetsHitByAoE() => Hints.NumPriorityTargetsInAOECircle(Player.Position, 5); //Returns the number of targets hit by AoE within a 5-yalm radius around the player - private bool IsPotionBeforeRaidbuffs() => RaidBuffsLeft == 0 && PotionLeft > RaidBuffsIn + 17.5f; //Checks if the potion should be used before raid buffs expire - public bool HasEffect(SID sid) where SID : Enum => Player.FindStatus((uint)(object)sid, Player.InstanceID) != null; //Checks if Status effect is on self + 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 HasEffect(SID sid) => SelfStatusLeft(sid, 1000) > 0; //Checks if Status effect is on self + + //TODO: try new things... + //public bool JustDid(AID aid) => Manager?.LastCast.Data?.IsSpell(aid) ?? false; //Check if the last action used was the desired ability + //public bool DidWithin(float variance) => (World.CurrentTime - Manager.LastCast.Time).TotalSeconds <= variance; //Check if the last action was used within a certain timeframe + //public bool JustUsed(AID aid, float variance) => JustDid(aid) && DidWithin(variance); //Check if the last action used was the desired ability & was used within a certain timeframe + #endregion + + #region Upgrade Paths + private AID BestZone //Determine the best Zone to use + => Unlocked(AID.BlastingZone) //If Blasting Zone is unlocked + ? AID.BlastingZone //Use Blasting Zone + : AID.DangerZone; //Otherwise, use Danger Zone + private AID BestCartSpender //Determine the best cartridge spender to use + => Unlocked(AID.FatedCircle) //If Fated Circle is unlocked + && ShouldUseFC //And we should use Fated Circle because of targets nearby + ? AID.FatedCircle //Use Fated Circle + : canBS //Otherwise, if Burst Strike is available + ? AID.BurstStrike //Use Burst Strike + : NextBestRotation(); //Otherwise, use the next best rotation + private AID BestContinuation //Determine the best Continuation to use + => canContinue //Continuation is available + && hasRip ? AID.JugularRip //Jugular Rip + : hasTear ? AID.AbdomenTear //Abdomen Tear + : hasGouge ? AID.EyeGouge //Eye Gouge + : hasRaze ? AID.FatedBrand //Fated Brand + : hasBlast ? AID.Hypervelocity //Hypervelocity + : AID.Continuation; //Otherwise, return Original Hook #endregion public override void Execute(StrategyValues strategy, Actor? primaryTarget, float estimatedAnimLockDelay, bool isMoving) //Executes our actions @@ -333,493 +419,626 @@ public override void Execute(StrategyValues strategy, Actor? primaryTarget, floa Ammo = gauge.Ammo; //Current cartridges GunComboStep = gauge.AmmoComboStep; //Combo step for Gnashing Fang or Reign of Beasts MaxCartridges = Unlocked(TraitID.CartridgeChargeII) ? 3 : 2; //Max cartridges based on level - //Cooldown Related + + //Cooldowns bfCD = CD(AID.Bloodfest); //Bloodfest cooldown (120s) nmCD = CD(AID.NoMercy); //No Mercy cooldown (60s) nmLeft = SelfStatusLeft(SID.NoMercy, 20); //Remaining time for No Mercy buff (20s) hasBreak = HasEffect(SID.ReadyToBreak); //Checks for Ready To Break buff hasReign = HasEffect(SID.ReadyToReign); //Checks for Ready To Reign buff hasNM = nmCD is >= 40 and <= 60; //Checks if No Mercy is active - hasBlast = HasEffect(SID.ReadyToBlast); //Checks for Ready To Blast buff - hasRaze = HasEffect(SID.ReadyToRaze); //Checks for Ready To Raze buff + hasBlast = Unlocked(AID.Hypervelocity) && HasEffect(SID.ReadyToBlast); //Checks for Ready To Blast buff + hasRaze = Unlocked(AID.FatedBrand) && HasEffect(SID.ReadyToRaze); //Checks for Ready To Raze buff hasRip = HasEffect(SID.ReadyToRip); //Checks for Ready To Rip buff hasTear = HasEffect(SID.ReadyToTear); //Checks for Ready To Tear buff hasGouge = HasEffect(SID.ReadyToGouge); //Checks for Ready To Gouge buff - //Misc - (RaidBuffsLeft, RaidBuffsIn) = EstimateRaidBuffTimings(primaryTarget); - PotionLeft = PotionStatusLeft(); //Remaining time for potion buff (30s) + + //GCD & Weaving + canWeaveIn = GCD is < 2.5f and > 0.6f; //Can weave in oGCDs + canWeaveEarly = GCD is < 2.5f and > 1.5f; //Can weave in oGCDs early + canWeaveLate = GCD is <= 1.5f and > 0.6f; //Can weave in oGCDs late + quarterWeave = GCD < 0.9f; //Can last second weave oGCDs GCDLength = ActionSpeed.GCDRounded(World.Client.PlayerStats.SkillSpeed, World.Client.PlayerStats.Haste, Player.Level); //GCD based on skill speed and haste NextGCD = AID.None; //Next global cooldown action to be used NextGCDPrio = GCDPriority.None; //Priority of the next GCD, used for decision making on cooldowns + //Misc + PotionLeft = PotionStatusLeft(); //Remaining time for potion buff (30s) + ShouldUseAOE = //Determine if we should use AOE + Unlocked(TraitID.MeleeMastery) //if Melee Mastery trait unlocked + ? TargetsInAOERange() > 2 //use AOE if 3+ targets would be hit + : TargetsInAOERange() > 1; //otherwise, use AOE if 2+ targets would be hit + ShouldUseFC = TargetsInAOERange() > 1; //Determine if we should use Fated Circle + var downtimeIn = Manager.Planner?.EstimateTimeToNextDowntime().Item2 ?? float.MaxValue; //Time until next downtime + #region Minimal Requirements //Ammo-relative - canBS = Unlocked(AID.BurstStrike) && Ammo > 0; //BurstStrike conditions; -1 Ammo ST - canGF = Unlocked(AID.GnashingFang) && ActionReady(AID.GnashingFang) && Ammo > 0; //GnashingFang conditions; -1 Ammo ST - canFC = Unlocked(AID.FatedCircle) && Ammo > 0; //FatedCircle conditions; -1 Ammo AOE - canDD = Unlocked(AID.DoubleDown) && ActionReady(AID.DoubleDown) && Ammo > 0; //DoubleDown conditions; -1 Ammo AOE + canNM = ActionReady(AID.NoMercy); //No Mercy conditions + canBS = Unlocked(AID.BurstStrike) && Ammo > 0; //Burst Strike conditions; -1 Ammo ST + canGF = Unlocked(AID.GnashingFang) && ActionReady(AID.GnashingFang) && Ammo > 0; //Gnashing Fang conditions; -1 Ammo ST + canFC = Unlocked(AID.FatedCircle) && Ammo > 0; //Fated Circle conditions; -1 Ammo AOE + canDD = Unlocked(AID.DoubleDown) && ActionReady(AID.DoubleDown) && Ammo > 0; //Double Down conditions; -1 Ammo AOE canBF = Unlocked(AID.Bloodfest) && ActionReady(AID.Bloodfest); //Bloodfest conditions; +all Ammo (must have target) //Cooldown-relative - canZone = Unlocked(AID.DangerZone) && ActionReady(AID.DangerZone); //Zone conditions - canBreak = hasBreak && Unlocked(AID.SonicBreak); //SonicBreak conditions - canBow = Unlocked(AID.BowShock) && ActionReady(AID.BowShock); //BowShock conditions + canZone = Unlocked(AID.DangerZone) && ActionReady(BestZone); //Zone conditions + canBreak = hasBreak && Unlocked(AID.SonicBreak); //Sonic Break conditions + canBow = Unlocked(AID.BowShock) && ActionReady(AID.BowShock); //Bow Shock conditions canContinue = Unlocked(AID.Continuation); //Continuation conditions - canReign = Unlocked(AID.ReignOfBeasts) && hasReign; //ReignOfBeasts conditions + canReign = Unlocked(AID.ReignOfBeasts) && hasReign; //Reign of Beasts conditions #endregion + #region Strategy Definitions + var AOEStrategy = strategy.Option(Track.AOE).As(); //AOE strategy + var hold = strategy.Option(Track.Cooldowns).As() == CooldownStrategy.Hold; //Determine if holding resources + var conserve = strategy.Option(Track.Cartridges).As() == CartridgeStrategy.Conserve; //Determine if conserving cartridges + var cartStrat = strategy.Option(Track.Cartridges).As(); //Cartridge strategy + var nmStrat = strategy.Option(Track.NoMercy).As(); //No Mercy strategy + var zoneStrat = strategy.Option(Track.Zone).As(); //Zone strategy + var bowStrat = strategy.Option(Track.BowShock).As(); //Bow Shock strategy + var bfStrat = strategy.Option(Track.Bloodfest).As(); //Bloodfest strategy + var gfStrat = strategy.Option(Track.GnashingFang).As(); //Gnashing Fang strategy + var ddStrat = strategy.Option(Track.DoubleDown).As(); //Double Down strategy + var sbStrat = strategy.Option(Track.SonicBreak).As(); //Sonic Break strategy + var reignStrat = strategy.Option(Track.Reign).As(); //Reign of Beasts strategy + var lsStrat = strategy.Option(Track.LightningShot).As(); //Lightning Shot strategy #endregion - #region Burst - //Burst (raid buff) windows typically last 20s every 120s - var burst = strategy.Option(Track.Burst); - var burstStrategy = burst.As(); - var hold = burstStrategy == BurstStrategy.ConserveCarts; //Determine if conserving cartridges - - //Calculate the burst window based on the current strategy - (BurstWindowIn, BurstWindowLeft) = burstStrategy switch - { - BurstStrategy.Automatic => (RaidBuffsIn, IsPotionBeforeRaidbuffs() ? 0 : Math.Max(PotionLeft, RaidBuffsLeft)), - BurstStrategy.UnderRaidBuffs => (RaidBuffsIn, RaidBuffsLeft), - BurstStrategy.UnderPotion => (PotionCD, PotionLeft), - _ => (0, 0) - }; #endregion - #region Targeting - //Define ST/AoE strategy and determine number of targets - var AOEStrategy = strategy.Option(Track.AoE).As(); - var AoETargets = AOEStrategy switch - { - AOEStrategy.SingleTarget => NumTargetsHitByAoE() > 0 ? 1 : 0, - AOEStrategy.FocusSingleTarget => NumTargetsHitByAoE() > 0 ? 1 : 0, - AOEStrategy.ForceAoE => NumTargetsHitByAoE() > 0 ? 100 : 0, - AOEStrategy.FocusAoE => NumTargetsHitByAoE() > 0 ? 100 : 0, - AOEStrategy.GenerateDowntime => NumTargetsHitByAoE() > 0 ? 100 : 0, - _ => NumTargetsHitByAoE() - }; + #region Full Rotation Execution + + #region Standard Rotations (1-2-3 / 1-2) + + #region Force Execution + if (AOEStrategy is AOEStrategy.ForceSTwithO) //if Single-target (with overcap protection) option is selected + QueueGCD(NextComboSingleTarget(), //queue the next single-target combo action with overcap protection + primaryTarget, //on the primary target + GCDPriority.ForcedGCD); //with priority for forced GCDs + if (AOEStrategy is AOEStrategy.ForceSTwithoutO) //if Single-target (without overcap protection) option is selected + QueueGCD(NextForceSingleTarget(), //queue the next single-target combo action without overcap protection + primaryTarget, //on the primary target + GCDPriority.ForcedGCD); //with priority for forced GCDs + if (AOEStrategy is AOEStrategy.ForceAOEwithO) //if AOE (with overcap protection) option is selected + QueueGCD(NextComboAOE(), //queue the next AOE combo action with overcap protection + Player, //on Self (no target needed) + GCDPriority.ForcedGCD); //with priority for forced GCDs + if (AOEStrategy is AOEStrategy.ForceAOEwithoutO) //if AOE (without overcap protection) option is selected + QueueGCD(NextForceAOE(), //queue the next AOE combo action without overcap protection + Player, //on Self (no target needed) + GCDPriority.ForcedGCD); //with priority for forced GCDs #endregion - #region Rotation Strategies - //Force Options - if (AOEStrategy == AOEStrategy.FocusSingleTarget) //ST (without overcap protection) - QueueGCD(NextForceSingleTarget(), primaryTarget, GCDPriority.ForcedGCD); - if (AOEStrategy == AOEStrategy.FocusAoE) //AoE (without overcap protection) - QueueGCD(NextForceAoE(), primaryTarget, GCDPriority.ForcedGCD); - #region Logic for Cart Generation before Downtime - //Estimate time to next downtime - var downtimeIn = Manager.Planner?.EstimateTimeToNextDowntime().Item2 ?? float.MaxValue; - var comboStepsRemaining = ComboLastMove switch + //TODO: refactor this + if (AOEStrategy == AOEStrategy.GenerateDowntime) //if Generate Downtime option is selected { - AID.KeenEdge => Unlocked(AID.SolidBarrel) ? 2 : Unlocked(AID.BrutalShell) ? 1 : 0, - AID.DemonSlice => Unlocked(AID.DemonSlaughter) ? 1 : 0, - _ => 0 - }; - - if (AOEStrategy == AOEStrategy.GenerateDowntime) - { - if (comboStepsRemaining == 0) //Not in any combo - { - if (downtimeIn == GCD * 2 && Ammo == 2 || - downtimeIn == GCD * 4 && Ammo == 1 || - downtimeIn == GCD * 6 && Ammo == 0) - QueueGCD(AID.DemonSlice, Player, GCDPriority.ForcedGCD); - - if (downtimeIn == GCD * 3 && Ammo == 2 || - downtimeIn == GCD * 5 && Ammo == 1 || - downtimeIn == GCD * 8 && Ammo == 0 || - downtimeIn == GCD * 9 && Ammo == 0) - QueueGCD(AID.KeenEdge, primaryTarget, GCDPriority.ForcedGCD); - } - - if (comboStepsRemaining == 1) //Combo initiated - { - if ((downtimeIn == GCD && Ammo == 2 || - downtimeIn == GCD * 3 && Ammo == 1 || - downtimeIn == GCD * 5 && Ammo == 0) && - ComboLastMove == AID.DemonSlice) - QueueGCD(AID.DemonSlaughter, Player, GCDPriority.ForcedGCD); - - if ((downtimeIn == GCD * 2 && Ammo == 2 || - downtimeIn == GCD * 4 && Ammo == 1 || - downtimeIn == GCD * 7 && Ammo == 2 || - downtimeIn == GCD * 8 && Ammo == 2) && - ComboLastMove == AID.KeenEdge) - QueueGCD(AID.BrutalShell, primaryTarget, GCDPriority.ForcedGCD); - } - - if (comboStepsRemaining == 2) + if (downtimeIn == GCD * 2 && Ammo == 2 || //if 2 GCDs until downtime & has 2 cartridges + downtimeIn == GCD * 4 && Ammo == 1 || //if 4 GCDs until downtime & has 1 cartridge + downtimeIn == GCD * 6 && Ammo == 0) //if 6 GCDs until downtime & has 0 cartridges + QueueGCD(AID.DemonSlice, //queue Demon Slice + Player, //on Self (no target needed) + GCDPriority.ForcedGCD); //with priority for forced GCDs + + if (downtimeIn == GCD * 3 && Ammo == 2 || //if 3 GCDs until downtime & has 2 cartridges + downtimeIn == GCD * 5 && Ammo == 1 || //if 5 GCDs until downtime & has 1 cartridge + downtimeIn == GCD * 8 && Ammo == 0 || //if 8 GCDs until downtime & has 0 cartridges + downtimeIn == GCD * 9 && Ammo == 0) //if 9 GCDs until downtime & has 0 cartridges + QueueGCD(AID.KeenEdge, //queue Keen Edge + primaryTarget, //on the primary target + GCDPriority.ForcedGCD); //with priority for forced GCDs + + if (ComboLastMove == AID.DemonSlice && //if last move was Demon Slice + (downtimeIn == GCD && Ammo == 2 || //if 1 GCD until downtime & has 2 cartridges + downtimeIn == GCD * 3 && Ammo == 1 || //if 3 GCDs until downtime & has 1 cartridge + downtimeIn == GCD * 5 && Ammo == 0)) //if 5 GCDs until downtime & has 0 cartridges + QueueGCD(AID.DemonSlaughter, //queue Demon Slaughter + Player, //on Self (no target needed) + GCDPriority.ForcedGCD); //with priority for forced GCDs + + if (ComboLastMove == AID.KeenEdge && //if last move was Keen Edge + (downtimeIn == GCD * 2 && Ammo == 2 || //if 2 GCDs until downtime & has 2 cartridges + downtimeIn == GCD * 4 && Ammo == 1 || //if 4 GCDs until downtime & has 1 cartridge + downtimeIn == GCD * 7 && Ammo == 2 || //if 7 GCDs until downtime & has 2 cartridges + downtimeIn == GCD * 8 && Ammo == 2)) //if 8 GCDs until downtime & has 2 cartridges + QueueGCD(AID.BrutalShell, //queue Brutal Shell + primaryTarget, //on the primary target + GCDPriority.ForcedGCD); //with priority for forced GCDs + + if (ComboLastMove == AID.BrutalShell) //if last move was Brutal Shell { - if ((downtimeIn == GCD && (Ammo == 2 || Ammo == 3) || - downtimeIn == GCD * 4 && Ammo == 1 || - downtimeIn == GCD * 7 && Ammo == 0) && - ComboLastMove == AID.BrutalShell) - QueueGCD(AID.SolidBarrel, primaryTarget, GCDPriority.ForcedGCD); + if (downtimeIn == GCD && (Ammo == 2 || Ammo == 3) || //if 1 GCD until downtime & has 2 or 3 cartridges + downtimeIn == GCD * 4 && Ammo == 1 || //if 4 GCDs until downtime & has 1 cartridge + downtimeIn == GCD * 7 && Ammo == 0) //if 7 GCDs until downtime & has 0 cartridges + QueueGCD(AID.SolidBarrel, //queue Solid Barrel + primaryTarget, //on the primary target + GCDPriority.ForcedGCD); //with priority for forced GCDs } - if (Ammo == MaxCartridges) - QueueGCD(NextForceSingleTarget(), primaryTarget, GCDPriority.ForcedGCD); + if (Ammo == MaxCartridges) //if at max cartridges + QueueGCD(NextForceSingleTarget(), //queue the next single-target combo action without overcap protection to save resources for uptime + primaryTarget, //on the primary target + GCDPriority.ForcedGCD); //with priority for forced GCDs } #endregion + #region Standard Execution + if (AOEStrategy == AOEStrategy.AutoBreakCombo) //if Break Combo option is selected + { + if (ShouldUseAOE) //if AOE rotation should be used + QueueGCD(NextComboAOE(), //queue the next AOE combo action + Player, //on Self (no target needed) + GCDPriority.Combo123); //with priority for 123/12 combo actions + if (!ShouldUseAOE) + QueueGCD(NextComboSingleTarget(), //queue the next single-target combo action + primaryTarget, //on the primary target + GCDPriority.Combo123); //with priority for 123/12 combo actions + } + if (AOEStrategy == AOEStrategy.AutoFinishCombo) //if Finish Combo option is selected + { + QueueGCD(NextBestRotation(), //queue the next single-target combo action only if combo is finished + primaryTarget, //on the primary target + GCDPriority.Combo123); //with priority for 123/12 combo actions + } #endregion - #region Rotation Execution - //Determine and queue combo actions - var (comboAction, comboPrio) = ComboActionPriority(AOEStrategy, AoETargets, burstStrategy, burst.Value.ExpireIn); - QueueGCD(comboAction, comboAction is AID.DemonSlice or AID.DemonSlaughter ? Player : primaryTarget, Ammo == 0 ? GCDPriority.comboNeed : comboPrio); + #endregion #region OGCDs - //No Mercy execution - var nmStrat = strategy.Option(Track.NoMercy).As(); - if (!hold && ShouldUseNoMercy(nmStrat, primaryTarget)) - QueueOGCD(AID.NoMercy, Player, nmStrat is NoMercyStrategy.Force or NoMercyStrategy.ForceLW or NoMercyStrategy.Force2 or NoMercyStrategy.Force2LW or NoMercyStrategy.Force3 or NoMercyStrategy.Force3LW ? OGCDPriority.ForcedOGCD : OGCDPriority.NoMercy); - - //Zone execution (Blasting Zone / Danger Zone) - var zoneAction = Unlocked(AID.BlastingZone) ? AID.BlastingZone : AID.DangerZone; - var zoneStrat = strategy.Option(Track.Zone).As(); - if (!hold && ShouldUseZone(zoneStrat, primaryTarget)) - QueueOGCD(zoneAction, primaryTarget, zoneStrat == OffensiveStrategy.Force ? OGCDPriority.ForcedOGCD : OGCDPriority.Zone); - - //Bow Shock execution - var bowStrat = strategy.Option(Track.BowShock).As(); - if (!hold && ShouldUseBowShock(bowStrat, primaryTarget)) - QueueOGCD(AID.BowShock, Player, bowStrat == OffensiveStrategy.Force ? OGCDPriority.ForcedOGCD : OGCDPriority.BowShock); - - //Bloodfest execution - var bfStrat = strategy.Option(Track.Bloodfest).As(); - if (!hold && ShouldUseBloodfest(bfStrat, primaryTarget)) - QueueOGCD(AID.Bloodfest, primaryTarget, bfStrat is BloodfestStrategy.Force or BloodfestStrategy.Force0 ? OGCDPriority.ForcedOGCD : OGCDPriority.Bloodfest); + if (!hold) //if not holding cooldowns + { + //No Mercy execution + if (ShouldUseNoMercy(nmStrat, primaryTarget)) //if No Mercy should be used + QueueOGCD(AID.NoMercy, //queue No Mercy + Player, //on Self (no target needed, but desired to not waste) + nmStrat is NoMercyStrategy.Force //if strategy option is Force + or NoMercyStrategy.ForceW //or Force weave + or NoMercyStrategy.ForceQW //or Force last second weave + or NoMercyStrategy.Force1 //or Force with 1 cartridge + or NoMercyStrategy.Force1W //or Force weave with 1 cartridge + or NoMercyStrategy.Force1QW //or Force last second weave with 1 cartridge + or NoMercyStrategy.Force2 //or Force with 2 cartridges + or NoMercyStrategy.Force2W //or Force weave with 2 cartridges + or NoMercyStrategy.Force2QW //or Force last second weave with 2 cartridges + or NoMercyStrategy.Force3 //or Force with 3 cartridges + or NoMercyStrategy.Force3W //or Force weave with 3 cartridges + or NoMercyStrategy.Force3QW //or Force last second weave with 3 cartridges + ? OGCDPriority.ForcedOGCD //use priority for forced oGCDs + : OGCDPriority.NoMercy); //otherwise, use intended priority + + //Zone execution (Blasting Zone / Danger Zone) + if (ShouldUseZone(zoneStrat, primaryTarget)) //if Zone should be used + QueueOGCD(BestZone, //queue the best Zone action + primaryTarget, //on the primary target + zoneStrat is OGCDStrategy.Force //if strategy option is Force + or OGCDStrategy.AnyWeave //or any Weave + or OGCDStrategy.EarlyWeave //or Early Weave + or OGCDStrategy.LateWeave //or Late Weave + ? OGCDPriority.ForcedOGCD //use priority for forced oGCDs + : OGCDPriority.Zone); //otherwise, use intended priority + + //Bow Shock execution + if (ShouldUseBowShock(bowStrat, primaryTarget)) //if Bow Shock should be used + QueueOGCD(AID.BowShock, //queue Bow Shock + Player, //on Self (no target needed) + bowStrat is OGCDStrategy.Force //if strategy option is Force + or OGCDStrategy.AnyWeave //or Any Weave + or OGCDStrategy.EarlyWeave //or Early Weave + or OGCDStrategy.LateWeave //or Late Weave + ? OGCDPriority.ForcedOGCD //use priority for forced oGCDs + : OGCDPriority.BowShock); //otherwise, use intended priority + + //Bloodfest execution + if (ShouldUseBloodfest(bfStrat, primaryTarget)) //if Bloodfest should be used + QueueOGCD(AID.Bloodfest, //queue Bloodfest + primaryTarget, //on the primary target + bfStrat is BloodfestStrategy.Force //if strategy option is Force + or BloodfestStrategy.ForceW //or Force weave + or BloodfestStrategy.Force0 //or Force with 0 cartridges + or BloodfestStrategy.Force0W //or Force weave with 0 cartridges + ? OGCDPriority.ForcedOGCD //use priority for forced oGCDs + : OGCDPriority.Bloodfest); //otherwise, use intended priority + } //Continuation execution - if (canContinue) + if (canContinue) //if Continuation is available { - if (hasRip) - QueueOGCD(AID.JugularRip, primaryTarget, OGCDPriority.ContinuationNeed); - if (hasTear) - QueueOGCD(AID.AbdomenTear, primaryTarget, OGCDPriority.ContinuationNeed); - if (hasGouge) - QueueOGCD(AID.EyeGouge, primaryTarget, OGCDPriority.ContinuationNeed); - if (hasBlast) - QueueOGCD(AID.Hypervelocity, primaryTarget, OGCDPriority.ContinuationNeed); - if (hasRaze) - QueueOGCD(AID.FatedBrand, primaryTarget, OGCDPriority.ContinuationNeed); + if (hasRip || //if Jugular Rip is ready + hasTear || //or Abdomen Tear is ready + hasGouge || //or Eye Gouge is ready + hasRaze || //or Fated Brand is ready + hasBlast) //or Hypervelocity is ready + QueueOGCD(BestContinuation, //queue the best Continuation action + primaryTarget, //on the primary target + OGCDPriority.Continuation); //with priority for needed Continuation actions } #endregion #region GCDs - //Gnashing Fang execution - var gfStrat = strategy.Option(Track.GnashingFang).As(); - if (!hold && ShouldUseGnashingFang(gfStrat, primaryTarget)) - QueueGCD(AID.GnashingFang, primaryTarget, gfStrat == GnashingStrategy.ForceGnash ? GCDPriority.ForcedGCD : GCDPriority.GF1); - - //Double Down execution - var ddStrat = strategy.Option(Track.DoubleDown).As(); - if (ShouldUseDoubleDown(ddStrat, primaryTarget)) - QueueGCD(AID.DoubleDown, primaryTarget, ddStrat == OffensiveStrategy.Force || Ammo == 1 ? GCDPriority.ForcedGCD : GCDPriority.DoubleDown); - - //Gnashing Fang Combo execution - if (GunComboStep == 1) - QueueGCD(AID.SavageClaw, primaryTarget, gfStrat == GnashingStrategy.ForceClaw ? GCDPriority.ForcedGCD : GCDPriority.GF23); - if (GunComboStep == 2) - QueueGCD(AID.WickedTalon, primaryTarget, gfStrat == GnashingStrategy.ForceTalon ? GCDPriority.ForcedGCD : GCDPriority.GF23); - - //Reign full combo execution - if (canReign && hasNM && GunComboStep == 0) - QueueGCD(AID.ReignOfBeasts, primaryTarget, GCDPriority.Reign); - if (GunComboStep == 3) - QueueGCD(AID.NobleBlood, primaryTarget, GCDPriority.Reign); - if (GunComboStep == 4) - QueueGCD(AID.LionHeart, primaryTarget, GCDPriority.Reign); - - //Sonic Break execution - var sbStrat = strategy.Option(Track.SonicBreak).As(); - if (ShouldUseSonicBreak(sbStrat, primaryTarget)) - QueueGCD(AID.SonicBreak, primaryTarget, sbStrat is SonicBreakStrategy.Force or SonicBreakStrategy.EarlySB ? GCDPriority.ForcedGCD : GCDPriority.SonicBreak); - - //Burst Strike execution - var strikeStrat = strategy.Option(Track.BurstStrike).As(); - if (Unlocked(AID.BurstStrike) && Unlocked(AID.Bloodfest) && ShouldUseBurstStrike(strikeStrat, primaryTarget)) - QueueGCD(AID.BurstStrike, primaryTarget, strikeStrat == OffensiveStrategy.Force ? GCDPriority.ForcedGCD : nmCD < 1 ? GCDPriority.ForcedGCD : GCDPriority.BurstStrike); - - //Fated Circle execution - var fcStrat = strategy.Option(Track.FatedCircle).As(); - if (ShouldUseFatedCircle(fcStrat, primaryTarget)) - QueueGCD(AID.FatedCircle, primaryTarget, fcStrat == OffensiveStrategy.Force ? GCDPriority.ForcedGCD : GCDPriority.FatedCircle); - if (!canFC && canBS) + if (!hold) //if not holding cooldowns { - if (Ammo > 0 && ShouldUseBurstStrike(strikeStrat, primaryTarget)) - QueueGCD(AID.BurstStrike, primaryTarget, GCDPriority.BurstStrike); + if (!conserve) //if not conserving cartridges + { + //Double Down execution + if (ShouldUseDoubleDown(ddStrat, primaryTarget)) //if Double Down should be used + QueueGCD(AID.DoubleDown, //queue Double Down + primaryTarget, //on the primary target + cartStrat == CartridgeStrategy.DoubleDown || //if Double Down is selected on Cartridge strategy + ddStrat == GCDStrategy.Force || //or Force Double Down is selected on Double Down strategy + Ammo == 1 //or only 1 cartridge is available + ? GCDPriority.ForcedGCD //use priority for forced GCDs + : GCDPriority.DoubleDown); //otherwise, use intended priority + //Gnashing Fang execution + if (ShouldUseGnashingFang(gfStrat, primaryTarget)) //if Gnashing Fang should be used + QueueGCD(AID.GnashingFang, //queue Gnashing Fang + primaryTarget, //on the primary target + cartStrat == CartridgeStrategy.GnashingFang || //if Gnashing Fang is selected on Cartridge strategy + gfStrat == GnashingStrategy.ForceGnash //or Force Gnashing Fang is selected on Gnashing Fang strategy + ? GCDPriority.ForcedGCD //use priority for forced GCDs + : GCDPriority.GF1); //otherwise, use intended priority + //Burst Strike & Fated Circle execution + if (ShouldUseCartridges(cartStrat, primaryTarget)) //if Cartridges should be used + { + //Optimal targeting & execution for both gauge spenders + if (cartStrat == CartridgeStrategy.Automatic) //if Automatic Cartridge strategy is selected + QueueGCD(BestCartSpender, //queue the best cartridge spender + primaryTarget, //on the primary target + nmCD < 1 && Ammo == 3 //if No Mercy is imminent and 3 cartridges are available + ? GCDPriority.ForcedGCD //use priority for forced GCDs + : GCDPriority.Gauge); //otherwise, use priority for gauge actions + //Burst Strike forced execution + if (cartStrat == CartridgeStrategy.BurstStrike) //if Burst Strike Cartridge strategy is selected + QueueGCD(AID.BurstStrike, //queue Burst Strike + primaryTarget, //on the primary target + GCDPriority.Gauge); //with priority for gauge actions + //Fated Circle forced execution + if (cartStrat == CartridgeStrategy.FatedCircle) //if Fated Circle Cartridge strategy is selected + QueueGCD(AID.FatedCircle, //queue Fated Circle + Player, //on Self (no target needed) + GCDPriority.Gauge); //with priority for gauge actions + } + } + + //Sonic Break execution + if (ShouldUseSonicBreak(sbStrat, primaryTarget)) //if Sonic Break should be used + QueueGCD(AID.SonicBreak, //queue Sonic Break + primaryTarget, //on the primary target + sbStrat is SonicBreakStrategy.Force //if strategy option is Force + or SonicBreakStrategy.Early //or Early + ? GCDPriority.ForcedGCD //use priority for forced GCDs + : GCDPriority.SonicBreak); //otherwise, use intended priority + //Reign of Beasts execution + if (ShouldUseReign(reignStrat, primaryTarget)) //if Reign of Beasts should be used + QueueGCD(AID.ReignOfBeasts, //queue Reign of Beasts + primaryTarget, //on the primary target + reignStrat == ReignStrategy.ForceReign //if Force Reign of Beasts is selected on Reign of Beasts strategy + ? GCDPriority.ForcedGCD //use priority for forced GCDs + : GCDPriority.Reign); //otherwise, use intended priority } + //Gnashing Fang combo execution + if (GunComboStep == 1) //if just used Gnashing Fang + QueueGCD(AID.SavageClaw, //queue Savage Claw + primaryTarget, //on the primary target + gfStrat == GnashingStrategy.ForceClaw //if Force Savage Claw is selected on Gnashing Fang strategy + ? GCDPriority.ForcedGCD //use priority for forced GCDs + : GCDPriority.GF23); //otherwise, use priority for Gnashing Fang combo steps + if (GunComboStep == 2) //if just used Savage Claw + QueueGCD(AID.WickedTalon, //queue Wicked Talon + primaryTarget, //on the primary target + gfStrat == GnashingStrategy.ForceTalon //if Force Wicked Talon is selected on Gnashing Fang strategy + ? GCDPriority.ForcedGCD //use priority for forced GCDs + : GCDPriority.GF23); //otherwise, use priority for Gnashing Fang combo steps + //Reign of Beasts combo execution + if (GunComboStep == 3) //if just used Wicked Talon + QueueGCD(AID.NobleBlood, //queue Noble Blood + primaryTarget, //on the primary target + reignStrat == ReignStrategy.ForceNoble //if Force Noble Blood is selected on Reign of Beasts strategy + ? GCDPriority.ForcedGCD //use priority for forced GCDs + : GCDPriority.Reign); //otherwise, use priority for Reign of Beasts combo steps + if (GunComboStep == 4) //if just used Noble Blood + QueueGCD(AID.LionHeart, //queue Lion Heart + primaryTarget, //on the primary target + reignStrat == ReignStrategy.ForceLion //if Force Lion Heart is selected on Reign of Beasts strategy + ? GCDPriority.ForcedGCD //use priority for forced GCDs + : GCDPriority.Reign); //otherwise, use priority for Reign of Beasts combo steps //Lightning Shot execution - var lsStrat = strategy.Option(Track.LightningShot).As(); - if (ShouldUseLightningShot(primaryTarget, strategy.Option(Track.LightningShot).As())) - QueueGCD(AID.LightningShot, primaryTarget, lsStrat is LightningShotStrategy.Force or LightningShotStrategy.Ranged ? GCDPriority.ForcedGCD : GCDPriority.Combo123); + if (ShouldUseLightningShot(primaryTarget, lsStrat)) //if Lightning Shot should be used + QueueGCD(AID.LightningShot, //queue Lightning Shot + primaryTarget, //on the primary target + lsStrat is LightningShotStrategy.Force //if strategy option is Force + or LightningShotStrategy.Allow //or Allow + ? GCDPriority.ForcedGCD //use priority for forced GCDs + : GCDPriority.Combo123); //otherwise, use priority for standard combo actions + //Potion execution + if (ShouldUsePotion(strategy.Option(Track.Potion).As())) //if Potion should be used + Hints.ActionsToExecute.Push(ActionDefinitions.IDPotionStr, //queue the potion + Player, //on Self (no target needed) + ActionQueue.Priority.VeryHigh //with very high priority + + (int)OGCDPriority.Potion, 0, GCD - 0.9f); //and the specified priority #endregion #endregion - - //Potion execution - if (ShouldUsePotion(strategy.Option(Track.Potion).As())) - Hints.ActionsToExecute.Push(ActionDefinitions.IDPotionStr, Player, ActionQueue.Priority.VeryHigh + (int)OGCDPriority.Potion, 0, GCD - 0.9f); } #region Core Execution Helpers - //QueueGCD execution - private void QueueGCD(AID aid, Actor? target, GCDPriority prio) + private void QueueGCD(AID aid, Actor? target, GCDPriority prio) //QueueGCD execution { - if (prio != GCDPriority.None) + if (prio != GCDPriority.None) //if priority is not None { - Hints.ActionsToExecute.Push(ActionID.MakeSpell(aid), target, ActionQueue.Priority.High + (int)prio); - if (prio > NextGCDPrio) + Hints.ActionsToExecute.Push(ActionID.MakeSpell(aid), //queue the action + target, //on the target + ActionQueue.Priority.High //with high priority + + (int)prio); //and the specified priority + if (prio > NextGCDPrio) //if the priority is higher than the current next GCD priority { - NextGCD = aid; - NextGCDPrio = prio; + NextGCD = aid; //set the next GCD to this action + NextGCDPrio = prio; //set the next GCD priority to this priority } } } - //QueueOGCD execution - private void QueueOGCD(AID aid, Actor? target, OGCDPriority prio, float basePrio = ActionQueue.Priority.Medium) + private void QueueOGCD(AID aid, Actor? target, OGCDPriority prio, float basePrio = ActionQueue.Priority.Medium) //QueueOGCD execution { - if (prio != OGCDPriority.None) + if (prio != OGCDPriority.None) //if priority is not None { - Hints.ActionsToExecute.Push(ActionID.MakeSpell(aid), target, basePrio + (int)prio); + Hints.ActionsToExecute.Push(ActionID.MakeSpell(aid), //queue the action + target, //on the target + basePrio //with the specified base priority + + (int)prio); //and the specified priority } } - //Returns the amount of ammo gained from specific actions - private int AmmoGainedFromAction(AID action) => action switch + private AID NextBestRotation() => ComboLastMove switch { - AID.SolidBarrel => 1, - AID.DemonSlaughter => 1, - AID.Bloodfest => 3, - _ => 0 + //ST + AID.SolidBarrel => ShouldUseAOE ? NextComboAOE() : NextComboSingleTarget(), + AID.BrutalShell => NextComboSingleTarget(), + AID.KeenEdge => NextComboSingleTarget(), + //AOE + AID.DemonSlaughter => ShouldUseAOE ? NextComboAOE() : NextComboSingleTarget(), + AID.DemonSlice => NextComboAOE(), + _ => ShouldUseAOE ? NextComboAOE() : NextComboSingleTarget(), }; - private (AID, GCDPriority) ComboActionPriority(AOEStrategy aoeStrategy, int AoETargets, BurstStrategy burstStrategy, float burstStrategyExpire) - { - //Determine how many combo steps are remaining based on the last action - var comboStepsRemaining = ComboLastMove switch - { - AID.KeenEdge => Unlocked(AID.SolidBarrel) ? 2 : Unlocked(AID.BrutalShell) ? 1 : 0, - AID.DemonSlice => Unlocked(AID.DemonSlaughter) ? 1 : 0, - _ => 0 - }; - - //Check if we can fit the GCD based on remaining time - if (comboStepsRemaining > 0 && !CanFitGCD(World.Client.ComboState.Remaining)) - comboStepsRemaining = 0; - - var doingAOECombo = ComboLastMove == AID.DemonSlice; - - //Determine if an AoE action is desirable based on target count and strategy - var wantAOEAction = Unlocked(AID.DemonSlice) && aoeStrategy switch - { - AOEStrategy.SingleTarget => false, - AOEStrategy.FocusSingleTarget => false, - AOEStrategy.ForceAoE => true, - AOEStrategy.FocusAoE => false, - AOEStrategy.Auto => AoETargets >= 3, - AOEStrategy.AutoFinishCombo => comboStepsRemaining > 0 - ? doingAOECombo - : Unlocked(AID.Continuation) ? AoETargets >= 3 : AoETargets >= 2, - AOEStrategy.GenerateDowntime => false, - _ => false - }; - - //Reset combo steps if the desired action does not match the current combo type - if (comboStepsRemaining > 0 && wantAOEAction != doingAOECombo) - comboStepsRemaining = 0; - - var nextAction = wantAOEAction ? NextComboAoE() : NextComboSingleTarget(); - var riskingAmmo = Ammo + AmmoGainedFromAction(nextAction) > 3; - - //Return combo priority based on the ability to fit GCDs and remaining combo steps - if (comboStepsRemaining > 0 && !CanFitGCD(World.Client.ComboState.Remaining, 1)) - return (nextAction, GCDPriority.Combo123); - - //Return normal combo action priority based on ammo risks - return (nextAction, riskingAmmo ? GCDPriority.BurstStrike : GCDPriority.Combo123); - } - #endregion #region Single-Target Helpers - private AID NextComboSingleTarget() => ComboLastMove switch //Determines the next single-target action based on the last action used + private AID NextComboSingleTarget() => ComboLastMove switch //with Overcap protection { - AID.BrutalShell => Ammo == MaxCartridges ? AID.BurstStrike : AID.SolidBarrel, - AID.KeenEdge => AID.BrutalShell, - _ => AID.KeenEdge, + AID.BrutalShell => + Ammo == MaxCartridges //if Brutal Shell is last move, check ammo count + ? (TargetsInAOERange() > 1 //if 2+ targets in AOE range + ? AID.FatedCircle //use Fated Circle + : AID.BurstStrike) //otherwise, use Burst Strike + : AID.SolidBarrel, //if not at max ammo, use Solid Barrel + AID.KeenEdge => AID.BrutalShell, //if Keen Edge is last move, use Brutal Shell + _ => AID.KeenEdge, //start with Keen Edge }; - private AID NextForceSingleTarget() => ComboLastMove switch + private AID NextForceSingleTarget() => ComboLastMove switch //without Overcap protection { - AID.BrutalShell => AID.SolidBarrel, - AID.KeenEdge => AID.BrutalShell, - _ => AID.KeenEdge, + AID.BrutalShell => AID.SolidBarrel, //if Brutal Shell is last move, use Solid Barrel regardless of ammo count + AID.KeenEdge => AID.BrutalShell, //if Keen Edge is last move, use Brutal Shell + _ => AID.KeenEdge, //start with Keen Edge }; #endregion - #region AoE Helpers - private AID NextComboAoE() => ComboLastMove switch //Determines the next AoE action based on the last action used + #region AOE Helpers + private AID NextComboAOE() => ComboLastMove switch //with Overcap protection { - AID.DemonSlice => Ammo == MaxCartridges - ? Unlocked(AID.FatedCircle) ? AID.FatedCircle : AID.BurstStrike - : AID.DemonSlaughter, - _ => AID.DemonSlice, + AID.DemonSlice => Ammo == MaxCartridges //if Demon Slice is last move, check ammo count + ? Unlocked(AID.FatedCircle) //if Fated Circle is unlocked + ? AID.FatedCircle //use Fated Circle + : AID.BurstStrike //otherwise, use Burst Strike + : AID.DemonSlaughter, //if not at max ammo, use Demon Slaughter + _ => AID.DemonSlice, //start with Demon Slice }; - private AID NextForceAoE() => ComboLastMove switch + private AID NextForceAOE() => ComboLastMove switch //without Overcap protection { - AID.DemonSlice => AID.DemonSlaughter, - _ => AID.DemonSlice, + AID.DemonSlice => AID.DemonSlaughter, //if Demon Slice is last move, use Demon Slaughter regardless of ammo count + _ => AID.DemonSlice, //start with Demon Slice }; #endregion #region Cooldown Helpers - - //Determines when to use Lightning Shot - private bool ShouldUseLightningShot(Actor? target, LightningShotStrategy strategy) => strategy switch - { - LightningShotStrategy.OpenerRanged => IsFirstGCD() && !In3y(target), - LightningShotStrategy.Opener => IsFirstGCD(), - LightningShotStrategy.Force => true, - LightningShotStrategy.Ranged => !In3y(target), - LightningShotStrategy.Forbid => false, - _ => false - }; - - //Determines when to use No Mercy + //No Mercy full strategy & conditions private bool ShouldUseNoMercy(NoMercyStrategy strategy, Actor? target) => strategy switch { NoMercyStrategy.Automatic => - Player.InCombat && target != null && ActionReady(AID.NoMercy) && GCD < 0.9f && - (Ammo < 3 || //Lv90+ Opener - Ammo >= 1 && bfCD == 0 && Unlocked(AID.Bloodfest) && !Unlocked(AID.DoubleDown) || //Lv80+ Opener - !Unlocked(AID.Bloodfest) && Ammo >= 1 && ActionReady(AID.GnashingFang)), //Lv70 & below - NoMercyStrategy.Force => true, - NoMercyStrategy.ForceLW => Player.InCombat && GCD < 0.9f, - NoMercyStrategy.Force2 => Ammo >= 2, - NoMercyStrategy.Force2LW => Player.InCombat && GCD < 0.9f && Ammo >= 2, - NoMercyStrategy.Force3 => Ammo == 3, - NoMercyStrategy.Force3LW => Player.InCombat && GCD < 0.9f && Ammo == 3, - NoMercyStrategy.Delay => false, + //Standard conditions + Player.InCombat && //In combat + target != null && //Target exists + canNM && //No Mercy is available + GCD < 0.9f && //GCD is less than 0.9s + ((Unlocked(AID.DoubleDown) && //Double Down is unlocked, indicating Lv90 or above + Ammo < 3) || //Ammo is not 3 + (!Unlocked(AID.DoubleDown) && Unlocked(AID.Bloodfest) && //Double Down is not unlocked but Bloodfest is, indicating Lv80-89 + Ammo >= 1 && //Ammo is 1 or more + bfCD is < 5 or 0) || //Bloodfest is ready or about to be + (!Unlocked(AID.Bloodfest) && canGF) || //Bloodfest is not unlocked but Gnashing Fang is, indicating Lv60-79 + !Unlocked(AID.GnashingFang)), //Gnashing Fang is not unlocked, indicating Lv59 and below + NoMercyStrategy.Force => canNM, //Force No Mercy, regardless of correct weaving + NoMercyStrategy.ForceW => canNM && canWeaveIn, //Force No Mercy into any weave slot + NoMercyStrategy.ForceQW => canNM && quarterWeave, //Force No Mercy into last possible second weave slot + NoMercyStrategy.Force1 => canNM && Ammo >= 1, //Force No Mercy if ready and 1 cartridge, regardless of weaving + NoMercyStrategy.Force1W => canNM && canWeaveIn && Ammo >= 1, //Force No Mercy into any weave slot if ready and 1 cartridge + NoMercyStrategy.Force1QW => canNM && quarterWeave && Ammo >= 1, //Force No Mercy into last possible second weave slot if ready and 1 cartridge + NoMercyStrategy.Force2 => canNM && Ammo >= 2, //Force No Mercy if ready and 2 cartridges, regardless of weaving + NoMercyStrategy.Force2W => canNM && canWeaveIn && Ammo >= 2, //Force No Mercy into any weave slot if ready and 2 cartridges + NoMercyStrategy.Force2QW => canNM && quarterWeave && Ammo >= 2, //Force No Mercy into last possible second weave slot if ready and 2 cartridges + NoMercyStrategy.Force3 => canNM && Ammo == 3, //Force No Mercy if ready and 3 cartridges, regardless of weaving + NoMercyStrategy.Force3W => canNM && canWeaveIn && Ammo == 3, //Force No Mercy into any weave slot if ready and 3 cartridges + NoMercyStrategy.Force3QW => canNM && quarterWeave && Ammo == 3, //Force No Mercy into last possible second weave slot if ready and 3 cartridges + NoMercyStrategy.Delay => false, //Delay No Mercy _ => false }; - //Determines when to use Bloodfest + //Bloodfest full strategy & conditions private bool ShouldUseBloodfest(BloodfestStrategy strategy, Actor? target) => strategy switch { BloodfestStrategy.Automatic => - Player.InCombat && target != null && - canBF && Ammo == 0, - BloodfestStrategy.Force => canBF, - BloodfestStrategy.Force0 => canBF && Ammo == 0, - BloodfestStrategy.Delay => false, + Player.InCombat && //In combat + target != null && //Target exists + canBF && Ammo == 0, //Bloodfest is available and empty on cartridges + BloodfestStrategy.Force => canBF, //Force Bloodfest, regardless of correct weaving + BloodfestStrategy.ForceW => canBF && canWeaveIn, //Force Bloodfest into any weave slot + BloodfestStrategy.Force0 => canBF && Ammo == 0, //Force Bloodfest if ready and 0 cartridges, regardless of weaving + BloodfestStrategy.Force0W => canBF && Ammo == 0 && canWeaveIn, //Force Bloodfest into any weave slot if ready and 0 cartridges + BloodfestStrategy.Delay => false, //Delay Bloodfest _ => false }; - //Determines when to use Zone - private bool ShouldUseZone(OffensiveStrategy strategy, Actor? target) => strategy switch + //Zone full strategy & conditions + private bool ShouldUseZone(OGCDStrategy strategy, Actor? target) => strategy switch { - OffensiveStrategy.Automatic => - Player.InCombat && In3y(target) && nmCD is < 57.55f and > 17 && - ActionReady(Unlocked(AID.BlastingZone) ? AID.BlastingZone : AID.DangerZone), - OffensiveStrategy.Force => canZone, - OffensiveStrategy.Delay => false, + OGCDStrategy.Automatic => + Player.InCombat && //In combat + In3y(target) && //Target in melee range + canZone && //Zone is available + nmCD is < 57.55f and > 17, //No Mercy is active & not just used within 1GCD or CD is greater than 17s + OGCDStrategy.Force => canZone, //Force Zone if available + OGCDStrategy.AnyWeave => canZone && canWeaveIn, //Force Zone into any weave slot + OGCDStrategy.EarlyWeave => canZone && canWeaveEarly, //Force weave Zone early + OGCDStrategy.LateWeave => canZone && canWeaveLate, //Force weave Zone late + OGCDStrategy.Delay => false, _ => false }; - //Determines when to use BowShock - private bool ShouldUseBowShock(OffensiveStrategy strategy, Actor? target) => strategy switch + //Bow Shock full strategy & conditions + private bool ShouldUseBowShock(OGCDStrategy strategy, Actor? target) => strategy switch { - OffensiveStrategy.Automatic => - Player.InCombat && ActionReady(AID.BowShock) && In5y(target) && nmCD is < 57.55f and > 17, - OffensiveStrategy.Force => canBow, - OffensiveStrategy.Delay => false, + OGCDStrategy.Automatic => + Player.InCombat && //In combat + ActionReady(AID.BowShock) && //Bow Shock is available + In5y(target) && //Target in range + nmCD is < 57.55f and >= 40, //No Mercy is active, but not just used within 1GCD + OGCDStrategy.Force => canBow, //Force Bow Shock if available, regardless of weaving + OGCDStrategy.AnyWeave => canBow && canWeaveIn, //Force Bow Shock into any weave slot + OGCDStrategy.EarlyWeave => canBow && canWeaveEarly, //Force weave Bow Shock early + OGCDStrategy.LateWeave => canBow && canWeaveLate, //Force weave Bow Shock late + OGCDStrategy.Delay => false, _ => false }; - //Determines when to use Sonic Break - private bool ShouldUseSonicBreak(SonicBreakStrategy strategy, Actor? target) => strategy switch + //Gauge full strategy & conditions + private bool ShouldUseCartridges(CartridgeStrategy strategy, Actor? target) => strategy switch { - SonicBreakStrategy.Automatic => - Player.InCombat && In3y(target) && canBreak, - SonicBreakStrategy.Force => canBreak, - SonicBreakStrategy.EarlySB => nmCD is >= 57.5f || hasBreak, - SonicBreakStrategy.LateSB => nmLeft <= GCDLength, - SonicBreakStrategy.Delay => false, + CartridgeStrategy.Automatic => + ShouldUseFC //enough targets for optimal use of Fated Circle + ? ShouldUseFatedCircle(CartridgeStrategy.Automatic, target) //use Fated Circle + : ShouldUseBurstStrike(CartridgeStrategy.Automatic, target), //otherwise, use Burst Strike + CartridgeStrategy.BurstStrike => canBS, //Force Burst Strike if available + CartridgeStrategy.FatedCircle => canFC, //Force Fated Circle if available + CartridgeStrategy.GnashingFang => canGF && GunComboStep == 0, //Force Gnashing Fang if available and not in Reign combo + CartridgeStrategy.DoubleDown => canDD, //Force Double Down if available + CartridgeStrategy.Conserve => false, //Conserve cartridges _ => false }; - //Determines when to use Double Down - private bool ShouldUseDoubleDown(OffensiveStrategy strategy, Actor? target) => strategy switch + //Double Down full strategy & conditions + private bool ShouldUseDoubleDown(GCDStrategy strategy, Actor? target) => strategy switch { - OffensiveStrategy.Automatic => - Player.InCombat && target != null && - In5y(target) && canDD && hasNM, - OffensiveStrategy.Force => canDD, - OffensiveStrategy.Delay => false, + GCDStrategy.Automatic => + Player.InCombat && //In combat + target != null && //Target exists + In5y(target) && //Target in range + canDD && //Double Down is available + hasNM, //No Mercy is active + GCDStrategy.Force => canDD, //Force Double Down if available + GCDStrategy.Delay => false, _ => false }; - //Determines when to use Gnashing Fang + //Gnashing Fang & combo chain full strategy & conditions private bool ShouldUseGnashingFang(GnashingStrategy strategy, Actor? target) => strategy switch { GnashingStrategy.Automatic => - Player.InCombat && target != null && In3y(target) && canGF && - (nmLeft > 0 || hasNM || nmCD is < 35 and > 17), - GnashingStrategy.ForceGnash => canGF, - GnashingStrategy.ForceClaw => Player.InCombat && GunComboStep == 1, - GnashingStrategy.ForceTalon => Player.InCombat && GunComboStep == 2, + Player.InCombat && //In combat + target != null && //Target exists + In3y(target) && //Target in melee range + canGF && //Gnashing Fang is available + (nmLeft > 0 || hasNM || //No Mercy is active + nmCD is < 35 and > 17), //or greater than 17s on No Mercy CD + GnashingStrategy.ForceGnash => canGF, //Gnashing Fang is available + GnashingStrategy.ForceClaw => Player.InCombat && GunComboStep == 1, //Force Savage Claw if available + GnashingStrategy.ForceTalon => Player.InCombat && GunComboStep == 2, //Force Wicked Talon if available GnashingStrategy.Delay => false, _ => false }; - //Determines when to use Burst Strike - private bool ShouldUseBurstStrike(OffensiveStrategy strategy, Actor? target) => strategy switch + //Burst Strike & Fated Circle full strategy & conditions + private bool ShouldUseBurstStrike(CartridgeStrategy strategy, Actor? target) => strategy switch { - OffensiveStrategy.Automatic => - Player.InCombat && target != null && In3y(target) && canBS && - (nmCD < 1.5f || - Unlocked(AID.DoubleDown) && hasNM && !ActionReady(AID.DoubleDown) && GunComboStep == 0 && !hasReign || //Lv90+ - !Unlocked(AID.DoubleDown) && !ActionReady(AID.GnashingFang) && hasNM && GunComboStep == 0 || //Lv80 & Below - ComboLastMove == AID.BrutalShell && Ammo == MaxCartridges), //Overcap protection + CartridgeStrategy.Automatic => + Player.InCombat && //In combat + target != null && //Target exists + In3y(target) && //Target in melee range + canBS && //Burst Strike is available + (hasNM || nmCD < 1), //No Mercy is active or almost ready + _ => false + }; - OffensiveStrategy.Force => canBS, - OffensiveStrategy.Delay => false, + //Fated Circle full strategy & conditions + private bool ShouldUseFatedCircle(CartridgeStrategy strategy, Actor? target) => strategy switch + { + CartridgeStrategy.Automatic => + Player.InCombat && //In combat + target != null && //Target exists + In5y(target) && //Target in range + canFC && //Fated Circle is available + (hasNM || nmCD < 1), //No Mercy is active or almost ready _ => false }; - //Determines when to use Fated Circle - private bool ShouldUseFatedCircle(OffensiveStrategy strategy, Actor? AoETargets) => strategy switch + //Sonic Break full strategy & conditions + private bool ShouldUseSonicBreak(SonicBreakStrategy strategy, Actor? target) => strategy switch { - OffensiveStrategy.Automatic => - Player.InCombat && AoETargets != null && In3y(AoETargets) && canFC && - (hasNM && !ActionReady(AID.DoubleDown) || - ComboLastMove == AID.DemonSlice && Ammo == MaxCartridges), - OffensiveStrategy.Force => canFC, - OffensiveStrategy.Delay => false, + SonicBreakStrategy.Automatic => + Player.InCombat && //In combat + In3y(target) && //Target in melee range + canBreak, //Sonic Break is available + SonicBreakStrategy.Force => canBreak, //Force Sonic Break + SonicBreakStrategy.Early => nmCD > 55 || hasBreak, //Use Sonic Break early + SonicBreakStrategy.Late => nmLeft <= GCDLength, //Use Sonic Break late + SonicBreakStrategy.Delay => false, _ => false }; - //Determines if potions are aligned with No Mercy - private bool IsPotionAlignedWithNM() + //Reign of Beasts & combo chain full strategy & conditions + private bool ShouldUseReign(ReignStrategy strategy, Actor? target) => strategy switch { - //Use potion before Solid Barrel in opener - //Use for 6m window - return Ammo == 1 && ActionReady(AID.GnashingFang) && - ActionReady(AID.DoubleDown) && - ActionReady(AID.Bloodfest) || //Opener - (bfCD < 15 || ActionReady(AID.Bloodfest)) && Ammo == 3; - } + ReignStrategy.Automatic => + Player.InCombat && //In combat + target != null && //Target exists + canReign && //Reign of Beasts is available + hasNM && //No Mercy is active + GunComboStep == 0, //not in GF combo + ReignStrategy.ForceReign => canReign, //Force Reign of Beasts + ReignStrategy.ForceNoble => Player.InCombat && GunComboStep == 3, //Force Noble Blood + ReignStrategy.ForceLion => Player.InCombat && GunComboStep == 4, //Force Lion Heart + ReignStrategy.Delay => false, + _ => false + }; + + //Lightning Shot full strategy & conditions + private bool ShouldUseLightningShot(Actor? target, LightningShotStrategy strategy) => strategy switch + { + LightningShotStrategy.OpenerFar => + (Player.InCombat || World.Client.CountdownRemaining < 0.8f) && //Prepull or already in combat + IsFirstGCD() && !In3y(target), //First GCD of fight and target is not in melee range + LightningShotStrategy.OpenerForce => (Player.InCombat || World.Client.CountdownRemaining < 0.8f) && IsFirstGCD(), //Prepull or already in combat and first GCD of fight + LightningShotStrategy.Force => true, //Force Lightning Shot, regardless of any cooldowns or GCDs + LightningShotStrategy.Allow => !In3y(target), //Use Lightning Shot if target is not in melee range + LightningShotStrategy.Forbid => false, //Do not use Lightning Shot + _ => false + }; - //Determines when to use a potion based on strategy + //Potion full strategy & conditions private bool ShouldUsePotion(PotionStrategy strategy) => strategy switch { - PotionStrategy.AlignWithRaidBuffs => - IsPotionAlignedWithNM() || nmCD < 5 && bfCD < 15, - PotionStrategy.Immediate => true, + PotionStrategy.AlignWithRaidBuffs => //Use potion when buffs are imminent + nmCD < 5 && //No Mercy is almost ready + bfCD < 15, //Bloodfest is almost ready + PotionStrategy.Immediate => true, //Use potion immediately _ => false }; #endregion From 82bcb1b06b56cb9f3496fb1a61a41eef9afb3295 Mon Sep 17 00:00:00 2001 From: Akechi-kun <167795370+Akechi-kun@users.noreply.github.com> Date: Wed, 18 Dec 2024 02:52:50 -0800 Subject: [PATCH 11/29] more tweaks & cleanup for DRG --- BossMod/Autorotation/akechi/AkechiDRG.cs | 190 ++++++++++++++--------- 1 file changed, 118 insertions(+), 72 deletions(-) diff --git a/BossMod/Autorotation/akechi/AkechiDRG.cs b/BossMod/Autorotation/akechi/AkechiDRG.cs index 29cd423201..f39f704130 100644 --- a/BossMod/Autorotation/akechi/AkechiDRG.cs +++ b/BossMod/Autorotation/akechi/AkechiDRG.cs @@ -411,7 +411,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 ability private bool In3y(Actor? target) => Player.DistanceToHitbox(target) < 4; //Checks if the target is within max melee range (3 yalms) private bool In1y(Actor? target) => Player.DistanceToHitbox(target) < 1; //Checks if the target is within close melee range (1 yalm) - private bool In15y(Actor? target) => Player.DistanceToHitbox(target) <= 14.9; //Checks if the target is within 15 yalms + private bool In15y(Actor? target) => Player.DistanceToHitbox(target) <= 14.9f; //Checks if the target is within 15 yalms + private bool In20y(Actor? target) => Player.DistanceToHitbox(target) <= 19.9f; //Checks if the target is within 20 yalms public bool CanWeave(AID aid, double weaveTime = 0.7) => CD(aid) > weaveTime; //Checks if we can weave an ability #region Targeting Helpers @@ -537,7 +538,7 @@ public override void Execute(StrategyValues strategy, Actor? primaryTarget, floa canMD = Unlocked(AID.MirageDive) && hasMD; //minimum condition(s) to execute Mirage Dive canNastrond = Unlocked(AID.Nastrond) && hasNastrond; //minimum condition(s) to execute Nastrond canSD = Unlocked(AID.Stardiver) && ActionReady(AID.Stardiver); //minimum condition(s) to execute Stardiver - canWT = Unlocked(AID.WyrmwindThrust) && ActionReady(AID.WyrmwindThrust); //minimum condition(s) to execute Wyrmwind Thrust + canWT = Unlocked(AID.WyrmwindThrust) && ActionReady(AID.WyrmwindThrust) && focusCount == 2; //minimum condition(s) to execute Wyrmwind Thrust canROTD = Unlocked(AID.RiseOfTheDragon) && hasDF; //minimum condition(s) to execute Rise of the Dragon canSC = Unlocked(AID.Starcross) && hasSC; //minimum condition(s) to execute Starcross #endregion @@ -994,13 +995,15 @@ private void QueueOGCD(AID aid, Actor? target, OGCDPriority prio, float basePrio #region Cooldown Helpers + #region Buffs //Determines when to use Lance Charge private bool ShouldUseLanceCharge(OffensiveStrategy strategy, Actor? target) => strategy switch { OffensiveStrategy.Automatic => - Player.InCombat && - target != null && - canLC && powerLeft > 0, + Player.InCombat && //if in combat + target != null && //if has target + canLC && //if Lance Charge is ready + powerLeft > 0, //if Power Surge is active OffensiveStrategy.Force => canLC, //Always use if forced OffensiveStrategy.ForceWeave => canLC && canWeaveIn, //Always use if inside weave window OffensiveStrategy.Delay => false, //Delay usage if strategy is set to delay @@ -1011,8 +1014,10 @@ private void QueueOGCD(AID aid, Actor? target, OGCDPriority prio, float basePrio private bool ShouldUseBattleLitany(OffensiveStrategy strategy, Actor? target) => strategy switch { OffensiveStrategy.Automatic => - //Use Battle Litany automatically if the player is in combat, the target is valid, the action is ready, and there is power remaining - Player.InCombat && target != null && canBL && powerLeft > 0, + Player.InCombat && //if in combat + target != null && //if has target + canBL && //if Battle Litany is ready + powerLeft > 0, //if Power Surge is active OffensiveStrategy.Force => canBL, //Always use if forced OffensiveStrategy.ForceWeave => canBL && canWeaveIn, //Always use if inside weave window OffensiveStrategy.Delay => false, //Delay usage if strategy is set to delay @@ -1022,20 +1027,45 @@ private void QueueOGCD(AID aid, Actor? target, OGCDPriority prio, float basePrio //Determines when to use Life Surge private bool ShouldUseLifeSurge(SurgeStrategy strategy, Actor? target) => strategy switch { - SurgeStrategy.Automatic => Player.InCombat && target != null && canLS && hasLC && - !HasEffect(SID.LifeSurge) && - (CD(AID.LifeSurge) < 40 || CD(AID.BattleLitany) > 50) && - (ComboLastMove is AID.WheelingThrust or AID.FangAndClaw && Unlocked(AID.Drakesbane) || - ComboLastMove is AID.VorpalThrust or AID.LanceBarrage && Unlocked(AID.FullThrust)), - SurgeStrategy.Force => canLS, + SurgeStrategy.Automatic => + Player.InCombat && //if in combat + target != null && //if has target + canLS && //if Life Surge is ready + hasLC && //if Lance Charge is active + !HasEffect(SID.LifeSurge) && //if Life Surge is not already active + (CD(AID.LifeSurge) < 40 || //if Life Surge cooldown is less than 40s + CD(AID.BattleLitany) > 50) && //or Battle Litany cooldown is greater than 50s + (ComboLastMove is AID.WheelingThrust or AID.FangAndClaw && Unlocked(AID.Drakesbane) || //if Wheeling Thrust or Fang and Claw was just used & Drakesbane is Unlocked + ComboLastMove is AID.VorpalThrust or AID.LanceBarrage && Unlocked(AID.FullThrust)), //or Vorpal Thrust or Lance Barrage was just used & Full Thrust is Unlocked + SurgeStrategy.Force => canLS, //Always use if forced SurgeStrategy.ForceWeave => canLS && canWeaveIn, //Always use if inside weave window - SurgeStrategy.ForceNextOpti => canLS && - (ComboLastMove is AID.WheelingThrust or AID.FangAndClaw && Unlocked(AID.Drakesbane) || - ComboLastMove is AID.VorpalThrust or AID.LanceBarrage && Unlocked(AID.FullThrust)), - SurgeStrategy.ForceNextOptiWeave => canLS && canWeaveIn && - (ComboLastMove is AID.WheelingThrust or AID.FangAndClaw && Unlocked(AID.Drakesbane) || - ComboLastMove is AID.VorpalThrust or AID.LanceBarrage && Unlocked(AID.FullThrust)), - SurgeStrategy.Delay => false, + SurgeStrategy.ForceNextOpti => canLS && //if Life Surge is ready + (ComboLastMove is AID.WheelingThrust or AID.FangAndClaw && Unlocked(AID.Drakesbane) || //if Wheeling Thrust or Fang and Claw was just used & Drakesbane is Unlocked + ComboLastMove is AID.VorpalThrust or AID.LanceBarrage && Unlocked(AID.FullThrust)), //or Vorpal Thrust or Lance Barrage was just used & Full Thrust is Unlocked + SurgeStrategy.ForceNextOptiWeave => canLS && canWeaveIn && //Always use if Life Surge is ready and inside weave window + (ComboLastMove is AID.WheelingThrust or AID.FangAndClaw && Unlocked(AID.Drakesbane) || //if Wheeling Thrust or Fang and Claw was just used & Drakesbane is Unlocked + ComboLastMove is AID.VorpalThrust or AID.LanceBarrage && Unlocked(AID.FullThrust)), //or Vorpal Thrust or Lance Barrage was just used & Full Thrust is Unlocked + SurgeStrategy.Delay => false, //Delay usage if strategy is set to delay + _ => false + }; + #endregion + + #region Dives + //Determines when to use Dragonfire Dive + private bool ShouldUseDragonfireDive(DragonfireStrategy strategy, Actor? target) => strategy switch + { + DragonfireStrategy.Automatic => + Player.InCombat && //if in combat + target != null && //if has target + In20y(target) && //if within 20 yalms + canDD && //if Dragonfire Dive is ready + hasLC && //if Lance Charge is active + hasBL && //if Battle Litany is active + hasLOTD, //if Life of the Dragon is active + DragonfireStrategy.Force => canDD, //Always use if forced + DragonfireStrategy.ForceEX => canDD, //Always use in ForceEX strategy + DragonfireStrategy.ForceWeave => canDD && canWeaveIn, //Always use if inside weave window + DragonfireStrategy.Delay => false, //Delay usage if strategy is set to delay _ => false }; @@ -1044,7 +1074,12 @@ private void QueueOGCD(AID aid, Actor? target, OGCDPriority prio, float basePrio { JumpStrategy.Automatic => //Use Jump automatically if the player is in combat, the target is valid, and Lance Charge-related conditions are met - Player.InCombat && target != null && canJump && (lcLeft > 0 || hasLC || lcCD is < 35 and > 17), + Player.InCombat && //if in combat + target != null && //if has target + In20y(target) && //if within 20 yalms + canJump && //if Jump is ready + (lcLeft > 0 || hasLC || //if Lance Charge is active + lcCD is < 35 and > 17), //or greater than 17s remaining on Lance Charge cooldown JumpStrategy.ForceEX => canJump, //Always use in ForceEX strategy JumpStrategy.ForceEX2 => canJump, //Always use in ForceEX2 strategy JumpStrategy.ForceWeave => canJump && canWeaveIn, //Always use if inside weave window @@ -1052,25 +1087,46 @@ private void QueueOGCD(AID aid, Actor? target, OGCDPriority prio, float basePrio _ => false }; - //Determines when to use Dragonfire Dive - private bool ShouldUseDragonfireDive(DragonfireStrategy strategy, Actor? target) => strategy switch + //Determines when to use Stardiver + private bool ShouldUseStardiver(StardiverStrategy strategy, Actor? target) => strategy switch { - DragonfireStrategy.Automatic => - //Use Dragonfire Dive automatically if the player is in combat, the target is valid, and both Lance Charge and Battle Litany are active - Player.InCombat && target != null && In3y(target) && canDD && hasLC && hasBL, - DragonfireStrategy.Force => canDD, //Always use if forced - DragonfireStrategy.ForceEX => canDD, //Always use in ForceEX strategy - DragonfireStrategy.ForceWeave => canDD && canWeaveIn, //Always use if inside weave window - DragonfireStrategy.Delay => false, //Delay usage if strategy is set to delay + StardiverStrategy.Automatic => + Player.InCombat && //if in combat + target != null && //if has target + In20y(target) && //if within 20 yalms + canSD && //if Stardiver is ready + hasLOTD, //if Life of the Dragon is active + StardiverStrategy.Force => canSD, //Always use if forced + StardiverStrategy.ForceEX => canSD, //Always use if forced + StardiverStrategy.ForceWeave => canSD && canWeaveInStardiver, //Always use if inside weave window + StardiverStrategy.Delay => false, //Delay usage if strategy is set to delay + _ => false + }; + + //Determines when to use Mirage Dive + private bool ShouldUseMirageDive(OffensiveStrategy strategy, Actor? target) => strategy switch + { + OffensiveStrategy.Automatic => + Player.InCombat && //if in combat + target != null && //if has target + In20y(target) && //if within 20 yalms + canMD, //if Mirage Dive is ready + OffensiveStrategy.Force => canMD, //Always use if forced + OffensiveStrategy.ForceWeave => canMD && canWeaveIn, //Always use if inside weave window + OffensiveStrategy.Delay => false, //Delay usage if strategy is set to delay _ => false }; + #endregion + #region Spears //Determines when to use Geirskogul private bool ShouldUseGeirskogul(GeirskogulStrategy strategy, Actor? target) => strategy switch { GeirskogulStrategy.Automatic => - //Use Geirskogul automatically if the player is in combat, the action is ready, the target is within 15y, and Lance Charge is active - Player.InCombat && In15y(target) && canGeirskogul && hasLC, + Player.InCombat && //if in combat + In15y(target) && //if within 15 yalms + canGeirskogul && //if Geirskogul is ready + hasLC, //if Lance Charge is active GeirskogulStrategy.Force => canGeirskogul, //Always use if forced GeirskogulStrategy.ForceEX => canGeirskogul, //Always use if forced GeirskogulStrategy.ForceWeave => canGeirskogul && canWeaveIn, //Always use if inside weave window @@ -1078,61 +1134,43 @@ private void QueueOGCD(AID aid, Actor? target, OGCDPriority prio, float basePrio _ => false }; - //Determines when to use Mirage Dive - private bool ShouldUseMirageDive(OffensiveStrategy strategy, Actor? target) => strategy switch - { - OffensiveStrategy.Automatic => - //Use Mirage Dive automatically if the player is in combat, the target is valid, and Dive Ready effect is active - Player.InCombat && target != null && canMD, - OffensiveStrategy.Force => canMD, //Always use if forced - OffensiveStrategy.ForceWeave => canMD && canWeaveIn, //Always use if inside weave window - OffensiveStrategy.Delay => false, //Delay usage if strategy is set to delay - _ => false - }; - //Determines when to use Nastrond private bool ShouldUseNastrond(OffensiveStrategy strategy, Actor? target) => strategy switch { OffensiveStrategy.Automatic => - //Use Nastrond automatically if the player is in combat, has Nastrond ready, the target is within 15y, and Lance Charge is active - Player.InCombat && In15y(target) && canNastrond, + Player.InCombat && //if in combat + In15y(target) && //if within 15 yalms + canNastrond, //if Nastrond is ready OffensiveStrategy.Force => canNastrond, //Always use if forced OffensiveStrategy.ForceWeave => canNastrond && canWeaveIn, //Always use if inside weave window OffensiveStrategy.Delay => false, //Delay usage if strategy is set to delay _ => false }; - //Determines when to use Stardiver - private bool ShouldUseStardiver(StardiverStrategy strategy, Actor? target) => strategy switch - { - StardiverStrategy.Automatic => - //Use Stardiver automatically if the player is in combat, the target is valid, the action is ready, and Life of the Dragon (LOTD) is active - Player.InCombat && target != null && In3y(target) && canSD && hasLOTD, - StardiverStrategy.Force => canSD, //Always use if forced - StardiverStrategy.ForceEX => canSD, //Always use if forced - StardiverStrategy.ForceWeave => canSD && canWeaveInStardiver, //Always use if inside weave window - StardiverStrategy.Delay => false, //Delay usage if strategy is set to delay - _ => false - }; - //Determines when to use Wyrmwind Thrust private bool ShouldUseWyrmwindThrust(OffensiveStrategy strategy, Actor? target) => strategy switch { OffensiveStrategy.Automatic => - //Use Wyrmwind Thrust automatically if the player is in combat, the target is within 15y, and focus count is exactly 2 - Player.InCombat && target != null && In15y(target) && canWT && focusCount is 2 && lcCD > GCDLength * 3, + Player.InCombat && //if in combat + target != null && //if has target + In15y(target) && //if within 15 yalms + canWT && //if Wyrmwind Thrust is ready + lcCD > GCDLength * 2, //if Lance Charge is imminent, hold until buff is active OffensiveStrategy.Force => canWT, //Always use if forced OffensiveStrategy.ForceWeave => canWT && canWeaveIn, //Always use if inside weave window OffensiveStrategy.Delay => false, //Delay usage if strategy is set to delay _ => false }; + #endregion //Determines when to use Rise of the Dragon private bool ShouldUseRiseOfTheDragon(OffensiveStrategy strategy, Actor? target) => strategy switch { OffensiveStrategy.Automatic => - //Use Rise of the Dragon automatically if the player is in combat, the target is valid, and Dragon's Flight effect is active - Player.InCombat && target != null && canROTD, + Player.InCombat && //if in combat + target != null && //if has target + In20y(target) && //if within 20 yalms + canROTD, //if Rise of the Dragon is ready OffensiveStrategy.Force => canROTD, //Always use if forced OffensiveStrategy.ForceWeave => canROTD && canWeaveIn, //Always use if inside weave window OffensiveStrategy.Delay => false, //Delay usage if strategy is set to delay @@ -1144,7 +1182,10 @@ private void QueueOGCD(AID aid, Actor? target, OGCDPriority prio, float basePrio { OffensiveStrategy.Automatic => //Use Starcross automatically if the player is in combat, the target is valid, and Starcross Ready effect is active - Player.InCombat && target != null && canSC, + Player.InCombat && //if in combat + target != null && //if has target + In20y(target) && //if within 20 yalms + canSC, //if Starcross is ready OffensiveStrategy.Force => canSC, //Always use if forced OffensiveStrategy.ForceWeave => canSC && canWeaveIn, //Always use if inside weave window OffensiveStrategy.Delay => false, //Delay usage if strategy is set to delay @@ -1155,11 +1196,16 @@ private void QueueOGCD(AID aid, Actor? target, OGCDPriority prio, float basePrio private bool ShouldUsePiercingTalon(Actor? target, PiercingTalonStrategy strategy) => strategy switch { PiercingTalonStrategy.AllowEX => - Player.InCombat && target != null && !In3y(target) && HasEffect(SID.EnhancedPiercingTalon), + Player.InCombat && //if in combat + target != null && //if has target + !In3y(target) && //if not in melee range + HasEffect(SID.EnhancedPiercingTalon), //if Enhanced Piercing Talon is active PiercingTalonStrategy.Allow => - Player.InCombat && target != null && !In3y(target), + Player.InCombat && //if in combat + target != null && //if has target + !In3y(target), //if not in melee range PiercingTalonStrategy.Force => true, //Always use if forced - PiercingTalonStrategy.ForceEX => HasEffect(SID.EnhancedPiercingTalon), + PiercingTalonStrategy.ForceEX => HasEffect(SID.EnhancedPiercingTalon), //Use if Enhanced Piercing Talon is active PiercingTalonStrategy.Forbid => false, //Never use if forbidden _ => false }; @@ -1168,8 +1214,8 @@ private void QueueOGCD(AID aid, Actor? target, OGCDPriority prio, float basePrio private bool ShouldUsePotion(PotionStrategy strategy) => strategy switch { PotionStrategy.AlignWithRaidBuffs => - //Use potion when Lance Charge and Battle Litany cooldowns align with raid buffs (GCD timing) - lcCD <= GCD * 3 && blCD <= GCD * 3, + lcCD <= GCD * 2 && //Lance Charge is imminent + blCD <= GCD * 2, //Battle Litany is imminent PotionStrategy.Immediate => true, //Use the potion immediately _ => false }; @@ -1179,9 +1225,9 @@ private void QueueOGCD(AID aid, Actor? target, OGCDPriority prio, float basePrio private bool ShouldUseTrueNorth(TrueNorthStrategy strategy, Actor? target) => strategy switch { TrueNorthStrategy.Automatic => - target != null && //if has target - Player.InCombat && //if in combat - !HasEffect(SID.TrueNorth) && // + target != null && + Player.InCombat && + !HasEffect(SID.TrueNorth) && GCD < 1.25f && (!IsOnRear(target) && //Side ComboLastMove is AID.Disembowel or AID.SpiralBlow From 500a79ce7d515c34b4b97afab73a9fdee6b9dbe9 Mon Sep 17 00:00:00 2001 From: Akechi-kun <167795370+Akechi-kun@users.noreply.github.com> Date: Thu, 19 Dec 2024 00:13:00 -0800 Subject: [PATCH 12/29] Blasting Zone & Continuation post-refactor fixes --- BossMod/Autorotation/akechi/AkechiGNB.cs | 66 +++++++++++++----------- 1 file changed, 35 insertions(+), 31 deletions(-) diff --git a/BossMod/Autorotation/akechi/AkechiGNB.cs b/BossMod/Autorotation/akechi/AkechiGNB.cs index ca43d99024..7ae074b3dd 100644 --- a/BossMod/Autorotation/akechi/AkechiGNB.cs +++ b/BossMod/Autorotation/akechi/AkechiGNB.cs @@ -315,10 +315,10 @@ public static RotationModuleDefinition Definition() public enum OGCDPriority //priorities for oGCDs (higher number = higher priority) { None = 0, //default + Continuation = 500, //Continuation procs Zone = 550, //Blasting Zone BowShock = 600, //Bow Shock Bloodfest = 700, //Bloodfest - Continuation = 800, //Continuation procs NoMercy = 875, //No Mercy Potion = 900, //Potion ForcedOGCD = 900, //Forced oGCDs @@ -401,14 +401,15 @@ private AID BestCartSpender //Determine the best cartridge spender to use : canBS //Otherwise, if Burst Strike is available ? AID.BurstStrike //Use Burst Strike : NextBestRotation(); //Otherwise, use the next best rotation + private AID BestContinuation //Determine the best Continuation to use - => canContinue //Continuation is available - && hasRip ? AID.JugularRip //Jugular Rip - : hasTear ? AID.AbdomenTear //Abdomen Tear - : hasGouge ? AID.EyeGouge //Eye Gouge - : hasRaze ? AID.FatedBrand //Fated Brand - : hasBlast ? AID.Hypervelocity //Hypervelocity - : AID.Continuation; //Otherwise, return Original Hook + => canContinue //And we can use Continuation + && hasBlast ? AID.Hypervelocity //If we have Ready To Blast buff + : hasRaze ? AID.FatedBrand //If we have Ready To Raze buff + : hasRip ? AID.JugularRip //If we have Ready To Rip buff + : hasTear ? AID.AbdomenTear //If we have Ready To Tear buff + : hasGouge ? AID.EyeGouge //If we have Ready To Gouge buff + : AID.Continuation; //Otherwise, dfault to original hook #endregion public override void Execute(StrategyValues strategy, Actor? primaryTarget, float estimatedAnimLockDelay, bool isMoving) //Executes our actions @@ -434,9 +435,9 @@ public override void Execute(StrategyValues strategy, Actor? primaryTarget, floa hasGouge = HasEffect(SID.ReadyToGouge); //Checks for Ready To Gouge buff //GCD & Weaving - canWeaveIn = GCD is < 2.5f and > 0.6f; //Can weave in oGCDs - canWeaveEarly = GCD is < 2.5f and > 1.5f; //Can weave in oGCDs early - canWeaveLate = GCD is <= 1.5f and > 0.6f; //Can weave in oGCDs late + 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 quarterWeave = GCD < 0.9f; //Can last second weave oGCDs GCDLength = ActionSpeed.GCDRounded(World.Client.PlayerStats.SkillSpeed, World.Client.PlayerStats.Haste, Player.Level); //GCD based on skill speed and haste NextGCD = AID.None; //Next global cooldown action to be used @@ -460,7 +461,7 @@ public override void Execute(StrategyValues strategy, Actor? primaryTarget, floa canDD = Unlocked(AID.DoubleDown) && ActionReady(AID.DoubleDown) && Ammo > 0; //Double Down conditions; -1 Ammo AOE canBF = Unlocked(AID.Bloodfest) && ActionReady(AID.Bloodfest); //Bloodfest conditions; +all Ammo (must have target) //Cooldown-relative - canZone = Unlocked(AID.DangerZone) && ActionReady(BestZone); //Zone conditions + canZone = Unlocked(AID.DangerZone) && ActionReady(AID.DangerZone); //Zone conditions canBreak = hasBreak && Unlocked(AID.SonicBreak); //Sonic Break conditions canBow = Unlocked(AID.BowShock) && ActionReady(AID.BowShock); //Bow Shock conditions canContinue = Unlocked(AID.Continuation); //Continuation conditions @@ -640,17 +641,18 @@ or BloodfestStrategy.Force0W //or Force weave with 0 cartridges } //Continuation execution - if (canContinue) //if Continuation is available - { - if (hasRip || //if Jugular Rip is ready - hasTear || //or Abdomen Tear is ready - hasGouge || //or Eye Gouge is ready - hasRaze || //or Fated Brand is ready - hasBlast) //or Hypervelocity is ready - QueueOGCD(BestContinuation, //queue the best Continuation action - primaryTarget, //on the primary target - OGCDPriority.Continuation); //with priority for needed Continuation actions - } + if (canContinue && //if Continuation is available + hasBlast || //and Ready To Blast buff is active + hasRaze || //or Ready To Raze buff is active + hasRip || //or Ready To Rip buff is active + hasTear || //or Ready To Tear buff is active + hasGouge) //or Ready To Gouge buff is active + QueueOGCD(BestContinuation, //queue the best Continuation action + primaryTarget, //on the primary target + canWeaveLate //if inside second weave slot & still havent used + ? OGCDPriority.ForcedOGCD //use priority for forced oGCDs + : OGCDPriority.Continuation); //otherwise, use intended priority + #endregion #region GCDs @@ -859,12 +861,12 @@ private void QueueOGCD(AID aid, Actor? target, OGCDPriority prio, float basePrio NoMercyStrategy.Force => canNM, //Force No Mercy, regardless of correct weaving NoMercyStrategy.ForceW => canNM && canWeaveIn, //Force No Mercy into any weave slot NoMercyStrategy.ForceQW => canNM && quarterWeave, //Force No Mercy into last possible second weave slot - NoMercyStrategy.Force1 => canNM && Ammo >= 1, //Force No Mercy if ready and 1 cartridge, regardless of weaving - NoMercyStrategy.Force1W => canNM && canWeaveIn && Ammo >= 1, //Force No Mercy into any weave slot if ready and 1 cartridge - NoMercyStrategy.Force1QW => canNM && quarterWeave && Ammo >= 1, //Force No Mercy into last possible second weave slot if ready and 1 cartridge - NoMercyStrategy.Force2 => canNM && Ammo >= 2, //Force No Mercy if ready and 2 cartridges, regardless of weaving - NoMercyStrategy.Force2W => canNM && canWeaveIn && Ammo >= 2, //Force No Mercy into any weave slot if ready and 2 cartridges - NoMercyStrategy.Force2QW => canNM && quarterWeave && Ammo >= 2, //Force No Mercy into last possible second weave slot if ready and 2 cartridges + NoMercyStrategy.Force1 => canNM && Ammo == 1, //Force No Mercy if ready and 1 cartridge, regardless of weaving + NoMercyStrategy.Force1W => canNM && canWeaveIn && Ammo == 1, //Force No Mercy into any weave slot if ready and 1 cartridge + NoMercyStrategy.Force1QW => canNM && quarterWeave && Ammo == 1, //Force No Mercy into last possible second weave slot if ready and 1 cartridge + NoMercyStrategy.Force2 => canNM && Ammo == 2, //Force No Mercy if ready and 2 cartridges, regardless of weaving + NoMercyStrategy.Force2W => canNM && canWeaveIn && Ammo == 2, //Force No Mercy into any weave slot if ready and 2 cartridges + NoMercyStrategy.Force2QW => canNM && quarterWeave && Ammo == 2, //Force No Mercy into last possible second weave slot if ready and 2 cartridges NoMercyStrategy.Force3 => canNM && Ammo == 3, //Force No Mercy if ready and 3 cartridges, regardless of weaving NoMercyStrategy.Force3W => canNM && canWeaveIn && Ammo == 3, //Force No Mercy into any weave slot if ready and 3 cartridges NoMercyStrategy.Force3QW => canNM && quarterWeave && Ammo == 3, //Force No Mercy into last possible second weave slot if ready and 3 cartridges @@ -973,7 +975,8 @@ private void QueueOGCD(AID aid, Actor? target, OGCDPriority prio, float basePrio target != null && //Target exists In3y(target) && //Target in melee range canBS && //Burst Strike is available - (hasNM || nmCD < 1), //No Mercy is active or almost ready + (hasNM || //No Mercy is active + nmCD < 1 && Ammo == 3), //No Mercy is almost ready and full carts _ => false }; @@ -985,7 +988,8 @@ private void QueueOGCD(AID aid, Actor? target, OGCDPriority prio, float basePrio target != null && //Target exists In5y(target) && //Target in range canFC && //Fated Circle is available - (hasNM || nmCD < 1), //No Mercy is active or almost ready + (hasNM || //No Mercy is active + nmCD < 1 && Ammo == 3), //No Mercy is almost ready and full carts _ => false }; From be99c5f85e1d8b216ec7c974cfced6f873df6629 Mon Sep 17 00:00:00 2001 From: Akechi-kun <167795370+Akechi-kun@users.noreply.github.com> Date: Sat, 21 Dec 2024 00:14:42 -0800 Subject: [PATCH 13/29] Utility: DRK Oblation & TBN status fixes --- BossMod/ActionQueue/Melee/SAM.cs | 1 + BossMod/ActionQueue/Tanks/DRK.cs | 15 ++++---- .../Autorotation/Utility/ClassDRKUtility.cs | 36 +++++++++---------- 3 files changed, 26 insertions(+), 26 deletions(-) diff --git a/BossMod/ActionQueue/Melee/SAM.cs b/BossMod/ActionQueue/Melee/SAM.cs index 294ee7f70b..3f641f0c76 100644 --- a/BossMod/ActionQueue/Melee/SAM.cs +++ b/BossMod/ActionQueue/Melee/SAM.cs @@ -96,6 +96,7 @@ public enum SID : uint Tengentsu = 3853, // applied by Tengentsu to self ZanshinReady = 3855, // applied by Ikishoten to self Tendo = 3856, // applied by Meikyo Shisui to self + TsubameReady = 4216, // applied by Midare Setsugekka to self //Shared Feint = ClassShared.SID.Feint, // applied by Feint to target diff --git a/BossMod/ActionQueue/Tanks/DRK.cs b/BossMod/ActionQueue/Tanks/DRK.cs index 2c58ec00d5..8d33b8fe3a 100644 --- a/BossMod/ActionQueue/Tanks/DRK.cs +++ b/BossMod/ActionQueue/Tanks/DRK.cs @@ -78,13 +78,14 @@ public enum TraitID : uint public enum SID : uint { None = 0, - Oblation = 2682, - BloodWeapon = 742, - Grit = 743, - SaltedEarth = 749, - Delirium = 1972, - EnhancedDelirium = 3836, - Scorn = 3837, + BloodWeapon = 742, // applied by BloodWeapon to self + Grit = 743, // applied by Grit to self + SaltedEarth = 749, // applied by Salted Earth + TheBlackestNight = 1308, // applied by The Blackest Night to target + Delirium = 1972, // applied by Delirium to self + Oblation = 2682, // applied by Oblation to target + EnhancedDelirium = 3836, // applied by Delirium to self (Lv96+) + Scorn = 3837, // applied by Living Shadow to self //Shared Reprisal = ClassShared.SID.Reprisal, // applied by Reprisal to target diff --git a/BossMod/Autorotation/Utility/ClassDRKUtility.cs b/BossMod/Autorotation/Utility/ClassDRKUtility.cs index 4915277926..e925779ff1 100644 --- a/BossMod/Autorotation/Utility/ClassDRKUtility.cs +++ b/BossMod/Autorotation/Utility/ClassDRKUtility.cs @@ -3,12 +3,13 @@ public sealed class ClassDRKUtility(RotationModuleManager manager, Actor player) : RoleTankUtility(manager, player) { public enum Track { DarkMind = SharedTrack.Count, ShadowWall, LivingDead, TheBlackestNight, Oblation, DarkMissionary, Shadowstride } - public enum WallOption { None, ShadowWall, ShadowedVigil } - public enum TBNStrategy { None, Force } - public enum OblationStrategy { None, Force } + public enum WallOption { None, ShadowWall, ShadowedVigil } //ShadowWall strategy + public enum TBNStrategy { None, Force } //TheBlackestNight strategy + public enum OblationStrategy { None, Force } //Oblation strategy public enum DashStrategy { None, GapClose } //GapCloser strategy public bool InMeleeRange(Actor? target) => Player.DistanceToHitbox(target) <= 3; //Checks if we're inside melee range - public bool TargetHasEffect(Actor target, SID sid) where SID : Enum => target?.FindStatus((uint)(object)sid, Player.InstanceID) != null; //Checks if Status effect is on target + public float GetStatusDetail(Actor target, DRK.SID sid) => StatusDetails(target, sid, Player.InstanceID).Left; //Checks if Status effect is on target + public bool HasEffect(Actor target, DRK.SID sid, float duration) => GetStatusDetail(target, sid) < duration; //Checks if anyone has a status effect public static readonly ActionID IDLimitBreak3 = ActionID.MakeSpell(DRK.AID.DarkForce); public static readonly ActionID IDStanceApply = ActionID.MakeSpell(DRK.AID.Grit); @@ -57,28 +58,24 @@ public override void Execute(StrategyValues strategy, Actor? primaryTarget, floa ExecuteSimple(strategy.Option(Track.DarkMissionary), DRK.AID.DarkMissionary, Player); //Execution of DarkMissionary //TBN execution + var canTBN = ActionUnlocked(ActionID.MakeSpell(DRK.AID.TheBlackestNight)) && Player.HPMP.CurMP >= 3000; var tbn = strategy.Option(Track.TheBlackestNight); var tbnTarget = ResolveTargetOverride(tbn.Value) ?? CoTank() ?? primaryTarget ?? Player; //Smart-Targets Co-Tank if set to Automatic, if no Co-Tank then targets self - var tbnight = tbn.As() switch - { - TBNStrategy.Force => DRK.AID.TheBlackestNight, - _ => default - }; - if (tbnight != default && Player.HPMP.CurMP >= 3000) - Hints.ActionsToExecute.Push(ActionID.MakeSpell(DRK.AID.TheBlackestNight), tbnTarget, tbn.Priority(), tbn.Value.ExpireIn); + if (tbn.As() == TBNStrategy.Force && + canTBN && + !HasEffect(tbnTarget, DRK.SID.TheBlackestNight, 7)) + Hints.ActionsToExecute.Push(ActionID.MakeSpell(DRK.AID.TheBlackestNight), tbnTarget, tbn.Priority()); //Oblation execution + var canObl = ActionUnlocked(ActionID.MakeSpell(DRK.AID.Oblation)); var obl = strategy.Option(Track.Oblation); var oblTarget = ResolveTargetOverride(obl.Value) ?? primaryTarget ?? Player; //Smart-Targets Co-Tank if set to Automatic, if no Co-Tank then targets self - var oblStatus = TargetHasEffect(oblTarget, DRK.SID.Oblation); //Checks if status is present - var oblat = obl.As() switch - { - OblationStrategy.Force => DRK.AID.Oblation, - _ => default - }; - if (oblat != default && !oblStatus) - Hints.ActionsToExecute.Push(ActionID.MakeSpell(DRK.AID.Oblation), oblTarget, obl.Priority(), obl.Value.ExpireIn); + if (obl.As() == OblationStrategy.Force && + canObl && + !HasEffect(oblTarget, DRK.SID.Oblation, 9)) + Hints.ActionsToExecute.Push(ActionID.MakeSpell(DRK.AID.Oblation), oblTarget, obl.Priority()); + //Shadow Wall / Vigil execution var wall = strategy.Option(Track.ShadowWall); var wallAction = wall.As() switch { @@ -89,6 +86,7 @@ public override void Execute(StrategyValues strategy, Actor? primaryTarget, floa if (wallAction != default) Hints.ActionsToExecute.Push(ActionID.MakeSpell(wallAction), Player, wall.Priority(), wall.Value.ExpireIn); //Checking proper use of said option + //Shadowstride execution var dashStrategy = strategy.Option(Track.Shadowstride).As(); if (ShouldUseDash(dashStrategy, primaryTarget)) Hints.ActionsToExecute.Push(ActionID.MakeSpell(DRK.AID.Shadowstride), primaryTarget, obl.Priority()); From 752c36e7ebfc16a528832a3e7159af39d272df05 Mon Sep 17 00:00:00 2001 From: Akechi-kun <167795370+Akechi-kun@users.noreply.github.com> Date: Sat, 21 Dec 2024 00:21:16 -0800 Subject: [PATCH 14/29] Utility: PLD Cover targeting fix --- BossMod/Autorotation/Utility/ClassPLDUtility.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/BossMod/Autorotation/Utility/ClassPLDUtility.cs b/BossMod/Autorotation/Utility/ClassPLDUtility.cs index cf3c81e9a0..69fb16e3ce 100644 --- a/BossMod/Autorotation/Utility/ClassPLDUtility.cs +++ b/BossMod/Autorotation/Utility/ClassPLDUtility.cs @@ -42,7 +42,7 @@ public static RotationModuleDefinition Definition() public override void Execute(StrategyValues strategy, Actor? primaryTarget, float estimatedAnimLockDelay, bool isMoving) { ExecuteShared(strategy, IDLimitBreak3, IDStanceApply, IDStanceRemove, (uint)PLD.SID.IronWill, primaryTarget); - ExecuteSimple(strategy.Option(Track.Cover), PLD.AID.Cover, primaryTarget); //Cover execution + ExecuteSimple(strategy.Option(Track.Cover), PLD.AID.Cover, ResolveTargetOverride(strategy.Option(Track.Cover).Value) ?? Player); //Cover execution ExecuteSimple(strategy.Option(Track.Bulwark), PLD.AID.Bulwark, Player); //Bulwark execution ExecuteSimple(strategy.Option(Track.DivineVeil), PLD.AID.DivineVeil, Player); //DivineVeil execution ExecuteSimple(strategy.Option(Track.PassageOfArms), PLD.AID.PassageOfArms, Player); //PassageOfArms execution From fd788cbe5fbd41e4e658f5e9bc30bc194fcfe8ad Mon Sep 17 00:00:00 2001 From: Akechi-kun <167795370+Akechi-kun@users.noreply.github.com> Date: Sat, 21 Dec 2024 00:25:50 -0800 Subject: [PATCH 15/29] Utility: GNB Aurora fix --- BossMod/Autorotation/Utility/ClassGNBUtility.cs | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/BossMod/Autorotation/Utility/ClassGNBUtility.cs b/BossMod/Autorotation/Utility/ClassGNBUtility.cs index 33d21d7f81..919b2731df 100644 --- a/BossMod/Autorotation/Utility/ClassGNBUtility.cs +++ b/BossMod/Autorotation/Utility/ClassGNBUtility.cs @@ -7,7 +7,8 @@ public enum Track { Camouflage = SharedTrack.Count, Nebula, Aurora, Superbolide, public enum AuroraStrategy { None, Force } //Aurora public enum DashStrategy { None, GapClose } //Gapcloser purposes public bool InMeleeRange(Actor? target) => Player.DistanceToHitbox(target) <= 3; //Checks if we're inside melee range - public bool TargetHasEffect(Actor target, SID sid) where SID : Enum => target?.FindStatus((uint)(object)sid, Player.InstanceID) != null; //Checks if Status effect is on target + public float GetStatusDetail(Actor target, GNB.SID sid) => StatusDetails(target, sid, Player.InstanceID).Left; //Checks if Status effect is on target + public bool HasEffect(Actor target, GNB.SID sid, float duration) => GetStatusDetail(target, sid) < duration; //Checks if anyone has a status effect public static readonly ActionID IDLimitBreak3 = ActionID.MakeSpell(GNB.AID.GunmetalSoul); public static readonly ActionID IDStanceApply = ActionID.MakeSpell(GNB.AID.RoyalGuard); @@ -54,7 +55,7 @@ public override void Execute(StrategyValues strategy, Actor? primaryTarget, floa //Aurora execution var aur = strategy.Option(Track.Aurora); var aurTarget = ResolveTargetOverride(aur.Value) ?? primaryTarget ?? Player; //Smart-Targeting - var aurStatus = TargetHasEffect(aurTarget, GNB.SID.Aurora); //Checks if status is present + var aurStatus = HasEffect(aurTarget, GNB.SID.Aurora, 17); //Checks if status is present var aurora = aur.As() switch { AuroraStrategy.Force => GNB.AID.Aurora, @@ -63,18 +64,19 @@ public override void Execute(StrategyValues strategy, Actor? primaryTarget, floa if (aurora != default && !aurStatus) Hints.ActionsToExecute.Push(ActionID.MakeSpell(GNB.AID.Aurora), aurTarget, aur.Priority(), aur.Value.ExpireIn); - //Heart of Corundum execution + //Heart of Stone / Corundum execution var hoc = strategy.Option(Track.HeartOfCorundum); var hocTarget = ResolveTargetOverride(hoc.Value) ?? CoTank() ?? primaryTarget ?? Player; //Smart-Targets Co-Tank if set to Automatic, if no Co-Tank then targets self - var aid = hoc.As() switch + var hocStrat = hoc.As() switch { HoCOption.HeartOfStone => GNB.AID.HeartOfStone, HoCOption.HeartOfCorundum => GNB.AID.HeartOfCorundum, _ => default }; - if (aid != default) - Hints.ActionsToExecute.Push(ActionID.MakeSpell(aid), hocTarget, hoc.Priority(), hoc.Value.ExpireIn); + if (hocStrat != default) + Hints.ActionsToExecute.Push(ActionID.MakeSpell(ActionUnlocked(ActionID.MakeSpell(GNB.AID.HeartOfCorundum)) ? GNB.AID.HeartOfCorundum : GNB.AID.HeartOfStone), hocTarget, hoc.Priority(), hoc.Value.ExpireIn); + //Trajectory execution var dashStrategy = strategy.Option(Track.Trajectory).As(); if (ShouldUseDash(dashStrategy, primaryTarget)) Hints.ActionsToExecute.Push(ActionID.MakeSpell(GNB.AID.Trajectory), primaryTarget, hoc.Priority()); From a0b4b628954bab323e36abb66819ebbddb9240d0 Mon Sep 17 00:00:00 2001 From: Akechi-kun <167795370+Akechi-kun@users.noreply.github.com> Date: Sat, 21 Dec 2024 01:47:38 -0800 Subject: [PATCH 16/29] fuck it we just fix all Utility targeting shit --- .../Autorotation/Utility/ClassASTUtility.cs | 16 ++++++----- .../Autorotation/Utility/ClassBLMUtility.cs | 2 +- .../Autorotation/Utility/ClassDRGUtility.cs | 3 +- .../Autorotation/Utility/ClassDRKUtility.cs | 8 ++++-- .../Autorotation/Utility/ClassGNBUtility.cs | 4 ++- .../Autorotation/Utility/ClassSCHUtility.cs | 18 +++++++----- .../Autorotation/Utility/ClassSGEUtility.cs | 28 +++++++++---------- .../Autorotation/Utility/ClassSMNUtility.cs | 8 ++++-- .../Autorotation/Utility/RoleCasterUtility.cs | 2 +- .../Autorotation/Utility/RoleHealerUtility.cs | 4 +-- 10 files changed, 54 insertions(+), 39 deletions(-) diff --git a/BossMod/Autorotation/Utility/ClassASTUtility.cs b/BossMod/Autorotation/Utility/ClassASTUtility.cs index b836eae147..9a1ba0f5c7 100644 --- a/BossMod/Autorotation/Utility/ClassASTUtility.cs +++ b/BossMod/Autorotation/Utility/ClassASTUtility.cs @@ -6,7 +6,9 @@ public enum Track { Helios = SharedTrack.Count, Lightspeed, BeneficII, Essential public enum HoroscopeOption { None, Use, End } public enum MacrocosmosOption { None, Use, End } public enum HeliosOption { None, Use, UseEx } - public bool HasEffect(SID sid) where SID : Enum => Player.FindStatus((uint)(object)sid, Player.InstanceID) != null; //Checks if Status effect is on self + public float GetStatusDetail(Actor target, AST.SID sid) => StatusDetails(target, sid, Player.InstanceID).Left; //Checks if Status effect is on target + public bool HasEffect(Actor target, AST.SID sid, float duration) => GetStatusDetail(target, sid) < duration; //Checks if anyone has a status effect + public Actor? TargetChoice(StrategyValues.OptionRef strategy) => ResolveTargetOverride(strategy.Value); public static readonly ActionID IDLimitBreak3 = ActionID.MakeSpell(AST.AID.AstralStasis); @@ -56,19 +58,19 @@ public override void Execute(StrategyValues strategy, Actor? primaryTarget, floa { ExecuteShared(strategy, IDLimitBreak3, primaryTarget); ExecuteSimple(strategy.Option(Track.Lightspeed), AST.AID.Lightspeed, Player); - ExecuteSimple(strategy.Option(Track.BeneficII), AST.AID.BeneficII, primaryTarget ?? Player); - ExecuteSimple(strategy.Option(Track.EssentialDignity), AST.AID.EssentialDignity, primaryTarget ?? Player); - ExecuteSimple(strategy.Option(Track.AspectedBenefic), AST.AID.AspectedBenefic, primaryTarget ?? Player); - ExecuteSimple(strategy.Option(Track.Synastry), AST.AID.Synastry, primaryTarget ?? Player); + ExecuteSimple(strategy.Option(Track.BeneficII), AST.AID.BeneficII, TargetChoice(strategy.Option(Track.BeneficII)) ?? Player); + ExecuteSimple(strategy.Option(Track.EssentialDignity), AST.AID.EssentialDignity, TargetChoice(strategy.Option(Track.EssentialDignity)) ?? Player); + ExecuteSimple(strategy.Option(Track.AspectedBenefic), AST.AID.AspectedBenefic, TargetChoice(strategy.Option(Track.AspectedBenefic)) ?? Player); + ExecuteSimple(strategy.Option(Track.Synastry), AST.AID.Synastry, TargetChoice(strategy.Option(Track.Synastry)) ?? Player); ExecuteSimple(strategy.Option(Track.CollectiveUnconscious), AST.AID.CollectiveUnconscious, Player); ExecuteSimple(strategy.Option(Track.CelestialOpposition), AST.AID.CelestialOpposition, Player); ExecuteSimple(strategy.Option(Track.CelestialIntersection), AST.AID.CelestialIntersection, Player); ExecuteSimple(strategy.Option(Track.NeutralSect), AST.AID.NeutralSect, Player); - ExecuteSimple(strategy.Option(Track.Exaltation), AST.AID.Exaltation, primaryTarget ?? Player); + ExecuteSimple(strategy.Option(Track.Exaltation), AST.AID.Exaltation, TargetChoice(strategy.Option(Track.Exaltation)) ?? Player); ExecuteSimple(strategy.Option(Track.SunSign), AST.AID.SunSign, Player); //Aspected Helios full execution - var heliosUp = HasEffect(AST.SID.AspectedHelios) || HasEffect(AST.SID.HeliosConjunction); + var heliosUp = HasEffect(Player, AST.SID.AspectedHelios, 15) || HasEffect(Player, AST.SID.HeliosConjunction, 15); var helios = strategy.Option(Track.AspectedHelios); var heliosAction = helios.As() switch { diff --git a/BossMod/Autorotation/Utility/ClassBLMUtility.cs b/BossMod/Autorotation/Utility/ClassBLMUtility.cs index b6aa8d3ecb..4413261d5e 100644 --- a/BossMod/Autorotation/Utility/ClassBLMUtility.cs +++ b/BossMod/Autorotation/Utility/ClassBLMUtility.cs @@ -31,7 +31,7 @@ public override void Execute(StrategyValues strategy, Actor? primaryTarget, floa var dashTarget = ResolveTargetOverride(dash.Value) ?? primaryTarget; //Smart-Targeting var dashStrategy = strategy.Option(Track.AetherialManipulation).As(); if (ShouldUseDash(dashStrategy, dashTarget)) - Hints.ActionsToExecute.Push(ActionID.MakeSpell(BLM.AID.AetherialManipulation), dashTarget, dash.Priority()); + Hints.ActionsToExecute.Push(ActionID.MakeSpell(BLM.AID.AetherialManipulation), dashTarget, dash.Priority(), dash.Value.ExpireIn); } private bool ShouldUseDash(DashStrategy strategy, Actor? primaryTarget) => strategy switch { diff --git a/BossMod/Autorotation/Utility/ClassDRGUtility.cs b/BossMod/Autorotation/Utility/ClassDRGUtility.cs index c87fc133a0..343e0c0472 100644 --- a/BossMod/Autorotation/Utility/ClassDRGUtility.cs +++ b/BossMod/Autorotation/Utility/ClassDRGUtility.cs @@ -26,9 +26,10 @@ public override void Execute(StrategyValues strategy, Actor? primaryTarget, floa ExecuteShared(strategy, IDLimitBreak3, primaryTarget); var dash = strategy.Option(Track.WingedGlide); + var dashTarget = ResolveTargetOverride(dash.Value) ?? primaryTarget; var dashStrategy = strategy.Option(Track.WingedGlide).As(); if (ShouldUseDash(dashStrategy, primaryTarget)) - Hints.ActionsToExecute.Push(ActionID.MakeSpell(DRG.AID.WingedGlide), primaryTarget, dash.Priority()); + Hints.ActionsToExecute.Push(ActionID.MakeSpell(DRG.AID.WingedGlide), dashTarget, dash.Priority(), dash.Value.ExpireIn); } private bool ShouldUseDash(DashStrategy strategy, Actor? primaryTarget) => strategy switch { diff --git a/BossMod/Autorotation/Utility/ClassDRKUtility.cs b/BossMod/Autorotation/Utility/ClassDRKUtility.cs index e925779ff1..e0ea183da3 100644 --- a/BossMod/Autorotation/Utility/ClassDRKUtility.cs +++ b/BossMod/Autorotation/Utility/ClassDRKUtility.cs @@ -64,7 +64,7 @@ public override void Execute(StrategyValues strategy, Actor? primaryTarget, floa if (tbn.As() == TBNStrategy.Force && canTBN && !HasEffect(tbnTarget, DRK.SID.TheBlackestNight, 7)) - Hints.ActionsToExecute.Push(ActionID.MakeSpell(DRK.AID.TheBlackestNight), tbnTarget, tbn.Priority()); + Hints.ActionsToExecute.Push(ActionID.MakeSpell(DRK.AID.TheBlackestNight), tbnTarget, tbn.Priority(), tbn.Value.ExpireIn); //Oblation execution var canObl = ActionUnlocked(ActionID.MakeSpell(DRK.AID.Oblation)); @@ -73,7 +73,7 @@ public override void Execute(StrategyValues strategy, Actor? primaryTarget, floa if (obl.As() == OblationStrategy.Force && canObl && !HasEffect(oblTarget, DRK.SID.Oblation, 9)) - Hints.ActionsToExecute.Push(ActionID.MakeSpell(DRK.AID.Oblation), oblTarget, obl.Priority()); + Hints.ActionsToExecute.Push(ActionID.MakeSpell(DRK.AID.Oblation), oblTarget, obl.Priority(), obl.Value.ExpireIn); //Shadow Wall / Vigil execution var wall = strategy.Option(Track.ShadowWall); @@ -87,9 +87,11 @@ public override void Execute(StrategyValues strategy, Actor? primaryTarget, floa Hints.ActionsToExecute.Push(ActionID.MakeSpell(wallAction), Player, wall.Priority(), wall.Value.ExpireIn); //Checking proper use of said option //Shadowstride execution + var dash = strategy.Option(Track.Shadowstride); + var dashTarget = ResolveTargetOverride(dash.Value) ?? primaryTarget; var dashStrategy = strategy.Option(Track.Shadowstride).As(); if (ShouldUseDash(dashStrategy, primaryTarget)) - Hints.ActionsToExecute.Push(ActionID.MakeSpell(DRK.AID.Shadowstride), primaryTarget, obl.Priority()); + Hints.ActionsToExecute.Push(ActionID.MakeSpell(DRK.AID.Shadowstride), dashTarget, dash.Priority(), dash.Value.ExpireIn); } private bool ShouldUseDash(DashStrategy strategy, Actor? primaryTarget) => strategy switch { diff --git a/BossMod/Autorotation/Utility/ClassGNBUtility.cs b/BossMod/Autorotation/Utility/ClassGNBUtility.cs index 919b2731df..1e381c02dd 100644 --- a/BossMod/Autorotation/Utility/ClassGNBUtility.cs +++ b/BossMod/Autorotation/Utility/ClassGNBUtility.cs @@ -77,9 +77,11 @@ public override void Execute(StrategyValues strategy, Actor? primaryTarget, floa Hints.ActionsToExecute.Push(ActionID.MakeSpell(ActionUnlocked(ActionID.MakeSpell(GNB.AID.HeartOfCorundum)) ? GNB.AID.HeartOfCorundum : GNB.AID.HeartOfStone), hocTarget, hoc.Priority(), hoc.Value.ExpireIn); //Trajectory execution + var dash = strategy.Option(Track.Trajectory); + var dashTarget = ResolveTargetOverride(dash.Value) ?? primaryTarget; var dashStrategy = strategy.Option(Track.Trajectory).As(); if (ShouldUseDash(dashStrategy, primaryTarget)) - Hints.ActionsToExecute.Push(ActionID.MakeSpell(GNB.AID.Trajectory), primaryTarget, hoc.Priority()); + Hints.ActionsToExecute.Push(ActionID.MakeSpell(GNB.AID.Trajectory), dashTarget, dash.Priority(), dash.Value.ExpireIn); } private bool ShouldUseDash(DashStrategy strategy, Actor? primaryTarget) => strategy switch { diff --git a/BossMod/Autorotation/Utility/ClassSCHUtility.cs b/BossMod/Autorotation/Utility/ClassSCHUtility.cs index f0064d51e8..a5c5731cc6 100644 --- a/BossMod/Autorotation/Utility/ClassSCHUtility.cs +++ b/BossMod/Autorotation/Utility/ClassSCHUtility.cs @@ -8,7 +8,11 @@ public enum DeployOption { None, Use, UseEx } public enum AetherpactOption { None, Use, End } public enum RecitationOption { None, Use, UseEx } public enum PetOption { None, Eos, Seraph } - public bool HasEffect(SID sid) where SID : Enum => Player.FindStatus((uint)(object)sid, Player.InstanceID) != null; //Checks if Status effect is on self + public float GetStatusDetail(Actor target, SGE.SID sid) => StatusDetails(target, sid, Player.InstanceID).Left; //Checks if Status effect is on target + public bool HasEffect(Actor target, SGE.SID sid, float duration) => GetStatusDetail(target, sid) < duration; //Checks if anyone has a status effect + public float GetStatusDetail(Actor target, SCH.SID sid) => StatusDetails(target, sid, Player.InstanceID).Left; //Checks if Status effect is on target + public bool HasEffect(Actor target, SCH.SID sid, float duration) => GetStatusDetail(target, sid) < duration; //Checks if anyone has a status effect + public Actor? TargetChoice(StrategyValues.OptionRef strategy) => ResolveTargetOverride(strategy.Value); public static readonly ActionID IDLimitBreak3 = ActionID.MakeSpell(SCH.AID.AngelFeathers); @@ -73,21 +77,21 @@ public override void Execute(StrategyValues strategy, Actor? primaryTarget, floa { ExecuteShared(strategy, IDLimitBreak3, primaryTarget); ExecuteSimple(strategy.Option(Track.WhisperingDawn), SCH.AID.WhisperingDawn, Player); - ExecuteSimple(strategy.Option(Track.Adloquium), SCH.AID.Adloquium, primaryTarget ?? Player); + ExecuteSimple(strategy.Option(Track.Adloquium), SCH.AID.Adloquium, TargetChoice(strategy.Option(Track.Adloquium)) ?? Player); ExecuteSimple(strategy.Option(Track.FeyIllumination), SCH.AID.FeyIllumination, Player); - ExecuteSimple(strategy.Option(Track.Lustrate), SCH.AID.Lustrate, primaryTarget ?? Player); + ExecuteSimple(strategy.Option(Track.Lustrate), SCH.AID.Lustrate, TargetChoice(strategy.Option(Track.Lustrate)) ?? Player); ExecuteSimple(strategy.Option(Track.SacredSoil), SCH.AID.SacredSoil, Player); ExecuteSimple(strategy.Option(Track.Indomitability), SCH.AID.Indomitability, Player); ExecuteSimple(strategy.Option(Track.EmergencyTactics), SCH.AID.EmergencyTactics, Player); ExecuteSimple(strategy.Option(Track.Dissipation), SCH.AID.Dissipation, Player); - ExecuteSimple(strategy.Option(Track.Excogitation), SCH.AID.Excogitation, primaryTarget ?? Player); + ExecuteSimple(strategy.Option(Track.Excogitation), SCH.AID.Excogitation, TargetChoice(strategy.Option(Track.Excogitation)) ?? Player); ExecuteSimple(strategy.Option(Track.FeyBlessing), SCH.AID.FeyBlessing, Player); ExecuteSimple(strategy.Option(Track.Consolation), SCH.AID.Consolation, Player); - ExecuteSimple(strategy.Option(Track.Protraction), SCH.AID.Protraction, primaryTarget ?? Player); + ExecuteSimple(strategy.Option(Track.Protraction), SCH.AID.Protraction, TargetChoice(strategy.Option(Track.Protraction)) ?? Player); ExecuteSimple(strategy.Option(Track.Expedient), SCH.AID.Expedient, Player); ExecuteSimple(strategy.Option(Track.Seraphism), SCH.AID.Seraphism, Player); - var alreadyUp = HasEffect(SGE.SID.EukrasianPrognosis) || HasEffect(SCH.SID.Galvanize); + var alreadyUp = HasEffect(Player, SGE.SID.EukrasianPrognosis, 30) || HasEffect(Player, SCH.SID.Galvanize, 30); var succ = strategy.Option(Track.Succor); var succAction = succ.As() switch { @@ -110,7 +114,7 @@ public override void Execute(StrategyValues strategy, Actor? primaryTarget, floa _ => default }; if (pactAction != default) - Hints.ActionsToExecute.Push(ActionID.MakeSpell(pactAction), primaryTarget ?? Player, pact.Priority(), pact.Value.ExpireIn); + Hints.ActionsToExecute.Push(ActionID.MakeSpell(pactAction), TargetChoice(pact) ?? Player, pact.Priority(), pact.Value.ExpireIn); var recit = strategy.Option(Track.Recitation); if (recit.As() != RecitationOption.None) diff --git a/BossMod/Autorotation/Utility/ClassSGEUtility.cs b/BossMod/Autorotation/Utility/ClassSGEUtility.cs index a2c7ec3408..1b626a48c2 100644 --- a/BossMod/Autorotation/Utility/ClassSGEUtility.cs +++ b/BossMod/Autorotation/Utility/ClassSGEUtility.cs @@ -10,7 +10,11 @@ public enum PhysisOption { None, Use, UseEx } public enum ZoeOption { None, Use, UseEx } public enum DashStrategy { None, Force, GapClose } //GapCloser strategy public bool InMeleeRange(Actor? target) => Player.DistanceToHitbox(target) <= 3; //Checks if we're inside melee range - public bool HasEffect(SID sid) where SID : Enum => Player.FindStatus((uint)(object)sid, Player.InstanceID) != null; //Checks if Status effect is on self + public float GetStatusDetail(Actor target, SGE.SID sid) => StatusDetails(target, sid, Player.InstanceID).Left; //Checks if Status effect is on target + public bool HasEffect(Actor target, SGE.SID sid, float duration) => GetStatusDetail(target, sid) < duration; //Checks if anyone has a status effect + public float GetStatusDetail(Actor target, SCH.SID sid) => StatusDetails(target, sid, Player.InstanceID).Left; //Checks if Status effect is on target + public bool HasEffect(Actor target, SCH.SID sid, float duration) => GetStatusDetail(target, sid) < duration; //Checks if anyone has a status effect + public Actor? TargetChoice(StrategyValues.OptionRef strategy) => ResolveTargetOverride(strategy.Value); public static readonly ActionID IDLimitBreak3 = ActionID.MakeSpell(SGE.AID.TechneMakre); @@ -78,12 +82,12 @@ public override void Execute(StrategyValues strategy, Actor? primaryTarget, floa { ExecuteShared(strategy, IDLimitBreak3, primaryTarget); ExecuteSimple(strategy.Option(Track.Eukrasia), SGE.AID.Eukrasia, Player); - ExecuteSimple(strategy.Option(Track.Druochole), SGE.AID.Druochole, primaryTarget ?? Player); + ExecuteSimple(strategy.Option(Track.Druochole), SGE.AID.Druochole, TargetChoice(strategy.Option(Track.Druochole)) ?? Player); ExecuteSimple(strategy.Option(Track.Kerachole), SGE.AID.Kerachole, Player); ExecuteSimple(strategy.Option(Track.Ixochole), SGE.AID.Ixochole, Player); ExecuteSimple(strategy.Option(Track.Pepsis), SGE.AID.Pepsis, Player); - ExecuteSimple(strategy.Option(Track.Taurochole), SGE.AID.Taurochole, primaryTarget ?? Player); - ExecuteSimple(strategy.Option(Track.Haima), SGE.AID.Haima, primaryTarget ?? Player); + ExecuteSimple(strategy.Option(Track.Taurochole), SGE.AID.Taurochole, TargetChoice(strategy.Option(Track.Taurochole)) ?? Player); + ExecuteSimple(strategy.Option(Track.Haima), SGE.AID.Haima, TargetChoice(strategy.Option(Track.Haima)) ?? Player); ExecuteSimple(strategy.Option(Track.Rhizomata), SGE.AID.Rhizomata, Player); ExecuteSimple(strategy.Option(Track.Holos), SGE.AID.Holos, Player); ExecuteSimple(strategy.Option(Track.Panhaima), SGE.AID.Panhaima, Player); @@ -99,10 +103,10 @@ public override void Execute(StrategyValues strategy, Actor? primaryTarget, floa _ => default }; if (kardiaAction != default) - Hints.ActionsToExecute.Push(ActionID.MakeSpell(kardiaAction), primaryTarget, kardia.Priority(), kardia.Value.ExpireIn); + Hints.ActionsToExecute.Push(ActionID.MakeSpell(kardiaAction), TargetChoice(kardia) ?? Player, kardia.Priority(), kardia.Value.ExpireIn); //Diagnosis full execution - var hasEukrasia = HasEffect(SGE.SID.Eukrasia); //Eukrasia + var hasEukrasia = HasEffect(Player, SGE.SID.Eukrasia, 1000); //Eukrasia var diag = strategy.Option(Track.Diagnosis); var diagAction = diag.As() switch { @@ -115,22 +119,20 @@ public override void Execute(StrategyValues strategy, Actor? primaryTarget, floa { if (!hasEukrasia) { - // Push the primary action based on the selected option Hints.ActionsToExecute.Push(ActionID.MakeSpell(diagAction), primaryTarget, diag.Priority(), diag.Value.ExpireIn); } - // Check for EukrasianDiagnosis if the effect is active if (hasEukrasia) { if (diag.As() == DiagOption.UseED) { - Hints.ActionsToExecute.Push(ActionID.MakeSpell(SGE.AID.EukrasianDiagnosis), primaryTarget, diag.Priority(), diag.Value.ExpireIn); + Hints.ActionsToExecute.Push(ActionID.MakeSpell(SGE.AID.EukrasianDiagnosis), TargetChoice(diag) ?? Player, diag.Priority(), diag.Value.ExpireIn); } } } //Prognosis full execution - var alreadyUp = HasEffect(SGE.SID.EukrasianPrognosis) || HasEffect(SCH.SID.Galvanize); + var alreadyUp = HasEffect(Player, SGE.SID.EukrasianPrognosis, 30) || HasEffect(Player, SCH.SID.Galvanize, 30); var prog = strategy.Option(Track.Prognosis); var progAction = prog.As() switch { @@ -147,13 +149,11 @@ public override void Execute(StrategyValues strategy, Actor? primaryTarget, floa if (hasEukrasia && !alreadyUp) { - // Check if UseEP is selected and push EukrasianPrognosis if (prog.As() == ProgOption.UseEP) { Hints.ActionsToExecute.Push(ActionID.MakeSpell(SGE.AID.EukrasianPrognosis), Player, prog.Priority(), prog.Value.ExpireIn); } - // Check if UseEPEx is selected and push EukrasianPrognosisII - else if (prog.As() == ProgOption.UseEPEx) + if (prog.As() == ProgOption.UseEPEx) { Hints.ActionsToExecute.Push(ActionID.MakeSpell(SGE.AID.EukrasianPrognosisII), Player, prog.Priority(), prog.Value.ExpireIn); } @@ -175,7 +175,7 @@ public override void Execute(StrategyValues strategy, Actor? primaryTarget, floa var dashTarget = ResolveTargetOverride(dash.Value) ?? primaryTarget; //Smart-Targeting var dashStrategy = strategy.Option(Track.Icarus).As(); if (ShouldUseDash(dashStrategy, dashTarget)) - Hints.ActionsToExecute.Push(ActionID.MakeSpell(SGE.AID.Icarus), dashTarget, dash.Priority()); + Hints.ActionsToExecute.Push(ActionID.MakeSpell(SGE.AID.Icarus), dashTarget, dash.Priority(), dash.Value.ExpireIn); } private bool ShouldUseDash(DashStrategy strategy, Actor? primaryTarget) => strategy switch { diff --git a/BossMod/Autorotation/Utility/ClassSMNUtility.cs b/BossMod/Autorotation/Utility/ClassSMNUtility.cs index 8c429284f6..79ec52c675 100644 --- a/BossMod/Autorotation/Utility/ClassSMNUtility.cs +++ b/BossMod/Autorotation/Utility/ClassSMNUtility.cs @@ -4,6 +4,9 @@ public sealed class ClassSMNUtility(RotationModuleManager manager, Actor player) { public enum Track { RadiantAegis = SharedTrack.Count } public enum AegisStrategy { None, Use } + public float GetStatusDetail(Actor target, SMN.SID sid) => StatusDetails(target, sid, Player.InstanceID).Left; //Checks if Status effect is on target + public bool HasEffect(Actor target, SMN.SID sid, float duration) => GetStatusDetail(target, sid) < duration; //Checks if anyone has a status effect + public static readonly ActionID IDLimitBreak3 = ActionID.MakeSpell(SMN.AID.Teraflare); @@ -24,8 +27,9 @@ public override void Execute(StrategyValues strategy, Actor? primaryTarget, floa ExecuteShared(strategy, IDLimitBreak3, primaryTarget); var radi = strategy.Option(Track.RadiantAegis); - var hasAegis = SelfStatusLeft(SMN.SID.RadiantAegis, 30) > 0; + var radiTarget = ResolveTargetOverride(radi.Value) ?? Player; + var hasAegis = HasEffect(radiTarget, SMN.SID.RadiantAegis, 30); if (radi.As() != AegisStrategy.None && !hasAegis) - Hints.ActionsToExecute.Push(ActionID.MakeSpell(SMN.AID.RadiantAegis), primaryTarget ?? Player, radi.Priority(), radi.Value.ExpireIn); + Hints.ActionsToExecute.Push(ActionID.MakeSpell(SMN.AID.RadiantAegis), radiTarget, radi.Priority(), radi.Value.ExpireIn); } } diff --git a/BossMod/Autorotation/Utility/RoleCasterUtility.cs b/BossMod/Autorotation/Utility/RoleCasterUtility.cs index 0758cf9509..a7a9c6f3cf 100644 --- a/BossMod/Autorotation/Utility/RoleCasterUtility.cs +++ b/BossMod/Autorotation/Utility/RoleCasterUtility.cs @@ -46,7 +46,7 @@ protected void ExecuteShared(StrategyValues strategy, ActionID lb3, Actor? prima var addle = strategy.Option(SharedTrack.Addle); if (addle.As() != AddleOption.None) - Hints.ActionsToExecute.Push(ActionID.MakeSpell(ClassShared.AID.Addle), primaryTarget, addle.Priority(), addle.Value.ExpireIn); + Hints.ActionsToExecute.Push(ActionID.MakeSpell(ClassShared.AID.Addle), ResolveTargetOverride(strategy.Option(SharedTrack.Addle).Value) ?? primaryTarget, addle.Priority(), addle.Value.ExpireIn); var swift = strategy.Option(SharedTrack.Swiftcast); if (swift.As() != SwiftcastOption.None) diff --git a/BossMod/Autorotation/Utility/RoleHealerUtility.cs b/BossMod/Autorotation/Utility/RoleHealerUtility.cs index e6431f1c41..1f0a9ad0e3 100644 --- a/BossMod/Autorotation/Utility/RoleHealerUtility.cs +++ b/BossMod/Autorotation/Utility/RoleHealerUtility.cs @@ -31,10 +31,10 @@ protected void ExecuteShared(StrategyValues strategy, ActionID lb3, Actor? prima { ExecuteSimple(strategy.Option(SharedTrack.Sprint), ClassShared.AID.Sprint, Player); ExecuteSimple(strategy.Option(SharedTrack.Repose), ClassShared.AID.Repose, primaryTarget); - ExecuteSimple(strategy.Option(SharedTrack.Esuna), ClassShared.AID.Esuna, primaryTarget); + ExecuteSimple(strategy.Option(SharedTrack.Esuna), ClassShared.AID.Esuna, ResolveTargetOverride(strategy.Option(SharedTrack.Esuna).Value) ?? primaryTarget); ExecuteSimple(strategy.Option(SharedTrack.LucidDreaming), ClassShared.AID.LucidDreaming, Player); ExecuteSimple(strategy.Option(SharedTrack.Surecast), ClassShared.AID.Surecast, Player); - ExecuteSimple(strategy.Option(SharedTrack.Rescue), ClassShared.AID.Rescue, primaryTarget); + ExecuteSimple(strategy.Option(SharedTrack.Rescue), ClassShared.AID.Rescue, ResolveTargetOverride(strategy.Option(SharedTrack.Rescue).Value) ?? primaryTarget); var lb = strategy.Option(SharedTrack.LB); var lbLevel = LBLevelToExecute(lb.As()); From 30bcee6cee1f45924661f02c148e9ad4130f00fd Mon Sep 17 00:00:00 2001 From: Akechi-kun <167795370+Akechi-kun@users.noreply.github.com> Date: Sat, 21 Dec 2024 04:09:43 -0800 Subject: [PATCH 17/29] last little tweaks i can think of for now, looking super spiffy --- BossMod/Autorotation/akechi/AkechiDRG.cs | 96 +++++++++++++++--------- BossMod/Autorotation/akechi/AkechiGNB.cs | 91 +++++++++++++--------- 2 files changed, 118 insertions(+), 69 deletions(-) diff --git a/BossMod/Autorotation/akechi/AkechiDRG.cs b/BossMod/Autorotation/akechi/AkechiDRG.cs index f39f704130..b65d93a3d8 100644 --- a/BossMod/Autorotation/akechi/AkechiDRG.cs +++ b/BossMod/Autorotation/akechi/AkechiDRG.cs @@ -171,13 +171,13 @@ public static RotationModuleDefinition Definition() 100); //Max Level supported #region Custom Strategies - //Targeting strategy + //AOE strategy res.Define(Track.AOE).As("Combo Option", "AOE", uiPriority: 200) - .AddOption(AOEStrategy.AutoTargetHitPrimary, "AutoTargetHitPrimary", "Use AOE actions if profitable, select best target that ensures primary target is hit") - .AddOption(AOEStrategy.AutoTargetHitMost, "AutoTargetHitMost", "Use AOE actions if profitable, select a target that ensures maximal number of targets are hit") - .AddOption(AOEStrategy.ForceST, "Force ST", "Force Single-Target rotation") - .AddOption(AOEStrategy.Force123ST, "Only 1-2-3 ST", "Force only ST 1-2-3 rotation (No Buff or DoT)") - .AddOption(AOEStrategy.ForceBuffsST, "Only 1-4-5 ST", "Force only ST 1-4-5 rotation (Buff & DoT only)") + .AddOption(AOEStrategy.AutoTargetHitPrimary, "AutoTargetHitPrimary", "Use AOE actions if profitable, select best target that ensures primary target is hit", supportedTargets: ActionTargets.Hostile) + .AddOption(AOEStrategy.AutoTargetHitMost, "AutoTargetHitMost", "Use AOE actions if profitable, select a target that ensures maximal number of targets are hit", supportedTargets: ActionTargets.Hostile) + .AddOption(AOEStrategy.ForceST, "Force ST", "Force Single-Target rotation", supportedTargets: ActionTargets.Hostile) + .AddOption(AOEStrategy.Force123ST, "Only 1-2-3 ST", "Force only ST 1-2-3 rotation (No Buff or DoT)", supportedTargets: ActionTargets.Hostile) + .AddOption(AOEStrategy.ForceBuffsST, "Only 1-4-5 ST", "Force only ST 1-4-5 rotation (Buff & DoT only)", supportedTargets: ActionTargets.Hostile) .AddOption(AOEStrategy.ForceAOE, "Force AOE", "Force AOE rotation, even if less than 3 targets"); //Spear targeting strategy @@ -555,18 +555,20 @@ public override void Execute(StrategyValues strategy, Actor? primaryTarget, floa #endregion #region AOEStrategy 'Force' Execution - var AOEStrategy = strategy.Option(Track.AOE).As(); //Retrieve the current AOE strategy + var AOE = strategy.Option(Track.AOE); + var AOEStrategy = AOE.As(); //Retrieve the current AOE strategy + var AOETargetChoice = ResolveTargetOverride(AOE.Value) ?? primaryTarget; //Retrieve the current AOE strategy's target choice var SpearStrategy = strategy.Option(Track.Spears).As(); //Retrieve the current Spear targeting strategy //Force specific actions based on the AOE strategy selected if (AOEStrategy == AOEStrategy.ForceST) //if forced single target - QueueGCD(NextFullST(), primaryTarget, GCDPriority.ForcedGCD); //Queue the next single target action + QueueGCD(NextFullST(), AOETargetChoice, GCDPriority.ForcedGCD); //Queue the next single target action if (AOEStrategy == AOEStrategy.Force123ST) //if forced 123 combo - QueueGCD(Useonly123ST(), primaryTarget, GCDPriority.ForcedGCD); //Queue the 123 combo action + QueueGCD(Useonly123ST(), AOETargetChoice, GCDPriority.ForcedGCD); //Queue the 123 combo action if (AOEStrategy == AOEStrategy.ForceBuffsST) //if forced buffs combo - QueueGCD(Useonly145ST(), primaryTarget, GCDPriority.ForcedGCD); //Queue the buffed 145 combo action + QueueGCD(Useonly145ST(), AOETargetChoice, GCDPriority.ForcedGCD); //Queue the buffed 145 combo action if (AOEStrategy == AOEStrategy.ForceAOE) //if forced AOE action - QueueGCD(NextFullAOE(), primaryTarget, GCDPriority.ForcedGCD); //Queue the next AOE action + QueueGCD(NextFullAOE(), AOETargetChoice, GCDPriority.ForcedGCD); //Queue the next AOE action #endregion #region Dives Strategy @@ -612,7 +614,11 @@ public override void Execute(StrategyValues strategy, Actor? primaryTarget, floa #region Combo & Cooldown Execution //Combo Action evecution - QueueGCD(useAOE ? NextFullAOE() : NextFullST(), bestAOEtarget, GCDPriority.Combo123); + QueueGCD(useAOE //if should use AOE + ? NextFullAOE() //use AOE combo + : NextFullST(), //otherwise, use single target combo + bestAOEtarget, //on best AOE target + GCDPriority.Combo123); //set priority to Combo 123 //Execute Lance Charge if available var lcStrat = strategy.Option(Track.LanceCharge).As(); //Retrieve the Lance Charge strategy if (!hold && //if not holding Cooldowns @@ -649,11 +655,13 @@ or SurgeStrategy.ForceNextOptiWeave //or Force Next Optimal Weave Window : OGCDPriority.Buffs); //otherwise, set priority to Buffs //Execute Jump ability if available - var jumpStrat = strategy.Option(Track.Jump).As(); //Retrieve the Jump strategy + var jump = strategy.Option(Track.Jump); //Retrieve the Jump track + var jumpStrat = jump.As(); //Retrieve the Jump strategy if (!hold && divesGood && //if not holding Cooldowns and dives are good ShouldUseJump(jumpStrat, primaryTarget)) //if Jump should be used QueueOGCD(Unlocked(AID.HighJump) ? AID.HighJump : AID.Jump, //Queue High Jump if unlocked, otherwise Jump - primaryTarget, //on the primary target + ResolveTargetOverride(jump.Value) //Check target choice + ?? primaryTarget, //if none, choose the primary target jumpStrat is JumpStrategy.Force //if strategy is Force or JumpStrategy.ForceEX //or Force EX or JumpStrategy.ForceEX2 //or Force EX2 @@ -662,56 +670,66 @@ or JumpStrategy.ForceWeave //or Force Weave : OGCDPriority.Jump); //otherwise, set priority to Jump //Execute Dragonfire Dive if available - var ddStrat = strategy.Option(Track.DragonfireDive).As(); //Retrieve the Dragonfire Dive strategy + var dd = strategy.Option(Track.DragonfireDive); //Retrieve the Dragonfire Dive track + var ddStrat = dd.As(); //Retrieve the Dragonfire Dive strategy if (!hold && divesGood && //if not holding Cooldowns and dives are good ShouldUseDragonfireDive(ddStrat, primaryTarget)) //if Dragonfire Dive should be used QueueOGCD(AID.DragonfireDive, //Queue Dragonfire Dive - primaryTarget, //on the primary target + ResolveTargetOverride(dd.Value) //Check target choice + ?? primaryTarget, //if none, choose the primary target ddStrat is DragonfireStrategy.Force //if strategy is Force or DragonfireStrategy.ForceWeave //or Force Weave ? OGCDPriority.ForcedOGCD //set priority to Forced oGCD : OGCDPriority.DragonfireDive); //otherwise, set priority to Dragonfire Dive //Execute Geirskogul if available - var geirskogul = strategy.Option(Track.Geirskogul).As(); //Retrieve the Geirskogul strategy + var geirskogul = strategy.Option(Track.Geirskogul); //Retrieve the Geirskogul track + var geirskogulStrat = geirskogul.As(); //Retrieve the Geirskogul strategy if (!hold && //if not holding Cooldowns - ShouldUseGeirskogul(geirskogul, primaryTarget)) //if Geirskogul should be used + ShouldUseGeirskogul(geirskogulStrat, primaryTarget)) //if Geirskogul should be used QueueOGCD(AID.Geirskogul, //Queue Geirskogul - bestSpeartarget, //on the best Spear target - geirskogul is GeirskogulStrategy.Force //if strategy is Force + ResolveTargetOverride(geirskogul.Value) //Check target choice + ?? bestSpeartarget, //if none, choose the best spear target + geirskogulStrat is GeirskogulStrategy.Force //if strategy is Force or GeirskogulStrategy.ForceEX //or Force EX or GeirskogulStrategy.ForceWeave //or Force Weave ? OGCDPriority.ForcedOGCD //set priority to Forced oGCD : OGCDPriority.Geirskogul); //otherwise, set priority to Geirskogul //Execute Mirage Dive if available - var mirageStrat = strategy.Option(Track.MirageDive).As(); //Retrieve the Mirage Dive strategy + var mirage = strategy.Option(Track.MirageDive); //Retrieve the Mirage Dive track + var mirageStrat = mirage.As(); //Retrieve the Mirage Dive strategy if (!hold && //if not holding Cooldowns ShouldUseMirageDive(mirageStrat, primaryTarget)) //if Mirage Dive should be used QueueOGCD(AID.MirageDive, //Queue Mirage Dive - primaryTarget, //on the primary target + ResolveTargetOverride(mirage.Value) //Check target choice + ?? primaryTarget, //if none, choose the primary target mirageStrat is OffensiveStrategy.Force //if strategy is Force or OffensiveStrategy.ForceWeave //or Force Weave ? OGCDPriority.ForcedOGCD //set priority to Forced oGCD : OGCDPriority.MirageDive); //otherwise, set priority to Mirage Dive //Execute Nastrond if available - var nastrondStrat = strategy.Option(Track.Nastrond).As(); //Retrieve the Nastrond strategy + var nastrond = strategy.Option(Track.Nastrond); //Retrieve the Nastrond track + var nastrondStrat = nastrond.As(); //Retrieve the Nastrond strategy if (!hold && //if not holding Cooldowns ShouldUseNastrond(nastrondStrat, primaryTarget)) //if Nastrond should be used QueueOGCD(AID.Nastrond, //Queue Nastrond - bestSpeartarget, //on the best Spear target + ResolveTargetOverride(geirskogul.Value) //Check target choice + ?? bestSpeartarget, //if none, choose the best spear target nastrondStrat is OffensiveStrategy.Force //if strategy is Force or OffensiveStrategy.ForceWeave //or Force Weave ? OGCDPriority.ForcedOGCD //set priority to Forced oGCD : OGCDPriority.Nastrond); //otherwise, set priority to Nastrond //Execute Stardiver if available - var sdStrat = strategy.Option(Track.Stardiver).As(); //Retrieve the Stardiver strategy + var sd = strategy.Option(Track.Stardiver); //Retrieve the Stardiver track + var sdStrat = sd.As(); //Retrieve the Stardiver strategy if (!hold && divesGood && //if not holding Cooldowns and dives are good ShouldUseStardiver(sdStrat, primaryTarget)) //if Stardiver should be used QueueOGCD(AID.Stardiver, //Queue Stardiver - primaryTarget, //on the primary target + ResolveTargetOverride(sd.Value) //Check target choice + ?? primaryTarget, //if none, choose the primary target sdStrat is StardiverStrategy.Force //on the primary target or StardiverStrategy.ForceEX //or Force EX or StardiverStrategy.ForceWeave //or Force Weave @@ -719,11 +737,14 @@ or StardiverStrategy.ForceWeave //or Force Weave : OGCDPriority.Stardiver); //otherwise, set priority to Stardiver //Execute Wyrmwind Thrust if available - var wtStrat = strategy.Option(Track.WyrmwindThrust).As(); //Retrieve the Wyrmwind Thrust strategy + var wt = strategy.Option(Track.WyrmwindThrust); //Retrieve the Wyrmwind Thrust track + var wtStrat = wt.As(); //Retrieve the Wyrmwind Thrust strategy if (!hold && //if not holding Cooldowns ShouldUseWyrmwindThrust(wtStrat, primaryTarget)) //if Wyrmwind Thrust should be used QueueOGCD(AID.WyrmwindThrust, //Queue Wyrmwind Thrust - bestSpeartarget, wtStrat is OffensiveStrategy.Force //on the best Spear target + ResolveTargetOverride(wt.Value) //Check target choice + ?? bestSpeartarget, //if none, choose the best spear target + wtStrat is OffensiveStrategy.Force //if strategy is Force or OffensiveStrategy.ForceWeave //or Force Weave ? OGCDPriority.ForcedOGCD //set priority to Forced oGCD : HasEffect(SID.LanceCharge) //if Lance Charge is active @@ -731,30 +752,36 @@ or OffensiveStrategy.ForceWeave //or Force Weave : OGCDPriority.WyrmwindThrust); //otherwise, set priority to Wyrmwind Thrust //Execute Rise of the Dragon if available - var riseStrat = strategy.Option(Track.RiseOfTheDragon).As(); //Retrieve the Rise of the Dragon strategy + var rise = strategy.Option(Track.RiseOfTheDragon); //Retrieve the Rise of the Dragon track + var riseStrat = rise.As(); //Retrieve the Rise of the Dragon strategy if (ShouldUseRiseOfTheDragon(riseStrat, primaryTarget)) //if Rise of the Dragon should be used QueueOGCD(AID.RiseOfTheDragon, //Queue Rise of the Dragon - primaryTarget, //on the primary target + ResolveTargetOverride(rise.Value) //Check target choice + ?? primaryTarget, //if none, choose the primary target riseStrat is OffensiveStrategy.Force //if strategy is Force or OffensiveStrategy.ForceWeave //or Force Weave ? OGCDPriority.ForcedOGCD //set priority to Forced oGCD : OGCDPriority.Buffs); //otherwise, set priority to Buffs //Execute Starcross if available - var crossStrat = strategy.Option(Track.Starcross).As(); //Retrieve the Starcross strategy + var cross = strategy.Option(Track.Starcross); //Retrieve the Starcross track + var crossStrat = cross.As(); //Retrieve the Starcross strategy if (ShouldUseStarcross(crossStrat, primaryTarget)) //if Starcross should be used QueueOGCD(AID.Starcross, //Queue Starcross - primaryTarget, //on the primary target + ResolveTargetOverride(cross.Value) //Check target choice + ?? primaryTarget, //if none, choose the primary target crossStrat is OffensiveStrategy.Force //if strategy is Force or OffensiveStrategy.ForceWeave //or Force Weave ? OGCDPriority.ForcedOGCD //set priority to Forced oGCD : OGCDPriority.Starcross); //otherwise, set priority to Starcross //Execute Piercing Talon if available - var ptStrat = strategy.Option(Track.PiercingTalon).As(); //Retrieve the Piercing Talon strategy + var pt = strategy.Option(Track.PiercingTalon); //Retrieve the Piercing Talon track + var ptStrat = pt.As(); //Retrieve the Piercing Talon strategy if (ShouldUsePiercingTalon(primaryTarget, ptStrat)) //if Piercing Talon should be used QueueGCD(AID.PiercingTalon, //Queue Piercing Talon - primaryTarget, //on the primary target + ResolveTargetOverride(pt.Value) //Check target choice + ?? primaryTarget, //if none, choose the primary target ptStrat is PiercingTalonStrategy.Force or //if strategy is Force PiercingTalonStrategy.ForceEX //or Force EX ? GCDPriority.ForcedGCD //set priority to Forced GCD @@ -1259,6 +1286,5 @@ ComboLastMove is AID.Disembowel or AID.SpiralBlow TrueNorthStrategy.Delay => false, _ => false }; - #endregion } diff --git a/BossMod/Autorotation/akechi/AkechiGNB.cs b/BossMod/Autorotation/akechi/AkechiGNB.cs index 7ae074b3dd..ac56eaaa01 100644 --- a/BossMod/Autorotation/akechi/AkechiGNB.cs +++ b/BossMod/Autorotation/akechi/AkechiGNB.cs @@ -170,10 +170,10 @@ public static RotationModuleDefinition Definition() #region Custom strategies //AOE strategy res.Define(Track.AOE).As("AOE", uiPriority: 200) - .AddOption(AOEStrategy.AutoFinishCombo, "Auto (Finish Combo)", "Auto-selects best rotation dependant on targets; Finishes combo first") - .AddOption(AOEStrategy.AutoBreakCombo, "Auto (Break Combo)", "Auto-selects best rotation dependant on targets; Breaks combo if needed") - .AddOption(AOEStrategy.ForceSTwithO, "Force ST with Overcap", "Force single-target rotation with overcap protection") - .AddOption(AOEStrategy.ForceSTwithoutO, "Force ST without Overcap", "Force ST rotation without overcap protection") + .AddOption(AOEStrategy.AutoFinishCombo, "Auto (Finish Combo)", "Auto-selects best rotation dependant on targets; Finishes combo first", supportedTargets: ActionTargets.Hostile) + .AddOption(AOEStrategy.AutoBreakCombo, "Auto (Break Combo)", "Auto-selects best rotation dependant on targets; Breaks combo if needed", supportedTargets: ActionTargets.Hostile) + .AddOption(AOEStrategy.ForceSTwithO, "Force ST with Overcap", "Force single-target rotation with overcap protection", supportedTargets: ActionTargets.Hostile) + .AddOption(AOEStrategy.ForceSTwithoutO, "Force ST without Overcap", "Force ST rotation without overcap protection", supportedTargets: ActionTargets.Hostile) .AddOption(AOEStrategy.ForceAOEwithO, "Force AOE with Overcap", "Force AOE rotation with overcap protection") .AddOption(AOEStrategy.ForceAOEwithoutO, "Force AOE without Overcap", "Force AOE rotation without overcap protection") .AddOption(AOEStrategy.GenerateDowntime, "Generate Downtime", "Generate cartridges before downtime"); @@ -185,10 +185,10 @@ public static RotationModuleDefinition Definition() //Cartridges strategy res.Define(Track.Cartridges).As("Cartridges", "Carts", uiPriority: 180) - .AddOption(CartridgeStrategy.Automatic, "Automatic", "Automatically decide when to use cartridges; uses them optimally") - .AddOption(CartridgeStrategy.BurstStrike, "Burst Strike", "Force the use of Burst Strike; consumes 1 cartridge") + .AddOption(CartridgeStrategy.Automatic, "Automatic", "Automatically decide when to use cartridges; uses them optimally", supportedTargets: ActionTargets.Hostile) + .AddOption(CartridgeStrategy.BurstStrike, "Burst Strike", "Force the use of Burst Strike; consumes 1 cartridge", supportedTargets: ActionTargets.Hostile) .AddOption(CartridgeStrategy.FatedCircle, "Fated Circle", "Force the use of Fated Circle; consumes 1 cartridge") - .AddOption(CartridgeStrategy.GnashingFang, "Gnashing Fang", "Force the use of Gnashing Fang (cooldown only, no combo or CDs); consumes 1 cartridge") + .AddOption(CartridgeStrategy.GnashingFang, "Gnashing Fang", "Force the use of Gnashing Fang (cooldown only, no combo or CDs); consumes 1 cartridge", supportedTargets: ActionTargets.Hostile) .AddOption(CartridgeStrategy.DoubleDown, "Double Down", "Force the use of Double Down; consumes 1 cartridge") .AddOption(CartridgeStrategy.Conserve, "Conserve", "Prohibit use of all cartridge-related abilities; will not use any of these actions listed above") .AddAssociatedActions(AID.BurstStrike, AID.FatedCircle, AID.GnashingFang, AID.DoubleDown); @@ -202,10 +202,10 @@ public static RotationModuleDefinition Definition() //LightningShot strategy res.Define(Track.LightningShot).As("Lightning Shot", "L.Shot", uiPriority: 30) - .AddOption(LightningShotStrategy.OpenerFar, "Far (Opener)", "Use Lightning Shot in pre-pull & out of melee range") - .AddOption(LightningShotStrategy.OpenerForce, "Force (Opener)", "Force use Lightning Shot in pre-pull in any range") - .AddOption(LightningShotStrategy.Force, "Force", "Force use Lightning Shot in any range") - .AddOption(LightningShotStrategy.Allow, "Allow", "Allow use of Lightning Shot when out of melee range") + .AddOption(LightningShotStrategy.OpenerFar, "Far (Opener)", "Use Lightning Shot in pre-pull & out of melee range", supportedTargets: ActionTargets.Hostile) + .AddOption(LightningShotStrategy.OpenerForce, "Force (Opener)", "Force use Lightning Shot in pre-pull in any range", supportedTargets: ActionTargets.Hostile) + .AddOption(LightningShotStrategy.Force, "Force", "Force use Lightning Shot in any range", supportedTargets: ActionTargets.Hostile) + .AddOption(LightningShotStrategy.Allow, "Allow", "Allow use of Lightning Shot when out of melee range", supportedTargets: ActionTargets.Hostile) .AddOption(LightningShotStrategy.Forbid, "Forbid", "Prohibit use of Lightning Shot") .AddAssociatedActions(AID.LightningShot); @@ -469,19 +469,30 @@ public override void Execute(StrategyValues strategy, Actor? primaryTarget, floa #endregion #region Strategy Definitions - var AOEStrategy = strategy.Option(Track.AOE).As(); //AOE strategy + var AOE = strategy.Option(Track.AOE); //AOE track + var AOEStrategy = AOE.As(); //AOE strategy + var carts = strategy.Option(Track.Cartridges); //Cartridges track + var cartStrat = carts.As(); //Cartridges strategy + var nm = strategy.Option(Track.NoMercy); //No Mercy track + var nmStrat = nm.As(); //No Mercy strategy + var zone = strategy.Option(Track.Zone); //Zone track + var zoneStrat = zone.As(); //Zone strategy + var bow = strategy.Option(Track.BowShock); //Bow Shock track + var bowStrat = bow.As(); //Bow Shock strategy + var bf = strategy.Option(Track.Bloodfest); //Bloodfest track + var bfStrat = bf.As(); //Bloodfest strategy + var dd = strategy.Option(Track.DoubleDown); //Double Down track + var ddStrat = dd.As(); //Double Down strategy + var gf = strategy.Option(Track.GnashingFang); //Gnashing Fang track + var gfStrat = gf.As(); //Gnashing Fang strategy + var reign = strategy.Option(Track.Reign); //Reign of Beasts track + var reignStrat = reign.As(); //Reign of Beasts strategy + var sb = strategy.Option(Track.SonicBreak); //Sonic Break track + var sbStrat = sb.As(); //Sonic Break strategy + var ls = strategy.Option(Track.LightningShot); //Lightning Shot track + var lsStrat = ls.As(); //Lightning Shot strategy var hold = strategy.Option(Track.Cooldowns).As() == CooldownStrategy.Hold; //Determine if holding resources var conserve = strategy.Option(Track.Cartridges).As() == CartridgeStrategy.Conserve; //Determine if conserving cartridges - var cartStrat = strategy.Option(Track.Cartridges).As(); //Cartridge strategy - var nmStrat = strategy.Option(Track.NoMercy).As(); //No Mercy strategy - var zoneStrat = strategy.Option(Track.Zone).As(); //Zone strategy - var bowStrat = strategy.Option(Track.BowShock).As(); //Bow Shock strategy - var bfStrat = strategy.Option(Track.Bloodfest).As(); //Bloodfest strategy - var gfStrat = strategy.Option(Track.GnashingFang).As(); //Gnashing Fang strategy - var ddStrat = strategy.Option(Track.DoubleDown).As(); //Double Down strategy - var sbStrat = strategy.Option(Track.SonicBreak).As(); //Sonic Break strategy - var reignStrat = strategy.Option(Track.Reign).As(); //Reign of Beasts strategy - var lsStrat = strategy.Option(Track.LightningShot).As(); //Lightning Shot strategy #endregion #endregion @@ -493,11 +504,13 @@ public override void Execute(StrategyValues strategy, Actor? primaryTarget, floa #region Force Execution if (AOEStrategy is AOEStrategy.ForceSTwithO) //if Single-target (with overcap protection) option is selected QueueGCD(NextComboSingleTarget(), //queue the next single-target combo action with overcap protection - primaryTarget, //on the primary target + ResolveTargetOverride(AOE.Value) //Get target choice + ?? primaryTarget, //if none, choose primary target GCDPriority.ForcedGCD); //with priority for forced GCDs if (AOEStrategy is AOEStrategy.ForceSTwithoutO) //if Single-target (without overcap protection) option is selected QueueGCD(NextForceSingleTarget(), //queue the next single-target combo action without overcap protection - primaryTarget, //on the primary target + ResolveTargetOverride(AOE.Value) //Get target choice + ?? primaryTarget, //if none, choose primary target GCDPriority.ForcedGCD); //with priority for forced GCDs if (AOEStrategy is AOEStrategy.ForceAOEwithO) //if AOE (with overcap protection) option is selected QueueGCD(NextComboAOE(), //queue the next AOE combo action with overcap protection @@ -571,13 +584,15 @@ public override void Execute(StrategyValues strategy, Actor? primaryTarget, floa GCDPriority.Combo123); //with priority for 123/12 combo actions if (!ShouldUseAOE) QueueGCD(NextComboSingleTarget(), //queue the next single-target combo action - primaryTarget, //on the primary target + ResolveTargetOverride(AOE.Value) //Get target choice + ?? primaryTarget, //if none, choose primary target GCDPriority.Combo123); //with priority for 123/12 combo actions } if (AOEStrategy == AOEStrategy.AutoFinishCombo) //if Finish Combo option is selected { QueueGCD(NextBestRotation(), //queue the next single-target combo action only if combo is finished - primaryTarget, //on the primary target + ResolveTargetOverride(AOE.Value) //Get target choice + ?? primaryTarget, //if none, choose primary target GCDPriority.Combo123); //with priority for 123/12 combo actions } #endregion @@ -609,7 +624,8 @@ or NoMercyStrategy.Force3QW //or Force last second weave with 3 cartridges //Zone execution (Blasting Zone / Danger Zone) if (ShouldUseZone(zoneStrat, primaryTarget)) //if Zone should be used QueueOGCD(BestZone, //queue the best Zone action - primaryTarget, //on the primary target + ResolveTargetOverride(zone.Value) //Get target choice + ?? primaryTarget, //if none, choose primary target zoneStrat is OGCDStrategy.Force //if strategy option is Force or OGCDStrategy.AnyWeave //or any Weave or OGCDStrategy.EarlyWeave //or Early Weave @@ -631,7 +647,8 @@ or OGCDStrategy.LateWeave //or Late Weave //Bloodfest execution if (ShouldUseBloodfest(bfStrat, primaryTarget)) //if Bloodfest should be used QueueOGCD(AID.Bloodfest, //queue Bloodfest - primaryTarget, //on the primary target + ResolveTargetOverride(bf.Value) //Get target choice + ?? primaryTarget, //if none, choose primary target bfStrat is BloodfestStrategy.Force //if strategy option is Force or BloodfestStrategy.ForceW //or Force weave or BloodfestStrategy.Force0 //or Force with 0 cartridges @@ -672,7 +689,8 @@ or BloodfestStrategy.Force0W //or Force weave with 0 cartridges //Gnashing Fang execution if (ShouldUseGnashingFang(gfStrat, primaryTarget)) //if Gnashing Fang should be used QueueGCD(AID.GnashingFang, //queue Gnashing Fang - primaryTarget, //on the primary target + ResolveTargetOverride(gf.Value) //Get target choice + ?? primaryTarget, //if none, choose primary target cartStrat == CartridgeStrategy.GnashingFang || //if Gnashing Fang is selected on Cartridge strategy gfStrat == GnashingStrategy.ForceGnash //or Force Gnashing Fang is selected on Gnashing Fang strategy ? GCDPriority.ForcedGCD //use priority for forced GCDs @@ -683,14 +701,16 @@ or BloodfestStrategy.Force0W //or Force weave with 0 cartridges //Optimal targeting & execution for both gauge spenders if (cartStrat == CartridgeStrategy.Automatic) //if Automatic Cartridge strategy is selected QueueGCD(BestCartSpender, //queue the best cartridge spender - primaryTarget, //on the primary target + ResolveTargetOverride(carts.Value) //Get target choice + ?? primaryTarget, //if none, choose primary target nmCD < 1 && Ammo == 3 //if No Mercy is imminent and 3 cartridges are available ? GCDPriority.ForcedGCD //use priority for forced GCDs : GCDPriority.Gauge); //otherwise, use priority for gauge actions //Burst Strike forced execution if (cartStrat == CartridgeStrategy.BurstStrike) //if Burst Strike Cartridge strategy is selected QueueGCD(AID.BurstStrike, //queue Burst Strike - primaryTarget, //on the primary target + ResolveTargetOverride(carts.Value) //Get target choice + ?? primaryTarget, //if none, choose primary target GCDPriority.Gauge); //with priority for gauge actions //Fated Circle forced execution if (cartStrat == CartridgeStrategy.FatedCircle) //if Fated Circle Cartridge strategy is selected @@ -703,7 +723,8 @@ or BloodfestStrategy.Force0W //or Force weave with 0 cartridges //Sonic Break execution if (ShouldUseSonicBreak(sbStrat, primaryTarget)) //if Sonic Break should be used QueueGCD(AID.SonicBreak, //queue Sonic Break - primaryTarget, //on the primary target + ResolveTargetOverride(sb.Value) //Get target choice + ?? primaryTarget, //if none, choose primary target sbStrat is SonicBreakStrategy.Force //if strategy option is Force or SonicBreakStrategy.Early //or Early ? GCDPriority.ForcedGCD //use priority for forced GCDs @@ -711,7 +732,8 @@ or SonicBreakStrategy.Early //or Early //Reign of Beasts execution if (ShouldUseReign(reignStrat, primaryTarget)) //if Reign of Beasts should be used QueueGCD(AID.ReignOfBeasts, //queue Reign of Beasts - primaryTarget, //on the primary target + ResolveTargetOverride(reign.Value) //Get target choice + ?? primaryTarget, //if none, choose primary target reignStrat == ReignStrategy.ForceReign //if Force Reign of Beasts is selected on Reign of Beasts strategy ? GCDPriority.ForcedGCD //use priority for forced GCDs : GCDPriority.Reign); //otherwise, use intended priority @@ -746,7 +768,8 @@ or SonicBreakStrategy.Early //or Early //Lightning Shot execution if (ShouldUseLightningShot(primaryTarget, lsStrat)) //if Lightning Shot should be used QueueGCD(AID.LightningShot, //queue Lightning Shot - primaryTarget, //on the primary target + ResolveTargetOverride(ls.Value) //Get target choice + ?? primaryTarget, //if none, choose primary target lsStrat is LightningShotStrategy.Force //if strategy option is Force or LightningShotStrategy.Allow //or Allow ? GCDPriority.ForcedGCD //use priority for forced GCDs From 480fcf678d3422161b56c4b5dc165d0325598ec0 Mon Sep 17 00:00:00 2001 From: Akechi-kun <167795370+Akechi-kun@users.noreply.github.com> Date: Tue, 24 Dec 2024 15:07:43 -0800 Subject: [PATCH 18/29] Sacred Soil fix added QueueGCD/OGCD from xan's files, as this has the appropriate code for ground targeting abilities --- .../Autorotation/Utility/ClassSCHUtility.cs | 98 ++++++++++++++++--- 1 file changed, 86 insertions(+), 12 deletions(-) diff --git a/BossMod/Autorotation/Utility/ClassSCHUtility.cs b/BossMod/Autorotation/Utility/ClassSCHUtility.cs index a5c5731cc6..7ede33b215 100644 --- a/BossMod/Autorotation/Utility/ClassSCHUtility.cs +++ b/BossMod/Autorotation/Utility/ClassSCHUtility.cs @@ -4,6 +4,7 @@ public sealed class ClassSCHUtility(RotationModuleManager manager, Actor player) { public enum Track { WhisperingDawn = SharedTrack.Count, Adloquium, Succor, FeyIllumination, Lustrate, SacredSoil, Indomitability, DeploymentTactics, EmergencyTactics, Dissipation, Excogitation, Aetherpact, Recitation, FeyBlessing, Consolation, Protraction, Expedient, Seraphism, Summons } public enum SuccorOption { None, Succor, Concitation } + public enum SacredSoilOption { None, Use, UseEx } public enum DeployOption { None, Use, UseEx } public enum AetherpactOption { None, Use, End } public enum RecitationOption { None, Use, UseEx } @@ -32,7 +33,13 @@ public static RotationModuleDefinition Definition() DefineSimpleConfig(res, Track.FeyIllumination, "FeyIllumination", "FeyIll", 240, SCH.AID.FeyIllumination, 20); DefineSimpleConfig(res, Track.Lustrate, "Lustrate", "Lust", 150, SCH.AID.Lustrate); - DefineSimpleConfig(res, Track.SacredSoil, "SacredSoil", "Soil", 180, SCH.AID.SacredSoil, 15); + + res.Define(Track.SacredSoil).As("Soil", "", 200) + .AddOption(SacredSoilOption.None, "None", "Do not use automatically") + .AddOption(SacredSoilOption.Use, "Use", "Use Sacred Soil", 30, 15, ActionTargets.All, 50, 77) + .AddOption(SacredSoilOption.UseEx, "UseEx", "Use Enhanced Sacred Soil", 30, 15, ActionTargets.All, 78) + .AddAssociatedActions(SCH.AID.SacredSoil); + DefineSimpleConfig(res, Track.Indomitability, "Indomitability", "Indom", 90, SCH.AID.Indomitability); res.Define(Track.DeploymentTactics).As("DeploymentTactics", "Deploy", 150) @@ -58,9 +65,10 @@ public static RotationModuleDefinition Definition() .AddAssociatedActions(SCH.AID.Recitation); DefineSimpleConfig(res, Track.FeyBlessing, "FeyBlessing", "Bless", 120, SCH.AID.FeyBlessing); - DefineSimpleConfig(res, Track.Consolation, "Consolation", "Consol", 80, SCH.AID.Consolation, 30); - DefineSimpleConfig(res, Track.Protraction, "Protraction", "Prot", 110, SCH.AID.Protraction, 10); - DefineSimpleConfig(res, Track.Expedient, "Expedient", "Exped", 200, SCH.AID.Expedient, 20); + DefineSimpleConfig(res, Track.FeyBlessing, "FeyBlessing", "F.Blessing", 120, SCH.AID.FeyBlessing); + DefineSimpleConfig(res, Track.Consolation, "Consolation", "Consol.", 80, SCH.AID.Consolation, 30); + DefineSimpleConfig(res, Track.Protraction, "Protraction", "Prot.", 110, SCH.AID.Protraction, 10); + DefineSimpleConfig(res, Track.Expedient, "Expedient", "Exped.", 200, SCH.AID.Expedient, 20); DefineSimpleConfig(res, Track.Seraphism, "Seraphism", "", 300, SCH.AID.Seraphism, 20); // Pet Summons @@ -79,11 +87,9 @@ public override void Execute(StrategyValues strategy, Actor? primaryTarget, floa ExecuteSimple(strategy.Option(Track.WhisperingDawn), SCH.AID.WhisperingDawn, Player); ExecuteSimple(strategy.Option(Track.Adloquium), SCH.AID.Adloquium, TargetChoice(strategy.Option(Track.Adloquium)) ?? Player); ExecuteSimple(strategy.Option(Track.FeyIllumination), SCH.AID.FeyIllumination, Player); - ExecuteSimple(strategy.Option(Track.Lustrate), SCH.AID.Lustrate, TargetChoice(strategy.Option(Track.Lustrate)) ?? Player); - ExecuteSimple(strategy.Option(Track.SacredSoil), SCH.AID.SacredSoil, Player); ExecuteSimple(strategy.Option(Track.Indomitability), SCH.AID.Indomitability, Player); ExecuteSimple(strategy.Option(Track.EmergencyTactics), SCH.AID.EmergencyTactics, Player); - ExecuteSimple(strategy.Option(Track.Dissipation), SCH.AID.Dissipation, Player); + ExecuteSimple(strategy.Option(Track.Dissipation), SCH.AID.Dissipation, primaryTarget); ExecuteSimple(strategy.Option(Track.Excogitation), SCH.AID.Excogitation, TargetChoice(strategy.Option(Track.Excogitation)) ?? Player); ExecuteSimple(strategy.Option(Track.FeyBlessing), SCH.AID.FeyBlessing, Player); ExecuteSimple(strategy.Option(Track.Consolation), SCH.AID.Consolation, Player); @@ -100,11 +106,20 @@ public override void Execute(StrategyValues strategy, Actor? primaryTarget, floa _ => default }; if (succAction != default && !alreadyUp) - Hints.ActionsToExecute.Push(ActionID.MakeSpell(succAction), Player, succ.Priority(), succ.Value.ExpireIn); + QueueOGCD(succAction, Player); + + var soil = strategy.Option(Track.SacredSoil); + var soilAction = soil.As() switch + { + SacredSoilOption.Use or SacredSoilOption.UseEx => SCH.AID.SacredSoil, + _ => default + }; + if (soilAction != default) + QueueOGCD(soilAction, TargetChoice(soil) ?? primaryTarget); var deploy = strategy.Option(Track.DeploymentTactics); if (deploy.As() != DeployOption.None) - Hints.ActionsToExecute.Push(ActionID.MakeSpell(SCH.AID.DeploymentTactics), Player, deploy.Priority(), deploy.Value.ExpireIn); + QueueOGCD(SCH.AID.DeploymentTactics, Player); var pact = strategy.Option(Track.Aetherpact); var pactAction = pact.As() switch @@ -114,11 +129,11 @@ public override void Execute(StrategyValues strategy, Actor? primaryTarget, floa _ => default }; if (pactAction != default) - Hints.ActionsToExecute.Push(ActionID.MakeSpell(pactAction), TargetChoice(pact) ?? Player, pact.Priority(), pact.Value.ExpireIn); + QueueOGCD(pactAction, Player); var recit = strategy.Option(Track.Recitation); if (recit.As() != RecitationOption.None) - Hints.ActionsToExecute.Push(ActionID.MakeSpell(SCH.AID.Recitation), Player, recit.Priority(), recit.Value.ExpireIn); + QueueOGCD(SCH.AID.Recitation, Player); var pet = strategy.Option(Track.Summons); var petSummons = pet.As() switch @@ -128,7 +143,66 @@ public override void Execute(StrategyValues strategy, Actor? primaryTarget, floa _ => default }; if (petSummons != default) - Hints.ActionsToExecute.Push(ActionID.MakeSpell(petSummons), Player, pet.Priority(), pet.Value.ExpireIn); + QueueOGCD(petSummons, Player); + } + + #region Core Execution Helpers + + public SCH.AID NextGCD; //Next global cooldown action to be used + public void QueueGCD

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

(SCH.AID aid, Actor? target, P priority, float delay = 0) where P : Enum + => QueueOGCD(aid, target, (int)(object)priority, delay); + + public void QueueOGCD(SCH.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(SCH.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 + } From c53725e3807070926cfa3dd93caf4cd89085826e Mon Sep 17 00:00:00 2001 From: Akechi-kun <167795370+Akechi-kun@users.noreply.github.com> Date: Tue, 24 Dec 2024 15:12:23 -0800 Subject: [PATCH 19/29] . --- .../Autorotation/Utility/ClassSCHUtility.cs | 29 +++++++++---------- 1 file changed, 14 insertions(+), 15 deletions(-) diff --git a/BossMod/Autorotation/Utility/ClassSCHUtility.cs b/BossMod/Autorotation/Utility/ClassSCHUtility.cs index 7ede33b215..ebf46a9300 100644 --- a/BossMod/Autorotation/Utility/ClassSCHUtility.cs +++ b/BossMod/Autorotation/Utility/ClassSCHUtility.cs @@ -22,54 +22,53 @@ public static RotationModuleDefinition Definition() var res = new RotationModuleDefinition("Utility: SCH", "Cooldown Planner support for Utility Actions.\nNOTE: This is NOT a rotation preset! All Utility modules are STRICTLY for cooldown-planning usage.", "Utility for planner", "Akechi", RotationModuleQuality.Ok, BitMask.Build((int)Class.SCH), 100); DefineShared(res, IDLimitBreak3); - DefineSimpleConfig(res, Track.WhisperingDawn, "WhisperingDawn", "W.Dawn", 140, SCH.AID.WhisperingDawn, 21); - DefineSimpleConfig(res, Track.Adloquium, "Adloquium", "Adlo", 100, SCH.AID.Adloquium, 30); + DefineSimpleConfig(res, Track.WhisperingDawn, "Whispering Dawn", "W.Dawn", 140, SCH.AID.WhisperingDawn, 21); + DefineSimpleConfig(res, Track.Adloquium, "Adloquium", "Adlo.", 100, SCH.AID.Adloquium, 30); - res.Define(Track.Succor).As("Succor", "", 200) + res.Define(Track.Succor).As("Succor", "Succor", 200) .AddOption(SuccorOption.None, "None", "Do not use automatically") .AddOption(SuccorOption.Succor, "Use", "Use Succor", 2, 30, ActionTargets.Self, 35, 95) .AddOption(SuccorOption.Concitation, "UseEx", "Use Concitation", 2, 30, ActionTargets.Self, 96) .AddAssociatedActions(SCH.AID.Succor, SCH.AID.Concitation); - DefineSimpleConfig(res, Track.FeyIllumination, "FeyIllumination", "FeyIll", 240, SCH.AID.FeyIllumination, 20); - DefineSimpleConfig(res, Track.Lustrate, "Lustrate", "Lust", 150, SCH.AID.Lustrate); + DefineSimpleConfig(res, Track.FeyIllumination, "Fey Illumination", "F.Illum.", 240, SCH.AID.FeyIllumination, 20); + DefineSimpleConfig(res, Track.Lustrate, "Lustrate", "Lustrate", 150, SCH.AID.Lustrate); - res.Define(Track.SacredSoil).As("Soil", "", 200) + res.Define(Track.SacredSoil).As("Sacred Soil", "S.Soil", 200) .AddOption(SacredSoilOption.None, "None", "Do not use automatically") .AddOption(SacredSoilOption.Use, "Use", "Use Sacred Soil", 30, 15, ActionTargets.All, 50, 77) .AddOption(SacredSoilOption.UseEx, "UseEx", "Use Enhanced Sacred Soil", 30, 15, ActionTargets.All, 78) .AddAssociatedActions(SCH.AID.SacredSoil); - DefineSimpleConfig(res, Track.Indomitability, "Indomitability", "Indom", 90, SCH.AID.Indomitability); + DefineSimpleConfig(res, Track.Indomitability, "Indomitability", "Indom.", 90, SCH.AID.Indomitability); - res.Define(Track.DeploymentTactics).As("DeploymentTactics", "Deploy", 150) + res.Define(Track.DeploymentTactics).As("DeploymentTactics", "Deploy.", 150) .AddOption(DeployOption.None, "None", "Do not use automatically") .AddOption(DeployOption.Use, "Use", "Use Deployment Tactics", 120, 0, ActionTargets.Self, 56, 87) .AddOption(DeployOption.UseEx, "UseEx", "Use Enhanced Deployment Tactics", 90, 0, ActionTargets.Self, 88) .AddAssociatedActions(SCH.AID.DeploymentTactics); - DefineSimpleConfig(res, Track.EmergencyTactics, "EmergencyTactics", "Emerg", 100, SCH.AID.EmergencyTactics, 15); - DefineSimpleConfig(res, Track.Dissipation, "Dissipation", "Dissi", 290, SCH.AID.Dissipation, 15); - DefineSimpleConfig(res, Track.Excogitation, "Excogitation", "Excog", 100, SCH.AID.Excogitation, 45); + DefineSimpleConfig(res, Track.EmergencyTactics, "EmergencyTactics", "Emerg.", 100, SCH.AID.EmergencyTactics, 15); + DefineSimpleConfig(res, Track.Dissipation, "Dissipation", "Dissi.", 290, SCH.AID.Dissipation, 15); + DefineSimpleConfig(res, Track.Excogitation, "Excogitation", "Excog.", 100, SCH.AID.Excogitation, 45); - res.Define(Track.Aetherpact).As("Aetherpact", "", 300) + res.Define(Track.Aetherpact).As("Aetherpact", "A.pact", 300) .AddOption(AetherpactOption.None, "None", "Do not use automatically") .AddOption(AetherpactOption.Use, "Use", "Use Aetherpact", 0, 0, ActionTargets.Self | ActionTargets.Party, 70) .AddOption(AetherpactOption.End, "UseEx", "End Aetherpact", 0, 0, ActionTargets.Self | ActionTargets.Party, 70) .AddAssociatedActions(SCH.AID.Aetherpact, SCH.AID.DissolveUnion); - res.Define(Track.Recitation).As("Recitation", "Recit", 130) + res.Define(Track.Recitation).As("Recitation", "Recit.", 130) .AddOption(RecitationOption.None, "None", "Do not use automatically") .AddOption(RecitationOption.Use, "Use", "Use Recitation", 90, 0, ActionTargets.Self, 74, 97) .AddOption(RecitationOption.UseEx, "UseEx", "Use Enhanced Recitation", 60, 0, ActionTargets.Self, 98) .AddAssociatedActions(SCH.AID.Recitation); - DefineSimpleConfig(res, Track.FeyBlessing, "FeyBlessing", "Bless", 120, SCH.AID.FeyBlessing); DefineSimpleConfig(res, Track.FeyBlessing, "FeyBlessing", "F.Blessing", 120, SCH.AID.FeyBlessing); DefineSimpleConfig(res, Track.Consolation, "Consolation", "Consol.", 80, SCH.AID.Consolation, 30); DefineSimpleConfig(res, Track.Protraction, "Protraction", "Prot.", 110, SCH.AID.Protraction, 10); DefineSimpleConfig(res, Track.Expedient, "Expedient", "Exped.", 200, SCH.AID.Expedient, 20); - DefineSimpleConfig(res, Track.Seraphism, "Seraphism", "", 300, SCH.AID.Seraphism, 20); + DefineSimpleConfig(res, Track.Seraphism, "Seraphism", "Seraphism", 300, SCH.AID.Seraphism, 20); // Pet Summons res.Define(Track.Summons).As("Pet", "", 180) From 18e21cb9ce5b30f4785612e86e548efdb8cdaf8f Mon Sep 17 00:00:00 2001 From: Akechi-kun <167795370+Akechi-kun@users.noreply.github.com> Date: Tue, 24 Dec 2024 18:17:04 -0800 Subject: [PATCH 20/29] AST Earthly Star addition --- .../Autorotation/Utility/ClassASTUtility.cs | 85 ++++++++++++++++++- .../Autorotation/Utility/ClassSCHUtility.cs | 2 +- 2 files changed, 82 insertions(+), 5 deletions(-) diff --git a/BossMod/Autorotation/Utility/ClassASTUtility.cs b/BossMod/Autorotation/Utility/ClassASTUtility.cs index 9a1ba0f5c7..f5015f18b6 100644 --- a/BossMod/Autorotation/Utility/ClassASTUtility.cs +++ b/BossMod/Autorotation/Utility/ClassASTUtility.cs @@ -2,7 +2,8 @@ public sealed class ClassASTUtility(RotationModuleManager manager, Actor player) : RoleHealerUtility(manager, player) { - public enum Track { Helios = SharedTrack.Count, Lightspeed, BeneficII, EssentialDignity, AspectedBenefic, AspectedHelios, Synastry, CollectiveUnconscious, CelestialOpposition, CelestialIntersection, Horoscope, NeutralSect, Exaltation, Macrocosmos, SunSign } + public enum Track { Helios = SharedTrack.Count, Lightspeed, BeneficII, EssentialDignity, AspectedBenefic, AspectedHelios, Synastry, CollectiveUnconscious, CelestialOpposition, EarthlyStar, CelestialIntersection, Horoscope, NeutralSect, Exaltation, Macrocosmos, SunSign } + public enum StarOption { None, Use, End } public enum HoroscopeOption { None, Use, End } public enum MacrocosmosOption { None, Use, End } public enum HeliosOption { None, Use, UseEx } @@ -32,6 +33,13 @@ public static RotationModuleDefinition Definition() DefineSimpleConfig(res, Track.Synastry, "Synastry", "", 200, AST.AID.Synastry, 20); //ST oGCD "kardia"-like heal, 120s CD, 20s effect duration DefineSimpleConfig(res, Track.CollectiveUnconscious, "CollectiveUnconscious", "C.Uncon", 100, AST.AID.CollectiveUnconscious, 5); //AoE oGCD mit/regen, 60s CD, 5s mitigation / 15s regen effect durations DefineSimpleConfig(res, Track.CelestialOpposition, "CelestialOpposition", "C.Oppo", 100, AST.AID.CelestialOpposition, 15); //AoE oGCD heal/regen, 60s CD, 15s effect duration + + res.Define(Track.EarthlyStar).As("EarthlyStar", "E.Star", 200) //AoE GCD heal, 60s CD, 10s + 10s effect duration + .AddOption(StarOption.None, "None", "Do not use automatically") + .AddOption(StarOption.Use, "Earthly Star", "Use Earthly Star", 60, 10, ActionTargets.Hostile, 62) + .AddOption(StarOption.End, "Stellar Detonation", "Use Stellar Detonation", 0, 1, ActionTargets.Hostile, 62) + .AddAssociatedActions(AST.AID.EarthlyStar, AST.AID.StellarDetonation); + DefineSimpleConfig(res, Track.CelestialIntersection, "CelestialIntersection", "C.Inter", 100, AST.AID.CelestialIntersection, 30); //ST oGCD heal/shield, 30s CD (60s Total), 2 charges res.Define(Track.Horoscope).As("Horoscope", "Horo", 130) //Conditional AoE heal, 60s CD, 30s effect duration @@ -69,6 +77,16 @@ public override void Execute(StrategyValues strategy, Actor? primaryTarget, floa ExecuteSimple(strategy.Option(Track.Exaltation), AST.AID.Exaltation, TargetChoice(strategy.Option(Track.Exaltation)) ?? Player); ExecuteSimple(strategy.Option(Track.SunSign), AST.AID.SunSign, Player); + var star = strategy.Option(Track.EarthlyStar); + var starAction = star.As() switch + { + StarOption.Use => AST.AID.EarthlyStar, + StarOption.End => AST.AID.StellarDetonation, + _ => default + }; + if (starAction != default) + QueueOGCD(starAction, TargetChoice(star) ?? primaryTarget ?? Player); + //Aspected Helios full execution var heliosUp = HasEffect(Player, AST.SID.AspectedHelios, 15) || HasEffect(Player, AST.SID.HeliosConjunction, 15); var helios = strategy.Option(Track.AspectedHelios); @@ -79,7 +97,7 @@ public override void Execute(StrategyValues strategy, Actor? primaryTarget, floa _ => default }; if (heliosAction != default && !heliosUp) - Hints.ActionsToExecute.Push(ActionID.MakeSpell(heliosAction), Player, helios.Priority(), helios.Value.ExpireIn); + QueueGCD(heliosAction, Player); //Horoscope full execution var horo = strategy.Option(Track.Horoscope); @@ -90,7 +108,7 @@ public override void Execute(StrategyValues strategy, Actor? primaryTarget, floa _ => default }; if (horoAction != default) - Hints.ActionsToExecute.Push(ActionID.MakeSpell(horoAction), Player, horo.Priority(), horo.Value.ExpireIn); + QueueOGCD(horoAction, Player); var cosmos = strategy.Option(Track.Macrocosmos); var cosmosAction = cosmos.As() switch @@ -100,7 +118,66 @@ public override void Execute(StrategyValues strategy, Actor? primaryTarget, floa _ => default }; if (cosmosAction != default) - Hints.ActionsToExecute.Push(ActionID.MakeSpell(cosmosAction), Player, cosmos.Priority(), cosmos.Value.ExpireIn); + QueueOGCD(cosmosAction, primaryTarget); + } + + #region Core Execution Helpers + + public AST.AID NextGCD; //Next global cooldown action to be used + public void QueueGCD

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

(AST.AID aid, Actor? target, P priority, float delay = 0) where P : Enum + => QueueOGCD(aid, target, (int)(object)priority, delay); + + public void QueueOGCD(AST.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(AST.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 + } diff --git a/BossMod/Autorotation/Utility/ClassSCHUtility.cs b/BossMod/Autorotation/Utility/ClassSCHUtility.cs index ebf46a9300..4439158c12 100644 --- a/BossMod/Autorotation/Utility/ClassSCHUtility.cs +++ b/BossMod/Autorotation/Utility/ClassSCHUtility.cs @@ -114,7 +114,7 @@ public override void Execute(StrategyValues strategy, Actor? primaryTarget, floa _ => default }; if (soilAction != default) - QueueOGCD(soilAction, TargetChoice(soil) ?? primaryTarget); + QueueOGCD(soilAction, TargetChoice(soil) ?? primaryTarget ?? Player); var deploy = strategy.Option(Track.DeploymentTactics); if (deploy.As() != DeployOption.None) From 57e0c262659f1ba0168248e3291be9bb98a43a21 Mon Sep 17 00:00:00 2001 From: Akechi-kun <167795370+Akechi-kun@users.noreply.github.com> Date: Tue, 24 Dec 2024 19:12:45 -0800 Subject: [PATCH 21/29] NIN Shukuchi addition --- .../Autorotation/Utility/ClassDRGUtility.cs | 9 +- .../Autorotation/Utility/ClassNINUtility.cs | 83 ++++++++++++++++++- 2 files changed, 87 insertions(+), 5 deletions(-) diff --git a/BossMod/Autorotation/Utility/ClassDRGUtility.cs b/BossMod/Autorotation/Utility/ClassDRGUtility.cs index 343e0c0472..0d0f2bb20b 100644 --- a/BossMod/Autorotation/Utility/ClassDRGUtility.cs +++ b/BossMod/Autorotation/Utility/ClassDRGUtility.cs @@ -3,8 +3,7 @@ public sealed class ClassDRGUtility(RotationModuleManager manager, Actor player) : RoleMeleeUtility(manager, player) { public enum Track { WingedGlide = SharedTrack.Count } - public enum DashStrategy { None, GapClose } - public bool InMeleeRange(Actor? target) => Player.DistanceToHitbox(target) <= 3; //Checks if we're inside melee range + public enum DashStrategy { None, GapClose, GapCloseHold1 } public static readonly ActionID IDLimitBreak3 = ActionID.MakeSpell(DRG.AID.DragonsongDive); @@ -15,7 +14,8 @@ public static RotationModuleDefinition Definition() res.Define(Track.WingedGlide).As("Winged Glide", "Dash", 20) .AddOption(DashStrategy.None, "Automatic", "No use.") - .AddOption(DashStrategy.GapClose, "GapClose", "Use as gapcloser if outside melee range", 60, 0, ActionTargets.Hostile, 45) + .AddOption(DashStrategy.GapClose, "GapClose", "Use Winged Glide as gapcloser if outside melee range", 60, 0, ActionTargets.Hostile, 45) + .AddOption(DashStrategy.GapCloseHold1, "GapCloseHold1", "Use Winged Glide as gapcloser if outside melee range; conserves 1 charge for manual usage", 60, 0, ActionTargets.Hostile, 84) .AddAssociatedActions(DRG.AID.WingedGlide); return res; @@ -34,7 +34,8 @@ public override void Execute(StrategyValues strategy, Actor? primaryTarget, floa private bool ShouldUseDash(DashStrategy strategy, Actor? primaryTarget) => strategy switch { DashStrategy.None => false, - DashStrategy.GapClose => !InMeleeRange(primaryTarget), + DashStrategy.GapClose => Player.DistanceToHitbox(primaryTarget) is > 3 and <= 20, + DashStrategy.GapCloseHold1 => Player.DistanceToHitbox(primaryTarget) is > 3 and <= 20 && World.Client.Cooldowns[ActionDefinitions.Instance.Spell(DRG.AID.WingedGlide)!.MainCooldownGroup].Remaining <= 59.9f, _ => false, }; } diff --git a/BossMod/Autorotation/Utility/ClassNINUtility.cs b/BossMod/Autorotation/Utility/ClassNINUtility.cs index b3cea3fae2..7369dc67f0 100644 --- a/BossMod/Autorotation/Utility/ClassNINUtility.cs +++ b/BossMod/Autorotation/Utility/ClassNINUtility.cs @@ -2,7 +2,8 @@ public sealed class ClassNINUtility(RotationModuleManager manager, Actor player) : RoleMeleeUtility(manager, player) { - public enum Track { ShadeShift = SharedTrack.Count } + public enum Track { ShadeShift = SharedTrack.Count, Shukuchi } + public enum DashStrategy { None, GapClose, GapCloseHold1 } public static readonly ActionID IDLimitBreak3 = ActionID.MakeSpell(NIN.AID.Chimatsuri); @@ -13,6 +14,12 @@ public static RotationModuleDefinition Definition() DefineSimpleConfig(res, Track.ShadeShift, "Shade", "", 400, NIN.AID.ShadeShift, 20); + res.Define(Track.Shukuchi).As("Shukuchi", "Dash", 20) + .AddOption(DashStrategy.None, "Automatic", "No use.") + .AddOption(DashStrategy.GapClose, "GapClose", "Use Shukuchi as gapcloser if outside melee range of any target; will dash to target selected via CDPlanner. If none selected, will dash to current target or not at all if no target", 60, 0, ActionTargets.All, 40) + .AddOption(DashStrategy.GapCloseHold1, "GapCloseHold1", "Use Shukuchi as gapcloser if outside melee range; conserves 1 charge for manual usage", 60, 0, ActionTargets.All, 76) + .AddAssociatedActions(NIN.AID.Shukuchi); + return res; } @@ -20,5 +27,79 @@ public override void Execute(StrategyValues strategy, Actor? primaryTarget, floa { ExecuteShared(strategy, IDLimitBreak3, primaryTarget); ExecuteSimple(strategy.Option(Track.ShadeShift), NIN.AID.ShadeShift, Player); + + var dash = strategy.Option(Track.Shukuchi); + var dashTarget = ResolveTargetOverride(dash.Value) ?? primaryTarget; + var dashStrategy = strategy.Option(Track.Shukuchi).As(); + if (ShouldUseDash(dashStrategy, primaryTarget)) + QueueOGCD(NIN.AID.Shukuchi, dashTarget); + } + private bool ShouldUseDash(DashStrategy strategy, Actor? primaryTarget) => strategy switch + { + DashStrategy.None => false, + DashStrategy.GapClose => Player.DistanceToHitbox(primaryTarget) is > 3 and <= 20, + DashStrategy.GapCloseHold1 => Player.DistanceToHitbox(primaryTarget) is > 3 and <= 20 && World.Client.Cooldowns[ActionDefinitions.Instance.Spell(DRG.AID.WingedGlide)!.MainCooldownGroup].Remaining <= 59.9f, + _ => false, + }; + + #region Core Execution Helpers + + public NIN.AID NextGCD; //Next global cooldown action to be used + public void QueueGCD

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

(NIN.AID aid, Actor? target, P priority, float delay = 0) where P : Enum + => QueueOGCD(aid, target, (int)(object)priority, delay); + + public void QueueOGCD(NIN.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(NIN.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 + } From 6674d37eb01b2b8c938326bd32dda29c73b4c241 Mon Sep 17 00:00:00 2001 From: Akechi-kun <167795370+Akechi-kun@users.noreply.github.com> Date: Tue, 24 Dec 2024 21:26:05 -0800 Subject: [PATCH 22/29] SCH WIP --- BossMod/ActionQueue/Healers/SCH.cs | 1 + BossMod/Autorotation/akechi/AkechiSCH.cs | 404 +++++++++++++++++++++++ 2 files changed, 405 insertions(+) create mode 100644 BossMod/Autorotation/akechi/AkechiSCH.cs diff --git a/BossMod/ActionQueue/Healers/SCH.cs b/BossMod/ActionQueue/Healers/SCH.cs index b354287754..3a123bcab4 100644 --- a/BossMod/ActionQueue/Healers/SCH.cs +++ b/BossMod/ActionQueue/Healers/SCH.cs @@ -100,6 +100,7 @@ public enum SID : uint Sleep = 3, // applied by Repose to target BanefulImpaction = 3883, // applied by Baneful Impaction to target ImpactImminent = 3882, // applied by Chain Stratagem to self + ChainStratagem = 1221, // applied by Chain Stratagem to target //Shared Swiftcast = ClassShared.SID.Swiftcast, // applied by Swiftcast to self diff --git a/BossMod/Autorotation/akechi/AkechiSCH.cs b/BossMod/Autorotation/akechi/AkechiSCH.cs new file mode 100644 index 0000000000..da31a8a278 --- /dev/null +++ b/BossMod/Autorotation/akechi/AkechiSCH.cs @@ -0,0 +1,404 @@ +using FFXIVClientStructs.FFXIV.Client.Game.Gauge; +using AID = BossMod.SCH.AID; +using SID = BossMod.SCH.SID; +using TraitID = BossMod.SCH.TraitID; + +namespace BossMod.Autorotation.akechi; +//Contribution by Akechi +//Discord: @akechdz or 'Akechi' on Puni.sh for maintenance + +public sealed class AkechiSCH(RotationModuleManager manager, Actor player) : RotationModule(manager, player) +{ + #region Enums: Abilities / Strategies + //Abilities tracked for Cooldown Planner & Autorotation execution + public enum Track + { + AOE, //ST&AOE rotations tracking + DOT, //DOT abilities tracking + Potion, //Potion item tracking + ChainStratagem, //Chain Stratagem tracking + Aetherflow, //Aetherflow tracking + EnergyDrain, //Energy Drain tracking + } + + //Defines the strategy for using ST/AOE actions based on the current target selection and conditions + public enum AOEStrategy + { + Auto, //Automatically decide when to use ST or AOE abilities + Ruin2, //Force use of Ruin II only + Broil, //Force use of Broil only + ArtOfWar, //Force use of Art of War only + } + + //Defines different strategies for executing burst damage actions based on cooldown and resource availability + public enum DOTStrategy + { + Auto, //Automatically decide when to use damage-over-time abilities + Bio, //Force use of Bio (ST, 30s duration) + Bio2, //Force use of Bio II (ST, 30s duration) + Biolysis, //Force use of Biolysis (ST, 30s duration) + BioOpti, //Force use of Bio (ST, 30s duration) if target does not have DOT effect + Bio2Opti, //Force use of Bio II (ST, 30s duration) if target does not have DOT effect + BiolysisOpti, //Force use of Biolysis (ST, 30s duration) if target does not have DOT effect + BanefulImpaction, //Force use of Baneful Impaction (AOE, 15s duration) + Forbid, //Forbids the use of all abilities with a cooldown + } + + //Defines strategies for potion usage in combat, determining when and how to consume potions based on the situation + public enum PotionStrategy + { + Manual, //Manual potion usage + AlignWithRaidBuffs, //Align potion usage with raid buffs + Immediate //Use potions immediately when available + } + + //Defines different offensive strategies that dictate how abilities and resources are used during combat + 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 + + //Module Definitions + public static RotationModuleDefinition Definition() + { + var res = new RotationModuleDefinition("Akechi SCH", //Title + "Standard Rotation Module", //Description + "Standard rotation (Akechi)", //Category + "Akechi", //Contributor + RotationModuleQuality.WIP, //Quality + BitMask.Build((int)Class.SCH), //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.Ruin2, "Ruin II", "Force use of Ruin II only (instant cast ST, less DPS)", supportedTargets: ActionTargets.Hostile) + .AddOption(AOEStrategy.Broil, "Broil", "Force use of Broil only (hardcast ST, more DPS)", supportedTargets: ActionTargets.Hostile) + .AddOption(AOEStrategy.ArtOfWar, "Art of War", "Force use of Art of War only (instant cast AOE)", supportedTargets: ActionTargets.Hostile) + .AddAssociatedActions(AID.Ruin2, AID.Broil1, AID.Broil2, AID.Broil3, AID.Broil4, AID.ArtOfWar1, AID.ArtOfWar2); + res.Define(Track.DOT).As("Damage Over Time", "DOTs", uiPriority: 210) + .AddOption(DOTStrategy.Auto, "Allow", "Automatically decide when to use DoT abilities") + .AddOption(DOTStrategy.Bio, "Bio", "Force use of Bio (ST, 30s duration)", 0, 30, ActionTargets.Hostile, 2, 26) + .AddOption(DOTStrategy.Bio2, "Bio II", "Force use of Bio II (ST, 30s duration)", 0, 30, ActionTargets.Hostile, 26, 72) + .AddOption(DOTStrategy.Biolysis, "Biolysis", "Force use of Biolysis (ST, 30s duration)", 0, 30, ActionTargets.Hostile, 72) + .AddOption(DOTStrategy.BioOpti, "Bio", "Force use of Bio (ST, 30s duration) if target does not have DOT effect", 0, 30, ActionTargets.Hostile, 2, 26) + .AddOption(DOTStrategy.Bio2Opti, "Bio II", "Force use of Bio II (ST, 30s duration) if target does not have DOT effect", 0, 30, ActionTargets.Hostile, 26, 72) + .AddOption(DOTStrategy.BiolysisOpti, "Biolysis", "Force use of Biolysis (ST, 30s duration) if target does not have DOT effect", 0, 30, ActionTargets.Hostile, 72) + .AddOption(DOTStrategy.BanefulImpaction, "Baneful Impaction", "Force use of Baneful Impaction (AOE, 15s duration)", 0, 15, ActionTargets.Self, 92) + .AddOption(DOTStrategy.Forbid, "Forbid", "Forbid the use of all DoT abilities", 0, 0, ActionTargets.None) + .AddAssociatedActions(AID.Bio1, AID.Bio2, AID.Biolysis, AID.BanefulImpaction); + res.Define(Track.Potion).As("Potion", uiPriority: 20) + .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.IDPotionStr); + #endregion + + #region Offensive Strategies + res.Define(Track.ChainStratagem).As("Chain Stratagem", "C.Stratagem", uiPriority: 150) + .AddOption(OffensiveStrategy.Automatic, "Auto", "Normal use of Chain Stratagem") + .AddOption(OffensiveStrategy.Force, "Force", "Force use of Chain Stratagem", 120, 20, ActionTargets.Hostile, 66) + .AddOption(OffensiveStrategy.AnyWeave, "Any Weave", "Force use of Chain Stratagem in any next possible weave slot", 120, 20, ActionTargets.Hostile, 66) + .AddOption(OffensiveStrategy.EarlyWeave, "Early Weave", "Force use of Chain Stratagem in very next FIRST weave slot only", 120, 20, ActionTargets.Hostile, 66) + .AddOption(OffensiveStrategy.LateWeave, "Late Weave", "Force use of Chain Stratagem in very next LAST weave slot only", 120, 20, ActionTargets.Hostile, 66) + .AddOption(OffensiveStrategy.Delay, "Delay", "Delay use of Chain Stratagem", 0, 0, ActionTargets.None, 66) + .AddAssociatedActions(AID.ChainStratagem); + res.Define(Track.Aetherflow).As("Aetherflow", "A.flow", uiPriority: 160) + .AddOption(OffensiveStrategy.Automatic, "Auto", "Normal use of Aetherflow") + .AddOption(OffensiveStrategy.Force, "Force", "Force use of Aetherflow", 60, 10, ActionTargets.Self, 45) + .AddOption(OffensiveStrategy.AnyWeave, "Any Weave", "Force use of Aetherflow in any next possible weave slot", 60, 10, ActionTargets.Self, 45) + .AddOption(OffensiveStrategy.EarlyWeave, "Early Weave", "Force use of Aetherflow in very next FIRST weave slot only", 60, 10, ActionTargets.Self, 45) + .AddOption(OffensiveStrategy.LateWeave, "Late Weave", "Force use of Aetherflow in very next LAST weave slot only", 60, 10, ActionTargets.Self, 45) + .AddOption(OffensiveStrategy.Delay, "Delay", "Delay use of Aetherflow", 0, 0, ActionTargets.None, 45) + .AddAssociatedActions(AID.Aetherflow); + res.Define(Track.EnergyDrain).As("Energy Drain", "E.Drain", uiPriority: 170) + .AddOption(OffensiveStrategy.Automatic, "Auto", "Normal use of Energy Drain") + .AddOption(OffensiveStrategy.Force, "Force", "Force use of Energy Drain", 60, 10, ActionTargets.Hostile, 45) + .AddOption(OffensiveStrategy.AnyWeave, "Any Weave", "Force use of Energy Drain in any next possible weave slot", 60, 10, ActionTargets.Hostile, 45) + .AddOption(OffensiveStrategy.EarlyWeave, "Early Weave", "Force use of Energy Drain in very next FIRST weave slot only", 60, 10, ActionTargets.Hostile, 45) + .AddOption(OffensiveStrategy.LateWeave, "Late Weave", "Force use of Energy Drain in very next LAST weave slot only", 60, 10, ActionTargets.Hostile, 45) + .AddOption(OffensiveStrategy.Delay, "Delay", "Delay use of Energy Drain", 0, 0, ActionTargets.None, 45) + .AddAssociatedActions(AID.EnergyDrain); + #endregion + + return res; + } + + #region Priorities + public enum GCDPriority //priorities for GCDs (higher number = higher priority) + { + None = 0, //default + Standard = 350, //combo actions + ForcedGCD = 900, //Forced GCDs + } + public enum OGCDPriority //priorities for oGCDs (higher number = higher priority) + { + None = 0, //default + Potion = 900, //Potion + ForcedOGCD = 900, //Forced oGCDs + } + #endregion + + #region Placeholders for Variables + //Cooldown Related + private bool canAF; //Checks if Aetherflow is completely available + private bool canED; //Checks if Energy Drain is completely available + private bool canCS; //Checks if Chain Stratagem is completely available + private float stratagemLeft; //Time left on Chain Stratagem (15s base) + private bool canZone; //Checks if Danger / Blasting Zone is completely available + private bool ShouldUseAOE; //Checks if AOE rotation should be used + //Misc + 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 (needed for cartridge management) + private GCDPriority NextGCDPrio; //Priority of the next GCD, used for decision making on cooldowns + #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 AID ComboLastMove => (AID)World.Client.ComboState.Action; //Get the last action used in the combo sequence + private bool In20y(Actor? target) => Player.DistanceToHitbox(target) <= 19.9f; //Check if the target is within ST range + private bool In5y(Actor? target) => Player.DistanceToHitbox(target) <= 4.99f; //Check if the target is within AOE range + 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 + + //TODO: try new things... + //public bool JustDid(AID aid) => Manager?.LastCast.Data?.IsSpell(aid) ?? false; //Check if the last action used was the desired ability + //public bool DidWithin(float variance) => (World.CurrentTime - Manager.LastCast.Time).TotalSeconds <= variance; //Check if the last action was used within a certain timeframe + //public bool JustUsed(AID aid, float variance) => JustDid(aid) && DidWithin(variance); //Check if the last action used was the desired ability & was used within a certain timeframe + #endregion + + #region Upgrade Paths + private AID BestBroil //Determine the best Broil to use + => Unlocked(AID.Broil4) //If Broil IV is unlocked + ? AID.Broil4 //Use Broil IV + : Unlocked(AID.Broil3) //Otherwise, if Broil III is unlocked + ? AID.Broil3 //Use Broil III + : Unlocked(AID.Broil2) //Otherwise, if Broil II is unlocked + ? AID.Broil2 //Use Broil II + : AID.Broil1; //Otherwise, default to Broil I + + private AID BestRuin //Determine the best Ruin to use + => Unlocked(AID.Ruin2) //Otherwise, if Ruin II is unlocked + ? AID.Ruin2 //Use Ruin II + : AID.Ruin1; //Otherwise, default to Ruin I + + private AID BestBio //Determine the best DOT to use + => Unlocked(AID.Biolysis) //If Biolysis is unlocked + ? AID.Biolysis //Use Biolysis + : Unlocked(AID.Bio2) //Otherwise, if Bio II is unlocked + ? AID.Bio2 //Use Bio II + : AID.Bio1; //Otherwise, default to Bio I + + private SID BestDOT //Determine the best DOT to use + => Unlocked(AID.Biolysis) //If Biolysis is unlocked + ? SID.Biolysis //Use Biolysis + : Unlocked(AID.Bio2) //Otherwise, if Bio II is unlocked + ? SID.Bio2 //Use Bio II + : SID.Bio1; //Otherwise, default to Bio I + + private AID BestST //Determine the best ST to use + => Unlocked(AID.Broil1) //If Broil I is unlocked + ? BestBroil //Use the best Broil + : BestRuin; //Otherwise, default to best Ruin + + private AID BestAOE //Determine the best AOE to use + => Unlocked(AID.ArtOfWar2) //If Art of War II is unlocked + ? AID.ArtOfWar2 //Use Art of War II + : AID.ArtOfWar1; //Otherwise, default to Art of War + + + #endregion + + public override void Execute(StrategyValues strategy, Actor? primaryTarget, float estimatedAnimLockDelay, bool isMoving) //Executes our actions + { + #region Variables + //Gauge + var gauge = World.Client.GetGauge(); //Retrieve Scholar gauge + var seraphLeft = gauge.SeraphTimer; //Current cartridges + var seraphUp = seraphLeft > 0; //Checks if Seraph is active + var FairyGauge = gauge.FairyGauge; //Current Fairy Gauge (max: 100) + var aetherflowStacks = gauge.Aetherflow; //Current Aetherflow stacks (max: 3) + var hasAetherflow = aetherflowStacks > 0; //Checks if Aetherflow is available + var hasFairy = gauge.DismissedFairy == 0; //Checks if Fairy is present + var bioLeft = StatusDetails(primaryTarget, BestDOT, Player.InstanceID).Left; + stratagemLeft = StatusDetails(primaryTarget, SID.ChainStratagem, Player.InstanceID).Left; + canCS = ActionReady(AID.ChainStratagem); //ChainStratagem is available + canED = Unlocked(AID.EnergyDrain) && hasAetherflow; //Energy Drain is available + canAF = ActionReady(AID.Aetherflow) && !hasAetherflow; //Aetherflow is available + 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 skill speed and haste + NextGCD = AID.None; //Next global cooldown action to be used + NextGCDPrio = GCDPriority.None; //Priority of the next GCD, used for decision making on cooldowns + PotionLeft = PotionStatusLeft(); //Remaining time for potion buff (30s) + ShouldUseAOE = TargetsInAOERange() > 1; //otherwise, use AOE if 2+ targets would be hit + var downtimeIn = Manager.Planner?.EstimateTimeToNextDowntime().Item2 ?? float.MaxValue; //Time until next downtime + + #region Strategy Definitions + var AOE = strategy.Option(Track.AOE); //AOE track + var AOEStrategy = AOE.As(); //AOE strategy + var DOT = strategy.Option(Track.DOT); //DOT track + var DOTStrategy = DOT.As(); //DOT strategy + var potion = strategy.Option(Track.Potion).As(); //Potion strategy + var chainStrategy = strategy.Option(Track.ChainStratagem).As(); //Chain Stratagem strategy + var aetherflow = strategy.Option(Track.Aetherflow).As(); //Aetherflow strategy + var energyDrain = strategy.Option(Track.EnergyDrain).As(); //Energy Drain strategy + #endregion + + #endregion + + #region Force Execution + if (AOEStrategy is AOEStrategy.Ruin2) + QueueGCD(BestRuin, ResolveTargetOverride(AOE.Value) ?? primaryTarget, GCDPriority.ForcedGCD); + if (AOEStrategy is AOEStrategy.Broil) + QueueGCD(BestBroil, ResolveTargetOverride(AOE.Value) ?? primaryTarget, GCDPriority.ForcedGCD); + if (AOEStrategy is AOEStrategy.ArtOfWar) + QueueGCD(BestAOE, Player, GCDPriority.ForcedGCD); + if (DOTStrategy is DOTStrategy.Bio) + QueueGCD(AID.Bio1, ResolveTargetOverride(DOT.Value) ?? primaryTarget, GCDPriority.ForcedGCD); + if (DOTStrategy is DOTStrategy.Bio2) + QueueGCD(AID.Bio2, ResolveTargetOverride(DOT.Value) ?? primaryTarget, GCDPriority.ForcedGCD); + if (DOTStrategy is DOTStrategy.Biolysis) + QueueGCD(AID.Biolysis, ResolveTargetOverride(DOT.Value) ?? primaryTarget, GCDPriority.ForcedGCD); + if (DOTStrategy is DOTStrategy.BanefulImpaction && PlayerHasEffect(SID.ImpactImminent, 30)) + QueueGCD(AID.BanefulImpaction, Player, GCDPriority.ForcedGCD); + #endregion + + #region Standard Execution + if (AOEStrategy == AOEStrategy.Auto) + { + if (ShouldUseAOE) + QueueGCD(BestAOE, Player, GCDPriority.Standard); + if (!ShouldUseAOE) + QueueGCD(BestST, ResolveTargetOverride(AOE.Value) ?? primaryTarget, GCDPriority.Standard); + } + if (DOTStrategy == DOTStrategy.Auto) + { + if (bioLeft <= 3) + QueueGCD(BestBio, ResolveTargetOverride(DOT.Value) ?? primaryTarget, GCDPriority.Standard); + if (PlayerHasEffect(SID.ImpactImminent, 30)) + QueueGCD(AID.BanefulImpaction, Player, GCDPriority.ForcedGCD); + } + if (ShouldUseChainStratagem(primaryTarget, chainStrategy)) + QueueGCD(AID.ChainStratagem, primaryTarget, GCDPriority.Standard); + if (ShouldUseAetherflow(primaryTarget, aetherflow)) + QueueGCD(AID.Aetherflow, Player, GCDPriority.Standard); + if (ShouldUseEnergyDrain(primaryTarget, energyDrain)) + QueueGCD(AID.EnergyDrain, ResolveTargetOverride(strategy.Option(Track.EnergyDrain).Value) ?? primaryTarget, GCDPriority.Standard); + #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, 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 + + + #region Cooldown Helpers + private bool ShouldUseChainStratagem(Actor? target, OffensiveStrategy strategy) => strategy switch + { + OffensiveStrategy.Automatic => Player.InCombat && target != null && canCS && stratagemLeft == 0, + OffensiveStrategy.Force => canCS, + OffensiveStrategy.AnyWeave => canCS && canWeaveIn, + OffensiveStrategy.EarlyWeave => canCS && canWeaveEarly, + OffensiveStrategy.LateWeave => canCS && canWeaveLate, + OffensiveStrategy.Delay => false, + _ => false + }; + + private bool ShouldUseAetherflow(Actor? target, OffensiveStrategy strategy) => strategy switch + { + OffensiveStrategy.Automatic => Player.InCombat && target != null && canAF, + OffensiveStrategy.Force => canAF, + OffensiveStrategy.AnyWeave => canAF && canWeaveIn, + OffensiveStrategy.EarlyWeave => canAF && canWeaveEarly, + OffensiveStrategy.LateWeave => canAF && canWeaveLate, + OffensiveStrategy.Delay => false, + _ => false + }; + + private bool ShouldUseEnergyDrain(Actor? target, OffensiveStrategy strategy) => strategy switch + { + OffensiveStrategy.Automatic => Player.InCombat && target != null && canED, + OffensiveStrategy.Force => canED, + OffensiveStrategy.AnyWeave => canED && canWeaveIn, + OffensiveStrategy.EarlyWeave => canED && canWeaveEarly, + OffensiveStrategy.LateWeave => canED && canWeaveLate, + OffensiveStrategy.Delay => false, + _ => false + }; + #endregion +} From 3f6c88d037c3f3839baef0571a5f2422fb9fb669 Mon Sep 17 00:00:00 2001 From: Akechi-kun <167795370+Akechi-kun@users.noreply.github.com> Date: Wed, 25 Dec 2024 01:53:26 -0800 Subject: [PATCH 23/29] AkechiSCH done --- BossMod/ActionQueue/Healers/SCH.cs | 9 +- .../ActionTweaks/ClassActions/SCHConfig.cs | 8 + BossMod/Autorotation/akechi/AkechiSCH.cs | 161 +++++++++--------- 3 files changed, 95 insertions(+), 83 deletions(-) create mode 100644 BossMod/ActionTweaks/ClassActions/SCHConfig.cs diff --git a/BossMod/ActionQueue/Healers/SCH.cs b/BossMod/ActionQueue/Healers/SCH.cs index 3a123bcab4..37caea13d8 100644 --- a/BossMod/ActionQueue/Healers/SCH.cs +++ b/BossMod/ActionQueue/Healers/SCH.cs @@ -101,6 +101,8 @@ public enum SID : uint BanefulImpaction = 3883, // applied by Baneful Impaction to target ImpactImminent = 3882, // applied by Chain Stratagem to self ChainStratagem = 1221, // applied by Chain Stratagem to target + FeyUnion = 1222, // applied by Aetherpact to target + Seraphism = 3884, // applied by Seraphism to self //Shared Swiftcast = ClassShared.SID.Swiftcast, // applied by Swiftcast to self @@ -108,6 +110,8 @@ public enum SID : uint public sealed class Definitions : IDisposable { + private readonly SCHConfig _config = Service.Config.Get(); + public Definitions(ActionDefinitions d) { d.RegisterSpell(AID.AngelFeathers, castAnimLock: 8.10f); // animLock=8.100s? @@ -160,6 +164,9 @@ public void Dispose() { } private void Customize(ActionDefinitions d) { - // *** add any properties that can't be autogenerated here *** + d.Spell(AID.Broil1)!.ForbidExecute = (ws, player, _, _) => _config.ForbidEarlyBroil && !player.InCombat && ws.Client.CountdownRemaining > 1.5f; + d.Spell(AID.Broil2)!.ForbidExecute = (ws, player, _, _) => _config.ForbidEarlyBroil && !player.InCombat && ws.Client.CountdownRemaining > 1.5f; + d.Spell(AID.Broil3)!.ForbidExecute = (ws, player, _, _) => _config.ForbidEarlyBroil && !player.InCombat && ws.Client.CountdownRemaining > 1.5f; + d.Spell(AID.Broil4)!.ForbidExecute = (ws, player, _, _) => _config.ForbidEarlyBroil && !player.InCombat && ws.Client.CountdownRemaining > 1.5f; } } diff --git a/BossMod/ActionTweaks/ClassActions/SCHConfig.cs b/BossMod/ActionTweaks/ClassActions/SCHConfig.cs new file mode 100644 index 0000000000..4df944817e --- /dev/null +++ b/BossMod/ActionTweaks/ClassActions/SCHConfig.cs @@ -0,0 +1,8 @@ +namespace BossMod; + +[ConfigDisplay(Parent = typeof(ActionTweaksConfig))] +class SCHConfig : ConfigNode +{ + [PropertyDisplay("Prevent use of 'Broil' too early when in pre-pull")] + public bool ForbidEarlyBroil = true; +} diff --git a/BossMod/Autorotation/akechi/AkechiSCH.cs b/BossMod/Autorotation/akechi/AkechiSCH.cs index da31a8a278..70c6852a6e 100644 --- a/BossMod/Autorotation/akechi/AkechiSCH.cs +++ b/BossMod/Autorotation/akechi/AkechiSCH.cs @@ -1,7 +1,6 @@ using FFXIVClientStructs.FFXIV.Client.Game.Gauge; using AID = BossMod.SCH.AID; using SID = BossMod.SCH.SID; -using TraitID = BossMod.SCH.TraitID; namespace BossMod.Autorotation.akechi; //Contribution by Akechi @@ -10,18 +9,15 @@ namespace BossMod.Autorotation.akechi; public sealed class AkechiSCH(RotationModuleManager manager, Actor player) : RotationModule(manager, player) { #region Enums: Abilities / Strategies - //Abilities tracked for Cooldown Planner & Autorotation execution public enum Track { AOE, //ST&AOE rotations tracking - DOT, //DOT abilities tracking + DOT, //DOT abilities tracking Potion, //Potion item tracking ChainStratagem, //Chain Stratagem tracking Aetherflow, //Aetherflow tracking EnergyDrain, //Energy Drain tracking } - - //Defines the strategy for using ST/AOE actions based on the current target selection and conditions public enum AOEStrategy { Auto, //Automatically decide when to use ST or AOE abilities @@ -29,8 +25,6 @@ public enum AOEStrategy Broil, //Force use of Broil only ArtOfWar, //Force use of Art of War only } - - //Defines different strategies for executing burst damage actions based on cooldown and resource availability public enum DOTStrategy { Auto, //Automatically decide when to use damage-over-time abilities @@ -43,16 +37,12 @@ public enum DOTStrategy BanefulImpaction, //Force use of Baneful Impaction (AOE, 15s duration) Forbid, //Forbids the use of all abilities with a cooldown } - - //Defines strategies for potion usage in combat, determining when and how to consume potions based on the situation public enum PotionStrategy { Manual, //Manual potion usage AlignWithRaidBuffs, //Align potion usage with raid buffs Immediate //Use potions immediately when available } - - //Defines different offensive strategies that dictate how abilities and resources are used during combat public enum OffensiveStrategy { Automatic, //Automatically decide when to use off-global offensive abilities @@ -64,14 +54,13 @@ public enum OffensiveStrategy } #endregion - //Module Definitions public static RotationModuleDefinition Definition() { var res = new RotationModuleDefinition("Akechi SCH", //Title "Standard Rotation Module", //Description "Standard rotation (Akechi)", //Category "Akechi", //Contributor - RotationModuleQuality.WIP, //Quality + RotationModuleQuality.Basic, //Quality BitMask.Build((int)Class.SCH), //Job 100); //Level supported @@ -82,18 +71,18 @@ public static RotationModuleDefinition Definition() .AddOption(AOEStrategy.Broil, "Broil", "Force use of Broil only (hardcast ST, more DPS)", supportedTargets: ActionTargets.Hostile) .AddOption(AOEStrategy.ArtOfWar, "Art of War", "Force use of Art of War only (instant cast AOE)", supportedTargets: ActionTargets.Hostile) .AddAssociatedActions(AID.Ruin2, AID.Broil1, AID.Broil2, AID.Broil3, AID.Broil4, AID.ArtOfWar1, AID.ArtOfWar2); - res.Define(Track.DOT).As("Damage Over Time", "DOTs", uiPriority: 210) + res.Define(Track.DOT).As("Damage Over Time", "DOTs", uiPriority: 190) .AddOption(DOTStrategy.Auto, "Allow", "Automatically decide when to use DoT abilities") .AddOption(DOTStrategy.Bio, "Bio", "Force use of Bio (ST, 30s duration)", 0, 30, ActionTargets.Hostile, 2, 26) .AddOption(DOTStrategy.Bio2, "Bio II", "Force use of Bio II (ST, 30s duration)", 0, 30, ActionTargets.Hostile, 26, 72) .AddOption(DOTStrategy.Biolysis, "Biolysis", "Force use of Biolysis (ST, 30s duration)", 0, 30, ActionTargets.Hostile, 72) - .AddOption(DOTStrategy.BioOpti, "Bio", "Force use of Bio (ST, 30s duration) if target does not have DOT effect", 0, 30, ActionTargets.Hostile, 2, 26) - .AddOption(DOTStrategy.Bio2Opti, "Bio II", "Force use of Bio II (ST, 30s duration) if target does not have DOT effect", 0, 30, ActionTargets.Hostile, 26, 72) - .AddOption(DOTStrategy.BiolysisOpti, "Biolysis", "Force use of Biolysis (ST, 30s duration) if target does not have DOT effect", 0, 30, ActionTargets.Hostile, 72) + .AddOption(DOTStrategy.BioOpti, "Bio Opti", "Force use of Bio (ST, 30s duration) if target does NOT have DOT effect", 0, 30, ActionTargets.Hostile, 2, 26) + .AddOption(DOTStrategy.Bio2Opti, "Bio II Opti", "Force use of Bio II (ST, 30s duration) if target does NOT have DOT effect", 0, 30, ActionTargets.Hostile, 26, 72) + .AddOption(DOTStrategy.BiolysisOpti, "Biolysis Opti", "Force use of Biolysis (ST, 30s duration) if target does NOT have DOT effect", 0, 30, ActionTargets.Hostile, 72) .AddOption(DOTStrategy.BanefulImpaction, "Baneful Impaction", "Force use of Baneful Impaction (AOE, 15s duration)", 0, 15, ActionTargets.Self, 92) .AddOption(DOTStrategy.Forbid, "Forbid", "Forbid the use of all DoT abilities", 0, 0, ActionTargets.None) .AddAssociatedActions(AID.Bio1, AID.Bio2, AID.Biolysis, AID.BanefulImpaction); - res.Define(Track.Potion).As("Potion", uiPriority: 20) + 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) @@ -101,7 +90,7 @@ public static RotationModuleDefinition Definition() #endregion #region Offensive Strategies - res.Define(Track.ChainStratagem).As("Chain Stratagem", "C.Stratagem", uiPriority: 150) + res.Define(Track.ChainStratagem).As("Chain Stratagem", "Stratagem", uiPriority: 170) .AddOption(OffensiveStrategy.Automatic, "Auto", "Normal use of Chain Stratagem") .AddOption(OffensiveStrategy.Force, "Force", "Force use of Chain Stratagem", 120, 20, ActionTargets.Hostile, 66) .AddOption(OffensiveStrategy.AnyWeave, "Any Weave", "Force use of Chain Stratagem in any next possible weave slot", 120, 20, ActionTargets.Hostile, 66) @@ -117,7 +106,7 @@ public static RotationModuleDefinition Definition() .AddOption(OffensiveStrategy.LateWeave, "Late Weave", "Force use of Aetherflow in very next LAST weave slot only", 60, 10, ActionTargets.Self, 45) .AddOption(OffensiveStrategy.Delay, "Delay", "Delay use of Aetherflow", 0, 0, ActionTargets.None, 45) .AddAssociatedActions(AID.Aetherflow); - res.Define(Track.EnergyDrain).As("Energy Drain", "E.Drain", uiPriority: 170) + res.Define(Track.EnergyDrain).As("Energy Drain", "E.Drain", uiPriority: 150) .AddOption(OffensiveStrategy.Automatic, "Auto", "Normal use of Energy Drain") .AddOption(OffensiveStrategy.Force, "Force", "Force use of Energy Drain", 60, 10, ActionTargets.Hostile, 45) .AddOption(OffensiveStrategy.AnyWeave, "Any Weave", "Force use of Energy Drain in any next possible weave slot", 60, 10, ActionTargets.Hostile, 45) @@ -133,27 +122,29 @@ public static RotationModuleDefinition Definition() #region Priorities public enum GCDPriority //priorities for GCDs (higher number = higher priority) { - None = 0, //default - Standard = 350, //combo actions - ForcedGCD = 900, //Forced GCDs + 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 = 900, //Potion - ForcedOGCD = 900, //Forced oGCDs + None = 0, //default + EnergyDrain = 300, //Energy Drain + Aetherflow = 400, //Aetherflow + ChainStratagem = 500, //Chain Stratagem + Potion = 800, //Potion + ForcedOGCD = 900, //Forced oGCDs } #endregion #region Placeholders for Variables - //Cooldown Related private bool canAF; //Checks if Aetherflow is completely available private bool canED; //Checks if Energy Drain is completely available private bool canCS; //Checks if Chain Stratagem is completely available + private float bioLeft; //Time left on DOT effect (30s base) private float stratagemLeft; //Time left on Chain Stratagem (15s base) - private bool canZone; //Checks if Danger / Blasting Zone is completely available - private bool ShouldUseAOE; //Checks if AOE rotation should be used - //Misc + 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 @@ -165,25 +156,17 @@ public static RotationModuleDefinition Definition() 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 (needed for cartridge management) - private GCDPriority NextGCDPrio; //Priority of the next GCD, used for decision making on cooldowns #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 AID ComboLastMove => (AID)World.Client.ComboState.Action; //Get the last action used in the combo sequence private bool In20y(Actor? target) => Player.DistanceToHitbox(target) <= 19.9f; //Check if the target is within ST range - private bool In5y(Actor? target) => Player.DistanceToHitbox(target) <= 4.99f; //Check if the target is within AOE range + 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 - - //TODO: try new things... - //public bool JustDid(AID aid) => Manager?.LastCast.Data?.IsSpell(aid) ?? false; //Check if the last action used was the desired ability - //public bool DidWithin(float variance) => (World.CurrentTime - Manager.LastCast.Time).TotalSeconds <= variance; //Check if the last action was used within a certain timeframe - //public bool JustUsed(AID aid, float variance) => JustDid(aid) && DidWithin(variance); //Check if the last action used was the desired ability & was used within a certain timeframe #endregion #region Upgrade Paths @@ -225,7 +208,6 @@ private AID BestAOE //Determine the best AOE to use ? AID.ArtOfWar2 //Use Art of War II : AID.ArtOfWar1; //Otherwise, default to Art of War - #endregion public override void Execute(StrategyValues strategy, Actor? primaryTarget, float estimatedAnimLockDelay, bool isMoving) //Executes our actions @@ -233,15 +215,11 @@ public override void Execute(StrategyValues strategy, Actor? primaryTarget, floa #region Variables //Gauge var gauge = World.Client.GetGauge(); //Retrieve Scholar gauge - var seraphLeft = gauge.SeraphTimer; //Current cartridges - var seraphUp = seraphLeft > 0; //Checks if Seraph is active - var FairyGauge = gauge.FairyGauge; //Current Fairy Gauge (max: 100) var aetherflowStacks = gauge.Aetherflow; //Current Aetherflow stacks (max: 3) var hasAetherflow = aetherflowStacks > 0; //Checks if Aetherflow is available - var hasFairy = gauge.DismissedFairy == 0; //Checks if Fairy is present - var bioLeft = StatusDetails(primaryTarget, BestDOT, Player.InstanceID).Left; + bioLeft = StatusDetails(primaryTarget, BestDOT, Player.InstanceID).Left; stratagemLeft = StatusDetails(primaryTarget, SID.ChainStratagem, Player.InstanceID).Left; - canCS = ActionReady(AID.ChainStratagem); //ChainStratagem is available + canCS = ActionReady(AID.ChainStratagem); //Chain Stratagem is available canED = Unlocked(AID.EnergyDrain) && hasAetherflow; //Energy Drain is available canAF = ActionReady(AID.Aetherflow) && !hasAetherflow; //Aetherflow is available canWeaveIn = GCD is <= 2.5f and >= 0.1f; //Can weave in oGCDs @@ -249,10 +227,8 @@ 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 skill speed and haste NextGCD = AID.None; //Next global cooldown action to be used - NextGCDPrio = GCDPriority.None; //Priority of the next GCD, used for decision making on cooldowns PotionLeft = PotionStatusLeft(); //Remaining time for potion buff (30s) ShouldUseAOE = TargetsInAOERange() > 1; //otherwise, use AOE if 2+ targets would be hit - var downtimeIn = Manager.Planner?.EstimateTimeToNextDowntime().Item2 ?? float.MaxValue; //Time until next downtime #region Strategy Definitions var AOE = strategy.Option(Track.AOE); //AOE track @@ -260,56 +236,65 @@ public override void Execute(StrategyValues strategy, Actor? primaryTarget, floa var DOT = strategy.Option(Track.DOT); //DOT track var DOTStrategy = DOT.As(); //DOT strategy var potion = strategy.Option(Track.Potion).As(); //Potion strategy - var chainStrategy = strategy.Option(Track.ChainStratagem).As(); //Chain Stratagem strategy - var aetherflow = strategy.Option(Track.Aetherflow).As(); //Aetherflow strategy - var energyDrain = strategy.Option(Track.EnergyDrain).As(); //Energy Drain strategy + var cs = strategy.Option(Track.ChainStratagem); //Chain Stratagem track + var csStrat = cs.As(); //Chain Stratagem strategy + var af = strategy.Option(Track.Aetherflow); //Aetherflow track + var afStrat = af.As(); //Aetherflow strategy + var ed = strategy.Option(Track.EnergyDrain); //Energy Drain track + var edStrat = ed.As(); //Energy Drain strategy #endregion #endregion #region Force Execution - if (AOEStrategy is AOEStrategy.Ruin2) - QueueGCD(BestRuin, ResolveTargetOverride(AOE.Value) ?? primaryTarget, GCDPriority.ForcedGCD); - if (AOEStrategy is AOEStrategy.Broil) - QueueGCD(BestBroil, ResolveTargetOverride(AOE.Value) ?? primaryTarget, GCDPriority.ForcedGCD); - if (AOEStrategy is AOEStrategy.ArtOfWar) - QueueGCD(BestAOE, Player, GCDPriority.ForcedGCD); - if (DOTStrategy is DOTStrategy.Bio) - QueueGCD(AID.Bio1, ResolveTargetOverride(DOT.Value) ?? primaryTarget, GCDPriority.ForcedGCD); - if (DOTStrategy is DOTStrategy.Bio2) - QueueGCD(AID.Bio2, ResolveTargetOverride(DOT.Value) ?? primaryTarget, GCDPriority.ForcedGCD); - if (DOTStrategy is DOTStrategy.Biolysis) - QueueGCD(AID.Biolysis, ResolveTargetOverride(DOT.Value) ?? primaryTarget, GCDPriority.ForcedGCD); - if (DOTStrategy is DOTStrategy.BanefulImpaction && PlayerHasEffect(SID.ImpactImminent, 30)) - QueueGCD(AID.BanefulImpaction, Player, GCDPriority.ForcedGCD); + if (In20y(primaryTarget)) + { + if (AOEStrategy is AOEStrategy.Ruin2) + QueueGCD(BestRuin, ResolveTargetOverride(AOE.Value) ?? primaryTarget, GCDPriority.ForcedGCD); + if (AOEStrategy is AOEStrategy.Broil) + QueueGCD(BestBroil, ResolveTargetOverride(AOE.Value) ?? primaryTarget, GCDPriority.ForcedGCD); + if (AOEStrategy is AOEStrategy.ArtOfWar) + QueueGCD(BestAOE, Player, GCDPriority.ForcedGCD); + } + if (In25y(primaryTarget)) + { + if (DOTStrategy is DOTStrategy.BanefulImpaction && + PlayerHasEffect(SID.ImpactImminent, 30)) + QueueGCD(AID.BanefulImpaction, ResolveTargetOverride(DOT.Value) ?? primaryTarget, GCDPriority.ForcedGCD); + } #endregion #region Standard Execution if (AOEStrategy == AOEStrategy.Auto) { + var STtarget = ResolveTargetOverride(AOE.Value) ?? primaryTarget; if (ShouldUseAOE) QueueGCD(BestAOE, Player, GCDPriority.Standard); - if (!ShouldUseAOE) - QueueGCD(BestST, ResolveTargetOverride(AOE.Value) ?? primaryTarget, GCDPriority.Standard); + if (In25y(STtarget) && + (!ShouldUseAOE || IsFirstGCD())) + QueueGCD(BestST, STtarget, GCDPriority.Standard); } + if (ShouldUseDOTs(primaryTarget, DOTStrategy)) + QueueGCD(BestBio, ResolveTargetOverride(DOT.Value) ?? primaryTarget, GCDPriority.ForcedGCD); + if (DOTStrategy == DOTStrategy.Auto) { - if (bioLeft <= 3) - QueueGCD(BestBio, ResolveTargetOverride(DOT.Value) ?? primaryTarget, GCDPriority.Standard); if (PlayerHasEffect(SID.ImpactImminent, 30)) - QueueGCD(AID.BanefulImpaction, Player, GCDPriority.ForcedGCD); + QueueGCD(AID.BanefulImpaction, ResolveTargetOverride(DOT.Value) ?? primaryTarget, GCDPriority.Standard + 15); } - if (ShouldUseChainStratagem(primaryTarget, chainStrategy)) - QueueGCD(AID.ChainStratagem, primaryTarget, GCDPriority.Standard); - if (ShouldUseAetherflow(primaryTarget, aetherflow)) - QueueGCD(AID.Aetherflow, Player, GCDPriority.Standard); - if (ShouldUseEnergyDrain(primaryTarget, energyDrain)) - QueueGCD(AID.EnergyDrain, ResolveTargetOverride(strategy.Option(Track.EnergyDrain).Value) ?? primaryTarget, GCDPriority.Standard); + if (ShouldUseChainStratagem(primaryTarget, csStrat)) + QueueGCD(AID.ChainStratagem, ResolveTargetOverride(cs.Value) ?? primaryTarget, GCDPriority.Standard + 500); + if (ShouldUseAetherflow(primaryTarget, afStrat)) + QueueGCD(AID.Aetherflow, Player, GCDPriority.Standard + 400); + if (ShouldUseEnergyDrain(primaryTarget, edStrat)) + QueueGCD(AID.EnergyDrain, ResolveTargetOverride(ed.Value) ?? primaryTarget, GCDPriority.Standard + 300); + if (potion is PotionStrategy.AlignWithRaidBuffs && CD(AID.ChainStratagem) < 5 || + potion is PotionStrategy.Immediate) + Hints.ActionsToExecute.Push(ActionDefinitions.IDPotionMnd, Player, ActionQueue.Priority.VeryHigh + (int)OGCDPriority.ForcedOGCD, 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); @@ -320,7 +305,7 @@ public void QueueGCD(AID aid, Actor? target, int priority = 8, float delay = 0) if (priority == 0) return; - if (QueueAction(aid, target, ActionQueue.Priority.High, delay) && priority > NextGCDPrio) + if (QueueAction(aid, target, ActionQueue.Priority.High + priority, delay) && priority > NextGCDPrio) { NextGCD = aid; } @@ -366,11 +351,23 @@ public bool QueueAction(AID aid, Actor? target, float priority, float delay) } #endregion - #region Cooldown Helpers + private bool ShouldUseDOTs(Actor? target, DOTStrategy strategy) => strategy switch + { + DOTStrategy.Auto => Player.InCombat && target != null && bioLeft <= 3 && In25y(target), + DOTStrategy.Bio => true, + DOTStrategy.Bio2 => true, + DOTStrategy.Biolysis => true, + DOTStrategy.BioOpti => bioLeft <= 3, + DOTStrategy.Bio2Opti => bioLeft <= 3, + DOTStrategy.BiolysisOpti => bioLeft <= 3, + DOTStrategy.BanefulImpaction => PlayerHasEffect(SID.ImpactImminent, 30), + DOTStrategy.Forbid => false, + _ => false + }; private bool ShouldUseChainStratagem(Actor? target, OffensiveStrategy strategy) => strategy switch { - OffensiveStrategy.Automatic => Player.InCombat && target != null && canCS && stratagemLeft == 0, + OffensiveStrategy.Automatic => Player.InCombat && target != null && canCS && canWeaveIn && stratagemLeft == 0 && In25y(target), OffensiveStrategy.Force => canCS, OffensiveStrategy.AnyWeave => canCS && canWeaveIn, OffensiveStrategy.EarlyWeave => canCS && canWeaveEarly, @@ -381,7 +378,7 @@ public bool QueueAction(AID aid, Actor? target, float priority, float delay) private bool ShouldUseAetherflow(Actor? target, OffensiveStrategy strategy) => strategy switch { - OffensiveStrategy.Automatic => Player.InCombat && target != null && canAF, + OffensiveStrategy.Automatic => Player.InCombat && target != null && canAF && canWeaveIn, OffensiveStrategy.Force => canAF, OffensiveStrategy.AnyWeave => canAF && canWeaveIn, OffensiveStrategy.EarlyWeave => canAF && canWeaveEarly, @@ -392,7 +389,7 @@ public bool QueueAction(AID aid, Actor? target, float priority, float delay) private bool ShouldUseEnergyDrain(Actor? target, OffensiveStrategy strategy) => strategy switch { - OffensiveStrategy.Automatic => Player.InCombat && target != null && canED, + OffensiveStrategy.Automatic => Player.InCombat && target != null && canED && canWeaveIn && In25y(target), OffensiveStrategy.Force => canED, OffensiveStrategy.AnyWeave => canED && canWeaveIn, OffensiveStrategy.EarlyWeave => canED && canWeaveEarly, From 1128228e210c98ee0d15e2e5c5b59034e0303671 Mon Sep 17 00:00:00 2001 From: Akechi-kun <167795370+Akechi-kun@users.noreply.github.com> Date: Wed, 25 Dec 2024 02:24:28 -0800 Subject: [PATCH 24/29] Energy Drain planning --- BossMod/Autorotation/akechi/AkechiSCH.cs | 56 +++++++++++++----------- 1 file changed, 31 insertions(+), 25 deletions(-) diff --git a/BossMod/Autorotation/akechi/AkechiSCH.cs b/BossMod/Autorotation/akechi/AkechiSCH.cs index 70c6852a6e..e6391f2315 100644 --- a/BossMod/Autorotation/akechi/AkechiSCH.cs +++ b/BossMod/Autorotation/akechi/AkechiSCH.cs @@ -14,9 +14,9 @@ public enum Track AOE, //ST&AOE rotations tracking DOT, //DOT abilities tracking Potion, //Potion item tracking + EnergyDrain, //Energy Drain tracking ChainStratagem, //Chain Stratagem tracking Aetherflow, //Aetherflow tracking - EnergyDrain, //Energy Drain tracking } public enum AOEStrategy { @@ -43,6 +43,13 @@ public enum PotionStrategy AlignWithRaidBuffs, //Align potion usage with raid buffs Immediate //Use potions immediately when available } + public enum EnergyStrategy + { + Use3, //Use all stacks of Aetherflow for Energy Drain + Use2, //Use 2 stacks of Aetherflow for Energy Drain + Use1, //Use 1 stack of Aetherflow for Energy Drain + Delay //Delay the use of Energy Drain + } public enum OffensiveStrategy { Automatic, //Automatically decide when to use off-global offensive abilities @@ -87,6 +94,12 @@ public static RotationModuleDefinition Definition() .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.IDPotionStr); + res.Define(Track.EnergyDrain).As("Energy Drain", "E.Drain", uiPriority: 150) + .AddOption(EnergyStrategy.Use3, "UseAll", "Uses all stacks of Aetherflow for Energy Drain; conserves no stacks for manual usage", 0, 0, ActionTargets.Hostile, 45) + .AddOption(EnergyStrategy.Use2, "Use2", "Uses 2 stacks of Aetherflow for Energy Drain; conserves 1 stack for manual usage", 0, 0, ActionTargets.Hostile, 45) + .AddOption(EnergyStrategy.Use1, "Use1", "Uses 1 stacks of Aetherflow for Energy Drain; conserves 1 stack for manual usage", 0, 0, ActionTargets.Hostile, 45) + .AddOption(EnergyStrategy.Delay, "Delay", "Delay use of Energy Drain", 0, 0, ActionTargets.None, 45) + .AddAssociatedActions(AID.EnergyDrain); #endregion #region Offensive Strategies @@ -106,14 +119,6 @@ public static RotationModuleDefinition Definition() .AddOption(OffensiveStrategy.LateWeave, "Late Weave", "Force use of Aetherflow in very next LAST weave slot only", 60, 10, ActionTargets.Self, 45) .AddOption(OffensiveStrategy.Delay, "Delay", "Delay use of Aetherflow", 0, 0, ActionTargets.None, 45) .AddAssociatedActions(AID.Aetherflow); - res.Define(Track.EnergyDrain).As("Energy Drain", "E.Drain", uiPriority: 150) - .AddOption(OffensiveStrategy.Automatic, "Auto", "Normal use of Energy Drain") - .AddOption(OffensiveStrategy.Force, "Force", "Force use of Energy Drain", 60, 10, ActionTargets.Hostile, 45) - .AddOption(OffensiveStrategy.AnyWeave, "Any Weave", "Force use of Energy Drain in any next possible weave slot", 60, 10, ActionTargets.Hostile, 45) - .AddOption(OffensiveStrategy.EarlyWeave, "Early Weave", "Force use of Energy Drain in very next FIRST weave slot only", 60, 10, ActionTargets.Hostile, 45) - .AddOption(OffensiveStrategy.LateWeave, "Late Weave", "Force use of Energy Drain in very next LAST weave slot only", 60, 10, ActionTargets.Hostile, 45) - .AddOption(OffensiveStrategy.Delay, "Delay", "Delay use of Energy Drain", 0, 0, ActionTargets.None, 45) - .AddAssociatedActions(AID.EnergyDrain); #endregion return res; @@ -139,6 +144,7 @@ public static RotationModuleDefinition Definition() #endregion #region Placeholders for Variables + private (int Stacks, bool IsActive) Aetherflow; //Current Aetherflow stacks (max: 3) private bool canAF; //Checks if Aetherflow is completely available private bool canED; //Checks if Energy Drain is completely available private bool canCS; //Checks if Chain Stratagem is completely available @@ -215,13 +221,13 @@ public override void Execute(StrategyValues strategy, Actor? primaryTarget, floa #region Variables //Gauge var gauge = World.Client.GetGauge(); //Retrieve Scholar gauge - var aetherflowStacks = gauge.Aetherflow; //Current Aetherflow stacks (max: 3) - var hasAetherflow = aetherflowStacks > 0; //Checks if Aetherflow is available + Aetherflow.Stacks = gauge.Aetherflow; //Current Aetherflow stacks + Aetherflow.IsActive = Aetherflow.Stacks > 0; //Checks if Aetherflow is available bioLeft = StatusDetails(primaryTarget, BestDOT, Player.InstanceID).Left; stratagemLeft = StatusDetails(primaryTarget, SID.ChainStratagem, Player.InstanceID).Left; canCS = ActionReady(AID.ChainStratagem); //Chain Stratagem is available - canED = Unlocked(AID.EnergyDrain) && hasAetherflow; //Energy Drain is available - canAF = ActionReady(AID.Aetherflow) && !hasAetherflow; //Aetherflow is available + canED = Unlocked(AID.EnergyDrain) && Aetherflow.IsActive; //Energy Drain is available + canAF = ActionReady(AID.Aetherflow) && !Aetherflow.IsActive; //Aetherflow is available 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 @@ -241,7 +247,7 @@ public override void Execute(StrategyValues strategy, Actor? primaryTarget, floa var af = strategy.Option(Track.Aetherflow); //Aetherflow track var afStrat = af.As(); //Aetherflow strategy var ed = strategy.Option(Track.EnergyDrain); //Energy Drain track - var edStrat = ed.As(); //Energy Drain strategy + var edStrat = ed.As(); //Energy Drain strategy #endregion #endregion @@ -272,7 +278,7 @@ public override void Execute(StrategyValues strategy, Actor? primaryTarget, floa QueueGCD(BestAOE, Player, GCDPriority.Standard); if (In25y(STtarget) && (!ShouldUseAOE || IsFirstGCD())) - QueueGCD(BestST, STtarget, GCDPriority.Standard); + QueueGCD(isMoving ? BestRuin : BestST, STtarget, GCDPriority.Standard); } if (ShouldUseDOTs(primaryTarget, DOTStrategy)) QueueGCD(BestBio, ResolveTargetOverride(DOT.Value) ?? primaryTarget, GCDPriority.ForcedGCD); @@ -283,11 +289,13 @@ public override void Execute(StrategyValues strategy, Actor? primaryTarget, floa QueueGCD(AID.BanefulImpaction, ResolveTargetOverride(DOT.Value) ?? primaryTarget, GCDPriority.Standard + 15); } if (ShouldUseChainStratagem(primaryTarget, csStrat)) - QueueGCD(AID.ChainStratagem, ResolveTargetOverride(cs.Value) ?? primaryTarget, GCDPriority.Standard + 500); + QueueOGCD(AID.ChainStratagem, ResolveTargetOverride(cs.Value) ?? primaryTarget, OGCDPriority.ChainStratagem); if (ShouldUseAetherflow(primaryTarget, afStrat)) - QueueGCD(AID.Aetherflow, Player, GCDPriority.Standard + 400); + QueueOGCD(AID.Aetherflow, Player, OGCDPriority.Aetherflow); if (ShouldUseEnergyDrain(primaryTarget, edStrat)) - QueueGCD(AID.EnergyDrain, ResolveTargetOverride(ed.Value) ?? primaryTarget, GCDPriority.Standard + 300); + QueueOGCD(AID.EnergyDrain, ResolveTargetOverride(ed.Value) ?? primaryTarget, OGCDPriority.EnergyDrain); + if (Player.HPMP.CurMP <= 9000 && canWeaveIn && ActionReady(AID.LucidDreaming)) + QueueOGCD(AID.LucidDreaming, Player, OGCDPriority.EnergyDrain); if (potion is PotionStrategy.AlignWithRaidBuffs && CD(AID.ChainStratagem) < 5 || potion is PotionStrategy.Immediate) Hints.ActionsToExecute.Push(ActionDefinitions.IDPotionMnd, Player, ActionQueue.Priority.VeryHigh + (int)OGCDPriority.ForcedOGCD, 0, GCD - 0.9f); @@ -387,14 +395,12 @@ public bool QueueAction(AID aid, Actor? target, float priority, float delay) _ => false }; - private bool ShouldUseEnergyDrain(Actor? target, OffensiveStrategy strategy) => strategy switch + private bool ShouldUseEnergyDrain(Actor? target, EnergyStrategy strategy) => strategy switch { - OffensiveStrategy.Automatic => Player.InCombat && target != null && canED && canWeaveIn && In25y(target), - OffensiveStrategy.Force => canED, - OffensiveStrategy.AnyWeave => canED && canWeaveIn, - OffensiveStrategy.EarlyWeave => canED && canWeaveEarly, - OffensiveStrategy.LateWeave => canED && canWeaveLate, - OffensiveStrategy.Delay => false, + EnergyStrategy.Use3 => canED && Aetherflow.IsActive, + EnergyStrategy.Use2 => canED && Aetherflow.Stacks > 1, + EnergyStrategy.Use1 => canED && Aetherflow.Stacks > 2, + EnergyStrategy.Delay => false, _ => false }; #endregion From c605c849209e49623b01c3ff4a3c494481ae43ed Mon Sep 17 00:00:00 2001 From: Akechi-kun <167795370+Akechi-kun@users.noreply.github.com> Date: Wed, 25 Dec 2024 02:41:31 -0800 Subject: [PATCH 25/29] oh, this is an oGCD lol --- BossMod/Autorotation/akechi/AkechiSCH.cs | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/BossMod/Autorotation/akechi/AkechiSCH.cs b/BossMod/Autorotation/akechi/AkechiSCH.cs index e6391f2315..2f2b2ae6b5 100644 --- a/BossMod/Autorotation/akechi/AkechiSCH.cs +++ b/BossMod/Autorotation/akechi/AkechiSCH.cs @@ -266,7 +266,7 @@ public override void Execute(StrategyValues strategy, Actor? primaryTarget, floa { if (DOTStrategy is DOTStrategy.BanefulImpaction && PlayerHasEffect(SID.ImpactImminent, 30)) - QueueGCD(AID.BanefulImpaction, ResolveTargetOverride(DOT.Value) ?? primaryTarget, GCDPriority.ForcedGCD); + QueueOGCD(AID.BanefulImpaction, ResolveTargetOverride(DOT.Value) ?? primaryTarget, OGCDPriority.ForcedOGCD); } #endregion @@ -281,12 +281,12 @@ public override void Execute(StrategyValues strategy, Actor? primaryTarget, floa QueueGCD(isMoving ? BestRuin : BestST, STtarget, GCDPriority.Standard); } if (ShouldUseDOTs(primaryTarget, DOTStrategy)) - QueueGCD(BestBio, ResolveTargetOverride(DOT.Value) ?? primaryTarget, GCDPriority.ForcedGCD); + QueueGCD(BestBio, ResolveTargetOverride(DOT.Value) ?? primaryTarget, GCDPriority.DOT); if (DOTStrategy == DOTStrategy.Auto) { if (PlayerHasEffect(SID.ImpactImminent, 30)) - QueueGCD(AID.BanefulImpaction, ResolveTargetOverride(DOT.Value) ?? primaryTarget, GCDPriority.Standard + 15); + QueueOGCD(AID.BanefulImpaction, ResolveTargetOverride(DOT.Value) ?? primaryTarget, OGCDPriority.ChainStratagem); } if (ShouldUseChainStratagem(primaryTarget, csStrat)) QueueOGCD(AID.ChainStratagem, ResolveTargetOverride(cs.Value) ?? primaryTarget, OGCDPriority.ChainStratagem); @@ -383,7 +383,6 @@ public bool QueueAction(AID aid, Actor? target, float priority, float delay) OffensiveStrategy.Delay => false, _ => false }; - private bool ShouldUseAetherflow(Actor? target, OffensiveStrategy strategy) => strategy switch { OffensiveStrategy.Automatic => Player.InCombat && target != null && canAF && canWeaveIn, @@ -394,7 +393,6 @@ public bool QueueAction(AID aid, Actor? target, float priority, float delay) OffensiveStrategy.Delay => false, _ => false }; - private bool ShouldUseEnergyDrain(Actor? target, EnergyStrategy strategy) => strategy switch { EnergyStrategy.Use3 => canED && Aetherflow.IsActive, From 525d636b686bfa5bcbfbb60d509cb40287e2079c Mon Sep 17 00:00:00 2001 From: Akechi-kun <167795370+Akechi-kun@users.noreply.github.com> Date: Thu, 26 Dec 2024 00:28:26 -0800 Subject: [PATCH 26/29] SCH ok --- BossMod/Autorotation/akechi/AkechiSCH.cs | 161 ++++++++++------------- 1 file changed, 67 insertions(+), 94 deletions(-) diff --git a/BossMod/Autorotation/akechi/AkechiSCH.cs b/BossMod/Autorotation/akechi/AkechiSCH.cs index 2f2b2ae6b5..e15035b18f 100644 --- a/BossMod/Autorotation/akechi/AkechiSCH.cs +++ b/BossMod/Autorotation/akechi/AkechiSCH.cs @@ -11,53 +11,51 @@ public sealed class AkechiSCH(RotationModuleManager manager, Actor player) : Rot #region Enums: Abilities / Strategies public enum Track { - AOE, //ST&AOE rotations tracking - DOT, //DOT abilities tracking - Potion, //Potion item tracking - EnergyDrain, //Energy Drain tracking - ChainStratagem, //Chain Stratagem tracking - Aetherflow, //Aetherflow tracking + AOE, //ST&AOE rotations tracking + Bio, //Bio tracking + Potion, //Potion item tracking + EnergyDrain, //Energy Drain tracking + ChainStratagem, //Chain Stratagem tracking + Aetherflow, //Aetherflow tracking } public enum AOEStrategy { - Auto, //Automatically decide when to use ST or AOE abilities - Ruin2, //Force use of Ruin II only - Broil, //Force use of Broil only - ArtOfWar, //Force use of Art of War only + Auto, //Automatically decide when to use ST or AOE abilities + Ruin2, //Force use of Ruin II only + Broil, //Force use of Broil only + ArtOfWar, //Force use of Art of War only } - public enum DOTStrategy + public enum BioStrategy { - Auto, //Automatically decide when to use damage-over-time abilities - Bio, //Force use of Bio (ST, 30s duration) - Bio2, //Force use of Bio II (ST, 30s duration) - Biolysis, //Force use of Biolysis (ST, 30s duration) - BioOpti, //Force use of Bio (ST, 30s duration) if target does not have DOT effect - Bio2Opti, //Force use of Bio II (ST, 30s duration) if target does not have DOT effect - BiolysisOpti, //Force use of Biolysis (ST, 30s duration) if target does not have DOT effect - BanefulImpaction, //Force use of Baneful Impaction (AOE, 15s duration) - Forbid, //Forbids the use of all abilities with a cooldown + Bio3, //Force use of Bio if target has 3s or less remaining on DOT effect + Bio6, //Force use of Bio if target has 6s or less remaining on DOT effect + Bio9, //Force use of Bio if target has 9s or less remaining on DOT effect + Bio0, //Force use of Bio if target has does not have DOT effect + Force, //Force use of Bio regardless of DOT effect + Delay //Delay the use of Bio for manual or strategic usage } public enum PotionStrategy { - Manual, //Manual potion usage - AlignWithRaidBuffs, //Align potion usage with raid buffs - Immediate //Use potions immediately when available + Manual, //Manual potion usage + AlignWithRaidBuffs, //Align potion usage with raid buffs + Immediate //Use potions immediately when available } public enum EnergyStrategy { Use3, //Use all stacks of Aetherflow for Energy Drain Use2, //Use 2 stacks of Aetherflow for Energy Drain Use1, //Use 1 stack of Aetherflow for Energy Drain + Force, //Force the use of Energy Drain if any Aetherflow is available Delay //Delay the use of Energy Drain } 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 + 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 @@ -67,7 +65,7 @@ public static RotationModuleDefinition Definition() "Standard Rotation Module", //Description "Standard rotation (Akechi)", //Category "Akechi", //Contributor - RotationModuleQuality.Basic, //Quality + RotationModuleQuality.Ok, //Quality BitMask.Build((int)Class.SCH), //Job 100); //Level supported @@ -78,17 +76,14 @@ public static RotationModuleDefinition Definition() .AddOption(AOEStrategy.Broil, "Broil", "Force use of Broil only (hardcast ST, more DPS)", supportedTargets: ActionTargets.Hostile) .AddOption(AOEStrategy.ArtOfWar, "Art of War", "Force use of Art of War only (instant cast AOE)", supportedTargets: ActionTargets.Hostile) .AddAssociatedActions(AID.Ruin2, AID.Broil1, AID.Broil2, AID.Broil3, AID.Broil4, AID.ArtOfWar1, AID.ArtOfWar2); - res.Define(Track.DOT).As("Damage Over Time", "DOTs", uiPriority: 190) - .AddOption(DOTStrategy.Auto, "Allow", "Automatically decide when to use DoT abilities") - .AddOption(DOTStrategy.Bio, "Bio", "Force use of Bio (ST, 30s duration)", 0, 30, ActionTargets.Hostile, 2, 26) - .AddOption(DOTStrategy.Bio2, "Bio II", "Force use of Bio II (ST, 30s duration)", 0, 30, ActionTargets.Hostile, 26, 72) - .AddOption(DOTStrategy.Biolysis, "Biolysis", "Force use of Biolysis (ST, 30s duration)", 0, 30, ActionTargets.Hostile, 72) - .AddOption(DOTStrategy.BioOpti, "Bio Opti", "Force use of Bio (ST, 30s duration) if target does NOT have DOT effect", 0, 30, ActionTargets.Hostile, 2, 26) - .AddOption(DOTStrategy.Bio2Opti, "Bio II Opti", "Force use of Bio II (ST, 30s duration) if target does NOT have DOT effect", 0, 30, ActionTargets.Hostile, 26, 72) - .AddOption(DOTStrategy.BiolysisOpti, "Biolysis Opti", "Force use of Biolysis (ST, 30s duration) if target does NOT have DOT effect", 0, 30, ActionTargets.Hostile, 72) - .AddOption(DOTStrategy.BanefulImpaction, "Baneful Impaction", "Force use of Baneful Impaction (AOE, 15s duration)", 0, 15, ActionTargets.Self, 92) - .AddOption(DOTStrategy.Forbid, "Forbid", "Forbid the use of all DoT abilities", 0, 0, ActionTargets.None) - .AddAssociatedActions(AID.Bio1, AID.Bio2, AID.Biolysis, AID.BanefulImpaction); + res.Define(Track.Bio).As("Damage Over Time", "Bio", uiPriority: 190) + .AddOption(BioStrategy.Bio3, "Bio3", "Use Bio if target has 3s or less remaining on DoT effect", 0, 30, ActionTargets.Hostile, 2) + .AddOption(BioStrategy.Bio6, "Bio6", "Use Bio if target has 6s or less remaining on DoT effect", 0, 30, ActionTargets.Hostile, 2) + .AddOption(BioStrategy.Bio9, "Bio9", "Use Bio if target has 9s or less remaining on DoT effect", 0, 30, ActionTargets.Hostile, 2) + .AddOption(BioStrategy.Bio0, "Bio0", "Use Bio if target does not have DoT effect", 0, 30, ActionTargets.Hostile, 2) + .AddOption(BioStrategy.Force, "Force", "Force use of Bio regardless of DoT effect", 0, 30, ActionTargets.Hostile, 2) + .AddOption(BioStrategy.Delay, "Delay", "Delay the use of Bio for manual or strategic usage", 0, 0, ActionTargets.Hostile, 2) + .AddAssociatedActions(AID.Bio1, AID.Bio2, AID.Biolysis); 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) @@ -97,7 +92,8 @@ public static RotationModuleDefinition Definition() res.Define(Track.EnergyDrain).As("Energy Drain", "E.Drain", uiPriority: 150) .AddOption(EnergyStrategy.Use3, "UseAll", "Uses all stacks of Aetherflow for Energy Drain; conserves no stacks for manual usage", 0, 0, ActionTargets.Hostile, 45) .AddOption(EnergyStrategy.Use2, "Use2", "Uses 2 stacks of Aetherflow for Energy Drain; conserves 1 stack for manual usage", 0, 0, ActionTargets.Hostile, 45) - .AddOption(EnergyStrategy.Use1, "Use1", "Uses 1 stacks of Aetherflow for Energy Drain; conserves 1 stack for manual usage", 0, 0, ActionTargets.Hostile, 45) + .AddOption(EnergyStrategy.Use1, "Use1", "Uses 1 stack of Aetherflow for Energy Drain; conserves 2 stacks for manual usage", 0, 0, ActionTargets.Hostile, 45) + .AddOption(EnergyStrategy.Force, "Force", "Force use of Energy Drain if any Aetherflow is available", 0, 0, ActionTargets.None, 45) .AddOption(EnergyStrategy.Delay, "Delay", "Delay use of Energy Drain", 0, 0, ActionTargets.None, 45) .AddAssociatedActions(AID.EnergyDrain); #endregion @@ -161,13 +157,12 @@ public static RotationModuleDefinition Definition() 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 (needed for cartridge management) + 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 float CD(AID aid) => World.Client.Cooldowns[ActionDefinitions.Instance.Spell(aid)!.MainCooldownGroup].Remaining; //Get remaining cooldown time for the specified action - private bool In20y(Actor? target) => Player.DistanceToHitbox(target) <= 19.9f; //Check if the target is within ST range 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 @@ -184,42 +179,35 @@ private AID BestBroil //Determine the best Broil to use : Unlocked(AID.Broil2) //Otherwise, if Broil II is unlocked ? AID.Broil2 //Use Broil II : AID.Broil1; //Otherwise, default to Broil I - private AID BestRuin //Determine the best Ruin to use => Unlocked(AID.Ruin2) //Otherwise, if Ruin II is unlocked ? AID.Ruin2 //Use Ruin II : AID.Ruin1; //Otherwise, default to Ruin I - private AID BestBio //Determine the best DOT to use => Unlocked(AID.Biolysis) //If Biolysis is unlocked ? AID.Biolysis //Use Biolysis : Unlocked(AID.Bio2) //Otherwise, if Bio II is unlocked ? AID.Bio2 //Use Bio II : AID.Bio1; //Otherwise, default to Bio I - private SID BestDOT //Determine the best DOT to use => Unlocked(AID.Biolysis) //If Biolysis is unlocked ? SID.Biolysis //Use Biolysis : Unlocked(AID.Bio2) //Otherwise, if Bio II is unlocked ? SID.Bio2 //Use Bio II : SID.Bio1; //Otherwise, default to Bio I - private AID BestST //Determine the best ST to use => Unlocked(AID.Broil1) //If Broil I is unlocked ? BestBroil //Use the best Broil : BestRuin; //Otherwise, default to best Ruin - private AID BestAOE //Determine the best AOE to use => Unlocked(AID.ArtOfWar2) //If Art of War II is unlocked ? AID.ArtOfWar2 //Use Art of War II : AID.ArtOfWar1; //Otherwise, default to Art of War - #endregion public override void Execute(StrategyValues strategy, Actor? primaryTarget, float estimatedAnimLockDelay, bool isMoving) //Executes our actions { #region Variables - //Gauge var gauge = World.Client.GetGauge(); //Retrieve Scholar gauge Aetherflow.Stacks = gauge.Aetherflow; //Current Aetherflow stacks Aetherflow.IsActive = Aetherflow.Stacks > 0; //Checks if Aetherflow is available @@ -231,7 +219,7 @@ public override void Execute(StrategyValues strategy, Actor? primaryTarget, floa 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 skill speed and haste + 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 @@ -239,8 +227,8 @@ 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 DOT = strategy.Option(Track.DOT); //DOT track - var DOTStrategy = DOT.As(); //DOT strategy + var Bio = strategy.Option(Track.Bio); //Bio track + var BioStrategy = Bio.As(); //Bio strategy var potion = strategy.Option(Track.Potion).As(); //Potion strategy var cs = strategy.Option(Track.ChainStratagem); //Chain Stratagem track var csStrat = cs.As(); //Chain Stratagem strategy @@ -253,21 +241,12 @@ public override void Execute(StrategyValues strategy, Actor? primaryTarget, floa #endregion #region Force Execution - if (In20y(primaryTarget)) - { - if (AOEStrategy is AOEStrategy.Ruin2) - QueueGCD(BestRuin, ResolveTargetOverride(AOE.Value) ?? primaryTarget, GCDPriority.ForcedGCD); - if (AOEStrategy is AOEStrategy.Broil) - QueueGCD(BestBroil, ResolveTargetOverride(AOE.Value) ?? primaryTarget, GCDPriority.ForcedGCD); - if (AOEStrategy is AOEStrategy.ArtOfWar) - QueueGCD(BestAOE, Player, GCDPriority.ForcedGCD); - } - if (In25y(primaryTarget)) - { - if (DOTStrategy is DOTStrategy.BanefulImpaction && - PlayerHasEffect(SID.ImpactImminent, 30)) - QueueOGCD(AID.BanefulImpaction, ResolveTargetOverride(DOT.Value) ?? primaryTarget, OGCDPriority.ForcedOGCD); - } + if (AOEStrategy is AOEStrategy.Ruin2) + QueueGCD(BestRuin, ResolveTargetOverride(AOE.Value) ?? primaryTarget, GCDPriority.ForcedGCD); + if (AOEStrategy is AOEStrategy.Broil) + QueueGCD(BestBroil, ResolveTargetOverride(AOE.Value) ?? primaryTarget, GCDPriority.ForcedGCD); + if (AOEStrategy is AOEStrategy.ArtOfWar) + QueueGCD(BestAOE, Player, GCDPriority.ForcedGCD); #endregion #region Standard Execution @@ -280,25 +259,21 @@ public override void Execute(StrategyValues strategy, Actor? primaryTarget, floa (!ShouldUseAOE || IsFirstGCD())) QueueGCD(isMoving ? BestRuin : BestST, STtarget, GCDPriority.Standard); } - if (ShouldUseDOTs(primaryTarget, DOTStrategy)) - QueueGCD(BestBio, ResolveTargetOverride(DOT.Value) ?? primaryTarget, GCDPriority.DOT); - - if (DOTStrategy == DOTStrategy.Auto) - { - if (PlayerHasEffect(SID.ImpactImminent, 30)) - QueueOGCD(AID.BanefulImpaction, ResolveTargetOverride(DOT.Value) ?? primaryTarget, OGCDPriority.ChainStratagem); - } + if (ShouldUseBio(primaryTarget, BioStrategy)) + QueueGCD(BestBio, ResolveTargetOverride(Bio.Value) ?? primaryTarget, GCDPriority.DOT); + if (PlayerHasEffect(SID.ImpactImminent, 30)) + QueueOGCD(AID.BanefulImpaction, ResolveTargetOverride(Bio.Value) ?? primaryTarget, OGCDPriority.ChainStratagem); if (ShouldUseChainStratagem(primaryTarget, csStrat)) - QueueOGCD(AID.ChainStratagem, ResolveTargetOverride(cs.Value) ?? primaryTarget, OGCDPriority.ChainStratagem); + QueueOGCD(AID.ChainStratagem, ResolveTargetOverride(cs.Value) ?? primaryTarget, csStrat is OffensiveStrategy.Force or OffensiveStrategy.AnyWeave or OffensiveStrategy.EarlyWeave or OffensiveStrategy.LateWeave ? OGCDPriority.ForcedOGCD : OGCDPriority.ChainStratagem); if (ShouldUseAetherflow(primaryTarget, afStrat)) - QueueOGCD(AID.Aetherflow, Player, OGCDPriority.Aetherflow); + QueueOGCD(AID.Aetherflow, Player, afStrat is OffensiveStrategy.Force or OffensiveStrategy.AnyWeave or OffensiveStrategy.EarlyWeave or OffensiveStrategy.LateWeave ? OGCDPriority.ForcedOGCD : OGCDPriority.Aetherflow); if (ShouldUseEnergyDrain(primaryTarget, edStrat)) - QueueOGCD(AID.EnergyDrain, ResolveTargetOverride(ed.Value) ?? primaryTarget, OGCDPriority.EnergyDrain); + QueueOGCD(AID.EnergyDrain, ResolveTargetOverride(ed.Value) ?? primaryTarget, edStrat is EnergyStrategy.Force ? OGCDPriority.ForcedOGCD : OGCDPriority.EnergyDrain); if (Player.HPMP.CurMP <= 9000 && canWeaveIn && ActionReady(AID.LucidDreaming)) QueueOGCD(AID.LucidDreaming, Player, OGCDPriority.EnergyDrain); if (potion is PotionStrategy.AlignWithRaidBuffs && CD(AID.ChainStratagem) < 5 || potion is PotionStrategy.Immediate) - Hints.ActionsToExecute.Push(ActionDefinitions.IDPotionMnd, Player, ActionQueue.Priority.VeryHigh + (int)OGCDPriority.ForcedOGCD, 0, GCD - 0.9f); + Hints.ActionsToExecute.Push(ActionDefinitions.IDPotionMnd, Player, ActionQueue.Priority.VeryHigh + (int)OGCDPriority.Potion, 0, GCD - 0.9f); #endregion } @@ -360,17 +335,14 @@ public bool QueueAction(AID aid, Actor? target, float priority, float delay) #endregion #region Cooldown Helpers - private bool ShouldUseDOTs(Actor? target, DOTStrategy strategy) => strategy switch + private bool ShouldUseBio(Actor? target, BioStrategy strategy) => strategy switch { - DOTStrategy.Auto => Player.InCombat && target != null && bioLeft <= 3 && In25y(target), - DOTStrategy.Bio => true, - DOTStrategy.Bio2 => true, - DOTStrategy.Biolysis => true, - DOTStrategy.BioOpti => bioLeft <= 3, - DOTStrategy.Bio2Opti => bioLeft <= 3, - DOTStrategy.BiolysisOpti => bioLeft <= 3, - DOTStrategy.BanefulImpaction => PlayerHasEffect(SID.ImpactImminent, 30), - DOTStrategy.Forbid => false, + BioStrategy.Bio3 => Player.InCombat && target != null && bioLeft <= 3 && In25y(target), + BioStrategy.Bio6 => Player.InCombat && target != null && bioLeft <= 3 && In25y(target), + BioStrategy.Bio9 => Player.InCombat && target != null && bioLeft <= 3 && In25y(target), + BioStrategy.Bio0 => Player.InCombat && target != null && bioLeft <= 3 && In25y(target), + BioStrategy.Force => true, + BioStrategy.Delay => false, _ => false }; private bool ShouldUseChainStratagem(Actor? target, OffensiveStrategy strategy) => strategy switch @@ -395,9 +367,10 @@ public bool QueueAction(AID aid, Actor? target, float priority, float delay) }; private bool ShouldUseEnergyDrain(Actor? target, EnergyStrategy strategy) => strategy switch { - EnergyStrategy.Use3 => canED && Aetherflow.IsActive, - EnergyStrategy.Use2 => canED && Aetherflow.Stacks > 1, - EnergyStrategy.Use1 => canED && Aetherflow.Stacks > 2, + EnergyStrategy.Use3 => canED && In25y(target) && canWeaveIn, + EnergyStrategy.Use2 => canED && Aetherflow.Stacks > 1 && In25y(target) && canWeaveIn, + EnergyStrategy.Use1 => canED && Aetherflow.Stacks > 2 && In25y(target) && canWeaveIn, + EnergyStrategy.Force => canED, EnergyStrategy.Delay => false, _ => false }; From 0199c12d6792d66755bdb954e764cf96fcb291d9 Mon Sep 17 00:00:00 2001 From: Akechi-kun <167795370+Akechi-kun@users.noreply.github.com> Date: Thu, 26 Dec 2024 02:44:47 -0800 Subject: [PATCH 27/29] NM fixes --- BossMod/Autorotation/akechi/AkechiGNB.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/BossMod/Autorotation/akechi/AkechiGNB.cs b/BossMod/Autorotation/akechi/AkechiGNB.cs index ac56eaaa01..17e4b3baa5 100644 --- a/BossMod/Autorotation/akechi/AkechiGNB.cs +++ b/BossMod/Autorotation/akechi/AkechiGNB.cs @@ -875,7 +875,8 @@ private void QueueOGCD(AID aid, Actor? target, OGCDPriority prio, float basePrio canNM && //No Mercy is available GCD < 0.9f && //GCD is less than 0.9s ((Unlocked(AID.DoubleDown) && //Double Down is unlocked, indicating Lv90 or above - Ammo < 3) || //Ammo is not 3 + ((bfCD is <= 90 and >= 30 && (Ammo >= 2 || (Ammo == 1 && ComboLastMove is AID.BrutalShell))) || //In Odd Window & conditions are met + (bfCD is not <= 90 and not >= 30 && Ammo < 3))) || //In Even Window & conditions are met (!Unlocked(AID.DoubleDown) && Unlocked(AID.Bloodfest) && //Double Down is not unlocked but Bloodfest is, indicating Lv80-89 Ammo >= 1 && //Ammo is 1 or more bfCD is < 5 or 0) || //Bloodfest is ready or about to be From 271d5c6cd37841b44cb9210e4c230851d1b196c9 Mon Sep 17 00:00:00 2001 From: Akechi-kun <167795370+Akechi-kun@users.noreply.github.com> Date: Thu, 26 Dec 2024 03:17:35 -0800 Subject: [PATCH 28/29] actual nm fixes --- BossMod/Autorotation/akechi/AkechiGNB.cs | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/BossMod/Autorotation/akechi/AkechiGNB.cs b/BossMod/Autorotation/akechi/AkechiGNB.cs index 17e4b3baa5..a66a228134 100644 --- a/BossMod/Autorotation/akechi/AkechiGNB.cs +++ b/BossMod/Autorotation/akechi/AkechiGNB.cs @@ -492,7 +492,7 @@ public override void Execute(StrategyValues strategy, Actor? primaryTarget, floa var ls = strategy.Option(Track.LightningShot); //Lightning Shot track var lsStrat = ls.As(); //Lightning Shot strategy var hold = strategy.Option(Track.Cooldowns).As() == CooldownStrategy.Hold; //Determine if holding resources - var conserve = strategy.Option(Track.Cartridges).As() == CartridgeStrategy.Conserve; //Determine if conserving cartridges + var conserve = cartStrat == CartridgeStrategy.Conserve; //Determine if conserving cartridges #endregion #endregion @@ -875,11 +875,10 @@ private void QueueOGCD(AID aid, Actor? target, OGCDPriority prio, float basePrio canNM && //No Mercy is available GCD < 0.9f && //GCD is less than 0.9s ((Unlocked(AID.DoubleDown) && //Double Down is unlocked, indicating Lv90 or above - ((bfCD is <= 90 and >= 30 && (Ammo >= 2 || (Ammo == 1 && ComboLastMove is AID.BrutalShell))) || //In Odd Window & conditions are met - (bfCD is not <= 90 and not >= 30 && Ammo < 3))) || //In Even Window & conditions are met + (((bfCD <= 90 && bfCD >= 30) && (Ammo >= 2 || (Ammo == 1 && ComboLastMove is AID.BrutalShell))) || //In Odd Window & conditions are met + (!(bfCD <= 90 && bfCD >= 30) && Ammo < 3))) || //In Even Window & conditions are met (!Unlocked(AID.DoubleDown) && Unlocked(AID.Bloodfest) && //Double Down is not unlocked but Bloodfest is, indicating Lv80-89 - Ammo >= 1 && //Ammo is 1 or more - bfCD is < 5 or 0) || //Bloodfest is ready or about to be + Ammo >= 1) || //Ammo is 1 or more (!Unlocked(AID.Bloodfest) && canGF) || //Bloodfest is not unlocked but Gnashing Fang is, indicating Lv60-79 !Unlocked(AID.GnashingFang)), //Gnashing Fang is not unlocked, indicating Lv59 and below NoMercyStrategy.Force => canNM, //Force No Mercy, regardless of correct weaving @@ -1000,7 +999,9 @@ private void QueueOGCD(AID aid, Actor? target, OGCDPriority prio, float basePrio In3y(target) && //Target in melee range canBS && //Burst Strike is available (hasNM || //No Mercy is active - nmCD < 1 && Ammo == 3), //No Mercy is almost ready and full carts + (!(bfCD is <= 90 and >= 30) && + nmCD < 1 && + Ammo == 3)), //No Mercy is almost ready and full carts _ => false }; @@ -1013,7 +1014,9 @@ private void QueueOGCD(AID aid, Actor? target, OGCDPriority prio, float basePrio In5y(target) && //Target in range canFC && //Fated Circle is available (hasNM || //No Mercy is active - nmCD < 1 && Ammo == 3), //No Mercy is almost ready and full carts + (!(bfCD is <= 90 and >= 30) && + nmCD < 1 && + Ammo == 3)), //No Mercy is almost ready and full carts _ => false }; From 36ac1c0d94e87e1ce315ab4ef990e6daaab754c6 Mon Sep 17 00:00:00 2001 From: Akechi-kun <167795370+Akechi-kun@users.noreply.github.com> Date: Thu, 26 Dec 2024 03:26:04 -0800 Subject: [PATCH 29/29] leeway for 9th GCD --- BossMod/Autorotation/akechi/AkechiGNB.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/BossMod/Autorotation/akechi/AkechiGNB.cs b/BossMod/Autorotation/akechi/AkechiGNB.cs index a66a228134..97b2e1f4bd 100644 --- a/BossMod/Autorotation/akechi/AkechiGNB.cs +++ b/BossMod/Autorotation/akechi/AkechiGNB.cs @@ -427,7 +427,7 @@ public override void Execute(StrategyValues strategy, Actor? primaryTarget, floa nmLeft = SelfStatusLeft(SID.NoMercy, 20); //Remaining time for No Mercy buff (20s) hasBreak = HasEffect(SID.ReadyToBreak); //Checks for Ready To Break buff hasReign = HasEffect(SID.ReadyToReign); //Checks for Ready To Reign buff - hasNM = nmCD is >= 40 and <= 60; //Checks if No Mercy is active + hasNM = nmCD is >= 39.5f and <= 60; //Checks if No Mercy is active hasBlast = Unlocked(AID.Hypervelocity) && HasEffect(SID.ReadyToBlast); //Checks for Ready To Blast buff hasRaze = Unlocked(AID.FatedBrand) && HasEffect(SID.ReadyToRaze); //Checks for Ready To Raze buff hasRip = HasEffect(SID.ReadyToRip); //Checks for Ready To Rip buff