diff --git a/BasicRotations/Melee/MNK_Default.cs b/BasicRotations/Melee/MNK_Default.cs index 6e0ef6394..ea5a4211b 100644 --- a/BasicRotations/Melee/MNK_Default.cs +++ b/BasicRotations/Melee/MNK_Default.cs @@ -138,7 +138,8 @@ protected override bool AttackAbility(IAction nextGCD, out IAction? act) if (HowlingFistPvE.CanUse(out act, skipAoeCheck: true)) return true; // Howling Fist } else - if (TheForbiddenChakraPvE.CanUse(out act)) return true; + if (SteelPeakPvE.CanUse(out act)) return true; + if (TheForbiddenChakraPvE.CanUse(out act)) return true; // use bh when bh and rof are ready (opener) or ask bh to wait for rof's cd to be close and then use bh if (!CombatElapsedLessGCD(2) diff --git a/ECommons b/ECommons index ffcf0c488..e746021d1 160000 --- a/ECommons +++ b/ECommons @@ -1 +1 @@ -Subproject commit ffcf0c48844596be490b13b697ef8bcac8646e2e +Subproject commit e746021d184cf53e7cbab228e326ffdd5f5c4d3e diff --git a/RotationSolver.Basic/Actions/BaseAction.cs b/RotationSolver.Basic/Actions/BaseAction.cs index 508ed9b66..1fc40b8d0 100644 --- a/RotationSolver.Basic/Actions/BaseAction.cs +++ b/RotationSolver.Basic/Actions/BaseAction.cs @@ -167,14 +167,32 @@ public bool CanUse(out IAction act, bool isLastAbility = false, bool isFirstAbil private bool IsLastAbilityUsable() { + if (Service.Config.UseV2AbilityChecks) + { + return IsLastAbilityv2Usable(); + } return DataCenter.InCombat && (DataCenter.NextAbilityToNextGCD <= Math.Max(ActionManagerHelper.GetCurrentAnimationLock(), DataCenter.MinAnimationLock) + Service.Config.isLastAbilityTimer); } private bool IsFirstAbilityUsable() { + if (Service.Config.UseV2AbilityChecks) + { + return IsFirstAbilityv2Usable(); + } return DataCenter.InCombat && (DataCenter.NextAbilityToNextGCD >= Math.Max(ActionManagerHelper.GetCurrentAnimationLock(), DataCenter.MinAnimationLock) + Service.Config.isFirstAbilityTimer); } + private bool IsLastAbilityv2Usable() + { + return DataCenter.InCombat && (DataCenter.DefaultGCDElapsed >= DataCenter.DefaultGCDRemain); + } + + private bool IsFirstAbilityv2Usable() + { + return DataCenter.InCombat && (DataCenter.DefaultGCDRemain >= DataCenter.DefaultGCDElapsed); + } + private bool IsTimeToKillValid() { return DataCenter.AverageTimeToKill >= Config.TimeToKill && DataCenter.AverageTimeToKill >= Config.TimeToUntargetable; diff --git a/RotationSolver.Basic/Configuration/Configs.cs b/RotationSolver.Basic/Configuration/Configs.cs index 459e769d4..b57d39ab4 100644 --- a/RotationSolver.Basic/Configuration/Configs.cs +++ b/RotationSolver.Basic/Configuration/Configs.cs @@ -334,6 +334,14 @@ public const string Filter = Extra)] private static readonly bool _autoOpenChest = true; + [ConditionBool, UI("Use experimental FirstAbility and LastAbility checks", + Filter = Extra)] + private static readonly bool _useV2AbilityChecks = false; + + [ConditionBool, UI("Enable RSR click counter in main menu", + Filter = Extra)] + private static readonly bool _enableClickingCount = true; + [ConditionBool, UI("Auto close the loot window when auto opened the chest.", Parent = nameof(AutoOpenChest))] private static readonly bool _autoCloseChestWindow = true; diff --git a/RotationSolver.Basic/Data/ObjectListDelay.cs b/RotationSolver.Basic/Data/ObjectListDelay.cs index ae5177e40..4d64df50a 100644 --- a/RotationSolver.Basic/Data/ObjectListDelay.cs +++ b/RotationSolver.Basic/Data/ObjectListDelay.cs @@ -10,8 +10,8 @@ public class ObjectListDelay : IEnumerable where T : IGameObject { private IEnumerable _list = new List(); private readonly Func<(float min, float max)> _getRange; - private SortedList _revealTime = new(); - private readonly Random _ran = new(DateTime.Now.Millisecond); + private Dictionary _revealTime = new(); + private readonly Random _ran = new(); /// /// Initializes a new instance of the class. @@ -41,8 +41,8 @@ public ObjectListDelay(Func getRange) /// The original list of objects. public void Delay(IEnumerable originData) { - var outList = new List(originData.Count()); - var revealTime = new SortedList(); + var outList = new List(); + var revealTime = new Dictionary(); var now = DateTime.Now; foreach (var item in originData) @@ -51,7 +51,7 @@ public void Delay(IEnumerable originData) { var (min, max) = _getRange(); var delaySecond = min + (float)_ran.NextDouble() * (max - min); - time = now + new TimeSpan(0, 0, 0, 0, (int)(delaySecond * 1000)); + time = now + TimeSpan.FromMilliseconds(delaySecond * 1000); } revealTime[item.GameObjectId] = time; diff --git a/RotationSolver.Basic/DataCenter.cs b/RotationSolver.Basic/DataCenter.cs index 7dfc71e77..245aa9661 100644 --- a/RotationSolver.Basic/DataCenter.cs +++ b/RotationSolver.Basic/DataCenter.cs @@ -11,8 +11,6 @@ using RotationSolver.Basic.Configuration; using RotationSolver.Basic.Configuration.Conditions; using RotationSolver.Basic.Rotations.Duties; -using Svg.FilterEffects; -using System.Linq; using Action = Lumina.Excel.Sheets.Action; using CharacterManager = FFXIVClientStructs.FFXIV.Client.Game.Character.CharacterManager; @@ -251,6 +249,9 @@ public static unsafe ushort FateId public static float GCDTime(uint gcdCount = 0, float offset = 0) => ActionManagerHelper.GetDefaultRecastTime() * gcdCount + offset; + 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); #endregion @@ -689,7 +690,7 @@ public static float PartyMembersDifferHP #region Action Record public const float MinAnimationLock = 0.6f; - const int QUEUECAPACITY = 16; + const int QUEUECAPACITY = 32; private static readonly Queue _actions = new(QUEUECAPACITY); private static readonly Queue _damages = new(QUEUECAPACITY); diff --git a/RotationSolver.Basic/Helpers/ObjectHelper.cs b/RotationSolver.Basic/Helpers/ObjectHelper.cs index 163a444a9..4a53e596f 100644 --- a/RotationSolver.Basic/Helpers/ObjectHelper.cs +++ b/RotationSolver.Basic/Helpers/ObjectHelper.cs @@ -460,31 +460,31 @@ internal static float GetTimeToKill(this IBattleChara b, bool wholeTime = false) var objectId = b.GameObjectId; DateTime startTime = DateTime.MinValue; - float thatTimeRatio = 0; + float initialHpRatio = 0; - // Create a copy of the RecordedHP collection to avoid modification during enumeration - var recordedHPCopy = DataCenter.RecordedHP.ToList(); + // Use a snapshot of the RecordedHP collection to avoid modification during enumeration + var recordedHPCopy = DataCenter.RecordedHP.ToArray(); foreach (var (time, hpRatios) in recordedHPCopy) { if (hpRatios.TryGetValue(objectId, out var ratio) && ratio != 1) { startTime = time; - thatTimeRatio = ratio; + initialHpRatio = ratio; break; } } - var timespan = DateTime.Now - startTime; - if (startTime == DateTime.MinValue || timespan < CheckSpan) return float.NaN; + if (startTime == DateTime.MinValue || (DateTime.Now - startTime) < CheckSpan) return float.NaN; - var ratioNow = b.GetHealthRatio(); - if (float.IsNaN(ratioNow)) return float.NaN; + var currentHpRatio = b.GetHealthRatio(); + if (float.IsNaN(currentHpRatio)) return float.NaN; - var ratioReduce = thatTimeRatio - ratioNow; - if (ratioReduce <= 0) return float.NaN; + var hpRatioDifference = initialHpRatio - currentHpRatio; + if (hpRatioDifference <= 0) return float.NaN; - return (float)timespan.TotalSeconds / ratioReduce * (wholeTime ? 1 : ratioNow); + var elapsedTime = (float)(DateTime.Now - startTime).TotalSeconds; + return elapsedTime / hpRatioDifference * (wholeTime ? 1 : currentHpRatio); } /// diff --git a/RotationSolver.Basic/Rotations/CustomRotation_GCD.cs b/RotationSolver.Basic/Rotations/CustomRotation_GCD.cs index 7e2e06b08..b04c25114 100644 --- a/RotationSolver.Basic/Rotations/CustomRotation_GCD.cs +++ b/RotationSolver.Basic/Rotations/CustomRotation_GCD.cs @@ -25,14 +25,6 @@ partial class CustomRotation if (EmergencyGCD(out act)) return act; - IBaseAction.TargetOverride = TargetType.Death; - - if (RaiseSpell(out act, false)) return act; - - if (Service.Config.RaisePlayerByCasting && SwiftcastPvE.Cooldown.IsCoolingDown && RaiseSpell(out act, true)) return act; - - IBaseAction.TargetOverride = null; - if (DataCenter.MergedStatus.HasFlag(AutoStatus.MoveForward) && MoveForwardGCD(out act)) { @@ -78,6 +70,14 @@ partial class CustomRotation if (DataCenter.MergedStatus.HasFlag(AutoStatus.Dispel) && DispelGCD(out act)) return act; + IBaseAction.TargetOverride = TargetType.Death; + + if (RaiseSpell(out act, false)) return act; + + if (Service.Config.RaisePlayerByCasting && SwiftcastPvE.Cooldown.IsCoolingDown && RaiseSpell(out act, true)) return act; + + IBaseAction.TargetOverride = null; + IBaseAction.ShouldEndSpecial = false; IBaseAction.TargetOverride = null; diff --git a/RotationSolver/Commands/RSCommands_Actions.cs b/RotationSolver/Commands/RSCommands_Actions.cs index b40cacc88..18e90bb15 100644 --- a/RotationSolver/Commands/RSCommands_Actions.cs +++ b/RotationSolver/Commands/RSCommands_Actions.cs @@ -70,7 +70,11 @@ public static void DoAction() if (nextAction.Use()) { - OtherConfiguration.RotationSolverRecord.ClickingCount++; + // Check if the setting to enable clicking count increment is enabled + if (Service.Config.EnableClickingCount) + { + OtherConfiguration.RotationSolverRecord.ClickingCount++; + } _lastActionID = nextAction.AdjustedID; _lastUsedTime = DateTime.Now; diff --git a/RotationSolver/UI/RotationConfigWindow.cs b/RotationSolver/UI/RotationConfigWindow.cs index 8e77d0c2f..ca3b36f75 100644 --- a/RotationSolver/UI/RotationConfigWindow.cs +++ b/RotationSolver/UI/RotationConfigWindow.cs @@ -15,6 +15,7 @@ using FFXIVClientStructs.FFXIV.Client.Game; using FFXIVClientStructs.FFXIV.Client.Game.Fate; using FFXIVClientStructs.FFXIV.Client.System.Framework; +using FFXIVClientStructs.FFXIV.Client.System.Scheduler; using FFXIVClientStructs.FFXIV.Common.Component.BGCollision; using Lumina.Excel.Sheets; using RotationSolver.Basic.Configuration; @@ -2612,6 +2613,7 @@ private static void DrawDebug() {() => "Next Action", DrawNextAction }, {() => "Last Action", DrawLastAction }, {() => "Others", DrawOthers }, + {() => "GCD Cooldown Visualization", DrawGCDCooldownStuff }, {() => "Effect", () => { ImGui.Text(Watcher.ShowStrSelf); @@ -2645,11 +2647,11 @@ private static unsafe void DrawStatus() } // VFX info - ImGui.Text("VFX Data:"); - foreach (var item in DataCenter.VfxDataQueue) - { - ImGui.Text(item.ToString()); - } + //ImGui.Text("VFX Data:"); + //foreach (var item in DataCenter.VfxDataQueue) + //{ + // ImGui.Text(item.ToString()); + //} // Check and display VFX casting status ImGui.Text($"Is Casting Tank VFX: {DataCenter.IsCastingTankVfx()}"); @@ -2826,6 +2828,8 @@ private static void DrawNextAction() ImGui.Text(ActionUpdater.NextAction?.Name ?? "null"); ImGui.Text($"LastAbilityorNot: {DataCenter.LastAbilityorNot}"); ImGui.Text($"FirstAbilityorNot: {DataCenter.FirstAbilityorNot}"); + ImGui.Text($"LastAbilityv2: {DataCenter.LastAbilityv2}"); + ImGui.Text($"FirstAbilityv2: {DataCenter.FirstAbilityv2}"); ImGui.Text($"GCD Total: {DataCenter.DefaultGCDTotal}"); ImGui.Text($"GCD Remain: {DataCenter.DefaultGCDRemain}"); ImGui.Text($"GCD Elapsed: {DataCenter.DefaultGCDElapsed}"); @@ -2848,6 +2852,83 @@ private static void DrawOthers() ImGui.Text($"Limit Break: {CustomRotation.LimitBreakLevel}"); } + private static float _maxAnimationLockTime = 0; + + private static void DrawGCDCooldownStuff() + { + ImGui.Text("GCD Cooldown Visualization"); + + ImGui.Text($"GCD Elapsed: {DataCenter.DefaultGCDElapsed}"); + ImGui.Text($"GCD Remain: {DataCenter.DefaultGCDRemain}"); + ImGui.Text($"GCD Total: {DataCenter.DefaultGCDTotal}"); + + // Change text color based on LastAbilityv2 + if (DataCenter.LastAbilityv2) + { + ImGui.PushStyleColor(ImGuiCol.Text, ImGuiColors.ParsedGreen); + ImGui.Text("LastAbilityv2: true"); + } + else + { + ImGui.PushStyleColor(ImGuiCol.Text, ImGuiColors.DalamudRed); + ImGui.Text("LastAbilityv2: false"); + } + ImGui.PopStyleColor(); + + // Change text color based on FirstAbilityv2 + if (DataCenter.FirstAbilityv2) + { + ImGui.PushStyleColor(ImGuiCol.Text, ImGuiColors.ParsedGreen); + ImGui.Text("FirstAbilityv2: true"); + } + else + { + ImGui.PushStyleColor(ImGuiCol.Text, ImGuiColors.DalamudRed); + ImGui.Text("FirstAbilityv2: false"); + } + ImGui.PopStyleColor(); + + // Visualize the GCD and oGCD slots + float gcdTotal = DataCenter.DefaultGCDElapsed + DataCenter.DefaultGCDRemain; + float gcdProgress = DataCenter.DefaultGCDElapsed / gcdTotal; + + // Draw the progress bar + ImGui.ProgressBar(gcdProgress, new Vector2(-1, 0), $"{DataCenter.DefaultGCDElapsed:F2}s / {gcdTotal:F2}s"); + + // Update the maximum Animation Lock Time if the current value is larger + float currentAnimationLockTime = ActionManagerHelper.GetCurrentAnimationLock(); // Assuming you have this value + if (currentAnimationLockTime > _maxAnimationLockTime) + { + _maxAnimationLockTime = currentAnimationLockTime; + } + + // Calculate the position for the Animation Lock Delay marker + float markerPosition = _maxAnimationLockTime / gcdTotal; + + // Draw the marker on the progress bar + Vector2 cursorPos = ImGui.GetCursorPos(); + float progressBarWidth = ImGui.GetContentRegionAvail().X; + float markerXPos = cursorPos.X + (progressBarWidth * markerPosition); + + // Draw the marker on the same line as the progress bar + ImGui.SetCursorPos(new Vector2(markerXPos, cursorPos.Y - ImGui.GetTextLineHeight() / 2)); + ImGui.Text("|"); + + // Check if the marker is hovered and display a tooltip + if (ImGui.IsItemHovered()) + { + ImGui.SetTooltip("Most recent recorded animation lock delay"); + } + + // Reset cursor position + ImGui.SetCursorPos(cursorPos); + + // Add space below the progress bar + ImGui.Dummy(new Vector2(0, 20)); + + // Add any additional visualization for oGCD slots if needed + } + private static void DrawAction(ActionID id, string type) { ImGui.Text($"{type}: {id}"); diff --git a/RotationSolver/Watcher.cs b/RotationSolver/Watcher.cs index cc20b760a..1241d3d2f 100644 --- a/RotationSolver/Watcher.cs +++ b/RotationSolver/Watcher.cs @@ -8,7 +8,6 @@ using Lumina.Excel.Sheets; using RotationSolver.Basic.Configuration; using System.Text.RegularExpressions; -using Action = Lumina.Excel.Sheets.Action; namespace RotationSolver;