From 3832210925c4b3e08f0500a818a3ba08b795044e Mon Sep 17 00:00:00 2001 From: ace Date: Mon, 3 Feb 2025 18:24:07 -0800 Subject: [PATCH 01/28] Angle-relative dashes --- .../Autorotation/Utility/ClassDNCUtility.cs | 28 ++++++++- .../Autorotation/Utility/ClassDRGUtility.cs | 30 ++++++++-- .../Autorotation/Utility/ClassPCTUtility.cs | 28 ++++++++- .../Autorotation/Utility/ClassRPRUtility.cs | 58 ++++++++++++++++++- 4 files changed, 135 insertions(+), 9 deletions(-) diff --git a/BossMod/Autorotation/Utility/ClassDNCUtility.cs b/BossMod/Autorotation/Utility/ClassDNCUtility.cs index f86c89668d..9d1048f521 100644 --- a/BossMod/Autorotation/Utility/ClassDNCUtility.cs +++ b/BossMod/Autorotation/Utility/ClassDNCUtility.cs @@ -1,9 +1,12 @@ -namespace BossMod.Autorotation; +using static BossMod.Autorotation.ClassPCTUtility; + +namespace BossMod.Autorotation; public sealed class ClassDNCUtility(RotationModuleManager manager, Actor player) : RoleRangedUtility(manager, player) { - public enum Track { CuringWaltz = SharedTrack.Count, ShieldSamba, Improvisation } + public enum Track { CuringWaltz = SharedTrack.Count, ShieldSamba, Improvisation, EnAvant } public enum SambaOption { None, Use87, Use87IfNotActive, Use88, Use88IfNotActive } + public enum EnAvantStrategy { None, CharacterForward, CharacterBackward, CameraForward, CameraBackward } public static readonly ActionID IDLimitBreak3 = ActionID.MakeSpell(DNC.AID.CrimsonLotus); @@ -24,6 +27,14 @@ public static RotationModuleDefinition Definition() DefineSimpleConfig(res, Track.Improvisation, "Improvisation", "Improv", 300, DNC.AID.Improvisation, 15); + res.Define(Track.EnAvant).As("En Avant", "EnAvant", 30) + .AddOption(EnAvantStrategy.None, "None", "No use.", 0, 0, ActionTargets.Self, 35) + .AddOption(EnAvantStrategy.CharacterForward, "CharacterForward", "Dashes in the Forward direction relative to the Character", 30, 0, ActionTargets.Self, 50) + .AddOption(EnAvantStrategy.CharacterBackward, "CharacterBackward", "Dashes in the Backward direction relative to the Character", 30, 0, ActionTargets.Self, 50) + .AddOption(EnAvantStrategy.CameraForward, "CameraForward", "Dashes in the Forward direction relative to the Camera", 30, 0, ActionTargets.Self, 50) + .AddOption(EnAvantStrategy.CameraBackward, "CameraBackward", "Dashes in the Backward direction relative to the Camera", 30, 0, ActionTargets.Self, 50) + .AddAssociatedActions(DNC.AID.EnAvant); + return res; } @@ -44,5 +55,18 @@ public override void Execute(StrategyValues strategy, ref Actor? primaryTarget, }; if (wantSamba) Hints.ActionsToExecute.Push(ActionID.MakeSpell(DNC.AID.ShieldSamba), Player, samba.Priority(), samba.Value.ExpireIn); + + var ea = strategy.Option(Track.EnAvant); + if (ea.As() != EnAvantStrategy.None) + { + var angle = ea.As() switch + { + EnAvantStrategy.CharacterBackward => Player.Rotation + 180.Degrees(), + EnAvantStrategy.CameraForward => World.Client.CameraAzimuth + 180.Degrees(), + EnAvantStrategy.CameraBackward => World.Client.CameraAzimuth, + _ => Player.Rotation + }; + Hints.ActionsToExecute.Push(ActionID.MakeSpell(DNC.AID.EnAvant), Player, ea.Priority(), ea.Value.ExpireIn, facingAngle: angle); + } } } diff --git a/BossMod/Autorotation/Utility/ClassDRGUtility.cs b/BossMod/Autorotation/Utility/ClassDRGUtility.cs index e4e699f9c1..63e558dfbd 100644 --- a/BossMod/Autorotation/Utility/ClassDRGUtility.cs +++ b/BossMod/Autorotation/Utility/ClassDRGUtility.cs @@ -2,8 +2,9 @@ public sealed class ClassDRGUtility(RotationModuleManager manager, Actor player) : RoleMeleeUtility(manager, player) { - public enum Track { WingedGlide = SharedTrack.Count } + public enum Track { WingedGlide = SharedTrack.Count, ElusiveJump } public enum DashStrategy { None, GapClose, GapCloseHold1 } + public enum ElusiveStrategy { None, CharacterForward, CharacterBackward, CameraForward, CameraBackward } public static readonly ActionID IDLimitBreak3 = ActionID.MakeSpell(DRG.AID.DragonsongDive); @@ -12,12 +13,20 @@ public static RotationModuleDefinition Definition() var res = new RotationModuleDefinition("Utility: DRG", "Cooldown Planner support for Utility Actions.\nNOTE: This is NOT a rotation preset! All Utility modules are STRICTLY for cooldown-planning usage.", "Utility for planner", "Akechi", RotationModuleQuality.Excellent, BitMask.Build((int)Class.DRG), 100); DefineShared(res, IDLimitBreak3); - res.Define(Track.WingedGlide).As("Winged Glide", "Dash", 20) - .AddOption(DashStrategy.None, "Automatic", "No use.") + res.Define(Track.WingedGlide).As("Winged Glide", "W.Glide", 20) + .AddOption(DashStrategy.None, "None", "No use.") .AddOption(DashStrategy.GapClose, "GapClose", "Use as gapcloser if outside melee range", 60, 0, ActionTargets.Hostile, 45) .AddOption(DashStrategy.GapCloseHold1, "GapCloseHold1", "Use as gapcloser if outside melee range; conserves 1 charge for manual usage", 60, 0, ActionTargets.Hostile, 84) .AddAssociatedActions(DRG.AID.WingedGlide); + res.Define(Track.ElusiveJump).As("Elusive Jump", "E.Jump", 30) + .AddOption(ElusiveStrategy.None, "None", "No use.", 0, 0, ActionTargets.Self, 35) + .AddOption(ElusiveStrategy.CharacterForward, "CharacterForward", "Dashes in the Forward direction relative to the Character", 30, 15, ActionTargets.Self, 35) + .AddOption(ElusiveStrategy.CharacterBackward, "CharacterBackward", "Dashes in the Backward direction relative to the Character", 30, 15, ActionTargets.Self, 35) + .AddOption(ElusiveStrategy.CameraForward, "CameraForward", "Dashes in the Forward direction relative to the Camera", 30, 15, ActionTargets.Self, 35) + .AddOption(ElusiveStrategy.CameraBackward, "CameraBackward", "Dashes in the Backward direction relative to the Camera", 30, 15, ActionTargets.Self, 35) + .AddAssociatedActions(DRG.AID.ElusiveJump); + return res; } @@ -25,9 +34,22 @@ public override void Execute(StrategyValues strategy, ref Actor? primaryTarget, { ExecuteShared(strategy, IDLimitBreak3, primaryTarget); + var ej = strategy.Option(Track.ElusiveJump); + if (ej.As() != ElusiveStrategy.None) + { + var angle = ej.As() switch + { + ElusiveStrategy.CharacterForward => Player.Rotation + 180.Degrees(), + ElusiveStrategy.CameraBackward => World.Client.CameraAzimuth + 180.Degrees(), + ElusiveStrategy.CameraForward => World.Client.CameraAzimuth, + _ => Player.Rotation + }; + Hints.ActionsToExecute.Push(ActionID.MakeSpell(DRG.AID.ElusiveJump), Player, ej.Priority(), ej.Value.ExpireIn, facingAngle: angle); + } + var dash = strategy.Option(Track.WingedGlide); var dashStrategy = strategy.Option(Track.WingedGlide).As(); - var dashTarget = ResolveTargetOverride(dash.Value); //Smart-Targeting: Target needs to be set in autorotation or CDPlanner to prevent unexpected behavior + var dashTarget = ResolveTargetOverride(dash.Value) ?? primaryTarget; //Smart-Targeting var distance = Player.DistanceToHitbox(dashTarget); var cd = World.Client.Cooldowns[ActionDefinitions.Instance.Spell(DRG.AID.WingedGlide)!.MainCooldownGroup].Remaining; var shouldDash = dashStrategy switch diff --git a/BossMod/Autorotation/Utility/ClassPCTUtility.cs b/BossMod/Autorotation/Utility/ClassPCTUtility.cs index 1fe5d225d2..0c26ebd0e7 100644 --- a/BossMod/Autorotation/Utility/ClassPCTUtility.cs +++ b/BossMod/Autorotation/Utility/ClassPCTUtility.cs @@ -1,9 +1,13 @@ -namespace BossMod.Autorotation; +using static BossMod.Autorotation.ClassDNCUtility; +using static BossMod.Autorotation.ClassRPRUtility; + +namespace BossMod.Autorotation; public sealed class ClassPCTUtility(RotationModuleManager manager, Actor player) : RoleCasterUtility(manager, player) { - public enum Track { TemperaCoat = SharedTrack.Count } + public enum Track { TemperaCoat = SharedTrack.Count, Smudge } public enum TemperaCoatOption { None, CoatOnly, CoatGrassaASAP, CoatGrassaWhenever } + public enum SmudgeStrategy { None, CharacterForward, CharacterBackward, CameraForward, CameraBackward } public static readonly ActionID IDLimitBreak3 = ActionID.MakeSpell(PCT.AID.ChromaticFantasy); @@ -19,6 +23,14 @@ public static RotationModuleDefinition Definition() .AddOption(TemperaCoatOption.CoatGrassaWhenever, "Tempera Coat + Grassa when available", "Use Tempera Coat + Tempera Grassa when weaving or not casting", 90, 10, ActionTargets.Self, 88) .AddAssociatedActions(PCT.AID.TemperaCoat, PCT.AID.TemperaGrassa); + res.Define(Track.Smudge).As("Smudge", uiPriority: 30) + .AddOption(SmudgeStrategy.None, "None", "No use.", 0, 0, ActionTargets.Self, 35) + .AddOption(SmudgeStrategy.CharacterForward, "CharacterForward", "Dashes in the Forward direction relative to the Character", 20, 5, ActionTargets.Self, 20) + .AddOption(SmudgeStrategy.CharacterBackward, "CharacterBackward", "Dashes in the Backward direction relative to the Character", 20, 5, ActionTargets.Self, 20) + .AddOption(SmudgeStrategy.CameraForward, "CameraForward", "Dashes in the Forward direction relative to the Camera", 20, 5, ActionTargets.Self, 20) + .AddOption(SmudgeStrategy.CameraBackward, "CameraBackward", "Dashes in the Backward direction relative to the Camera", 20, 5, ActionTargets.Self, 20) + .AddAssociatedActions(PCT.AID.Smudge); + return res; } @@ -54,5 +66,17 @@ public override void Execute(StrategyValues strategy, ref Actor? primaryTarget, Hints.ActionsToExecute.Push(ActionID.MakeSpell(PCT.AID.TemperaGrassa), Player, tempera.Priority(), tempera.Value.ExpireIn); } } + var smuh = strategy.Option(Track.Smudge); + if (smuh.As() != SmudgeStrategy.None) + { + var angle = smuh.As() switch + { + SmudgeStrategy.CharacterBackward => Player.Rotation + 180.Degrees(), + SmudgeStrategy.CameraForward => World.Client.CameraAzimuth, + SmudgeStrategy.CameraBackward => World.Client.CameraAzimuth + 180.Degrees(), + _ => Player.Rotation + }; + Hints.ActionsToExecute.Push(ActionID.MakeSpell(PCT.AID.Smudge), Player, smuh.Priority(), smuh.Value.ExpireIn, facingAngle: angle); + } } } diff --git a/BossMod/Autorotation/Utility/ClassRPRUtility.cs b/BossMod/Autorotation/Utility/ClassRPRUtility.cs index cd407e4153..de6c826d4e 100644 --- a/BossMod/Autorotation/Utility/ClassRPRUtility.cs +++ b/BossMod/Autorotation/Utility/ClassRPRUtility.cs @@ -2,7 +2,10 @@ public sealed class ClassRPRUtility(RotationModuleManager manager, Actor player) : RoleMeleeUtility(manager, player) { - public enum Track { ArcaneCrest = SharedTrack.Count } + public enum Track { ArcaneCrest = SharedTrack.Count, Ingress, Egress, Regress } + public enum IngressStrategy { None, CharacterForward, CharacterBackward, CameraForward, CameraBackward } + public enum EgressStrategy { None, CharacterForward, CharacterBackward, CameraForward, CameraBackward } + public enum RegressStrategy { None, Use } public static readonly ActionID IDLimitBreak3 = ActionID.MakeSpell(RPR.AID.TheEnd); @@ -13,6 +16,27 @@ public static RotationModuleDefinition Definition() DefineSimpleConfig(res, Track.ArcaneCrest, "Crest", "", 600, RPR.AID.ArcaneCrest, 5); + res.Define(Track.Ingress).As("Hell's Ingress", "Ingress", 30) + .AddOption(IngressStrategy.None, "None", "No use.", 0, 0, ActionTargets.Self, 20) + .AddOption(IngressStrategy.CharacterForward, "CharacterForward", "Dashes in the Forward direction relative to the Character", 20, 10, ActionTargets.Self, 20) + .AddOption(IngressStrategy.CharacterBackward, "CharacterBackward", "Dashes in the Backward direction relative to the Character", 20, 10, ActionTargets.Self, 20) + .AddOption(IngressStrategy.CameraForward, "CameraForward", "Dashes in the Forward direction relative to the Camera", 20, 10, ActionTargets.Self, 20) + .AddOption(IngressStrategy.CameraBackward, "CameraBackward", "Dashes in the Backward direction relative to the Camera", 20, 10, ActionTargets.Self, 20) + .AddAssociatedActions(RPR.AID.HellsIngress); + + res.Define(Track.Egress).As("Hell's Egress", "Egress", 30) + .AddOption(EgressStrategy.None, "None", "No use.", 0, 0, ActionTargets.Self, 20) + .AddOption(EgressStrategy.CharacterForward, "CharacterForward", "Dashes in the Forward direction relative to the Character", 20, 10, ActionTargets.Self, 20) + .AddOption(EgressStrategy.CharacterBackward, "CharacterBackward", "Dashes in the Backward direction relative to the Character", 20, 10, ActionTargets.Self, 20) + .AddOption(EgressStrategy.CameraForward, "CameraForward", "Dashes in the Forward direction relative to the Camera", 20, 10, ActionTargets.Self, 20) + .AddOption(EgressStrategy.CameraBackward, "CameraBackward", "Dashes in the Backward direction relative to the Camera", 20, 10, ActionTargets.Self, 20) + .AddAssociatedActions(RPR.AID.HellsEgress); + + res.Define(Track.Regress).As("Regress", "Regress", 30) + .AddOption(RegressStrategy.None, "None", "No use.", 0, 0, ActionTargets.Self, 20) + .AddOption(RegressStrategy.Use, "Use", "Use Regress", 20, 10, ActionTargets.Self, 20) + .AddAssociatedActions(RPR.AID.Regress); + return res; } @@ -20,5 +44,37 @@ public override void Execute(StrategyValues strategy, ref Actor? primaryTarget, { ExecuteShared(strategy, IDLimitBreak3, primaryTarget); ExecuteSimple(strategy.Option(Track.ArcaneCrest), RPR.AID.ArcaneCrest, Player); + + var reg = strategy.Option(Track.Regress); + var regStrat = strategy.Option(Track.Regress).As(); + var zone = World.Actors.FirstOrDefault(x => x.OID == 0x4C3 && x.OwnerID == Player.InstanceID); + if (regStrat != RegressStrategy.None) + Hints.ActionsToExecute.Push(ActionID.MakeSpell(RPR.AID.Regress), Player, reg.Priority(), reg.Value.ExpireIn, targetPos: zone!.PosRot.XYZ()); + + var ing = strategy.Option(Track.Ingress); + if (ing.As() != IngressStrategy.None && Player.FindStatus(RPR.SID.Threshold) == null) + { + var angle = ing.As() switch + { + IngressStrategy.CharacterBackward => Player.Rotation + 180.Degrees(), + IngressStrategy.CameraForward => World.Client.CameraAzimuth + 180.Degrees(), + IngressStrategy.CameraBackward => World.Client.CameraAzimuth, + _ => Player.Rotation + }; + Hints.ActionsToExecute.Push(ActionID.MakeSpell(RPR.AID.HellsIngress), Player, ing.Priority(), ing.Value.ExpireIn, facingAngle: angle); + } + + var egg = strategy.Option(Track.Egress); + if (egg.As() != EgressStrategy.None && Player.FindStatus(RPR.SID.Threshold) == null) + { + var angle = egg.As() switch + { + EgressStrategy.CharacterForward => Player.Rotation + 180.Degrees(), + EgressStrategy.CameraBackward => World.Client.CameraAzimuth + 180.Degrees(), + EgressStrategy.CameraForward => World.Client.CameraAzimuth, + _ => Player.Rotation + }; + Hints.ActionsToExecute.Push(ActionID.MakeSpell(RPR.AID.HellsEgress), Player, egg.Priority(), egg.Value.ExpireIn, facingAngle: angle); + } } } From c6170ad4cf51dfd26a2949767b403b830795397c Mon Sep 17 00:00:00 2001 From: ace Date: Mon, 3 Feb 2025 18:28:03 -0800 Subject: [PATCH 02/28] Regress minLevel --- BossMod/Autorotation/Utility/ClassRPRUtility.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/BossMod/Autorotation/Utility/ClassRPRUtility.cs b/BossMod/Autorotation/Utility/ClassRPRUtility.cs index de6c826d4e..000d4f9300 100644 --- a/BossMod/Autorotation/Utility/ClassRPRUtility.cs +++ b/BossMod/Autorotation/Utility/ClassRPRUtility.cs @@ -33,8 +33,8 @@ public static RotationModuleDefinition Definition() .AddAssociatedActions(RPR.AID.HellsEgress); res.Define(Track.Regress).As("Regress", "Regress", 30) - .AddOption(RegressStrategy.None, "None", "No use.", 0, 0, ActionTargets.Self, 20) - .AddOption(RegressStrategy.Use, "Use", "Use Regress", 20, 10, ActionTargets.Self, 20) + .AddOption(RegressStrategy.None, "None", "No use.", 0, 0, ActionTargets.Self, 74) + .AddOption(RegressStrategy.Use, "Use", "Use Regress", 0, 0, ActionTargets.Self, 74) .AddAssociatedActions(RPR.AID.Regress); return res; From 4dc4e50186fed15b10cfbf1ad03fb5dc022afa43 Mon Sep 17 00:00:00 2001 From: ace Date: Mon, 3 Feb 2025 18:29:21 -0800 Subject: [PATCH 03/28] nothing to see here --- BossMod/Autorotation/Utility/ClassDNCUtility.cs | 4 +--- BossMod/Autorotation/Utility/ClassPCTUtility.cs | 5 +---- 2 files changed, 2 insertions(+), 7 deletions(-) diff --git a/BossMod/Autorotation/Utility/ClassDNCUtility.cs b/BossMod/Autorotation/Utility/ClassDNCUtility.cs index 9d1048f521..bf5a1506b3 100644 --- a/BossMod/Autorotation/Utility/ClassDNCUtility.cs +++ b/BossMod/Autorotation/Utility/ClassDNCUtility.cs @@ -1,6 +1,4 @@ -using static BossMod.Autorotation.ClassPCTUtility; - -namespace BossMod.Autorotation; +namespace BossMod.Autorotation; public sealed class ClassDNCUtility(RotationModuleManager manager, Actor player) : RoleRangedUtility(manager, player) { diff --git a/BossMod/Autorotation/Utility/ClassPCTUtility.cs b/BossMod/Autorotation/Utility/ClassPCTUtility.cs index 0c26ebd0e7..e0efe56fac 100644 --- a/BossMod/Autorotation/Utility/ClassPCTUtility.cs +++ b/BossMod/Autorotation/Utility/ClassPCTUtility.cs @@ -1,7 +1,4 @@ -using static BossMod.Autorotation.ClassDNCUtility; -using static BossMod.Autorotation.ClassRPRUtility; - -namespace BossMod.Autorotation; +namespace BossMod.Autorotation; public sealed class ClassPCTUtility(RotationModuleManager manager, Actor player) : RoleCasterUtility(manager, player) { From 073dc76a0e883580356ad68b8894dcc434c077e2 Mon Sep 17 00:00:00 2001 From: ace Date: Tue, 4 Feb 2025 06:18:30 -0800 Subject: [PATCH 04/28] oh --- BossMod/Autorotation/Utility/ClassDNCUtility.cs | 2 +- BossMod/Autorotation/Utility/ClassPCTUtility.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/BossMod/Autorotation/Utility/ClassDNCUtility.cs b/BossMod/Autorotation/Utility/ClassDNCUtility.cs index bf5a1506b3..19f2fc140f 100644 --- a/BossMod/Autorotation/Utility/ClassDNCUtility.cs +++ b/BossMod/Autorotation/Utility/ClassDNCUtility.cs @@ -26,7 +26,7 @@ public static RotationModuleDefinition Definition() DefineSimpleConfig(res, Track.Improvisation, "Improvisation", "Improv", 300, DNC.AID.Improvisation, 15); res.Define(Track.EnAvant).As("En Avant", "EnAvant", 30) - .AddOption(EnAvantStrategy.None, "None", "No use.", 0, 0, ActionTargets.Self, 35) + .AddOption(EnAvantStrategy.None, "None", "No use.", 0, 0, ActionTargets.Self, 50) .AddOption(EnAvantStrategy.CharacterForward, "CharacterForward", "Dashes in the Forward direction relative to the Character", 30, 0, ActionTargets.Self, 50) .AddOption(EnAvantStrategy.CharacterBackward, "CharacterBackward", "Dashes in the Backward direction relative to the Character", 30, 0, ActionTargets.Self, 50) .AddOption(EnAvantStrategy.CameraForward, "CameraForward", "Dashes in the Forward direction relative to the Camera", 30, 0, ActionTargets.Self, 50) diff --git a/BossMod/Autorotation/Utility/ClassPCTUtility.cs b/BossMod/Autorotation/Utility/ClassPCTUtility.cs index e0efe56fac..5271c3ab16 100644 --- a/BossMod/Autorotation/Utility/ClassPCTUtility.cs +++ b/BossMod/Autorotation/Utility/ClassPCTUtility.cs @@ -21,7 +21,7 @@ public static RotationModuleDefinition Definition() .AddAssociatedActions(PCT.AID.TemperaCoat, PCT.AID.TemperaGrassa); res.Define(Track.Smudge).As("Smudge", uiPriority: 30) - .AddOption(SmudgeStrategy.None, "None", "No use.", 0, 0, ActionTargets.Self, 35) + .AddOption(SmudgeStrategy.None, "None", "Do not use automatically", 0, 0, ActionTargets.Self, 20) .AddOption(SmudgeStrategy.CharacterForward, "CharacterForward", "Dashes in the Forward direction relative to the Character", 20, 5, ActionTargets.Self, 20) .AddOption(SmudgeStrategy.CharacterBackward, "CharacterBackward", "Dashes in the Backward direction relative to the Character", 20, 5, ActionTargets.Self, 20) .AddOption(SmudgeStrategy.CameraForward, "CameraForward", "Dashes in the Forward direction relative to the Camera", 20, 5, ActionTargets.Self, 20) From cac919d3ca9484e9774e354c60110379c0c8759f Mon Sep 17 00:00:00 2001 From: ace Date: Tue, 4 Feb 2025 09:17:24 -0800 Subject: [PATCH 05/28] Regress breaks ar without this condition lmfao --- BossMod/Autorotation/Utility/ClassRPRUtility.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/BossMod/Autorotation/Utility/ClassRPRUtility.cs b/BossMod/Autorotation/Utility/ClassRPRUtility.cs index 000d4f9300..b9a171ffff 100644 --- a/BossMod/Autorotation/Utility/ClassRPRUtility.cs +++ b/BossMod/Autorotation/Utility/ClassRPRUtility.cs @@ -48,7 +48,7 @@ public override void Execute(StrategyValues strategy, ref Actor? primaryTarget, var reg = strategy.Option(Track.Regress); var regStrat = strategy.Option(Track.Regress).As(); var zone = World.Actors.FirstOrDefault(x => x.OID == 0x4C3 && x.OwnerID == Player.InstanceID); - if (regStrat != RegressStrategy.None) + if (regStrat != RegressStrategy.None && Player.FindStatus(RPR.SID.Threshold) != null) Hints.ActionsToExecute.Push(ActionID.MakeSpell(RPR.AID.Regress), Player, reg.Priority(), reg.Value.ExpireIn, targetPos: zone!.PosRot.XYZ()); var ing = strategy.Option(Track.Ingress); From 35662f885b0427e104558ec34e604bd3abceaf97 Mon Sep 17 00:00:00 2001 From: ace Date: Tue, 4 Feb 2025 13:43:51 -0800 Subject: [PATCH 06/28] WAR fixes, AST cleanup --- .../Standard/akechi/Tank/AkechiWAR.cs | 13 ++- .../Autorotation/Utility/ClassASTUtility.cs | 83 ++++--------------- 2 files changed, 25 insertions(+), 71 deletions(-) diff --git a/BossMod/Autorotation/Standard/akechi/Tank/AkechiWAR.cs b/BossMod/Autorotation/Standard/akechi/Tank/AkechiWAR.cs index 3f4a81eb4a..92f233a444 100644 --- a/BossMod/Autorotation/Standard/akechi/Tank/AkechiWAR.cs +++ b/BossMod/Autorotation/Standard/akechi/Tank/AkechiWAR.cs @@ -142,10 +142,10 @@ private GCDPriority FellCleave() public enum GCDPriority { None = 0, - Standard = 100, Gauge = 300, PrimalRuination = 400, DelayFC = 390, + Standard = 400, FlexibleFC = 470, FlexibleIR = 490, PrimalRend = 500, @@ -217,6 +217,8 @@ public enum OGCDPriority #endregion public bool IsRiskingGauge() { + if (InnerRelease.Stacks > 0) + return true; if (BeastGauge >= 90 && //if 90 ComboLastMove is AID.Maim) //next is Storm's Path, which overcaps. We need spender here return true; @@ -324,6 +326,9 @@ public override void Execution(StrategyValues strategy, Enemy? primaryTarget) // #endregion ShouldUseAOE = ShouldUseAOECircle(5).OnThreeOrMore; + + BurstWindowLeft = (InnerRelease.CD >= 40) ? 1.0f : 0.0f; + BurstWindowIn = (InnerRelease.CD == 0) ? 1.0f : 0.0f; (BestSplashTargets, NumSplashTargets) = GetBestTarget(primaryTarget, 20, IsSplashTarget); BestSplashTarget = Unlocked(AID.PrimalRend) && NumSplashTargets >= 2 ? BestSplashTargets : primaryTarget; (TwoMinuteLeft, TwoMinuteIn) = EstimateRaidBuffTimings(primaryTarget?.Actor); @@ -337,20 +342,20 @@ public override void Execution(StrategyValues strategy, Enemy? primaryTarget) // QueueGCD(BestRotation(), //queue the next single-target combo action only if combo is finished TargetChoice(strategy.Option(SharedTrack.AOE)) //Get target choice ?? primaryTarget?.Actor, //if none, pick primary target - GCDPriority.Standard); //with priority for 123/10 combo actions + IsRiskingGauge() ? GCDPriority.Standard - 500 : GCDPriority.Standard); //with priority for 123/10 combo actions } if (strategy.ForceST()) //if Force Single Target option is picked { QueueGCD(ST(), TargetChoice(strategy.Option(SharedTrack.AOE)) //Get target choice ?? primaryTarget?.Actor, //if none, pick primary target - GCDPriority.Standard); //with priority for 123/10 combo actions + IsRiskingGauge() ? GCDPriority.Standard - 500 : GCDPriority.Standard); //with priority for 123/10 combo actions } if (strategy.ForceAOE()) //if Force AOE option is picked { QueueGCD(AOE(), Player, - GCDPriority.Standard); //with priority for 123/10 combo actions + IsRiskingGauge() ? GCDPriority.Standard - 500 : GCDPriority.Standard); //with priority for 123/10 combo actions } #endregion diff --git a/BossMod/Autorotation/Utility/ClassASTUtility.cs b/BossMod/Autorotation/Utility/ClassASTUtility.cs index b34badf2c7..cad87ca361 100644 --- a/BossMod/Autorotation/Utility/ClassASTUtility.cs +++ b/BossMod/Autorotation/Utility/ClassASTUtility.cs @@ -33,8 +33,8 @@ public static RotationModuleDefinition Definition() res.Define(Track.EarthlyStar).As("EarthlyStar", "E.Star", 200) //AoE GCD heal, 60s CD, 10s + 10s effect duration .AddOption(StarOption.None, "None", "Do not use automatically") - .AddOption(StarOption.Use, "Earthly Star", "Use Earthly Star", 60, 10, ActionTargets.Hostile, 62) - .AddOption(StarOption.End, "Stellar Detonation", "Use Stellar Detonation", 0, 1, ActionTargets.Hostile, 62) + .AddOption(StarOption.Use, "Earthly Star", "Use Earthly Star", 60, 10, ActionTargets.Party | ActionTargets.Self | ActionTargets.Hostile, 62) + .AddOption(StarOption.End, "Stellar Detonation", "Use Stellar Detonation", 0, 1, ActionTargets.Self, 62) .AddAssociatedActions(AST.AID.EarthlyStar, AST.AID.StellarDetonation); DefineSimpleConfig(res, Track.CelestialIntersection, "CelestialIntersection", "C.Inter", 100, AST.AID.CelestialIntersection, 30); //ST oGCD heal/shield, 30s CD (60s Total), 2 charges @@ -75,7 +75,10 @@ public override void Execute(StrategyValues strategy, ref Actor? primaryTarget, ExecuteSimple(strategy.Option(Track.Exaltation), AST.AID.Exaltation, Player); ExecuteSimple(strategy.Option(Track.SunSign), AST.AID.SunSign, Player); + //TODO: These should work with all Area targeting options? Able to target Waymarks, Center, AbsolutePoint, etc. + //Figure out how to make compatible with WPos var star = strategy.Option(Track.EarthlyStar); + var starTarget = ResolveTargetOverride(star.Value) ?? primaryTarget ?? Player; var starAction = star.As() switch { StarOption.Use => AST.AID.EarthlyStar, @@ -83,7 +86,7 @@ public override void Execute(StrategyValues strategy, ref Actor? primaryTarget, _ => default }; if (starAction != default) - QueueOGCD(starAction, ResolveTargetOverride(star.Value) ?? primaryTarget ?? Player); + Hints.ActionsToExecute.Push(ActionID.MakeSpell(starAction), starTarget, star.Priority(), star.Value.ExpireIn, targetPos: starTarget!.PosRot.XYZ()); //Aspected Helios full execution var heliosUp = StatusDetails(Player, AST.SID.AspectedHelios, Player.InstanceID).Left > 0.1f || StatusDetails(Player, AST.SID.HeliosConjunction, Player.InstanceID).Left > 0.1f; @@ -95,18 +98,24 @@ public override void Execute(StrategyValues strategy, ref Actor? primaryTarget, _ => default }; if (heliosAction != default && !heliosUp) - QueueGCD(heliosAction, Player); + Hints.ActionsToExecute.Push(ActionID.MakeSpell(heliosAction), Player, helios.Priority(), helios.Value.ExpireIn); //Horoscope full execution var horo = strategy.Option(Track.Horoscope); + var horoStrat = horo.As() switch + { + HoroscopeOption.Use => Player.FindStatus(AST.SID.Horoscope) == null, + HoroscopeOption.End => Player.FindStatus(AST.SID.Horoscope) != null, + _ => default + }; var horoAction = horo.As() switch { HoroscopeOption.Use => AST.AID.Horoscope, HoroscopeOption.End => AST.AID.HoroscopeEnd, _ => default }; - if (horoAction != default) - QueueOGCD(horoAction, Player); + if (horoStrat != default && horoAction != default) + Hints.ActionsToExecute.Push(ActionID.MakeSpell(horoAction), Player, horo.Priority(), horo.Value.ExpireIn); var cosmos = strategy.Option(Track.Macrocosmos); var cosmosAction = cosmos.As() switch @@ -116,66 +125,6 @@ public override void Execute(StrategyValues strategy, ref Actor? primaryTarget, _ => default }; if (cosmosAction != default) - QueueOGCD(cosmosAction, primaryTarget); - } - - #region Core Execution Helpers - - public AST.AID NextGCD; //Next global cooldown action to be used - public void QueueGCD

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

(AST.AID aid, Actor? target, P priority, float delay = 0) where P : Enum - => QueueOGCD(aid, target, (int)(object)priority, delay); - - public void QueueOGCD(AST.AID aid, Actor? target, int priority = 4, float delay = 0) - { - if (priority == 0) - return; - - QueueAction(aid, target, ActionQueue.Priority.Medium + priority, delay); + Hints.ActionsToExecute.Push(ActionID.MakeSpell(cosmosAction), primaryTarget, cosmos.Priority(), cosmos.Value.ExpireIn); } - - public bool QueueAction(AST.AID aid, Actor? target, float priority, float delay) - { - if ((uint)(object)aid == 0) - return false; - - var def = ActionDefinitions.Instance.Spell(aid); - if (def == null) - return false; - - if (def.Range != 0 && target == null) - { - return false; - } - - Vector3 targetPos = default; - - if (def.AllowedTargets.HasFlag(ActionTargets.Area)) - { - if (def.Range == 0) - targetPos = Player.PosRot.XYZ(); - else if (target != null) - targetPos = target.PosRot.XYZ(); - } - - Hints.ActionsToExecute.Push(ActionID.MakeSpell(aid), target, priority, delay: delay, castTime: def.CastTime, targetPos: targetPos); // TODO[cast-time]: this probably needs explicit cast-time argument (adjusted by swiftcast etc) - return true; - } - #endregion - } From cbc864a0fdb0433b152d63a6ee841cc00dd7d3f2 Mon Sep 17 00:00:00 2001 From: ace Date: Tue, 4 Feb 2025 13:55:50 -0800 Subject: [PATCH 07/28] NIN cleanup --- BossMod/Autorotation/Utility/ClassNINUtility.cs | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/BossMod/Autorotation/Utility/ClassNINUtility.cs b/BossMod/Autorotation/Utility/ClassNINUtility.cs index 997d705905..16a35bcc43 100644 --- a/BossMod/Autorotation/Utility/ClassNINUtility.cs +++ b/BossMod/Autorotation/Utility/ClassNINUtility.cs @@ -16,7 +16,7 @@ public static RotationModuleDefinition Definition() res.Define(Track.Shukuchi).As("Shukuchi", "Dash", 20) .AddOption(DashStrategy.None, "Automatic", "No use.") - .AddOption(DashStrategy.GapClose, "GapClose", "Use as gapcloser if outside melee range", 60, 0, ActionTargets.Hostile, 45) + .AddOption(DashStrategy.GapClose, "GapClose", "Use as gapcloser if outside melee range", 60, 0, ActionTargets.Party | ActionTargets.Hostile, 45) .AddOption(DashStrategy.GapCloseHold1, "GapCloseHold1", "Use as gapcloser if outside melee range; conserves 1 charge for manual usage", 60, 0, ActionTargets.Hostile, 74) .AddAssociatedActions(NIN.AID.Shukuchi); @@ -29,6 +29,8 @@ public override void Execute(StrategyValues strategy, ref Actor? primaryTarget, ExecuteSimple(strategy.Option(Track.ShadeShift), NIN.AID.ShadeShift, Player); // TODO: revise, this doesn't look correct (shukuchi is area targeted, so it should use that; probably should expose options to use regardless of melee distance...) + //These should work with all Area targeting options? Able to target Waymarks, Center, AbsolutePoint, etc. + //Figure out how to make compatible with WPos var dash = strategy.Option(Track.Shukuchi); var dashStrategy = strategy.Option(Track.Shukuchi).As(); var dashTarget = ResolveTargetOverride(dash.Value) ?? primaryTarget; //Smart-Targeting @@ -38,10 +40,10 @@ public override void Execute(StrategyValues strategy, ref Actor? primaryTarget, { DashStrategy.None => false, DashStrategy.GapClose => distance is > 3f and <= 20f, - DashStrategy.GapCloseHold1 => distance is > 3f and <= 20f && cd <= 60.5f, // TODO: this condition doesn't look correct... + DashStrategy.GapCloseHold1 => distance is > 3f and <= 20f && cd < 0.6f, _ => false, }; if (shouldDash && dashTarget != null) - Hints.ActionsToExecute.Push(ActionID.MakeSpell(NIN.AID.Shukuchi), null, dash.Priority(), dash.Value.ExpireIn, 0, 0, dashTarget.PosRot.XYZ()); + Hints.ActionsToExecute.Push(ActionID.MakeSpell(NIN.AID.Shukuchi), null, dash.Priority(), dash.Value.ExpireIn, targetPos: dashTarget.PosRot.XYZ()); } } From 200e9a0bec69e71e9fd43d93489bedcbf8cf219e Mon Sep 17 00:00:00 2001 From: ace Date: Tue, 4 Feb 2025 14:10:06 -0800 Subject: [PATCH 08/28] SCH cleanup, remove summons and replace with Seraph only Summons only worked correctly with Seraph, so I just made it its own option. Will return to it later. --- .../Autorotation/Utility/ClassASTUtility.cs | 10 ++- .../Autorotation/Utility/ClassNINUtility.cs | 4 +- .../Autorotation/Utility/ClassSCHUtility.cs | 81 +++---------------- 3 files changed, 22 insertions(+), 73 deletions(-) diff --git a/BossMod/Autorotation/Utility/ClassASTUtility.cs b/BossMod/Autorotation/Utility/ClassASTUtility.cs index cad87ca361..01eda812e5 100644 --- a/BossMod/Autorotation/Utility/ClassASTUtility.cs +++ b/BossMod/Autorotation/Utility/ClassASTUtility.cs @@ -33,7 +33,7 @@ public static RotationModuleDefinition Definition() res.Define(Track.EarthlyStar).As("EarthlyStar", "E.Star", 200) //AoE GCD heal, 60s CD, 10s + 10s effect duration .AddOption(StarOption.None, "None", "Do not use automatically") - .AddOption(StarOption.Use, "Earthly Star", "Use Earthly Star", 60, 10, ActionTargets.Party | ActionTargets.Self | ActionTargets.Hostile, 62) + .AddOption(StarOption.Use, "Earthly Star", "Use Earthly Star", 60, 10, ActionTargets.Party | ActionTargets.Self | ActionTargets.Hostile, 62) // TODO: should use ActionTargets.Area, but the Point options do not work yet .AddOption(StarOption.End, "Stellar Detonation", "Use Stellar Detonation", 0, 1, ActionTargets.Self, 62) .AddAssociatedActions(AST.AID.EarthlyStar, AST.AID.StellarDetonation); @@ -118,13 +118,19 @@ public override void Execute(StrategyValues strategy, ref Actor? primaryTarget, Hints.ActionsToExecute.Push(ActionID.MakeSpell(horoAction), Player, horo.Priority(), horo.Value.ExpireIn); var cosmos = strategy.Option(Track.Macrocosmos); + var cosmosStrat = cosmos.As() switch + { + MacrocosmosOption.Use => Player.FindStatus(AST.SID.Macrocosmos) == null, + MacrocosmosOption.End => Player.FindStatus(AST.SID.Macrocosmos) != null, + _ => default + }; var cosmosAction = cosmos.As() switch { MacrocosmosOption.Use => AST.AID.Macrocosmos, MacrocosmosOption.End => AST.AID.MicrocosmosEnd, _ => default }; - if (cosmosAction != default) + if (cosmosStrat != default) Hints.ActionsToExecute.Push(ActionID.MakeSpell(cosmosAction), primaryTarget, cosmos.Priority(), cosmos.Value.ExpireIn); } } diff --git a/BossMod/Autorotation/Utility/ClassNINUtility.cs b/BossMod/Autorotation/Utility/ClassNINUtility.cs index 16a35bcc43..395e0b740f 100644 --- a/BossMod/Autorotation/Utility/ClassNINUtility.cs +++ b/BossMod/Autorotation/Utility/ClassNINUtility.cs @@ -16,8 +16,8 @@ public static RotationModuleDefinition Definition() res.Define(Track.Shukuchi).As("Shukuchi", "Dash", 20) .AddOption(DashStrategy.None, "Automatic", "No use.") - .AddOption(DashStrategy.GapClose, "GapClose", "Use as gapcloser if outside melee range", 60, 0, ActionTargets.Party | ActionTargets.Hostile, 45) - .AddOption(DashStrategy.GapCloseHold1, "GapCloseHold1", "Use as gapcloser if outside melee range; conserves 1 charge for manual usage", 60, 0, ActionTargets.Hostile, 74) + .AddOption(DashStrategy.GapClose, "GapClose", "Use as gapcloser if outside melee range", 60, 0, ActionTargets.Party | ActionTargets.Hostile, 45) // TODO: should use ActionTargets.Area, but the Point options do not work yet + .AddOption(DashStrategy.GapCloseHold1, "GapCloseHold1", "Use as gapcloser if outside melee range; conserves 1 charge for manual usage", 60, 0, ActionTargets.Party | ActionTargets.Hostile, 74) .AddAssociatedActions(NIN.AID.Shukuchi); return res; diff --git a/BossMod/Autorotation/Utility/ClassSCHUtility.cs b/BossMod/Autorotation/Utility/ClassSCHUtility.cs index 2d7bbe67cb..af7e3c08ea 100644 --- a/BossMod/Autorotation/Utility/ClassSCHUtility.cs +++ b/BossMod/Autorotation/Utility/ClassSCHUtility.cs @@ -2,13 +2,12 @@ public sealed class ClassSCHUtility(RotationModuleManager manager, Actor player) : RoleHealerUtility(manager, player) { - public enum Track { WhisperingDawn = SharedTrack.Count, Adloquium, Succor, FeyIllumination, Lustrate, SacredSoil, Indomitability, DeploymentTactics, EmergencyTactics, Dissipation, Excogitation, Aetherpact, Recitation, FeyBlessing, Consolation, Protraction, Expedient, Seraphism, Summons } + public enum Track { WhisperingDawn = SharedTrack.Count, Adloquium, Succor, FeyIllumination, Lustrate, SacredSoil, Indomitability, DeploymentTactics, EmergencyTactics, Dissipation, Excogitation, Aetherpact, Recitation, FeyBlessing, Consolation, Protraction, Expedient, Seraphism, Seraph } public enum SuccorOption { None, Succor, Concitation } public enum SacredSoilOption { None, Use, UseEx } public enum DeployOption { None, Use, UseEx } public enum AetherpactOption { None, Use, End } public enum RecitationOption { None, Use, UseEx } - public enum PetOption { None, Eos, Seraph } public static readonly ActionID IDLimitBreak3 = ActionID.MakeSpell(SCH.AID.AngelFeathers); @@ -31,8 +30,8 @@ public static RotationModuleDefinition Definition() res.Define(Track.SacredSoil).As("Sacred Soil", "S.Soil", 200) .AddOption(SacredSoilOption.None, "None", "Do not use automatically") - .AddOption(SacredSoilOption.Use, "Use", "Use Sacred Soil", 30, 15, ActionTargets.All, 50, 77) - .AddOption(SacredSoilOption.UseEx, "UseEx", "Use Enhanced Sacred Soil", 30, 15, ActionTargets.All, 78) + .AddOption(SacredSoilOption.Use, "Use", "Use Sacred Soil", 30, 15, ActionTargets.Party | ActionTargets.Self | ActionTargets.Hostile, 50, 77) // TODO: should use ActionTargets.Area, but the Point options do not work yet + .AddOption(SacredSoilOption.UseEx, "UseEx", "Use Enhanced Sacred Soil", 30, 15, ActionTargets.Area, 78) .AddAssociatedActions(SCH.AID.SacredSoil); DefineSimpleConfig(res, Track.Indomitability, "Indomitability", "Indom.", 90, SCH.AID.Indomitability); @@ -64,13 +63,7 @@ public static RotationModuleDefinition Definition() DefineSimpleConfig(res, Track.Protraction, "Protraction", "Prot.", 110, SCH.AID.Protraction, 10); DefineSimpleConfig(res, Track.Expedient, "Expedient", "Exped.", 200, SCH.AID.Expedient, 20); DefineSimpleConfig(res, Track.Seraphism, "Seraphism", "Seraphism", 300, SCH.AID.Seraphism, 20); - - // Pet Summons - res.Define(Track.Summons).As("Pet", "", 180) - .AddOption(PetOption.None, "None", "Do not use automatically") - .AddOption(PetOption.Eos, "Eos", "Summon Eos", 2, 0, ActionTargets.Self, 4) - .AddOption(PetOption.Seraph, "Seraph", "Summon Seraph", 120, 22, ActionTargets.Self, 80) - .AddAssociatedActions(SCH.AID.SummonEos, SCH.AID.SummonSeraph); + DefineSimpleConfig(res, Track.Seraph, "Seraph", "Seraph", 300, SCH.AID.SummonSeraph, 20); return res; } @@ -91,6 +84,7 @@ public override void Execute(StrategyValues strategy, ref Actor? primaryTarget, ExecuteSimple(strategy.Option(Track.Protraction), SCH.AID.Protraction, Player); ExecuteSimple(strategy.Option(Track.Expedient), SCH.AID.Expedient, Player); ExecuteSimple(strategy.Option(Track.Seraphism), SCH.AID.Seraphism, Player); + ExecuteSimple(strategy.Option(Track.Seraph), SCH.AID.SummonSeraph, Player); var shieldUp = StatusDetails(Player, SCH.SID.Galvanize, Player.InstanceID).Left > 0.1f || StatusDetails(Player, SGE.SID.EukrasianPrognosis, Player.InstanceID).Left > 0.1f; var succ = strategy.Option(Track.Succor); @@ -101,21 +95,21 @@ public override void Execute(StrategyValues strategy, ref Actor? primaryTarget, _ => default }; if (succAction != default && !shieldUp) - QueueOGCD(succAction, Player); + Hints.ActionsToExecute.Push(ActionID.MakeSpell(succAction), Player, succ.Priority(), succ.Value.ExpireIn, castTime: 2); // TODO[cast-time]: adjustment (swiftcast etc) var soil = strategy.Option(Track.SacredSoil); + var soilTarget = ResolveTargetOverride(soil.Value) ?? primaryTarget ?? Player; var soilAction = soil.As() switch { SacredSoilOption.Use or SacredSoilOption.UseEx => SCH.AID.SacredSoil, _ => default }; if (soilAction != default) - QueueOGCD(soilAction, ResolveTargetOverride(soil.Value) ?? primaryTarget ?? Player); + Hints.ActionsToExecute.Push(ActionID.MakeSpell(soilAction), soilTarget, soil.Priority(), soil.Value.ExpireIn, targetPos: soilTarget.PosRot.XYZ()); var deploy = strategy.Option(Track.DeploymentTactics); if (deploy.As() != DeployOption.None) - QueueOGCD(SCH.AID.DeploymentTactics, Player); - + Hints.ActionsToExecute.Push(ActionID.MakeSpell(SCH.AID.DeploymentTactics), Player, deploy.Priority(), deploy.Value.ExpireIn); var pact = strategy.Option(Track.Aetherpact); var pactStrat = pact.As(); @@ -124,64 +118,13 @@ public override void Execute(StrategyValues strategy, ref Actor? primaryTarget, if (pactStrat != AetherpactOption.None) { if (pactStrat == AetherpactOption.Use && !juicing) - QueueOGCD(SCH.AID.Aetherpact, pactTarget); + Hints.ActionsToExecute.Push(ActionID.MakeSpell(SCH.AID.Aetherpact), pactTarget, pact.Priority(), pact.Value.ExpireIn); if (pactStrat == AetherpactOption.End && juicing) - QueueOGCD(SCH.AID.DissolveUnion, pactTarget); + Hints.ActionsToExecute.Push(ActionID.MakeSpell(SCH.AID.DissolveUnion), pactTarget, pact.Priority(), pact.Value.ExpireIn); } var recit = strategy.Option(Track.Recitation); if (recit.As() != RecitationOption.None) - QueueOGCD(SCH.AID.Recitation, Player); - - var pet = strategy.Option(Track.Summons); - var petSummons = pet.As() switch - { - PetOption.Eos => SCH.AID.SummonEos, - PetOption.Seraph => SCH.AID.SummonSeraph, - _ => default - }; - if (petSummons != default) - QueueOGCD(petSummons, Player); - } - - #region Core Execution Helpers - public void QueueOGCD

(SCH.AID aid, Actor? target, P priority, float delay = 0) where P : Enum - => QueueOGCD(aid, target, (int)(object)priority, delay); - - public void QueueOGCD(SCH.AID aid, Actor? target, int priority = 4, float delay = 0) - { - if (priority == 0) - return; - - QueueAction(aid, target, ActionQueue.Priority.Medium + priority, delay); - } - - public bool QueueAction(SCH.AID aid, Actor? target, float priority, float delay) - { - if ((uint)(object)aid == 0) - return false; - - var def = ActionDefinitions.Instance.Spell(aid); - if (def == null) - return false; - - if (def.Range != 0 && target == null) - { - return false; - } - - Vector3 targetPos = default; - - if (def.AllowedTargets.HasFlag(ActionTargets.Area)) - { - if (def.Range == 0) - targetPos = Player.PosRot.XYZ(); - else if (target != null) - targetPos = target.PosRot.XYZ(); - } - - Hints.ActionsToExecute.Push(ActionID.MakeSpell(aid), target, priority, delay: delay, castTime: def.CastTime, targetPos: targetPos); // TODO[cast-time]: this probably needs explicit cast-time argument (adjusted by swiftcast etc) - return true; + Hints.ActionsToExecute.Push(ActionID.MakeSpell(SCH.AID.Recitation), Player, recit.Priority(), recit.Value.ExpireIn); } - #endregion } From 181eb12b3be2539148ecc6bbca4c812c5799db4b Mon Sep 17 00:00:00 2001 From: ace Date: Tue, 4 Feb 2025 14:15:24 -0800 Subject: [PATCH 09/28] hm --- BossMod/Autorotation/Utility/ClassASTUtility.cs | 4 ++-- BossMod/Autorotation/Utility/ClassNINUtility.cs | 2 +- BossMod/Autorotation/Utility/ClassSCHUtility.cs | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/BossMod/Autorotation/Utility/ClassASTUtility.cs b/BossMod/Autorotation/Utility/ClassASTUtility.cs index 01eda812e5..4361469343 100644 --- a/BossMod/Autorotation/Utility/ClassASTUtility.cs +++ b/BossMod/Autorotation/Utility/ClassASTUtility.cs @@ -33,7 +33,7 @@ public static RotationModuleDefinition Definition() res.Define(Track.EarthlyStar).As("EarthlyStar", "E.Star", 200) //AoE GCD heal, 60s CD, 10s + 10s effect duration .AddOption(StarOption.None, "None", "Do not use automatically") - .AddOption(StarOption.Use, "Earthly Star", "Use Earthly Star", 60, 10, ActionTargets.Party | ActionTargets.Self | ActionTargets.Hostile, 62) // TODO: should use ActionTargets.Area, but the Point options do not work yet + .AddOption(StarOption.Use, "Earthly Star", "Use Earthly Star", 60, 10, ActionTargets.Party | ActionTargets.Self | ActionTargets.Hostile, 62) // TODO: should use ActionTargets.Area, but the Point options do not work outside of AI yet .AddOption(StarOption.End, "Stellar Detonation", "Use Stellar Detonation", 0, 1, ActionTargets.Self, 62) .AddAssociatedActions(AST.AID.EarthlyStar, AST.AID.StellarDetonation); @@ -86,7 +86,7 @@ public override void Execute(StrategyValues strategy, ref Actor? primaryTarget, _ => default }; if (starAction != default) - Hints.ActionsToExecute.Push(ActionID.MakeSpell(starAction), starTarget, star.Priority(), star.Value.ExpireIn, targetPos: starTarget!.PosRot.XYZ()); + Hints.ActionsToExecute.Push(ActionID.MakeSpell(starAction), starTarget, star.Priority(), star.Value.ExpireIn, targetPos: starTarget.PosRot.XYZ()); //Aspected Helios full execution var heliosUp = StatusDetails(Player, AST.SID.AspectedHelios, Player.InstanceID).Left > 0.1f || StatusDetails(Player, AST.SID.HeliosConjunction, Player.InstanceID).Left > 0.1f; diff --git a/BossMod/Autorotation/Utility/ClassNINUtility.cs b/BossMod/Autorotation/Utility/ClassNINUtility.cs index 395e0b740f..667137e532 100644 --- a/BossMod/Autorotation/Utility/ClassNINUtility.cs +++ b/BossMod/Autorotation/Utility/ClassNINUtility.cs @@ -16,7 +16,7 @@ public static RotationModuleDefinition Definition() res.Define(Track.Shukuchi).As("Shukuchi", "Dash", 20) .AddOption(DashStrategy.None, "Automatic", "No use.") - .AddOption(DashStrategy.GapClose, "GapClose", "Use as gapcloser if outside melee range", 60, 0, ActionTargets.Party | ActionTargets.Hostile, 45) // TODO: should use ActionTargets.Area, but the Point options do not work yet + .AddOption(DashStrategy.GapClose, "GapClose", "Use as gapcloser if outside melee range", 60, 0, ActionTargets.Party | ActionTargets.Hostile, 45) // TODO: should use ActionTargets.Area, but the Point options do not work outside of AI yet .AddOption(DashStrategy.GapCloseHold1, "GapCloseHold1", "Use as gapcloser if outside melee range; conserves 1 charge for manual usage", 60, 0, ActionTargets.Party | ActionTargets.Hostile, 74) .AddAssociatedActions(NIN.AID.Shukuchi); diff --git a/BossMod/Autorotation/Utility/ClassSCHUtility.cs b/BossMod/Autorotation/Utility/ClassSCHUtility.cs index af7e3c08ea..c396023e37 100644 --- a/BossMod/Autorotation/Utility/ClassSCHUtility.cs +++ b/BossMod/Autorotation/Utility/ClassSCHUtility.cs @@ -30,7 +30,7 @@ public static RotationModuleDefinition Definition() res.Define(Track.SacredSoil).As("Sacred Soil", "S.Soil", 200) .AddOption(SacredSoilOption.None, "None", "Do not use automatically") - .AddOption(SacredSoilOption.Use, "Use", "Use Sacred Soil", 30, 15, ActionTargets.Party | ActionTargets.Self | ActionTargets.Hostile, 50, 77) // TODO: should use ActionTargets.Area, but the Point options do not work yet + .AddOption(SacredSoilOption.Use, "Use", "Use Sacred Soil", 30, 15, ActionTargets.Party | ActionTargets.Self | ActionTargets.Hostile, 50, 77) // TODO: should use ActionTargets.Area, but the Point options do not work outside of AI yet .AddOption(SacredSoilOption.UseEx, "UseEx", "Use Enhanced Sacred Soil", 30, 15, ActionTargets.Area, 78) .AddAssociatedActions(SCH.AID.SacredSoil); From 2c316fb394e3b1da025f7652ccc3658d292629f9 Mon Sep 17 00:00:00 2001 From: Akechi Date: Wed, 5 Feb 2025 11:14:14 -0800 Subject: [PATCH 10/28] we're gaming --- BossMod/Autorotation/Utility/ClassASTUtility.cs | 7 ++----- BossMod/Autorotation/Utility/ClassNINUtility.cs | 13 +++++-------- BossMod/Autorotation/Utility/ClassSCHUtility.cs | 5 ++--- BossMod/Data/Actor.cs | 1 + 4 files changed, 10 insertions(+), 16 deletions(-) diff --git a/BossMod/Autorotation/Utility/ClassASTUtility.cs b/BossMod/Autorotation/Utility/ClassASTUtility.cs index 4361469343..5d5ae61db5 100644 --- a/BossMod/Autorotation/Utility/ClassASTUtility.cs +++ b/BossMod/Autorotation/Utility/ClassASTUtility.cs @@ -33,7 +33,7 @@ public static RotationModuleDefinition Definition() res.Define(Track.EarthlyStar).As("EarthlyStar", "E.Star", 200) //AoE GCD heal, 60s CD, 10s + 10s effect duration .AddOption(StarOption.None, "None", "Do not use automatically") - .AddOption(StarOption.Use, "Earthly Star", "Use Earthly Star", 60, 10, ActionTargets.Party | ActionTargets.Self | ActionTargets.Hostile, 62) // TODO: should use ActionTargets.Area, but the Point options do not work outside of AI yet + .AddOption(StarOption.Use, "Earthly Star", "Use Earthly Star", 60, 10, ActionTargets.Area, 62) // TODO: should use ActionTargets.Area, but the Point options do not work outside of AI yet .AddOption(StarOption.End, "Stellar Detonation", "Use Stellar Detonation", 0, 1, ActionTargets.Self, 62) .AddAssociatedActions(AST.AID.EarthlyStar, AST.AID.StellarDetonation); @@ -75,10 +75,7 @@ public override void Execute(StrategyValues strategy, ref Actor? primaryTarget, ExecuteSimple(strategy.Option(Track.Exaltation), AST.AID.Exaltation, Player); ExecuteSimple(strategy.Option(Track.SunSign), AST.AID.SunSign, Player); - //TODO: These should work with all Area targeting options? Able to target Waymarks, Center, AbsolutePoint, etc. - //Figure out how to make compatible with WPos var star = strategy.Option(Track.EarthlyStar); - var starTarget = ResolveTargetOverride(star.Value) ?? primaryTarget ?? Player; var starAction = star.As() switch { StarOption.Use => AST.AID.EarthlyStar, @@ -86,7 +83,7 @@ public override void Execute(StrategyValues strategy, ref Actor? primaryTarget, _ => default }; if (starAction != default) - Hints.ActionsToExecute.Push(ActionID.MakeSpell(starAction), starTarget, star.Priority(), star.Value.ExpireIn, targetPos: starTarget.PosRot.XYZ()); + Hints.ActionsToExecute.Push(ActionID.MakeSpell(starAction), null, star.Priority(), star.Value.ExpireIn, targetPos: ResolveTargetLocation(star.Value).ToVec3()); //Aspected Helios full execution var heliosUp = StatusDetails(Player, AST.SID.AspectedHelios, Player.InstanceID).Left > 0.1f || StatusDetails(Player, AST.SID.HeliosConjunction, Player.InstanceID).Left > 0.1f; diff --git a/BossMod/Autorotation/Utility/ClassNINUtility.cs b/BossMod/Autorotation/Utility/ClassNINUtility.cs index 667137e532..9a020fffb4 100644 --- a/BossMod/Autorotation/Utility/ClassNINUtility.cs +++ b/BossMod/Autorotation/Utility/ClassNINUtility.cs @@ -16,8 +16,8 @@ public static RotationModuleDefinition Definition() res.Define(Track.Shukuchi).As("Shukuchi", "Dash", 20) .AddOption(DashStrategy.None, "Automatic", "No use.") - .AddOption(DashStrategy.GapClose, "GapClose", "Use as gapcloser if outside melee range", 60, 0, ActionTargets.Party | ActionTargets.Hostile, 45) // TODO: should use ActionTargets.Area, but the Point options do not work outside of AI yet - .AddOption(DashStrategy.GapCloseHold1, "GapCloseHold1", "Use as gapcloser if outside melee range; conserves 1 charge for manual usage", 60, 0, ActionTargets.Party | ActionTargets.Hostile, 74) + .AddOption(DashStrategy.GapClose, "GapClose", "Use as gapcloser if outside melee range", 60, 0, ActionTargets.Area, 45) + .AddOption(DashStrategy.GapCloseHold1, "GapCloseHold1", "Use as gapcloser if outside melee range; conserves 1 charge for manual usage", 60, 0, ActionTargets.Area, 74) .AddAssociatedActions(NIN.AID.Shukuchi); return res; @@ -29,12 +29,9 @@ public override void Execute(StrategyValues strategy, ref Actor? primaryTarget, ExecuteSimple(strategy.Option(Track.ShadeShift), NIN.AID.ShadeShift, Player); // TODO: revise, this doesn't look correct (shukuchi is area targeted, so it should use that; probably should expose options to use regardless of melee distance...) - //These should work with all Area targeting options? Able to target Waymarks, Center, AbsolutePoint, etc. - //Figure out how to make compatible with WPos var dash = strategy.Option(Track.Shukuchi); var dashStrategy = strategy.Option(Track.Shukuchi).As(); - var dashTarget = ResolveTargetOverride(dash.Value) ?? primaryTarget; //Smart-Targeting - var distance = Player.DistanceToHitbox(dashTarget); + var distance = Player.DistanceToPoint(ResolveTargetLocation(dash.Value)); var cd = World.Client.Cooldowns[ActionDefinitions.Instance.Spell(NIN.AID.Shukuchi)!.MainCooldownGroup].Remaining; var shouldDash = dashStrategy switch { @@ -43,7 +40,7 @@ public override void Execute(StrategyValues strategy, ref Actor? primaryTarget, DashStrategy.GapCloseHold1 => distance is > 3f and <= 20f && cd < 0.6f, _ => false, }; - if (shouldDash && dashTarget != null) - Hints.ActionsToExecute.Push(ActionID.MakeSpell(NIN.AID.Shukuchi), null, dash.Priority(), dash.Value.ExpireIn, targetPos: dashTarget.PosRot.XYZ()); + if (shouldDash) + Hints.ActionsToExecute.Push(ActionID.MakeSpell(NIN.AID.Shukuchi), null, dash.Priority(), dash.Value.ExpireIn, targetPos: ResolveTargetLocation(dash.Value).ToVec3()); } } diff --git a/BossMod/Autorotation/Utility/ClassSCHUtility.cs b/BossMod/Autorotation/Utility/ClassSCHUtility.cs index c396023e37..030a9639c2 100644 --- a/BossMod/Autorotation/Utility/ClassSCHUtility.cs +++ b/BossMod/Autorotation/Utility/ClassSCHUtility.cs @@ -30,7 +30,7 @@ public static RotationModuleDefinition Definition() res.Define(Track.SacredSoil).As("Sacred Soil", "S.Soil", 200) .AddOption(SacredSoilOption.None, "None", "Do not use automatically") - .AddOption(SacredSoilOption.Use, "Use", "Use Sacred Soil", 30, 15, ActionTargets.Party | ActionTargets.Self | ActionTargets.Hostile, 50, 77) // TODO: should use ActionTargets.Area, but the Point options do not work outside of AI yet + .AddOption(SacredSoilOption.Use, "Use", "Use Sacred Soil", 30, 15, ActionTargets.Area, 50, 77) .AddOption(SacredSoilOption.UseEx, "UseEx", "Use Enhanced Sacred Soil", 30, 15, ActionTargets.Area, 78) .AddAssociatedActions(SCH.AID.SacredSoil); @@ -98,14 +98,13 @@ public override void Execute(StrategyValues strategy, ref Actor? primaryTarget, Hints.ActionsToExecute.Push(ActionID.MakeSpell(succAction), Player, succ.Priority(), succ.Value.ExpireIn, castTime: 2); // TODO[cast-time]: adjustment (swiftcast etc) var soil = strategy.Option(Track.SacredSoil); - var soilTarget = ResolveTargetOverride(soil.Value) ?? primaryTarget ?? Player; var soilAction = soil.As() switch { SacredSoilOption.Use or SacredSoilOption.UseEx => SCH.AID.SacredSoil, _ => default }; if (soilAction != default) - Hints.ActionsToExecute.Push(ActionID.MakeSpell(soilAction), soilTarget, soil.Priority(), soil.Value.ExpireIn, targetPos: soilTarget.PosRot.XYZ()); + Hints.ActionsToExecute.Push(ActionID.MakeSpell(soilAction), null, soil.Priority(), soil.Value.ExpireIn, targetPos: ResolveTargetLocation(soil.Value).ToVec3()); var deploy = strategy.Option(Track.DeploymentTactics); if (deploy.As() != DeployOption.None) diff --git a/BossMod/Data/Actor.cs b/BossMod/Data/Actor.cs index aa4e7f06fa..2e63cf7ddf 100644 --- a/BossMod/Data/Actor.cs +++ b/BossMod/Data/Actor.cs @@ -168,6 +168,7 @@ public sealed class Actor(ulong instanceID, uint oid, int spawnIndex, string nam public Angle AngleTo(Actor other) => Angle.FromDirection(other.Position - Position); public float DistanceToHitbox(Actor? other) => other == null ? float.MaxValue : (other.Position - Position).Length() - other.HitboxRadius - HitboxRadius; + public float DistanceToPoint(WPos pos) => (pos - Position).Length(); public override string ToString() => $"{OID:X} '{Name}' <{InstanceID:X}>"; } From 147d50baab95e1e78d04b5ad878449c2123a784a Mon Sep 17 00:00:00 2001 From: Akechi Date: Wed, 5 Feb 2025 11:17:07 -0800 Subject: [PATCH 11/28] . --- BossMod/Autorotation/Utility/ClassASTUtility.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/BossMod/Autorotation/Utility/ClassASTUtility.cs b/BossMod/Autorotation/Utility/ClassASTUtility.cs index 5d5ae61db5..715a741471 100644 --- a/BossMod/Autorotation/Utility/ClassASTUtility.cs +++ b/BossMod/Autorotation/Utility/ClassASTUtility.cs @@ -33,7 +33,7 @@ public static RotationModuleDefinition Definition() res.Define(Track.EarthlyStar).As("EarthlyStar", "E.Star", 200) //AoE GCD heal, 60s CD, 10s + 10s effect duration .AddOption(StarOption.None, "None", "Do not use automatically") - .AddOption(StarOption.Use, "Earthly Star", "Use Earthly Star", 60, 10, ActionTargets.Area, 62) // TODO: should use ActionTargets.Area, but the Point options do not work outside of AI yet + .AddOption(StarOption.Use, "Earthly Star", "Use Earthly Star", 60, 10, ActionTargets.Area, 62) .AddOption(StarOption.End, "Stellar Detonation", "Use Stellar Detonation", 0, 1, ActionTargets.Self, 62) .AddAssociatedActions(AST.AID.EarthlyStar, AST.AID.StellarDetonation); From 3fc7b5c1f2c73064b2c7f445ef8981a02fb744d1 Mon Sep 17 00:00:00 2001 From: Akechi Date: Wed, 5 Feb 2025 12:14:38 -0800 Subject: [PATCH 12/28] `Player.Pos.Y` --- BossMod/Autorotation/Utility/ClassASTUtility.cs | 2 +- BossMod/Autorotation/Utility/ClassNINUtility.cs | 2 +- BossMod/Autorotation/Utility/ClassSCHUtility.cs | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/BossMod/Autorotation/Utility/ClassASTUtility.cs b/BossMod/Autorotation/Utility/ClassASTUtility.cs index 715a741471..c8e7b4ff1e 100644 --- a/BossMod/Autorotation/Utility/ClassASTUtility.cs +++ b/BossMod/Autorotation/Utility/ClassASTUtility.cs @@ -83,7 +83,7 @@ public override void Execute(StrategyValues strategy, ref Actor? primaryTarget, _ => default }; if (starAction != default) - Hints.ActionsToExecute.Push(ActionID.MakeSpell(starAction), null, star.Priority(), star.Value.ExpireIn, targetPos: ResolveTargetLocation(star.Value).ToVec3()); + Hints.ActionsToExecute.Push(ActionID.MakeSpell(starAction), null, star.Priority(), star.Value.ExpireIn, targetPos: ResolveTargetLocation(star.Value).ToVec3(Player.PosRot.Y)); //Aspected Helios full execution var heliosUp = StatusDetails(Player, AST.SID.AspectedHelios, Player.InstanceID).Left > 0.1f || StatusDetails(Player, AST.SID.HeliosConjunction, Player.InstanceID).Left > 0.1f; diff --git a/BossMod/Autorotation/Utility/ClassNINUtility.cs b/BossMod/Autorotation/Utility/ClassNINUtility.cs index 9a020fffb4..4b893a82e0 100644 --- a/BossMod/Autorotation/Utility/ClassNINUtility.cs +++ b/BossMod/Autorotation/Utility/ClassNINUtility.cs @@ -41,6 +41,6 @@ public override void Execute(StrategyValues strategy, ref Actor? primaryTarget, _ => false, }; if (shouldDash) - Hints.ActionsToExecute.Push(ActionID.MakeSpell(NIN.AID.Shukuchi), null, dash.Priority(), dash.Value.ExpireIn, targetPos: ResolveTargetLocation(dash.Value).ToVec3()); + Hints.ActionsToExecute.Push(ActionID.MakeSpell(NIN.AID.Shukuchi), null, dash.Priority(), dash.Value.ExpireIn, targetPos: ResolveTargetLocation(dash.Value).ToVec3(Player.PosRot.Y)); } } diff --git a/BossMod/Autorotation/Utility/ClassSCHUtility.cs b/BossMod/Autorotation/Utility/ClassSCHUtility.cs index 030a9639c2..e850fcc360 100644 --- a/BossMod/Autorotation/Utility/ClassSCHUtility.cs +++ b/BossMod/Autorotation/Utility/ClassSCHUtility.cs @@ -104,7 +104,7 @@ public override void Execute(StrategyValues strategy, ref Actor? primaryTarget, _ => default }; if (soilAction != default) - Hints.ActionsToExecute.Push(ActionID.MakeSpell(soilAction), null, soil.Priority(), soil.Value.ExpireIn, targetPos: ResolveTargetLocation(soil.Value).ToVec3()); + Hints.ActionsToExecute.Push(ActionID.MakeSpell(soilAction), null, soil.Priority(), soil.Value.ExpireIn, targetPos: ResolveTargetLocation(soil.Value).ToVec3(Player.PosRot.Y)); var deploy = strategy.Option(Track.DeploymentTactics); if (deploy.As() != DeployOption.None) From 37877d20ac565d4acae1157fa6381042270a97dc Mon Sep 17 00:00:00 2001 From: Akechi Date: Fri, 7 Feb 2025 05:44:49 -0800 Subject: [PATCH 13/28] Passage of Arms (I forgot about this) --- .../Autorotation/Utility/ClassPLDUtility.cs | 29 +++++++++++++++++-- 1 file changed, 26 insertions(+), 3 deletions(-) diff --git a/BossMod/Autorotation/Utility/ClassPLDUtility.cs b/BossMod/Autorotation/Utility/ClassPLDUtility.cs index 624bcc528b..e2ec8205e3 100644 --- a/BossMod/Autorotation/Utility/ClassPLDUtility.cs +++ b/BossMod/Autorotation/Utility/ClassPLDUtility.cs @@ -1,10 +1,13 @@ -namespace BossMod.Autorotation; +using static BossMod.Autorotation.ClassPCTUtility; + +namespace BossMod.Autorotation; public sealed class ClassPLDUtility(RotationModuleManager manager, Actor player) : RoleTankUtility(manager, player) { public enum Track { Sheltron = SharedTrack.Count, Sentinel, Cover, Bulwark, DivineVeil, PassageOfArms, HallowedGround } //What we're tracking public enum ShelOption { None, Sheltron, HolySheltron, Intervention } //Sheltron Options public enum SentOption { None, Sentinel, Guardian } //Sentinel enhancement + public enum ArmsDirection { None, CharacterForward, CharacterBackward, CameraForward, CameraBackward } public static readonly ActionID IDLimitBreak3 = ActionID.MakeSpell(PLD.AID.LastBastion); //LB public static readonly ActionID IDStanceApply = ActionID.MakeSpell(PLD.AID.IronWill); //StanceOn @@ -32,7 +35,15 @@ public static RotationModuleDefinition Definition() DefineSimpleConfig(res, Track.Cover, "Cover", "", 320, PLD.AID.Cover, 12); //120s CD, 12s duration, -50 OathGauge cost DefineSimpleConfig(res, Track.Bulwark, "Bulwark", "Bul", 450, PLD.AID.Bulwark, 10); //90s CD, 15s duration DefineSimpleConfig(res, Track.DivineVeil, "DivineVeil", "Veil", 220, PLD.AID.DivineVeil, 30); //90s CD, 30s duration - DefineSimpleConfig(res, Track.PassageOfArms, "PassageOfArms", "Arms", 470, PLD.AID.PassageOfArms, 3); //120s CD, 18s max duration + + res.Define(Track.PassageOfArms).As("PassageOfArms", "PoA", 400) //PassageOfArms definition for CD plans + .AddOption(ArmsDirection.None, "None", "Do not use automatically") + .AddOption(ArmsDirection.CharacterForward, "CharacterForward", "Faces the Forward direction relative to the Character", 120, 18, ActionTargets.Self, 70) + .AddOption(ArmsDirection.CharacterBackward, "CharacterBackward", "Faces the Backward direction relative to the Character", 120, 18, ActionTargets.Self, 70) + .AddOption(ArmsDirection.CameraForward, "CameraForward", "Faces the Forward direction relative to the Camera", 120, 18, ActionTargets.Self, 70) + .AddOption(ArmsDirection.CameraBackward, "CameraBackward", "Faces the Backward direction relative to the Camera", 120, 18, ActionTargets.Self, 70) + .AddAssociatedActions(PLD.AID.PassageOfArms); + DefineSimpleConfig(res, Track.HallowedGround, "HallowedGround", "Inv", 400, PLD.AID.HallowedGround, 10); //420s CD, 10s duration return res; @@ -44,7 +55,6 @@ public override void Execute(StrategyValues strategy, ref Actor? primaryTarget, ExecuteSimple(strategy.Option(Track.Cover), PLD.AID.Cover, primaryTarget ?? Player); //Cover execution ExecuteSimple(strategy.Option(Track.Bulwark), PLD.AID.Bulwark, Player); //Bulwark execution ExecuteSimple(strategy.Option(Track.DivineVeil), PLD.AID.DivineVeil, Player); //DivineVeil execution - ExecuteSimple(strategy.Option(Track.PassageOfArms), PLD.AID.PassageOfArms, Player); //PassageOfArms execution ExecuteSimple(strategy.Option(Track.HallowedGround), PLD.AID.HallowedGround, Player); //HallowedGround execution var shel = strategy.Option(Track.Sheltron); @@ -67,5 +77,18 @@ public override void Execute(StrategyValues strategy, ref Actor? primaryTarget, }; if (sentAction != default) Hints.ActionsToExecute.Push(ActionID.MakeSpell(sentAction), Player, sent.Priority(), sent.Value.ExpireIn); //Sentinel execution + + var poa = strategy.Option(Track.PassageOfArms); + if (poa.As() != ArmsDirection.None) + { + var angle = poa.As() switch + { + ArmsDirection.CharacterBackward => Player.Rotation + 180.Degrees(), + ArmsDirection.CameraForward => World.Client.CameraAzimuth + 180.Degrees(), + ArmsDirection.CameraBackward => World.Client.CameraAzimuth, + _ => Player.Rotation + }; + Hints.ActionsToExecute.Push(ActionID.MakeSpell(PLD.AID.PassageOfArms), Player, poa.Priority(), poa.Value.ExpireIn, facingAngle: angle); + } } } From 0b2aae2f14a1a139299df7a8e75f606c11973e1a Mon Sep 17 00:00:00 2001 From: ace Date: Sun, 9 Feb 2025 14:42:03 -0800 Subject: [PATCH 14/28] small improvements --- .../Autorotation/Standard/akechi/AkechiTools.cs | 15 ++++++++++----- .../Autorotation/Standard/akechi/DPS/AkechiBLM.cs | 6 +++++- .../Standard/akechi/Tank/AkechiDRK.cs | 10 ++++++---- .../Standard/akechi/Tank/AkechiGNB.cs | 3 ++- .../Standard/akechi/Tank/AkechiPLD.cs | 3 ++- .../Standard/akechi/Tank/AkechiWAR.cs | 3 ++- 6 files changed, 27 insertions(+), 13 deletions(-) diff --git a/BossMod/Autorotation/Standard/akechi/AkechiTools.cs b/BossMod/Autorotation/Standard/akechi/AkechiTools.cs index 326426cb7c..2ea4d6e9c7 100644 --- a/BossMod/Autorotation/Standard/akechi/AkechiTools.cs +++ b/BossMod/Autorotation/Standard/akechi/AkechiTools.cs @@ -1152,23 +1152,28 @@ public sealed override void Execute(StrategyValues strategy, ref Actor? primaryT static class ModuleExtensions { #region Shared Definitions - ///

Defines our shared AOE (rotation) and Hold strategies. + /// Defines our shared AOE (rotation) strategies. /// The definitions of our base module's strategies. /// - Options for shared custom strategies to be used via AutoRotation or Cooldown Planner - public static RotationModuleDefinition DefineShared(this RotationModuleDefinition res) + public static RotationModuleDefinition.ConfigRef DefineAOE(this RotationModuleDefinition res) { - res.Define(SharedTrack.AOE).As("AOE", uiPriority: 300) + return res.Define(SharedTrack.AOE).As("AOE", uiPriority: 300) .AddOption(AOEStrategy.Automatic, "Auto", "Automatically execute optimal rotation based on targets", supportedTargets: ActionTargets.Hostile) .AddOption(AOEStrategy.ForceST, "ForceST", "Force-execute Single Target", supportedTargets: ActionTargets.Hostile) .AddOption(AOEStrategy.ForceAOE, "ForceAOE", "Force-execute AOE rotation", supportedTargets: ActionTargets.Hostile); + } - res.Define(SharedTrack.Hold).As("Hold", uiPriority: 290) + /// Defines our shared Hold strategies. + /// The definitions of our base module's strategies. + /// - Options for shared custom strategies to be used via AutoRotation or Cooldown Planner + public static RotationModuleDefinition.ConfigRef DefineHold(this RotationModuleDefinition res) + { + return res.Define(SharedTrack.Hold).As("Hold", uiPriority: 290) .AddOption(HoldStrategy.DontHold, "DontHold", "Allow use of all cooldowns, buffs, or gauge abilities") .AddOption(HoldStrategy.HoldCooldowns, "Hold", "Forbid use of all cooldowns only") .AddOption(HoldStrategy.HoldGauge, "HoldGauge", "Forbid use of all gauge abilities only") .AddOption(HoldStrategy.HoldBuffs, "HoldBuffs", "Forbid use of all raidbuffs or buff-related abilities only") .AddOption(HoldStrategy.HoldEverything, "HoldEverything", "Forbid use of all cooldowns, buffs, and gauge abilities"); - return res; } /// A quick and easy helper for shortcutting how we define our GCD abilities. diff --git a/BossMod/Autorotation/Standard/akechi/DPS/AkechiBLM.cs b/BossMod/Autorotation/Standard/akechi/DPS/AkechiBLM.cs index a1a0adac83..0373f55bf2 100644 --- a/BossMod/Autorotation/Standard/akechi/DPS/AkechiBLM.cs +++ b/BossMod/Autorotation/Standard/akechi/DPS/AkechiBLM.cs @@ -32,7 +32,11 @@ public static RotationModuleDefinition Definition() BitMask.Build(Class.THM, Class.BLM), //Job 100); //Level supported - res.DefineShared(); + res.DefineAOE().AddAssociatedActions( + AID.Fire1, AID.Fire2, AID.Fire3, AID.Fire4, AID.HighFire2, + AID.Blizzard1, AID.Blizzard2, AID.Blizzard3, AID.Freeze, AID.Blizzard4, AID.HighBlizzard2, + AID.Flare, AID.Despair, AID.FlareStar); + res.DefineHold(); res.Define(Track.Movement).As("Movement", uiPriority: 195) .AddOption(MovementStrategy.Allow, "Allow", "Allow the use of all appropriate abilities for movement") .AddOption(MovementStrategy.AllowNoScathe, "AllowNoScathe", "Allow the use of all appropriate abilities for movement except for Scathe") diff --git a/BossMod/Autorotation/Standard/akechi/Tank/AkechiDRK.cs b/BossMod/Autorotation/Standard/akechi/Tank/AkechiDRK.cs index 4eb5d9f139..bdea2bc8f6 100644 --- a/BossMod/Autorotation/Standard/akechi/Tank/AkechiDRK.cs +++ b/BossMod/Autorotation/Standard/akechi/Tank/AkechiDRK.cs @@ -29,14 +29,16 @@ public static RotationModuleDefinition Definition() BitMask.Build((int)Class.DRK), //Job 100); //Level supported - res.DefineShared(); + res.DefineAOE().AddAssociatedActions(AID.HardSlash, AID.SyphonStrike, AID.Souleater, AID.Unleash, AID.StalwartSoul); ; + res.DefineHold(); res.Define(Track.Blood).As("Blood", "Blood", uiPriority: 200) .AddOption(BloodStrategy.Automatic, "Automatic", "Automatically use Blood-related abilities optimally") .AddOption(BloodStrategy.OnlyBloodspiller, "Only Bloodspiller", "Uses Bloodspiller optimally as Blood spender only, regardless of targets", 0, 0, ActionTargets.Hostile, 62) .AddOption(BloodStrategy.OnlyQuietus, "Only Quietus", "Uses Quietus optimally as Blood spender only, regardless of targets", 0, 0, ActionTargets.Hostile, 64) .AddOption(BloodStrategy.ForceBloodspiller, "Force Bloodspiller", "Force use Bloodspiller ASAP", 0, 0, ActionTargets.Hostile, 62) .AddOption(BloodStrategy.ForceQuietus, "Force Quietus", "Force use Quietus ASAP", 0, 0, ActionTargets.Hostile, 64) - .AddOption(BloodStrategy.Conserve, "Conserve", "Conserves all Blood-related abilities as much as possible"); + .AddOption(BloodStrategy.Conserve, "Conserve", "Conserves all Blood-related abilities as much as possible") + .AddAssociatedActions(AID.Bloodspiller, AID.Quietus); res.Define(Track.MP).As("MP", "MP", uiPriority: 190) .AddOption(MPStrategy.Optimal, "Optimal", "Use MP actions optimally; 2 for 1 minute, 4 (or 5 if Dark Arts is active) for 2 minutes") .AddOption(MPStrategy.Auto3k, "Auto 3k", "Automatically decide best MP action to use; Uses when at 3000+ MP", 0, 0, ActionTargets.Self, 30) @@ -60,7 +62,7 @@ public static RotationModuleDefinition Definition() .AddOption(CarveStrategy.ForceCarve, "Force Carve and Spit", "Force use Carve and Spit ASAP", 60, 0, ActionTargets.Hostile, 60) .AddOption(CarveStrategy.ForceDrain, "Force Abyssal Drain", "Force use Abyssal Drain ASAP", 60, 0, ActionTargets.Hostile, 56) .AddOption(CarveStrategy.Delay, "Delay", "Delay the use of Carve and Spit for strategic reasons", 0, 0, ActionTargets.None, 56) - .AddAssociatedActions(AID.CarveAndSpit); + .AddAssociatedActions(AID.CarveAndSpit, AID.AbyssalDrain); res.Define(Track.DeliriumCombo).As("Delirium Combo", "Scarlet", uiPriority: 180) .AddOption(DeliriumComboStrategy.Automatic, "Auto", "Automatically decide when to use Delirium Combo", 0, 0, ActionTargets.Hostile, 96) .AddOption(DeliriumComboStrategy.ScarletDelirum, "Scarlet Delirium", "Force use Scarlet Delirium ASAP", 0, 0, ActionTargets.Hostile, 96) @@ -81,7 +83,7 @@ public static RotationModuleDefinition Definition() .AddOption(UnmendStrategy.Allow, "Allow", "Allow use of Unmend when out of melee range", supportedTargets: ActionTargets.Hostile) .AddOption(UnmendStrategy.Forbid, "Forbid", "Prohibit use of Unmend") .AddAssociatedActions(AID.Unmend); - res.DefineOGCD(Track.Delirium, AID.Delirium, "Delirium", "Deli.", uiPriority: 170, 60, 15, ActionTargets.Self, 35); + res.DefineOGCD(Track.Delirium, AID.Delirium, "Delirium", "Deli.", uiPriority: 170, 60, 15, ActionTargets.Self, 35).AddAssociatedActions(AID.BloodWeapon, AID.Delirium); res.DefineOGCD(Track.SaltedEarth, AID.SaltedEarth, "Salted Earth", "S.Earth", uiPriority: 140, 90, 15, ActionTargets.Self, 52); res.DefineOGCD(Track.SaltAndDarkness, AID.SaltAndDarkness, "Salt & Darkness", "Salt & D.", uiPriority: 135, 20, 0, ActionTargets.Self, 86); res.DefineOGCD(Track.LivingShadow, AID.LivingShadow, "Living Shadow", "L.Shadow", uiPriority: 175, 120, 20, ActionTargets.Self, 80); diff --git a/BossMod/Autorotation/Standard/akechi/Tank/AkechiGNB.cs b/BossMod/Autorotation/Standard/akechi/Tank/AkechiGNB.cs index e167e3b2d4..526f5d5551 100644 --- a/BossMod/Autorotation/Standard/akechi/Tank/AkechiGNB.cs +++ b/BossMod/Autorotation/Standard/akechi/Tank/AkechiGNB.cs @@ -40,7 +40,8 @@ public static RotationModuleDefinition Definition() .AddOption(AOEStrategy.ForceSTwithoutO, "Force ST without Overcap", "Force ST rotation without overcap protection", supportedTargets: ActionTargets.Hostile) .AddOption(AOEStrategy.ForceAOEwithO, "Force AOE with Overcap", "Force AOE rotation with overcap protection") .AddOption(AOEStrategy.ForceAOEwithoutO, "Force AOE without Overcap", "Force AOE rotation without overcap protection") - .AddOption(AOEStrategy.GenerateDowntime, "Generate Downtime", "Generate cartridges before downtime"); + .AddOption(AOEStrategy.GenerateDowntime, "Generate Downtime", "Generate cartridges before downtime") + .AddAssociatedActions(AID.KeenEdge, AID.BrutalShell, AID.SolidBarrel, AID.DemonSlice, AID.DemonSlaughter); res.Define(Track.Cooldowns).As("Hold", uiPriority: 190) .AddOption(CooldownStrategy.Allow, "Allow", "Allows the use of all cooldowns & buffs; will use them optimally") .AddOption(CooldownStrategy.Forbid, "Hold", "Forbids the use of all cooldowns & buffs; will not use any actions with a cooldown timer"); diff --git a/BossMod/Autorotation/Standard/akechi/Tank/AkechiPLD.cs b/BossMod/Autorotation/Standard/akechi/Tank/AkechiPLD.cs index 85b9c6458e..1ce563afbf 100644 --- a/BossMod/Autorotation/Standard/akechi/Tank/AkechiPLD.cs +++ b/BossMod/Autorotation/Standard/akechi/Tank/AkechiPLD.cs @@ -35,7 +35,8 @@ public static RotationModuleDefinition Definition() .AddOption(AOEStrategy.AutoFinishCombo, "Auto (Finish Combo)", "Auto-selects best rotation dependant on targets; Finishes combo first", supportedTargets: ActionTargets.Hostile) .AddOption(AOEStrategy.AutoBreakCombo, "Auto (Break Combo)", "Auto-selects best rotation dependant on targets; Breaks combo if needed", supportedTargets: ActionTargets.Hostile) .AddOption(AOEStrategy.ForceST, "Use AOE", "Force single-target rotation", supportedTargets: ActionTargets.Hostile) - .AddOption(AOEStrategy.ForceAOE, "Force AOE", "Force AOE rotation"); + .AddOption(AOEStrategy.ForceAOE, "Force AOE", "Force AOE rotation") + .AddAssociatedActions(AID.FastBlade, AID.RiotBlade, AID.RageOfHalone, AID.RoyalAuthority, AID.Prominence, AID.TotalEclipse); res.Define(Track.Cooldowns).As("Hold", uiPriority: 190) .AddOption(CooldownStrategy.Allow, "Allow", "Allows the use of all cooldowns & buffs; will use them optimally") .AddOption(CooldownStrategy.Forbid, "Hold", "Forbids the use of all cooldowns & buffs; will not use any actions with a cooldown timer"); diff --git a/BossMod/Autorotation/Standard/akechi/Tank/AkechiWAR.cs b/BossMod/Autorotation/Standard/akechi/Tank/AkechiWAR.cs index eb4dd4f8d3..6c4431b199 100644 --- a/BossMod/Autorotation/Standard/akechi/Tank/AkechiWAR.cs +++ b/BossMod/Autorotation/Standard/akechi/Tank/AkechiWAR.cs @@ -31,7 +31,8 @@ public static RotationModuleDefinition Definition() BitMask.Build(Class.MRD, Class.WAR), //Job 100); //Level supported - res.DefineShared(); + res.DefineAOE().AddAssociatedActions(AID.HeavySwing, AID.Maim, AID.StormEye, AID.StormPath, AID.Overpower, AID.MythrilTempest); + res.DefineHold(); res.Define(Track.Gauge).As("Gauge", "Gauge", uiPriority: 200) .AddOption(GaugeStrategy.Automatic, "Automatic", "Automatically use Gauge-related abilities optimally", minLevel: 35) .AddOption(GaugeStrategy.OnlyST, "Only ST", "Uses Inner Beast / Fell Cleave / Inner Chaos optimally as Beast Gauge spender only, regardless of targets", 0, 0, ActionTargets.Hostile, 35) From e08c1ba14c40b9741ac5aeeb59d9952346e88a83 Mon Sep 17 00:00:00 2001 From: ace Date: Sun, 9 Feb 2025 08:02:42 -0800 Subject: [PATCH 15/28] `Tank` goal zones fix --- BossMod/Autorotation/Standard/akechi/AkechiTools.cs | 2 +- .../Autorotation/Standard/akechi/Tank/AkechiDRK.cs | 4 ++-- .../Autorotation/Standard/akechi/Tank/AkechiGNB.cs | 12 ++++++------ .../Autorotation/Standard/akechi/Tank/AkechiPLD.cs | 6 +++--- .../Autorotation/Standard/akechi/Tank/AkechiWAR.cs | 4 ++-- 5 files changed, 14 insertions(+), 14 deletions(-) diff --git a/BossMod/Autorotation/Standard/akechi/AkechiTools.cs b/BossMod/Autorotation/Standard/akechi/AkechiTools.cs index 2ea4d6e9c7..8427c7882f 100644 --- a/BossMod/Autorotation/Standard/akechi/AkechiTools.cs +++ b/BossMod/Autorotation/Standard/akechi/AkechiTools.cs @@ -839,7 +839,7 @@ protected bool CanWeave(AID aid, int extraGCDs = 0, float extraFixedDelay = 0) /// /// The user's picked strategy's option Track, retrieved from module's enums and definitions. (e.g. strategy.Option(Track.NoMercy)) /// - protected Actor? TargetChoice(StrategyValues.OptionRef track) => ResolveTargetOverride(track.Value); //Resolves the target choice based on the strategy + protected Actor? TargetChoice(StrategyValues.OptionRef track) => ResolveTargetOverride(track.Value); /// Targeting function for indicating when or not AOE Circle abilities should be used based on targets nearby. /// The range of the AOE Circle ability, or radius from center of Player; this should be adjusted accordingly to user's module specific to job's abilities. diff --git a/BossMod/Autorotation/Standard/akechi/Tank/AkechiDRK.cs b/BossMod/Autorotation/Standard/akechi/Tank/AkechiDRK.cs index bdea2bc8f6..e24f4536b4 100644 --- a/BossMod/Autorotation/Standard/akechi/Tank/AkechiDRK.cs +++ b/BossMod/Autorotation/Standard/akechi/Tank/AkechiDRK.cs @@ -387,12 +387,12 @@ bloodStrat is BloodStrategy.ForceQuietus ? GCDPriority.ForcedGCD #region AI var goalST = primaryTarget?.Actor != null ? Hints.GoalSingleTarget(primaryTarget!.Actor, 3) : null; //Set goal for single target - var goalAOE = primaryTarget?.Actor != null ? Hints.GoalAOECircle(5) : null; //Set goal for AOE + var goalAOE = Hints.GoalAOECircle(3); //Set goal for AOE var goal = strategy.Option(SharedTrack.AOE).As() switch //Set goal based on AOE strategy { AOEStrategy.ForceST => goalST, //if forced single target AOEStrategy.ForceAOE => goalAOE, //if forced AOE - _ => goalST != null && goalAOE != null ? Hints.GoalCombined(goalST, goalAOE, 2) : goalAOE //otherwise, combine goals + _ => goalST != null ? Hints.GoalCombined(goalST, goalAOE, 3) : goalAOE //otherwise, combine goals }; if (goal != null) //if goal is set Hints.GoalZones.Add(goal); //add goal to zones diff --git a/BossMod/Autorotation/Standard/akechi/Tank/AkechiGNB.cs b/BossMod/Autorotation/Standard/akechi/Tank/AkechiGNB.cs index 526f5d5551..af03a1538f 100644 --- a/BossMod/Autorotation/Standard/akechi/Tank/AkechiGNB.cs +++ b/BossMod/Autorotation/Standard/akechi/Tank/AkechiGNB.cs @@ -459,14 +459,14 @@ reignStrat is ReignStrategy.ForceLion #region AI var goalST = primaryTarget?.Actor != null ? Hints.GoalSingleTarget(primaryTarget!.Actor, 3) : null; //Set goal for single target - var goalAOE = primaryTarget?.Actor != null ? Hints.GoalAOECircle(5) : null; //Set goal for AOE - var goal = AOEStrategy switch //Set goal based on AOE strategy + var goalAOE = Hints.GoalAOECircle(3); //Set goal for AOE + var goal = strategy.Option(Track.AOE).As() switch //Set goal based on AOE strategy { + AOEStrategy.ForceSTwithO => goalST, //if forced single target AOEStrategy.ForceSTwithoutO => goalST, //if forced single target - AOEStrategy.ForceSTwithO => goalST, //if forced 123 combo - AOEStrategy.ForceAOEwithoutO => goalAOE, //if forced buffs combo - AOEStrategy.ForceAOEwithO => goalAOE, //if forced AOE action - _ => goalST != null && goalAOE != null ? Hints.GoalCombined(goalST, goalAOE, 2) : goalAOE //otherwise, combine goals + AOEStrategy.ForceAOEwithO => goalAOE, //if forced single target + AOEStrategy.ForceAOEwithoutO => goalAOE, //if forced single target + _ => goalST != null ? Hints.GoalCombined(goalST, goalAOE, 3) : goalAOE //otherwise, combine goals }; if (goal != null) //if goal is set Hints.GoalZones.Add(goal); //add goal to zones diff --git a/BossMod/Autorotation/Standard/akechi/Tank/AkechiPLD.cs b/BossMod/Autorotation/Standard/akechi/Tank/AkechiPLD.cs index 1ce563afbf..d3e671a9b7 100644 --- a/BossMod/Autorotation/Standard/akechi/Tank/AkechiPLD.cs +++ b/BossMod/Autorotation/Standard/akechi/Tank/AkechiPLD.cs @@ -386,12 +386,12 @@ gbStrat is GCDStrategy.Force #region AI var goalST = primaryTarget?.Actor != null ? Hints.GoalSingleTarget(primaryTarget!.Actor, 3) : null; //Set goal for single target - var goalAOE = primaryTarget?.Actor != null ? Hints.GoalAOECircle(5) : null; //Set goal for AOE + var goalAOE = Hints.GoalAOECircle(3); //Set goal for AOE var goal = strategy.Option(Track.AOE).As() switch //Set goal based on AOE strategy { AOEStrategy.ForceST => goalST, //if forced single target - AOEStrategy.ForceAOE => goalAOE, //if forced AOE - _ => goalST != null && goalAOE != null ? Hints.GoalCombined(goalST, goalAOE, 2) : goalAOE //otherwise, combine goals + AOEStrategy.ForceAOE => goalAOE, //if forced single target + _ => goalST != null ? Hints.GoalCombined(goalST, goalAOE, 3) : goalAOE //otherwise, combine goals }; if (goal != null) //if goal is set Hints.GoalZones.Add(goal); //add goal to zones diff --git a/BossMod/Autorotation/Standard/akechi/Tank/AkechiWAR.cs b/BossMod/Autorotation/Standard/akechi/Tank/AkechiWAR.cs index 6c4431b199..0c7189b4fc 100644 --- a/BossMod/Autorotation/Standard/akechi/Tank/AkechiWAR.cs +++ b/BossMod/Autorotation/Standard/akechi/Tank/AkechiWAR.cs @@ -490,12 +490,12 @@ bgStrat is GaugeStrategy.ForceAOE #region AI var goalST = primaryTarget?.Actor != null ? Hints.GoalSingleTarget(primaryTarget!.Actor, 3) : null; //Set goal for single target - var goalAOE = primaryTarget?.Actor != null ? Hints.GoalAOECircle(5) : null; //Set goal for AOE + var goalAOE = Hints.GoalAOECircle(3); //Set goal for AOE var goal = strategy.Option(SharedTrack.AOE).As() switch //Set goal based on AOE strategy { AOEStrategy.ForceST => goalST, //if forced single target AOEStrategy.ForceAOE => goalAOE, //if forced AOE - _ => goalST != null && goalAOE != null ? Hints.GoalCombined(goalST, goalAOE, 2) : goalAOE //otherwise, combine goals + _ => goalST != null ? Hints.GoalCombined(goalST, goalAOE, 3) : goalAOE //otherwise, combine goals }; if (goal != null) //if goal is set Hints.GoalZones.Add(goal); //add goal to zones From 3b35c527d8d485e5b4ccdcbb7982743f8bd3d242 Mon Sep 17 00:00:00 2001 From: ace Date: Sun, 9 Feb 2025 14:52:47 -0800 Subject: [PATCH 16/28] forgot these --- BossMod/Autorotation/Standard/akechi/Tank/AkechiDRK.cs | 2 +- BossMod/Autorotation/Standard/akechi/Tank/AkechiWAR.cs | 4 +++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/BossMod/Autorotation/Standard/akechi/Tank/AkechiDRK.cs b/BossMod/Autorotation/Standard/akechi/Tank/AkechiDRK.cs index e24f4536b4..77df668606 100644 --- a/BossMod/Autorotation/Standard/akechi/Tank/AkechiDRK.cs +++ b/BossMod/Autorotation/Standard/akechi/Tank/AkechiDRK.cs @@ -29,7 +29,7 @@ public static RotationModuleDefinition Definition() BitMask.Build((int)Class.DRK), //Job 100); //Level supported - res.DefineAOE().AddAssociatedActions(AID.HardSlash, AID.SyphonStrike, AID.Souleater, AID.Unleash, AID.StalwartSoul); ; + res.DefineAOE().AddAssociatedActions(AID.HardSlash, AID.SyphonStrike, AID.Souleater, AID.Unleash, AID.StalwartSoul); res.DefineHold(); res.Define(Track.Blood).As("Blood", "Blood", uiPriority: 200) .AddOption(BloodStrategy.Automatic, "Automatic", "Automatically use Blood-related abilities optimally") diff --git a/BossMod/Autorotation/Standard/akechi/Tank/AkechiWAR.cs b/BossMod/Autorotation/Standard/akechi/Tank/AkechiWAR.cs index 0c7189b4fc..1f2ab1dbcf 100644 --- a/BossMod/Autorotation/Standard/akechi/Tank/AkechiWAR.cs +++ b/BossMod/Autorotation/Standard/akechi/Tank/AkechiWAR.cs @@ -239,7 +239,9 @@ public bool IsRiskingGauge() if (ComboLastMove is AID.HeavySwing) return true; } - + if (NascentChaos.IsActive && //if NC is active + InnerRelease.CD > 5) //and IR is not imminent + return true; return false; } #endregion From b0896b16292833c18ed510537105a46f6d653fe3 Mon Sep 17 00:00:00 2001 From: ace Date: Sun, 9 Feb 2025 15:34:32 -0800 Subject: [PATCH 17/28] even more GNB options & optis --- .../Standard/akechi/Tank/AkechiGNB.cs | 128 +++++++++++------- 1 file changed, 77 insertions(+), 51 deletions(-) diff --git a/BossMod/Autorotation/Standard/akechi/Tank/AkechiGNB.cs b/BossMod/Autorotation/Standard/akechi/Tank/AkechiGNB.cs index af03a1538f..608eac2593 100644 --- a/BossMod/Autorotation/Standard/akechi/Tank/AkechiGNB.cs +++ b/BossMod/Autorotation/Standard/akechi/Tank/AkechiGNB.cs @@ -12,14 +12,15 @@ public sealed class AkechiGNB(RotationModuleManager manager, Actor player) : Ake public enum Track { AOE, Cooldowns, Cartridges, Potion, LightningShot, NoMercy, SonicBreak, GnashingFang, Reign, Bloodfest, DoubleDown, Zone, BowShock } public enum AOEStrategy { AutoFinishCombo, AutoBreakCombo, ForceSTwithO, ForceSTwithoutO, ForceAOEwithO, ForceAOEwithoutO, GenerateDowntime } public enum CooldownStrategy { Allow, Forbid } - public enum CartridgeStrategy { Automatic, OnlyBS, OnlyFC, ForceBS, ForceFC, Conserve } + public enum CartridgeStrategy { Automatic, OnlyBS, OnlyFC, ForceBS, ForceBS1, ForceBS2, ForceBS3, ForceFC, ForceFC1, ForceFC2, ForceFC3, Conserve } public enum PotionStrategy { Manual, AlignWithRaidBuffs, Immediate } public enum LightningShotStrategy { OpenerFar, OpenerForce, Force, Allow, Forbid } public enum NoMercyStrategy { Automatic, Force, ForceW, ForceQW, Force1, Force1W, Force1QW, Force2, Force2W, Force2QW, Force3, Force3W, Force3QW, Delay } public enum SonicBreakStrategy { Automatic, Force, Early, Late, Delay } - public enum GnashingStrategy { Automatic, ForceGnash, ForceClaw, ForceTalon, Delay } + public enum GnashingStrategy { Automatic, ForceGnash, ForceGnash1, ForceGnash2, ForceGnash3, ForceClaw, ForceTalon, Delay } public enum ReignStrategy { Automatic, ForceReign, ForceNoble, ForceLion, Delay } public enum BloodfestStrategy { Automatic, Force, ForceW, Force0, Force0W, Delay } + public enum DoubleDownStrategy { Automatic, Force, Force1, Force2, Force3, Delay } #endregion #region Module Definitions @@ -49,9 +50,15 @@ public static RotationModuleDefinition Definition() .AddOption(CartridgeStrategy.Automatic, "Automatic", "Automatically decide when to use cartridges; uses them optimally") .AddOption(CartridgeStrategy.OnlyBS, "Only Burst Strike", "Uses Burst Strike optimally as cartridge spender only, regardless of targets", 0, 0, ActionTargets.Hostile, 30) .AddOption(CartridgeStrategy.OnlyFC, "Only Fated Circle", "Uses Fated Circle optimally as cartridge spender only, regardless of targets", 0, 0, ActionTargets.Hostile, 72) - .AddOption(CartridgeStrategy.ForceBS, "Force Burst Strike", "Force use of Burst Strike; consumes 1 cartridge", 0, 0, ActionTargets.Hostile, 30) - .AddOption(CartridgeStrategy.ForceFC, "Force Fated Circle", "Force use of Fated Circle; consumes 1 cartridge", 0, 0, ActionTargets.Hostile, 72) - .AddOption(CartridgeStrategy.Conserve, "Conserve", "Prohibit use of all cartridge-related abilities; will not use any of these actions listed above") + .AddOption(CartridgeStrategy.ForceBS, "Force Burst Strike", "Force use of Burst Strike regardless of cartridge count", 0, 0, ActionTargets.Hostile, 30) + .AddOption(CartridgeStrategy.ForceBS1, "Force Burst Strike (1 cart)", "Force use of Burst Strike when only 1 cartridge is available", 0, 0, ActionTargets.Hostile, 30) + .AddOption(CartridgeStrategy.ForceBS2, "Force Burst Strike (2 cart)", "Force use of Burst Strike when only 2 cartridges are available", 0, 0, ActionTargets.Hostile, 30) + .AddOption(CartridgeStrategy.ForceBS3, "Force Burst Strike (3 cart)", "Force use of Burst Strike when only 3 cartridges are available", 0, 0, ActionTargets.Hostile, 30) + .AddOption(CartridgeStrategy.ForceFC, "Force Fated Circle", "Force use of Fated Circle when any cartridges are available", 0, 0, ActionTargets.Hostile, 72) + .AddOption(CartridgeStrategy.ForceFC1, "Force Fated Circle (1 cart)", "Force use of Fated Circle when only 1 cartridge is available", 0, 0, ActionTargets.Hostile, 72) + .AddOption(CartridgeStrategy.ForceFC2, "Force Fated Circle (2 cart)", "Force use of Fated Circle when only 2 cartridges are available", 0, 0, ActionTargets.Hostile, 72) + .AddOption(CartridgeStrategy.ForceFC3, "Force Fated Circle (3 cart)", "Force use of Fated Circle when only 3 cartridges are available", 0, 0, ActionTargets.Hostile, 72) + .AddOption(CartridgeStrategy.Conserve, "Conserve", "Forbid use of Burst Strike & Fated Circle", 0, 0, ActionTargets.None, 30) .AddAssociatedActions(AID.BurstStrike, AID.FatedCircle); res.Define(Track.Potion).As("Potion", uiPriority: 20) .AddOption(PotionStrategy.Manual, "Manual", "Do not use automatically") @@ -70,15 +77,15 @@ public static RotationModuleDefinition Definition() .AddOption(NoMercyStrategy.Force, "Force", "Force use of No Mercy, regardless of weaving", 60, 20, ActionTargets.Self, 2) .AddOption(NoMercyStrategy.ForceW, "Force (Weave)", "Force use of No Mercy in next possible weave slot", 60, 20, ActionTargets.Self, 2) .AddOption(NoMercyStrategy.ForceQW, "Force (Q.Weave)", "Force use of No Mercy in next possible last second weave slot", 60, 20, ActionTargets.Self, 2) - .AddOption(NoMercyStrategy.Force1, "Force (1 cart)", "Force use of No Mercy when 1 cartridge is available, regardless of weaving", 60, 20, ActionTargets.Self, 2) - .AddOption(NoMercyStrategy.Force1W, "Force (1 cart, Weave)", "Force use of No Mercy when 1 cartridge is available & in next weave slot", 60, 20, ActionTargets.Self, 2) - .AddOption(NoMercyStrategy.Force1QW, "Force (1 cart, Q.Weave)", "Force use of No Mercy when 1 cartridge is available & in next possible last-second weave slot", 60, 20, ActionTargets.Self, 2) - .AddOption(NoMercyStrategy.Force2, "Force (2 carts)", "Force use of No Mercy when 2 cartridges are available, regardless of weaving", 60, 20, ActionTargets.Self, 2) - .AddOption(NoMercyStrategy.Force2W, "Force (2 carts, Weave)", "Force use of No Mercy when 2 cartridges are available & in next possible weave slot", 60, 20, ActionTargets.Self, 2) - .AddOption(NoMercyStrategy.Force2QW, "Force (2 carts, Q.Weave)", "Force use of No Mercy when 2 cartridges are available & in next possible last-second weave slot", 60, 20, ActionTargets.Self, 2) - .AddOption(NoMercyStrategy.Force3, "Force (3 carts)", "Force use of No Mercy when 3 cartridges are available, regardless of weaving", 60, 20, ActionTargets.Self, 2) - .AddOption(NoMercyStrategy.Force3W, "Force (3 carts, Weave)", "Force use of No Mercy when 3 cartridges are available & in next possible weave slot", 60, 20, ActionTargets.Self, 2) - .AddOption(NoMercyStrategy.Force3QW, "Force (3 carts, Q.Weave)", "Force use of No Mercy when 3 cartridges are available & in next possible last-second weave slot", 60, 20, ActionTargets.Self, 2) + .AddOption(NoMercyStrategy.Force1, "Force (1 cart)", "Force use of No Mercy when only 1 cartridge is available, regardless of weaving", 60, 20, ActionTargets.Self, 2) + .AddOption(NoMercyStrategy.Force1W, "Force (1 cart, Weave)", "Force use of No Mercy when only 1 cartridge is available & in next weave slot", 60, 20, ActionTargets.Self, 2) + .AddOption(NoMercyStrategy.Force1QW, "Force (1 cart, Q.Weave)", "Force use of No Mercy when only 1 cartridge is available & in next possible last-second weave slot", 60, 20, ActionTargets.Self, 2) + .AddOption(NoMercyStrategy.Force2, "Force (2 carts)", "Force use of No Mercy when only 2 cartridges are available, regardless of weaving", 60, 20, ActionTargets.Self, 2) + .AddOption(NoMercyStrategy.Force2W, "Force (2 carts, Weave)", "Force use of No Mercy when only 2 cartridges are available & in next possible weave slot", 60, 20, ActionTargets.Self, 2) + .AddOption(NoMercyStrategy.Force2QW, "Force (2 carts, Q.Weave)", "Force use of No Mercy when only 2 cartridges are available & in next possible last-second weave slot", 60, 20, ActionTargets.Self, 2) + .AddOption(NoMercyStrategy.Force3, "Force (3 carts)", "Force use of No Mercy when only 3 cartridges are available, regardless of weaving", 60, 20, ActionTargets.Self, 2) + .AddOption(NoMercyStrategy.Force3W, "Force (3 carts, Weave)", "Force use of No Mercy when only 3 cartridges are available & in next possible weave slot", 60, 20, ActionTargets.Self, 2) + .AddOption(NoMercyStrategy.Force3QW, "Force (3 carts, Q.Weave)", "Force use of No Mercy when only 3 cartridges are available & in next possible last-second weave slot", 60, 20, ActionTargets.Self, 2) .AddOption(NoMercyStrategy.Delay, "Delay", "Delay use of No Mercy", 0, 0, ActionTargets.None, 2) .AddAssociatedActions(AID.NoMercy); res.Define(Track.SonicBreak).As("Sonic Break", "S.Break", uiPriority: 150) @@ -90,9 +97,12 @@ public static RotationModuleDefinition Definition() .AddAssociatedActions(AID.SonicBreak); res.Define(Track.GnashingFang).As("Gnashing Fang", "G.Fang", uiPriority: 160) .AddOption(GnashingStrategy.Automatic, "Auto", "Normal use of Gnashing Fang") - .AddOption(GnashingStrategy.ForceGnash, "Force", "Force use of Gnashing Fang (Step 1)", 30, 0, ActionTargets.Hostile, 60) - .AddOption(GnashingStrategy.ForceClaw, "Force", "Force use of Savage Claw (Step 2)", 0, 0, ActionTargets.Hostile, 60) - .AddOption(GnashingStrategy.ForceTalon, "Force", "Force use of Wicked Talon (Step 3)", 0, 0, ActionTargets.Hostile, 60) + .AddOption(GnashingStrategy.ForceGnash, "Force", "Force use of Gnashing Fang", 30, 0, ActionTargets.Hostile, 60) + .AddOption(GnashingStrategy.ForceGnash1, "Force (1 cart)", "Force use of Gnashing Fang when only 1 cartridge is available", 30, 0, ActionTargets.Hostile, 60) + .AddOption(GnashingStrategy.ForceGnash2, "Force (2 carts)", "Force use of Gnashing Fang when only 2 cartridges are available", 30, 0, ActionTargets.Hostile, 60) + .AddOption(GnashingStrategy.ForceGnash3, "Force (3 carts)", "Force use of Gnashing Fang when only 3 cartridges are available", 30, 0, ActionTargets.Hostile, 60) + .AddOption(GnashingStrategy.ForceClaw, "Force Savage Claw", "Force use of Savage Claw", 0, 0, ActionTargets.Hostile, 60) + .AddOption(GnashingStrategy.ForceTalon, "Force Talon", "Force use of Wicked Talon", 0, 0, ActionTargets.Hostile, 60) .AddOption(GnashingStrategy.Delay, "Delay", "Delay use of Gnashing Fang", 0, 0, ActionTargets.None, 60) .AddAssociatedActions(AID.GnashingFang, AID.SavageClaw, AID.WickedTalon); res.Define(Track.Reign).As("Reign of Beasts", "Reign", uiPriority: 160) @@ -110,7 +120,15 @@ public static RotationModuleDefinition Definition() .AddOption(BloodfestStrategy.Force0W, "Force (0 cart, Weave)", "Force use of Bloodfest only if empty on cartridges & in next possible weave slot", 120, 0, ActionTargets.Hostile, 80) .AddOption(BloodfestStrategy.Delay, "Delay", "Delay use of Bloodfest", 0, 0, ActionTargets.None, 80) .AddAssociatedActions(AID.Bloodfest); - res.DefineGCD(Track.DoubleDown, AID.DoubleDown, "DoubleDown", "D.Down", uiPriority: 160, 60, 0, ActionTargets.Hostile, 90); + res.Define(Track.DoubleDown).As("DoubleDown", "D.Down", uiPriority: 160) + .AddOption(DoubleDownStrategy.Automatic, "Automatic", "Normal use of Double Down") + .AddOption(DoubleDownStrategy.Force, "Force Double Down", "Force use of Double Down regardless of cartridge count", 60, 0, ActionTargets.Hostile, 90) + .AddOption(DoubleDownStrategy.Force1, "Force Double Down (1 cart)", "Force use of Double Down when only 1 cartridge is available", 60, 0, ActionTargets.Hostile, 90) + .AddOption(DoubleDownStrategy.Force2, "Force Double Down (2 cart)", "Force use of Double Down when only 2 cartridges are available", 60, 0, ActionTargets.Hostile, 90) + .AddOption(DoubleDownStrategy.Force3, "Force Double Down (3 cart)", "Force use of Double Down when only 3 cartridges are available", 60, 0, ActionTargets.Hostile, 90) + .AddOption(DoubleDownStrategy.Delay, "Delay", "Delay use of Double Down", 0, 0, ActionTargets.None, 90) + .AddAssociatedActions(AID.DoubleDown); + res.DefineOGCD(Track.Zone, AID.DangerZone, "Zone", "Zone", uiPriority: 150, 30, 0, ActionTargets.Hostile, 18).AddAssociatedActions(AID.BlastingZone, AID.DangerZone); res.DefineOGCD(Track.BowShock, AID.BowShock, "BowShock", "B.Shock", uiPriority: 150, 60, 15, ActionTargets.Self, 62); @@ -123,24 +141,25 @@ public enum GCDPriority { None = 0, Standard = 100, - ForcedCombo = 499, - Gauge = 500, - Reign = 525, - comboNeed = 550, - GF23 = 575, + Gauge = 400, + ForcedCombo = 425, + Reign = 450, + comboNeed = 500, + GF23 = 550, SonicBreak = 600, - DoubleDown = 675, + DoubleDown = 650, GF1 = 700, + Only1Ammo = 750, ForcedGCD = 900, } public enum OGCDPriority { None = 0, - Continuation = 500, - Zone = 550, - BowShock = 600, - Bloodfest = 700, - NoMercy = 875, + Continuation = 400, + Zone = 450, + BowShock = 500, + Bloodfest = 600, + NoMercy = 650, Potion = 900, ForcedOGCD = 1100, //Enough to put it past CDPlanner's "Automatic" priority, which is really only Medium priority } @@ -239,7 +258,7 @@ public override void Execution(StrategyValues strategy, Enemy? primaryTarget) var bf = strategy.Option(Track.Bloodfest); //Bloodfest track var bfStrat = bf.As(); //Bloodfest strategy var dd = strategy.Option(Track.DoubleDown); //Double Down track - var ddStrat = dd.As(); //Double Down strategy + var ddStrat = dd.As(); //Double Down strategy var gf = strategy.Option(Track.GnashingFang); //Gnashing Fang track var gfStrat = gf.As(); //Gnashing Fang strategy var reign = strategy.Option(Track.Reign); //Reign of Beasts track @@ -395,12 +414,13 @@ reignStrat is ReignStrategy.ForceReign if (ShouldUseDoubleDown(ddStrat, primaryTarget?.Actor)) QueueGCD(AID.DoubleDown, primaryTarget?.Actor, - ddStrat is GCDStrategy.Force || Ammo == 1 - ? GCDPriority.ForcedGCD : GCDPriority.DoubleDown); + ddStrat is DoubleDownStrategy.Force or DoubleDownStrategy.Force1 or DoubleDownStrategy.Force2 or DoubleDownStrategy.Force3 + ? GCDPriority.ForcedGCD + : Ammo == 1 ? GCDPriority.Only1Ammo : GCDPriority.DoubleDown); if (ShouldUseGnashingFang(gfStrat, primaryTarget?.Actor)) QueueGCD(AID.GnashingFang, TargetChoice(gf) ?? primaryTarget?.Actor, - gfStrat is GnashingStrategy.ForceGnash + gfStrat is GnashingStrategy.ForceGnash or GnashingStrategy.ForceGnash1 or GnashingStrategy.ForceGnash2 or GnashingStrategy.ForceGnash3 ? GCDPriority.ForcedGCD : GCDPriority.GF1); if (ShouldUseCartridges(cartStrat, primaryTarget?.Actor)) { @@ -409,11 +429,11 @@ gfStrat is GnashingStrategy.ForceGnash TargetChoice(carts) ?? primaryTarget?.Actor, nmCD < 1 && Ammo == 3 ? GCDPriority.ForcedGCD : GCDPriority.Gauge); - if (cartStrat is CartridgeStrategy.OnlyBS or CartridgeStrategy.ForceBS) + if (cartStrat is CartridgeStrategy.OnlyBS or CartridgeStrategy.ForceBS or CartridgeStrategy.ForceBS1 or CartridgeStrategy.ForceBS2 or CartridgeStrategy.ForceBS3) QueueGCD(AID.BurstStrike, TargetChoice(carts) ?? primaryTarget?.Actor, GCDPriority.Gauge); - if (cartStrat is CartridgeStrategy.ForceFC or CartridgeStrategy.OnlyFC) + if (cartStrat is CartridgeStrategy.ForceFC or CartridgeStrategy.OnlyFC or CartridgeStrategy.ForceFC1 or CartridgeStrategy.ForceFC2 or CartridgeStrategy.ForceFC3) QueueGCD(BestFatedCircle, Unlocked(AID.FatedCircle) ? Player : primaryTarget?.Actor, GCDPriority.Gauge); @@ -571,40 +591,46 @@ reignStrat is ReignStrategy.ForceLion }; private bool ShouldUseCartridges(CartridgeStrategy strategy, Actor? target) => strategy switch { - CartridgeStrategy.Automatic => ShouldUseFC ? ShouldUseFatedCircle(CartridgeStrategy.Automatic, target) : ShouldUseBurstStrike(CartridgeStrategy.Automatic, target), - CartridgeStrategy.OnlyBS => ShouldUseBurstStrike(CartridgeStrategy.Automatic, target), - CartridgeStrategy.OnlyFC => ShouldUseFatedCircle(CartridgeStrategy.Automatic, target), + CartridgeStrategy.Automatic => ShouldSpendCarts(CartridgeStrategy.Automatic, target), + CartridgeStrategy.OnlyBS => ShouldSpendCarts(CartridgeStrategy.Automatic, target), + CartridgeStrategy.OnlyFC => ShouldSpendCarts(CartridgeStrategy.Automatic, target), CartridgeStrategy.ForceBS => canBS, + CartridgeStrategy.ForceBS1 => canBS && Ammo == 1, + CartridgeStrategy.ForceBS2 => canBS && Ammo == 2, + CartridgeStrategy.ForceBS3 => canBS && Ammo == 3, CartridgeStrategy.ForceFC => canFC, + CartridgeStrategy.ForceFC1 => canFC && Ammo == 1, + CartridgeStrategy.ForceFC2 => canFC && Ammo == 2, + CartridgeStrategy.ForceFC3 => canFC && Ammo == 3, CartridgeStrategy.Conserve => false, _ => false }; - private bool ShouldUseDoubleDown(GCDStrategy strategy, Actor? target) => strategy switch + private bool ShouldUseDoubleDown(DoubleDownStrategy strategy, Actor? target) => strategy switch { - GCDStrategy.Automatic => Player.InCombat && target != null && In5y(target) && canDD && hasNM, - GCDStrategy.Force => canDD, - GCDStrategy.Delay => false, + DoubleDownStrategy.Automatic => Player.InCombat && target != null && In5y(target) && canDD && hasNM, + DoubleDownStrategy.Force => canDD, + DoubleDownStrategy.Force1 => canDD && Ammo == 1, + DoubleDownStrategy.Force2 => canDD && Ammo == 2, + DoubleDownStrategy.Force3 => canDD && Ammo == 3, + DoubleDownStrategy.Delay => false, _ => false }; private bool ShouldUseGnashingFang(GnashingStrategy strategy, Actor? target) => strategy switch { GnashingStrategy.Automatic => Player.InCombat && target != null && In3y(target) && canGF && (nmLeft > 0 || hasNM || nmCD is < 35 and > 17), GnashingStrategy.ForceGnash => canGF, + GnashingStrategy.ForceGnash1 => canGF && Ammo == 1, + GnashingStrategy.ForceGnash2 => canGF && Ammo == 2, + GnashingStrategy.ForceGnash3 => canGF && Ammo == 3, GnashingStrategy.ForceClaw => Player.InCombat && GunComboStep == 1, GnashingStrategy.ForceTalon => Player.InCombat && GunComboStep == 2, GnashingStrategy.Delay => false, _ => false }; - private bool ShouldUseBurstStrike(CartridgeStrategy strategy, Actor? target) => strategy switch - { - CartridgeStrategy.Automatic => Player.InCombat && target != null && In3y(target) && canBS && - (hasNM || (!(bfCD is <= 90 and >= 30) && nmCD < 1 && Ammo == 3)) || - Ammo == MaxCartridges && ComboLastMove is AID.BrutalShell or AID.DemonSlice, - _ => false - }; - private bool ShouldUseFatedCircle(CartridgeStrategy strategy, Actor? target) => strategy switch + private bool ShouldSpendCarts(CartridgeStrategy strategy, Actor? target) => strategy switch { - CartridgeStrategy.Automatic => Player.InCombat && target != null && In5y(target) && canFC && + CartridgeStrategy.Automatic => Player.InCombat && target != null && + (ShouldUseFC ? (In5y(target) && canFC) : (In3y(target) && canBS)) && (hasNM || (!(bfCD is <= 90 and >= 30) && nmCD < 1 && Ammo == 3)) || Ammo == MaxCartridges && ComboLastMove is AID.BrutalShell or AID.DemonSlice, _ => false From 509b1b6c9bec2fb41bb6a4328a83be33fb57e9b6 Mon Sep 17 00:00:00 2001 From: ace Date: Mon, 10 Feb 2025 06:23:28 -0800 Subject: [PATCH 18/28] small tools cleanup, prep for `AkechiGNBPvP` fixes --- .../Standard/akechi/AkechiTools.cs | 78 ++++++++++++++++++- 1 file changed, 76 insertions(+), 2 deletions(-) diff --git a/BossMod/Autorotation/Standard/akechi/AkechiTools.cs b/BossMod/Autorotation/Standard/akechi/AkechiTools.cs index 8427c7882f..143f55ee45 100644 --- a/BossMod/Autorotation/Standard/akechi/AkechiTools.cs +++ b/BossMod/Autorotation/Standard/akechi/AkechiTools.cs @@ -753,37 +753,94 @@ protected bool CanWeave(AID aid, int extraGCDs = 0, float extraFixedDelay = 0) #region Targeting #region Position Checks + + #region Core /// /// Checks precise positioning between player target and any other targets. /// protected delegate bool PositionCheck(Actor playerTarget, Actor targetToTest); + /// /// Calculates the priority of a target based on the total number of targets and the primary target itself. /// It is generic, so it can return different types based on the implementation. /// protected delegate P PriorityFunc

(int totalTargets, Actor primaryTarget); + #endregion + + #region Splash ///

/// Position checker for determining the best target for an ability that deals Splash damage. /// protected PositionCheck IsSplashTarget => (primary, other) => Hints.TargetInAOECircle(other, primary.Position, 5); + #endregion + + #region Cones + // some of these use-cases really are only for BLU modules, since their job's ability ranges are all over the place (i.e. 4y, 16y specifically) + + /// + /// Position checker for determining the best target for an ability that deals damage in a Cone within Four (4) yalms. + /// + protected PositionCheck Is4yConeTarget => (primary, other) => Hints.TargetInAOECone(other, Player.Position, 4, Player.DirectionTo(primary), 45.Degrees()); + + /// + /// Position checker for determining the best target for an ability that deals damage in a Cone within Six (6) yalms. + /// + protected PositionCheck Is6yConeTarget => (primary, other) => Hints.TargetInAOECone(other, Player.Position, 6, Player.DirectionTo(primary), 45.Degrees()); + + /// + /// Position checker for determining the best target for an ability that deals damage in a Cone within Eight (8) yalms. + /// + protected PositionCheck Is8yConeTarget => (primary, other) => Hints.TargetInAOECone(other, Player.Position, 8, Player.DirectionTo(primary), 45.Degrees()); + + /// + /// Position checker for determining the best target for an ability that deals damage in a Cone within Ten (10) yalms. + /// + protected PositionCheck Is10yConeTarget => (primary, other) => Hints.TargetInAOECone(other, Player.Position, 10, Player.DirectionTo(primary), 45.Degrees()); + + /// + /// Position checker for determining the best target for an ability that deals damage in a Cone within Twelve (12) yalms. + /// + protected PositionCheck Is12yConeTarget => (primary, other) => Hints.TargetInAOECone(other, Player.Position, 12, Player.DirectionTo(primary), 45.Degrees()); + + /// + /// Position checker for determining the best target for an ability that deals damage in a Cone within Fifteen (15) yalms. + /// + protected PositionCheck Is15yConeTarget => (primary, other) => Hints.TargetInAOECone(other, Player.Position, 15, Player.DirectionTo(primary), 45.Degrees()); + /// - /// Position checker for determining the best target for an ability that deals damage in a Cone . + /// Position checker for determining the best target for an ability that deals damage in a Cone within Sixteen (16) yalms. /// - protected PositionCheck IsConeTarget => (primary, other) => Hints.TargetInAOECone(other, Player.Position, 8, Player.DirectionTo(primary), 45.Degrees()); + protected PositionCheck Is16yConeTarget => (primary, other) => Hints.TargetInAOECone(other, Player.Position, 16, Player.DirectionTo(primary), 45.Degrees()); + + #endregion + + #region Lines (aka AOE Rectangles) /// /// Position checker for determining the best target for an ability that deals damage in a Line within Ten (10) yalms. /// protected PositionCheck Is10yRectTarget => (primary, other) => Hints.TargetInAOERect(other, Player.Position, Player.DirectionTo(primary), 10, 2); + /// /// Position checker for determining the best target for an ability that deals damage in a Line within Fifteen (15) yalms. /// protected PositionCheck Is15yRectTarget => (primary, other) => Hints.TargetInAOERect(other, Player.Position, Player.DirectionTo(primary), 15, 2); + + /// + /// Position checker for determining the best target for an ability that deals damage in a Line within Twenty (20) yalms. + /// + protected PositionCheck Is20yRectTarget => (primary, other) => Hints.TargetInAOERect(other, Player.Position, Player.DirectionTo(primary), 20, 2); + + /// /// Position checker for determining the best target for an ability that deals damage in a Line within Twenty-five (25) yalms /// protected PositionCheck Is25yRectTarget => (primary, other) => Hints.TargetInAOERect(other, Player.Position, Player.DirectionTo(primary), 25, 2); + + #endregion + #endregion + #region Range Checks /// /// Checks if target is within Zero (0) yalms in distance, or if Player is inside hitbox. /// @@ -832,6 +889,7 @@ protected bool CanWeave(AID aid, int extraGCDs = 0, float extraFixedDelay = 0) /// The user's specified Target being checked. /// protected bool In25y(Actor? target) => Player.DistanceToHitbox(target) <= 24.99f; + #endregion /// /// A simpler smart-targeting helper for picking a specific target over your current target. @@ -878,6 +936,22 @@ protected void GetPrimaryTarget(StrategyValues strategy, ref Enemy? primaryTarge } } + /// + /// This function attempts to pick ANY suitable primary target automatically, even if a target is not already picked. + /// + /// The user's current specified Target. + /// + protected void GetPvPTarget(ref Enemy? primaryTarget, float range) + { + if (Player.DistanceToHitbox(primaryTarget?.Actor) > range) + { + var newTarget = Hints.PriorityTargets.FirstOrDefault(x => Player.DistanceToHitbox(x.Actor) <= range); + if (newTarget != null) + primaryTarget = newTarget; + } + } + + /// /// This function attempts to pick the best target automatically. /// From cc10f69c79cf6e0f6057271f9c80d4abc4dc8eaa Mon Sep 17 00:00:00 2001 From: ace Date: Mon, 10 Feb 2025 06:26:05 -0800 Subject: [PATCH 19/28] kinda fix `AkechiGNBPvP` (LB & `RoughDivide`) --- BossMod/ActionQueue/Tanks/GNB.cs | 1 + .../Standard/akechi/PvP/AkechiGNBPvP.cs | 180 ++++++++---------- 2 files changed, 85 insertions(+), 96 deletions(-) diff --git a/BossMod/ActionQueue/Tanks/GNB.cs b/BossMod/ActionQueue/Tanks/GNB.cs index 7e8bf67f30..370e59b964 100644 --- a/BossMod/ActionQueue/Tanks/GNB.cs +++ b/BossMod/ActionQueue/Tanks/GNB.cs @@ -152,6 +152,7 @@ public enum SID : uint HeartOfCorundumPvP = 4295, // applied by Heart of Corundum to self CatharsisOfCorundumPvP = 4296, // applied by Heart of Corundum to self RelentlessRushPvP = 3052, + RelentlessShrapnelPvP = 3053, //Shared Elixir = ClassShared.AID.Elixir, diff --git a/BossMod/Autorotation/Standard/akechi/PvP/AkechiGNBPvP.cs b/BossMod/Autorotation/Standard/akechi/PvP/AkechiGNBPvP.cs index 9ac911dba1..eda284b350 100644 --- a/BossMod/Autorotation/Standard/akechi/PvP/AkechiGNBPvP.cs +++ b/BossMod/Autorotation/Standard/akechi/PvP/AkechiGNBPvP.cs @@ -1,19 +1,21 @@ -using FFXIVClientStructs.FFXIV.Client.Game.Gauge; -using AID = BossMod.GNB.AID; -using SID = BossMod.GNB.SID; +using static BossMod.AIHints; +using FFXIVClientStructs.FFXIV.Client.Game.Gauge; +using BossMod.GNB; namespace BossMod.Autorotation.akechi; //Contribution by Akechi //Discord @akechdz or 'Akechi' on Puni.sh for maintenance -public sealed class AkechiGNBPvP(RotationModuleManager manager, Actor player) : RotationModule(manager, player) +public sealed class AkechiGNBPvP(RotationModuleManager manager, Actor player) : AkechiTools(manager, player) + { #region Enums: Abilities / Strategies public enum Track { Burst, Combo, - LimitBreak, + RelentlessRush, + TerminalTrigger, GnashingFang, FatedCircle, RoughDivide, @@ -35,7 +37,14 @@ public enum ComboStrategy Hold } - public enum LimitBreakStrategy + public enum RushStrategy + { + Automatic, + Force, + Hold + } + + public enum TriggerStrategy { Automatic, Force, @@ -65,7 +74,6 @@ public static RotationModuleDefinition Definition() { var res = new RotationModuleDefinition("Akechi GNB (PvP)", "PvP Rotation Module", "PvP", "Akechi", RotationModuleQuality.Basic, BitMask.Build((int)Class.GNB), 100, 30); - #region Custom strategies res.Define(Track.Burst).As("Burst", uiPriority: 190) .AddOption(BurstStrategy.Automatic, "Automatic", "Use everything optimally") .AddOption(BurstStrategy.Force, "Force", "Force everything") @@ -76,13 +84,16 @@ public static RotationModuleDefinition Definition() .AddOption(ComboStrategy.Force, "Force", "Force combo") .AddOption(ComboStrategy.Hold, "Hold", "Hold combo"); - res.Define(Track.LimitBreak).As("Limit Break", uiPriority: 190) - .AddOption(LimitBreakStrategy.Automatic, "Automatic", "Use Limit Break optimally") - .AddOption(LimitBreakStrategy.Force, "Force", "Force Limit Break") - .AddOption(LimitBreakStrategy.Hold, "Hold", "Hold Limit Break"); - #endregion + res.Define(Track.RelentlessRush).As("Relentless Rush", uiPriority: 190) + .AddOption(RushStrategy.Automatic, "Automatic", "Use Relentless Rush optimally") + .AddOption(RushStrategy.Force, "Force", "Force Relentless Rush") + .AddOption(RushStrategy.Hold, "Hold", "Hold Relentless Rush"); + + res.Define(Track.TerminalTrigger).As("Terminal Trigger", uiPriority: 190) + .AddOption(TriggerStrategy.Automatic, "Automatic", "Use Terminal Trigger optimally") + .AddOption(TriggerStrategy.Force, "Force", "Force Terminal Trigger") + .AddOption(TriggerStrategy.Hold, "Hold", "Hold Terminal Trigger"); - #region Offensive Strategies res.Define(Track.GnashingFang).As("Gnashing Fang", uiPriority: 150) .AddOption(OffensiveStrategy.Automatic, "Automatic", "Use normally") .AddOption(OffensiveStrategy.Force, "Force", "Force", 0, 0, ActionTargets.Hostile, 30) @@ -112,7 +123,6 @@ public static RotationModuleDefinition Definition() .AddOption(OffensiveStrategy.Force, "Force", "Force", 0, 0, ActionTargets.Hostile, 30) .AddOption(OffensiveStrategy.Delay, "Delay", "Delay", 0, 0, ActionTargets.None, 30) .AddAssociatedActions(AID.HeartOfCorundumPvP); - #endregion return res; } @@ -160,40 +170,28 @@ public enum OGCDPriority private bool canRip; private bool canTear; private bool canGouge; - public bool LBready; public float GFcomboStep; public float comboStep; public bool inCombo; public bool inGF; - public float GCDLength; - public AID NextGCD; - private GCDPriority NextGCDPrio; #endregion - #region Module Helpers - private float CD(AID aid) => World.Client.Cooldowns[ActionDefinitions.Instance.Spell(aid)!.MainCooldownGroup].Remaining; - private AID ComboLastMove => (AID)World.Client.ComboState.Action; - private bool In5y(Actor? target) => Player.DistanceToHitbox(target) <= 4.9; - private bool IsOffCooldown(AID aid) => World.Client.Cooldowns[ActionDefinitions.Instance.Spell(aid)!.MainCooldownGroup].Remaining < 0.6f; - public bool HasEffect(SID sid) => SelfStatusLeft(sid) > 0; - public bool TargetHasEffect(SID sid, Actor? target) => StatusDetails(target, sid, Player.InstanceID, 1000).Left > 0; - public AID LimitBreak => HasEffect(SID.RelentlessRushPvP) ? AID.TerminalTriggerPvP : AID.RelentlessRushPvP; - #endregion - - public override void Execute(StrategyValues strategy, ref Actor? primaryTarget, float estimatedAnimLockDelay, bool isMoving) + public override void Execution(StrategyValues strategy, Enemy? primaryTarget) { + GetPvPTarget(ref primaryTarget, 3); + #region Variables var gauge = World.Client.GetGauge(); var GunStep = gauge.AmmoComboStep; - rdCD = CD(AID.RoughDividePvP); - nmLeft = SelfStatusLeft(SID.NoMercyPvP, 7); + rdCD = TotalCD(AID.RoughDividePvP); + nmLeft = StatusRemaining(Player, SID.NoMercyPvP, 7); hasNM = nmLeft > 0; - hasBlast = HasEffect(SID.ReadyToBlastPvP); - hasRaze = HasEffect(SID.ReadyToRazePvP); - hasRip = HasEffect(SID.ReadyToRipPvP) || GunStep == 1; - hasTear = HasEffect(SID.ReadyToTearPvP) || GunStep == 2; - hasGouge = HasEffect(SID.ReadyToGougePvP); + hasBlast = PlayerHasEffect(SID.ReadyToBlastPvP); + hasRaze = PlayerHasEffect(SID.ReadyToRazePvP); + hasRip = PlayerHasEffect(SID.ReadyToRipPvP) || GunStep == 1; + hasTear = PlayerHasEffect(SID.ReadyToTearPvP) || GunStep == 2; + hasGouge = PlayerHasEffect(SID.ReadyToGougePvP); LBready = World.Party.LimitBreakLevel >= 1; GFcomboStep = ComboLastMove switch { @@ -212,19 +210,16 @@ public override void Execute(StrategyValues strategy, ref Actor? primaryTarget, }; inCombo = comboStep > 0; inGF = GFcomboStep > 0; - GCDLength = ActionSpeed.GCDRounded(World.Client.PlayerStats.SkillSpeed, World.Client.PlayerStats.Haste, Player.Level); - NextGCD = AID.None; - NextGCDPrio = GCDPriority.None; #region Minimal Requirements canGF = IsOffCooldown(AID.GnashingFangPvP); canFC = IsOffCooldown(AID.GnashingFangPvP); canZone = IsOffCooldown(AID.BlastingZonePvP); - canHyper = hasBlast && In5y(primaryTarget); - canBrand = hasRaze && In5y(primaryTarget); - canRip = hasRip && In5y(primaryTarget); - canTear = hasTear && In5y(primaryTarget); - canGouge = hasGouge && In5y(primaryTarget); + canHyper = hasBlast && In5y(primaryTarget?.Actor); + canBrand = hasRaze && In5y(primaryTarget?.Actor); + canRip = hasRip && In5y(primaryTarget?.Actor); + canTear = hasTear && In5y(primaryTarget?.Actor); + canGouge = hasGouge && In5y(primaryTarget?.Actor); #endregion #endregion @@ -232,80 +227,64 @@ public override void Execute(StrategyValues strategy, ref Actor? primaryTarget, var burstStrategy = burst.As(); var hold = burstStrategy == BurstStrategy.Hold; - if (strategy.Option(Track.Combo).As() == ComboStrategy.Force) - QueueGCD(NextCombo(), primaryTarget, GCDPriority.ForcedGCD); + if (strategy.Option(Track.Combo).As() is ComboStrategy.Force) + QueueGCD(NextCombo(), primaryTarget?.Actor, GCDPriority.ForcedGCD); #region Rotation Execution - if (!hold && !inGF) - QueueGCD(NextCombo(), primaryTarget, GCDPriority.Combo); + if (!inGF) + QueueGCD(NextCombo(), primaryTarget?.Actor, GCDPriority.Combo); #region OGCDs var rdStrat = strategy.Option(Track.RoughDivide).As(); if (!hold && - ShouldUseRoughDivide(rdStrat, primaryTarget)) - QueueOGCD(AID.RoughDividePvP, primaryTarget, rdStrat is OffensiveStrategy.Force ? OGCDPriority.ForcedOGCD : OGCDPriority.RoughDivide); + ShouldUseRoughDivide(rdStrat, primaryTarget?.Actor)) + QueueOGCD(AID.RoughDividePvP, primaryTarget?.Actor, rdStrat is OffensiveStrategy.Force ? OGCDPriority.ForcedOGCD : OGCDPriority.RoughDivide); var zoneStrat = strategy.Option(Track.Zone).As(); if (!hold && - ShouldUseZone(zoneStrat, primaryTarget)) - QueueOGCD(AID.BlastingZonePvP, primaryTarget, zoneStrat == OffensiveStrategy.Force ? OGCDPriority.ForcedOGCD : OGCDPriority.Zone); + ShouldUseZone(zoneStrat, primaryTarget?.Actor)) + QueueOGCD(AID.BlastingZonePvP, primaryTarget?.Actor, zoneStrat == OffensiveStrategy.Force ? OGCDPriority.ForcedOGCD : OGCDPriority.Zone); if (canRip || GunStep == 1) - QueueOGCD(AID.JugularRipPvP, primaryTarget, OGCDPriority.Continuation); - if (canTear || GunStep == 1) - QueueOGCD(AID.AbdomenTearPvP, primaryTarget, OGCDPriority.Continuation); + QueueOGCD(AID.JugularRipPvP, primaryTarget?.Actor, OGCDPriority.Continuation); + if (canTear || GunStep == 2) + QueueOGCD(AID.AbdomenTearPvP, primaryTarget?.Actor, OGCDPriority.Continuation); if (canGouge) - QueueOGCD(AID.EyeGougePvP, primaryTarget, OGCDPriority.Continuation); + QueueOGCD(AID.EyeGougePvP, primaryTarget?.Actor, OGCDPriority.Continuation); if (canHyper) - QueueOGCD(AID.HypervelocityPvP, primaryTarget, OGCDPriority.Continuation); + QueueOGCD(AID.HypervelocityPvP, primaryTarget?.Actor, OGCDPriority.Continuation); if (canBrand) - QueueOGCD(AID.FatedBrandPvP, primaryTarget, OGCDPriority.Continuation); + QueueOGCD(AID.FatedBrandPvP, primaryTarget?.Actor, OGCDPriority.Continuation); + + if (TargetHPP(Player) < 50) + QueueOGCD(AID.HeartOfCorundumPvP, primaryTarget?.Actor, OGCDPriority.Corundum); #endregion #region GCDs var gfStrat = strategy.Option(Track.GnashingFang).As(); if (!hold && - ShouldUseGnashingFang(gfStrat, primaryTarget)) - QueueGCD(AID.GnashingFangPvP, primaryTarget, GCDPriority.GnashingFang); - if (GunStep == 1 && In5y(primaryTarget)) - QueueGCD(AID.SavageClawPvP, primaryTarget, GCDPriority.GnashingFang); - if (GunStep == 2 && In5y(primaryTarget)) - QueueGCD(AID.WickedTalonPvP, primaryTarget, GCDPriority.GnashingFang); + ShouldUseGnashingFang(gfStrat, primaryTarget?.Actor)) + QueueGCD(AID.GnashingFangPvP, primaryTarget?.Actor, GCDPriority.GnashingFang); + if (GunStep == 1 && In5y(primaryTarget?.Actor)) + QueueGCD(AID.SavageClawPvP, primaryTarget?.Actor, GCDPriority.GnashingFang); + if (GunStep == 2 && In5y(primaryTarget?.Actor)) + QueueGCD(AID.WickedTalonPvP, primaryTarget?.Actor, GCDPriority.GnashingFang); var fcStrat = strategy.Option(Track.FatedCircle).As(); - if (ShouldUseFatedCircle(fcStrat, primaryTarget)) - QueueGCD(AID.FatedCirclePvP, primaryTarget, fcStrat == OffensiveStrategy.Force ? GCDPriority.ForcedGCD : GCDPriority.FatedCircle); + if (ShouldUseFatedCircle(fcStrat, primaryTarget?.Actor)) + QueueGCD(AID.FatedCirclePvP, primaryTarget?.Actor, fcStrat == OffensiveStrategy.Force ? GCDPriority.ForcedGCD : GCDPriority.FatedCircle); #endregion #endregion - var lbStrat = strategy.Option(Track.LimitBreak).As(); - if (ShouldUseLimitBreak(lbStrat, primaryTarget)) - QueueOGCD(LimitBreak, primaryTarget, lbStrat == LimitBreakStrategy.Force ? OGCDPriority.ForcedOGCD : OGCDPriority.LB); + var rrStrat = strategy.Option(Track.RelentlessRush).As(); + if (ShouldUseRR(rrStrat, primaryTarget?.Actor)) + QueueOGCD(AID.RelentlessRushPvP, Player, rrStrat == RushStrategy.Force ? OGCDPriority.ForcedOGCD : OGCDPriority.LB); + var ttStrat = strategy.Option(Track.TerminalTrigger).As(); + if (ShouldUseTT(ttStrat, primaryTarget?.Actor) && Hints.NumPriorityTargetsInAOECircle(Player.Position, 5) > 0) + QueueGCD(AID.TerminalTriggerPvP, Player, ttStrat == TriggerStrategy.Force ? GCDPriority.ForcedGCD : GCDPriority.ForcedGCD); } - #region Core Execution Helpers - private void QueueGCD(AID aid, Actor? target, GCDPriority prio) - { - if (prio != GCDPriority.None) - { - Hints.ActionsToExecute.Push(ActionID.MakeSpell(aid), target, ActionQueue.Priority.High + (int)prio); - if (prio > NextGCDPrio) - { - NextGCD = aid; - NextGCDPrio = prio; - } - } - } - private void QueueOGCD(AID aid, Actor? target, OGCDPriority prio, float basePrio = ActionQueue.Priority.Medium) - { - if (prio != OGCDPriority.None) - { - Hints.ActionsToExecute.Push(ActionID.MakeSpell(aid), target, basePrio + (int)prio); - } - } - #endregion - #region Single-Target Helpers private AID NextCombo() => ComboLastMove switch { @@ -321,7 +300,7 @@ private void QueueOGCD(AID aid, Actor? target, OGCDPriority prio, float basePrio { OffensiveStrategy.Automatic => target != null && - !hasNM || rdCD >= 7 || IsOffCooldown(AID.RoughDividePvP), + !hasNM && rdCD >= 7 || !OnCooldown(AID.RoughDividePvP), OffensiveStrategy.Force => true, OffensiveStrategy.Delay => false, _ => false @@ -366,15 +345,24 @@ private void QueueOGCD(AID aid, Actor? target, OGCDPriority prio, float basePrio _ => false }; - private bool ShouldUseLimitBreak(LimitBreakStrategy strategy, Actor? target) => strategy switch + private bool ShouldUseRR(RushStrategy strategy, Actor? target) => strategy switch { - LimitBreakStrategy.Automatic => + RushStrategy.Automatic => target != null && In5y(target) && hasNM && LBready, - LimitBreakStrategy.Force => true, - LimitBreakStrategy.Hold => false, + RushStrategy.Force => LBready, + RushStrategy.Hold => false, + _ => false + }; + + private bool ShouldUseTT(TriggerStrategy strategy, Actor? target) => strategy switch + { + TriggerStrategy.Automatic + => StacksRemaining(target, SID.RelentlessShrapnelPvP) > 0 && PlayerHasEffect(SID.RelentlessRushPvP), + TriggerStrategy.Force => PlayerHasEffect(SID.RelentlessRushPvP), + TriggerStrategy.Hold => false, _ => false }; #endregion From 996109458683483e47655f1a55f79d383ce13dc6 Mon Sep 17 00:00:00 2001 From: ace Date: Tue, 11 Feb 2025 09:57:34 -0800 Subject: [PATCH 20/28] Fix up this shit, this module was omega broken kek --- .../Autorotation/Utility/RolePvPUtility.cs | 234 ++++++------------ 1 file changed, 75 insertions(+), 159 deletions(-) diff --git a/BossMod/Autorotation/Utility/RolePvPUtility.cs b/BossMod/Autorotation/Utility/RolePvPUtility.cs index f299d119f6..6f2d6da782 100644 --- a/BossMod/Autorotation/Utility/RolePvPUtility.cs +++ b/BossMod/Autorotation/Utility/RolePvPUtility.cs @@ -4,52 +4,11 @@ public sealed class RolePvPUtility(RotationModuleManager manager, Actor player) : RotationModule(manager, player) { - #region Enums: Abilities / Strategies - public enum Track - { - Elixir, - Recuperate, - Guard, - Purify, - Sprint, - } - public enum ElixirStrategy - { - Automatic, - Close, - Mid, - Far, - Force, - Hold - } - - public enum RecuperateStrategy - { - Automatic, - Seventy, - Fifty, - Thirty, - Force, - Hold - } - - public enum GuardStrategy - { - Automatic, - Seventy, - Fifty, - Thirty, - Force, - Hold - } - - public enum DefensiveStrategy - { - Automatic, - Force, - Delay - } - #endregion + public enum Track { Elixir, Recuperate, Guard, Purify, Sprint } + public enum ElixirStrategy { Automatic, Close, Far, Force, Delay } + public enum RecuperateStrategy { Automatic, Seventy, Fifty, Thirty, Force, Delay } + public enum GuardStrategy { Automatic, Seventy, Fifty, Thirty, Force, Delay } + public enum DefensiveStrategy { Automatic, Force, Delay } public static RotationModuleDefinition Definition() { @@ -59,41 +18,40 @@ public static RotationModuleDefinition Definition() Class.WHM, Class.SCH, Class.AST, Class.SGE, Class.MNK, Class.DRG, Class.NIN, Class.SAM, Class.RPR, Class.VPR, Class.BRD, Class.MCH, Class.DNC, - Class.BLM, Class.SMN, Class.RDM, Class.PCT), 100, 30); + Class.BLM, Class.SMN, Class.RDM, Class.PCT), 1, 30); res.Define(Track.Elixir).As("Elixir", uiPriority: 150) - .AddOption(ElixirStrategy.Automatic, "Automatic") - .AddOption(ElixirStrategy.Close, "Close") - .AddOption(ElixirStrategy.Mid, "Mid") - .AddOption(ElixirStrategy.Far, "Far") - .AddOption(ElixirStrategy.Force, "Force") - .AddOption(ElixirStrategy.Hold, "Hold") + .AddOption(ElixirStrategy.Automatic, "Automatic", "Automatically use Elixir when no targets are nearby within 30 yalms") + .AddOption(ElixirStrategy.Close, "Close", "Automatically use Elixir when no targets are nearby within 15 yalms") + .AddOption(ElixirStrategy.Far, "Far", "Automatically use Elixir when no targets are nearby within 45 yalms") + .AddOption(ElixirStrategy.Force, "Force", "Force use Elixir") + .AddOption(ElixirStrategy.Delay, "Delay", "Forbids use of Elixir") .AddAssociatedActions(ClassShared.AID.Elixir); res.Define(Track.Recuperate).As("Recuperate", uiPriority: 150) - .AddOption(RecuperateStrategy.Automatic, "Automatic") - .AddOption(RecuperateStrategy.Seventy, "Seventy") - .AddOption(RecuperateStrategy.Fifty, "Fifty") - .AddOption(RecuperateStrategy.Thirty, "Thirty") - .AddOption(RecuperateStrategy.Force, "Force") - .AddOption(RecuperateStrategy.Hold, "Hold") + .AddOption(RecuperateStrategy.Automatic, "Automatic", "Automatically use Recuperate when HP% is under 40%") + .AddOption(RecuperateStrategy.Seventy, "Seventy", "Automatically use Recuperate when HP% is under 70%") + .AddOption(RecuperateStrategy.Fifty, "Fifty", "Automatically use Recuperate when HP% is under 50%") + .AddOption(RecuperateStrategy.Thirty, "Thirty", "Automatically use Recuperate when HP% is under 30%") + .AddOption(RecuperateStrategy.Force, "Force", "Force use Recuperate") + .AddOption(RecuperateStrategy.Delay, "Delay", "Forbids use of Recuperate") .AddAssociatedActions(ClassShared.AID.Recuperate); res.Define(Track.Guard).As("Guard", uiPriority: 150) - .AddOption(GuardStrategy.Automatic, "Automatic") - .AddOption(GuardStrategy.Seventy, "Seventy") - .AddOption(GuardStrategy.Fifty, "Fifty") - .AddOption(GuardStrategy.Thirty, "Thirty") - .AddOption(GuardStrategy.Force, "Force") - .AddOption(GuardStrategy.Hold, "Hold") + .AddOption(GuardStrategy.Automatic, "Automatic", "Automatically use Guard when HP% is under 35%") + .AddOption(GuardStrategy.Seventy, "Seventy", "Automatically use Guard when HP% is under 70%") + .AddOption(GuardStrategy.Fifty, "Fifty", "Automatically use Guard when HP% is under 50%") + .AddOption(GuardStrategy.Thirty, "Thirty", "Automatically use Guard when HP% is under 30%") + .AddOption(GuardStrategy.Force, "Force", "Force use Guard") + .AddOption(GuardStrategy.Delay, "Delay", "Forbids use of Guard") .AddAssociatedActions(ClassShared.AID.Guard); res.Define(Track.Purify).As("Purify", uiPriority: 150) - .AddOption(DefensiveStrategy.Automatic, "Automatic") - .AddOption(DefensiveStrategy.Force, "Force") - .AddOption(DefensiveStrategy.Delay, "Delay") + .AddOption(DefensiveStrategy.Automatic, "Automatic", "Automatically use Purify when under any debuff that can be cleansed") + .AddOption(DefensiveStrategy.Force, "Force", "Force use Purify") + .AddOption(DefensiveStrategy.Delay, "Delay", "Forbids use of Purify") .AddAssociatedActions(ClassShared.AID.Purify); res.Define(Track.Sprint).As("Sprint", uiPriority: 150) - .AddOption(DefensiveStrategy.Automatic, "Automatic") - .AddOption(DefensiveStrategy.Force, "Force") - .AddOption(DefensiveStrategy.Delay, "Delay") + .AddOption(DefensiveStrategy.Automatic, "Automatic", "Automatically uses Sprint when no target is nearby within 15 yalms") + .AddOption(DefensiveStrategy.Force, "Force", "Force use Sprint") + .AddOption(DefensiveStrategy.Delay, "Delay", "Forbids use of Sprint") .AddAssociatedActions(ClassShared.AID.Sprint); return res; } @@ -116,22 +74,20 @@ public enum OGCDPriority } #endregion - #region Placeholders for Variables - private bool hasSprint; - private bool canElixir; - private bool canRecuperate; - private bool canGuard; - private bool canPurify; - private bool canSprint; - public float GCDLength; - #endregion - #region Module Helpers - private bool In10y(Actor? target) => Player.DistanceToHitbox(target) <= 9.9; - private bool In20y(Actor? target) => Player.DistanceToHitbox(target) <= 19.9; - private bool In30y(Actor? target) => Player.DistanceToHitbox(target) <= 29.9; - private bool IsOffCooldown(ClassShared.AID aid) => World.Client.Cooldowns[ActionDefinitions.Instance.Spell(aid)!.MainCooldownGroup].Remaining < 0.6f; - #endregion + public static float TargetHPP(Actor? target = null) + { + if (target is null || target.IsDead) + return 0f; + + if (target is Actor actor) + { + var HPP = (float)actor.HPMP.CurHP / actor.HPMP.MaxHP * 1f; + return Math.Clamp(HPP, 0f, 1f); + } + + return 0f; + } public float DebuffsLeft(Actor? target) { return target == null ? 0f @@ -145,23 +101,27 @@ public float DebuffsLeft(Actor? target) ); } public bool HasAnyDebuff(Actor? target) => DebuffsLeft(target) > 0; + private bool IsOffCooldown(ClassShared.AID aid) => World.Client.Cooldowns[ActionDefinitions.Instance.Spell(aid)!.MainCooldownGroup].Remaining < 0.6f; + #endregion + + private bool hasSprint; + private bool canElixir; + private bool canRecuperate; + private bool canGuard; + private bool canPurify; + private bool canSprint; public override void Execute(StrategyValues strategy, ref Actor? primaryTarget, float estimatedAnimLockDelay, bool isMoving) { - #region Variables hasSprint = Player.FindStatus(ClassShared.SID.SprintPvP) != null; - - #region Minimal Requirements - canElixir = IsOffCooldown(ClassShared.AID.Elixir) && strategy.Option(Track.Elixir).As() != ElixirStrategy.Hold; - canRecuperate = Player.HPMP.CurMP >= 2500 && strategy.Option(Track.Recuperate).As() != RecuperateStrategy.Hold; - canGuard = IsOffCooldown(ClassShared.AID.Guard) && strategy.Option(Track.Guard).As() != GuardStrategy.Hold; + canElixir = IsOffCooldown(ClassShared.AID.Elixir) && strategy.Option(Track.Elixir).As() != ElixirStrategy.Delay; + canRecuperate = Player.HPMP.CurMP >= 2500 && strategy.Option(Track.Recuperate).As() != RecuperateStrategy.Delay; + canGuard = IsOffCooldown(ClassShared.AID.Guard) && strategy.Option(Track.Guard).As() != GuardStrategy.Delay; canPurify = IsOffCooldown(ClassShared.AID.Purify) && strategy.Option(Track.Purify).As() != DefensiveStrategy.Delay; canSprint = !hasSprint && strategy.Option(Track.Sprint).As() != DefensiveStrategy.Delay; - #endregion - #endregion var elixirStrat = strategy.Option(Track.Elixir).As(); - if (ShouldUseElixir(elixirStrat, primaryTarget)) + if (ShouldUseElixir(elixirStrat)) Hints.ActionsToExecute.Push(ActionID.MakeSpell(ClassShared.AID.Elixir), Player, strategy.Option(Track.Elixir).Priority(), strategy.Option(Track.Elixir).Value.ExpireIn); var recuperateStrat = strategy.Option(Track.Recuperate).As(); @@ -173,7 +133,7 @@ public override void Execute(StrategyValues strategy, ref Actor? primaryTarget, Hints.ActionsToExecute.Push(ActionID.MakeSpell(ClassShared.AID.Guard), Player, strategy.Option(Track.Guard).Priority(), strategy.Option(Track.Guard).Value.ExpireIn); var purifyStrat = strategy.Option(Track.Purify).As(); - if (ShouldUsePurify(purifyStrat, primaryTarget)) + if (ShouldUsePurify(purifyStrat)) Hints.ActionsToExecute.Push(ActionID.MakeSpell(ClassShared.AID.Purify), Player, strategy.Option(Track.Purify).Priority(), strategy.Option(Track.Purify).Value.ExpireIn); var sprintStrat = strategy.Option(Track.Sprint).As(); @@ -181,90 +141,46 @@ public override void Execute(StrategyValues strategy, ref Actor? primaryTarget, Hints.ActionsToExecute.Push(ActionID.MakeSpell(ClassShared.AID.Sprint), Player, strategy.Option(Track.Sprint).Priority(), strategy.Option(Track.Sprint).Value.ExpireIn); } - //TODO: fix this later - #region Core Execution Helpers - /* - public ClassShared.AID NextGCD; //Next global cooldown action to be used - public GCDPriority NextGCDPrio; //Priority of the next GCD for cooldown decision making - - private void QueueGCD(ClassShared.AID aid, Actor? target, GCDPriority prio) + public bool ShouldUseElixir(ElixirStrategy strategy) => strategy switch { - if (prio != GCDPriority.None) - { - Hints.ActionsToExecute.Push(ActionID.MakeSpell(aid), target, ActionQueue.Priority.High + (int)prio); - if (prio > NextGCDPrio) - { - NextGCD = aid; - NextGCDPrio = prio; - } - } - } - private void QueueOGCD(ClassShared.AID aid, Actor? target, OGCDPriority prio, float basePrio = ActionQueue.Priority.Medium) - { - if (prio != OGCDPriority.None) - { - Hints.ActionsToExecute.Push(ActionID.MakeSpell(aid), target, basePrio + (int)prio); - } - } - */ - #endregion - - public bool ShouldUseElixir(ElixirStrategy strategy, Actor? target) => strategy switch - { - ElixirStrategy.Automatic => - canElixir && - Player.HPMP.CurHP <= 2500 && - (In20y(target) || target != null), - ElixirStrategy.Close => Player.HPMP.CurHP <= 4000 && In10y(target), - ElixirStrategy.Mid => Player.HPMP.CurHP <= 4000 && In20y(target), - ElixirStrategy.Far => Player.HPMP.CurHP <= 4000 && In30y(target), + ElixirStrategy.Automatic => canElixir && TargetHPP(Player) <= 60 && Hints.NumPriorityTargetsInAOECircle(Player.Position, 30) == 0, + ElixirStrategy.Close => TargetHPP(Player) <= 60 && Hints.NumPriorityTargetsInAOECircle(Player.Position, 15) == 0, + ElixirStrategy.Far => TargetHPP(Player) <= 60 && Hints.NumPriorityTargetsInAOECircle(Player.Position, 45) == 0, ElixirStrategy.Force => canElixir, - ElixirStrategy.Hold => false, + ElixirStrategy.Delay => false, _ => false, }; - public bool ShouldUseRecuperate(RecuperateStrategy strategy) => strategy switch { - RecuperateStrategy.Automatic => - canRecuperate && - Player.HPMP.CurHP <= 4000, - RecuperateStrategy.Seventy => canRecuperate && Player.HPMP.CurHP <= 7000, - RecuperateStrategy.Fifty => canRecuperate && Player.HPMP.CurHP <= 5000, - RecuperateStrategy.Thirty => canRecuperate && Player.HPMP.CurHP <= 3000, + RecuperateStrategy.Automatic => canRecuperate && TargetHPP(Player) <= 40, + RecuperateStrategy.Seventy => canRecuperate && TargetHPP(Player) <= 70, + RecuperateStrategy.Fifty => canRecuperate && TargetHPP(Player) <= 50, + RecuperateStrategy.Thirty => canRecuperate && TargetHPP(Player) <= 30, RecuperateStrategy.Force => canRecuperate, - RecuperateStrategy.Hold => false, + RecuperateStrategy.Delay => false, _ => false, }; - public bool ShouldUseGuard(GuardStrategy strategy) => strategy switch { - GuardStrategy.Automatic => - canGuard && - Player.HPMP.CurHP <= 3500, - GuardStrategy.Seventy => canGuard && Player.HPMP.CurHP <= 7000, - GuardStrategy.Fifty => canGuard && Player.HPMP.CurHP <= 5000, - GuardStrategy.Thirty => canGuard && Player.HPMP.CurHP <= 3000, + GuardStrategy.Automatic => canGuard && TargetHPP(Player) <= 35, + GuardStrategy.Seventy => canGuard && TargetHPP(Player) <= 70, + GuardStrategy.Fifty => canGuard && TargetHPP(Player) <= 50, + GuardStrategy.Thirty => canGuard && TargetHPP(Player) <= 30, GuardStrategy.Force => canGuard, - GuardStrategy.Hold => false, + GuardStrategy.Delay => false, _ => false, }; - - public bool ShouldUsePurify(DefensiveStrategy strategy, Actor? target) => strategy switch + public bool ShouldUsePurify(DefensiveStrategy strategy) => strategy switch { - DefensiveStrategy.Automatic => - canPurify && - HasAnyDebuff(target), + DefensiveStrategy.Automatic => canPurify && HasAnyDebuff(Player), DefensiveStrategy.Force => canPurify, DefensiveStrategy.Delay => false, _ => false, }; - public bool ShouldUseSprint(DefensiveStrategy strategy) => strategy switch { - DefensiveStrategy.Automatic => - !Player.InCombat && - canSprint, - DefensiveStrategy.Force => true, + DefensiveStrategy.Automatic => Hints.NumPriorityTargetsInAOECircle(Player.Position, 15) == 0 && canSprint, + DefensiveStrategy.Force => canSprint, DefensiveStrategy.Delay => false, _ => false, }; From 92a58c1e7084b3612d3d41db42a9aeb362a45bd3 Mon Sep 17 00:00:00 2001 From: ace Date: Tue, 11 Feb 2025 09:59:17 -0800 Subject: [PATCH 21/28] xdd --- BossMod/Autorotation/Utility/RolePvPUtility.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/BossMod/Autorotation/Utility/RolePvPUtility.cs b/BossMod/Autorotation/Utility/RolePvPUtility.cs index 6f2d6da782..083e7e5182 100644 --- a/BossMod/Autorotation/Utility/RolePvPUtility.cs +++ b/BossMod/Autorotation/Utility/RolePvPUtility.cs @@ -18,7 +18,7 @@ public static RotationModuleDefinition Definition() Class.WHM, Class.SCH, Class.AST, Class.SGE, Class.MNK, Class.DRG, Class.NIN, Class.SAM, Class.RPR, Class.VPR, Class.BRD, Class.MCH, Class.DNC, - Class.BLM, Class.SMN, Class.RDM, Class.PCT), 1, 30); + Class.BLM, Class.SMN, Class.RDM, Class.PCT), 100, 30); res.Define(Track.Elixir).As("Elixir", uiPriority: 150) .AddOption(ElixirStrategy.Automatic, "Automatic", "Automatically use Elixir when no targets are nearby within 30 yalms") From 07c30fb491d77e354ce23254f450a6691e78b2b4 Mon Sep 17 00:00:00 2001 From: ace Date: Tue, 11 Feb 2025 10:45:26 -0800 Subject: [PATCH 22/28] Okay, last fixes to PvP stuff --- .../Standard/akechi/AkechiTools.cs | 34 +++-- .../Standard/akechi/PvP/AkechiGNBPvP.cs | 126 +++++++----------- .../Autorotation/Utility/RolePvPUtility.cs | 36 ++--- 3 files changed, 80 insertions(+), 116 deletions(-) diff --git a/BossMod/Autorotation/Standard/akechi/AkechiTools.cs b/BossMod/Autorotation/Standard/akechi/AkechiTools.cs index 143f55ee45..394a356fb3 100644 --- a/BossMod/Autorotation/Standard/akechi/AkechiTools.cs +++ b/BossMod/Autorotation/Standard/akechi/AkechiTools.cs @@ -460,6 +460,17 @@ protected bool QueueAction(AID aid, Actor? target, float priority, float delay, /// Any specified player, ally, or target protected bool TargetHasShield(Actor actor) => actor.HPMP.Shield > 0.1f; + /// + /// A quick and easy helper for retrieving the Current HP Percentage of the Player specifically. + /// Example:
+ /// - PlayerHPP() > 69 + /// Explanation:
+ /// - "PlayerHPP" represents the current HP Percentage value of the specified actor.
+ /// - "> 69" is the example conditional expression specified by the user.
+ ///
+ /// - A value representing the current HP Percentage (%) of the Player. + protected float PlayerHPP() => (float)Player.HPMP.CurHP / Player.HPMP.MaxHP * 100; + /// /// A quick and easy helper for retrieving the Current HP Percentage of any specified actor, whether it is the player or any other target user desires. /// Example:
@@ -471,7 +482,7 @@ protected bool QueueAction(AID aid, Actor? target, float priority, float delay, ///
/// Any specified player, ally, or target /// - A value representing the current HP Percentage (%) of user's specified actor. - public static float TargetHPP(Actor? target = null) + protected static float TargetHPP(Actor? target = null) { if (target is null || target.IsDead) return 0f; @@ -811,7 +822,6 @@ protected bool CanWeave(AID aid, int extraGCDs = 0, float extraFixedDelay = 0) /// Position checker for determining the best target for an ability that deals damage in a Cone within Sixteen (16) yalms. ///
protected PositionCheck Is16yConeTarget => (primary, other) => Hints.TargetInAOECone(other, Player.Position, 16, Player.DirectionTo(primary), 45.Degrees()); - #endregion #region Lines (aka AOE Rectangles) @@ -830,12 +840,10 @@ protected bool CanWeave(AID aid, int extraGCDs = 0, float extraFixedDelay = 0) /// protected PositionCheck Is20yRectTarget => (primary, other) => Hints.TargetInAOERect(other, Player.Position, Player.DirectionTo(primary), 20, 2); - /// /// Position checker for determining the best target for an ability that deals damage in a Line within Twenty-five (25) yalms /// protected PositionCheck Is25yRectTarget => (primary, other) => Hints.TargetInAOERect(other, Player.Position, Player.DirectionTo(primary), 25, 2); - #endregion #endregion @@ -918,6 +926,7 @@ protected bool CanWeave(AID aid, int extraGCDs = 0, float extraFixedDelay = 0) /// /// This function attempts to pick a suitable primary target automatically, even if a target is not already picked. + /// NOTE: This function is solely used for auto-targeting enemies without having a target selected, mainly for AI usage. Please use appropriately. /// /// The user's picked Strategy /// The user's current specified Target. @@ -937,21 +946,25 @@ protected void GetPrimaryTarget(StrategyValues strategy, ref Enemy? primaryTarge } /// - /// This function attempts to pick ANY suitable primary target automatically, even if a target is not already picked. + /// This function attempts to pick the most suitable primary target automatically, prioritizing the target with the lowest HP percentage within range. + /// NOTE: This function is solely used for finding a PvP target without having to click on other targets. Please use appropriately. /// /// The user's current specified Target. - /// + /// The max range to search for a new target. protected void GetPvPTarget(ref Enemy? primaryTarget, float range) { - if (Player.DistanceToHitbox(primaryTarget?.Actor) > range) + if (primaryTarget == null || Player.DistanceToHitbox(primaryTarget.Actor) > range) { - var newTarget = Hints.PriorityTargets.FirstOrDefault(x => Player.DistanceToHitbox(x.Actor) <= range); + var newTarget = Hints.PriorityTargets + .Where(x => x != null && x.Actor != null && Player.DistanceToHitbox(x.Actor) <= range) + .OrderBy(x => (float)x.Actor.HPMP.CurHP / x.Actor.HPMP.MaxHP) + .FirstOrDefault(); + if (newTarget != null) primaryTarget = newTarget; } } - /// /// This function attempts to pick the best target automatically. /// @@ -1048,9 +1061,6 @@ P targetPrio(Actor potentialTarget) /// Player's "actual" target; guaranteed to be an enemy. /// protected Enemy? PlayerTarget { get; private set; } - - //TODO: implement this soon - protected Actor? AnyTarget { get; private set; } #endregion #region Positionals diff --git a/BossMod/Autorotation/Standard/akechi/PvP/AkechiGNBPvP.cs b/BossMod/Autorotation/Standard/akechi/PvP/AkechiGNBPvP.cs index eda284b350..1d8afb4136 100644 --- a/BossMod/Autorotation/Standard/akechi/PvP/AkechiGNBPvP.cs +++ b/BossMod/Autorotation/Standard/akechi/PvP/AkechiGNBPvP.cs @@ -7,7 +7,6 @@ namespace BossMod.Autorotation.akechi; //Discord @akechdz or 'Akechi' on Puni.sh for maintenance public sealed class AkechiGNBPvP(RotationModuleManager manager, Actor player) : AkechiTools(manager, player) - { #region Enums: Abilities / Strategies public enum Track @@ -153,7 +152,7 @@ public enum OGCDPriority } #endregion - #region Placeholders for Variables + #region Module Variables private float nmLeft; private float rdCD; private bool hasNM; @@ -177,10 +176,8 @@ public enum OGCDPriority public bool inGF; #endregion - public override void Execution(StrategyValues strategy, Enemy? primaryTarget) + public override void Execution(StrategyValues strategy, Enemy? PlayerTarget) { - GetPvPTarget(ref primaryTarget, 3); - #region Variables var gauge = World.Client.GetGauge(); var GunStep = gauge.AmmoComboStep; @@ -210,82 +207,81 @@ public override void Execution(StrategyValues strategy, Enemy? primaryTarget) }; inCombo = comboStep > 0; inGF = GFcomboStep > 0; - - #region Minimal Requirements + var burst = strategy.Option(Track.Burst); + var burstStrategy = burst.As(); + var hold = burstStrategy == BurstStrategy.Hold; canGF = IsOffCooldown(AID.GnashingFangPvP); canFC = IsOffCooldown(AID.GnashingFangPvP); canZone = IsOffCooldown(AID.BlastingZonePvP); - canHyper = hasBlast && In5y(primaryTarget?.Actor); - canBrand = hasRaze && In5y(primaryTarget?.Actor); - canRip = hasRip && In5y(primaryTarget?.Actor); - canTear = hasTear && In5y(primaryTarget?.Actor); - canGouge = hasGouge && In5y(primaryTarget?.Actor); - #endregion + canHyper = hasBlast && In5y(PlayerTarget?.Actor); + canBrand = hasRaze && In5y(PlayerTarget?.Actor); + canRip = hasRip && In5y(PlayerTarget?.Actor); + canTear = hasTear && In5y(PlayerTarget?.Actor); + canGouge = hasGouge && In5y(PlayerTarget?.Actor); #endregion - var burst = strategy.Option(Track.Burst); - var burstStrategy = burst.As(); - var hold = burstStrategy == BurstStrategy.Hold; - - if (strategy.Option(Track.Combo).As() is ComboStrategy.Force) - QueueGCD(NextCombo(), primaryTarget?.Actor, GCDPriority.ForcedGCD); - #region Rotation Execution + GetPvPTarget(ref PlayerTarget, 3); + if (!inGF) - QueueGCD(NextCombo(), primaryTarget?.Actor, GCDPriority.Combo); + QueueGCD(NextCombo(), PlayerTarget?.Actor, GCDPriority.Combo); + if (strategy.Option(Track.Combo).As() is ComboStrategy.Force) + QueueGCD(NextCombo(), PlayerTarget?.Actor, GCDPriority.ForcedGCD); #region OGCDs var rdStrat = strategy.Option(Track.RoughDivide).As(); if (!hold && - ShouldUseRoughDivide(rdStrat, primaryTarget?.Actor)) - QueueOGCD(AID.RoughDividePvP, primaryTarget?.Actor, rdStrat is OffensiveStrategy.Force ? OGCDPriority.ForcedOGCD : OGCDPriority.RoughDivide); + ShouldUseRoughDivide(rdStrat, PlayerTarget?.Actor)) + QueueOGCD(AID.RoughDividePvP, PlayerTarget?.Actor, rdStrat is OffensiveStrategy.Force ? OGCDPriority.ForcedOGCD : OGCDPriority.RoughDivide); var zoneStrat = strategy.Option(Track.Zone).As(); if (!hold && - ShouldUseZone(zoneStrat, primaryTarget?.Actor)) - QueueOGCD(AID.BlastingZonePvP, primaryTarget?.Actor, zoneStrat == OffensiveStrategy.Force ? OGCDPriority.ForcedOGCD : OGCDPriority.Zone); + ShouldUseZone(zoneStrat, PlayerTarget?.Actor)) + QueueOGCD(AID.BlastingZonePvP, PlayerTarget?.Actor, zoneStrat == OffensiveStrategy.Force ? OGCDPriority.ForcedOGCD : OGCDPriority.Zone); if (canRip || GunStep == 1) - QueueOGCD(AID.JugularRipPvP, primaryTarget?.Actor, OGCDPriority.Continuation); + QueueOGCD(AID.JugularRipPvP, PlayerTarget?.Actor, OGCDPriority.Continuation); if (canTear || GunStep == 2) - QueueOGCD(AID.AbdomenTearPvP, primaryTarget?.Actor, OGCDPriority.Continuation); + QueueOGCD(AID.AbdomenTearPvP, PlayerTarget?.Actor, OGCDPriority.Continuation); if (canGouge) - QueueOGCD(AID.EyeGougePvP, primaryTarget?.Actor, OGCDPriority.Continuation); + QueueOGCD(AID.EyeGougePvP, PlayerTarget?.Actor, OGCDPriority.Continuation); if (canHyper) - QueueOGCD(AID.HypervelocityPvP, primaryTarget?.Actor, OGCDPriority.Continuation); + QueueOGCD(AID.HypervelocityPvP, PlayerTarget?.Actor, OGCDPriority.Continuation); if (canBrand) - QueueOGCD(AID.FatedBrandPvP, primaryTarget?.Actor, OGCDPriority.Continuation); + QueueOGCD(AID.FatedBrandPvP, PlayerTarget?.Actor, OGCDPriority.Continuation); - if (TargetHPP(Player) < 50) - QueueOGCD(AID.HeartOfCorundumPvP, primaryTarget?.Actor, OGCDPriority.Corundum); + if (TargetHPP(Player) < 55) + QueueOGCD(AID.HeartOfCorundumPvP, Player, OGCDPriority.Corundum); #endregion #region GCDs var gfStrat = strategy.Option(Track.GnashingFang).As(); if (!hold && - ShouldUseGnashingFang(gfStrat, primaryTarget?.Actor)) - QueueGCD(AID.GnashingFangPvP, primaryTarget?.Actor, GCDPriority.GnashingFang); - if (GunStep == 1 && In5y(primaryTarget?.Actor)) - QueueGCD(AID.SavageClawPvP, primaryTarget?.Actor, GCDPriority.GnashingFang); - if (GunStep == 2 && In5y(primaryTarget?.Actor)) - QueueGCD(AID.WickedTalonPvP, primaryTarget?.Actor, GCDPriority.GnashingFang); + ShouldUseGnashingFang(gfStrat, PlayerTarget?.Actor)) + QueueGCD(AID.GnashingFangPvP, PlayerTarget?.Actor, GCDPriority.GnashingFang); + if (GunStep == 1 && In5y(PlayerTarget?.Actor)) + QueueGCD(AID.SavageClawPvP, PlayerTarget?.Actor, GCDPriority.GnashingFang); + if (GunStep == 2 && In5y(PlayerTarget?.Actor)) + QueueGCD(AID.WickedTalonPvP, PlayerTarget?.Actor, GCDPriority.GnashingFang); var fcStrat = strategy.Option(Track.FatedCircle).As(); - if (ShouldUseFatedCircle(fcStrat, primaryTarget?.Actor)) - QueueGCD(AID.FatedCirclePvP, primaryTarget?.Actor, fcStrat == OffensiveStrategy.Force ? GCDPriority.ForcedGCD : GCDPriority.FatedCircle); + if (ShouldUseFatedCircle(fcStrat, PlayerTarget?.Actor)) + QueueGCD(AID.FatedCirclePvP, PlayerTarget?.Actor, fcStrat == OffensiveStrategy.Force ? GCDPriority.ForcedGCD : GCDPriority.FatedCircle); #endregion #endregion + #region Limit Break var rrStrat = strategy.Option(Track.RelentlessRush).As(); - if (ShouldUseRR(rrStrat, primaryTarget?.Actor)) + if (ShouldUseRR(rrStrat, PlayerTarget?.Actor)) QueueOGCD(AID.RelentlessRushPvP, Player, rrStrat == RushStrategy.Force ? OGCDPriority.ForcedOGCD : OGCDPriority.LB); var ttStrat = strategy.Option(Track.TerminalTrigger).As(); - if (ShouldUseTT(ttStrat, primaryTarget?.Actor) && Hints.NumPriorityTargetsInAOECircle(Player.Position, 5) > 0) + if (ShouldUseTT(ttStrat, PlayerTarget?.Actor) && Hints.NumPriorityTargetsInAOECircle(Player.Position, 5) > 0) QueueGCD(AID.TerminalTriggerPvP, Player, ttStrat == TriggerStrategy.Force ? GCDPriority.ForcedGCD : GCDPriority.ForcedGCD); + #endregion } - #region Single-Target Helpers + #region Cooldown Helpers private AID NextCombo() => ComboLastMove switch { AID.SolidBarrelPvP => AID.BurstStrikePvP, @@ -293,74 +289,44 @@ public override void Execution(StrategyValues strategy, Enemy? primaryTarget) AID.KeenEdgePvP => AID.BrutalShellPvP, _ => AID.KeenEdgePvP, }; - #endregion - - #region Cooldown Helpers private bool ShouldUseRoughDivide(OffensiveStrategy strategy, Actor? target) => strategy switch { - OffensiveStrategy.Automatic => - target != null && - !hasNM && rdCD >= 7 || !OnCooldown(AID.RoughDividePvP), - OffensiveStrategy.Force => true, + OffensiveStrategy.Automatic => target != null && (!hasNM && rdCD <= 14 || !OnCooldown(AID.RoughDividePvP)), + OffensiveStrategy.Force => rdCD <= 14.5f, OffensiveStrategy.Delay => false, _ => false }; - private bool ShouldUseZone(OffensiveStrategy strategy, Actor? target) => strategy switch { - OffensiveStrategy.Automatic => - Player.InCombat && - target != null && - canZone && - hasNM && - In5y(target), + OffensiveStrategy.Automatic => target != null && canZone && hasNM && In5y(target), OffensiveStrategy.Force => canZone, OffensiveStrategy.Delay => false, _ => false }; - private bool ShouldUseGnashingFang(OffensiveStrategy strategy, Actor? target) => strategy switch { - OffensiveStrategy.Automatic => - Player.InCombat && - target != null && - In5y(target) && - hasNM && - canGF, + OffensiveStrategy.Automatic => target != null && In5y(target) && hasNM && canGF, OffensiveStrategy.Force => canGF, OffensiveStrategy.Delay => false, _ => false }; - private bool ShouldUseFatedCircle(OffensiveStrategy strategy, Actor? target) => strategy switch { - OffensiveStrategy.Automatic => - Player.InCombat && - target != null && - In5y(target) && - hasNM && - canFC, + OffensiveStrategy.Automatic => target != null && In5y(target) && hasNM && canFC, OffensiveStrategy.Force => canFC, OffensiveStrategy.Delay => false, _ => false }; - private bool ShouldUseRR(RushStrategy strategy, Actor? target) => strategy switch { - RushStrategy.Automatic => - target != null && - In5y(target) && - hasNM && - LBready, + RushStrategy.Automatic => target != null && In5y(target) && hasNM && LBready, RushStrategy.Force => LBready, RushStrategy.Hold => false, _ => false }; - private bool ShouldUseTT(TriggerStrategy strategy, Actor? target) => strategy switch { - TriggerStrategy.Automatic - => StacksRemaining(target, SID.RelentlessShrapnelPvP) > 0 && PlayerHasEffect(SID.RelentlessRushPvP), + TriggerStrategy.Automatic => StacksRemaining(target, SID.RelentlessShrapnelPvP) > 0 && PlayerHasEffect(SID.RelentlessRushPvP), TriggerStrategy.Force => PlayerHasEffect(SID.RelentlessRushPvP), TriggerStrategy.Hold => false, _ => false diff --git a/BossMod/Autorotation/Utility/RolePvPUtility.cs b/BossMod/Autorotation/Utility/RolePvPUtility.cs index 083e7e5182..3f67c40baa 100644 --- a/BossMod/Autorotation/Utility/RolePvPUtility.cs +++ b/BossMod/Autorotation/Utility/RolePvPUtility.cs @@ -75,19 +75,7 @@ public enum OGCDPriority #endregion #region Module Helpers - public static float TargetHPP(Actor? target = null) - { - if (target is null || target.IsDead) - return 0f; - - if (target is Actor actor) - { - var HPP = (float)actor.HPMP.CurHP / actor.HPMP.MaxHP * 1f; - return Math.Clamp(HPP, 0f, 1f); - } - - return 0f; - } + public float PlayerHPP() => (float)Player.HPMP.CurHP / Player.HPMP.MaxHP * 100; public float DebuffsLeft(Actor? target) { return target == null ? 0f @@ -143,29 +131,29 @@ public override void Execute(StrategyValues strategy, ref Actor? primaryTarget, public bool ShouldUseElixir(ElixirStrategy strategy) => strategy switch { - ElixirStrategy.Automatic => canElixir && TargetHPP(Player) <= 60 && Hints.NumPriorityTargetsInAOECircle(Player.Position, 30) == 0, - ElixirStrategy.Close => TargetHPP(Player) <= 60 && Hints.NumPriorityTargetsInAOECircle(Player.Position, 15) == 0, - ElixirStrategy.Far => TargetHPP(Player) <= 60 && Hints.NumPriorityTargetsInAOECircle(Player.Position, 45) == 0, + ElixirStrategy.Automatic => canElixir && PlayerHPP() <= 60 && Hints.NumPriorityTargetsInAOECircle(Player.Position, 30) == 0, + ElixirStrategy.Close => canElixir && PlayerHPP() <= 60 && Hints.NumPriorityTargetsInAOECircle(Player.Position, 15) == 0, + ElixirStrategy.Far => canElixir && PlayerHPP() <= 60 && Hints.NumPriorityTargetsInAOECircle(Player.Position, 45) == 0, ElixirStrategy.Force => canElixir, ElixirStrategy.Delay => false, _ => false, }; public bool ShouldUseRecuperate(RecuperateStrategy strategy) => strategy switch { - RecuperateStrategy.Automatic => canRecuperate && TargetHPP(Player) <= 40, - RecuperateStrategy.Seventy => canRecuperate && TargetHPP(Player) <= 70, - RecuperateStrategy.Fifty => canRecuperate && TargetHPP(Player) <= 50, - RecuperateStrategy.Thirty => canRecuperate && TargetHPP(Player) <= 30, + RecuperateStrategy.Automatic => canRecuperate && PlayerHPP() <= 40, + RecuperateStrategy.Seventy => canRecuperate && PlayerHPP() <= 70, + RecuperateStrategy.Fifty => canRecuperate && PlayerHPP() <= 50, + RecuperateStrategy.Thirty => canRecuperate && PlayerHPP() <= 30, RecuperateStrategy.Force => canRecuperate, RecuperateStrategy.Delay => false, _ => false, }; public bool ShouldUseGuard(GuardStrategy strategy) => strategy switch { - GuardStrategy.Automatic => canGuard && TargetHPP(Player) <= 35, - GuardStrategy.Seventy => canGuard && TargetHPP(Player) <= 70, - GuardStrategy.Fifty => canGuard && TargetHPP(Player) <= 50, - GuardStrategy.Thirty => canGuard && TargetHPP(Player) <= 30, + GuardStrategy.Automatic => canGuard && PlayerHPP() <= 35, + GuardStrategy.Seventy => canGuard && PlayerHPP() <= 70, + GuardStrategy.Fifty => canGuard && PlayerHPP() <= 50, + GuardStrategy.Thirty => canGuard && PlayerHPP() <= 30, GuardStrategy.Force => canGuard, GuardStrategy.Delay => false, _ => false, From 56124f86a42b4f9802ad05a51de9b80c5edb46db Mon Sep 17 00:00:00 2001 From: ace Date: Tue, 11 Feb 2025 10:49:35 -0800 Subject: [PATCH 23/28] fix error --- BossMod/Autorotation/Standard/akechi/PvP/AkechiGNBPvP.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/BossMod/Autorotation/Standard/akechi/PvP/AkechiGNBPvP.cs b/BossMod/Autorotation/Standard/akechi/PvP/AkechiGNBPvP.cs index 1d8afb4136..b076c1852d 100644 --- a/BossMod/Autorotation/Standard/akechi/PvP/AkechiGNBPvP.cs +++ b/BossMod/Autorotation/Standard/akechi/PvP/AkechiGNBPvP.cs @@ -176,7 +176,7 @@ public enum OGCDPriority public bool inGF; #endregion - public override void Execution(StrategyValues strategy, Enemy? PlayerTarget) + public override void Execution(StrategyValues strategy, Enemy? primaryTarget) { #region Variables var gauge = World.Client.GetGauge(); @@ -221,7 +221,7 @@ public override void Execution(StrategyValues strategy, Enemy? PlayerTarget) #endregion #region Rotation Execution - GetPvPTarget(ref PlayerTarget, 3); + GetPvPTarget(ref primaryTarget, 3); if (!inGF) QueueGCD(NextCombo(), PlayerTarget?.Actor, GCDPriority.Combo); From 50cbb721ff2ec3cb60dfeeabb952344ac15675bd Mon Sep 17 00:00:00 2001 From: ace Date: Tue, 11 Feb 2025 11:07:48 -0800 Subject: [PATCH 24/28] fix auto-targeting oops --- BossMod/Autorotation/Standard/akechi/AkechiTools.cs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/BossMod/Autorotation/Standard/akechi/AkechiTools.cs b/BossMod/Autorotation/Standard/akechi/AkechiTools.cs index 394a356fb3..3e22e60fec 100644 --- a/BossMod/Autorotation/Standard/akechi/AkechiTools.cs +++ b/BossMod/Autorotation/Standard/akechi/AkechiTools.cs @@ -953,7 +953,9 @@ protected void GetPrimaryTarget(StrategyValues strategy, ref Enemy? primaryTarge /// The max range to search for a new target. protected void GetPvPTarget(ref Enemy? primaryTarget, float range) { - if (primaryTarget == null || Player.DistanceToHitbox(primaryTarget.Actor) > range) + primaryTarget ??= PlayerTarget; + + if (Player.DistanceToHitbox(PlayerTarget?.Actor) > range) { var newTarget = Hints.PriorityTargets .Where(x => x != null && x.Actor != null && Player.DistanceToHitbox(x.Actor) <= range) @@ -961,7 +963,7 @@ protected void GetPvPTarget(ref Enemy? primaryTarget, float range) .FirstOrDefault(); if (newTarget != null) - primaryTarget = newTarget; + PlayerTarget = newTarget; } } From 4233919892a9d9888e6645022420b20015b7d503 Mon Sep 17 00:00:00 2001 From: ace Date: Thu, 13 Feb 2025 15:48:44 -0800 Subject: [PATCH 25/28] shorten `AkechiTools` by removing redundant XMLs --- .../Standard/akechi/AkechiTools.cs | 767 +++++------------- 1 file changed, 197 insertions(+), 570 deletions(-) diff --git a/BossMod/Autorotation/Standard/akechi/AkechiTools.cs b/BossMod/Autorotation/Standard/akechi/AkechiTools.cs index 3e22e60fec..64846fdc24 100644 --- a/BossMod/Autorotation/Standard/akechi/AkechiTools.cs +++ b/BossMod/Autorotation/Standard/akechi/AkechiTools.cs @@ -2,286 +2,94 @@ namespace BossMod.Autorotation.akechi; -#region Shared Enums: Strategies +#region Shared Enums /// -/// The SharedTrack enum used for AOE and Hold strategies, typically for modules featuring damage rotations. -///
This enum defines tracks that can be used for all PvE classes and jobs, such as strategies containing executing standard rotations or explicitly holding abilities.
-/// Example Given: -///
- public enum Track { NoMercy = SharedTrack.Count }
-/// Explanation: -///
- Track is the enum for tracking specific abilities on user's specific rotation module.
-///
- NoMercy is the example enum of a specific ability being tracked on user's specific rotation module.
-///
- SharedTrack.Count is the shared track being used for executing our shared strategies listed above, called using .
+/// SharedTrack enum for AOE and Hold strategies, used in damage rotations for all PvE classes and jobs. ///
-/// - All strategies listed under into user's rotation module public enum SharedTrack { - /// - /// The main strategy for tracking single-target and AOE rotations. - /// - /// - All strategies listed under into user's rotation module + /// Tracks single-target and AOE rotations. AOE, - /// - /// The main strategy used for tracking when to hold any buffs, gauge, or cooldown abilties for optimal usage. - /// - /// - All strategies listed under into user's rotation module + /// Tracks holding buffs, gauge, or cooldown abilities for optimal usage. Hold, - /// - /// Represents the total count of strategies available inside this specific track. We generally never actually use this as a strategy since there isn't any logic really linked to this besides the counting. - /// + /// Represents the total count of strategies in this track. Count } /// -/// The Default Strategy enum used for tracking single-target and AOE strategies. -/// Example Given:
-/// - strategy.Option(SharedTrack.AOE).As<>(); -/// Explanation:
-/// - "strategy" is the parameter for tracking a specific strategy for a specific ability in the user's rotation module.
-/// - "Option" are the relative options for user's specific strategy.
-/// - "(SharedTrack.AOE)" is the enum representing all options relating to this custom strategy being tracked in the user's rotation module.
-/// - ".As<>();" is the relative strategy for user's specific ability being tracked in the user's rotation module. +/// AOEStrategy enum for tracking single-target and AOE strategies. ///
-/// - All strategies listed under into user's rotation module public enum AOEStrategy { - /// - /// The default strategy used for automatically executing the rotation.
- /// This can also be called using , or also as `strategy.Automatic()` in some cases. - /// Example:
- /// - strategy.Option(SharedTrack.AOE).As<>() == - /// Explanation:
- /// - "strategy.Option(SharedTrack.AOE).As<>()" is the full function (or local variable if user desires) for calling .
- /// - "" is the chosen option for this specific strategy.
- ///
- /// - The most optimal rotation automatically executed. + /// Executes the most optimal rotation automatically. Automatic, - /// - /// The main strategy used for force-executing the single-target rotation.
- /// This can also be called using , or also as `strategy.ForceST()` in some cases. - /// Example:
- /// - strategy.Option(SharedTrack.AOE).As<>() == - /// Explanation:
- /// - "strategy.Option(SharedTrack.AOE).As<>()" is the full function (or local variable if user desires) for calling .
- /// - "" is the chosen option for this specific strategy.
- ///
- /// - The single-target rotation force-executed, regardless of any conditions. + /// Forces execution of the single-target rotation. ForceST, - /// - /// The main strategy used for force-executing the AOE rotation.
- /// This can also be called using , or also as `strategy.ForceAOE()` in some cases. - /// Example:
- /// - strategy.Option(SharedTrack.AOE).As<>() == - /// Explanation:
- /// - "strategy.Option(SharedTrack.AOE).As<>()" is the full function (or local variable if user desires) for calling .
- /// - "" is the chosen option for this specific strategy.
- ///
- /// - The AOE rotation force-executed, regardless of any conditions. + /// Forces execution of the AOE rotation. ForceAOE } /// -/// The Default Strategy enum used for tracking when to hold any buffs, gauge, or cooldown abilties for optimal usage. -/// Example Given:
-/// - strategy.Option(SharedTrack.Hold).As<>(); -/// Explanation:
-/// - "strategy" is the parameter for tracking a specific strategy for a specific ability in the user's rotation module.
-/// - "Option" are the relative options for user's specific strategy.
-/// - "(SharedTrack.Hold)" is the enum representing all options relating to this custom strategy being tracked in the user's rotation module.
-/// - ".As<>();" is the relative strategy for user's specific ability being tracked in the user's rotation module. +/// HoldStrategy enum for tracking when to hold buffs, gauge, or cooldown abilities. ///
-/// - All strategies listed under into user's rotation module public enum HoldStrategy { - /// - /// The default strategy used for not holding any buffs, gauge, or cooldown abilties.
- /// This can also be called using , or also as `strategy.DontHold()` in some cases. - /// Example:
- /// - strategy.Option(SharedTrack.Hold).As<>() == - /// Explanation:
- /// - "strategy.Option(SharedTrack.Hold).As<>()" is the full function (or local variable if user desires) for calling .
- /// - "" is the chosen option for this specific strategy.
- ///
- /// - The availability of using all buffs, gauge, or cooldown abilties. + /// Uses all buffs, gauge, and cooldown abilities without restriction. DontHold, - /// - /// The main strategy used for only holding any ability that is cooldown-related.
- /// This can also be called using , or also as `strategy.HoldCDs()` in some cases. - /// Example:
- /// - strategy.Option(SharedTrack.Hold).As<>() == - /// Explanation:
- /// - "strategy.Option(SharedTrack.Hold).As<>()" is the full function (or local variable if user desires) for calling .
- /// - "" is the chosen option for this specific strategy.
- ///
- /// - The availability of using all buffs and gauge abilties, but forbidden from using any cooldowns. + /// Holds all cooldown-related abilities only. HoldCooldowns, - /// - /// The main strategy used for only holding any ability that is gauge-related.
- /// This can also be called using , or also as `strategy.HoldGauge()` in some cases. - /// Example:
- /// - strategy.Option(SharedTrack.Hold).As<>() == - /// Explanation:
- /// - "strategy.Option(SharedTrack.Hold).As<>()" is the full function (or local variable if user desires) for calling .
- /// - "" is the chosen option for this specific strategy.
- ///
- /// - The availability of using all buffs and cooldowns, but forbidden from using any gauge abilties. + /// Holds all gauge-related abilities only. HoldGauge, - /// - /// The main strategy used for only holding any ability that is buff-related.
- /// This can also be called using , or also as `strategy.HoldBuffs()` in some cases. - /// Example:
- /// - strategy.Option(SharedTrack.Hold).As<>() == - /// Explanation:
- /// - "strategy.Option(SharedTrack.Hold).As<>()" is the full function (or local variable if user desires) for calling .
- /// - "" is the chosen option for this specific strategy.
- ///
- /// - The availability of using all cooldowns and gauge abilties, but forbidden from using any buffs. + /// Holds all buff-related abilities only. HoldBuffs, - /// - /// The main strategy used for holding any ability that is buff, cooldown, or gauge-related.
- /// This can also be called using , or also as `strategy.HoldAll()` in some cases. - /// Example:
- /// - strategy.Option(SharedTrack.Hold).As<>() == - /// Explanation:
- /// - "strategy.Option(SharedTrack.Hold).As<>()" is the full function (or local variable if user desires) for calling .
- /// - "" is the chosen option for this specific strategy.
- ///
- /// - The forbiddance from using all buffs, cooldowns and gauge abilties. + /// Holds all buffs, cooldowns, and gauge abilities. HoldEverything } /// -/// The Default Strategy enum used for allowing or forbidding use of module-specific GCD abilities. -/// Example Given:
-/// - strategy.Option(Track.SonicBreak).As<>() -/// Explanation:
-/// - "strategy" is the parameter for tracking a specific strategy for a specific ability in the user's rotation module.
-/// - "Option" are the relative options for user's specific strategy.
-/// - "(Track.SonicBreak)" is the user's module-specific GCD ability enum being tracked.
-/// - ".As<>()" is the relative Default strategy for user's module-specific GCD ability being tracked in the user's rotation module. +/// GCDStrategy enum for managing module-specific GCD abilities. ///
-/// - All strategies listed under . public enum GCDStrategy { - /// - /// The default strategy used for automatically executing user's module-specific GCD ability. - /// Example:
- /// - strategy.Option(Track.SonicBreak).As<>() == - /// Explanation:
- /// - "strategy.Option(Track.SonicBreak).As<>()" is the full function (or local variable if user desires) for calling .
- /// - "" is the chosen option for this specific strategy.
- ///
- /// - Automatic execution of ability based on user's logic. + /// Executes the ability automatically based on user logic. Automatic, - /// - /// The main strategy used for force-executing user's module-specific GCD ability. - /// Example:
- /// - strategy.Option(Track.SonicBreak).As<>() == - /// Explanation:
- /// - "strategy.Option(Track.SonicBreak).As<>()" is the full function (or local variable if user desires) for calling .
- /// - "" is the chosen option for this specific strategy.
- ///
- /// - Forced execution of user's module-specific GCD ability. + /// Forces execution of the ability. Force, - /// - /// The main strategy used for forbidding use of user's module-specific GCD ability. - /// Example:
- /// - strategy.Option(Track.SonicBreak).As<>() == - /// Explanation:
- /// - "strategy.Option(Track.SonicBreak).As<>()" is the full function (or local variable if user desires) for calling .
- /// - "" is the chosen option for this specific strategy.
- ///
- /// - Forbiddance of execution for user's module-specific GCD ability. + /// Forbids execution of the ability. Delay } /// -/// The Default Strategy enum used for allowing or forbidding use of module-specific GCD abilities. -/// Example Given:
-/// - strategy.Option(Track.NoMercy).As<>() -/// Explanation:
-/// - "strategy" is the parameter for tracking a specific strategy for a specific ability in the user's rotation module.
-/// - "Option" are the relative options for user's specific strategy.
-/// - "(Track.NoMercy)" is the user's module-specific OGCD ability enum being tracked.
-/// - ".As<>()" is the relative Default strategy for user's module-specific OGCD ability being tracked in the user's rotation module. +/// OGCDStrategy enum for managing module-specific OGCD abilities. ///
-/// - All strategies listed under . public enum OGCDStrategy { - /// - /// The default strategy used for automatically executing user's module-specific OGCD ability. - /// Example:
- /// - strategy.Option(Track.NoMercy).As<>() == - /// Explanation:
- /// - "strategy.Option(Track.NoMercy).As<>()" is the full function (or local variable if user desires) for calling .
- /// - "" is the chosen option for this specific strategy.
- ///
- /// - Automatic execution of ability based on user's logic. + /// Executes the ability automatically based on user logic. Automatic, - /// - /// The main strategy used for force-executing user's module-specific OGCD ability. - /// Example:
- /// - strategy.Option(Track.NoMercy).As<>() == - /// Explanation:
- /// - "strategy.Option(Track.NoMercy).As<>()" is the full function (or local variable if user desires) for calling .
- /// - "" is the chosen option for this specific strategy.
- ///
- /// - Forced execution of user's module-specific OGCD ability. + /// Forces execution of the ability. Force, - /// - /// The main strategy used for force-executing user's module-specific OGCD ability in the very next weave window. - /// Example:
- /// - strategy.Option(Track.NoMercy).As<>() == - /// Explanation:
- /// - "strategy.Option(Track.NoMercy).As<>()" is the full function (or local variable if user desires) for calling .
- /// - "" is the chosen option for this specific strategy.
- ///
- /// - Forced execution of user's module-specific OGCD ability inside next weave. + /// Forces execution of the ability in the very next weave window. AnyWeave, - /// - /// The main strategy used for force-executing user's module-specific OGCD ability in the first-half of very next weave window. - /// Example:
- /// - strategy.Option(Track.NoMercy).As<>() == - /// Explanation:
- /// - "strategy.Option(Track.NoMercy).As<>()" is the full function (or local variable if user desires) for calling .
- /// - "" is the chosen option for this specific strategy.
- ///
- /// - Forced execution of user's module-specific OGCD ability inside next early-weave. + /// Forces execution of the ability in the very next EARLY-weave window. EarlyWeave, - /// - /// The main strategy used for force-executing user's module-specific OGCD ability in the second-half of very next weave window. - /// Example:
- /// - strategy.Option(Track.NoMercy).As<>() == - /// Explanation:
- /// - "strategy.Option(Track.NoMercy).As<>()" is the full function (or local variable if user desires) for calling .
- /// - "" is the chosen option for this specific strategy.
- ///
- /// - Forced execution of user's module-specific OGCD ability inside next late-weave. + /// Forces execution of the ability in the very next LATE-weave window. LateWeave, - /// - /// The main strategy used for forbidding use of user's module-specific OGCD ability. - /// Example:
- /// - strategy.Option(Track.NoMercy).As<>() == - /// Explanation:
- /// - "strategy.Option(Track.NoMercy).As<>()" is the full function (or local variable if user desires) for calling .
- /// - "" is the chosen option for this specific strategy.
- ///
- /// - Forbiddance of execution for user's module-specific OGCD ability. + /// Forbids execution of the ability. Delay } #endregion @@ -302,10 +110,10 @@ public abstract class AkechiTools(RotationModuleManager manager, A protected int NextGCDPrio; #region Queuing + #region GCD /// - /// The primary helper we use for calling all our GCD abilities onto any actor. - ///
This also handles Ground Target abilities, such as BLM:LeyLines or NIN:Shukuchi
+ /// The primary helper we use for calling all our GCD abilities onto any actor. /// NOTE: For compatibility between Actor? and Enemy? inside one function, use primaryTarget?.Actor as Enemy? definition. ///
/// The user's specified Action ID being checked. @@ -332,8 +140,7 @@ protected void QueueGCD(AID aid, Actor? target, int priority = 2, float delay = #region OGCD /// - /// The primary helper we use for calling all our OGCD abilities onto any actor.
- /// This also handles Ground Target abilities, such as BLM:LeyLines or NIN:Shukuchi + /// The primary helper we use for calling all our OGCD abilities onto any actor. /// NOTE: For compatibility between Actor? and Enemy? inside one function, use primaryTarget?.Actor as Enemy? definition. ///
/// The user's specified Action ID being checked. @@ -391,240 +198,152 @@ protected bool QueueAction(AID aid, Actor? target, float priority, float delay, #region HP/MP/Shield /// - /// A quick and easy helper for retrieving the current HP value of the player. - /// Example:
- /// - HP >= 6900 - /// Explanation:
- /// - "HP" represents the current HP value of the player.
- /// - ">= 6900" is the example conditional expression specified by the user.
+ /// Retrieves the current HP value of the player. ///
- /// - A value representing the Player's current HP protected uint HP { get; private set; } /// - /// A quick and easy helper for retrieving the current MP value of the player. - /// Example:
- /// - MP != 4200 - /// Explanation:
- /// - "MP" represents the current MP value of the player.
- /// - "!= 4200" is the example conditional expression specified by the user.
+ /// Retrieves the current MP value of the player. ///
- /// - A value representing the Player's current MP protected uint MP { get; private set; } /// - /// A quick and easy helper for retrieving the current Shield value of the player. - /// Example:
- /// - Shield > 0 - /// Explanation:
- /// - "Shield" represents the current Shield value of the player.
- /// - "> 0" is the example conditional expression specified by the user.
+ /// Retrieves the current Shield value of the player. ///
- /// - A value representing the Player's current Shield protected uint Shield { get; private set; } /// - /// A quick and easy helper for retrieving the Current HP of any specified actor, whether it is the player or any other target user desires. - /// Example:
- /// - TargetCurrentHP(primaryTarget) > 0 - /// Explanation:
- /// - "TargetCurrentHP" represents the current HP value of the specified actor.
- /// - "(primaryTarget)" represents the specified actor being checked.
- /// - "> 0" is the example conditional expression specified by the user.
+ /// Retrieves the current HP of a specified actor. ///
- /// Any specified player, ally, or target - /// - A value representing the current HP of user's specified actor + /// The target actor. protected uint TargetCurrentHP(Actor actor) => actor.HPMP.CurHP; /// - /// A quick and easy helper for retrieving the Current Shield of any specified actor, whether it is the player or any other target user desires. - /// Example:
- /// - TargetCurrentShield(primaryTarget) > 0 - /// Explanation:
- /// - "TargetCurrentShield" represents the current Shield value of the specified actor.
- /// - "(primaryTarget)" represents the specified actor being checked.
- /// - "> 0" is the example conditional expression specified by the user.
+ /// Retrieves the current Shield value of a specified actor. ///
- /// Any specified player, ally, or target - /// - A value representing the current Shield of user's specified actor. + /// The target actor. protected uint TargetCurrentShield(Actor actor) => actor.HPMP.Shield; /// - /// A quick and easy helper for checking if specified actor has any current Shield present, whether it is the player or any other target user desires. - /// Example:
- /// - TargetHasShield(primaryTarget) - /// Explanation:
- /// - "TargetHasShield" checks if the specified actor has any current Shield value.
- /// - "(primaryTarget)" represents the specified actor being checked.
+ /// Checks if a specified actor has any active Shield. ///
- /// Any specified player, ally, or target + /// The target actor. protected bool TargetHasShield(Actor actor) => actor.HPMP.Shield > 0.1f; /// - /// A quick and easy helper for retrieving the Current HP Percentage of the Player specifically. - /// Example:
- /// - PlayerHPP() > 69 - /// Explanation:
- /// - "PlayerHPP" represents the current HP Percentage value of the specified actor.
- /// - "> 69" is the example conditional expression specified by the user.
+ /// Retrieves the player's current HP percentage. ///
- /// - A value representing the current HP Percentage (%) of the Player. protected float PlayerHPP() => (float)Player.HPMP.CurHP / Player.HPMP.MaxHP * 100; /// - /// A quick and easy helper for retrieving the Current HP Percentage of any specified actor, whether it is the player or any other target user desires. - /// Example:
- /// - TargetHPP(primaryTarget) > 50 - /// Explanation:
- /// - "TargetHPP" represents the current HP Percentage value of the specified actor.
- /// - "(primaryTarget)" represents the specified actor being checked.
- /// - "> 50" is the example conditional expression specified by the user.
+ /// Retrieves the HP percentage of a specified actor. ///
- /// Any specified player, ally, or target - /// - A value representing the current HP Percentage (%) of user's specified actor. + /// The target actor. protected static float TargetHPP(Actor? target = null) { if (target is null || target.IsDead) return 0f; - if (target is Actor actor) - { - var HPP = (float)actor.HPMP.CurHP / actor.HPMP.MaxHP * 100f; - return Math.Clamp(HPP, 0f, 100f); - } - - return 0f; + var HPP = (float)target.HPMP.CurHP / target.HPMP.MaxHP * 100f; + return Math.Clamp(HPP, 0f, 100f); } #endregion #region Actions /// - /// Checks if specified action is Unlocked based on Level and Job Quest (if required). - /// Example:
- /// - Unlocked(AID.GnashingFang) - /// Explanation:
- /// - "Unlocked" is the function.
- /// - "(AID.GnashingFang)" is the example ability being checked, called using .. + /// Checks if specified action is Unlocked based on Level and Job Quest (if required). ///
/// The user's specified Action ID being checked. - /// - TRUE if the specified action is Unlocked.
- /// - FALSE if the specified action is still locked or Job Quest is unfinished.
- protected bool Unlocked(AID aid) => ActionUnlocked(ActionID.MakeSpell(aid)); //Check if the desired ability is unlocked + protected bool Unlocked(AID aid) => ActionUnlocked(ActionID.MakeSpell(aid)); /// - /// Checks if specified trait is Unlocked based on Level and Job Quest (if required). - /// Example:
- /// - Unlocked(TraitID.EnhancedBloodfest) - /// Explanation:
- /// - "Unlocked" is the function.
- /// - "(TraitID.EnhancedBloodfest)" is the example trait being checked, called using .. + /// Checks if specified trait is Unlocked based on Level and Job Quest (if required). ///
- /// The user's specified Trait ID being checked. - /// - TRUE if the specified action is Unlocked.
- /// - FALSE if the specified action is still locked or Job Quest is unfinished.
+ /// The Trait ID being checked. protected bool Unlocked(TraitID tid) => TraitUnlocked((uint)(object)tid); /// - /// Checks if last combo action is what the user is specifying.
- /// NOTE: This does NOT check all actions, only combo actions. - /// Example:
- /// - ComboLastMove == AID.BrutalShell - /// Explanation:
- /// - "ComboLastMove" is the function.
- /// - "AID.BrutalShell" is the example specified combo action being checked, called using .. + /// Checks the last combo action is what the user is specifying. ///
- /// - TRUE if the last combo action is what the user is specifying.
- /// - FALSE if otherwise or last action was not a combo action.
protected AID ComboLastMove => (AID)(object)World.Client.ComboState.Action; /// - /// Checks the time left remaining inside current combo before expiration. - /// NOTE: This does NOT check all actions, only combo actions. + /// Checks the time left remaining inside current combo before expiration. /// protected float ComboTimer => (float)(object)World.Client.ComboState.Remaining; /// - /// Retrieves actual cast time of a specified action. - /// Example:
- /// - ActualCastTime(AID.Fire3) > 0 - /// Explanation:
- /// - "ActualCastTime" is the function.
- /// - "AID.Fire3" is the example specified action being checked, called using ..
- /// - "> 0" is the example conditional expression specified by the user.
+ /// Retrieves actual cast time of a specified action. ///
/// The user's specified Action ID being checked. - /// - A value representing the current real-time Cast Time of user's specified action protected virtual float ActualCastTime(AID aid) => ActionDefinitions.Instance.Spell(aid)!.CastTime; /// - /// Retrieves effective cast time of a specified action. - /// Example:
- /// - EffectiveCastTime(AID.Fire3) > 0 - /// Explanation:
- /// - "EffectiveCastTime" is the function.
- /// - "AID.Fire3" is the example specified action being checked, called using ..
- /// - "> 0" is the example conditional expression specified by the user.
+ /// Retrieves effective cast time of a specified action. ///
/// The user's specified Action ID being checked. - /// - A value representing the current effective Cast Time of user's specified action protected virtual float EffectiveCastTime(AID aid) => PlayerHasEffect(ClassShared.SID.Swiftcast, 10) ? 0 : ActualCastTime(aid) * SpSGCDLength / 2.5f; - /// Retrieves player's GCD length based on Skill-Speed. - /// NOTE: This function is only recommended for jobs with Skill-Speed. Spell-Speed users are unaffected by this function. - /// - A value representing the player's current GCD Length + /// + /// Retrieves player's GCD length based on Skill-Speed. + /// protected float SkSGCDLength => ActionSpeed.GCDRounded(World.Client.PlayerStats.SkillSpeed, World.Client.PlayerStats.Haste, Player.Level); - /// Retrieves player's current Skill-Speed stat. - /// - A value representing the player's current Skill-Speed + /// + /// Retrieves player's current Skill-Speed stat. + /// protected float SkS => ActionSpeed.Round(World.Client.PlayerStats.SkillSpeed); - /// Retrieves player's GCD length based on Spell-Speed. - /// NOTE: This function is only recommended for jobs with Spell-Speed. Skill-Speed users are unaffected by this function. - /// - A value representing the player's current GCD Length + /// + /// Retrieves player's GCD length based on Spell-Speed. + /// protected float SpSGCDLength => ActionSpeed.GCDRounded(World.Client.PlayerStats.SpellSpeed, World.Client.PlayerStats.Haste, Player.Level); - /// Retrieves player's current Spell-Speed stat. - /// - A value representing the player's current Spell-Speed + /// + /// Retrieves player's current Spell-Speed stat. + /// protected float SpS => ActionSpeed.Round(World.Client.PlayerStats.SpellSpeed); - /// Checks if we can fit in a skill-speed based GCD. - /// - /// How many extra GCDs the user can fit in. - /// - TRUE if we can fit in a skill-speed based GCD - FALSE if otherwise + /// + /// Checks if we can fit in a skill-speed based GCD. + /// + /// The duration to check against. + /// How many extra GCDs the user can fit in. protected bool CanFitSkSGCD(float duration, int extraGCDs = 0) => GCD + SkSGCDLength * extraGCDs < duration; - /// Checks if we can fit in a spell-speed based GCD. - /// - /// How many extra GCDs the user can fit in. - /// - TRUE if we can fit in a spell-speed based GCD - FALSE if otherwise + /// + /// Checks if we can fit in a spell-speed based GCD. + /// + /// The duration to check against. + /// How many extra GCDs the user can fit in. protected bool CanFitSpSGCD(float duration, int extraGCDs = 0) => GCD + SpSGCDLength * extraGCDs < duration; - /// Checks if player is available to weave in any abilities. - /// NOTE: This function is only recommended for jobs with Skill-Speed. Spell-Speed users are unaffected by this. - /// The cooldown time of the action specified. - /// The animation lock time of the action specified. - /// How many extra GCDs the user can fit in. - /// How much extra delay the user can add in, in seconds. - /// - TRUE if we can weave in a skill-speed based GCD - FALSE if otherwise + /// + /// Checks if player is available to weave in any abilities. + /// + /// The cooldown time of the action specified. + /// The animation lock time of the action specified. + /// How many extra GCDs the user can fit in. + /// How much extra delay the user can add in, in seconds. protected bool CanWeave(float cooldown, float actionLock, int extraGCDs = 0, float extraFixedDelay = 0) => MathF.Max(cooldown, World.Client.AnimationLock) + actionLock + AnimationLockDelay <= GCD + SkSGCDLength * extraGCDs + extraFixedDelay; - /// Checks if player is available to weave in any spells. - /// NOTE: This function is only recommended for jobs with Spell-Speed. Skill-Speed users are unaffected by this. - /// The cooldown time of the action specified. - /// The animation lock time of the action specified. - /// How many extra GCDs the user can fit in. - /// How much extra delay the user can add in, in seconds. - /// - TRUE if we can weave in a spell-speed based GCD - FALSE if otherwise + /// + /// Checks if player is available to weave in any spells. + /// + /// The cooldown time of the action specified. + /// The animation lock time of the action specified. + /// How many extra GCDs the user can fit in. + /// How much extra delay the user can add in, in seconds. protected bool CanSpellWeave(float cooldown, float actionLock, int extraGCDs = 0, float extraFixedDelay = 0) => MathF.Max(cooldown, World.Client.AnimationLock) + actionLock + AnimationLockDelay <= GCD + SpSGCDLength * extraGCDs + extraFixedDelay; - /// Checks if player is available to weave in any abilities. - /// NOTE: This function is only recommended for jobs with Skill-Speed. Spell-Speed users are unaffected by this. - /// The user's specified Action ID being checked. - /// How many extra GCDs the user can fit in. - /// How much extra delay the user can add in, in seconds. - /// - TRUE if we can weave in any abilities - FALSE if otherwise + /// + /// Checks if player is available to weave in any abilities. + /// + /// The Action ID being checked. + /// How many extra GCDs the user can fit in. + /// How much extra delay the user can add in, in seconds. protected bool CanWeave(AID aid, int extraGCDs = 0, float extraFixedDelay = 0) { if (!Unlocked(aid)) @@ -636,127 +355,113 @@ protected bool CanWeave(AID aid, int extraGCDs = 0, float extraFixedDelay = 0) : SpS > 100 && CanWeave(ChargeCD(aid), res.InstantAnimLock, extraGCDs, extraFixedDelay); } - /// Checks if user is in pre-pull stage; useful for First GCD openings. - /// - TRUE if user is in pre-pull stage or fully not in combat- FALSE if otherwise + /// + /// Checks if user is in pre-pull stage. + /// protected bool IsFirstGCD() => !Player.InCombat || (World.CurrentTime - Manager.CombatStart).TotalSeconds < 0.1f; - /// Checks if user can Weave in any abilities.NOTE: This function is pretty sub-optimal, but gets the job done. CanWeave() is much more intricate if user really wants it. + /// + /// Checks if user can Weave in any abilities. + /// protected bool CanWeaveIn => GCD is <= 2.49f and >= 0.01f; - /// Checks if user can Early Weave in any abilities.NOTE: This function is pretty sub-optimal, but gets the job done. CanWeave() is much more intricate if user really wants it. + /// + /// Checks if user can Early Weave in any abilities. + /// protected bool CanEarlyWeaveIn => GCD is <= 2.49f and >= 1.26f; - /// Checks if user can Late Weave in any abilities.NOTE: This function is pretty sub-optimal, but gets the job done. CanWeave() is much more intricate if user really wants it. + /// + /// Checks if user can Late Weave in any abilities. + /// protected bool CanLateWeaveIn => GCD is <= 1.25f and >= 0.01f; - /// Checks if user can Quarter Weave in any abilities.NOTE: This function is pretty sub-optimal, but gets the job done. CanWeave() is much more intricate if user really wants it. + /// + /// Checks if user can Quarter Weave in any abilities. + /// protected bool CanQuarterWeaveIn => GCD is < 0.9f and >= 0.01f; #endregion #region Cooldown - /// Retrieves the total cooldown time left on the specified action. + /// Retrieves the total cooldown time left on the specified action. /// The user's specified Action ID being checked. - /// - A value representing the current cooldown on user's specified Action ID protected float TotalCD(AID aid) => World.Client.Cooldowns[ActionDefinitions.Instance.Spell(aid)!.MainCooldownGroup].Remaining; - /// Returns the charge cooldown time left on the specified action. + /// Returns the charge cooldown time left on the specified action. /// The user's specified Action ID being checked. - /// - A value representing the current charge cooldown on user's specified Action ID protected float ChargeCD(AID aid) => Unlocked(aid) ? ActionDefinitions.Instance.Spell(aid)!.ReadyIn(World.Client.Cooldowns, World.Client.DutyActions) : float.MaxValue; - /// Checks if action is ready to be used based on if it's Unlocked and its charge cooldown timer. + /// Checks if action is ready to be used based on if it's Unlocked and its charge cooldown timer. /// The user's specified Action ID being checked. - /// - TRUE if the specified Action is Unlocked and off cooldown. - FALSE if locked or still on cooldown protected bool ActionReady(AID aid) => Unlocked(aid) && ChargeCD(aid) < 0.6f; - /// Checks if action has any charges remaining. + /// Checks if action has any charges remaining. /// The user's specified Action ID being checked. - /// - TRUE if the specified Action has any charges available - FALSE if locked or still on cooldown protected bool HasCharges(AID aid) => ChargeCD(aid) < 0.6f; - /// Checks if action is on cooldown based on its total cooldown timer. + /// Checks if action is on cooldown based on its total cooldown timer. /// The user's specified Action ID being checked. - /// - TRUE if the specified Action is still on cooldown - FALSE if off cooldown protected bool IsOnCooldown(AID aid) => TotalCD(aid) > 0.6f; - /// Checks if action is off cooldown based on its total cooldown timer. + /// Checks if action is off cooldown based on its total cooldown timer. /// The user's specified Action ID being checked. - /// - TRUE if the specified Action is off cooldown - FALSE if still on cooldown protected bool IsOffCooldown(AID aid) => !IsOnCooldown(aid); - /// Checks if action is off cooldown based on its charge cooldown timer. + /// Checks if action is off cooldown based on its charge cooldown timer. /// The user's specified Action ID being checked. - /// - TRUE if the specified Action is off cooldown - FALSE if still on cooldown protected bool OnCooldown(AID aid) => MaxChargesIn(aid) > 0; - /// Checks if last action used is what the user is specifying and within however long. + /// Checks if last action used is what the user is specifying. /// The user's specified Action ID being checked. - /// - TRUE if the specified Action was just used - FALSE if was not just used protected bool LastActionUsed(AID aid) => Manager.LastCast.Data?.IsSpell(aid) == true; - /// Retrieves time remaining until specified action is at max charges. + /// Retrieves time remaining until specified action is at max charges. /// The user's specified Action ID being checked. protected float MaxChargesIn(AID aid) => Unlocked(aid) ? ActionDefinitions.Instance.Spell(aid)!.ChargeCapIn(World.Client.Cooldowns, World.Client.DutyActions, Player.Level) : float.MaxValue; #endregion #region Status - /// Retrieves the amount of specified status effect's stacks remaining on any target. - /// NOTE: The effect MUST be owned by the Player. - /// Example Given: "StacksRemaining(Player, SID.Requiescat, 30) > 0" + /// Retrieves the amount of specified status effect's stacks remaining on any target. + /// NOTE: The effect MUST be owned by the Player. /// The user's specified Target being checked. /// The user's specified Status ID being checked. /// The Total Effect Duration of specified Status ID being checked. - /// - A value indicating how many stacks exist protected int StacksRemaining(Actor? target, SID sid, float duration = 1000f) where SID : Enum => StatusDetails(target, sid, Player.InstanceID, duration).Stacks; - /// Retrieves the amount of specified status effect's time left remaining on any target. - /// NOTE: The effect MUST be owned by the Player. - /// Example Given: "StatusRemaining(Player, SID.Requiescat, 30) > 0f" + /// Retrieves the amount of specified status effect's time left remaining on any target. + /// NOTE: The effect MUST be owned by the Player. /// The user's specified Target being checked. /// The user's specified Status ID being checked. /// The Total Effect Duration of specified Status ID being checked. - /// - A value indicating how much time left on existing effect - protected float StatusRemaining(Actor? target, SID sid, float duration) where SID : Enum => StatusDetails(target, sid, Player.InstanceID, duration).Left; + protected float StatusRemaining(Actor? target, SID sid, float duration = 1000f) where SID : Enum => StatusDetails(target, sid, Player.InstanceID, duration).Left; - /// Checks if a specific status effect on the player exists. - /// NOTE: The effect MUST be owned by the Player. - /// Example Given: "PlayerHasEffect(SID.NoMercy, 20)" + /// Checks if a specific status effect on the Player exists. + /// NOTE: The effect MUST be owned by the Player. /// The user's specified Status ID being checked. /// The Total Effect Duration of specified Status ID being checked. - /// - A value indicating if the effect exists protected bool PlayerHasEffect(SID sid, float duration = 1000f) where SID : Enum => StatusRemaining(Player, sid, duration) > 0.1f; - /// Checks if a specific status effect on the player exists. - /// NOTE: The effect can be owned by anyone; Player, Party, Alliance, NPCs or even enemies - /// Example Given: "PlayerHasEffectAny(SID.Troubadour)" + /// Checks if a specific status effect on the Player exists. + /// NOTE: The effect can be owned by anyone; Player, Party, Alliance, NPCs, or even enemies. /// The user's specified Status ID being checked. - /// The Total Effect Duration of specified Status ID being checked. - /// - A value indicating if the effect exists protected bool PlayerHasAnyEffect(SID sid) where SID : Enum => Player.FindStatus(sid) != null; - /// Checks if a specific status effect on any specified target exists. - /// NOTE: The effect MUST be owned by the Player. - /// Example Given: "TargetHasEffect(primaryTarget, SID.SonicBreak, 30)" + /// Checks if a specific status effect on any specified Target exists. + /// NOTE: The effect MUST be owned by the Player. /// The user's specified Target being checked. /// The user's specified Status ID being checked. /// The Total Effect Duration of specified Status ID being checked. - /// - A value indicating if the effect exists protected bool TargetHasEffect(Actor? target, SID sid, float duration = 1000f) where SID : Enum => StatusRemaining(target, sid, duration) > 0.1f; - /// Checks if a specific status effect on any specified target exists. - /// NOTE: The effect can be owned by anyone; Player, Party, Alliance, NPCs or even enemies - /// Example Given: "TargetHasAnyEffect(primaryTarget, SID.MeditativeBrotherhood)" + /// Checks if a specific status effect on any specified Target exists. + /// NOTE: The effect can be owned by anyone; Player, Party, Alliance, NPCs, or even enemies. /// The user's specified Target being checked. /// The user's specified Status ID being checked. - /// - A value indicating if the effect exists protected bool TargetHasAnyEffect(Actor? target, SID sid) where SID : Enum => target?.FindStatus(sid) != null; - /// Checks if Player has any stacks of specific status effect. - /// NOTE: The effect MUST be owned by the Player. - /// Example Given: "PlayerHasStacks(SID.Requiescat)" + /// Checks if Player has any stacks of a specific status effect. + /// NOTE: The effect MUST be owned by the Player. /// The user's specified Status ID being checked. - /// - A value indicating if the effect exists protected bool PlayerHasStacks(SID sid) where SID : Enum => StacksRemaining(Player, sid) > 0; #endregion @@ -786,117 +491,58 @@ protected bool CanWeave(AID aid, int extraGCDs = 0, float extraFixedDelay = 0) #endregion #region Cones - // some of these use-cases really are only for BLU modules, since their job's ability ranges are all over the place (i.e. 4y, 16y specifically) - /// - /// Position checker for determining the best target for an ability that deals damage in a Cone within Four (4) yalms. + /// Creates a Position Check for Cone AOE attacks with the given range. /// - protected PositionCheck Is4yConeTarget => (primary, other) => Hints.TargetInAOECone(other, Player.Position, 4, Player.DirectionTo(primary), 45.Degrees()); - - /// - /// Position checker for determining the best target for an ability that deals damage in a Cone within Six (6) yalms. - /// - protected PositionCheck Is6yConeTarget => (primary, other) => Hints.TargetInAOECone(other, Player.Position, 6, Player.DirectionTo(primary), 45.Degrees()); - - /// - /// Position checker for determining the best target for an ability that deals damage in a Cone within Eight (8) yalms. - /// - protected PositionCheck Is8yConeTarget => (primary, other) => Hints.TargetInAOECone(other, Player.Position, 8, Player.DirectionTo(primary), 45.Degrees()); - - /// - /// Position checker for determining the best target for an ability that deals damage in a Cone within Ten (10) yalms. - /// - protected PositionCheck Is10yConeTarget => (primary, other) => Hints.TargetInAOECone(other, Player.Position, 10, Player.DirectionTo(primary), 45.Degrees()); - - /// - /// Position checker for determining the best target for an ability that deals damage in a Cone within Twelve (12) yalms. - /// - protected PositionCheck Is12yConeTarget => (primary, other) => Hints.TargetInAOECone(other, Player.Position, 12, Player.DirectionTo(primary), 45.Degrees()); - - /// - /// Position checker for determining the best target for an ability that deals damage in a Cone within Fifteen (15) yalms. - /// - protected PositionCheck Is15yConeTarget => (primary, other) => Hints.TargetInAOECone(other, Player.Position, 15, Player.DirectionTo(primary), 45.Degrees()); + private PositionCheck ConeTargetCheck(float range) => (primary, other) => + { + var playerDir = Player.DirectionTo(primary); // Cache calculation + return Hints.TargetInAOECone(other, Player.Position, range, playerDir, 45.Degrees()); + }; - /// - /// Position checker for determining the best target for an ability that deals damage in a Cone within Sixteen (16) yalms. - /// - protected PositionCheck Is16yConeTarget => (primary, other) => Hints.TargetInAOECone(other, Player.Position, 16, Player.DirectionTo(primary), 45.Degrees()); + protected PositionCheck Is4yConeTarget => ConeTargetCheck(4); + protected PositionCheck Is6yConeTarget => ConeTargetCheck(6); + protected PositionCheck Is8yConeTarget => ConeTargetCheck(8); + protected PositionCheck Is10yConeTarget => ConeTargetCheck(10); + protected PositionCheck Is12yConeTarget => ConeTargetCheck(12); + protected PositionCheck Is15yConeTarget => ConeTargetCheck(15); + protected PositionCheck Is16yConeTarget => ConeTargetCheck(16); #endregion - #region Lines (aka AOE Rectangles) + #region Lines (AOE Rectangles) /// - /// Position checker for determining the best target for an ability that deals damage in a Line within Ten (10) yalms. + /// Creates a Position Check for Line AOE (AOE Rectangle) attacks with the given range. /// - protected PositionCheck Is10yRectTarget => (primary, other) => Hints.TargetInAOERect(other, Player.Position, Player.DirectionTo(primary), 10, 2); - - /// - /// Position checker for determining the best target for an ability that deals damage in a Line within Fifteen (15) yalms. - /// - protected PositionCheck Is15yRectTarget => (primary, other) => Hints.TargetInAOERect(other, Player.Position, Player.DirectionTo(primary), 15, 2); - - /// - /// Position checker for determining the best target for an ability that deals damage in a Line within Twenty (20) yalms. - /// - protected PositionCheck Is20yRectTarget => (primary, other) => Hints.TargetInAOERect(other, Player.Position, Player.DirectionTo(primary), 20, 2); + private PositionCheck LineTargetCheck(float range) => (primary, other) => + { + var playerDir = Player.DirectionTo(primary); // Cache calculation + return Hints.TargetInAOERect(other, Player.Position, playerDir, range, 2); + }; - /// - /// Position checker for determining the best target for an ability that deals damage in a Line within Twenty-five (25) yalms - /// - protected PositionCheck Is25yRectTarget => (primary, other) => Hints.TargetInAOERect(other, Player.Position, Player.DirectionTo(primary), 25, 2); + protected PositionCheck Is10yRectTarget => LineTargetCheck(10); + protected PositionCheck Is15yRectTarget => LineTargetCheck(15); + protected PositionCheck Is20yRectTarget => LineTargetCheck(20); + protected PositionCheck Is25yRectTarget => LineTargetCheck(25); #endregion #endregion #region Range Checks /// - /// Checks if target is within Zero (0) yalms in distance, or if Player is inside hitbox. + /// Checks if target is within the specified distance in yalms. /// /// The user's specified Target being checked. + /// The maximum distance threshold. /// - protected bool In0y(Actor? target) => Player.DistanceToHitbox(target) <= 0.00f; - - /// - /// Checks if target is within Three (3) yalms in distance. - /// - /// The user's specified Target being checked. - /// - protected bool In3y(Actor? target) => Player.DistanceToHitbox(target) <= 2.99f; - - /// - /// Checks if target is within Five (5) yalms in distance. - /// - /// The user's specified Target being checked. - /// - protected bool In5y(Actor? target) => Player.DistanceToHitbox(target) <= 4.99f; - - /// - /// Checks if target is within Ten (10) yalms in distance. - /// - /// The user's specified Target being checked. - /// - protected bool In10y(Actor? target) => Player.DistanceToHitbox(target) <= 9.99f; - - /// - /// Checks if target is within Fifteen (15) yalms in distance. - /// - /// The user's specified Target being checked. - /// - protected bool In15y(Actor? target) => Player.DistanceToHitbox(target) <= 14.99f; - - /// - /// Checks if target is within Twenty (20) yalms in distance. - /// - /// The user's specified Target being checked. - /// - protected bool In20y(Actor? target) => Player.DistanceToHitbox(target) <= 19.99f; - - /// - /// Checks if target is within Twenty-five (25) yalms in distance. - /// - /// The user's specified Target being checked. - /// - protected bool In25y(Actor? target) => Player.DistanceToHitbox(target) <= 24.99f; + protected bool InRange(Actor? target, float maxDistance) => Player.DistanceToHitbox(target) <= maxDistance - 0.01f; + + protected bool In0y(Actor? target) => InRange(target, 0.00f); + protected bool In3y(Actor? target) => InRange(target, 3.00f); + protected bool In5y(Actor? target) => InRange(target, 5.00f); + protected bool In10y(Actor? target) => InRange(target, 10.00f); + protected bool In15y(Actor? target) => InRange(target, 15.00f); + protected bool In20y(Actor? target) => InRange(target, 20.00f); + protected bool In25y(Actor? target) => InRange(target, 25.00f); #endregion /// @@ -907,21 +553,27 @@ protected bool CanWeave(AID aid, int extraGCDs = 0, float extraFixedDelay = 0) /// protected Actor? TargetChoice(StrategyValues.OptionRef track) => ResolveTargetOverride(track.Value); - /// Targeting function for indicating when or not AOE Circle abilities should be used based on targets nearby. - /// The range of the AOE Circle ability, or radius from center of Player; this should be adjusted accordingly to user's module specific to job's abilities. - /// - A tuple with the following booleans: - /// -- OnTwoOrMore: A boolean indicating if there are two (2) or more targets inside Player's AOE Circle. - /// -- OnThreeOrMore: A boolean indicating if there are three (3) or more targets inside Player's AOE Circle. - /// -- OnFourOrMore: A boolean indicating if there are four (4) or more targets inside Player's AOE Circle. - /// -- OnFiveOrMore: A boolean indicating if there are five (5) or more targets inside Player's AOE Circle. - protected (bool OnTwoOrMore, bool OnThreeOrMore, bool OnFourOrMore, bool OnFiveOrMore) ShouldUseAOECircle(float range) + /// + /// Targeting function for indicating when AOE Circle abilities should be used based on nearby targets. + /// + /// The radius of the AOE Circle ability from the Player. + /// + /// A tuple with boolean values indicating whether the AOE Circle should be used based on the number of targets nearby:
+ /// OnAny: At least 1 target is inside.
+ /// OnTwoOrMore: At least 2 targets are inside.
+ /// OnThreeOrMore: At least 3 targets are inside.
+ /// OnFourOrMore: At least 4 targets are inside.
+ /// OnFiveOrMore: At least 5 targets are inside. + ///
+ protected (bool OnAny, bool OnTwoOrMore, bool OnThreeOrMore, bool OnFourOrMore, bool OnFiveOrMore) + ShouldUseAOECircle(float range) { - var OnTwoOrMore = Hints.NumPriorityTargetsInAOECircle(Player.Position, range) > 1; - var OnThreeOrMore = Hints.NumPriorityTargetsInAOECircle(Player.Position, range) > 2; - var OnFourOrMore = Hints.NumPriorityTargetsInAOECircle(Player.Position, range) > 3; - var OnFiveOrMore = Hints.NumPriorityTargetsInAOECircle(Player.Position, range) > 4; - - return (OnTwoOrMore, OnThreeOrMore, OnFourOrMore, OnFiveOrMore); + var numTargets = Hints.NumPriorityTargetsInAOECircle(Player.Position, range); + return (numTargets > 0, // OnAny + numTargets > 1, // OnTwoOrMore + numTargets > 2, // OnThreeOrMore + numTargets > 3, // OnFourOrMore + numTargets > 4); // OnFiveOrMore } /// @@ -931,7 +583,7 @@ protected bool CanWeave(AID aid, int extraGCDs = 0, float extraFixedDelay = 0) /// The user's picked Strategy /// The user's current specified Target. /// - protected void GetPrimaryTarget(StrategyValues strategy, ref Enemy? primaryTarget, float range) + protected void GetPvETarget(StrategyValues strategy, ref Enemy? primaryTarget, float range) { var AOEStrat = strategy.Option(SharedTrack.AOE).As(); if (AOEStrat is AOEStrategy.Automatic) @@ -970,7 +622,6 @@ protected void GetPvPTarget(ref Enemy? primaryTarget, float range) /// /// This function attempts to pick the best target automatically. /// - /// The user's picked Strategy /// The user's current picked Target. /// /// @@ -980,7 +631,6 @@ protected void GetPvPTarget(ref Enemy? primaryTarget, float range) /// /// This function picks the target based on HP, modified by how many targets are in the AOE. /// - /// /// The user's current picked Target. /// /// @@ -991,7 +641,6 @@ protected void GetPvPTarget(ref Enemy? primaryTarget, float range) /// Main function for picking a target, generalized for any prioritization and simplification logic. /// /// - /// /// The user's current picked Target. /// /// @@ -1015,14 +664,12 @@ P targetPrio(Actor potentialTarget) /// Identify an appropriate target for applying DoT effect. This has no impact if any auto-targeting is disabled. ///
/// - /// /// /// /// /// - protected (Enemy? Target, P Timer) GetDOTTarget

(Enemy? initial, Func getTimer, int maxAllowedTargets) where P : struct, IComparable + protected (Enemy? Best, P Timer) GetDOTTarget

(Enemy? initial, Func getTimer, int maxAllowedTargets) where P : struct, IComparable { - // Check if the initial target is null or if the maxAllowedTargets is 0, in which case no valid target can be selected if (initial == null || maxAllowedTargets <= 0) { return (null, getTimer(null)); @@ -1035,18 +682,14 @@ P targetPrio(Actor potentialTarget) foreach (var dotTarget in Hints.PriorityTargets) { - // Skip targets that forbid DOTs if (dotTarget.ForbidDOTs) continue; - // If we exceed the max number of allowed targets, stop and return the current best if (++numTargets > maxAllowedTargets) return (newTarget, newTimer); - // Get the timer for the current target var thisTimer = getTimer(dotTarget.Actor); - // Update the new target and timer if the current one is better (has a smaller timer value) if (thisTimer.CompareTo(newTimer) < 0) { newTarget = dotTarget; @@ -1058,13 +701,6 @@ P targetPrio(Actor potentialTarget) } #endregion - #region Actors - ///

- /// Player's "actual" target; guaranteed to be an enemy. - /// - protected Enemy? PlayerTarget { get; private set; } - #endregion - #region Positionals /// /// Retrieves the current positional of the target based on target's position and rotation. @@ -1154,7 +790,6 @@ protected void AnyGoalZoneCombined(float range, Func fAoe, AID firs #region Misc /// Estimates the delay caused by animation lock. - /// - A value representing the current Animation Lock Delay protected float AnimationLockDelay { get; private set; } /// Estimates the time remaining until the next Down-time phase. @@ -1168,6 +803,11 @@ protected void AnyGoalZoneCombined(float range, Func fAoe, AID firs /// Checks if player is currently moving. protected bool IsMoving { get; private set; } + + /// + /// Player's "actual" target; guaranteed to be an enemy. + /// + protected Enemy? PlayerTarget { get; private set; } #endregion #region Shared Abilities @@ -1313,44 +953,31 @@ public static RotationModuleDefinition.ConfigRef DefineOGCDA global helper for easily retrieving the user's Rotation strategy. See for more details. + public static AOEStrategy Rotation(this StrategyValues strategy) => strategy.Option(SharedTrack.AOE).As(); + /// A global helper for automatically executing the best optimal rotation. See for more details. - /// - TRUE if is set to - /// - FALSE if set to any other option public static bool Automatic(this StrategyValues strategy) => strategy.Option(SharedTrack.AOE).As() is AOEStrategy.Automatic; /// A global helper for force-executing the single-target rotation. See for more details. - /// - TRUE if is set to - /// - FALSE if set to any other option public static bool ForceST(this StrategyValues strategy) => strategy.Option(SharedTrack.AOE).As() is AOEStrategy.ForceST; /// A global helper for force-executing the AOE rotation. See for more details. - /// - TRUE if is set to - /// - FALSE if set to any other option public static bool ForceAOE(this StrategyValues strategy) => strategy.Option(SharedTrack.AOE).As() == AOEStrategy.ForceAOE; /// A global helper for forbidding ALL available abilities that are buff, gauge, or cooldown related. See for more details. - /// - TRUE if is set to - /// - FALSE if set to any other option public static bool HoldAll(this StrategyValues strategy) => strategy.Option(SharedTrack.Hold).As() == HoldStrategy.HoldEverything; /// A global helper for forbidding ALL available abilities that are related to raidbuffs. See for more details. - /// - TRUE if is set to - /// - FALSE if set to any other option public static bool HoldBuffs(this StrategyValues strategy) => strategy.Option(SharedTrack.Hold).As() == HoldStrategy.HoldBuffs; /// A global helper for forbidding ALL available abilities that have any sort of cooldown attached to it. See for more details. - /// - TRUE if is set to - /// - FALSE if set to any other option public static bool HoldCDs(this StrategyValues strategy) => strategy.Option(SharedTrack.Hold).As() == HoldStrategy.HoldCooldowns; /// A global helper for forbidding ALL available abilities that are related to the job's gauge. See for more details. - /// - TRUE if is set to - /// - FALSE if set to any other option public static bool HoldGauge(this StrategyValues strategy) => strategy.Option(SharedTrack.Hold).As() == HoldStrategy.HoldGauge; /// A global helper for allowing ALL available abilities that are buff, gauge, or cooldown related. This is the default option for this strategy. See for more details. - /// - TRUE if is set to - /// - FALSE if set to any other option public static bool DontHold(this StrategyValues strategy) => strategy.Option(SharedTrack.Hold).As() == HoldStrategy.DontHold; #endregion } From 02ec692089bfd373e134e21d900cf3d76a652858 Mon Sep 17 00:00:00 2001 From: ace Date: Thu, 13 Feb 2025 20:59:49 -0800 Subject: [PATCH 26/28] last little cleanup --- .../Standard/akechi/AkechiTools.cs | 443 +++++++----------- 1 file changed, 173 insertions(+), 270 deletions(-) diff --git a/BossMod/Autorotation/Standard/akechi/AkechiTools.cs b/BossMod/Autorotation/Standard/akechi/AkechiTools.cs index 64846fdc24..99d18d2017 100644 --- a/BossMod/Autorotation/Standard/akechi/AkechiTools.cs +++ b/BossMod/Autorotation/Standard/akechi/AkechiTools.cs @@ -83,10 +83,10 @@ public enum OGCDStrategy /// Forces execution of the ability in the very next weave window. AnyWeave, - /// Forces execution of the ability in the very next EARLY-weave window. + /// Forces execution of the ability in the very next EARLY-weave window. EarlyWeave, - /// Forces execution of the ability in the very next LATE-weave window. + /// Forces execution of the ability in the very next LATE-weave window. LateWeave, /// Forbids execution of the ability. @@ -197,130 +197,88 @@ protected bool QueueAction(AID aid, Actor? target, float priority, float delay, #endregion #region HP/MP/Shield - /// - /// Retrieves the current HP value of the player. - /// + /// Retrieves the current HP value of the player. protected uint HP { get; private set; } - /// - /// Retrieves the current MP value of the player. - /// + /// Retrieves the current MP value of the player. protected uint MP { get; private set; } - /// - /// Retrieves the current Shield value of the player. - /// + /// Retrieves the current Shield value of the player. protected uint Shield { get; private set; } - /// - /// Retrieves the current HP of a specified actor. - /// - /// The target actor. + /// Retrieves the current HP value of a specified actor. + /// The specified target actor. protected uint TargetCurrentHP(Actor actor) => actor.HPMP.CurHP; - /// - /// Retrieves the current Shield value of a specified actor. - /// + /// Retrieves the current Shield value of a specified actor. /// The target actor. protected uint TargetCurrentShield(Actor actor) => actor.HPMP.Shield; - /// - /// Checks if a specified actor has any active Shield. - /// + /// Checks if a specified actor has any current active Shield. /// The target actor. protected bool TargetHasShield(Actor actor) => actor.HPMP.Shield > 0.1f; - /// - /// Retrieves the player's current HP percentage. - /// + /// Retrieves the player's current HP percentage. protected float PlayerHPP() => (float)Player.HPMP.CurHP / Player.HPMP.MaxHP * 100; - /// - /// Retrieves the HP percentage of a specified actor. - /// - /// The target actor. - protected static float TargetHPP(Actor? target = null) + /// Retrieves the current HP percentage of a specified actor. + /// The target actor. + protected static float TargetHPP(Actor? actor = null) { - if (target is null || target.IsDead) + if (actor is null || actor.IsDead) return 0f; - var HPP = (float)target.HPMP.CurHP / target.HPMP.MaxHP * 100f; + var HPP = (float)actor.HPMP.CurHP / actor.HPMP.MaxHP * 100f; return Math.Clamp(HPP, 0f, 100f); } #endregion #region Actions - /// - /// Checks if specified action is Unlocked based on Level and Job Quest (if required). - /// + /// Checks if specified action is Unlocked based on Level and Job Quest (if required). /// The user's specified Action ID being checked. protected bool Unlocked(AID aid) => ActionUnlocked(ActionID.MakeSpell(aid)); - /// - /// Checks if specified trait is Unlocked based on Level and Job Quest (if required). - /// + /// Checks if specified trait is Unlocked based on Level and Job Quest (if required). /// The Trait ID being checked. protected bool Unlocked(TraitID tid) => TraitUnlocked((uint)(object)tid); - /// - /// Checks the last combo action is what the user is specifying. - /// + /// Checks the last combo action is what the user is specifying. protected AID ComboLastMove => (AID)(object)World.Client.ComboState.Action; - /// - /// Checks the time left remaining inside current combo before expiration. - /// + /// Checks the time left remaining inside current combo before expiration. protected float ComboTimer => (float)(object)World.Client.ComboState.Remaining; - /// - /// Retrieves actual cast time of a specified action. - /// + /// Retrieves actual cast time of a specified action. /// The user's specified Action ID being checked. protected virtual float ActualCastTime(AID aid) => ActionDefinitions.Instance.Spell(aid)!.CastTime; - /// - /// Retrieves effective cast time of a specified action. - /// + /// Retrieves effective cast time of a specified action. /// The user's specified Action ID being checked. protected virtual float EffectiveCastTime(AID aid) => PlayerHasEffect(ClassShared.SID.Swiftcast, 10) ? 0 : ActualCastTime(aid) * SpSGCDLength / 2.5f; - /// - /// Retrieves player's GCD length based on Skill-Speed. - /// + /// Retrieves player's GCD length based on Skill-Speed. protected float SkSGCDLength => ActionSpeed.GCDRounded(World.Client.PlayerStats.SkillSpeed, World.Client.PlayerStats.Haste, Player.Level); - /// - /// Retrieves player's current Skill-Speed stat. - /// + /// Retrieves player's current Skill-Speed stat. protected float SkS => ActionSpeed.Round(World.Client.PlayerStats.SkillSpeed); - /// - /// Retrieves player's GCD length based on Spell-Speed. - /// + /// Retrieves player's GCD length based on Spell-Speed. protected float SpSGCDLength => ActionSpeed.GCDRounded(World.Client.PlayerStats.SpellSpeed, World.Client.PlayerStats.Haste, Player.Level); - /// - /// Retrieves player's current Spell-Speed stat. - /// + /// Retrieves player's current Spell-Speed stat. protected float SpS => ActionSpeed.Round(World.Client.PlayerStats.SpellSpeed); - /// - /// Checks if we can fit in a skill-speed based GCD. - /// + /// Checks if we can fit in a skill-speed based GCD. /// The duration to check against. /// How many extra GCDs the user can fit in. protected bool CanFitSkSGCD(float duration, int extraGCDs = 0) => GCD + SkSGCDLength * extraGCDs < duration; - /// - /// Checks if we can fit in a spell-speed based GCD. - /// + /// Checks if we can fit in a spell-speed based GCD. /// The duration to check against. /// How many extra GCDs the user can fit in. protected bool CanFitSpSGCD(float duration, int extraGCDs = 0) => GCD + SpSGCDLength * extraGCDs < duration; - /// - /// Checks if player is available to weave in any abilities. - /// + /// Checks if player is available to weave in any abilities. /// The cooldown time of the action specified. /// The animation lock time of the action specified. /// How many extra GCDs the user can fit in. @@ -328,9 +286,7 @@ protected static float TargetHPP(Actor? target = null) protected bool CanWeave(float cooldown, float actionLock, int extraGCDs = 0, float extraFixedDelay = 0) => MathF.Max(cooldown, World.Client.AnimationLock) + actionLock + AnimationLockDelay <= GCD + SkSGCDLength * extraGCDs + extraFixedDelay; - /// - /// Checks if player is available to weave in any spells. - /// + /// Checks if player is available to weave in any spells. /// The cooldown time of the action specified. /// The animation lock time of the action specified. /// How many extra GCDs the user can fit in. @@ -338,9 +294,7 @@ protected bool CanWeave(float cooldown, float actionLock, int extraGCDs = 0, flo protected bool CanSpellWeave(float cooldown, float actionLock, int extraGCDs = 0, float extraFixedDelay = 0) => MathF.Max(cooldown, World.Client.AnimationLock) + actionLock + AnimationLockDelay <= GCD + SpSGCDLength * extraGCDs + extraFixedDelay; - /// - /// Checks if player is available to weave in any abilities. - /// + /// Checks if player is available to weave in any abilities. /// The Action ID being checked. /// How many extra GCDs the user can fit in. /// How much extra delay the user can add in, in seconds. @@ -355,111 +309,101 @@ protected bool CanWeave(AID aid, int extraGCDs = 0, float extraFixedDelay = 0) : SpS > 100 && CanWeave(ChargeCD(aid), res.InstantAnimLock, extraGCDs, extraFixedDelay); } - /// - /// Checks if user is in pre-pull stage. - /// + /// Checks if user is in pre-pull stage. protected bool IsFirstGCD() => !Player.InCombat || (World.CurrentTime - Manager.CombatStart).TotalSeconds < 0.1f; - /// - /// Checks if user can Weave in any abilities. - /// + /// Checks if user can Weave in any abilities. protected bool CanWeaveIn => GCD is <= 2.49f and >= 0.01f; - /// - /// Checks if user can Early Weave in any abilities. - /// + /// Checks if user can Early Weave in any abilities. protected bool CanEarlyWeaveIn => GCD is <= 2.49f and >= 1.26f; - /// - /// Checks if user can Late Weave in any abilities. - /// + /// Checks if user can Late Weave in any abilities. protected bool CanLateWeaveIn => GCD is <= 1.25f and >= 0.01f; - /// - /// Checks if user can Quarter Weave in any abilities. - /// + /// Checks if user can Quarter Weave in any abilities. protected bool CanQuarterWeaveIn => GCD is < 0.9f and >= 0.01f; #endregion #region Cooldown - /// Retrieves the total cooldown time left on the specified action. + /// Retrieves the total cooldown time left on the specified action. /// The user's specified Action ID being checked. protected float TotalCD(AID aid) => World.Client.Cooldowns[ActionDefinitions.Instance.Spell(aid)!.MainCooldownGroup].Remaining; - /// Returns the charge cooldown time left on the specified action. + /// Returns the charge cooldown time left on the specified action. /// The user's specified Action ID being checked. protected float ChargeCD(AID aid) => Unlocked(aid) ? ActionDefinitions.Instance.Spell(aid)!.ReadyIn(World.Client.Cooldowns, World.Client.DutyActions) : float.MaxValue; - /// Checks if action is ready to be used based on if it's Unlocked and its charge cooldown timer. + /// Checks if action is ready to be used based on if it's Unlocked and its charge cooldown timer. /// The user's specified Action ID being checked. protected bool ActionReady(AID aid) => Unlocked(aid) && ChargeCD(aid) < 0.6f; - /// Checks if action has any charges remaining. + /// Checks if action has any charges remaining. /// The user's specified Action ID being checked. protected bool HasCharges(AID aid) => ChargeCD(aid) < 0.6f; - /// Checks if action is on cooldown based on its total cooldown timer. + /// Checks if action is on cooldown based on its total cooldown timer. /// The user's specified Action ID being checked. protected bool IsOnCooldown(AID aid) => TotalCD(aid) > 0.6f; - /// Checks if action is off cooldown based on its total cooldown timer. + /// Checks if action is off cooldown based on its total cooldown timer. /// The user's specified Action ID being checked. protected bool IsOffCooldown(AID aid) => !IsOnCooldown(aid); - /// Checks if action is off cooldown based on its charge cooldown timer. + /// Checks if action is off cooldown based on its charge cooldown timer. /// The user's specified Action ID being checked. protected bool OnCooldown(AID aid) => MaxChargesIn(aid) > 0; - /// Checks if last action used is what the user is specifying. + /// Checks if last action used is what the user is specifying. /// The user's specified Action ID being checked. protected bool LastActionUsed(AID aid) => Manager.LastCast.Data?.IsSpell(aid) == true; - /// Retrieves time remaining until specified action is at max charges. + /// Retrieves time remaining until specified action is at max charges. /// The user's specified Action ID being checked. protected float MaxChargesIn(AID aid) => Unlocked(aid) ? ActionDefinitions.Instance.Spell(aid)!.ChargeCapIn(World.Client.Cooldowns, World.Client.DutyActions, Player.Level) : float.MaxValue; #endregion #region Status - /// Retrieves the amount of specified status effect's stacks remaining on any target. + /// Retrieves the amount of specified status effect's stacks remaining on any target. /// NOTE: The effect MUST be owned by the Player. /// The user's specified Target being checked. /// The user's specified Status ID being checked. /// The Total Effect Duration of specified Status ID being checked. protected int StacksRemaining(Actor? target, SID sid, float duration = 1000f) where SID : Enum => StatusDetails(target, sid, Player.InstanceID, duration).Stacks; - /// Retrieves the amount of specified status effect's time left remaining on any target. + /// Retrieves the amount of specified status effect's time left remaining on any target. /// NOTE: The effect MUST be owned by the Player. /// The user's specified Target being checked. /// The user's specified Status ID being checked. /// The Total Effect Duration of specified Status ID being checked. protected float StatusRemaining(Actor? target, SID sid, float duration = 1000f) where SID : Enum => StatusDetails(target, sid, Player.InstanceID, duration).Left; - /// Checks if a specific status effect on the Player exists. + /// Checks if a specific status effect on the Player exists. /// NOTE: The effect MUST be owned by the Player. /// The user's specified Status ID being checked. /// The Total Effect Duration of specified Status ID being checked. protected bool PlayerHasEffect(SID sid, float duration = 1000f) where SID : Enum => StatusRemaining(Player, sid, duration) > 0.1f; - /// Checks if a specific status effect on the Player exists. + /// Checks if a specific status effect on the Player exists. /// NOTE: The effect can be owned by anyone; Player, Party, Alliance, NPCs, or even enemies. /// The user's specified Status ID being checked. protected bool PlayerHasAnyEffect(SID sid) where SID : Enum => Player.FindStatus(sid) != null; - /// Checks if a specific status effect on any specified Target exists. + /// Checks if a specific status effect on any specified Target exists. /// NOTE: The effect MUST be owned by the Player. /// The user's specified Target being checked. /// The user's specified Status ID being checked. /// The Total Effect Duration of specified Status ID being checked. protected bool TargetHasEffect(Actor? target, SID sid, float duration = 1000f) where SID : Enum => StatusRemaining(target, sid, duration) > 0.1f; - /// Checks if a specific status effect on any specified Target exists. + /// Checks if a specific status effect on any specified Target exists. /// NOTE: The effect can be owned by anyone; Player, Party, Alliance, NPCs, or even enemies. /// The user's specified Target being checked. /// The user's specified Status ID being checked. protected bool TargetHasAnyEffect(Actor? target, SID sid) where SID : Enum => target?.FindStatus(sid) != null; - /// Checks if Player has any stacks of a specific status effect. + /// Checks if Player has any stacks of a specific status effect. /// NOTE: The effect MUST be owned by the Player. /// The user's specified Status ID being checked. protected bool PlayerHasStacks(SID sid) where SID : Enum => StacksRemaining(Player, sid) > 0; @@ -471,182 +415,178 @@ protected bool CanWeave(AID aid, int extraGCDs = 0, float extraFixedDelay = 0) #region Position Checks #region Core - /// - /// Checks precise positioning between player target and any other targets. - /// + /// Checks precise positioning between player target and any other targets. protected delegate bool PositionCheck(Actor playerTarget, Actor targetToTest); - /// - /// Calculates the priority of a target based on the total number of targets and the primary target itself. - /// It is generic, so it can return different types based on the implementation. - /// + /// Calculates the priority of a target based on the total number of targets and the primary target itself.
It is generic, so it can return different types based on the implementation.
protected delegate P PriorityFunc

(int totalTargets, Actor primaryTarget); #endregion #region Splash - ///

- /// Position checker for determining the best target for an ability that deals Splash damage. - /// + /// Position checker for determining the best target for an ability that deals Splash damage. protected PositionCheck IsSplashTarget => (primary, other) => Hints.TargetInAOECircle(other, primary.Position, 5); #endregion #region Cones - /// - /// Creates a Position Check for Cone AOE attacks with the given range. - /// + //some use-cases for these are mainly for BLU modules, since the ranges for their abilities are all over the place. (e.g. 4y & 16y specifically) + + /// Creates a Position Check for Cone AOE attacks with the given range. private PositionCheck ConeTargetCheck(float range) => (primary, other) => { - var playerDir = Player.DirectionTo(primary); // Cache calculation + var playerDir = Player.DirectionTo(primary); return Hints.TargetInAOECone(other, Player.Position, range, playerDir, 45.Degrees()); }; + /// Checks if the target is within a 4-yalm Cone range. protected PositionCheck Is4yConeTarget => ConeTargetCheck(4); + + /// Checks if the target is within a 6-yalm Cone range. protected PositionCheck Is6yConeTarget => ConeTargetCheck(6); + + /// Checks if the target is within an 8-yalm Cone range. protected PositionCheck Is8yConeTarget => ConeTargetCheck(8); + + /// Checks if the target is within a 10-yalm Cone range. protected PositionCheck Is10yConeTarget => ConeTargetCheck(10); + + /// Checks if the target is within a 12-yalm Cone range. protected PositionCheck Is12yConeTarget => ConeTargetCheck(12); + + /// Checks if the target is within a 15-yalm Cone range. protected PositionCheck Is15yConeTarget => ConeTargetCheck(15); + + /// Checks if the target is within a 16-yalm Cone range. protected PositionCheck Is16yConeTarget => ConeTargetCheck(16); #endregion #region Lines (AOE Rectangles) - /// - /// Creates a Position Check for Line AOE (AOE Rectangle) attacks with the given range. - /// + /// Creates a Position Check for Line AOE (AOE Rectangle) attacks with the given range. private PositionCheck LineTargetCheck(float range) => (primary, other) => { var playerDir = Player.DirectionTo(primary); // Cache calculation return Hints.TargetInAOERect(other, Player.Position, playerDir, range, 2); }; + /// Checks if the target is within a 10-yalm AOE Rect range. protected PositionCheck Is10yRectTarget => LineTargetCheck(10); + + /// Checks if the target is within a 15-yalm AOE Rect range. protected PositionCheck Is15yRectTarget => LineTargetCheck(15); + + /// Checks if the target is within a 20-yalm AOE Rect range. protected PositionCheck Is20yRectTarget => LineTargetCheck(20); + + /// Checks if the target is within a 25-yalm AOE Rect range. protected PositionCheck Is25yRectTarget => LineTargetCheck(25); #endregion #endregion #region Range Checks - /// - /// Checks if target is within the specified distance in yalms. - /// + /// Checks if target is within the specified distance in yalms. /// The user's specified Target being checked. /// The maximum distance threshold. - /// protected bool InRange(Actor? target, float maxDistance) => Player.DistanceToHitbox(target) <= maxDistance - 0.01f; - protected bool In0y(Actor? target) => InRange(target, 0.00f); + /// Checks if the target is within 0-yalm range. + protected bool In0y(Actor? target) => InRange(target, 0.01f); + + /// Checks if the target is within 3-yalm range. protected bool In3y(Actor? target) => InRange(target, 3.00f); + + /// Checks if the target is within 5-yalm range. protected bool In5y(Actor? target) => InRange(target, 5.00f); + + /// Checks if the target is within 10-yalm range. protected bool In10y(Actor? target) => InRange(target, 10.00f); + + /// Checks if the target is within 15-yalm range. protected bool In15y(Actor? target) => InRange(target, 15.00f); + + /// Checks if the target is within 20-yalm range. protected bool In20y(Actor? target) => InRange(target, 20.00f); + + /// Checks if the target is within 25-yalm range. protected bool In25y(Actor? target) => InRange(target, 25.00f); #endregion - /// - /// A simpler smart-targeting helper for picking a specific target over your current target. - /// Very useful for intricate planning of ability targeting in specific situations. - /// - /// The user's picked strategy's option Track, retrieved from module's enums and definitions. (e.g. strategy.Option(Track.NoMercy)) + #region Smart-Targeting + /// A simpler smart-targeting helper for picking a specific target over your current target.
Very useful for intricate planning of ability targeting in specific situations.
+ /// The user's selected strategy's option Track, retrieved from module's enums and definitions. (e.g. strategy.Option(Track.NoMercy)) /// protected Actor? TargetChoice(StrategyValues.OptionRef track) => ResolveTargetOverride(track.Value); - /// - /// Targeting function for indicating when AOE Circle abilities should be used based on nearby targets. - /// - /// The radius of the AOE Circle ability from the Player. - /// - /// A tuple with boolean values indicating whether the AOE Circle should be used based on the number of targets nearby:
- /// OnAny: At least 1 target is inside.
- /// OnTwoOrMore: At least 2 targets are inside.
- /// OnThreeOrMore: At least 3 targets are inside.
- /// OnFourOrMore: At least 4 targets are inside.
- /// OnFiveOrMore: At least 5 targets are inside. - ///
- protected (bool OnAny, bool OnTwoOrMore, bool OnThreeOrMore, bool OnFourOrMore, bool OnFiveOrMore) - ShouldUseAOECircle(float range) - { - var numTargets = Hints.NumPriorityTargetsInAOECircle(Player.Position, range); - return (numTargets > 0, // OnAny - numTargets > 1, // OnTwoOrMore - numTargets > 2, // OnThreeOrMore - numTargets > 3, // OnFourOrMore - numTargets > 4); // OnFiveOrMore - } - - /// - /// This function attempts to pick a suitable primary target automatically, even if a target is not already picked. - /// NOTE: This function is solely used for auto-targeting enemies without having a target selected, mainly for AI usage. Please use appropriately. - /// - /// The user's picked Strategy - /// The user's current specified Target. - /// + /// Attempts to select a suitable primary PvE target automatically. + /// NOTE: This function is solely used for auto-targeting enemies without having a target selected or for AI usage. Please use appropriately. + /// The user's selected strategy. + /// The user's current target. + /// The max range to consider a new target. protected void GetPvETarget(StrategyValues strategy, ref Enemy? primaryTarget, float range) { - var AOEStrat = strategy.Option(SharedTrack.AOE).As(); - if (AOEStrat is AOEStrategy.Automatic) + if (primaryTarget?.Actor == null || Player.DistanceToHitbox(primaryTarget.Actor) > range) { - if (Player.DistanceToHitbox(primaryTarget?.Actor) > range) + var AOEStrat = strategy.Option(SharedTrack.AOE).As(); + if (AOEStrat == AOEStrategy.Automatic) { - var newTarget = Hints.PriorityTargets.FirstOrDefault(x => Player.DistanceToHitbox(x.Actor) <= range); - if (newTarget != null) - primaryTarget = newTarget; + primaryTarget = Hints.PriorityTargets + .FirstOrDefault(x => Player.DistanceToHitbox(x.Actor) <= range); } } } - /// - /// This function attempts to pick the most suitable primary target automatically, prioritizing the target with the lowest HP percentage within range. - /// NOTE: This function is solely used for finding a PvP target without having to click on other targets. Please use appropriately. - /// - /// The user's current specified Target. - /// The max range to search for a new target. + /// Attempts to select the most suitable PvP target automatically, prioritizing the target with the lowest HP percentage within range. + /// NOTE: This function is solely used for finding the best PvP target without having to manually scan & click on other targets. Please use appropriately. + /// The user's current target. + /// The max range to consider a new target. protected void GetPvPTarget(ref Enemy? primaryTarget, float range) { - primaryTarget ??= PlayerTarget; - - if (Player.DistanceToHitbox(PlayerTarget?.Actor) > range) + if (primaryTarget?.Actor == null || Player.DistanceToHitbox(primaryTarget.Actor) > range) { - var newTarget = Hints.PriorityTargets - .Where(x => x != null && x.Actor != null && Player.DistanceToHitbox(x.Actor) <= range) + primaryTarget = Hints.PriorityTargets + .Where(x => Player.DistanceToHitbox(x.Actor) <= range && x.Actor.FindStatus(ClassShared.SID.Guard) == null) .OrderBy(x => (float)x.Actor.HPMP.CurHP / x.Actor.HPMP.MaxHP) .FirstOrDefault(); - - if (newTarget != null) - PlayerTarget = newTarget; } } + #endregion - /// - /// This function attempts to pick the best target automatically. - /// - /// The user's current picked Target. - /// - /// - /// - protected (Enemy? Best, int Targets) GetBestTarget(Enemy? primaryTarget, float range, PositionCheck isInAOE) => GetTarget(primaryTarget, range, isInAOE, (numTargets, _) => numTargets, a => a); - - /// - /// This function picks the target based on HP, modified by how many targets are in the AOE. - /// - /// The user's current picked Target. - /// - /// - /// - protected (Enemy? Best, int Targets) GetTargetByHP(Enemy? primaryTarget, float range, PositionCheck isInAOE) => GetTarget(primaryTarget, range, isInAOE, (numTargets, enemy) => (numTargets, numTargets > 2 ? enemy.HPMP.CurHP : 0), args => args.numTargets); + /// Targeting function for indicating when AOE Circle abilities should be used based on nearby targets. + /// The radius of the AOE Circle ability from the Player. + /// + /// A tuple with boolean values indicating whether the AOE Circle should be used based on the number of targets nearby:
+ /// - OnAny: At least 1 target is inside.
+ /// - OnTwoOrMore: At least 2 targets are inside.
+ /// - OnThreeOrMore: At least 3 targets are inside.
+ /// - OnFourOrMore: At least 4 targets are inside.
+ /// - OnFiveOrMore: At least 5 targets are inside. + ///
+ protected (bool OnAny, bool OnTwoOrMore, bool OnThreeOrMore, bool OnFourOrMore, bool OnFiveOrMore) ShouldUseAOECircle(float range) + { + var numTargets = Hints.NumPriorityTargetsInAOECircle(Player.Position, range); + return (numTargets > 0, numTargets > 1, numTargets > 2, numTargets > 3, numTargets > 4); + } - /// - /// Main function for picking a target, generalized for any prioritization and simplification logic. - /// + /// This function attempts to pick the best target automatically. + /// The user's current selected Target. + /// The range within which to evaluate potential targets. + /// A flag indicating if the target is within the Area of Effect (AOE). + protected (Enemy? Best, int Targets) GetBestTarget(Enemy? primaryTarget, float range, PositionCheck isInAOE) + => GetTarget(primaryTarget, range, isInAOE, (numTargets, _) => numTargets, a => a); + + /// This function picks the target based on HP, modified by how many targets are in the AOE. + /// The user's current selected Target. + /// The range within which to evaluate potential targets. + /// A flag indicating if the target is within the Area of Effect (AOE). + protected (Enemy? Best, int Targets) GetBestHPTarget(Enemy? primaryTarget, float range, PositionCheck isInAOE) + => GetTarget(primaryTarget, range, isInAOE, (numTargets, enemy) => (numTargets, numTargets > 2 ? enemy.HPMP.CurHP : 0), args => args.numTargets); + + /// Main function for picking a target, generalized for any prioritization and simplification logic. /// - /// The user's current picked Target. - /// - /// - /// - /// - /// + /// The user's current selected Target. + /// The range within which to evaluate potential targets. + /// A flag indicating if the target is within the Area of Effect (AOE). + /// A flag indicating whether prioritization of certain targets should occur. + /// A flag indicating whether the simplification of target selection should apply. protected (Enemy? Best, int Priority) GetTarget

(Enemy? primaryTarget, float range, PositionCheck isInAOE, PriorityFunc

prioritize, Func simplify) where P : struct, IComparable { P targetPrio(Actor potentialTarget) @@ -660,14 +600,11 @@ P targetPrio(Actor potentialTarget) return (newnewprio > 0 ? Hints.FindEnemy(newtarget) : null, newnewprio); } - ///

- /// Identify an appropriate target for applying DoT effect. This has no impact if any auto-targeting is disabled. - /// - /// - /// - /// - /// - /// + /// Identify an appropriate target for applying DoT effect. This has no impact if any auto-targeting is disabled. + /// The type representing the timer value, which must be a struct and implement . + /// The initial target to consider for applying the DoT effect. + /// A function that retrieves the timer value associated with a given actor. + /// The maximum number of valid targets to evaluate. protected (Enemy? Best, P Timer) GetDOTTarget

(Enemy? initial, Func getTimer, int maxAllowedTargets) where P : struct, IComparable { if (initial == null || maxAllowedTargets <= 0) @@ -702,38 +639,27 @@ P targetPrio(Actor potentialTarget) #endregion #region Positionals - ///

- /// Retrieves the current positional of the target based on target's position and rotation. - /// + /// Retrieves the current positional of the target based on target's position and rotation. /// The user's specified Target being checked. - /// - protected Positional GetCurrentPositional(Actor target) => (Player.Position - target.Position).Normalized().Dot(target.Rotation.ToDirection()) switch //Check current positional based on target + protected Positional GetCurrentPositional(Actor target) => (Player.Position - target.Position).Normalized().Dot(target.Rotation.ToDirection()) switch { - < -0.7071068f => Positional.Rear, //value for Rear positional - < 0.7071068f => Positional.Flank, //value for Flank positional - _ => Positional.Front //default to Front positional if not on Rear or Flank + < -0.7071068f => Positional.Rear, + < 0.7071068f => Positional.Flank, + _ => Positional.Front }; - /// - /// Checks if player is on specified target's Rear Positional. - /// + /// Checks if player is on specified target's Rear Positional. /// The user's specified Target being checked. - /// protected bool IsOnRear(Actor target) => GetCurrentPositional(target) == Positional.Rear; - /// - /// Checks if player is on specified target's Flank Positional. - /// + /// Checks if player is on specified target's Flank Positional. /// The user's specified Target being checked. - /// protected bool IsOnFlank(Actor target) => GetCurrentPositional(target) == Positional.Flank; #endregion #region AI - /// - /// Establishes a goal-zone for a single target within a specified range.
- /// Primarily utilized by caster-DPS jobs that lack a dedicated maximize-AOE function. - ///
+ /// Establishes a goal-zone for a single target within a specified range.
+ /// Primarily utilized by caster-DPS jobs that lack a dedicated maximize-AOE function.
/// The range within which the goal zone is applied. protected void GoalZoneSingle(float range) { @@ -741,9 +667,7 @@ protected void GoalZoneSingle(float range) Hints.GoalZones.Add(Hints.GoalSingleTarget(PlayerTarget.Actor, range)); } - /// - /// Defines a goal-zone using a combined strategy, factoring in AOE considerations. - /// + /// Defines a goal-zone using a combined strategy, factoring in AOE considerations. /// The strategy values that influence the goal zone logic. /// The base range for the goal zone. /// A function determining the area of effect. @@ -804,41 +728,27 @@ protected void AnyGoalZoneCombined(float range, Func fAoe, AID firs /// Checks if player is currently moving. protected bool IsMoving { get; private set; } - /// - /// Player's "actual" target; guaranteed to be an enemy. - /// + /// Player's actual target; guaranteed to be an enemy. protected Enemy? PlayerTarget { get; private set; } #endregion #region Shared Abilities - /// - /// Checks if player is able to execute the melee-DPS shared ability: True North - /// + /// Checks if player is able to execute the melee-DPS shared ability: True North protected bool CanTrueNorth { get; private set; } - /// - /// Checks if player is under the effect of the melee-DPS shared ability: True North - /// + /// Checks if player is under the effect of the melee-DPS shared ability: True North protected bool HasTrueNorth { get; private set; } - /// - /// Checks if player is able to execute the caster-DPS shared ability: Swiftcast - /// + /// Checks if player is able to execute the caster-DPS shared ability: Swiftcast protected bool CanSwiftcast { get; private set; } - /// - /// Checks if player is under the effect of the caster-DPS shared ability: Swiftcast - /// + /// Checks if player is under the effect of the caster-DPS shared ability: Swiftcast protected bool HasSwiftcast { get; private set; } - /// - /// Checks if player is able to execute the ranged-DPS shared ability: Peloton - /// + /// Checks if player is able to execute the ranged-DPS shared ability: Peloton protected bool CanPeloton { get; private set; } - /// - /// Checks if player is under the effect of the ranged-DPS shared ability: Peloton - /// + /// Checks if player is under the effect of the ranged-DPS shared ability: Peloton protected bool HasPeloton { get; private set; } #endregion @@ -866,12 +776,9 @@ public sealed override void Execute(StrategyValues strategy, ref Actor? primaryT Execution(strategy, PlayerTarget); } - /// - /// The core function responsible for orchestrating the execution of all abilities and strategies.
- ///
+ /// The core function responsible for orchestrating the execution of all abilities and strategies.
/// The user's specified Strategy. /// The user's specified Enemy. - /// - Primary execution of user's rotation module. public abstract void Execution(StrategyValues strategy, Enemy? primaryTarget); } @@ -880,7 +787,6 @@ static class ModuleExtensions #region Shared Definitions /// Defines our shared AOE (rotation) strategies. /// The definitions of our base module's strategies. - /// - Options for shared custom strategies to be used via AutoRotation or Cooldown Planner public static RotationModuleDefinition.ConfigRef DefineAOE(this RotationModuleDefinition res) { return res.Define(SharedTrack.AOE).As("AOE", uiPriority: 300) @@ -891,7 +797,6 @@ public static RotationModuleDefinition.ConfigRef DefineAOE(this Rot /// Defines our shared Hold strategies. /// The definitions of our base module's strategies. - /// - Options for shared custom strategies to be used via AutoRotation or Cooldown Planner public static RotationModuleDefinition.ConfigRef DefineHold(this RotationModuleDefinition res) { return res.Define(SharedTrack.Hold).As("Hold", uiPriority: 290) @@ -912,7 +817,6 @@ public static RotationModuleDefinition.ConfigRef DefineHold(this R /// The Targets Supported for the ability that the user is specifying. /// The Minimum Level required for the ability that the user is specifying. /// The Maximum Level required for the ability that the user is specifying. - /// - Basic GCD options for any specified ability to be used via AutoRotation or Cooldown Planner public static RotationModuleDefinition.ConfigRef DefineGCD(this RotationModuleDefinition res, Index track, AID aid, string internalName, string displayName = "", int uiPriority = 100, float cooldown = 0, float effectDuration = 0, ActionTargets supportedTargets = ActionTargets.None, int minLevel = 1, int maxLevel = 100) where Index : Enum where AID : Enum @@ -935,7 +839,6 @@ public static RotationModuleDefinition.ConfigRef DefineGCDThe Targets Supported for the ability that the user is specifying. /// The Minimum Level required for the ability that the user is specifying. /// The Maximum Level required for the ability that the user is specifying. - /// - Basic OGCD options for any specified ability to be used via AutoRotation or Cooldown Planner public static RotationModuleDefinition.ConfigRef DefineOGCD(this RotationModuleDefinition res, Index track, AID aid, string internalName, string displayName = "", int uiPriority = 100, float cooldown = 0, float effectDuration = 0, ActionTargets supportedTargets = ActionTargets.None, int minLevel = 1, int maxLevel = 100) where Index : Enum where AID : Enum From f1e9118363af79476303e5efaab81f6c07ee35ed Mon Sep 17 00:00:00 2001 From: ace Date: Wed, 19 Feb 2025 20:34:19 -0800 Subject: [PATCH 27/28] stuff --- .../Standard/akechi/AkechiTools.cs | 35 +++++++---- .../Standard/akechi/DPS/AkechiBLM.cs | 4 +- .../Standard/akechi/Tank/AkechiDRK.cs | 6 +- .../Standard/akechi/Tank/AkechiGNB.cs | 60 +++++++++---------- .../Standard/akechi/Tank/AkechiWAR.cs | 10 +++- 5 files changed, 64 insertions(+), 51 deletions(-) diff --git a/BossMod/Autorotation/Standard/akechi/AkechiTools.cs b/BossMod/Autorotation/Standard/akechi/AkechiTools.cs index 99d18d2017..21aafa6744 100644 --- a/BossMod/Autorotation/Standard/akechi/AkechiTools.cs +++ b/BossMod/Autorotation/Standard/akechi/AkechiTools.cs @@ -19,12 +19,18 @@ public enum SharedTrack } /// -/// AOEStrategy enum for tracking single-target and AOE strategies. +/// AOEStrategy enum for tracking single-target and AOE strategies. +/// NOTE: For jobs with combos that have no relative combo timer (e.g. BLM), and are essentially the same function. /// public enum AOEStrategy { - /// Executes the most optimal rotation automatically. - Automatic, + /// Executes the most optimal rotation automatically.
+ /// Finishes any combo if currently inside one.
+ AutoFinish, + + /// Executes the most optimal rotation automatically.
+ /// Breaks any combo if currently inside one.
+ AutoBreak, /// Forces execution of the single-target rotation. ForceST, @@ -334,9 +340,9 @@ protected bool CanWeave(AID aid, int extraGCDs = 0, float extraFixedDelay = 0) /// The user's specified Action ID being checked. protected float ChargeCD(AID aid) => Unlocked(aid) ? ActionDefinitions.Instance.Spell(aid)!.ReadyIn(World.Client.Cooldowns, World.Client.DutyActions) : float.MaxValue; - /// Checks if action is ready to be used based on if it's Unlocked and its charge cooldown timer. + /// Checks if action is ready to be used based on if it's Unlocked and its total cooldown timer. /// The user's specified Action ID being checked. - protected bool ActionReady(AID aid) => Unlocked(aid) && ChargeCD(aid) < 0.6f; + protected bool ActionReady(AID aid) => Unlocked(aid) && !IsOnCooldown(aid); /// Checks if action has any charges remaining. /// The user's specified Action ID being checked. @@ -526,16 +532,15 @@ protected void GetPvETarget(StrategyValues strategy, ref Enemy? primaryTarget, f if (primaryTarget?.Actor == null || Player.DistanceToHitbox(primaryTarget.Actor) > range) { var AOEStrat = strategy.Option(SharedTrack.AOE).As(); - if (AOEStrat == AOEStrategy.Automatic) + if (AOEStrat is AOEStrategy.AutoFinish or AOEStrategy.AutoBreak) { - primaryTarget = Hints.PriorityTargets - .FirstOrDefault(x => Player.DistanceToHitbox(x.Actor) <= range); + primaryTarget = Hints.PriorityTargets.FirstOrDefault(x => Player.DistanceToHitbox(x.Actor) <= range); } } } /// Attempts to select the most suitable PvP target automatically, prioritizing the target with the lowest HP percentage within range. - /// NOTE: This function is solely used for finding the best PvP target without having to manually scan & click on other targets. Please use appropriately. + /// NOTE: This function is solely used for finding the best PvP target without having to manually scan and click on other targets. Please use appropriately.
/// The user's current target. /// The max range to consider a new target. protected void GetPvPTarget(ref Enemy? primaryTarget, float range) @@ -790,9 +795,10 @@ static class ModuleExtensions public static RotationModuleDefinition.ConfigRef DefineAOE(this RotationModuleDefinition res) { return res.Define(SharedTrack.AOE).As("AOE", uiPriority: 300) - .AddOption(AOEStrategy.Automatic, "Auto", "Automatically execute optimal rotation based on targets", supportedTargets: ActionTargets.Hostile) + .AddOption(AOEStrategy.AutoFinish, "Auto (Finish combo)", "Automatically execute optimal rotation based on targets; finishes combo if possible", supportedTargets: ActionTargets.Hostile) + .AddOption(AOEStrategy.AutoBreak, "Auto (Break combo)", "Automatically execute optimal rotation based on targets; breaks combo if necessary", supportedTargets: ActionTargets.Hostile) .AddOption(AOEStrategy.ForceST, "ForceST", "Force-execute Single Target", supportedTargets: ActionTargets.Hostile) - .AddOption(AOEStrategy.ForceAOE, "ForceAOE", "Force-execute AOE rotation", supportedTargets: ActionTargets.Hostile); + .AddOption(AOEStrategy.ForceAOE, "ForceAOE", "Force-execute AOE rotation", supportedTargets: ActionTargets.Hostile | ActionTargets.Self); } /// Defines our shared Hold strategies. @@ -859,8 +865,11 @@ public static RotationModuleDefinition.ConfigRef DefineOGCDA global helper for easily retrieving the user's Rotation strategy. See for more details.
public static AOEStrategy Rotation(this StrategyValues strategy) => strategy.Option(SharedTrack.AOE).As(); - /// A global helper for automatically executing the best optimal rotation. See for more details. - public static bool Automatic(this StrategyValues strategy) => strategy.Option(SharedTrack.AOE).As() is AOEStrategy.Automatic; + /// A global helper for automatically executing the best optimal rotation; finishes combo if possible. See for more details. + public static bool AutoFinish(this StrategyValues strategy) => strategy.Option(SharedTrack.AOE).As() is AOEStrategy.AutoFinish; + + /// A global helper for automatically executing the best optimal rotation; breaks combo if necessary. See for more details. + public static bool AutoBreak(this StrategyValues strategy) => strategy.Option(SharedTrack.AOE).As() is AOEStrategy.AutoBreak; /// A global helper for force-executing the single-target rotation. See for more details. public static bool ForceST(this StrategyValues strategy) => strategy.Option(SharedTrack.AOE).As() is AOEStrategy.ForceST; diff --git a/BossMod/Autorotation/Standard/akechi/DPS/AkechiBLM.cs b/BossMod/Autorotation/Standard/akechi/DPS/AkechiBLM.cs index 0373f55bf2..ade6cef995 100644 --- a/BossMod/Autorotation/Standard/akechi/DPS/AkechiBLM.cs +++ b/BossMod/Autorotation/Standard/akechi/DPS/AkechiBLM.cs @@ -400,7 +400,7 @@ movingOption is CastingOption.Forbid && SelfStatusLeft(SID.Firestarter, 30) is < 25 and not 0 || //or can use F3P Unlocked(TraitID.EnhancedAstralFire) && MP is < 1600 and not 0)) //instant cast Despair { - if (strategy.Automatic()) + if (strategy.AutoFinish() || strategy.AutoBreak()) BestRotation(TargetChoice(AOE) ?? BestSplashTarget?.Actor); if (strategy.ForceST()) BestST(TargetChoice(AOE) ?? primaryTarget?.Actor); @@ -413,7 +413,7 @@ movingOption is CastingOption.Forbid && //Thunder if (ShouldUseThunder(BestSplashTarget?.Actor, thunderStrat)) //if Thunder should be used based on strategy { - if (strategy.Automatic()) + if (strategy.AutoFinish() || strategy.AutoBreak()) QueueGCD(BestThunder, TargetChoice(thunder) ?? (ShouldUseAOE ? BestSplashTargets?.Actor : BestDOTTarget?.Actor), thunderLeft <= 3 ? GCDPriority.NeedDOT : diff --git a/BossMod/Autorotation/Standard/akechi/Tank/AkechiDRK.cs b/BossMod/Autorotation/Standard/akechi/Tank/AkechiDRK.cs index 77df668606..275424b0ef 100644 --- a/BossMod/Autorotation/Standard/akechi/Tank/AkechiDRK.cs +++ b/BossMod/Autorotation/Standard/akechi/Tank/AkechiDRK.cs @@ -235,10 +235,14 @@ public override void Execution(StrategyValues strategy, Enemy? primaryTarget) #region Full Rotation Execution #region Standard Rotations - if (strategy.Automatic()) + if (strategy.AutoFinish()) QueueGCD(BestRotation(), TargetChoice(strategy.Option(SharedTrack.AOE)) ?? primaryTarget?.Actor, GCDPriority.Standard); + if (strategy.AutoBreak()) + QueueGCD(ShouldUseAOE ? AOE() : ST(), + TargetChoice(strategy.Option(SharedTrack.AOE)) ?? primaryTarget?.Actor, + GCDPriority.Standard); if (strategy.ForceST()) QueueGCD(ST(), TargetChoice(strategy.Option(SharedTrack.AOE)) ?? primaryTarget?.Actor, diff --git a/BossMod/Autorotation/Standard/akechi/Tank/AkechiGNB.cs b/BossMod/Autorotation/Standard/akechi/Tank/AkechiGNB.cs index 608eac2593..3c22305764 100644 --- a/BossMod/Autorotation/Standard/akechi/Tank/AkechiGNB.cs +++ b/BossMod/Autorotation/Standard/akechi/Tank/AkechiGNB.cs @@ -167,7 +167,7 @@ public enum OGCDPriority #region Upgrade Paths private AID BestZone => Unlocked(AID.BlastingZone) ? AID.BlastingZone : AID.DangerZone; - private AID BestCartSpender => ShouldUseFC ? BestFatedCircle : canBS ? AID.BurstStrike : BestRotation(); + private AID BestCartSpender => ShouldUseAOE ? BestFatedCircle : canBS ? AID.BurstStrike : BestRotation(); private AID BestFatedCircle => Unlocked(AID.FatedCircle) ? AID.FatedCircle : AID.BurstStrike; private AID BestContinuation => hasRaze ? AID.FatedBrand : hasBlast ? AID.Hypervelocity : hasGouge ? AID.EyeGouge : hasTear ? AID.AbdomenTear : hasRip ? AID.JugularRip : AID.Continuation; #endregion @@ -200,7 +200,6 @@ public enum OGCDPriority private bool canContinue; //Checks if Continuation is completely available private bool canReign; //Checks if Reign of Beasts & its combo chain are completely available private bool ShouldUseAOE; //Checks if AOE rotation should be used - private bool ShouldUseFC; //Checks if Fated Circle should be used private int NumSplashTargets; private Enemy? BestSplashTargets; private Enemy? BestSplashTarget; @@ -225,21 +224,20 @@ public override void Execution(StrategyValues strategy, Enemy? primaryTarget) hasTear = PlayerHasEffect(SID.ReadyToTear, 10f) && !LastActionUsed(AID.AbdomenTear); //Checks for Ready To Tear buff hasGouge = PlayerHasEffect(SID.ReadyToGouge, 10f) && !LastActionUsed(AID.EyeGouge); //Checks for Ready To Gouge buff inOdd = bfCD is <= 90 and >= 30; //Checks if we are in an odd-minute window - ShouldUseAOE = Unlocked(TraitID.MeleeMastery) ? ShouldUseAOECircle(5).OnThreeOrMore : ShouldUseAOECircle(5).OnTwoOrMore; - ShouldUseFC = ShouldUseAOECircle(5).OnTwoOrMore; //Determine if we should use Fated Circle + ShouldUseAOE = ShouldUseAOECircle(5).OnTwoOrMore; (BestSplashTargets, NumSplashTargets) = GetBestTarget(primaryTarget, 3, IsSplashTarget); BestSplashTarget = Unlocked(AID.ReignOfBeasts) && NumSplashTargets > 1 ? BestSplashTargets : primaryTarget; #region Minimal Requirements - canNM = TotalCD(AID.NoMercy) < 1; //No Mercy conditions + canNM = ActionReady(AID.NoMercy); //No Mercy conditions canBS = Unlocked(AID.BurstStrike) && Ammo > 0; //Burst Strike conditions; -1 Ammo ST - canGF = Unlocked(AID.GnashingFang) && ActionReady(AID.GnashingFang) && Ammo > 0; //Gnashing Fang conditions; -1 Ammo ST + canGF = ActionReady(AID.GnashingFang) && Ammo > 0; //Gnashing Fang conditions; -1 Ammo ST canFC = Unlocked(AID.FatedCircle) && Ammo > 0; //Fated Circle conditions; -1 Ammo AOE - canDD = Unlocked(AID.DoubleDown) && ActionReady(AID.DoubleDown) && Ammo > 0; //Double Down conditions; -1 Ammo AOE - canBF = Unlocked(AID.Bloodfest) && ActionReady(AID.Bloodfest); //Bloodfest conditions; +all Ammo (must have target) - canZone = Unlocked(AID.DangerZone) && ActionReady(AID.DangerZone); //Zone conditions - canBreak = hasBreak && Unlocked(AID.SonicBreak); //Sonic Break conditions - canBow = Unlocked(AID.BowShock) && ActionReady(AID.BowShock); //Bow Shock conditions + canDD = ActionReady(AID.DoubleDown) && Ammo > 0; //Double Down conditions; -1 Ammo AOE + canBF = ActionReady(AID.Bloodfest); //Bloodfest conditions; +all Ammo (must have target) + canZone = ActionReady(AID.DangerZone); //Zone conditions + canBreak = Unlocked(AID.SonicBreak) && hasBreak; //Sonic Break conditions + canBow = ActionReady(AID.BowShock); //Bow Shock conditions canContinue = Unlocked(AID.Continuation); //Continuation conditions canReign = Unlocked(AID.ReignOfBeasts) && hasReign; //Reign of Beasts conditions #endregion @@ -302,34 +300,34 @@ public override void Execution(StrategyValues strategy, Enemy? primaryTarget) //TODO: refactor this if (AOEStrategy == AOEStrategy.GenerateDowntime) //if Generate Downtime option is selected { - if (DowntimeIn == GCD * 2 && Ammo == 2 || //if 2 GCDs until downtime & has 2 cartridges - DowntimeIn == GCD * 4 && Ammo == 1 || //if 4 GCDs until downtime & has 1 cartridge - DowntimeIn == GCD * 6 && Ammo == 0) //if 6 GCDs until downtime & has 0 cartridges + if (DowntimeIn == SkSGCDLength * 2 && Ammo == 2 || //if 2 GCDs until downtime & has 2 cartridges + DowntimeIn == SkSGCDLength * 4 && Ammo == 1 || //if 4 GCDs until downtime & has 1 cartridge + DowntimeIn == SkSGCDLength * 6 && Ammo == 0) //if 6 GCDs until downtime & has 0 cartridges QueueGCD(AID.DemonSlice, //queue Demon Slice Player, //on Self (no target needed) GCDPriority.ForcedCombo); //with priority for forced GCDs - if (DowntimeIn == GCD * 3 && Ammo == 2 || //if 3 GCDs until downtime & has 2 cartridges - DowntimeIn == GCD * 5 && Ammo == 1 || //if 5 GCDs until downtime & has 1 cartridge - DowntimeIn == GCD * 8 && Ammo == 0 || //if 8 GCDs until downtime & has 0 cartridges - DowntimeIn == GCD * 9 && Ammo == 0) //if 9 GCDs until downtime & has 0 cartridges + if (DowntimeIn == SkSGCDLength * 3 && Ammo == 2 || //if 3 GCDs until downtime & has 2 cartridges + DowntimeIn == SkSGCDLength * 5 && Ammo == 1 || //if 5 GCDs until downtime & has 1 cartridge + DowntimeIn == SkSGCDLength * 8 && Ammo == 0 || //if 8 GCDs until downtime & has 0 cartridges + DowntimeIn == SkSGCDLength * 9 && Ammo == 0) //if 9 GCDs until downtime & has 0 cartridges QueueGCD(AID.KeenEdge, //queue Keen Edge primaryTarget?.Actor, //on the primary target GCDPriority.ForcedCombo); //with priority for forced GCDs if (ComboLastMove == AID.DemonSlice && //if last move was Demon Slice (DowntimeIn == GCD && Ammo == 2 || //if 1 GCD until downtime & has 2 cartridges - DowntimeIn == GCD * 3 && Ammo == 1 || //if 3 GCDs until downtime & has 1 cartridge - DowntimeIn == GCD * 5 && Ammo == 0)) //if 5 GCDs until downtime & has 0 cartridges + DowntimeIn == SkSGCDLength * 3 && Ammo == 1 || //if 3 GCDs until downtime & has 1 cartridge + DowntimeIn == SkSGCDLength * 5 && Ammo == 0)) //if 5 GCDs until downtime & has 0 cartridges QueueGCD(AID.DemonSlaughter, //queue Demon Slaughter Player, //on Self (no target needed) GCDPriority.ForcedCombo); //with priority for forced GCDs if (ComboLastMove == AID.KeenEdge && //if last move was Keen Edge - (DowntimeIn == GCD * 2 && Ammo == 2 || //if 2 GCDs until downtime & has 2 cartridges - DowntimeIn == GCD * 4 && Ammo == 1 || //if 4 GCDs until downtime & has 1 cartridge - DowntimeIn == GCD * 7 && Ammo == 2 || //if 7 GCDs until downtime & has 2 cartridges - DowntimeIn == GCD * 8 && Ammo == 2)) //if 8 GCDs until downtime & has 2 cartridges + (DowntimeIn == SkSGCDLength * 2 && Ammo == 2 || //if 2 GCDs until downtime & has 2 cartridges + DowntimeIn == SkSGCDLength * 4 && Ammo == 1 || //if 4 GCDs until downtime & has 1 cartridge + DowntimeIn == SkSGCDLength * 7 && Ammo == 2 || //if 7 GCDs until downtime & has 2 cartridges + DowntimeIn == SkSGCDLength * 8 && Ammo == 2)) //if 8 GCDs until downtime & has 2 cartridges QueueGCD(AID.BrutalShell, //queue Brutal Shell primaryTarget?.Actor, //on the primary target GCDPriority.ForcedCombo); //with priority for forced GCDs @@ -337,8 +335,8 @@ public override void Execution(StrategyValues strategy, Enemy? primaryTarget) if (ComboLastMove == AID.BrutalShell) //if last move was Brutal Shell { if (DowntimeIn == GCD && (Ammo == 2 || Ammo == 3) || //if 1 GCD until downtime & has 2 or 3 cartridges - DowntimeIn == GCD * 4 && Ammo == 1 || //if 4 GCDs until downtime & has 1 cartridge - DowntimeIn == GCD * 7 && Ammo == 0) //if 7 GCDs until downtime & has 0 cartridges + DowntimeIn == SkSGCDLength * 4 && Ammo == 1 || //if 4 GCDs until downtime & has 1 cartridge + DowntimeIn == SkSGCDLength * 7 && Ammo == 0) //if 7 GCDs until downtime & has 0 cartridges QueueGCD(AID.SolidBarrel, //queue Solid Barrel primaryTarget?.Actor, //on the primary target GCDPriority.ForcedCombo); //with priority for forced GCDs @@ -540,10 +538,8 @@ reignStrat is ReignStrategy.ForceLion private bool ShouldUseNoMercy(NoMercyStrategy strategy, Actor? target) => strategy switch { NoMercyStrategy.Automatic => Player.InCombat && target != null && canNM && - ((Unlocked(AID.DoubleDown) && (inOdd && Ammo >= 2 || !inOdd && Ammo < 3)) || - (!Unlocked(AID.DoubleDown) && CanQuarterWeaveIn && - ((Unlocked(AID.Bloodfest) && Ammo >= 1) || (!Unlocked(AID.Bloodfest) && canGF) || - !Unlocked(AID.GnashingFang)))), + ((Unlocked(AID.DoubleDown) && (inOdd && Ammo >= 2 || !inOdd && Ammo < 3)) || //90+ + (!Unlocked(AID.DoubleDown) && CanQuarterWeaveIn && Ammo >= 1)), //2-89 NoMercyStrategy.Force => canNM, NoMercyStrategy.ForceW => canNM && CanWeaveIn, NoMercyStrategy.ForceQW => canNM && CanQuarterWeaveIn, @@ -630,9 +626,9 @@ reignStrat is ReignStrategy.ForceLion private bool ShouldSpendCarts(CartridgeStrategy strategy, Actor? target) => strategy switch { CartridgeStrategy.Automatic => Player.InCombat && target != null && - (ShouldUseFC ? (In5y(target) && canFC) : (In3y(target) && canBS)) && + ((ShouldUseAOE ? (In5y(target) && canFC) : (In3y(target) && canBS)) && (hasNM || (!(bfCD is <= 90 and >= 30) && nmCD < 1 && Ammo == 3)) || - Ammo == MaxCartridges && ComboLastMove is AID.BrutalShell or AID.DemonSlice, + (Ammo == MaxCartridges && ComboLastMove is AID.BrutalShell or AID.DemonSlice)), _ => false }; private bool ShouldUseSonicBreak(SonicBreakStrategy strategy, Actor? target) => strategy switch diff --git a/BossMod/Autorotation/Standard/akechi/Tank/AkechiWAR.cs b/BossMod/Autorotation/Standard/akechi/Tank/AkechiWAR.cs index 1f2ab1dbcf..4fe6f3c4ac 100644 --- a/BossMod/Autorotation/Standard/akechi/Tank/AkechiWAR.cs +++ b/BossMod/Autorotation/Standard/akechi/Tank/AkechiWAR.cs @@ -150,12 +150,12 @@ public enum GCDPriority { None = 0, Gauge = 300, - PrimalRuination = 400, DelayFC = 390, Standard = 400, FlexibleFC = 470, FlexibleIR = 490, PrimalRend = 500, + PrimalRuination = 500, BuffedFC = 550, BuffedIR = 570, NeedTempest = 650, @@ -350,10 +350,14 @@ public override void Execution(StrategyValues strategy, Enemy? primaryTarget) #region Full Rotation Execution #region Standard Rotations - if (strategy.Automatic()) + if (strategy.AutoFinish()) QueueGCD(BestRotation(), TargetChoice(strategy.Option(SharedTrack.AOE)) ?? primaryTarget?.Actor, - IsRiskingGauge() ? GCDPriority.Standard - 400 : GCDPriority.ForcedCombo); + IsRiskingGauge() ? GCDPriority.Standard - 400 : GCDPriority.Standard); + if (strategy.AutoBreak()) + QueueGCD(ShouldUseAOE ? AOE() : ST(), + TargetChoice(strategy.Option(SharedTrack.AOE)) ?? primaryTarget?.Actor, + IsRiskingGauge() ? GCDPriority.Standard - 400 : GCDPriority.Standard); if (strategy.ForceST()) QueueGCD(ST(), TargetChoice(strategy.Option(SharedTrack.AOE)) ?? primaryTarget?.Actor, From 5e154b1c704b77fcf5d36c447fb772f4d657eeb3 Mon Sep 17 00:00:00 2001 From: ace Date: Thu, 20 Feb 2025 14:35:39 -0800 Subject: [PATCH 28/28] Requested Changes --- .../Autorotation/Utility/ClassDNCUtility.cs | 24 +------- .../Autorotation/Utility/ClassDRGUtility.cs | 24 +------- .../Autorotation/Utility/ClassPCTUtility.cs | 23 +------- .../Autorotation/Utility/ClassPLDUtility.cs | 29 +--------- .../Autorotation/Utility/ClassRPRUtility.cs | 58 +------------------ 5 files changed, 6 insertions(+), 152 deletions(-) diff --git a/BossMod/Autorotation/Utility/ClassDNCUtility.cs b/BossMod/Autorotation/Utility/ClassDNCUtility.cs index 19f2fc140f..f86c89668d 100644 --- a/BossMod/Autorotation/Utility/ClassDNCUtility.cs +++ b/BossMod/Autorotation/Utility/ClassDNCUtility.cs @@ -2,9 +2,8 @@ public sealed class ClassDNCUtility(RotationModuleManager manager, Actor player) : RoleRangedUtility(manager, player) { - public enum Track { CuringWaltz = SharedTrack.Count, ShieldSamba, Improvisation, EnAvant } + public enum Track { CuringWaltz = SharedTrack.Count, ShieldSamba, Improvisation } public enum SambaOption { None, Use87, Use87IfNotActive, Use88, Use88IfNotActive } - public enum EnAvantStrategy { None, CharacterForward, CharacterBackward, CameraForward, CameraBackward } public static readonly ActionID IDLimitBreak3 = ActionID.MakeSpell(DNC.AID.CrimsonLotus); @@ -25,14 +24,6 @@ public static RotationModuleDefinition Definition() DefineSimpleConfig(res, Track.Improvisation, "Improvisation", "Improv", 300, DNC.AID.Improvisation, 15); - res.Define(Track.EnAvant).As("En Avant", "EnAvant", 30) - .AddOption(EnAvantStrategy.None, "None", "No use.", 0, 0, ActionTargets.Self, 50) - .AddOption(EnAvantStrategy.CharacterForward, "CharacterForward", "Dashes in the Forward direction relative to the Character", 30, 0, ActionTargets.Self, 50) - .AddOption(EnAvantStrategy.CharacterBackward, "CharacterBackward", "Dashes in the Backward direction relative to the Character", 30, 0, ActionTargets.Self, 50) - .AddOption(EnAvantStrategy.CameraForward, "CameraForward", "Dashes in the Forward direction relative to the Camera", 30, 0, ActionTargets.Self, 50) - .AddOption(EnAvantStrategy.CameraBackward, "CameraBackward", "Dashes in the Backward direction relative to the Camera", 30, 0, ActionTargets.Self, 50) - .AddAssociatedActions(DNC.AID.EnAvant); - return res; } @@ -53,18 +44,5 @@ public override void Execute(StrategyValues strategy, ref Actor? primaryTarget, }; if (wantSamba) Hints.ActionsToExecute.Push(ActionID.MakeSpell(DNC.AID.ShieldSamba), Player, samba.Priority(), samba.Value.ExpireIn); - - var ea = strategy.Option(Track.EnAvant); - if (ea.As() != EnAvantStrategy.None) - { - var angle = ea.As() switch - { - EnAvantStrategy.CharacterBackward => Player.Rotation + 180.Degrees(), - EnAvantStrategy.CameraForward => World.Client.CameraAzimuth + 180.Degrees(), - EnAvantStrategy.CameraBackward => World.Client.CameraAzimuth, - _ => Player.Rotation - }; - Hints.ActionsToExecute.Push(ActionID.MakeSpell(DNC.AID.EnAvant), Player, ea.Priority(), ea.Value.ExpireIn, facingAngle: angle); - } } } diff --git a/BossMod/Autorotation/Utility/ClassDRGUtility.cs b/BossMod/Autorotation/Utility/ClassDRGUtility.cs index 63e558dfbd..bc6afba67f 100644 --- a/BossMod/Autorotation/Utility/ClassDRGUtility.cs +++ b/BossMod/Autorotation/Utility/ClassDRGUtility.cs @@ -2,9 +2,8 @@ public sealed class ClassDRGUtility(RotationModuleManager manager, Actor player) : RoleMeleeUtility(manager, player) { - public enum Track { WingedGlide = SharedTrack.Count, ElusiveJump } + public enum Track { WingedGlide = SharedTrack.Count } public enum DashStrategy { None, GapClose, GapCloseHold1 } - public enum ElusiveStrategy { None, CharacterForward, CharacterBackward, CameraForward, CameraBackward } public static readonly ActionID IDLimitBreak3 = ActionID.MakeSpell(DRG.AID.DragonsongDive); @@ -19,14 +18,6 @@ public static RotationModuleDefinition Definition() .AddOption(DashStrategy.GapCloseHold1, "GapCloseHold1", "Use as gapcloser if outside melee range; conserves 1 charge for manual usage", 60, 0, ActionTargets.Hostile, 84) .AddAssociatedActions(DRG.AID.WingedGlide); - res.Define(Track.ElusiveJump).As("Elusive Jump", "E.Jump", 30) - .AddOption(ElusiveStrategy.None, "None", "No use.", 0, 0, ActionTargets.Self, 35) - .AddOption(ElusiveStrategy.CharacterForward, "CharacterForward", "Dashes in the Forward direction relative to the Character", 30, 15, ActionTargets.Self, 35) - .AddOption(ElusiveStrategy.CharacterBackward, "CharacterBackward", "Dashes in the Backward direction relative to the Character", 30, 15, ActionTargets.Self, 35) - .AddOption(ElusiveStrategy.CameraForward, "CameraForward", "Dashes in the Forward direction relative to the Camera", 30, 15, ActionTargets.Self, 35) - .AddOption(ElusiveStrategy.CameraBackward, "CameraBackward", "Dashes in the Backward direction relative to the Camera", 30, 15, ActionTargets.Self, 35) - .AddAssociatedActions(DRG.AID.ElusiveJump); - return res; } @@ -34,19 +25,6 @@ public override void Execute(StrategyValues strategy, ref Actor? primaryTarget, { ExecuteShared(strategy, IDLimitBreak3, primaryTarget); - var ej = strategy.Option(Track.ElusiveJump); - if (ej.As() != ElusiveStrategy.None) - { - var angle = ej.As() switch - { - ElusiveStrategy.CharacterForward => Player.Rotation + 180.Degrees(), - ElusiveStrategy.CameraBackward => World.Client.CameraAzimuth + 180.Degrees(), - ElusiveStrategy.CameraForward => World.Client.CameraAzimuth, - _ => Player.Rotation - }; - Hints.ActionsToExecute.Push(ActionID.MakeSpell(DRG.AID.ElusiveJump), Player, ej.Priority(), ej.Value.ExpireIn, facingAngle: angle); - } - var dash = strategy.Option(Track.WingedGlide); var dashStrategy = strategy.Option(Track.WingedGlide).As(); var dashTarget = ResolveTargetOverride(dash.Value) ?? primaryTarget; //Smart-Targeting diff --git a/BossMod/Autorotation/Utility/ClassPCTUtility.cs b/BossMod/Autorotation/Utility/ClassPCTUtility.cs index 5271c3ab16..1fe5d225d2 100644 --- a/BossMod/Autorotation/Utility/ClassPCTUtility.cs +++ b/BossMod/Autorotation/Utility/ClassPCTUtility.cs @@ -2,9 +2,8 @@ public sealed class ClassPCTUtility(RotationModuleManager manager, Actor player) : RoleCasterUtility(manager, player) { - public enum Track { TemperaCoat = SharedTrack.Count, Smudge } + public enum Track { TemperaCoat = SharedTrack.Count } public enum TemperaCoatOption { None, CoatOnly, CoatGrassaASAP, CoatGrassaWhenever } - public enum SmudgeStrategy { None, CharacterForward, CharacterBackward, CameraForward, CameraBackward } public static readonly ActionID IDLimitBreak3 = ActionID.MakeSpell(PCT.AID.ChromaticFantasy); @@ -20,14 +19,6 @@ public static RotationModuleDefinition Definition() .AddOption(TemperaCoatOption.CoatGrassaWhenever, "Tempera Coat + Grassa when available", "Use Tempera Coat + Tempera Grassa when weaving or not casting", 90, 10, ActionTargets.Self, 88) .AddAssociatedActions(PCT.AID.TemperaCoat, PCT.AID.TemperaGrassa); - res.Define(Track.Smudge).As("Smudge", uiPriority: 30) - .AddOption(SmudgeStrategy.None, "None", "Do not use automatically", 0, 0, ActionTargets.Self, 20) - .AddOption(SmudgeStrategy.CharacterForward, "CharacterForward", "Dashes in the Forward direction relative to the Character", 20, 5, ActionTargets.Self, 20) - .AddOption(SmudgeStrategy.CharacterBackward, "CharacterBackward", "Dashes in the Backward direction relative to the Character", 20, 5, ActionTargets.Self, 20) - .AddOption(SmudgeStrategy.CameraForward, "CameraForward", "Dashes in the Forward direction relative to the Camera", 20, 5, ActionTargets.Self, 20) - .AddOption(SmudgeStrategy.CameraBackward, "CameraBackward", "Dashes in the Backward direction relative to the Camera", 20, 5, ActionTargets.Self, 20) - .AddAssociatedActions(PCT.AID.Smudge); - return res; } @@ -63,17 +54,5 @@ public override void Execute(StrategyValues strategy, ref Actor? primaryTarget, Hints.ActionsToExecute.Push(ActionID.MakeSpell(PCT.AID.TemperaGrassa), Player, tempera.Priority(), tempera.Value.ExpireIn); } } - var smuh = strategy.Option(Track.Smudge); - if (smuh.As() != SmudgeStrategy.None) - { - var angle = smuh.As() switch - { - SmudgeStrategy.CharacterBackward => Player.Rotation + 180.Degrees(), - SmudgeStrategy.CameraForward => World.Client.CameraAzimuth, - SmudgeStrategy.CameraBackward => World.Client.CameraAzimuth + 180.Degrees(), - _ => Player.Rotation - }; - Hints.ActionsToExecute.Push(ActionID.MakeSpell(PCT.AID.Smudge), Player, smuh.Priority(), smuh.Value.ExpireIn, facingAngle: angle); - } } } diff --git a/BossMod/Autorotation/Utility/ClassPLDUtility.cs b/BossMod/Autorotation/Utility/ClassPLDUtility.cs index e2ec8205e3..5a472c3dab 100644 --- a/BossMod/Autorotation/Utility/ClassPLDUtility.cs +++ b/BossMod/Autorotation/Utility/ClassPLDUtility.cs @@ -1,13 +1,10 @@ -using static BossMod.Autorotation.ClassPCTUtility; - -namespace BossMod.Autorotation; +namespace BossMod.Autorotation; public sealed class ClassPLDUtility(RotationModuleManager manager, Actor player) : RoleTankUtility(manager, player) { - public enum Track { Sheltron = SharedTrack.Count, Sentinel, Cover, Bulwark, DivineVeil, PassageOfArms, HallowedGround } //What we're tracking + public enum Track { Sheltron = SharedTrack.Count, Sentinel, Cover, Bulwark, DivineVeil, HallowedGround } //What we're tracking public enum ShelOption { None, Sheltron, HolySheltron, Intervention } //Sheltron Options public enum SentOption { None, Sentinel, Guardian } //Sentinel enhancement - public enum ArmsDirection { None, CharacterForward, CharacterBackward, CameraForward, CameraBackward } public static readonly ActionID IDLimitBreak3 = ActionID.MakeSpell(PLD.AID.LastBastion); //LB public static readonly ActionID IDStanceApply = ActionID.MakeSpell(PLD.AID.IronWill); //StanceOn @@ -35,15 +32,6 @@ public static RotationModuleDefinition Definition() DefineSimpleConfig(res, Track.Cover, "Cover", "", 320, PLD.AID.Cover, 12); //120s CD, 12s duration, -50 OathGauge cost DefineSimpleConfig(res, Track.Bulwark, "Bulwark", "Bul", 450, PLD.AID.Bulwark, 10); //90s CD, 15s duration DefineSimpleConfig(res, Track.DivineVeil, "DivineVeil", "Veil", 220, PLD.AID.DivineVeil, 30); //90s CD, 30s duration - - res.Define(Track.PassageOfArms).As("PassageOfArms", "PoA", 400) //PassageOfArms definition for CD plans - .AddOption(ArmsDirection.None, "None", "Do not use automatically") - .AddOption(ArmsDirection.CharacterForward, "CharacterForward", "Faces the Forward direction relative to the Character", 120, 18, ActionTargets.Self, 70) - .AddOption(ArmsDirection.CharacterBackward, "CharacterBackward", "Faces the Backward direction relative to the Character", 120, 18, ActionTargets.Self, 70) - .AddOption(ArmsDirection.CameraForward, "CameraForward", "Faces the Forward direction relative to the Camera", 120, 18, ActionTargets.Self, 70) - .AddOption(ArmsDirection.CameraBackward, "CameraBackward", "Faces the Backward direction relative to the Camera", 120, 18, ActionTargets.Self, 70) - .AddAssociatedActions(PLD.AID.PassageOfArms); - DefineSimpleConfig(res, Track.HallowedGround, "HallowedGround", "Inv", 400, PLD.AID.HallowedGround, 10); //420s CD, 10s duration return res; @@ -77,18 +65,5 @@ public override void Execute(StrategyValues strategy, ref Actor? primaryTarget, }; if (sentAction != default) Hints.ActionsToExecute.Push(ActionID.MakeSpell(sentAction), Player, sent.Priority(), sent.Value.ExpireIn); //Sentinel execution - - var poa = strategy.Option(Track.PassageOfArms); - if (poa.As() != ArmsDirection.None) - { - var angle = poa.As() switch - { - ArmsDirection.CharacterBackward => Player.Rotation + 180.Degrees(), - ArmsDirection.CameraForward => World.Client.CameraAzimuth + 180.Degrees(), - ArmsDirection.CameraBackward => World.Client.CameraAzimuth, - _ => Player.Rotation - }; - Hints.ActionsToExecute.Push(ActionID.MakeSpell(PLD.AID.PassageOfArms), Player, poa.Priority(), poa.Value.ExpireIn, facingAngle: angle); - } } } diff --git a/BossMod/Autorotation/Utility/ClassRPRUtility.cs b/BossMod/Autorotation/Utility/ClassRPRUtility.cs index b9a171ffff..cd407e4153 100644 --- a/BossMod/Autorotation/Utility/ClassRPRUtility.cs +++ b/BossMod/Autorotation/Utility/ClassRPRUtility.cs @@ -2,10 +2,7 @@ public sealed class ClassRPRUtility(RotationModuleManager manager, Actor player) : RoleMeleeUtility(manager, player) { - public enum Track { ArcaneCrest = SharedTrack.Count, Ingress, Egress, Regress } - public enum IngressStrategy { None, CharacterForward, CharacterBackward, CameraForward, CameraBackward } - public enum EgressStrategy { None, CharacterForward, CharacterBackward, CameraForward, CameraBackward } - public enum RegressStrategy { None, Use } + public enum Track { ArcaneCrest = SharedTrack.Count } public static readonly ActionID IDLimitBreak3 = ActionID.MakeSpell(RPR.AID.TheEnd); @@ -16,27 +13,6 @@ public static RotationModuleDefinition Definition() DefineSimpleConfig(res, Track.ArcaneCrest, "Crest", "", 600, RPR.AID.ArcaneCrest, 5); - res.Define(Track.Ingress).As("Hell's Ingress", "Ingress", 30) - .AddOption(IngressStrategy.None, "None", "No use.", 0, 0, ActionTargets.Self, 20) - .AddOption(IngressStrategy.CharacterForward, "CharacterForward", "Dashes in the Forward direction relative to the Character", 20, 10, ActionTargets.Self, 20) - .AddOption(IngressStrategy.CharacterBackward, "CharacterBackward", "Dashes in the Backward direction relative to the Character", 20, 10, ActionTargets.Self, 20) - .AddOption(IngressStrategy.CameraForward, "CameraForward", "Dashes in the Forward direction relative to the Camera", 20, 10, ActionTargets.Self, 20) - .AddOption(IngressStrategy.CameraBackward, "CameraBackward", "Dashes in the Backward direction relative to the Camera", 20, 10, ActionTargets.Self, 20) - .AddAssociatedActions(RPR.AID.HellsIngress); - - res.Define(Track.Egress).As("Hell's Egress", "Egress", 30) - .AddOption(EgressStrategy.None, "None", "No use.", 0, 0, ActionTargets.Self, 20) - .AddOption(EgressStrategy.CharacterForward, "CharacterForward", "Dashes in the Forward direction relative to the Character", 20, 10, ActionTargets.Self, 20) - .AddOption(EgressStrategy.CharacterBackward, "CharacterBackward", "Dashes in the Backward direction relative to the Character", 20, 10, ActionTargets.Self, 20) - .AddOption(EgressStrategy.CameraForward, "CameraForward", "Dashes in the Forward direction relative to the Camera", 20, 10, ActionTargets.Self, 20) - .AddOption(EgressStrategy.CameraBackward, "CameraBackward", "Dashes in the Backward direction relative to the Camera", 20, 10, ActionTargets.Self, 20) - .AddAssociatedActions(RPR.AID.HellsEgress); - - res.Define(Track.Regress).As("Regress", "Regress", 30) - .AddOption(RegressStrategy.None, "None", "No use.", 0, 0, ActionTargets.Self, 74) - .AddOption(RegressStrategy.Use, "Use", "Use Regress", 0, 0, ActionTargets.Self, 74) - .AddAssociatedActions(RPR.AID.Regress); - return res; } @@ -44,37 +20,5 @@ public override void Execute(StrategyValues strategy, ref Actor? primaryTarget, { ExecuteShared(strategy, IDLimitBreak3, primaryTarget); ExecuteSimple(strategy.Option(Track.ArcaneCrest), RPR.AID.ArcaneCrest, Player); - - var reg = strategy.Option(Track.Regress); - var regStrat = strategy.Option(Track.Regress).As(); - var zone = World.Actors.FirstOrDefault(x => x.OID == 0x4C3 && x.OwnerID == Player.InstanceID); - if (regStrat != RegressStrategy.None && Player.FindStatus(RPR.SID.Threshold) != null) - Hints.ActionsToExecute.Push(ActionID.MakeSpell(RPR.AID.Regress), Player, reg.Priority(), reg.Value.ExpireIn, targetPos: zone!.PosRot.XYZ()); - - var ing = strategy.Option(Track.Ingress); - if (ing.As() != IngressStrategy.None && Player.FindStatus(RPR.SID.Threshold) == null) - { - var angle = ing.As() switch - { - IngressStrategy.CharacterBackward => Player.Rotation + 180.Degrees(), - IngressStrategy.CameraForward => World.Client.CameraAzimuth + 180.Degrees(), - IngressStrategy.CameraBackward => World.Client.CameraAzimuth, - _ => Player.Rotation - }; - Hints.ActionsToExecute.Push(ActionID.MakeSpell(RPR.AID.HellsIngress), Player, ing.Priority(), ing.Value.ExpireIn, facingAngle: angle); - } - - var egg = strategy.Option(Track.Egress); - if (egg.As() != EgressStrategy.None && Player.FindStatus(RPR.SID.Threshold) == null) - { - var angle = egg.As() switch - { - EgressStrategy.CharacterForward => Player.Rotation + 180.Degrees(), - EgressStrategy.CameraBackward => World.Client.CameraAzimuth + 180.Degrees(), - EgressStrategy.CameraForward => World.Client.CameraAzimuth, - _ => Player.Rotation - }; - Hints.ActionsToExecute.Push(ActionID.MakeSpell(RPR.AID.HellsEgress), Player, egg.Priority(), egg.Value.ExpireIn, facingAngle: angle); - } } }