diff --git a/BasicRotations/Magical/BLM_Beta.cs b/BasicRotations/Magical/BLM_Beta.cs index 5f86300..e34a0cf 100644 --- a/BasicRotations/Magical/BLM_Beta.cs +++ b/BasicRotations/Magical/BLM_Beta.cs @@ -1,6 +1,6 @@ namespace DefaultRotations.Magical; -[Rotation("Testing Rotations", CombatType.PvE, GameVersion = "6.58")] +[Rotation("Beta Rotations", CombatType.PvE, GameVersion = "6.58")] [SourceCode(Path = "main/DefaultRotations/Magical/BLM_Beta.cs")] [Api(1)] public class BLM_Beta : BlackMageRotation diff --git a/BasicRotations/Ranged/BRD_Beta.cs b/BasicRotations/Ranged/BRD_Beta.cs new file mode 100644 index 0000000..f9342e8 --- /dev/null +++ b/BasicRotations/Ranged/BRD_Beta.cs @@ -0,0 +1,208 @@ +namespace DefaultRotations.Ranged; + +[Rotation("Beta Rotations", CombatType.PvE, GameVersion = "6.58", + Description = "Please make sure that the three song times add up to 120 seconds!")] +[SourceCode(Path = "main/DefaultRotations/Ranged/BRD_Beta.cs")] +[Api(1)] +public sealed class BRD_Beta : BardRotation +{ + #region Config Options + [RotationConfig(CombatType.PvE, Name = @"Use Raging Strikes on ""Wanderer's Minuet""")] + public bool BindWAND { get; set; } = false; + + [Range(1, 45, ConfigUnitType.Seconds, 1)] + [RotationConfig(CombatType.PvE, Name = "Wanderer's Minuet Uptime")] + public float WANDTime { get; set; } = 43; + + [Range(0, 45, ConfigUnitType.Seconds, 1)] + [RotationConfig(CombatType.PvE, Name = "Mage's Ballad Uptime")] + public float MAGETime { get; set; } = 34; + + [Range(0, 45, ConfigUnitType.Seconds, 1)] + [RotationConfig(CombatType.PvE, Name = "Army's Paeon Uptime")] + public float ARMYTime { get; set; } = 43; + + [RotationConfig(CombatType.PvE, Name = "First Song")] + private Song FirstSong { get; set; } = Song.WANDERER; + + private bool BindWANDEnough => BindWAND && this.TheWanderersMinuetPvE.EnoughLevel; + private float WANDRemainTime => 45 - WANDTime; + private float MAGERemainTime => 45 - MAGETime; + private float ARMYRemainTime => 45 - ARMYTime; + #endregion + + #region Emergency Logic + protected override bool EmergencyAbility(IAction nextGCD, out IAction? act) + { + if (nextGCD.IsTheSameTo(true, StraightShotPvE, VenomousBitePvE, WindbitePvE, IronJawsPvE)) + { + return base.EmergencyAbility(nextGCD, out act); + } + else if ((!RagingStrikesPvE.EnoughLevel || Player.HasStatus(true, StatusID.RagingStrikes)) && (!BattleVoicePvE.EnoughLevel || Player.HasStatus(true, StatusID.BattleVoice))) + { + if ((EmpyrealArrowPvE.Cooldown.IsCoolingDown && !EmpyrealArrowPvE.Cooldown.WillHaveOneChargeGCD(1) || !EmpyrealArrowPvE.EnoughLevel) && Repertoire != 3) + { + if (!Player.HasStatus(true, StatusID.StraightShotReady) && BarragePvE.CanUse(out act)) return true; + } + } + + return base.EmergencyAbility(nextGCD, out act); + } + #endregion + + #region oGCD Logic + protected override bool AttackAbility(IAction nextGCD, out IAction? act) + { + act = null; + if (Song == Song.NONE) + { + switch (FirstSong) + { + case Song.WANDERER: + if (TheWanderersMinuetPvE.CanUse(out act)) return true; + break; + + case Song.ARMY: + if (ArmysPaeonPvE.CanUse(out act)) return true; + break; + + case Song.MAGE: + if (MagesBalladPvE.CanUse(out act)) return true; + break; + } + if (TheWanderersMinuetPvE.CanUse(out act)) return true; + if (MagesBalladPvE.CanUse(out act)) return true; + if (ArmysPaeonPvE.CanUse(out act)) return true; + } + + if (IsBurst && Song != Song.NONE && MagesBalladPvE.EnoughLevel) + { + if (RagingStrikesPvE.CanUse(out act)) + { + if (BindWANDEnough && Song == Song.WANDERER && TheWanderersMinuetPvE.EnoughLevel) return true; + if (!BindWANDEnough) return true; + } + + if (RadiantFinalePvE.CanUse(out act, skipAoeCheck: true)) + { + if (Player.HasStatus(true, StatusID.RagingStrikes) && RagingStrikesPvE.Cooldown.ElapsedOneChargeAfterGCD(1)) return true; + } + + if (BattleVoicePvE.CanUse(out act, skipAoeCheck: true)) + { + if (IsLastAction(true, RadiantFinalePvE)) return true; + + if (Player.HasStatus(true, StatusID.RagingStrikes) && RagingStrikesPvE.Cooldown.ElapsedOneChargeAfterGCD(1)) return true; + } + } + + if (RadiantFinalePvE.EnoughLevel && RadiantFinalePvE.Cooldown.IsCoolingDown && BattleVoicePvE.EnoughLevel && !BattleVoicePvE.Cooldown.IsCoolingDown) return false; + + if (TheWanderersMinuetPvE.CanUse(out act, onLastAbility: true)) + { + if (SongEndAfter(ARMYRemainTime) && (Song != Song.NONE || Player.HasStatus(true, StatusID.ArmysEthos))) return true; + } + + if (Song != Song.NONE && EmpyrealArrowPvE.CanUse(out act)) return true; + + if (PitchPerfectPvE.CanUse(out act)) + { + if (SongEndAfter(3) && Repertoire > 0) return true; + + if (Repertoire == 3) return true; + + // if (Repertoire == 2 && EmpyrealArrowPvE.Cooldown.WillHaveOneChargeGCD(1) && NextAbilityToNextGCD < PitchPerfectPvE.AnimationLockTime + Ping) return true; + + if (Repertoire == 2 && EmpyrealArrowPvE.Cooldown.WillHaveOneChargeGCD()) return true; // && NextAbilityToNextGCD > PitchPerfectPvE.AnimationLockTime + Ping) return true; + } + + if (MagesBalladPvE.CanUse(out act)) + { + if (Song == Song.WANDERER && SongEndAfter(WANDRemainTime) && Repertoire == 0) return true; + if (Song == Song.ARMY && SongEndAfterGCD(2) && TheWanderersMinuetPvE.Cooldown.IsCoolingDown) return true; + } + + if (ArmysPaeonPvE.CanUse(out act)) + { + if (TheWanderersMinuetPvE.EnoughLevel && SongEndAfter(MAGERemainTime) && Song == Song.MAGE) return true; + if (TheWanderersMinuetPvE.EnoughLevel && SongEndAfter(2) && MagesBalladPvE.Cooldown.IsCoolingDown && Song == Song.WANDERER) return true; + if (!TheWanderersMinuetPvE.EnoughLevel && SongEndAfter(2)) return true; + } + + if (SidewinderPvE.CanUse(out act)) + { + if (Player.HasStatus(true, StatusID.BattleVoice) && (Player.HasStatus(true, StatusID.RadiantFinale) || !RadiantFinalePvE.EnoughLevel)) return true; + + if (!BattleVoicePvE.Cooldown.WillHaveOneCharge(10) && !RadiantFinalePvE.Cooldown.WillHaveOneCharge(10)) return true; + + if (RagingStrikesPvE.Cooldown.IsCoolingDown && !Player.HasStatus(true, StatusID.RagingStrikes)) return true; + } + if (BloodletterLogic(out act, Player.HasStatus(true, StatusID.RagingStrikes))) return true; + + return base.AttackAbility(nextGCD, out act); + } + #endregion + + #region GCD Logic + protected override bool GeneralGCD(out IAction? act) + { + if (IronJawsPvE.CanUse(out act)) return true; + if (IronJawsPvE.CanUse(out act, skipStatusProvideCheck: true) && (IronJawsPvE.Target.Target?.WillStatusEnd(30, true, IronJawsPvE.Setting.TargetStatusProvide ?? []) ?? false)) + { + if (Player.HasStatus(true, StatusID.RagingStrikes) && Player.WillStatusEndGCD(1, 0, true, StatusID.RagingStrikes)) return true; + } + + if (CanUseApexArrow(out act)) return true; + + if (BlastArrowPvE.CanUse(out act, skipAoeCheck: true)) + { + if (!Player.HasStatus(true, StatusID.RagingStrikes)) return true; + if (Player.HasStatus(true, StatusID.RagingStrikes) && BarragePvE.Cooldown.IsCoolingDown) return true; + } + + if (ShadowbitePvE.CanUse(out act)) return true; + if (QuickNockPvE.CanUse(out act)) return true; + + if (WindbitePvE.CanUse(out act)) return true; + if (VenomousBitePvE.CanUse(out act)) return true; + + if (StraightShotPvE.CanUse(out act)) return true; + if (HeavyShotPvE.CanUse(out act)) return true; + + return base.GeneralGCD(out act); + } + #endregion + + #region Extra Methods + private bool CanUseApexArrow(out IAction act) + { + if (!ApexArrowPvE.CanUse(out act, skipAoeCheck: true)) return false; + + if (QuickNockPvE.CanUse(out _) && SoulVoice == 100) return true; + + if (SoulVoice == 100 && BattleVoicePvE.Cooldown.WillHaveOneCharge(25)) return false; + + if (SoulVoice >= 80 && Player.HasStatus(true, StatusID.RagingStrikes) && Player.WillStatusEnd(10, false, StatusID.RagingStrikes)) return true; + + if (SoulVoice == 100 && Player.HasStatus(true, StatusID.RagingStrikes) && Player.HasStatus(true, StatusID.BattleVoice)) return true; + + if (Song == Song.MAGE && SoulVoice >= 80 && SongEndAfter(22) && SongEndAfter(18)) return true; + + if (!Player.HasStatus(true, StatusID.RagingStrikes) && SoulVoice == 100) return true; + + return false; + } + private bool BloodletterLogic(out IAction? act, bool burst) + { + act = null; + if (!burst && RagingStrikesPvE.Cooldown.WillHaveOneCharge(30) && !(BloodletterPvE.Cooldown.CurrentCharges >= 3)) return false; + + if (burst && EmpyrealArrowPvE.Cooldown.IsCoolingDown || !EmpyrealArrowPvE.Cooldown.WillHaveOneChargeGCD() || Repertoire != 3 || !EmpyrealArrowPvE.EnoughLevel) + { + if (RainOfDeathPvE.CanUse(out act, usedUp: true)) return true; + if (BloodletterPvE.CanUse(out act, usedUp: true)) return true; + } + return false; + } + #endregion +} \ No newline at end of file diff --git a/BasicRotations/Ranged/BRD_Default.cs b/BasicRotations/Ranged/BRD_Default.cs index 4d310f4..8f49736 100644 --- a/BasicRotations/Ranged/BRD_Default.cs +++ b/BasicRotations/Ranged/BRD_Default.cs @@ -1,5 +1,3 @@ -using Lumina.Excel.GeneratedSheets2; - namespace DefaultRotations.Ranged; [Rotation("LTS's Default", CombatType.PvE, GameVersion = "6.58", @@ -8,6 +6,7 @@ namespace DefaultRotations.Ranged; [Api(1)] public sealed class BRD_Default : BardRotation { + #region Config Options [RotationConfig(CombatType.PvE, Name = @"Use Raging Strikes on ""Wanderer's Minuet""")] public bool BindWAND { get; set; } = false; @@ -30,35 +29,9 @@ public sealed class BRD_Default : BardRotation private float WANDRemainTime => 45 - WANDTime; private float MAGERemainTime => 45 - MAGETime; private float ARMYRemainTime => 45 - ARMYTime; + #endregion - protected override bool GeneralGCD(out IAction? act) - { - if (IronJawsPvE.CanUse(out act)) return true; - if (IronJawsPvE.CanUse(out act, skipStatusProvideCheck: true) && (IronJawsPvE.Target.Target?.WillStatusEnd(30, true, IronJawsPvE.Setting.TargetStatusProvide ?? []) ?? false)) - { - if (Player.HasStatus(true, StatusID.RagingStrikes) && Player.WillStatusEndGCD(1, 0, true, StatusID.RagingStrikes)) return true; - } - - if (CanUseApexArrow(out act)) return true; - - if (BlastArrowPvE.CanUse(out act, skipAoeCheck : true)) - { - if (!Player.HasStatus(true, StatusID.RagingStrikes)) return true; - if (Player.HasStatus(true, StatusID.RagingStrikes) && BarragePvE.Cooldown.IsCoolingDown) return true; - } - - if (ShadowbitePvE.CanUse(out act)) return true; - if (QuickNockPvE.CanUse(out act)) return true; - - if (WindbitePvE.CanUse(out act)) return true; - if (VenomousBitePvE.CanUse(out act)) return true; - - if (StraightShotPvE.CanUse(out act)) return true; - if (HeavyShotPvE.CanUse(out act)) return true; - - return base.GeneralGCD(out act); - } - + #region Emergency Logic protected override bool EmergencyAbility(IAction nextGCD, out IAction? act) { if (nextGCD.IsTheSameTo(true, StraightShotPvE, VenomousBitePvE, WindbitePvE, IronJawsPvE)) @@ -75,7 +48,9 @@ protected override bool EmergencyAbility(IAction nextGCD, out IAction? act) return base.EmergencyAbility(nextGCD, out act); } + #endregion + #region oGCD Logic protected override bool AttackAbility(IAction nextGCD, out IAction? act) { act = null; @@ -172,10 +147,42 @@ protected override bool AttackAbility(IAction nextGCD, out IAction? act) return base.AttackAbility(nextGCD, out act); } + #endregion + + #region GCD Logic + protected override bool GeneralGCD(out IAction? act) + { + if (IronJawsPvE.CanUse(out act)) return true; + if (IronJawsPvE.CanUse(out act, skipStatusProvideCheck: true) && (IronJawsPvE.Target.Target?.WillStatusEnd(30, true, IronJawsPvE.Setting.TargetStatusProvide ?? []) ?? false)) + { + if (Player.HasStatus(true, StatusID.RagingStrikes) && Player.WillStatusEndGCD(1, 0, true, StatusID.RagingStrikes)) return true; + } + + if (CanUseApexArrow(out act)) return true; + + if (BlastArrowPvE.CanUse(out act, skipAoeCheck: true)) + { + if (!Player.HasStatus(true, StatusID.RagingStrikes)) return true; + if (Player.HasStatus(true, StatusID.RagingStrikes) && BarragePvE.Cooldown.IsCoolingDown) return true; + } + + if (ShadowbitePvE.CanUse(out act)) return true; + if (QuickNockPvE.CanUse(out act)) return true; + + if (WindbitePvE.CanUse(out act)) return true; + if (VenomousBitePvE.CanUse(out act)) return true; + + if (StraightShotPvE.CanUse(out act)) return true; + if (HeavyShotPvE.CanUse(out act)) return true; + + return base.GeneralGCD(out act); + } + #endregion + #region Extra Methods private bool CanUseApexArrow(out IAction act) { - if (!ApexArrowPvE.CanUse(out act,skipAoeCheck: true)) return false; + if (!ApexArrowPvE.CanUse(out act, skipAoeCheck: true)) return false; if (QuickNockPvE.CanUse(out _) && SoulVoice == 100) return true; @@ -191,4 +198,5 @@ private bool CanUseApexArrow(out IAction act) return false; } + #endregion } diff --git a/BasicRotations/Ranged/DNC_Beta.cs b/BasicRotations/Ranged/DNC_Beta.cs index 0522731..b3cd77f 100644 --- a/BasicRotations/Ranged/DNC_Beta.cs +++ b/BasicRotations/Ranged/DNC_Beta.cs @@ -1,13 +1,15 @@ -using RotationSolver.Basic.Attributes; +namespace DefaultRotations.Ranged; -namespace DefaultRotations.Ranged; - -[Rotation("Dancer Beta Rotation", CombatType.PvE, GameVersion = "6.58", Description = "Additonal contributions to this rotation thanks to Toshi!")] +[Rotation("Beta Rotations", CombatType.PvE, GameVersion = "6.58", Description = "Additonal contributions to this rotation thanks to Toshi!")] [SourceCode(Path = "main/DefaultRotations/Ranged/DNC_Beta.cs")] [Api(1)] public sealed class DNC_Beta : DancerRotation { - [RotationConfig(CombatType.PvE, Name = "Holds Standard and Tech Step if no targets in range (Warning, steps will drift)")] + #region Config Options + [RotationConfig(CombatType.PvE, Name = "Holds Tech Step if no targets in range (Warning, will drift)")] + public bool HoldTechForTargets { get; set; } = true; + + [RotationConfig(CombatType.PvE, Name = "Holds Standard Step if no targets in range (Warning, will drift & Buff may fall off)")] public bool HoldStepForTargets { get; set; } = false; // Override the method for actions to be taken during countdown phase of combat @@ -24,7 +26,9 @@ public sealed class DNC_Beta : DancerRotation // If none of the above conditions are met, fallback to the base class method return base.CountDownAction(remainTime); } + #endregion + #region Emergency Logic // Override the method for handling emergency abilities protected override bool EmergencyAbility(IAction nextGCD, out IAction? act) { @@ -56,7 +60,9 @@ protected override bool EmergencyAbility(IAction nextGCD, out IAction? act) // Fallback to base class method if none of the above conditions are met return base.EmergencyAbility(nextGCD, out act); } + #endregion + #region oGCD Logic // Override the method for handling attack abilities protected override bool AttackAbility(IAction nextGCD, out IAction? act) { @@ -90,7 +96,9 @@ protected override bool AttackAbility(IAction nextGCD, out IAction? act) // Fallback to base class attack ability method if none of the above conditions are met return base.AttackAbility(nextGCD, out act); } + #endregion + #region GCD Logic // Override the method for handling general Global Cooldown (GCD) actions protected override bool GeneralGCD(out IAction? act) { @@ -102,14 +110,14 @@ protected override bool GeneralGCD(out IAction? act) if (ExecuteStepGCD(out act)) return true; // Attempt to use Technical Step in burst mode and if in combat, checks for hostiles if bool is true - if (HoldStepForTargets) - { + if (HoldTechForTargets) + { if (HasHostilesInMaxRange && IsBurst && InCombat && TechnicalStepPvE.CanUse(out act, skipAoeCheck: true)) return true; - } - if (!HoldStepForTargets) - { + } + if (!HoldTechForTargets) + { if (IsBurst && InCombat && TechnicalStepPvE.CanUse(out act, skipAoeCheck: true)) return true; - } + } // Delegate to AttackGCD method to handle attack actions during GCD if (AttackGCD(out act, Player.HasStatus(true, StatusID.Devilment))) return true; @@ -117,7 +125,9 @@ protected override bool GeneralGCD(out IAction? act) // Fallback to base class general GCD method if none of the above conditions are met return base.GeneralGCD(out act); } + #endregion + #region Extra Methods // Helper method to handle attack actions during GCD based on certain conditions private bool AttackGCD(out IAction? act, bool burst) { @@ -159,7 +169,7 @@ private bool AttackGCD(out IAction? act, bool burst) // Return false if no action is determined to be taken return false; } - + // Method for Standard Step Logic private bool UseStandardStep(out IAction act) { // Attempt to use Standard Step if available and certain conditions are met @@ -194,6 +204,7 @@ private bool UseClosedPosition(out IAction act) } return false; } + // Rewrite of method to hold dance finish until target is in range 14 yalms private bool FinishTheDance(out IAction? act) { bool areDanceTargetsInRange = AllHostileTargets.Any(hostile => hostile.DistanceToPlayer() < 14); @@ -217,5 +228,5 @@ private bool FinishTheDance(out IAction? act) act = null; return false; } - + #endregion } diff --git a/BasicRotations/Ranged/MCH_Beta.cs b/BasicRotations/Ranged/MCH_Beta.cs index d2a8942..db2f5e8 100644 --- a/BasicRotations/Ranged/MCH_Beta.cs +++ b/BasicRotations/Ranged/MCH_Beta.cs @@ -1,12 +1,12 @@ -using RotationSolver.Basic.Data; +namespace DefaultRotations.Ranged; -namespace DefaultRotations.Ranged; - -[Rotation("MCH Beta Rotation", CombatType.PvE, GameVersion = "6.58", Description = "Additonal contributions to this rotation thanks to Toshi!")] +[Rotation("Beta Rotations", CombatType.PvE, GameVersion = "6.58", Description = "Additonal contributions to this rotation thanks to Toshi!")] [SourceCode(Path = "main/DefaultRotations/Ranged/MCH_Beta.cs")] [Api(1)] public sealed class MCH_Beta : MachinistRotation { + [RotationConfig(CombatType.PvE, Name = "Uses Rook Autoturret/Automaton Queen immediately whenever you get 50 battery")] + public bool UseQueenWhenever { get; set; } = true; #region Countdown logic // Defines logic for actions to take during the countdown before combat starts. @@ -72,11 +72,11 @@ protected override bool EmergencyAbility(IAction nextGCD, out IAction? act) // Logic for using attack abilities outside of GCD, focusing on burst windows and cooldown management. protected override bool AttackAbility(IAction nextGCD, out IAction? act) { - // Define conditions under which the Rook Autoturret can be used. + // Define conditions under which the Rook Autoturret/Queen can be used. bool OpenerQueen = !CombatElapsedLess(20f) && CombatElapsedLess(25f); bool CombatTimeQueen = CombatElapsedLess(60f) && !CombatElapsedLess(45f); bool WildfireCooldownQueen = WildfirePvE.Cooldown.IsCoolingDown && WildfirePvE.Cooldown.ElapsedAfter(105f) && Battery == 100 && - (nextGCD.IsTheSameTo(true, AirAnchorPvE) || nextGCD.IsTheSameTo(true, CleanShotPvE)); + (nextGCD.IsTheSameTo(true, AirAnchorPvE) || nextGCD.IsTheSameTo(true, CleanShotPvE)) || nextGCD.IsTheSameTo(true, HeatedCleanShotPvE) || nextGCD.IsTheSameTo(true, ChainSawPvE); bool BatteryCheckQueen = Battery >= 90 && !WildfirePvE.Cooldown.ElapsedAfter(70f); bool LastGCDCheckQueen = Battery >= 80 && !WildfirePvE.Cooldown.ElapsedAfter(77.5f) && IsLastGCD(true, AirAnchorPvE); @@ -85,7 +85,7 @@ protected override bool AttackAbility(IAction nextGCD, out IAction? act) { return HyperchargePvE.CanUse(out act, skipClippingCheck: true); } - + // Burst if (IsBurst) { if (UseBurstMedicine(out act)) return true; @@ -100,12 +100,17 @@ protected override bool AttackAbility(IAction nextGCD, out IAction? act) { if (CanUseHyperchargePvE(out act)) return true; } - - if (OpenerQueen || CombatTimeQueen || WildfireCooldownQueen || BatteryCheckQueen || LastGCDCheckQueen) + // Rook Autoturret/Queen Logic toggle on + if (UseQueenWhenever && (OpenerQueen || CombatTimeQueen || WildfireCooldownQueen || BatteryCheckQueen || LastGCDCheckQueen)) { return RookAutoturretPvE.CanUse(out act, skipComboCheck: true); } - + // Rook Autoturret/Queen Logic toggle off + if (!UseQueenWhenever) + { + return RookAutoturretPvE.CanUse(out act, skipComboCheck: true); + } + // Use Barrel Stabilizer on CD if won't cap if (BarrelStabilizerPvE.CanUse(out act)) return true; return base.AttackAbility(nextGCD, out act); @@ -139,7 +144,10 @@ protected override bool GeneralGCD(out IAction? act) if (!CombatElapsedLessGCD(4) && ChainSawPvE.CanUse(out act, skipAoeCheck: true)) return true; // AoE actions: ChainSaw and SpreadShot based on their usability. - if (ChainSawPvE.CanUse(out act)) return true; + if (SpreadShotPvE.CanUse(out _)) + { + if (ChainSawPvE.CanUse(out act)) return true; + } if (SpreadShotPvE.CanUse(out act)) return true; // Single target actions: CleanShot, SlugShot, and SplitShot based on their usability. @@ -183,6 +191,5 @@ private bool CanUseHyperchargePvE(out IAction? act) return HyperchargePvE.CanUse(out act); } } - #endregion } diff --git a/Directory.Build.props b/Directory.Build.props index ca68356..d0d6516 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -2,7 +2,7 @@ net8.0-windows enable - LTS Toshi + LTS and Toshi 1.0.0 x64 AnyCPU