diff --git a/BasicRotations/Magical/BLM_Beta.cs b/BasicRotations/Magical/BLM_Beta.cs new file mode 100644 index 0000000..03a58d6 --- /dev/null +++ b/BasicRotations/Magical/BLM_Beta.cs @@ -0,0 +1,370 @@ +namespace DefaultRotations.Magical; + +[Rotation("Testing Rotations", CombatType.PvE, GameVersion = "6.58")] +[SourceCode(Path = "main/DefaultRotations/Magical/BLM_Beta.cs")] +public class BLM_Beta : BlackMageRotation +{ + private bool NeedToGoIce + { + get + { + //Can use Despair. + if (DespairPvE.EnoughLevel && CurrentMp >= DespairPvE.Info.MPNeed) return false; + + //Can use Fire1 + if (FirePvE.EnoughLevel && CurrentMp >= FirePvE.Info.MPNeed) return false; + + return true; + } + } + + private bool NeedToTransposeGoIce(bool usedOne) + { + if (!NeedToGoIce) return false; + if (!ParadoxPvE.EnoughLevel) return false; + var compare = usedOne ? -1 : 0; + var count = PolyglotStacks; + if (count == compare++) return false; + if (count == compare++ && !EnchinaEndAfterGCD(2)) return false; + if (count >= compare && (HasFire || SwiftcastPvE.Cooldown.WillHaveOneChargeGCD(2) || TriplecastPvE.Cooldown.WillHaveOneChargeGCD(2))) return true; + if (!HasFire && !SwiftcastPvE.Cooldown.WillHaveOneChargeGCD(2) && !TriplecastPvE.CanUse(out _, gcdCountForAbility: 8)) return false; + return true; + } + + [RotationConfig(CombatType.PvE, Name = "Use Transpose to Astral Fire before Paradox")] + public bool UseTransposeForParadox { get; set; } = true; + + [RotationConfig(CombatType.PvE, Name = "Extend Astral Fire Time Safely")] + public bool ExtendTimeSafely { get; set; } = false; + + [RotationConfig(CombatType.PvE, Name = @"Use ""Double Paradox"" rotation [N15]")] + public bool UseN15 { get; set; } = false; + + protected override IAction? CountDownAction(float remainTime) + { + IAction act; + if (remainTime < FireIiiPvE.Info.CastTime + CountDownAhead) + { + if (FireIiiPvE.CanUse(out act)) return act; + } + if (remainTime <= 12 && SharpcastPvE.CanUse(out act, usedUp: true)) return act; + return base.CountDownAction(remainTime); + } + + protected override bool AttackAbility(out IAction? act) + { + if (IsBurst && UseBurstMedicine(out act)) return true; + if (InUmbralIce) + { + if (UmbralIceStacks == 2 && !HasFire + && !IsLastGCD(ActionID.ParadoxPvE)) + { + if (SwiftcastPvE.CanUse(out act)) return true; + if (TriplecastPvE.CanUse(out act, usedUp: true)) return true; + } + + if (UmbralIceStacks < 3 && LucidDreamingPvE.CanUse(out act)) return true; + if (SharpcastPvE.CanUse(out act, usedUp: true)) return true; + } + if (InAstralFire) + { + if (!CombatElapsedLess(6) && CombatElapsedLess(9) && LeyLinesPvE.CanUse(out act)) return true; + if (TriplecastPvE.CanUse(out act, gcdCountForAbility: 5)) return true; + } + if (AmplifierPvE.CanUse(out act)) return true; + return base.AttackAbility(out act); + } + + protected override bool EmergencyAbility(IAction nextGCD, out IAction? act) + { + //To Fire + if (CurrentMp >= 7200 && UmbralIceStacks == 2 && ParadoxPvE.EnoughLevel) + { + if ((HasFire || HasSwift) && TransposePvE.CanUse(out act, onLastAbility: true)) return true; + } + if (nextGCD.IsTheSameTo(false, FireIiiPvE) && HasFire) + { + if (TransposePvE.CanUse(out act)) return true; + } + + //Using Manafont + if (InAstralFire) + { + if (CurrentMp == 0 && ManafontPvE.CanUse(out act)) return true; + //To Ice + if (NeedToTransposeGoIce(true) && TransposePvE.CanUse(out act)) return true; + } + + return base.EmergencyAbility(nextGCD, out act); + } + + protected override bool GeneralGCD(out IAction? act) + { + if (InFireOrIce(out act, out var mustGo)) return true; + if (mustGo) return false; + //Triplecast for moving. + if (IsMoving && HasHostilesInRange && TriplecastPvE.CanUse(out act, usedUp: true, skipClippingCheck: true)) return true; + + if (AddElementBase(out act)) return true; + if (ScathePvE.CanUse(out act)) return true; + if (MaintainStatus(out act)) return true; + + return base.GeneralGCD(out act); + } + + private bool InFireOrIce(out IAction? act, out bool mustGo) + { + act = null; + mustGo = false; + if (InUmbralIce) + { + if (GoFire(out act)) return true; + if (MaintainIce(out act)) return true; + if (DoIce(out act)) return true; + } + if (InAstralFire) + { + if (GoIce(out act)) return true; + if (MaintainFire(out act)) return true; + if (DoFire(out act)) return true; + } + return false; + } + + private bool GoIce(out IAction? act) + { + act = null; + + if (!NeedToGoIce) return false; + + //Use Manafont or transpose. + if ((!ManafontPvE.Cooldown.IsCoolingDown || NeedToTransposeGoIce(false)) + && UseInstanceSpell(out act)) return true; + + //Go to Ice. + if (BlizzardIiPvE.CanUse(out act)) return true; + if (BlizzardIiiPvE.CanUse(out act)) return true; + if (TransposePvE.CanUse(out act)) return true; + if (BlizzardPvE.CanUse(out act)) return true; + return false; + } + + private bool MaintainIce(out IAction? act) + { + act = null; + if (UmbralIceStacks == 1) + { + if (BlizzardIiPvE.CanUse(out act)) return true; + + if (Player.Level == 90 && BlizzardPvE.CanUse(out act)) return true; + if (BlizzardIiiPvE.CanUse(out act)) return true; + } + if (UmbralIceStacks == 2 && Player.Level < 90) + { + if (BlizzardIiPvE.CanUse(out act)) return true; + if (BlizzardPvE.CanUse(out act)) return true; + } + return false; + } + + private bool DoIce(out IAction? act) + { + act = null; + + if (IsLastAction(ActionID.UmbralSoulPvE, ActionID.TransposePvE) + && IsParadoxActive && BlizzardPvE.CanUse(out act)) return true; + + if (UmbralIceStacks == 3 && UsePolyglot(out act)) return true; + + //Add Hearts + if (UmbralIceStacks == 3 && + BlizzardIvPvE.EnoughLevel && UmbralHearts < 3 && !IsLastGCD + (ActionID.BlizzardIvPvE, ActionID.FreezePvE)) + { + if (FreezePvE.CanUse(out act)) return true; + if (BlizzardIvPvE.CanUse(out act)) return true; + } + + if (AddThunder(out act, 5)) return true; + if (UmbralIceStacks == 2 && UsePolyglot(out act, 0)) return true; + + if (IsParadoxActive) + { + if (BlizzardPvE.CanUse(out act)) return true; + } + + if (BlizzardIiPvE.CanUse(out act)) return true; + if (BlizzardIvPvE.CanUse(out act)) return true; + if (BlizzardPvE.CanUse(out act)) return true; + return false; + } + + private bool GoFire(out IAction? act) + { + act = null; + + //Transpose line + if (UmbralIceStacks < 3) return false; + + //Need more MP + if (CurrentMp < 9600) return false; + + if (IsParadoxActive) + { + if (BlizzardPvE.CanUse(out act)) return true; + } + + //Go to Fire. + if (FireIiPvE.CanUse(out act)) return true; + if (FireIiiPvE.CanUse(out act)) return true; + if (TransposePvE.CanUse(out act)) return true; + if (FirePvE.CanUse(out act)) return true; + + return false; + } + + private bool MaintainFire(out IAction? act) + { + act = null; + switch (AstralFireStacks) + { + case 1: + if (FireIiPvE.CanUse(out act)) return true; + if (UseN15) + { + if (HasFire && FireIiiPvE.CanUse(out act)) return true; + if (IsParadoxActive && FirePvE.CanUse(out act)) return true; + } + if (FireIiiPvE.CanUse(out act)) return true; + break; + case 2: + if (FireIiPvE.CanUse(out act)) return true; + if (FirePvE.CanUse(out act)) return true; + break; + } + + if (ElementTimeEndAfterGCD(ExtendTimeSafely ? 3u : 2u)) + { + if (CurrentMp >= FirePvE.Info.MPNeed * 2 + 800 && FirePvE.CanUse(out act)) return true; + if (FlarePvE.CanUse(out act)) return true; + if (DespairPvE.CanUse(out act)) return true; + } + + return false; + } + + private bool DoFire(out IAction? act) + { + act = null; + if (UsePolyglot(out act)) return true; + + // Add thunder only at combat start. + if (CombatElapsedLess(5)) + { + if (AddThunder(out act, 0)) return true; + } + + if (TriplecastPvE.CanUse(out act, skipClippingCheck: true)) return true; + + if (AddThunder(out act, 0) && Player.WillStatusEndGCD(1, 0, true, + StatusID.Thundercloud)) return true; + + if (UmbralHearts < 2 && FlarePvE.CanUse(out act)) return true; + if (FireIiPvE.CanUse(out act)) return true; + + if (CurrentMp >= FirePvE.Info.MPNeed + 800) + { + if (FireIvPvE.EnoughLevel) + { + if (FireIvPvE.CanUse(out act)) return true; + } + else if (HasFire) + { + if (FireIiiPvE.CanUse(out act)) return true; + } + if (FirePvE.CanUse(out act)) return true; + } + + if (DespairPvE.CanUse(out act)) return true; + + return false; + } + + private bool UseInstanceSpell(out IAction? act) + { + act = null; + if (UsePolyglot(out act)) return true; + if (HasThunder && AddThunder(out act, 1)) return true; + if (UsePolyglot(out act, 0)) return true; + return false; + } + + private bool AddThunder(out IAction? act, uint gcdCount = 3) + { + act = null; + //Return if just used. + if (IsLastGCD(ActionID.ThunderPvE, ActionID.ThunderIiPvE, ActionID.ThunderIiiPvE, ActionID.ThunderIvPvE)) return false; + + //So long for thunder. + if (ThunderPvE.CanUse(out _) && (!ThunderPvE.Target.Target?.WillStatusEndGCD(gcdCount, 0, true, + StatusID.Thunder, StatusID.ThunderIi, StatusID.ThunderIii, StatusID.ThunderIv) ?? false)) + return false; + + if (ThunderIiPvE.CanUse(out act)) return true; + if (ThunderPvE.CanUse(out act)) return true; + + return false; + } + + private bool AddElementBase(out IAction? act) + { + act = null; + if (CurrentMp >= 7200) + { + if (FireIiPvE.CanUse(out act)) return true; + if (FireIiiPvE.CanUse(out act)) return true; + if (FirePvE.CanUse(out act)) return true; + } + else + { + if (BlizzardIiPvE.CanUse(out act)) return true; + if (BlizzardIiiPvE.CanUse(out act)) return true; + if (BlizzardPvE.CanUse(out act)) return true; + } + return false; + } + + private bool UsePolyglot(out IAction? act, uint gcdCount = 3) + { + act = null; + + if (gcdCount == 0 || IsPolyglotStacksMaxed && EnchinaEndAfterGCD(gcdCount)) + { + if (FoulPvE.CanUse(out act)) return true; + if (XenoglossyPvE.CanUse(out act)) return true; + } + return false; + } + + private bool MaintainStatus(out IAction? act) + { + act = null; + if (CombatElapsedLess(6)) return false; + if (UmbralSoulPvE.CanUse(out act)) return true; + if (InAstralFire && TransposePvE.CanUse(out act)) return true; + if (UseTransposeForParadox && + InUmbralIce && !IsParadoxActive && UmbralIceStacks == 3 + && TransposePvE.CanUse(out act)) return true; + + return false; + } + + [RotationDesc(ActionID.BetweenTheLinesPvE, ActionID.LeyLinesPvE)] + protected override bool HealSingleAbility(out IAction? act) + { + if (BetweenTheLinesPvE.CanUse(out act)) return true; + if (LeyLinesPvE.CanUse(out act)) return true; + + return base.HealSingleAbility(out act); + } +} \ No newline at end of file diff --git a/BasicRotations/Ranged/DNC_Beta.cs b/BasicRotations/Ranged/DNC_Beta.cs new file mode 100644 index 0000000..7b2abdc --- /dev/null +++ b/BasicRotations/Ranged/DNC_Beta.cs @@ -0,0 +1,203 @@ +namespace DefaultRotations.Ranged; + +[Rotation("Testing Rotations", CombatType.PvE, GameVersion = "6.58", Description = "Additonal contributions to this rotation thanks to Toshi!")] +[SourceCode(Path = "main/DefaultRotations/Ranged/DNC_Beta.cs")] +public sealed class DNC_Beta : DancerRotation +{ + // Override the method for actions to be taken during countdown phase of combat + protected override IAction? CountDownAction(float remainTime) + { + // If there are 15 or fewer seconds remaining in the countdown + if (remainTime <= 15) + { + // Attempt to use Standard Step if applicable + if (StandardStepPvE.CanUse(out var act, skipAoeCheck: true)) return act; + // Fallback to executing step GCD action if Standard Step is not used + if (ExecuteStepGCD(out act)) return act; + } + // If none of the above conditions are met, fallback to the base class method + return base.CountDownAction(remainTime); + } + + // Override the method for handling emergency abilities + protected override bool EmergencyAbility(IAction nextGCD, out IAction? act) + { + act = null; + // Special handling if the last action was Quadruple Technical Finish and level requirement is met + if (IsLastAction(ActionID.QuadrupleTechnicalFinishPvE) && TechnicalStepPvE.EnoughLevel) + { + // Attempt to use Devilment ignoring clipping checks + if (DevilmentPvE.CanUse(out act, skipClippingCheck: true)) return true; + } + // Similar handling for Double Standard Finish when level requirement is not met + else if (IsLastAction(ActionID.DoubleStandardFinishPvE) && !TechnicalStepPvE.EnoughLevel) + { + if (DevilmentPvE.CanUse(out act, skipClippingCheck: true)) return true; + } + + // If currently dancing, defer to the base class emergency handling + if (IsDancing) + { + return base.EmergencyAbility(nextGCD, out act); + } + + // Use burst medicine if cooldown for Technical Step has elapsed sufficiently + if (TechnicalStepPvE.Cooldown.ElapsedAfter(115) + && UseBurstMedicine(out act)) return true; + + // Attempt to use Fan Dance III if available + if (FanDanceIiiPvE.CanUse(out act, skipAoeCheck: true)) return true; + + // Fallback to base class method if none of the above conditions are met + return base.EmergencyAbility(nextGCD, out act); + } + + // Override the method for handling attack abilities + protected override bool AttackAbility(out IAction? act) + { + act = null; + + // If currently in the middle of a dance, no attack ability should be executed + if (IsDancing) return false; + + // Logic for using Fan Dance abilities based on certain conditions + if ((Player.HasStatus(true, StatusID.Devilment) || Feathers > 3 || !TechnicalStepPvE.EnoughLevel) && !FanDanceIiiPvE.CanUse(out act, skipAoeCheck: true)) + { + if (FanDancePvE.CanUse(out act, skipAoeCheck: true)) return true; + if (FanDanceIiPvE.CanUse(out act)) return true; + } + + // Check for conditions to use Flourish + if (((Player.HasStatus(true, StatusID.Devilment)) && (Player.HasStatus(true, StatusID.TechnicalFinish))) || ((!Player.HasStatus(true, StatusID.Devilment)) && (!Player.HasStatus(true, StatusID.TechnicalFinish)))) + { + if (!Player.HasStatus(true, StatusID.ThreefoldFanDance) && FlourishPvE.CanUse(out act)) + { + return true; + } + } + + // Attempt to use Fan Dance IV if available + if (FanDanceIvPvE.CanUse(out act, skipAoeCheck: true)) return true; + + // Attempt to use Closed Position if applicable + if (UseClosedPosition(out act)) return true; + + // Fallback to base class attack ability method if none of the above conditions are met + return base.AttackAbility(out act); + } + + // Override the method for handling general Global Cooldown (GCD) actions + protected override bool GeneralGCD(out IAction? act) + { + act = null; + // If not in combat and lacking the Closed Position status, attempt to use Closed Position + if (!InCombat && !Player.HasStatus(true, StatusID.ClosedPosition) && ClosedPositionPvE.CanUse(out act)) return true; + + // Attempt to execute Dance Finish GCD or Step GCD actions + if (FinishTheDance(out act)) return true; + if (ExecuteStepGCD(out act)) return true; + + // Attempt to use Technical Step in burst mode and if in combat + 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; + + // Fallback to base class general GCD method if none of the above conditions are met + return base.GeneralGCD(out act); + } + + // Helper method to handle attack actions during GCD based on certain conditions + private bool AttackGCD(out IAction? act, bool burst) + { + act = null; + + // Prevent action if currently dancing or holding too many feathers + if (IsDancing || Feathers > 3) return false; + + // Logic for using Saber Dance and Starfall Dance based on burst mode or Esprit levels + if ((burst || Esprit >= 85) && SaberDancePvE.CanUse(out act, skipAoeCheck: true)) return true; + + // Additional logic for using Tillana and Standard Step based on various checks + if (!DevilmentPvE.CanUse(out act, skipComboCheck: true)) + { + if (TillanaPvE.CanUse(out act, skipAoeCheck: true)) return true; + } + + if (StarfallDancePvE.CanUse(out act, skipAoeCheck: true)) return true; + + if (UseStandardStep(out act)) return true; + + // Attempt to use various dance moves based on availability and conditions + if (BloodshowerPvE.CanUse(out act)) return true; + if (FountainfallPvE.CanUse(out act)) return true; + if (RisingWindmillPvE.CanUse(out act)) return true; + if (ReverseCascadePvE.CanUse(out act)) return true; + if (BladeshowerPvE.CanUse(out act)) return true; + if (WindmillPvE.CanUse(out act)) return true; + if (FountainPvE.CanUse(out act)) return true; + if (CascadePvE.CanUse(out act)) return true; + + // Return false if no action is determined to be taken + return false; + } + + private bool UseStandardStep(out IAction act) + { + // Attempt to use Standard Step if available and certain conditions are met + if (!StandardStepPvE.CanUse(out act, skipAoeCheck: true)) return false; + if (Player.WillStatusEndGCD(2, 0, true, StatusID.StandardFinish)) return true; + + // Check for hostiles in range and technical step conditions + if (!HasHostilesInRange) return false; + if (Player.HasStatus(true, StatusID.TechnicalFinish) && Player.WillStatusEndGCD(2, 0, true, StatusID.TechnicalFinish) || TechnicalStepPvE.Cooldown.IsCoolingDown && TechnicalStepPvE.Cooldown.WillHaveOneChargeGCD(2)) return false; + + return true; + } + + // Helper method to decide usage of Closed Position based on specific conditions + private bool UseClosedPosition(out IAction act) + { + // Attempt to use Closed Position if available and certain conditions are met + if (!ClosedPositionPvE.CanUse(out act)) return false; + + if (InCombat && Player.HasStatus(true, StatusID.ClosedPosition)) + { + // Check for party members with Closed Position status + foreach (var friend in PartyMembers) + { + if (friend.HasStatus(true, StatusID.ClosedPosition_2026)) + { + // Use Closed Position if target is not the same as the friend with the status + if (ClosedPositionPvE.Target.Target != friend) return true; + break; + } + } + } + return false; + } + private bool FinishTheDance(out IAction? act) + { + bool areDanceTargetsInRange = AllHostileTargets.Any(hostile => hostile.DistanceToPlayer() < 15); + + // Check for Standard Step if targets are in range or status is about to end. + if (Player.HasStatus(true, StatusID.StandardStep) && CompletedSteps == 2 && + (areDanceTargetsInRange || Player.WillStatusEnd(1f, true, StatusID.StandardStep)) && + DoubleStandardFinishPvE.CanUse(out act, skipAoeCheck: true)) + { + return true; + } + + // Check for Technical Step if targets are in range or status is about to end. + if (Player.HasStatus(true, StatusID.TechnicalStep) && CompletedSteps == 4 && + (areDanceTargetsInRange || Player.WillStatusEnd(1f, true, StatusID.TechnicalStep)) && + QuadrupleTechnicalFinishPvE.CanUse(out act, skipAoeCheck: true)) + { + return true; + } + + act = null; + return false; + } + +} diff --git a/BasicRotations/Ranged/MCH_Beta.cs b/BasicRotations/Ranged/MCH_Beta.cs index d605d97..3877eba 100644 --- a/BasicRotations/Ranged/MCH_Beta.cs +++ b/BasicRotations/Ranged/MCH_Beta.cs @@ -155,9 +155,6 @@ private bool CanUseHyperchargePvE(out IAction? act) //Cannot AOE ((!SpreadShotPvE.CanUse(out _)) && - ////Combat elapsed 12 seconds - //(!CombatElapsedLess(12)) - //&& // AirAnchor Enough Level % AirAnchor ((AirAnchorPvE.EnoughLevel && AirAnchorPvE.Cooldown.WillHaveOneCharge(REST_TIME)) ||