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 @@ - +