diff --git a/BossMod/ActionQueue/Casters/BLM.cs b/BossMod/ActionQueue/Casters/BLM.cs index ca98c43d4..06a0dc5a8 100644 --- a/BossMod/ActionQueue/Casters/BLM.cs +++ b/BossMod/ActionQueue/Casters/BLM.cs @@ -84,7 +84,6 @@ public enum TraitID : uint public enum SID : uint { None = 0, - Addle = 1203, // applied by Addle to target LucidDreaming = 1204, // applied by Lucid Dreaming to self Swiftcast = 167, // applied by Swiftcast to self Manaward = 168, // applied by Manaward to self @@ -99,6 +98,9 @@ public enum SID : uint Thunderhead = 3870, HighThunder = 3871, // applied by High Thunder to target HighThunderII = 3872, // applied by High Thunder II to target + + //Shared + Addle = ClassShared.SID.Addle, // applied by Addle to target } public sealed class Definitions : IDisposable diff --git a/BossMod/ActionQueue/Casters/PCT.cs b/BossMod/ActionQueue/Casters/PCT.cs index ca7a166af..861915e81 100644 --- a/BossMod/ActionQueue/Casters/PCT.cs +++ b/BossMod/ActionQueue/Casters/PCT.cs @@ -100,6 +100,9 @@ public enum SID : uint Hyperphantasia = 3688, // applied by Starry Muse to self Starstruck = 3681, // applied by Starry Muse to self RainbowBright = 3679, + + //Shared + Addle = ClassShared.SID.Addle, // applied by Addle to target } public sealed class Definitions : IDisposable diff --git a/BossMod/ActionQueue/Casters/RDM.cs b/BossMod/ActionQueue/Casters/RDM.cs index 0a103c0b8..29544b284 100644 --- a/BossMod/ActionQueue/Casters/RDM.cs +++ b/BossMod/ActionQueue/Casters/RDM.cs @@ -97,7 +97,10 @@ public enum SID : uint MagickedSwordplay = 3875, ThornedFlourish = 3876, GrandImpactReady = 3877, - PrefulgenceReady = 3878 + PrefulgenceReady = 3878, + + //Shared + Addle = ClassShared.SID.Addle, // applied by Addle to target } public sealed class Definitions : IDisposable diff --git a/BossMod/ActionQueue/Casters/SMN.cs b/BossMod/ActionQueue/Casters/SMN.cs index d38e273d8..1c28bf3e9 100644 --- a/BossMod/ActionQueue/Casters/SMN.cs +++ b/BossMod/ActionQueue/Casters/SMN.cs @@ -124,8 +124,6 @@ public enum TraitID : uint public enum SID : uint { None = 0, - Addle = 1203, // applied by Addle to target, -5% phys and -10% magic damage dealt - Swiftcast = 167, // applied by Swiftcast to self, next cast is instant Sleep = 3, // applied by Sleep to target FurtherRuin = 2701, // applied by Energy Drain, Energy Siphon to self LucidDreaming = 1204, // applied by Lucid Dreaming to self @@ -138,6 +136,11 @@ public enum SID : uint GarudasFavor = 2725, // applied by Summon Garuda II to self RubysGlimmer = 3873, // applied by Searing Light to self RefulgentLux = 3874, // applied by Summon Solar Bahamut to self + + //Shared + Addle = ClassShared.SID.Addle, // applied by Addle to target + Swiftcast = ClassShared.SID.Swiftcast, // applied by Swiftcast to self + } public sealed class Definitions : IDisposable diff --git a/BossMod/ActionQueue/ClassShared.cs b/BossMod/ActionQueue/ClassShared.cs index c647192c1..fd63ab431 100644 --- a/BossMod/ActionQueue/ClassShared.cs +++ b/BossMod/ActionQueue/ClassShared.cs @@ -62,6 +62,26 @@ public enum AID : uint DeflectVeryEasy = 18863 } +public enum SID : uint +{ + None = 0, + + // Tank + Reprisal = 1193, // applied by Reprisal to target + + // Melee + Feint = 1195, // applied by Feint to self + TrueNorth = 1250, // applied by True North to self + + // PhysRanged + Peloton = 1199, // applied by Peloton to self/party + + // Caster/Healer + Addle = 1203, // applied by Addle to target + Swiftcast = 167, // applied by Swiftcast to self + Raise = 148, // applied by Raise to target +} + public sealed class Definitions : IDisposable { public Definitions(ActionDefinitions d) diff --git a/BossMod/ActionQueue/Healers/AST.cs b/BossMod/ActionQueue/Healers/AST.cs index 4cbd3db06..bed6164e2 100644 --- a/BossMod/ActionQueue/Healers/AST.cs +++ b/BossMod/ActionQueue/Healers/AST.cs @@ -96,7 +96,6 @@ public enum SID : uint { None = 0, LucidDreaming = 1204, // applied by Lucid Dreaming to self - Swiftcast = 167, // applied by Swiftcast to self Surecast = 160, // applied by Surecast to self Combust = 838, AspectedHelios = 836, @@ -109,6 +108,9 @@ public enum SID : uint TheBalance = 3887, TheSpear = 3889, Divining = 3893, + + //Shared + Swiftcast = ClassShared.SID.Swiftcast, // applied by Swiftcast to self } public sealed class Definitions : IDisposable diff --git a/BossMod/ActionQueue/Healers/SCH.cs b/BossMod/ActionQueue/Healers/SCH.cs index 677872a15..b35428775 100644 --- a/BossMod/ActionQueue/Healers/SCH.cs +++ b/BossMod/ActionQueue/Healers/SCH.cs @@ -97,10 +97,12 @@ public enum SID : uint Biolysis = 1895, Galvanize = 297, // applied by Adloquium to target, shield LucidDreaming = 1204, // applied by Lucid Dreaming to self - Swiftcast = 167, // applied by Swiftcast to self Sleep = 3, // applied by Repose to target BanefulImpaction = 3883, // applied by Baneful Impaction to target ImpactImminent = 3882, // applied by Chain Stratagem to self + + //Shared + Swiftcast = ClassShared.SID.Swiftcast, // applied by Swiftcast to self } public sealed class Definitions : IDisposable diff --git a/BossMod/ActionQueue/Healers/SGE.cs b/BossMod/ActionQueue/Healers/SGE.cs index b99230c56..2576c275f 100644 --- a/BossMod/ActionQueue/Healers/SGE.cs +++ b/BossMod/ActionQueue/Healers/SGE.cs @@ -87,7 +87,6 @@ public enum SID : uint None = 0, Sprint = 50, // applied by Sprint to self Surecast = 160, // applied by Surecast to self - Swiftcast = 167, // applied by Swiftcast to self Eukrasia = 2606, Kardion = 2605, // applied by Kardia to self Kardia = 2604, // applied by Kardia to self @@ -113,6 +112,9 @@ public enum SID : uint EukrasianDosisII = 2615, // applied by Eukrasian Dosis II to target EukrasianDosisIII = 2616, // applied by Eukrasian Dosis III to target EukrasianDyskrasia = 3897, // applied by Eukrasian Dyskrasia to target + + //Shared + Swiftcast = ClassShared.SID.Swiftcast, // applied by Swiftcast to self } public sealed class Definitions : IDisposable diff --git a/BossMod/ActionQueue/Healers/WHM.cs b/BossMod/ActionQueue/Healers/WHM.cs index 2c16862e1..3dac156c9 100644 --- a/BossMod/ActionQueue/Healers/WHM.cs +++ b/BossMod/ActionQueue/Healers/WHM.cs @@ -90,7 +90,6 @@ public enum SID : uint Dia = 1871, // applied by Dia to target, dot Medica2 = 150, // applied by Medica2 to targets, hot Freecure = 155, // applied by Cure1 to self, next cure2 is free - Swiftcast = 167, // applied by Swiftcast to self, next gcd is instant ThinAir = 1217, // applied by Thin Air to self, next gcd costs no mp LucidDreaming = 1204, // applied by Lucid Dreaming to self, mp regen DivineBenison = 1218, // applied by Divine Benison to target, shield @@ -108,6 +107,9 @@ public enum SID : uint MedicaIII = 3880, // applied by Medica III to self/target DivineGrace = 3881, // applied by Temperance to self DivineCaress = 3903, // applied by Divine Caress to self/target + + //Shared + Swiftcast = ClassShared.SID.Swiftcast, // applied by Swiftcast to self } public sealed class Definitions : IDisposable diff --git a/BossMod/ActionQueue/Melee/DRG.cs b/BossMod/ActionQueue/Melee/DRG.cs index 5d11d4a57..20f87c0ca 100644 --- a/BossMod/ActionQueue/Melee/DRG.cs +++ b/BossMod/ActionQueue/Melee/DRG.cs @@ -86,13 +86,15 @@ public enum SID : uint DraconianFire = 1863, // applied by Fang and Claw, Wheeling Thrust to self RightEye = 1910, // applied by Dragon Sight to self DiveReady = 1243, // applied by Jump to self - TrueNorth = 1250, // applied by True North to self, ignore positionals Bloodbath = 84, // applied by Bloodbath to self, lifesteal - Feint = 1195, // applied by Feint to target, -10% phys and -5% magic damage dealt Stun = 2, // applied by Leg Sweep to target NastrondReady = 3844, // applied by Geirskogul to self DragonsFlight = 3845, // applied by Dragonfire Dive to self StarcrossReady = 3846, // applied by Stardiver to self + + //Shared + Feint = ClassShared.SID.Feint, // applied by Feint to target + TrueNorth = ClassShared.SID.TrueNorth, // applied by True North to self } public sealed class Definitions : IDisposable diff --git a/BossMod/ActionQueue/Melee/MNK.cs b/BossMod/ActionQueue/Melee/MNK.cs index e155090fd..5d9ec8fbb 100644 --- a/BossMod/ActionQueue/Melee/MNK.cs +++ b/BossMod/ActionQueue/Melee/MNK.cs @@ -105,14 +105,16 @@ public enum SID : uint SixSidedStar = 2514, // applied by Six-Sided Star to self OpoOpoForm = 107, // applied by Demolish, Pouncing Coeurl to self PerfectBalance = 110, // applied by Perfect Balance to self - Feint = 1195, // applied by Feint to target - TrueNorth = 1250, // applied by True North to self, ignore positionals Stun = 2, // applied by Leg Sweep to target LostFontofPower = 2346, BannerHonoredSacrifice = 2327, LostExcellence = 2564, Memorable = 2565, + + //Shared + Feint = ClassShared.SID.Feint, // applied by Feint to target + TrueNorth = ClassShared.SID.TrueNorth, // applied by True North to self } public sealed class Definitions : IDisposable diff --git a/BossMod/ActionQueue/Melee/NIN.cs b/BossMod/ActionQueue/Melee/NIN.cs index 4aab04340..07927bf6f 100644 --- a/BossMod/ActionQueue/Melee/NIN.cs +++ b/BossMod/ActionQueue/Melee/NIN.cs @@ -107,7 +107,6 @@ public enum SID : uint Bloodbath = 84, // applied by Bloodbath to self TrickAttack = 3254, // applied by Trick Attack to target KunaisBane = 3906, // applied by Kunai's Bane to target - Feint = 1195, // applied by Feint to target TenChiJin = 1186, // applied by Ten Chi Jin to self Mudra = 496, // applied by Ten, Ten, Chi, Chi, Jin, Jin to self RaijuReady = 2690, // applied by Raiton, Raiton to self @@ -120,6 +119,10 @@ public enum SID : uint Meisui = 2689, // applied by Meisui to self Bunshin = 1954, // applied by Bunshin to self PhantomKamaitachiReady = 2723, // applied by Bunshin to self + + //Shared + Feint = ClassShared.SID.Feint, // applied by Feint to target + TrueNorth = ClassShared.SID.TrueNorth, // applied by True North to self } public sealed class Definitions : IDisposable diff --git a/BossMod/ActionQueue/Melee/RPR.cs b/BossMod/ActionQueue/Melee/RPR.cs index 99a560875..088c9b655 100644 --- a/BossMod/ActionQueue/Melee/RPR.cs +++ b/BossMod/ActionQueue/Melee/RPR.cs @@ -95,15 +95,17 @@ public enum SID : uint Threshold = 2595, CircleofSacrifice = 2600, BloodsownCircle = 2972, - TrueNorth = 1250, Bloodbath = 84, - Feint = 1195, Stun = 2, IdealHost = 3905, Oblatio = 3857, Executioner = 3858, PerfectioOcculta = 3859, - PerfectioParata = 3860 + PerfectioParata = 3860, + + //Shared + Feint = ClassShared.SID.Feint, // applied by Feint to target + TrueNorth = ClassShared.SID.TrueNorth, // applied by True North to self } public sealed class Definitions : IDisposable diff --git a/BossMod/ActionQueue/Melee/SAM.cs b/BossMod/ActionQueue/Melee/SAM.cs index f61b6a5c4..294ee7f70 100644 --- a/BossMod/ActionQueue/Melee/SAM.cs +++ b/BossMod/ActionQueue/Melee/SAM.cs @@ -83,7 +83,6 @@ public enum SID : uint { None = 0, Fugetsu = 1298, // applied by Jinpu, Gekko to self - Feint = 1195, // applied by Feint to target Higanbana = 1228, // applied by Higanbana to target Fuka = 1299, // applied by Kasha to self MeikyoShisui = 1233, // applied by Meikyo Shisui to self @@ -91,13 +90,16 @@ public enum SID : uint KaeshiSetsugekka = 4216, // applied by Midare Setsugekka to self TendoKaeshiGoken = 4217, // applied by Tendo Goken to self TendoKaeshiSetsugekka = 4218, // applied by Tendo Setsugekka to self - TrueNorth = 1250, // applied by True North to self EnhancedEnpi = 1236, // applied by Hissatsu: Yaten to self Meditate = 1231, // applied by Meditate to self OgiNamikiriReady = 2959, // applied by Ikishoten to self Tengentsu = 3853, // applied by Tengentsu to self ZanshinReady = 3855, // applied by Ikishoten to self Tendo = 3856, // applied by Meikyo Shisui to self + + //Shared + Feint = ClassShared.SID.Feint, // applied by Feint to target + TrueNorth = ClassShared.SID.TrueNorth, // applied by True North to self } public sealed class Definitions : IDisposable diff --git a/BossMod/ActionQueue/Melee/VPR.cs b/BossMod/ActionQueue/Melee/VPR.cs index 0a7ce74bd..f86028293 100644 --- a/BossMod/ActionQueue/Melee/VPR.cs +++ b/BossMod/ActionQueue/Melee/VPR.cs @@ -106,6 +106,10 @@ public enum SID : uint TwinbloodfangAOE = 3774, // applied by Swiftskin's Den, Hunter's Den to self PoisedForTwinfang = 3665, // applied by Uncoiled Fury, Uncoiled Twinblood to self PoisedForTwinblood = 3666, // applied by Uncoiled Fury, Uncoiled Twinfang to self + + //Shared + Feint = ClassShared.SID.Feint, // applied by Feint to target + TrueNorth = ClassShared.SID.TrueNorth, // applied by True North to self } public sealed class Definitions : IDisposable diff --git a/BossMod/ActionQueue/Ranged/BRD.cs b/BossMod/ActionQueue/Ranged/BRD.cs index b22eb148f..b137e6303 100644 --- a/BossMod/ActionQueue/Ranged/BRD.cs +++ b/BossMod/ActionQueue/Ranged/BRD.cs @@ -84,7 +84,6 @@ public enum SID : uint { None = 0, RagingStrikes = 125, // applied by Raging Strikes to self - Peloton = 1199, // applied by Peloton to self/target ArmsLength = 1209, // applied by Arm's Length to self Barrage = 128, // applied by Barrage to self BattleVoice = 141, // applied by Battle Voice to self/target @@ -106,6 +105,9 @@ public enum SID : uint ArmysEthos = 1933, // applied when leaving army's paeon without starting new song ResonantArrowReady = 3862, // applied by Barrage to self RadiantEncoreReady = 3863, // applied by Radiant Finale to self + + //Shared + Peloton = ClassShared.SID.Peloton, // applied by Peloton to self/party } public sealed class Definitions : IDisposable diff --git a/BossMod/ActionQueue/Ranged/DNC.cs b/BossMod/ActionQueue/Ranged/DNC.cs index 30c3e345d..4c7b27896 100644 --- a/BossMod/ActionQueue/Ranged/DNC.cs +++ b/BossMod/ActionQueue/Ranged/DNC.cs @@ -110,12 +110,14 @@ public enum SID : uint ThreefoldFanDance = 1820, // applied by Flourish, Fan Dance, Fan Dance II to self FourfoldFanDance = 2699, // applied by Flourish to self SilkenFlow = 2694, // applied by Fountain to self - Peloton = 1199, // applied by Peloton to self DancePartner = 1824, // applied by Closed Position to target ClosedPosition = 1823, // applied by Closed Position to self LastDanceReady = 3867, FinishingMoveReady = 3868, DanceOfTheDawnReady = 3869, + + //Shared + Peloton = ClassShared.SID.Peloton, // applied by Peloton to self/party } public sealed class Definitions : IDisposable diff --git a/BossMod/ActionQueue/Ranged/MCH.cs b/BossMod/ActionQueue/Ranged/MCH.cs index fc2422beb..1b27ad7d8 100644 --- a/BossMod/ActionQueue/Ranged/MCH.cs +++ b/BossMod/ActionQueue/Ranged/MCH.cs @@ -91,6 +91,9 @@ public enum SID : uint Flamethrower = 1205, // applied by Flamethrower to self ExcavatorReady = 3865, // applied by Chain Saw to self FullMetalMachinist = 3866, // applied by Hypercharge to self + + //Shared + Peloton = ClassShared.SID.Peloton, // applied by Peloton to self/party } public sealed class Definitions : IDisposable diff --git a/BossMod/ActionQueue/Tanks/DRK.cs b/BossMod/ActionQueue/Tanks/DRK.cs index 08cadb422..2c58ec00d 100644 --- a/BossMod/ActionQueue/Tanks/DRK.cs +++ b/BossMod/ActionQueue/Tanks/DRK.cs @@ -85,6 +85,9 @@ public enum SID : uint Delirium = 1972, EnhancedDelirium = 3836, Scorn = 3837, + + //Shared + Reprisal = ClassShared.SID.Reprisal, // applied by Reprisal to target } public sealed class Definitions : IDisposable diff --git a/BossMod/ActionQueue/Tanks/GNB.cs b/BossMod/ActionQueue/Tanks/GNB.cs index 524cb2bc6..e5e804ed5 100644 --- a/BossMod/ActionQueue/Tanks/GNB.cs +++ b/BossMod/ActionQueue/Tanks/GNB.cs @@ -88,7 +88,6 @@ public enum SID : uint ReadyToBlast = 2686, // applied by Burst Strike to self Nebula = 1834, // applied by Nebula to self Rampart = 1191, // applied by Rampart to self - Reprisal = 1193, // applied by Reprisal to target Camouflage = 1832, // applied by Camouflage to self ArmsLength = 1209, // applied by Arm's Length to self HeartOfLight = 1839, // applied by Heart of Light to self @@ -103,6 +102,9 @@ public enum SID : uint ReadyToRaze = 3839, // applied by Fated Circle to self ReadyToBreak = 3886, // applied by No mercy to self ReadyToReign = 3840, // applied by Bloodfest to target + + //Shared + Reprisal = ClassShared.SID.Reprisal, // applied by Reprisal to target } public sealed class Definitions : IDisposable diff --git a/BossMod/ActionQueue/Tanks/PLD.cs b/BossMod/ActionQueue/Tanks/PLD.cs index 8e15648e6..f2961df58 100644 --- a/BossMod/ActionQueue/Tanks/PLD.cs +++ b/BossMod/ActionQueue/Tanks/PLD.cs @@ -93,6 +93,9 @@ public enum SID : uint SupplicationReady = 3827, // applied by Atonement to self SepulchreReady = 3828, // applied by Supplication to self BladeOfHonorReady = 3831, // applied by Requiescat to self + + //Shared + Reprisal = ClassShared.SID.Reprisal, // applied by Reprisal to target } public sealed class Definitions : IDisposable diff --git a/BossMod/ActionQueue/Tanks/WAR.cs b/BossMod/ActionQueue/Tanks/WAR.cs index 82b5eaffd..2b453486d 100644 --- a/BossMod/ActionQueue/Tanks/WAR.cs +++ b/BossMod/ActionQueue/Tanks/WAR.cs @@ -98,7 +98,6 @@ public enum SID : uint ThrillOfBattle = 87, // applied by Thrill of Battle to self Holmgang = 409, // applied by Holmgang to self EquilibriumRegen = 2681, // applied by Equilibrium to self, hp regen - Reprisal = 1193, // applied by Reprisal to target ShakeItOff = 1457, // applied by Shake It Off to self/target, damage shield ShakeItOffHOT = 2108, // applied by Shake It Off to self/target RawIntuition = 735, // applied by Raw Intuition to self @@ -118,6 +117,9 @@ public enum SID : uint LostFontOfPower = 2346, LostBloodRage = 2566, BloodRush = 2567, + + //Shared + Reprisal = ClassShared.SID.Reprisal, // applied by Reprisal to target } public sealed class Definitions : IDisposable diff --git a/BossMod/Autorotation/akechi/AkechiDRG.cs b/BossMod/Autorotation/akechi/AkechiDRG.cs index dcacad4cd..7741dfdf6 100644 --- a/BossMod/Autorotation/akechi/AkechiDRG.cs +++ b/BossMod/Autorotation/akechi/AkechiDRG.cs @@ -8,6 +8,7 @@ public sealed class AkechiDRG(RotationModuleManager manager, Actor player) : Rot { #region Enums: Abilities / Strategies + //Which abilities/strategies we're tracking public enum Track { AOE, //Area of Effect actions @@ -19,6 +20,7 @@ public enum Track Geirskogul, //Geirskogul ability Stardiver, //Stardiver ability PiercingTalon, //Piercing Talon ability + TrueNorth, //True North ability LanceCharge, //Lance Charge ability BattleLitany, //Battle Litany ability MirageDive, //Mirage Dive ability @@ -57,16 +59,6 @@ public enum PotionStrategy Immediate //Use potion immediately } - //Piercing Talon strategy - public enum PiercingTalonStrategy - { - Automatic, //Use Piercing Talon when appropriate - Opener, //Use Piercing Talon as an opener - Force, //Force use of Piercing Talon - Ranged, //Use Piercing Talon for ranged situations - Forbid //Forbid the use of Piercing Talon - } - //Life Surge strategy public enum SurgeStrategy { @@ -113,16 +105,16 @@ public enum StardiverStrategy Delay //Delay use of Stardiver } - //general offensive strategies - public enum OffensiveStrategy + //Piercing Talon strategy + public enum PiercingTalonStrategy { - Automatic, //Automatically use offensive abilities - Force, //Force offensive abilities - Delay //Delay offensive abilities + Forbid, //Forbid the use of Piercing Talon + Allow, //Use Piercing Talon when appropriate + Opener, //Use Piercing Talon as an opener + Force, //Force use of Piercing Talon } - /* - //TODO: Positional shit + //True North strategy public enum TrueNorthStrategy { Automatic, //Late-Weave @@ -132,14 +124,22 @@ public enum TrueNorthStrategy Force, //Force Delay //Delay } - */ + + //Offensive strategies + public enum OffensiveStrategy + { + Automatic, //Automatically use offensive abilities + Force, //Force offensive abilities + Delay //Delay offensive abilities + } #endregion + //Module Definitions public static RotationModuleDefinition Definition() { //Module title & signature - var res = new RotationModuleDefinition("DRG (Akechi)", "Standard Rotation Module", "Standard rotation (Akechi)", "Akechi", RotationModuleQuality.Ok, BitMask.Build(Class.LNC, Class.DRG), 100); + var res = new RotationModuleDefinition("DRG (Akechi)", "Standard Rotation Module", "Standard rotation (Akechi)", "Akechi", RotationModuleQuality.Good, BitMask.Build(Class.LNC, Class.DRG), 100); #region Custom Strategies //Targeting strategy @@ -149,7 +149,7 @@ public static RotationModuleDefinition Definition() .AddOption(AOEStrategy.AutoOnPrimary, "AutoOnPrimary", "Use AOE actions on primary target if profitable") .AddOption(AOEStrategy.ForceST, "Force ST", "Force Single-Target rotation") .AddOption(AOEStrategy.Force123ST, "Only 1-2-3 ST", "Force only ST 1-2-3 rotation (No Buffs)") - .AddOption(AOEStrategy.ForceBuffsST, "Only 1-4-5 ST", "Force only ST 1-4-5 rotation (No 1/1.5-2-3)") + .AddOption(AOEStrategy.ForceBuffsST, "Only 1-4-5 ST", "Force only ST 1-4-5 rotation (Buffs Only)") .AddOption(AOEStrategy.ForceAOE, "Force AOE", "Force AOE rotation, even if less than 3 targets"); //Burst strategy @@ -209,24 +209,20 @@ public static RotationModuleDefinition Definition() //Piercing Talon strategy res.Define(Track.PiercingTalon).As("Piercing Talon", "Talon", uiPriority: 20) - .AddOption(PiercingTalonStrategy.Automatic, "Automatic", "Use Piercing Talon only if already in combat & outside melee range") - .AddOption(PiercingTalonStrategy.Opener, "Opener", "Use Piercing Talon as the first GCD, regardless of range") + .AddOption(PiercingTalonStrategy.Forbid, "Forbid", "Forbid use of Piercing Talon at all") + .AddOption(PiercingTalonStrategy.Allow, "Allow", "Allow use of Piercing Talon only if already in combat & outside melee range") .AddOption(PiercingTalonStrategy.Force, "Force", "Force Piercing Talon usage ASAP (even in melee range)") - .AddOption(PiercingTalonStrategy.Ranged, "Ranged", "Use Piercing Talon if outside melee range") - .AddOption(PiercingTalonStrategy.Forbid, "Forbid", "Do not use Piercing Talon at all") .AddAssociatedActions(DRG.AID.PiercingTalon); - /* TODO: Positional shit //True North strategy res.Define(Track.TrueNorth).As("True North", "T.North", uiPriority: 10) .AddOption(TrueNorthStrategy.Automatic, "Automatic", "Late-weaves True North when out of positional") .AddOption(TrueNorthStrategy.ASAP, "ASAP", "Use True North as soon as possible when out of positional", 45, 10, ActionTargets.Self, 50) .AddOption(TrueNorthStrategy.Rear, "Rear", "Use True North for rear positional only", 45, 10, ActionTargets.Self, 50) .AddOption(TrueNorthStrategy.Flank, "Flank", "Use True North for flank positional only", 45, 10, ActionTargets.Self, 50) - .AddOption(TrueNorthStrategy.Delay, "Delay", "Delay True North usage", 0, 0, ActionTargets.None, 50) .AddOption(TrueNorthStrategy.Force, "Force", "Force True North usage", 45, 10, ActionTargets.Self, 50) + .AddOption(TrueNorthStrategy.Delay, "Delay", "Delay True North usage", 0, 0, ActionTargets.None, 50) .AddAssociatedActions(ClassShared.AID.TrueNorth); - */ #endregion @@ -287,123 +283,121 @@ public static RotationModuleDefinition Definition() #region Priorities - // Priorities for Global Cooldowns (GCDs) + //Priorities for Global Cooldowns (GCDs) public enum GCDPriority { - None = 0, // No priority - Combo123 = 350, // Priority for the first three combo actions - NormalGCD = 500, // Standard priority for normal GCD actions - ForcedGCD = 900, // High priority for forced GCD actions + None = 0, //No priority + Combo123 = 350, //Priority for the first three combo actions + NormalGCD = 500, //Standard priority for normal GCD actions + ForcedGCD = 900, //High priority for forced GCD actions } - // Priorities for Off Global Cooldowns (oGCDs) + //Priorities for Off Global Cooldowns (oGCDs) public enum OGCDPriority { - None = 0, // No priority - // Flexible actions with varying priorities - MirageDive = 500, // Priority for Mirage Dive - Nastrond = 540, // Priority for Nastrond - RiseOfTheDragon = 550, // Priority for Rise of the Dragon - Starcross = 550, // Priority for Starcross - WyrmwindThrust = 590, // Priority for Wyrmwind Thrust - TrueNorth = 595, // Priority for True North - Stardiver = 600, // Priority for Stardiver - // Non-flexible actions with fixed priorities - Jump = 680, // Priority for Jump - DragonfireDive = 690, // Priority for Dragonfire Dive - Geirskogul = 700, // Priority for Geirskogul - Buffs = 800, // Priority for buffs - ForcedOGCD = 900, // High priority for forced oGCD actions + None = 0, //No priority + //Flexible actions with varying priorities + MirageDive = 500, //Priority for Mirage Dive + Nastrond = 540, //Priority for Nastrond + Stardiver = 550, //Priority for Stardiver + RiseOfTheDragon = 560, //Priority for Rise of the Dragon + Starcross = 560, //Priority for Starcross + WyrmwindThrust = 570, //Priority for Wyrmwind Thrust (normal) + TrueNorth = 580, //Priority for True North + //Non-flexible actions with fixed priorities + Jump = 660, //Priority for Jump + WyrmwindThrustOpti = 670, //Priority for Wyrmwind Thrust (optimal) + DragonfireDive = 680, //Priority for Dragonfire Dive + Geirskogul = 700, //Priority for Geirskogul + Buffs = 800, //Priority for buffs + ForcedOGCD = 900, //High priority for forced oGCD actions } #endregion #region Placeholders for Variables - private float GCDLength; // Length of the global cooldown - private float blCD; // Cooldown for Battle Litany - private float lcLeft; // Time remaining for Lance Charge - private float lcCD; // Cooldown for Lance Charge - private float powerLeft; // Time remaining for Power Surge - private float chaosLeft; // Remaining time for Chaotic Spring effect - - public float downtimeIn; // Duration of downtime in combat - private float PotionLeft; // Remaining time for potion effect - private float RaidBuffsLeft; // Time left for raid buffs - private float RaidBuffsIn; // Time until next raid buffs are applied - public float BurstWindowLeft; // Time left in the burst window - public float BurstWindowIn; // Time until the next burst window starts - - private int focusCount; // Count of Firstmind's Focus gauge - - private bool hasLOTD; // Flag for Life of the Dragon status - private bool hasLC; // Flag for Lance Charge status - private bool hasBL; // Flag for Battle Litany status - private bool hasMD; // Flag for Mirage Dive status - private bool hasDF; // Flag for Dragon's Flight status - private bool hasSC; // Flag for Starcross status - private bool hasNastrond; // Flag for Nastrond status - - private bool canLC; // Ability to use Lance Charge - private bool canBL; // Ability to use Blood of the Dragon - private bool canLS; // Ability to use Life Surge - private bool canJump; // Ability to use Jump - private bool canDD; // Ability to use Dragonfire Dive - private bool canGeirskogul; // Ability to use Geirskogul - private bool canMD; // Ability to use Mirage Dive - private bool canNastrond; // Ability to use Nastrond - private bool canSD; // Ability to use Sonic Dance - private bool canWT; // Ability to use Wyrmwind Thrust - private bool canROTD; // Ability to use Rise of the Dragon - private bool canSC; // Ability to use Starcross - - public DRG.AID NextGCD; // Next global cooldown action to be used - private GCDPriority NextGCDPrio; // Priority of the next GCD for cooldown decision making + private float GCDLength; //Length of the global cooldown + private float blCD; //Cooldown for Battle Litany + private float lcLeft; //Time remaining for Lance Charge + private float lcCD; //Cooldown for Lance Charge + private float powerLeft; //Time remaining for Power Surge + private float chaosLeft; //Remaining time for Chaotic Spring DoT + + public float downtimeIn; //Duration of downtime in combat + private float PotionLeft; //Remaining time for potion effect + private float RaidBuffsLeft; //Time left for raid buffs + private float RaidBuffsIn; //Time until next raid buffs are applied + public float BurstWindowLeft; //Time left in the burst window + public float BurstWindowIn; //Time until the next burst window starts + + private int focusCount; //Count of Firstmind's Focus gauge + + private bool hasLOTD; //Flag for Life of the Dragon status + private bool hasLC; //Flag for Lance Charge status + private bool hasBL; //Flag for Battle Litany status + private bool hasMD; //Flag for Mirage Dive status + private bool hasDF; //Flag for Dragon's Flight status + private bool hasSC; //Flag for Starcross status + private bool hasNastrond; //Flag for Nastrond status + + private bool canLC; //Ability to use Lance Charge + private bool canBL; //Ability to use Battle Litany + private bool canLS; //Ability to use Life Surge + private bool canJump; //Ability to use Jump + private bool canDD; //Ability to use Dragonfire Dive + private bool canGeirskogul; //Ability to use Geirskogul + private bool canMD; //Ability to use Mirage Dive + private bool canNastrond; //Ability to use Nastrond + private bool canSD; //Ability to use Stardiver + private bool canWT; //Ability to use Wyrmwind Thrust + private bool canROTD; //Ability to use Rise of the Dragon + private bool canSC; //Ability to use Starcross + + public DRG.AID NextGCD; //Next global cooldown action to be used + private GCDPriority NextGCDPrio; //Priority of the next GCD for cooldown decision making #endregion #region Module Helpers - // Check if the desired ability is unlocked + //Check if the desired ability is unlocked private bool Unlocked(DRG.AID aid) => ActionUnlocked(ActionID.MakeSpell(aid)); - // Get remaining cooldown time for the specified action + //Get remaining cooldown time for the specified action private float CD(DRG.AID aid) => World.Client.Cooldowns[ActionDefinitions.Instance.Spell(aid)!.MainCooldownGroup].Remaining; - // Get the last action used in the combo sequence + //Get the last action used in the combo sequence private DRG.AID ComboLastMove => (DRG.AID)World.Client.ComboState.Action; - // Check if the target is within melee range (3 yalms) + //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 + //Check if the target is within 15 yalms private bool In15y(Actor? target) => Player.DistanceToHitbox(target) <= 14.75; - // Check if the desired action is ready (cooldown < 0.6 seconds) + //Check if the desired action is ready (cooldown < 0.6 seconds) private bool ActionReady(DRG.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; - - // Check if the potion should be used before raid buffs expire + //Check if the potion should be used before raid buffs expire private bool IsPotionBeforeRaidbuffs() => RaidBuffsLeft == 0 && PotionLeft > RaidBuffsIn + 17.5f; - // Check if status effect is on self + //Check if status effect is on self public bool HasEffect(SID sid) where SID : Enum => Player.FindStatus((uint)(object)sid, Player.InstanceID) != null; - // Count number of targets hit by AOE attack + //Count number of targets hit by AOE attack private int NumTargetsHitByAOE(Actor primary) => Hints.NumPriorityTargetsInAOECone(Player.Position, 10, (primary.Position - Player.Position).Normalized(), 45.Degrees()); - // Check if a target is hit by AOE + //Check if a target is hit by AOE private bool IsHitByAOE(Actor primary, Actor check) => Hints.TargetInAOECone(check, Player.Position, 10, (primary.Position - Player.Position).Normalized(), 45.Degrees()); - // Count number of targets hit by spear attack + //Count number of targets hit by spear attack private int NumTargetsHitBySpear(Actor primary) => Hints.NumPriorityTargetsInAOERect(Player.Position, (primary.Position - Player.Position).Normalized(), 15, 2); - // Check if a target is hit by spear + //Check if a target is hit by spear private bool IsHitBySpear(Actor primary, Actor check) => Hints.TargetInAOERect(check, Player.Position, (primary.Position - Player.Position).Normalized(), 15, 2); - // Check targeting for AOE strategies + //Check targeting for AOE strategies private (Actor?, int) CheckAOETargeting(AOEStrategy strategy, Actor? primaryTarget, float range, Func numTargets, Func check) => strategy switch { AOEStrategy.AutoTargetHitPrimary => FindBetterTargetBy(primaryTarget, range, t => primaryTarget == null || check(t, primaryTarget) ? numTargets(t) : 0), @@ -413,6 +407,20 @@ public enum OGCDPriority _ => (null, 0) }; + //Check which positional Player is on + private Positional GetCurrentPositional(Actor target) => (Player.Position - target.Position).Normalized().Dot(target.Rotation.ToDirection()) switch + { + < -0.7071068f => Positional.Rear, + < 0.7071068f => Positional.Flank, + _ => Positional.Front + }; + + //Check if Player is on Rear (back) positional + private bool IsOnRear(Actor target) => GetCurrentPositional(target) == Positional.Rear; + + //Check if Player is on Flank (side) positional + private bool IsOnFlank(Actor target) => GetCurrentPositional(target) == Positional.Flank; + #endregion public override void Execute(StrategyValues strategy, Actor? primaryTarget, float estimatedAnimLockDelay, bool isMoving) @@ -421,39 +429,39 @@ public override void Execute(StrategyValues strategy, Actor? primaryTarget, floa #region Cooldown-related - // Gauge management - var gauge = World.Client.GetGauge(); // Retrieve Dragoon gauge data - focusCount = gauge.FirstmindsFocusCount; // Update focus count from the gauge - hasLOTD = gauge.LotdTimer > 0; // Check if Life of the Dragon (LOTD) is active + //Gauge Management + var gauge = World.Client.GetGauge(); //Retrieve Dragoon gauge data + focusCount = gauge.FirstmindsFocusCount; //Update focus count from the gauge + hasLOTD = gauge.LotdTimer > 0; //Check if Life of the Dragon (LOTD) is active - // Cooldown checks - lcCD = CD(DRG.AID.LanceCharge); // Get cooldown for Lance Charge - lcLeft = SelfStatusLeft(DRG.SID.LanceCharge, 20); // Get remaining time for Lance Charge effect - powerLeft = SelfStatusLeft(DRG.SID.PowerSurge, 5); // Get remaining time for Power Surge effect + //Cooldown Checks + lcCD = CD(DRG.AID.LanceCharge); //Get cooldown for Lance Charge + lcLeft = SelfStatusLeft(DRG.SID.LanceCharge, 20); //Get remaining time for Lance Charge effect + powerLeft = SelfStatusLeft(DRG.SID.PowerSurge, 30); //Get remaining time for Power Surge effect chaosLeft = MathF.Max(StatusDetails(primaryTarget, DRG.SID.ChaosThrust, Player.InstanceID).Left, - StatusDetails(primaryTarget, DRG.SID.ChaoticSpring, Player.InstanceID).Left); // Get max remaining time for Chaos Thrust and Chaotic Spring - blCD = CD(DRG.AID.BattleLitany); // Get cooldown for Battle Litany - GCDLength = ActionSpeed.GCDRounded(World.Client.PlayerStats.SkillSpeed, World.Client.PlayerStats.Haste, Player.Level); // Calculate rounded global cooldown (GCD) duration - hasMD = HasEffect(DRG.SID.DiveReady); // Check if Mirage Dive is ready - hasNastrond = HasEffect(DRG.SID.NastrondReady); // Check if Nastrond is ready - hasLC = lcCD is >= 40 and <= 60; // Check if Lance Charge is within cooldown range - hasBL = blCD is >= 100 and <= 120; // Check if Battle Litany is within cooldown range - hasDF = HasEffect(DRG.SID.DragonsFlight); // Check if Dragon's Flight effect is active - hasSC = HasEffect(DRG.SID.StarcrossReady); // Check if Starcross is ready - - // Cooldown availability checks - canLC = Unlocked(DRG.AID.LanceCharge) && ActionReady(DRG.AID.LanceCharge); // Can use Lance Charge? - canBL = Unlocked(DRG.AID.BattleLitany) && ActionReady(DRG.AID.BattleLitany); // Can use Battle Litany? - canLS = Unlocked(DRG.AID.LifeSurge) && CD(DRG.AID.LifeSurge) >= 40; // Can use Life Surge if cooldown is sufficient - canJump = Unlocked(DRG.AID.Jump) && ActionReady(DRG.AID.Jump); // Can use Jump? - canDD = Unlocked(DRG.AID.DragonfireDive) && ActionReady(DRG.AID.DragonfireDive); // Can use Dragonfire Dive? - canGeirskogul = Unlocked(DRG.AID.Geirskogul) && ActionReady(DRG.AID.Geirskogul); // Can use Geirskogul? - canMD = Unlocked(DRG.AID.MirageDive) && hasMD; // Can use Mirage Dive if it's ready - canNastrond = Unlocked(DRG.AID.Nastrond) && hasNastrond; // Can use Nastrond if it's ready - canSD = Unlocked(DRG.AID.Stardiver) && ActionReady(DRG.AID.Stardiver); // Can use Stardiver? - canWT = Unlocked(DRG.AID.WyrmwindThrust) && ActionReady(DRG.AID.WyrmwindThrust); // Can use Wyrmwind Thrust? - canROTD = Unlocked(DRG.AID.RiseOfTheDragon) && hasDF; // Can use Rise of the Dragon if Dragon's Flight is active - canSC = Unlocked(DRG.AID.Starcross) && hasSC; // Can use Starcross if it's ready + StatusDetails(primaryTarget, DRG.SID.ChaoticSpring, Player.InstanceID).Left); //Get max remaining time for Chaos Thrust or Chaotic Spring + blCD = CD(DRG.AID.BattleLitany); //Get cooldown for Battle Litany + GCDLength = ActionSpeed.GCDRounded(World.Client.PlayerStats.SkillSpeed, World.Client.PlayerStats.Haste, Player.Level); //Calculate rounded global cooldown (GCD) duration + hasMD = HasEffect(DRG.SID.DiveReady); //Check if Mirage Dive is ready + hasNastrond = HasEffect(DRG.SID.NastrondReady); //Check if Nastrond is ready + hasLC = lcCD is >= 40 and <= 60; //Check if Lance Charge is within cooldown range + hasBL = blCD is >= 100 and <= 120; //Check if Battle Litany is within cooldown range + hasDF = HasEffect(DRG.SID.DragonsFlight); //Check if Dragon's Flight effect is active + hasSC = HasEffect(DRG.SID.StarcrossReady); //Check if Starcross is ready + + //Minimum Conditions + canLC = Unlocked(DRG.AID.LanceCharge) && ActionReady(DRG.AID.LanceCharge); //minimum condition(s) to execute Lance Charge + canBL = Unlocked(DRG.AID.BattleLitany) && ActionReady(DRG.AID.BattleLitany); //minimum condition(s) to execute Battle Litany + canLS = Unlocked(DRG.AID.LifeSurge); //minimum condition(s) to execute Life Surge + canJump = Unlocked(DRG.AID.Jump) && ActionReady(DRG.AID.Jump); //minimum condition(s) to execute Jump + canDD = Unlocked(DRG.AID.DragonfireDive) && ActionReady(DRG.AID.DragonfireDive); //minimum condition(s) to execute Dragonfire Dive + canGeirskogul = Unlocked(DRG.AID.Geirskogul) && ActionReady(DRG.AID.Geirskogul); //minimum condition(s) to execute Geirskogul + canMD = Unlocked(DRG.AID.MirageDive) && hasMD; //minimum condition(s) to execute Mirage Dive + canNastrond = Unlocked(DRG.AID.Nastrond) && hasNastrond; //minimum condition(s) to execute Nastrond + canSD = Unlocked(DRG.AID.Stardiver) && ActionReady(DRG.AID.Stardiver); //minimum condition(s) to execute Stardiver + canWT = Unlocked(DRG.AID.WyrmwindThrust) && ActionReady(DRG.AID.WyrmwindThrust); //minimum condition(s) to execute Wyrmwind Thrust + canROTD = Unlocked(DRG.AID.RiseOfTheDragon) && hasDF; //minimum condition(s) to execute Rise of the Dragon + canSC = Unlocked(DRG.AID.Starcross) && hasSC; //minimum condition(s) to execute Starcross #endregion @@ -470,61 +478,61 @@ public override void Execute(StrategyValues strategy, Actor? primaryTarget, floa #region AOEStrategy 'Force' Execution - var AOEStrategy = strategy.Option(Track.AOE).As(); // Retrieve the current AOE strategy + var AOEStrategy = strategy.Option(Track.AOE).As(); //Retrieve the current AOE strategy - // Force specific actions based on the AOE strategy selected - if (AOEStrategy == AOEStrategy.ForceST) // If forced single target - QueueGCD(NextFullST(), primaryTarget, GCDPriority.ForcedGCD); // Queue the next single target action + //Force specific actions based on the AOE strategy selected + if (AOEStrategy == AOEStrategy.ForceST) //If forced single target + QueueGCD(NextFullST(), primaryTarget, GCDPriority.ForcedGCD); //Queue the next single target action - if (AOEStrategy == AOEStrategy.Force123ST) // If forced 123 combo - QueueGCD(UseOnly123ST(), primaryTarget, GCDPriority.ForcedGCD); // Queue the 123 combo action + if (AOEStrategy == AOEStrategy.Force123ST) //If forced 123 combo + QueueGCD(UseOnly123ST(), primaryTarget, GCDPriority.ForcedGCD); //Queue the 123 combo action - if (AOEStrategy == AOEStrategy.ForceBuffsST) // If forced buffs combo - QueueGCD(UseOnly145ST(), primaryTarget, GCDPriority.ForcedGCD); // Queue the buffed 145 combo action + if (AOEStrategy == AOEStrategy.ForceBuffsST) //If forced buffs combo + QueueGCD(UseOnly145ST(), primaryTarget, GCDPriority.ForcedGCD); //Queue the buffed 145 combo action - if (AOEStrategy == AOEStrategy.ForceAOE) // If forced AOE action - QueueGCD(NextFullAOE(), primaryTarget, GCDPriority.ForcedGCD); // Queue the next AOE action + if (AOEStrategy == AOEStrategy.ForceAOE) //If forced AOE action + QueueGCD(NextFullAOE(), primaryTarget, GCDPriority.ForcedGCD); //Queue the next AOE action #endregion #region Burst Window Strategy - var burst = strategy.Option(Track.Burst); // Retrieve the current burst strategy - var burstStrategy = burst.As(); // Cast to BurstStrategy type - var hold = burstStrategy == BurstStrategy.Conserve; // Check if the burst should be conserved + var burst = strategy.Option(Track.Burst); //Retrieve the current burst strategy + var burstStrategy = burst.As(); //Cast to BurstStrategy type + var hold = burstStrategy == BurstStrategy.Conserve; //Check if the burst should be conserved - // Set burst window timings based on the selected burst strategy + //Set burst window timings based on the selected burst strategy (BurstWindowIn, BurstWindowLeft) = burstStrategy switch { - BurstStrategy.Automatic => (RaidBuffsIn, IsPotionBeforeRaidbuffs() ? 0 : Math.Max(PotionLeft, RaidBuffsLeft)), // Automatic mode: Set timings based on raid buffs and potion availability - BurstStrategy.UnderRaidBuffs => (RaidBuffsIn, RaidBuffsLeft), // Under Raid Buffs: Set timings directly from raid buffs - BurstStrategy.UnderPotion => (PotionCD, PotionLeft), // Under Potion: Use potion cooldown and remaining time - _ => (0, 0) // Default case: Set timings to zero + BurstStrategy.Automatic => (RaidBuffsIn, IsPotionBeforeRaidbuffs() ? 0 : Math.Max(PotionLeft, RaidBuffsLeft)), //Automatic mode: Set timings based on raid buffs and potion availability + BurstStrategy.UnderRaidBuffs => (RaidBuffsIn, RaidBuffsLeft), //Under Raid Buffs: Set timings directly from raid buffs + BurstStrategy.UnderPotion => (PotionCD, PotionLeft), //Under Potion: Use potion cooldown and remaining time + _ => (0, 0) //Default case: Set timings to zero }; #endregion #region Targeting - // Check if Doom Spike is unlocked; if so, determine the best AOE target and count + //Check if Doom Spike is unlocked; if so, determine the best AOE target and count var (AOEBestTarget, AOETargetCount) = Unlocked(DRG.AID.DoomSpike) ? CheckAOETargeting(AOEStrategy, primaryTarget, 10, NumTargetsHitByAOE, IsHitByAOE) - : (null, 0); // Set to null and count 0 if not unlocked + : (null, 0); //Set to null and count 0 if not unlocked - // Check if Geirskogul is unlocked; if so, determine the best spear target and count + //Check if Geirskogul is unlocked; if so, determine the best spear target and count var (SpearBestTarget, SpearTargetCount) = Unlocked(DRG.AID.Geirskogul) ? CheckAOETargeting(AOEStrategy, primaryTarget, 15, NumTargetsHitBySpear, IsHitBySpear) - : (null, 0); // Set to null and count 0 if not unlocked + : (null, 0); //Set to null and count 0 if not unlocked - // Determine if using AOE is viable (at least 3 targets hit) + //Determine if using AOE is viable (at least 3 targets hit) var useAOE = AOETargetCount >= 3; - // Determine if using Spear is viable (at least 1 target hit) - var useSpear = SpearTargetCount > 0; + //Determine if using Spear is viable (at least 2 targets hit) + var useSpear = SpearTargetCount > 1; - // Select the best target for Spear based on whether it's viable, or default to primary target + //Select the best target for Spear based on whether it's viable, or default to primary target var bestSpeartarget = useSpear ? SpearBestTarget : primaryTarget; - // Select the best target for AOE based on whether it's viable, or default to primary target + //Select the best target for AOE based on whether it's viable, or default to primary target var bestAOEtarget = useAOE ? AOEBestTarget : primaryTarget; #endregion @@ -545,7 +553,7 @@ public override void Execute(StrategyValues strategy, Actor? primaryTarget, floa //Execute Life Surge if conditions met var lsStrat = strategy.Option(Track.LifeSurge).As(); - if (!hold && GCD < 1.25f && ShouldUseLifeSurge(lsStrat, primaryTarget)) + if (!hold && ShouldUseLifeSurge(lsStrat, primaryTarget)) QueueOGCD(DRG.AID.LifeSurge, Player, lsStrat is SurgeStrategy.Force ? OGCDPriority.ForcedOGCD : OGCDPriority.Buffs); //Execute Jump ability if available @@ -581,7 +589,7 @@ public override void Execute(StrategyValues strategy, Actor? primaryTarget, floa //Execute Wyrmwind Thrust if available var wtStrat = strategy.Option(Track.WyrmwindThrust).As(); if (!hold && ShouldUseWyrmwindThrust(wtStrat, primaryTarget)) - QueueOGCD(DRG.AID.WyrmwindThrust, bestSpeartarget, wtStrat == OffensiveStrategy.Force ? OGCDPriority.ForcedOGCD : OGCDPriority.WyrmwindThrust); + QueueOGCD(DRG.AID.WyrmwindThrust, bestSpeartarget, wtStrat == OffensiveStrategy.Force ? OGCDPriority.ForcedOGCD : (HasEffect(DRG.SID.LanceCharge) ? OGCDPriority.WyrmwindThrustOpti : OGCDPriority.WyrmwindThrust)); //Execute Rise of the Dragon if available var riseStrat = strategy.Option(Track.RiseOfTheDragon).As(); @@ -602,10 +610,14 @@ public override void Execute(StrategyValues strategy, Actor? primaryTarget, floa if (ShouldUsePotion(strategy.Option(Track.Potion).As())) Hints.ActionsToExecute.Push(ActionDefinitions.IDPotionStr, Player, ActionQueue.Priority.VeryHigh + (int)OGCDPriority.ForcedOGCD, 0, GCD - 0.9f); + //Execute True North if available + if (!hold && ShouldUseTrueNorth(strategy.Option(Track.TrueNorth).As(), primaryTarget)) + QueueOGCD(DRG.AID.TrueNorth, Player, OGCDPriority.TrueNorth); + #endregion #region AI - // AI hints for positioning + //AI hints for positioning var goalST = primaryTarget != null ? Hints.GoalSingleTarget(primaryTarget, 3) : null; var goalAOE = primaryTarget != null ? Hints.GoalAOECone(primaryTarget, 10, 45.Degrees()) : null; var goal = AOEStrategy switch @@ -624,53 +636,53 @@ public override void Execute(StrategyValues strategy, Actor? primaryTarget, floa #region Core Execution Helpers - // Determine the effect application delay for specific abilities + //Determine the effect application delay for specific abilities private float EffectApplicationDelay(DRG.AID aid) => aid switch { - DRG.AID.ChaoticSpring => 0.45f, // Chaotic Spring delay - DRG.AID.HighJump => 0.49f, // High Jump delay - DRG.AID.CoerthanTorment => 0.49f, // Coerthan Torment delay - DRG.AID.BattleLitany => 0.62f, // Battle Litany delay - DRG.AID.LanceBarrage => 0.62f, // Lance Barrage delay - DRG.AID.FangAndClaw => 0.62f, // Fang and Claw delay - DRG.AID.RaidenThrust => 0.62f, // Raiden Thrust delay - DRG.AID.Geirskogul => 0.67f, // Geirskogul delay - DRG.AID.WheelingThrust => 0.67f, // Wheeling Thrust delay - DRG.AID.HeavensThrust => 0.71f, // Heavens Thrust delay - DRG.AID.DraconianFury => 0.76f, // Draconian Fury delay - DRG.AID.Nastrond => 0.76f, // Nastrond delay - DRG.AID.TrueThrust => 0.76f, // True Thrust delay - DRG.AID.DragonfireDive => 0.8f, // Dragonfire Dive delay - DRG.AID.MirageDive => 0.8f, // Mirage Dive delay - DRG.AID.SonicThrust => 0.8f, // Sonic Thrust delay - DRG.AID.PiercingTalon => 0.85f, // Piercing Talon delay - DRG.AID.Starcross => 0.98f, // Starcross delay - DRG.AID.VorpalThrust => 1.02f, // Vorpal Thrust delay - DRG.AID.RiseOfTheDragon => 1.16f, // Rise of the Dragon delay - DRG.AID.WyrmwindThrust => 1.2f, // Wyrmwind Thrust delay - DRG.AID.DoomSpike => 1.29f, // Doom Spike delay - DRG.AID.Stardiver => 1.29f, // Stardiver delay - DRG.AID.SpiralBlow => 1.38f, // Spiral Blow delay - DRG.AID.Disembowel => 1.65f, // Disembowel delay - DRG.AID.DragonsongDive => 2.23f, // Dragonsong Dive delay - _ => 0 // Default case for unknown abilities + DRG.AID.ChaoticSpring => 0.45f, //Chaotic Spring delay + DRG.AID.HighJump => 0.49f, //High Jump delay + DRG.AID.CoerthanTorment => 0.49f, //Coerthan Torment delay + DRG.AID.BattleLitany => 0.62f, //Battle Litany delay + DRG.AID.LanceBarrage => 0.62f, //Lance Barrage delay + DRG.AID.FangAndClaw => 0.62f, //Fang and Claw delay + DRG.AID.RaidenThrust => 0.62f, //Raiden Thrust delay + DRG.AID.Geirskogul => 0.67f, //Geirskogul delay + DRG.AID.WheelingThrust => 0.67f, //Wheeling Thrust delay + DRG.AID.HeavensThrust => 0.71f, //Heavens Thrust delay + DRG.AID.DraconianFury => 0.76f, //Draconian Fury delay + DRG.AID.Nastrond => 0.76f, //Nastrond delay + DRG.AID.TrueThrust => 0.76f, //True Thrust delay + DRG.AID.DragonfireDive => 0.8f, //Dragonfire Dive delay + DRG.AID.MirageDive => 0.8f, //Mirage Dive delay + DRG.AID.SonicThrust => 0.8f, //Sonic Thrust delay + DRG.AID.PiercingTalon => 0.85f, //Piercing Talon delay + DRG.AID.Starcross => 0.98f, //Starcross delay + DRG.AID.VorpalThrust => 1.02f, //Vorpal Thrust delay + DRG.AID.RiseOfTheDragon => 1.16f, //Rise of the Dragon delay + DRG.AID.WyrmwindThrust => 1.2f, //Wyrmwind Thrust delay + DRG.AID.DoomSpike => 1.29f, //Doom Spike delay + DRG.AID.Stardiver => 1.29f, //Stardiver delay + DRG.AID.SpiralBlow => 1.38f, //Spiral Blow delay + DRG.AID.Disembowel => 1.65f, //Disembowel delay + DRG.AID.DragonsongDive => 2.23f, //Dragonsong Dive delay + _ => 0 //Default case for unknown abilities }; - // Queue a global cooldown (GCD) action + //Queue a global cooldown (GCD) action private void QueueGCD(DRG.AID aid, Actor? target, GCDPriority prio) { - if (prio != GCDPriority.None) // Check if priority is not None + if (prio != GCDPriority.None) //Check if priority is not None { - // Calculate delay based on combat status and countdown + //Calculate delay based on combat status and countdown var delay = !Player.InCombat && World.Client.CountdownRemaining > 0 ? Math.Max(0, World.Client.CountdownRemaining.Value - EffectApplicationDelay(aid)) : 0; - // Push action to execute with appropriate priority and delay + //Push action to execute with appropriate priority and delay Hints.ActionsToExecute.Push(ActionID.MakeSpell(aid), target, ActionQueue.Priority.High + (int)prio, delay: delay); - // Update next GCD and its priority if the new priority is higher + //Update next GCD and its priority if the new priority is higher if (prio > NextGCDPrio) { NextGCD = aid; @@ -679,17 +691,17 @@ private void QueueGCD(DRG.AID aid, Actor? target, GCDPriority prio) } } - // Queue an off-global cooldown (oGCD) action + //Queue an off-global cooldown (oGCD) action private void QueueOGCD(DRG.AID aid, Actor? target, OGCDPriority prio, float basePrio = ActionQueue.Priority.Low) { - if (prio != OGCDPriority.None) // Check if priority is not None + if (prio != OGCDPriority.None) //Check if priority is not None { - // Calculate delay based on combat status and countdown + //Calculate delay based on combat status and countdown var delay = !Player.InCombat && World.Client.CountdownRemaining > 0 ? Math.Max(0, World.Client.CountdownRemaining.Value - EffectApplicationDelay(aid)) : 0; - // Push action to execute with appropriate base priority and delay + //Push action to execute with appropriate base priority and delay Hints.ActionsToExecute.Push(ActionID.MakeSpell(aid), target, basePrio + (int)prio, delay: delay); } @@ -745,7 +757,7 @@ private void QueueOGCD(DRG.AID aid, Actor? target, OGCDPriority prio, float base private DRG.AID UseOnly123ST() => ComboLastMove switch { //Start combo with TrueThrust - DRG.AID.TrueThrust => + DRG.AID.TrueThrust or DRG.AID.RaidenThrust => Unlocked(DRG.AID.LanceBarrage) ? DRG.AID.LanceBarrage //LanceBarrage if Unlocked : Unlocked(DRG.AID.VorpalThrust) ? DRG.AID.VorpalThrust //VorpalThrust otherwise : DRG.AID.TrueThrust, //Else return to TrueThrust @@ -775,8 +787,8 @@ private void QueueOGCD(DRG.AID aid, Actor? target, OGCDPriority prio, float base private DRG.AID UseOnly145ST() => ComboLastMove switch { //Start combo with TrueThrust - DRG.AID.TrueThrust => - Unlocked(DRG.AID.Disembowel) && powerLeft <= GCD * 6 + DRG.AID.TrueThrust or DRG.AID.RaidenThrust => + Unlocked(DRG.AID.Disembowel) ? Unlocked(DRG.AID.SpiralBlow) ? DRG.AID.SpiralBlow : DRG.AID.Disembowel //Disembowel/SpiralBlow if Unlocked : DRG.AID.TrueThrust, //Else return to TrueThrust @@ -832,7 +844,7 @@ private void QueueOGCD(DRG.AID aid, Actor? target, OGCDPriority prio, float base OffensiveStrategy.Automatic => //Use Lance Charge automatically if the player is in combat, the target is valid, the action is ready, and there is power remaining Player.InCombat && target != null && canLC && powerLeft > 0, - OffensiveStrategy.Force => true, //Always use if forced + OffensiveStrategy.Force => canLC, //Always use if forced OffensiveStrategy.Delay => false, //Delay usage if strategy is set to delay _ => false }; @@ -843,18 +855,20 @@ private void QueueOGCD(DRG.AID aid, Actor? target, OGCDPriority prio, float base OffensiveStrategy.Automatic => //Use Battle Litany automatically if the player is in combat, the target is valid, the action is ready, and there is power remaining Player.InCombat && target != null && canBL && powerLeft > 0, - OffensiveStrategy.Force => true, //Always use if forced + OffensiveStrategy.Force => canBL, //Always use if forced OffensiveStrategy.Delay => false, //Delay usage if strategy is set to delay _ => false }; + //Determines when to use Life Surge private bool ShouldUseLifeSurge(SurgeStrategy strategy, Actor? target) => strategy switch { SurgeStrategy.Automatic => Player.InCombat && target != null && canLS && hasLC && + !HasEffect(DRG.SID.LifeSurge) && (CD(DRG.AID.LifeSurge) < 40 || CD(DRG.AID.BattleLitany) > 50) && - (ComboLastMove == DRG.AID.WheelingThrust || ComboLastMove == DRG.AID.FangAndClaw) && Unlocked(DRG.AID.Drakesbane) || - (ComboLastMove == DRG.AID.VorpalThrust || ComboLastMove == DRG.AID.LanceBarrage) && Unlocked(DRG.AID.FullThrust), - SurgeStrategy.Force => true, + (ComboLastMove is DRG.AID.WheelingThrust or DRG.AID.FangAndClaw && Unlocked(DRG.AID.Drakesbane) || + ComboLastMove is DRG.AID.VorpalThrust or DRG.AID.LanceBarrage && Unlocked(DRG.AID.FullThrust)), + SurgeStrategy.Force => canLS, SurgeStrategy.Delay => false, _ => false }; @@ -865,8 +879,8 @@ private void QueueOGCD(DRG.AID aid, Actor? target, OGCDPriority prio, float base JumpStrategy.Automatic => //Use Jump automatically if the player is in combat, the target is valid, and Lance Charge-related conditions are met Player.InCombat && target != null && canJump && (lcLeft > 0 || hasLC || lcCD is < 35 and > 17), - JumpStrategy.ForceEX => true, //Always use in ForceEX strategy - JumpStrategy.ForceEX2 => true, //Always use in ForceEX2 strategy + JumpStrategy.ForceEX => canJump, //Always use in ForceEX strategy + JumpStrategy.ForceEX2 => canJump, //Always use in ForceEX2 strategy JumpStrategy.Delay => false, //Delay usage if strategy is set to delay _ => false }; @@ -877,8 +891,8 @@ private void QueueOGCD(DRG.AID aid, Actor? target, OGCDPriority prio, float base DragonfireStrategy.Automatic => //Use Dragonfire Dive automatically if the player is in combat, the target is valid, and both Lance Charge and Battle Litany are active Player.InCombat && target != null && In3y(target) && canDD && hasLC && hasBL, - DragonfireStrategy.Force => true, //Always use if forced - DragonfireStrategy.ForceEX => true, //Always use in ForceEX strategy + DragonfireStrategy.Force => canDD, //Always use if forced + DragonfireStrategy.ForceEX => canDD, //Always use in ForceEX strategy DragonfireStrategy.Delay => false, //Delay usage if strategy is set to delay _ => false }; @@ -889,8 +903,8 @@ private void QueueOGCD(DRG.AID aid, Actor? target, OGCDPriority prio, float base GeirskogulStrategy.Automatic => //Use Geirskogul automatically if the player is in combat, the action is ready, the target is within 15y, and Lance Charge is active Player.InCombat && In15y(target) && canGeirskogul && hasLC, - GeirskogulStrategy.Force => true, //Always use if forced - GeirskogulStrategy.ForceEX => true, //Always use if forced + GeirskogulStrategy.Force => canGeirskogul, //Always use if forced + GeirskogulStrategy.ForceEX => canGeirskogul, //Always use if forced GeirskogulStrategy.Delay => false, //Delay usage if strategy is set to delay _ => false }; @@ -901,7 +915,7 @@ private void QueueOGCD(DRG.AID aid, Actor? target, OGCDPriority prio, float base OffensiveStrategy.Automatic => //Use Mirage Dive automatically if the player is in combat, the target is valid, and Dive Ready effect is active Player.InCombat && target != null && canMD, - OffensiveStrategy.Force => true, //Always use if forced + OffensiveStrategy.Force => canMD, //Always use if forced OffensiveStrategy.Delay => false, //Delay usage if strategy is set to delay _ => false }; @@ -911,8 +925,8 @@ private void QueueOGCD(DRG.AID aid, Actor? target, OGCDPriority prio, float base { OffensiveStrategy.Automatic => //Use Nastrond automatically if the player is in combat, has Nastrond ready, the target is within 15y, and Lance Charge is active - Player.InCombat && In15y(target) && canNastrond && hasLC, - OffensiveStrategy.Force => true, //Always use if forced + Player.InCombat && In15y(target) && canNastrond, + OffensiveStrategy.Force => canNastrond, //Always use if forced OffensiveStrategy.Delay => false, //Delay usage if strategy is set to delay _ => false }; @@ -923,8 +937,8 @@ private void QueueOGCD(DRG.AID aid, Actor? target, OGCDPriority prio, float base StardiverStrategy.Automatic => //Use Stardiver automatically if the player is in combat, the target is valid, the action is ready, and Life of the Dragon (LOTD) is active Player.InCombat && target != null && In3y(target) && canSD && hasLOTD, - StardiverStrategy.Force => true, //Always use if forced - StardiverStrategy.ForceEX => true, //Always use if forced + StardiverStrategy.Force => canSD, //Always use if forced + StardiverStrategy.ForceEX => canSD, //Always use if forced StardiverStrategy.Delay => false, //Delay usage if strategy is set to delay _ => false }; @@ -934,8 +948,8 @@ private void QueueOGCD(DRG.AID aid, Actor? target, OGCDPriority prio, float base { OffensiveStrategy.Automatic => //Use Wyrmwind Thrust automatically if the player is in combat, the target is within 15y, and focus count is exactly 2 - Player.InCombat && target != null && In15y(target) && canWT && focusCount is 2, - OffensiveStrategy.Force => true, //Always use if forced + Player.InCombat && target != null && In15y(target) && canWT && focusCount is 2 && lcCD > GCDLength * 3, + OffensiveStrategy.Force => canWT, //Always use if forced OffensiveStrategy.Delay => false, //Delay usage if strategy is set to delay _ => false }; @@ -946,7 +960,7 @@ private void QueueOGCD(DRG.AID aid, Actor? target, OGCDPriority prio, float base OffensiveStrategy.Automatic => //Use Rise of the Dragon automatically if the player is in combat, the target is valid, and Dragon's Flight effect is active Player.InCombat && target != null && canROTD, - OffensiveStrategy.Force => true, //Always use if forced + OffensiveStrategy.Force => canROTD, //Always use if forced OffensiveStrategy.Delay => false, //Delay usage if strategy is set to delay _ => false }; @@ -957,7 +971,7 @@ private void QueueOGCD(DRG.AID aid, Actor? target, OGCDPriority prio, float base OffensiveStrategy.Automatic => //Use Starcross automatically if the player is in combat, the target is valid, and Starcross Ready effect is active Player.InCombat && target != null && canSC, - OffensiveStrategy.Force => true, //Always use if forced + OffensiveStrategy.Force => canSC, //Always use if forced OffensiveStrategy.Delay => false, //Delay usage if strategy is set to delay _ => false }; @@ -965,17 +979,11 @@ private void QueueOGCD(DRG.AID aid, Actor? target, OGCDPriority prio, float base //Determines when to use Piercing Talon private bool ShouldUsePiercingTalon(Actor? target, PiercingTalonStrategy strategy) => strategy switch { - PiercingTalonStrategy.Automatic => + PiercingTalonStrategy.Forbid => false, //Never use if forbidden + PiercingTalonStrategy.Allow => //Use Piercing Talon if the target is not within 3y range and already in combat Player.InCombat && target != null && !In3y(target), - PiercingTalonStrategy.Opener => - //Use Piercing Talon as the first GCD - IsFirstGCD(), PiercingTalonStrategy.Force => true, //Always use if forced - PiercingTalonStrategy.Ranged => - //Use Piercing Talon if the target is not within 3y range - !In3y(target), - PiercingTalonStrategy.Forbid => false, //Never use if forbidden _ => false }; @@ -989,18 +997,44 @@ private void QueueOGCD(DRG.AID aid, Actor? target, OGCDPriority prio, float base _ => false }; - /* + //Determines when to use True North + //TODO: reconsider this method, it's jank as fuck but it works private bool ShouldUseTrueNorth(TrueNorthStrategy strategy, Actor? target) => strategy switch { - TrueNorthStrategy.Automatic => target != null && Player.InCombat, - TrueNorthStrategy.ASAP => , - TrueNorthStrategy.Flank => , - TrueNorthStrategy.Rear => , - TrueNorthStrategy.Force => true, + TrueNorthStrategy.Automatic => + target != null && Player.InCombat && + !HasEffect(ClassShared.SID.TrueNorth) && + GCD < 1.25f && + ((!IsOnRear(target) && //Side + (ComboLastMove is DRG.AID.Disembowel or DRG.AID.SpiralBlow + or DRG.AID.ChaosThrust or DRG.AID.ChaoticSpring)) || + (!IsOnFlank(target) && //Back + (ComboLastMove is DRG.AID.HeavensThrust or DRG.AID.FullThrust))), + TrueNorthStrategy.ASAP => + target != null && Player.InCombat && + !HasEffect(ClassShared.SID.TrueNorth) && + ((!IsOnRear(target) && //Side + (ComboLastMove is DRG.AID.Disembowel or DRG.AID.SpiralBlow + or DRG.AID.ChaosThrust or DRG.AID.ChaoticSpring)) || + (!IsOnFlank(target) && //Back + (ComboLastMove is DRG.AID.HeavensThrust or DRG.AID.FullThrust))), + TrueNorthStrategy.Flank => + target != null && Player.InCombat && + !HasEffect(ClassShared.SID.TrueNorth) && + GCD < 1.25f && + (!IsOnFlank(target) && //Back + (ComboLastMove is DRG.AID.HeavensThrust or DRG.AID.FullThrust)), + TrueNorthStrategy.Rear => + target != null && Player.InCombat && + !HasEffect(ClassShared.SID.TrueNorth) && + GCD < 1.25f && + (!IsOnRear(target) && //Side + (ComboLastMove is DRG.AID.Disembowel or DRG.AID.SpiralBlow + or DRG.AID.ChaosThrust or DRG.AID.ChaoticSpring)), + TrueNorthStrategy.Force => !HasEffect(DRG.SID.TrueNorth), TrueNorthStrategy.Delay => false, _ => false }; - */ #endregion } diff --git a/BossMod/Autorotation/akechi/AkechiGNB.cs b/BossMod/Autorotation/akechi/AkechiGNB.cs index 9f90483eb..553d53281 100644 --- a/BossMod/Autorotation/akechi/AkechiGNB.cs +++ b/BossMod/Autorotation/akechi/AkechiGNB.cs @@ -8,6 +8,7 @@ namespace BossMod.Autorotation.akechi; public sealed class AkechiGNB(RotationModuleManager manager, Actor player) : RotationModule(manager, player) { + #region Enums: Abilities / Strategies //Actions tracked for Cooldown Planner execution public enum Track { @@ -18,11 +19,11 @@ public enum Track GnashingFang, //Gnashing Fang action tracking NoMercy, //No Mercy ability tracking SonicBreak, //Sonic Break ability tracking + Bloodfest, //Bloodfest ability tracking DoubleDown, //Double Down ability tracking BurstStrike, //Burst Strike ability tracking FatedCircle, //Fated Circle ability tracking Zone, //Blasting Zone or Danger Zone tracking - Bloodfest, //Bloodfest ability tracking BowShock, //Bow Shock ability tracking } @@ -98,6 +99,15 @@ public enum SonicBreakStrategy Delay //Delay the use of Sonic Break 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 + Force0, //Force the use of Bloodfest only when ammo is empty + Delay //Delay the use of Sonic Break for strategic reasons + } + //Defines different offensive strategies that dictate how abilities and resources are used during combat public enum OffensiveStrategy { @@ -105,13 +115,14 @@ public enum OffensiveStrategy Force, //Force the use of offensive abilities regardless of conditions Delay //Delay the use of offensive abilities for strategic reasons } + #endregion public static RotationModuleDefinition Definition() { //Module title & signature - var res = new RotationModuleDefinition("GNB (Akechi)", "Standard Rotation Module", "Standard rotation (Akechi)", "Akechi", RotationModuleQuality.Ok, BitMask.Build((int)Class.GNB), 100); + var res = new RotationModuleDefinition("GNB (Akechi)", "Standard Rotation Module", "Standard rotation (Akechi)", "Akechi", RotationModuleQuality.Good, BitMask.Build((int)Class.GNB), 100); - //Custom strategies + #region Custom strategies //Targeting strategy res.Define(Track.AoE).As("Combo Option", "AoE", uiPriority: 200) .AddOption(AOEStrategy.SingleTarget, "ST", "Use ST rotation (with overcap protection)") @@ -175,7 +186,16 @@ public static RotationModuleDefinition Definition() .AddOption(SonicBreakStrategy.Delay, "Delay", "Delay use of Sonic Break", 0, 0, ActionTargets.None, 54) .AddAssociatedActions(GNB.AID.SonicBreak); - //Offensive Strategies + //Bloodfest strategy + res.Define(Track.Bloodfest).As("Bloodfest", "Fest", uiPriority: 170) + .AddOption(BloodfestStrategy.Automatic, "Auto", "Normal use of Bloodfest") + .AddOption(BloodfestStrategy.Force, "Force", "Force use of Bloodfest, regardless of ammo count", 120, 0, ActionTargets.Hostile, 80) + .AddOption(BloodfestStrategy.Force0, "Force (0 cart)", "Force use of Bloodfest as soon as you have 0 carts", 120, 0, ActionTargets.Hostile, 80) + .AddOption(BloodfestStrategy.Delay, "Delay", "Delay use of Bloodfest", 0, 0, ActionTargets.None, 80) + .AddAssociatedActions(GNB.AID.Bloodfest); + #endregion + + #region Offensive Strategies //DoubleDown strategy res.Define(Track.DoubleDown).As("Double Down", "D.Down", uiPriority: 160) .AddOption(OffensiveStrategy.Automatic, "Auto", "Normal use of Double Down") @@ -204,69 +224,59 @@ public static RotationModuleDefinition Definition() .AddOption(OffensiveStrategy.Delay, "Delay", "Delay", 0, 0, ActionTargets.None, 18) .AddAssociatedActions(GNB.AID.BlastingZone, GNB.AID.DangerZone); - //Bloodfest strategy - res.Define(Track.Bloodfest).As("Bloodfest", "Fest", uiPriority: 170) - .AddOption(OffensiveStrategy.Automatic, "Auto", "Normal use of Bloodfest") - .AddOption(OffensiveStrategy.Force, "Force", "Force use of Bloodfest", 120, 0, ActionTargets.Hostile, 80) - .AddOption(OffensiveStrategy.Delay, "Delay", "Delay use of Bloodfest", 0, 0, ActionTargets.None, 80) - .AddAssociatedActions(GNB.AID.Bloodfest); - //BowShock strategy res.Define(Track.BowShock).As("Bow Shock", "B.Shock", uiPriority: 150) .AddOption(OffensiveStrategy.Automatic, "Auto", "Normal use of Bow Shock") .AddOption(OffensiveStrategy.Force, "Force", "Force use of Bow Shock", 60, 15, ActionTargets.Self, 62) .AddOption(OffensiveStrategy.Delay, "Delay", "Delay use of Bow Shock", 0, 0, ActionTargets.None, 62) .AddAssociatedActions(GNB.AID.BowShock); + #endregion return res; } - public enum GCDPriority //Priority for GCDs used + #region Priorities + //Priority for GCDs used + public enum GCDPriority { None = 0, Combo123 = 350, - NormalGCD = 500, - NormalBS = 600, - NormalSB = 670, - GF23 = 660, - NormalDD = 680, - GF1 = 690, + FatedCircle = 400, + BurstStrike = 500, + Reign = 525, + comboNeed = 550, + GF23 = 575, + SonicBreak = 600, + DoubleDown = 675, + GF1 = 700, ForcedGCD = 900, - StopAll = 980, } - - public enum OGCDPriority //Priority for oGCDs used + //Priority for oGCDs used + public enum OGCDPriority { None = 0, - Continuation = 510, - Zone = 540, - BowShock = 550, - Continuation1 = 580, - Bloodfest = 600, - NoMercy = 850, + Continuation = 500, + Zone = 550, + BowShock = 600, + Continuation1 = 650, + Bloodfest = 700, + ContinuationNeed = 800, + NoMercy = 875, Potion = 900, - ContinuationNeed = 950, ForcedOGCD = 900, - StopAll = 980, } + #endregion - public byte Ammo; //Range: 0-2, 0-3 - current ammo count - public byte GunComboStep; //0 = Gnashing Fang & Reign of Beasts, 1 = Savage Claw, 2 = Wicked Talon, etc. + #region Placeholders for Variables + //Gauge + public byte Ammo; //Range: 0-2 or 0-3 max; this counts current ammo count + public byte GunComboStep; //0 = Gnashing Fang & Reign of Beasts, 1 = Savage Claw, 2 = Wicked Talon, 4 = NobleBlood, 5 = LionHeart. public int MaxCartridges; //Maximum number of cartridges based on player level - - private float GCDLength; //Current GCD length, adjusted by skill speed/haste (2.5s baseline) + //Cooldown Related 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 float PotionLeft; //Time left on potion buff (typically 30s) - private float RaidBuffsLeft; //Time left on raid-wide buffs (typically 20s-22s) - private float RaidBuffsIn; //Time until raid-wide buffs are applied again (typically 20s-22s) - - public float BurstWindowLeft; //Time left in current burst window (typically 20s-22s) - public float BurstWindowIn; //Time until next burst window (typically 20s-22s) - 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 @@ -275,64 +285,55 @@ public static RotationModuleDefinition Definition() private bool hasRip; //Checks self for Ready To Rip buff private bool hasTear; //Checks self for Ready To Tear buff private bool hasGouge; //Checks self for Ready To Gouge buff - + private bool canBS; + private bool canGF; + private bool canFC; + private bool canDD; + private bool canBF; + private bool canZone; + private bool canBreak; + private bool canBow; + private bool canContinue; + private bool canReign; + + //Misc + public float PotionLeft; //Time left on potion buff (30s base) + public float RaidBuffsLeft; //Time left on raid-wide buffs (typically 20s-22s) + public float RaidBuffsIn; //Time until raid-wide buffs are applied again (typically 20s-22s) + public float GCDLength; //Current GCD length, adjusted by skill speed/haste (2.5s baseline) + public float BurstWindowLeft; //Time left in current burst window (typically 20s-22s) + public float BurstWindowIn; //Time until next burst window (typically 20s-22s) public GNB.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 - - //Check if the desired ability is unlocked - private bool Unlocked(GNB.AID aid) => ActionUnlocked(ActionID.MakeSpell(aid)); - - //Check if the desired trait is unlocked - private bool Unlocked(GNB.TraitID tid) => TraitUnlocked((uint)tid); - - //Get remaining cooldown time for the specified action - private float CD(GNB.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 GNB.AID ComboLastMove => (GNB.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(GNB.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; + #endregion + + #region Module Helpers + private bool Unlocked(GNB.AID aid) => ActionUnlocked(ActionID.MakeSpell(aid)); //Check if the desired ability is unlocked + private bool Unlocked(GNB.TraitID tid) => TraitUnlocked((uint)tid); //Check if the desired trait is unlocked + private float CD(GNB.AID aid) => World.Client.Cooldowns[ActionDefinitions.Instance.Spell(aid)!.MainCooldownGroup].Remaining; //Get remaining cooldown time for the specified action + private bool CanFitGCD(float deadline, int extraGCDs = 0) => GCD + GCDLength * extraGCDs < deadline; //Check if we can fit an additional GCD within the provided deadline + private GNB.AID ComboLastMove => (GNB.AID)World.Client.ComboState.Action; //Get the last action used in the combo sequence + private bool In3y(Actor? target) => Player.DistanceToHitbox(target) <= 3; //Check if the target is within melee range (3 yalms) + private bool In5y(Actor? target) => Player.DistanceToHitbox(target) <= 4.75; //Check if the target is within 5 yalms + private bool ActionReady(GNB.AID aid) => World.Client.Cooldowns[ActionDefinitions.Instance.Spell(aid)!.MainCooldownGroup].Remaining < 0.6f; //Check if the desired action is ready (cooldown less than 0.6 seconds) + private bool IsFirstGCD() => !Player.InCombat || (World.CurrentTime - Manager.CombatStart).TotalSeconds < 0.1f; //Check if this is the first GCD in combat + private int NumTargetsHitByAoE() => Hints.NumPriorityTargetsInAOECircle(Player.Position, 5); //Returns the number of targets hit by AoE within a 5-yalm radius around the player + private bool IsPotionBeforeRaidbuffs() => RaidBuffsLeft == 0 && PotionLeft > RaidBuffsIn + 17.5f; //Checks if the potion should be used before raid buffs expire + public bool HasEffect(SID sid) where SID : Enum => Player.FindStatus((uint)(object)sid, Player.InstanceID) != null; //Checks if Status effect is on self + #endregion public override void Execute(StrategyValues strategy, Actor? primaryTarget, float estimatedAnimLockDelay, bool isMoving) //Executes our actions { - //Gauge values + #region Variables + //Gauge var gauge = World.Client.GetGauge(); //Retrieve Gunbreaker gauge Ammo = gauge.Ammo; //Current cartridges GunComboStep = gauge.AmmoComboStep; //Combo step for Gnashing Fang or Reign of Beasts MaxCartridges = Unlocked(GNB.TraitID.CartridgeChargeII) ? 3 : 2; //Max cartridges based on level - - //Cooldowns and buff timers + //Cooldown Related bfCD = CD(GNB.AID.Bloodfest); //Bloodfest cooldown (120s) nmCD = CD(GNB.AID.NoMercy); //No Mercy cooldown (60s) - nmLeft = SelfStatusLeft(GNB.SID.NoMercy); //Remaining time for No Mercy buff (20s) - GCDLength = ActionSpeed.GCDRounded(World.Client.PlayerStats.SkillSpeed, World.Client.PlayerStats.Haste, Player.Level); //GCD based on skill speed and haste - - //Buff durations - PotionLeft = PotionStatusLeft(); //Remaining time for potion buff (30s) - - //Buff and cooldown checks + nmLeft = SelfStatusLeft(GNB.SID.NoMercy, 20); //Remaining time for No Mercy buff (20s) hasBreak = HasEffect(GNB.SID.ReadyToBreak); //Checks for Ready To Break buff hasReign = HasEffect(GNB.SID.ReadyToReign); //Checks for Ready To Reign buff hasNM = nmCD is >= 40 and <= 60; //Checks if No Mercy is active @@ -341,26 +342,31 @@ public override void Execute(StrategyValues strategy, Actor? primaryTarget, floa hasRip = HasEffect(GNB.SID.ReadyToRip); //Checks for Ready To Rip buff hasTear = HasEffect(GNB.SID.ReadyToTear); //Checks for Ready To Tear buff hasGouge = HasEffect(GNB.SID.ReadyToGouge); //Checks for Ready To Gouge buff - - //Raid buff timings + //Misc (RaidBuffsLeft, RaidBuffsIn) = EstimateRaidBuffTimings(primaryTarget); - - //Next GCD action - NextGCD = GNB.AID.None; - NextGCDPrio = GCDPriority.None; - - //Define ST/AoE strategy and determine number of targets - var AOEStrategy = strategy.Option(Track.AoE).As(); - var AoETargets = AOEStrategy switch - { - AOEStrategy.SingleTarget => NumTargetsHitByAoE() > 0 ? 1 : 0, - AOEStrategy.FocusSingleTarget => NumTargetsHitByAoE() > 0 ? 1 : 0, - AOEStrategy.ForceAoE => NumTargetsHitByAoE() > 0 ? 100 : 0, - AOEStrategy.FocusAoE => NumTargetsHitByAoE() > 0 ? 100 : 0, - AOEStrategy.GenerateDowntime => NumTargetsHitByAoE() > 0 ? 100 : 0, - _ => NumTargetsHitByAoE() - }; - + PotionLeft = PotionStatusLeft(); //Remaining time for potion buff (30s) + GCDLength = ActionSpeed.GCDRounded(World.Client.PlayerStats.SkillSpeed, World.Client.PlayerStats.Haste, Player.Level); //GCD based on skill speed and haste + NextGCD = GNB.AID.None; //Next global cooldown action to be used + NextGCDPrio = GCDPriority.None; //Priority of the next GCD, used for decision making on cooldowns + + #region Minimal Requirements + //Ammo-relative + canBS = Unlocked(GNB.AID.BurstStrike) && Ammo > 0; //BurstStrike conditions; -1 Ammo ST + canGF = Unlocked(GNB.AID.GnashingFang) && ActionReady(GNB.AID.GnashingFang) && Ammo >= 1; //GnashingFang conditions; -1 Ammo ST + canFC = Unlocked(GNB.AID.FatedCircle) && Ammo > 0; //FatedCircle conditions; -1 Ammo AOE + canDD = Unlocked(GNB.AID.DoubleDown) && ActionReady(GNB.AID.DoubleDown) && Ammo >= 2; //DoubleDown conditions; -2 Ammo AOE + canBF = Unlocked(GNB.AID.Bloodfest) && ActionReady(GNB.AID.Bloodfest); //Bloodfest conditions; +all Ammo (must have target) + //Cooldown-relative + canZone = Unlocked(GNB.AID.DangerZone) && ActionReady(GNB.AID.DangerZone); //Zone conditions + canBreak = hasBreak && Unlocked(GNB.AID.SonicBreak); //SonicBreak conditions + canBow = Unlocked(GNB.AID.BowShock) && ActionReady(GNB.AID.BowShock); //BowShock conditions + canContinue = Unlocked(GNB.AID.Continuation); //Continuation conditions + canReign = Unlocked(GNB.AID.ReignOfBeasts); //ReignOfBeasts conditions + #endregion + + #endregion + + #region Burst //Burst (raid buff) windows typically last 20s every 120s var burst = strategy.Option(Track.Burst); var burstStrategy = burst.As(); @@ -374,24 +380,30 @@ public override void Execute(StrategyValues strategy, Actor? primaryTarget, floa BurstStrategy.UnderPotion => (PotionCD, PotionLeft), _ => (0, 0) }; + #endregion - //CDs minimal conditions - var canSonic = hasBreak && Unlocked(GNB.AID.SonicBreak); //SonicBreak conditions - var canDD = Ammo >= 2 && Unlocked(GNB.AID.DoubleDown); //DoubleDown conditions - var canBS = Unlocked(GNB.AID.BurstStrike); //BurstStrike conditions - var canGF = Ammo >= 1 && Unlocked(GNB.AID.GnashingFang); //GnashingFang conditions - var canFC = Unlocked(GNB.AID.FatedCircle); //FatedCircle conditions - - //Determine and queue combo action - var (comboAction, comboPrio) = ComboActionPriority(AOEStrategy, AoETargets, burstStrategy, burst.Value.ExpireIn); - QueueGCD(comboAction, comboAction is GNB.AID.DemonSlice or GNB.AID.DemonSlaughter ? Player : primaryTarget, comboPrio); + #region Targeting + //Define ST/AoE strategy and determine number of targets + var AOEStrategy = strategy.Option(Track.AoE).As(); + var AoETargets = AOEStrategy switch + { + AOEStrategy.SingleTarget => NumTargetsHitByAoE() > 0 ? 1 : 0, + AOEStrategy.FocusSingleTarget => NumTargetsHitByAoE() > 0 ? 1 : 0, + AOEStrategy.ForceAoE => NumTargetsHitByAoE() > 0 ? 100 : 0, + AOEStrategy.FocusAoE => NumTargetsHitByAoE() > 0 ? 100 : 0, + AOEStrategy.GenerateDowntime => NumTargetsHitByAoE() > 0 ? 100 : 0, + _ => NumTargetsHitByAoE() + }; + #endregion - //Focused actions for AoE strategies + #region Rotation Strategies + //Force Options if (AOEStrategy == AOEStrategy.FocusSingleTarget) //ST (without overcap protection) QueueGCD(NextForceSingleTarget(), primaryTarget, GCDPriority.ForcedGCD); if (AOEStrategy == AOEStrategy.FocusAoE) //AoE (without overcap protection) QueueGCD(NextForceAoE(), primaryTarget, GCDPriority.ForcedGCD); + #region Logic for Cart Generation before Downtime //Estimate time to next downtime var downtimeIn = Manager.Planner?.EstimateTimeToNextDowntime().Item2 ?? float.MaxValue; var comboStepsRemaining = ComboLastMove switch @@ -401,7 +413,6 @@ public override void Execute(StrategyValues strategy, Actor? primaryTarget, floa _ => 0 }; - //Generate downtime logic if (AOEStrategy == AOEStrategy.GenerateDowntime) { if (comboStepsRemaining == 0) //Not in any combo @@ -446,7 +457,16 @@ public override void Execute(StrategyValues strategy, Actor? primaryTarget, floa if (Ammo == MaxCartridges) QueueGCD(NextForceSingleTarget(), primaryTarget, GCDPriority.ForcedGCD); } + #endregion + + #endregion + #region Rotation Execution + //Determine and queue combo actions + var (comboAction, comboPrio) = ComboActionPriority(AOEStrategy, AoETargets, burstStrategy, burst.Value.ExpireIn); + QueueGCD(comboAction, comboAction is GNB.AID.DemonSlice or GNB.AID.DemonSlaughter ? Player : primaryTarget, Ammo == 0 ? GCDPriority.comboNeed : comboPrio); + + #region OGCDs //No Mercy execution var nmStrat = strategy.Option(Track.NoMercy).As(); if (!hold && ShouldUseNoMercy(nmStrat, primaryTarget)) @@ -464,12 +484,12 @@ public override void Execute(StrategyValues strategy, Actor? primaryTarget, floa QueueOGCD(GNB.AID.BowShock, Player, bowStrat == OffensiveStrategy.Force ? OGCDPriority.ForcedOGCD : OGCDPriority.BowShock); //Bloodfest execution - var bfStrat = strategy.Option(Track.Bloodfest).As(); + var bfStrat = strategy.Option(Track.Bloodfest).As(); if (!hold && ShouldUseBloodfest(bfStrat, primaryTarget)) - QueueOGCD(GNB.AID.Bloodfest, primaryTarget, bfStrat == OffensiveStrategy.Force ? OGCDPriority.ForcedOGCD : OGCDPriority.Bloodfest); + QueueOGCD(GNB.AID.Bloodfest, primaryTarget, bfStrat is BloodfestStrategy.Force or BloodfestStrategy.Force0 ? OGCDPriority.ForcedOGCD : OGCDPriority.Bloodfest); //Continuation execution - if (Unlocked(GNB.AID.Continuation)) + if (canContinue) { if (hasRip) QueueOGCD(GNB.AID.JugularRip, primaryTarget, OGCDPriority.ContinuationNeed); @@ -477,21 +497,23 @@ public override void Execute(StrategyValues strategy, Actor? primaryTarget, floa QueueOGCD(GNB.AID.AbdomenTear, primaryTarget, OGCDPriority.ContinuationNeed); if (hasGouge) QueueOGCD(GNB.AID.EyeGouge, primaryTarget, OGCDPriority.ContinuationNeed); - if (hasBlast || ComboLastMove is GNB.AID.BurstStrike) + if (hasBlast) QueueOGCD(GNB.AID.Hypervelocity, primaryTarget, OGCDPriority.ContinuationNeed); - if (hasRaze || ComboLastMove is GNB.AID.FatedCircle) + if (hasRaze) QueueOGCD(GNB.AID.FatedBrand, primaryTarget, OGCDPriority.ContinuationNeed); } + #endregion + #region GCDs //Gnashing Fang execution var gfStrat = strategy.Option(Track.GnashingFang).As(); - if (!hold && canGF && ShouldUseGnashingFang(gfStrat, primaryTarget)) + if (!hold && ShouldUseGnashingFang(gfStrat, primaryTarget)) QueueGCD(GNB.AID.GnashingFang, primaryTarget, gfStrat == GnashingStrategy.ForceGnash ? GCDPriority.ForcedGCD : GCDPriority.GF1); //Double Down execution var ddStrat = strategy.Option(Track.DoubleDown).As(); - if (canDD && ShouldUseDoubleDown(ddStrat, primaryTarget)) - QueueGCD(GNB.AID.DoubleDown, primaryTarget, ddStrat == OffensiveStrategy.Force ? GCDPriority.ForcedGCD : GCDPriority.NormalDD); + if (ShouldUseDoubleDown(ddStrat, primaryTarget)) + QueueGCD(GNB.AID.DoubleDown, primaryTarget, ((ddStrat == OffensiveStrategy.Force) || Ammo == 2) ? GCDPriority.ForcedGCD : GCDPriority.DoubleDown); //Gnashing Fang Combo execution if (GunComboStep == 1) @@ -499,46 +521,48 @@ public override void Execute(StrategyValues strategy, Actor? primaryTarget, floa if (GunComboStep == 2) QueueGCD(GNB.AID.WickedTalon, primaryTarget, gfStrat == GnashingStrategy.ForceTalon ? GCDPriority.ForcedGCD : GCDPriority.GF23); - //Reign of Beasts execution - if (hasReign && GunComboStep == 0 && !ActionReady(GNB.AID.DoubleDown)) - QueueGCD(GNB.AID.ReignOfBeasts, primaryTarget, GCDPriority.NormalGCD); - - //Reign Combo execution + //Reign full combo execution + if (canReign && hasNM && GunComboStep == 0) + QueueGCD(GNB.AID.ReignOfBeasts, primaryTarget, GCDPriority.Reign); if (GunComboStep == 3) - QueueGCD(GNB.AID.NobleBlood, primaryTarget, GCDPriority.NormalGCD); + QueueGCD(GNB.AID.NobleBlood, primaryTarget, GCDPriority.Reign); if (GunComboStep == 4) - QueueGCD(GNB.AID.LionHeart, primaryTarget, GCDPriority.NormalGCD); + QueueGCD(GNB.AID.LionHeart, primaryTarget, GCDPriority.Reign); //Sonic Break execution var sbStrat = strategy.Option(Track.SonicBreak).As(); - if (canSonic && hasNM && ShouldUseSonicBreak(sbStrat, primaryTarget)) - QueueGCD(GNB.AID.SonicBreak, primaryTarget, sbStrat is SonicBreakStrategy.Force or SonicBreakStrategy.EarlySB ? GCDPriority.ForcedGCD : GCDPriority.NormalSB); + if (ShouldUseSonicBreak(sbStrat, primaryTarget)) + QueueGCD(GNB.AID.SonicBreak, primaryTarget, sbStrat is SonicBreakStrategy.Force or SonicBreakStrategy.EarlySB ? GCDPriority.ForcedGCD : GCDPriority.SonicBreak); //Burst Strike execution var strikeStrat = strategy.Option(Track.BurstStrike).As(); - if (Ammo >= 1 && Unlocked(GNB.AID.BurstStrike) && Unlocked(GNB.AID.Bloodfest) && ShouldUseBurstStrike(strikeStrat, primaryTarget)) - QueueGCD(GNB.AID.BurstStrike, primaryTarget, strikeStrat == OffensiveStrategy.Force ? GCDPriority.ForcedGCD : GCDPriority.NormalBS); + if (Unlocked(GNB.AID.BurstStrike) && Unlocked(GNB.AID.Bloodfest) && ShouldUseBurstStrike(strikeStrat, primaryTarget)) + QueueGCD(GNB.AID.BurstStrike, primaryTarget, strikeStrat == OffensiveStrategy.Force ? GCDPriority.ForcedGCD : GCDPriority.BurstStrike); //Fated Circle execution var fcStrat = strategy.Option(Track.FatedCircle).As(); - if (canFC && ShouldUseFatedCircle(fcStrat, primaryTarget)) - QueueGCD(GNB.AID.FatedCircle, primaryTarget, fcStrat == OffensiveStrategy.Force ? GCDPriority.ForcedGCD : GCDPriority.NormalGCD); + if (ShouldUseFatedCircle(fcStrat, primaryTarget)) + QueueGCD(GNB.AID.FatedCircle, primaryTarget, fcStrat == OffensiveStrategy.Force ? GCDPriority.ForcedGCD : GCDPriority.FatedCircle); if (!canFC && canBS) { if (Ammo > 0 && ShouldUseBurstStrike(strikeStrat, primaryTarget)) - QueueGCD(UseCorrectBS(), primaryTarget, GCDPriority.NormalBS); + QueueGCD(GNB.AID.BurstStrike, primaryTarget, GCDPriority.BurstStrike); } //Lightning Shot execution var lsStrat = strategy.Option(Track.LightningShot).As(); if (ShouldUseLightningShot(primaryTarget, strategy.Option(Track.LightningShot).As())) - QueueGCD(GNB.AID.LightningShot, primaryTarget, lsStrat == LightningShotStrategy.Force ? GCDPriority.ForcedGCD : GCDPriority.NormalGCD); + QueueGCD(GNB.AID.LightningShot, primaryTarget, lsStrat is LightningShotStrategy.Force or LightningShotStrategy.Ranged ? GCDPriority.ForcedGCD : GCDPriority.Combo123); + #endregion + + #endregion //Potion execution if (ShouldUsePotion(strategy.Option(Track.Potion).As())) Hints.ActionsToExecute.Push(ActionDefinitions.IDPotionStr, Player, ActionQueue.Priority.VeryHigh + (int)OGCDPriority.Potion, 0, GCD - 0.9f); } + #region Core Execution Helpers //QueueGCD execution private void QueueGCD(GNB.AID aid, Actor? target, GCDPriority prio) { @@ -562,57 +586,8 @@ private void QueueOGCD(GNB.AID aid, Actor? target, OGCDPriority prio, float base } } - private GNB.AID UseCorrectBS() - { - //If under No Mercy and Fated Circle is not unlocked, use Burst Strike even in single-target situations - if (Ammo == MaxCartridges && ComboLastMove is GNB.AID.DemonSlice) - return Unlocked(GNB.AID.FatedCircle) ? GNB.AID.FatedCircle : GNB.AID.BurstStrike; - - //Optimal AoE usage for specific target counts - var hasStrike = Unlocked(GNB.AID.BurstStrike); - var hasCircle = Unlocked(GNB.AID.FatedCircle); - - //If Fated Circle is unlocked and there are 2+ targets, use Fated Circle for AoE - if (hasCircle && NumTargetsHitByAoE() >= 2) - return GNB.AID.FatedCircle; - - //If Fated Circle is not unlocked but Burst Strike is available, use Burst Strike on 2+ targets to prevent overcapping - if (!hasCircle && hasStrike && NumTargetsHitByAoE() >= 2) - return GNB.AID.BurstStrike; - - //If none of the conditions match, return Burst Strike for default - return GNB.AID.BurstStrike; - } - - private GNB.AID NextComboSingleTarget() => ComboLastMove switch //Determines the next single-target action based on the last action used - { - GNB.AID.BrutalShell => Ammo == MaxCartridges ? GNB.AID.BurstStrike : GNB.AID.SolidBarrel, - GNB.AID.KeenEdge => GNB.AID.BrutalShell, - _ => GNB.AID.KeenEdge, - }; - - private GNB.AID NextComboAoE() => ComboLastMove switch //Determines the next AoE action based on the last action used - { - GNB.AID.DemonSlice => Ammo == MaxCartridges - ? Unlocked(GNB.AID.FatedCircle) ? GNB.AID.FatedCircle : GNB.AID.BurstStrike - : GNB.AID.DemonSlaughter, - _ => GNB.AID.DemonSlice, - }; - - private GNB.AID NextForceSingleTarget() => ComboLastMove switch - { - GNB.AID.BrutalShell => GNB.AID.SolidBarrel, - GNB.AID.KeenEdge => GNB.AID.BrutalShell, - _ => GNB.AID.KeenEdge, - }; - - private GNB.AID NextForceAoE() => ComboLastMove switch - { - GNB.AID.DemonSlice => GNB.AID.DemonSlaughter, - _ => GNB.AID.DemonSlice, - }; - - private int AmmoGainedFromAction(GNB.AID action) => action switch //Returns the amount of ammo gained from specific actions + //Returns the amount of ammo gained from specific actions + private int AmmoGainedFromAction(GNB.AID action) => action switch { GNB.AID.SolidBarrel => 1, GNB.AID.DemonSlaughter => 1, @@ -663,9 +638,43 @@ private GNB.AID UseCorrectBS() return (nextAction, GCDPriority.Combo123); //Return normal combo action priority based on ammo risks - return (nextAction, riskingAmmo ? GCDPriority.NormalGCD : GCDPriority.Combo123); + return (nextAction, riskingAmmo ? GCDPriority.BurstStrike : GCDPriority.Combo123); } + #endregion + + #region Single-Target Helpers + private GNB.AID NextComboSingleTarget() => ComboLastMove switch //Determines the next single-target action based on the last action used + { + GNB.AID.BrutalShell => Ammo == MaxCartridges ? GNB.AID.BurstStrike : GNB.AID.SolidBarrel, + GNB.AID.KeenEdge => GNB.AID.BrutalShell, + _ => GNB.AID.KeenEdge, + }; + private GNB.AID NextForceSingleTarget() => ComboLastMove switch + { + GNB.AID.BrutalShell => GNB.AID.SolidBarrel, + GNB.AID.KeenEdge => GNB.AID.BrutalShell, + _ => GNB.AID.KeenEdge, + }; + #endregion + + #region AoE Helpers + private GNB.AID NextComboAoE() => ComboLastMove switch //Determines the next AoE action based on the last action used + { + GNB.AID.DemonSlice => Ammo == MaxCartridges + ? Unlocked(GNB.AID.FatedCircle) ? GNB.AID.FatedCircle : GNB.AID.BurstStrike + : GNB.AID.DemonSlaughter, + _ => GNB.AID.DemonSlice, + }; + private GNB.AID NextForceAoE() => ComboLastMove switch + { + GNB.AID.DemonSlice => GNB.AID.DemonSlaughter, + _ => GNB.AID.DemonSlice, + }; + #endregion + + #region Cooldown Helpers + //Determines when to use Lightning Shot private bool ShouldUseLightningShot(Actor? target, LightningShotStrategy strategy) => strategy switch { @@ -678,15 +687,14 @@ private GNB.AID UseCorrectBS() }; //Determines when to use No Mercy - //NOTE: Using SkS GNB for now; use No Mercy as soon as full cartridges are available private bool ShouldUseNoMercy(NoMercyStrategy strategy, Actor? target) => strategy switch { NoMercyStrategy.Automatic => Player.InCombat && target != null && ActionReady(GNB.AID.NoMercy) && GCD < 0.9f && - ((Ammo == 1 && bfCD == 0 && Unlocked(GNB.AID.Bloodfest) && Unlocked(GNB.AID.DoubleDown)) || //Lv90+ Opener - (Ammo >= 1 && bfCD == 0 && Unlocked(GNB.AID.Bloodfest) && !Unlocked(GNB.AID.DoubleDown)) || //Lv80+ Opener - (!Unlocked(GNB.AID.Bloodfest) && Ammo >= 1 && ActionReady(GNB.AID.GnashingFang)) || //Lv70 & below - Ammo == MaxCartridges), //60s & 120s burst windows + ((Ammo == 1 && bfCD == 0 && Unlocked(GNB.AID.Bloodfest) && Unlocked(GNB.AID.DoubleDown)) || //Lv90+ Opener + (Ammo >= 1 && bfCD == 0 && Unlocked(GNB.AID.Bloodfest) && !Unlocked(GNB.AID.DoubleDown)) || //Lv80+ Opener + (!Unlocked(GNB.AID.Bloodfest) && Ammo >= 1 && ActionReady(GNB.AID.GnashingFang)) || //Lv70 & below + Ammo == MaxCartridges), //60s & 120s burst windows NoMercyStrategy.Force => true, NoMercyStrategy.ForceLW => Player.InCombat && GCD < 0.9f, NoMercyStrategy.Force2 => Ammo >= 2, @@ -698,12 +706,14 @@ private GNB.AID UseCorrectBS() }; //Determines when to use Bloodfest - private bool ShouldUseBloodfest(OffensiveStrategy strategy, Actor? target) => strategy switch - { - OffensiveStrategy.Automatic => - Player.InCombat && ActionReady(GNB.AID.Bloodfest) && Ammo == 0 && hasNM, - OffensiveStrategy.Force => true, - OffensiveStrategy.Delay => false, + private bool ShouldUseBloodfest(BloodfestStrategy strategy, Actor? target) => strategy switch + { + BloodfestStrategy.Automatic => + Player.InCombat && target != null && + canBF && Ammo == 0 && hasNM, + BloodfestStrategy.Force => canBF, + BloodfestStrategy.Force0 => canBF && Ammo == 0, + BloodfestStrategy.Delay => false, _ => false }; @@ -713,7 +723,7 @@ private GNB.AID UseCorrectBS() OffensiveStrategy.Automatic => Player.InCombat && In3y(target) && nmCD is < 57.55f and > 17 && ActionReady(Unlocked(GNB.AID.BlastingZone) ? GNB.AID.BlastingZone : GNB.AID.DangerZone), - OffensiveStrategy.Force => true, + OffensiveStrategy.Force => canZone, OffensiveStrategy.Delay => false, _ => false }; @@ -723,7 +733,7 @@ private GNB.AID UseCorrectBS() { OffensiveStrategy.Automatic => Player.InCombat && ActionReady(GNB.AID.BowShock) && In5y(target) && nmCD is < 57.55f and > 17, - OffensiveStrategy.Force => true, + OffensiveStrategy.Force => canBow, OffensiveStrategy.Delay => false, _ => false }; @@ -732,8 +742,8 @@ private GNB.AID UseCorrectBS() private bool ShouldUseSonicBreak(SonicBreakStrategy strategy, Actor? target) => strategy switch { SonicBreakStrategy.Automatic => - Player.InCombat && In3y(target) && hasNM && hasBreak, - SonicBreakStrategy.Force => true, + Player.InCombat && In3y(target) && canBreak, + SonicBreakStrategy.Force => canBreak, SonicBreakStrategy.EarlySB => nmCD is >= 57.5f || hasBreak, SonicBreakStrategy.LateSB => nmLeft <= GCDLength, SonicBreakStrategy.Delay => false, @@ -744,8 +754,9 @@ private GNB.AID UseCorrectBS() private bool ShouldUseDoubleDown(OffensiveStrategy strategy, Actor? target) => strategy switch { OffensiveStrategy.Automatic => - Player.InCombat && ActionReady(GNB.AID.DoubleDown) && In5y(target) && hasNM && Ammo >= 2, - OffensiveStrategy.Force => true, + Player.InCombat && target != null && + In5y(target) && canDD && hasNM, + OffensiveStrategy.Force => canDD, OffensiveStrategy.Delay => false, _ => false }; @@ -754,9 +765,9 @@ private GNB.AID UseCorrectBS() private bool ShouldUseGnashingFang(GnashingStrategy strategy, Actor? target) => strategy switch { GnashingStrategy.Automatic => - Player.InCombat && Ammo >= 1 && In3y(target) && ActionReady(GNB.AID.GnashingFang) && + Player.InCombat && target != null && In3y(target) && canGF && (nmLeft > 0 || hasNM || nmCD is < 35 and > 17), - GnashingStrategy.ForceGnash => Player.InCombat && GunComboStep == 0 && Ammo >= 1, + GnashingStrategy.ForceGnash => canGF, GnashingStrategy.ForceClaw => Player.InCombat && GunComboStep == 1, GnashingStrategy.ForceTalon => Player.InCombat && GunComboStep == 2, GnashingStrategy.Delay => false, @@ -767,13 +778,11 @@ private GNB.AID UseCorrectBS() private bool ShouldUseBurstStrike(OffensiveStrategy strategy, Actor? target) => strategy switch { OffensiveStrategy.Automatic => - Player.InCombat && In3y(target) && - ( - (Unlocked(GNB.AID.DoubleDown) && hasNM && !ActionReady(GNB.AID.DoubleDown) && GunComboStep == 0 && !hasReign) || //Lv90+ - (!Unlocked(GNB.AID.DoubleDown) && !ActionReady(GNB.AID.GnashingFang) && hasNM && GunComboStep == 0) || //Lv80 & Below - (ComboLastMove == GNB.AID.BrutalShell && Ammo == MaxCartridges) //Overcap - ), - OffensiveStrategy.Force => true, + Player.InCombat && target != null && In3y(target) && canBS && + ((Unlocked(GNB.AID.DoubleDown) && hasNM && !ActionReady(GNB.AID.DoubleDown) && GunComboStep == 0 && !hasReign) || //Lv90+ + (!Unlocked(GNB.AID.DoubleDown) && !ActionReady(GNB.AID.GnashingFang) && hasNM && GunComboStep == 0) || //Lv80 & Below + (ComboLastMove == GNB.AID.BrutalShell && Ammo == MaxCartridges)), //Overcap protection + OffensiveStrategy.Force => canBS, OffensiveStrategy.Delay => false, _ => false }; @@ -782,10 +791,10 @@ private GNB.AID UseCorrectBS() private bool ShouldUseFatedCircle(OffensiveStrategy strategy, Actor? AoETargets) => strategy switch { OffensiveStrategy.Automatic => - Player.InCombat && In3y(AoETargets) && - ((hasNM && !ActionReady(GNB.AID.DoubleDown) && Ammo > 0) || + Player.InCombat && AoETargets != null && In3y(AoETargets) && canFC && + ((hasNM && !ActionReady(GNB.AID.DoubleDown)) || (ComboLastMove == GNB.AID.DemonSlice && Ammo == MaxCartridges)), - OffensiveStrategy.Force => true, + OffensiveStrategy.Force => canFC, OffensiveStrategy.Delay => false, _ => false }; @@ -809,4 +818,5 @@ private bool IsPotionAlignedWithNM() PotionStrategy.Immediate => true, _ => false }; + #endregion }