(SID sid) where SID : Enum => StacksRemaining(Player, sid) > 0;
#endregion
@@ -753,162 +419,179 @@ protected bool CanWeave(AID aid, int extraGCDs = 0, float extraFixedDelay = 0)
#region Targeting
#region Position Checks
- ///
- /// Checks precise positioning between player target and any other targets.
- ///
+
+ #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.
- ///
+
+ /// 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);
- ///
- /// Position checker for determining the best target for an ability that deals Splash damage.
- ///
+ #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);
- ///
- /// Position checker for determining the best target for an ability that deals damage in a Cone .
- ///
- protected PositionCheck IsConeTarget => (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 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-five (25) yalms
- ///
- protected PositionCheck Is25yRectTarget => (primary, other) => Hints.TargetInAOERect(other, Player.Position, Player.DirectionTo(primary), 25, 2);
#endregion
- ///
- /// Checks if target is within Zero (0) yalms in distance, or if Player is inside hitbox.
- ///
- /// The user's specified Target being checked.
- ///
- protected bool In0y(Actor? target) => Player.DistanceToHitbox(target) <= 0.00f;
+ #region Cones
+ //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)
- ///
- /// 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;
+ /// Creates a Position Check for Cone AOE attacks with the given range.
+ private PositionCheck ConeTargetCheck(float range) => (primary, other) =>
+ {
+ var playerDir = Player.DirectionTo(primary);
+ return Hints.TargetInAOECone(other, Player.Position, range, playerDir, 45.Degrees());
+ };
- ///
- /// 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 the target is within a 4-yalm Cone range.
+ protected PositionCheck Is4yConeTarget => ConeTargetCheck(4);
- ///
- /// 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 the target is within a 6-yalm Cone range.
+ protected PositionCheck Is6yConeTarget => ConeTargetCheck(6);
- ///
- /// 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 the target is within an 8-yalm Cone range.
+ protected PositionCheck Is8yConeTarget => ConeTargetCheck(8);
- ///
- /// 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 the target is within a 10-yalm Cone range.
+ protected PositionCheck Is10yConeTarget => ConeTargetCheck(10);
- ///
- /// 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;
+ /// Checks if the target is within a 12-yalm Cone range.
+ protected PositionCheck Is12yConeTarget => ConeTargetCheck(12);
- ///
- /// 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))
- ///
- protected Actor? TargetChoice(StrategyValues.OptionRef track) => ResolveTargetOverride(track.Value); //Resolves the target choice based on the strategy
-
- /// 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)
+ /// 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.
+ private PositionCheck LineTargetCheck(float range) => (primary, other) =>
{
- 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;
+ var playerDir = Player.DirectionTo(primary); // Cache calculation
+ return Hints.TargetInAOERect(other, Player.Position, playerDir, range, 2);
+ };
- return (OnTwoOrMore, OnThreeOrMore, OnFourOrMore, OnFiveOrMore);
- }
+ /// Checks if the target is within a 10-yalm AOE Rect range.
+ protected PositionCheck Is10yRectTarget => LineTargetCheck(10);
- ///
- /// This function attempts to pick a suitable primary target automatically, even if a target is not already picked.
- ///
- /// The user's picked Strategy
- /// The user's current specified Target.
- ///
- protected void GetPrimaryTarget(StrategyValues strategy, ref Enemy? primaryTarget, float range)
+ /// 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.
+ /// The user's specified Target being checked.
+ /// The maximum distance threshold.
+ protected bool InRange(Actor? target, float maxDistance) => Player.DistanceToHitbox(target) <= maxDistance - 0.01f;
+
+ /// 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
+
+ #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);
+
+ /// 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 is AOEStrategy.AutoFinish or AOEStrategy.AutoBreak)
{
- 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 best target automatically.
- ///
- /// The user's picked Strategy
- /// 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);
+ /// 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 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)
+ {
+ if (primaryTarget?.Actor == null || Player.DistanceToHitbox(primaryTarget.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();
+ }
+ }
+ #endregion
- ///
- /// 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)
@@ -922,18 +605,13 @@ 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.
- ///
- ///
- ///
- ///
- ///
- ///
- ///
- protected (Enemy? Target, P Timer) GetDOTTarget
(Enemy? initial, Func getTimer, int maxAllowedTargets) where P : struct, IComparable
+ /// 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
{
- // 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));
@@ -946,18 +624,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;
@@ -969,49 +643,28 @@ P targetPrio(Actor potentialTarget)
}
#endregion
- #region Actors
- ///
- /// 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
- ///
- /// 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)
{
@@ -1019,9 +672,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.
@@ -1068,7 +719,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.
@@ -1082,37 +732,28 @@ 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
- ///
- /// 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
@@ -1140,35 +781,36 @@ 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);
}
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)
- .AddOption(AOEStrategy.Automatic, "Auto", "Automatically execute optimal rotation based on targets", supportedTargets: ActionTargets.Hostile)
+ return res.Define(SharedTrack.AOE).As("AOE", uiPriority: 300)
+ .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);
+ }
- res.Define(SharedTrack.Hold).As("Hold", uiPriority: 290)
+ /// Defines our shared Hold strategies.
+ /// The definitions of our base module's strategies.
+ 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.
@@ -1181,7 +823,6 @@ public static RotationModuleDefinition DefineShared(this RotationModuleDefinitio
/// 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
@@ -1204,7 +845,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
@@ -1222,44 +862,34 @@ public static RotationModuleDefinition.ConfigRef DefineOGCDA 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 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; 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.
- /// - 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
}
diff --git a/BossMod/Autorotation/Standard/akechi/DPS/AkechiBLM.cs b/BossMod/Autorotation/Standard/akechi/DPS/AkechiBLM.cs
index a1a0adac83..ade6cef995 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")
@@ -396,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);
@@ -409,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/PvP/AkechiGNBPvP.cs b/BossMod/Autorotation/Standard/akechi/PvP/AkechiGNBPvP.cs
index 9ac911dba1..b076c1852d 100644
--- a/BossMod/Autorotation/Standard/akechi/PvP/AkechiGNBPvP.cs
+++ b/BossMod/Autorotation/Standard/akechi/PvP/AkechiGNBPvP.cs
@@ -1,19 +1,20 @@
-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 +36,14 @@ public enum ComboStrategy
Hold
}
- public enum LimitBreakStrategy
+ public enum RushStrategy
+ {
+ Automatic,
+ Force,
+ Hold
+ }
+
+ public enum TriggerStrategy
{
Automatic,
Force,
@@ -65,7 +73,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 +83,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 +122,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;
}
@@ -143,7 +152,7 @@ public enum OGCDPriority
}
#endregion
- #region Placeholders for Variables
+ #region Module Variables
private float nmLeft;
private float rdCD;
private bool hasNM;
@@ -160,40 +169,26 @@ 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)
{
#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,101 +207,81 @@ 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
+ 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);
- canBrand = hasRaze && In5y(primaryTarget);
- canRip = hasRip && In5y(primaryTarget);
- canTear = hasTear && In5y(primaryTarget);
- canGouge = hasGouge && In5y(primaryTarget);
- #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() == ComboStrategy.Force)
- QueueGCD(NextCombo(), primaryTarget, GCDPriority.ForcedGCD);
-
#region Rotation Execution
- if (!hold && !inGF)
- QueueGCD(NextCombo(), primaryTarget, GCDPriority.Combo);
+ GetPvPTarget(ref primaryTarget, 3);
+
+ if (!inGF)
+ 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))
- QueueOGCD(AID.RoughDividePvP, primaryTarget, 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))
- QueueOGCD(AID.BlastingZonePvP, primaryTarget, 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, OGCDPriority.Continuation);
- if (canTear || GunStep == 1)
- QueueOGCD(AID.AbdomenTearPvP, primaryTarget, OGCDPriority.Continuation);
+ QueueOGCD(AID.JugularRipPvP, PlayerTarget?.Actor, OGCDPriority.Continuation);
+ if (canTear || GunStep == 2)
+ QueueOGCD(AID.AbdomenTearPvP, PlayerTarget?.Actor, OGCDPriority.Continuation);
if (canGouge)
- QueueOGCD(AID.EyeGougePvP, primaryTarget, OGCDPriority.Continuation);
+ QueueOGCD(AID.EyeGougePvP, PlayerTarget?.Actor, OGCDPriority.Continuation);
if (canHyper)
- QueueOGCD(AID.HypervelocityPvP, primaryTarget, OGCDPriority.Continuation);
+ QueueOGCD(AID.HypervelocityPvP, PlayerTarget?.Actor, OGCDPriority.Continuation);
if (canBrand)
- QueueOGCD(AID.FatedBrandPvP, primaryTarget, OGCDPriority.Continuation);
+ QueueOGCD(AID.FatedBrandPvP, PlayerTarget?.Actor, OGCDPriority.Continuation);
+
+ 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))
- 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, 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))
- QueueGCD(AID.FatedCirclePvP, primaryTarget, 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
- var lbStrat = strategy.Option(Track.LimitBreak).As();
- if (ShouldUseLimitBreak(lbStrat, primaryTarget))
- QueueOGCD(LimitBreak, primaryTarget, lbStrat == LimitBreakStrategy.Force ? OGCDPriority.ForcedOGCD : OGCDPriority.LB);
- }
-
- #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);
- }
+ #region Limit Break
+ var rrStrat = strategy.Option(Track.RelentlessRush).As();
+ 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, PlayerTarget?.Actor) && Hints.NumPriorityTargetsInAOECircle(Player.Position, 5) > 0)
+ QueueGCD(AID.TerminalTriggerPvP, Player, ttStrat == TriggerStrategy.Force ? GCDPriority.ForcedGCD : GCDPriority.ForcedGCD);
+ #endregion
}
- #endregion
- #region Single-Target Helpers
+ #region Cooldown Helpers
private AID NextCombo() => ComboLastMove switch
{
AID.SolidBarrelPvP => AID.BurstStrikePvP,
@@ -314,67 +289,46 @@ private void QueueOGCD(AID aid, Actor? target, OGCDPriority prio, float basePrio
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 || IsOffCooldown(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 ShouldUseLimitBreak(LimitBreakStrategy strategy, Actor? target) => strategy switch
+ private bool ShouldUseRR(RushStrategy strategy, Actor? target) => strategy switch
+ {
+ RushStrategy.Automatic => target != null && In5y(target) && hasNM && LBready,
+ RushStrategy.Force => LBready,
+ RushStrategy.Hold => false,
+ _ => false
+ };
+ private bool ShouldUseTT(TriggerStrategy strategy, Actor? target) => strategy switch
{
- LimitBreakStrategy.Automatic =>
- target != null &&
- In5y(target) &&
- hasNM &&
- LBready,
- LimitBreakStrategy.Force => true,
- LimitBreakStrategy.Hold => false,
+ TriggerStrategy.Automatic => StacksRemaining(target, SID.RelentlessShrapnelPvP) > 0 && PlayerHasEffect(SID.RelentlessRushPvP),
+ TriggerStrategy.Force => PlayerHasEffect(SID.RelentlessRushPvP),
+ TriggerStrategy.Hold => false,
_ => false
};
#endregion
diff --git a/BossMod/Autorotation/Standard/akechi/Tank/AkechiDRK.cs b/BossMod/Autorotation/Standard/akechi/Tank/AkechiDRK.cs
index 4eb5d9f139..275424b0ef 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);
@@ -233,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,
@@ -385,12 +391,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 e167e3b2d4..3c22305764 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
@@ -40,7 +41,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");
@@ -48,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")
@@ -69,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)
@@ -89,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)
@@ -109,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);
@@ -122,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
}
@@ -147,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
@@ -180,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;
@@ -205,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
@@ -238,7 +256,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
@@ -282,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
@@ -317,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
@@ -394,12 +412,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))
{
@@ -408,11 +427,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);
@@ -458,14 +477,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
@@ -519,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,
@@ -570,42 +587,48 @@ 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 &&
+ ((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/AkechiPLD.cs b/BossMod/Autorotation/Standard/akechi/Tank/AkechiPLD.cs
index 85b9c6458e..d3e671a9b7 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");
@@ -385,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 fa04af7635..4fe6f3c4ac 100644
--- a/BossMod/Autorotation/Standard/akechi/Tank/AkechiWAR.cs
+++ b/BossMod/Autorotation/Standard/akechi/Tank/AkechiWAR.cs
@@ -1,4 +1,4 @@
-using static BossMod.AIHints;
+using static BossMod.AIHints;
using FFXIVClientStructs.FFXIV.Client.Game.Gauge;
using BossMod.WAR;
@@ -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)
@@ -148,13 +149,13 @@ private GCDPriority FellCleave()
public enum GCDPriority
{
None = 0,
- Standard = 100,
Gauge = 300,
- PrimalRuination = 400,
DelayFC = 390,
+ Standard = 400,
FlexibleFC = 470,
FlexibleIR = 490,
PrimalRend = 500,
+ PrimalRuination = 500,
BuffedFC = 550,
BuffedIR = 570,
NeedTempest = 650,
@@ -225,6 +226,8 @@ public enum OGCDPriority
#region Module Helpers
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;
@@ -236,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
@@ -334,6 +339,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);
@@ -342,18 +350,22 @@ 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.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,
- GCDPriority.ForcedCombo);
+ IsRiskingGauge() ? GCDPriority.Standard - 400 : GCDPriority.ForcedCombo);
if (strategy.ForceAOE())
QueueGCD(AOE(),
Player,
- GCDPriority.ForcedCombo);
+ IsRiskingGauge() ? GCDPriority.Standard - 400 : GCDPriority.ForcedCombo);
#endregion
#region Cooldowns
@@ -484,12 +496,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
diff --git a/BossMod/Autorotation/Utility/ClassASTUtility.cs b/BossMod/Autorotation/Utility/ClassASTUtility.cs
index b34badf2c7..c8e7b4ff1e 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.Area, 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
@@ -83,7 +83,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), 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;
@@ -95,87 +95,39 @@ 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 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)
- QueueOGCD(cosmosAction, primaryTarget);
+ if (cosmosStrat != default)
+ Hints.ActionsToExecute.Push(ActionID.MakeSpell(cosmosAction), primaryTarget, cosmos.Priority(), cosmos.Value.ExpireIn);
}
-
- #region Core Execution Helpers
-
- public AST.AID NextGCD; //Next global cooldown action to be used
- public void QueueGCD(AST.AID aid, Actor? target, P priority, float delay = 0) where P : Enum
- => QueueGCD(aid, target, (int)(object)priority, delay);
-
- public void QueueGCD(AST.AID aid, Actor? target, int priority = 8, float delay = 0)
- {
- var NextGCDPrio = 0;
-
- if (priority == 0)
- return;
-
- if (QueueAction(aid, target, ActionQueue.Priority.High, delay) && priority > NextGCDPrio)
- {
- NextGCD = aid;
- }
- }
-
- public void QueueOGCD
(AST.AID aid, Actor? target, P priority, float delay = 0) where P : Enum
- => QueueOGCD(aid, target, (int)(object)priority, delay);
-
- public void QueueOGCD(AST.AID aid, Actor? target, int priority = 4, float delay = 0)
- {
- if (priority == 0)
- return;
-
- QueueAction(aid, target, ActionQueue.Priority.Medium + priority, delay);
- }
-
- public bool QueueAction(AST.AID aid, Actor? target, float priority, float delay)
- {
- if ((uint)(object)aid == 0)
- return false;
-
- var def = ActionDefinitions.Instance.Spell(aid);
- if (def == null)
- return false;
-
- if (def.Range != 0 && target == null)
- {
- return false;
- }
-
- Vector3 targetPos = default;
-
- if (def.AllowedTargets.HasFlag(ActionTargets.Area))
- {
- if (def.Range == 0)
- targetPos = Player.PosRot.XYZ();
- else if (target != null)
- targetPos = target.PosRot.XYZ();
- }
-
- Hints.ActionsToExecute.Push(ActionID.MakeSpell(aid), target, priority, delay: delay, castTime: def.CastTime, targetPos: targetPos); // TODO[cast-time]: this probably needs explicit cast-time argument (adjusted by swiftcast etc)
- return true;
- }
- #endregion
-
}
diff --git a/BossMod/Autorotation/Utility/ClassDRGUtility.cs b/BossMod/Autorotation/Utility/ClassDRGUtility.cs
index e4e699f9c1..bc6afba67f 100644
--- a/BossMod/Autorotation/Utility/ClassDRGUtility.cs
+++ b/BossMod/Autorotation/Utility/ClassDRGUtility.cs
@@ -12,8 +12,8 @@ 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);
@@ -27,7 +27,7 @@ public override void Execute(StrategyValues strategy, ref Actor? primaryTarget,
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/ClassNINUtility.cs b/BossMod/Autorotation/Utility/ClassNINUtility.cs
index 997d705905..4b893a82e0 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.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.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;
@@ -31,17 +31,16 @@ public override void Execute(StrategyValues strategy, ref Actor? primaryTarget,
// 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...)
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
{
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());
+ if (shouldDash)
+ 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/ClassPLDUtility.cs b/BossMod/Autorotation/Utility/ClassPLDUtility.cs
index 624bcc528b..5a472c3dab 100644
--- a/BossMod/Autorotation/Utility/ClassPLDUtility.cs
+++ b/BossMod/Autorotation/Utility/ClassPLDUtility.cs
@@ -2,7 +2,7 @@
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
@@ -32,7 +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
- DefineSimpleConfig(res, Track.PassageOfArms, "PassageOfArms", "Arms", 470, PLD.AID.PassageOfArms, 3); //120s CD, 18s max duration
DefineSimpleConfig(res, Track.HallowedGround, "HallowedGround", "Inv", 400, PLD.AID.HallowedGround, 10); //420s CD, 10s duration
return res;
@@ -44,7 +43,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);
diff --git a/BossMod/Autorotation/Utility/ClassSCHUtility.cs b/BossMod/Autorotation/Utility/ClassSCHUtility.cs
index 2d7bbe67cb..e850fcc360 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.Area, 50, 77)
+ .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,7 +95,7 @@ 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 soilAction = soil.As() switch
@@ -110,12 +104,11 @@ public override void Execute(StrategyValues strategy, ref Actor? primaryTarget,
_ => default
};
if (soilAction != default)
- QueueOGCD(soilAction, ResolveTargetOverride(soil.Value) ?? primaryTarget ?? Player);
+ 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)
- 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 +117,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
}
diff --git a/BossMod/Autorotation/Utility/RolePvPUtility.cs b/BossMod/Autorotation/Utility/RolePvPUtility.cs
index f299d119f6..3f67c40baa 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()
{
@@ -62,38 +21,37 @@ public static RotationModuleDefinition Definition()
Class.BLM, Class.SMN, Class.RDM, Class.PCT), 100, 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,8 @@ 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 float PlayerHPP() => (float)Player.HPMP.CurHP / Player.HPMP.MaxHP * 100;
public float DebuffsLeft(Actor? target)
{
return target == null ? 0f
@@ -145,23 +89,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 +121,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 +129,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)
- {
- 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)
+ public bool ShouldUseElixir(ElixirStrategy strategy) => strategy switch
{
- 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 && 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.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 && PlayerHPP() <= 40,
+ RecuperateStrategy.Seventy => canRecuperate && PlayerHPP() <= 70,
+ RecuperateStrategy.Fifty => canRecuperate && PlayerHPP() <= 50,
+ RecuperateStrategy.Thirty => canRecuperate && PlayerHPP() <= 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 && PlayerHPP() <= 35,
+ GuardStrategy.Seventy => canGuard && PlayerHPP() <= 70,
+ GuardStrategy.Fifty => canGuard && PlayerHPP() <= 50,
+ GuardStrategy.Thirty => canGuard && PlayerHPP() <= 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,
};
diff --git a/BossMod/Data/Actor.cs b/BossMod/Data/Actor.cs
index c82d118c7a..765651add6 100644
--- a/BossMod/Data/Actor.cs
+++ b/BossMod/Data/Actor.cs
@@ -171,6 +171,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}>";
}