diff --git a/BasicRotations/Melee/MNK_Default.cs b/BasicRotations/Melee/MNK_Default.cs index 30cbf85ec..c35bbf36e 100644 --- a/BasicRotations/Melee/MNK_Default.cs +++ b/BasicRotations/Melee/MNK_Default.cs @@ -17,6 +17,13 @@ public enum RiddleOfFireFirst : byte [Description("Perfect Balance")] PerfectBalance, } + public enum MasterfulBlitzUse : byte + { + [Description("Use Immediately")] UseAsAble, + + [Description("With ROF burst logic")] RiddleOfFireUse, + } + [RotationConfig(CombatType.PvE, Name = "Use Form Shift")] public bool AutoFormShift { get; set; } = true; @@ -32,6 +39,9 @@ public enum RiddleOfFireFirst : byte [RotationConfig(CombatType.PvE, Name = "Enable TEA Checker.")] public bool EnableTEAChecker { get; set; } = false; + [RotationConfig(CombatType.PvE, Name = "Use Masterful Blitz abilites as soon as they are available.")] + public MasterfulBlitzUse MBAbilities { get; set; } = MasterfulBlitzUse.RiddleOfFireUse; + [RotationConfig(CombatType.PvE, Name = "Use Riddle of Fire after this ability")] public RiddleOfFireFirst ROFFirst { get; set; } = RiddleOfFireFirst.Brotherhood; #endregion @@ -91,13 +101,18 @@ protected override bool EmergencyAbility(IAction nextGCD, out IAction? act) // 'If you are in brotherhood and forbidden chakra is available, use it.' if (TheForbiddenChakraPvE.CanUse(out act)) return true; } - if (!InBrotherhood) + else { // 'If you are not in brotherhood and brotherhood is about to be available, hold for burst.' - if (BrotherhoodPvE.Cooldown.WillHaveOneChargeGCD(1) && TheForbiddenChakraPvE.CanUse(out act)) return false; + if (BrotherhoodPvE.Cooldown.WillHaveOneChargeGCD(1) && TheForbiddenChakraPvE.CanUse(out act)) return true; // 'If you are not in brotherhood use it.' if (TheForbiddenChakraPvE.CanUse(out act)) return true; } + if (!BrotherhoodPvE.EnoughLevel) + { + // 'If you are not high enough level for brotherhood, use it.' + if (TheForbiddenChakraPvE.CanUse(out act)) return true; + } if (!TheForbiddenChakraPvE.EnoughLevel) { // 'If you are not high enough level for TheForbiddenChakra, use immediately at 5 chakra.' @@ -239,22 +254,47 @@ protected override bool GeneralGCD(out IAction? act) // bullet proofed finisher - use when during burst // or if burst was missed, and next burst is not arriving in time, use it better than waste it, otherwise, hold it for next rof - if (!BeastChakras.Contains(BeastChakra.NONE) && (Player.HasStatus(true, StatusID.RiddleOfFire) || RiddleOfFirePvE.Cooldown.JustUsedAfter(42))) + if (!BeastChakras.Contains(BeastChakra.NONE)) { - // Both Nadi and 3 beasts - if (PhantomRushPvE.CanUse(out act)) return true; - if (TornadoKickPvE.CanUse(out act)) return true; + switch (MBAbilities) + { + case MasterfulBlitzUse.UseAsAble: + default: + if (PhantomRushPvE.CanUse(out act)) return true; + if (TornadoKickPvE.CanUse(out act)) return true; + + // Needing Solar Nadi and has 3 different beasts + if (RisingPhoenixPvE.CanUse(out act)) return true; + if (FlintStrikePvE.CanUse(out act)) return true; + + // Needing Lunar Nadi and has 3 of the same beasts + if (ElixirBurstPvE.CanUse(out act)) return true; + if (ElixirFieldPvE.CanUse(out act)) return true; + + // No Nadi and 3 beasts + if (CelestialRevolutionPvE.CanUse(out act)) return true; + break; + + case MasterfulBlitzUse.RiddleOfFireUse: + if (Player.HasStatus(true, StatusID.RiddleOfFire) || RiddleOfFirePvE.Cooldown.JustUsedAfter(42)) + { + // Both Nadi and 3 beasts + if (PhantomRushPvE.CanUse(out act)) return true; + if (TornadoKickPvE.CanUse(out act)) return true; - // Needing Solar Nadi and has 3 different beasts - if (RisingPhoenixPvE.CanUse(out act)) return true; - if (FlintStrikePvE.CanUse(out act)) return true; + // Needing Solar Nadi and has 3 different beasts + if (RisingPhoenixPvE.CanUse(out act)) return true; + if (FlintStrikePvE.CanUse(out act)) return true; - // Needing Lunar Nadi and has 3 of the same beasts - if (ElixirBurstPvE.CanUse(out act)) return true; - if (ElixirFieldPvE.CanUse(out act)) return true; + // Needing Lunar Nadi and has 3 of the same beasts + if (ElixirBurstPvE.CanUse(out act)) return true; + if (ElixirFieldPvE.CanUse(out act)) return true; - // No Nadi and 3 beasts - if (CelestialRevolutionPvE.CanUse(out act)) return true; + // No Nadi and 3 beasts + if (CelestialRevolutionPvE.CanUse(out act)) return true; + } + break; + } } // 'Because Fire¡¯s Reply grants formless, we have an imposed restriction that we prefer not to use it while under PB, or if we have a formless already.' + 'Cast Fire's Reply after an opo gcd' diff --git a/RotationSolver.Basic/Actions/ActionTargetInfo.cs b/RotationSolver.Basic/Actions/ActionTargetInfo.cs index 422b7bed8..4e092f85a 100644 --- a/RotationSolver.Basic/Actions/ActionTargetInfo.cs +++ b/RotationSolver.Basic/Actions/ActionTargetInfo.cs @@ -189,7 +189,7 @@ public bool GeneralCheck(IBattleChara gameObject, bool skipStatusProvideCheck) { if (!gameObject.IsTargetable) return false; - if (Service.Config.RaiseType == RaiseType.PartyOnly && gameObject.IsAlliance() && !gameObject.IsParty()) + if (Service.Config.RaiseType == RaiseType.PartyOnly && !gameObject.IsParty()) { return false; } diff --git a/RotationSolver.Basic/Actions/BaseAction.cs b/RotationSolver.Basic/Actions/BaseAction.cs index 8bda2184c..c1d0ddd95 100644 --- a/RotationSolver.Basic/Actions/BaseAction.cs +++ b/RotationSolver.Basic/Actions/BaseAction.cs @@ -173,7 +173,7 @@ private bool IsLastAbilityUsable() { return IsLastAbilityv2Usable(); } - return DataCenter.InCombat && (DataCenter.NextAbilityToNextGCD <= Math.Max(ActionManagerHelper.GetCurrentAnimationLock(), DataCenter.MinAnimationLock) + Service.Config.isLastAbilityTimer); + return DataCenter.InCombat && (DataCenter.NextAbilityToNextGCD <= Math.Max(ActionManagerHelper.GetCurrentAnimationLock(), DataCenter.MinAnimationLock) + Service.Config.IsLastAbilityTimer); } private bool IsFirstAbilityUsable() @@ -182,7 +182,7 @@ private bool IsFirstAbilityUsable() { return IsFirstAbilityv2Usable(); } - return DataCenter.InCombat && (DataCenter.NextAbilityToNextGCD >= Math.Max(ActionManagerHelper.GetCurrentAnimationLock(), DataCenter.MinAnimationLock) + Service.Config.isFirstAbilityTimer); + return DataCenter.InCombat && (DataCenter.NextAbilityToNextGCD >= Math.Max(ActionManagerHelper.GetCurrentAnimationLock(), DataCenter.MinAnimationLock) + Service.Config.IsFirstAbilityTimer); } private bool IsLastAbilityv2Usable() diff --git a/RotationSolver.Basic/Configuration/Configs.cs b/RotationSolver.Basic/Configuration/Configs.cs index 402540b26..a96da4f05 100644 --- a/RotationSolver.Basic/Configuration/Configs.cs +++ b/RotationSolver.Basic/Configuration/Configs.cs @@ -382,12 +382,12 @@ public const string [UI("isLastAbilityTimer", Description = "Don't fuck with this if you dont know what it does", Filter = Extra)] [Range(0.100f, 2.500f, ConfigUnitType.Seconds, 0.001f)] - public float isLastAbilityTimer { get; set; } = 0.800f; + public float IsLastAbilityTimer { get; set; } = 0.800f; [UI("isFirstAbilityTimer", Description = "Don't fuck with this if you dont know what it does", Filter = Extra)] [Range(0.100f, 2.500f, ConfigUnitType.Seconds, 0.001f)] - public float isFirstAbilityTimer { get; set; } = 0.600f; + public float IsFirstAbilityTimer { get; set; } = 0.600f; [UI("Auto turn off RSR when combat is over more for more then...", Parent = nameof(AutoOffAfterCombat))] @@ -478,7 +478,7 @@ public const string Filter = HealingActionCondition, Section = 3)] private static readonly bool _chocoboPartyMember = false; - [ConditionBool, UI("Treat focus targetted player as party member", Description = "Experimental.", + [ConditionBool, UI("Treat focus targeted player as party member in alliance raids", Description = "Experimental, includes Chaotic.", Filter = HealingActionCondition, Section = 3)] private static readonly bool _focusTargetIsParty = false; diff --git a/RotationSolver.Basic/DataCenter.cs b/RotationSolver.Basic/DataCenter.cs index 0fb2535b5..45d633b2b 100644 --- a/RotationSolver.Basic/DataCenter.cs +++ b/RotationSolver.Basic/DataCenter.cs @@ -271,8 +271,8 @@ public static float GCDTime(uint gcdCount = 0, float offset = 0) public static bool LastAbilityv2 => DataCenter.InCombat && !ActionHelper.CanUseGCD && (ActionManagerHelper.GetCurrentAnimationLock() == 0) && !Player.Object.IsCasting && (DataCenter.DefaultGCDElapsed >= DataCenter.DefaultGCDRemain); public static bool FirstAbilityv2 => DataCenter.InCombat && !ActionHelper.CanUseGCD && (ActionManagerHelper.GetCurrentAnimationLock() == 0) && !Player.Object.IsCasting && (DataCenter.DefaultGCDRemain >= DataCenter.DefaultGCDElapsed); - public static bool LastAbilityorNot => DataCenter.InCombat && (DataCenter.NextAbilityToNextGCD <= Math.Max(ActionManagerHelper.GetCurrentAnimationLock(), DataCenter.MinAnimationLock) + Service.Config.isLastAbilityTimer); - public static bool FirstAbilityorNot => DataCenter.InCombat && (DataCenter.NextAbilityToNextGCD >= Math.Max(ActionManagerHelper.GetCurrentAnimationLock(), DataCenter.MinAnimationLock) + Service.Config.isFirstAbilityTimer); + public static bool LastAbilityorNot => DataCenter.InCombat && (DataCenter.NextAbilityToNextGCD <= Math.Max(ActionManagerHelper.GetCurrentAnimationLock(), DataCenter.MinAnimationLock) + Service.Config.IsLastAbilityTimer); + public static bool FirstAbilityorNot => DataCenter.InCombat && (DataCenter.NextAbilityToNextGCD >= Math.Max(ActionManagerHelper.GetCurrentAnimationLock(), DataCenter.MinAnimationLock) + Service.Config.IsFirstAbilityTimer); #endregion public static uint[] BluSlots { get; internal set; } = new uint[24]; diff --git a/RotationSolver.Basic/Helpers/ObjectHelper.cs b/RotationSolver.Basic/Helpers/ObjectHelper.cs index 042b7abe0..f07acbcd6 100644 --- a/RotationSolver.Basic/Helpers/ObjectHelper.cs +++ b/RotationSolver.Basic/Helpers/ObjectHelper.cs @@ -83,7 +83,7 @@ internal static unsafe bool IsOthersPlayers(this IGameObject obj) internal static bool IsAttackable(this IBattleChara battleChara) { - if (battleChara.IsAlliance() == true) return false; + if (battleChara.IsAllianceMember() == true) return false; // Dead. if (Service.Config.FilterOneHpInvincible && battleChara.CurrentHp <= 1) return false; @@ -245,7 +245,7 @@ internal static unsafe bool IsEnemy(this IGameObject obj) => obj != null && ActionManager.CanUseActionOnTarget((uint)ActionID.BlizzardPvE, obj.Struct()); - internal static unsafe bool IsAlliance(this IGameObject obj) + internal static unsafe bool IsAllianceMember(this IGameObject obj) => obj.GameObjectId is not 0 && (!DataCenter.IsPvP && DataCenter.IsInAllianceRaid && obj is IPlayerCharacter || DataCenter.IsInAllianceRaid && ActionManager.CanUseActionOnTarget((uint)ActionID.CurePvE, obj.Struct())); @@ -268,7 +268,7 @@ internal static bool IsParty(this IGameObject gameObject) if (Service.Config.FriendlyPartyNpcHealRaise2 && gameObject.GetBattleNPCSubKind() == BattleNpcSubKind.NpcPartyMember) return true; if (Service.Config.ChocoboPartyMember && gameObject.GetNameplateKind() == NameplateKind.PlayerCharacterChocobo) return true; if (Service.Config.FriendlyBattleNpcHeal && gameObject.GetNameplateKind() == NameplateKind.FriendlyBattleNPC) return true; - if (Service.Config.FocusTargetIsParty && gameObject.IsFocusTarget() == true && gameObject.IsAlliance() == true) return true; + if (Service.Config.FocusTargetIsParty && gameObject.IsFocusTarget() == true && gameObject.IsAllianceMember() == true) return true; } return false; } @@ -342,7 +342,7 @@ internal static bool IsTopPriorityNamedHostile(this IGameObject obj) /// internal static bool IsTopPriorityHostile(this IGameObject obj) { - if (obj.IsAlliance() || obj.IsParty() || obj == null) return false; + if (obj.IsAllianceMember() || obj.IsParty() || obj == null) return false; var fateId = DataCenter.FateId; diff --git a/RotationSolver.Basic/Rotations/CustomRotation_OtherInfo.cs b/RotationSolver.Basic/Rotations/CustomRotation_OtherInfo.cs index 34451f823..6ab48033d 100644 --- a/RotationSolver.Basic/Rotations/CustomRotation_OtherInfo.cs +++ b/RotationSolver.Basic/Rotations/CustomRotation_OtherInfo.cs @@ -84,7 +84,7 @@ partial class CustomRotation /// Whether the number of party members is 8. /// [Description("Is Full Party")] - public static bool IsFullParty => PartyMembers.Count() is 8; + public static bool IsFullParty => PartyMembers.Count() is 8 or 9; /// /// party members HP. diff --git a/RotationSolver/UI/RotationConfigWindow.cs b/RotationSolver/UI/RotationConfigWindow.cs index c47744cf4..484a7e5a5 100644 --- a/RotationSolver/UI/RotationConfigWindow.cs +++ b/RotationSolver/UI/RotationConfigWindow.cs @@ -2882,7 +2882,7 @@ private static unsafe void DrawTargetData() ImGui.Text($"Is Alive: {battleChara.IsAlive()}"); ImGui.Text($"Is Party: {battleChara.IsParty()}"); ImGui.Text($"Is Healer: {battleChara.IsJobCategory(JobRole.Healer)}"); - ImGui.Text($"Is Alliance: {battleChara.IsAlliance()}"); + ImGui.Text($"Is Alliance: {battleChara.IsAllianceMember()}"); ImGui.Text($"Is Enemy: {battleChara.IsEnemy()}"); ImGui.Text($"Distance To Player: {battleChara.DistanceToPlayer()}"); ImGui.Text($"Is In EnemiesList: {battleChara.IsInEnemiesList()}"); diff --git a/RotationSolver/Updaters/TargetUpdater.cs b/RotationSolver/Updaters/TargetUpdater.cs index f15bfe083..8c0902f75 100644 --- a/RotationSolver/Updaters/TargetUpdater.cs +++ b/RotationSolver/Updaters/TargetUpdater.cs @@ -76,7 +76,7 @@ private static unsafe List GetAllianceMembers() { foreach (var target in DataCenter.AllTargets) { - if (ObjectHelper.IsAlliance(target) && target.Character() != null && + if (ObjectHelper.IsAllianceMember(target) && target.Character() != null && target.Character()->CharacterData.OnlineStatus != 15 && target.Character()->CharacterData.OnlineStatus != 5 && target.IsTargetable) {