From 9190888d976ac17dc8a4f0dd3349f3bf3495bb0c Mon Sep 17 00:00:00 2001
From: LTS-FFXIV <127939494+LTS-FFXIV@users.noreply.github.com>
Date: Sat, 11 Jan 2025 03:33:20 -0600
Subject: [PATCH] Refactor and improve code readability and performance
Refactored multiple classes to improve readability, maintainability, and performance. Introduced new helper methods to encapsulate specific logic, reducing code duplication. Enhanced exception handling and logging for better error management. Updated various methods to use more explicit loops and conditions instead of LINQ queries. Added new configuration options and reorganized existing ones. Improved asynchronous handling in the `OverlayWindow` class and updated several classes to use properties instead of methods for better clarity.
---
BasicRotations/Healer/SGE_Default.cs | 4 +-
BasicRotations/Magical/PCT_Default.cs | 6 +-
BasicRotations/Magical/SMN_Default.cs | 4 +-
BasicRotations/Melee/NIN_Default.cs | 12 +-
Resources/InvincibleStatus.json | 1 -
RotationSolver.Basic/Configuration/Configs.cs | 22 +--
RotationSolver.Basic/Helpers/ObjectHelper.cs | 8 +-
.../Rotations/Basic/NinjaRotation.cs | 2 +-
.../Rotations/Basic/PictomancerRotation.cs | 6 +-
RotationSolver.Basic/Service.cs | 25 ++-
RotationSolver/Commands/RSCommands_Actions.cs | 92 ++-------
.../Commands/RSCommands_BasicInfo.cs | 128 +++++++------
.../Commands/RSCommands_OtherCommand.cs | 179 ++++++++----------
.../RSCommands_StateSpecialCommand.cs | 64 ++++---
RotationSolver/TextureItems/StatusTexture.cs | 27 +++
RotationSolver/UI/CollapsingHeaderGroup.cs | 17 +-
RotationSolver/UI/CooldownWindow.cs | 10 +-
RotationSolver/UI/FontManager.cs | 22 +--
RotationSolver/UI/ImGuiHelper.cs | 105 +++++-----
RotationSolver/UI/ImguiTooltips.cs | 12 +-
RotationSolver/UI/OverlayWindow.cs | 41 ++--
RotationSolver/UI/RotationConfigWindow.cs | 4 +-
RotationSolver/UI/SearchableCollection.cs | 135 ++++++-------
.../Updaters/ActionSequencerUpdater.cs | 67 +++++--
RotationSolver/Updaters/ActionUpdater.cs | 50 +++--
RotationSolver/Updaters/MajorUpdater.cs | 85 +++++----
RotationSolver/Updaters/MovingUpdater.cs | 80 ++++----
RotationSolver/Updaters/PreviewUpdater.cs | 88 +++++----
RotationSolver/Updaters/StateUpdater.cs | 163 +++++++++-------
RotationSolver/Updaters/TargetUpdater.cs | 169 +++++++++--------
30 files changed, 879 insertions(+), 749 deletions(-)
diff --git a/BasicRotations/Healer/SGE_Default.cs b/BasicRotations/Healer/SGE_Default.cs
index 4512d352a..a93bef70f 100644
--- a/BasicRotations/Healer/SGE_Default.cs
+++ b/BasicRotations/Healer/SGE_Default.cs
@@ -154,6 +154,8 @@ protected override bool DefenseSingleAbility(IAction nextGCD, out IAction? act)
[RotationDesc(ActionID.KeracholePvE, ActionID.PhysisPvE, ActionID.HolosPvE, ActionID.IxocholePvE)]
protected override bool HealAreaAbility(IAction nextGCD, out IAction? act)
{
+ if ((!MergedStatus.HasFlag(AutoStatus.DefenseArea) || !MergedStatus.HasFlag(AutoStatus.DefenseSingle)) && PepsisPvE.CanUse(out act)) return true;
+
if (KeracholePvE.CanUse(out act) && EnhancedKeracholeTrait.EnoughLevel) return true;
if (IxocholePvE.CanUse(out act)) return true;
@@ -213,8 +215,6 @@ protected override bool GeneralAbility(IAction nextGCD, out IAction? act)
if (SoteriaPvE.CanUse(out act) && PartyMembers.Any(b => b.HasStatus(true, StatusID.Kardion) && b.GetHealthRatio() < HealthSingleAbility)) return true;
- if (PepsisPvE.CanUse(out act)) return true;
-
return base.GeneralAbility(nextGCD, out act);
}
#endregion
diff --git a/BasicRotations/Magical/PCT_Default.cs b/BasicRotations/Magical/PCT_Default.cs
index 09f14940a..e64642a78 100644
--- a/BasicRotations/Magical/PCT_Default.cs
+++ b/BasicRotations/Magical/PCT_Default.cs
@@ -5,8 +5,6 @@
[Api(4)]
public sealed class PCT_Default : PictomancerRotation
{
- private const float CountdownBuffer = 0.2f;
-
#region Config Options
[RotationConfig(CombatType.PvE, Name = "Use HolyInWhite or CometInBlack while moving")]
public bool HolyCometMoving { get; set; } = true;
@@ -41,7 +39,7 @@ public sealed class PCT_Default : PictomancerRotation
if (!LandscapeMotifDrawn && StarrySkyMotifPvE.CanUse(out act) && !HasHyperphantasia) return act;
}
- if (remainTime <= RainbowDripPvE.Info.CastTime + CountdownBuffer + CountDownAhead && RainbowDripPvE.CanUse(out act))
+ if (remainTime <= RainbowDripPvE.Info.CastTime + CountDownAhead && RainbowDripPvE.CanUse(out act))
{
return act;
}
@@ -146,7 +144,7 @@ protected override bool GeneralGCD(out IAction? act)
if (Paint == HolyCometMax && HolyInWhitePvE.CanUse(out act)) return true;
// Landscape Paining Burst
- if (RainbowDripPvE.CanUse(out act)) return true;
+ if (HasRainbowBright && RainbowDripPvE.CanUse(out act, skipCastingCheck: HasRainbowBright)) return true;
if (StarPrismPvE.CanUse(out act)) return true;
// timings for motif casting
diff --git a/BasicRotations/Magical/SMN_Default.cs b/BasicRotations/Magical/SMN_Default.cs
index 955e557a1..da1f319ee 100644
--- a/BasicRotations/Magical/SMN_Default.cs
+++ b/BasicRotations/Magical/SMN_Default.cs
@@ -42,7 +42,7 @@ public enum SummonOrderType : byte
[RotationConfig(CombatType.PvE, Name = "Use this if there's no other raid buff in your party")]
public bool SecondTypeOpenerLogic { get; set; } = false;
- [RotationConfig(CombatType.PvE, Name = "Use Physick")]
+ [RotationConfig(CombatType.PvE, Name = "Use Physick above level 30")]
public bool Healbot { get; set; } = false;
#endregion
@@ -169,7 +169,7 @@ protected override bool EmergencyAbility(IAction nextGCD, out IAction? act)
[RotationDesc(ActionID.PhysickPvE)]
protected override bool HealSingleGCD(out IAction? act)
{
- if ((Healbot || Player.Level <= 20) && PhysickPvE.CanUse(out act)) return true;
+ if ((Healbot || Player.Level <= 30) && PhysickPvE.CanUse(out act)) return true;
return base.HealSingleGCD(out act);
}
diff --git a/BasicRotations/Melee/NIN_Default.cs b/BasicRotations/Melee/NIN_Default.cs
index 85a163591..a736ebdcf 100644
--- a/BasicRotations/Melee/NIN_Default.cs
+++ b/BasicRotations/Melee/NIN_Default.cs
@@ -19,13 +19,15 @@ public sealed class NIN_Default : NinjaRotation
[RotationConfig(CombatType.PvE, Name = "Use Mudras Outside of Combat when enemies are near")]
public bool CommbatMudra { get; set; } = true;
+
+ [RotationConfig(CombatType.PvE, Name = "Use Forked Raiju instead of Fleeting Raiju if you are outside of range (Dangerous)")]
+ public bool ForkedUse { get; set; } = false;
#endregion
#region Tracking Properties
// Properties to track RabbitMediumPvE failures and related information.
private int _rabbitMediumFailures = 0;
private IBaseAction? _lastNinActionAim = null;
- private IAction? _followUpGCDAction = null;
#endregion
#region CountDown Logic
@@ -399,13 +401,17 @@ protected override bool GeneralGCD(out IAction? act)
{
act = null;
- if (RabbitMediumPvE.CanUse(out act)) return true;
+ if (_ninActionAim == null && RabbitMediumPvE.CanUse(out act)) return true;
if (!IsExecutingMudra && (InTrickAttack || InMug) && NoNinjutsu && !HasRaijuReady
&& !Player.HasStatus(true, StatusID.TenChiJin)
&& PhantomKamaitachiPvE.CanUse(out act)) return true;
- if (!IsExecutingMudra && FleetingRaijuPvE.CanUse(out act)) return true;
+ if (!IsExecutingMudra)
+ {
+ if (FleetingRaijuPvE.CanUse(out act)) return true;
+ if (ForkedUse && ForkedRaijuPvE.CanUse(out act)) return true;
+ }
if ((InCombat || (CommbatMudra && HasHostilesInMaxRange)) && ChoiceNinjutsu(out act)) return true;
if (((!InCombat && CommbatMudra && HasHostilesInMaxRange) || !CombatElapsedLess(7)) && DoNinjutsu(out act)) return true;
diff --git a/Resources/InvincibleStatus.json b/Resources/InvincibleStatus.json
index febef3b0c..2b72fdfa2 100644
--- a/Resources/InvincibleStatus.json
+++ b/Resources/InvincibleStatus.json
@@ -1,7 +1,6 @@
[
151,
198,
- 385,
469,
592,
1240,
diff --git a/RotationSolver.Basic/Configuration/Configs.cs b/RotationSolver.Basic/Configuration/Configs.cs
index 375eca895..f073238d6 100644
--- a/RotationSolver.Basic/Configuration/Configs.cs
+++ b/RotationSolver.Basic/Configuration/Configs.cs
@@ -375,10 +375,6 @@ public const string
[ConditionBool, UI("Record knockback actions", Filter = List2)]
private static readonly bool _recordKnockbackies = false;
- [JobConfig, UI("Override Action Ahead Timer", Description = "If you don't know what this does, you don't need to modify it",
- Filter = BasicTimer)]
- private static readonly bool _overrideActionAheadTimer = false;
-
[UI("Use additional conditions", Filter = BasicParams)]
public bool UseAdditionalConditions { get; set; } = false;
@@ -634,9 +630,9 @@ public const string
public int AutoDefenseNumber { get; set; } = 2;
#endregion
-
+
#region PvP
-
+
[JobConfig, UI("Ignore Invincibility for PvP purposes.", Filter = PvPSpecificControls)]
private readonly bool _ignorePvPInvincibility = false;
@@ -687,15 +683,19 @@ public const string
PvEFilter = JobFilterType.Tank)]
private readonly float _healthForAutoDefense = 1;
+ [JobConfig, UI("Engage settings", Filter = TargetConfig, PvPFilter = JobFilterType.NoJob)]
+ private readonly TargetHostileType _hostileType = TargetHostileType.AllTargetsWhenSoloInDuty;
+
+ [JobConfig, UI("Override Action Ahead Timer", Description = "If you don't know what this does, you don't need to modify it",
+ Filter = BasicTimer)]
+ private readonly bool _overrideActionAheadTimer = false;
+
[JobConfig, Range(0, 1.0f, ConfigUnitType.Seconds)]
[UI("Action Ahead (How far in advance of GCD being available RSR will try to queue the next GCD)",
- Description = "This setting controls how many oGCDs RSR will try to fit in a single GCD window\nLower numbers mean more oGCDs, but potentially more GCD clipping",
- Parent = nameof(OverrideActionAheadTimer))]
+ Description = "This setting controls how many oGCDs RSR will try to fit in a single GCD window\nLower numbers mean more oGCDs, but potentially more GCD clipping",
+ Parent = nameof(OverrideActionAheadTimer))]
private readonly float _action4head = 0.4f;
- [JobConfig, UI("Engage settings", Filter = TargetConfig, PvPFilter = JobFilterType.NoJob)]
- private readonly TargetHostileType _hostileType = TargetHostileType.AllTargetsWhenSoloInDuty;
-
[JobConfig]
private readonly string _PvPRotationChoice = string.Empty;
diff --git a/RotationSolver.Basic/Helpers/ObjectHelper.cs b/RotationSolver.Basic/Helpers/ObjectHelper.cs
index 6f767c7e1..bf231309a 100644
--- a/RotationSolver.Basic/Helpers/ObjectHelper.cs
+++ b/RotationSolver.Basic/Helpers/ObjectHelper.cs
@@ -134,12 +134,12 @@ internal static bool IsAttackable(this IBattleChara battleChara)
{
TargetHostileType.AllTargetsCanAttack => true,
TargetHostileType.TargetsHaveTarget => battleChara.TargetObject is IBattleChara,
- TargetHostileType.AllTargetsWhenSolo => DataCenter.PartyMembers.Count() < 2 || battleChara.TargetObject is IBattleChara,
- TargetHostileType.AllTargetsWhenSoloInDuty => (DataCenter.PartyMembers.Count() < 2 && (Svc.Condition[ConditionFlag.BoundByDuty] || Svc.Condition[ConditionFlag.BoundByDuty56]))
+ TargetHostileType.AllTargetsWhenSolo => DataCenter.PartyMembers.Count < 2 || battleChara.TargetObject is IBattleChara,
+ TargetHostileType.AllTargetsWhenSoloInDuty => (DataCenter.PartyMembers.Count < 2 && (Svc.Condition[ConditionFlag.BoundByDuty] || Svc.Condition[ConditionFlag.BoundByDuty56]))
|| battleChara.TargetObject is IBattleChara,
TargetHostileType.TargetIsInEnemiesList => battleChara.TargetObject is IBattleChara target && target.IsInEnemiesList(),
- TargetHostileType.AllTargetsWhenSoloTargetIsInEnemiesList => (DataCenter.PartyMembers.Count() < 2 && (Svc.Condition[ConditionFlag.BoundByDuty] || Svc.Condition[ConditionFlag.BoundByDuty56])) || battleChara.TargetObject is IBattleChara target && target.IsInEnemiesList(),
- TargetHostileType.AllTargetsWhenSoloInDutyTargetIsInEnemiesList => DataCenter.PartyMembers.Count() < 2 || battleChara.TargetObject is IBattleChara target && target.IsInEnemiesList(),
+ TargetHostileType.AllTargetsWhenSoloTargetIsInEnemiesList => (DataCenter.PartyMembers.Count < 2 && (Svc.Condition[ConditionFlag.BoundByDuty] || Svc.Condition[ConditionFlag.BoundByDuty56])) || battleChara.TargetObject is IBattleChara target && target.IsInEnemiesList(),
+ TargetHostileType.AllTargetsWhenSoloInDutyTargetIsInEnemiesList => DataCenter.PartyMembers.Count < 2 || battleChara.TargetObject is IBattleChara target && target.IsInEnemiesList(),
_ => true,
};
}
diff --git a/RotationSolver.Basic/Rotations/Basic/NinjaRotation.cs b/RotationSolver.Basic/Rotations/Basic/NinjaRotation.cs
index 47b8d4574..fa66fa064 100644
--- a/RotationSolver.Basic/Rotations/Basic/NinjaRotation.cs
+++ b/RotationSolver.Basic/Rotations/Basic/NinjaRotation.cs
@@ -26,7 +26,7 @@ partial class NinjaRotation
///
/// Do you need to prep or currently use shadowwalker
///
- public bool ShadowWalkerNeeded => (TrickAttackPvE.EnoughLevel && TrickAttackPvE.Cooldown.WillHaveOneCharge(18)) || (KunaisBanePvE.EnoughLevel && KunaisBanePvE.Cooldown.WillHaveOneCharge(18)) || (MeisuiPvE.EnoughLevel && MeisuiPvE.Cooldown.WillHaveOneCharge(18));
+ public bool ShadowWalkerNeeded => (TrickAttackPvE.EnoughLevel && TrickAttackPvE.Cooldown.WillHaveOneCharge(18)) || (KunaisBanePvE.EnoughLevel && KunaisBanePvE.Cooldown.WillHaveOneCharge(18));
///
/// Determines if Trick Attack is in its effective period.
diff --git a/RotationSolver.Basic/Rotations/Basic/PictomancerRotation.cs b/RotationSolver.Basic/Rotations/Basic/PictomancerRotation.cs
index cf9518bb8..ab82c9b86 100644
--- a/RotationSolver.Basic/Rotations/Basic/PictomancerRotation.cs
+++ b/RotationSolver.Basic/Rotations/Basic/PictomancerRotation.cs
@@ -347,6 +347,11 @@ public override void DisplayStatus()
///
public static bool HasStarryMuse => !Player.WillStatusEnd(0, true, StatusID.StarryMuse);
+ ///
+ /// Indicates if the player has Rainbow Bright.
+ ///
+ public static bool HasRainbowBright => !Player.WillStatusEnd(0, true, StatusID.RainbowBright);
+
///
/// Holds the remaining amount of HammerTime stacks
///
@@ -500,7 +505,6 @@ static partial void ModifyCometInBlackPvE(ref ActionSetting setting)
static partial void ModifyRainbowDripPvE(ref ActionSetting setting)
{
- setting.StatusNeed = [StatusID.RainbowBright];
setting.CreateConfig = () => new ActionConfig()
{
AoeCount = 1,
diff --git a/RotationSolver.Basic/Service.cs b/RotationSolver.Basic/Service.cs
index 75e4e5a28..f97ee8214 100644
--- a/RotationSolver.Basic/Service.cs
+++ b/RotationSolver.Basic/Service.cs
@@ -25,10 +25,8 @@ internal class Service : IDisposable
private EzHook actorVfxCreateHook = null!;
private unsafe delegate IntPtr ActorVfxCreateDelegate2(char* a1, nint a2, nint a3, float a4, char a5, ushort a6, char a7);
-
// From https://GitHub.com/PunishXIV/Orbwalker/blame/master/Orbwalker/Memory.cs#L74-L76
- [Signature("F3 0F 10 05 ?? ?? ?? ?? 0F 2E C7", ScanType = ScanType.StaticAddress,
- Fallibility = Fallibility.Infallible)]
+ [Signature("F3 0F 10 05 ?? ?? ?? ?? 0F 2E C7", ScanType = ScanType.StaticAddress, Fallibility = Fallibility.Infallible)]
static IntPtr forceDisableMovementPtr = IntPtr.Zero;
private static unsafe ref int ForceDisableMovement => ref *(int*)(forceDisableMovementPtr + 4);
@@ -69,8 +67,6 @@ private unsafe IntPtr ActorVfxCreateDetour(char* a1, nint a2, nint a3, float a4,
throw new Exception("Failed to create object reference during VfxCreateDetour");
}
- //Svc.Log.Verbose($"{obj.Name} is casting {path}");
-
var newVfx = new VfxNewData(obj.GameObjectId, path);
DataCenter.VfxDataQueue.Add(newVfx);
}
@@ -122,7 +118,6 @@ public static ActionID GetAdjustedActionId(ActionID id)
public static unsafe uint GetAdjustedActionId(uint id)
=> ActionManager.Instance()->GetAdjustedActionId(id);
-
private static readonly ConcurrentDictionary AddonCache = new();
///
@@ -159,11 +154,23 @@ public static IEnumerable GetAddons() where T : struct
///
/// Releases unmanaged resources and performs other cleanup operations.
///
- public void Dispose()
+ protected virtual void Dispose(bool disposing)
{
- if (!_canMove && ForceDisableMovement > 0)
+ if (disposing)
{
- ForceDisableMovement--;
+ if (!_canMove && ForceDisableMovement > 0)
+ {
+ ForceDisableMovement--;
+ }
}
}
+
+ ///
+ /// Releases unmanaged resources and performs other cleanup operations.
+ ///
+ public void Dispose()
+ {
+ Dispose(true);
+ GC.SuppressFinalize(this);
+ }
}
\ No newline at end of file
diff --git a/RotationSolver/Commands/RSCommands_Actions.cs b/RotationSolver/Commands/RSCommands_Actions.cs
index 97a53ebd1..a8b470dd9 100644
--- a/RotationSolver/Commands/RSCommands_Actions.cs
+++ b/RotationSolver/Commands/RSCommands_Actions.cs
@@ -2,7 +2,6 @@
using ECommons.DalamudServices;
using ECommons.ExcelServices;
using ECommons.GameHelpers;
-using FFXIVClientStructs.FFXIV.Client.Game.Character;
using RotationSolver.Basic.Configuration;
using RotationSolver.Updaters;
@@ -17,6 +16,7 @@ public static partial class RSCommands
internal static uint _lastActionID;
static float _lastCountdownTime = 0;
static Job _previousJob = Job.ADV;
+ static readonly Random random = new();
public static void IncrementState()
{
@@ -38,7 +38,7 @@ internal static unsafe bool CanDoAnAction(bool isGCD)
if (!Player.Available) return false;
// Do not click the button in random time.
- if (DateTime.Now - _lastClickTime < TimeSpan.FromMilliseconds(new Random().Next(
+ if (DateTime.Now - _lastClickTime < TimeSpan.FromMilliseconds(random.Next(
(int)(Service.Config.ClickingDelay.X * 1000), (int)(Service.Config.ClickingDelay.Y * 1000)))) return false;
_lastClickTime = DateTime.Now;
@@ -127,7 +127,7 @@ static void PulseSimulation(uint id)
started = true;
try
{
- int pulseCount = new Random().Next((int)Service.Config.KeyboardNoise.X, (int)Service.Config.KeyboardNoise.Y);
+ int pulseCount = random.Next((int)Service.Config.KeyboardNoise.X, (int)Service.Config.KeyboardNoise.Y);
PulseAction(id, pulseCount);
}
catch (Exception ex)
@@ -151,17 +151,14 @@ static void PulseAction(uint id, int remainingPulses)
}
PreviewUpdater.PulseActionBar(id);
- var time = Service.Config.ClickingDelay.X + new Random().NextDouble() * (Service.Config.ClickingDelay.Y - Service.Config.ClickingDelay.X);
+ var time = Service.Config.ClickingDelay.X + random.NextDouble() * (Service.Config.ClickingDelay.Y - Service.Config.ClickingDelay.X);
Svc.Framework.RunOnTick(() =>
{
PulseAction(id, remainingPulses - 1);
}, TimeSpan.FromSeconds(time));
}
- internal static void ResetSpecial()
- {
- DoSpecialCommandType(SpecialCommandType.EndSpecial, false);
- }
+ internal static void ResetSpecial() => DoSpecialCommandType(SpecialCommandType.EndSpecial, false);
internal static void CancelState()
{
@@ -181,62 +178,28 @@ internal static void UpdateRotationState()
var target = DataCenter.AllHostileTargets
.FirstOrDefault(t => t != null && t.TargetObjectId == Player.Object?.GameObjectId);
- if (Svc.Condition[ConditionFlag.LoggingOut])
- {
- CancelState();
- }
- else if (Service.Config.AutoOffWhenDead
- && Player.Available
- && Player.Object?.CurrentHp == 0)
- {
- CancelState();
- }
- else if (Service.Config.AutoOffWhenDeadPvP && DataCenter.Territory?.IsPvP == true
- && Player.Available
- && Player.Object?.CurrentHp == 0)
- {
- CancelState();
- }
- else if (Service.Config.AutoOffCutScene
- && Svc.Condition[ConditionFlag.OccupiedInCutSceneEvent])
- {
- CancelState();
- }
- else if (Service.Config.AutoOffSwitchClass
- && Player.Job != _previousJob)
- {
- _previousJob = Player.Job;
- CancelState();
- }
- else if (Service.Config.AutoOffBetweenArea
- && (Svc.Condition[ConditionFlag.BetweenAreas]
- || Svc.Condition[ConditionFlag.BetweenAreas51]))
+ if (Svc.Condition[ConditionFlag.LoggingOut] ||
+ (Service.Config.AutoOffWhenDead && Player.Available && Player.Object?.CurrentHp == 0) ||
+ (Service.Config.AutoOffWhenDeadPvP && DataCenter.Territory?.IsPvP == true && Player.Available && Player.Object?.CurrentHp == 0) ||
+ (Service.Config.AutoOffCutScene && Svc.Condition[ConditionFlag.OccupiedInCutSceneEvent]) ||
+ (Service.Config.AutoOffSwitchClass && Player.Job != _previousJob) ||
+ (Service.Config.AutoOffBetweenArea && (Svc.Condition[ConditionFlag.BetweenAreas] || Svc.Condition[ConditionFlag.BetweenAreas51])) ||
+ (Service.Config.CancelStateOnCombatBeforeCountdown && Service.CountDownTime > 0.2f && DataCenter.InCombat) ||
+ (ActionUpdater.AutoCancelTime != DateTime.MinValue && DateTime.Now > ActionUpdater.AutoCancelTime) ||
+ (DataCenter.RightSet.SwitchCancelConditionSet?.IsTrue(DataCenter.RightNowRotation) ?? false))
{
CancelState();
+ if (Player.Job != _previousJob) _previousJob = Player.Job;
+ if (ActionUpdater.AutoCancelTime != DateTime.MinValue) ActionUpdater.AutoCancelTime = DateTime.MinValue;
}
-
- // Auto manual on being attacked by someone.
- else if (Service.Config.StartOnAttackedBySomeone
- && target != null
- && !target.IsDummy())
+ else if (Service.Config.StartOnAttackedBySomeone && target != null && !target.IsDummy())
{
if (!DataCenter.State)
{
DoStateCommandType(StateCommandType.Manual);
}
}
-
- // Cancel state if combat starts before countdown is finished
- else if (Service.Config.CancelStateOnCombatBeforeCountdown
- && Service.CountDownTime > 0.2f
- && DataCenter.InCombat)
- {
- CancelState();
- }
-
- // Auto start at count down.
- else if (Service.Config.StartOnCountdown
- && Service.CountDownTime > 0)
+ else if (Service.Config.StartOnCountdown && Service.CountDownTime > 0)
{
_lastCountdownTime = Service.CountDownTime;
if (!DataCenter.State)
@@ -251,27 +214,11 @@ internal static void UpdateRotationState()
}
}
}
-
- // Holds state if countdown is running and setting is enabled
else if (Service.Config.StartOnCountdown && Service.CountDownTime == 0 && _lastCountdownTime > 0.2f)
{
_lastCountdownTime = 0;
CancelState();
}
-
- // Cancel when after combat.
- else if (ActionUpdater.AutoCancelTime != DateTime.MinValue
- && DateTime.Now > ActionUpdater.AutoCancelTime)
- {
- CancelState();
- ActionUpdater.AutoCancelTime = DateTime.MinValue;
- }
-
- // Auto switch conditions.
- else if (DataCenter.RightSet.SwitchCancelConditionSet?.IsTrue(DataCenter.RightNowRotation) ?? false)
- {
- CancelState();
- }
else if (DataCenter.RightSet.SwitchManualConditionSet?.IsTrue(DataCenter.RightNowRotation) ?? false)
{
if (!DataCenter.State)
@@ -292,6 +239,5 @@ internal static void UpdateRotationState()
Svc.Log.Error(ex, "Exception in UpdateRotationState");
}
}
-
}
-}
\ No newline at end of file
+}
diff --git a/RotationSolver/Commands/RSCommands_BasicInfo.cs b/RotationSolver/Commands/RSCommands_BasicInfo.cs
index f47446482..72bfa1be4 100644
--- a/RotationSolver/Commands/RSCommands_BasicInfo.cs
+++ b/RotationSolver/Commands/RSCommands_BasicInfo.cs
@@ -2,80 +2,88 @@
using ECommons.DalamudServices;
using RotationSolver.Data;
-
-namespace RotationSolver.Commands;
-
-public static partial class RSCommands
+namespace RotationSolver.Commands
{
- internal static void Enable()
+ public static partial class RSCommands
{
- Svc.Commands.AddHandler(Service.COMMAND, new CommandInfo(OnCommand)
- {
- HelpMessage = UiString.Commands_Rotation.GetDescription(),
- ShowInHelp = true,
- });
- Svc.Commands.AddHandler(Service.ALTCOMMAND, new CommandInfo(OnCommand)
+ internal static void Enable()
{
- HelpMessage = UiString.Commands_Rotation.GetDescription(),
- ShowInHelp = true,
- });
- }
-
- internal static void Disable()
- {
- Svc.Commands.RemoveHandler(Service.COMMAND);
- Svc.Commands.RemoveHandler(Service.ALTCOMMAND);
- }
-
- private static void OnCommand(string command, string arguments)
- {
- DoOneCommand(arguments);
- }
-
- private static void DoOneCommand(string str)
- {
- if (str.ToLower() == "cancel") str = "off";
- if (TryGetOneEnum(str, out var stateType))
- {
- var intStr = str.Split(' ', StringSplitOptions.RemoveEmptyEntries).LastOrDefault();
- if (!int.TryParse(intStr, out var index)) index = -1;
- DoStateCommandType(stateType, index);
- }
- else if (TryGetOneEnum(str, out var specialType))
- {
- DoSpecialCommandType(specialType);
+ Svc.Commands.AddHandler(Service.COMMAND, new CommandInfo(OnCommand)
+ {
+ HelpMessage = UiString.Commands_Rotation.GetDescription(),
+ ShowInHelp = true,
+ });
+ Svc.Commands.AddHandler(Service.ALTCOMMAND, new CommandInfo(OnCommand)
+ {
+ HelpMessage = UiString.Commands_Rotation.GetDescription(),
+ ShowInHelp = true,
+ });
}
- else if (TryGetOneEnum(str, out var otherType))
+
+ internal static void Disable()
{
- DoOtherCommand(otherType, str[otherType.ToString().Length..].Trim());
+ Svc.Commands.RemoveHandler(Service.COMMAND);
+ Svc.Commands.RemoveHandler(Service.ALTCOMMAND);
}
- else
+
+ private static void OnCommand(string command, string arguments)
{
- RotationSolverPlugin.OpenConfigWindow();
+ DoOneCommand(arguments);
}
- }
- private static bool TryGetOneEnum(string str, out T type) where T : struct, Enum
- {
- type = default;
- try
+ private static void DoOneCommand(string command)
{
- type = Enum.GetValues().First(c => str.StartsWith(c.ToString(), StringComparison.OrdinalIgnoreCase));
- return true;
+ if (command.Equals("cancel", StringComparison.OrdinalIgnoreCase))
+ {
+ command = "off";
+ }
+
+ if (TryGetOneEnum(command, out var stateType))
+ {
+ var indexStr = command.Split(' ', StringSplitOptions.RemoveEmptyEntries).LastOrDefault();
+ if (!int.TryParse(indexStr, out var index))
+ {
+ index = -1;
+ }
+ DoStateCommandType(stateType, index);
+ }
+ else if (TryGetOneEnum(command, out var specialType))
+ {
+ DoSpecialCommandType(specialType);
+ }
+ else if (TryGetOneEnum(command, out var otherType))
+ {
+ var extraCommand = command.Substring(otherType.ToString().Length).Trim();
+ DoOtherCommand(otherType, extraCommand);
+ }
+ else
+ {
+ RotationSolverPlugin.OpenConfigWindow();
+ }
}
- catch
+
+ private static bool TryGetOneEnum(string command, out T type) where T : struct, Enum
{
- return false;
+ type = default;
+ try
+ {
+ type = Enum.GetValues().First(c => command.StartsWith(c.ToString(), StringComparison.OrdinalIgnoreCase));
+ return true;
+ }
+ catch (InvalidOperationException)
+ {
+ return false;
+ }
}
- }
- internal static string GetCommandStr(this Enum command, string extraCommand = "")
- {
- var cmdStr = Service.COMMAND + " " + command.ToString();
- if (!string.IsNullOrEmpty(extraCommand))
+ internal static string GetCommandStr(this Enum command, string extraCommand = "")
{
- cmdStr += " " + extraCommand;
+ var cmdStr = $"{Service.COMMAND} {command}";
+ if (!string.IsNullOrEmpty(extraCommand))
+ {
+ cmdStr += $" {extraCommand}";
+ }
+ return cmdStr;
}
- return cmdStr;
}
-}
+}
\ No newline at end of file
diff --git a/RotationSolver/Commands/RSCommands_OtherCommand.cs b/RotationSolver/Commands/RSCommands_OtherCommand.cs
index ab2726848..e5caab6b5 100644
--- a/RotationSolver/Commands/RSCommands_OtherCommand.cs
+++ b/RotationSolver/Commands/RSCommands_OtherCommand.cs
@@ -1,8 +1,8 @@
using ECommons.DalamudServices;
using RotationSolver.Basic.Configuration;
using RotationSolver.Data;
-
using RotationSolver.Updaters;
+using System.Reflection;
namespace RotationSolver.Commands;
@@ -13,10 +13,7 @@ private static void DoOtherCommand(OtherCommandType otherType, string str)
switch (otherType)
{
case OtherCommandType.Rotations:
- var customCombo = DataCenter.RightNowRotation;
- if (customCombo == null) return;
-
- DoRotationCommand(customCombo, str);
+ ExecuteRotationCommand(str);
break;
case OtherCommandType.DoActions:
@@ -37,6 +34,14 @@ private static void DoOtherCommand(OtherCommandType otherType, string str)
}
}
+ private static void ExecuteRotationCommand(string str)
+ {
+ var customCombo = DataCenter.RightNowRotation;
+ if (customCombo == null) return;
+
+ DoRotationCommand(customCombo, str);
+ }
+
private static void DoSettingCommand(string str)
{
var strs = str.Split(' ', 3);
@@ -57,65 +62,22 @@ private static void DoSettingCommand(string str)
if (settingName.Equals("TargetingTypes", StringComparison.OrdinalIgnoreCase))
{
- HandleTargetingTypesCommand(settingName, command);
+ HandleTargetingTypesCommand(command);
return;
}
+ UpdateSetting(settingName, command);
+ }
+
+ private static void UpdateSetting(string settingName, string? command)
+ {
foreach (var property in typeof(Configs).GetRuntimeProperties().Where(p => p.GetMethod?.IsPublic ?? false))
{
if (!settingName.Equals(property.Name, StringComparison.OrdinalIgnoreCase))
continue;
- var type = property.PropertyType;
- if (type == typeof(ConditionBoolean))
- type = typeof(bool);
-
- object? convertedValue = null;
- bool valueParsedSuccessfully = true;
-
- if (type.IsEnum)
- {
- valueParsedSuccessfully = Enum.TryParse(type, command, ignoreCase: true, out var parsedEnum);
- if (valueParsedSuccessfully)
- {
- convertedValue = parsedEnum;
- }
- }
- else
- {
- try
- {
- convertedValue = Convert.ChangeType(command, type);
- }
- catch
- {
- valueParsedSuccessfully = false;
- }
- }
-
- if (!valueParsedSuccessfully)
- {
- if (type == typeof(bool))
- {
- var config = property.GetValue(Service.Config) as ConditionBoolean;
- if (config != null)
- {
- config.Value = !config.Value;
- convertedValue = config.Value;
- }
- }
- else if (type.IsEnum)
- {
- // If invalid enum value provided - increment to the next enum value
- var currentEnumValue = property.GetValue(Service.Config) as Enum;
- if (currentEnumValue != null)
- {
- convertedValue = GetNextEnumValue(currentEnumValue);
- }
- }
- }
-
- if (convertedValue == null)
+ var type = property.PropertyType == typeof(ConditionBoolean) ? typeof(bool) : property.PropertyType;
+ if (!TryConvertValue(type, command, out var convertedValue))
{
Svc.Chat.PrintError("Failed to parse the value.");
return;
@@ -133,7 +95,7 @@ private static void DoSettingCommand(string str)
if (Service.Config.ShowToggledActionInChat)
{
- Svc.Chat.Print(string.Format(UiString.CommandsChangeSettingsValue.GetDescription(), property.Name, command));
+ Svc.Chat.Print($"Changed setting {property.Name} to {command}");
}
return;
@@ -142,7 +104,26 @@ private static void DoSettingCommand(string str)
Svc.Chat.PrintError("Failed to find the config in this rotation, please check it.");
}
- private static void HandleTargetingTypesCommand(string settingName, string? command)
+ private static bool TryConvertValue(Type type, string? command, out object? convertedValue)
+ {
+ convertedValue = null;
+ if (type.IsEnum)
+ {
+ return Enum.TryParse(type, command, ignoreCase: true, out convertedValue);
+ }
+
+ try
+ {
+ convertedValue = Convert.ChangeType(command, type);
+ return true;
+ }
+ catch
+ {
+ return false;
+ }
+ }
+
+ private static void HandleTargetingTypesCommand(string? command)
{
if (string.IsNullOrEmpty(command))
{
@@ -163,41 +144,11 @@ private static void HandleTargetingTypesCommand(string settingName, string? comm
switch (action.ToLower())
{
case "add":
- if (string.IsNullOrEmpty(value) || !Enum.TryParse(typeof(TargetingType), value, true, out var parsedEnumAdd))
- {
- Svc.Chat.PrintError("Invalid TargetingType value.");
- return;
- }
-
- var targetingTypeAdd = (TargetingType)parsedEnumAdd;
- if (!Service.Config.TargetingTypes.Contains(targetingTypeAdd))
- {
- Service.Config.TargetingTypes.Add(targetingTypeAdd);
- Svc.Chat.Print($"Added {targetingTypeAdd} to TargetingTypes.");
- }
- else
- {
- Svc.Chat.Print($"{targetingTypeAdd} is already in TargetingTypes.");
- }
+ AddTargetingType(value);
break;
case "remove":
- if (string.IsNullOrEmpty(value) || !Enum.TryParse(typeof(TargetingType), value, true, out var parsedEnumRemove))
- {
- Svc.Chat.PrintError("Invalid TargetingType value.");
- return;
- }
-
- var targetingTypeRemove = (TargetingType)parsedEnumRemove;
- if (Service.Config.TargetingTypes.Contains(targetingTypeRemove))
- {
- Service.Config.TargetingTypes.Remove(targetingTypeRemove);
- Svc.Chat.Print($"Removed {targetingTypeRemove} from TargetingTypes.");
- }
- else
- {
- Svc.Chat.Print($"{targetingTypeRemove} is not in TargetingTypes.");
- }
+ RemoveTargetingType(value);
break;
case "removeall":
@@ -213,6 +164,46 @@ private static void HandleTargetingTypesCommand(string settingName, string? comm
Service.Config.Save();
}
+ private static void AddTargetingType(string? value)
+ {
+ if (string.IsNullOrEmpty(value) || !Enum.TryParse(typeof(TargetingType), value, true, out var parsedEnumAdd))
+ {
+ Svc.Chat.PrintError("Invalid TargetingType value.");
+ return;
+ }
+
+ var targetingTypeAdd = (TargetingType)parsedEnumAdd;
+ if (!Service.Config.TargetingTypes.Contains(targetingTypeAdd))
+ {
+ Service.Config.TargetingTypes.Add(targetingTypeAdd);
+ Svc.Chat.Print($"Added {targetingTypeAdd} to TargetingTypes.");
+ }
+ else
+ {
+ Svc.Chat.Print($"{targetingTypeAdd} is already in TargetingTypes.");
+ }
+ }
+
+ private static void RemoveTargetingType(string? value)
+ {
+ if (string.IsNullOrEmpty(value) || !Enum.TryParse(typeof(TargetingType), value, true, out var parsedEnumRemove))
+ {
+ Svc.Chat.PrintError("Invalid TargetingType value.");
+ return;
+ }
+
+ var targetingTypeRemove = (TargetingType)parsedEnumRemove;
+ if (Service.Config.TargetingTypes.Contains(targetingTypeRemove))
+ {
+ Service.Config.TargetingTypes.Remove(targetingTypeRemove);
+ Svc.Chat.Print($"Removed {targetingTypeRemove} from TargetingTypes.");
+ }
+ else
+ {
+ Svc.Chat.Print($"{targetingTypeRemove} is not in TargetingTypes.");
+ }
+ }
+
private static Enum GetNextEnumValue(Enum currentEnumValue)
{
var enumValues = Enum.GetValues(currentEnumValue.GetType()).Cast().ToArray();
@@ -228,7 +219,6 @@ private static void ToggleActionCommand(string str)
if (str.StartsWith(act.Name))
{
var flag = str[act.Name.Length..].Trim();
-
act.IsEnabled = bool.TryParse(flag, out var parse) ? parse : !act.IsEnabled;
if (Service.Config.ShowToggledActionInChat)
@@ -263,7 +253,7 @@ private static void DoActionCommand(string str)
if (Service.Config.ShowToastsAboutDoAction)
{
- Svc.Toasts.ShowQuest(string.Format(UiString.CommandsInsertAction.GetDescription(), time),
+ Svc.Toasts.ShowQuest($"Inserted action {iAct.Name} with time {time}",
new Dalamud.Game.Gui.Toast.QuestToastOptions()
{
IconId = iAct.IconID,
@@ -278,7 +268,6 @@ private static void DoActionCommand(string str)
Svc.Chat.PrintError(UiString.CommandsInsertActionFailure.GetDescription());
}
-
private static void DoRotationCommand(ICustomRotation customCombo, string str)
{
var configs = customCombo.Configs;
@@ -288,9 +277,7 @@ private static void DoRotationCommand(ICustomRotation customCombo, string str)
{
if (Service.Config.ShowToggledActionInChat)
{
- Svc.Chat.Print(string.Format(UiString.CommandsChangeSettingsValue.GetDescription(),
- config.DisplayName, config.Value));
-
+ Svc.Chat.Print($"Changed setting {config.DisplayName} to {config.Value}");
return;
}
}
diff --git a/RotationSolver/Commands/RSCommands_StateSpecialCommand.cs b/RotationSolver/Commands/RSCommands_StateSpecialCommand.cs
index 2a5affcd0..0a657ba95 100644
--- a/RotationSolver/Commands/RSCommands_StateSpecialCommand.cs
+++ b/RotationSolver/Commands/RSCommands_StateSpecialCommand.cs
@@ -10,13 +10,13 @@ public static partial class RSCommands
{
public static string _stateString = "Off", _specialString = string.Empty;
- internal static string EntryString => _stateString + (DataCenter.SpecialTimeLeft < 0 ? string.Empty : $" - {_specialString}: {DataCenter.SpecialTimeLeft:F2}s");
+ internal static string EntryString => $"{_stateString}{(DataCenter.SpecialTimeLeft < 0 ? string.Empty : $" - {_specialString}: {DataCenter.SpecialTimeLeft:F2}s")}";
private static void UpdateToast()
{
if (!Service.Config.ShowInfoOnToast) return;
- Svc.Toasts.ShowQuest(" " + EntryString, new Dalamud.Game.Gui.Toast.QuestToastOptions()
+ Svc.Toasts.ShowQuest($" {EntryString}", new Dalamud.Game.Gui.Toast.QuestToastOptions
{
IconId = 101,
});
@@ -26,32 +26,45 @@ public static unsafe void DoStateCommandType(StateCommandType stateType, int ind
{
if (DataCenter.State)
{
- if (DataCenter.IsManual && stateType == StateCommandType.Manual
- && Service.Config.ToggleManual)
+ stateType = AdjustStateType(stateType, ref index);
+ }
+
+ UpdateState(stateType, role);
+ return stateType;
+ });
+
+ private static StateCommandType AdjustStateType(StateCommandType stateType, ref int index)
+ {
+ if (DataCenter.IsManual && stateType == StateCommandType.Manual && Service.Config.ToggleManual)
+ {
+ return StateCommandType.Off;
+ }
+ else if (stateType == StateCommandType.Auto)
+ {
+ if (Service.Config.ToggleAuto)
{
- stateType = StateCommandType.Off;
+ return StateCommandType.Off;
}
- else if (stateType == StateCommandType.Auto)
+ else
{
- if (Service.Config.ToggleAuto)
- {
- stateType = StateCommandType.Off;
- }
- else
- {
- if (index == -1)
- {
- // Increment the TargetingIndex to cycle through the TargetingTypes
- index = Service.Config.TargetingIndex + 1;
- }
- // Ensure the index wraps around if it exceeds the number of TargetingTypes
- index %= Service.Config.TargetingTypes.Count;
- // Update the TargetingIndex in the configuration
- Service.Config.TargetingIndex = index;
- }
+ UpdateTargetingIndex(ref index);
}
}
+ return stateType;
+ }
+
+ private static void UpdateTargetingIndex(ref int index)
+ {
+ if (index == -1)
+ {
+ index = Service.Config.TargetingIndex + 1;
+ }
+ index %= Service.Config.TargetingTypes.Count;
+ Service.Config.TargetingIndex = index;
+ }
+ private static void UpdateState(StateCommandType stateType, JobRole role)
+ {
switch (stateType)
{
case StateCommandType.Off:
@@ -75,8 +88,7 @@ public static unsafe void DoStateCommandType(StateCommandType stateType, int ind
_stateString = stateType.ToStateString(role);
UpdateToast();
- return stateType;
- });
+ }
private static void DoSpecialCommandType(SpecialCommandType specialType, bool sayout = true) => DoOneCommandType((type, role) => type.ToSpecialString(role), role =>
{
@@ -89,15 +101,13 @@ private static void DoSpecialCommandType(SpecialCommandType specialType, bool sa
private static void DoOneCommandType(Func sayout, Func doingSomething)
where T : struct, Enum
{
- //Get job role.
var role = Player.Object?.ClassJob.Value.GetJobRole() ?? JobRole.None;
if (role == JobRole.None) return;
T type = doingSomething(role);
- //Saying out.
if (Service.Config.SayOutStateChanged) SpeechHelper.Speak(sayout(type, role));
}
}
-}
+}
\ No newline at end of file
diff --git a/RotationSolver/TextureItems/StatusTexture.cs b/RotationSolver/TextureItems/StatusTexture.cs
index 2a63f9aec..21dd4a8ca 100644
--- a/RotationSolver/TextureItems/StatusTexture.cs
+++ b/RotationSolver/TextureItems/StatusTexture.cs
@@ -11,17 +11,44 @@ public StatusTexture(Status status)
_status = status;
}
+ ///
+ /// Gets the icon ID associated with the texture.
+ ///
public uint IconID => _status.Icon;
+
+ ///
+ /// Gets the ID of the status.
+ ///
public StatusID ID => (StatusID)_status.RowId;
+
+ ///
+ /// Gets the name of the status.
+ ///
public string Name => $"{_status.Name} ({_status.RowId})";
+
+ ///
+ /// Gets the description of the status.
+ ///
public string Description => _status.Description.ExtractText() ?? string.Empty;
+
+ ///
+ /// Gets or sets a value indicating whether the texture is enabled.
+ ///
public bool IsEnabled { get; set; } = true;
+ ///
+ /// Initializes a new instance of the class with the specified status ID.
+ ///
+ /// The ID of the status.
public StatusTexture(StatusID id)
: this((uint)id)
{
}
+ ///
+ /// Initializes a new instance of the class with the specified status ID.
+ ///
+ /// The ID of the status.
public StatusTexture(uint id)
: this(Service.GetSheet().GetRow(id))
{
diff --git a/RotationSolver/UI/CollapsingHeaderGroup.cs b/RotationSolver/UI/CollapsingHeaderGroup.cs
index fa49ba8bf..2a694ebbf 100644
--- a/RotationSolver/UI/CollapsingHeaderGroup.cs
+++ b/RotationSolver/UI/CollapsingHeaderGroup.cs
@@ -5,7 +5,7 @@ namespace RotationSolver.UI;
internal class CollapsingHeaderGroup
{
- private readonly Dictionary, Action> _headers = new Dictionary, Action>();
+ private readonly Dictionary, Action> _headers;
private int _openedIndex = -1;
public float HeaderSize { get; set; } = 24;
@@ -17,14 +17,14 @@ public CollapsingHeaderGroup(Dictionary, Action> headers)
public void AddCollapsingHeader(Func name, Action action)
{
- if (name == null) throw new ArgumentNullException(nameof(name));
- if (action == null) throw new ArgumentNullException(nameof(action));
- _headers.Add(name, action);
+ if (name is null) throw new ArgumentNullException(nameof(name));
+ if (action is null) throw new ArgumentNullException(nameof(action));
+ _headers[name] = action;
}
public void RemoveCollapsingHeader(Func name)
{
- if (name == null) throw new ArgumentNullException(nameof(name));
+ if (name is null) throw new ArgumentNullException(nameof(name));
_headers.Remove(name);
}
@@ -40,8 +40,7 @@ public void Draw()
{
index++;
- if (header.Key == null) continue;
- if (header.Value == null) continue;
+ if (header.Key is null || header.Value is null) continue;
var name = header.Key();
if (string.IsNullOrEmpty(name)) continue;
@@ -72,8 +71,8 @@ public void Draw()
}
catch (Exception ex)
{
- Svc.Log.Warning(ex, "An error occurred while drawing the header.");
+ Svc.Log.Warning($"An error occurred while drawing the header '{name}': {ex}");
}
}
}
-}
+}
\ No newline at end of file
diff --git a/RotationSolver/UI/CooldownWindow.cs b/RotationSolver/UI/CooldownWindow.cs
index 3a52ff2dc..845135495 100644
--- a/RotationSolver/UI/CooldownWindow.cs
+++ b/RotationSolver/UI/CooldownWindow.cs
@@ -35,7 +35,15 @@ public override void Draw()
uint itemIndex = 0;
foreach (var item in showItems)
{
- ControlWindow.DrawIAction(item, width, 1f);
+ try
+ {
+ ControlWindow.DrawIAction(item, width, 1f);
+ }
+ catch (Exception ex)
+ {
+ // Log the exception or handle it as needed
+ Console.WriteLine($"Error drawing action: {ex.Message}");
+ }
itemIndex++;
if (itemIndex % count != 0)
{
diff --git a/RotationSolver/UI/FontManager.cs b/RotationSolver/UI/FontManager.cs
index 223926139..e8fc30dea 100644
--- a/RotationSolver/UI/FontManager.cs
+++ b/RotationSolver/UI/FontManager.cs
@@ -17,20 +17,18 @@ public unsafe static ImFontPtr GetFont(float size)
try
{
// Lock the handle to get the font
- using (var lockedHandle = handle.Lock())
- {
- var font = lockedHandle.ImFont;
-
- // Check if the font pointer is valid
- if (font.NativePtr == null)
- {
- return ImGui.GetFont();
- }
+ using var lockedHandle = handle.Lock();
+ var font = lockedHandle.ImFont;
- // Scale the font to the desired size
- font.Scale = size / font.FontSize;
- return font;
+ // Check if the font pointer is valid
+ if (font.NativePtr == null)
+ {
+ return ImGui.GetFont();
}
+
+ // Scale the font to the desired size
+ font.Scale = size / font.FontSize;
+ return font;
}
catch (Exception)
{
diff --git a/RotationSolver/UI/ImGuiHelper.cs b/RotationSolver/UI/ImGuiHelper.cs
index 70e9d4c96..b6b4d0cea 100644
--- a/RotationSolver/UI/ImGuiHelper.cs
+++ b/RotationSolver/UI/ImGuiHelper.cs
@@ -9,6 +9,9 @@
using RotationSolver.Basic.Configuration;
using RotationSolver.Commands;
using RotationSolver.Data;
+using System;
+using System.Collections.Generic;
+using System.Numerics;
namespace RotationSolver.UI;
@@ -65,34 +68,16 @@ public static void DisplayMacro(this MacroInfo info)
ImGui.SetNextItemWidth(50);
// Display a draggable integer input for the macro index
- if (ImGui.DragInt($"{UiString.ConfigWindow_Events_MacroIndex.GetDescription()}##MacroIndex{info.GetHashCode()}",
- ref info.MacroIndex, 1, -1, 99))
+ if (ImGui.DragInt($"{UiString.ConfigWindow_Events_MacroIndex.GetDescription()}##MacroIndex{info.GetHashCode()}", ref info.MacroIndex, 1, -1, 99))
{
- // Save the configuration if the value changes
- try
- {
- Service.Config.Save();
- }
- catch (Exception ex)
- {
- Svc.Log.Warning(ex, "Failed to save configuration.");
- }
+ SaveConfig();
}
// Display a checkbox for the shared macro option
ImGui.SameLine();
- if (ImGui.Checkbox($"{UiString.ConfigWindow_Events_ShareMacro.GetDescription()}##ShareMacro{info.GetHashCode()}",
- ref info.IsShared))
+ if (ImGui.Checkbox($"{UiString.ConfigWindow_Events_ShareMacro.GetDescription()}##ShareMacro{info.GetHashCode()}", ref info.IsShared))
{
- // Save the configuration if the value changes
- try
- {
- Service.Config.Save();
- }
- catch (Exception ex)
- {
- Svc.Log.Warning(ex, "Failed to save configuration.");
- }
+ SaveConfig();
}
}
@@ -102,7 +87,7 @@ public static void DisplayEvent(this ActionEventInfo info)
if (ImGui.InputText($"{UiString.ConfigWindow_Events_ActionName.GetDescription()}##ActionName{info.GetHashCode()}", ref name, 100))
{
info.Name = name;
- Service.Config.Save();
+ SaveConfig();
}
info.DisplayMacro();
@@ -131,16 +116,21 @@ public static void SearchCombo(string popId, string name, ref string searchTx
var searchingKey = searchTxt;
- var members = items.Select(m => (m, getSearchName(m)))
- .OrderByDescending(s => SearchableCollection.Similarity(s.Item2, searchingKey));
+ var members = new List<(T, string)>();
+ foreach (var item in items)
+ {
+ members.Add((item, getSearchName(item)));
+ }
- ImGui.SetNextItemWidth(Math.Max(50 * ImGuiHelpers.GlobalScale, members.Max(i => ImGuiHelpers.GetButtonSize(i.Item2).X)));
+ members.Sort((x, y) => SearchableCollection.Similarity(y.Item2, searchingKey).CompareTo(SearchableCollection.Similarity(x.Item2, searchingKey)));
+
+ ImGui.SetNextItemWidth(Math.Max(50 * ImGuiHelpers.GlobalScale, GetMaxButtonSize(members)));
ImGui.InputTextWithHint("##Searching the member", searchingHint, ref searchTxt, 128);
ImGui.Spacing();
ImRaii.IEndObject? child = null;
- if (members.Count() >= 15)
+ if (members.Count >= 15)
{
ImGui.SetNextWindowSizeConstraints(new Vector2(0, 300), new Vector2(500, 300));
child = ImRaii.Child(popId);
@@ -151,13 +141,27 @@ public static void SearchCombo(string popId, string name, ref string searchTx
{
if (ImGui.Selectable(member.Item2))
{
- selectAction?.Invoke(member.m);
+ selectAction?.Invoke(member.Item1);
ImGui.CloseCurrentPopup();
}
}
child?.Dispose();
}
+ private static float GetMaxButtonSize(List<(T, string)> members)
+ {
+ float maxSize = 0;
+ foreach (var member in members)
+ {
+ var size = ImGuiHelpers.GetButtonSize(member.Item2).X;
+ if (size > maxSize)
+ {
+ maxSize = size;
+ }
+ }
+ return maxSize;
+ }
+
public static unsafe bool SelectableCombo(string popUp, string[] items, ref int index, ImFontPtr? font = null, Vector4? color = null)
{
var count = items.Length;
@@ -315,10 +319,10 @@ internal static void TextShade(Vector2 pos, string text, float width = 1.5f)
{
var offsets = new Vector2[]
{
- new Vector2(0, -width),
- new Vector2(0, width),
- new Vector2(-width, 0),
- new Vector2(width, 0)
+ new Vector2(0, -width),
+ new Vector2(0, width),
+ new Vector2(-width, 0),
+ new Vector2(width, 0)
};
var drawList = ImGui.GetWindowDrawList();
@@ -339,11 +343,8 @@ internal static void DrawActionOverlay(Vector2 cursor, float width, float percen
{
ImGui.SetCursorPos(cursor - new Vector2(pixPerUnit * 3, pixPerUnit * 4));
- //var step = new Vector2(88f / cover.Width, 96f / cover.Height);
var start = new Vector2((96f * 0 + 4f) / cover.Width, (96f * 2) / cover.Height);
- //Out Size is 88, 96
- //Inner Size is 82, 82
ImGui.Image(cover.ImGuiHandle, new Vector2(pixPerUnit * 88, pixPerUnit * 94),
start, start + new Vector2(88f / cover.Width, 94f / cover.Height));
}
@@ -356,12 +357,9 @@ internal static void DrawActionOverlay(Vector2 cursor, float width, float percen
var P = (int)(percent * 81);
-
var step = new Vector2(88f / cover.Width, 96f / cover.Height);
var start = new Vector2(P % 9 * step.X, P / 9 * step.Y);
- //Out Size is 88, 96
- //Inner Size is 82, 82
ImGui.Image(cover.ImGuiHandle, new Vector2(pixPerUnit * 88, pixPerUnit * 94),
start, start + new Vector2(88f / cover.Width, 94f / cover.Height));
}
@@ -370,11 +368,8 @@ internal static void DrawActionOverlay(Vector2 cursor, float width, float percen
{
if (IconSet.GetTexture("ui/uld/icona_frame_hr1.tex", out var cover))
{
-
ImGui.SetCursorPos(cursor - new Vector2(pixPerUnit * 3, pixPerUnit * 4));
- //Out Size is 88, 96
- //Inner Size is 82, 82
ImGui.Image(cover.ImGuiHandle, new Vector2(pixPerUnit * 88, pixPerUnit * 94),
new Vector2(4f / cover.Width, 0f / cover.Height),
new Vector2(92f / cover.Width, 94f / cover.Height));
@@ -392,8 +387,6 @@ internal static void DrawActionOverlay(Vector2 cursor, float width, float percen
var step = new Vector2(88f / cover.Width, 96f / cover.Height);
var start = new Vector2((P % 9 + 9) * step.X, P / 9 * step.Y);
- //Out Size is 88, 96
- //Inner Size is 82, 82
ImGui.Image(cover.ImGuiHandle, new Vector2(pixPerUnit * 88, pixPerUnit * 94),
start, start + new Vector2(88f / cover.Width, 94f / cover.Height));
}
@@ -480,7 +473,7 @@ private static void CopyCommand(string command)
Notify.Success($"\"{command}\" copied to clipboard.");
}
- private static readonly SortedList _lastChecked = [];
+ private static readonly Dictionary _lastChecked = new();
private static void ExecuteHotKeys(Action action, params VirtualKey[] keys)
{
if (action == null) return;
@@ -489,7 +482,15 @@ private static void ExecuteHotKeys(Action action, params VirtualKey[] keys)
var name = string.Join(' ', keys);
if (!_lastChecked.TryGetValue(name, out var last)) last = false;
- var now = keys.All(k => Svc.KeyState[k]);
+ var now = true;
+ foreach (var key in keys)
+ {
+ if (!Svc.KeyState[key])
+ {
+ now = false;
+ break;
+ }
+ }
_lastChecked[name] = now;
if (!last && now) action();
@@ -526,7 +527,7 @@ public static bool IsInRect(Vector2 leftTop, Vector2 size)
ConfigUnitType.Degree => " °",
ConfigUnitType.Pixels => " p",
ConfigUnitType.Yalms => " y",
- ConfigUnitType.Percent => " %%",
+ ConfigUnitType.Percent => " %",
_ => string.Empty,
};
@@ -551,4 +552,16 @@ public static void Draw(this CombatType type)
ImGui.TextColored(ImGuiColors.DalamudRed, " None of PvE or PvP!");
}
}
+
+ private static void SaveConfig()
+ {
+ try
+ {
+ Service.Config.Save();
+ }
+ catch (Exception ex)
+ {
+ Svc.Log.Warning(ex, "Failed to save configuration.");
+ }
+ }
}
\ No newline at end of file
diff --git a/RotationSolver/UI/ImguiTooltips.cs b/RotationSolver/UI/ImguiTooltips.cs
index 648f801bd..2c395aaa5 100644
--- a/RotationSolver/UI/ImguiTooltips.cs
+++ b/RotationSolver/UI/ImguiTooltips.cs
@@ -6,7 +6,7 @@ namespace RotationSolver.UI;
internal static class ImguiTooltips
{
- const ImGuiWindowFlags TOOLTIP_FLAG =
+ private const ImGuiWindowFlags TooltipFlag =
ImGuiWindowFlags.Tooltip |
ImGuiWindowFlags.NoMove |
ImGuiWindowFlags.NoSavedSettings |
@@ -15,7 +15,7 @@ internal static class ImguiTooltips
ImGuiWindowFlags.NoInputs |
ImGuiWindowFlags.AlwaysAutoResize;
- const string TOOLTIP_ID = "RotationSolverReborn Tooltips";
+ private const string TooltipId = "RotationSolverReborn Tooltips";
///
/// Displays a tooltip when the item is hovered.
@@ -45,9 +45,9 @@ public static void ShowTooltip(string? text)
/// Displays a tooltip with the specified action.
///
/// The action to perform to render the tooltip content.
- public static void ShowTooltip(Action act)
+ public static void ShowTooltip(Action? act)
{
- if (act == null || Service.Config == null || !Service.Config.ShowTooltips) return;
+ if (act == null || Service.Config.ShowTooltips != true) return;
ImGui.SetNextWindowBgAlpha(1);
@@ -55,9 +55,9 @@ public static void ShowTooltip(Action act)
var globalScale = ImGuiHelpers.GlobalScale;
ImGui.SetNextWindowSizeConstraints(new Vector2(150, 0) * globalScale, new Vector2(1200, 1500) * globalScale);
- ImGui.SetWindowPos(TOOLTIP_ID, ImGui.GetIO().MousePos);
+ ImGui.SetWindowPos(TooltipId, ImGui.GetIO().MousePos);
- if (ImGui.Begin(TOOLTIP_ID, TOOLTIP_FLAG))
+ if (ImGui.Begin(TooltipId, TooltipFlag))
{
act();
ImGui.End();
diff --git a/RotationSolver/UI/OverlayWindow.cs b/RotationSolver/UI/OverlayWindow.cs
index 97310e743..70f893d24 100644
--- a/RotationSolver/UI/OverlayWindow.cs
+++ b/RotationSolver/UI/OverlayWindow.cs
@@ -2,6 +2,8 @@
using Dalamud.Interface.Windowing;
using ECommons.DalamudServices;
using RotationSolver.UI.HighlightTeachingMode;
+using System.Linq;
+using System.Threading.Tasks;
namespace RotationSolver.UI;
@@ -35,7 +37,7 @@ public override void PreDraw()
base.PreDraw();
}
- public override void Draw()
+ public override async void Draw()
{
if (!HotbarHighlightManager.Enable || Svc.ClientState == null || Svc.ClientState.LocalPlayer == null)
return;
@@ -44,24 +46,11 @@ public override void Draw()
try
{
- if (!HotbarHighlightManager.UseTaskToAccelerate)
- {
- HotbarHighlightManager._drawingElements2D = HotbarHighlightManager.To2DAsync().Result;
- }
+ await UpdateDrawingElementsAsync();
if (HotbarHighlightManager._drawingElements2D != null)
{
- foreach (var item in HotbarHighlightManager._drawingElements2D.OrderBy(drawing =>
- {
- if (drawing is PolylineDrawing poly)
- {
- return poly._thickness == 0 ? 0 : 1;
- }
- else
- {
- return 2;
- }
- }))
+ foreach (var item in HotbarHighlightManager._drawingElements2D.OrderBy(GetDrawingOrder))
{
item.Draw();
}
@@ -73,6 +62,26 @@ public override void Draw()
}
}
+ private async Task UpdateDrawingElementsAsync()
+ {
+ if (!HotbarHighlightManager.UseTaskToAccelerate)
+ {
+ HotbarHighlightManager._drawingElements2D = await HotbarHighlightManager.To2DAsync();
+ }
+ }
+
+ private int GetDrawingOrder(object drawing)
+ {
+ if (drawing is PolylineDrawing poly)
+ {
+ return poly._thickness == 0 ? 0 : 1;
+ }
+ else
+ {
+ return 2;
+ }
+ }
+
public override void PostDraw()
{
ImGui.PopStyleVar();
diff --git a/RotationSolver/UI/RotationConfigWindow.cs b/RotationSolver/UI/RotationConfigWindow.cs
index 1b484c72c..07a83c21a 100644
--- a/RotationSolver/UI/RotationConfigWindow.cs
+++ b/RotationSolver/UI/RotationConfigWindow.cs
@@ -2829,8 +2829,8 @@ private static unsafe void DrawStatus()
private static unsafe void DrawParty()
{
- ImGui.Text($"Party: {DataCenter.PartyMembers.Count()}");
- ImGui.Text($"Alliance: {DataCenter.AllianceMembers.Count()}");
+ ImGui.Text($"Party: {DataCenter.PartyMembers.Count}");
+ ImGui.Text($"Alliance: {DataCenter.AllianceMembers.Count}");
ImGui.Text($"PartyMembersAverHP: {DataCenter.PartyMembersAverHP}");
diff --git a/RotationSolver/UI/SearchableCollection.cs b/RotationSolver/UI/SearchableCollection.cs
index 0f3d571dd..fe7d959a6 100644
--- a/RotationSolver/UI/SearchableCollection.cs
+++ b/RotationSolver/UI/SearchableCollection.cs
@@ -15,43 +15,32 @@ internal class SearchableCollection
public SearchableCollection()
{
- // Retrieve properties from the Configs class
var properties = typeof(Configs).GetRuntimeProperties().ToArray();
var pairs = new List(properties.Length);
var parents = new Dictionary(properties.Length);
-
- // Cache attributes to avoid repeated reflection calls
var attributes = new ConcurrentDictionary();
- // Iterate over each property
foreach (var property in properties)
{
- // Get the UIAttribute for the property
var ui = property.GetCustomAttribute();
if (ui == null) continue;
- // Create an ISearchable instance for the property
var item = CreateSearchable(property);
if (item == null) continue;
- // Set PvE and PvP filters
item.PvEFilter = new(ui.PvEFilter);
item.PvPFilter = new(ui.PvPFilter);
- // Add the SearchPair to the list
pairs.Add(new(ui, item));
- // If the item is a CheckBoxSearch, add it to the parents dictionary
if (item is CheckBoxSearch search)
{
parents[property.Name] = search;
}
}
- // Initialize the _items list
_items = new List(pairs.Count);
- // Organize items based on parent-child relationships
foreach (var pair in pairs)
{
var parentName = pair.Attribute.Parent;
@@ -69,21 +58,28 @@ public SearchableCollection()
public void DrawItems(string filter)
{
bool isFirst = true;
+ var filteredItems = new Dictionary>();
- // Filter and group items based on the provided filter
- var filteredItems = _items.Where(i => i.Attribute.Filter == filter)
- .GroupBy(i => i.Attribute.Section);
+ foreach (var item in _items)
+ {
+ if (item.Attribute.Filter == filter)
+ {
+ if (!filteredItems.ContainsKey(item.Attribute.Section))
+ {
+ filteredItems[item.Attribute.Section] = new List();
+ }
+ filteredItems[item.Attribute.Section].Add(item);
+ }
+ }
foreach (var grp in filteredItems)
{
- // Add a separator between groups
if (!isFirst)
{
ImGui.Separator();
}
- // Draw each item in the group, ordered by Attribute.Order
- foreach (var item in grp.OrderBy(i => i.Attribute.Order))
+ foreach (var item in grp.Value.OrderBy(i => i.Attribute.Order))
{
item.Searchable.Draw();
}
@@ -99,95 +95,82 @@ public ISearchable[] SearchItems(string searchingText)
var results = new HashSet();
var finalResults = new List(MaxResultLength);
- foreach (var searchable in _items.Select(i => i.Searchable).SelectMany(GetChildren))
+ foreach (var pair in _items)
{
- var parent = GetParent(searchable);
- if (results.Contains(parent)) continue;
-
- if (Similarity(searchable.SearchingKeys, searchingText) > 0)
+ foreach (var searchable in GetChildren(pair.Searchable))
{
- results.Add(parent);
- finalResults.Add(parent);
- if (finalResults.Count >= MaxResultLength) break;
+ var parent = GetParent(searchable);
+ if (results.Contains(parent)) continue;
+
+ if (Similarity(searchable.SearchingKeys, searchingText) > 0)
+ {
+ results.Add(parent);
+ finalResults.Add(parent);
+ if (finalResults.Count >= MaxResultLength) break;
+ }
}
}
return finalResults.ToArray();
}
- private static ISearchable? CreateSearchable(PropertyInfo property)
+ private static ISearchable? CreateSearchable(PropertyInfo property) => property.Name switch
{
- var type = property.PropertyType;
-
- // Create an instance of ISearchable based on the property type
- return property.Name switch
- {
- // Special case for AutoHeal property
- nameof(Configs.AutoHeal) => new AutoHealCheckBox(property),
-
- // Handle enum properties
- _ when type.IsEnum => new EnumSearch(property),
-
- // Handle boolean properties without conditions
- _ when type == typeof(bool) => new CheckBoxSearchNoCondition(property),
-
- // Handle ConditionBoolean properties
- _ when type == typeof(ConditionBoolean) => new CheckBoxSearchCondition(property),
-
- // Handle float properties
- _ when type == typeof(float) => new DragFloatSearch(property),
-
- // Handle int properties
- _ when type == typeof(int) => new DragIntSearch(property),
-
- // Handle Vector2 properties
- _ when type == typeof(Vector2) => new DragFloatRangeSearch(property),
-
- // Handle Vector2Int properties
- _ when type == typeof(Vector2Int) => new DragIntRangeSearch(property),
-
- // Handle Vector4 properties
- _ when type == typeof(Vector4) => new ColorEditSearch(property),
-
- // Return null for unsupported property types
- _ => null
- };
- }
+ nameof(Configs.AutoHeal) => new AutoHealCheckBox(property),
+ _ when property.PropertyType.IsEnum => new EnumSearch(property),
+ _ when property.PropertyType == typeof(bool) => new CheckBoxSearchNoCondition(property),
+ _ when property.PropertyType == typeof(ConditionBoolean) => new CheckBoxSearchCondition(property),
+ _ when property.PropertyType == typeof(float) => new DragFloatSearch(property),
+ _ when property.PropertyType == typeof(int) => new DragIntSearch(property),
+ _ when property.PropertyType == typeof(Vector2) => new DragFloatRangeSearch(property),
+ _ when property.PropertyType == typeof(Vector2Int) => new DragIntRangeSearch(property),
+ _ when property.PropertyType == typeof(Vector4) => new ColorEditSearch(property),
+ _ => null
+ };
private static IEnumerable GetChildren(ISearchable searchable)
{
- // Include the current searchable item
yield return searchable;
- // If the searchable item is a CheckBoxSearch and has children, recursively get all children
if (searchable is CheckBoxSearch c && c.Children != null)
{
- foreach (var child in c.Children.SelectMany(GetChildren))
+ foreach (var child in c.Children)
{
- yield return child;
+ foreach (var grandChild in GetChildren(child))
+ {
+ yield return grandChild;
+ }
}
}
}
- private static ISearchable GetParent(ISearchable searchable)
- {
- // Recursively get the top-most parent
- return searchable.Parent == null ? searchable : GetParent(searchable.Parent);
- }
+ private static ISearchable GetParent(ISearchable searchable) => searchable.Parent == null ? searchable : GetParent(searchable.Parent);
public static float Similarity(string text, string key)
{
if (string.IsNullOrEmpty(text)) return 0;
- // Split the text and key into words using the specified delimiters
var chars = text.Split(_splitChar, StringSplitOptions.RemoveEmptyEntries);
var keys = key.Split(_splitChar, StringSplitOptions.RemoveEmptyEntries);
- // Count the number of words that start with or contain the key
- var startWithCount = chars.Count(i => keys.Any(k => i.StartsWith(k, StringComparison.OrdinalIgnoreCase)));
- var containCount = chars.Count(i => keys.Any(k => i.Contains(k, StringComparison.OrdinalIgnoreCase)));
+ var startWithCount = 0;
+ var containCount = 0;
+
+ foreach (var c in chars)
+ {
+ foreach (var k in keys)
+ {
+ if (c.StartsWith(k, StringComparison.OrdinalIgnoreCase))
+ {
+ startWithCount++;
+ }
+ else if (c.Contains(k, StringComparison.OrdinalIgnoreCase))
+ {
+ containCount++;
+ }
+ }
+ }
- // Calculate the similarity score
return startWithCount * 3 + containCount;
}
}
\ No newline at end of file
diff --git a/RotationSolver/Updaters/ActionSequencerUpdater.cs b/RotationSolver/Updaters/ActionSequencerUpdater.cs
index ade481033..6681a6134 100644
--- a/RotationSolver/Updaters/ActionSequencerUpdater.cs
+++ b/RotationSolver/Updaters/ActionSequencerUpdater.cs
@@ -17,31 +17,30 @@ public static void UpdateActionSequencerAction()
var set = DataCenter.RightSet;
if (set == null) return;
- DataCenter.DisabledActionSequencer = new HashSet(set.DisableConditionDict
- .Where(pair => pair.Value.IsTrue(customRotation))
- .Select(pair => pair.Key));
+ var disabledActions = new HashSet();
+ foreach (var pair in set.DisableConditionDict)
+ {
+ if (pair.Value.IsTrue(customRotation))
+ {
+ disabledActions.Add(pair.Key);
+ }
+ }
+ DataCenter.DisabledActionSequencer = disabledActions;
- bool find = false;
var conditions = set.ConditionDict;
if (conditions != null)
{
foreach (var conditionPair in conditions)
{
var nextAct = allActions.FirstOrDefault(a => a.ID == conditionPair.Key);
- if (nextAct == null) continue;
-
- if (!conditionPair.Value.IsTrue(customRotation)) continue;
+ if (nextAct == null || !conditionPair.Value.IsTrue(customRotation)) continue;
DataCenter.ActionSequencerAction = nextAct;
- find = true;
- break;
+ return;
}
}
- if (!find)
- {
- DataCenter.ActionSequencerAction = null;
- }
+ DataCenter.ActionSequencerAction = null;
}
public static void Enable(string folder)
@@ -57,13 +56,15 @@ public static void SaveFiles()
if (_actionSequencerFolder == null) return;
try
{
- Directory.Delete(_actionSequencerFolder);
+ Directory.Delete(_actionSequencerFolder, true);
Directory.CreateDirectory(_actionSequencerFolder);
}
- catch
+ catch (Exception ex)
{
-
+ // Log the exception or handle it as needed
+ Console.WriteLine($"Error deleting directory: {ex.Message}");
}
+
foreach (var set in DataCenter.ConditionSets)
{
set.Save(_actionSequencerFolder);
@@ -79,15 +80,39 @@ public static void LoadFiles()
public static void AddNew()
{
- if (!DataCenter.ConditionSets.Any(c => c.IsUnnamed))
+ bool hasUnnamed = false;
+ foreach (var conditionSet in DataCenter.ConditionSets)
+ {
+ if (conditionSet.IsUnnamed)
+ {
+ hasUnnamed = true;
+ break;
+ }
+ }
+
+ if (!hasUnnamed)
{
- DataCenter.ConditionSets = [.. DataCenter.ConditionSets, new MajorConditionSet()];
+ var newConditionSets = new List(DataCenter.ConditionSets)
+ {
+ new MajorConditionSet()
+ };
+ DataCenter.ConditionSets = newConditionSets.ToArray();
}
}
public static void Delete(string name)
{
- DataCenter.ConditionSets = DataCenter.ConditionSets.Where(c => c.Name != name).ToArray();
- File.Delete(_actionSequencerFolder + $"\\{name}.json");
+ var newConditionSets = new List();
+ foreach (var conditionSet in DataCenter.ConditionSets)
+ {
+ if (conditionSet.Name != name)
+ {
+ newConditionSets.Add(conditionSet);
+ }
+ }
+ DataCenter.ConditionSets = newConditionSets.ToArray();
+
+ var filePath = Path.Combine(_actionSequencerFolder ?? string.Empty, $"{name}.json");
+ File.Delete(filePath);
}
-}
+}
\ No newline at end of file
diff --git a/RotationSolver/Updaters/ActionUpdater.cs b/RotationSolver/Updaters/ActionUpdater.cs
index 22b031cf9..bfa98bb95 100644
--- a/RotationSolver/Updaters/ActionUpdater.cs
+++ b/RotationSolver/Updaters/ActionUpdater.cs
@@ -36,7 +36,7 @@ internal static IAction? NextAction
}
private static IBaseAction? _nextGCDAction;
- const float gcdHeight = 5;
+ const float GcdHeight = 5;
internal static IBaseAction? NextGCDAction
{
get => _nextGCDAction;
@@ -73,9 +73,7 @@ internal static void UpdateNextAction()
}
catch (Exception ex)
{
-#pragma warning disable 0436
- WarningHelper.AddSystemWarning($"Failed to update the next action in the rotation because: {ex.Message}");
- Svc.Log.Error(ex, "Failed to update next action.");
+ LogError("Failed to update the next action in the rotation", ex);
}
NextAction = NextGCDAction = null;
@@ -98,7 +96,6 @@ private static void SetAction(uint id)
internal unsafe static void UpdateActionInfo()
{
SetAction(NextGCDAction?.AdjustedID ?? 0);
- //UpdateWeaponTime();
UpdateCombatTime();
UpdateSlots();
UpdateMoving();
@@ -192,6 +189,20 @@ private static void UpdateMPTimer()
}
internal unsafe static bool CanDoAction()
+ {
+ if (IsPlayerOccupied() || Player.Object.CurrentHp == 0) return false;
+
+ var nextAction = NextAction;
+ if (nextAction == null) return false;
+
+ // Skip when casting
+ if (Player.Object.TotalCastTime - DataCenter.ActionAhead > 0) return false;
+
+ // GCD
+ return RSCommands.CanDoAnAction(ActionHelper.CanUseGCD);
+ }
+
+ private unsafe static bool IsPlayerOccupied()
{
if (Svc.Condition[ConditionFlag.OccupiedInQuestEvent]
|| Svc.Condition[ConditionFlag.OccupiedInCutSceneEvent]
@@ -201,25 +212,32 @@ internal unsafe static bool CanDoAction()
|| Svc.Condition[ConditionFlag.BetweenAreas]
|| Svc.Condition[ConditionFlag.BetweenAreas51]
|| Svc.Condition[ConditionFlag.Mounted]
- //|| Svc.Condition[ConditionFlag.SufferingStatusAffliction] //Because of BLU30!
|| Svc.Condition[ConditionFlag.SufferingStatusAffliction2]
|| Svc.Condition[ConditionFlag.RolePlaying]
|| Svc.Condition[ConditionFlag.InFlight]
|| Svc.Condition[ConditionFlag.Diving]
|| Svc.Condition[ConditionFlag.Swimming]
|| Svc.Condition[ConditionFlag.Unconscious]
- || Svc.Condition[ConditionFlag.MeldingMateria]
- || (ActionManager.Instance()->ActionQueued && NextAction != null
- && ActionManager.Instance()->QueuedActionId != NextAction.AdjustedID)
- || Player.Object.CurrentHp == 0) return false;
+ || Svc.Condition[ConditionFlag.MeldingMateria])
+ {
+ return true;
+ }
- var nextAction = NextAction;
- if (nextAction == null) return false;
+ var actionManager = ActionManager.Instance();
+ if (actionManager->ActionQueued && NextAction != null
+ && actionManager->QueuedActionId != NextAction.AdjustedID)
+ {
+ return true;
+ }
- //Skip when casting
- if (Player.Object.TotalCastTime - DataCenter.ActionAhead > 0) return false;
+ return false;
+ }
- //GCD
- return RSCommands.CanDoAnAction(ActionHelper.CanUseGCD);
+ private static void LogError(string message, Exception ex)
+ {
+#pragma warning disable 0436
+
+ WarningHelper.AddSystemWarning($"{message} because: {ex.Message}");
+ Svc.Log.Error(ex, message);
}
}
\ No newline at end of file
diff --git a/RotationSolver/Updaters/MajorUpdater.cs b/RotationSolver/Updaters/MajorUpdater.cs
index 6b67ec9a4..fbb361cee 100644
--- a/RotationSolver/Updaters/MajorUpdater.cs
+++ b/RotationSolver/Updaters/MajorUpdater.cs
@@ -1,5 +1,4 @@
using Dalamud.Game.ClientState.Conditions;
-using Dalamud.Game.ClientState.Objects.Enums;
using Dalamud.Plugin.Services;
using ECommons.DalamudServices;
using ECommons.GameHelpers;
@@ -10,7 +9,6 @@
using Lumina.Excel.Sheets;
using RotationSolver.Commands;
using RotationSolver.Data;
-
using RotationSolver.UI.HighlightTeachingMode;
using System.Runtime.InteropServices;
using static FFXIVClientStructs.FFXIV.Client.UI.Misc.RaptureHotbarModule;
@@ -42,6 +40,10 @@ private unsafe static void RSRUpdate(IFramework framework)
if (!IsValid)
{
+ if (Service.Config.AutoOffBetweenArea)
+ {
+ RSCommands.DoStateCommandType(StateCommandType.Off);
+ }
ActionUpdater.ClearNextAction();
CustomRotation.MoveTarget = null;
return;
@@ -84,7 +86,7 @@ private unsafe static void RSRUpdate(IFramework framework)
private static void HandleSystemWarnings()
{
- if (DataCenter.SystemWarnings.Any())
+ if (DataCenter.SystemWarnings.Count > 0)
{
var warningsToRemove = new List();
@@ -113,17 +115,17 @@ private static async Task HandleWorkUpdateAsync()
try
{
- if (Service.Config.FrameworkStyle == FrameworkStyle.WorkTask)
- {
- await Task.Run(() => UpdateWork());
- }
- else if (Service.Config.FrameworkStyle == FrameworkStyle.RunOnTick)
+ switch (Service.Config.FrameworkStyle)
{
- await Svc.Framework.RunOnTick(() => UpdateWork());
- }
- else if (Service.Config.FrameworkStyle == FrameworkStyle.MainThread)
- {
- UpdateWork();
+ case FrameworkStyle.WorkTask:
+ await Task.Run(() => UpdateWork());
+ break;
+ case FrameworkStyle.RunOnTick:
+ await Svc.Framework.RunOnTick(() => UpdateWork());
+ break;
+ case FrameworkStyle.MainThread:
+ UpdateWork();
+ break;
}
}
catch (Exception tEx)
@@ -163,22 +165,7 @@ private static void UpdateWork()
RSCommands.UpdateRotationState();
HotbarHighlightManager.UpdateSettings();
- // Collect expired VfxNewData items
- var expiredVfx = new List();
- for (int i = 0; i < DataCenter.VfxDataQueue.Count; i++)
- {
- var vfx = DataCenter.VfxDataQueue[i];
- if (vfx.TimeDuration > TimeSpan.FromSeconds(10))
- {
- expiredVfx.Add(vfx);
- }
- }
-
- // Remove expired VfxNewData items
- foreach (var vfx in expiredVfx)
- {
- DataCenter.VfxDataQueue.Remove(vfx);
- }
+ RemoveExpiredVfxData();
}
catch (Exception ex)
{
@@ -189,14 +176,32 @@ private static void UpdateWork()
}
}
+ private static void RemoveExpiredVfxData()
+ {
+ var expiredVfx = new List();
+ for (int i = 0; i < DataCenter.VfxDataQueue.Count; i++)
+ {
+ var vfx = DataCenter.VfxDataQueue[i];
+ if (vfx.TimeDuration > TimeSpan.FromSeconds(10))
+ {
+ expiredVfx.Add(vfx);
+ }
+ }
+
+ foreach (var vfx in expiredVfx)
+ {
+ DataCenter.VfxDataQueue.Remove(vfx);
+ }
+ }
+
private static void UpdateHighlight()
{
if (!Service.Config.TeachingMode || ActionUpdater.NextAction is not IAction nextAction) return;
HotbarID? hotbar = nextAction switch
- {
+ {
IBaseItem item => new HotbarID(HotbarSlotType.Item, item.ID),
- IBaseAction baseAction when baseAction.Action.ActionCategory.RowId is 10 or 11 => Svc.Data.GetExcelSheet()?.FirstOrDefault(g => g.Action.RowId == baseAction.ID) is GeneralAction gAct ? new HotbarID(HotbarSlotType.GeneralAction, gAct.RowId) : null,
+ IBaseAction baseAction when baseAction.Action.ActionCategory.RowId is 10 or 11 => GetGeneralActionHotbarID(baseAction),
IBaseAction baseAction => new HotbarID(HotbarSlotType.Action, baseAction.AdjustedID),
_ => null
};
@@ -207,6 +212,22 @@ private static void UpdateHighlight()
}
}
+ private static HotbarID? GetGeneralActionHotbarID(IBaseAction baseAction)
+ {
+ var generalActions = Svc.Data.GetExcelSheet();
+ if (generalActions == null) return null;
+
+ foreach (var gAct in generalActions)
+ {
+ if (gAct.Action.RowId == baseAction.ID)
+ {
+ return new HotbarID(HotbarSlotType.GeneralAction, gAct.RowId);
+ }
+ }
+
+ return null;
+ }
+
private static void ShowWarning()
{
if (!Svc.PluginInterface.InstalledPlugins.Any(p => p.InternalName == "Avarice"))
@@ -264,7 +285,7 @@ private unsafe static void OpenChest()
if (dis > 0.5f) return false;
var address = (FFXIVClientStructs.FFXIV.Client.Game.Object.GameObject*)(void*)o.Address;
- if ((ObjectKind)address->ObjectKind != ObjectKind.Treasure) return false;
+ if (address->ObjectKind != FFXIVClientStructs.FFXIV.Client.Game.Object.ObjectKind.Treasure) return false;
//Opened!
foreach (var item in Loot.Instance()->Items)
diff --git a/RotationSolver/Updaters/MovingUpdater.cs b/RotationSolver/Updaters/MovingUpdater.cs
index 8273d9480..c1741a157 100644
--- a/RotationSolver/Updaters/MovingUpdater.cs
+++ b/RotationSolver/Updaters/MovingUpdater.cs
@@ -10,50 +10,66 @@ internal static class MovingUpdater
{
internal unsafe static void UpdateCanMove(bool doNextAction)
{
- //Special state.
+ // Special state.
if (Svc.Condition[ConditionFlag.OccupiedInEvent])
{
Service.CanMove = true;
+ return;
}
- //Casting the action in list.
- else if (Svc.Condition[ConditionFlag.Casting] && Player.Available)
+
+ // Casting the action in list.
+ if (Svc.Condition[ConditionFlag.Casting] && Player.Available)
{
Service.CanMove = ActionBasicInfo.ActionsNoNeedCasting.Contains(Player.Object.CastActionId);
+ return;
+ }
+
+ // Special actions.
+ var statusList = new List(4);
+ var actionList = new List(4);
+
+ if (Service.Config.PosFlameThrower)
+ {
+ statusList.Add(StatusID.Flamethrower);
+ actionList.Add(ActionID.FlameThrowerPvE);
}
- //Special actions.
- else
+ if (Service.Config.PosPassageOfArms)
{
- var statusList = new List(4);
- var actionList = new List(4);
+ statusList.Add(StatusID.PassageOfArms);
+ actionList.Add(ActionID.PassageOfArmsPvE);
+ }
+ if (Service.Config.PosImprovisation)
+ {
+ statusList.Add(StatusID.Improvisation);
+ actionList.Add(ActionID.ImprovisationPvE);
+ }
- if (Service.Config.PosFlameThrower)
- {
- statusList.Add(StatusID.Flamethrower);
- actionList.Add(ActionID.FlameThrowerPvE);
- }
- if (Service.Config.PosPassageOfArms)
+ // Action
+ var action = DateTime.Now - RSCommands._lastUsedTime < TimeSpan.FromMilliseconds(100)
+ ? (ActionID)RSCommands._lastActionID
+ : doNextAction ? (ActionID)(ActionUpdater.NextAction?.AdjustedID ?? 0) : 0;
+
+ bool specialActions = ActionManager.GetAdjustedCastTime(ActionType.Action, (uint)action) > 0;
+ foreach (var id in actionList)
+ {
+ if (Service.GetAdjustedActionId(id) == action)
{
- statusList.Add(StatusID.PassageOfArms);
- actionList.Add(ActionID.PassageOfArmsPvE);
+ specialActions = true;
+ break;
}
- if (Service.Config.PosImprovisation)
+ }
+
+ // Status
+ bool specialStatus = false;
+ foreach (var status in statusList)
+ {
+ if (Player.Object.HasStatus(true, status))
{
- statusList.Add(StatusID.Improvisation);
- actionList.Add(ActionID.ImprovisationPvE);
+ specialStatus = true;
+ break;
}
-
- //Action
- var action = DateTime.Now - RSCommands._lastUsedTime < TimeSpan.FromMilliseconds(100)
- ? (ActionID)RSCommands._lastActionID
- : doNextAction ? (ActionID)(ActionUpdater.NextAction?.AdjustedID ?? 0) : 0;
-
- var specialActions = ActionManager.GetAdjustedCastTime(ActionType.Action, (uint)action) > 0
- || actionList.Any(id => Service.GetAdjustedActionId(id) == action);
-
- //Status
- var specialStatus = Player.Object.HasStatus(true, [.. statusList]);
-
- Service.CanMove = !specialStatus && !specialActions;
}
+
+ Service.CanMove = !specialStatus && !specialActions;
}
-}
+}
\ No newline at end of file
diff --git a/RotationSolver/Updaters/PreviewUpdater.cs b/RotationSolver/Updaters/PreviewUpdater.cs
index a76fbd5c7..572240c78 100644
--- a/RotationSolver/Updaters/PreviewUpdater.cs
+++ b/RotationSolver/Updaters/PreviewUpdater.cs
@@ -21,41 +21,66 @@ internal static void UpdatePreview()
UpdateCancelCast();
}
- //public static byte Job => Job;
static IDtrBarEntry? _dtrEntry;
private static void UpdateEntry()
{
var showStr = RSCommands.EntryString;
- var icon = Player.Job switch
+ var icon = GetJobIcon(Player.Job);
+
+ if (Service.Config.ShowInfoOnDtr && !string.IsNullOrEmpty(showStr))
+ {
+ try
+ {
+ _dtrEntry ??= Svc.DtrBar.Get("Rotation Solver Reborn");
+ }
+ catch
+ {
+#pragma warning disable 0436
+ WarningHelper.AddSystemWarning("Unable to add server bar entry");
+ return;
+ }
+
+ if (!_dtrEntry.Shown) _dtrEntry.Shown = true;
+
+ _dtrEntry.Text = new SeString(
+ new IconPayload(icon),
+ new TextPayload(showStr)
+ );
+ _dtrEntry.OnClick = RSCommands.IncrementState;
+ }
+ else if (_dtrEntry != null && _dtrEntry.Shown)
+ {
+ _dtrEntry.Shown = false;
+ }
+ }
+
+ private static BitmapFontIcon GetJobIcon(Job job)
+ {
+ return job switch
{
Job.WAR => BitmapFontIcon.Warrior,
Job.PLD => BitmapFontIcon.Paladin,
Job.DRK => BitmapFontIcon.DarkKnight,
Job.GNB => BitmapFontIcon.Gunbreaker,
-
Job.AST => BitmapFontIcon.Astrologian,
Job.WHM => BitmapFontIcon.WhiteMage,
Job.SGE => BitmapFontIcon.Sage,
Job.SCH => BitmapFontIcon.Scholar,
-
Job.BLM => BitmapFontIcon.BlackMage,
Job.SMN => BitmapFontIcon.Summoner,
Job.RDM => BitmapFontIcon.RedMage,
Job.PCT => BitmapFontIcon.Pictomancer,
Job.BLU => BitmapFontIcon.BlueMage,
-
Job.MNK => BitmapFontIcon.Monk,
Job.SAM => BitmapFontIcon.Samurai,
Job.DRG => BitmapFontIcon.Dragoon,
Job.RPR => BitmapFontIcon.Reaper,
Job.NIN => BitmapFontIcon.Ninja,
Job.VPR => BitmapFontIcon.Viper,
-
Job.BRD => BitmapFontIcon.Bard,
Job.MCH => BitmapFontIcon.Machinist,
Job.DNC => BitmapFontIcon.Dancer,
-
Job.BSM => BitmapFontIcon.Blacksmith,
Job.ARM => BitmapFontIcon.Armorer,
Job.WVR => BitmapFontIcon.Weaver,
@@ -64,11 +89,9 @@ private static void UpdateEntry()
Job.LTW => BitmapFontIcon.Leatherworker,
Job.CUL => BitmapFontIcon.Culinarian,
Job.GSM => BitmapFontIcon.Goldsmith,
-
Job.FSH => BitmapFontIcon.Fisher,
Job.MIN => BitmapFontIcon.Miner,
Job.BTN => BitmapFontIcon.Botanist,
-
Job.GLA => BitmapFontIcon.Gladiator,
Job.CNJ => BitmapFontIcon.Conjurer,
Job.MRD => BitmapFontIcon.Marauder,
@@ -78,35 +101,8 @@ private static void UpdateEntry()
Job.ARC => BitmapFontIcon.Archer,
Job.THM => BitmapFontIcon.Thaumaturge,
Job.ACN => BitmapFontIcon.Arcanist,
-
_ => BitmapFontIcon.ExclamationRectangle,
};
-
- if (Service.Config.ShowInfoOnDtr && !string.IsNullOrEmpty(showStr))
- {
- try
- {
- _dtrEntry ??= Svc.DtrBar.Get("Rotation Solver Reborn");
- }
- catch
- {
-#pragma warning disable 0436
- WarningHelper.AddSystemWarning($"Unable to add server bar entry");
- return;
- }
-
- if (!_dtrEntry.Shown) _dtrEntry.Shown = true;
-
- _dtrEntry.Text = new SeString(
- new IconPayload(icon),
- new TextPayload(showStr)
- );
- _dtrEntry.OnClick = RSCommands.IncrementState;
- }
- else if (_dtrEntry != null && _dtrEntry.Shown)
- {
- _dtrEntry.Shown = false;
- }
}
static RandomDelay _tarStopCastDelay = new(() => Service.Config.StopCastingDelay);
@@ -119,9 +115,9 @@ private static unsafe void UpdateCancelCast()
&& Svc.Objects.SearchById(Player.Object.CastTargetObjectId) is IBattleChara b
&& b.IsEnemy() && b.CurrentHp == 0;
- var statusTimes = Player.Object.StatusTimes(false, [.. OtherConfiguration.NoCastingStatus.Select(i => (StatusID)i)]);
+ var statusTimes = GetStatusTimes();
- var stopDueStatus = statusTimes.Any() && statusTimes.Min() > Player.Object.TotalCastTime - Player.Object.CurrentCastTime && statusTimes.Min() < 5;
+ var stopDueStatus = statusTimes.Length > 0 && statusTimes.Min() > Player.Object.TotalCastTime - Player.Object.CurrentCastTime && statusTimes.Min() < 5;
if (_tarStopCastDelay.Delay(tarDead) || stopDueStatus)
{
@@ -129,6 +125,18 @@ private static unsafe void UpdateCancelCast()
}
}
+ private static float[] GetStatusTimes()
+ {
+ var statusTimes = new List();
+ foreach (var status in Player.Object.StatusList)
+ {
+ if (OtherConfiguration.NoCastingStatus.Contains((uint)status.StatusId))
+ {
+ statusTimes.Add(status.RemainingTime);
+ }
+ }
+ return statusTimes.ToArray();
+ }
internal static unsafe void PulseActionBar(uint actionID)
{
@@ -138,7 +146,6 @@ internal static unsafe void PulseActionBar(uint actionID)
});
}
-
private unsafe static bool IsActionSlotRight(ActionBarSlot slot, RaptureHotbarModule.HotbarSlot? hot, uint actionID)
{
if (hot.HasValue)
@@ -185,9 +192,8 @@ private static unsafe void LoopAllSlotBar(ActionBarAction doingSomething)
}
}
-
public unsafe static void Dispose()
{
}
-}
+}
\ No newline at end of file
diff --git a/RotationSolver/Updaters/StateUpdater.cs b/RotationSolver/Updaters/StateUpdater.cs
index ef0d10842..b42acf424 100644
--- a/RotationSolver/Updaters/StateUpdater.cs
+++ b/RotationSolver/Updaters/StateUpdater.cs
@@ -126,12 +126,15 @@ private static bool ShouldAddDispel()
{
if (DataCenter.DispelTarget != null)
{
- if (DataCenter.DispelTarget.StatusList.Any(StatusHelper.IsDangerous))
+ foreach (var status in DataCenter.DispelTarget.StatusList)
{
- return true;
+ if (StatusHelper.IsDangerous(status))
+ {
+ return true;
+ }
}
- else if (!DataCenter.HasHostilesInRange || Service.Config.DispelAll
- || DataCenter.IsPvP)
+
+ if (!DataCenter.HasHostilesInRange || Service.Config.DispelAll || DataCenter.IsPvP)
{
return true;
}
@@ -146,39 +149,25 @@ private static bool ShouldAddRaise()
private static bool ShouldAddPositional()
{
- // Check if the player's role is Melee and if there is a next GCD (Global Cooldown) action
- // Also, check if the configuration allows automatic use of True North
- if (DataCenter.Role == JobRole.Melee && ActionUpdater.NextGCDAction != null
- && Service.Config.AutoUseTrueNorth)
+ if (DataCenter.Role == JobRole.Melee && ActionUpdater.NextGCDAction != null && Service.Config.AutoUseTrueNorth)
{
- // Get the ID of the next GCD action
var id = ActionUpdater.NextGCDAction.ID;
- // Get the target of the next GCD action
var target = ActionUpdater.NextGCDAction.Target.Target;
- // Check if the action ID has a positional requirement
if (ConfigurationHelper.ActionPositional.TryGetValue((ActionID)id, out var positional)
- // Check if the positional requirement is not met by the target's current position
- && positional != target?.FindEnemyPositional()
- // Check if the target has positional requirements
- && target?.HasPositional() == true
- // Check if the target does not have a status that turns target into a wallboss
- && !target.HasStatus(true, StatusID.DirectionalDisregard))
+ && positional != target?.FindEnemyPositional()
+ && target?.HasPositional() == true
+ && !target.HasStatus(true, StatusID.DirectionalDisregard))
{
- // If all conditions are met, return true to add the Positional flag
return true;
}
}
- // If any condition is not met, return false
return false;
}
private static bool ShouldAddDefenseArea()
{
- if (!DataCenter.InCombat || !Service.Config.UseDefenseAbility)
- return false;
-
- return DataCenter.IsHostileCastingAOE;
+ return DataCenter.InCombat && Service.Config.UseDefenseAbility && DataCenter.IsHostileCastingAOE;
}
private static bool ShouldAddDefenseSingle()
@@ -188,29 +177,43 @@ private static bool ShouldAddDefenseSingle()
if (DataCenter.Role == JobRole.Healer)
{
- if (DataCenter.PartyMembers.Any((tank) =>
+ foreach (var tank in DataCenter.PartyMembers)
{
- var attackingTankObj = DataCenter.AllHostileTargets.Where(t => t.TargetObjectId == tank.GameObjectId);
-
- if (attackingTankObj.Count() != 1)
- return false;
-
- return DataCenter.IsHostileCastingToTank;
- }))
- {
- return true;
+ int attackingTankCount = 0;
+ foreach (var hostile in DataCenter.AllHostileTargets)
+ {
+ if (hostile.TargetObjectId == tank.GameObjectId)
+ {
+ attackingTankCount++;
+ }
+ }
+
+ if (attackingTankCount == 1 && DataCenter.IsHostileCastingToTank)
+ {
+ return true;
+ }
}
}
if (DataCenter.Role == JobRole.Tank)
{
- var movingHere = (float)DataCenter.NumberOfHostilesInRange / DataCenter.NumberOfHostilesInMaxRange > 0.3f;
+ bool movingHere = (float)DataCenter.NumberOfHostilesInRange / DataCenter.NumberOfHostilesInMaxRange > 0.3f;
+
+ int tarOnMeCount = 0;
+ int attackedCount = 0;
+ foreach (var hostile in DataCenter.AllHostileTargets)
+ {
+ if (hostile.DistanceToPlayer() <= 3 && hostile.TargetObject == Player.Object)
+ {
+ tarOnMeCount++;
+ if (ObjectHelper.IsAttacked(hostile))
+ {
+ attackedCount++;
+ }
+ }
+ }
- var tarOnMe = DataCenter.AllHostileTargets.Where(t => t.DistanceToPlayer() <= 3
- && t.TargetObject == Player.Object);
- var tarOnMeCount = tarOnMe.Count();
- var attackedCount = tarOnMe.Count(ObjectHelper.IsAttacked);
- var attacked = (float)attackedCount / tarOnMeCount > 0.7f;
+ bool attacked = (float)attackedCount / tarOnMeCount > 0.7f;
if (tarOnMeCount >= Service.Config.AutoDefenseNumber
&& Player.Object.GetHealthRatio() <= Service.Config.HealthForAutoDefense
@@ -233,15 +236,15 @@ private static bool ShouldAddHealAreaAbility()
if (!DataCenter.HPNotFull || !CanUseHealAction)
return false;
- var singleAbility = ShouldHealSingle(StatusHelper.SingleHots,
+ int singleAbility = ShouldHealSingle(StatusHelper.SingleHots,
Service.Config.HealthSingleAbility,
Service.Config.HealthSingleAbilityHot);
- var canHealAreaAbility = singleAbility > 2;
+ bool canHealAreaAbility = singleAbility > 2;
- if (DataCenter.PartyMembers.Count() > 2)
+ if (DataCenter.PartyMembers.Count > 2)
{
- var ratio = GetHealingOfTimeRatio(Player.Object, StatusHelper.AreaHots);
+ float ratio = GetHealingOfTimeRatio(Player.Object, StatusHelper.AreaHots);
if (!canHealAreaAbility)
canHealAreaAbility = DataCenter.PartyMembersDifferHP < Service.Config.HealthDifference
@@ -256,15 +259,15 @@ private static bool ShouldAddHealAreaSpell()
if (!DataCenter.HPNotFull || !CanUseHealAction)
return false;
- var singleSpell = ShouldHealSingle(StatusHelper.SingleHots,
+ int singleSpell = ShouldHealSingle(StatusHelper.SingleHots,
Service.Config.HealthSingleSpell,
Service.Config.HealthSingleSpellHot);
- var canHealAreaSpell = singleSpell > 2;
+ bool canHealAreaSpell = singleSpell > 2;
- if (DataCenter.PartyMembers.Count() > 2)
+ if (DataCenter.PartyMembers.Count > 2)
{
- var ratio = GetHealingOfTimeRatio(Player.Object, StatusHelper.AreaHots);
+ float ratio = GetHealingOfTimeRatio(Player.Object, StatusHelper.AreaHots);
if (!canHealAreaSpell)
canHealAreaSpell = DataCenter.PartyMembersDifferHP < Service.Config.HealthDifference
@@ -279,7 +282,7 @@ private static bool ShouldAddHealSingleAbility()
if (!DataCenter.HPNotFull || !CanUseHealAction)
return false;
- var onlyHealSelf = Service.Config.OnlyHealSelfWhenNoHealer
+ bool onlyHealSelf = Service.Config.OnlyHealSelfWhenNoHealer
&& DataCenter.Role != JobRole.Healer;
if (onlyHealSelf)
@@ -289,7 +292,7 @@ private static bool ShouldAddHealSingleAbility()
}
else
{
- var singleAbility = ShouldHealSingle(StatusHelper.SingleHots,
+ int singleAbility = ShouldHealSingle(StatusHelper.SingleHots,
Service.Config.HealthSingleAbility,
Service.Config.HealthSingleAbilityHot);
@@ -302,7 +305,7 @@ private static bool ShouldAddHealSingleSpell()
if (!DataCenter.HPNotFull || !CanUseHealAction)
return false;
- var onlyHealSelf = Service.Config.OnlyHealSelfWhenNoHealer
+ bool onlyHealSelf = Service.Config.OnlyHealSelfWhenNoHealer
&& DataCenter.Role != JobRole.Healer;
if (onlyHealSelf)
@@ -312,7 +315,7 @@ private static bool ShouldAddHealSingleSpell()
}
else
{
- var singleSpell = ShouldHealSingle(StatusHelper.SingleHots,
+ int singleSpell = ShouldHealSingle(StatusHelper.SingleHots,
Service.Config.HealthSingleSpell,
Service.Config.HealthSingleSpellHot);
@@ -322,10 +325,7 @@ private static bool ShouldAddHealSingleSpell()
private static bool ShouldAddAntiKnockback()
{
- if (!DataCenter.InCombat || !Service.Config.UseKnockback)
- return false;
-
- return DataCenter.AreHostilesCastingKnockback;
+ return DataCenter.InCombat && Service.Config.UseKnockback && DataCenter.AreHostilesCastingKnockback;
}
private static bool ShouldAddProvoke()
@@ -335,7 +335,7 @@ private static bool ShouldAddProvoke()
if (DataCenter.Role == JobRole.Tank
&& (Service.Config.AutoProvokeForTank
- || DataCenter.AllianceMembers.Count(o => o.IsJobCategory(JobRole.Tank)) < 2)
+ || CountAllianceTanks() < 2)
&& DataCenter.ProvokeTarget != null)
{
return true;
@@ -346,10 +346,7 @@ private static bool ShouldAddProvoke()
private static bool ShouldAddInterrupt()
{
- if (!DataCenter.InCombat)
- return false;
-
- return DataCenter.InterruptTarget != null && Service.Config.InterruptibleMoreCheck;
+ return DataCenter.InCombat && DataCenter.InterruptTarget != null && Service.Config.InterruptibleMoreCheck;
}
private static bool ShouldAddTankStance()
@@ -357,8 +354,7 @@ private static bool ShouldAddTankStance()
if (!Service.Config.AutoTankStance || DataCenter.Role != JobRole.Tank)
return false;
- if (!DataCenter.AllianceMembers.Any(t => t.IsJobCategory(JobRole.Tank) && t.CurrentHp != 0 && t.HasStatus(false, StatusHelper.TankStanceStatus))
- && !CustomRotation.HasTankStance)
+ if (!AnyAllianceTankWithStance() && !CustomRotation.HasTankStance)
{
return true;
}
@@ -377,21 +373,31 @@ static float GetHealingOfTimeRatio(IBattleChara target, params StatusID[] status
{
const float buffWholeTime = 15;
- var buffTime = target.StatusTime(false, statusIds);
+ float buffTime = target.StatusTime(false, statusIds);
return Math.Min(1, buffTime / buffWholeTime);
}
static int ShouldHealSingle(StatusID[] hotStatus, float healSingle, float healSingleHot)
- => DataCenter.PartyMembers.Count(p => ShouldHealSingle(p, hotStatus, healSingle, healSingleHot));
+ {
+ int count = 0;
+ foreach (var member in DataCenter.PartyMembers)
+ {
+ if (ShouldHealSingle(member, hotStatus, healSingle, healSingleHot))
+ {
+ count++;
+ }
+ }
+ return count;
+ }
static bool ShouldHealSingle(IBattleChara target, StatusID[] hotStatus, float healSingle, float healSingleHot)
{
if (target == null) return false;
- var ratio = GetHealingOfTimeRatio(target, hotStatus);
+ float ratio = GetHealingOfTimeRatio(target, hotStatus);
- var h = target.GetHealthRatio();
+ float h = target.GetHealthRatio();
if (h == 0 || !target.NeedHealing()) return false;
return h < Lerp(healSingle, healSingleHot, ratio);
@@ -459,4 +465,29 @@ private static void AddStatus(ref AutoStatus status, AutoStatus flag, Func
status |= flag;
}
+
+ private static int CountAllianceTanks()
+ {
+ int count = 0;
+ foreach (var member in DataCenter.AllianceMembers)
+ {
+ if (member.IsJobCategory(JobRole.Tank))
+ {
+ count++;
+ }
+ }
+ return count;
+ }
+
+ private static bool AnyAllianceTankWithStance()
+ {
+ foreach (var member in DataCenter.AllianceMembers)
+ {
+ if (member.IsJobCategory(JobRole.Tank) && member.CurrentHp != 0 && member.HasStatus(false, StatusHelper.TankStanceStatus))
+ {
+ return true;
+ }
+ }
+ return false;
+ }
}
\ No newline at end of file
diff --git a/RotationSolver/Updaters/TargetUpdater.cs b/RotationSolver/Updaters/TargetUpdater.cs
index dd6551af9..f15bfe083 100644
--- a/RotationSolver/Updaters/TargetUpdater.cs
+++ b/RotationSolver/Updaters/TargetUpdater.cs
@@ -24,8 +24,8 @@ internal static void UpdateTargets()
DataCenter.DeathTarget = GetDeathTarget();
DataCenter.DispelTarget = GetDispelTarget();
DataCenter.AllHostileTargets = GetAllHostileTargets();
- DataCenter.ProvokeTarget = DataCenter.AllHostileTargets.FirstOrDefault(ObjectHelper.CanProvoke);
- DataCenter.InterruptTarget = DataCenter.AllHostileTargets.FirstOrDefault(ObjectHelper.CanInterrupt);
+ DataCenter.ProvokeTarget = GetFirstHostileTarget(ObjectHelper.CanProvoke);
+ DataCenter.InterruptTarget = GetFirstHostileTarget(ObjectHelper.CanInterrupt);
UpdateTimeToKill();
}
@@ -145,7 +145,8 @@ private static List GetAllHostileTargets()
{
if (target == null) continue;
if (!target.IsEnemy() || !target.IsTargetable) continue;
- if (target.StatusList?.Any(StatusHelper.IsInvincible) == true && (DataCenter.IsPvP && !Service.Config.IgnorePvPInvincibility || !DataCenter.IsPvP)) continue;
+ if (target.StatusList != null && target.StatusList.Any(StatusHelper.IsInvincible) &&
+ (DataCenter.IsPvP && !Service.Config.IgnorePvPInvincibility || !DataCenter.IsPvP)) continue;
if (target.HasStatus(true, StatusID.StrongOfShield) && strongOfShieldPositional != target.FindEnemyPositional()) continue;
hostileTargets.Add(target);
@@ -158,76 +159,82 @@ private static List GetAllHostileTargets()
return hostileTargets;
}
+ private static IBattleChara? GetFirstHostileTarget(Func predicate)
+ {
+ foreach (var target in DataCenter.AllHostileTargets)
+ {
+ if (predicate(target))
+ {
+ return target;
+ }
+ }
+ return null;
+ }
+
private static IBattleChara? GetDeathTarget()
{
- var rotation = DataCenter.RightNowRotation;
if (Player.Job == Job.WHM || Player.Job == Job.SCH || Player.Job == Job.AST || Player.Job == Job.SGE ||
Player.Job == Job.SMN || Player.Job == Job.RDM)
{
try
{
- var deathAll = DataCenter.AllianceMembers?.GetDeath() ?? new List();
- var deathParty = DataCenter.PartyMembers?.GetDeath() ?? new List();
- var deathNPC = DataCenter.FriendlyNPCMembers?.GetDeath() ?? new List();
+ var deathAll = DataCenter.AllianceMembers?.GetDeath().ToList() ?? new List();
+ var deathParty = DataCenter.PartyMembers?.GetDeath().ToList() ?? new List();
+ var deathNPC = DataCenter.FriendlyNPCMembers?.GetDeath().ToList() ?? new List();
- if (deathParty.Any())
- {
- var deathT = deathParty.GetJobCategory(JobRole.Tank).ToList();
- var deathH = deathParty.GetJobCategory(JobRole.Healer).ToList();
-
- if (deathT.Count > 1) return deathT.FirstOrDefault();
- if (deathH.Any()) return deathH.FirstOrDefault();
- if (deathT.Any()) return deathT.FirstOrDefault();
+ var deathTarget = GetPriorityDeathTarget(deathParty);
+ if (deathTarget != null) return deathTarget;
- return deathParty.FirstOrDefault();
- }
+ deathTarget = GetPriorityDeathTarget(deathAll, Service.Config.RaiseType);
+ if (deathTarget != null) return deathTarget;
- if (deathAll.Any())
+ if (Service.Config.FriendlyPartyNpcHealRaise2)
{
- if (Service.Config.RaiseType == RaiseType.PartyAndAllianceHealers)
- {
- var deathAllH = deathAll.GetJobCategory(JobRole.Healer).ToList();
- if (deathAllH.Any()) return deathAllH.FirstOrDefault();
- }
-
- if (Service.Config.RaiseType == RaiseType.PartyAndAlliance)
- {
- var deathAllH = deathAll.GetJobCategory(JobRole.Healer).ToList();
- var deathAllT = deathAll.GetJobCategory(JobRole.Tank).ToList();
-
- if (deathAllH.Any()) return deathAllH.FirstOrDefault();
- if (deathAllT.Any()) return deathAllT.FirstOrDefault();
-
- return deathAll.FirstOrDefault();
- }
+ deathTarget = GetPriorityDeathTarget(deathNPC);
+ if (deathTarget != null) return deathTarget;
}
+ }
+ catch (Exception ex)
+ {
+ Svc.Log.Error($"Error in GetDeathTarget: {ex.Message}");
+ }
+ }
+ return null;
+ }
- if (deathNPC.Any() && Service.Config.FriendlyPartyNpcHealRaise2)
- {
- var deathNPCT = deathNPC.GetJobCategory(JobRole.Tank).ToList();
- var deathNPCH = deathNPC.GetJobCategory(JobRole.Healer).ToList();
+ private static IBattleChara? GetPriorityDeathTarget(List deathList, RaiseType raiseType = RaiseType.PartyOnly)
+ {
+ if (deathList.Count == 0) return null;
- if (deathNPCT.Count > 1) return deathNPCT.FirstOrDefault();
- if (deathNPCH.Any()) return deathNPCH.FirstOrDefault();
- if (deathNPCT.Any()) return deathNPCT.FirstOrDefault();
+ var deathTanks = new List();
+ var deathHealers = new List();
- return deathNPC.FirstOrDefault();
- }
+ foreach (var chara in deathList)
+ {
+ if (chara.IsJobCategory(JobRole.Tank))
+ {
+ deathTanks.Add(chara);
}
- catch (Exception ex)
+ else if (chara.IsJobCategory(JobRole.Healer))
{
- Svc.Log.Error($"Error in GetDeathTarget: {ex.Message}");
+ deathHealers.Add(chara);
}
+ }
- return null;
+ if (raiseType == RaiseType.PartyAndAllianceHealers && deathHealers.Count > 0)
+ {
+ return deathHealers[0];
}
- return null;
+ if (deathTanks.Count > 1) return deathTanks[0];
+ if (deathHealers.Count > 0) return deathHealers[0];
+ if (deathTanks.Count > 0) return deathTanks[0];
+
+ return deathList[0];
}
private static IBattleChara? GetDispelTarget()
{
- var rotation = DataCenter.RightNowRotation;
if (Player.Job == Job.WHM || Player.Job == Job.SCH || Player.Job == Job.AST || Player.Job == Job.SGE ||
Player.Job == Job.BRD)
{
@@ -235,47 +242,51 @@ private static List GetAllHostileTargets()
var weakenNPC = new List();
var dyingPeople = new List();
- if (DataCenter.PartyMembers != null)
- {
- foreach (var member in DataCenter.PartyMembers)
- {
- if (member is IBattleChara b && b.StatusList != null &&
- b.StatusList.Any(status => status != null && status.CanDispel()))
- {
- weakenPeople.Add(b);
- }
- }
- }
+ AddDispelTargets(DataCenter.PartyMembers, weakenPeople);
+ AddDispelTargets(DataCenter.FriendlyNPCMembers, weakenNPC);
- if (DataCenter.FriendlyNPCMembers != null)
+ foreach (var person in weakenPeople)
{
- foreach (var npc in DataCenter.FriendlyNPCMembers)
+ if (person.StatusList != null && person.StatusList.Any(status => status != null && status.IsDangerous()))
{
- if (npc is IBattleChara b && b.StatusList != null &&
- b.StatusList.Any(status => status != null && status.CanDispel()))
- {
- weakenNPC.Add(b);
- }
+ dyingPeople.Add(person);
}
}
- foreach (var person in weakenPeople)
+ return GetClosestTarget(dyingPeople) ?? GetClosestTarget(weakenPeople) ?? GetClosestTarget(weakenNPC);
+ }
+ return null;
+ }
+
+ private static void AddDispelTargets(List? members, List targetList)
+ {
+ if (members == null) return;
+
+ foreach (var member in members)
+ {
+ if (member.StatusList != null && member.StatusList.Any(status => status != null && status.CanDispel()))
{
- if (person is IBattleChara b && b.StatusList != null &&
- b.StatusList.Any(status => status != null && status.IsDangerous()))
- {
- dyingPeople.Add(b);
- }
+ targetList.Add(member);
}
-
- return dyingPeople.OrderBy(ObjectHelper.DistanceToPlayer).FirstOrDefault()
- ?? weakenPeople.OrderBy(ObjectHelper.DistanceToPlayer).FirstOrDefault()
- ?? weakenNPC.OrderBy(ObjectHelper.DistanceToPlayer).FirstOrDefault();
}
- else
+ }
+
+ private static IBattleChara? GetClosestTarget(List targets)
+ {
+ IBattleChara? closestTarget = null;
+ float closestDistance = float.MaxValue;
+
+ foreach (var target in targets)
{
- return null;
+ var distance = ObjectHelper.DistanceToPlayer(target);
+ if (distance < closestDistance)
+ {
+ closestDistance = distance;
+ closestTarget = target;
+ }
}
+
+ return closestTarget;
}
private static void UpdateTimeToKill()
@@ -300,4 +311,4 @@ private static void UpdateTimeToKill()
DataCenter.RecordedHP.Enqueue((now, currentHPs));
}
-}
+}
\ No newline at end of file