diff --git a/XIVComboExpanded/Combos/VPR.cs b/XIVComboExpanded/Combos/VPR.cs index fa6d5e0f..ed78a1c4 100644 --- a/XIVComboExpanded/Combos/VPR.cs +++ b/XIVComboExpanded/Combos/VPR.cs @@ -1,4 +1,5 @@ -using Dalamud.Game.ClientState.JobGauge.Enums; +using System.Collections; +using Dalamud.Game.ClientState.JobGauge.Enums; using Dalamud.Game.ClientState.JobGauge.Types; namespace XIVComboExpandedPlugin.Combos; @@ -211,6 +212,60 @@ protected override uint Invoke(uint actionID, uint lastComboMove, float comboTim if (IsEnabled(CustomComboPreset.ViperAutoSteelReavingFeature) && OriginalHook(VPR.SteelFangs) == VPR.SteelFangs) return HasEffect(VPR.Buffs.HonedReavers) ? VPR.ReavingFangs : VPR.SteelFangs; + + if (IsEnabled(CustomComboPreset.ViperPvPMainComboFeature)) + { + // Switch case here for optimization, rather than calling OriginalHook in a lot of places. + switch (OriginalHook(VPR.SteelFangs)) + { + // Combo step 1, detect presence of buffs, returned buffed Reavers or SteelFangs + case VPR.SteelFangs: + return HasEffect(VPR.Buffs.HonedReavers) ? VPR.ReavingFangs : VPR.SteelFangs; + + // Combo step 2, prioritize whichever buff we don't have. Starts with Swiftscaled since that speeds up the rotation significantly + case VPR.HuntersSting: + if (HasEffect(VPR.Buffs.FlanksbaneVenom) || + HasEffect(VPR.Buffs.FlankstungVenom)) + return VPR.HuntersSting; + if (HasEffect(VPR.Buffs.HindsbaneVenom) || + HasEffect(VPR.Buffs.HindstungVenom)) + return VPR.SwiftskinsSting; + + // No buff case + if (IsEnabled(CustomComboPreset.ViperPvPMainComboStartFlankstingFeature) || + IsEnabled(CustomComboPreset.ViperPvPMainComboStartFlanksbaneFeature)) + return VPR.HuntersSting; + return VPR.SwiftskinsSting; + + // Combo step 3, flank. Use whichever buff we have, or default to Flanksbane if we're here and buff has fallen off. + case VPR.FlankstingStrike: + if (HasEffect(VPR.Buffs.FlanksbaneVenom)) + return VPR.FlanksbaneFang; + if (HasEffect(VPR.Buffs.FlankstungVenom)) + return VPR.FlankstingStrike; + + // No buff case + if (IsEnabled(CustomComboPreset.ViperPvPMainComboStartFlanksbaneFeature)) + return VPR.FlanksbaneFang; + return VPR.FlankstingStrike; + + // Combo step 3, use whichever buff we have, or default to start hindsbane unless otherwise specified + case VPR.HindstingStrike: + if (HasEffect(VPR.Buffs.HindsbaneVenom)) + return VPR.HindsbaneFang; + if (HasEffect(VPR.Buffs.HindstungVenom)) + return VPR.HindstingStrike; + + // No buff case + if (IsEnabled(CustomComboPreset.ViperPvPMainComboStartHindsbaneFeature)) + return VPR.HindsbaneFang; + return VPR.HindstingStrike; + + // Default return of actionID + default: + return actionID; + } + } } return actionID; @@ -300,6 +355,36 @@ protected override uint Invoke(uint actionID, uint lastComboMove, float comboTim if (IsEnabled(CustomComboPreset.ViperAutoSteelReavingFeature) && OriginalHook(VPR.SteelMaw) == VPR.SteelMaw) return HasEffect(VPR.Buffs.HonedReavers) ? VPR.ReavingMaw : VPR.SteelMaw; + + if (IsEnabled(CustomComboPreset.ViperPvPAoEFeature)) + { + switch (OriginalHook(VPR.SteelMaw)) + { + case VPR.SteelMaw: + return HasEffect(VPR.Buffs.HonedReavers) ? VPR.ReavingMaw : VPR.SteelMaw; + + case VPR.HuntersBite: + var swift = FindEffect(VPR.Buffs.Swiftscaled); + var instinct = FindEffect(VPR.Buffs.HuntersInstinct); + if (swift is null || swift?.RemainingTime <= instinct?.RemainingTime) // We'd always want to prioritize swift since it speeds up the rotation + return VPR.SwiftskinsBite; + + return VPR.HuntersBite; + + case VPR.JaggedMaw: + if (HasEffect(VPR.Buffs.GrimskinsVenom)) + return VPR.BloodiedMaw; + if (HasEffect(VPR.Buffs.GrimhuntersVenom)) + return VPR.JaggedMaw; + + if (IsEnabled(CustomComboPreset.ViperPvPMainComboAoEStartBloodiedFeature)) + return VPR.BloodiedMaw; + return VPR.JaggedMaw; + + default: + return actionID; + } + } } return actionID; @@ -388,6 +473,8 @@ protected override uint Invoke(uint actionID, uint lastComboMove, float comboTim { if (actionID == VPR.UncoiledFury) { + var gauge = GetJobGauge(); + if (IsEnabled(CustomComboPreset.ViperUncoiledFollowupFeature)) { if (OriginalHook(VPR.Twinfang) == VPR.UncoiledTwinfang && HasEffect(VPR.Buffs.PoisedForTwinfang)) @@ -399,10 +486,14 @@ protected override uint Invoke(uint actionID, uint lastComboMove, float comboTim if (IsEnabled(CustomComboPreset.ViperFuryAndIreFeature) && level >= VPR.Levels.SerpentsIre) { - var gauge = GetJobGauge(); if (gauge.RattlingCoilStacks == 0) return VPR.SerpentsIre; } + + if (IsEnabled(CustomComboPreset.ViperSnapCoilFeature) && gauge.RattlingCoilStacks == 0) + { + return VPR.WrithingSnap; + } } return actionID; @@ -516,10 +607,10 @@ protected override uint Invoke(uint actionID, uint lastComboMove, float comboTim if (level >= VPR.Levels.Legacies) { var original = OriginalHook(VPR.SerpentsTail); - if (original == VPR.FirstLegacy || - original == VPR.SecondLegacy || - original == VPR.ThirdLegacy || - original == VPR.FourthLegacy) + if (original is VPR.FirstLegacy or + VPR.SecondLegacy or + VPR.ThirdLegacy or + VPR.FourthLegacy) return original; } @@ -537,6 +628,11 @@ protected override uint Invoke(uint actionID, uint lastComboMove, float comboTim } } + if (IsEnabled(CustomComboPreset.ViperReawakenIreFeature) && IsCooldownUsable(VPR.SerpentsIre)) + { + return VPR.SerpentsIre; + } + return actionID; } } @@ -580,146 +676,4 @@ protected override uint Invoke(uint actionID, uint lastComboMove, float comboTim return actionID; } -} - -//internal class PvPMainComboFeature : CustomCombo -//{ -// protected internal override CustomComboPreset Preset { get; } = CustomComboPreset.ViperPvPMainComboFeature; - -// protected override uint Invoke(uint actionID, uint lastComboMove, float comboTime, byte level) -// { -// if (actionID == VPR.SteelFangs) -// { -// if (HasEffect(VPR.Buffs.Reawakened)) -// { -// var gauge = GetJobGauge(); -// var maxtribute = 4; -// if (level >= VPR.Levels.Ouroboros) -// maxtribute = 5; -// if (gauge.AnguineTribute == maxtribute) -// return VPR.FirstGeneration; -// if (gauge.AnguineTribute == maxtribute - 1) -// return VPR.SecondGeneration; -// if (gauge.AnguineTribute == maxtribute - 2) -// return VPR.ThirdGeneration; -// if (gauge.AnguineTribute == maxtribute - 3) -// return VPR.FourthGeneration; -// } - -// // First step, decide whether or not we need to apply debuff -// if (OriginalHook(VPR.SteelFangs) == VPR.SteelFangs) -// { -// var noxious = FindTargetEffect(VPR.Debuffs.NoxiousGash); -// if (level >= VPR.Levels.ReavingFangs && (noxious is null || noxious?.RemainingTime < 12)) // 12s hopefully means we won't miss anything on a Reawaken window -// return VPR.ReavingFangs; - -// return VPR.SteelFangs; -// } - -// // Second step, if we have a third step buff use that combo, otherwise use from default combo -// if (OriginalHook(VPR.SteelFangs) == VPR.HuntersSting) -// { -// if (HasEffect(VPR.Buffs.HindsbaneVenom) || HasEffect(VPR.Buffs.HindstungVenom)) -// return VPR.SwiftskinsSting; -// if (HasEffect(VPR.Buffs.FlanksbaneVenom) || HasEffect(VPR.Buffs.FlankstungVenom)) -// return VPR.HuntersSting; - -// if (IsEnabled(CustomComboPreset.ViperPvPMainComboStartFlankstingFeature) || IsEnabled(CustomComboPreset.ViperPvPMainComboStartFlanksbaneFeature)) -// return VPR.HuntersSting; - -// return VPR.SwiftskinsSting; -// } - -// // Third step, if we are here, prefer to use what we have buffs for, otherwise use defaults -// if (OriginalHook(VPR.SteelFangs) == VPR.FlankstingStrike || OriginalHook(VPR.SteelFangs) == VPR.HindstingStrike) -// { -// if (HasEffect(VPR.Buffs.HindsbaneVenom)) -// return VPR.HindsbaneFang; -// if (HasEffect(VPR.Buffs.HindstungVenom)) -// return VPR.HindstingStrike; -// if (HasEffect(VPR.Buffs.FlanksbaneVenom)) -// return VPR.FlanksbaneFang; -// if (HasEffect(VPR.Buffs.FlankstungVenom)) -// return VPR.FlankstingStrike; - -// if (IsEnabled(CustomComboPreset.ViperPvPMainComboStartHindstingFeature)) -// return VPR.HindstingStrike; -// if (IsEnabled(CustomComboPreset.ViperPvPMainComboStartFlanksbaneFeature)) -// return VPR.FlanksbaneFang; -// if (IsEnabled(CustomComboPreset.ViperPvPMainComboStartFlankstingFeature)) -// return VPR.FlankstingStrike; -// return VPR.HindsbaneFang; -// } -// } - -// return actionID; -// } -//} - -//internal class PvPMainComboAoEFeature : CustomCombo -//{ -// protected internal override CustomComboPreset Preset { get; } = CustomComboPreset.ViperPvPMainComboAoEFeature; - -// protected override uint Invoke(uint actionID, uint lastComboMove, float comboTime, byte level) -// { -// if (actionID == VPR.SteelMaw) -// { -// if (HasEffect(VPR.Buffs.Reawakened)) -// { -// var gauge = GetJobGauge(); -// var maxtribute = 4; -// if (level >= VPR.Levels.Ouroboros) -// maxtribute = 5; -// if (gauge.AnguineTribute == maxtribute) -// return VPR.FirstGeneration; -// if (gauge.AnguineTribute == maxtribute - 1) -// return VPR.SecondGeneration; -// if (gauge.AnguineTribute == maxtribute - 2) -// return VPR.ThirdGeneration; -// if (gauge.AnguineTribute == maxtribute - 3) -// return VPR.FourthGeneration; -// } - -// // First step, decide whether or not we need to apply debuff -// if (OriginalHook(VPR.SteelMaw) == VPR.SteelMaw) -// { -// var noxious = FindTargetEffect(VPR.Debuffs.NoxiousGash); // TODO: Would be useful to handle the case with no target -// if (level >= VPR.Levels.ReavingMaw && (noxious is null || noxious?.RemainingTime < 12)) // 12s hopefully means we won't miss anything on a Reawaken window -// return VPR.ReavingMaw; - -// return VPR.SteelMaw; -// } - -// // Second step, since there's no requirement here, we can just use whichever has the shorter buff timer -// if (OriginalHook(VPR.SteelMaw) == VPR.HuntersBite) -// { -// var swift = FindEffect(VPR.Buffs.Swiftscaled); -// var instinct = FindEffect(VPR.Buffs.HuntersInstinct); -// if (swift is null) // I think we'd always want to prioritize swift since it speeds up the rotation -// return VPR.SwiftskinsBite; -// if (instinct is null) -// return VPR.HuntersBite; -// if (swift?.RemainingTime <= instinct?.RemainingTime) -// return VPR.SwiftskinsBite; - -// return VPR.HuntersBite; -// } - -// if (OriginalHook(VPR.SteelMaw) == VPR.JaggedMaw) -// { -// if (HasEffect(VPR.Buffs.GrimhuntersVenom)) -// return VPR.JaggedMaw; -// if (HasEffect(VPR.Buffs.GrimskinsVenom)) -// return VPR.BloodiedMaw; - -// if (IsEnabled(CustomComboPreset.ViperPvPMainComboAoEStartBloodiedFeature)) -// return VPR.BloodiedMaw; - -// return VPR.JaggedMaw; -// } -// } - -// return actionID; -// } -//} - +} \ No newline at end of file diff --git a/XIVComboExpanded/CustomComboPreset.cs b/XIVComboExpanded/CustomComboPreset.cs index 441f525b..fb8f380d 100644 --- a/XIVComboExpanded/CustomComboPreset.cs +++ b/XIVComboExpanded/CustomComboPreset.cs @@ -2306,6 +2306,12 @@ public enum CustomComboPreset [CustomComboInfo("All-in-one Reawaken Feature", "Replace Reawaken with the Generation skills and their respective Legacies in order.", VPR.JobID)] ViperReawakenAIOFeature = 4123, + [SectionCombo("Reawaken")] + [IconsCombo([VPR.Reawaken, UTL.ArrowLeft, VPR.SerpentsIre])] + [AccessibilityCustomCombo] + [CustomComboInfo("Serpent's Ire on Reawaken", "Replace Reawaken with Serpent's Ire when it's up.", VPR.JobID)] + ViperReawakenIreFeature = 4127, + [SectionCombo("Uncoiled Fury")] [IconsCombo([VPR.UncoiledFury, UTL.ArrowLeft, VPR.UncoiledTwinfang, VPR.UncoiledTwinblood])] [ExpandedCustomCombo] @@ -2318,6 +2324,12 @@ public enum CustomComboPreset [CustomComboInfo("Fury And Ire", "Replace Uncoiled Fury with Serpent's Ire when out of Rattling Coil stacks.\n\nNOTE: This is strongly discouraged. Serpent's Ire is not just a Rattling Coil generator, it is also Viper's primary 2-minute cooldown, as it also enables a free Reawaken. It should be aligned with party buffs, not held and used when you happen to be out of Rattling Coil charges.", VPR.JobID)] ViperFuryAndIreFeature = 4108, + [SectionCombo("Uncoiled Fury")] + [IconsCombo([VPR.UncoiledFury, UTL.ArrowLeft, VPR.WrithingSnap])] + [AccessibilityCustomCombo] + [CustomComboInfo("Uncoiled Fury to Writhing Snap", "Replace Uncoiled Fury with Writhing Snap when you are out of Rattling Coil charges.", VPR.JobID)] + ViperSnapCoilFeature = 4125, + [SectionCombo("oGCDs")] [IconsCombo([VPR.SerpentsTail, UTL.ArrowLeft, VPR.Twinfang, VPR.Twinblood, UTL.Idea])] [AccessibilityCustomCombo] @@ -2332,38 +2344,45 @@ public enum CustomComboPreset [CustomComboInfo("Merge Serpent's Tail onto Twinfang/Twinblood Feature", "Merge all Serpent's Tail abilities onto Twinfang/Twinblood.", VPR.JobID)] ViperMergeTwinsSerpentFeature = 4112, - // [SecretCustomCombo] - // [ConflictingCombos(ViperSteelTailFeature)] - // [CustomComboInfo("Viper PvP Style Main Combo", "Condenses the main combo to a single button, like PvP.\nThe combo detects buffs and debuffs to prioritize skills.\nThe default combo ender is Hindsbane Fang, configurable below.", VPR.JobID)] - // ViperPvPMainComboFeature = 4113, - - // [SecretCustomCombo] - // [ConflictingCombos(ViperPvPMainComboStartFlankstingFeature, ViperPvPMainComboStartHindstingFeature)] - // [ParentCombo(ViperPvPMainComboFeature)] - // [CustomComboInfo("PvP Combo Start Flanksbane Fang", "With no buffs, end first combo with Flanksbane Fang.", VPR.JobID)] - // ViperPvPMainComboStartFlanksbaneFeature = 4114, - - // [SecretCustomCombo] - // [ConflictingCombos(ViperPvPMainComboStartFlanksbaneFeature, ViperPvPMainComboStartHindstingFeature)] - // [ParentCombo(ViperPvPMainComboFeature)] - // [CustomComboInfo("PvP Combo Start Flanksting Strike", "With no buffs, end first combo with Flanksting Strike.", VPR.JobID)] - // ViperPvPMainComboStartFlankstingFeature = 4115, - - // [SecretCustomCombo] - // [ConflictingCombos(ViperPvPMainComboStartFlanksbaneFeature, ViperPvPMainComboStartFlankstingFeature)] - // [ParentCombo(ViperPvPMainComboFeature)] - // [CustomComboInfo("PvP Combo Start Hindsting Strike", "With no buffs, end first combo with Hindsting Strike.", VPR.JobID)] - // ViperPvPMainComboStartHindstingFeature = 4116, - - // [SecretCustomCombo] - // [ConflictingCombos(ViperSteelTailAoEFeature)] - // [CustomComboInfo("Viper PvP Style AoE Combo", "Condenses the main combo to a single button, like PvP.\nThe combo can only detect debuffs on the current target.\nStarts with Jagged Maw by default, configurable below.", VPR.JobID)] - // ViperPvPMainComboAoEFeature = 4117, - - // [SecretCustomCombo] - // [ParentCombo(ViperPvPMainComboAoEFeature)] - // [CustomComboInfo("PvP AoE Combo Start Bloodied Maw", "With no buffs, end first combo with Bloodied Maw.", VPR.JobID)] - // ViperPvPMainComboAoEStartBloodiedFeature = 4118, + [SectionCombo("One-Button Combos")] + [IconsCombo([VPR.SteelFangs, UTL.ArrowLeft, VPR.ReavingFangs, VPR.HuntersSting, VPR.SwiftskinsSting, VPR.FlanksbaneFang, VPR.FlankstingStrike, VPR.HindsbaneFang, VPR.HindstingStrike])] + [SecretCustomCombo] + [ConflictingCombos(ViperAutoSteelReavingFeature)] + [CustomComboInfo("Viper PvP Style Main Combo", "Condenses the main combo to a single button, like PvP.\nThe combo detects buffs to prioritize skills.\nThe default combo ender is Hindsting Strike, configurable below.", VPR.JobID)] + ViperPvPMainComboFeature = 4113, + + [SectionCombo("One-Button Combos")] + [SecretCustomCombo] + [ConflictingCombos(ViperPvPMainComboStartFlankstingFeature, ViperPvPMainComboStartHindsbaneFeature)] + [ParentCombo(ViperPvPMainComboFeature)] + [CustomComboInfo("PvP Combo Start Flanksbane Fang", "With no buffs, end first combo with Flanksbane Fang.", VPR.JobID)] + ViperPvPMainComboStartFlanksbaneFeature = 4114, + + [SectionCombo("One-Button Combos")] + [SecretCustomCombo] + [ConflictingCombos(ViperPvPMainComboStartFlanksbaneFeature, ViperPvPMainComboStartHindsbaneFeature)] + [ParentCombo(ViperPvPMainComboFeature)] + [CustomComboInfo("PvP Combo Start Flanksting Strike", "With no buffs, end first combo with Flanksting Strike.", VPR.JobID)] + ViperPvPMainComboStartFlankstingFeature = 4115, + + [SectionCombo("One-Button Combos")] + [SecretCustomCombo] + [ConflictingCombos(ViperPvPMainComboStartFlanksbaneFeature, ViperPvPMainComboStartFlankstingFeature)] + [ParentCombo(ViperPvPMainComboFeature)] + [CustomComboInfo("PvP Combo Start Hindsbane Fang", "With no buffs, end first combo with Hindsbane Fang.", VPR.JobID)] + ViperPvPMainComboStartHindsbaneFeature = 4116, + + [SectionCombo("One-Button Combos")] + [SecretCustomCombo] + [IconsCombo([VPR.SteelMaw, UTL.ArrowLeft, VPR.ReavingMaw, VPR.HuntersBite, VPR.SwiftskinsBite, VPR.JaggedMaw, VPR.BloodiedMaw])] + [CustomComboInfo("Viper PvP Style AoE Combo", "Condenses the main combo to a single button, like PvP.\nStarts with Reaving Maw by default, configurable below.", VPR.JobID)] + ViperPvPAoEFeature = 4117, + + [SectionCombo("One-Button Combos")] + [SecretCustomCombo] + [ParentCombo(ViperPvPAoEFeature)] + [CustomComboInfo("PvP AoE Combo Start Bloodied Maw", "With no buffs, end first combo with Bloodied Maw.", VPR.JobID)] + ViperPvPMainComboAoEStartBloodiedFeature = 4118, [SectionCombo("One-Button Combos")] [IconsCombo([VPR.Vicewinder, UTL.ArrowLeft, VPR.SwiftskinsCoil, VPR.HuntersCoil])]