diff --git a/Resources/HostileCastingArea.json b/Resources/HostileCastingArea.json index 58db71b49..fa68e2458 100644 --- a/Resources/HostileCastingArea.json +++ b/Resources/HostileCastingArea.json @@ -640,5 +640,12 @@ 36610, 36612, 27145, - 27181 + 27181, + 24522, + 13066, + 13073, + 13085, + 13074, + 26050, + 26049 ] \ No newline at end of file diff --git a/Resources/HostileCastingTank.json b/Resources/HostileCastingTank.json index 015779de9..61557738c 100644 --- a/Resources/HostileCastingTank.json +++ b/Resources/HostileCastingTank.json @@ -22,5 +22,8 @@ 33965, 35715, 37845, - 29023 + 29023, + 10542, + 26040, + 26041 ] \ No newline at end of file diff --git a/RotationSolver.Basic/Configuration/Configs.cs b/RotationSolver.Basic/Configuration/Configs.cs index 5b58c2a8e..c1c2be7bb 100644 --- a/RotationSolver.Basic/Configuration/Configs.cs +++ b/RotationSolver.Basic/Configuration/Configs.cs @@ -102,14 +102,6 @@ public const string [UI("", Action = ActionID.ImprovisationPvE, Parent = nameof(PoslockCasting))] public bool PosImprovisation { get; set; } = false; - [ConditionBool, UI("Add enemy list to the hostile targets.", - Filter = TargetConfig)] - private static readonly bool _addEnemyListToHostile = true; - - [ConditionBool, UI("Only attack the targets in enemy list.", - Parent = nameof(AddEnemyListToHostile))] - private static readonly bool _onlyAttackInEnemyList = false; - [JobConfig, UI("Only used automatically if coded into the rotation", Filter = AutoActionUsage, PvPFilter = JobFilterType.NoJob)] private readonly TinctureUseType _TinctureType = TinctureUseType.Nowhere; diff --git a/RotationSolver.Basic/Data/TargetHostileType.cs b/RotationSolver.Basic/Data/TargetHostileType.cs index c103a256c..48f85e1af 100644 --- a/RotationSolver.Basic/Data/TargetHostileType.cs +++ b/RotationSolver.Basic/Data/TargetHostileType.cs @@ -28,4 +28,16 @@ public enum TargetHostileType : byte /// [Description("All targets when solo, or previously engaged.")] AllTargetsWhenSolo, + + /// + /// Targets in your enemy list. + /// + [Description("Only attack targets in your parties enemy list")] + TargetIsInEnemiesList, + + /// + /// All targets when solo, or only attack targets in your parties enemy list. + /// + [Description("All targets when solo, or only attack targets in your parties enemy list")] + AllTargetsWhenSoloTargetIsInEnemiesList, } \ No newline at end of file diff --git a/RotationSolver.Basic/DataCenter.cs b/RotationSolver.Basic/DataCenter.cs index 8d7712f9c..d9b1b87d7 100644 --- a/RotationSolver.Basic/DataCenter.cs +++ b/RotationSolver.Basic/DataCenter.cs @@ -471,12 +471,12 @@ public static ulong[] TreasureCharas public static bool HasHostilesInRange => NumberOfHostilesInRange > 0; public static bool HasHostilesInMaxRange => NumberOfHostilesInMaxRange > 0; - public static int NumberOfHostilesInRange => AllHostileTargets.Count(o => o.DistanceToPlayer() <= JobRange); - public static int NumberOfHostilesInMaxRange => AllHostileTargets.Count(o => o.DistanceToPlayer() <= 25); - public static int NumberOfAllHostilesInRange => AllHostileTargets.Count(o => o.DistanceToPlayer() <= JobRange); - public static int NumberOfAllHostilesInMaxRange => AllHostileTargets.Count(o => o.DistanceToPlayer() <= 25); + public static int NumberOfHostilesInRange => AllHostileTargets.Count(o => o.DistanceToPlayer() < JobRange); + public static int NumberOfHostilesInMaxRange => AllHostileTargets.Count(o => o.DistanceToPlayer() < 25); + public static int NumberOfAllHostilesInRange => AllHostileTargets.Count(o => o.DistanceToPlayer() < JobRange); + public static int NumberOfAllHostilesInMaxRange => AllHostileTargets.Count(o => o.DistanceToPlayer() < 25); - public static bool MobsTime => AllHostileTargets.Count(o => o.DistanceToPlayer() <= JobRange && o.CanSee()) + public static bool MobsTime => AllHostileTargets.Count(o => o.DistanceToPlayer() < JobRange && o.CanSee()) >= Service.Config.AutoDefenseNumber; public static bool AreHostilesCastingKnockback => AllHostileTargets.Any(IsHostileCastingKnockback); diff --git a/RotationSolver.Basic/Helpers/ObjectHelper.cs b/RotationSolver.Basic/Helpers/ObjectHelper.cs index 343b0bebb..0fa594a24 100644 --- a/RotationSolver.Basic/Helpers/ObjectHelper.cs +++ b/RotationSolver.Basic/Helpers/ObjectHelper.cs @@ -10,8 +10,11 @@ using FFXIVClientStructs.FFXIV.Client.Graphics; using FFXIVClientStructs.FFXIV.Client.UI; using FFXIVClientStructs.FFXIV.Common.Component.BGCollision; +using FFXIVClientStructs.FFXIV.Component.GUI; using RotationSolver.Basic.Configuration; using System.Collections.Concurrent; +using System.Runtime.InteropServices; +using System.Text; using System.Text.RegularExpressions; namespace RotationSolver.Basic.Helpers; @@ -95,53 +98,123 @@ internal static bool IsAttackable(this IBattleChara battleChara) if (tarFateId != 0 && tarFateId != DataCenter.FateId) return false; } - if (Service.Config.AddEnemyListToHostile) - { - if (battleChara.IsInEnemiesList()) return true; - // Only attack - if (Service.Config.OnlyAttackInEnemyList) return false; - } - - // Tar on me - if (battleChara.TargetObject == Player.Object - || battleChara.TargetObject?.OwnerId == Player.Object.GameObjectId) return true; - if (battleChara.IsOthersPlayers()) return false; if (battleChara.IsTopPriorityHostile()) return true; if (Service.CountDownTime > 0 || DataCenter.IsPvP) return true; - return DataCenter.RightNowTargetToHostileType switch { + // Tar on me + if (battleChara.TargetObject == Player.Object + || battleChara.TargetObject?.OwnerId == Player.Object.GameObjectId) return true; + + return DataCenter.RightNowTargetToHostileType switch + { TargetHostileType.AllTargetsCanAttack => true, TargetHostileType.TargetsHaveTarget => battleChara.TargetObject is IBattleChara, TargetHostileType.AllTargetsWhenSolo => DataCenter.PartyMembers.Length < 2 || battleChara.TargetObject is IBattleChara, TargetHostileType.AllTargetsWhenSoloInDuty => (DataCenter.PartyMembers.Length < 2 && Svc.Condition[ConditionFlag.BoundByDuty]) || battleChara.TargetObject is IBattleChara, + TargetHostileType.TargetIsInEnemiesList => battleChara.TargetObject is IBattleChara target && target.IsInEnemiesList(), + TargetHostileType.AllTargetsWhenSoloTargetIsInEnemiesList => DataCenter.PartyMembers.Length < 2 || battleChara.TargetObject is IBattleChara target && target.IsInEnemiesList(), _ => true, }; } - internal static unsafe bool IsInEnemiesList(this IBattleChara IBattleChara) + private static string RemoveControlCharacters(string input) + { + if (string.IsNullOrEmpty(input)) + return input; + + // Use a StringBuilder for efficient string manipulation + var output = new StringBuilder(input.Length); + foreach (char c in input) + { + // Exclude control characters and private use area characters + if (!char.IsControl(c) && (c < '\uE000' || c > '\uF8FF')) + { + output.Append(c); + } + } + return output.ToString(); + } + + //Below never returns true + internal static unsafe bool IsInEnemiesList(this IBattleChara battleChara) { var addons = Service.GetAddons(); - if (!addons.Any()) return false; + if (!addons.Any()) + { + return false; + } + + if (!addons.Any()) + { + return false; + } + var addon = addons.FirstOrDefault(); - var enemy = (AddonEnemyList*)addon; + if (addon == IntPtr.Zero) + { + return false; + } - var numArray = FFXIVClientStructs.FFXIV.Client.System.Framework.Framework.Instance()->GetUIModule()->GetRaptureAtkModule()->AtkModule.AtkArrayDataHolder.NumberArrays[19]; - if (numArray == null) return false; + var enemyList = (AddonEnemyList*)addon; - const int baseIndex = 8; - const int step = 6; + // Ensure that EnemyOneComponent is valid + if (enemyList->EnemyOneComponent == null) + { + return false; + } + + // EnemyCount indicates how many enemies are in the list + var enemyCount = enemyList->EnemyCount; - for (var i = 0; i < enemy->EnemyCount; i++) + for (int i = 0; i < enemyCount; i++) { - var id = (uint)numArray->IntArray[baseIndex + i * step]; + // Access each enemy component + var enemyComponentPtr = enemyList->EnemyOneComponent + i; + if (enemyComponentPtr == null || *enemyComponentPtr == null) + { + continue; + } + + var enemyComponent = *enemyComponentPtr; + var atkComponentBase = enemyComponent->AtkComponentBase; - if (IBattleChara.GameObjectId == id) return true; + // Access the UldManager's NodeList + var uldManager = atkComponentBase.UldManager; + + for (int j = 0; j < uldManager.NodeListCount; j++) + { + var node = uldManager.NodeList[j]; + if (node == null) + continue; + + if (node->Type == NodeType.Text) + { + var textNode = (AtkTextNode*)node; + if (textNode->NodeText.StringPtr == null) + continue; + + // Read the enemy's name + var enemyNameRaw = Marshal.PtrToStringUTF8((IntPtr)textNode->NodeText.StringPtr); + if (string.IsNullOrEmpty(enemyNameRaw)) + continue; + + // Remove control characters from the enemy's name + var enemyName = RemoveControlCharacters(enemyNameRaw); + + // Compare with battleChara's name + if (string.Equals(enemyName, battleChara.Name.TextValue, StringComparison.OrdinalIgnoreCase)) + { + return true; + } + } + } } + return false; } diff --git a/RotationSolver/RotationSolverPlugin.cs b/RotationSolver/RotationSolverPlugin.cs index 2435cc26e..e4de69828 100644 --- a/RotationSolver/RotationSolverPlugin.cs +++ b/RotationSolver/RotationSolverPlugin.cs @@ -12,12 +12,10 @@ using RotationSolver.Commands; using RotationSolver.Data; using RotationSolver.Helpers; - using RotationSolver.UI; using RotationSolver.UI.HighlightTeachingMode; using RotationSolver.UI.HighlightTeachingMode.ElementSpecial; using RotationSolver.Updaters; -using static FFXIVClientStructs.FFXIV.Client.UI.Agent.AgentPartyMember.Delegates; using WelcomeWindow = RotationSolver.UI.WelcomeWindow; namespace RotationSolver; @@ -249,7 +247,7 @@ internal static void UpdateDisplayWindow() isValid &= !Service.Config.OnlyShowWithHostileOrInDuty || Svc.Condition[ConditionFlag.BoundByDuty] - || DataCenter.AllHostileTargets.Any(o => o.DistanceToPlayer() <= 25); + || DataCenter.AllHostileTargets.Any(o => o.DistanceToPlayer() < 25); _controlWindow!.IsOpen = isValid && Service.Config.ShowControlWindow; _cooldownWindow!.IsOpen = isValid && Service.Config.ShowCooldownWindow; diff --git a/RotationSolver/UI/RotationConfigWindow.cs b/RotationSolver/UI/RotationConfigWindow.cs index 507c6ca7a..09c3b5d1f 100644 --- a/RotationSolver/UI/RotationConfigWindow.cs +++ b/RotationSolver/UI/RotationConfigWindow.cs @@ -2607,21 +2607,21 @@ private static void DrawDebug() } private static readonly CollapsingHeaderGroup _debugHeader = new(new() -{ - {() => DataCenter.RightNowRotation != null ? "Rotation" : string.Empty, DrawDebugRotationStatus}, - {() => "Status", DrawStatus }, - {() => "Party", DrawParty }, - {() => "Target Data", DrawTargetData }, - {() => "Next Action", DrawNextAction }, - {() => "Last Action", DrawLastAction }, - {() => "Others", DrawOthers }, - {() => "Effect", () => - { - ImGui.Text(Watcher.ShowStrSelf); - ImGui.Separator(); - ImGui.Text(DataCenter.Role.ToString()); - } }, -}); + { + {() => DataCenter.RightNowRotation != null ? "Rotation" : string.Empty, DrawDebugRotationStatus}, + {() => "Status", DrawStatus }, + {() => "Party", DrawParty }, + {() => "Target Data", DrawTargetData }, + {() => "Next Action", DrawNextAction }, + {() => "Last Action", DrawLastAction }, + {() => "Others", DrawOthers }, + {() => "Effect", () => + { + ImGui.Text(Watcher.ShowStrSelf); + ImGui.Separator(); + ImGui.Text(DataCenter.Role.ToString()); + } }, + }); private static void DrawDebugRotationStatus() {