diff --git a/Source/Game/AI/SmartScripts/SmartAIManager.cs b/Source/Game/AI/SmartScripts/SmartAIManager.cs index 440bc32ae..6309191e2 100644 --- a/Source/Game/AI/SmartScripts/SmartAIManager.cs +++ b/Source/Game/AI/SmartScripts/SmartAIManager.cs @@ -818,6 +818,10 @@ namespace Game.AI case SmartTargets.PlayerRange: case SmartTargets.PlayerDistance: break; + case SmartTargets.ActionInvoker: + if (!NotNULL(e, e.Event.friendlyHealthPct.radius)) + return false; + break; default: Log.outError(LogFilter.Sql, $"SmartAIMgr: {e} uses invalid target_type {e.GetTargetType()}, skipped."); return false; @@ -2303,6 +2307,7 @@ namespace Game.AI public uint maxHpPct; public uint repeatMin; public uint repeatMax; + public uint radius; } public struct Distance { diff --git a/Source/Game/AI/SmartScripts/SmartScript.cs b/Source/Game/AI/SmartScripts/SmartScript.cs index 7aec7d159..1073b1628 100644 --- a/Source/Game/AI/SmartScripts/SmartScript.cs +++ b/Source/Game/AI/SmartScripts/SmartScript.cs @@ -3614,7 +3614,7 @@ namespace Game.AI if (_me == null || !_me.IsEngaged()) return; - List targets; + Unit unitTarget = null; switch (e.GetTargetType()) { @@ -3625,30 +3625,29 @@ namespace Game.AI case SmartTargets.ClosestPlayer: case SmartTargets.PlayerRange: case SmartTargets.PlayerDistance: - targets = GetTargets(e); + { + var targets = GetTargets(e); + foreach (WorldObject target in targets) + { + if (IsUnit(target) && _me.IsFriendlyTo(target.ToUnit()) && target.ToUnit().IsAlive() && target.ToUnit().IsInCombat()) + { + uint healthPct = (uint)target.ToUnit().GetHealthPct(); + if (healthPct > e.Event.friendlyHealthPct.maxHpPct || healthPct < e.Event.friendlyHealthPct.minHpPct) + continue; + + unitTarget = target.ToUnit(); + break; + } + } + } + break; + case SmartTargets.ActionInvoker: + unitTarget = DoSelectLowestHpPercentFriendly((float)e.Event.friendlyHealthPct.radius, e.Event.friendlyHealthPct.minHpPct, e.Event.friendlyHealthPct.maxHpPct); break; default: return; } - if (targets == null) - return; - - Unit unitTarget = null; - foreach (var target in targets) - { - if (IsUnit(target) && _me.IsFriendlyTo(target.ToUnit()) && target.ToUnit().IsAlive() && target.ToUnit().IsInCombat()) - { - uint healthPct = (uint)target.ToUnit().GetHealthPct(); - - if (healthPct > e.Event.friendlyHealthPct.maxHpPct || healthPct < e.Event.friendlyHealthPct.minHpPct) - continue; - - unitTarget = target.ToUnit(); - break; - } - } - if (unitTarget == null) return; @@ -4180,6 +4179,17 @@ namespace Game.AI return searcher.GetTarget(); } + Unit DoSelectLowestHpPercentFriendly(float range, uint minHpPct, uint maxHpPct) + { + if (_me == null) + return null; + + MostHPPercentMissingInRange u_check = new(_me, range, minHpPct, maxHpPct); + UnitLastSearcher searcher = new(_me, u_check); + Cell.VisitGridObjects(_me, searcher, range); + return searcher.GetTarget(); + } + void DoFindFriendlyCC(List creatures, float range) { if (_me == null) diff --git a/Source/Game/Maps/GridNotifiers.cs b/Source/Game/Maps/GridNotifiers.cs index 7d88a7dca..92484c781 100644 --- a/Source/Game/Maps/GridNotifiers.cs +++ b/Source/Game/Maps/GridNotifiers.cs @@ -1859,6 +1859,34 @@ namespace Game.Maps ulong i_hp; } + class MostHPPercentMissingInRange : ICheck + { + Unit _obj; + float _range; + float _minHpPct; + float _maxHpPct; + float _hpPct; + + public MostHPPercentMissingInRange(Unit obj, float range, uint minHpPct, uint maxHpPct) + { + _obj = obj; + _range = range; + _minHpPct = minHpPct; + _maxHpPct = maxHpPct; + _hpPct = 101.0f; + } + + public bool Invoke(Unit u) + { + if (u.IsAlive() && u.IsInCombat() && !_obj.IsHostileTo(u) && _obj.IsWithinDistInMap(u, _range) && _minHpPct <= u.GetHealthPct() && u.GetHealthPct() <= _maxHpPct && u.GetHealthPct() < _hpPct) + { + _hpPct = u.GetHealthPct(); + return true; + } + return false; + } + } + public class FriendlyBelowHpPctEntryInRange : ICheck { public FriendlyBelowHpPctEntryInRange(Unit obj, uint entry, float range, byte pct, bool excludeSelf)