From c97be9ec5185bc4dc1548d9636a8fa8515b2218c Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E7=A7=8B=E6=B0=B4?= <1123993881@qq.com>
Date: Fri, 19 Jan 2024 12:43:04 +0800
Subject: [PATCH] fix: add back the rotations.
---
DefaultRotations/AssemblyInfo.cs | 2 +
DefaultRotations/DefaultRotations.csproj | 29 ++
DefaultRotations/Healer/AST_Default.cs | 242 ++++++++++
DefaultRotations/Healer/SCH_Default.cs | 225 +++++++++
DefaultRotations/Healer/SGE_Default.cs | 294 ++++++++++++
DefaultRotations/Healer/WHM_Default.cs | 166 +++++++
DefaultRotations/Magical/BLM_Default.cs | 367 ++++++++++++++
DefaultRotations/Magical/BLU_Default.cs | 448 ++++++++++++++++++
DefaultRotations/Magical/BLU_Extra.cs | 42 ++
DefaultRotations/Magical/RDM_Default.cs | 162 +++++++
DefaultRotations/Magical/SMN_Default.cs | 175 +++++++
DefaultRotations/Melee/DRG_Default.cs | 103 ++++
DefaultRotations/Melee/MNK_Default.cs | 210 ++++++++
DefaultRotations/Melee/NIN_Default.cs | 379 +++++++++++++++
DefaultRotations/Melee/RPR_Default.cs | 165 +++++++
DefaultRotations/Melee/SAM_Default.cs | 137 ++++++
DefaultRotations/Ranged/BRD_Default.cs | 178 +++++++
DefaultRotations/Ranged/DNC_Default.cs | 142 ++++++
DefaultRotations/Ranged/MCH_Default.cs | 164 +++++++
DefaultRotations/Tank/DRK_Default.cs | 220 +++++++++
DefaultRotations/Tank/GNB_Default.cs | 239 ++++++++++
DefaultRotations/Tank/PLD_Default.cs | 146 ++++++
DefaultRotations/Tank/WAR_Default.cs | 129 +++++
Directory.Build.props | 1 +
.../Actions/BaseAction_Target.cs | 10 +-
RotationSolver.Basic/Data/Aspect.cs | 5 -
.../Rotations/Basic/BLU_Base.cs | 2 +-
RotationSolver.sln | 30 +-
RotationSolver/RotationSolver.csproj | 2 +-
29 files changed, 4392 insertions(+), 22 deletions(-)
create mode 100644 DefaultRotations/AssemblyInfo.cs
create mode 100644 DefaultRotations/DefaultRotations.csproj
create mode 100644 DefaultRotations/Healer/AST_Default.cs
create mode 100644 DefaultRotations/Healer/SCH_Default.cs
create mode 100644 DefaultRotations/Healer/SGE_Default.cs
create mode 100644 DefaultRotations/Healer/WHM_Default.cs
create mode 100644 DefaultRotations/Magical/BLM_Default.cs
create mode 100644 DefaultRotations/Magical/BLU_Default.cs
create mode 100644 DefaultRotations/Magical/BLU_Extra.cs
create mode 100644 DefaultRotations/Magical/RDM_Default.cs
create mode 100644 DefaultRotations/Magical/SMN_Default.cs
create mode 100644 DefaultRotations/Melee/DRG_Default.cs
create mode 100644 DefaultRotations/Melee/MNK_Default.cs
create mode 100644 DefaultRotations/Melee/NIN_Default.cs
create mode 100644 DefaultRotations/Melee/RPR_Default.cs
create mode 100644 DefaultRotations/Melee/SAM_Default.cs
create mode 100644 DefaultRotations/Ranged/BRD_Default.cs
create mode 100644 DefaultRotations/Ranged/DNC_Default.cs
create mode 100644 DefaultRotations/Ranged/MCH_Default.cs
create mode 100644 DefaultRotations/Tank/DRK_Default.cs
create mode 100644 DefaultRotations/Tank/GNB_Default.cs
create mode 100644 DefaultRotations/Tank/PLD_Default.cs
create mode 100644 DefaultRotations/Tank/WAR_Default.cs
diff --git a/DefaultRotations/AssemblyInfo.cs b/DefaultRotations/AssemblyInfo.cs
new file mode 100644
index 000000000..5adc8f397
--- /dev/null
+++ b/DefaultRotations/AssemblyInfo.cs
@@ -0,0 +1,2 @@
+[assembly: AuthorHash(Hash ="Ig4lHXUohMZNIeheUtAtRg==")]
+[assembly: AssemblyLink(Donate = "https://ko-fi.com/archited", UserName = "ArchiDog1998", Repository = "FFXIVRotations")]
\ No newline at end of file
diff --git a/DefaultRotations/DefaultRotations.csproj b/DefaultRotations/DefaultRotations.csproj
new file mode 100644
index 000000000..d24d89298
--- /dev/null
+++ b/DefaultRotations/DefaultRotations.csproj
@@ -0,0 +1,29 @@
+
+
+ full
+
+
+ full
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/DefaultRotations/Healer/AST_Default.cs b/DefaultRotations/Healer/AST_Default.cs
new file mode 100644
index 000000000..104aec380
--- /dev/null
+++ b/DefaultRotations/Healer/AST_Default.cs
@@ -0,0 +1,242 @@
+namespace DefaultRotations.Healer;
+
+[RotationDesc(ActionID.Divination)]
+[SourceCode(Path = "main/DefaultRotations/Healer/AST_Default.cs")]
+public sealed class AST_Default : AST_Base
+{
+ public override CombatType Type => CombatType.PvE;
+
+ public override string GameVersion => "6.28";
+
+ public override string RotationName => "Default";
+
+ protected override IRotationConfigSet CreateConfiguration()
+ => base.CreateConfiguration()
+ .SetFloat(RotationSolver.Basic.Configuration.ConfigUnitType.Seconds, CombatType.PvE, "UseEarthlyStarTime", 15, "Use Earthly Star during countdown timer.", 4, 20);
+
+ static IBaseAction AspectedBeneficDefense { get; } = new BaseAction(ActionID.AspectedBenefic, ActionOption.Hot)
+ {
+ ChoiceTarget = TargetFilter.FindAttackedTarget,
+ ActionCheck = (b, m) => b.IsJobCategory(JobRole.Tank),
+ TargetStatus = new StatusID[] { StatusID.AspectedBenefic },
+ };
+
+ protected override IAction CountDownAction(float remainTime)
+ {
+ if (remainTime < Malefic.CastTime + CountDownAhead
+ && Malefic.CanUse(out var act, CanUseOption.IgnoreClippingCheck)) return act;
+ if (remainTime < 3 && UseBurstMedicine(out act)) return act;
+ if (remainTime < 4 && AspectedBeneficDefense.CanUse(out act, CanUseOption.IgnoreClippingCheck)) return act;
+ if (remainTime < Configs.GetFloat("UseEarthlyStarTime")
+ && EarthlyStar.CanUse(out act, CanUseOption.IgnoreClippingCheck)) return act;
+ if (remainTime < 30 && Draw.CanUse(out act, CanUseOption.IgnoreClippingCheck)) return act;
+
+ return base.CountDownAction(remainTime);
+ }
+
+ [RotationDesc(ActionID.CelestialIntersection, ActionID.Exaltation)]
+ protected override bool DefenseSingleAbility(out IAction act)
+ {
+ //天星交错
+ if (CelestialIntersection.CanUse(out act, CanUseOption.EmptyOrSkipCombo)) return true;
+
+ //给T减伤,这个很重要。
+ if (Exaltation.CanUse(out act)) return true;
+ return base.DefenseSingleAbility(out act);
+ }
+
+ [RotationDesc(ActionID.Macrocosmos)]
+ protected override bool DefenseAreaGCD(out IAction act)
+ {
+ if (Macrocosmos.CanUse(out act)) return true;
+ return base.DefenseAreaGCD(out act);
+ }
+
+ [RotationDesc(ActionID.CollectiveUnconscious)]
+ protected override bool DefenseAreaAbility(out IAction act)
+ {
+ if (CollectiveUnconscious.CanUse(out act)) return true;
+
+ return base.DefenseAreaAbility(out act);
+ }
+
+ protected override bool GeneralGCD(out IAction act)
+ {
+ //Add AspectedBeneficwhen not in combat.
+ if (NotInCombatDelay && AspectedBeneficDefense.CanUse(out act)) return true;
+
+ //群体输出
+ if (Gravity.CanUse(out act)) return true;
+
+ //单体输出
+ if (Combust.CanUse(out act)) return true;
+ if (Malefic.CanUse(out act)) return true;
+ if (Combust.CanUse(out act, CanUseOption.MustUse)) return true;
+
+ return base.GeneralGCD(out act);
+ }
+
+ [RotationDesc(ActionID.AspectedHelios, ActionID.Helios)]
+ protected override bool HealAreaGCD(out IAction act)
+ {
+ //阳星相位
+ if (AspectedHelios.CanUse(out act)) return true;
+
+ //阳星
+ if (Helios.CanUse(out act)) return true;
+
+ return base.HealAreaGCD(out act);
+ }
+
+ protected override bool EmergencyAbility(IAction nextGCD, out IAction act)
+ {
+ if (base.EmergencyAbility(nextGCD, out act)) return true;
+
+ if (PartyHealers.Count() == 1 && Player.HasStatus(false, StatusID.Silence)
+ && HasHostilesInRange && EchoDrops.CanUse(out act)) return true;
+
+ if (!InCombat) return false;
+
+ //如果要群奶了,先上个天宫图!
+ if (nextGCD.IsTheSameTo(true, AspectedHelios, Helios))
+ {
+ if (Horoscope.CanUse(out act)) return true;
+
+ //中间学派
+ if (NeutralSect.CanUse(out act)) return true;
+ }
+
+ //如果要单奶了,先上星位合图!
+ if (nextGCD.IsTheSameTo(true, Benefic, Benefic2, AspectedBenefic))
+ {
+ if (Synastry.CanUse(out act)) return true;
+ }
+ return base.EmergencyAbility(nextGCD, out act);
+ }
+
+ protected override bool GeneralAbility(out IAction act)
+ {
+ //如果当前还没有卡牌,那就抽一张
+ if (Draw.CanUse(out act)) return true;
+
+ //如果当前卡牌已经拥有了,就重抽
+ if (Redraw.CanUse(out act)) return true;
+
+ return base.GeneralAbility(out act);
+ }
+
+ [RotationDesc(ActionID.AspectedBenefic, ActionID.Benefic2, ActionID.Benefic)]
+ protected override bool HealSingleGCD(out IAction act)
+ {
+ //吉星相位
+ if (AspectedBenefic.CanUse(out act)
+ && (IsMoving || AspectedBenefic.Target.GetHealthRatio() > 0.4)) return true;
+
+ //福星
+ if (Benefic2.CanUse(out act)) return true;
+
+ //吉星
+ if (Benefic.CanUse(out act)) return true;
+
+ return base.HealSingleGCD(out act);
+ }
+
+ protected override bool AttackAbility(out IAction act)
+ {
+ if (IsBurst && !IsMoving && Divination.CanUse(out act)) return true;
+
+ //如果当前还没有皇冠卡牌,那就抽一张
+ if (MinorArcana.CanUse(out act, CanUseOption.EmptyOrSkipCombo)) return true;
+
+ //如果当前还没有卡牌,那就抽一张
+ if (Draw.CanUse(out act, IsBurst ? CanUseOption.EmptyOrSkipCombo : CanUseOption.None)) return true;
+
+ //光速,创造更多的内插能力技的机会。
+ if (IsMoving && Lightspeed.CanUse(out act)) return true;
+
+
+ if (!IsMoving)
+ {
+ //如果没有地星也没有巨星,那就试试看能不能放个。
+ if (!Player.HasStatus(true, StatusID.EarthlyDominance, StatusID.GiantDominance))
+ {
+ if (EarthlyStar.CanUse(out act, CanUseOption.MustUse)) return true;
+ }
+ //加星星的进攻Buff
+ if (Astrodyne.CanUse(out act)) return true;
+ }
+
+ if (DrawnCrownCard == CardType.LORD || MinorArcana.WillHaveOneChargeGCD(1, 0))
+ {
+ //进攻牌,随便发。或者CD要转好了,赶紧发掉。
+ if (MinorArcana.CanUse(out act, CanUseOption.MustUse)) return true;
+ }
+
+ //发牌
+ if (Redraw.CanUse(out act)) return true;
+ if (PlayCard(out act)) return true;
+
+ return base.AttackAbility(out act);
+ }
+
+ [RotationDesc(ActionID.EssentialDignity, ActionID.CelestialIntersection, ActionID.CelestialOpposition,
+ ActionID.EarthlyStar, ActionID.Horoscope)]
+ protected override bool HealSingleAbility(out IAction act)
+ {
+ //常规奶
+ if (EssentialDignity.CanUse(out act)) return true;
+ //带盾奶
+ if (CelestialIntersection.CanUse(out act, CanUseOption.EmptyOrSkipCombo)) return true;
+
+ //奶量牌,要看情况。
+ if (DrawnCrownCard == CardType.LADY && MinorArcana.CanUse(out act, CanUseOption.MustUse)) return true;
+
+ var tank = PartyTanks;
+ var isBoss = Malefic.Target.IsBossFromTTK();
+ if (EssentialDignity.IsCoolingDown && tank.Count() == 1 && tank.Any(t => t.GetHealthRatio() < 0.5) && !isBoss)
+ {
+ //群Hot
+ if (CelestialOpposition.CanUse(out act)) return true;
+
+ //如果有巨星主宰
+ if (Player.HasStatus(true, StatusID.GiantDominance))
+ {
+ //需要回血的时候炸了。
+ act = EarthlyStar;
+ return true;
+ }
+
+ //天宫图
+ if (!Player.HasStatus(true, StatusID.HoroscopeHelios, StatusID.Horoscope) && Horoscope.CanUse(out act)) return true;
+ //阳星天宫图
+ if (Player.HasStatus(true, StatusID.HoroscopeHelios) && Horoscope.CanUse(out act)) return true;
+ //超紧急情况天宫图
+ if (tank.Any(t => t.GetHealthRatio() < 0.3) && Horoscope.CanUse(out act)) return true;
+ }
+
+ return base.HealSingleAbility(out act);
+ }
+
+ [RotationDesc(ActionID.CelestialOpposition, ActionID.EarthlyStar, ActionID.Horoscope)]
+ protected override bool HealAreaAbility(out IAction act)
+ {
+ //群Hot
+ if (CelestialOpposition.CanUse(out act)) return true;
+
+ //如果有巨星主宰
+ if (Player.HasStatus(true, StatusID.GiantDominance))
+ {
+ //需要回血的时候炸了。
+ act = EarthlyStar;
+ return true;
+ }
+
+ //天宫图
+ if (Player.HasStatus(true, StatusID.HoroscopeHelios) && Horoscope.CanUse(out act)) return true;
+
+ //奶量牌,要看情况。
+ if (DrawnCrownCard == CardType.LADY && MinorArcana.CanUse(out act, CanUseOption.MustUse)) return true;
+
+ return base.HealAreaAbility(out act);
+ }
+}
diff --git a/DefaultRotations/Healer/SCH_Default.cs b/DefaultRotations/Healer/SCH_Default.cs
new file mode 100644
index 000000000..82a0a938f
--- /dev/null
+++ b/DefaultRotations/Healer/SCH_Default.cs
@@ -0,0 +1,225 @@
+namespace DefaultRotations.Healer;
+
+[RotationDesc(ActionID.ChainStratagem)]
+[SourceCode(Path = "main/DefaultRotations/Healer/SCH_Default.cs")]
+public sealed class SCH_Default : SCH_Base
+{
+ public override CombatType Type => CombatType.PvE;
+
+ public override string GameVersion => "6.28";
+
+ public override string RotationName => "Default";
+
+ public override bool CanHealSingleSpell => base.CanHealSingleSpell && (Configs.GetBool("GCDHeal") || PartyHealers.Count() < 2);
+ public override bool CanHealAreaSpell => base.CanHealAreaSpell && (Configs.GetBool("GCDHeal") || PartyHealers.Count() < 2);
+
+ protected override IRotationConfigSet CreateConfiguration()
+ {
+ return base.CreateConfiguration().SetBool(CombatType.PvE,"GCDHeal", false, "Use spells with cast times to heal.")
+ .SetBool(CombatType.PvE, "prevDUN", false, "Recitation at 15 seconds remaining on Countdown.")
+ .SetBool(CombatType.PvE, "GiveT", false, "Give Recitation to Tank");
+ }
+
+ protected override bool EmergencyAbility(IAction nextGCD, out IAction act)
+ {
+ //秘策绑定单盾群盾
+ if (nextGCD.IsTheSameTo(true, Succor, Adloquium))
+ {
+ if (Recitation.CanUse(out act)) return true;
+ }
+
+ //Remove Aetherpact
+ foreach (var item in PartyMembers)
+ {
+ if (item.GetHealthRatio() < 0.9) continue;
+ if (item.HasStatus(true, StatusID.Aetherpact))
+ {
+ act = Aetherpact;
+ return true;
+ }
+ }
+
+ return base.EmergencyAbility(nextGCD, out act);
+ }
+
+ protected override bool GeneralGCD(out IAction act)
+ {
+ //召唤小仙女
+ if (SummonEos.CanUse(out act)) return true;
+
+ //DoT
+ if (Bio.CanUse(out act)) return true;
+
+ //AOE
+ if (ArtOfWar.CanUse(out act)) return true;
+
+ //Single
+ if (Ruin.CanUse(out act)) return true;
+ if (Ruin2.CanUse(out act)) return true;
+
+ //Add dot.
+ if (Bio.CanUse(out act, CanUseOption.MustUse)) return true;
+
+ return base.GeneralGCD(out act);
+ }
+
+ [RotationDesc(ActionID.Adloquium, ActionID.Physick)]
+ protected override bool HealSingleGCD(out IAction act)
+ {
+ //鼓舞激励之策
+ if (Adloquium.CanUse(out act)) return true;
+
+ //医术
+ if (Physick.CanUse(out act)) return true;
+
+ return base.HealSingleGCD(out act);
+ }
+
+ [RotationDesc(ActionID.Aetherpact, ActionID.Protraction, ActionID.SacredSoil, ActionID.Excogitation, ActionID.Lustrate, ActionID.Aetherpact)]
+ protected override bool HealSingleAbility(out IAction act)
+ {
+ //判断是否有人有线
+ var haveLink = PartyMembers.Any(p => p.HasStatus(true, StatusID.Aetherpact));
+
+ //以太契约
+ if (Aetherpact.CanUse(out act) && FairyGauge >= 70 && !haveLink) return true;
+
+ //生命回生法
+ if (Protraction.CanUse(out act)) return true;
+
+ //野战治疗阵
+ if (SacredSoil.CanUse(out act)) return true;
+
+ //深谋远虑之策
+ if (Excogitation.CanUse(out act)) return true;
+
+ //生命活性法
+ if (Lustrate.CanUse(out act)) return true;
+
+ //以太契约
+ if (Aetherpact.CanUse(out act) && !haveLink) return true;
+
+ return base.HealSingleAbility(out act);
+ }
+
+ [RotationDesc(ActionID.Excogitation)]
+ protected override bool DefenseSingleAbility(out IAction act)
+ {
+ if (Excogitation.CanUse(out act)) return true;
+ return base.DefenseSingleAbility(out act);
+ }
+
+ [RotationDesc(ActionID.Succor)]
+ protected override bool HealAreaGCD(out IAction act)
+ {
+ //士气高扬之策
+ if (Succor.CanUse(out act)) return true;
+
+ return base.HealAreaGCD(out act);
+ }
+
+
+ [RotationDesc(ActionID.SummonSeraph, ActionID.Consolation, ActionID.WhisperingDawn, ActionID.SacredSoil, ActionID.Indomitability)]
+ protected override bool HealAreaAbility(out IAction act)
+ {
+ //慰藉
+ if (WhisperingDawn.ElapsedOneChargeAfterGCD(1) || FeyIllumination.ElapsedOneChargeAfterGCD(1) || FeyBlessing.ElapsedOneChargeAfterGCD(1))
+ {
+ if (SummonSeraph.CanUse(out act)) return true;
+ }
+ if (Consolation.CanUse(out act, CanUseOption.EmptyOrSkipCombo)) return true;
+
+ //异想的祥光
+ if (FeyBlessing.CanUse(out act)) return true;
+
+ //仙光的低语
+ if (WhisperingDawn.CanUse(out act)) return true;
+
+ //野战治疗阵
+ if (SacredSoil.CanUse(out act)) return true;
+
+ //不屈不挠之策
+ if (Indomitability.CanUse(out act)) return true;
+
+ return base.HealAreaAbility(out act);
+ }
+
+ [RotationDesc(ActionID.Succor)]
+ protected override bool DefenseAreaGCD(out IAction act)
+ {
+ if (Succor.CanUse(out act)) return true;
+ return base.DefenseAreaGCD(out act);
+ }
+
+ [RotationDesc(ActionID.FeyIllumination, ActionID.Expedient, ActionID.SummonSeraph, ActionID.Consolation, ActionID.SacredSoil)]
+ protected override bool DefenseAreaAbility(out IAction act)
+ {
+ //异想的幻光
+ if (FeyIllumination.CanUse(out act)) return true;
+
+ //疾风怒涛之计
+ if (Expedient.CanUse(out act)) return true;
+
+ //慰藉
+ if (WhisperingDawn.ElapsedOneChargeAfterGCD(1) || FeyIllumination.ElapsedOneChargeAfterGCD(1) || FeyBlessing.ElapsedOneChargeAfterGCD(1))
+ {
+ if (SummonSeraph.CanUse(out act)) return true;
+ }
+ if (Consolation.CanUse(out act, CanUseOption.EmptyOrSkipCombo)) return true;
+
+ //野战治疗阵
+ if (SacredSoil.CanUse(out act)) return true;
+
+ return base.DefenseAreaAbility(out act);
+ }
+
+
+ protected override bool AttackAbility(out IAction act)
+ {
+ if (IsBurst)
+ {
+ //连环计
+ if (ChainStratagem.CanUse(out act)) return true;
+ }
+
+ if (Dissipation.EnoughLevel && Dissipation.WillHaveOneChargeGCD(3) && Dissipation.IsEnabled || Aetherflow.WillHaveOneChargeGCD(3))
+ {
+ //能量吸收
+ if (EnergyDrain.CanUse(out act, CanUseOption.EmptyOrSkipCombo)) return true;
+ }
+
+ //转化
+ if (Dissipation.CanUse(out act)) return true;
+
+ //以太超流
+ if (Aetherflow.CanUse(out act)) return true;
+
+ return base.AttackAbility(out act);
+ }
+
+ //15秒秘策单盾扩散
+ protected override IAction CountDownAction(float remainTime)
+ {
+ if (remainTime < Ruin.CastTime + CountDownAhead
+ && Ruin.CanUse(out var act)) return act;
+
+ if (Configs.GetBool("prevDUN") && remainTime <= 15 && !DeploymentTactics.IsCoolingDown && PartyMembers.Count() > 1)
+ {
+
+ if (!Recitation.IsCoolingDown) return Recitation;
+ if (!PartyMembers.Any((n) => n.HasStatus(true, StatusID.Galvanize)))
+ {
+ //如果还没上激励就给t一个激励
+ if (Configs.GetBool("GiveT"))
+ {
+ return Adloquium;
+ }
+ }
+ else
+ {
+ return DeploymentTactics;
+ }
+ }
+ return base.CountDownAction(remainTime);
+ }
+}
\ No newline at end of file
diff --git a/DefaultRotations/Healer/SGE_Default.cs b/DefaultRotations/Healer/SGE_Default.cs
new file mode 100644
index 000000000..6fc662b5d
--- /dev/null
+++ b/DefaultRotations/Healer/SGE_Default.cs
@@ -0,0 +1,294 @@
+namespace DefaultRotations.Healer;
+
+[SourceCode(Path = "main/DefaultRotations/Healer/SGE_Default.cs")]
+public sealed class SGE_Default : SGE_Base
+{
+ public override CombatType Type => CombatType.PvE;
+
+ public override string GameVersion => "6.38";
+
+ public override string RotationName => "Default";
+
+ public override string Description => "Please contact Nore#7219 on Discord for questions about this rotation.";
+
+ private static bool InTwoMIsBurst()
+ {
+ if (RatioOfMembersIn2minsBurst >= 0.5) return true;
+ if (RatioOfMembersIn2minsBurst == -1) return true;
+ else return false;
+ }
+
+ private static BaseAction MEukrasianDiagnosis { get; } = new(ActionID.EukrasianDiagnosis, ActionOption.Heal)
+ {
+ ChoiceTarget = (Targets, mustUse) =>
+ {
+ var targets = Targets.GetJobCategory(JobRole.Tank);
+ if (!targets.Any()) return null;
+ return targets.FirstOrDefault();
+ },
+ ActionCheck = (b, m) =>
+ {
+ if (InCombat || HasHostilesInRange) return false;
+ if (b == Player) return false;
+ if (b.HasStatus(false, StatusID.EukrasianDiagnosis, StatusID.EukrasianPrognosis, StatusID.Galvanize)) return false;
+ return true;
+ }
+ };
+
+ public override bool CanHealSingleSpell => base.CanHealSingleSpell && (Configs.GetBool("GCDHeal") || PartyHealers.Count() < 2);
+ public override bool CanHealAreaSpell => base.CanHealAreaSpell && (Configs.GetBool("GCDHeal") || PartyHealers.Count() < 2);
+
+ protected override IRotationConfigSet CreateConfiguration()
+ {
+ return base.CreateConfiguration().SetBool(CombatType.PvE, "GCDHeal", false, "Use spells with cast times to heal.");
+ }
+
+ protected override IAction CountDownAction(float remainTime)
+ {
+ if (remainTime <= 1.5 && Dosis.CanUse(out var act)) return act;
+ if (remainTime <= 3 && UseBurstMedicine(out act)) return act;
+ return base.CountDownAction(remainTime);
+ }
+ protected override bool EmergencyAbility(IAction nextGCD, out IAction act)
+ {
+ if (base.EmergencyAbility(nextGCD, out act)) return true;
+
+ if (nextGCD.IsTheSameTo(false, Pneuma, EukrasianDiagnosis,
+ EukrasianPrognosis, Diagnosis, Prognosis))
+ {
+ if (Zoe.CanUse(out act)) return true;
+ }
+
+ if (nextGCD.IsTheSameTo(false, Pneuma))
+ {
+ if (Krasis.CanUse(out act)) return true;
+ }
+
+ return base.EmergencyAbility(nextGCD, out act);
+ }
+
+ [RotationDesc(ActionID.Haima, ActionID.Taurochole, ActionID.Panhaima, ActionID.Kerachole, ActionID.Holos)]
+ protected override bool DefenseSingleAbility(out IAction act)
+ {
+ if (Addersgall <= 1)
+ {
+ if (Haima.CanUse(out act, CanUseOption.OnLastAbility)) return true;
+ }
+
+ if (Taurochole.CanUse(out act, CanUseOption.OnLastAbility) && Taurochole.Target.GetHealthRatio() < 0.8) return true;
+
+ if (Addersgall <= 1)
+ {
+ if ((!Haima.EnoughLevel || Haima.ElapsedAfter(20)) && Panhaima.CanUse(out act, CanUseOption.OnLastAbility)) return true;
+ }
+
+ if ((!Taurochole.EnoughLevel || Taurochole.ElapsedAfter(20)) && Kerachole.CanUse(out act, CanUseOption.OnLastAbility)) return true;
+
+ if (Holos.CanUse(out act, CanUseOption.OnLastAbility)) return true;
+
+ return base.DefenseSingleAbility(out act);
+ }
+
+ [RotationDesc(ActionID.EukrasianDiagnosis)]
+ protected override bool DefenseSingleGCD(out IAction act)
+ {
+ if (EukrasianDiagnosis.CanUse(out act))
+ {
+ if (EukrasianDiagnosis.Target.HasStatus(false,
+ StatusID.EukrasianDiagnosis,
+ StatusID.EukrasianPrognosis,
+ StatusID.Galvanize
+ )) return false;
+
+ if (Eukrasia.CanUse(out act)) return true;
+
+ act = EukrasianDiagnosis;
+ return true;
+ }
+
+ return base.DefenseSingleGCD(out act);
+ }
+
+ [RotationDesc(ActionID.Panhaima, ActionID.Kerachole, ActionID.Holos)]
+ protected override bool DefenseAreaAbility(out IAction act)
+ {
+ if (Addersgall <= 1)
+ {
+ if (Panhaima.CanUse(out act, CanUseOption.OnLastAbility)) return true;
+ }
+
+ if (Kerachole.CanUse(out act, CanUseOption.OnLastAbility)) return true;
+
+ if (Holos.CanUse(out act, CanUseOption.OnLastAbility)) return true;
+
+ return base.DefenseAreaAbility(out act);
+ }
+
+ [RotationDesc(ActionID.EukrasianPrognosis)]
+ protected override bool DefenseAreaGCD(out IAction act)
+ {
+ if (EukrasianPrognosis.CanUse(out act))
+ {
+ if (EukrasianDiagnosis.Target.HasStatus(false,
+ StatusID.EukrasianDiagnosis,
+ StatusID.EukrasianPrognosis,
+ StatusID.Galvanize
+ )) return false;
+
+ if (Eukrasia.CanUse(out act)) return true;
+
+ act = EukrasianPrognosis;
+ return true;
+ }
+
+ return base.DefenseAreaGCD(out act);
+ }
+
+ protected override bool GeneralAbility(out IAction act)
+ {
+ if (Kardia.CanUse(out act)) return true;
+
+ if (Addersgall <= 1 && Rhizomata.CanUse(out act)) return true;
+
+ if (Soteria.CanUse(out act) && PartyMembers.Any(b => b.HasStatus(true, StatusID.Kardion) && b.GetHealthRatio() < HealthSingleAbility)) return true;
+
+ if (Pepsis.CanUse(out act)) return true;
+
+ return base.GeneralAbility(out act);
+ }
+
+ protected override bool GeneralGCD(out IAction act)
+ {
+ if (HostileTarget?.IsBossFromTTK() ?? false)
+ {
+ if (EukrasianDosis.CanUse(out _, CanUseOption.IgnoreCastCheck))
+ {
+ if (Eukrasia.CanUse(out act, CanUseOption.IgnoreCastCheck)) return true;
+ act = EukrasianDosis;
+ return true;
+ }
+ }
+
+ var option = CanUseOption.MustUse;
+ if (IsMoving || Dyskrasia.CanUse(out _) || InTwoMIsBurst()) option |= CanUseOption.EmptyOrSkipCombo;
+ if (Phlegma3.CanUse(out act, option)) return true;
+ if (!Phlegma3.EnoughLevel && Phlegma2.CanUse(out act, option)) return true;
+ if (!Phlegma2.EnoughLevel && Phlegma.CanUse(out act, option)) return true;
+
+ if (PartyMembers.Any(b => b.GetHealthRatio() < 0.20f) || PartyTanks.Any(t => t.GetHealthRatio() < 0.6f))
+ {
+ if (Pneuma.CanUse(out act, CanUseOption.MustUse)) return true;
+ }
+
+ if (IsMoving && Toxikon.CanUse(out act, CanUseOption.MustUse)) return true;
+
+ if (Dyskrasia.CanUse(out act)) return true;
+
+ if (EukrasianDosis.CanUse(out _, CanUseOption.IgnoreCastCheck))
+ {
+ if (Eukrasia.CanUse(out act, CanUseOption.IgnoreCastCheck)) return true;
+ act = EukrasianDosis;
+ return true;
+ }
+
+ if (Dosis.CanUse(out act)) return true;
+
+ if (MEukrasianDiagnosis.CanUse(out _))
+ {
+ if (Eukrasia.CanUse(out act)) return true;
+
+ act = MEukrasianDiagnosis;
+ return true;
+ }
+
+ return base.GeneralGCD(out act);
+ }
+
+ [RotationDesc(ActionID.Taurochole, ActionID.Kerachole, ActionID.Druochole, ActionID.Holos, ActionID.Physis, ActionID.Panhaima)]
+ protected override bool HealSingleAbility(out IAction act)
+ {
+ if (Taurochole.CanUse(out act)) return true;
+
+ if (Kerachole.CanUse(out act) && EnhancedKerachole.EnoughLevel) return true;
+
+ if ((!Taurochole.EnoughLevel || Taurochole.IsCoolingDown) && Druochole.CanUse(out act)) return true;
+
+ if (Soteria.CanUse(out act) && PartyMembers.Any(b => b.HasStatus(true, StatusID.Kardion) && b.GetHealthRatio() < 0.85f)) return true;
+
+
+ var tank = PartyTanks;
+ if (Addersgall < 1 && (tank.Any(t => t.GetHealthRatio() < 0.65f) || PartyMembers.Any(b => b.GetHealthRatio() < 0.20f)))
+ {
+ if (Haima.CanUse(out act, CanUseOption.OnLastAbility)) return true;
+
+ if (Physis2.CanUse(out act)) return true;
+ if (!Physis2.EnoughLevel && Physis.CanUse(out act)) return true;
+
+ if (Holos.CanUse(out act, CanUseOption.OnLastAbility)) return true;
+
+ if ((!Haima.EnoughLevel || Haima.ElapsedAfter(20)) && Panhaima.CanUse(out act, CanUseOption.OnLastAbility)) return true;
+ }
+
+ if (PartyTanks.Any(t => t.GetHealthRatio() < 0.60f))
+ {
+ if (Zoe.CanUse(out act)) return true;
+ }
+
+ if (PartyTanks.Any(t => t.GetHealthRatio() < 0.70f) || PartyMembers.Any(b => b.GetHealthRatio() < 0.30f))
+ {
+ if (Krasis.CanUse(out act)) return true;
+ }
+
+ if (Kerachole.CanUse(out act)) return true;
+
+ return base.HealSingleAbility(out act);
+ }
+
+ [RotationDesc(ActionID.Diagnosis)]
+ protected override bool HealSingleGCD(out IAction act)
+ {
+ if (Diagnosis.CanUse(out act)) return true;
+ return base.HealSingleGCD(out act);
+ }
+
+ [RotationDesc(ActionID.Pneuma, ActionID.Prognosis, ActionID.EukrasianPrognosis)]
+ protected override bool HealAreaGCD(out IAction act)
+ {
+ if (PartyMembersAverHP < 0.65f || Dyskrasia.CanUse(out _) && PartyTanks.Any(t => t.GetHealthRatio() < 0.6f))
+ {
+ if (Pneuma.CanUse(out act, CanUseOption.MustUse)) return true;
+ }
+
+ if (Player.HasStatus(false, StatusID.EukrasianDiagnosis, StatusID.EukrasianPrognosis, StatusID.Galvanize))
+ {
+ if (Prognosis.CanUse(out act)) return true;
+ }
+
+ if (EukrasianPrognosis.CanUse(out _))
+ {
+ if (Eukrasia.CanUse(out act)) return true;
+
+ act = EukrasianPrognosis;
+ return true;
+ }
+
+ return base.HealAreaGCD(out act);
+ }
+
+ [RotationDesc(ActionID.Kerachole, ActionID.Physis, ActionID.Holos, ActionID.Ixochole)]
+ protected override bool HealAreaAbility(out IAction act)
+ {
+ if (Physis2.CanUse(out act)) return true;
+ if (!Physis2.EnoughLevel && Physis.CanUse(out act)) return true;
+
+ if (Kerachole.CanUse(out act, CanUseOption.OnLastAbility) && EnhancedKerachole.EnoughLevel) return true;
+
+ if (Holos.CanUse(out act, CanUseOption.OnLastAbility) && PartyMembersAverHP < 0.50f) return true;
+
+ if (Ixochole.CanUse(out act, CanUseOption.OnLastAbility)) return true;
+
+ if (Kerachole.CanUse(out act, CanUseOption.OnLastAbility)) return true;
+
+ return base.HealAreaAbility(out act);
+ }
+}
diff --git a/DefaultRotations/Healer/WHM_Default.cs b/DefaultRotations/Healer/WHM_Default.cs
new file mode 100644
index 000000000..7c8cbc200
--- /dev/null
+++ b/DefaultRotations/Healer/WHM_Default.cs
@@ -0,0 +1,166 @@
+namespace DefaultRotations.Healer;
+
+[SourceCode(Path = "main/DefaultRotations/Healer/WHM_Default.cs")]
+public sealed class WHM_Default : WHM_Base
+{
+ public override CombatType Type => CombatType.PvE;
+
+ public override string GameVersion => "6.28";
+
+ public override string RotationName => "Default";
+
+ protected override IRotationConfigSet CreateConfiguration()
+ => base.CreateConfiguration()
+ .SetBool(CombatType.PvE, "UseLilyWhenFull", true, "Use Lily at max stacks.")
+ .SetBool(CombatType.PvE, "UsePreRegen", false, "Regen on Tank at 5 seconds remaining on Countdown.");
+ public static IBaseAction RegenDefense { get; } = new BaseAction(ActionID.Regen, ActionOption.Hot)
+ {
+ ChoiceTarget = TargetFilter.FindAttackedTarget,
+ ActionCheck = (b, m) => b.IsJobCategory(JobRole.Tank),
+ TargetStatus = Regen.TargetStatus,
+ };
+
+ protected override bool GeneralGCD(out IAction act)
+ {
+ if (NotInCombatDelay && RegenDefense.CanUse(out act)) return true;
+
+ if (AfflatusMisery.CanUse(out act, CanUseOption.MustUse)) return true;
+
+ bool liliesNearlyFull = Lily == 2 && LilyAfter(17);
+ bool liliesFullNoBlood = Lily == 3 && BloodLily < 3;
+ if (Configs.GetBool("UseLilyWhenFull") && (liliesNearlyFull || liliesFullNoBlood) && AfflatusMisery.EnoughLevel)
+ {
+ if (PartyMembersAverHP < 0.7)
+ {
+ if (AfflatusRapture.CanUse(out act)) return true;
+ }
+ if (AfflatusSolace.CanUse(out act)) return true;
+ }
+
+ if (Holy.CanUse(out act)) return true;
+
+ if (Aero.CanUse(out act)) return true;
+ if (Stone.CanUse(out act)) return true;
+ if (Aero.CanUse(out act, CanUseOption.MustUse )) return true;
+
+ return base.GeneralGCD(out act);
+ }
+
+ protected override bool AttackAbility(out IAction act)
+ {
+ if (PresenceOfMind.CanUse(out act)) return true;
+
+ if (Assize.CanUse(out act, CanUseOption.MustUse)) return true;
+
+ return base.AttackAbility(out act);
+ }
+
+ protected override bool EmergencyAbility(IAction nextGCD, out IAction act)
+ {
+ if (nextGCD is IBaseAction action && action.MPNeed >= 1000 &&
+ ThinAir.CanUse(out act)) return true;
+
+ if (nextGCD.IsTheSameTo(true, AfflatusRapture, Medica, Medica2, Cure3))
+ {
+ if (PlenaryIndulgence.CanUse(out act)) return true;
+ }
+
+ return base.EmergencyAbility(nextGCD, out act);
+ }
+
+ [RotationDesc(ActionID.AfflatusSolace, ActionID.Regen, ActionID.Cure2, ActionID.Cure)]
+ protected override bool HealSingleGCD(out IAction act)
+ {
+ if (AfflatusSolace.CanUse(out act)) return true;
+
+ if (Regen.CanUse(out act)
+ && (IsMoving || Regen.Target.GetHealthRatio() > 0.4)) return true;
+
+ if (Cure2.CanUse(out act)) return true;
+
+ if (Cure.CanUse(out act)) return true;
+
+ return base.HealSingleGCD(out act);
+ }
+
+ [RotationDesc(ActionID.Benediction, ActionID.Asylum, ActionID.DivineBenison, ActionID.Tetragrammaton)]
+ protected override bool HealSingleAbility(out IAction act)
+ {
+ if (Benediction.CanUse(out act) &&
+ Benediction.Target.GetHealthRatio() < 0.3) return true;
+
+ if (!IsMoving && Asylum.CanUse(out act)) return true;
+
+ if (DivineBenison.CanUse(out act)) return true;
+
+ if (Tetragrammaton.CanUse(out act)) return true;
+ return base.HealSingleAbility(out act);
+ }
+
+ [RotationDesc(ActionID.AfflatusRapture, ActionID.Medica2, ActionID.Cure3, ActionID.Medica)]
+ protected override bool HealAreaGCD(out IAction act)
+ {
+ if (AfflatusRapture.CanUse(out act)) return true;
+
+ int hasMedica2 = PartyMembers.Count((n) => n.HasStatus(true, StatusID.Medica2));
+
+ if (Medica2.CanUse(out act) && hasMedica2 < PartyMembers.Count() / 2 && !IsLastAction(true, Medica2)) return true;
+
+ if (Cure3.CanUse(out act)) return true;
+
+ if (Medica.CanUse(out act)) return true;
+
+ return base.HealAreaGCD(out act);
+ }
+
+ [RotationDesc(ActionID.Asylum)]
+ protected override bool HealAreaAbility(out IAction act)
+ {
+ if (Asylum.CanUse(out act)) return true;
+ return base.HealAreaAbility(out act);
+ }
+
+ [RotationDesc(ActionID.DivineBenison, ActionID.Aquaveil)]
+ protected override bool DefenseSingleAbility(out IAction act)
+ {
+ if (DivineBenison.CanUse(out act)) return true;
+
+ if (Aquaveil.CanUse(out act)) return true;
+ return base.DefenseSingleAbility(out act);
+ }
+
+ [RotationDesc(ActionID.Temperance, ActionID.LiturgyOfTheBell)]
+ protected override bool DefenseAreaAbility(out IAction act)
+ {
+ if (Temperance.CanUse(out act)) return true;
+
+ if (LiturgyOfTheBell.CanUse(out act)) return true;
+ return base.DefenseAreaAbility(out act);
+ }
+
+ //[RotationDesc(ActionID.Regen)]
+ //protected override bool DefenseSingleGCD(out IAction act)
+ //{
+ // if (RegenDefense.CanUse(out act)) return true;
+ // return base.DefenseSingleGCD(out act);
+ //}
+
+ //protected override bool DefenseAreaGCD(out IAction act)
+ //{
+ // if (Medica2.CanUse(out act) && PartyMembers.Count((n) => n.HasStatus(true, StatusID.Medica2)) < PartyMembers.Count() / 2) return true;
+ // return base.DefenseAreaGCD(out act);
+ //}
+
+ protected override IAction CountDownAction(float remainTime)
+ {
+ if (remainTime < Stone.CastTime + CountDownAhead
+ && Stone.CanUse(out var act)) return act;
+
+ if (Configs.GetBool("UsePreRegen") && remainTime <= 5 && remainTime > 3)
+ {
+ if (RegenDefense.CanUse(out act, CanUseOption.IgnoreClippingCheck)) return act;
+ if (DivineBenison.CanUse(out act, CanUseOption.IgnoreClippingCheck)) return act;
+ }
+ return base.CountDownAction(remainTime);
+ }
+}
\ No newline at end of file
diff --git a/DefaultRotations/Magical/BLM_Default.cs b/DefaultRotations/Magical/BLM_Default.cs
new file mode 100644
index 000000000..20aac9274
--- /dev/null
+++ b/DefaultRotations/Magical/BLM_Default.cs
@@ -0,0 +1,367 @@
+namespace DefaultRotations.Magical;
+
+[SourceCode(Path = "main/DefaultRotations/Magical/BLM_Default.cs")]
+public class BLM_Default : BLM_Base
+{
+ public override CombatType Type => CombatType.PvE;
+
+ public override string GameVersion => "6.31";
+
+ public override string RotationName => "Default";
+
+ private static bool NeedToGoIce
+ {
+ get
+ {
+ //Can use Despair.
+ if (Despair.EnoughLevel && CurrentMp >= Despair.MPNeed) return false;
+
+ //Can use Fire1
+ if (Fire.EnoughLevel && CurrentMp >= Fire.MPNeed) return false;
+
+ return true;
+ }
+ }
+
+ private static bool NeedToTransposeGoIce(bool usedOne)
+ {
+ if (!NeedToGoIce) return false;
+ if (!Paradox.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 || Swiftcast.WillHaveOneChargeGCD(2) || TripleCast.WillHaveOneChargeGCD(2))) return true;
+ if (!HasFire && !Swiftcast.WillHaveOneChargeGCD(2) && !TripleCast.CanUse(out _, gcdCountForAbility: 8)) return false;
+ return true;
+ }
+
+ protected override IRotationConfigSet CreateConfiguration()
+ => base.CreateConfiguration()
+ .SetBool(CombatType.PvE, "UseTransposeForParadox", true, "Use Transpose to Astral Fire before Paradox")
+ .SetBool(CombatType.PvE, "ExtendTimeSafely", false, "Extend Astral Fire Time Safely")
+ .SetBool(CombatType.PvE, "UseN15", false, @"Use ""Double Paradox"" rotation [N15]");
+
+ protected override IAction CountDownAction(float remainTime)
+ {
+ IAction act;
+ if (remainTime < Fire3.CastTime + CountDownAhead)
+ {
+ if (Fire3.CanUse(out act)) return act;
+ }
+ if (remainTime <= 12 && SharpCast.CanUse(out act, CanUseOption.EmptyOrSkipCombo | CanUseOption.IgnoreClippingCheck)) 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.Paradox))
+ {
+ if (Swiftcast.CanUse(out act)) return true;
+ if (TripleCast.CanUse(out act, CanUseOption.EmptyOrSkipCombo)) return true;
+ }
+
+ if (UmbralIceStacks < 3 && LucidDreaming.CanUse(out act)) return true;
+ if (SharpCast.CanUse(out act, CanUseOption.EmptyOrSkipCombo)) return true;
+ }
+ if (InAstralFire)
+ {
+ if (!CombatElapsedLess(6) && CombatElapsedLess(9) && LeyLines.CanUse(out act)) return true;
+ if (TripleCast.CanUse(out act, gcdCountForAbility: 5)) return true;
+ }
+ if (Amplifier.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 && Paradox.EnoughLevel)
+ {
+ if ((HasFire || HasSwift) && Transpose.CanUse(out act, CanUseOption.OnLastAbility)) return true;
+ }
+ if (nextGCD.IsTheSameTo(false, Fire3) && HasFire)
+ {
+ if (Transpose.CanUse(out act)) return true;
+ }
+
+ //Using Manafont
+ if (InAstralFire)
+ {
+ if (CurrentMp == 0 && Manafont.CanUse(out act)) return true;
+ //To Ice
+ if (NeedToTransposeGoIce(true) && Transpose.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 && TripleCast.CanUse(out act, CanUseOption.EmptyOrSkipCombo | CanUseOption.IgnoreClippingCheck)) return true;
+
+ if (AddElementBase(out act)) return true;
+ if (Scathe.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 static bool GoIce(out IAction act)
+ {
+ act = null;
+
+ if (!NeedToGoIce) return false;
+
+ //Use Manafont or transpose.
+ if ((!Manafont.IsCoolingDown || NeedToTransposeGoIce(false))
+ && UseInstanceSpell(out act)) return true;
+
+ //Go to Ice.
+ if (Blizzard2.CanUse(out act)) return true;
+ if (Blizzard3.CanUse(out act)) return true;
+ if (Transpose.CanUse(out act)) return true;
+ if (Blizzard.CanUse(out act)) return true;
+ return false;
+ }
+
+ private static bool MaintainIce(out IAction act)
+ {
+ act = null;
+ if (UmbralIceStacks == 1)
+ {
+ if (Blizzard2.CanUse(out act)) return true;
+
+ if (Player.Level == 90 && Blizzard.CanUse(out act)) return true;
+ if (Blizzard3.CanUse(out act)) return true;
+ }
+ if (UmbralIceStacks == 2 && Player.Level < 90)
+ {
+ if (Blizzard2.CanUse(out act)) return true;
+ if (Blizzard.CanUse(out act)) return true;
+ }
+ return false;
+ }
+
+ private static bool DoIce(out IAction act)
+ {
+ if (IsLastAction(ActionID.UmbralSoul, ActionID.Transpose)
+ && IsParadoxActive && Blizzard.CanUse(out act)) return true;
+
+ if (UmbralIceStacks == 3 && UsePolyglot(out act)) return true;
+
+ //Add Hearts
+ if (UmbralIceStacks == 3 &&
+ Blizzard4.EnoughLevel && UmbralHearts < 3 && !IsLastGCD
+ (ActionID.Blizzard4, ActionID.Freeze))
+ {
+ if (Freeze.CanUse(out act)) return true;
+ if (Blizzard4.CanUse(out act)) return true;
+ }
+
+ if (AddThunder(out act, 5)) return true;
+ if (UmbralIceStacks == 2 && UsePolyglot(out act, 0)) return true;
+
+ if (IsParadoxActive)
+ {
+ if (Blizzard.CanUse(out act)) return true;
+ }
+
+ if (Blizzard2.CanUse(out act)) return true;
+ if (Blizzard4.CanUse(out act)) return true;
+ if (Blizzard.CanUse(out act)) return true;
+ return false;
+ }
+
+ private static 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 (Blizzard.CanUse(out act)) return true;
+ }
+
+ //Go to Fire.
+ if (Fire2.CanUse(out act)) return true;
+ if (Fire3.CanUse(out act)) return true;
+ if (Transpose.CanUse(out act)) return true;
+ if (Fire.CanUse(out act)) return true;
+
+ return false;
+ }
+
+ private bool MaintainFire(out IAction act)
+ {
+ switch (AstralFireStacks)
+ {
+ case 1:
+ if (Fire2.CanUse(out act)) return true;
+ if (Configs.GetBool("UseN15"))
+ {
+ if (HasFire && Fire3.CanUse(out act)) return true;
+ if (IsParadoxActive && Fire.CanUse(out act)) return true;
+ }
+ if (Fire3.CanUse(out act)) return true;
+ break;
+ case 2:
+ if (Fire2.CanUse(out act)) return true;
+ if (Fire.CanUse(out act)) return true;
+ break;
+ }
+
+ if (ElementTimeEndAfterGCD(Configs.GetBool("ExtendTimeSafely") ? 3u : 2u))
+ {
+ if (CurrentMp >= Fire.MPNeed * 2 + 800 && Fire.CanUse(out act)) return true;
+ if (Flare.CanUse(out act)) return true;
+ if (Despair.CanUse(out act)) return true;
+ }
+
+ act = null;
+ return false;
+ }
+
+ private static bool DoFire(out IAction act)
+ {
+ if (UsePolyglot(out act)) return true;
+
+ // Add thunder only at combat start.
+ if (CombatElapsedLess(5))
+ {
+ if (AddThunder(out act, 0)) return true;
+ }
+
+ if (TripleCast.CanUse(out act, CanUseOption.IgnoreClippingCheck)) return true;
+
+ if (AddThunder(out act, 0) && Player.WillStatusEndGCD(1, 0, true,
+ StatusID.Thundercloud)) return true;
+
+ if (UmbralHearts < 2 && Flare.CanUse(out act)) return true;
+ if (Fire2.CanUse(out act)) return true;
+
+ if (CurrentMp >= Fire.MPNeed + 800)
+ {
+ if (Fire4.EnoughLevel)
+ {
+ if (Fire4.CanUse(out act)) return true;
+ }
+ else if (HasFire)
+ {
+ if (Fire3.CanUse(out act)) return true;
+ }
+ if (Fire.CanUse(out act)) return true;
+ }
+
+ if (Despair.CanUse(out act)) return true;
+
+ return false;
+ }
+
+ private static bool UseInstanceSpell(out IAction act)
+ {
+ if (UsePolyglot(out act)) return true;
+ if (HasThunder && AddThunder(out act, 1)) return true;
+ if (UsePolyglot(out act, 0)) return true;
+ return false;
+ }
+
+ private static bool AddThunder(out IAction act, uint gcdCount = 3)
+ {
+ act = null;
+ //Return if just used.
+ if (IsLastGCD(ActionID.Thunder, ActionID.Thunder2, ActionID.Thunder3, ActionID.Thunder4)) return false;
+
+ //So long for thunder.
+ if (Thunder.CanUse(out _) && !Thunder.Target.WillStatusEndGCD(gcdCount, 0, true,
+ StatusID.Thunder, StatusID.Thunder2, StatusID.Thunder3, StatusID.Thunder4))
+ return false;
+
+ if (Thunder2.CanUse(out act)) return true;
+ if (Thunder.CanUse(out act)) return true;
+
+ return false;
+ }
+
+ private static bool AddElementBase(out IAction act)
+ {
+ if (CurrentMp >= 7200)
+ {
+ if (Fire2.CanUse(out act)) return true;
+ if (Fire3.CanUse(out act)) return true;
+ if (Fire.CanUse(out act)) return true;
+ }
+ else
+ {
+ if (Blizzard2.CanUse(out act)) return true;
+ if (Blizzard3.CanUse(out act)) return true;
+ if (Blizzard.CanUse(out act)) return true;
+ }
+ return false;
+ }
+
+ private static bool UsePolyglot(out IAction act, uint gcdCount = 3)
+ {
+ if (gcdCount == 0 || IsPolyglotStacksMaxed && EnchinaEndAfterGCD(gcdCount))
+ {
+ if (Foul.CanUse(out act)) return true;
+ if (Xenoglossy.CanUse(out act)) return true;
+ }
+
+ act = null;
+ return false;
+ }
+
+ private bool MaintainStatus(out IAction act)
+ {
+ act = null;
+ if (CombatElapsedLess(6)) return false;
+ if (UmbralSoul.CanUse(out act)) return true;
+ if (InAstralFire && Transpose.CanUse(out act)) return true;
+ if (Configs.GetBool("UseTransposeForParadox") &&
+ InUmbralIce && !IsParadoxActive && UmbralIceStacks == 3
+ && Transpose.CanUse(out act)) return true;
+
+ return false;
+ }
+
+ [RotationDesc(ActionID.BetweenTheLines, ActionID.LeyLines)]
+ protected override bool HealSingleAbility(out IAction act)
+ {
+ if (BetweenTheLines.CanUse(out act)) return true;
+ if (LeyLines.CanUse(out act)) return true;
+
+ return base.HealSingleAbility(out act);
+ }
+}
diff --git a/DefaultRotations/Magical/BLU_Default.cs b/DefaultRotations/Magical/BLU_Default.cs
new file mode 100644
index 000000000..f1ba1c32f
--- /dev/null
+++ b/DefaultRotations/Magical/BLU_Default.cs
@@ -0,0 +1,448 @@
+namespace DefaultRotations.Magical;
+
+[SourceCode(Path = "main/DefaultRotations/Magical/BLU_Default.cs")]
+public sealed class BLU_Default : BLU_Base
+{
+ public override CombatType Type => CombatType.PvE;
+
+ public override string GameVersion => "6.18";
+
+ public override string RotationName => "Default";
+
+ public override bool CanHealAreaSpell => base.CanHealAreaSpell && BlueId == BLUID.Healer;
+ public override bool CanHealSingleSpell => base.CanHealSingleSpell && BlueId == BLUID.Healer;
+
+ protected override IRotationConfigSet CreateConfiguration()
+ {
+ return base.CreateConfiguration()
+ .SetBool(CombatType.PvE, "MoonFluteBreak", false, "Use Moon Flute")
+ .SetBool(CombatType.PvE, "SingleAOE", true, "Use high-damage AoE skills on single target")
+ .SetBool(CombatType.PvE, "GamblerKill", false, "Use skills with a chance to fail")
+ .SetBool(CombatType.PvE, "UseFinalSting", false, "Use Final Sting")
+ .SetFloat( RotationSolver.Basic.Configuration.ConfigUnitType.Percent, CombatType.PvE, "FinalStingHP", 0, "Target HPP for Final Sting");
+ }
+
+ private bool MoonFluteBreak => Configs.GetBool("MoonFluteBreak");
+ private bool UseFinalSting => Configs.GetBool("UseFinalSting");
+ private float FinalStingHP => Configs.GetFloat("FinalStingHP");
+ ///
+ /// 0-70练级,快速练级,滑舌拉怪
+ ///
+ private static bool QuickLevel => false;
+ ///
+ /// 赌几率秒杀
+ ///
+ private bool GamblerKill => Configs.GetBool("GamblerKill");
+ ///
+ /// 单体时是否释放高伤害AOE
+ ///
+ private bool SingleAOE => Configs.GetBool("SingleAOE");
+
+ protected override bool EmergencyAbility(IAction nextGCD, out IAction act)
+ {
+ if (nextGCD.IsTheSameTo(false, SelfDestruct, FinalSting))
+ {
+ if (Swiftcast.CanUse(out act)) return true;
+ }
+ return base.EmergencyAbility(nextGCD, out act);
+ }
+
+ protected override bool GeneralGCD(out IAction act)
+ {
+ act = null;
+ //狂战士副作用期间
+ if (Player.HasStatus(true, StatusID.WaningNocturne)) return false;
+ //鬼宿脚
+ if (PhantomFlurry.IsCoolingDown && !PhantomFlurry.ElapsedOneChargeAfter(1) || Player.HasStatus(true, StatusID.PhantomFlurry))
+ {
+ if (!Player.WillStatusEnd(0.1f, true, StatusID.PhantomFlurry) && Player.WillStatusEnd(1, true, StatusID.PhantomFlurry) && PhantomFlurry2.CanUse(out act, CanUseOption.MustUse)) return true;
+ return false;
+ }
+ //穿甲散弹
+ if (Player.HasStatus(true, StatusID.SurpanakhaFury))
+ {
+ if (Surpanakha.CanUse(out act, CanUseOption.MustUse | CanUseOption.EmptyOrSkipCombo)) return true;
+ }
+
+ //终极针组合
+ if (UseFinalSting && CanUseFinalSting(out act)) return true;
+
+ //爆发
+ if (MoonFluteBreak && DBlueBreak(out act)) return true;
+
+ //高伤害
+ if (PrimalSpell(out act)) return true;
+ //群体
+ if (AreaGCD(out act)) return true;
+ //单体填充
+ if (SingleGCD(out act)) return true;
+
+ return base.GeneralGCD(out act);
+ }
+
+ protected override bool HealSingleGCD(out IAction act)
+ {
+ if (BlueId == BLUID.Healer)
+ {
+ //有某些非常危险的状态。
+ if (IsEsunaStanceNorth && WeakenPeople.Any() || DyingPeople.Any())
+ {
+ if (Exuviation.CanUse(out act, CanUseOption.MustUse)) return true;
+ }
+ if (AngelsSnack.CanUse(out act)) return true;
+ if (Stotram.CanUse(out act)) return true;
+ if (PomCure.CanUse(out act)) return true;
+ }
+ else
+ {
+ if (WhiteWind.CanUse(out act, CanUseOption.MustUse)) return true;
+ }
+
+ return base.HealSingleGCD(out act);
+ }
+
+ ///
+ /// D青爆发
+ ///
+ ///
+ ///
+ private bool DBlueBreak(out IAction act)
+ {
+ if (TripleTrident.OnSlot && TripleTrident.WillHaveOneChargeGCD(OnSlotCount(Whistle, Tingle), 0))
+ {
+ //口笛
+ if (Whistle.CanUse(out act)) return true;
+ //哔哩哔哩
+ if (!Player.HasStatus(true, StatusID.Tingling)
+ && Tingle.CanUse(out act, CanUseOption.MustUse)) return true;
+ if (OffGuard.CanUse(out act)) return true;
+ //鱼叉
+ if (TripleTrident.CanUse(out act, CanUseOption.MustUse)) return true;
+ }
+
+ if (AllOnSlot(Whistle, FinalSting, BasicInstinct) && UseFinalSting)
+ {
+ if (Whistle.CanUse(out act)) return true;
+ //破防
+ if (OffGuard.CanUse(out act)) return true;
+ //哔哩哔哩
+ if (Tingle.CanUse(out act)) return true;
+ }
+
+ //月笛
+ if (CanUseMoonFlute(out act)) return true;
+
+ if (!Player.HasStatus(true, StatusID.WaxingNocturne)) return false;
+
+ //月下彼岸花
+ if (NightBloom.CanUse(out act, CanUseOption.MustUse)) return true;
+ //地火喷发
+ if (Eruption.CanUse(out act, CanUseOption.MustUse)) return true;
+ //马特拉魔术
+ if (MatraMagic.CanUse(out act, CanUseOption.MustUse)) return true;
+ //正义飞踢
+ if (JKick.CanUse(out act, CanUseOption.MustUse)) return true;
+ //捕食
+ if (Devour.CanUse(out act, CanUseOption.MustUse)) return true;
+ //轰雷
+ if (ShockStrike.CanUse(out act, CanUseOption.MustUse)) return true;
+ //冰雪乱舞
+ if (GlassDance.CanUse(out act, CanUseOption.MustUse)) return true;
+ //魔法锤
+ if (MagicHammer.CanUse(out act, CanUseOption.MustUse)) return true;
+ //穿甲散弹
+ if (Surpanakha.CurrentCharges >= 3 && Surpanakha.CanUse(out act, CanUseOption.MustUse | CanUseOption.EmptyOrSkipCombo)) return true;
+ //鬼宿脚
+ if (PhantomFlurry.CanUse(out act, CanUseOption.MustUse)) return true;
+
+ //冰雾
+ if (WhiteDeath.CanUse(out act)) return true;
+ //如意大旋风
+ if (IsBurst && !MoonFluteBreak && BothEnds.CanUse(out act, CanUseOption.MustUse)) return true;
+ //类星体
+ if (Quasar.CanUse(out act, CanUseOption.MustUse)) return true;
+ //飞翎雨
+ if (FeatherRain.CanUse(out act, CanUseOption.MustUse)) return true;
+ //山崩
+ if (MountainBuster.CanUse(out act, CanUseOption.MustUse)) return true;
+ //冰雪乱舞
+ if (GlassDance.CanUse(out act, CanUseOption.MustUse)) return true;
+
+ //音爆
+ if (SonicBoom.CanUse(out act)) return true;
+
+ return false;
+ }
+
+
+ ///
+ /// 月笛条件
+ ///
+ ///
+ ///
+ private static bool CanUseMoonFlute(out IAction act)
+ {
+ if (!MoonFlute.CanUse(out act) && !HasHostilesInRange) return false;
+
+ if (Player.HasStatus(true, StatusID.WaxingNocturne)) return false;
+
+ if (Player.HasStatus(true, StatusID.Harmonized)) return true;
+
+ return false;
+ }
+
+ ///
+ /// 终极针组合
+ ///
+ ///
+ ///
+ private bool CanUseFinalSting(out IAction act)
+ {
+ act = null;
+ if (!UseFinalSting) return false;
+ if (!FinalSting.CanUse(out _)) return false;
+
+ var useFinalSting = Player.HasStatus(true, StatusID.WaxingNocturne, StatusID.Harmonized);
+
+ if (AllOnSlot(Whistle, MoonFlute, FinalSting) && !AllOnSlot(BasicInstinct))
+ {
+ if (HostileTarget?.GetHealthRatio() > FinalStingHP) return false;
+
+ if (Whistle.CanUse(out act)) return true;
+ if (MoonFlute.CanUse(out act)) return true;
+ if (useFinalSting && FinalSting.CanUse(out act)) return true;
+ }
+
+ if (AllOnSlot(Whistle, MoonFlute, FinalSting, BasicInstinct))
+ {
+ //破防
+ if (Player.HasStatus(true, StatusID.WaxingNocturne) && OffGuard.CanUse(out act)) return true;
+
+ if (HostileTarget?.GetHealthRatio() > FinalStingHP) return false;
+ if (Whistle.CanUse(out act)) return true;
+ if (MoonFlute.CanUse(out act)) return true;
+ if (useFinalSting && FinalSting.CanUse(out act)) return true;
+ }
+
+ return false;
+ }
+
+ ///
+ /// 单体GCD填充
+ ///
+ ///
+ ///
+ private bool SingleGCD(out IAction act)
+ {
+ act = null;
+ if (Player.HasStatus(true, StatusID.WaxingNocturne)) return false;
+
+ //滑舌 +眩晕 0-70练级用
+ if (QuickLevel && StickyTongue.CanUse(out act)) return true;
+
+ //苦闷之歌
+ if (AllOnSlot(Bristle, SongOfTorment) && SongOfTorment.CanUse(out _))
+ {
+ //怒发冲冠
+ if (Bristle.CanUse(out act)) return true;
+ if (SongOfTorment.CanUse(out act)) return true;
+ }
+ if (SongOfTorment.CanUse(out act)) return true;
+
+ //复仇冲击
+ if (RevengeBlast.CanUse(out act)) return true;
+ //赌徒行为
+ if (GamblerKill)
+ {
+ //导弹
+ if (Missile.CanUse(out act)) return true;
+ //螺旋尾
+ if (TailScrew.CanUse(out act)) return true;
+ //死亡宣告
+ if (Doom.CanUse(out act)) return true;
+ }
+
+ //锋利菜刀 近战 眩晕增伤
+ if (SharpenedKnife.CanUse(out act)) return true;
+
+ //吸血 回蓝
+ if (CurrentMp < 1000 && BloodDrain.CanUse(out act)) return true;
+ //音爆
+ if (SonicBoom.CanUse(out act)) return true;
+ if (DrillCannons.CanUse(out act, CanUseOption.MustUse)) return true;
+ //永恒射线 无法 +眩晕1s
+ if (PerpetualRay.CanUse(out act)) return true;
+ //深渊贯穿 无物 +麻痹
+ if (AbyssalTransfixion.CanUse(out act)) return true;
+ //逆流 雷法 +加重
+ if (Reflux.CanUse(out act)) return true;
+ //水炮
+ if (WaterCannon.CanUse(out act)) return true;
+
+ //小侦测
+ if (CondensedLibra.CanUse(out act)) return true;
+
+ //滑舌 +眩晕
+ if (StickyTongue.CanUse(out act)) return true;
+
+ //投掷沙丁鱼(打断)
+ if (FlyingSardine.CanUse(out act)) return true;
+
+ return false;
+ }
+
+ ///
+ /// 范围GCD填充
+ ///
+ ///
+ ///
+ private bool AreaGCD(out IAction act)
+ {
+ act = null;
+ if (Player.HasStatus(true, StatusID.WaxingNocturne)) return false;
+
+ //赌徒行为
+ if (GamblerKill)
+ {
+ //火箭炮
+ if (Launcher.CanUse(out act, CanUseOption.MustUse)) return true;
+ //5级即死
+ if (Level5Death.CanUse(out act, CanUseOption.MustUse)) return true;
+ }
+
+ if (false)
+ {
+ if (AcornBomb.CanUse(out act, CanUseOption.MustUse))
+ {
+ return true;
+ }
+
+ if (Faze.CanUse(out act, CanUseOption.MustUse)) return true;
+ if (Snort.CanUse(out act, CanUseOption.MustUse)) return true;
+ if (BadBreath.CanUse(out act, CanUseOption.MustUse)) return true;
+ if (Chirp.CanUse(out act, CanUseOption.MustUse)) return true;
+ if (Level5Petrify.CanUse(out act, CanUseOption.MustUse)) return true;
+ }
+
+ //陆行鸟陨石
+ if (HasCompanion && ChocoMeteor.CanUse(out act, CanUseOption.MustUse)) return true;
+
+ if (HostileTargets.GetObjectInRadius(6).Count() < 3)
+ {
+ //水力吸引
+ if (HydroPull.CanUse(out act)) return true;
+ }
+
+ //寒冰咆哮
+ if (TheRamVoice.CanUse(out act)) return true;
+
+ //超振动
+ if (!IsMoving && (HostileTarget?.HasStatus(false, StatusID.DeepFreeze) ?? false) && TheRamVoice.CanUse(out act)) return true;
+
+ //雷电咆哮
+ if (TheDragonVoice.CanUse(out act)) return true;
+
+ //冰焰
+ if (Blaze.CanUse(out act)) return true;
+ if (FeculentFlood.CanUse(out act)) return true;
+ //火炎放射
+ if (FlameThrower.CanUse(out act)) return true;
+ //水流吐息
+ if (AquaBreath.CanUse(out act)) return true;
+ //高压电流
+ if (HighVoltage.CanUse(out act)) return true;
+ //怒视
+ if (Glower.CanUse(out act)) return true;
+ //平原震裂
+ if (PlainCracker.CanUse(out act)) return true;
+ //诡异视线
+ if (TheLook.CanUse(out act)) return true;
+ //喷墨
+ if (InkJet.CanUse(out act)) return true;
+ if (FireAngon.CanUse(out act)) return true;
+ if (MindBlast.CanUse(out act)) return true;
+ if (AlpineDraft.CanUse(out act)) return true;
+ if (ProteanWave.CanUse(out act)) return true;
+ if (Northerlies.CanUse(out act)) return true;
+ if (Electrogenesis.CanUse(out act)) return true;
+ if (WhiteKnightsTour.CanUse(out act)) return true;
+ if (BlackKnightsTour.CanUse(out act)) return true;
+ if (Tatamigaeshi.CanUse(out act)) return true;
+
+ if (MustardBomb.CanUse(out act)) return true;
+ if (AetherialSpark.CanUse(out act)) return true;
+ if (MaledictionOfWater.CanUse(out act)) return true;
+ if (FlyingFrenzy.CanUse(out act)) return true;
+ if (DrillCannons.CanUse(out act)) return true;
+ if (Weight4Tonze.CanUse(out act)) return true;
+ if (Needles1000.CanUse(out act)) return true;
+ if (Kaltstrahl.CanUse(out act)) return true;
+ if (PeripheralSynthesis.CanUse(out act)) return true;
+ if (FlameThrower.CanUse(out act)) return true;
+ if (FlameThrower.CanUse(out act)) return true;
+ if (SaintlyBeam.CanUse(out act)) return true;
+
+ return false;
+ }
+
+ ///
+ /// 有CD的技能
+ ///
+ ///
+ ///
+ private bool PrimalSpell(out IAction act)
+ {
+ act = null;
+ if (Player.HasStatus(true, StatusID.WaxingNocturne)) return false;
+
+ //冰雾
+ if (WhiteDeath.CanUse(out act)) return true;
+ //玄天武水壁
+ if (DivineCataract.CanUse(out act)) return true;
+
+ //斗灵弹
+ if (TheRoseOfDestruction.CanUse(out act)) return true;
+
+ //渔叉三段
+ if (IsBurst && !MoonFluteBreak && TripleTrident.CanUse(out act)) return true;
+ //马特拉魔术
+ if (IsBurst && !MoonFluteBreak && MatraMagic.CanUse(out act)) return true;
+
+ //捕食
+ if (Devour.CanUse(out act)) return true;
+ //魔法锤
+ //if (MagicHammer.ShouldUse(out act)) return true;
+
+ var option = SingleAOE ? CanUseOption.MustUse : CanUseOption.None;
+ //月下彼岸花
+ if (IsBurst && !MoonFluteBreak && NightBloom.CanUse(out act, option)) return true;
+ //如意大旋风
+ if (IsBurst && !MoonFluteBreak && BothEnds.CanUse(out act, option)) return true;
+
+ //穿甲散弹
+ if (IsBurst && !MoonFluteBreak && Surpanakha.CurrentCharges >= 3 && Surpanakha.CanUse(out act, option | CanUseOption.EmptyOrSkipCombo)) return true;
+
+ //类星体
+ if (Quasar.CanUse(out act, option)) return true;
+ //正义飞踢
+ if (!IsMoving && JKick.CanUse(out act, option)) return true;
+
+ //地火喷发
+ if (Eruption.CanUse(out act, option)) return true;
+ //飞翎雨
+ if (FeatherRain.CanUse(out act, option)) return true;
+
+ //轰雷
+ if (ShockStrike.CanUse(out act, option)) return true;
+ //山崩
+ if (MountainBuster.CanUse(out act, option)) return true;
+
+ //冰雪乱舞
+ if (GlassDance.CanUse(out act, option)) return true;
+
+ //if (MountainBuster.ShouldUse(out act, option)) return true;
+
+
+ return false;
+ }
+}
diff --git a/DefaultRotations/Magical/BLU_Extra.cs b/DefaultRotations/Magical/BLU_Extra.cs
new file mode 100644
index 000000000..10ec68145
--- /dev/null
+++ b/DefaultRotations/Magical/BLU_Extra.cs
@@ -0,0 +1,42 @@
+namespace ExtraRotations.Magical;
+
+[SourceCode(Path = "main/DefaultRotations/Magical/BLU_Extra.cs")]
+public sealed class BLU_Extra : BLU_Base
+{
+ public override CombatType Type => CombatType.PvE;
+
+ public override string GameVersion => "6.3";
+
+ public override string RotationName => "Extra";
+
+ public override string Description => "This is a simplified version for me (ArchiTed) using, \nwhich doesn't contain all actions.";
+
+ protected override bool AttackAbility(out IAction act)
+ {
+ act = null;
+ return false;
+ }
+
+ protected override bool GeneralGCD(out IAction act)
+ {
+ if (ChocoMeteor.CanUse(out act)) return true;
+ if (DrillCannons.CanUse(out act)) return true;
+
+ if (TripleTrident.OnSlot && TripleTrident.RightType && TripleTrident.WillHaveOneChargeGCD(OnSlotCount(Whistle, Tingle), 0))
+ {
+ if ((TripleTrident.CanUse(out _, CanUseOption.MustUse) || !HasHostilesInRange) && Whistle.CanUse(out act)) return true;
+
+ if (!Player.HasStatus(true, StatusID.Tingling)
+ && Tingle.CanUse(out act, CanUseOption.MustUse)) return true;
+ if (OffGuard.CanUse(out act)) return true;
+
+ if (TripleTrident.CanUse(out act, CanUseOption.MustUse)) return true;
+ }
+ if (ChocoMeteor.CanUse(out act, HasCompanion ? CanUseOption.MustUse : CanUseOption.None)) return true;
+
+ if (SonicBoom.CanUse(out act)) return true;
+ if (DrillCannons.CanUse(out act, CanUseOption.MustUse)) return true;
+
+ return false;
+ }
+}
diff --git a/DefaultRotations/Magical/RDM_Default.cs b/DefaultRotations/Magical/RDM_Default.cs
new file mode 100644
index 000000000..15616dc7c
--- /dev/null
+++ b/DefaultRotations/Magical/RDM_Default.cs
@@ -0,0 +1,162 @@
+namespace DefaultRotations.Magical;
+
+[RotationDesc(ActionID.Embolden)]
+[SourceCode(Path = "main/DefaultRotations/Magical/RDM_Default.cs")]
+[LinkDescription("https://www.thebalanceffxiv.com/img/jobs/rdm/rdm_ew_opener.png")]
+public sealed class RDM_Default : RDM_Base
+{
+ public override CombatType Type => CombatType.PvE;
+
+ public override string GameVersion => "6.31";
+
+ public override string RotationName => "Standard";
+
+ static IBaseAction VerthunderStartUp { get; } = new BaseAction(ActionID.Verthunder);
+
+ private static bool CanStartMeleeCombo
+ {
+ get
+ {
+ if (Player.HasStatus(true, StatusID.Manafication, StatusID.Embolden) ||
+ BlackMana == 100 || WhiteMana == 100) return true;
+
+ if (BlackMana == WhiteMana) return false;
+
+ else if (WhiteMana < BlackMana)
+ {
+ if (Player.HasStatus(true, StatusID.VerstoneReady)) return false;
+ }
+ else
+ {
+ if (Player.HasStatus(true, StatusID.VerfireReady)) return false;
+ }
+
+ if (Player.HasStatus(true, Vercure.StatusProvide)) return false;
+
+ //Waiting for embolden.
+ if (Embolden.EnoughLevel && Embolden.WillHaveOneChargeGCD(5)) return false;
+
+ return true;
+ }
+ }
+
+ protected override IRotationConfigSet CreateConfiguration()
+ {
+ return base.CreateConfiguration()
+ .SetBool(CombatType.PvE, "UseVercure", false, "Use Vercure for Dualcast when out of combat.");
+ }
+
+ protected override IAction CountDownAction(float remainTime)
+ {
+ if (remainTime < VerthunderStartUp.CastTime + CountDownAhead
+ && VerthunderStartUp.CanUse(out var act, CanUseOption.EmptyOrSkipCombo)) return act;
+
+ //Remove Swift
+ StatusHelper.StatusOff(StatusID.DualCast);
+ StatusHelper.StatusOff(StatusID.Acceleration);
+ StatusHelper.StatusOff(StatusID.SwiftCast);
+
+ return base.CountDownAction(remainTime);
+ }
+
+ protected override bool GeneralGCD(out IAction act)
+ {
+ act = null;
+ if (ManaStacks == 3) return false;
+
+ if (!Verthunder2.CanUse(out _))
+ {
+ if (Verfire.CanUse(out act)) return true;
+ if (Verstone.CanUse(out act)) return true;
+ }
+
+ if (Scatter.CanUse(out act)) return true;
+ if (WhiteMana < BlackMana)
+ {
+ if (Veraero2.CanUse(out act) && BlackMana - WhiteMana != 5) return true;
+ if (Veraero.CanUse(out act) && BlackMana - WhiteMana != 6) return true;
+ }
+ if (Verthunder2.CanUse(out act)) return true;
+ if (Verthunder.CanUse(out act)) return true;
+
+ if (Jolt.CanUse(out act)) return true;
+
+ if (Configs.GetBool("UseVercure") && NotInCombatDelay && Vercure.CanUse(out act)) return true;
+
+ return base.GeneralGCD(out act);
+ }
+
+ protected override bool EmergencyGCD(out IAction act)
+ {
+ if (ManaStacks == 3)
+ {
+ if (BlackMana > WhiteMana)
+ {
+ if (Verholy.CanUse(out act, CanUseOption.MustUse)) return true;
+ }
+ if (Verflare.CanUse(out act, CanUseOption.MustUse)) return true;
+ }
+
+ if (Resolution.CanUse(out act, CanUseOption.MustUse)) return true;
+ if (Scorch.CanUse(out act, CanUseOption.MustUse)) return true;
+
+
+ if (IsLastGCD(true, Moulinet) && Moulinet.CanUse(out act, CanUseOption.MustUse)) return true;
+ if (Zwerchhau.CanUse(out act)) return true;
+ if (Redoublement.CanUse(out act)) return true;
+
+ if (!CanStartMeleeCombo) return false;
+
+ if (Moulinet.CanUse(out act))
+ {
+ if (BlackMana >= 60 && WhiteMana >= 60) return true;
+ }
+ else
+ {
+ if (BlackMana >= 50 && WhiteMana >= 50 && Riposte.CanUse(out act)) return true;
+ }
+ if (ManaStacks > 0 && Riposte.CanUse(out act)) return true;
+
+ return base.EmergencyGCD(out act);
+ }
+
+ protected override bool EmergencyAbility(IAction nextGCD, out IAction act)
+ {
+ act = null;
+ if (CombatElapsedLess(4)) return false;
+
+ if (IsBurst && HasHostilesInRange && Embolden.CanUse(out act, CanUseOption.MustUse)) return true;
+
+ //Use Manafication after embolden.
+ if ((Player.HasStatus(true, StatusID.Embolden) || IsLastAbility(ActionID.Embolden))
+ && Manafication.CanUse(out act)) return true;
+
+ return base.EmergencyAbility(nextGCD, out act);
+ }
+
+ protected override bool AttackAbility(out IAction act)
+ {
+ //Swift
+ if (ManaStacks == 0 && (BlackMana < 50 || WhiteMana < 50)
+ && (CombatElapsedLess(4) || !Manafication.EnoughLevel || !Manafication.WillHaveOneChargeGCD(0, 1)))
+ {
+ if (!Player.HasStatus(true, StatusID.VerfireReady, StatusID.VerstoneReady))
+ {
+ if (Swiftcast.CanUse(out act)) return true;
+ if (InCombat && Acceleration.CanUse(out act, CanUseOption.EmptyOrSkipCombo)) return true;
+ }
+ }
+
+ if (IsBurst && UseBurstMedicine(out act)) return true;
+
+ //Attack abilities.
+ if (ContreSixte.CanUse(out act, CanUseOption.MustUse)) return true;
+ if (Fleche.CanUse(out act)) return true;
+
+ if (Engagement.CanUse(out act, CanUseOption.EmptyOrSkipCombo)) return true;
+ if (CorpsACorps.CanUse(out act, CanUseOption.MustUse) && !IsMoving) return true;
+
+ return base.AttackAbility(out act);
+ }
+}
+
diff --git a/DefaultRotations/Magical/SMN_Default.cs b/DefaultRotations/Magical/SMN_Default.cs
new file mode 100644
index 000000000..5a3355b1a
--- /dev/null
+++ b/DefaultRotations/Magical/SMN_Default.cs
@@ -0,0 +1,175 @@
+namespace DefaultRotations.Magical;
+
+[BetaRotation]
+[RotationDesc(ActionID.SearingLight)]
+[SourceCode(Path = "main/DefaultRotations/Magical/SMN_Default.cs")]
+[LinkDescription("https://www.thebalanceffxiv.com/img/jobs/smn/6.png")]
+public sealed class SMN_Default : SMN_Base
+{
+ public override CombatType Type => CombatType.PvE;
+
+ public override string GameVersion => "6.38";
+
+ public override string RotationName => "General purpose";
+
+ public override string Description => "Beta for testing...";
+
+ protected override IRotationConfigSet CreateConfiguration()
+ {
+ return base.CreateConfiguration()
+ .SetCombo(CombatType.PvE, "addSwiftcast", 0, "Use Swiftcast", "No", "Emerald", "Ruby", "All")
+ .SetCombo(CombatType.PvE, "SummonOrder", 0, "Order", "Topaz-Emerald-Ruby", "Topaz-Ruby-Emerald", "Emerald-Topaz-Ruby")
+ .SetBool(CombatType.PvE, "addCrimsonCyclone", true, "Use Crimson Cyclone");
+ }
+
+ public override bool CanHealSingleSpell => false;
+
+ [RotationDesc(ActionID.CrimsonCyclone)]
+ protected override bool MoveForwardGCD(out IAction act)
+ {
+ //火神突进
+ if (CrimsonCyclone.CanUse(out act, CanUseOption.MustUse)) return true;
+ return base.MoveForwardGCD(out act);
+ }
+
+ protected override bool GeneralGCD(out IAction act)
+ {
+ //宝石兽召唤
+ if (SummonCarbuncle.CanUse(out act)) return true;
+
+ //风神读条
+ if (Slipstream.CanUse(out act, CanUseOption.MustUse)) return true;
+ //火神冲锋
+ if (CrimsonStrike.CanUse(out act, CanUseOption.MustUse)) return true;
+
+ //AOE
+ if (PreciousBrilliance.CanUse(out act)) return true;
+ //单体
+ if (Gemshine.CanUse(out act)) return true;
+
+ if (!IsMoving && Configs.GetBool("addCrimsonCyclone") && CrimsonCyclone.CanUse(out act, CanUseOption.MustUse)) return true;
+
+ //龙神不死鸟
+ if ((Player.HasStatus(false, StatusID.SearingLight) || SearingLight.IsCoolingDown) && SummonBahamut.CanUse(out act)) return true;
+ if (!SummonBahamut.EnoughLevel && HasHostilesInRange && AetherCharge.CanUse(out act)) return true;
+
+ //毁4
+ if (IsMoving && (Player.HasStatus(true, StatusID.GarudasFavor) || InIfrit)
+ && !Player.HasStatus(true, StatusID.SwiftCast) && !InBahamut && !InPhoenix
+ && RuinIV.CanUse(out act, CanUseOption.MustUse)) return true;
+
+ //召唤蛮神
+ switch (Configs.GetCombo("SummonOrder"))
+ {
+ default:
+ //土
+ if (SummonTopaz.CanUse(out act)) return true;
+ //风
+ if (SummonEmerald.CanUse(out act)) return true;
+ //火
+ if (SummonRuby.CanUse(out act)) return true;
+ break;
+
+ case 1:
+ //土
+ if (SummonTopaz.CanUse(out act)) return true;
+ //火
+ if (SummonRuby.CanUse(out act)) return true;
+ //风
+ if (SummonEmerald.CanUse(out act)) return true;
+ break;
+
+ case 2:
+ //风
+ if (SummonEmerald.CanUse(out act)) return true;
+ //土
+ if (SummonTopaz.CanUse(out act)) return true;
+ //火
+ if (SummonRuby.CanUse(out act)) return true;
+ break;
+ }
+ if (SummonTimeEndAfterGCD() && AttunmentTimeEndAfterGCD() &&
+ !Player.HasStatus(true, StatusID.SwiftCast) && !InBahamut && !InPhoenix &&
+ RuinIV.CanUse(out act, CanUseOption.MustUse)) return true;
+ //迸裂三灾
+ if (Outburst.CanUse(out act)) return true;
+
+ //毁123
+ if (Ruin.CanUse(out act)) return true;
+ return base.GeneralGCD(out act);
+ }
+
+ protected override bool AttackAbility(out IAction act)
+ {
+ if (IsBurst && !Player.HasStatus(false, StatusID.SearingLight))
+ {
+ //灼热之光
+ if (SearingLight.CanUse(out act)) return true;
+ }
+
+ var IsTargetBoss = HostileTarget?.IsBossFromTTK() ?? false;
+ var IsTargetDying = HostileTarget?.IsDying() ?? false;
+
+ //龙神不死鸟迸发
+ if ((InBahamut && SummonBahamut.ElapsedOneChargeAfterGCD(3) || InPhoenix || IsTargetBoss && IsTargetDying) && EnkindleBahamut.CanUse(out act, CanUseOption.MustUse)) return true;
+ //死星核爆
+ if ((SummonBahamut.ElapsedOneChargeAfterGCD(3) || IsTargetBoss && IsTargetDying) && DeathFlare.CanUse(out act, CanUseOption.MustUse)) return true;
+ //苏生之炎
+ if (Rekindle.CanUse(out act, CanUseOption.MustUse)) return true;
+ //山崩
+ if (MountainBuster.CanUse(out act, CanUseOption.MustUse)) return true;
+
+ //痛苦核爆
+ if ((Player.HasStatus(false, StatusID.SearingLight) && InBahamut && (SummonBahamut.ElapsedOneChargeAfterGCD(3) || !EnergyDrain.IsCoolingDown) ||
+ !SearingLight.EnoughLevel || IsTargetBoss && IsTargetDying) && PainFlare.CanUse(out act)) return true;
+ //溃烂爆发
+ if ((Player.HasStatus(false, StatusID.SearingLight) && InBahamut && (SummonBahamut.ElapsedOneChargeAfterGCD(3) || !EnergyDrain.IsCoolingDown) ||
+ !SearingLight.EnoughLevel || IsTargetBoss && IsTargetDying) && Fester.CanUse(out act)) return true;
+
+ //能量抽取
+ if (EnergySiphon.CanUse(out act)) return true;
+ //能量吸收
+ if (EnergyDrain.CanUse(out act)) return true;
+
+ return base.AttackAbility(out act);
+ }
+ protected override bool EmergencyAbility(IAction nextGCD, out IAction act)
+ {
+ //即刻进循环
+ switch (Configs.GetCombo("addSwiftcast"))
+ {
+ default:
+ break;
+ case 1:
+ if (nextGCD.IsTheSameTo(true, Slipstream) || Attunement == 0 && Player.HasStatus(true, StatusID.GarudasFavor))
+ {
+ if (Swiftcast.CanUse(out act, CanUseOption.MustUse)) return true;
+ }
+ break;
+ case 2:
+ if (InIfrit && (nextGCD.IsTheSameTo(true, Gemshine, PreciousBrilliance) || IsMoving))
+ {
+ if (Swiftcast.CanUse(out act, CanUseOption.MustUse)) return true;
+ }
+ break;
+
+ case 3:
+ if (nextGCD.IsTheSameTo(true, Slipstream) || Attunement == 0 && Player.HasStatus(true, StatusID.GarudasFavor) ||
+ InIfrit && (nextGCD.IsTheSameTo(true, Gemshine, PreciousBrilliance) || IsMoving))
+ {
+ if (Swiftcast.CanUse(out act, CanUseOption.MustUse)) return true;
+ }
+ break;
+ }
+ return base.EmergencyAbility(nextGCD, out act);
+ }
+
+ protected override IAction CountDownAction(float remainTime)
+ {
+ if (SummonCarbuncle.CanUse(out _)) return SummonCarbuncle;
+ //1.5s预读毁3
+ if (remainTime <= Ruin.CastTime + CountDownAhead
+ && Ruin.CanUse(out _)) return Ruin;
+ return base.CountDownAction(remainTime);
+ }
+}
diff --git a/DefaultRotations/Melee/DRG_Default.cs b/DefaultRotations/Melee/DRG_Default.cs
new file mode 100644
index 000000000..1c3612500
--- /dev/null
+++ b/DefaultRotations/Melee/DRG_Default.cs
@@ -0,0 +1,103 @@
+namespace DefaultRotations.Melee;
+
+[SourceCode(Path = "main/DefaultRotations/Melee/DRG_Default.cs")]
+public sealed class DRG_Default : DRG_Base
+{
+ public override CombatType Type => CombatType.PvE;
+
+ public override string GameVersion => "6.18";
+
+ public override string RotationName => "Default";
+
+ [RotationDesc(ActionID.SpineShatterDive, ActionID.DragonFireDive)]
+ protected override bool MoveForwardAbility(out IAction act)
+ {
+ if (SpineShatterDive.CanUse(out act)) return true;
+ if (DragonFireDive.CanUse(out act, CanUseOption.MustUse)) return true;
+
+ return false;
+ }
+ protected override bool EmergencyAbility(IAction nextGCD, out IAction act)
+ {
+ if (nextGCD.IsTheSameTo(true, FullThrust, CoerthanTorment)
+ || Player.HasStatus(true, StatusID.LanceCharge) && nextGCD.IsTheSameTo(false, FangandClaw))
+ {
+ if (LifeSurge.CanUse(out act, CanUseOption.EmptyOrSkipCombo | CanUseOption.OnLastAbility)) return true;
+ }
+
+ return base.EmergencyAbility(nextGCD, out act);
+ }
+
+ protected override bool AttackAbility(out IAction act)
+ {
+ if (IsBurst)
+ {
+ if (LanceCharge.CanUse(out act, CanUseOption.MustUse) && Player.HasStatus(true, StatusID.PowerSurge)) return true;
+ if (LanceCharge.CanUse(out act, CanUseOption.MustUse | CanUseOption.OnLastAbility) && !Player.HasStatus(true, StatusID.PowerSurge)) return true;
+
+ if (DragonSight.CanUse(out act, CanUseOption.MustUse)) return true;
+
+ if (BattleLitany.CanUse(out act, CanUseOption.MustUse)) return true;
+ }
+
+ if (Nastrond.CanUse(out act, CanUseOption.MustUse)) return true;
+
+ if (StarDiver.CanUse(out act, CanUseOption.MustUse)) return true;
+
+ if (HighJump.EnoughLevel)
+ {
+ if (HighJump.CanUse(out act)) return true;
+ }
+ else
+ {
+ if (Jump.CanUse(out act)) return true;
+ }
+
+ if (Geirskogul.CanUse(out act, CanUseOption.MustUse)) return true;
+
+ if (SpineShatterDive.CanUse(out act, CanUseOption.EmptyOrSkipCombo))
+ {
+ if (Player.HasStatus(true, StatusID.LanceCharge) && LanceCharge.ElapsedOneChargeAfterGCD(3)) return true;
+ }
+ if (Player.HasStatus(true, StatusID.PowerSurge) && SpineShatterDive.CurrentCharges != 1 && SpineShatterDive.CanUse(out act)) return true;
+
+ if (MirageDive.CanUse(out act)) return true;
+
+ if (DragonFireDive.CanUse(out act, CanUseOption.MustUse))
+ {
+ if (Player.HasStatus(true, StatusID.LanceCharge) && LanceCharge.ElapsedOneChargeAfterGCD(3)) return true;
+ }
+
+ if (WyrmwindThrust.CanUse(out act, CanUseOption.MustUse)) return true;
+
+ return base.AttackAbility(out act);
+ }
+
+ protected override bool GeneralGCD(out IAction act)
+ {
+ if (CoerthanTorment.CanUse(out act)) return true;
+ if (SonicThrust.CanUse(out act)) return true;
+ if (DoomSpike.CanUse(out act)) return true;
+
+
+ if (WheelingThrust.CanUse(out act)) return true;
+ if (FangandClaw.CanUse(out act)) return true;
+
+
+ if (FullThrust.CanUse(out act)) return true;
+ if (ChaosThrust.CanUse(out act)) return true;
+
+ if (Player.WillStatusEndGCD(5, 0, true, StatusID.PowerSurge))
+ {
+ if (Disembowel.CanUse(out act)) return true;
+ }
+
+ if (VorpalThrust.CanUse(out act)) return true;
+ if (TrueThrust.CanUse(out act)) return true;
+
+ if (IsMoveForward && MoveForwardAbility(out act)) return true;
+ if (PiercingTalon.CanUse(out act)) return true;
+
+ return base.GeneralGCD(out act);
+ }
+}
diff --git a/DefaultRotations/Melee/MNK_Default.cs b/DefaultRotations/Melee/MNK_Default.cs
new file mode 100644
index 000000000..5650ccc6e
--- /dev/null
+++ b/DefaultRotations/Melee/MNK_Default.cs
@@ -0,0 +1,210 @@
+namespace DefaultRotations.Melee;
+
+[RotationDesc(ActionID.RiddleOfFire)]
+[SourceCode(Path = "main/DefaultRotations/Melee/MNK_Default.cs")]
+[LinkDescription("https://i.imgur.com/C5lQhpe.png")]
+public sealed class MNK_Default : MNK_Base
+{
+ public override CombatType Type => CombatType.PvE;
+
+ public override string GameVersion => "6.35";
+
+ public override string RotationName => "Lunar Solar";
+
+ protected override IRotationConfigSet CreateConfiguration()
+ {
+ return base.CreateConfiguration().SetBool(CombatType.PvE, "AutoFormShift", true, "Use Form Shift");
+ }
+
+ protected override IAction CountDownAction(float remainTime)
+ {
+ if (remainTime < 0.2)
+ {
+ if (Thunderclap.CanUse(out var act, CanUseOption.IgnoreClippingCheck)) return act;
+ }
+ if (remainTime < 15)
+ {
+ if (Chakra < 5 && Meditation.CanUse(out var act, CanUseOption.IgnoreClippingCheck)) return act;
+ if (FormShift.CanUse(out act, CanUseOption.IgnoreClippingCheck)) return act;
+ }
+
+ return base.CountDownAction(remainTime);
+ }
+
+ private static bool OpoOpoForm(out IAction act)
+ {
+ if (ArmOfTheDestroyer.CanUse(out act)) return true;
+ if (DragonKick.CanUse(out act)) return true;
+ if (BootShine.CanUse(out act)) return true;
+ return false;
+ }
+
+ private static bool UseLunarPerfectBalance => (HasSolar || Player.HasStatus(false, StatusID.PerfectBalance))
+ && (!Player.WillStatusEndGCD(0, 0, false, StatusID.RiddleOfFire) || Player.HasStatus(false, StatusID.RiddleOfFire) || RiddleOfFire.WillHaveOneChargeGCD(2)) && PerfectBalance.WillHaveOneChargeGCD(3);
+
+ private static bool RaptorForm(out IAction act)
+ {
+ if (FourPointFury.CanUse(out act)) return true;
+ if ((Player.WillStatusEndGCD(3, 0, true, StatusID.DisciplinedFist)
+ || Player.WillStatusEndGCD(7, 0, true, StatusID.DisciplinedFist)
+ && UseLunarPerfectBalance) && TwinSnakes.CanUse(out act)) return true;
+ if (TrueStrike.CanUse(out act)) return true;
+ return false;
+ }
+
+ private static bool CoerlForm(out IAction act)
+ {
+ if (RockBreaker.CanUse(out act)) return true;
+ if (UseLunarPerfectBalance && Demolish.CanUse(out act, CanUseOption.MustUse)
+ && (Demolish.Target?.WillStatusEndGCD(7, 0, true, StatusID.Demolish) ?? false)) return true;
+ if (Demolish.CanUse(out act)) return true;
+ if (SnapPunch.CanUse(out act)) return true;
+ return false;
+ }
+
+ protected override bool GeneralGCD(out IAction act)
+ {
+ if (PerfectBalanceActions(out act)) return true;
+
+
+ if (Player.HasStatus(true, StatusID.CoerlForm))
+ {
+ if (CoerlForm(out act)) return true;
+ }
+ if (Player.HasStatus(true, StatusID.RiddleOfFire)
+ && !RiddleOfFire.ElapsedAfterGCD(2) && (PerfectBalance.ElapsedAfter(60) || !PerfectBalance.IsCoolingDown))
+ {
+ if (OpoOpoForm(out act)) return true;
+ }
+ if (Player.HasStatus(true, StatusID.RaptorForm))
+ {
+ if (RaptorForm(out act)) return true;
+ }
+ if (OpoOpoForm(out act)) return true;
+
+ if (IsMoveForward && MoveForwardAbility(out act)) return true;
+ if (Chakra < 5 && Meditation.CanUse(out act)) return true;
+ if (Configs.GetBool("AutoFormShift") && FormShift.CanUse(out act)) return true;
+
+ return base.GeneralGCD(out act);
+ }
+
+ static bool PerfectBalanceActions(out IAction act)
+ {
+ if (!BeastChakras.Contains(BeastChakra.NONE))
+ {
+ if (HasSolar && HasLunar)
+ {
+ if (PhantomRush.CanUse(out act, CanUseOption.MustUse)) return true;
+ if (TornadoKick.CanUse(out act, CanUseOption.MustUse)) return true;
+ }
+ if (BeastChakras.Contains(BeastChakra.RAPTOR))
+ {
+ if (RisingPhoenix.CanUse(out act, CanUseOption.MustUse)) return true;
+ if (FlintStrike.CanUse(out act, CanUseOption.MustUse)) return true;
+ }
+ else
+ {
+ if (ElixirField.CanUse(out act, CanUseOption.MustUse)) return true;
+ }
+ }
+ else if (Player.HasStatus(true, StatusID.PerfectBalance) && ElixirField.EnoughLevel)
+ {
+ //Sometimes, no choice
+ if (HasSolar || BeastChakras.Count(c => c == BeastChakra.OPOOPO) > 1)
+ {
+ if (LunarNadi(out act)) return true;
+ }
+ else if (BeastChakras.Contains(BeastChakra.COEURL) || BeastChakras.Contains(BeastChakra.RAPTOR))
+ {
+ if (SolarNadi(out act)) return true;
+ }
+
+ //Add status when solar.
+ if (Player.WillStatusEndGCD(3, 0, true, StatusID.DisciplinedFist)
+ || (HostileTarget?.WillStatusEndGCD(3, 0, true, StatusID.Demolish) ?? false))
+ {
+ if (SolarNadi(out act)) return true;
+ }
+ if (LunarNadi(out act)) return true;
+ }
+
+ act = null;
+ return false;
+ }
+
+ static bool LunarNadi(out IAction act)
+ {
+ if (OpoOpoForm(out act)) return true;
+ return false;
+ }
+
+ static bool SolarNadi(out IAction act)
+ {
+ //Emergency usage of status.
+ if (!BeastChakras.Contains(BeastChakra.RAPTOR)
+ && HasLunar
+ && Player.WillStatusEndGCD(1, 0, true, StatusID.DisciplinedFist))
+ {
+ if (RaptorForm(out act)) return true;
+ }
+ if (!BeastChakras.Contains(BeastChakra.COEURL)
+ && (HostileTarget?.WillStatusEndGCD(1, 0, true, StatusID.Demolish) ?? false))
+ {
+ if (CoerlForm(out act)) return true;
+ }
+
+ if (!BeastChakras.Contains(BeastChakra.OPOOPO))
+ {
+ if (OpoOpoForm(out act)) return true;
+ }
+ if (HasLunar && !BeastChakras.Contains(BeastChakra.RAPTOR))
+ {
+ if (RaptorForm(out act)) return true;
+ }
+ if (!BeastChakras.Contains(BeastChakra.COEURL))
+ {
+ if (CoerlForm(out act)) return true;
+ }
+ if (!BeastChakras.Contains(BeastChakra.RAPTOR))
+ {
+ if (RaptorForm(out act)) return true;
+ }
+
+ return CoerlForm(out act);
+ }
+
+ protected override bool EmergencyAbility(IAction nextGCD, out IAction act)
+ {
+ if (InCombat)
+ {
+ if (UseBurstMedicine(out act)) return true;
+ if (IsBurst && !CombatElapsedLessGCD(2) && RiddleOfFire.CanUse(out act, CanUseOption.OnLastAbility)) return true;
+ }
+ return base.EmergencyAbility(nextGCD, out act);
+ }
+
+ protected override bool AttackAbility(out IAction act)
+ {
+ act = null;
+
+ if (CombatElapsedLessGCD(3)) return false;
+
+ if (BeastChakras.Contains(BeastChakra.NONE) && Player.HasStatus(true, StatusID.RaptorForm)
+ && (!RiddleOfFire.EnoughLevel || Player.HasStatus(false, StatusID.RiddleOfFire) && !Player.WillStatusEndGCD(3, 0, false, StatusID.RiddleOfFire)
+ || RiddleOfFire.WillHaveOneChargeGCD(1) && (PerfectBalance.ElapsedAfter(60) || !PerfectBalance.IsCoolingDown)))
+ {
+ if (PerfectBalance.CanUse(out act, CanUseOption.EmptyOrSkipCombo)) return true;
+ }
+
+ if (Brotherhood.CanUse(out act)) return true;
+
+ if (HowlingFist.CanUse(out act)) return true;
+ if (SteelPeak.CanUse(out act)) return true;
+ if (HowlingFist.CanUse(out act, CanUseOption.MustUse)) return true;
+
+ if (RiddleOfWind.CanUse(out act)) return true;
+
+ return base.AttackAbility(out act);
+ }
+}
diff --git a/DefaultRotations/Melee/NIN_Default.cs b/DefaultRotations/Melee/NIN_Default.cs
new file mode 100644
index 000000000..c9b4dff8a
--- /dev/null
+++ b/DefaultRotations/Melee/NIN_Default.cs
@@ -0,0 +1,379 @@
+namespace DefaultRotations.Melee;
+
+[RotationDesc(ActionID.Mug)]
+[SourceCode(Path = "main/DefaultRotations/Melee/NIN_Default.cs")]
+[LinkDescription("https://www.thebalanceffxiv.com/img/jobs/nin/earlymug3.png")]
+[LinkDescription("https://www.thebalanceffxiv.com/img/jobs/nin/nininfographicwindows.png")]
+[LinkDescription("https://docs.google.com/spreadsheets/u/0/d/1BZZrqWMRrugCeiBICEgjCz2vRNXt_lRTxPnSQr24Em0/htmlview#",
+ "Under the “Planner (With sample section)”")]
+[YoutubeLink(ID = "Al9KlhA3Zvw")]
+public sealed class NIN_Default : NIN_Base
+{
+ public override CombatType Type => CombatType.PvE;
+
+ public override string GameVersion => "6.35";
+
+ public override string RotationName => "Standard";
+
+ private static INinAction _ninActionAim = null;
+ private static bool InTrickAttack => TrickAttack.IsCoolingDown && !TrickAttack.ElapsedAfter(17);
+ private static bool InMug => Mug.IsCoolingDown && !Mug.ElapsedAfter(19);
+ private static bool NoNinjutsu => AdjustId(ActionID.Ninjutsu) is ActionID.Ninjutsu or ActionID.RabbitMedium;
+
+ protected override IRotationConfigSet CreateConfiguration()
+ {
+ return base.CreateConfiguration()
+ .SetBool(CombatType.PvE, "UseHide", true, "Use Hide")
+ .SetBool(CombatType.PvE, "AutoUnhide", true, "Use Unhide");
+ }
+
+ protected override IAction CountDownAction(float remainTime)
+ {
+ if (remainTime > 10) ClearNinjutsu();
+
+ var realInHuton = !HutonEndAfterGCD() || IsLastAction(false, Huton);
+ if (realInHuton && _ninActionAim == Huton) ClearNinjutsu();
+
+ if (DoNinjutsu(out var act))
+ {
+ if (act == Suiton && remainTime > CountDownAhead) return null;
+ return act;
+ }
+
+ else if (remainTime < 5)
+ {
+ SetNinjutsu(Suiton);
+ }
+ else if (remainTime < 10)
+ {
+ if (_ninActionAim == null && Ten.IsCoolingDown && Hide.CanUse(out act)) return act;
+ if (!realInHuton)
+ {
+ SetNinjutsu(Huton);
+ }
+ }
+ return base.CountDownAction(remainTime);
+ }
+
+ #region Ninjutsu
+ private static void SetNinjutsu(INinAction act)
+ {
+ if (act == null || AdjustId(ActionID.Ninjutsu) == ActionID.RabbitMedium) return;
+ if (_ninActionAim != null && IsLastAction(false, Ten, Jin, Chi, FumaShurikenTen, FumaShurikenJin)) return;
+ if (_ninActionAim != act)
+ {
+ _ninActionAim = act;
+ }
+ }
+
+ private static void ClearNinjutsu()
+ {
+ if (_ninActionAim != null)
+ {
+ _ninActionAim = null;
+ }
+ }
+
+ private static bool ChoiceNinjutsu(out IAction act)
+ {
+ act = null;
+ if (AdjustId(ActionID.Ninjutsu) != ActionID.Ninjutsu) return false;
+ if (TimeSinceLastAction.TotalSeconds > 4.5) ClearNinjutsu();
+ if (_ninActionAim != null && WeaponRemain < 0.2) return false;
+
+ //Kassatsu
+ if (Player.HasStatus(true, StatusID.Kassatsu))
+ {
+ if (GokaMekkyaku.CanUse(out _))
+ {
+ SetNinjutsu(GokaMekkyaku);
+ return false;
+ }
+ if (HyoshoRanryu.CanUse(out _))
+ {
+ SetNinjutsu(HyoshoRanryu);
+ return false;
+ }
+
+ if (Katon.CanUse(out _))
+ {
+ SetNinjutsu(Katon);
+ return false;
+ }
+
+ if (Raiton.CanUse(out _))
+ {
+ SetNinjutsu(Raiton);
+ return false;
+ }
+ }
+ else
+ {
+ //Buff
+ if (Huraijin.CanUse(out act)) return true;
+ if (!HutonEndAfterGCD() && _ninActionAim?.ID == Huton.ID)
+ {
+ ClearNinjutsu();
+ return false;
+ }
+ if (Ten.CanUse(out _, CanUseOption.EmptyOrSkipCombo)
+ && (!InCombat || !Huraijin.EnoughLevel) && Huton.CanUse(out _)
+ && !IsLastAction(false, Huton))
+ {
+ SetNinjutsu(Huton);
+ return false;
+ }
+
+ //Aoe
+ if (Katon.CanUse(out _))
+ {
+ if (!Player.HasStatus(true, StatusID.Doton) && !IsMoving && !TenChiJin.WillHaveOneCharge(10))
+ SetNinjutsu(Doton);
+ else SetNinjutsu(Katon);
+ return false;
+ }
+
+ //Vulnerable
+ if (IsBurst && TrickAttack.WillHaveOneCharge(18) && Suiton.CanUse(out _))
+ {
+ SetNinjutsu(Suiton);
+ return false;
+ }
+
+ //Single
+ if (Ten.CanUse(out _, InTrickAttack && !Player.HasStatus(false, StatusID.RaijuReady) ? CanUseOption.EmptyOrSkipCombo : CanUseOption.None))
+ {
+ if (Raiton.CanUse(out _))
+ {
+ SetNinjutsu(Raiton);
+ return false;
+ }
+
+ if (!Chi.EnoughLevel && FumaShuriken.CanUse(out _))
+ {
+ SetNinjutsu(FumaShuriken);
+ return false;
+ }
+ }
+ }
+
+ if (IsLastAction(false, DotonChi, SuitonJin,
+ RabbitMedium, FumaShuriken, Katon, Raiton,
+ Hyoton, Huton, Doton, Suiton, GokaMekkyaku, HyoshoRanryu))
+ {
+ ClearNinjutsu();
+ }
+ return false;
+ }
+
+ private static bool DoNinjutsu(out IAction act)
+ {
+ act = null;
+
+ //TenChiJin
+ if (Player.HasStatus(true, StatusID.TenChiJin))
+ {
+ uint tenId = AdjustId(Ten.ID);
+ uint chiId = AdjustId(Chi.ID);
+ uint jinId = AdjustId(Jin.ID);
+
+ //First
+ if (tenId == FumaShurikenTen.ID
+ && !IsLastAction(false, FumaShurikenJin, FumaShurikenTen))
+ {
+ //AOE
+ if (Katon.CanUse(out _))
+ {
+ if (FumaShurikenJin.CanUse(out act)) return true;
+ }
+ //Single
+ if (FumaShurikenTen.CanUse(out act)) return true;
+ }
+
+ //Second
+ else if (tenId == KatonTen.ID && !IsLastAction(false, KatonTen))
+ {
+ if (KatonTen.CanUse(out act, CanUseOption.MustUse)) return true;
+ }
+ //Others
+ else if (chiId == RaitonChi.ID && !IsLastAction(false, RaitonChi))
+ {
+ if (RaitonChi.CanUse(out act, CanUseOption.MustUse)) return true;
+ }
+ else if (chiId == DotonChi.ID && !IsLastAction(false, DotonChi))
+ {
+ if (DotonChi.CanUse(out act, CanUseOption.MustUse)) return true;
+ }
+ else if (jinId == SuitonJin.ID && !IsLastAction(false, SuitonJin))
+ {
+ if (SuitonJin.CanUse(out act, CanUseOption.MustUse)) return true;
+ }
+ }
+
+ //Keep Kassatsu in Burst.
+ if (!Player.WillStatusEnd(3, false, StatusID.Kassatsu)
+ && Player.HasStatus(false, StatusID.Kassatsu) && !InTrickAttack) return false;
+ if (_ninActionAim == null) return false;
+
+ var id = AdjustId(ActionID.Ninjutsu);
+
+ //Failed
+ if ((uint)id == RabbitMedium.ID)
+ {
+ ClearNinjutsu();
+ act = null;
+ return false;
+ }
+ //First
+ else if (id == ActionID.Ninjutsu)
+ {
+ //Can't use.
+ if (!Player.HasStatus(true, StatusID.Kassatsu, StatusID.TenChiJin)
+ && !Ten.CanUse(out _, CanUseOption.EmptyOrSkipCombo)
+ && !IsLastAction(false, _ninActionAim.Ninjutsu[0]))
+ {
+ return false;
+ }
+ act = _ninActionAim.Ninjutsu[0];
+ return true;
+ }
+ //Finished
+ else if ((uint)id == _ninActionAim.ID)
+ {
+ if (_ninActionAim.CanUse(out act, CanUseOption.MustUse)) return true;
+ if (_ninActionAim.ID == Doton.ID && !InCombat)
+ {
+ act = _ninActionAim;
+ return true;
+ }
+ }
+ //Second
+ else if ((uint)id == FumaShuriken.ID)
+ {
+ if (_ninActionAim.Ninjutsu.Length > 1
+ && !IsLastAction(false, _ninActionAim.Ninjutsu[1]))
+ {
+ act = _ninActionAim.Ninjutsu[1];
+ return true;
+ }
+ }
+ //Third
+ else if ((uint)id == Katon.ID || (uint)id == Raiton.ID || (uint)id == Hyoton.ID)
+ {
+ if (_ninActionAim.Ninjutsu.Length > 2
+ && !IsLastAction(false, _ninActionAim.Ninjutsu[2]))
+ {
+ act = _ninActionAim.Ninjutsu[2];
+ return true;
+ }
+ }
+ return false;
+ }
+ #endregion
+
+ protected override bool GeneralGCD(out IAction act)
+ {
+ var hasRaijuReady = Player.HasStatus(true, StatusID.RaijuReady);
+
+ if ((InTrickAttack || InMug) && NoNinjutsu && !hasRaijuReady
+ && PhantomKamaitachi.CanUse(out act)) return true;
+
+ if (ChoiceNinjutsu(out act)) return true;
+ if ((!InCombat || !CombatElapsedLess(9)) && DoNinjutsu(out act)) return true;
+
+ //No Ninjutsu
+ if (NoNinjutsu)
+ {
+ if (!CombatElapsedLess(10) && FleetingRaiju.CanUse(out act)) return true;
+ if (hasRaijuReady) return false;
+
+ if (Huraijin.CanUse(out act)) return true;
+
+ //AOE
+ if (HakkeMujinsatsu.CanUse(out act)) return true;
+ if (DeathBlossom.CanUse(out act)) return true;
+
+ //Single
+ if (ArmorCrush.CanUse(out act)) return true;
+ if (AeolianEdge.CanUse(out act)) return true;
+ if (GustSlash.CanUse(out act)) return true;
+ if (SpinningEdge.CanUse(out act)) return true;
+
+ //Range
+ if (IsMoveForward && MoveForwardAbility(out act)) return true;
+ if (ThrowingDagger.CanUse(out act)) return true;
+ }
+
+ if (Configs.GetBool("AutoUnhide"))
+ {
+ StatusHelper.StatusOff(StatusID.Hidden);
+ }
+ if (!InCombat && _ninActionAim == null && Configs.GetBool("UseHide")
+ && Ten.IsCoolingDown && Hide.CanUse(out act)) return true;
+
+ return base.GeneralGCD(out act);
+ }
+
+ [RotationDesc(ActionID.ForkedRaiju)]
+ protected override bool MoveForwardGCD(out IAction act)
+ {
+ if (ForkedRaiju.CanUse(out act)) return true;
+ return base.MoveForwardGCD(out act);
+ }
+
+ protected override bool EmergencyAbility(IAction nextGCD, out IAction act)
+ {
+ if (!NoNinjutsu || !InCombat) return base.EmergencyAbility(nextGCD, out act);
+
+ if (Kassatsu.CanUse(out act)) return true;
+ if (UseBurstMedicine(out act)) return true;
+
+ if (IsBurst && !CombatElapsedLess(5) && Mug.CanUse(out act)) return true;
+
+ //Use Suiton
+ if (!CombatElapsedLess(6))
+ {
+ if (TrickAttack.CanUse(out act)) return true;
+ if (TrickAttack.IsCoolingDown && !TrickAttack.WillHaveOneCharge(19)
+ && Meisui.CanUse(out act)) return true;
+ }
+
+ return base.EmergencyAbility(nextGCD, out act);
+ }
+
+ protected override bool AttackAbility(out IAction act)
+ {
+ act = null;
+ if (!NoNinjutsu || !InCombat) return false;
+
+ if (!IsMoving && InTrickAttack && !Ten.ElapsedAfter(30) && TenChiJin.CanUse(out act)) return true;
+
+ if (!CombatElapsedLess(5) && Bunshin.CanUse(out act)) return true;
+
+ if (InTrickAttack)
+ {
+ if (!DreamWithinADream.EnoughLevel)
+ {
+ if (Assassinate.CanUse(out act)) return true;
+ }
+ else
+ {
+ if (DreamWithinADream.CanUse(out act)) return true;
+ }
+ }
+
+ if ((!InMug || InTrickAttack)
+ && (!Bunshin.WillHaveOneCharge(10) || Player.HasStatus(false, StatusID.PhantomKamaitachiReady) || Mug.WillHaveOneCharge(2)))
+ {
+ if (HellfrogMedium.CanUse(out act)) return true;
+ if (Bhavacakra.CanUse(out act)) return true;
+ }
+ return base.AttackAbility(out act);
+ }
+
+ public override void DisplayStatus()
+ {
+ ImGui.Text(_ninActionAim?.ToString() ?? "No Aimed Ninjustus.");
+ base.DisplayStatus();
+ }
+}
diff --git a/DefaultRotations/Melee/RPR_Default.cs b/DefaultRotations/Melee/RPR_Default.cs
new file mode 100644
index 000000000..43d17498e
--- /dev/null
+++ b/DefaultRotations/Melee/RPR_Default.cs
@@ -0,0 +1,165 @@
+namespace DefaultRotations.Melee;
+
+[BetaRotation]
+[RotationDesc(ActionID.ArcaneCircle)]
+[LinkDescription("https://www.thebalanceffxiv.com/img/jobs/rpr/double_communio.png")]
+[LinkDescription("https://www.thebalanceffxiv.com/img/jobs/rpr/rpr_6.3_early_enshroud.png")]
+[SourceCode(Path = "main/DefaultRotations/Melee/RPR_Default.cs")]
+public sealed class RPR_Default : RPR_Base
+{
+ public override CombatType Type => CombatType.PvE;
+
+ public override string GameVersion => "6.38";
+
+ public override string RotationName => "Early Enshroud";
+
+ protected override IAction CountDownAction(float remainTime)
+ {
+ if (remainTime < Harpe.CastTime + CountDownAhead
+ && Harpe.CanUse(out var act)) return act;
+
+ if (SoulSow.CanUse(out act)) return act;
+
+ return base.CountDownAction(remainTime);
+ }
+
+ private static bool Reaping(out IAction act)
+ {
+ if (GrimReaping.CanUse(out act)) return true;
+ if (Player.HasStatus(true, StatusID.EnhancedCrossReaping) || !Player.HasStatus(true, StatusID.EnhancedVoidReaping))
+ {
+ if (CrossReaping.CanUse(out act)) return true;
+ }
+ else
+ {
+ if (VoidReaping.CanUse(out act)) return true;
+ }
+ return false;
+ }
+
+ protected override bool GeneralGCD(out IAction act)
+ {
+ if (SoulSow.CanUse(out act)) return true;
+
+ if (WhorlOfDeath.CanUse(out act)) return true;
+ if (ShadowOfDeath.CanUse(out act)) return true;
+
+ if (HasEnshrouded)
+ {
+ if (ShadowOfDeath.CanUse(out act)) return true;
+
+ if (LemureShroud > 1)
+ {
+ if (PlentifulHarvest.EnoughLevel && ArcaneCircle.WillHaveOneCharge(9) &&
+ (LemureShroud == 4 && (HostileTarget?.WillStatusEnd(30, true, StatusID.DeathsDesign) ?? false) || LemureShroud == 3 && (HostileTarget?.WillStatusEnd(50, true, StatusID.DeathsDesign) ?? false)))
+ {
+ if (ShadowOfDeath.CanUse(out act, CanUseOption.MustUse)) return true;
+ }
+
+ if(Reaping(out act)) return true;
+ }
+ if (LemureShroud == 1)
+ {
+ if (Communio.EnoughLevel)
+ {
+ if (!IsMoving && Communio.CanUse(out act, CanUseOption.MustUse))
+ {
+ return true;
+ }
+ else
+ {
+ if (ShadowOfDeath.CanUse(out act, IsMoving ? CanUseOption.MustUse : CanUseOption.None)) return true;
+ }
+ }
+ else
+ {
+ if (Reaping(out act)) return true;
+ }
+ }
+ }
+
+ if (HasSoulReaver)
+ {
+ if (Guillotine.CanUse(out act)) return true;
+ if (Player.HasStatus(true, StatusID.EnhancedGibbet))
+ {
+ if (Gibbet.CanUse(out act)) return true;
+ }
+ else
+ {
+ if (Gallows.CanUse(out act)) return true;
+ }
+ }
+
+ if (!CombatElapsedLessGCD(2) && PlentifulHarvest.CanUse(out act, CanUseOption.MustUse)) return true;
+
+ if (SoulScythe.CanUse(out act, CanUseOption.EmptyOrSkipCombo)) return true;
+ if (SoulSlice.CanUse(out act, CanUseOption.EmptyOrSkipCombo)) return true;
+
+ if (NightmareScythe.CanUse(out act)) return true;
+ if (SpinningScythe.CanUse(out act)) return true;
+
+ if (InfernalSlice.CanUse(out act)) return true;
+ if (WaxingSlice.CanUse(out act)) return true;
+ if (Slice.CanUse(out act)) return true;
+
+ if (InCombat && HarvestMoon.CanUse(out act, CanUseOption.MustUse)) return true;
+ if (Harpe.CanUse(out act)) return true;
+
+ return base.GeneralGCD(out act);
+ }
+
+ protected override bool AttackAbility(out IAction act)
+ {
+ var IsTargetBoss = HostileTarget?.IsBossFromTTK() ?? false;
+ var IsTargetDying = HostileTarget?.IsDying() ?? false;
+
+ if (IsBurst)
+ {
+ if (UseBurstMedicine(out act))
+ {
+ if (CombatElapsedLess(10))
+ {
+ if (!CombatElapsedLess(5)) return true;
+ }
+ else
+ {
+ if(ArcaneCircle.WillHaveOneCharge(5)) return true;
+ }
+ }
+ if ((HostileTarget?.HasStatus(true, StatusID.DeathsDesign) ?? false)
+ && ArcaneCircle.CanUse(out act)) return true;
+ }
+
+ if (IsTargetBoss && IsTargetDying ||
+ !Configs.GetBool("EnshroudPooling") && Shroud >= 50 ||
+ Configs.GetBool("EnshroudPooling") && Shroud >= 50 &&
+ (!PlentifulHarvest.EnoughLevel ||
+ Player.HasStatus(true, StatusID.ArcaneCircle) ||
+ ArcaneCircle.WillHaveOneCharge(8) ||
+ !Player.HasStatus(true, StatusID.ArcaneCircle) && ArcaneCircle.WillHaveOneCharge(65) && !ArcaneCircle.WillHaveOneCharge(50) ||
+ !Player.HasStatus(true, StatusID.ArcaneCircle) && Shroud >= 90))
+ {
+ if (Enshroud.CanUse(out act)) return true;
+ }
+
+ if (HasEnshrouded && (Player.HasStatus(true, StatusID.ArcaneCircle) || LemureShroud < 3))
+ {
+ if (LemuresScythe.CanUse(out act, CanUseOption.EmptyOrSkipCombo)) return true;
+ if (LemuresSlice.CanUse(out act, CanUseOption.EmptyOrSkipCombo)) return true;
+ }
+
+ if (PlentifulHarvest.EnoughLevel && !Player.HasStatus(true, StatusID.ImmortalSacrifice) && !Player.HasStatus(true, StatusID.BloodSownCircle) || !PlentifulHarvest.EnoughLevel)
+ {
+ if (Gluttony.CanUse(out act, CanUseOption.MustUse)) return true;
+ }
+
+ if (!Player.HasStatus(true, StatusID.BloodSownCircle) && !Player.HasStatus(true, StatusID.ImmortalSacrifice) && (Gluttony.EnoughLevel && !Gluttony.WillHaveOneChargeGCD(4) || !Gluttony.EnoughLevel || Soul == 100))
+ {
+ if (GrimSwathe.CanUse(out act)) return true;
+ if (BloodStalk.CanUse(out act)) return true;
+ }
+
+ return base.AttackAbility(out act);
+ }
+}
\ No newline at end of file
diff --git a/DefaultRotations/Melee/SAM_Default.cs b/DefaultRotations/Melee/SAM_Default.cs
new file mode 100644
index 000000000..ad3952689
--- /dev/null
+++ b/DefaultRotations/Melee/SAM_Default.cs
@@ -0,0 +1,137 @@
+namespace DefaultRotations.Melee;
+
+[SourceCode(Path = "main/DefaultRotations/Melee/SAM_Default.cs")]
+public sealed class SAM_Default : SAM_Base
+{
+ public override CombatType Type => CombatType.PvE;
+
+ public override string GameVersion => "6.28";
+
+ public override string RotationName => "Default";
+
+ protected override IRotationConfigSet CreateConfiguration()
+ {
+ return base.CreateConfiguration()
+ .SetInt(CombatType.PvE, "addKenki", 50, "Use Kenki above.", min: 0, max: 85, speed: 5);
+ }
+
+ ///
+ /// 明镜止水
+ ///
+ private static bool HaveMeikyoShisui => Player.HasStatus(true, StatusID.MeikyoShisui);
+
+ protected override bool GeneralGCD(out IAction act)
+ {
+ //奥义回返
+ if (KaeshiNamikiri.CanUse(out act, CanUseOption.MustUse)) return true;
+
+ var IsTargetBoss = HostileTarget?.IsBossFromTTK() ?? false;
+ var IsTargetDying = HostileTarget?.IsDying() ?? false;
+
+ //燕回返
+ if (KaeshiGoken.CanUse(out act, CanUseOption.MustUse | CanUseOption.EmptyOrSkipCombo)) return true;
+ if (KaeshiSetsugekka.CanUse(out act, CanUseOption.MustUse | CanUseOption.EmptyOrSkipCombo)) return true;
+
+ //奥义斩浪
+ if ((!IsTargetBoss || (HostileTarget?.HasStatus(true, StatusID.Higanbana) ?? false)) && HasMoon && HasFlower
+ && OgiNamikiri.CanUse(out act, CanUseOption.MustUse)) return true;
+
+ //处理居合术
+ if (SenCount == 1 && IsTargetBoss && !IsTargetDying)
+ {
+ if (HasMoon && HasFlower && Higanbana.CanUse(out act)) return true;
+ }
+ if (SenCount == 2)
+ {
+ if (TenkaGoken.CanUse(out act, !MidareSetsugekka.EnoughLevel ? CanUseOption.MustUse : CanUseOption.None)) return true;
+ }
+ if (SenCount == 3)
+ {
+ if (MidareSetsugekka.CanUse(out act)) return true;
+ }
+
+ //连击2
+ if ((!HasMoon || IsMoonTimeLessThanFlower || !Oka.EnoughLevel) && Mangetsu.CanUse(out act, HaveMeikyoShisui && !HasGetsu ? CanUseOption.EmptyOrSkipCombo : CanUseOption.None)) return true;
+ if ((!HasFlower || !IsMoonTimeLessThanFlower) && Oka.CanUse(out act, HaveMeikyoShisui && !HasKa ? CanUseOption.EmptyOrSkipCombo : CanUseOption.None)) return true;
+ if (!HasSetsu && Yukikaze.CanUse(out act, HaveMeikyoShisui && HasGetsu && HasKa && !HasSetsu ? CanUseOption.EmptyOrSkipCombo : CanUseOption.None)) return true;
+
+ //连击3
+ if (Gekko.CanUse(out act, HaveMeikyoShisui && !HasGetsu ? CanUseOption.EmptyOrSkipCombo : CanUseOption.None)) return true;
+ if (Kasha.CanUse(out act, HaveMeikyoShisui && !HasKa ? CanUseOption.EmptyOrSkipCombo : CanUseOption.None)) return true;
+
+ //连击2
+ if ((!HasMoon || IsMoonTimeLessThanFlower || !Shifu.EnoughLevel) && Jinpu.CanUse(out act)) return true;
+ if ((!HasFlower || !IsMoonTimeLessThanFlower) && Shifu.CanUse(out act)) return true;
+
+ if (!HaveMeikyoShisui)
+ {
+ //连击1
+ if (Fuko.CanUse(out act)) return true;
+ if (!Fuko.EnoughLevel && Fuga.CanUse(out act)) return true;
+ if (Hakaze.CanUse(out act)) return true;
+
+ //燕飞
+ if (Enpi.CanUse(out act)) return true;
+ }
+
+ return base.GeneralGCD(out act);
+ }
+
+ protected override bool AttackAbility(out IAction act)
+ {
+ var IsTargetBoss = HostileTarget?.IsBossFromTTK() ?? false;
+ var IsTargetDying = HostileTarget?.IsDying() ?? false;
+
+ //意气冲天
+ if (Kenki <= 50 && Ikishoten.CanUse(out act)) return true;
+
+ //叶隐
+ if ((HostileTarget?.HasStatus(true, StatusID.Higanbana) ?? false) && (HostileTarget?.WillStatusEnd(32, true, StatusID.Higanbana) ?? false) && !(HostileTarget?.WillStatusEnd(28, true, StatusID.Higanbana) ?? false) && SenCount == 1 && IsLastAction(true, Yukikaze) && !HaveMeikyoShisui)
+ {
+ if (Hagakure.CanUse(out act)) return true;
+ }
+
+ //闪影、红莲
+ if (HasMoon && HasFlower)
+ {
+ if (HissatsuGuren.CanUse(out act, !HissatsuSenei.EnoughLevel ? CanUseOption.MustUse : CanUseOption.None)) return true;
+ if (HissatsuSenei.CanUse(out act)) return true;
+ }
+
+ //照破、无明照破
+ if (Shoha2.CanUse(out act)) return true;
+ if (Shoha.CanUse(out act)) return true;
+
+ //震天、九天
+ if (Kenki >= 50 && Ikishoten.WillHaveOneCharge(10) || Kenki >= Configs.GetInt("addKenki") || IsTargetBoss && IsTargetDying)
+ {
+ if (HissatsuKyuten.CanUse(out act)) return true;
+ if (HissatsuShinten.CanUse(out act)) return true;
+ }
+
+ return base.AttackAbility(out act);
+ }
+ protected override bool EmergencyAbility(IAction nextGCD, out IAction act)
+ {
+ var IsTargetBoss = HostileTarget?.IsBossFromTTK() ?? false;
+ var IsTargetDying = HostileTarget?.IsDying() ?? false;
+
+ //明镜止水
+ if (HasHostilesInRange && IsLastGCD(true, Yukikaze, Mangetsu, Oka) &&
+ (!IsTargetBoss || (HostileTarget?.HasStatus(true, StatusID.Higanbana) ?? false) && !(HostileTarget?.WillStatusEnd(40, true, StatusID.Higanbana) ?? false) || !HasMoon && !HasFlower || IsTargetBoss && IsTargetDying))
+ {
+ if (MeikyoShisui.CanUse(out act, CanUseOption.EmptyOrSkipCombo)) return true;
+ }
+ return base.EmergencyAbility(nextGCD, out act);
+ }
+
+
+ protected override IAction CountDownAction(float remainTime)
+ {
+ //开局使用明镜
+ if (remainTime <= 5 && MeikyoShisui.CanUse(out _, CanUseOption.IgnoreClippingCheck)) return MeikyoShisui;
+ //真北防止boss面向没到位
+ if (remainTime <= 2 && TrueNorth.CanUse(out _, CanUseOption.IgnoreClippingCheck)) return TrueNorth;
+ return base.CountDownAction(remainTime);
+ }
+}
\ No newline at end of file
diff --git a/DefaultRotations/Ranged/BRD_Default.cs b/DefaultRotations/Ranged/BRD_Default.cs
new file mode 100644
index 000000000..727a88ed0
--- /dev/null
+++ b/DefaultRotations/Ranged/BRD_Default.cs
@@ -0,0 +1,178 @@
+namespace DefaultRotations.Ranged;
+
+[SourceCode(Path = "main/DefaultRotations/Ranged/BRD_Default.cs")]
+public sealed class BRD_Default : BRD_Base
+{
+ public override CombatType Type => CombatType.PvE;
+
+ public override string GameVersion => "6.28";
+
+ public override string RotationName => "Default";
+
+ protected override IRotationConfigSet CreateConfiguration() => base.CreateConfiguration()
+ .SetBool(CombatType.PvE, "BindWAND", false, @"Use Raging Strikes on ""Wanderer's Minuet""")
+ .SetCombo(CombatType.PvE, "FirstSong", 0, "First Song", "Wanderer's Minuet", "Mage's Ballad", "Army's Paeon")
+ .SetFloat( RotationSolver.Basic.Configuration.ConfigUnitType.Seconds, CombatType.PvE, "WANDTime", 43, "Wanderer's Minuet Uptime", min: 0, max: 45, speed: 1)
+ .SetFloat(RotationSolver.Basic.Configuration.ConfigUnitType.Seconds, CombatType.PvE,"MAGETime", 34, "Mage's Ballad Uptime", min: 0, max: 45, speed: 1)
+ .SetFloat(RotationSolver.Basic.Configuration.ConfigUnitType.Seconds, CombatType.PvE, "ARMYTime", 43, "Army's Paeon Uptime", min: 0, max: 45, speed: 1);
+
+ public override string Description => "Please make sure that the three song times add up to 120 seconds!";
+
+ private bool BindWAND => Configs.GetBool("BindWAND") && WanderersMinuet.EnoughLevel;
+ private int FirstSong => Configs.GetCombo("FirstSong");
+ private float WANDRemainTime => 45 - Configs.GetFloat("WANDTime");
+ private float MAGERemainTime => 45 - Configs.GetFloat("MAGETime");
+ private float ARMYRemainTime => 45 - Configs.GetFloat("ARMYTime");
+
+ protected override bool GeneralGCD(out IAction act)
+ {
+ if (IronJaws.CanUse(out act)) return true;
+ if (IronJaws.CanUse(out act, CanUseOption.MustUse) && IronJaws.Target.WillStatusEnd(30, true, IronJaws.TargetStatus))
+ {
+ if (Player.HasStatus(true, StatusID.RagingStrikes) && Player.WillStatusEndGCD(1, 0, true, StatusID.RagingStrikes)) return true;
+ }
+
+ if (CanUseApexArrow(out act)) return true;
+
+ if (BlastArrow.CanUse(out act, CanUseOption.MustUse))
+ {
+ if (!Player.HasStatus(true, StatusID.RagingStrikes)) return true;
+ if (Player.HasStatus(true, StatusID.RagingStrikes) && Barrage.IsCoolingDown) return true;
+ }
+
+ if (ShadowBite.CanUse(out act)) return true;
+ if (QuickNock.CanUse(out act)) return true;
+
+ if (WindBite.CanUse(out act)) return true;
+ if (VenomousBite.CanUse(out act)) return true;
+
+ if (StraitShoot.CanUse(out act)) return true;
+
+ if (HeavyShoot.CanUse(out act)) return true;
+
+ return base.GeneralGCD(out act);
+ }
+
+ protected override bool EmergencyAbility(IAction nextGCD, out IAction act)
+ {
+ if (nextGCD.IsTheSameTo(true, StraitShoot, VenomousBite, WindBite, IronJaws))
+ {
+ return base.EmergencyAbility(nextGCD, out act);
+ }
+ else if ((!RagingStrikes.EnoughLevel || Player.HasStatus(true, StatusID.RagingStrikes)) && (!BattleVoice.EnoughLevel || Player.HasStatus(true, StatusID.BattleVoice)))
+ {
+ if ((EmpyrealArrow.IsCoolingDown && !EmpyrealArrow.WillHaveOneChargeGCD(1) || !EmpyrealArrow.EnoughLevel) && Repertoire != 3)
+ {
+ if (!Player.HasStatus(true, StatusID.StraightShotReady) && Barrage.CanUse(out act)) return true;
+ }
+ }
+
+ return base.EmergencyAbility(nextGCD, out act);
+ }
+
+ protected override bool AttackAbility(out IAction act)
+ {
+ act = null;
+
+ if (Song == Song.NONE)
+ {
+ if (FirstSong == 0 && WanderersMinuet.CanUse(out act)) return true;
+ if (FirstSong == 1 && MagesBallad.CanUse(out act)) return true;
+ if (FirstSong == 2 && ArmysPaeon.CanUse(out act)) return true;
+ if (WanderersMinuet.CanUse(out act)) return true;
+ if (MagesBallad.CanUse(out act)) return true;
+ if (ArmysPaeon.CanUse(out act)) return true;
+ }
+
+ if (IsBurst && Song != Song.NONE && MagesBallad.EnoughLevel)
+ {
+ if (RagingStrikes.CanUse(out act))
+ {
+ if (BindWAND && Song == Song.WANDERER && WanderersMinuet.EnoughLevel) return true;
+ if (!BindWAND) return true;
+ }
+
+ if (RadiantFinale.CanUse(out act, CanUseOption.MustUse))
+ {
+ if (Player.HasStatus(true, StatusID.RagingStrikes) && RagingStrikes.ElapsedOneChargeAfterGCD(1)) return true;
+ }
+
+ if (BattleVoice.CanUse(out act, CanUseOption.MustUse))
+ {
+ if (IsLastAction(true, RadiantFinale)) return true;
+
+ if (Player.HasStatus(true, StatusID.RagingStrikes) && RagingStrikes.ElapsedOneChargeAfterGCD(1)) return true;
+ }
+ }
+
+ if (RadiantFinale.EnoughLevel && RadiantFinale.IsCoolingDown && BattleVoice.EnoughLevel && !BattleVoice.IsCoolingDown) return false;
+
+ if (WanderersMinuet.CanUse(out act, CanUseOption.OnLastAbility))
+ {
+ if (SongEndAfter(ARMYRemainTime) && (Song != Song.NONE || Player.HasStatus(true, StatusID.ArmyEthos))) return true;
+ }
+
+ if (Song != Song.NONE && EmpyrealArrow.CanUse(out act)) return true;
+
+ if (PitchPerfect.CanUse(out act))
+ {
+ if (SongEndAfter(3) && Repertoire > 0) return true;
+
+ if (Repertoire == 3) return true;
+
+ if (Repertoire == 2 && EmpyrealArrow.WillHaveOneChargeGCD(1) && NextAbilityToNextGCD < PitchPerfect.AnimationLockTime + Ping) return true;
+
+ if (Repertoire == 2 && EmpyrealArrow.WillHaveOneChargeGCD() && NextAbilityToNextGCD > PitchPerfect.AnimationLockTime + Ping) return true;
+ }
+
+ if (MagesBallad.CanUse(out act))
+ {
+ if (Song == Song.WANDERER && SongEndAfter(WANDRemainTime) && Repertoire == 0) return true;
+ if (Song == Song.ARMY && SongEndAfterGCD(2) && WanderersMinuet.IsCoolingDown) return true;
+ }
+
+ if (ArmysPaeon.CanUse(out act))
+ {
+ if (WanderersMinuet.EnoughLevel && SongEndAfter(MAGERemainTime) && Song == Song.MAGE) return true;
+ if (WanderersMinuet.EnoughLevel && SongEndAfter(2) && MagesBallad.IsCoolingDown && Song == Song.WANDERER) return true;
+ if (!WanderersMinuet.EnoughLevel && SongEndAfter(2)) return true;
+ }
+
+ if (Sidewinder.CanUse(out act))
+ {
+ if (Player.HasStatus(true, StatusID.BattleVoice) && (Player.HasStatus(true, StatusID.RadiantFinale) || !RadiantFinale.EnoughLevel)) return true;
+
+ if (!BattleVoice.WillHaveOneCharge(10) && !RadiantFinale.WillHaveOneCharge(10)) return true;
+
+ if (RagingStrikes.IsCoolingDown && !Player.HasStatus(true, StatusID.RagingStrikes)) return true;
+ }
+
+ if (EmpyrealArrow.IsCoolingDown || !EmpyrealArrow.WillHaveOneChargeGCD() || Repertoire != 3 || !EmpyrealArrow.EnoughLevel)
+ {
+ if (RainOfDeath.CanUse(out act, CanUseOption.EmptyOrSkipCombo)) return true;
+
+ if (Bloodletter.CanUse(out act, CanUseOption.EmptyOrSkipCombo)) return true;
+ }
+
+ return base.AttackAbility(out act);
+ }
+
+ private static bool CanUseApexArrow(out IAction act)
+ {
+ if (!ApexArrow.CanUse(out act, CanUseOption.MustUse)) return false;
+
+ if (QuickNock.CanUse(out _) && SoulVoice == 100) return true;
+
+ if (SoulVoice == 100 && BattleVoice.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;
+ }
+}
diff --git a/DefaultRotations/Ranged/DNC_Default.cs b/DefaultRotations/Ranged/DNC_Default.cs
new file mode 100644
index 000000000..cdc08b146
--- /dev/null
+++ b/DefaultRotations/Ranged/DNC_Default.cs
@@ -0,0 +1,142 @@
+namespace DefaultRotations.Ranged;
+
+[SourceCode(Path = "main/DefaultRotations/Ranged/DNC_Default.cs")]
+public sealed class DNC_Default : DNC_Base
+{
+ public override CombatType Type => CombatType.PvE;
+
+ public override string GameVersion => "6.28";
+
+ public override string RotationName => "Default";
+
+ protected override IAction CountDownAction(float remainTime)
+ {
+ //if(remainTime <= CountDownAhead)
+ //{
+ // if(DanceFinishGCD(out))
+ //}
+ if (remainTime <= 15)
+ {
+ if (StandardStep.CanUse(out var act, CanUseOption.MustUse)) return act;
+ if (ExecuteStepGCD(out act)) return act;
+ }
+ return base.CountDownAction(remainTime);
+ }
+
+ protected override bool EmergencyAbility(IAction nextGCD, out IAction act)
+ {
+ if (IsDancing)
+ {
+ return base.EmergencyAbility(nextGCD, out act);
+ }
+
+ if (TechnicalStep.ElapsedAfter(115)
+ && UseBurstMedicine(out act)) return true;
+
+ return base.EmergencyAbility(nextGCD, out act);
+ }
+
+ protected override bool AttackAbility(out IAction act)
+ {
+ act = null;
+ if (IsDancing) return false;
+
+ if (Devilment.CanUse(out act))
+ {
+ if (IsBurst && !TechnicalStep.EnoughLevel) return true;
+
+ if (Player.HasStatus(true, StatusID.TechnicalFinish)) return true;
+ }
+
+ if (UseClosedPosition(out act)) return true;
+
+ if (Flourish.CanUse(out act)) return true;
+ if (FanDance3.CanUse(out act, CanUseOption.MustUse)) return true;
+
+ if (Player.HasStatus(true, StatusID.Devilment) || Feathers > 3 || !TechnicalStep.EnoughLevel)
+ {
+ if (FanDance2.CanUse(out act)) return true;
+ if (FanDance.CanUse(out act)) return true;
+ }
+
+ if (FanDance4.CanUse(out act, CanUseOption.MustUse))
+ {
+ if (TechnicalStep.EnoughLevel && TechnicalStep.IsCoolingDown && TechnicalStep.WillHaveOneChargeGCD()) return false;
+ return true;
+ }
+
+ return base.AttackAbility(out act);
+ }
+
+ protected override bool GeneralGCD(out IAction act)
+ {
+ if (!InCombat && !Player.HasStatus(true, StatusID.ClosedPosition1) && ClosedPosition.CanUse(out act)) return true;
+
+ if (DanceFinishGCD(out act)) return true;
+ if (ExecuteStepGCD(out act)) return true;
+
+ if (IsBurst && InCombat && TechnicalStep.CanUse(out act, CanUseOption.MustUse)) return true;
+
+ if (AttackGCD(out act, Player.HasStatus(true, StatusID.Devilment))) return true;
+
+ return base.GeneralGCD(out act);
+ }
+
+ private static bool AttackGCD(out IAction act, bool breaking)
+ {
+ act = null;
+ if (IsDancing) return false;
+
+ if ((breaking || Esprit >= 85) && SaberDance.CanUse(out act, CanUseOption.MustUse)) return true;
+
+ if (StarFallDance.CanUse(out act, CanUseOption.MustUse)) return true;
+
+ if (Tillana.CanUse(out act, CanUseOption.MustUse)) return true;
+
+ if (UseStandardStep(out act)) return true;
+
+ if (BloodShower.CanUse(out act)) return true;
+ if (FountainFall.CanUse(out act)) return true;
+
+ if (RisingWindmill.CanUse(out act)) return true;
+ if (ReverseCascade.CanUse(out act)) return true;
+
+ if (BladeShower.CanUse(out act)) return true;
+ if (Windmill.CanUse(out act)) return true;
+
+ if (Fountain.CanUse(out act)) return true;
+ if (Cascade.CanUse(out act)) return true;
+
+ return false;
+ }
+
+ private static bool UseStandardStep(out IAction act)
+ {
+ if (!StandardStep.CanUse(out act, CanUseOption.MustUse)) return false;
+ if (Player.WillStatusEndGCD(2, 0, true, StatusID.StandardFinish)) return true;
+
+ if (!HasHostilesInRange) return false;
+
+ if (TechnicalStep.EnoughLevel && (Player.HasStatus(true, StatusID.TechnicalFinish) || TechnicalStep.IsCoolingDown && TechnicalStep.WillHaveOneCharge(5))) return false;
+
+ return true;
+ }
+
+ private static bool UseClosedPosition(out IAction act)
+ {
+ if (!ClosedPosition.CanUse(out act)) return false;
+
+ if (InCombat && Player.HasStatus(true, StatusID.ClosedPosition1))
+ {
+ foreach (var friend in PartyMembers)
+ {
+ if (friend.HasStatus(true, StatusID.ClosedPosition2))
+ {
+ if (ClosedPosition.Target != friend) return true;
+ break;
+ }
+ }
+ }
+ return false;
+ }
+}
diff --git a/DefaultRotations/Ranged/MCH_Default.cs b/DefaultRotations/Ranged/MCH_Default.cs
new file mode 100644
index 000000000..7af2dc7a0
--- /dev/null
+++ b/DefaultRotations/Ranged/MCH_Default.cs
@@ -0,0 +1,164 @@
+namespace DefaultRotations.Ranged;
+
+[BetaRotation]
+[SourceCode(Path = "main/DefaultRotations/Ranged/MCH_Default.cs")]
+[LinkDescription("https://cdn.discordapp.com/attachments/277968251789639680/1086348727691780226/mch_rotation.png")]
+[RotationDesc(ActionID.Wildfire)]
+public sealed class MCH_Default : MCH_Base
+{
+ public override CombatType Type => CombatType.PvE;
+
+ public override string GameVersion => "6.38";
+
+ public override string RotationName => "Delayed Tools Opener";
+
+ protected override IAction CountDownAction(float remainTime)
+ {
+ if (remainTime < CountDownAhead)
+ {
+ if (AirAnchor.CanUse(out var act1)) return act1;
+ else if (!AirAnchor.EnoughLevel && HotShot.CanUse(out act1)) return act1;
+ }
+ if (remainTime < 2 && UseBurstMedicine(out var act)) return act;
+ if (remainTime < 5 && Reassemble.CanUse(out act, CanUseOption.EmptyOrSkipCombo | CanUseOption.IgnoreClippingCheck)) return act;
+ return base.CountDownAction(remainTime);
+ }
+
+ protected override IRotationConfigSet CreateConfiguration()
+ {
+ return base.CreateConfiguration()
+ .SetBool(CombatType.PvE, "MCH_Reassemble", true, "Use Reassamble with ChainSaw");
+ }
+
+ protected override bool GeneralGCD(out IAction act)
+ {
+ //Overheated
+ if (AutoCrossbow.CanUse(out act)) return true;
+ if (HeatBlast.CanUse(out act)) return true;
+
+ //Long Cds
+ if (BioBlaster.CanUse(out act)) return true;
+ if (!SpreadShot.CanUse(out _))
+ {
+ if (AirAnchor.CanUse(out act)) return true;
+ else if (!AirAnchor.EnoughLevel && HotShot.CanUse(out act)) return true;
+
+ if (Drill.CanUse(out act)) return true;
+ }
+
+ if (!CombatElapsedLessGCD(4) && ChainSaw.CanUse(out act, CanUseOption.MustUse)) return true;
+
+ //Aoe
+ if (ChainSaw.CanUse(out act)) return true;
+ if (SpreadShot.CanUse(out act)) return true;
+
+ //Single
+ if (CleanShot.CanUse(out act)) return true;
+ if (SlugShot.CanUse(out act)) return true;
+ if (SplitShot.CanUse(out act)) return true;
+
+ return base.GeneralGCD(out act);
+ }
+
+ protected override bool EmergencyAbility(IAction nextGCD, out IAction act)
+ {
+ if (Configs.GetBool("MCH_Reassemble") && ChainSaw.EnoughLevel && nextGCD.IsTheSameTo(true, ChainSaw))
+ {
+ if (Reassemble.CanUse(out act, CanUseOption.EmptyOrSkipCombo)) return true;
+ }
+ if (Ricochet.CanUse(out act, CanUseOption.MustUse)) return true;
+ if (GaussRound.CanUse(out act, CanUseOption.MustUse)) return true;
+
+ if (!Drill.EnoughLevel && nextGCD.IsTheSameTo(true, CleanShot)
+ || nextGCD.IsTheSameTo(false, AirAnchor, ChainSaw, Drill))
+ {
+ if (Reassemble.CanUse(out act, CanUseOption.EmptyOrSkipCombo)) return true;
+ }
+ return base.EmergencyAbility(nextGCD, out act);
+ }
+
+ protected override bool AttackAbility(out IAction act)
+ {
+ if (IsBurst)
+ {
+ if (UseBurstMedicine(out act)) return true;
+ if ((IsLastAbility(false, Hypercharge) || Heat >= 50) && !CombatElapsedLess(10)
+ && Wildfire.CanUse(out act, CanUseOption.OnLastAbility)) return true;
+ }
+
+ if (!CombatElapsedLess(12) && CanUseHypercharge(out act)) return true;
+ if (CanUseRookAutoturret(out act)) return true;
+
+ if (BarrelStabilizer.CanUse(out act)) return true;
+
+ if (CombatElapsedLess(8)) return false;
+
+ var option = CanUseOption.MustUse | CanUseOption.EmptyOrSkipCombo;
+ if (GaussRound.CurrentCharges <= Ricochet.CurrentCharges)
+ {
+ if (Ricochet.CanUse(out act, option)) return true;
+ }
+ if (GaussRound.CanUse(out act, option)) return true;
+
+ return base.AttackAbility(out act);
+ }
+
+ //private static bool AirAnchorBlockTime(float time)
+ //{
+ // if (AirAnchor.EnoughLevel)
+ // {
+ // return AirAnchor.IsCoolingDown && AirAnchor.WillHaveOneCharge(time);
+ // }
+ // else
+ // {
+ // return HotShot.IsCoolingDown && HotShot.WillHaveOneCharge(time);
+ // }
+ //}
+
+ private static bool CanUseRookAutoturret(out IAction act)
+ {
+ act = null;
+ if (AirAnchor.EnoughLevel)
+ {
+ if (!AirAnchor.IsCoolingDown || AirAnchor.ElapsedAfter(18)) return false;
+ }
+ else
+ {
+ if (!HotShot.IsCoolingDown || HotShot.ElapsedAfter(18)) return false;
+ }
+
+ return RookAutoturret.CanUse(out act);
+ }
+
+ const float REST_TIME = 6f;
+ private static bool CanUseHypercharge(out IAction act)
+ {
+ act = null;
+
+ //if (BarrelStabilizer.IsCoolingDown && BarrelStabilizer.WillHaveOneChargeGCD(8))
+ //{
+ // if (AirAnchorBlockTime(8)) return false;
+ //}
+ //else
+ //{
+ // if (AirAnchorBlockTime(12)) return false;
+ //}
+
+ //Check recast.
+ if (!SpreadShot.CanUse(out _))
+ {
+ if (AirAnchor.EnoughLevel)
+ {
+ if (AirAnchor.WillHaveOneCharge(REST_TIME)) return false;
+ }
+ else
+ {
+ if (HotShot.EnoughLevel && HotShot.WillHaveOneCharge(REST_TIME)) return false;
+ }
+ }
+ if (Drill.EnoughLevel && Drill.WillHaveOneCharge(REST_TIME)) return false;
+ if (ChainSaw.EnoughLevel && ChainSaw.WillHaveOneCharge(REST_TIME)) return false;
+
+ return Hypercharge.CanUse(out act);
+ }
+}
diff --git a/DefaultRotations/Tank/DRK_Default.cs b/DefaultRotations/Tank/DRK_Default.cs
new file mode 100644
index 000000000..e1fb3d00d
--- /dev/null
+++ b/DefaultRotations/Tank/DRK_Default.cs
@@ -0,0 +1,220 @@
+namespace DefaultRotations.Tank;
+
+
+[RotationDesc(ActionID.BloodWeapon, ActionID.Delirium)]
+[SourceCode(Path = "main/DefaultRotations/Tank/DRK_Balance.cs")]
+[LinkDescription("https://www.thebalanceffxiv.com/img/jobs/drk/drk_standard_6.2_v1.png")]
+public sealed class DRK_Default : DRK_Base
+{
+ public override CombatType Type => CombatType.PvE;
+
+ public override string GameVersion => "6.38";
+
+ public override string RotationName => "Balance";
+
+ public override string Description => "Please contact Nore#7219 on Discord for questions about this rotation.";
+
+ public override bool CanHealSingleAbility => false;
+
+ private static bool InTwoMIsBurst()
+ {
+ if (RatioOfMembersIn2minsBurst >= 0.5) return true;
+ if (RatioOfMembersIn2minsBurst == -1 && (BloodWeapon.IsCoolingDown && Delirium.IsCoolingDown && ((LivingShadow.IsCoolingDown && !(LivingShadow.ElapsedAfter(15))) || !LivingShadow.EnoughLevel))) return true;
+ else return false;
+ }
+
+ private static bool CombatLess => CombatElapsedLess(3);
+
+ private bool CheckDarkSide
+ {
+ get
+ {
+ if (DarkSideEndAfterGCD(3)) return true;
+
+ if (CombatLess) return false;
+
+ if ((InTwoMIsBurst() && HasDarkArts) || (HasDarkArts && Player.HasStatus(true, StatusID.TheBlackestNight)) || (HasDarkArts && DarkSideEndAfterGCD(3))) return true;
+
+ if ((InTwoMIsBurst() && BloodWeapon.IsCoolingDown && LivingShadow.IsCoolingDown && SaltedEarth.IsCoolingDown && ShadowBringer.CurrentCharges == 0 && CarveAndSpit.IsCoolingDown && SaltandDarkness.IsCoolingDown)) return true;
+
+ if (Configs.GetBool("TheBlackestNight") && CurrentMp < 6000) return false;
+
+ return CurrentMp >= 8500;
+ }
+ }
+
+ private static bool UseBlood
+ {
+ get
+ {
+ if (!Delirium.EnoughLevel) return true;
+
+ if (Player.HasStatus(true, StatusID.Delirium) && LivingShadow.IsCoolingDown) return true;
+
+ if ((Delirium.WillHaveOneChargeGCD(1) && !LivingShadow.WillHaveOneChargeGCD(3)) || Blood >= 90 && !LivingShadow.WillHaveOneChargeGCD(1)) return true;
+
+ return false;
+ }
+ }
+
+ protected override IRotationConfigSet CreateConfiguration()
+ => base.CreateConfiguration()
+ .SetBool(CombatType.PvE, "TheBlackestNight", true, "Keep at least 3000 MP");
+
+ protected override IAction CountDownAction(float remainTime)
+ {
+ //Provoke when has Shield.
+ if (remainTime <= CountDownAhead)
+ {
+ if (HasTankStance)
+ {
+ if (Provoke.CanUse(out _, CanUseOption.IgnoreClippingCheck)) return Provoke;
+ }
+ //else
+ //{
+ // if (Unmend.CanUse(out var act1)) return act1;
+ //}
+ }
+ if (remainTime <= 2 && UseBurstMedicine(out var act)) return act;
+ if (remainTime <= 3 && TheBlackestNight.CanUse(out act, CanUseOption.IgnoreClippingCheck)) return act;
+ if (remainTime <= 4 && BloodWeapon.CanUse(out act, CanUseOption.IgnoreClippingCheck)) return act;
+ return base.CountDownAction(remainTime);
+ }
+
+ protected override bool EmergencyAbility(IAction nextGCD, out IAction act)
+ {
+ if (base.EmergencyAbility(nextGCD, out act)) return true;
+
+ //if ((InCombat && CombatElapsedLess(2) || DataCenter.TimeSinceLastAction.TotalSeconds >= 10) && nextGCD.IsTheSameTo(false, HardSlash, SyphonStrike, Souleater, BloodSpiller, Unmend))
+ //if ((InCombat && CombatElapsedLess(2) || DataCenter.TimeSinceLastAction.TotalSeconds >= 10) && Target != null && Target.IsNPCEnemy() && NumberOfHostilesIn(25) == 1)
+ if ((InCombat && CombatElapsedLess(2) || TimeSinceLastAction.TotalSeconds >= 10))
+ {
+ //int[] numbers = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
+ //foreach (int number in numbers)
+ //{
+ // if (BloodWeapon.IsCoolingDown)
+ // {
+ // break;
+ // }
+
+ // BloodWeapon.CanUse(out act, CanUseOption.MustUse);
+ //}
+ if (BloodWeapon.CanUse(out act, CanUseOption.MustUse)) return true;
+
+ }
+
+ return base.EmergencyAbility(nextGCD, out act);
+ }
+
+ [RotationDesc(ActionID.TheBlackestNight)]
+ protected override bool HealSingleAbility(out IAction act)
+ {
+ if (TheBlackestNight.CanUse(out act)) return true;
+
+ return base.HealSingleAbility(out act);
+ }
+
+ [RotationDesc(ActionID.DarkMissionary, ActionID.Reprisal)]
+ protected override bool DefenseAreaAbility(out IAction act)
+ {
+ if (!InTwoMIsBurst() && DarkMissionary.CanUse(out act)) return true;
+ if (!InTwoMIsBurst() && Reprisal.CanUse(out act, CanUseOption.MustUse)) return true;
+
+ return base.DefenseAreaAbility(out act);
+ }
+
+ [RotationDesc(ActionID.TheBlackestNight, ActionID.Oblation, ActionID.Reprisal, ActionID.ShadowWall, ActionID.Rampart, ActionID.DarkMind)]
+ protected override bool DefenseSingleAbility(out IAction act)
+ {
+ act = null;
+
+ if (Player.HasStatus(true, StatusID.TheBlackestNight)) return false;
+
+ //10
+ if (Oblation.CanUse(out act, CanUseOption.EmptyOrSkipCombo | CanUseOption.OnLastAbility)) return true;
+
+ if (Reprisal.CanUse(out act, CanUseOption.MustUse | CanUseOption.OnLastAbility)) return true;
+
+ if (TheBlackestNight.CanUse(out act, CanUseOption.OnLastAbility)) return true;
+ //30
+ if ((!Rampart.IsCoolingDown || Rampart.ElapsedAfter(60)) && ShadowWall.CanUse(out act)) return true;
+
+ //20
+ if (ShadowWall.IsCoolingDown && ShadowWall.ElapsedAfter(60) && Rampart.CanUse(out act)) return true;
+ if (DarkMind.CanUse(out act)) return true;
+
+ return base.DefenseAreaAbility(out act);
+ }
+
+ protected override bool GeneralGCD(out IAction act)
+ {
+ //Use Blood
+ if (UseBlood)
+ {
+ if (Quietus.CanUse(out act)) return true;
+ if (BloodSpiller.CanUse(out act)) return true;
+ }
+
+ //AOE
+ if (StalwartSoul.CanUse(out act)) return true;
+ if (Unleash.CanUse(out act)) return true;
+
+ //单体
+ if (Souleater.CanUse(out act)) return true;
+ if (SyphonStrike.CanUse(out act)) return true;
+ if (HardSlash.CanUse(out act)) return true;
+
+ if (IsMoveForward && MoveForwardAbility(out act)) return true;
+ if (BloodWeapon.IsCoolingDown && !Player.HasStatus(true, StatusID.BloodWeapon) && Unmend.CanUse(out act)) return true;
+
+ return base.GeneralGCD(out act);
+ }
+
+ protected override bool AttackAbility(out IAction act)
+ {
+ //if (InCombat && CombatElapsedLess(2) && BloodWeapon.CanUse(out act)) return true;
+ if (CheckDarkSide)
+ {
+ if (FloodOfDarkness.CanUse(out act)) return true;
+ if (EdgeOfDarkness.CanUse(out act)) return true;
+ }
+
+ if (IsBurst)
+ {
+ if (UseBurstMedicine(out act)) return true;
+ if (Delirium.CanUse(out act)) return true;
+ if (Delirium.ElapsedAfterGCD(1) && !Delirium.ElapsedAfterGCD(3) && BloodWeapon.CanUse(out act)) return true;
+ if (LivingShadow.CanUse(out act, CanUseOption.MustUse)) return true;
+ }
+
+ if (CombatLess)
+ {
+ act = null;
+ return false;
+ }
+
+ if (!IsMoving && SaltedEarth.CanUse(out act, CanUseOption.MustUse)) return true;
+
+ if (ShadowBringer.CanUse(out act, CanUseOption.MustUse)) return true;
+
+ if (NumberOfHostilesInRange >= 3 && AbyssalDrain.CanUse(out act)) return true;
+ if (CarveAndSpit.CanUse(out act)) return true;
+
+ if (InTwoMIsBurst())
+ {
+ if (ShadowBringer.CanUse(out act, CanUseOption.MustUse | CanUseOption.EmptyOrSkipCombo)) return true;
+
+ }
+
+ if (Plunge.CanUse(out act, CanUseOption.MustUse) && !IsMoving) return true;
+
+ if (SaltandDarkness.CanUse(out act)) return true;
+
+ if (InTwoMIsBurst())
+ {
+ if (Plunge.CanUse(out act, CanUseOption.MustUse | CanUseOption.EmptyOrSkipCombo) && !IsMoving) return true;
+ }
+
+ return base.AttackAbility(out act);
+ }
+}
diff --git a/DefaultRotations/Tank/GNB_Default.cs b/DefaultRotations/Tank/GNB_Default.cs
new file mode 100644
index 000000000..16be31aa2
--- /dev/null
+++ b/DefaultRotations/Tank/GNB_Default.cs
@@ -0,0 +1,239 @@
+namespace DefaultRotations.Tank;
+
+[SourceCode(Path = "main/DefaultRotations/Tank/GNB_Default.cs")]
+public sealed class GNB_Default : GNB_Base
+{
+ public override CombatType Type => CombatType.PvE;
+
+ public override string GameVersion => "6.38";
+
+ public override string RotationName => "Default";
+
+ public override bool CanHealSingleSpell => false;
+
+ public override bool CanHealAreaSpell => false;
+
+ protected override IAction CountDownAction(float remainTime)
+ {
+ if (remainTime <= 0.7 && LightningShot.CanUse(out var act)) return act;
+ if (remainTime <= 1.2 && UseBurstMedicine(out act)) return act;
+ return base.CountDownAction(remainTime);
+ }
+
+ protected override bool EmergencyAbility(IAction nextGCD, out IAction act)
+ {
+ if (base.EmergencyAbility(nextGCD, out act)) return true;
+
+ if (InCombat && CombatElapsedLess(30))
+ {
+ if (!CombatElapsedLessGCD(2) && NoMercy.CanUse(out act, CanUseOption.MustUse | CanUseOption.IgnoreClippingCheck)) return true;
+ if (Player.HasStatus(true, StatusID.NoMercy) && BloodFest.CanUse(out act, CanUseOption.MustUse | CanUseOption.IgnoreClippingCheck)) return true;
+ }
+
+ return base.EmergencyAbility(nextGCD, out act);
+ }
+
+ protected override bool GeneralGCD(out IAction act)
+ {
+ if (FatedCircle.CanUse(out act, aoeCount: 4)) return true;
+ if (CanUseGnashingFang(out act)) return true;
+ if (FatedCircle.CanUse(out act)) return true;
+
+ if (DemonSlaughter.CanUse(out act)) return true;
+ if (DemonSlice.CanUse(out act)) return true;
+
+ if (Player.HasStatus(true, StatusID.NoMercy) && CanUseSonicBreak(out act)) return true;
+
+ if (Player.HasStatus(true, StatusID.NoMercy) && CanUseDoubleDown(out act)) return true;
+
+ if (SavageClaw.CanUse(out act, CanUseOption.EmptyOrSkipCombo)) return true;
+ if (WickedTalon.CanUse(out act, CanUseOption.EmptyOrSkipCombo)) return true;
+
+ if (CanUseBurstStrike(out act)) return true;
+
+ if (SolidBarrel.CanUse(out act)) return true;
+ if (BrutalShell.CanUse(out act)) return true;
+ if (KeenEdge.CanUse(out act)) return true;
+
+ if (IsMoveForward && MoveForwardAbility(out act)) return true;
+
+ if (LightningShot.CanUse(out act)) return true;
+
+ return base.GeneralGCD(out act);
+ }
+
+ protected override bool AttackAbility(out IAction act)
+ {
+ //if (IsBurst && CanUseNoMercy(out act)) return true;
+
+ if (!CombatElapsedLessGCD(5) && NoMercy.CanUse(out act, CanUseOption.MustUse | CanUseOption.IgnoreClippingCheck)) return true;
+
+ if (JugularRip.CanUse(out act)) return true;
+
+ if (DangerZone.CanUse(out act))
+ {
+ if (!IsFullParty && !(DangerZone.Target?.IsBossFromTTK() ?? false)) return true;
+
+ if (!GnashingFang.EnoughLevel && (Player.HasStatus(true, StatusID.NoMercy) || !NoMercy.WillHaveOneCharge(15))) return true;
+
+ if (Player.HasStatus(true, StatusID.NoMercy) && GnashingFang.IsCoolingDown) return true;
+
+ if (!Player.HasStatus(true, StatusID.NoMercy) && !GnashingFang.WillHaveOneCharge(20)) return true;
+ }
+
+ if (Player.HasStatus(true, StatusID.NoMercy) && CanUseBowShock(out act)) return true;
+
+ if (RoughDivide.CanUse(out act, CanUseOption.MustUse) && !IsMoving) return true;
+ if (GnashingFang.IsCoolingDown && DoubleDown.IsCoolingDown && Ammo == 0 && BloodFest.CanUse(out act)) return true;
+
+ if (AbdomenTear.CanUse(out act)) return true;
+
+ if (Player.HasStatus(true, StatusID.NoMercy))
+ {
+ if (RoughDivide.CanUse(out act, CanUseOption.MustUse | CanUseOption.EmptyOrSkipCombo) && !IsMoving) return true;
+ }
+
+ if (EyeGouge.CanUse(out act)) return true;
+ if (Hypervelocity.CanUse(out act)) return true;
+
+ return base.AttackAbility(out act);
+ }
+
+ [RotationDesc(ActionID.HeartOfLight, ActionID.Reprisal)]
+ protected override bool DefenseAreaAbility(out IAction act)
+ {
+ if (!Player.HasStatus(true, StatusID.NoMercy) && HeartOfLight.CanUse(out act, CanUseOption.EmptyOrSkipCombo)) return true;
+ if (!Player.HasStatus(true, StatusID.NoMercy) && Reprisal.CanUse(out act, CanUseOption.MustUse)) return true;
+ return base.DefenseAreaAbility(out act);
+ }
+
+ [RotationDesc(ActionID.HeartOfStone, ActionID.Nebula, ActionID.Rampart, ActionID.Camouflage, ActionID.Reprisal)]
+ protected override bool DefenseSingleAbility(out IAction act)
+ {
+ //10
+ if (Camouflage.CanUse(out act, CanUseOption.OnLastAbility)) return true;
+ //10
+ if (HeartOfStone.CanUse(out act, CanUseOption.OnLastAbility)) return true;
+
+ //30
+ if ((!Rampart.IsCoolingDown || Rampart.ElapsedAfter(60)) && Nebula.CanUse(out act)) return true;
+ //20
+ if (Nebula.IsCoolingDown && Nebula.ElapsedAfter(60) && Rampart.CanUse(out act)) return true;
+
+ if (Reprisal.CanUse(out act)) return true;
+
+ return base.DefenseSingleAbility(out act);
+ }
+
+ [RotationDesc(ActionID.Aurora)]
+ protected override bool HealSingleAbility(out IAction act)
+ {
+ if (Aurora.CanUse(out act, CanUseOption.EmptyOrSkipCombo | CanUseOption.OnLastAbility)) return true;
+ return base.HealSingleAbility(out act);
+ }
+
+ //private bool CanUseNoMercy(out IAction act)
+ //{
+ // if (!NoMercy.CanUse(out act, CanUseOption.OnLastAbility)) return false;
+
+ // if (!IsFullParty && !IsTargetBoss && !IsMoving && DemonSlice.CanUse(out _)) return true;
+
+ // if (!BurstStrike.EnoughLevel) return true;
+
+ // if (BurstStrike.EnoughLevel)
+ // {
+ // if (IsLastGCD((ActionID)KeenEdge.ID) && Ammo == 1 && !GnashingFang.IsCoolingDown && !BloodFest.IsCoolingDown) return true;
+ // else if (Ammo == (Level >= 88 ? 3 : 2)) return true;
+ // else if (Ammo == 2 && GnashingFang.IsCoolingDown) return true;
+ // }
+
+ // act = null;
+ // return false;
+ //}
+
+ private static bool CanUseGnashingFang(out IAction act)
+ {
+ if (GnashingFang.CanUse(out act))
+ {
+ if (DemonSlice.CanUse(out _)) return false;
+
+ if (Ammo == MaxAmmo && (Player.HasStatus(true, StatusID.NoMercy) || !NoMercy.WillHaveOneCharge(55))) return true;
+
+ if (Ammo > 0 && !NoMercy.WillHaveOneCharge(17) && NoMercy.WillHaveOneCharge(35)) return true;
+
+ if (Ammo == 3 && IsLastGCD((ActionID)BrutalShell.ID) && NoMercy.WillHaveOneCharge(3)) return true;
+
+ if (Ammo == 1 && !NoMercy.WillHaveOneCharge(55) && BloodFest.WillHaveOneCharge(5)) return true;
+
+ if (Ammo == 1 && !NoMercy.WillHaveOneCharge(55) && (!BloodFest.IsCoolingDown && BloodFest.EnoughLevel || !BloodFest.EnoughLevel)) return true;
+ }
+ return false;
+ }
+
+ private static bool CanUseSonicBreak(out IAction act)
+ {
+ if (SonicBreak.CanUse(out act))
+ {
+ if (DemonSlice.CanUse(out _)) return false;
+
+ //if (!IsFullParty && !SonicBreak.IsTargetBoss) return false;
+
+ if (!GnashingFang.EnoughLevel && Player.HasStatus(true, StatusID.NoMercy)) return true;
+
+ if (GnashingFang.IsCoolingDown && Player.HasStatus(true, StatusID.NoMercy)) return true;
+
+ if (!DoubleDown.EnoughLevel && Player.HasStatus(true, StatusID.ReadyToRip)
+ && GnashingFang.IsCoolingDown) return true;
+
+ }
+ return false;
+ }
+
+ private static bool CanUseDoubleDown(out IAction act)
+ {
+ if (DoubleDown.CanUse(out act, CanUseOption.MustUse))
+ {
+ if (DemonSlice.CanUse(out _) && Player.HasStatus(true, StatusID.NoMercy)) return true;
+
+ if (SonicBreak.IsCoolingDown && Player.HasStatus(true, StatusID.NoMercy)) return true;
+
+ if (Player.HasStatus(true, StatusID.NoMercy) && !NoMercy.WillHaveOneCharge(55) && BloodFest.WillHaveOneCharge(5)) return true;
+
+ }
+ return false;
+ }
+
+ private static bool CanUseBurstStrike(out IAction act)
+ {
+ if (BurstStrike.CanUse(out act))
+ {
+ if (DemonSlice.CanUse(out _)) return false;
+
+ if (SonicBreak.IsCoolingDown && SonicBreak.WillHaveOneCharge(0.5f) && GnashingFang.EnoughLevel) return false;
+
+ if (Player.HasStatus(true, StatusID.NoMercy) &&
+ AmmoComboStep == 0 &&
+ !GnashingFang.WillHaveOneCharge(1)) return true;
+ if (!CartridgeCharge2.EnoughLevel && Ammo == 2) return true;
+
+ if (IsLastGCD((ActionID)BrutalShell.ID) &&
+ (Ammo == MaxAmmo ||
+ BloodFest.WillHaveOneCharge(6) && Ammo <= 2 && !NoMercy.WillHaveOneCharge(10) && BloodFest.EnoughLevel)) return true;
+
+ }
+ return false;
+ }
+
+ private static bool CanUseBowShock(out IAction act)
+ {
+ if (BowShock.CanUse(out act, CanUseOption.MustUse))
+ {
+ if (DemonSlice.CanUse(out _) && !IsFullParty) return true;
+
+ if (!SonicBreak.EnoughLevel && Player.HasStatus(true, StatusID.NoMercy)) return true;
+
+ if (Player.HasStatus(true, StatusID.NoMercy) && SonicBreak.IsCoolingDown) return true;
+ }
+ return false;
+ }
+}
\ No newline at end of file
diff --git a/DefaultRotations/Tank/PLD_Default.cs b/DefaultRotations/Tank/PLD_Default.cs
new file mode 100644
index 000000000..0a6c73fd3
--- /dev/null
+++ b/DefaultRotations/Tank/PLD_Default.cs
@@ -0,0 +1,146 @@
+namespace DefaultRotations.Tank;
+
+[LinkDescription("https://xiv.sleepyshiba.com/pld/img/63-60stentative2.png")]
+[RotationDesc("The whole rotation's burst\nis base on:")]
+[RotationDesc(ActionID.FightOrFlight)]
+[SourceCode(Path = "main/DefaultRotations/Tank/PLD_Default.cs")]
+public class PLD_Default : PLD_Base
+{
+ public override CombatType Type => CombatType.PvE;
+
+ public override string GameVersion => "6.31";
+
+ public override string RotationName => "Tentative v1.2";
+
+ public override string Description => "Please work well!";
+
+ protected override IRotationConfigSet CreateConfiguration()
+ {
+ return base.CreateConfiguration()
+ .SetBool(CombatType.PvE, "UseDivineVeilPre", false, "Use Divine Veil at 15 seconds remaining on Countdown")
+ .SetBool(CombatType.PvE, "UseHolyWhenAway", true, "Use Holy Circle or Holy Spirit when out of melee range")
+ .SetBool(CombatType.PvE, "UseShieldBash", true, "Use Shield Bash when Low Blow is cooling down");
+ }
+
+ protected override IAction CountDownAction(float remainTime)
+ {
+ if (remainTime < HolySpirit.CastTime + CountDownAhead
+ && HolySpirit.CanUse(out var act)) return act;
+
+ if (remainTime < 15 && Configs.GetBool("UseDivineVeilPre")
+ && DivineVeil.CanUse(out act, CanUseOption.IgnoreClippingCheck)) return act;
+
+ return base.CountDownAction(remainTime);
+ }
+
+ protected override bool AttackAbility(out IAction act)
+ {
+ act = null;
+
+ if (InCombat)
+ {
+ if (UseBurstMedicine(out act)) return true;
+ if (IsBurst && !CombatElapsedLess(5) && FightOrFlight.CanUse(out act, CanUseOption.OnLastAbility)) return true;
+ }
+ if (CombatElapsedLess(8)) return false;
+
+ if (CircleOfScorn.CanUse(out act, CanUseOption.MustUse)) return true;
+ if (SpiritsWithin.CanUse(out act, CanUseOption.MustUse)) return true;
+
+ if (Player.WillStatusEndGCD(6, 0, true, StatusID.FightOrFlight)
+ && Requiescat.CanUse(out act, CanUseOption.MustUse)) return true;
+
+ var option = CanUseOption.MustUse;
+ if (HasFightOrFlight) option |= CanUseOption.EmptyOrSkipCombo;
+ if (!IsMoving && Intervene.CanUse(out act, option)) return true;
+
+ if (HasTankStance && OathGauge == 100 && UseOath(out act)) return true;
+
+ return base.AttackAbility(out act);
+ }
+
+ protected override bool GeneralGCD(out IAction act)
+ {
+ if (Player.HasStatus(true, StatusID.Requiescat))
+ {
+ if (Confiteor.CanUse(out act, CanUseOption.MustUse))
+ {
+ if (Player.HasStatus(true, StatusID.ConfiteorReady)) return true;
+ if (Confiteor.ID != Confiteor.AdjustedID) return true;
+ }
+ if (HolyCircle.CanUse(out act)) return true;
+ if (HolySpirit.CanUse(out act)) return true;
+ }
+
+ //AOE
+ if (HasDivineMight && HolyCircle.CanUse(out act)) return true;
+ if (Prominence.CanUse(out act)) return true;
+ if (TotalEclipse.CanUse(out act)) return true;
+
+ //Single
+ if (!CombatElapsedLess(8) && HasFightOrFlight && GoringBlade.CanUse(out act)) return true; // Dot
+ if (!FightOrFlight.WillHaveOneChargeGCD(2))
+ {
+ if (!FightOrFlight.WillHaveOneChargeGCD(6) &&
+ HasDivineMight && HolySpirit.CanUse(out act)) return true;
+ if (RageOfHalone.CanUse(out act)) return true;
+ if (Atonement.CanUse(out act)) return true;
+ }
+ //123
+ if( Configs.GetBool("UseShieldBash") && ShieldBash.CanUse(out act)) return true;
+
+ if (RageOfHalone.CanUse(out act)) return true;
+ if (RiotBlade.CanUse(out act)) return true;
+ if (FastBlade.CanUse(out act)) return true;
+
+ //Range
+ if (Configs.GetBool("UseHolyWhenAway"))
+ {
+ if (HolyCircle.CanUse(out act)) return true;
+ if (HolySpirit.CanUse(out act)) return true;
+ }
+ if (ShieldLob.CanUse(out act)) return true;
+
+ return base.GeneralGCD(out act);
+ }
+
+ [RotationDesc(ActionID.Reprisal, ActionID.DivineVeil)]
+ protected override bool DefenseAreaAbility(out IAction act)
+ {
+ if (Reprisal.CanUse(out act, CanUseOption.MustUse)) return true;
+ if (DivineVeil.CanUse(out act)) return true;
+ return base.DefenseAreaAbility(out act);
+ }
+
+ [RotationDesc(ActionID.PassageOfArms)]
+ protected override bool HealAreaAbility(out IAction act)
+ {
+ if (PassageOfArms.CanUse(out act)) return true;
+ return base.HealAreaAbility(out act);
+ }
+
+ [RotationDesc(ActionID.Sentinel, ActionID.Rampart, ActionID.Bulwark, ActionID.Sheltron, ActionID.Reprisal)]
+ protected override bool DefenseSingleAbility(out IAction act)
+ {
+ //10
+ if (Bulwark.CanUse(out act, CanUseOption.OnLastAbility)) return true;
+ if (UseOath(out act, CanUseOption.OnLastAbility)) return true;
+ //30
+ if ((!Rampart.IsCoolingDown || Rampart.ElapsedAfter(60)) && Sentinel.CanUse(out act)) return true;
+
+ //20
+ if (Sentinel.IsCoolingDown && Sentinel.ElapsedAfter(60) && Rampart.CanUse(out act)) return true;
+
+ if (Reprisal.CanUse(out act)) return true;
+
+ return base.DefenseSingleAbility(out act);
+ }
+
+ private static bool UseOath(out IAction act, CanUseOption option = CanUseOption.None)
+ {
+ if (Sheltron.CanUse(out act, option)) return true;
+ if (Intervention.CanUse(out act, option)) return true;
+
+ return false;
+ }
+}
diff --git a/DefaultRotations/Tank/WAR_Default.cs b/DefaultRotations/Tank/WAR_Default.cs
new file mode 100644
index 000000000..3c5a3aa8d
--- /dev/null
+++ b/DefaultRotations/Tank/WAR_Default.cs
@@ -0,0 +1,129 @@
+namespace DefaultRotations.Tank;
+
+[SourceCode(Path = "main/DefaultRotations/Tank/WAR_Default.cs")]
+[LinkDescription("https://cdn.discordapp.com/attachments/277962807813865472/963548326433796116/unknown.png")]
+public sealed class WAR_Default : WAR_Base
+{
+ public override CombatType Type => CombatType.PvE;
+
+ public override string GameVersion => "6.35";
+
+ public override string RotationName => "All-Around";
+
+ private static bool IsBurstStatus => !Player.WillStatusEndGCD(0, 0, false, StatusID.InnerStrength);
+
+ protected override IAction CountDownAction(float remainTime)
+ {
+ if (remainTime <= CountDownAhead)
+ {
+ if (HasTankStance)
+ {
+ if (Provoke.CanUse(out var act1, CanUseOption.IgnoreClippingCheck)) return act1;
+ }
+ else
+ {
+ if (Tomahawk.CanUse(out var act1, CanUseOption.IgnoreClippingCheck)) return act1;
+ }
+ }
+ return base.CountDownAction(remainTime);
+ }
+
+ protected override bool GeneralGCD(out IAction act)
+ {
+ if (!Player.WillStatusEndGCD(3, 0, true, StatusID.SurgingTempest))
+ {
+ if (!IsMoving && IsBurstStatus && PrimalRend.CanUse(out act, CanUseOption.MustUse))
+ {
+ if (PrimalRend.Target.DistanceToPlayer() < 1) return true;
+ }
+ if (IsBurstStatus || !Player.HasStatus(false, StatusID.NascentChaos) || BeastGauge > 80)
+ {
+ if (SteelCyclone.CanUse(out act)) return true;
+ if (InnerBeast.CanUse(out act)) return true;
+ }
+ }
+
+ if (MythrilTempest.CanUse(out act)) return true;
+ if (Overpower.CanUse(out act)) return true;
+
+ if (StormsEye.CanUse(out act)) return true;
+ if (StormsPath.CanUse(out act)) return true;
+ if (Maim.CanUse(out act)) return true;
+ if (HeavySwing.CanUse(out act)) return true;
+
+ if (IsMoveForward && MoveForwardAbility(out act)) return true;
+ if (Tomahawk.CanUse(out act)) return true;
+
+ return base.GeneralGCD(out act);
+ }
+
+ protected override bool AttackAbility(out IAction act)
+ {
+ if (Infuriate.CanUse(out act, gcdCountForAbility: 3)) return true;
+
+ if (CombatElapsedLessGCD(1)) return false;
+
+ if (UseBurstMedicine(out act)) return true;
+ if (Player.HasStatus(false, StatusID.SurgingTempest)
+ && !Player.WillStatusEndGCD(6, 0, true, StatusID.SurgingTempest)
+ || !MythrilTempest.EnoughLevel)
+ {
+ if (Berserk.CanUse(out act, CanUseOption.OnLastAbility)) return true;
+ }
+
+ if (IsBurstStatus)
+ {
+ if (Infuriate.CanUse(out act, CanUseOption.EmptyOrSkipCombo)) return true;
+ }
+
+ if (CombatElapsedLessGCD(4)) return false;
+
+ if (Orogeny.CanUse(out act)) return true;
+ if (Upheaval.CanUse(out act)) return true;
+
+ var option = CanUseOption.MustUse;
+ if (IsBurstStatus) option |= CanUseOption.EmptyOrSkipCombo;
+ if (Onslaught.CanUse(out act, option) && !IsMoving) return true;
+
+ return base.AttackAbility(out act);
+ }
+
+ protected override bool GeneralAbility(out IAction act)
+ {
+ //Auto healing
+ if (Player.GetHealthRatio() < 0.6f)
+ {
+ if (ThrillOfBattle.CanUse(out act)) return true;
+ if (Equilibrium.CanUse(out act)) return true;
+ }
+
+ if (!HasTankStance && NascentFlash.CanUse(out act)) return true;
+
+ return base.GeneralAbility(out act);
+ }
+
+ [RotationDesc(ActionID.RawIntuition, ActionID.Vengeance, ActionID.Rampart, ActionID.RawIntuition, ActionID.Reprisal)]
+ protected override bool DefenseSingleAbility(out IAction act)
+ {
+ //10
+ if (RawIntuition.CanUse(out act, CanUseOption.OnLastAbility)) return true;
+
+ //30
+ if ((!Rampart.IsCoolingDown || Rampart.ElapsedAfter(60)) && Vengeance.CanUse(out act)) return true;
+
+ //20
+ if (Vengeance.IsCoolingDown && Vengeance.ElapsedAfter(60) && Rampart.CanUse(out act)) return true;
+
+ if (Reprisal.CanUse(out act)) return true;
+
+ return false;
+ }
+
+ [RotationDesc(ActionID.ShakeItOff, ActionID.Reprisal)]
+ protected override bool DefenseAreaAbility(out IAction act)
+ {
+ if (ShakeItOff.CanUse(out act, CanUseOption.MustUse)) return true;
+ if (Reprisal.CanUse(out act, CanUseOption.MustUse)) return true;
+ return base.DefenseAreaAbility(out act);
+ }
+}
diff --git a/Directory.Build.props b/Directory.Build.props
index a9b2774f0..20375f1cc 100644
--- a/Directory.Build.props
+++ b/Directory.Build.props
@@ -6,6 +6,7 @@
3.5.4
x64
AnyCPU
+ latest
$(AppData)\XIVLauncher\addon\Hooks\dev\
diff --git a/RotationSolver.Basic/Actions/BaseAction_Target.cs b/RotationSolver.Basic/Actions/BaseAction_Target.cs
index 242da7a37..1810f4c03 100644
--- a/RotationSolver.Basic/Actions/BaseAction_Target.cs
+++ b/RotationSolver.Basic/Actions/BaseAction_Target.cs
@@ -118,7 +118,7 @@ public bool FindTarget(bool mustUse, byte aoeCount, out BattleChara target, out
if (range == 0 && EffectRange == 0)
{
target = player;
- affectedTargets = Array.Empty();
+ affectedTargets = [];
return true;
}
else if (IsTargetArea)
@@ -384,11 +384,11 @@ private static bool TargetDeath(out BattleChara target, out BattleChara[] affect
target = TargetFilter.GetDeathPeople(DataCenter.DeathPeopleAll, DataCenter.DeathPeopleParty);
if (target == null)
{
- affectedTargets = Array.Empty();
+ affectedTargets = [];
return false;
}
- affectedTargets = new BattleChara[] { target };
+ affectedTargets = [target];
return true;
}
#endregion
@@ -404,14 +404,14 @@ private bool TargetHostile(float range, bool mustUse, int aoeCount, out BattleCh
}
target = null;
- affectedTargets = Array.Empty();
+ affectedTargets = [];
return false;
}
if (!IsSingleTarget && NoAOE)
{
target = null;
- affectedTargets = Array.Empty();
+ affectedTargets = [];
return false;
}
diff --git a/RotationSolver.Basic/Data/Aspect.cs b/RotationSolver.Basic/Data/Aspect.cs
index c031e3be2..4cd15715c 100644
--- a/RotationSolver.Basic/Data/Aspect.cs
+++ b/RotationSolver.Basic/Data/Aspect.cs
@@ -39,9 +39,4 @@ public enum Aspect : byte
///
///
Piercing = 7,
-
- ///
- ///
- ///
- None = 7,
}
diff --git a/RotationSolver.Basic/Rotations/Basic/BLU_Base.cs b/RotationSolver.Basic/Rotations/Basic/BLU_Base.cs
index a5a1cc80d..755b6520e 100644
--- a/RotationSolver.Basic/Rotations/Basic/BLU_Base.cs
+++ b/RotationSolver.Basic/Rotations/Basic/BLU_Base.cs
@@ -168,7 +168,7 @@ public bool RightType
internal BLUAction(ActionID actionID, ActionOption option = ActionOption.None)
: base(actionID, option)
{
- Type = AttackType != AttackType.Magic ? BLUActionType.Physical : Aspect == Aspect.None ? BLUActionType.None : BLUActionType.Magical;
+ Type = AttackType != AttackType.Magic ? BLUActionType.Physical : Aspect == Aspect.Piercing ? BLUActionType.None : BLUActionType.Magical;
ActionCheck = (t, m) => OnSlot && RightType;
}
diff --git a/RotationSolver.sln b/RotationSolver.sln
index 548bbfe41..784e7b7a2 100644
--- a/RotationSolver.sln
+++ b/RotationSolver.sln
@@ -15,14 +15,16 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution
Resources\UsersHash.json = Resources\UsersHash.json
EndProjectSection
EndProject
-Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "RotationSolver.Basic", "RotationSolver.Basic\RotationSolver.Basic.csproj", "{140CC27C-0923-4183-9C30-26A7BDEBE606}"
-EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "RotationSolver", "RotationSolver\RotationSolver.csproj", "{99EA3D95-33DC-424F-A9E6-FE6064F11592}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ECommons", "ECommons\ECommons\ECommons.csproj", "{8EEA92E7-5D31-47AE-868A-0DCB96BAD31B}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "XIVPainter", "XIVPainter\XIVPainter\XIVPainter.csproj", "{43F3C89B-8538-4951-A3F7-E79E5970E54C}"
EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DefaultRotations", "DefaultRotations\DefaultRotations.csproj", "{B3140AB3-5FAE-4AE9-9DF4-DD2233FC4DED}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "RotationSolver.Basic", "RotationSolver.Basic\RotationSolver.Basic.csproj", "{E4895C89-77DB-48CE-81DD-22B7B524EFFA}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -31,14 +33,6 @@ Global
Release|x64 = Release|x64
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
- {140CC27C-0923-4183-9C30-26A7BDEBE606}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {140CC27C-0923-4183-9C30-26A7BDEBE606}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {140CC27C-0923-4183-9C30-26A7BDEBE606}.Debug|x64.ActiveCfg = Debug|Any CPU
- {140CC27C-0923-4183-9C30-26A7BDEBE606}.Debug|x64.Build.0 = Debug|Any CPU
- {140CC27C-0923-4183-9C30-26A7BDEBE606}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {140CC27C-0923-4183-9C30-26A7BDEBE606}.Release|Any CPU.Build.0 = Release|Any CPU
- {140CC27C-0923-4183-9C30-26A7BDEBE606}.Release|x64.ActiveCfg = Release|Any CPU
- {140CC27C-0923-4183-9C30-26A7BDEBE606}.Release|x64.Build.0 = Release|Any CPU
{99EA3D95-33DC-424F-A9E6-FE6064F11592}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{99EA3D95-33DC-424F-A9E6-FE6064F11592}.Debug|Any CPU.Build.0 = Debug|Any CPU
{99EA3D95-33DC-424F-A9E6-FE6064F11592}.Debug|x64.ActiveCfg = Debug|Any CPU
@@ -63,6 +57,22 @@ Global
{43F3C89B-8538-4951-A3F7-E79E5970E54C}.Release|Any CPU.Build.0 = Release|Any CPU
{43F3C89B-8538-4951-A3F7-E79E5970E54C}.Release|x64.ActiveCfg = Release|Any CPU
{43F3C89B-8538-4951-A3F7-E79E5970E54C}.Release|x64.Build.0 = Release|Any CPU
+ {B3140AB3-5FAE-4AE9-9DF4-DD2233FC4DED}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {B3140AB3-5FAE-4AE9-9DF4-DD2233FC4DED}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {B3140AB3-5FAE-4AE9-9DF4-DD2233FC4DED}.Debug|x64.ActiveCfg = Debug|Any CPU
+ {B3140AB3-5FAE-4AE9-9DF4-DD2233FC4DED}.Debug|x64.Build.0 = Debug|Any CPU
+ {B3140AB3-5FAE-4AE9-9DF4-DD2233FC4DED}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {B3140AB3-5FAE-4AE9-9DF4-DD2233FC4DED}.Release|Any CPU.Build.0 = Release|Any CPU
+ {B3140AB3-5FAE-4AE9-9DF4-DD2233FC4DED}.Release|x64.ActiveCfg = Release|Any CPU
+ {B3140AB3-5FAE-4AE9-9DF4-DD2233FC4DED}.Release|x64.Build.0 = Release|Any CPU
+ {E4895C89-77DB-48CE-81DD-22B7B524EFFA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {E4895C89-77DB-48CE-81DD-22B7B524EFFA}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {E4895C89-77DB-48CE-81DD-22B7B524EFFA}.Debug|x64.ActiveCfg = Debug|Any CPU
+ {E4895C89-77DB-48CE-81DD-22B7B524EFFA}.Debug|x64.Build.0 = Debug|Any CPU
+ {E4895C89-77DB-48CE-81DD-22B7B524EFFA}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {E4895C89-77DB-48CE-81DD-22B7B524EFFA}.Release|Any CPU.Build.0 = Release|Any CPU
+ {E4895C89-77DB-48CE-81DD-22B7B524EFFA}.Release|x64.ActiveCfg = Release|Any CPU
+ {E4895C89-77DB-48CE-81DD-22B7B524EFFA}.Release|x64.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
diff --git a/RotationSolver/RotationSolver.csproj b/RotationSolver/RotationSolver.csproj
index c630b2ebe..1a978ce9e 100644
--- a/RotationSolver/RotationSolver.csproj
+++ b/RotationSolver/RotationSolver.csproj
@@ -16,8 +16,8 @@
-
+