From 990293271e7fa287f52ced4381f3d282d8ebfee2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=A7=8B=E6=B0=B4?= <1123993881@qq.com> Date: Tue, 31 Jan 2023 16:09:48 +0800 Subject: [PATCH] style: change the target updating. --- .../Actions/BaseAction/BaseAction_Target.cs | 54 +++--- .../Configuration/PluginConfiguration.cs | 1 + RotationSolver/Helpers/ObjectHelper.cs | 18 ++ RotationSolver/Helpers/StatusHelper.cs | 10 ++ RotationSolver/Helpers/TargetFilter.cs | 20 +-- RotationSolver/Rotations/Basic/DNC_Base.cs | 31 +--- .../RangedPhysicial/DNC/DNC_Default.cs | 25 ++- RotationSolver/Updaters/MajorUpdater.cs | 3 +- .../Updaters/TargetUpdater_Friends.cs | 155 ++++++------------ .../Updaters/TargetUpdater_Hostile.cs | 133 +++++++-------- .../Updaters/TargetUpdater_Major.cs | 22 +++ .../Windows/RotationConfigWindow_Debug.cs | 2 +- .../Windows/RotationConfigWindow_Param.cs | 2 + 13 files changed, 215 insertions(+), 261 deletions(-) create mode 100644 RotationSolver/Updaters/TargetUpdater_Major.cs diff --git a/RotationSolver/Actions/BaseAction/BaseAction_Target.cs b/RotationSolver/Actions/BaseAction/BaseAction_Target.cs index 8405a9d57..470bff050 100644 --- a/RotationSolver/Actions/BaseAction/BaseAction_Target.cs +++ b/RotationSolver/Actions/BaseAction/BaseAction_Target.cs @@ -1,4 +1,5 @@ -using Dalamud.Game.ClientState.Objects.Types; +using Dalamud.Game.ClientState.Objects.SubKinds; +using Dalamud.Game.ClientState.Objects.Types; using Dalamud.Logging; using RotationSolver.Commands; using RotationSolver.Data; @@ -78,7 +79,8 @@ private bool FindTarget(bool mustUse, out BattleChara target) } else if (_action.TargetArea) { - return TargetArea(range, mustUse, aoeCount, out target); + target = player; + return TargetArea(range, mustUse, aoeCount, player); } //如果能对友方和敌方都能选中 else if (_action.CanTargetParty && _action.CanTargetHostile) @@ -103,49 +105,44 @@ private bool FindTarget(bool mustUse, out BattleChara target) } else { - target = Service.TargetManager.Target is BattleChara battle ? battle : Service.ClientState.LocalPlayer; + target = Service.TargetManager.Target is BattleChara battle ? battle : player; return true; } } #region TargetArea - private bool TargetArea(float range, bool mustUse, int aoeCount, out BattleChara target) + private bool TargetArea(float range, bool mustUse, int aoeCount, PlayerCharacter player) { //移动 if (_action.EffectRange == 1 && range >= 15) { - return TargetAreaMove(range, mustUse, out target); + return TargetAreaMove(range, mustUse); } //其他友方 else if (_isFriendly) { - return TargetAreaFriend(range, mustUse, out target); + return TargetAreaFriend(range, mustUse, player); } //敌方 else { - return TargetAreaHostile(aoeCount, out target); + return TargetAreaHostile(aoeCount); } } - private bool TargetAreaHostile(int aoeCount, out BattleChara target) + private bool TargetAreaHostile(int aoeCount) { - target = GetMostObjects(TargetUpdater.HostileTargets, aoeCount) + var target = GetMostObjects(TargetUpdater.HostileTargets, aoeCount) .OrderByDescending(ObjectHelper.GetHealthRatio).FirstOrDefault(); - if (target == null) - { - target = Service.ClientState.LocalPlayer; - return false; - } + if (target == null) return false; _position = target.Position; return true; } - private bool TargetAreaMove(float range, bool mustUse, out BattleChara target) + private bool TargetAreaMove(float range, bool mustUse) { if (Service.Configuration.MoveAreaActionFarthest) { - target = null; Vector3 pPosition = Service.ClientState.LocalPlayer.Position; float rotation = Service.ClientState.LocalPlayer.Rotation; _position = new Vector3(pPosition.X + (float)Math.Sin(rotation) * range, pPosition.Y, @@ -154,25 +151,24 @@ private bool TargetAreaMove(float range, bool mustUse, out BattleChara target) } else { - var availableCharas = Service.ObjectTable.Where(b => b.ObjectId != Service.ClientState.LocalPlayer.ObjectId).OfType(); - target = TargetFilter.GetObjectInRadius(availableCharas, range).FindTargetForMoving(mustUse); + + var availableCharas = TargetUpdater.AllTargets.Where(b => b.ObjectId != Service.ClientState.LocalPlayer.ObjectId); + var target = TargetFilter.GetObjectInRadius(availableCharas, range).FindTargetForMoving(mustUse); if (target == null) return false; _position = target.Position; return true; } } - private bool TargetAreaFriend(float range, bool mustUse, out BattleChara target) + private bool TargetAreaFriend(float range, bool mustUse, PlayerCharacter player) { - target = null; //如果用户不想使用自动友方地面放置功能 if (!Service.Configuration.UseGroundBeneficialAbility) return false; //如果当前目标是Boss且有身位,放他身上。 if (Service.TargetManager.Target is BattleChara b && b.DistanceToPlayer() < range && b.IsBoss() && b.HasPositional()) { - target = b; - _position = target.Position; + _position = b.Position; return true; } //计算玩家和被打的T之间的关系。 @@ -180,27 +176,25 @@ private bool TargetAreaFriend(float range, bool mustUse, out BattleChara target) { var attackT = TargetFilter.FindAttackedTarget(TargetUpdater.PartyTanks.GetObjectInRadius(range + _action.EffectRange), mustUse); - target = Service.ClientState.LocalPlayer; - if (attackT == null) { - _position = target.Position; + _position = player.Position; } else { - var disToTankRound = Math.Max(range, Vector3.Distance(target.Position, attackT.Position) + attackT.HitboxRadius); + var disToTankRound = Math.Max(range, Vector3.Distance(player.Position, attackT.Position) + attackT.HitboxRadius); if (disToTankRound < _action.EffectRange - || disToTankRound > 2 * _action.EffectRange - target.HitboxRadius + || disToTankRound > 2 * _action.EffectRange - player.HitboxRadius || disToTankRound > range) { - _position = target.Position; + _position = player.Position; } else { - Vector3 directionToTank = attackT.Position - target.Position; + Vector3 directionToTank = attackT.Position - player.Position; var MoveDirection = directionToTank / directionToTank.Length() * (disToTankRound - _action.EffectRange); - _position = target.Position + MoveDirection; + _position = player.Position + MoveDirection; } } return true; diff --git a/RotationSolver/Configuration/PluginConfiguration.cs b/RotationSolver/Configuration/PluginConfiguration.cs index e74c7f1cd..1354bed52 100644 --- a/RotationSolver/Configuration/PluginConfiguration.cs +++ b/RotationSolver/Configuration/PluginConfiguration.cs @@ -91,6 +91,7 @@ public class PluginConfiguration : IPluginConfiguration public Dictionary HealthForDyingTanks { get; set; } = new Dictionary(); public float InterruptibleTime = 0.5f; + public bool InterruptibleMoreCheck = true; public float SpecialDuration = 3; public float WeaponInterval = 0.67f; public float WeaponFaster = 0.08f; diff --git a/RotationSolver/Helpers/ObjectHelper.cs b/RotationSolver/Helpers/ObjectHelper.cs index 88b49524e..2190b5d13 100644 --- a/RotationSolver/Helpers/ObjectHelper.cs +++ b/RotationSolver/Helpers/ObjectHelper.cs @@ -1,4 +1,5 @@ using Dalamud.Game.ClientState.Objects.Types; +using FFXIVClientStructs.FFXIV.Client.Game; using Lumina.Excel.GeneratedSheets; using RotationSolver.Data; using RotationSolver.Updaters; @@ -28,6 +29,23 @@ internal static unsafe uint FateId(this GameObject obj) internal static unsafe bool IsTargetable(this GameObject obj) => ((FFXIVClientStructs.FFXIV.Client.Game.Object.GameObject*)(void*)obj.Address)->GetIsTargetable(); + internal static bool CanInterrupt(this BattleChara b) + { + var baseCheck = b.IsCasting && b.IsCastInterruptible && b.TotalCastTime >= 2 + && b.CurrentCastTime >= Service.Configuration.InterruptibleTime; + + if (!baseCheck) return false; + if (!Service.Configuration.InterruptibleMoreCheck) return true; + + var act = Service.DataManager.GetExcelSheet().GetRow(b.CastActionId); + if (act == null) return false; + + //跳过扇形圆型 + if (act.CastType is 3 or 4) return false; + if (ActionManager.GetActionRange(b.CastActionId) < 8) return false; + return true; + } + /// /// Is character a boss? Max HP exceeds a certain amount. /// diff --git a/RotationSolver/Helpers/StatusHelper.cs b/RotationSolver/Helpers/StatusHelper.cs index 89f65c7aa..d550cbb53 100644 --- a/RotationSolver/Helpers/StatusHelper.cs +++ b/RotationSolver/Helpers/StatusHelper.cs @@ -12,6 +12,16 @@ namespace RotationSolver.Helpers; internal static class StatusHelper { + public static StatusID[] AreaHots { get; } = new StatusID[] + { + StatusID.AspectedHelios, StatusID.Medica2, StatusID.TrueMedica2 + }; + + public static StatusID[] SingleHots { get; } = new StatusID[] + { + StatusID.AspectedBenefic, StatusID.Regen1, StatusID.Regen2, StatusID.Regen3 + }; + public static StatusID[] SheildStatus { get; } = new StatusID[] { StatusID.Grit, StatusID.RoyalGuard, StatusID.IronWill, StatusID.Defiance diff --git a/RotationSolver/Helpers/TargetFilter.cs b/RotationSolver/Helpers/TargetFilter.cs index 732737c2d..10972ab15 100644 --- a/RotationSolver/Helpers/TargetFilter.cs +++ b/RotationSolver/Helpers/TargetFilter.cs @@ -57,7 +57,7 @@ internal static BattleChara DefaultFindHostile(IEnumerable availabl .OrderBy(DistanceToPlayer).First(); } - internal static BattleChara FindTargetForMoving(this IEnumerable charas, bool mustUse) + internal static GameObject FindTargetForMoving(this IEnumerable charas, bool mustUse) { if (mustUse) { @@ -78,7 +78,7 @@ internal static BattleChara FindTargetForMoving(this IEnumerable ch } const float DISTANCE_TO_MOVE = 3; - private static BattleChara FindMoveTargetFaceDirection(IEnumerable charas) + private static GameObject FindMoveTargetFaceDirection(IEnumerable charas) { Vector3 pPosition = Service.ClientState.LocalPlayer.Position; float rotation = Service.ClientState.LocalPlayer.Rotation; @@ -97,7 +97,7 @@ private static BattleChara FindMoveTargetFaceDirection(IEnumerable return tars.FirstOrDefault(); } - private static BattleChara FindMoveTargetScreenCenter(IEnumerable charas) + private static GameObject FindMoveTargetScreenCenter(IEnumerable charas) { var pPosition = Service.ClientState.LocalPlayer.Position; if (!Service.GameGui.WorldToScreen(pPosition, out var playerScrPos)) return null; @@ -228,17 +228,10 @@ internal static BattleChara GetDeathPeople(IEnumerable deathAll, IE return null; } - internal unsafe static IEnumerable GetTargetable(IEnumerable charas) - { - return charas.Where(item => ((FFXIVClientStructs.FFXIV.Client.Game.Object.GameObject*)(void*)item.Address)->GetIsTargetable()); - } - - internal unsafe static IEnumerable GetDeath(this IEnumerable charas) - { - charas = GetTargetable(charas); - - return charas.Where(item => + internal unsafe static IEnumerable GetDeath(this IEnumerable charas) => charas.Where(item => { + if (!item.IsTargetable()) return false; + //如果还有血,就算了。 if (item.CurrentHp != 0) return false; @@ -253,7 +246,6 @@ internal unsafe static IEnumerable GetDeath(this IEnumerable GetJobCategory(this IEnumerable objects, params JobRole[] roles) { diff --git a/RotationSolver/Rotations/Basic/DNC_Base.cs b/RotationSolver/Rotations/Basic/DNC_Base.cs index 61aed5a2a..0259e9fc0 100644 --- a/RotationSolver/Rotations/Basic/DNC_Base.cs +++ b/RotationSolver/Rotations/Basic/DNC_Base.cs @@ -209,7 +209,7 @@ internal abstract class DNC_Base : CustomRotation.CustomRotation /// /// ��׼�貽���� /// - private static IBaseAction StandardFinish { get; } = new BaseAction(ActionID.StandardFinish) + protected static IBaseAction StandardFinish { get; } = new BaseAction(ActionID.StandardFinish) { StatusNeed = new[] { StatusID.StandardStep }, ActionCheck = b => IsDancing && JobGauge.CompletedSteps == 2, @@ -218,7 +218,7 @@ internal abstract class DNC_Base : CustomRotation.CustomRotation /// /// �����貽���� /// - private static IBaseAction TechnicalFinish { get; } = new BaseAction(ActionID.TechnicalFinish) + protected static IBaseAction TechnicalFinish { get; } = new BaseAction(ActionID.TechnicalFinish) { StatusNeed = new[] { StatusID.TechnicalStep }, ActionCheck = b => IsDancing && JobGauge.CompletedSteps == 4, @@ -290,33 +290,6 @@ internal abstract class DNC_Base : CustomRotation.CustomRotation StatusNeed = new[] { StatusID.FlourishingFinish }, }; - /// - /// �����貽 - /// - /// - /// - protected static bool FinishStepGCD(out IAction act) - { - act = null; - if (!IsDancing) return false; - - //��׼�貽���� - if (Player.HasStatus(true, StatusID.StandardStep) && Player.WillStatusEnd(1, true, StatusID.StandardStep) || StandardFinish.CanUse(out _, mustUse: true)) - { - act = StandardStep; - return true; - } - - //�����貽���� - if (Player.HasStatus(true, StatusID.TechnicalStep) && Player.WillStatusEnd(1, true, StatusID.TechnicalStep) || TechnicalFinish.CanUse(out _, mustUse: true)) - { - act = TechnicalStep; - return true; - } - - return false; - } - /// /// ִ���貽 /// diff --git a/RotationSolver/Rotations/RangedPhysicial/DNC/DNC_Default.cs b/RotationSolver/Rotations/RangedPhysicial/DNC/DNC_Default.cs index 63f9bf8f3..cc93faa00 100644 --- a/RotationSolver/Rotations/RangedPhysicial/DNC/DNC_Default.cs +++ b/RotationSolver/Rotations/RangedPhysicial/DNC/DNC_Default.cs @@ -39,8 +39,7 @@ private protected override IAction CountDownAction(float remainTime) { if (remainTime <= 15) { - if (StandardStep.CanUse(out _, mustUse: true)) return StandardStep; - IAction act; + if (StandardStep.CanUse(out var act, mustUse: true)) return act; if (ExcutionStepGCD(out act)) return act; } return base.CountDownAction(remainTime); @@ -213,4 +212,26 @@ private bool UseClosedPosition(out IAction act) act = null; return false; } + + private static bool FinishStepGCD(out IAction act) + { + act = null; + if (!IsDancing) return false; + + //��׼�貽���� + if (Player.HasStatus(true, StatusID.StandardStep) && Player.WillStatusEnd(1, true, StatusID.StandardStep) || StandardFinish.CanUse(out _, mustUse: true)) + { + act = StandardStep; + return true; + } + + //�����貽���� + if (Player.HasStatus(true, StatusID.TechnicalStep) && Player.WillStatusEnd(1, true, StatusID.TechnicalStep) || TechnicalFinish.CanUse(out _, mustUse: true)) + { + act = TechnicalStep; + return true; + } + + return false; + } } diff --git a/RotationSolver/Updaters/MajorUpdater.cs b/RotationSolver/Updaters/MajorUpdater.cs index 4b59bbf50..a8909b181 100644 --- a/RotationSolver/Updaters/MajorUpdater.cs +++ b/RotationSolver/Updaters/MajorUpdater.cs @@ -35,8 +35,7 @@ private unsafe static void FrameworkUpdate(Framework framework) ActionUpdater.UpdateActionInfo(); - TargetUpdater.UpdateHostileTargets(); - TargetUpdater.UpdateFriends(); + TargetUpdater.UpdateTarget(); MovingUpdater.UpdateLocation(); diff --git a/RotationSolver/Updaters/TargetUpdater_Friends.cs b/RotationSolver/Updaters/TargetUpdater_Friends.cs index 4f069eb05..a6f269e73 100644 --- a/RotationSolver/Updaters/TargetUpdater_Friends.cs +++ b/RotationSolver/Updaters/TargetUpdater_Friends.cs @@ -82,8 +82,6 @@ internal static partial class TargetUpdater [EditorBrowsable(EditorBrowsableState.Never)] internal static bool CanHealSingleSpell { get; private set; } = false; - - /// /// 有宠物 /// @@ -98,50 +96,36 @@ internal static partial class TargetUpdater /// internal static bool HPNotFull { get; private set; } = false; - static RandomDelay _deathAllDelay = new RandomDelay(() => DeathPeopleAll.Any(), - () => (Service.Configuration.DeathDelayMin, Service.Configuration.DeathDelayMax)); + private static IEnumerable GetPartyMembers(IEnumerable allTargets) + { + var party = Service.PartyList.Select(p => p.GameObject).OfType().Where(b => b.DistanceToPlayer() <= 30); + + if (!party.Any()) party = new BattleChara[] { Service.ClientState.LocalPlayer }; - static RandomDelay _deathPartyDelay = new RandomDelay(() => DeathPeopleParty.Any(), - () => (Service.Configuration.DeathDelayMin, Service.Configuration.DeathDelayMax)); + return party.Union(allTargets.Where(obj => obj.SubKind == 9)); + } - internal unsafe static void UpdateFriends() + private unsafe static void UpdateFriends(IEnumerable allTargets) { #region Friend - var party = Service.PartyList; - PartyMembers = party.Length == 0 ? (Service.ClientState.LocalPlayer == null ? new BattleChara[0] : new BattleChara[] { Service.ClientState.LocalPlayer }) - : party.Where(obj => obj != null && obj.GameObject is BattleChara).Select(obj => obj.GameObject as BattleChara); - - //添加亲信 - PartyMembers = PartyMembers.Union(Service.ObjectTable.OfType().Where(obj => obj.SubKind == 9)); - - var mayPet = Service.ObjectTable.OfType().Where(npc => npc.OwnerId == Service.ClientState.LocalPlayer.ObjectId); + PartyMembers = GetPartyMembers(allTargets); + var mayPet = allTargets.OfType().Where(npc => npc.OwnerId == Service.ClientState.LocalPlayer.ObjectId); HavePet = mayPet.Any(npc => npc.BattleNpcKind == BattleNpcSubKind.Pet); - HaveChocobo = mayPet.Any(npc => npc.BattleNpcKind == BattleNpcSubKind.Chocobo); - AllianceMembers = Service.ObjectTable.OfType(); + AllianceMembers = allTargets.OfType(); PartyTanks = PartyMembers.GetJobCategory(JobRole.Tank); - PartyHealers = PartyMembers.GetObjectInRadius(30).GetJobCategory(JobRole.Healer); - AllianceTanks = AllianceMembers.GetObjectInRadius(30).GetJobCategory(JobRole.Tank); + PartyHealers = PartyMembers.GetJobCategory(JobRole.Healer); + AllianceTanks = AllianceMembers.GetJobCategory(JobRole.Tank); - var lastDeathAll = DeathPeopleAll; - var lastDeathparty = DeathPeopleParty; - DeathPeopleAll = AllianceMembers.GetDeath().GetObjectInRadius(30); - DeathPeopleParty = PartyMembers.GetDeath().GetObjectInRadius(30); + DeathPeopleAll = AllianceMembers.GetDeath(); + DeathPeopleParty = PartyMembers.GetDeath(); MaintainDeathPeople(); - if (!_deathAllDelay.Update()) HostileTargets = lastDeathAll; - if (!_deathPartyDelay.Update()) HostileTargets = lastDeathparty; - WeakenPeople = TargetFilter.GetObjectInRadius(PartyMembers, 30).Where(p => - { - foreach (var status in p.StatusList) - { - if (status.GameData.CanDispel && status.RemainingTime > 2) return true; - } - return false; - }); + WeakenPeople = PartyMembers.Where(p => p.StatusList.Any(status => + status.GameData.CanDispel && status.RemainingTime > 2)); DyingPeople = WeakenPeople.Where(p => p.StatusList.Any(StatusHelper.IsDangerous)); @@ -149,93 +133,52 @@ internal unsafe static void UpdateFriends() #endregion #region Health - PartyMembersHP = TargetFilter.GetObjectInRadius(PartyMembers, 30).Where(r => r.CurrentHp > 0).Select(p => (float)p.CurrentHp / p.MaxHp); + PartyMembersHP = PartyMembers.Select(ObjectHelper.GetHealthRatio).Where(r => r > 0); + PartyMembersAverHP = PartyMembersHP.Average(); + PartyMembersDifferHP = (float)Math.Sqrt(PartyMembersHP.Average(d => Math.Pow(d - PartyMembersAverHP, 2))); - float averHP = 0; - foreach (var hp in PartyMembersHP) - { - averHP += hp; - } - PartyMembersAverHP = averHP / PartyMembersHP.Count(); + UpdateCanHeal(Service.ClientState.LocalPlayer); + #endregion + } - double differHP = 0; - float average = PartyMembersAverHP; - foreach (var hp in PartyMembersHP) - { - differHP += Math.Pow(hp - average, 2); - } - PartyMembersDifferHP = (float)Math.Sqrt(differHP / PartyMembersHP.Count()); + static void UpdateCanHeal(PlayerCharacter player) + { + var job = (ClassJobID)player.ClassJob.Id; - var job = (ClassJobID)Service.ClientState.LocalPlayer.ClassJob.Id; + var hotSubSingle = job.GetHealingOfTimeSubtractSingle(); + var singleAbility = ShouldHealSingle(StatusHelper.SingleHots, job.GetHealSingleAbility(), hotSubSingle); + var singleSpell = ShouldHealSingle(StatusHelper.SingleHots, job.GetHealSingleSpell(), hotSubSingle); + CanHealSingleAbility = singleAbility > 0; + CanHealSingleSpell = singleSpell > 0; + CanHealAreaAbility = singleAbility > 2; + CanHealAreaSpell = singleSpell > 2; if (PartyMembers.Count() > 2) { - var hotSubArea = job.GetHealingOfTimeSubtractArea(); - - var areaHots = new StatusID[] - { - StatusID.AspectedHelios, - StatusID.Medica2, - StatusID.TrueMedica2, - }; - //TODO:少了所有罩子类技能 - var ratio = GetHealingOfTimeRatio(Service.ClientState.LocalPlayer, areaHots) * hotSubArea; - - var healAreability = ConfigurationHelper.GetHealAreaAbility(job); + var ratio = GetHealingOfTimeRatio(player, StatusHelper.AreaHots) * job.GetHealingOfTimeSubtractArea(); - var healAreaspell = ConfigurationHelper.GetHealAreafSpell(job); + if(!CanHealAreaAbility) + CanHealAreaAbility = PartyMembersDifferHP < Service.Configuration.HealthDifference && PartyMembersAverHP < ConfigurationHelper.GetHealAreaAbility(job) - ratio; - CanHealAreaAbility = PartyMembersDifferHP < Service.Configuration.HealthDifference && PartyMembersAverHP < healAreability - ratio; - - CanHealAreaSpell = PartyMembersDifferHP < Service.Configuration.HealthDifference && PartyMembersAverHP < healAreaspell - ratio; - } - else - { - CanHealAreaAbility = CanHealAreaSpell = false; + if (!CanHealAreaSpell) + CanHealAreaSpell = PartyMembersDifferHP < Service.Configuration.HealthDifference && PartyMembersAverHP < ConfigurationHelper.GetHealAreafSpell(job) - ratio; } - var hotSubSingle = job.GetHealingOfTimeSubtractSingle(); - - var singleHots = new StatusID[] - { - StatusID.AspectedBenefic, - StatusID.Regen1, - StatusID.Regen2, - StatusID.Regen3 - }; - - var healsingAbility = job.GetHealSingleAbility(); - //Hot衰减 - var abilityCount = PartyMembers.Count(p => - { - var ratio = GetHealingOfTimeRatio(p, singleHots); - - var h = p.GetHealthRatio(); - if (h == 0 || !StatusHelper.NeedHealing(p)) return false; - - return h < healsingAbility - hotSubSingle * ratio; - }); - CanHealSingleAbility = abilityCount > 0; - - - var healsingSpell = job.GetHealSingleSpell(); - - var gcdCount = PartyMembers.Count(p => - { - var ratio = GetHealingOfTimeRatio(p, singleHots); - var h = p.GetHealthRatio(); - if (h == 0 || !StatusHelper.NeedHealing(p)) return false; - - return h < healsingSpell - hotSubSingle * ratio; - }); - CanHealSingleSpell = gcdCount > 0; - PartyMembersMinHP = PartyMembersHP.Any() ? PartyMembersHP.Min() : 0; HPNotFull = PartyMembersMinHP < 1; - #endregion } + static int ShouldHealSingle(StatusID[] hotStatus, float healSingle, float hotSubSingle) => PartyMembers.Count(p => + { + var ratio = GetHealingOfTimeRatio(p, hotStatus); + + var h = p.GetHealthRatio(); + if (h == 0 || !StatusHelper.NeedHealing(p)) return false; + + return h < healSingle - hotSubSingle * ratio; + }); + static float GetHealingOfTimeRatio(BattleChara target, params StatusID[] statusIds) { const float buffWholeTime = 15; @@ -315,7 +258,7 @@ private static void SayHelloToAuthor() //没找到作者 if (author == null) return; - //随机事件 + //Random Time if (relayTime == TimeSpan.Zero) { foundTime = DateTime.Now; diff --git a/RotationSolver/Updaters/TargetUpdater_Hostile.cs b/RotationSolver/Updaters/TargetUpdater_Hostile.cs index 37d9bd544..56d7e74b3 100644 --- a/RotationSolver/Updaters/TargetUpdater_Hostile.cs +++ b/RotationSolver/Updaters/TargetUpdater_Hostile.cs @@ -1,4 +1,5 @@ -using Dalamud.Game.ClientState.Objects.Types; +using Dalamud.Game.ClientState.Fates; +using Dalamud.Game.ClientState.Objects.Types; using Dalamud.Logging; using FFXIVClientStructs.FFXIV.Client.Game; using FFXIVClientStructs.FFXIV.Client.Game.Fate; @@ -16,12 +17,6 @@ namespace RotationSolver.Updaters; internal static partial class TargetUpdater { -#if DEBUG - internal static IEnumerable AllTargets { get; set; } = new BattleChara[0]; -#else - private static IEnumerable AllTargets { get; set; } = new BattleChara[0]; -#endif - /// /// 敌人 /// @@ -37,13 +32,13 @@ internal static partial class TargetUpdater internal static bool IsHostileCastingToTank { get; private set; } = false; - internal static unsafe ushort Infate + internal static unsafe ushort FateId { get { try { - if( Service.Configuration.ChangeTargetForFate && (IntPtr)FateManager.Instance() != IntPtr.Zero + if(Service.Configuration.ChangeTargetForFate && (IntPtr)FateManager.Instance() != IntPtr.Zero && (IntPtr)FateManager.Instance()->CurrentFate != IntPtr.Zero && Service.ClientState.LocalPlayer.Level <= FateManager.Instance()->CurrentFate->MaxLevel) { @@ -58,96 +53,80 @@ internal static unsafe ushort Infate } } - static RandomDelay _hostileDelay = new RandomDelay(() => HostileTargets.Any(), () => (Service.Configuration.HostileDelayMin, Service.Configuration.HostileDelayMax)); - - - internal unsafe static void UpdateHostileTargets() + private static float JobRange { - var inFate = Infate; - - AllTargets = TargetFilter.GetTargetable(TargetFilter.GetObjectInRadius(Service.ObjectTable, 30).Where(obj => + get { - if (obj is BattleChara c && c.CurrentHp != 0) + float radius = 25; + switch (Service.DataManager.GetExcelSheet().GetRow( + Service.ClientState.LocalPlayer.ClassJob.Id).GetJobRole()) { - if (c.StatusList.Any(StatusHelper.IsInvincible)) return false; - - if (!c.IsTargetable()) return false; - - if (obj.CanAttack()) return true; + case JobRole.Tank: + case JobRole.Melee: + radius = 3; + break; } - return false; - }).Cast()); - - uint[] ids = GetEnemies(); + return radius; + } + } - if (AllTargets != null) + private unsafe static void UpdateHostileTargets(IEnumerable allTargets) + { + var allAttackableTargets = allTargets.Where(b => { - var lastHostile = HostileTargets; - HostileTargets = CountDown.CountDownTime > 0 ? AllTargets : inFate > 0 ? - AllTargets.Where(t => t.FateId() == inFate) : - AllTargets.Where(t => (t.TargetObject is BattleChara || ids.Contains(t.ObjectId)) && t.FateId() == 0 - || t.TargetObject == Service.ClientState.LocalPlayer); + if (!b.IsTargetable()) return false; - switch (IconReplacer.RightNowTargetToHostileType) - { - case TargetHostileType.AllTargetsCanAttack: - HostileTargets = AllTargets; - break; + if (b.StatusList.Any(StatusHelper.IsInvincible)) return false; - default: - case TargetHostileType.TargetsHaveTargetOrAllTargetsCanAttack: - if (!HostileTargets.Any()) - HostileTargets = AllTargets; - break; - - case TargetHostileType.TargetsHaveTarget: - break; - } - - if (!_hostileDelay.Update()) HostileTargets = lastHostile; + return b.CanAttack(); + }); - CanInterruptTargets = HostileTargets.Where(tar => - { - var baseCheck = tar.IsCasting && tar.IsCastInterruptible && tar.TotalCastTime >= 2 - && tar.CurrentCastTime >= Service.Configuration.InterruptibleTime; + HostileTargets = GetHostileTargets(allAttackableTargets); - if(!baseCheck) return false; + CanInterruptTargets = HostileTargets.Where(ObjectHelper.CanInterrupt); - var act = Service.DataManager.GetExcelSheet().GetRow(tar.CastActionId); - if (act == null) return false; + TarOnMeTargets = HostileTargets.Where(tar => tar.TargetObjectId == Service.ClientState.LocalPlayer.ObjectId); - //跳过扇形圆型 - if (act.CastType is 3 or 4) return false; - if (ActionManager.GetActionRange(tar.CastActionId) < 8) return false; - return true; - }); + HasHostilesInRange = TargetFilter.GetObjectInRadius(HostileTargets, JobRange).Any(); - TarOnMeTargets = HostileTargets.Where(tar => tar.TargetObjectId == Service.ClientState.LocalPlayer.ObjectId); + if (HostileTargets.Count() == 1) + { + var tar = HostileTargets.FirstOrDefault(); - float radius = 25; - switch (Service.DataManager.GetExcelSheet().GetRow( - Service.ClientState.LocalPlayer.ClassJob.Id).GetJobRole()) - { - case JobRole.Tank: - case JobRole.Melee: - radius = 3; - break; - } - HasHostilesInRange = TargetFilter.GetObjectInRadius(HostileTargets, radius).Any(); + IsHostileCastingToTank = IsHostileCastingTank(tar); + IsHostileCastingAOE = IsHostileCastingArea(tar); } else { - AllTargets = HostileTargets = CanInterruptTargets = new BattleChara[0]; - HasHostilesInRange = false; + IsHostileCastingToTank = IsHostileCastingAOE = false; } + } - if (HostileTargets.Count() == 1) + private static IEnumerable GetHostileTargets(IEnumerable allattackableTargets) + { + var type = IconReplacer.RightNowTargetToHostileType; + if (type == TargetHostileType.AllTargetsCanAttack || CountDown.CountDownTime > 0) { - var tar = HostileTargets.FirstOrDefault(); + return allattackableTargets; + } - IsHostileCastingToTank = IsHostileCastingTank(tar); - IsHostileCastingAOE = IsHostileCastingArea(tar); + uint[] ids = GetEnemies(); + var fateId = FateId; + + var hostiles = allattackableTargets.Where(t => + { + if (ids.Contains(t.ObjectId)) return true; + if (t.TargetObject == Service.ClientState.LocalPlayer) return true; + + return fateId > 0 ? t.FateId() == fateId : t.TargetObject is BattleChara; + }); + + if (type == TargetHostileType.TargetsHaveTargetOrAllTargetsCanAttack) + { + if (!hostiles.Any()) hostiles = allattackableTargets; } + + return hostiles; } private static unsafe uint[] GetEnemies() diff --git a/RotationSolver/Updaters/TargetUpdater_Major.cs b/RotationSolver/Updaters/TargetUpdater_Major.cs new file mode 100644 index 000000000..91f8f2158 --- /dev/null +++ b/RotationSolver/Updaters/TargetUpdater_Major.cs @@ -0,0 +1,22 @@ +using Dalamud.Game.ClientState.Objects.Types; +using RotationSolver.Helpers; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace RotationSolver.Updaters +{ + internal static partial class TargetUpdater + { + internal static IEnumerable AllTargets { get; private set; } + internal static void UpdateTarget() + { + AllTargets = TargetFilter.GetObjectInRadius(Service.ObjectTable, 30); + var battles = AllTargets.OfType(); + UpdateHostileTargets(battles); + UpdateFriends(battles); + } + } +} diff --git a/RotationSolver/Windows/RotationConfigWindow_Debug.cs b/RotationSolver/Windows/RotationConfigWindow_Debug.cs index eda55e275..ee22bdd3c 100644 --- a/RotationSolver/Windows/RotationConfigWindow_Debug.cs +++ b/RotationSolver/Windows/RotationConfigWindow_Debug.cs @@ -24,7 +24,7 @@ private unsafe void DrawDebugTab() ImGui.Text("Friends: " + TargetUpdater.PartyMembers.Count().ToString()); if ((IntPtr)FateManager.Instance() != IntPtr.Zero) { - ImGui.Text("Fate: " + TargetUpdater.Infate.ToString()); + ImGui.Text("Fate: " + TargetUpdater.FateId.ToString()); } if (ImGui.CollapsingHeader("Status")) diff --git a/RotationSolver/Windows/RotationConfigWindow_Param.cs b/RotationSolver/Windows/RotationConfigWindow_Param.cs index 03b36b07d..618cb2b96 100644 --- a/RotationSolver/Windows/RotationConfigWindow_Param.cs +++ b/RotationSolver/Windows/RotationConfigWindow_Param.cs @@ -74,6 +74,8 @@ private void DrawParamBasic() DrawFloatNumber(LocalizationManager.RightLang.Configwindow_Param_InterruptibleTime, ref Service.Configuration.InterruptibleTime, min: 0, max: 2); + // Service.Configuraton.InterruptibleMoreCheck + DrawFloatNumber(LocalizationManager.RightLang.Configwindow_Param_SpecialDuration, ref Service.Configuration.SpecialDuration, speed: 0.02f, min: 1, max: 20);