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