From 545c7dad224f91467f6325c0c8d9333b90eaf574 Mon Sep 17 00:00:00 2001 From: x Date: Sat, 24 Aug 2024 09:40:16 +0800 Subject: [PATCH] update BRD --- XIVSlothComboX/Combos/CustomComboPreset.cs | 112 ++- XIVSlothComboX/Combos/PvE/BRD.cs | 868 +++++++++++++----- XIVSlothComboX/Window/Functions/UserConfig.cs | 7 +- 3 files changed, 719 insertions(+), 268 deletions(-) diff --git a/XIVSlothComboX/Combos/CustomComboPreset.cs b/XIVSlothComboX/Combos/CustomComboPreset.cs index 71b0bd7ef..095f4cf9c 100644 --- a/XIVSlothComboX/Combos/CustomComboPreset.cs +++ b/XIVSlothComboX/Combos/CustomComboPreset.cs @@ -734,15 +734,19 @@ public enum CustomComboPreset #region BARD [ReplaceSkill(BRD.HeavyShot, BRD.BurstShot)] - [ConflictingCombos(BRD_ST_SimpleMode)] + [ConflictingCombos(BRD_ST_AdvMode)] [CustomComboInfo("直线射击 替换 强力射击 选项", "触发直线射击预备状态时,替换强力射击/爆发射击为直线射击/辉煌箭。", BRD.JobID, 0, "", "")] BRD_StraightShotUpgrade = 3001, - [ConflictingCombos(BRD_ST_SimpleMode)] + [ConflictingCombos(BRD_ST_AdvMode)] [ParentCombo(BRD_StraightShotUpgrade)] [CustomComboInfo("Dot选项", "开启此选项可适时插入毒/风箭。", BRD.JobID, 0, "", "")] BRD_DoTMaintainance = 3002, + [ParentCombo(BRD_StraightShotUpgrade)] + [CustomComboInfo("Apex Arrow Option", "Replaces Burst Shot with Apex Arrow when gauge is full and Blast Arrow when you are Blast Arrow ready.", BRD.JobID)] + BRD_ApexST = 3034, + [ReplaceSkill(BRD.IronJaws)] [ConflictingCombos(BRD_IronJaws_Alternate)] [CustomComboInfo("伶牙俐齿续dot模式A", "当目标身上没有毒/风dot时,替换伶牙俐齿为毒/风箭。\n当还未习得伶牙俐齿时会在毒/风箭之间自动切换。", BRD.JobID, 0, "", "")] @@ -754,12 +758,12 @@ public enum CustomComboPreset BRD_IronJaws_Alternate = 3004, [ReplaceSkill(BRD.BurstShot, BRD.QuickNock)] - [ConflictingCombos(BRD_ST_SimpleMode)] + [ConflictingCombos(BRD_ST_AdvMode)] [CustomComboInfo("爆发射击/连珠箭 替换 绝峰箭 选项", "当灵魂之声蓄满时,替换爆发射击/连珠箭为绝峰箭,触发爆破箭预备状态时替换为爆破箭。", BRD.JobID, 0, "", "")] BRD_Apex = 3005, [ReplaceSkill(BRD.Bloodletter)] - [ConflictingCombos(BRD_ST_SimpleMode)] + [ConflictingCombos(BRD_ST_AdvMode)] [CustomComboInfo("单目标能力技插入选项", "在三歌循环中根据cd时间替换失血箭为其他能力技。", BRD.JobID, 0, "", "")] BRD_ST_oGCD = 3006, @@ -769,9 +773,11 @@ public enum CustomComboPreset BRD_AoE_oGCD = 3007, [ReplaceSkill(BRD.QuickNock, BRD.Ladonsbite)] - [ConflictingCombos(BRD_AoE_SimpleMode)] + [ConflictingCombos(BRD_AoE_AdvMode)] [CustomComboInfo("AOE连击", "影噬箭 可用时把连珠箭/百首龙牙箭替换为 影噬箭。", BRD.JobID, 0, "", "")] BRD_AoE_Combo = 3008, + + [ReplaceSkill(BRD.HeavyShot, BRD.BurstShot)] [ConflictingCombos(BRD_StraightShotUpgrade, BRD_DoTMaintainance, BRD_Apex, BRD_ST_oGCD, BRD_IronJawsApex)] @@ -781,15 +787,15 @@ public enum CustomComboPreset "Adds every single target ability to one button,\nIf there are DoTs on target, Simple Bard will try to maintain their uptime.", BRD.JobID, 0, "", "" )] - BRD_ST_SimpleMode = 3009, + BRD_ST_AdvMode = 3009, - [ParentCombo(BRD_ST_SimpleMode)] + [ParentCombo(BRD_ST_AdvMode)] [CustomComboInfo("简易诗人Dots", "如果目标身上不存在风/毒dot,开启此选项会在连击中加入风/毒箭。", BRD.JobID, 0, "", "")] - BRD_Simple_DoT = 3010, + BRD_Adv_DoT = 3010, - [ParentCombo(BRD_ST_SimpleMode)] + [ParentCombo(BRD_ST_AdvMode)] [CustomComboInfo("简易诗人唱歌", "这个选项将诗人的三首歌加入简易诗人功能里", BRD.JobID, 0, "", "")] - BRD_Simple_Song = 3011, + BRD_Adv_Song = 3011, [ParentCombo(BRD_AoE_oGCD)] [CustomComboInfo("唱歌相关设置", "在AOE连击中加入三首歌循环。", BRD.JobID, 0, "", "")] @@ -803,87 +809,99 @@ public enum CustomComboPreset [ReplaceSkill(BRD.QuickNock, BRD.Ladonsbite)] [CustomComboInfo("简易诗人(AOE)", "连珠箭/百首龙牙箭 插入能力技。", BRD.JobID, 0, "", "")] - BRD_AoE_SimpleMode = 3015, + BRD_AoE_AdvMode = 3015, - [ParentCombo(BRD_AoE_SimpleMode)] + [ParentCombo(BRD_AoE_AdvMode)] [CustomComboInfo("简易诗人唱歌(AOE)", "在简单的AOE中插入歌曲。", BRD.JobID, 0, "", "")] - BRD_AoE_Simple_Songs = 3016, + BRD_AoE_Adv_Songs = 3016, - [ParentCombo(BRD_ST_SimpleMode)] - [CustomComboInfo("简易诗人Buffs", "自动插入Buff技能。", BRD.JobID, 0, "", "")] - BRD_Simple_Buffs = 3017, + [ParentCombo(BRD_AoE_AdvMode)] + [CustomComboInfo("oGcd Option", "Weave Sidewinder, Empyreal arrow, Rain of death, and Pitch perfect when available.", BRD.JobID)] + BRD_AoE_Adv_oGCD = 3037, + + + [ParentCombo(BRD_ST_AdvMode)] + [CustomComboInfo("oGcd Option", "Weave Sidewinder, Empyreal arrow, Rain of death, and Pitch perfect when available.", BRD.JobID)] + BRD_ST_Adv_oGCD = 3038, - [ParentCombo(BRD_Simple_Buffs)] - [CustomComboInfo("光明神的最终乐章替换设置", "当可用时自动插入光明神的最终乐章。", BRD.JobID, 0, "", "")] - BRD_Simple_BuffsRadiant = 3018, + [ParentCombo(BRD_ST_AdvMode)] + [CustomComboInfo("Buffs Option", "Adds buffs onto the Advanced Bard feature.", BRD.JobID)] + BRD_Adv_Buffs = 3017, - [ParentCombo(BRD_ST_SimpleMode)] - [CustomComboInfo - ( - "防止资源浪费相关选项", - "Adds enemy health checking on mobs for buffs, DoTs and Songs.\nThey will not be reapplied if less than specified.", BRD.JobID, 0, "", - "" - )] - BRD_Simple_NoWaste = 3019, + [ParentCombo(BRD_Adv_Buffs)] + [CustomComboInfo("Buffs - Radiant Option", "Adds Radiant Finale to theBuffs feature.", BRD.JobID)] + BRD_Adv_BuffsRadiant = 3018, - [ParentCombo(BRD_ST_SimpleMode)] + [ParentCombo(BRD_ST_AdvMode)] + [CustomComboInfo("No Waste Option", "Adds enemy health checking on mobs for buffs, DoTs and Songs.\nThey will not be reapplied if less than specified.", BRD.JobID)] + BRD_Adv_NoWaste = 3019, + + [ParentCombo(BRD_ST_AdvMode)] [CustomComboInfo("单体连击打断技能设置", "如果可用,在循环中加入打断技能", BRD.JobID, 0, "", "")] - BRD_Simple_Interrupt = 3020, + BRD_Adv_Interrupt = 3020, + + [ParentCombo(BRD_ST_AdvMode)] + [CustomComboInfo("Apex Arrow Option", "Adds Apex Arrow and Blast shot", BRD.JobID)] + BRD_ST_ApexArrow = 3021, + + [ParentCombo(BRD_AoE_AdvMode)] + [CustomComboInfo("Apex Arrow Option", "Adds Apex Arrow and Blast shot", BRD.JobID)] + BRD_Aoe_ApexArrow = 3039, + + - [CustomComboInfo("禁用绝峰箭", "不在一键连击中自动替换插入绝峰箭。", BRD.JobID, 0, "", "")] - BRD_RemoveApexArrow = 3021, //[ConflictingCombos(BardoGCDSingleTargetFeature)] //[ParentCombo(SimpleBardFeature)] //[CustomComboInfo("单体简易起手", "在单体一键连击中加入最佳起手技能。\n此选项与其它绝大部分类似选项均有冲突。", BRD.JobID, 0, "", "")] //BardSimpleOpener = 3022, - [ParentCombo(BRD_ST_SimpleMode)] - [CustomComboInfo("一键循环异言设置", "将失血箭集中于最佳爆发期", BRD.JobID, 0, "", "")] - BRD_Simple_Pooling = 3023, + [ParentCombo(BRD_ST_AdvMode)] + [CustomComboInfo("一键循环设置", "将失血箭集中于最佳爆发期", BRD.JobID, 0, "", "")] + BRD_Adv_Pooling = 3023, - [ConflictingCombos(BRD_ST_SimpleMode)] + [ConflictingCombos(BRD_ST_AdvMode)] [ParentCombo(BRD_IronJaws)] [CustomComboInfo("伶牙俐齿替换绝峰箭", "在有条件的情况下,将 绝峰箭 和 爆破箭 添加到 伶牙俐齿 上。", BRD.JobID, 0, "", "")] BRD_IronJawsApex = 3024, - [ParentCombo(BRD_ST_SimpleMode)] + [ParentCombo(BRD_ST_AdvMode)] [CustomComboInfo("简易猛者中续伶牙俐齿", "Enable the snapshotting of DoTs, within the remaining time of Raging Strikes below:", BRD.JobID, 0, "", "")] - BRD_Simple_RagingJaws = 3025, + BRD_Adv_RagingJaws = 3025, - [ParentCombo(BRD_Simple_DoT)] + [ParentCombo(BRD_Adv_DoT)] [CustomComboInfo("只有起手", "你可以自动对新目标释放Dots直到第一次自动刷新(伶牙俐齿)\n即启用本选项后,在第一次自动刷新(伶牙俐齿)后不会对新目标上Dot(包括同一目标上天断Dot后)", BRD.JobID, 0, "", "")] BRD_Simple_DoTOpener = 3026, - [ParentCombo(BRD_AoE_Simple_Songs)] + [ParentCombo(BRD_AoE_Adv_Songs)] [CustomComboInfo("除外放浪神的小步舞曲", "不使用放浪神的小步舞曲", BRD.JobID, 0, "", "")] BRD_AoE_Simple_SongsExcludeWM = 3027, - [ParentCombo(BRD_ST_SimpleMode)] + [ParentCombo(BRD_ST_AdvMode)] [CustomComboInfo("内丹选项", "当低于此生命值百分比时,使用内丹", BRD.JobID, 0, "", "")] BRD_ST_SecondWind = 3028, - [ParentCombo(BRD_AoE_SimpleMode)] + [ParentCombo(BRD_AoE_AdvMode)] [CustomComboInfo("内丹选项", "当低于此生命值百分比时,使用内丹", BRD.JobID, 0, "", "")] BRD_AoE_SecondWind = 3029, [Variant] - [VariantParent(BRD_ST_SimpleMode, BRD_AoE_SimpleMode)] + [VariantParent(BRD_ST_AdvMode, BRD_AoE_AdvMode)] [CustomComboInfo("铁壁 选项", "冷却结束时使用多变铁壁", BRD.JobID)] BRD_Variant_Rampart = 3030, [Variant] - [VariantParent(BRD_ST_SimpleMode, BRD_AoE_SimpleMode)] + [VariantParent(BRD_ST_AdvMode, BRD_AoE_AdvMode)] [CustomComboInfo("治疗 选项", "在下水道使用治疗当HP低于某个值", BRD.JobID)] BRD_Variant_Cure = 3031, - [ParentCombo(BRD_AoE_Simple_Songs)] + [ParentCombo(BRD_AoE_Adv_Songs)] [CustomComboInfo("Simple AoE Buffs Option", "Adds buffs onto the Simple AoE Bard feature.", BRD.JobID)] - BRD_AoE_Simple_Buffs = 3032, + BRD_AoE_Adv_Buffs = 3032, - [ParentCombo(BRD_AoE_SimpleMode)] + [ParentCombo(BRD_AoE_AdvMode)] [CustomComboInfo("Simple AoE No Waste Option", "Adds enemy health checking on targetted mob for songs.\nThey will not be reapplied if less than specified.", BRD.JobID)] - BRD_AoE_Simple_NoWaste = 3033, + BRD_AoE_Adv_NoWaste = 3033, // Last value = 3033 #endregion diff --git a/XIVSlothComboX/Combos/PvE/BRD.cs b/XIVSlothComboX/Combos/PvE/BRD.cs index 3aba4e31e..1111709a4 100644 --- a/XIVSlothComboX/Combos/PvE/BRD.cs +++ b/XIVSlothComboX/Combos/PvE/BRD.cs @@ -8,7 +8,7 @@ namespace XIVSlothComboX.Combos.PvE { - internal static class BRD + internal static class BRD { public const byte ClassID = 5; public const byte JobID = 23; @@ -42,6 +42,7 @@ public const uint BlastArrow = 25784, RadiantFinale = 25785, WideVolley = 36974, + HeartbreakShot = 36975, ResonantArrow = 36976, RadiantEncore = 36977; @@ -89,7 +90,7 @@ public const string internal static bool SongIsWandererMinuet(Song value) => value == Song.WANDERER; #endregion - // Replace HS/BS with SS/RA when procced. + // Replace HS/BS with SS/RA when procced, Apex feature added. internal class BRD_StraightShotUpgrade : CustomCombo { protected internal override CustomComboPreset Preset { get; } = CustomComboPreset.BRD_StraightShotUpgrade; @@ -98,16 +99,7 @@ protected override uint Invoke(uint actionID, uint lastComboMove, float comboTim { if (actionID is HeavyShot or BurstShot) { - if (IsEnabled(CustomComboPreset.BRD_Apex)) - { - BRDGauge? gauge = GetJobGauge(); - - if (!IsEnabled(CustomComboPreset.BRD_RemoveApexArrow) && gauge.SoulVoice == 100) - return ApexArrow; - if (LevelChecked(BlastArrow) && HasEffect(Buffs.BlastArrowReady)) - return BlastArrow; - } - + if (IsEnabled(CustomComboPreset.BRD_DoTMaintainance)) { bool venomous = TargetHasEffect(Debuffs.VenomousBite); @@ -132,10 +124,18 @@ protected override uint Invoke(uint actionID, uint lastComboMove, float comboTim } } - if (HasEffect(Buffs.HawksEye)) - return LevelChecked(RefulgentArrow) - ? RefulgentArrow - : StraightShot; + if (IsEnabled(CustomComboPreset.BRD_ApexST)) + { + BRDGauge? gauge = GetJobGauge(); + + if (LevelChecked(ApexArrow) && gauge.SoulVoice == 100) + return ApexArrow; + if (LevelChecked(BlastArrow) && HasEffect(Buffs.BlastArrowReady)) + return BlastArrow; + } + + if (HasEffect(Buffs.HawksEye) || HasEffect(Buffs.Barrage)) + return OriginalHook(StraightShot); } return actionID; @@ -150,16 +150,7 @@ protected override uint Invoke(uint actionID, uint lastComboMove, float comboTim { if (actionID is IronJaws) { - if (IsEnabled(CustomComboPreset.BRD_IronJawsApex) && LevelChecked(ApexArrow)) - { - BRDGauge? gauge = GetJobGauge(); - - if (LevelChecked(BlastArrow) && HasEffect(Buffs.BlastArrowReady)) - return BlastArrow; - if (gauge.SoulVoice == 100 && IsOffCooldown(ApexArrow)) - return ApexArrow; - } - + if (!LevelChecked(IronJaws)) { Status? venomous = FindTargetEffect(Debuffs.VenomousBite); @@ -203,6 +194,16 @@ protected override uint Invoke(uint actionID, uint lastComboMove, float comboTim return CausticBite; if (LevelChecked(Stormbite)) return Stormbite; + + if (IsEnabled(CustomComboPreset.BRD_IronJawsApex) && LevelChecked(ApexArrow)) + { + BRDGauge? gauge = GetJobGauge(); + + if (LevelChecked(BlastArrow) && HasEffect(Buffs.BlastArrowReady)) + return BlastArrow; + if (gauge.SoulVoice == 100 && IsOffCooldown(ApexArrow)) + return ApexArrow; + } } return actionID; @@ -271,25 +272,7 @@ protected override uint Invoke(uint actionID, uint lastComboMove, float comboTim return actionID; } - } - - internal class BRD_Apex : CustomCombo - { - protected internal override CustomComboPreset Preset { get; } = CustomComboPreset.BRD_Apex; - - protected override uint Invoke(uint actionID, uint lastComboMove, float comboTime, byte level) - { - if (actionID is QuickNock) - { - BRDGauge? gauge = GetJobGauge(); - - if (!IsEnabled(CustomComboPreset.BRD_RemoveApexArrow) && LevelChecked(ApexArrow) && gauge.SoulVoice == 100) - return ApexArrow; - } - - return actionID; - } - } + } internal class BRD_AoE_oGCD : CustomCombo { @@ -300,18 +283,15 @@ protected override uint Invoke(uint actionID, uint lastComboMove, float comboTim if (actionID is RainOfDeath) { BRDGauge? gauge = GetJobGauge(); - bool songWanderer = gauge.Song == Song.WANDERER; - bool empyrealReady = LevelChecked(EmpyrealArrow) && IsOffCooldown(EmpyrealArrow); - bool bloodletterReady = LevelChecked(Bloodletter) && IsOffCooldown(Bloodletter); - bool sidewinderReady = LevelChecked(Sidewinder) && IsOffCooldown(Sidewinder); + bool songWanderer = gauge.Song == Song.WANDERER; if (LevelChecked(WanderersMinuet) && songWanderer && gauge.Repertoire == 3) return OriginalHook(WanderersMinuet); - if (empyrealReady) + if (ActionReady(EmpyrealArrow)) return EmpyrealArrow; - if (bloodletterReady) + if (ActionReady(RainOfDeath)) return RainOfDeath; - if (sidewinderReady) + if (ActionReady(Sidewinder)) return Sidewinder; } @@ -319,12 +299,12 @@ protected override uint Invoke(uint actionID, uint lastComboMove, float comboTim } } - internal class BRD_AoE_SimpleMode : CustomCombo + internal class BRD_AoE_AdvMode : CustomCombo { internal static bool inOpener = false; internal static bool openerFinished = false; - protected internal override CustomComboPreset Preset { get; } = CustomComboPreset.BRD_AoE_SimpleMode; + protected internal override CustomComboPreset Preset { get; } = CustomComboPreset.BRD_AoE_AdvMode; protected override uint Invoke(uint actionID, uint lastComboMove, float comboTime, byte level) { @@ -340,7 +320,7 @@ protected override uint Invoke(uint actionID, uint lastComboMove, float comboTim bool songMage = gauge.Song == Song.MAGE; bool songArmy = gauge.Song == Song.ARMY; int targetHPThreshold = PluginConfiguration.GetCustomIntValue(Config.BRD_AoENoWasteHPPercentage); - bool isEnemyHealthHigh = !IsEnabled(CustomComboPreset.BRD_AoE_Simple_NoWaste) || GetTargetHPPercent() > targetHPThreshold; + bool isEnemyHealthHigh = !IsEnabled(CustomComboPreset.BRD_AoE_Adv_NoWaste) || GetTargetHPPercent() > targetHPThreshold; if (IsEnabled(CustomComboPreset.BRD_Variant_Cure) && IsEnabled(Variant.VariantCure) && PlayerHealthPercentageHp() <= GetOptionValue(Config.BRD_VariantCure)) return Variant.VariantCure; @@ -351,27 +331,22 @@ protected override uint Invoke(uint actionID, uint lastComboMove, float comboTim canWeave) return Variant.VariantRampart; - if (IsEnabled(CustomComboPreset.BRD_AoE_Simple_Songs) && canWeave) + if (IsEnabled(CustomComboPreset.BRD_AoE_Adv_Songs) && canWeave) { // Limit optimisation to when you are high enough level to benefit from it. if (LevelChecked(WanderersMinuet)) - { - bool minuetReady = IsOffCooldown(WanderersMinuet); - bool balladReady = IsOffCooldown(MagesBallad); - bool paeonReady = IsOffCooldown(ArmysPaeon); - bool empyrealReady = LevelChecked(EmpyrealArrow) && IsOffCooldown(EmpyrealArrow); - + { if (canWeave) { if (songNone) { // Logic to determine first song - if (minuetReady && !(JustUsed(MagesBallad) || JustUsed(ArmysPaeon))) + if (ActionReady(WanderersMinuet) && !(JustUsed(MagesBallad) || JustUsed(ArmysPaeon))) return WanderersMinuet; - if (balladReady && !(JustUsed(WanderersMinuet) || JustUsed(ArmysPaeon))) + if (ActionReady(MagesBallad) && !(JustUsed(WanderersMinuet) || JustUsed(ArmysPaeon))) return MagesBallad; - if (paeonReady && !(JustUsed(MagesBallad) || JustUsed(WanderersMinuet))) + if (ActionReady(ArmysPaeon) && !(JustUsed(MagesBallad) || JustUsed(WanderersMinuet))) return ArmysPaeon; } @@ -379,7 +354,7 @@ protected override uint Invoke(uint actionID, uint lastComboMove, float comboTim { if (songTimerInSeconds <= 3 && gauge.Repertoire > 0) // Spend any repertoire before switching to next song return OriginalHook(PitchPerfect); - if (songTimerInSeconds <= 3 && balladReady) // Move to Mage's Ballad if <= 3 seconds left on song + if (songTimerInSeconds <= 3 && ActionReady(MagesBallad)) // Move to Mage's Ballad if <= 3 seconds left on song return MagesBallad; } @@ -387,10 +362,10 @@ protected override uint Invoke(uint actionID, uint lastComboMove, float comboTim { // Move to Army's Paeon if < 3 seconds left on song - if (songTimerInSeconds <= 3 && paeonReady) + if (songTimerInSeconds <= 3 && ActionReady(ArmysPaeon)) { // Special case for Empyreal Arrow: it must be cast before you change to it to avoid drift! - if (empyrealReady) + if (ActionReady(EmpyrealArrow)) return EmpyrealArrow; return ArmysPaeon; } @@ -400,39 +375,36 @@ protected override uint Invoke(uint actionID, uint lastComboMove, float comboTim if (songArmy && canWeaveDelayed) { // Move to Wanderer's Minuet if <= 12 seconds left on song or WM off CD and have 4 repertoires of AP - if (songTimerInSeconds <= 12 || (minuetReady && gauge.Repertoire == 4)) + if (songTimerInSeconds <= 12 || (ActionReady(WanderersMinuet) && gauge.Repertoire == 4)) return WanderersMinuet; } } else if (songTimerInSeconds <= 3 && canWeave) { - bool balladReady = LevelChecked(MagesBallad) && IsOffCooldown(MagesBallad); - bool paeonReady = LevelChecked(ArmysPaeon) && IsOffCooldown(ArmysPaeon); - - if (balladReady) + if (ActionReady(MagesBallad)) return MagesBallad; - if (paeonReady) + if (ActionReady(ArmysPaeon)) return ArmysPaeon; } } - if (IsEnabled(CustomComboPreset.BRD_AoE_Simple_Buffs) && (!songNone || !LevelChecked(MagesBallad)) && isEnemyHealthHigh) + if (IsEnabled(CustomComboPreset.BRD_AoE_Adv_Buffs) && (!songNone || !LevelChecked(MagesBallad)) && isEnemyHealthHigh) { - bool radiantReady = LevelChecked(RadiantFinale) && IsOffCooldown(RadiantFinale); - bool ragingReady = LevelChecked(RagingStrikes) && IsOffCooldown(RagingStrikes); - bool battleVoiceReady = LevelChecked(BattleVoice) && IsOffCooldown(BattleVoice); - bool barrageReady = LevelChecked(Barrage) && IsOffCooldown(Barrage); - //Raging and BV before Finale to minimize drift - if (canWeaveBuffs && ragingReady) - return RagingStrikes; - if (canWeaveBuffs && battleVoiceReady) - return BattleVoice; - if (canWeaveBuffs && IsEnabled(CustomComboPreset.BRD_Simple_BuffsRadiant) && radiantReady && + float battleVoiceCD = GetCooldownRemainingTime(BattleVoice); + float ragingCD = GetCooldownRemainingTime(RagingStrikes); + + if (canWeaveDelayed && ActionReady(RadiantFinale) && (Array.TrueForAll(gauge.Coda, SongIsNotNone) || Array.Exists(gauge.Coda, SongIsWandererMinuet)) - && HasEffect(Buffs.BattleVoice)) + && (battleVoiceCD < 3 || ActionReady(BattleVoice)) && (ragingCD < 3 || ActionReady(RagingStrikes))) return RadiantFinale; - //removed requirement to not have hawks eye, it is better to maybe lose 60 potency than allow it to drift a 1000 potency gain out of the window - if (canWeaveBuffs && barrageReady && HasEffect(Buffs.RagingStrikes)) + + if (canWeaveBuffs && ActionReady(BattleVoice) && (HasEffect(Buffs.RadiantFinale) || !LevelChecked(RadiantFinale))) + return BattleVoice; + + if (canWeaveBuffs && ActionReady(RagingStrikes) && (HasEffect(Buffs.RadiantFinale) || !LevelChecked(RadiantFinale))) + return RagingStrikes; + + if (canWeaveBuffs && ActionReady(Barrage) && HasEffect(Buffs.RagingStrikes)) { if (LevelChecked(RadiantFinale) && HasEffect(Buffs.RadiantFinale)) return Barrage; @@ -445,46 +417,40 @@ protected override uint Invoke(uint actionID, uint lastComboMove, float comboTim } - if (canWeave) + if (canWeave && IsEnabled(CustomComboPreset.BRD_AoE_Adv_oGCD)) { - bool empyrealReady = LevelChecked(EmpyrealArrow) && IsOffCooldown(EmpyrealArrow); - bool sidewinderReady = LevelChecked(Sidewinder) && IsOffCooldown(Sidewinder); float battleVoiceCD = GetCooldownRemainingTime(BattleVoice); float empyrealCD = GetCooldownRemainingTime(EmpyrealArrow); float ragingCD = GetCooldownRemainingTime(RagingStrikes); float radiantCD = GetCooldownRemainingTime(RadiantFinale); - if (empyrealReady && ((!openerFinished && IsOnCooldown(RagingStrikes) || (!openerFinished && JustUsed(WanderersMinuet)) || (openerFinished && battleVoiceCD >= 3.5) || !IsEnabled(CustomComboPreset.BRD_Simple_Buffs)))) + if (ActionReady(EmpyrealArrow)) return EmpyrealArrow; if (LevelChecked(PitchPerfect) && songWanderer && - (gauge.Repertoire == 3 || (gauge.Repertoire == 2 && empyrealCD < 2)) && - ((!openerFinished && IsOnCooldown(RagingStrikes)) || (openerFinished && battleVoiceCD >= 3.5))) + (gauge.Repertoire == 3 || (gauge.Repertoire == 2 && empyrealCD < 2))) return OriginalHook(PitchPerfect); - if (sidewinderReady && ((!openerFinished && IsOnCooldown(RagingStrikes)) || (openerFinished && battleVoiceCD >= 3.5) || !IsEnabled(CustomComboPreset.BRD_AoE_Simple_Songs))) - { - if (IsEnabled(CustomComboPreset.BRD_Simple_Pooling)) + if (ActionReady(Sidewinder)) + { + if (songWanderer) { - if (songWanderer) - { - if ((HasEffect(Buffs.RagingStrikes) || ragingCD > 10) && - (HasEffect(Buffs.BattleVoice) || battleVoiceCD > 10) && - (HasEffect(Buffs.RadiantFinale) || radiantCD > 10 || - !LevelChecked(RadiantFinale))) - return Sidewinder; - } - else return Sidewinder; + if ((HasEffect(Buffs.RagingStrikes) || ragingCD > 10) && + (HasEffect(Buffs.BattleVoice) || battleVoiceCD > 10) && + (HasEffect(Buffs.RadiantFinale) || radiantCD > 10 || + !LevelChecked(RadiantFinale))) + return Sidewinder; } else return Sidewinder; + } - if (LevelChecked(Bloodletter) && (empyrealCD > 1 || !LevelChecked(EmpyrealArrow)) && ((!openerFinished && IsOnCooldown(RagingStrikes)) || openerFinished)) + if (LevelChecked(RainOfDeath) && (empyrealCD > 1 || !LevelChecked(EmpyrealArrow))) { uint rainOfDeathCharges = LevelChecked(RainOfDeath) ? GetRemainingCharges(RainOfDeath) : 0; - if (IsEnabled(CustomComboPreset.BRD_Simple_Pooling) && LevelChecked(WanderersMinuet)) + if (LevelChecked(WanderersMinuet)) { if (songWanderer) { @@ -508,11 +474,11 @@ protected override uint Invoke(uint actionID, uint lastComboMove, float comboTim return OriginalHook(RainOfDeath); } //Moved Below ogcds as it was preventing them from happening. - if (HasEffect(Buffs.RadiantEncoreReady) && !JustUsed(RadiantFinale) && GetCooldownElapsed(BattleVoice) >= 4.2f) + if (HasEffect(Buffs.RadiantEncoreReady) && !JustUsed(RadiantFinale) && GetCooldownElapsed(BattleVoice) >= 4.2f && IsEnabled(CustomComboPreset.BRD_AoE_Adv_Buffs)) return OriginalHook(RadiantEncore); // healing - please move if not appropriate priority - if (IsEnabled(CustomComboPreset.BRD_ST_SecondWind)) + if (IsEnabled(CustomComboPreset.BRD_AoE_SecondWind)) { if (PlayerHealthPercentageHp() <= PluginConfiguration.GetCustomIntValue(Config.BRD_AoESecondWindThreshold) && ActionReady(All.SecondWind)) return All.SecondWind; @@ -525,13 +491,13 @@ protected override uint Invoke(uint actionID, uint lastComboMove, float comboTim if (wideVolleyReady) return OriginalHook(WideVolley); - if (LevelChecked(ApexArrow) && gauge.SoulVoice == 100 && !IsEnabled(CustomComboPreset.BRD_RemoveApexArrow)) + if (LevelChecked(ApexArrow) && gauge.SoulVoice == 100 && IsEnabled(CustomComboPreset.BRD_Aoe_ApexArrow)) return ApexArrow; - if (blastArrowReady) + if (blastArrowReady && IsEnabled(CustomComboPreset.BRD_Aoe_ApexArrow)) return BlastArrow; - if (resonantArrowReady) + if (resonantArrowReady && IsEnabled(CustomComboPreset.BRD_AoE_Adv_Buffs)) return ResonantArrow; - if (HasEffect(Buffs.RadiantEncoreReady)) + if (HasEffect(Buffs.RadiantEncoreReady) && IsEnabled(CustomComboPreset.BRD_AoE_Adv_Buffs)) return OriginalHook(RadiantEncore); } @@ -546,20 +512,16 @@ internal class BRD_ST_oGCD : CustomCombo protected override uint Invoke(uint actionID, uint lastComboMove, float comboTime, byte level) { - if (actionID is Bloodletter) + if (actionID is Bloodletter or HeartbreakShot) { BRDGauge? gauge = GetJobGauge(); bool songArmy = gauge.Song == Song.ARMY; bool songWanderer = gauge.Song == Song.WANDERER; bool minuetReady = LevelChecked(WanderersMinuet) && IsOffCooldown(WanderersMinuet); bool balladReady = LevelChecked(MagesBallad) && IsOffCooldown(MagesBallad); - bool paeonReady = LevelChecked(ArmysPaeon) && IsOffCooldown(ArmysPaeon); - bool empyrealReady = LevelChecked(EmpyrealArrow) && IsOffCooldown(EmpyrealArrow); - bool bloodletterReady = LevelChecked(Bloodletter) && IsOffCooldown(Bloodletter); - bool sidewinderReady = LevelChecked(Sidewinder) && IsOffCooldown(Sidewinder); - - if (IsEnabled(CustomComboPreset.BRD_oGCDSongs) && - (gauge.SongTimer < 1 || songArmy)) + bool paeonReady = LevelChecked(ArmysPaeon) && IsOffCooldown(ArmysPaeon); + + if (gauge.SongTimer < 1 || songArmy) { if (minuetReady) return WanderersMinuet; @@ -570,13 +532,14 @@ protected override uint Invoke(uint actionID, uint lastComboMove, float comboTim } if (songWanderer && gauge.Repertoire == 3) - return OriginalHook(WanderersMinuet); - if (empyrealReady) + return OriginalHook(PitchPerfect); + if (ActionReady(EmpyrealArrow)) return EmpyrealArrow; - if (bloodletterReady) - return OriginalHook(Bloodletter); - if (sidewinderReady) + if (ActionReady(Sidewinder)) return Sidewinder; + if (ActionReady(Bloodletter)) + return OriginalHook(Bloodletter); + } return actionID; @@ -595,7 +558,7 @@ protected override uint Invoke(uint actionID, uint lastComboMove, float comboTim BRDGauge? gauge = GetJobGauge(); bool blastReady = LevelChecked(BlastArrow) && HasEffect(Buffs.BlastArrowReady); - if (LevelChecked(ApexArrow) && gauge.SoulVoice == 100 && !IsEnabled(CustomComboPreset.BRD_RemoveApexArrow)) + if (LevelChecked(ApexArrow) && gauge.SoulVoice == 100) return ApexArrow; if (blastReady) return BlastArrow; @@ -610,9 +573,9 @@ protected override uint Invoke(uint actionID, uint lastComboMove, float comboTim return actionID; } } - internal class BRD_ST_SimpleMode : CustomCombo + internal class BRD_ST_AdvMode : CustomCombo { - protected internal override CustomComboPreset Preset { get; } = CustomComboPreset.BRD_ST_SimpleMode; + protected internal override CustomComboPreset Preset { get; } = CustomComboPreset.BRD_ST_AdvMode; internal static bool inOpener = false; internal static bool openerFinished = false; internal static byte step = 0; @@ -635,17 +598,14 @@ protected override uint Invoke(uint actionID, uint lastComboMove, float comboTim bool songArmy = gauge.Song == Song.ARMY; bool canInterrupt = CanInterruptEnemy() && IsOffCooldown(All.HeadGraze); int targetHPThreshold = PluginConfiguration.GetCustomIntValue(Config.BRD_NoWasteHPPercentage); - bool isEnemyHealthHigh = !IsEnabled(CustomComboPreset.BRD_Simple_NoWaste) || GetTargetHPPercent() > targetHPThreshold; + bool isEnemyHealthHigh = !IsEnabled(CustomComboPreset.BRD_Adv_NoWaste) || GetTargetHPPercent() > targetHPThreshold; if (!InCombat() && (inOpener || openerFinished)) { openerFinished = false; } - - if (!IsEnabled(CustomComboPreset.BRD_Simple_NoWaste)) - openerFinished = true; - - if (IsEnabled(CustomComboPreset.BRD_Simple_Interrupt) && canInterrupt) + + if (IsEnabled(CustomComboPreset.BRD_Adv_Interrupt) && canInterrupt) return All.HeadGraze; if (IsEnabled(CustomComboPreset.BRD_Variant_Cure) && IsEnabled(Variant.VariantCure) && PlayerHealthPercentageHp() <= GetOptionValue(Config.BRD_VariantCure)) @@ -657,7 +617,7 @@ protected override uint Invoke(uint actionID, uint lastComboMove, float comboTim canWeave) return Variant.VariantRampart; - if (IsEnabled(CustomComboPreset.BRD_Simple_Song) && isEnemyHealthHigh) + if (IsEnabled(CustomComboPreset.BRD_Adv_Song) && isEnemyHealthHigh) { int songTimerInSeconds = gauge.SongTimer / 1000; @@ -668,9 +628,8 @@ protected override uint Invoke(uint actionID, uint lastComboMove, float comboTim bool minuetReady = IsOffCooldown(WanderersMinuet); bool balladReady = IsOffCooldown(MagesBallad); bool paeonReady = IsOffCooldown(ArmysPaeon); - bool empyrealReady = LevelChecked(EmpyrealArrow) && IsOffCooldown(EmpyrealArrow); - - if (empyrealReady && !openerFinished && JustUsed(WanderersMinuet)) + + if (ActionReady(EmpyrealArrow) && JustUsed(WanderersMinuet)) return EmpyrealArrow; if (canWeave) @@ -701,7 +660,7 @@ protected override uint Invoke(uint actionID, uint lastComboMove, float comboTim if (songTimerInSeconds <= 3 && paeonReady) { // Special case for Empyreal Arrow: it must be cast before you change to it to avoid drift! - if (empyrealReady) + if (ActionReady(EmpyrealArrow)) return EmpyrealArrow; return ArmysPaeon; } @@ -727,21 +686,26 @@ protected override uint Invoke(uint actionID, uint lastComboMove, float comboTim } } - if (IsEnabled(CustomComboPreset.BRD_Simple_Buffs) && (!songNone || !LevelChecked(MagesBallad)) && isEnemyHealthHigh) + if (IsEnabled(CustomComboPreset.BRD_Adv_Buffs) && (!songNone || !LevelChecked(MagesBallad)) && isEnemyHealthHigh) { bool radiantReady = LevelChecked(RadiantFinale) && IsOffCooldown(RadiantFinale); bool ragingReady = LevelChecked(RagingStrikes) && IsOffCooldown(RagingStrikes); bool battleVoiceReady = LevelChecked(BattleVoice) && IsOffCooldown(BattleVoice); bool barrageReady = LevelChecked(Barrage) && IsOffCooldown(Barrage); - //Raging and BV before Finale to minimize drift - if (canWeaveBuffs && ragingReady) - return RagingStrikes; - if (canWeaveBuffs && battleVoiceReady) - return BattleVoice; - if (canWeaveBuffs && IsEnabled(CustomComboPreset.BRD_Simple_BuffsRadiant) && radiantReady && + float battleVoiceCD = GetCooldownRemainingTime(BattleVoice); + float ragingCD = GetCooldownRemainingTime(RagingStrikes); + + if (canWeaveDelayed && IsEnabled(CustomComboPreset.BRD_Adv_BuffsRadiant) && radiantReady && (Array.TrueForAll(gauge.Coda, SongIsNotNone) || Array.Exists(gauge.Coda, SongIsWandererMinuet)) - && HasEffect(Buffs.BattleVoice)) + && (battleVoiceCD < 3 || ActionReady(BattleVoice)) && (ragingCD < 3 || ActionReady(RagingStrikes))) return RadiantFinale; + + if (canWeaveBuffs && battleVoiceReady && (HasEffect(Buffs.RadiantFinale) || !LevelChecked(RadiantFinale))) + return BattleVoice; + + if (canWeaveBuffs && ragingReady && (HasEffect(Buffs.RadiantFinale) || !LevelChecked(RadiantFinale))) + return RagingStrikes; + //removed requirement to not have hawks eye, it is better to maybe lose 60 potency than allow it to drift a 1000 potency gain out of the window if (canWeaveBuffs && barrageReady && HasEffect(Buffs.RagingStrikes)) { @@ -753,27 +717,24 @@ protected override uint Invoke(uint actionID, uint lastComboMove, float comboTim return Barrage; } } - - if (canWeave) + + if (canWeave && IsEnabled(CustomComboPreset.BRD_ST_Adv_oGCD)) { - bool empyrealReady = LevelChecked(EmpyrealArrow) && IsOffCooldown(EmpyrealArrow); - bool sidewinderReady = LevelChecked(Sidewinder) && IsOffCooldown(Sidewinder); float battleVoiceCD = GetCooldownRemainingTime(BattleVoice); float empyrealCD = GetCooldownRemainingTime(EmpyrealArrow); float ragingCD = GetCooldownRemainingTime(RagingStrikes); float radiantCD = GetCooldownRemainingTime(RadiantFinale); - if (empyrealReady && ((!openerFinished && IsOnCooldown(RagingStrikes) || (!openerFinished && JustUsed(WanderersMinuet)) || (openerFinished && battleVoiceCD >= 3.5) || !IsEnabled(CustomComboPreset.BRD_Simple_Buffs)))) + if (ActionReady(EmpyrealArrow)) return EmpyrealArrow; if (LevelChecked(PitchPerfect) && songWanderer && - (gauge.Repertoire == 3 || (gauge.Repertoire == 2 && empyrealCD < 2)) && - ((!openerFinished && IsOnCooldown(RagingStrikes)) || (openerFinished && battleVoiceCD >= 3.5))) + (gauge.Repertoire == 3 || (gauge.Repertoire == 2 && empyrealCD < 2))) return OriginalHook(PitchPerfect); - if (sidewinderReady && ((!openerFinished && IsOnCooldown(RagingStrikes)) || (openerFinished && battleVoiceCD >= 3.5) || !IsEnabled(CustomComboPreset.BRD_Simple_Buffs))) + if (ActionReady(Sidewinder)) { - if (IsEnabled(CustomComboPreset.BRD_Simple_Pooling)) + if (IsEnabled(CustomComboPreset.BRD_Adv_Pooling)) { if (songWanderer) { @@ -789,11 +750,11 @@ protected override uint Invoke(uint actionID, uint lastComboMove, float comboTim } - if (LevelChecked(Bloodletter) && (empyrealCD > 1 || !LevelChecked(EmpyrealArrow)) && ((!openerFinished && IsOnCooldown(RagingStrikes)) || openerFinished)) + if (ActionReady(Bloodletter) && (empyrealCD > 1 || !LevelChecked(EmpyrealArrow))) { uint bloodletterCharges = GetRemainingCharges(Bloodletter); - if (IsEnabled(CustomComboPreset.BRD_Simple_Pooling) && LevelChecked(WanderersMinuet)) + if (IsEnabled(CustomComboPreset.BRD_Adv_Pooling) && LevelChecked(WanderersMinuet)) { if (songWanderer) { @@ -825,7 +786,7 @@ protected override uint Invoke(uint actionID, uint lastComboMove, float comboTim } } //Moved below weaves bc roobert says it is blocking his weaves from happening - if (HasEffect(Buffs.RadiantEncoreReady) && !JustUsed(RadiantFinale) && GetCooldownElapsed(BattleVoice) >= 4.2f) + if (HasEffect(Buffs.RadiantEncoreReady) && !JustUsed(RadiantFinale) && GetCooldownElapsed(BattleVoice) >= 4.2f && IsEnabled(CustomComboPreset.BRD_Adv_BuffsRadiant)) return OriginalHook(RadiantEncore); if (isEnemyHealthHigh) @@ -838,6 +799,9 @@ protected override uint Invoke(uint actionID, uint lastComboMove, float comboTim float windRemaining = GetDebuffRemainingTime(Debuffs.Windbite); float causticRemaining = GetDebuffRemainingTime(Debuffs.CausticBite); float stormRemaining = GetDebuffRemainingTime(Debuffs.Stormbite); + float ragingStrikesDuration = GetBuffRemainingTime(Buffs.RagingStrikes); + float radiantFinaleDuration = GetBuffRemainingTime(Buffs.RadiantFinale); + int ragingJawsRenewTime = PluginConfiguration.GetCustomIntValue(Config.BRD_RagingJawsRenewTime); DotRecast poisonRecast = delegate (int duration) { @@ -847,27 +811,31 @@ protected override uint Invoke(uint actionID, uint lastComboMove, float comboTim DotRecast windRecast = delegate (int duration) { return (windbite && windRemaining < duration) || (stormbite && stormRemaining < duration); - }; - - float ragingStrikesDuration = GetBuffRemainingTime(Buffs.RagingStrikes); - float radiantFinaleDuration = GetBuffRemainingTime(Buffs.RadiantFinale); - int ragingJawsRenewTime = PluginConfiguration.GetCustomIntValue(Config.BRD_RagingJawsRenewTime); - bool useIronJaws = (LevelChecked(IronJaws) && poisonRecast(4)) || - (LevelChecked(IronJaws) && windRecast(4)) || - ((LevelChecked(IronJaws) && IsEnabled(CustomComboPreset.BRD_Simple_RagingJaws) || - HasEffect(Buffs.RagingStrikes) && ragingStrikesDuration < ragingJawsRenewTime) && - HasEffect(Buffs.RadiantFinale) && radiantFinaleDuration < 4 && - poisonRecast(40) && windRecast(40)); + }; + + if (IsEnabled(CustomComboPreset.BRD_Adv_DoT)) + { + if (ActionReady(IronJaws) && IsEnabled(CustomComboPreset.BRD_Adv_RagingJaws) && HasEffect(Buffs.RagingStrikes) && + !WasLastAction(IronJaws) && ragingStrikesDuration < ragingJawsRenewTime && poisonRecast(40) && windRecast(40)) + { + openerFinished = true; + return IronJaws; + } + if (LevelChecked(Stormbite) && !stormbite) + return Stormbite; + if (LevelChecked(CausticBite) && !caustic) + return CausticBite; + if (LevelChecked(Windbite) && !windbite && !LevelChecked(Stormbite)) + return Windbite; + if (LevelChecked(VenomousBite) && !venomous && !LevelChecked(CausticBite)) + return VenomousBite; - if (!LevelChecked(Stormbite)) - { - if (useIronJaws) + if (ActionReady(IronJaws) && poisonRecast(4) && windRecast(4)) { openerFinished = true; return IronJaws; } - if (!LevelChecked(IronJaws)) { if (windbite && windRemaining < 4) @@ -881,36 +849,11 @@ protected override uint Invoke(uint actionID, uint lastComboMove, float comboTim openerFinished = true; return VenomousBite; } - } - - if (IsEnabled(CustomComboPreset.BRD_Simple_DoT)) - { - if (LevelChecked(Windbite) && !windbite) - return Windbite; - if (LevelChecked(VenomousBite) && !venomous) - return VenomousBite; - } - } - - else - { - if (useIronJaws) - { - openerFinished = true; - return IronJaws; - } - - if (IsEnabled(CustomComboPreset.BRD_Simple_DoT)) - { - if (LevelChecked(Stormbite) && !stormbite) - return Stormbite; - if (LevelChecked(CausticBite) && !caustic) - return CausticBite; - } + } } } - if (!IsEnabled(CustomComboPreset.BRD_RemoveApexArrow)) + if (IsEnabled(CustomComboPreset.BRD_ST_ApexArrow)) { if (LevelChecked(BlastArrow) && HasEffect(Buffs.BlastArrowReady)) return BlastArrow; @@ -933,7 +876,7 @@ protected override uint Invoke(uint actionID, uint lastComboMove, float comboTim if (HasEffect(Buffs.HawksEye) || HasEffect(Buffs.Barrage)) return OriginalHook(StraightShot); - if (HasEffect(Buffs.ResonantArrowReady)) + if (HasEffect(Buffs.ResonantArrowReady) && IsEnabled(CustomComboPreset.BRD_Adv_Buffs)) return ResonantArrow; } @@ -949,12 +892,9 @@ protected override uint Invoke(uint actionID, uint lastComboMove, float comboTim { if (actionID is Barrage) { - bool ragingReady = LevelChecked(RagingStrikes) && IsOffCooldown(RagingStrikes); - bool battleVoiceReady = LevelChecked(BattleVoice) && IsOffCooldown(BattleVoice); - - if (ragingReady) + if (ActionReady(RagingStrikes)) return RagingStrikes; - if (battleVoiceReady) + if (ActionReady(BattleVoice)) return BattleVoice; } @@ -971,22 +911,518 @@ protected override uint Invoke(uint actionID, uint lastComboMove, float comboTim { // Doesn't display the lowest cooldown song if they have been used out of order and are all on cooldown. BRDGauge? gauge = GetJobGauge(); - int songTimerInSeconds = gauge.SongTimer / 1000; - bool wanderersMinuetReady = LevelChecked(WanderersMinuet) && IsOffCooldown(WanderersMinuet); - bool magesBalladReady = LevelChecked(MagesBallad) && IsOffCooldown(MagesBallad); - bool armysPaeonReady = LevelChecked(ArmysPaeon) && IsOffCooldown(ArmysPaeon); + int songTimerInSeconds = gauge.SongTimer / 1000; - if (wanderersMinuetReady || (gauge.Song == Song.WANDERER && songTimerInSeconds > 11)) + if (ActionReady(WanderersMinuet) || (gauge.Song == Song.WANDERER && songTimerInSeconds > 11)) return WanderersMinuet; - if (magesBalladReady || (gauge.Song == Song.MAGE && songTimerInSeconds > 2)) + if (ActionReady(MagesBallad) || (gauge.Song == Song.MAGE && songTimerInSeconds > 2)) return MagesBallad; - if (armysPaeonReady || (gauge.Song == Song.ARMY && songTimerInSeconds > 2)) + if (ActionReady(ArmysPaeon) || (gauge.Song == Song.ARMY && songTimerInSeconds > 2)) return ArmysPaeon; } + + return actionID; + } + } + internal class BRD_AoE_SimpleMode : CustomCombo + { + internal static bool inOpener = false; + internal static bool openerFinished = false; + + protected internal override CustomComboPreset Preset { get; } = CustomComboPreset.BRD_AoE_AdvMode; + + protected override uint Invoke(uint actionID, uint lastComboMove, float comboTime, byte level) + { + if (actionID is Ladonsbite or QuickNock) + { + BRDGauge? gauge = GetJobGauge(); + bool canWeave = CanWeave(actionID); + bool canWeaveBuffs = CanWeave(actionID, 0.6); + bool canWeaveDelayed = CanDelayedWeave(actionID, 0.9); + int songTimerInSeconds = gauge.SongTimer / 1000; + bool songNone = gauge.Song == Song.NONE; + bool songWanderer = gauge.Song == Song.WANDERER; + bool songMage = gauge.Song == Song.MAGE; + bool songArmy = gauge.Song == Song.ARMY; + int targetHPThreshold = PluginConfiguration.GetCustomIntValue(Config.BRD_AoENoWasteHPPercentage); + bool isEnemyHealthHigh = GetTargetHPPercent() > 1; + + if (IsEnabled(Variant.VariantCure) && PlayerHealthPercentageHp() <= 50) + return Variant.VariantCure; + + if (IsEnabled(Variant.VariantRampart) && + IsOffCooldown(Variant.VariantRampart) && + canWeave) + return Variant.VariantRampart; + + if (canWeave) + { + // Limit optimisation to when you are high enough level to benefit from it. + if (LevelChecked(WanderersMinuet)) + { + if (canWeave) + { + if (songNone) + { + // Logic to determine first song + if (ActionReady(WanderersMinuet) && !(JustUsed(MagesBallad) || JustUsed(ArmysPaeon))) + return WanderersMinuet; + if (ActionReady(MagesBallad) && !(JustUsed(WanderersMinuet) || JustUsed(ArmysPaeon))) + return MagesBallad; + if (ActionReady(ArmysPaeon) && !(JustUsed(MagesBallad) || JustUsed(WanderersMinuet))) + return ArmysPaeon; + } + + if (songWanderer) + { + if (songTimerInSeconds <= 3 && gauge.Repertoire > 0) // Spend any repertoire before switching to next song + return OriginalHook(PitchPerfect); + if (songTimerInSeconds <= 3 && ActionReady(MagesBallad)) // Move to Mage's Ballad if <= 3 seconds left on song + return MagesBallad; + } + + if (songMage) + { + + // Move to Army's Paeon if < 3 seconds left on song + if (songTimerInSeconds <= 3 && ActionReady(ArmysPaeon)) + { + // Special case for Empyreal Arrow: it must be cast before you change to it to avoid drift! + if (ActionReady(EmpyrealArrow)) + return EmpyrealArrow; + return ArmysPaeon; + } + } + } + + if (songArmy && canWeaveDelayed) + { + // Move to Wanderer's Minuet if <= 12 seconds left on song or WM off CD and have 4 repertoires of AP + if (songTimerInSeconds <= 12 || (ActionReady(WanderersMinuet) && gauge.Repertoire == 4)) + return WanderersMinuet; + } + } + else if (songTimerInSeconds <= 3 && canWeave) + { + if (ActionReady(MagesBallad)) + return MagesBallad; + if (ActionReady(ArmysPaeon)) + return ArmysPaeon; + } + } + + if ((!songNone || !LevelChecked(MagesBallad)) && isEnemyHealthHigh) + { + float battleVoiceCD = GetCooldownRemainingTime(BattleVoice); + float ragingCD = GetCooldownRemainingTime(RagingStrikes); + + if (canWeaveDelayed && ActionReady(RadiantFinale) && + (Array.TrueForAll(gauge.Coda, SongIsNotNone) || Array.Exists(gauge.Coda, SongIsWandererMinuet)) + && (battleVoiceCD < 3 || ActionReady(BattleVoice)) && (ragingCD < 3 || ActionReady(RagingStrikes))) + return RadiantFinale; + + if (canWeaveBuffs && ActionReady(BattleVoice) && (HasEffect(Buffs.RadiantFinale) || !LevelChecked(RadiantFinale))) + return BattleVoice; + + if (canWeaveBuffs && ActionReady(RagingStrikes) && (HasEffect(Buffs.RadiantFinale) || !LevelChecked(RadiantFinale))) + return RagingStrikes; + + if (canWeaveBuffs && ActionReady(Barrage) && HasEffect(Buffs.RagingStrikes)) + { + if (LevelChecked(RadiantFinale) && HasEffect(Buffs.RadiantFinale)) + return Barrage; + else if (LevelChecked(BattleVoice) && HasEffect(Buffs.BattleVoice)) + return Barrage; + else if (!LevelChecked(BattleVoice) && HasEffect(Buffs.RagingStrikes)) + return Barrage; + + } + } + + if (canWeave) + { + float battleVoiceCD = GetCooldownRemainingTime(BattleVoice); + float empyrealCD = GetCooldownRemainingTime(EmpyrealArrow); + float ragingCD = GetCooldownRemainingTime(RagingStrikes); + float radiantCD = GetCooldownRemainingTime(RadiantFinale); + + if (ActionReady(EmpyrealArrow)) + return EmpyrealArrow; + + if (LevelChecked(PitchPerfect) && songWanderer && + (gauge.Repertoire == 3 || (gauge.Repertoire == 2 && empyrealCD < 2))) + return OriginalHook(PitchPerfect); + + if (ActionReady(Sidewinder)) + { + if (songWanderer) + { + if ((HasEffect(Buffs.RagingStrikes) || ragingCD > 10) && + (HasEffect(Buffs.BattleVoice) || battleVoiceCD > 10) && + (HasEffect(Buffs.RadiantFinale) || radiantCD > 10 || + !LevelChecked(RadiantFinale))) + return Sidewinder; + + } + else return Sidewinder; + } + + if (LevelChecked(RainOfDeath) && (empyrealCD > 1 || !LevelChecked(EmpyrealArrow))) + { + uint rainOfDeathCharges = LevelChecked(RainOfDeath) ? GetRemainingCharges(RainOfDeath) : 0; + + if (LevelChecked(WanderersMinuet)) + { + if (songWanderer) + { + if (((HasEffect(Buffs.RagingStrikes) || ragingCD > 10) && + (HasEffect(Buffs.BattleVoice) || battleVoiceCD > 10 || + !LevelChecked(BattleVoice)) && + (HasEffect(Buffs.RadiantFinale) || radiantCD > 10 || + !LevelChecked(RadiantFinale)) && + rainOfDeathCharges > 0) || rainOfDeathCharges > 2) + return OriginalHook(RainOfDeath); + } + + if (songArmy && (rainOfDeathCharges == 3 || ((gauge.SongTimer / 1000) > 30 && rainOfDeathCharges > 0))) + return OriginalHook(RainOfDeath); + if (songMage && rainOfDeathCharges > 0) + return OriginalHook(RainOfDeath); + if (songNone && rainOfDeathCharges == 3) + return OriginalHook(RainOfDeath); + } + else if (rainOfDeathCharges > 0) + return OriginalHook(RainOfDeath); + } + //Moved Below ogcds as it was preventing them from happening. + if (HasEffect(Buffs.RadiantEncoreReady) && !JustUsed(RadiantFinale) && GetCooldownElapsed(BattleVoice) >= 4.2f) + return OriginalHook(RadiantEncore); + + // healing - please move if not appropriate priority + + if (PlayerHealthPercentageHp() <= 40 && ActionReady(All.SecondWind)) + return All.SecondWind; + + } + + bool wideVolleyReady = LevelChecked(WideVolley) && HasEffect(Buffs.HawksEye); + bool blastArrowReady = LevelChecked(BlastArrow) && HasEffect(Buffs.BlastArrowReady); + bool resonantArrowReady = LevelChecked(ResonantArrow) && HasEffect(Buffs.ResonantArrowReady); + + if (wideVolleyReady) + return OriginalHook(WideVolley); + if (LevelChecked(ApexArrow) && gauge.SoulVoice == 100) + return ApexArrow; + if (blastArrowReady) + return BlastArrow; + if (resonantArrowReady) + return ResonantArrow; + if (HasEffect(Buffs.RadiantEncoreReady)) + return OriginalHook(RadiantEncore); + + } + + return actionID; + } + } + internal class BRD_ST_SimpleMode : CustomCombo + { + protected internal override CustomComboPreset Preset { get; } = CustomComboPreset.BRD_ST_AdvMode; + internal static bool inOpener = false; + internal static bool openerFinished = false; + internal static byte step = 0; + internal static byte subStep = 0; + internal static bool usedStraightShotReady = false; + internal static bool usedPitchPerfect = false; + internal delegate bool DotRecast(int value); + + protected override uint Invoke(uint actionID, uint lastComboMove, float comboTime, byte level) + { + if (actionID is HeavyShot or BurstShot) + { + BRDGauge? gauge = GetJobGauge(); + bool canWeave = CanWeave(actionID); + bool canWeaveBuffs = CanWeave(actionID, 0.6); + bool canWeaveDelayed = CanDelayedWeave(actionID, 0.9); + bool songNone = gauge.Song == Song.NONE; + bool songWanderer = gauge.Song == Song.WANDERER; + bool songMage = gauge.Song == Song.MAGE; + bool songArmy = gauge.Song == Song.ARMY; + bool canInterrupt = CanInterruptEnemy() && IsOffCooldown(All.HeadGraze); + bool isEnemyHealthHigh = GetTargetHPPercent() > 1; + + if (!InCombat() && (inOpener || openerFinished)) + { + openerFinished = false; + } + + if (canInterrupt) + return All.HeadGraze; + + if (IsEnabled(Variant.VariantCure) && PlayerHealthPercentageHp() <= 50) + return Variant.VariantCure; + + if (IsEnabled(Variant.VariantRampart) && + IsOffCooldown(Variant.VariantRampart) && + canWeave) + return Variant.VariantRampart; + + if (isEnemyHealthHigh) + { + int songTimerInSeconds = gauge.SongTimer / 1000; + + // Limit optimisation to when you are high enough level to benefit from it. + if (LevelChecked(WanderersMinuet)) + { + // 43s of Wanderer's Minute, ~36s of Mage's Ballad, and ~43s of Army's Paeon + bool minuetReady = IsOffCooldown(WanderersMinuet); + bool balladReady = IsOffCooldown(MagesBallad); + bool paeonReady = IsOffCooldown(ArmysPaeon); + + if (ActionReady(EmpyrealArrow) && JustUsed(WanderersMinuet)) + return EmpyrealArrow; + + if (canWeave) + { + if (songNone) + { + // Logic to determine first song + if (minuetReady && !(JustUsed(MagesBallad) || JustUsed(ArmysPaeon))) + return WanderersMinuet; + if (balladReady && !(JustUsed(WanderersMinuet) || JustUsed(ArmysPaeon))) + return MagesBallad; + if (paeonReady && !(JustUsed(MagesBallad) || JustUsed(WanderersMinuet))) + return ArmysPaeon; + } + + if (songWanderer) + { + if (songTimerInSeconds <= 3 && gauge.Repertoire > 0) // Spend any repertoire before switching to next song + return OriginalHook(PitchPerfect); + if (songTimerInSeconds <= 3 && balladReady) // Move to Mage's Ballad if <= 3 seconds left on song + return MagesBallad; + } + + if (songMage) + { + + // Move to Army's Paeon if <= 3 seconds left on song + if (songTimerInSeconds <= 3 && paeonReady) + { + // Special case for Empyreal Arrow: it must be cast before you change to it to avoid drift! + if (ActionReady(EmpyrealArrow)) + return EmpyrealArrow; + return ArmysPaeon; + } + } + } + + if (songArmy && canWeaveDelayed) + { + // Move to Wanderer's Minuet if <= 12 seconds left on song or WM off CD and have 4 repertoires of AP + if (songTimerInSeconds <= 12 || (minuetReady && gauge.Repertoire == 4)) + return WanderersMinuet; + } + } + else if (songTimerInSeconds <= 3 && canWeave) + { + bool balladReady = LevelChecked(MagesBallad) && IsOffCooldown(MagesBallad); + bool paeonReady = LevelChecked(ArmysPaeon) && IsOffCooldown(ArmysPaeon); + + if (balladReady) + return MagesBallad; + if (paeonReady) + return ArmysPaeon; + } + } + + if ((!songNone || !LevelChecked(MagesBallad)) && isEnemyHealthHigh) + { + bool radiantReady = LevelChecked(RadiantFinale) && IsOffCooldown(RadiantFinale); + bool ragingReady = LevelChecked(RagingStrikes) && IsOffCooldown(RagingStrikes); + bool battleVoiceReady = LevelChecked(BattleVoice) && IsOffCooldown(BattleVoice); + bool barrageReady = LevelChecked(Barrage) && IsOffCooldown(Barrage); + float battleVoiceCD = GetCooldownRemainingTime(BattleVoice); + float ragingCD = GetCooldownRemainingTime(RagingStrikes); + + if (canWeaveDelayed && radiantReady && + (Array.TrueForAll(gauge.Coda, SongIsNotNone) || Array.Exists(gauge.Coda, SongIsWandererMinuet)) + && (battleVoiceCD < 3 || ActionReady(BattleVoice)) && (ragingCD < 3 || ActionReady(RagingStrikes))) + return RadiantFinale; + + if (canWeaveBuffs && battleVoiceReady && (HasEffect(Buffs.RadiantFinale) || !LevelChecked(RadiantFinale))) + return BattleVoice; + + if (canWeaveBuffs && ragingReady && (HasEffect(Buffs.RadiantFinale) || !LevelChecked(RadiantFinale))) + return RagingStrikes; + + //removed requirement to not have hawks eye, it is better to maybe lose 60 potency than allow it to drift a 1000 potency gain out of the window + if (canWeaveBuffs && barrageReady && HasEffect(Buffs.RagingStrikes)) + { + if (LevelChecked(RadiantFinale) && HasEffect(Buffs.RadiantFinale)) + return Barrage; + else if (LevelChecked(BattleVoice) && HasEffect(Buffs.BattleVoice)) + return Barrage; + else if (!LevelChecked(BattleVoice) && HasEffect(Buffs.RagingStrikes)) + return Barrage; + } + } + + if (canWeave) + { + float battleVoiceCD = GetCooldownRemainingTime(BattleVoice); + float empyrealCD = GetCooldownRemainingTime(EmpyrealArrow); + float ragingCD = GetCooldownRemainingTime(RagingStrikes); + float radiantCD = GetCooldownRemainingTime(RadiantFinale); + + if (ActionReady(EmpyrealArrow)) + return EmpyrealArrow; + + if (LevelChecked(PitchPerfect) && songWanderer && + (gauge.Repertoire == 3 || (gauge.Repertoire == 2 && empyrealCD < 2))) + return OriginalHook(PitchPerfect); + + if (ActionReady(Sidewinder)) + { + if (songWanderer) + { + if ((HasEffect(Buffs.RagingStrikes) || ragingCD > 10) && + (HasEffect(Buffs.BattleVoice) || battleVoiceCD > 10) && + (HasEffect(Buffs.RadiantFinale) || radiantCD > 10 || + !LevelChecked(RadiantFinale))) + return Sidewinder; + } + else return Sidewinder; + } + + + if (ActionReady(Bloodletter) && (empyrealCD > 1 || !LevelChecked(EmpyrealArrow))) + { + uint bloodletterCharges = GetRemainingCharges(Bloodletter); + + if (LevelChecked(WanderersMinuet)) + { + if (songWanderer) + { + if (((HasEffect(Buffs.RagingStrikes) || ragingCD > 10) && + (HasEffect(Buffs.BattleVoice) || battleVoiceCD > 10 || + !LevelChecked(BattleVoice)) && + (HasEffect(Buffs.RadiantFinale) || radiantCD > 10 || + !LevelChecked(RadiantFinale)) && + bloodletterCharges > 0) || bloodletterCharges > 2) + return OriginalHook(Bloodletter); + } + + if (songArmy && (bloodletterCharges == 3 || ((gauge.SongTimer / 1000) > 30 && bloodletterCharges > 0))) + return OriginalHook(Bloodletter); + if (songMage && bloodletterCharges > 0) + return OriginalHook(Bloodletter); + if (songNone && bloodletterCharges == 3) + return OriginalHook(Bloodletter); + } + else if (bloodletterCharges > 0) + return OriginalHook(Bloodletter); + } + + // healing - please move if not appropriate priority + + if (PlayerHealthPercentageHp() <= 40 && ActionReady(All.SecondWind)) + return All.SecondWind; + + } + + //Moved below weaves bc roobert says it is blocking his weaves from happening + if (HasEffect(Buffs.RadiantEncoreReady) && !JustUsed(RadiantFinale) && GetCooldownElapsed(BattleVoice) >= 4.2f) + return OriginalHook(RadiantEncore); + + if (isEnemyHealthHigh) + { + bool venomous = TargetHasEffect(Debuffs.VenomousBite); + bool windbite = TargetHasEffect(Debuffs.Windbite); + bool caustic = TargetHasEffect(Debuffs.CausticBite); + bool stormbite = TargetHasEffect(Debuffs.Stormbite); + float venomRemaining = GetDebuffRemainingTime(Debuffs.VenomousBite); + float windRemaining = GetDebuffRemainingTime(Debuffs.Windbite); + float causticRemaining = GetDebuffRemainingTime(Debuffs.CausticBite); + float stormRemaining = GetDebuffRemainingTime(Debuffs.Stormbite); + float ragingStrikesDuration = GetBuffRemainingTime(Buffs.RagingStrikes); + float radiantFinaleDuration = GetBuffRemainingTime(Buffs.RadiantFinale); + int ragingJawsRenewTime = 5; + + DotRecast poisonRecast = delegate (int duration) + { + return (venomous && venomRemaining < duration) || (caustic && causticRemaining < duration); + }; + + DotRecast windRecast = delegate (int duration) + { + return (windbite && windRemaining < duration) || (stormbite && stormRemaining < duration); + }; + + if (ActionReady(IronJaws) && HasEffect(Buffs.RagingStrikes) && + !WasLastAction(IronJaws) && ragingStrikesDuration < ragingJawsRenewTime && poisonRecast(40) && windRecast(40)) + { + openerFinished = true; + return IronJaws; + } + + if (LevelChecked(Stormbite) && !stormbite) + return Stormbite; + if (LevelChecked(CausticBite) && !caustic) + return CausticBite; + if (LevelChecked(Windbite) && !windbite && !LevelChecked(Stormbite)) + return Windbite; + if (LevelChecked(VenomousBite) && !venomous && !LevelChecked(CausticBite)) + return VenomousBite; + + if (ActionReady(IronJaws) && poisonRecast(4) && windRecast(4)) + { + openerFinished = true; + return IronJaws; + } + if (!LevelChecked(IronJaws)) + { + if (windbite && windRemaining < 4) + { + openerFinished = true; + return Windbite; + } + + if (venomous && venomRemaining < 4) + { + openerFinished = true; + return VenomousBite; + } + } + + } + + if (LevelChecked(BlastArrow) && HasEffect(Buffs.BlastArrowReady)) + return BlastArrow; + + if (LevelChecked(ApexArrow)) + { + int songTimerInSeconds = gauge.SongTimer / 1000; + + if (songMage && gauge.SoulVoice == 100) + return ApexArrow; + if (songMage && gauge.SoulVoice >= 80 && + songTimerInSeconds > 18 && songTimerInSeconds < 22) + return ApexArrow; + if (songWanderer && HasEffect(Buffs.RagingStrikes) && HasEffect(Buffs.BattleVoice) && + (HasEffect(Buffs.RadiantFinale) || !LevelChecked(RadiantFinale)) && gauge.SoulVoice >= 80) + return ApexArrow; + } + if (HasEffect(Buffs.HawksEye) || HasEffect(Buffs.Barrage)) + return OriginalHook(StraightShot); + + if (HasEffect(Buffs.ResonantArrowReady)) + return ResonantArrow; + } return actionID; } } diff --git a/XIVSlothComboX/Window/Functions/UserConfig.cs b/XIVSlothComboX/Window/Functions/UserConfig.cs index a6deebd22..99566a4f8 100644 --- a/XIVSlothComboX/Window/Functions/UserConfig.cs +++ b/XIVSlothComboX/Window/Functions/UserConfig.cs @@ -1788,13 +1788,10 @@ internal static void Draw(CustomComboPreset preset, bool enabled) #region BARD - if (preset == CustomComboPreset.BRD_Simple_RagingJaws) + if (preset == CustomComboPreset.BRD_Adv_RagingJaws) UserConfig.DrawSliderInt(3, 5, BRD.Config.BRD_RagingJawsRenewTime, "持续时间 (单位:秒)"); - if (preset == CustomComboPreset.BRD_Simple_NoWaste) - UserConfig.DrawSliderInt(1, 10, BRD.Config.BRD_NoWasteHPPercentage, "目标血量百分比"); - - if (preset == CustomComboPreset.BRD_AoE_Simple_NoWaste) + if (preset == CustomComboPreset.BRD_AoE_Adv_NoWaste) UserConfig.DrawSliderInt(1, 10, BRD.Config.BRD_AoENoWasteHPPercentage, "Remaining target HP percentage");