Updated Dragoon. Secondary skills added. Like all of them except Skull Sunder because I'm lazy to figure out aggro stuff at the moment. Smart Animation Lock system. Won't jump to annoy you. More settings than you can imagine. I think there is too many settings? Not going to lie here, I'm not 100% sure if I've tested every single setting to see if it works correctly but I did test a lot. People use and report please. Planned: Skull Sunder... Auto Dragoon tank incoming. Make Jump less spammy still. Even more settings?!?!? Dear XC, add Dragoon to Behaviors and the basic HostileUnitsCount pulse for Dragoon as well so people can load this out of the box. Pretty please.
Settings is coming to the Dark Knight routine! Now it has both automatic mode and hotkey mode. (I still don't see the need for a semi automatic one, tell me if you have an idea). It still uses "left shift" to trigger Enmity Combo, but this time it will override AoE also! Uses AoE settings in the config. So don't forget to turn it on. On Hotkey rotation you can now toggle Cooldowns and AoE!! Please report all bugs below. For those who can't wait Rotations\Jobs\DarkKnight.cs Code: // By HeinzSkies using System.Threading.Tasks; using System.Windows.Forms; using ff14bot; using ff14bot.Enums; using ff14bot.Managers; using ff14bot.Objects; using YourRaidingBuddy.Books; using YourRaidingBuddy.Helpers; using YourRaidingBuddy.Interfaces.Settings; using YourRaidingBuddy.Settings; namespace YourRaidingBuddy.Rotations { public class DarkKnight : Root { private static LocalPlayer Me { get { return Core.Player; } } public override ClassJobType[] Class { get { return new[] { ClassJobType.DarkKnight }; } } public static SettingsG GeneralSettings { get { return InternalSettings.Instance.General; } } public static DarkKnightSetting DarkKnightSettings { get { return InternalSettings.Instance.DarkKnight; } } public override void OnInitialize() { } public static async Task<bool> AutoMode() { if (!Me.CurrentTarget.IsViable()) { return false; } Unit.UpdatePriorities(0, 5); return await AutoRotation(); } public static async Task<bool> HotkeyMode() { if (!Me.CurrentTarget.IsViable()) { return false; } Unit.UpdatePriorities(0, 5); return await HotkeyRotation(); } public static async Task<bool> AutoRotation() { if (await Darkside()) return true; if (!IsEnmityKeyDown() && await Aoe(false)) return true; if (await DotRotation()) return true; if (await FinishedRotation()) return true; if (IsEnmityKeyDown()) { if (await EnmityRotation()) return true; } else { if (await DpsRotation()) return true; } if (GeneralSettings.Cooldowns == CooldownUse.Always) { if (await OffGcdRotation()) return true; if (await MpGeneratorRotation()) return true; } return false; } public static async Task<bool> HotkeyRotation() { if (await Darkside()) return true; if (VariableBook.HkmMultiTarget) { if (!IsEnmityKeyDown() && await Aoe(true)) return true; } if (await DotRotation()) return true; if (await FinishedRotation()) return true; if (IsEnmityKeyDown()) { if (await EnmityRotation()) return true; } else { if (await DpsRotation()) return true; } if (VariableBook.HkmCooldowns) { if (await OffGcdRotation()) return true; if (await MpGeneratorRotation()) return true; } return false; } private static bool IsEnmityKeyDown() { return YourRaidingBuddy.Managers.HotkeyManager.IsKeyDown(Keys.LShiftKey); } public static async Task<bool> Darkside() { if (await Spell.NoneGcdCast("Darkside", Me, () => DarkKnightSettings.UseDarkside && !Me.HasAura("Darkside") && Me.CurrentManaPercent > DarkKnightSettings.DarksideMpPercentage)) return true; return false; } public static async Task<bool> DpsRotation() { if (await Spell.CastSpell("Syphon Strike", () => Actionmanager.LastSpell.Name == "Hard Slash")) return true; if (await Spell.CastSpell("Hard Slash", () => true)) return true; return false; } // We always want to finished out combo. public static async Task<bool> FinishedRotation() { await DarkArts(); if ( await Spell.CastSpell("Souleater", () => Actionmanager.LastSpell.Name == "Syphon Strike" && ((!ShouldApplyDelirium() && (Me.CurrentHealthPercent < DarkKnightSettings.SouleaterHpPercentage && Me.HasAura("Grit"))) || Me.HasAura("Dark Arts")))) return true; if ( await Spell.ApplyCast("Delirium", Me.CurrentTarget, () => Actionmanager.LastSpell.Name == "Syphon Strike")) return true; return await Spell.CastSpell("Power Slash", () => Actionmanager.LastSpell.Name == "Spinning Slash"); } public static async Task<bool> DarkArts() { if (!DarkKnightSettings.UseDarkArts) return false; await Spell.NoneGcdCast("Dark Arts", Me, () => Actionmanager.LastSpell.Name == "Syphon Strike" && Me.CurrentManaPercent > DarkKnightSettings.DarkArtsSouleaterHpPercentage && !ShouldApplyDelirium() && !Me.HasAura("Dark Arts")); await Spell.NoneGcdCast("Dark Arts", Me, () => Actionmanager.LastSpell.Name == "Spinning Slash" && Me.CurrentManaPercent > DarkKnightSettings.DarkArtsPowerSlashHpPercentage && !Me.HasAura("Dark Arts")); return false; } public static bool ShouldApplyDelirium() { return !Me.CurrentTarget.HasAura("Delirium", false, 4000) && !Me.CurrentTarget.HasAura("Dragon Kick") && Me.ClassLevel >= 50 && DarkKnightSettings.UseDelirium; } public static async Task<bool> Aoe(bool force) { if (!force && !GeneralSettings.Aoe) return false; return await Spell.CastSpell("Unleash", Me, () => ((Me.CurrentManaPercent > 50 && VariableBook.HostileUnitsCount >= GeneralSettings.AoeCount) || Me.HasAura("Enhanced Unleash") || force)); } public static async Task<bool> DotRotation() { if (await Spell.ApplyCast("Scourge", Me.CurrentTarget, () => !Me.CurrentTarget.HasAura("Scourge", true, 4000) && Actionmanager.LastSpell.Name != "Syphon Strike" && Actionmanager.LastSpell.Name != "Hard Slash" && Actionmanager.LastSpell.Name != "Spinning Slash" && (Me.CurrentTarget != null && Me.CurrentTarget.CurrentHealth > 3000))) return true; return false; } // Currently without a way to detect enmity, we will use hotkey to trigger this. public static async Task<bool> EnmityRotation() { if (await Spell.CastSpell("Spinning Slash", () => Actionmanager.LastSpell.Name == "Hard Slash")) return true; if (await Spell.CastSpell("Hard Slash", () => true)) return true; return false; } public static bool TargetIsNear() { return Me.CurrentTarget.Distance(Me) < 5; } public static async Task<bool> OffGcdRotation() { // Defensive that keeps you from dying await Spell.NoneGcdCast("Convalescence", Me, () => DarkKnightSettings.UseConvalescence && Me.CurrentHealthPercent < DarkKnightSettings.ConvalescenceHpPercentage); await Spell.NoneGcdCast("Shadow Wall", Me, () => DarkKnightSettings.UseShadowWall && Me.CurrentHealthPercent < DarkKnightSettings.ShadowWallHpPercentage); await Spell.NoneGcdCast("Shadowskin", Me, () => DarkKnightSettings.UseShadowskin && Me.CurrentHealthPercent < DarkKnightSettings.ShadowskinHpPercentage); // Offensive by DPS await Spell.NoneGcdCast("Reprisal", Me.CurrentTarget, () => DarkKnightSettings.UseReprisal); await Spell.NoneGcdCast("Mercy Stroke", Me.CurrentTarget, () => DarkKnightSettings.UseMercyStroke && (Me.CurrentTarget != null && Me.CurrentTarget.CurrentHealthPercent <= 20)); await Spell.NoneGcdCast("Low Blow", Me.CurrentTarget, () => DarkKnightSettings.UseLowBlow); await Spell.CastLocation("Salted Earth", Me.CurrentTarget, () => DarkKnightSettings.UseSaltedEarth); // Defensive await Spell.NoneGcdCast("Bloodbath", Me, () => DarkKnightSettings.UseBloodbath && TargetIsNear() && Me.CurrentHealthPercent < DarkKnightSettings.BloodbathHpPercentage); await Spell.NoneGcdCast("Dark Dance", Me, () => DarkKnightSettings.UseDarkDance && Me.CurrentHealthPercent < DarkKnightSettings.DarkDanceHpPercentage); await Spell.NoneGcdCast("Foresight", Me, () => DarkKnightSettings.UseForesight && Me.CurrentHealthPercent < DarkKnightSettings.ForesightHpPercentage); return false; } public static async Task<bool> MpGeneratorRotation() { await Spell.NoneGcdCast("Blood Weapon", Me, () => DarkKnightSettings.UseBloodWeapon && Me.CurrentManaPercent < DarkKnightSettings.BloodWeaponManaPercentage && TargetIsNear()); await Spell.NoneGcdCast("Blood Price", Me, () => DarkKnightSettings.UseBloodPrice && Me.CurrentManaPercent < DarkKnightSettings.BloodPriceManaPercentage && VariableBook.HostileUnitsTargettingMeCount > 0); await Spell.NoneGcdCast("Carve and Spit", Me.CurrentTarget, () => !Me.HasAura("Dark Arts") && Actionmanager.LastSpell.Name != "Syphon Strike" && Actionmanager.LastSpell.Name != "Spinning Slash" && Me.CurrentManaPercent < 50); await Spell.NoneGcdCast("Sole Survivor", Me.CurrentTarget, () => DarkKnightSettings.UseSoleSurvivor && (Me.CurrentManaPercent < DarkKnightSettings.SoleSurvivorManaPercentage || Me.CurrentHealthPercent < DarkKnightSettings.SoleSurvivorHpPercentage) && (Me.CurrentTarget != null && Me.CurrentTarget.CurrentHealth < 5000)); return false; } } } Core/Settings/Jobs/DarkKnightSettings.cs Code: using ff14bot.Helpers; using System.Collections.Generic; using System.ComponentModel; using System.Configuration; using System.IO; using System.Windows.Ink; using YourRaidingBuddy.Interfaces.Settings; namespace YourRaidingBuddy.Settings { public class DarkKnightSetting : JsonSettings { public DarkKnightSetting() : base(InternalSettings.RoutineSettingsPath + "_Dark-Knight.json") { } [Setting] [DefaultValue(70)] [Category("Dark Knight - GCD Attacks")] [DisplayName("Souleater HP%")] public int SouleaterHpPercentage { get; set; } [Setting] [DefaultValue(true)] [Category("Dark Knight - GCD Attacks")] [DisplayName("Use Delirium")] public bool UseDelirium { get; set; } [Setting] [DefaultValue(true)] [Category("Dark Knight - Off GCD Buffs")] [DisplayName("Use Darkside")] public bool UseDarkside { get; set; } [Setting] [DefaultValue(90)] [Category("Dark Knight - Off GCD Buffs")] [DisplayName("Darkside MP%")] public int DarksideMpPercentage { get; set; } [Setting] [DefaultValue(true)] [Category("Dark Knight - Off GCD Attack Buffs")] [DisplayName("Use Dark Arts")] public bool UseDarkArts { get; set; } [Setting] [DefaultValue(50)] [Category("Dark Knight - Off GCD Attack Buffs")] [DisplayName("Dark Arts > Power Slash MP%")] public int DarkArtsPowerSlashHpPercentage { get; set; } [Setting] [DefaultValue(70)] [Category("Dark Knight - Off GCD Attack Buffs")] [DisplayName("Dark Arts > Souleater MP%")] public int DarkArtsSouleaterHpPercentage { get; set; } [Setting] [DefaultValue(true)] [Category("Dark Knight - Attacks")] [DisplayName("Use Scourge")] public bool UseScourge { get; set; } [Setting] [DefaultValue(true)] [Category("Dark Knight - Off GCD Attacks")] [DisplayName("Use Reprisal")] public bool UseReprisal { get; set; } [Setting] [DefaultValue(true)] [Category("Dark Knight - Off GCD Attacks")] [DisplayName("Use Low Blow")] public bool UseLowBlow { get; set; } [Setting] [DefaultValue(true)] [Category("Dark Knight - Off GCD Attacks")] [DisplayName("Use Salted Earth")] public bool UseSaltedEarth { get; set; } [Setting] [DefaultValue(true)] [Category("Dark Knight - Off GCD Defense Buffs")] [DisplayName("Use Dark Dance")] public bool UseDarkDance { get; set; } [Setting] [DefaultValue(70)] [Category("Dark Knight - Off GCD Defense Buffs")] [DisplayName("Dark Dance HP%")] public int DarkDanceHpPercentage { get; set; } [Setting] [DefaultValue(false)] [Category("Dark Knight - Off GCD Defense Buffs")] [DisplayName("Use Dark Mind")] public bool UseDarkMind { get; set; } [Setting] [DefaultValue(false)] [Category("Dark Knight - Off GCD Defense Buffs")] [DisplayName("Dark Mind HP%")] public bool DarkMindHpPercentage { get; set; } [Setting] [DefaultValue(false)] [Category("Dark Knight - Off GCD Defense Buffs")] [DisplayName("Use Shadowskin")] public bool UseShadowskin { get; set; } [Setting] [DefaultValue(60)] [Category("Dark Knight - Off GCD Defense Buffs")] [DisplayName("Shadowskin HP%")] public int ShadowskinHpPercentage { get; set; } [Setting] [DefaultValue(false)] [Category("Dark Knight - Off GCD Defense Buffs")] [DisplayName("Use Shadow Wall")] public bool UseShadowWall { get; set; } [Setting] [DefaultValue(30)] [Category("Dark Knight - Off GCD Defense Buffs")] [DisplayName("Shadow Wall HP%")] public int ShadowWallHpPercentage { get; set; } [Setting] [DefaultValue(true)] [Category("Dark Knight - Off GCD Mana Buff")] [DisplayName("Use Blood Weapon")] public bool UseBloodWeapon { get; set; } [Setting] [DefaultValue(90)] [Category("Dark Knight - Off GCD Mana Buff")] [DisplayName("Blood Weapon MP%")] public int BloodWeaponManaPercentage { get; set; } [Setting] [DefaultValue(true)] [Category("Dark Knight - Off GCD Mana Buff")] [DisplayName("Use Blood Price")] public bool UseBloodPrice { get; set; } [Setting] [DefaultValue(70)] [Category("Dark Knight - Off GCD Mana Buff")] [DisplayName("Blood Price Mana%")] public int BloodPriceManaPercentage { get; set; } [Setting] [DefaultValue(true)] [Category("Dark Knight - Off GCD Mana Buff")] [DisplayName("Use Carve and Spit")] public bool UseCarveAndSpit { get; set; } [Setting] [DefaultValue(50)] [Category("Dark Knight - Off GCD Mana Buff")] [DisplayName("Carve and Spit MP%")] public int CarveAndSpitManaPercentage { get; set; } [Setting] [DefaultValue(true)] [Category("Dark Knight - Off GCD Mana Buff")] [DisplayName("Use Sole Survivor")] public bool UseSoleSurvivor { get; set; } [Setting] [DefaultValue(70)] [Category("Dark Knight - Off GCD Mana Buff")] [DisplayName("Sole Survivor HP%")] public int SoleSurvivorHpPercentage { get; set; } [Setting] [DefaultValue(70)] [Category("Dark Knight - Off GCD Mana Buff")] [DisplayName("Sole Survivor MP%")] public int SoleSurvivorManaPercentage { get; set; } [Setting] [DefaultValue(true)] [Category("Cross Class")] [DisplayName("Use Foresight")] public bool UseForesight { get; set; } [Setting] [DefaultValue(50)] [Category("Cross Class")] [DisplayName("Foresight HP%")] public int ForesightHpPercentage { get; set; } [Setting] [DefaultValue(true)] [Category("Cross Class")] [DisplayName("Use Convalescence")] public bool UseConvalescence { get; set; } [Setting] [DefaultValue(40)] [Category("Cross Class")] [DisplayName("Convalescence HP%")] public int ConvalescenceHpPercentage { get; set; } [Setting] [DefaultValue(true)] [Category("Cross Class")] [DisplayName("Use Bloodbath")] public bool UseBloodbath { get; set; } [Setting] [DefaultValue(70)] [Category("Cross Class")] [DisplayName("Bloodbath HP%")] public int BloodbathHpPercentage { get; set; } [Setting] [DefaultValue(true)] [Category("Cross Class")] [DisplayName("Use Mercy Strokeh")] public bool UseMercyStroke { get; set; } } }
New update is out! --> Updated Dragoon and DragoonSettings //Thanks khld! Updated DarkKnight and DarkKnightSettings //Thanks heinzskies! Added AoECount for Dragoon.
Hmm. No longer seems to use Reprisal or Salted Earth (single target) even though those toggles are set to True.
also for dark knight what do i have to do in the Settings so it does the emnity when i hit left shift. also still cant use Order bot with DRK =(
I will have to take a look, didn't test everything yet. If you have to set Hotkey mode to Automatic and have cooldown active = always.
You still can press Left Shift key. There's no settings for that, it's hard coded as it don't work in Automatic mode. I still have no time to look into Order bot problem.
Added Ninja Settings to the Ninja Routine. Added Clip settings for Dancing Edge, Shadow Fang, and Mutilate. Added toggle for Shuriken use. If it is set to true it will cast Fuma instead of Raiton. Added Emergency Huton Refresh Clip. This setting determines when to prioritize refreshing huton with Armor Crush instead of dots and debuffs. I recommend leaving it at 15000 but its up to you. Added Kassatsu toggle. If set to true will automatically cast Kassatsu after Trick Attack. Added DancingEdge Toggle. In case you have a warrior that puts storms eye on a little slow. Changelog: Updated ninjutsu conditions it should now never use Kassatsu to cast Suiton so you do not get that double sution cast. If you have any settings you wish to be added let me know. Next on list Adding Katon and Kassatsu to AoE rotation. Routine: Code: using System; using System.Linq; using System.Threading.Tasks; using System.Windows.Media; using Buddy.Coroutines; using ff14bot; using ff14bot.Enums; using ff14bot.Helpers; using ff14bot.Managers; using ff14bot.Objects; using YourRaidingBuddy.Helpers; using YourRaidingBuddy.Settings; using TreeSharp; using System.Collections.Generic; using YourRaidingBuddy.Books; using YourRaidingBuddy.Interfaces.Settings; namespace YourRaidingBuddy.Rotations { public class Ninja : Root { private static LocalPlayer Me { get { return Core.Player; } } public static SettingsG GeneralSettings { get { return InternalSettings.Instance.General; } } public static NinjaSetting NinjaSettings { get { return InternalSettings.Instance.Ninja; } } public override ClassJobType[] Class { get { return new[] { ClassJobType.Ninja, ClassJobType.Rogue }; } } private const int MobHp = 0; private const int BuffHp = 0; public override void OnInitialize() { ; } #region NewRotation public static async Task<bool> AutoMode() { if (!Me.CurrentTarget.IsViable()) return false; return await NinjaRotation(); } public static async Task<bool> HotkeyMode() { if (!Me.CurrentTarget.IsViable()) return false; if (VariableBook.HkmMultiTarget) await NinjaAoE(); return await NinjaRotation(); } public static async Task<bool> NinjaRotation() { if (Managers.HotkeyManager.IsKeyDown(System.Windows.Forms.Keys.LShiftKey)) { await EmergHuton(); } await DoNinjutsu(); await Kassatsu(); await EmergenHuton(); await HutonRefresh(); await DancingEdge(); await Shadow(); await SingleTarget(); await NoneGCD(); return false; } public static async Task<bool> NinjaAoE() { if (VariableBook.HostileUnitsCount >= 3 && Core.Me.CurrentTarget.CurrentHealthPercent >= 60) { await CastDoton(); } if (VariableBook.HostileUnitsCount >= 5) { await Spell.CastSpell("Death Blossom", Me, () => true); } return false; } public static async Task<bool> EmergenHuton() { if (!Me.HasAura("Duality") && !Core.Me.HasAura("Huton", true, NinjaSettings.EmergencyHutonClip) && Actionmanager.LastSpell.Name == "Gust Slash") { await Spell.CastSpell("Armor Crush", () => true); } return false; } public static async Task<bool> EmergHuton() { await Spell.CastSpell("Armor Crush", () => Actionmanager.LastSpell.Name == "Gust Slash"); await Spell.CastSpell("Gust Slash", () => Actionmanager.LastSpell.Name == "Spinning Edge"); await Spell.CastSpell("Spinning Edge", () => true); return false; } public static async Task<bool> HutonRefresh() { if (!Me.HasAura("Duality") && !Core.Me.HasAura("Huton", true, 40000) && Actionmanager.LastSpell.Name == "Gust Slash") { await DancingEdge(); await Shadow(); await Spell.CastSpell("Armor Crush", () => true); } return false; } public static async Task<bool> DancingEdge() { if(NinjaSettings.DancingEdge) { if (!Me.HasAura("Duality") && !Me.CurrentTarget.HasAura("Dancing Edge", true, NinjaSettings.DancingEdgeClip) && !Me.CurrentTarget.HasAura("Storm's Eye", false) && Actionmanager.LastSpell.Name == "Gust Slash") { return await Spell.CastSpell("Dancing Edge", () => true); } } return false; } public static async Task<bool> Shadow() { if (!Me.CurrentTarget.HasAura("Shadow Fang", true, NinjaSettings.ShadowFangClip) && (Me.CurrentTarget.HasAura("Dancing Edge") || Me.CurrentTarget.HasAura("Storm's Eye")) && Actionmanager.LastSpell.Name == "Spinning Edge" && Core.Me.CurrentTarget.CurrentHealth >= MobHp) { return await Spell.CastSpell("Shadow Fang", () => true); } return false; } public static async Task<bool> SingleTarget() { await Spell.CastSpell("Duality", Me, () => Actionmanager.LastSpell.Name == "Gust Slash" && Me.CurrentTarget.HasAura("Shadow Fang", true) && (Me.CurrentTarget.HasAura("Dancing Edge") || Me.CurrentTarget.HasAura("Storm's Eye"))); await Spell.CastSpell("Aeolian Edge", () => Me.HasAura("Duality") || Actionmanager.LastSpell.Name == "Gust Slash"); await Spell.CastSpell("Gust Slash", () => Actionmanager.LastSpell.Name == "Spinning Edge"); await Spell.CastSpell("Mutilate", () => Me.CurrentTarget.HasAura(AuraBook.ShadowFang) && !Me.CurrentTarget.HasAura(AuraBook.Mutilate, true, NinjaSettings.MutilationClip) && Core.Me.CurrentTarget.CurrentHealth >= MobHp); await Spell.CastSpell("Spinning Edge", () => true); return false; } public static async Task<bool> NoneGCD() { await Spell.NoneGcdCast("Trick Attack", Me.CurrentTarget, () => Me.HasAura("Suiton") && Me.CurrentTarget.IsBehind); await Spell.NoneGcdCast("Internal Release", Me, () => !Me.HasAura(AuraBook.InternalRelease) && Core.Me.CurrentTarget.CurrentHealth >= BuffHp); await Spell.NoneGcdCast("Blood for Blood", Me, () => Unit.CombatTime.ElapsedMilliseconds > 2000 && !Me.HasAura("Blood for Blood") && Core.Me.CurrentTarget.CurrentHealth >= BuffHp); await Spell.NoneGcdCast("Invigorate", Me, () => Me.CurrentTP < 550); await Spell.NoneGcdCast("Second Wind", Me, () => Me.CurrentHealthPercent <= 30); await Spell.NoneGcdCast("Jugulate", Me.CurrentTarget, () => Unit.CombatTime.ElapsedMilliseconds > 8000); await Spell.NoneGcdCast("Mug", Me.CurrentTarget, () => Unit.CombatTime.ElapsedMilliseconds > 4000); await Spell.NoneGcdCast("Dream Within a Dream", Me.CurrentTarget, () => Unit.CombatTime.ElapsedMilliseconds > 6000); await Spell.NoneGcdCast("Assassinate", Me.CurrentTarget, () => true); return false; } #endregion #region Ninjatsu Copied From Kupo Credits Masta private static bool HasBleedingDebuff() { return Me.CurrentTarget.HasAura("Storm's Eye", false, 2000) || Me.CurrentTarget.HasAura("Dancing Edge"); } private static readonly SpellData Jin = DataManager.GetSpellData(2263); private static readonly SpellData Chi = DataManager.GetSpellData(2261); private static readonly SpellData Ten = DataManager.GetSpellData(2259); private static readonly SpellData Ninjutsu = DataManager.GetSpellData(2260); private static readonly SpellData Jugulate = DataManager.GetSpellData(2251); //private static readonly SpellData Kassatsu = DataManager.GetSpellData(2264); private static readonly SpellData Trick_Attack = DataManager.GetSpellData(2258); private static readonly SpellData Sneak_Attack = DataManager.GetSpellData(2250); public static HashSet<uint> OverrideBackstabIds = new HashSet<uint>() { 3240//Cloud of darkness }; private const int HutonRecast = 6000; internal static async Task<bool> DoNinjutsu() { if(Core.Player.HasAura("Mudra")) return true; if(Actionmanager.CanCastOrQueue(Jin, null)) { if(!Me.HasAura("Huton", true, HutonRecast)) { await CastHuton(); } var taCD = Trick_Attack.Cooldown; if(taCD.TotalMilliseconds <= 1300) { await CastSuiton(); } if(Me.CurrentTarget.HasAura("Vulnerability Up")) { await Kassatsu(); } if(NinjaSettings.ShurikenAlways) { await CastFuma(); } else { await CastRaiton(); } } return false; } private static async Task CastHuton() { if (await Coroutine.Wait(2000, () => Actionmanager.DoAction(Jin, null))) { if (await Coroutine.Wait(2000, () => Actionmanager.DoAction(Chi, null))) { if (await Coroutine.Wait(2000, () => Actionmanager.DoAction(Ten, null))) { await CastNinjutsu(); } } } } private static async Task<bool> Kassatsu() { if(NinjaSettings.Kassatsu) { if(Me.CurrentTarget.HasAura("Vulnerability Up")) { await Spell.NoneGcdCast("Kassatsu", Me, () => true); } } return false; } private static async Task<bool> CastFuma() { if (await Coroutine.Wait(2000, () => Actionmanager.DoAction(Ten, null))) { return await CastNinjutsu(); } return false; } private static async Task<bool> CastDoton() { if (Actionmanager.CanCastOrQueue(Jin, null)) { if (await Coroutine.Wait(2000, () => Actionmanager.DoAction(Jin, null))) { if (await Coroutine.Wait(2000, () => Actionmanager.DoAction(Ten, null))) { if (await Coroutine.Wait(2000, () => Actionmanager.DoAction(Chi, null))) { return await CastNinjutsu(); } } } } return false; } private static async Task<bool> CastRaiton() { if (!await Coroutine.Wait(2000, () => Actionmanager.DoAction(Ten, null))) return false; if (await Coroutine.Wait(2000, () => Actionmanager.DoAction(Chi, null))) { return await CastNinjutsu(); } return false; } private static async Task<bool> CastSuiton() { if (await Coroutine.Wait(2000, () => Actionmanager.DoAction(Ten, null))) { if (await Coroutine.Wait(2000, () => Actionmanager.DoAction(Chi, null))) { if (await Coroutine.Wait(2000, () => Actionmanager.DoAction(Jin, null))) { return await CastNinjutsu(); } } } return false; } private static async Task<bool> CastNinjutsu() { if (await Coroutine.Wait(2000, () => Core.Player.HasAura("Mudra"))) { bool possibly = false; while (Core.Player.HasAura("Mudra")) { if (Core.Player.HasTarget) { if (Actionmanager.DoAction(Ninjutsu, Core.Target)) { possibly = true; } } if (!Core.Player.InCombat) return false; await Coroutine.Yield(); } await Coroutine.Wait(2000, () => Ninjutsu.Cooldown.TotalSeconds > 10); return possibly; } return false; } #endregion } } Settings: Code: using ff14bot.Helpers; using System.Collections.Generic; using System.ComponentModel; using System.Configuration; using System.IO; using YourRaidingBuddy.Interfaces.Settings; namespace YourRaidingBuddy.Settings { public class NinjaSetting : JsonSettings { public NinjaSetting() : base(InternalSettings.RoutineSettingsPath + "_Ninja.json") { } [Setting] [DefaultValue(false)] [Category("Ninja")] public bool ShurikenAlways { get; set; } [Setting] [DefaultValue(true)] [Category("Ninja")] [DisplayName("Use Kassatsu")] public bool Kassatsu { get; set; } [Setting] [DefaultValue(6000)] [Category("Ninja")] public int HutonClip { get; set; } [Setting] [DefaultValue(true)] [Category("Rogue")] public bool DancingEdge { get; set; } [Setting] [DefaultValue(3000)] [Category("Rogue")] public int DancingEdgeClip { get; set; } [Setting] [DefaultValue(3000)] [Category("Rogue")] public int ShadowFangClip { get; set; } [Setting] [DefaultValue(3000)] [Category("Rogue")] public int MutilationClip { get; set; } [Setting] [DefaultValue(80)] [Category("Rogue")] public int TrickIsBehindAdjustment { get; set; } [Setting] [DefaultValue(15000)] [Category("Rogue")] public int EmergencyHutonClip {get; set; } } }
Follow the guide at the front to add in Dragoon hooks to Behaviors.cs... Code: #region Dragoon Hooks // Dragoon Job if (Core.Player.CurrentJob == ff14bot.Enums.ClassJobType.Dragoon) { // if (InternalSettings.Instance.General.ArmsRotVersion == Enums.ArmsRotationVersion.Normal) { switch (InternalSettings.Instance.Hotkeys.HotkeyModeSelection) { case HotkeyMode.Automatic: TreeHooks.Instance.ReplaceHook("YRB_Combat_Selector", new ActionRunCoroutine(ctx => Rotations.Dragoon.AutoMode())); // TreeHooks.Instance.ReplaceHook("YRB_Pre_Combat_Selector", new ActionRunCoroutine(ctx => Rotations.Ninja.NinjaBuildPreCombatBuffs())); // TreeHooks.Instance.ReplaceHook("YRB_CombatBuff_Selector", new ActionRunCoroutine(ctx => Rotations.Ninja.NinjaBuildCombatBuffs())); break; case HotkeyMode.SemiHotkeyMode: // TreeHooks.Instance.ReplaceHook("YRB_Combat_Selector", new ActionRunCoroutine(ctx => Enhancement.SemiHkMode())); // TreeHooks.Instance.ReplaceHook("YRB_Pre_Combat_Selector", new ActionRunCoroutine(ctx => Enhancement.PreCombat())); break; case HotkeyMode.HotkeyMode: TreeHooks.Instance.ReplaceHook("YRB_Combat_Selector", new ActionRunCoroutine(ctx => Rotations.Dragoon.HotkeyMode())); // TreeHooks.Instance.ReplaceHook("YRB_Pre_Combat_Selector", new ActionRunCoroutine(ctx => Enhancement.PreCombat())); break; default: StopBehaviors("Unable to intialize Dragoon", "HookBehaviors"); break; } } Logger.WriteDebug(reinitialized ? "Hooks reinitialized for Dragoon (RebuildBehaviors())." : "Hooks initialized for Dragoon (HookBehaviors())."); } #endregion
New update out ! --> Added Dragoon to Behaviors.cs Sorry! Added new NinjaSettings and updated Ninja Rotation //Thanks wolfrain62
All right need help with two things for the ninja rotation. 1. After casting Sution the CR pauses for about half a sec take a look at the code and see how I can fix it. 2. How do I make sure Mutilate does not break a combo chain. When it hits the clip timer it will break the 3 combo chain to cast mutilate but i want it the CR to prioritize finishing the combo before refreshing mutilate. Suiton Code: Code: private static async Task<bool> CastSuiton() { if (await Coroutine.Wait(2000, () => Actionmanager.DoAction(Ten, null))) { if (await Coroutine.Wait(2000, () => Actionmanager.DoAction(Chi, null))) { if (await Coroutine.Wait(2000, () => Actionmanager.DoAction(Jin, null))) { return await CastNinjutsu(); } } } return false; } Mutilate Code: Code: public static async Task<bool> Mutilate() { if (Me.CurrentTarget.HasAura(AuraBook.ShadowFang) && !Me.CurrentTarget.HasAura(AuraBook.Mutilate, true, NinjaSettings.MutilationClip)) { await Spell.CastSpell("Mutilate", () => true); } return false; }
I actually played around with Ninja a bit and trying coming up better Ninjutsu code, I also came across the same problem. It would try to cast spells during Mudras. I coded pretty much the full rotation from scratch as well sans Ninjutsu. I thought that in theory just using CastQueue instead would work, something like: Code: Spell.CastQueue("Ten", target, ...) Spell.CastQueue("Chi", target, ...) Spell.CastQueue("Jin", target, ...) Spell.CastQueue("Ninjutsu", target, ...) // I think Spell.Applycast does a OK job also? Rough code but you get the idea. One after the other, no checks needed. It works but would cast other spells in between still, which is a bit unexpected to me. This does fix that after delay. It also still cast things in between when I use !Me.HasAura("Mudra") before the rotation/spells or even when I add a quick check to see if there is enough time in between GCD spells before doing Ninjutsu. I do think Ninjutsu can be more elegantly pulled off with one of the YRB Spell helpers though instead of using DoAction directly... Then... I ran out of ideas and gave up... .
Maybe I was sleepy when I first tried this a couple of nights ago or something but what I came up with works now suddenly? Presenting my basic and usable Ninja routine as long as you stick to single targets... Code: using System.Threading.Tasks; using ff14bot; using ff14bot.Enums; using ff14bot.Managers; using ff14bot.Objects; using YourRaidingBuddy.Books; using YourRaidingBuddy.Helpers; using YourRaidingBuddy.Interfaces.Settings; using YourRaidingBuddy.Settings; namespace YourRaidingBuddy.Rotations { public class Ninja : Root { public static LocalPlayer Me { get { return Core.Player; } } public static SettingsG GeneralSettings { get { return InternalSettings.Instance.General; } } public static NinjaSetting NinjaSettings { get { return InternalSettings.Instance.Ninja; } } public override ClassJobType[] Class { get { return new[] {ClassJobType.Ninja}; } } public override void OnInitialize() { ; } public static async Task<bool> AutoMode() { if (!Me.CurrentTarget.IsViable()) return false; if (Actionmanager.CanCastOrQueue(Spell.GetSpellData("Jin"), null)) await NinjaNinjutsu(); if (GeneralSettings.Aoe && VariableBook.HostileUnitsCount >= GeneralSettings.AoeCount) await NinjaMultipleTarget(); await NinjaSingleTarget(); if (GeneralSettings.Cooldowns == CooldownUse.Always) await NinjaWeave(); return false; } public static async Task<bool> HotkeyMode() { if (!Me.CurrentTarget.IsViable()) return false; if (VariableBook.HkmMultiTarget) await NinjaMultipleTarget(); if (VariableBook.HkmCooldowns) await NinjaWeave(); if (VariableBook.HkmSpecialKey) await NinjaNinjutsu(); await NinjaSingleTarget(); return false; } public static async Task<bool> NinjaMultipleTarget() { await Spell.CastSpell("Death Blossom", null, () => true); return false; } public static async Task<bool> NinjaSingleTarget() { await Spell.CastSpell("Armor Crush", () => Actionmanager.LastSpell.Name == "Gust Slash" && Me.HasAura("Huton") && !Me.HasAura("Huton", true, 40000) && !Me.HasAura("Duality")); await Spell.ApplyCast("Dancing Edge", Me.CurrentTarget, () => Actionmanager.LastSpell.Name == "Gust Slash" && !Me.CurrentTarget.HasAura("Dancing Edge", true, 6000) && !Me.HasAura("Duality")); await Spell.CastSpell("Aeolian Edge", () => Actionmanager.LastSpell.Name == "Gust Slash"); await Spell.CastSpell("Shadow Fang", () => Actionmanager.LastSpell.Name == "Spinning Edge" && !Me.CurrentTarget.HasAura("Shadow Fang", true, 6000) && Me.CurrentTarget.HasAura("Dancing Edge")); await Spell.CastSpell("Gust Slash", () => Actionmanager.LastSpell.Name == "Spinning Edge"); await Spell.CastSpell("Mutilate", () => !Me.CurrentTarget.HasAura("Mutilation", true, 6000) && Me.CurrentTarget.HasAura("Dancing Edge")); await Spell.CastSpell("Spinning Edge", () => true); return false; } public static async Task<bool> NinjaWeave() { await Spell.CastSpell("Duality", () => Actionmanager.LastSpell.Name == "Gust Slash" && Me.HasAura("Huton") && Me.CurrentTarget.HasAura("Dancing Edge")); await Spell.CastSpell("Trick Attack", () => Me.CurrentTarget != null && Me.CurrentTarget.IsBehind && Me.HasAura("Suiton")); await Spell.CastSpell("Kassatsu", Me, () => Me.CurrentTarget.HasAura("Vulnerability Up")); await Spell.CastSpell("Invigorate", Me, () => Me.CurrentTP <= 540); await Spell.CastSpell("Blood for Blood", Me, () => Me.CurrentTarget != null); await Spell.CastSpell("Internal Release", Me, () => Me.CurrentTarget != null); await Spell.CastSpell("Dream Within a Dream", () => Me.CurrentTarget.HasAura("Dancing Edge")); await Spell.CastSpell("Mug", () => Me.CurrentTarget.HasAura("Dancing Edge")); await Spell.CastSpell("Jugulate", () => Me.CurrentTarget.HasAura("Dancing Edge")); await Spell.CastSpell("Assassinate", () => true); return false; } public static async Task<bool> NinjaNinjutsu() { if (!Me.HasAura("Huton")) await NinjaCastHuton(); if (!Me.CurrentTarget.HasAura("Vulnerability Up", true, 3000) && !Me.CurrentTarget.HasAura("Vulnerability Up", false, 3000) && Spell.CanCast(Spell.GetSpellData("Trick Attack"), Me.CurrentTarget) && Me.CurrentTarget != null) await NinjaCastSuiton(); if (Me.CurrentTarget != null) await NinjaCastRaiton(); return false; } private static async Task<bool> NinjaCastHuton() { await Spell.CastQueue(2263, null, () => true, false, true); await Spell.CastQueue(2261, null, () => true, false, true); await Spell.CastQueue(2259, null, () => true, false, true); return await Spell.CastQueue(2260, null, () => true, false, true); } private static async Task<bool> NinjaCastSuiton() { await Spell.CastQueue(2259, null, () => true, false, true); await Spell.CastQueue(2261, null, () => true, false, true); await Spell.CastQueue(2263, null, () => true, false, true); return await Spell.CastQueue(2260, Me.CurrentTarget, () => true, false, true); } private static async Task<bool> NinjaCastRaiton() { await Spell.CastQueue(2259, null, () => true, false, true); await Spell.CastQueue(2261, null, () => true, false, true); return await Spell.CastQueue(2260, Me.CurrentTarget, () => true, false, true); } } } If it works for you, hope it inspires you wolfy .
Thanks for the help khld. Now for the mutilate problem I went with this Code: public static async Task<bool> Mutilate() { if (Me.CurrentTarget.HasAura(AuraBook.ShadowFang) && !Me.CurrentTarget.HasAura(AuraBook.Mutilate, true, NinjaSettings.MutilationClip) && (Actionmanager.LastSpell.Name == "Armor Crush" || Actionmanager.LastSpell.Name == "Dancing Edge" || Actionmanager.LastSpell.Name == "Aeolian Edge" || Actionmanager.LastSpell.Name == "Shadow Fang")) { await Spell.CastSpell("Mutilate", () => true); } return false; } The only problem is that Actionmanager.LastSpell.Name == "Armor Crush" does not seem to work. If Armor Crush was the last spell used mutilate does not cast. All the other ones work except for this one. Any help is much appreciated.
I think it just doesn't work for last part of any combo... EDIT: I think it is better to do something like "Actionmanager.Lastspellid == 0". If you do this, to account for lag you need to use ApplyCast instead for your combos or any of the other spell helpers that uses the ping setting...