diff --git a/BossMod/Autorotation/akechi/AkechiGNB.cs b/BossMod/Autorotation/akechi/AkechiGNB.cs index 97b2e1f4bd..060740d435 100644 --- a/BossMod/Autorotation/akechi/AkechiGNB.cs +++ b/BossMod/Autorotation/akechi/AkechiGNB.cs @@ -6,8 +6,6 @@ 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 easily achievable public sealed class AkechiGNB(RotationModuleManager manager, Actor player) : RotationModule(manager, player) { @@ -15,148 +13,148 @@ public sealed class AkechiGNB(RotationModuleManager manager, Actor player) : Rot //Abilities tracked for Cooldown Planner & Autorotation execution public enum Track { - 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 - Zone, //Blasting Zone or Danger Zone ability tracking - BowShock, //Bow Shock ability 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 + 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 public enum AOEStrategy { - AutoFinishCombo, //Decide action based on target count but finish current combo if possible - 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 + AutoFinishCombo, //Decide action based on target count but finish current combo if possible + 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 CooldownStrategy { - Automatic, //Automatically execute based on conditions - Hold, //Hold all resources + Automatic, //Automatically execute based on conditions + 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 + Automatic, //Automatically decide when to use Burst Strike & Fated Circle + OnlyBS, //Only use Burst Strike as cartridge spender + OnlyFC, //Only use Fated Circle as cartridge spender + ForceBS, //Force the use of Burst Strike + ForceFC, //Force the use of Fated Circle + 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 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 } //Defines strategies for using Lightning Shot during combat based on various conditions public enum LightningShotStrategy { - 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 + 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 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 - 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 - 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 - 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 + Automatic, //Automatically decide when to use No Mercy + Force, //Force the use of No Mercy regardless of conditions + 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 + 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 + 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 } //Defines the strategy for using Sonic Break, allowing for different behaviors based on combat scenarios public enum SonicBreakStrategy { - Automatic, //Automatically decide when to use Sonic Break - Force, //Force the use of Sonic Break regardless of conditions - 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 + Automatic, //Automatically decide when to use Sonic Break + Force, //Force the use of Sonic Break regardless of conditions + 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 + 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 + 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 + 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 GCDStrategy //Global Cooldown Strategy { - 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 + 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 + 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 + #region Module Definitions & Strategies public static RotationModuleDefinition Definition() { var res = new RotationModuleDefinition("Akechi GNB", //Title @@ -185,13 +183,13 @@ 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", 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", supportedTargets: ActionTargets.Hostile) - .AddOption(CartridgeStrategy.DoubleDown, "Double Down", "Force the use of Double Down; consumes 1 cartridge") + .AddOption(CartridgeStrategy.Automatic, "Automatic", "Automatically decide when to use cartridges; uses them optimally") + .AddOption(CartridgeStrategy.OnlyBS, "Only Burst Strike", "Uses Burst Strike optimally as cartridge spender only, regardless of targets", 0, 0, ActionTargets.Hostile, 30) + .AddOption(CartridgeStrategy.OnlyFC, "Only Fated Circle", "Uses Fated Circle optimally as cartridge spender only, regardless of targets", 0, 0, ActionTargets.Hostile, 72) + .AddOption(CartridgeStrategy.ForceBS, "Force Burst Strike", "Force use of Burst Strike; consumes 1 cartridge", 0, 0, ActionTargets.Hostile, 30) + .AddOption(CartridgeStrategy.ForceFC, "Force Fated Circle", "Force use of Fated Circle; consumes 1 cartridge", 0, 0, ActionTargets.Hostile, 72) .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); + .AddAssociatedActions(AID.BurstStrike, AID.FatedCircle); //Potion strategy res.Define(Track.Potion).As("Potion", uiPriority: 20) @@ -297,6 +295,7 @@ public static RotationModuleDefinition Definition() return res; } + #endregion #region Priorities public enum GCDPriority //priorities for GCDs (higher number = higher priority) @@ -325,7 +324,31 @@ public static RotationModuleDefinition Definition() } #endregion - #region Placeholders for Variables + #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 + => ShouldUseFC //And we should use Fated Circle because of targets nearby + ? BestFatedCircle //Use Fated Circle + : canBS //Otherwise, if Burst Strike is available + ? AID.BurstStrike //Use Burst Strike + : NextBestRotation(); //Otherwise, use the next best rotation + private AID BestFatedCircle //for AOE cart spending Lv30-71 + => Unlocked(AID.FatedCircle) //If Fated Circle is unlocked + ? AID.FatedCircle //Use Fated Circle + : AID.BurstStrike; //Otherwise, use Burst Strike + private AID BestContinuation //Determine the best Continuation to use + => hasRaze ? AID.FatedBrand //If we have Ready To Raze buff + : hasBlast ? AID.Hypervelocity //If we have Ready To Blast buff + : hasGouge ? AID.EyeGouge //If we have Ready To Gouge buff + : hasTear ? AID.AbdomenTear //If we have Ready To Tear buff + : hasRip ? AID.JugularRip //If we have Ready To Rip buff + : AID.Continuation; //Otherwise, default to original hook + #endregion + + #region Module 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 @@ -334,6 +357,7 @@ public static RotationModuleDefinition Definition() private float bfCD; //Time left on Bloodfest cooldown (120s base) private float nmLeft; //Time left on No Mercy buff (20s base) private float nmCD; //Time left on No Mercy cooldown (60s base) + private bool inOdd; //Checks if player is in an odd-minute window private bool hasNM; //Checks self for No Mercy buff private bool hasBreak; //Checks self for Ready To Break buff private bool hasReign; //Checks self for Ready To Reign buff @@ -368,7 +392,6 @@ 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 @@ -381,35 +404,10 @@ public static RotationModuleDefinition Definition() 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 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 //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 + public bool HasEffect(SID sid, float duration) => SelfStatusLeft(sid, duration) > 0; //Checks if Status effect is on self + 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 public override void Execute(StrategyValues strategy, Actor? primaryTarget, float estimatedAnimLockDelay, bool isMoving) //Executes our actions @@ -425,14 +423,14 @@ public override void Execute(StrategyValues strategy, Actor? primaryTarget, floa 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 + hasBreak = HasEffect(SID.ReadyToBreak, 30); //Checks for Ready To Break buff + hasReign = HasEffect(SID.ReadyToReign, 30); //Checks for Ready To Reign buff 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 - hasTear = HasEffect(SID.ReadyToTear); //Checks for Ready To Tear buff - hasGouge = HasEffect(SID.ReadyToGouge); //Checks for Ready To Gouge buff + hasBlast = Unlocked(AID.Hypervelocity) && HasEffect(SID.ReadyToBlast, 10f) && !JustUsed(AID.Hypervelocity, 10f); //Checks for Ready To Blast buff + hasRaze = Unlocked(AID.FatedBrand) && HasEffect(SID.ReadyToRaze, 10f) && !JustUsed(AID.FatedBrand, 10f); //Checks for Ready To Raze buff + hasRip = HasEffect(SID.ReadyToRip, 10f) && !JustUsed(AID.JugularRip, 10f); //Checks for Ready To Rip buff + hasTear = HasEffect(SID.ReadyToTear, 10f) && !JustUsed(AID.AbdomenTear, 10f); //Checks for Ready To Tear buff + hasGouge = HasEffect(SID.ReadyToGouge, 10f) && !JustUsed(AID.EyeGouge, 10f); //Checks for Ready To Gouge buff //GCD & Weaving canWeaveIn = GCD is <= 2.5f and >= 0.1f; //Can weave in oGCDs @@ -441,9 +439,9 @@ public override void Execute(StrategyValues strategy, Actor? primaryTarget, floa 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 + inOdd = bfCD is <= 90 and >= 30; //Checks if we are in an odd-minute window PotionLeft = PotionStatusLeft(); //Remaining time for potion buff (30s) ShouldUseAOE = //Determine if we should use AOE Unlocked(TraitID.MeleeMastery) //if Melee Mastery trait unlocked @@ -454,7 +452,7 @@ public override void Execute(StrategyValues strategy, Actor? primaryTarget, floa #region Minimal Requirements //Ammo-relative - canNM = ActionReady(AID.NoMercy); //No Mercy conditions + canNM = CD(AID.NoMercy) < 1; //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 @@ -503,21 +501,21 @@ 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 + QueueGCD(STwithOvercap(), //queue the next single-target combo action with overcap protection 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 + QueueGCD(STwithoutOvercap(), //queue the next single-target combo action without overcap protection 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 + QueueGCD(AOEwithOvercap(), //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 + QueueGCD(AOEwithoutOvercap(), //queue the next AOE combo action without overcap protection Player, //on Self (no target needed) GCDPriority.ForcedGCD); //with priority for forced GCDs #endregion @@ -569,7 +567,7 @@ public override void Execute(StrategyValues strategy, Actor? primaryTarget, floa } if (Ammo == MaxCartridges) //if at max cartridges - QueueGCD(NextForceSingleTarget(), //queue the next single-target combo action without overcap protection to save resources for uptime + QueueGCD(STwithoutOvercap(), //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 } @@ -579,11 +577,11 @@ public override void Execute(StrategyValues strategy, Actor? primaryTarget, floa 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 + QueueGCD(AOEwithoutOvercap(), //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 + QueueGCD(STwithoutOvercap(), //queue the next single-target combo action ResolveTargetOverride(AOE.Value) //Get target choice ?? primaryTarget, //if none, choose primary target GCDPriority.Combo123); //with priority for 123/12 combo actions @@ -659,15 +657,15 @@ or BloodfestStrategy.Force0W //or Force weave with 0 cartridges //Continuation execution if (canContinue && //if Continuation is available - hasBlast || //and Ready To Blast buff is active + (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 + 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 + canWeaveLate || GCD is 0 //if inside second weave slot & still havent used + ? OGCDPriority.Continuation + 1201 //force the fuck out of this to prevent loss, any loss is very bad : OGCDPriority.Continuation); //otherwise, use intended priority #endregion @@ -681,7 +679,6 @@ or BloodfestStrategy.Force0W //or Force weave with 0 cartridges 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 @@ -691,7 +688,6 @@ or BloodfestStrategy.Force0W //or Force weave with 0 cartridges QueueGCD(AID.GnashingFang, //queue Gnashing Fang 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 : GCDPriority.GF1); //otherwise, use intended priority @@ -707,15 +703,15 @@ or BloodfestStrategy.Force0W //or Force weave with 0 cartridges ? 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 + if (cartStrat is CartridgeStrategy.OnlyBS or CartridgeStrategy.ForceBS) //if Burst Strike Cartridge strategies are selected QueueGCD(AID.BurstStrike, //queue Burst Strike 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 - QueueGCD(AID.FatedCircle, //queue Fated Circle - Player, //on Self (no target needed) + if (cartStrat is CartridgeStrategy.ForceFC or CartridgeStrategy.OnlyFC) //if Fated Circle Cartridge strategies are selected + QueueGCD(BestFatedCircle, //queue Fated Circle + primaryTarget ?? Player, //on Self (no target needed) if Fated Circle, on target if Burst Strike GCDPriority.Gauge); //with priority for gauge actions } } @@ -786,84 +782,91 @@ or LightningShotStrategy.Allow //or Allow } #region Core Execution Helpers - private void QueueGCD(AID aid, Actor? target, GCDPriority prio) //QueueGCD execution + public void QueueGCD

(AID aid, Actor? target, P priority, float delay = 0) where P : Enum => QueueGCD(aid, target, (int)(object)priority, delay); + public void QueueOGCD

(AID aid, Actor? target, P priority, float delay = 0) where P : Enum => QueueOGCD(aid, target, (int)(object)priority, delay); + public void QueueGCD(AID aid, Actor? target, int priority = 8, float delay = 0) { - if (prio != GCDPriority.None) //if priority is not None + var NextGCDPrio = 0; + if (priority == 0) + return; + if (QueueAction(aid, target, ActionQueue.Priority.High + priority, delay) && priority > 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; //set the next GCD to this action - NextGCDPrio = prio; //set the next GCD priority to this priority - } + NextGCD = aid; } } - - private void QueueOGCD(AID aid, Actor? target, OGCDPriority prio, float basePrio = ActionQueue.Priority.Medium) //QueueOGCD execution + public void QueueOGCD(AID aid, Actor? target, int priority = 4, float delay = 0) { - if (prio != OGCDPriority.None) //if priority is not None + if (priority == 0) + return; + QueueAction(aid, target, ActionQueue.Priority.Medium + priority, delay); + } + public bool QueueAction(AID aid, Actor? target, float priority, float delay) + { + Vector3 targetPos = default; + var def = ActionDefinitions.Instance.Spell(aid); + if ((uint)(object)aid == 0) + return false; + if (def == null) + return false; + if (def.Range != 0 && target == null) + { + return false; + } + if (def.AllowedTargets.HasFlag(ActionTargets.Area)) { - 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 + 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 Rotation Helpers private AID NextBestRotation() => ComboLastMove switch { //ST - AID.SolidBarrel => ShouldUseAOE ? NextComboAOE() : NextComboSingleTarget(), - AID.BrutalShell => NextComboSingleTarget(), - AID.KeenEdge => NextComboSingleTarget(), + AID.SolidBarrel => ShouldUseAOE ? AOEwithoutOvercap() : STwithoutOvercap(), + AID.BrutalShell => STwithoutOvercap(), + AID.KeenEdge => STwithoutOvercap(), //AOE - AID.DemonSlaughter => ShouldUseAOE ? NextComboAOE() : NextComboSingleTarget(), - AID.DemonSlice => NextComboAOE(), - _ => ShouldUseAOE ? NextComboAOE() : NextComboSingleTarget(), + AID.DemonSlaughter => ShouldUseAOE ? AOEwithoutOvercap() : STwithoutOvercap(), + AID.DemonSlice => AOEwithoutOvercap(), + _ => ShouldUseAOE ? AOEwithoutOvercap() : STwithoutOvercap(), }; - #endregion - #region Single-Target Helpers - private AID NextComboSingleTarget() => ComboLastMove switch //with Overcap protection + private AID STwithOvercap() => ComboLastMove switch //with Overcap protection { - 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.BrutalShell => Ammo == MaxCartridges ? BestCartSpender : 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 }; - private AID NextForceSingleTarget() => ComboLastMove switch //without Overcap protection + private AID STwithoutOvercap() => ComboLastMove switch //without Overcap protection { - AID.BrutalShell => AID.SolidBarrel, //if Brutal Shell is last move, use Solid Barrel regardless of ammo count + AID.BrutalShell => AID.SolidBarrel, //if Brutal Shell is last move, use Solid Barrel 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 //with Overcap protection + private AID AOEwithOvercap() => ComboLastMove switch //with Overcap protection { - 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 => Ammo == MaxCartridges ? BestCartSpender : AID.DemonSlaughter, //if full ammo & Demon Slice is last move, use best cartridge spender _ => AID.DemonSlice, //start with Demon Slice }; - private AID NextForceAOE() => ComboLastMove switch //without Overcap protection + private AID AOEwithoutOvercap() => ComboLastMove switch //without Overcap protection { - AID.DemonSlice => AID.DemonSlaughter, //if Demon Slice is last move, use Demon Slaughter regardless of ammo count + AID.DemonSlice => AID.DemonSlaughter, //if not at max ammo, use Demon Slaughter _ => AID.DemonSlice, //start with Demon Slice }; #endregion + #endregion + #region Cooldown Helpers //No Mercy full strategy & conditions private bool ShouldUseNoMercy(NoMercyStrategy strategy, Actor? target) => strategy switch @@ -873,14 +876,14 @@ private void QueueOGCD(AID aid, Actor? target, OGCDPriority prio, float basePrio 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 - (((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 + (inOdd && Ammo >= 2) || //In Odd Window & conditions are met + (!inOdd && Ammo < 3)) || //In Even Window & conditions are met + (!Unlocked(AID.DoubleDown) && GCD < 0.9f && //Double Down is not unlocked, so we late weave it + ((Unlocked(AID.Bloodfest) && //but Bloodfest is, indicating Lv80-89 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 + !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 @@ -951,10 +954,10 @@ private void QueueOGCD(AID aid, Actor? target, OGCDPriority prio, float basePrio 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.OnlyBS => ShouldUseBurstStrike(CartridgeStrategy.Automatic, target), //Optimally use Burst Strike + CartridgeStrategy.OnlyFC => ShouldUseFatedCircle(CartridgeStrategy.Automatic, target), //Optimally use Fated Circle + CartridgeStrategy.ForceBS => canBS, //Force Burst Strike + CartridgeStrategy.ForceFC => canFC, //Force Fated Circle CartridgeStrategy.Conserve => false, //Conserve cartridges _ => false }; @@ -1001,7 +1004,8 @@ private void QueueOGCD(AID aid, Actor? target, OGCDPriority prio, float basePrio (hasNM || //No Mercy is active (!(bfCD is <= 90 and >= 30) && nmCD < 1 && - Ammo == 3)), //No Mercy is almost ready and full carts + Ammo == 3)) || //No Mercy is almost ready and full carts + Ammo == MaxCartridges && ComboLastMove is AID.BrutalShell or AID.DemonSlice, //Full carts and last move was Brutal Shell or Demon Slice _ => false }; @@ -1016,7 +1020,8 @@ private void QueueOGCD(AID aid, Actor? target, OGCDPriority prio, float basePrio (hasNM || //No Mercy is active (!(bfCD is <= 90 and >= 30) && nmCD < 1 && - Ammo == 3)), //No Mercy is almost ready and full carts + Ammo == 3)) || //No Mercy is almost ready and full carts + Ammo == MaxCartridges && ComboLastMove is AID.BrutalShell or AID.DemonSlice, //Full carts and last move was Brutal Shell or Demon Slice _ => false }; diff --git a/BossMod/Autorotation/akechi/AkechiPLD.cs b/BossMod/Autorotation/akechi/AkechiPLD.cs index 0b8c6ecc66..82e006e840 100644 --- a/BossMod/Autorotation/akechi/AkechiPLD.cs +++ b/BossMod/Autorotation/akechi/AkechiPLD.cs @@ -8,594 +8,682 @@ namespace BossMod.Autorotation.akechi; public sealed class AkechiPLD(RotationModuleManager manager, Actor player) : RotationModule(manager, player) { - //Actions tracked for Cooldown Planner execution + #region Enums public enum Track { - AoE, //Tracks both AoE and single-target actions - Burst, //Tracks burst damage actions - Potion, //Tracks potion usage - Atonement, //Tracks Atonement actions - BladeCombo, //Tracks Blade Combo actions - Dash, //Tracks the use of Intervene - Ranged, //Tracks ranged attacks - FightOrFlight, //Tracks Fight or Flight actions - Requiescat, //Tracks Requiescat actions - SpiritsWithin, //Tracks Spirits Within actions - CircleOfScorn, //Tracks Circle of Scorn actions - GoringBlade, //Tracks Goring Blade actions - HolySpirit, //Tracks Holy Spirit actions - HolyCircle, //Tracks Holy Circle actions - BladeOfHonor, //Tracks Blade of Honor actions + AOE, //Tracks both AOE and single-target rotations + Cooldowns, //Tracks Cooldowns damage actions + Potion, //Tracks potion usage + Atonement, //Tracks Atonement combo actions + BladeCombo, //Tracks Blade combo actions + Holy, //Tracks Holy actions + Dash, //Tracks Intervene + Ranged, //Tracks ranged attacks + FightOrFlight, //Tracks Fight or Flight + Requiescat, //Tracks Requiescat actions + SpiritsWithin, //Tracks Spirits Within actions + CircleOfScorn, //Tracks Circle of Scorn + GoringBlade, //Tracks Goring Blade + BladeOfHonor, //Tracks Blade of Honor } - - //Strategy definitions for AoE usage public enum AOEStrategy { - UseST, //Use single-target actions when appropriate - ForceST, //Force the use of single-target actions - UseAoE, //Use AoE actions when beneficial - ForceAoE, //Force the use of AoE actions - Auto, //Automatically decide based on target count; may break combos - AutoFinishCombo, //Automatically decide with a preference to finish combos + AutoFinishCombo, //Automatically decide based on targets; finish combo first + AutoBreakCombo, //Automatically decide based on targets; break combo if needed + ForceST, //Force single-target rotation + ForceAOE //Force AOE rotation } - - //Strategy definitions for burst damage - public enum BurstStrategy + public enum CooldownStrategy { - Automatic, //Automatically execute burst actions when conditions are met - Conserve, //Conserve MP and cooldowns for strategic usage - UnderRaidBuffs, //Execute burst actions when under raid buffs for maximum effect - UnderPotion, //Execute burst actions while under potion effects + Allow, //Allow cooldowns when conditions are met + Hold, //Hold MP and cooldowns for strategic usage } - - //Strategy definitions for potion usage public enum PotionStrategy { Manual, //Use potions manually based on player discretion AlignWithRaidBuffs, //Align potion usage with the timing of raid buffs Immediate //Use potions immediately when available } - - //Strategy definitions for Atonement usage public enum AtonementStrategy { - Automatic, //Automatically use Atonement when needed - ForceAtonement, //Force the use of Atonement regardless of other actions - ForceSupplication, //Force use of Supplication - ForceSepulchre, //Force use of Sepulchre actions - Delay //Delay the use of Atonement for optimal timing + Automatic, //Automatically use Atonement when needed + ForceAtonement, //Force the use of Atonement regardless of other actions + ForceSupplication, //Force use of Supplication + ForceSepulchre, //Force use of Sepulchre actions + Delay //Delay the use of Atonement for optimal timing } - - //Strategy definitions for Blade Combo actions public enum BladeComboStrategy { - Automatic, //Automatically execute Blade Combo when conditions are favorable - ForceConfiteor, //Force the use of Confiteor action - ForceFaith, //Force the use of Blade of Faith - ForceTruth, //Force the use of Blade of Truth - ForceValor, //Force the use of Blade of Valor - Delay //Delay the use of Confiteor and Blade Combo for timing + Automatic, //Automatically execute Blade Combo when conditions are favorable + ForceConfiteor, //Force the use of Confiteor action + ForceFaith, //Force the use of Blade of Faith + ForceTruth, //Force the use of Blade of Truth + ForceValor, //Force the use of Blade of Valor + Delay //Delay the use of Confiteor and Blade Combo for timing + } + public enum HolyStrategy + { + Automatic, //Automatically decide on Holy actions based on conditions + Spirit, //Force the use of Holy Spirit + Circle, //Force the use of Holy Circle + Delay //Delay Holy actions for strategic timing } - - //Strategy definitions for dash actions public enum DashStrategy { - Automatic, //Automatically use Intervene as needed - Force, //Force the use of Intervene regardless of other factors - Conserve1, //Conserve one use of Intervene for later - GapClose, //Use Intervene to close gaps between targets - Delay //Delay the use of Intervene for strategic reasons + Automatic, //Automatically use Intervene as needed + Force, //Force the use of Intervene regardless of other factors + Force1, //Force the use of Intervene; Holds one charge for manual usage + GapClose, //Use Intervene to close gaps between targets + GapClose1, //Use Intervene to close gaps between targets; Hold one charge of Intervene for manual usage + Delay //Delay the use of Intervene for strategic reasons } - - //Strategy definitions for ranged attacks public enum RangedStrategy { - OpenerRanged, //Use Shield Lob as part of the opening sequence - OpenerRangedCast, //Use Holy Spirit as part of the opening sequence - Opener, //Use Shield Lob at the start of combat - OpenerCast, //Use Holy Spirit at the start of combat - Force, //Force Shield Lob when possible - ForceCast, //Force Holy Spirit when possible - Ranged, //Use Shield Lob for ranged attacks - RangedCast, //Use Holy Spirit for ranged attacks - Forbid //Prohibit the use of ranged attacks entirely + Automatic, //Automatically decide on ranged attacks based on conditions + OpenerRangedCast, //Use Holy Spirit at the start of combat only if outside melee range + OpenerCast, //Use Holy Spirit at the start of combat regardless of range + ForceCast, //Force Holy Spirit when possible + RangedCast, //Use Holy Spirit for ranged attacks + OpenerRanged, //Use Shield Lob at the start of combat only if outside melee range + Opener, //Use Shield Lob at the start of combat regardless of range + Force, //Force Shield Lob when possible + Ranged, //Use Shield Lob for ranged attacks + Forbid //Prohibit the use of all ranged attacks entirely (unless under Divine Might) } - - //Strategy definitions for offensive actions - public enum OffensiveStrategy + public enum GCDStrategy { - Automatic, //Automatically decide on offensive actions based on conditions - Force, //Force the use of offensive actions regardless of context - Delay //Delay offensive actions for strategic timing + Automatic, //Automatically decide on GCD actions based on conditions + Force, //Force GCD actions regardless of any conditions + Delay //Delay GCD actions for strategic reasons } + public enum OGCDStrategy + { + 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 + #region Module & Strategy Definitions public static RotationModuleDefinition Definition() { - //Define the rotation module - var res = new RotationModuleDefinition("Akechi PLD", "Standard Rotation Module", "Standard rotation (Akechi)", "Akechi", RotationModuleQuality.Ok, BitMask.Build((int)Class.GLA, (int)Class.PLD), 100); - - //AoE Strategy: Manage AoE versus single-target rotations - res.Define(Track.AoE).As("AoE", uiPriority: 200) - .AddOption(AOEStrategy.UseST, "Use ST", "Use single-target rotation") - .AddOption(AOEStrategy.ForceST, "Use AoE", "Force single-target rotation") - .AddOption(AOEStrategy.UseAoE, "Force ST", "Use AoE rotation") - .AddOption(AOEStrategy.ForceAoE, "Force AoE", "Force AoE rotation") - .AddOption(AOEStrategy.Auto, "Auto", "Choose AoE if 3+ targets; otherwise, use single-target") - .AddOption(AOEStrategy.AutoFinishCombo, "Auto Finish Combo", "Choose AoE if 3+ targets; otherwise, finish combo if possible"); - - //Burst Strategy: Control burst actions based on situational needs - res.Define(Track.Burst).As("Burst", uiPriority: 190) - .AddOption(BurstStrategy.Automatic, "Automatic", "Spend cartridges optimally") - .AddOption(BurstStrategy.Conserve, "Conserve", "Conserve MP and cooldowns") - .AddOption(BurstStrategy.UnderRaidBuffs, "Under Raid Buffs", "Spend under raid buffs; conserve otherwise") - .AddOption(BurstStrategy.UnderPotion, "Under Potion", "Spend under potion; conserve otherwise"); - - //Potion Strategy: Manage potion usage + var res = new RotationModuleDefinition("Akechi PLD", + "Standard Rotation Module", + "Standard rotation (Akechi)", + "Akechi", + RotationModuleQuality.Good, + BitMask.Build((int)Class.GLA, (int)Class.PLD), + 100); + + //AOE definitions + res.Define(Track.AOE).As("AOE", uiPriority: 200) + .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.ForceST, "Use AOE", "Force single-target rotation", supportedTargets: ActionTargets.Hostile) + .AddOption(AOEStrategy.ForceAOE, "Force AOE", "Force AOE rotation"); + //Cooldown definitions + res.Define(Track.Cooldowns).As("Cooldowns", uiPriority: 190) + .AddOption(CooldownStrategy.Allow, "Allow", "Allow use of cooldowns") + .AddOption(CooldownStrategy.Hold, "Hold", "Hold all cooldowns"); + //Potion definitions res.Define(Track.Potion).As("Potion", uiPriority: 180) .AddOption(PotionStrategy.Manual, "Manual", "Do not use potions automatically") .AddOption(PotionStrategy.AlignWithRaidBuffs, "Align With Raid Buffs", "Align potion usage with raid buffs", 270, 30, ActionTargets.Self) .AddOption(PotionStrategy.Immediate, "Immediate", "Use potions immediately when available", 270, 30, ActionTargets.Self) .AddAssociatedAction(ActionDefinitions.IDPotionStr); - - //Atonement Strategy: Control the use of Atonement abilities - res.Define(Track.Atonement).As("Atonement", "Atone", uiPriority: 160) - .AddOption(AtonementStrategy.Automatic, "Automatic", "Normal use of Atonement & it's combo") - .AddOption(AtonementStrategy.ForceAtonement, "Force Atonement", "Force use of Atonement", 30, 0, ActionTargets.Hostile, 76) - .AddOption(AtonementStrategy.ForceSupplication, "Force Supplication", "Force use of Supplication", 0, 0, ActionTargets.Hostile, 76) + //Atonement Combo definitions + res.Define(Track.Atonement).As("Atonement", "Atones", uiPriority: 160) + .AddOption(AtonementStrategy.Automatic, "Automatic", "Normal use of Atonement & its combo chain") + .AddOption(AtonementStrategy.ForceAtonement, "Force Atonement", "Force use of Atonement", 0, 30, ActionTargets.Hostile, 76) + .AddOption(AtonementStrategy.ForceSupplication, "Force Supplication", "Force use of Supplication", 0, 30, ActionTargets.Hostile, 76) .AddOption(AtonementStrategy.ForceSepulchre, "Force Sepulchre", "Force use of Sepulchre", 0, 0, ActionTargets.Hostile, 76) - .AddOption(AtonementStrategy.Delay, "Delay", "Delay use of Atonement", 0, 0, ActionTargets.None, 60) + .AddOption(AtonementStrategy.Delay, "Delay", "Delay use of Atonement & its combo chain", 0, 0, ActionTargets.None, 60) .AddAssociatedActions(AID.Atonement, AID.Supplication, AID.Sepulchre); - - //Blade Combo Strategy: Manage the Blade Combo actions + //Blade Combo definitions res.Define(Track.BladeCombo).As("Blade Combo", "Blades", uiPriority: 160) - .AddOption(BladeComboStrategy.Automatic, "Automatic", "Normal use of Confiteor & Blades Combo") + .AddOption(BladeComboStrategy.Automatic, "Automatic", "Normal use of Confiteor & Blades combo chain") .AddOption(BladeComboStrategy.ForceConfiteor, "Force", "Force use of Confiteor", 0, 0, ActionTargets.Hostile, 80) .AddOption(BladeComboStrategy.ForceFaith, "Force Faith", "Force use of Blade of Faith", 0, 0, ActionTargets.Hostile, 90) .AddOption(BladeComboStrategy.ForceTruth, "Force Truth", "Force use of Blade of Truth", 0, 0, ActionTargets.Hostile, 90) .AddOption(BladeComboStrategy.ForceValor, "Force Valor", "Force use of Blade of Valor", 0, 0, ActionTargets.Hostile, 90) - .AddOption(BladeComboStrategy.Delay, "Delay", "Delay use of Confiteor & Blade Combo", 0, 0, ActionTargets.None, 80) - .AddAssociatedActions(AID.BladeOfFaith, AID.BladeOfTruth, AID.BladeOfValor); - - //Dash Strategy: Control the use of the Intervene ability + .AddOption(BladeComboStrategy.Delay, "Delay", "Delay use of Confiteor & Blades combo chain", 0, 0, ActionTargets.None, 80) + .AddAssociatedActions(AID.Confiteor, AID.BladeOfFaith, AID.BladeOfTruth, AID.BladeOfValor); + //Holy action definitions + res.Define(Track.Holy).As("Holy Spirit / Circle", "Holy S/C", uiPriority: 150) + .AddOption(HolyStrategy.Automatic, "Automatic", "Automatically choose which Holy action to use based on conditions") + .AddOption(HolyStrategy.Spirit, "Spirit", "Force use of Holy Spirit", 0, 0, ActionTargets.Hostile, 64) + .AddOption(HolyStrategy.Circle, "Circle", "Force use of Holy Circle", 0, 0, ActionTargets.Hostile, 72) + .AddOption(HolyStrategy.Delay, "Delay", "Delay use of Holy actions", 0, 0, ActionTargets.None, 64) + .AddAssociatedActions(AID.HolySpirit, AID.HolyCircle); + //Intervene definitions res.Define(Track.Dash).As("Intervene", "Dash", uiPriority: 150) .AddOption(DashStrategy.Automatic, "Automatic", "Normal use of Intervene") - .AddOption(DashStrategy.Force, "Force", "Force use of Intervene", 30, 0, ActionTargets.Hostile, 74) - .AddOption(DashStrategy.Conserve1, "Conserve 1", "Conserve one use of Intervene for manual usage", 30, 0, ActionTargets.Hostile, 74) - .AddOption(DashStrategy.GapClose, "Gap Close", "Use as gap closer if outside melee range", 30, 0, ActionTargets.None, 74) - .AddOption(DashStrategy.Delay, "Delay", "Delay use of Intervene", 30, 0, ActionTargets.None, 74) + .AddOption(DashStrategy.Force, "Force", "Force use of Intervene", 30, 0, ActionTargets.Hostile, 66) + .AddOption(DashStrategy.Force1, "Force (Hold 1)", "Force use of Intervene; Hold one charge for manual usage", 30, 0, ActionTargets.Hostile, 66) + .AddOption(DashStrategy.GapClose, "Gap Close", "Use as gap closer if outside melee range", 30, 0, ActionTargets.None, 66) + .AddOption(DashStrategy.GapClose1, "Gap Close (Hold 1)", "Use as gap closer if outside melee range; Hold one charge for manual usage", 30, 0, ActionTargets.None, 66) + .AddOption(DashStrategy.Delay, "Delay", "Delay use of Intervene", 0, 0, ActionTargets.None, 66) .AddAssociatedActions(AID.Intervene); - - //Ranged Strategy: Manage ranged attacks when outside melee range - res.Define(Track.Ranged).As("Ranged", uiPriority: 20) - .AddOption(RangedStrategy.OpenerRanged, "Opener Ranged", "Use Shield Lob as the first GCD if outside melee range") - .AddOption(RangedStrategy.OpenerRangedCast, "Opener Ranged Cast", "Use Holy Spirit as the first GCD if outside melee range", 0, 0, ActionTargets.Hostile, 64) - .AddOption(RangedStrategy.Opener, "Opener", "Use Shield Lob as the first GCD regardless of range") - .AddOption(RangedStrategy.OpenerCast, "Opener Cast", "Use Holy Spirit as the first GCD regardless of range", 0, 0, ActionTargets.Hostile, 64) - .AddOption(RangedStrategy.Force, "Force", "Always use Shield Lob regardless of conditions") - .AddOption(RangedStrategy.ForceCast, "Force Cast", "Always use Holy Spirit regardless of conditions", 0, 0, ActionTargets.Hostile, 64) - .AddOption(RangedStrategy.Ranged, "Ranged", "Use Shield Lob when outside melee range") - .AddOption(RangedStrategy.RangedCast, "Ranged Cast", "Use Holy Spirit when outside melee range", 0, 0, ActionTargets.Hostile, 64) - .AddOption(RangedStrategy.Forbid, "Forbid", "Prohibit use of both ranged attacks") + //Ranged attack definitions + res.Define(Track.Ranged).As("Ranged", "Ranged", uiPriority: 140) + .AddOption(RangedStrategy.Automatic, "Automatic", "Uses Holy Spirit when standing still; Uses Shield Lob if moving") + .AddOption(RangedStrategy.OpenerRangedCast, "Opener (Cast)", "Use Holy Spirit at the start of combat if outside melee range", 0, 0, ActionTargets.Hostile, 64) + .AddOption(RangedStrategy.OpenerCast, "Opener", "Use Holy Spirit at the start of combat regardless of range", 0, 0, ActionTargets.Hostile, 64) + .AddOption(RangedStrategy.ForceCast, "Force Cast", "Force use of Holy Spirit", 0, 0, ActionTargets.Hostile, 64) + .AddOption(RangedStrategy.RangedCast, "Ranged Cast", "Use Holy Spirit for ranged attacks", 0, 0, ActionTargets.Hostile, 64) + .AddOption(RangedStrategy.OpenerRanged, "Opener (Lob)", "Use Shield Lob at the start of combat if outside melee range", 0, 0, ActionTargets.Hostile, 15) + .AddOption(RangedStrategy.Opener, "Opener", "Use Shield Lob at the start of combat regardless of range", 0, 0, ActionTargets.Hostile, 15) + .AddOption(RangedStrategy.Force, "Force", "Force use of Shield Lob", 0, 0, ActionTargets.Hostile, 15) + .AddOption(RangedStrategy.Ranged, "Ranged", "Use Shield Lob for ranged attacks", 0, 0, ActionTargets.Hostile, 15) + .AddOption(RangedStrategy.Forbid, "Forbid", "Prohibit the use of ranged attacks", 0, 0, ActionTargets.Hostile, 15) .AddAssociatedActions(AID.ShieldLob, AID.HolySpirit); - - //Fight or Flight Strategy: Manage offensive cooldowns - res.Define(Track.FightOrFlight).As("Fight or Flight", "FoF", uiPriority: 170) - .AddOption(OffensiveStrategy.Automatic, "Automatic", "Use Fight or Flight normally") - .AddOption(OffensiveStrategy.Force, "Force", "Force use of Fight or Flight", 60, 20, ActionTargets.Self, 2) - .AddOption(OffensiveStrategy.Delay, "Delay", "Delay use of Fight or Flight", 0, 0, ActionTargets.None, 2) + //Fight or Flight definitions + res.Define(Track.FightOrFlight).As("Fight or Flight", "F.Flight", uiPriority: 170) + .AddOption(OGCDStrategy.Automatic, "Automatic", "Use Fight or Flight normally") + .AddOption(OGCDStrategy.Force, "Force", "Force use of Fight or Flight", 60, 20, ActionTargets.Self, 2) + .AddOption(OGCDStrategy.AnyWeave, "Any Weave", "Force use of Fight or Flight in any weave slot", 60, 20, ActionTargets.Self, 2) + .AddOption(OGCDStrategy.EarlyWeave, "Early Weave", "Force use of Fight or Flight in the first weave slot", 60, 20, ActionTargets.Self, 2) + .AddOption(OGCDStrategy.LateWeave, "Late Weave", "Force use of Fight or Flight in the last weave slot", 60, 20, ActionTargets.Self, 2) + .AddOption(OGCDStrategy.Delay, "Delay", "Delay use of Fight or Flight", 0, 0, ActionTargets.None, 2) .AddAssociatedActions(AID.FightOrFlight); - - //Requiescat Strategy: Control the use of Requiescat ability - res.Define(Track.Requiescat).As("Requiescat", "Req", uiPriority: 170) - .AddOption(OffensiveStrategy.Automatic, "Automatic", "Use Requiescat normally") - .AddOption(OffensiveStrategy.Force, "Force", "Force use of Requiescat / Imperator", 60, 20, ActionTargets.Self, 68) - .AddOption(OffensiveStrategy.Delay, "Delay", "Delay use of Requiescat / Imperator", 0, 0, ActionTargets.None, 68) + //Requiescat definitions + res.Define(Track.Requiescat).As("Requiescat", "R.scat", uiPriority: 170) + .AddOption(OGCDStrategy.Automatic, "Automatic", "Use Requiescat normally") + .AddOption(OGCDStrategy.Force, "Force", "Force use of Requiescat / Imperator", 60, 30, ActionTargets.Hostile, 68) + .AddOption(OGCDStrategy.AnyWeave, "Any Weave", "Force use of Requiescat / Imperator in any weave slot", 60, 30, ActionTargets.Hostile, 68) + .AddOption(OGCDStrategy.EarlyWeave, "Early Weave", "Force use of Requiescat / Imperator in the first weave slot", 60, 30, ActionTargets.Hostile, 68) + .AddOption(OGCDStrategy.LateWeave, "Late Weave", "Force use of Requiescat / Imperator in the last weave slot", 60, 30, ActionTargets.Hostile, 68) + .AddOption(OGCDStrategy.Delay, "Delay", "Delay use of Requiescat / Imperator", 0, 0, ActionTargets.None, 68) .AddAssociatedActions(AID.Requiescat, AID.Imperator); - - //Spirits Within Strategy: Manage usage of Spirits Within ability - res.Define(Track.SpiritsWithin).As("Spirits Within", "S.Within", uiPriority: 150) - .AddOption(OffensiveStrategy.Automatic, "Automatic", "Use Spirits Within normally") - .AddOption(OffensiveStrategy.Force, "Force", "Force use of Spirits Within / Expiacion", 30, 0, ActionTargets.Hostile, 30) - .AddOption(OffensiveStrategy.Delay, "Delay", "Delay use of Spirits Within / Expiacion", 0, 0, ActionTargets.None, 30) + //Spirits Within definitions + res.Define(Track.SpiritsWithin).As("Spirits Within", "S.Within", uiPriority: 150) + .AddOption(OGCDStrategy.Automatic, "Automatic", "Use Spirits Within normally") + .AddOption(OGCDStrategy.Force, "Force", "Force use of Spirits Within / Expiacion", 30, 0, ActionTargets.Hostile, 30) + .AddOption(OGCDStrategy.AnyWeave, "Any Weave", "Force use of Spirits Within / Expiacion in any weave slot", 30, 0, ActionTargets.Hostile, 30) + .AddOption(OGCDStrategy.EarlyWeave, "Early Weave", "Force use of Spirits Within / Expiacion in the first weave slot", 30, 0, ActionTargets.Hostile, 30) + .AddOption(OGCDStrategy.LateWeave, "Late Weave", "Force use of Spirits Within / Expiacion in the last weave slot", 30, 0, ActionTargets.Hostile, 30) + .AddOption(OGCDStrategy.Delay, "Delay", "Delay use of Spirits Within / Expiacion", 0, 0, ActionTargets.None, 30) .AddAssociatedActions(AID.SpiritsWithin, AID.Expiacion); - - //Circle of Scorn Strategy: Control the use of Circle of Scorn - res.Define(Track.CircleOfScorn).As("Circle of Scorn Strategy", "Circle of Scorn", uiPriority: 150) - .AddOption(OffensiveStrategy.Automatic, "Automatic", "Use Circle of Scorn normally") - .AddOption(OffensiveStrategy.Force, "Force", "Force use of Circle of Scorn ASAP", 60, 15, ActionTargets.Hostile, 50) - .AddOption(OffensiveStrategy.Delay, "Delay", "Delay use of Circle of Scorn", 0, 0, ActionTargets.None, 50) + //Circle of Scorn definitions + res.Define(Track.CircleOfScorn).As("Circle of Scorn", "C.Scorn", uiPriority: 150) + .AddOption(OGCDStrategy.Automatic, "Automatic", "Use Circle of Scorn normally") + .AddOption(OGCDStrategy.Force, "Force", "Force use of Circle of Scorn ASAP", 30, 15, ActionTargets.Self, 50) + .AddOption(OGCDStrategy.AnyWeave, "Any Weave", "Force use of Circle of Scorn in any weave slot", 30, 15, ActionTargets.Self, 50) + .AddOption(OGCDStrategy.EarlyWeave, "Early Weave", "Force use of Circle of Scorn in the first weave slot", 30, 15, ActionTargets.Self, 50) + .AddOption(OGCDStrategy.LateWeave, "Late Weave", "Force use of Circle of Scorn in the last weave slot", 30, 15, ActionTargets.Self, 50) + .AddOption(OGCDStrategy.Delay, "Delay", "Delay use of Circle of Scorn", 0, 0, ActionTargets.None, 50) .AddAssociatedActions(AID.CircleOfScorn); - - //Goring Blade Strategy: Manage the Goring Blade action - res.Define(Track.GoringBlade).As("Goring Blade Strategy", "Sonic Break", uiPriority: 150) - .AddOption(OffensiveStrategy.Automatic, "Automatic", "Use Goring Blade normally") - .AddOption(OffensiveStrategy.Force, "Force", "Force use of Goring Blade ASAP", 0, 30, ActionTargets.Hostile, 54) - .AddOption(OffensiveStrategy.Delay, "Delay", "Delay use of Goring Blade", 0, 0, ActionTargets.None, 54) + //Goring Blade definitions + res.Define(Track.GoringBlade).As("Goring Blade", "G.Blade", uiPriority: 150) + .AddOption(GCDStrategy.Automatic, "Automatic", "Use Goring Blade normally") + .AddOption(GCDStrategy.Force, "Force", "Force use of Goring Blade ASAP", 0, 0, ActionTargets.Hostile, 54) + .AddOption(GCDStrategy.Delay, "Delay", "Delay use of Goring Blade", 0, 0, ActionTargets.None, 54) .AddAssociatedActions(AID.GoringBlade); - - //Holy Spirit Strategy: Control usage of Holy Spirit ability - res.Define(Track.HolySpirit).As("Holy Spirit Strategy", "Holy Spirit", uiPriority: 140) - .AddOption(OffensiveStrategy.Automatic, "Automatic", "Use Holy Spirit normally") - .AddOption(OffensiveStrategy.Force, "Force", "Force use of Holy Spirit ASAP", 0, 0, ActionTargets.Hostile, 30) - .AddOption(OffensiveStrategy.Delay, "Delay", "Delay use of Holy Spirit", 0, 0, ActionTargets.None, 30) - .AddAssociatedActions(AID.HolySpirit); - - //Holy Circle Strategy: Manage usage of Holy Circle ability - res.Define(Track.HolyCircle).As("Holy Circle Strategy", "Holy Circle", uiPriority: 140) - .AddOption(OffensiveStrategy.Automatic, "Automatic", "Use Holy Circle normally") - .AddOption(OffensiveStrategy.Force, "Force", "Force use of Holy Circle ASAP", 0, 0, ActionTargets.Hostile, 72) - .AddOption(OffensiveStrategy.Delay, "Delay", "Delay use of Holy Circle", 0, 0, ActionTargets.None, 72) - .AddAssociatedActions(AID.HolyCircle); - - //Blade of Honor Strategy: Manage usage of Blade of Honor ability - res.Define(Track.BladeOfHonor).As("Blade of Honor Strategy", "Blade of Honor", uiPriority: 150) - .AddOption(OffensiveStrategy.Automatic, "Automatic", "Use Blade of Honor normally") - .AddOption(OffensiveStrategy.Force, "Force", "Force use of Blade of Honor ASAP", 0, 0, ActionTargets.Hostile, 100) - .AddOption(OffensiveStrategy.Delay, "Delay", "Delay use of Blade of Honor", 0, 0, ActionTargets.None, 100) + //Blade of Honor definitions + res.Define(Track.BladeOfHonor).As("Blade of Honor", "B.Honor", uiPriority: 150) + .AddOption(OGCDStrategy.Automatic, "Automatic", "Use Blade of Honor normally") + .AddOption(OGCDStrategy.Force, "Force", "Force use of Blade of Honor ASAP", 0, 0, ActionTargets.Hostile, 100) + .AddOption(OGCDStrategy.AnyWeave, "Any Weave", "Force use of Blade of Honor in any weave slot", 0, 0, ActionTargets.Hostile, 100) + .AddOption(OGCDStrategy.EarlyWeave, "Early Weave", "Force use of Blade of Honor in the first weave slot", 0, 0, ActionTargets.Hostile, 100) + .AddOption(OGCDStrategy.LateWeave, "Late Weave", "Force use of Blade of Honor in the last weave slot", 0, 0, ActionTargets.Hostile, 100) + .AddOption(OGCDStrategy.Delay, "Delay", "Delay use of Blade of Honor", 0, 0, ActionTargets.None, 100) .AddAssociatedActions(AID.BladeOfHonor); return res; } + #endregion + #region Priorities public enum GCDPriority //Priority for GCDs used { None = 0, - Combo123 = 350, - NormalGCD = 450, - HolyCircle = 490, - HolySpirit = 500, - Atonement3 = 560, - Atonement2 = 570, - Atonement1 = 580, - GoringBlade = 590, - Valor = 600, - Truth = 610, - Faith = 620, - Confiteor = 650, + Combo123 = 300, + HolySpirit = 400, + Atonement = 500, + GoringBlade = 600, + Blades = 700, ForcedGCD = 900, } - public enum OGCDPriority //Priority for oGCDs used { None = 0, - BladeOfHonor = 520, - Intervene = 530, - SpiritsWithin = 540, + BladeOfHonor = 400, + Intervene = 450, + SpiritsWithin = 500, CircleOfScorn = 550, - Requiescat = 650, + Requiescat = 600, FightOrFlight = 700, ForcedOGCD = 900, Potion = 910, } - + #endregion + + #region Upgrade Paths + public AID BestSpirits + => Unlocked(AID.Expiacion) //if Expiacion is unlocked + ? AID.Expiacion //then use Expiacion + : AID.SpiritsWithin; //otherwise use Spirits Within + public AID BestRequiescat + => Unlocked(AID.Imperator) //if Imperator is unlocked + ? AID.Imperator //then use Imperator + : AID.Requiescat; //otherwise use Requiescat + public AID BestHoly + => ShouldUseDMHolyCircle || ShouldNormalHolyCircle //if Holy Circle should be used + ? BestHolyCircle //then use Holy Circle + : AID.HolySpirit; //otherwise use Holy Spirit + public AID BestHolyCircle //for AOE use from Lv64-Lv71 + => HolyCircle.IsReady //if Holy Circle is ready + ? AID.HolyCircle //then use Holy Circle + : AID.HolySpirit; //then use Holy Spirit + public AID BestAtonement + => Sepulchre.IsReady //if Sepulchre is ready + ? AID.Sepulchre //then use Sepulchre + : Supplication.IsReady //if Supplication is ready + ? AID.Supplication //then use Supplication + : AID.Atonement; //otherwise use Atonement + public AID BestBlade + => BladeComboStep is 3 //if Confiteor combo is at step 3 + ? AID.BladeOfValor //then use Blade of Valor + : BladeComboStep is 2 //if Confiteor combo is at step 2 + ? AID.BladeOfTruth //then use Blade of Truth + : BladeComboStep is 1 //if Confiteor combo is at step 1 + && Unlocked(AID.BladeOfFaith) + ? AID.BladeOfFaith //then use Blade of Faith + : Unlocked(AID.Confiteor) //if Confiteor is unlocked + ? AID.Confiteor //otherwise use Confiteor + : BestHoly; + #endregion + + #region Module Variables public int Oath; //Current value of the oath gauge public int BladeComboStep; //Current step in the Confiteor combo sequence - public float GCDLength; //Length of the global cooldown, adjusted by skill speed and haste (baseline: 2.5s) - public float PotionLeft; //Remaining duration of the potion buff (typically 30s) public float RaidBuffsLeft; //Remaining duration of active raid-wide buffs (typically 20s-22s) public float RaidBuffsIn; //Time until the next set of raid-wide buffs are applied (typically 20s-22s) - - public float BurstWindowLeft; //Time remaining in the current burst window (typically 20s-22s) - public float BurstWindowIn; //Time until the next burst window begins (typically 20s-22s) - - //Buff and cooldown management - public float fofCD; //Cooldown remaining on the Fight or Flight ability - public float fofLeft; //Time left before Fight or Flight is available again - public float reqCD; //Cooldown remaining on the Requiescat ability - public (float Left, int Stacks) req; //Remaining cooldown for Requiescat, along with its stack count - public uint playerMP; //Current MP (mana points) of the player - - //Buff status indicators - public bool hasFoF; //Indicates if the Fight or Flight buff is currently active - public bool hasReq; //Indicates if the Requiescat buff is currently active - public bool hasMight; //Indicates if the Divine Might buff is currently active - public bool hasMPforMight; //Checks if there is enough MP to use Holy Spirit with Divine Might - public bool hasMPforReq; //Checks if there is enough MP to use Holy Spirit under the Requiescat buff - - //Phase and condition monitoring - public bool isDivineMightExpiring; //Indicates if the Divine Might buff is nearing expiration - public bool isAtonementExpiring; //Indicates if any Atonement buffs are about to expire - + public float CooldownsWindowLeft; //Time remaining in the current Cooldowns window (typically 20s-22s) + public float CooldownsWindowIn; //Time until the next Cooldowns window begins (typically 20s-22s) + public (float Left, bool IsActive) DivineMight; //Conditions for the Divine Might buff + public (float CD, float Left, bool IsReady, bool IsActive) FightOrFlight; //Conditions for Fight or Flight ability + public (float CD, bool IsReady) SpiritsWithin; //Conditions for Spirits Within ability + public (float CD, bool IsReady) CircleOfScorn; //Conditions for Circle of Scorn ability + public (float CD, float Left, bool IsReady, bool IsActive) GoringBlade; //Conditions for Goring Blade ability + public (float TotalCD, bool HasCharges, bool IsReady) Intervene; //Conditions for Intervene ability + public (float CD, float Left, bool IsReady, bool IsActive) Requiescat; //Conditions for Requiescat ability + public (float Left, bool IsReady, bool IsActive) Atonement; //Conditions for Atonement ability + public (float Left, bool IsReady, bool IsActive) Supplication; //Conditions for Supplication ability + public (float Left, bool IsReady, bool IsActive) Sepulchre; //Conditions for Sepulchre ability + public (float Left, bool HasMP, bool IsReady, bool IsActive) Confiteor; //Conditions for Confiteor ability + public (float Left, bool IsReady, bool IsActive) BladeOfHonor; //Conditions for Blade of Honor ability + public (bool HasMP, bool IsReady) HolySpirit; //Conditions for Holy Spirit ability + public (bool HasMP, bool IsReady) HolyCircle; //Conditions for Holy Circle ability + public uint MP; //Current MP of the player + public bool ShouldUseAOE; //Check if AOE rotation should be used + public bool ShouldNormalHolyCircle; //Check if Holy Circle should be used + public bool ShouldUseDMHolyCircle; //Check if Holy Circle should be used under Divine Might public AID NextGCD; //The next action to be executed during the global cooldown (for cartridge management) - private GCDPriority NextGCDPrio; //Priority of the next global cooldown action for decision-making on cooldowns - - //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; - - //Check if we can fit an additional GCD within the provided deadline - private bool CanFitGCD(float deadline, int extraGCDs = 0) => GCD + GCDLength * extraGCDs < deadline; - - //Get the last action used in the combo sequence - private AID ComboLastMove => (AID)World.Client.ComboState.Action; - - //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 5 yalms - private bool In5y(Actor? target) => Player.DistanceToHitbox(target) <= 4.75; - - //Check if the desired action is ready (cooldown less than 0.6 seconds) - private bool ActionReady(AID aid) => World.Client.Cooldowns[ActionDefinitions.Instance.Spell(aid)!.MainCooldownGroup].Remaining < 0.6f; - - //Check if this is the first GCD in combat - private bool IsFirstGCD() => !Player.InCombat || (World.CurrentTime - Manager.CombatStart).TotalSeconds < 0.1f; - - //Returns the number of targets hit by AoE within a 5-yalm radius around the player - private int NumTargetsHitByAoE() => Hints.NumPriorityTargetsInAOECircle(Player.Position, 5); - - //Checks if the potion should be used before raid buffs expire - private bool IsPotionBeforeRaidbuffs() => RaidBuffsLeft == 0 && PotionLeft > RaidBuffsIn + 17.5f; - - //Checks if Status effect is on self - public bool HasEffect(SID sid) where SID : Enum => Player.FindStatus((uint)(object)sid, Player.InstanceID) != null; - - //Calculates the elapsed time since combat started in seconds - public float CombatTimer => (float)(World.CurrentTime - Manager.CombatStart).TotalSeconds; + public bool canWeaveIn; //Can weave in oGCDs + public bool canWeaveEarly; //Can early weave oGCDs + public bool canWeaveLate; //Can late weave oGCDs + #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 AID ComboLastMove => (AID)World.Client.ComboState.Action; //Get the last action used in the combo sequence + private bool In3y(Actor? target) => Player.DistanceToHitbox(target) <= 2.99f; //Check if the target is within ST melee range (3 yalms) + private bool In5y(Actor? target) => Player.DistanceToHitbox(target) <= 4.99f; //Check if the target is within AOE melee range (5 yalms) + private bool In25y(Actor? target) => Player.DistanceToHitbox(target) <= 24.99f; //Check if the target is within 25 yalms + private bool IsFirstGCD() => !Player.InCombat || (World.CurrentTime - Manager.CombatStart).TotalSeconds < 0.1f; //Check if this is the first GCD in combat + private int TargetsHitByPlayerAOE() => 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) > 0; //Checks if Status effect is on self + public float CombatTimer => (float)(World.CurrentTime - Manager.CombatStart).TotalSeconds; //Calculates the elapsed time since combat started in seconds + public Actor? TargetChoice(StrategyValues.OptionRef strategy) => ResolveTargetOverride(strategy.Value); //Resolves the target choice based on the strategy + + //TODO: add better targeting for Blades + //public Actor? BestSplashTarget() + #endregion public override void Execute(StrategyValues strategy, Actor? primaryTarget, float estimatedAnimLockDelay, bool isMoving) //Executes our actions { - - var gauge = World.Client.GetGauge(); //Retrieve current Paladin gauge - Oath = gauge.OathGauge; //Get the current value of the Oath gauge - BladeComboStep = gauge.ConfiteorComboStep; //Get the current step in the Confiteor/Blades combo - - //Buff and cooldown management - fofCD = CD(AID.FightOrFlight); //Remaining cooldown for the Fight or Flight ability - fofLeft = SelfStatusLeft(SID.FightOrFlight); //Remaining duration of the Fight or Flight buff - reqCD = CD(AID.Requiescat); //Remaining cooldown for the Requiescat ability - playerMP = Player.HPMP.CurMP; //Current mana points (MP) of the player - - //Buff status checks - hasFoF = fofCD is >= 40 and <= 60; //Check if the Fight or Flight buff is active (within cooldown range) - hasReq = HasEffect(AID.Requiescat); //Check if the Requiescat buff is active - hasMight = HasEffect(SID.DivineMight); //Check if the Divine Might buff is active - hasMPforMight = playerMP >= 1000; //Check if there is enough MP for Holy Spirit while using Divine Might - hasMPforReq = playerMP >= 1000 * 3.6; //Check if there is enough MP for Holy Spirit under the Requiescat buff - - //Phase and condition monitoring - isDivineMightExpiring = SelfStatusLeft(SID.DivineMight) < 6; //Check if the Divine Might buff is about to expire - isAtonementExpiring = - HasEffect(SID.AtonementReady) && SelfStatusLeft(SID.AtonementReady) < 6 || - HasEffect(SID.SupplicationReady) && SelfStatusLeft(SID.SupplicationReady) < 6 || - HasEffect(SID.SepulchreReady) && SelfStatusLeft(SID.SepulchreReady) < 6; //Check if any Atonement buffs are close to expiring - + #region Variables + var gauge = World.Client.GetGauge(); //Retrieve Paladin gauge + Oath = gauge.OathGauge; //Retrieve current Oath gauge + BladeComboStep = gauge.ConfiteorComboStep; //Retrieve current step in the Confiteor/Blades combo + MP = Player.HPMP.CurMP; //Current MP of the player + DivineMight.Left = SelfStatusLeft(SID.DivineMight, 30); //Remaining duration of the Divine Might buff + DivineMight.IsActive = DivineMight.Left > 0; //Check if the Divine Might buff is active + FightOrFlight.CD = CD(AID.FightOrFlight); //Remaining cooldown for the Fight or Flight ability + FightOrFlight.Left = SelfStatusLeft(SID.FightOrFlight, 20); //Remaining duration of the Fight or Flight buff + FightOrFlight.IsActive = FightOrFlight.CD is >= 39.5f and <= 60; //Check if the Fight or Flight buff is active + FightOrFlight.IsReady = FightOrFlight.CD < 0.6f; //Fight or Flight ability + SpiritsWithin.CD = CD(BestSpirits); //Remaining cooldown for the Spirits Within ability + SpiritsWithin.IsReady = Unlocked(AID.SpiritsWithin) && SpiritsWithin.CD < 0.6f; //Spirits Within ability + CircleOfScorn.CD = CD(AID.CircleOfScorn); //Remaining cooldown for the Circle of Scorn ability + CircleOfScorn.IsReady = Unlocked(AID.CircleOfScorn) && CircleOfScorn.CD < 0.6f; //Circle of Scorn ability + GoringBlade.CD = CD(AID.GoringBlade); //Remaining cooldown for the Goring Blade ability + GoringBlade.Left = SelfStatusLeft(SID.GoringBladeReady, 30); //Remaining duration of the Goring Blade buff + GoringBlade.IsActive = GoringBlade.Left > 0; //Check if the Goring Blade buff is active + GoringBlade.IsReady = Unlocked(AID.GoringBlade) && GoringBlade.IsActive; //Goring Blade ability + Intervene.TotalCD = CD(AID.Intervene); //Total cooldown for the Intervene ability (60s) + Intervene.HasCharges = Intervene.TotalCD <= 30f; //Check if the player has charges of Intervene + Intervene.IsReady = Unlocked(AID.Intervene) && Intervene.HasCharges; //Intervene ability + Requiescat.CD = CD(BestRequiescat); //Remaining cooldown for the Requiescat ability + Requiescat.Left = SelfStatusLeft(SID.Requiescat, 30); //Get the current number of Requiescat stacks + Requiescat.IsActive = Requiescat.Left > 0; //Check if the Requiescat buff is active + Requiescat.IsReady = Unlocked(AID.Requiescat) && Requiescat.CD < 0.6f; //Requiescat ability + Atonement.Left = SelfStatusLeft(SID.AtonementReady, 30); //Remaining duration of the Atonement buff + Atonement.IsActive = Atonement.Left > 0; //Check if the Atonement buff is active + Atonement.IsReady = Unlocked(AID.Atonement) && Atonement.IsActive; //Atonement ability + Supplication.Left = SelfStatusLeft(SID.SupplicationReady, 30); //Remaining duration of the Supplication buff + Supplication.IsActive = Supplication.Left > 0; //Check if the Supplication buff is active + Supplication.IsReady = Unlocked(AID.Supplication) && Supplication.IsActive; //Supplication ability + Sepulchre.Left = SelfStatusLeft(SID.SepulchreReady, 30); //Remaining duration of the Sepulchre buff + Sepulchre.IsActive = Sepulchre.Left > 0; //Check if the Sepulchre buff is active + Sepulchre.IsReady = Unlocked(AID.Sepulchre) && Sepulchre.IsActive; //Sepulchre ability + Confiteor.Left = SelfStatusLeft(SID.ConfiteorReady, 30); //Remaining duration of the Confiteor buff + Confiteor.IsActive = Confiteor.Left > 0; //Check if the Confiteor buff is active + Confiteor.IsReady = Unlocked(AID.Confiteor) && Confiteor.IsActive && MP >= 1000; //Confiteor ability + BladeOfHonor.Left = SelfStatusLeft(SID.BladeOfHonorReady, 30); //Remaining duration of the Blade of Honor buff + BladeOfHonor.IsActive = BladeOfHonor.Left > 0; //Check if the Blade of Honor buff is active + BladeOfHonor.IsReady = Unlocked(AID.BladeOfHonor) && BladeOfHonor.IsActive; //Checks if Blade of Honor is ready + HolySpirit.HasMP = MP >= 1000; //Check if the player has enough MP for Holy Spirit + HolySpirit.IsReady = Unlocked(AID.HolySpirit) && HolySpirit.HasMP; //Holy Spirit ability + HolyCircle.HasMP = MP >= 1000; //Check if the player has enough MP for Holy Circle + HolyCircle.IsReady = Unlocked(AID.HolyCircle) && HolyCircle.HasMP; //Holy Circle ability GCDLength = ActionSpeed.GCDRounded(World.Client.PlayerStats.SkillSpeed, World.Client.PlayerStats.Haste, Player.Level); //Calculate GCD based on skill speed and haste - - //Buff durations PotionLeft = PotionStatusLeft(); //Remaining duration of the potion buff (typically 30s) - - //Raid buff timings (RaidBuffsLeft, RaidBuffsIn) = EstimateRaidBuffTimings(primaryTarget); //Estimate remaining and incoming raid buff durations - - //Next global cooldown action NextGCD = AID.None; //Initialize next action as none - NextGCDPrio = GCDPriority.None; //Set next GCD priority to none - - //Define the AoE strategy and determine the number of targets hit - var AOEStrategy = strategy.Option(Track.AoE).As(); - var AoETargets = AOEStrategy switch + 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 + ShouldUseAOE = TargetsHitByPlayerAOE() > 2; //Check if AOE rotation should be used + ShouldNormalHolyCircle = !DivineMight.IsActive && TargetsHitByPlayerAOE() > 3; //Check if Holy Circle should be used (very niche) + ShouldUseDMHolyCircle = DivineMight.IsActive && TargetsHitByPlayerAOE() > 2; //Check if Holy Circle should be used under Divine Might + + #region Strategy Options + var AOE = strategy.Option(Track.AOE); //Retrieves AOE track + var AOEStrategy = AOE.As(); //Retrieves AOE strategy + var cd = strategy.Option(Track.Cooldowns); //Retrieves Cooldowns track + var cdStrat = cd.As(); //Retrieves Cooldowns strategy + var pot = strategy.Option(Track.Potion); //Retrieves Potion track + var potStrat = pot.As(); //Retrieves Potion strategy + var fof = strategy.Option(Track.FightOrFlight); //Retrieves Fight or Flight track + var fofStrat = fof.As(); //Retrieves Fight or Flight strategy + var req = strategy.Option(Track.Requiescat); //Retrieves Requiescat track + var reqStrat = req.As(); //Retrieves Requiescat strategy + var atone = strategy.Option(Track.Atonement); //Retrieves Atonement track + var atoneStrat = atone.As(); //Retrieves Atonement strategy + var blade = strategy.Option(Track.BladeCombo); //Retrieves Blade Combo track + var bladeStrat = blade.As(); //Retrieves Blade Combo strategy + var cos = strategy.Option(Track.CircleOfScorn); //Retrieves Circle of Scorn track + var cosStrat = cos.As(); //Retrieves Circle of Scorn strategy + var sw = strategy.Option(Track.SpiritsWithin); //Retrieves Spirits Within track + var swStrat = sw.As(); //Retrieves Spirits Within strategy + var dash = strategy.Option(Track.Dash); //Retrieves Dash track + var dashStrat = dash.As(); //Retrieves Dash strategy + var gb = strategy.Option(Track.GoringBlade); //Retrieves Goring Blade track + var gbStrat = gb.As(); //Retrieves Goring Blade strategy + var boh = strategy.Option(Track.BladeOfHonor); //Retrieves Blade of Honor track + var bohStrat = boh.As(); //Retrieves Blade of Honor strategy + var holy = strategy.Option(Track.Holy); //Retrieves Holy track + var holyStrat = holy.As(); //Retrieves Holy strategy + var ranged = strategy.Option(Track.Ranged); //Retrieves Ranged track + var rangedStrat = ranged.As(); //Retrieves Ranged strategy + #endregion + + #endregion + + #region Standard Execution + if (AOEStrategy == AOEStrategy.AutoBreakCombo) //if Break Combo option is selected { - AOEStrategy.UseST => NumTargetsHitByAoE() > 0 ? 1 : 0, //Use single-target actions if any AoE targets exist - AOEStrategy.UseAoE => NumTargetsHitByAoE() > 0 ? 100 : 0, //Use AoE actions if any targets are hit - _ => NumTargetsHitByAoE() //Default to the number of targets hit by AoE - }; - - //Burst (raid buff) windows typically last 20 seconds every 120 seconds - var burst = strategy.Option(Track.Burst); - var burstStrategy = burst.As(); - var hold = burstStrategy == BurstStrategy.Conserve; //Determine if we are conserving cartridges - - //Calculate the burst window based on the current strategy - (BurstWindowIn, BurstWindowLeft) = burstStrategy switch + if (ShouldUseAOE) //if AOE rotation should be used + QueueGCD(RotationAOE(), //queue the next AOE combo action + Player, //on Self (no target needed) + GCDPriority.Combo123); //use priority for 123/12 combo actions + if (!ShouldUseAOE) + QueueGCD(RotationST(), //queue the next single-target combo action + TargetChoice(AOE) //Get target choice + ?? primaryTarget, //if none, choose primary target + GCDPriority.Combo123); //use priority for 123/12 combo actions + } + if (AOEStrategy == AOEStrategy.AutoFinishCombo) //if Finish Combo option is selected { - BurstStrategy.Automatic => (RaidBuffsIn, IsPotionBeforeRaidbuffs() ? 0 : Math.Max(PotionLeft, RaidBuffsLeft)), //Automatically calculate based on current buffs and potions - BurstStrategy.UnderRaidBuffs => (RaidBuffsIn, RaidBuffsLeft), //Under raid buffs, use remaining durations - BurstStrategy.UnderPotion => (PotionCD, PotionLeft), //Under potion effects, use potion cooldown and remaining duration - _ => (0, 0), //no burst window - }; - - //Check GCD (Global Cooldown) conditions for various abilities - var canFoF = Unlocked(AID.FightOrFlight) && ActionReady(AID.FightOrFlight); //Fight or Flight ability - var canReq = Unlocked(AID.Requiescat) && ActionReady(AID.Requiescat); //Requiescat ability - var canScorn = Unlocked(AID.CircleOfScorn) && ActionReady(AID.CircleOfScorn); //Circle of Scorn ability - var canSpirit = Unlocked(AID.SpiritsWithin) && ActionReady(AID.SpiritsWithin); //Spirits Within ability - var canGB = Unlocked(AID.GoringBlade) && HasEffect(SID.GoringBladeReady); //Goring Blade ability readiness - var canHS = Unlocked(AID.HolySpirit); //Holy Spirit ability - var canHC = Unlocked(AID.HolyCircle); //Holy Circle ability - var canAtone = Unlocked(AID.Atonement) && HasEffect(SID.AtonementReady); //Atonement ability readiness - var canDash = Unlocked(AID.Intervene) && CD(AID.Intervene) <= 30; //Intervene ability with cooldown check - var canConfiteor = Unlocked(AID.Confiteor) && HasEffect(SID.ConfiteorReady) && BladeComboStep is 0; //Confiteor ability readiness and combo step check - var canBlade = Unlocked(AID.BladeOfValor) && BladeComboStep is not 0; //Blade abilities, only if combo step is not zero - var canHonor = Unlocked(AID.BladeOfHonor) && HasEffect(SID.BladeOfHonorReady); //Blade of Honor ability readiness - - //Determine and queue the next combo action based on AoE strategy - var (comboAction, comboPrio) = ComboActionPriority(AOEStrategy, AoETargets, burstStrategy, burst.Value.ExpireIn); - QueueGCD(comboAction, comboAction is AID.TotalEclipse or AID.Prominence ? Player : primaryTarget, - AOEStrategy is AOEStrategy.ForceST or AOEStrategy.ForceAoE ? GCDPriority.ForcedGCD : comboPrio); - - //Execute Fight or Flight if conditions are met - var fofStrat = strategy.Option(Track.FightOrFlight).As(); - if (!hold && canFoF && ShouldUseFightOrFlight(fofStrat, primaryTarget)) - QueueOGCD(AID.FightOrFlight, Player, fofStrat is OffensiveStrategy.Force ? OGCDPriority.ForcedOGCD : OGCDPriority.FightOrFlight); - - //Execute Requiescat if conditions are met - var reqStrat = strategy.Option(Track.Requiescat).As(); - if (!hold && canReq && ShouldUseRequiescat(reqStrat, primaryTarget)) - QueueOGCD(Unlocked(AID.Imperator) ? AID.Imperator : AID.Requiescat, primaryTarget, reqStrat is OffensiveStrategy.Force ? OGCDPriority.ForcedOGCD : OGCDPriority.Requiescat); - - //Execute Confiteor if conditions are met - var bladeStrat = strategy.Option(Track.BladeCombo).As(); - if (canConfiteor && BladeComboStep is 0 && ShouldUseBladeCombo(bladeStrat, primaryTarget)) - QueueGCD(AID.Confiteor, primaryTarget, bladeStrat is BladeComboStrategy.ForceConfiteor ? GCDPriority.ForcedGCD : GCDPriority.Confiteor); - - //Execute Circle of Scorn if conditions are met - var scornStrat = strategy.Option(Track.CircleOfScorn).As(); - if (!hold && canScorn && ShouldUseCircleOfScorn(scornStrat, primaryTarget)) - QueueOGCD(AID.CircleOfScorn, primaryTarget, scornStrat is OffensiveStrategy.Force ? OGCDPriority.ForcedOGCD : OGCDPriority.CircleOfScorn); - - //Execute Spirits Within if conditions are met - var spiritsStrat = strategy.Option(Track.SpiritsWithin).As(); - if (!hold && canSpirit && ShouldUseSpiritsWithin(spiritsStrat, primaryTarget)) - QueueOGCD(Unlocked(AID.Expiacion) ? AID.Expiacion : AID.SpiritsWithin, primaryTarget, spiritsStrat is OffensiveStrategy.Force ? OGCDPriority.ForcedOGCD : OGCDPriority.SpiritsWithin); + QueueGCD(BestRotation(), //queue the next single-target combo action only if combo is finished + TargetChoice(AOE) //Get target choice + ?? primaryTarget, //if none, choose primary target + GCDPriority.Combo123); //use priority for 123/12 combo actions + } + #endregion - //Execute Blade Combo actions based on the current combo step - if (canBlade) + #region Cooldowns Execution + var hold = cdStrat == CooldownStrategy.Hold; //Check if Cooldowns should be held + if (!hold) //if Cooldowns should not be held { - if (BladeComboStep is 1) - QueueGCD(AID.BladeOfFaith, primaryTarget, bladeStrat is BladeComboStrategy.ForceFaith ? GCDPriority.ForcedGCD : GCDPriority.Faith); - if (BladeComboStep is 2) - QueueGCD(AID.BladeOfTruth, primaryTarget, bladeStrat is BladeComboStrategy.ForceTruth ? GCDPriority.ForcedGCD : GCDPriority.Truth); - if (BladeComboStep is 3) - QueueGCD(AID.BladeOfValor, primaryTarget, bladeStrat is BladeComboStrategy.ForceValor ? GCDPriority.ForcedGCD : GCDPriority.Valor); + if (ShouldUseFightOrFlight(fofStrat, primaryTarget)) //if Fight or Flight should be used + QueueOGCD(AID.FightOrFlight, //queue Fight or Flight + Player, //on Self (no target needed) + fofStrat is OGCDStrategy.Force //if Force strategy is selected + or OGCDStrategy.AnyWeave //or Any Weave strategy is selected + or OGCDStrategy.EarlyWeave //or Early Weave strategy is selected + or OGCDStrategy.LateWeave //or Late Weave strategy is selected + ? OGCDPriority.ForcedOGCD //use priority for forced oGCDs + : OGCDPriority.FightOrFlight); //otherwise with normal priority + if (ShouldUseRequiescat(reqStrat, primaryTarget)) //if Requiescat should be used + QueueOGCD(BestRequiescat, //queue Requiescat + TargetChoice(req) ?? primaryTarget, //with target choice + reqStrat is OGCDStrategy.Force //if Force strategy is selected + or OGCDStrategy.AnyWeave //or Any Weave strategy is selected + or OGCDStrategy.EarlyWeave //or Early Weave strategy is selected + or OGCDStrategy.LateWeave //or Late Weave strategy is selected + ? OGCDPriority.ForcedOGCD //use priority for forced oGCDs + : OGCDPriority.Requiescat); //otherwise with normal priority + if (bladeStrat == BladeComboStrategy.ForceConfiteor) //if Confiteor should be forced + QueueGCD(AID.Confiteor, //queue Confiteor + TargetChoice(blade) ?? primaryTarget, //with target choice + GCDPriority.ForcedGCD); //use priority for forced GCDs + if (ShouldUseCircleOfScorn(cosStrat, primaryTarget)) //if Circle of Scorn should be used + QueueOGCD(AID.CircleOfScorn, //queue Circle of Scorn + Player, //on Self (no target needed) + cosStrat is OGCDStrategy.Force //if Force strategy is selected + or OGCDStrategy.AnyWeave //or Any Weave strategy is selected + or OGCDStrategy.EarlyWeave //or Early Weave strategy is selected + or OGCDStrategy.LateWeave //or Late Weave strategy is selected + ? OGCDPriority.ForcedOGCD //use priority for forced oGCDs + : OGCDPriority.CircleOfScorn); //otherwise with normal priority + if (ShouldUseSpiritsWithin(swStrat, primaryTarget)) //if Spirits Within should be used + QueueOGCD(BestSpirits, //queue Spirits Within + TargetChoice(sw) ?? primaryTarget, //with target choice + swStrat is OGCDStrategy.Force //if Force strategy is selected + or OGCDStrategy.AnyWeave //or Any Weave strategy is selected + or OGCDStrategy.EarlyWeave //or Early Weave strategy is selected + or OGCDStrategy.LateWeave //or Late Weave strategy is selected + ? OGCDPriority.ForcedOGCD //use priority for forced oGCDs + : OGCDPriority.SpiritsWithin); //otherwise with normal priority + if (ShouldUseDash(dashStrat, primaryTarget)) //if Dash should be used + QueueOGCD(AID.Intervene, //queue Dash + TargetChoice(dash) ?? primaryTarget, //with target choice + dashStrat is DashStrategy.Force //if Force strategy is selected + or DashStrategy.Force1 //or Force1 strategy is selected + or DashStrategy.GapClose //or GapClose strategy is selected + or DashStrategy.GapClose1 //or GapClose1 strategy is selected + ? OGCDPriority.ForcedOGCD //use priority for forced oGCDs + : OGCDPriority.Intervene); //otherwise with normal priority + if (ShouldUseBladeOfHonor(bohStrat, primaryTarget)) //if Blade of Honor should be used + QueueOGCD(AID.BladeOfHonor, //queue Blade of Honor + TargetChoice(boh) ?? primaryTarget, //with target choice + bohStrat is OGCDStrategy.Force //if Force strategy is selected + or OGCDStrategy.AnyWeave //or Any Weave strategy is selected + or OGCDStrategy.EarlyWeave //or Early Weave strategy is selected + or OGCDStrategy.LateWeave //or Late Weave strategy is selected + ? OGCDPriority.ForcedOGCD //use priority for forced oGCDs + : OGCDPriority.BladeOfHonor); //otherwise with normal priority + if (ShouldUseGoringBlade(gbStrat, primaryTarget)) //if Goring Blade should be used + QueueGCD(AID.GoringBlade, //queue Goring Blade + TargetChoice(gb) ?? primaryTarget, //with target choice + gbStrat is GCDStrategy.Force //if Force strategy is selected + ? GCDPriority.ForcedGCD //use priority for forced GCDs + : GCDPriority.GoringBlade); //otherwise with normal priority } - - //Execute Intervene if conditions are met - var dashStrat = strategy.Option(Track.Dash).As(); - if (canDash && ShouldUseDash(dashStrat, primaryTarget)) - QueueOGCD(AID.Intervene, primaryTarget, dashStrat is DashStrategy.Force or DashStrategy.GapClose ? OGCDPriority.ForcedOGCD : OGCDPriority.Intervene); - - //Execute Goring Blade if conditions are met - var gbStrat = strategy.Option(Track.GoringBlade).As(); - if (!hold && canGB && ShouldUseGoringBlade(gbStrat, primaryTarget)) - QueueGCD(AID.GoringBlade, primaryTarget, gbStrat is OffensiveStrategy.Force ? GCDPriority.ForcedGCD : GCDPriority.GoringBlade); - - //Execute Blade of Honor if conditions are met - var bohStrat = strategy.Option(Track.GoringBlade).As(); - if (!hold && canHonor && ShouldUseGoringBlade(bohStrat, primaryTarget)) - QueueOGCD(AID.BladeOfHonor, primaryTarget, bohStrat is OffensiveStrategy.Force ? OGCDPriority.ForcedOGCD : OGCDPriority.BladeOfHonor); - - //Execute Atonement if conditions are met - var atoneStrat = strategy.Option(Track.Atonement).As(); - if (!hold && canAtone && ShouldUseAtonement(atoneStrat, primaryTarget)) - QueueGCD(AID.Atonement, primaryTarget, atoneStrat is AtonementStrategy.ForceAtonement ? GCDPriority.ForcedGCD : GCDPriority.Atonement1); - - //Execute Atonement Combo actions based on readiness - if (HasEffect(SID.SupplicationReady)) - QueueGCD(AID.Supplication, primaryTarget, atoneStrat is AtonementStrategy.ForceSupplication ? GCDPriority.ForcedGCD : GCDPriority.Atonement2); - if (HasEffect(SID.SepulchreReady)) - QueueGCD(AID.Sepulchre, primaryTarget, atoneStrat is AtonementStrategy.ForceSepulchre ? GCDPriority.ForcedGCD : GCDPriority.Atonement3); - - //Execute Holy Spirit if conditions are met - var hsStrat = strategy.Option(Track.HolySpirit).As(); - var hcStrat = strategy.Option(Track.HolyCircle).As(); - if (canHS && ShouldUseHolySpirit(hsStrat, primaryTarget)) - QueueGCD(AID.HolySpirit, primaryTarget, hsStrat is OffensiveStrategy.Force ? GCDPriority.ForcedGCD : GCDPriority.HolySpirit); - - //Execute Holy Circle if conditions are met - if (canHC && ShouldUseHolyCircle(hcStrat, primaryTarget)) - QueueGCD(AID.HolyCircle, primaryTarget, hcStrat is OffensiveStrategy.Force ? GCDPriority.ForcedGCD : GCDPriority.HolyCircle); - if (!canHC && canHS) - QueueGCD(AID.HolySpirit, primaryTarget, hsStrat is OffensiveStrategy.Force ? GCDPriority.ForcedGCD : GCDPriority.HolySpirit); - - //Execute ranged skills based on strategy - var rangedStrat = strategy.Option(Track.Ranged).As(); - if (ShouldUseRangedLob(primaryTarget, rangedStrat)) - QueueGCD(AID.ShieldLob, primaryTarget, rangedStrat is RangedStrategy.Force ? GCDPriority.ForcedGCD : GCDPriority.NormalGCD); - if (ShouldUseRangedCast(primaryTarget, rangedStrat)) - QueueGCD(AID.HolySpirit, primaryTarget, rangedStrat is RangedStrategy.ForceCast ? GCDPriority.ForcedGCD : GCDPriority.NormalGCD); - - //Execute potion if conditions are met - if (ShouldUsePotion(strategy.Option(Track.Potion).As())) - Hints.ActionsToExecute.Push(ActionDefinitions.IDPotionStr, Player, ActionQueue.Priority.VeryHigh + (int)OGCDPriority.Potion, 0, GCD - 0.9f); - + if (ShouldUseBladeCombo(bladeStrat, primaryTarget)) //if Blade Combo should be used + { + if (bladeStrat is BladeComboStrategy.Automatic) //if Automatic strategy is selected + QueueGCD(BestBlade, //queue the best Blade combo action + TargetChoice(blade) ?? primaryTarget, //with target choice + GCDPriority.Blades); //use priority for Blade combo actions + if (bladeStrat is BladeComboStrategy.ForceFaith) //if Force Faith strategy is selected + QueueGCD(AID.BladeOfFaith, //queue Blade of Faith + TargetChoice(blade) ?? primaryTarget, //with target choice + GCDPriority.ForcedGCD); //use priority for forced GCDs + if (bladeStrat is BladeComboStrategy.ForceTruth) //if Force Truth strategy is selected + QueueGCD(AID.BladeOfTruth, //queue Blade of Truth + TargetChoice(blade) ?? primaryTarget, //with target choice + GCDPriority.ForcedGCD); //use priority for forced GCDs + if (bladeStrat is BladeComboStrategy.ForceValor) //if Force Valor strategy is selected + QueueGCD(AID.BladeOfValor, //queue Blade of Valor + TargetChoice(blade) ?? primaryTarget, //with target choice + GCDPriority.ForcedGCD); //use priority for forced GCDs + } + if (ShouldUseAtonement(atoneStrat, primaryTarget)) //if Atonement should be used + { + if (atoneStrat == AtonementStrategy.Automatic) //if Automatic strategy is selected + QueueGCD(BestAtonement, //queue the best Atonement action + TargetChoice(atone) ?? primaryTarget, //with target choice + GCDPriority.Atonement); //use priority for Atonement actions + if (atoneStrat is AtonementStrategy.ForceAtonement) //if Force Atonement strategy is selected + QueueGCD(AID.Atonement, //queue Atonement + TargetChoice(atone) ?? primaryTarget, //with target choice + GCDPriority.ForcedGCD); //use priority for forced GCDs + if (atoneStrat is AtonementStrategy.ForceSupplication) //if Force Supplication strategy is selected + QueueGCD(AID.Supplication, //queue Supplication + TargetChoice(atone) ?? primaryTarget, //with target choice + GCDPriority.ForcedGCD); //use priority for forced GCDs + if (atoneStrat is AtonementStrategy.ForceSepulchre) //if Force Sepulchre strategy is selected + QueueGCD(AID.Sepulchre, //queue Sepulchre + TargetChoice(atone) ?? primaryTarget, //with target choice + GCDPriority.ForcedGCD); //use priority for forced GCDs + } + if (ShouldUseHoly(holyStrat, primaryTarget)) //if Holy Spirit / Circle should be used + { + if (holyStrat == HolyStrategy.Automatic) //if Automatic strategy is selected + QueueGCD(BestHoly, //queue the best Holy action + TargetChoice(holy) ?? primaryTarget, //with target choice + FightOrFlight.Left is <= 2.5f and >= 0.01f //if Fight or Flight is active + && !Supplication.IsActive //and Supplication is not active + && !Sepulchre.IsActive //and Sepulchre is not active + ? GCDPriority.GoringBlade //use priority for Goring Blade + : GCDPriority.HolySpirit); //otherwise use priority for Holy Spirit + if (holyStrat == HolyStrategy.Spirit) //if Spirit strategy is selected + QueueGCD(AID.HolySpirit, //queue Holy Spirit + TargetChoice(holy) ?? primaryTarget, //with target choice + GCDPriority.ForcedGCD); //use priority for forced GCDs + if (holyStrat == HolyStrategy.Circle) //if Circle strategy is selected + QueueGCD(BestHolyCircle, //queue Holy Circle + Player, //on Self (no target needed) + GCDPriority.ForcedGCD); //use priority + } + if (rangedStrat is RangedStrategy.Automatic && //if Automatic strategy is selected + !In3y(TargetChoice(ranged) ?? primaryTarget)) //and target is not in melee range + QueueGCD(isMoving ? AID.ShieldLob //queue Shield Lob if moving + : AID.HolySpirit, //otherwise queue Holy Spirit + TargetChoice(ranged) ?? primaryTarget, //with target choice + GCDPriority.Combo123); //use priority for 123/12 combo actions + if (ShouldUseRangedLob(primaryTarget, rangedStrat)) //if Shield Lob should be used + QueueGCD(AID.ShieldLob, //queue Shield Lob + TargetChoice(ranged) ?? primaryTarget, //with target choice + rangedStrat is RangedStrategy.Force //if Force strategy is selected + ? GCDPriority.ForcedGCD //use priority for forced GCDs + : GCDPriority.Combo123); //otherwise use priority for 123/12 combo actions + if (ShouldUseRangedCast(primaryTarget, rangedStrat)) //if Shield Cast should be used + QueueGCD(AID.HolySpirit, //queue Holy Spirit + TargetChoice(ranged) ?? primaryTarget, //with target choice + rangedStrat is RangedStrategy.ForceCast //if Force Cast strategy is selected + ? GCDPriority.ForcedGCD //use priority for forced GCDs + : GCDPriority.Combo123); //otherwise use priority for 123/12 combo actions + if (ShouldUsePotion(potStrat)) //if Potion should be used + Hints.ActionsToExecute.Push(ActionDefinitions.IDPotionStr, //queue the potion action + Player, //on Self (no target needed) + ActionQueue.Priority.VeryHigh + (int)OGCDPriority.Potion, //use priority for potions + 0, //no delay + GCD - 0.9f); //Lateweave + #endregion } - //Method to queue a GCD (Global Cooldown) action - private void QueueGCD(AID aid, Actor? target, GCDPriority prio) + #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 QueueOGCD

(AID aid, Actor? target, P priority, float delay = 0) where P : Enum => QueueOGCD(aid, target, (int)(object)priority, delay); + public void QueueGCD(AID aid, Actor? target, int priority = 8, float delay = 0) { - //Check if the priority for this action is valid - if (prio != GCDPriority.None) + var NextGCDPrio = 0; + if (priority == 0) + return; + if (QueueAction(aid, target, ActionQueue.Priority.High + priority, delay) && priority > NextGCDPrio) { - //Push the action to the execution queue with the specified priority - Hints.ActionsToExecute.Push(ActionID.MakeSpell(aid), target, ActionQueue.Priority.High + (int)prio); - - //Update the next GCD action and its priority if this one has a higher priority - if (prio > NextGCDPrio) - { - NextGCD = aid; //Set the next GCD action - NextGCDPrio = prio; //Update the priority for the next GCD - } + NextGCD = aid; } } - - //Method to queue an OGCD (Off Global Cooldown) action - private void QueueOGCD(AID aid, Actor? target, OGCDPriority prio, float basePrio = ActionQueue.Priority.Medium) + public void QueueOGCD(AID aid, Actor? target, int priority = 4, float delay = 0) { - //Check if the priority for this action is valid - if (prio != OGCDPriority.None) + if (priority == 0) + return; + QueueAction(aid, target, ActionQueue.Priority.Medium + priority, delay); + } + public bool QueueAction(AID aid, Actor? target, float priority, float delay) + { + Vector3 targetPos = default; + var def = ActionDefinitions.Instance.Spell(aid); + if ((uint)(object)aid == 0) + return false; + if (def == null) + return false; + if (def.Range != 0 && target == null) { - //Push the OGCD action to the execution queue with the specified priority - Hints.ActionsToExecute.Push(ActionID.MakeSpell(aid), target, basePrio + (int)prio); + return false; } + 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 - //Method to determine the next single-target action based on the last action used - private AID NextComboSingleTarget() => ComboLastMove switch + #region Rotation Helpers + private AID RotationST() => ComboLastMove switch { AID.RiotBlade => Unlocked(AID.RoyalAuthority) ? AID.RoyalAuthority : AID.RageOfHalone, //If Riot Blade was last used, choose Royal Authority if available, otherwise Rage of Halone AID.FastBlade => AID.RiotBlade, //If Fast Blade was last used, go back to Riot Blade _ => AID.FastBlade, //Default to Fast Blade if no recognized last action }; - - //Method to determine the next AoE action based on the last action used - private AID NextComboAoE() => ComboLastMove switch + private AID RotationAOE() => ComboLastMove switch { AID.TotalEclipse => AID.Prominence, //If Total Eclipse was last used, use Prominence next _ => AID.TotalEclipse, //Default to Total Eclipse if no recognized last action }; + private AID BestRotation() => ComboLastMove switch + { + //ST + AID.RoyalAuthority => ShouldUseAOE ? RotationAOE() : RotationST(), //If Royal Authority was last used, choose between AOE and ST rotations + AID.RageOfHalone => ShouldUseAOE ? RotationAOE() : RotationST(), //If Rage of Halone was last used, choose between AOE and ST rotations + AID.RiotBlade => RotationST(), //If Riot Blade was last used, continue ST rotation + AID.FastBlade => RotationST(), //If Fast Blade was last used, continue ST rotation + //AOE + AID.Prominence => ShouldUseAOE ? RotationAOE() : RotationST(), //If Prominence was last used, choose between AOE and ST rotations + AID.TotalEclipse => RotationAOE(), //If Total Eclipse was last used, continue AOE rotation + _ => ShouldUseAOE ? RotationAOE() : RotationST(), //Default to AOE or ST rotation based on conditions + }; + #endregion - //Method to determine the next combo action and its priority based on AoE strategy, target count, and burst strategy - 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.FastBlade => Unlocked(AID.RiotBlade) ? 2 : Unlocked(AID.RoyalAuthority) ? 1 : 0, //Fast Blade allows up to 2 or 1 additional combo actions based on availability - AID.TotalEclipse => Unlocked(AID.Prominence) ? 1 : 0, //Total Eclipse allows 1 more combo action if Prominence is unlocked - _ => 0 //Default to no combo steps if the last action doesn't match - }; - - //Check if we can fit a GCD based on remaining combo state time - if (comboStepsRemaining > 0 && !CanFitGCD(World.Client.ComboState.Remaining)) - comboStepsRemaining = 0; //Reset combo steps if not enough time to complete them - - var doingAOECombo = ComboLastMove == AID.TotalEclipse; //Track if currently performing an AoE combo - - //Determine if an AoE action is desirable based on target count and strategy - var wantAOEAction = Unlocked(AID.TotalEclipse) && aoeStrategy switch - { - AOEStrategy.UseST => false, //Explicitly using single-target strategy, so AoE action is not desired - AOEStrategy.ForceST => false, //Forcing single-target, so AoE action is not desired - AOEStrategy.UseAoE => true, //Explicitly using AoE strategy, so AoE action is desired - AOEStrategy.ForceAoE => false, //Forcing AoE action but this is mainly for planning, so AoE not action is desired - AOEStrategy.Auto => AoETargets >= 3, //Automatically choose AoE if there are 3 or more targets - AOEStrategy.AutoFinishCombo => comboStepsRemaining > 0 - ? doingAOECombo : AoETargets >= 3, //Continue combo if ongoing, otherwise switch to AoE if targets are sufficient - _ => false //Default to no AoE action if no matching strategy - }; - - //Reset combo steps if the desired action type doesn't match the current combo type - if (comboStepsRemaining > 0 && wantAOEAction != doingAOECombo) - comboStepsRemaining = 0; //Reset if we need to switch from AoE to single-target or vice versa - - //Determine the next action based on whether an AoE or single-target action is desired - var nextAction = wantAOEAction ? NextComboAoE() : NextComboSingleTarget(); - - //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 with priority if combo steps cannot fit - - //Return normal combo action priority based on action risks - return (nextAction, GCDPriority.Combo123); //Return the next action and its priority - } - - //Determines when to use Shield Lob for Ranged purposes + #region Cooldown Helpers private bool ShouldUseRangedLob(Actor? target, RangedStrategy strategy) => strategy switch { RangedStrategy.OpenerRanged => IsFirstGCD() && !In3y(target), //Use if it's the first GCD and target is not within 3y @@ -605,8 +693,6 @@ private void QueueOGCD(AID aid, Actor? target, OGCDPriority prio, float basePrio RangedStrategy.Forbid => false, //Delay usage _ => false }; - - //Determines when to use Holy Spirit for Ranged purposes private bool ShouldUseRangedCast(Actor? target, RangedStrategy strategy) => strategy switch { RangedStrategy.OpenerRangedCast => IsFirstGCD() && !In3y(target), //Use if it's the first GCD and target is not within 3y @@ -615,134 +701,168 @@ private void QueueOGCD(AID aid, Actor? target, OGCDPriority prio, float basePrio RangedStrategy.RangedCast => !In3y(target), //Use if target is not within 3y _ => false }; - - //Determines when to use Fight or Flight - private bool ShouldUseFightOrFlight(OffensiveStrategy strategy, Actor? target) => strategy switch - { - OffensiveStrategy.Automatic => - Player.InCombat && target != null && ActionReady(AID.FightOrFlight) && CombatTimer >= GCDLength * 2 + 0.5f, //Use if in combat, target is valid, action is ready - OffensiveStrategy.Force => true, //Force - OffensiveStrategy.Delay => false, //Delay usage + private bool ShouldUseFightOrFlight(OGCDStrategy strategy, Actor? target) => strategy switch + { + OGCDStrategy.Automatic => + Player.InCombat && //In combat + target != null && //Target exists + FightOrFlight.IsReady && //Fight or Flight is ready + (CombatTimer <= 30 && ComboLastMove is AID.RoyalAuthority or AID.RageOfHalone || //Use within 30s of combat and after Royal Authority or Rage of Halone + CombatTimer > 30), //Use after 30s of combat + OGCDStrategy.Force => FightOrFlight.IsReady, //Force Fight or Flight + OGCDStrategy.AnyWeave => FightOrFlight.IsReady && canWeaveIn, //Force Weave Fight or Flight + OGCDStrategy.EarlyWeave => FightOrFlight.IsReady && canWeaveEarly, //Force Early Weave Fight or Flight + OGCDStrategy.LateWeave => FightOrFlight.IsReady && canWeaveLate, //Force Late Weave Fight or Flight + OGCDStrategy.Delay => false, //Delay Fight or Flight _ => false }; - - //Determines when to use Requiescat - private bool ShouldUseRequiescat(OffensiveStrategy strategy, Actor? target) => strategy switch - { - OffensiveStrategy.Automatic => - Player.InCombat && target != null && ActionReady(AID.Requiescat) && hasFoF, //Use if in combat, target is valid, action is ready, and has Fight or Flight - OffensiveStrategy.Force => true, //Force - OffensiveStrategy.Delay => false, //Delay usage + private bool ShouldUseRequiescat(OGCDStrategy strategy, Actor? target) => strategy switch + { + OGCDStrategy.Automatic => + Player.InCombat && //In combat + target != null && //Target exists + Requiescat.IsReady && //Requiescat is ready + FightOrFlight.IsActive, //Fight or Flight is active + OGCDStrategy.Force => Requiescat.IsReady, //Force Requiescat + OGCDStrategy.AnyWeave => Requiescat.IsReady && canWeaveIn, //Force Weave Requiescat + OGCDStrategy.EarlyWeave => Requiescat.IsReady && canWeaveEarly, //Force Early Weave Requiescat + OGCDStrategy.LateWeave => Requiescat.IsReady && canWeaveLate, //Force Late Weave Requiescat + OGCDStrategy.Delay => false, //Delay Requiescat _ => false }; - - //Determines when to use Spirits Within - private bool ShouldUseSpiritsWithin(OffensiveStrategy strategy, Actor? target) => strategy switch - { - OffensiveStrategy.Automatic => - Player.InCombat && In3y(target) && fofCD is < 57.55f and > 17 && //Use if in combat, target is in range, and cooldown is within limits - ActionReady(Unlocked(AID.Expiacion) ? AID.Expiacion : AID.SpiritsWithin), //Action is ready, prioritizing Expiacion if unlocked - OffensiveStrategy.Force => true, //Force - OffensiveStrategy.Delay => false, //Delay usage + private bool ShouldUseSpiritsWithin(OGCDStrategy strategy, Actor? target) => strategy switch + { + OGCDStrategy.Automatic => + Player.InCombat && //In combat + target != null && //Target exists + In3y(target) && //Target in range + FightOrFlight.CD is < 57.55f and > 17 && //One use inside FoF, one use outside FoF + SpiritsWithin.IsReady, //Spirits Within is ready + OGCDStrategy.Force => SpiritsWithin.IsReady, //Force Spirits Within + OGCDStrategy.AnyWeave => SpiritsWithin.IsReady && canWeaveIn, //Force Weave Spirits Within + OGCDStrategy.EarlyWeave => SpiritsWithin.IsReady && canWeaveEarly, //Force Early Weave Spirits Within + OGCDStrategy.LateWeave => SpiritsWithin.IsReady && canWeaveLate, //Force Late Weave Spirits Within + OGCDStrategy.Delay => false, //Delay Spirits Within _ => false }; - - //Determines when to use Circle of Scorn - private bool ShouldUseCircleOfScorn(OffensiveStrategy strategy, Actor? target) => strategy switch - { - OffensiveStrategy.Automatic => - Player.InCombat && ActionReady(AID.CircleOfScorn) && In5y(target) && fofCD is < 57.55f and > 17, //Use if in combat, action is ready, target is within 5y, and cooldown is within limits - OffensiveStrategy.Force => true, //Force - OffensiveStrategy.Delay => false, //Delay usage + private bool ShouldUseCircleOfScorn(OGCDStrategy strategy, Actor? target) => strategy switch + { + OGCDStrategy.Automatic => + Player.InCombat && //In combat + target != null && //Target exists + CircleOfScorn.IsReady && //Circle of Scorn is ready + In5y(target) && //Target in range + FightOrFlight.CD is < 57.55f and > 17, //One use inside FoF, one use outside FoF + OGCDStrategy.Force => CircleOfScorn.IsReady, //Force Circle of Scorn + OGCDStrategy.AnyWeave => CircleOfScorn.IsReady && canWeaveIn, //Force Weave Circle of Scorn + OGCDStrategy.EarlyWeave => CircleOfScorn.IsReady && canWeaveEarly, //Force Early Weave Circle of Scorn + OGCDStrategy.LateWeave => CircleOfScorn.IsReady && canWeaveLate, //Force Late Weave Circle of Scorn + OGCDStrategy.Delay => false, //Delay Circle of Scorn _ => false }; - - //Determines when to use Goring Blade - private bool ShouldUseGoringBlade(OffensiveStrategy strategy, Actor? target) => strategy switch - { - OffensiveStrategy.Automatic => - Player.InCombat && In3y(target) && hasFoF, //Use if in combat, target is in range, and has Fight or Flight active - OffensiveStrategy.Force => true, //Force - OffensiveStrategy.Delay => false, //Delay usage + private bool ShouldUseBladeOfHonor(OGCDStrategy strategy, Actor? target) => strategy switch + { + OGCDStrategy.Automatic => + Player.InCombat && //In combat + target != null && //Target exists + BladeOfHonor.IsReady, //Blade of Honor is ready + OGCDStrategy.Force => BladeOfHonor.IsReady, //Force Blade of Honor + OGCDStrategy.AnyWeave => BladeOfHonor.IsReady && canWeaveIn, //Force Weave Blade of Honor + OGCDStrategy.EarlyWeave => BladeOfHonor.IsReady && canWeaveEarly, //Force Early Weave Blade of Honor + OGCDStrategy.LateWeave => BladeOfHonor.IsReady && canWeaveLate, //Force Late Weave Blade of Honor + OGCDStrategy.Delay => false, //Delay Blade of Honor + _ => false + }; + private bool ShouldUseGoringBlade(GCDStrategy strategy, Actor? target) => strategy switch + { + GCDStrategy.Automatic => + Player.InCombat && //In combat + In3y(target) && //Target in range + GoringBlade.IsReady && //Goring Blade is ready + FightOrFlight.IsActive, //Fight or Flight is active + GCDStrategy.Force => GoringBlade.IsReady, //Force Goring Blade + GCDStrategy.Delay => false, //Delay Goring Blade _ => false }; - - //Determines when to use Blade Combo private bool ShouldUseBladeCombo(BladeComboStrategy strategy, Actor? target) => strategy switch { BladeComboStrategy.Automatic => - Player.InCombat && HasEffect(SID.ConfiteorReady) && hasFoF && BladeComboStep is 0, //Use if in combat, Confiteor is ready, has Fight or Flight, and it's the first step - BladeComboStrategy.ForceConfiteor => HasEffect(SID.ConfiteorReady) && BladeComboStep is 0, //Force use of Confiteor if ready and it's the first step - BladeComboStrategy.ForceFaith => BladeComboStep is 1, //Force use of Faith if it's the second step - BladeComboStrategy.ForceTruth => BladeComboStep is 2, //Force use of Truth if it's the third step - BladeComboStrategy.ForceValor => BladeComboStep is 3, //Force use of Valor if it's the fourth step - BladeComboStrategy.Delay => false, //Delay usage + Player.InCombat && //In combat + target != null && //Target exists + In25y(target) && //Target in range + Requiescat.IsActive && //Requiescat is active + BladeComboStep is 0 or 1 or 2 or 3, //Blade Combo conditions are met + BladeComboStrategy.ForceConfiteor => Confiteor.IsReady && BladeComboStep is 0, //Force Confiteor + BladeComboStrategy.ForceFaith => BladeComboStep is 1, //Force Blade of Faith + BladeComboStrategy.ForceTruth => BladeComboStep is 2, //Force Blade of Truth + BladeComboStrategy.ForceValor => BladeComboStep is 3, //Force Blade of Valor + BladeComboStrategy.Delay => false, //Delay Blade Combo _ => false }; - - //Determines when to use Atonement private bool ShouldUseAtonement(AtonementStrategy strategy, Actor? target) => strategy switch { AtonementStrategy.Automatic => - Player.InCombat && In3y(target) && HasEffect(SID.AtonementReady), //Use if in combat, target is in range, and Atonement is ready - AtonementStrategy.ForceAtonement => HasEffect(SID.AtonementReady), //Force use of Atonement if ready - AtonementStrategy.ForceSupplication => HasEffect(SID.SupplicationReady), //Force use of Supplication if ready - AtonementStrategy.ForceSepulchre => HasEffect(SID.SepulchreReady), //Force use of Sepulchre if ready - AtonementStrategy.Delay => false, //Delay usage + Player.InCombat && //In combat + target != null && //Target exists + In3y(target) && //Target in range + Atonement.IsReady || Supplication.IsReady || Sepulchre.IsReady, //if any of the three are ready + AtonementStrategy.ForceAtonement => Atonement.IsReady, //Force Atonement + AtonementStrategy.ForceSupplication => Supplication.IsReady, //Force Supplication + AtonementStrategy.ForceSepulchre => Sepulchre.IsReady, //Force Sepulchre + AtonementStrategy.Delay => false, //Delay Atonement _ => false }; - - //Determines when to use Holy Spirit - private bool ShouldUseHolySpirit(OffensiveStrategy strategy, Actor? target) => strategy switch - { - OffensiveStrategy.Automatic => - Player.InCombat && //Use if in combat - hasMight && hasMPforMight && !hasReq, //Check if we have Might, sufficient MP for Might, and not under Requiescat - OffensiveStrategy.Force => true, //Force - OffensiveStrategy.Delay => false, //Delay usage + private bool ShouldUseHoly(HolyStrategy strategy, Actor? target) => strategy switch + { + HolyStrategy.Automatic => + ShouldUseDMHolyCircle || ShouldNormalHolyCircle //if Holy Circle should be used + ? ShouldUseHolyCircle(HolyStrategy.Automatic, target) //then use Holy Circle + : ShouldUseHolySpirit(HolyStrategy.Automatic, target), //otherwise use Holy Spirit + HolyStrategy.Spirit => HolySpirit.IsReady, //Force Holy Spirit + HolyStrategy.Circle => HolyCircle.IsReady, //Force Holy Circle + HolyStrategy.Delay => false, //Delay Holy Spirit _ => false }; - - //Determines when to use Holy Circle - private bool ShouldUseHolyCircle(OffensiveStrategy strategy, Actor? AoETargets) => strategy switch - { - OffensiveStrategy.Automatic => - Player.InCombat && //Use if in combat - hasMight && hasMPforMight && NumTargetsHitByAoE() >= 3 && !hasReq, //Check if we have Might, sufficient MP for Might, hit at least 3 targets, and not under Requiescat - OffensiveStrategy.Force => true, //Force - OffensiveStrategy.Delay => false, //Delay usage + private bool ShouldUseHolySpirit(HolyStrategy strategy, Actor? target) => strategy switch + { + HolyStrategy.Automatic => + Player.InCombat && //In combat + target != null && //Target exists + In25y(target) && //Target in range + HolySpirit.IsReady && //can execute Holy Spirit + DivineMight.IsActive, //Divine Might is active + _ => false + }; + private bool ShouldUseHolyCircle(HolyStrategy strategy, Actor? target) => strategy switch + { + HolyStrategy.Automatic => + Player.InCombat && //In combat + target != null && //Target exists + In5y(target) && //Target in range + HolyCircle.IsReady && //can execute Holy Circle + DivineMight.IsActive, //Divine Might is active _ => false }; - - //Determines when to use Dash private bool ShouldUseDash(DashStrategy strategy, Actor? target) => strategy switch { DashStrategy.Automatic => - Player.InCombat && target != null && hasFoF, //Use in Fight or Flight - DashStrategy.Force => true, //Force - DashStrategy.Conserve1 => CD(AID.Intervene) > 30, //Use if cooldown is greater than 30 seconds - DashStrategy.GapClose => !In3y(target), //Use if target is out of range + Player.InCombat && //In combat + target != null && //Target exists + In3y(target) && //Target in range + Intervene.IsReady && //can execute Intervene + FightOrFlight.IsActive, //Fight or Flight is active + DashStrategy.Force => true, //Force all charges + DashStrategy.Force1 => Intervene.TotalCD < 1f, //Force 1 charge + DashStrategy.GapClose => !In3y(target), //Force gap close + DashStrategy.GapClose1 => Intervene.TotalCD < 1f && !In3y(target), //Force gap close only if at max charges _ => false }; - - //Potion Helpers - //Determines if potions are aligned with buffs - private bool IsPotionAlignedWithNM() - { - //Use potion before FoF/Req in opener - //Use for 6m window - return ActionReady(AID.FightOrFlight) && - (ActionReady(AID.Requiescat) || //Opener - reqCD < 15); //Use if Requiescat is on cooldown for less than 15 seconds - } - - //Determines when to use a potion based on strategy private bool ShouldUsePotion(PotionStrategy strategy) => strategy switch { - PotionStrategy.AlignWithRaidBuffs => - IsPotionAlignedWithNM() || fofCD < 5 && reqCD < 15, //Align potions with major buffs + PotionStrategy.AlignWithRaidBuffs => FightOrFlight.CD < 5, //Align potions with buffs PotionStrategy.Immediate => true, //Force potion immediately _ => false, }; + #endregion } diff --git a/BossMod/Autorotation/akechi/AkechiSCH.cs b/BossMod/Autorotation/akechi/AkechiSCH.cs index e15035b18f..c4672e8de1 100644 --- a/BossMod/Autorotation/akechi/AkechiSCH.cs +++ b/BossMod/Autorotation/akechi/AkechiSCH.cs @@ -338,9 +338,9 @@ public bool QueueAction(AID aid, Actor? target, float priority, float delay) private bool ShouldUseBio(Actor? target, BioStrategy strategy) => strategy switch { 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.Bio6 => Player.InCombat && target != null && bioLeft <= 6 && In25y(target), + BioStrategy.Bio9 => Player.InCombat && target != null && bioLeft <= 9 && In25y(target), + BioStrategy.Bio0 => Player.InCombat && target != null && bioLeft is 0 && In25y(target), BioStrategy.Force => true, BioStrategy.Delay => false, _ => false diff --git a/BossMod/Modules/Dawntrail/Ultimate/FRU/FRUStates.cs b/BossMod/Modules/Dawntrail/Ultimate/FRU/FRUStates.cs index 57c589fe27..226c5f6075 100644 --- a/BossMod/Modules/Dawntrail/Ultimate/FRU/FRUStates.cs +++ b/BossMod/Modules/Dawntrail/Ultimate/FRU/FRUStates.cs @@ -321,11 +321,13 @@ private void P2LightRampant(uint id, float delay) ComponentCondition(id + 0x38, 3.2f, comp => comp.Casters.Count > 0) .ActivateOnEnter() .ActivateOnEnter() - .ActivateOnEnter() + .ActivateOnEnter() + .DeactivateOnExit() .DeactivateOnExit(); // last puddle is baited right before holy light burst casts start ComponentCondition(id + 0x40, 2.5f, comp => !comp.Active, "Stack") - .DeactivateOnExit() - .DeactivateOnExit(); + .ActivateOnEnter() + .DeactivateOnExit() + .DeactivateOnExit(); ComponentCondition(id + 0x50, 2.4f, comp => comp.NumCasts > 0, "Orbs 1") .ActivateOnEnter() .ActivateOnEnter(); diff --git a/BossMod/Modules/Dawntrail/Ultimate/FRU/P2LightRampant.cs b/BossMod/Modules/Dawntrail/Ultimate/FRU/P2LightRampant.cs index cce1a12f70..7caf05899f 100644 --- a/BossMod/Modules/Dawntrail/Ultimate/FRU/P2LightRampant.cs +++ b/BossMod/Modules/Dawntrail/Ultimate/FRU/P2LightRampant.cs @@ -225,10 +225,29 @@ public override void AddAIHints(int slot, Actor actor, PartyRolesConfig.Assignme } } -// movement to preposition for resolving stacks -class P2LightRampantAIStack(BossModule module) : BossComponent(module) +// movement to stack N/S after towers (and bait last two puddles) +class P2LightRampantAIStackPrepos(BossModule module) : BossComponent(module) { private readonly P2LuminousHammer? _puddles = module.FindComponent(); + + public override void AddAIHints(int slot, Actor actor, PartyRolesConfig.Assignment assignment, AIHints hints) + { + var isPuddleBaiter = _puddles?.ActiveBaitsOn(actor).Any() ?? false; + var northCamp = isPuddleBaiter ? actor.Position.X < Module.Center.X : actor.Position.Z < Module.Center.Z; // this assumes CW movement for baiter + var dest = Module.Center + new WDir(0, northCamp ? -18 : 18); + if (isPuddleBaiter) + { + var maxDist = _puddles?.BaitsPerPlayer[slot] == 4 ? 7 : 13; + if (dest.InCircle(actor.Position, maxDist)) + return; // don't move _too_ fast as a baiter + } + hints.AddForbiddenZone(ShapeDistance.InvertedCircle(dest, 1), DateTime.MaxValue); + } +} + +// movement to resolve stacks +class P2LightRampantAIStackResolve(BossModule module) : BossComponent(module) +{ private readonly P2PowerfulLight? _stack = module.FindComponent(); private readonly P2HolyLightBurst? _orbs = module.FindComponent(); @@ -236,54 +255,40 @@ class P2LightRampantAIStack(BossModule module) : BossComponent(module) public override void AddAIHints(int slot, Actor actor, PartyRolesConfig.Assignment assignment, AIHints hints) { - if (_puddles == null || _stack == null || _orbs == null) + if (_stack == null || _orbs == null) return; - // initially we don't know whether first orbs will cover N/S, puddle baiter is still relatively far away and has two baits left - // when orbs start, baiter has just finished his puddles; he can still be relatively far away from N/S center, so we might need to wait for him var northCamp = IsNorthCamp(actor); - var startingDir = (northCamp ? 180 : 0).Degrees(); - var startingPos = Module.Center + new WDir(0, northCamp ? -Radius : Radius); - if (_puddles.ActiveBaits.Any()) + var centerDangerous = _orbs.ActiveCasters.Any(c => c.Position.Z - Module.Center.Z is var off && (northCamp ? off < -15 : off > 15)); + var destDir = (northCamp ? 180 : 0).Degrees() - (centerDangerous ? 40 : 20).Degrees(); + var destPos = Module.Center + Radius * destDir.ToDirection(); + if (_stack.IsStackTarget(actor)) { - // just move to starting position, until all puddles are resolved - hints.AddForbiddenZone(ShapeDistance.InvertedCircle(startingPos, 1), DateTime.MaxValue); - return; - } - - var isStackTarget = _stack.IsStackTarget(actor); - var haveOrbs = _orbs.Casters.Count > 0; - var centerDangerous = haveOrbs && _orbs.ActiveCasters.Any(c => actor.Position.Z - Module.Center.Z is var off && (northCamp ? off < -15 : off > 15)); - var idealDestDir = startingDir - (centerDangerous ? 40 : 20).Degrees(); // alt: haveOrbs ? 20 : 30 (but i don't think it's how people really move...) - var idealPos = Module.Center + Radius * idealDestDir.ToDirection(); - - if (isStackTarget) - { - // as a stack target, our responsibility is to wait for everyone to stack up, then carefully move towards ideal dir + // as a stack target, our responsibility is to wait for everyone to stack up, then carefully move towards destination // note that we need to be careful to avoid oscillations - var toIdeal = idealPos - actor.Position; - foreach (var partner in Raid.WithoutSlot().Exclude(actor).Where(p => IsNorthCamp(p) == northCamp)) + var toDest = destPos - actor.Position; + bool needToWaitFor(Actor partner) { + if (partner == actor || IsNorthCamp(partner) != northCamp) + return false; // this is not our partner, we don't need to wait for him var toPartner = partner.Position - actor.Position; var distSq = toPartner.LengthSq(); - if (distSq > 9 && toIdeal.Dot(toPartner) < 0) - { - // partner is far enough away, and moving towards ideal pos will not bring us closer => just stay where we are - return; - } + return distSq > 9 && toDest.Dot(toPartner) < 0; // partner is far enough away, and moving towards destination will not bring us closer } - hints.AddForbiddenZone(ShapeDistance.InvertedCircle(idealPos, 1), DateTime.MaxValue); + if (!Raid.WithoutSlot().Any(needToWaitFor)) + hints.AddForbiddenZone(ShapeDistance.InvertedCircle(destPos, 1), DateTime.MaxValue); + // else: we still have someone we need to wait for, just stay where we are... } else if (_stack.Stacks.FirstOrDefault(s => IsNorthCamp(s.Target) == northCamp).Target is var stackTarget && stackTarget != null) { - // otherwise we just want to stay close to the stack target, slightly offset to the ideal position - var dirToIdeal = idealPos - stackTarget.Position; - var dest = dirToIdeal.LengthSq() <= 4 ? idealPos : stackTarget.Position + 2 * dirToIdeal.Normalized(); + // we just want to stay close to the stack target, slightly offset to the destination + var dirToDest = destPos - stackTarget.Position; + var dest = dirToDest.LengthSq() <= 4 ? destPos : stackTarget.Position + 2 * dirToDest.Normalized(); hints.AddForbiddenZone(ShapeDistance.InvertedCircle(dest, 1), DateTime.MaxValue); } } - private bool IsNorthCamp(Actor actor) => (_puddles?.ActiveBaitsOn(actor).Any() ?? false) ? actor.Position.X < Module.Center.X : actor.Position.Z < Module.Center.Z; + private bool IsNorthCamp(Actor actor) => actor.Position.Z < Module.Center.Z; } // movement to dodge orbs after resolving stack @@ -296,20 +301,32 @@ public override void AddAIHints(int slot, Actor actor, PartyRolesConfig.Assignme if (_orbs == null || _orbs.Casters.Count == 0) return; + // actual orb aoes + //var resolve = Module.CastFinishAt(_orbs.Casters[0].CastInfo, _orbs.NumCasts == 0 ? 0 : -1); // for second set, we want to dodge out asap without weird movement to the center + foreach (var c in _orbs.ActiveCasters) + hints.AddForbiddenZone(_orbs.Shape.Distance(c.Position, default), Module.CastFinishAt(c.CastInfo)); + if (_orbs.NumCasts == 0) { // dodge first orbs, while staying near edge hints.AddForbiddenZone(ShapeDistance.Circle(Module.Center, 16)); + // ... and close to the aoes + var cushioned = ShapeDistance.Union([.. _orbs.ActiveCasters.Select(c => ShapeDistance.Circle(c.Position, 13))]); + hints.AddForbiddenZone(p => -cushioned(p), DateTime.MaxValue); + } + else if (_orbs.Casters.Any(c => _orbs.Shape.Check(actor.Position, c))) + { + // dodge second orbs while staying near edge (tethers are still up for a bit after first dodge) + hints.AddForbiddenZone(ShapeDistance.Circle(Module.Center, 16)); } else { - // dodge second orbs, while trying to come closer to the center - hints.AddForbiddenZone(ShapeDistance.InvertedCircle(Module.Center, 6), DateTime.MaxValue); + // now that we're safe, move closer to the center (this is a bit sus, but whatever...) + hints.AddForbiddenZone(ShapeDistance.InvertedCircle(Module.Center, 16), WorldState.FutureTime(30)); + hints.AddForbiddenZone(ShapeDistance.InvertedCircle(Module.Center, 13), WorldState.FutureTime(40)); + hints.AddForbiddenZone(ShapeDistance.InvertedCircle(Module.Center, 10), WorldState.FutureTime(50)); + hints.AddForbiddenZone(ShapeDistance.InvertedCircle(Module.Center, 7), DateTime.MaxValue); } - - // actual orb aoes - foreach (var c in _orbs.ActiveCasters) - hints.AddForbiddenZone(_orbs.Shape.Distance(c.Position, default), Module.CastFinishAt(c.CastInfo, -1)); } } @@ -326,6 +343,7 @@ public override void AddAIHints(int slot, Actor actor, PartyRolesConfig.Assignme ref var t = ref _tower.Towers.Ref(0); hints.AddForbiddenZone(t.ForbiddenSoakers[slot] ? ShapeDistance.Circle(t.Position, t.Radius) : ShapeDistance.InvertedCircle(t.Position, t.Radius), t.Activation); + hints.AddForbiddenZone(t.ForbiddenSoakers[slot] ? ShapeDistance.Circle(t.Position, t.Radius + 2) : ShapeDistance.InvertedCircle(t.Position, t.Radius - 2), DateTime.MaxValue); var clockspot = _config.P2Banish2SpreadSpots[assignment]; if (clockspot >= 0) diff --git a/BossMod/Modules/Stormblood/Foray/BaldesionsArsenal/BA2Raiden/BA2Raiden.cs b/BossMod/Modules/Stormblood/Foray/BaldesionsArsenal/BA2Raiden/BA2Raiden.cs index 887eb0a5f7..33c382bb15 100644 --- a/BossMod/Modules/Stormblood/Foray/BaldesionsArsenal/BA2Raiden/BA2Raiden.cs +++ b/BossMod/Modules/Stormblood/Foray/BaldesionsArsenal/BA2Raiden/BA2Raiden.cs @@ -25,8 +25,8 @@ class LateralZantetsuken1(BossModule module) : LateralZantetsuken(module, AID.La class LateralZantetsuken2(BossModule module) : LateralZantetsuken(module, AID.LateralZantetsuken2); class BitterBarbs(BossModule module) : Components.Chains(module, (uint)TetherID.Chains, ActionID.MakeSpell(AID.BitterBarbs)); -class BoomingLament(BossModule module) : Components.LocationTargetedAOEs(module, ActionID.MakeSpell(AID.BoomingLament), new AOEShapeCircle(10)); -class SilentLevin(BossModule module) : Components.LocationTargetedAOEs(module, ActionID.MakeSpell(AID.SilentLevin), new AOEShapeCircle(5)); +class BoomingLament(BossModule module) : Components.LocationTargetedAOEs(module, ActionID.MakeSpell(AID.BoomingLament), 10); +class SilentLevin(BossModule module) : Components.LocationTargetedAOEs(module, ActionID.MakeSpell(AID.SilentLevin), 5); class UltimateZantetsuken(BossModule module) : Components.CastHint(module, ActionID.MakeSpell(AID.UltimateZantetsuken), "Enrage, kill the adds!", true);