Combat/Threat rewrite - prep & refactor

Port From (https://github.com/TrinityCore/TrinityCore/commit/8be23fcbbdf26e8169defd761e61765f301bebe0)
This commit is contained in:
hondacrx
2020-08-22 11:58:19 -04:00
parent 67bacbb731
commit 193ad3a48d
42 changed files with 533 additions and 421 deletions
+14 -23
View File
@@ -102,16 +102,8 @@ namespace Game.AI
{
creature.SetInCombatWith(player);
player.SetInCombatWith(creature);
creature.AddThreat(player, 0.0f);
creature.GetThreatManager().AddThreat(player, 0.0f, null, true, true);
}
/* Causes certain things to never leave the threat list (Priest Lightwell, etc):
foreach (var unit in player.m_Controlled)
{
me.SetInCombatWith(unit);
unit.SetInCombatWith(me);
me.AddThreat(unit, 0.0f);
}*/
}
}
@@ -119,6 +111,7 @@ namespace Game.AI
{
if (MoveInLineOfSight_locked)
return;
MoveInLineOfSight_locked = true;
MoveInLineOfSight(who);
MoveInLineOfSight_locked = false;
@@ -126,11 +119,11 @@ namespace Game.AI
public virtual void MoveInLineOfSight(Unit who)
{
if (me.GetVictim() != null)
if (me.IsEngaged())
return;
if (me.HasReactState(ReactStates.Aggressive) && me.CanStartAttack(who, false))
AttackStart(who);
me.EngageWithTarget(who);
}
void _OnOwnerCombatInteraction(Unit target)
@@ -139,12 +132,7 @@ namespace Game.AI
return;
if (!me.HasReactState(ReactStates.Passive) && me.CanStartAttack(target, true))
{
if (me.IsInCombat())
me.AddThreat(target, 0.0f);
else
AttackStart(target);
}
me.EngageWithTarget(target);
}
// Distract creature, if player gets too close while stealthed/prowling
@@ -154,8 +142,8 @@ namespace Game.AI
if (!who || !who.IsTypeId(TypeId.Player))
return;
// If this unit isn't an NPC, is already distracted, is in combat, is confused, stunned or fleeing, do nothing
if (!me.IsTypeId(TypeId.Unit) || me.IsInCombat() || me.HasUnitState(UnitState.Confused | UnitState.Stunned | UnitState.Fleeing | UnitState.Distracted))
// If this unit isn't an NPC, is already distracted, is fighting, is confused, stunned or fleeing, do nothing
if (!me.IsTypeId(TypeId.Unit) || me.IsEngaged() || me.HasUnitState(UnitState.Confused | UnitState.Stunned | UnitState.Fleeing | UnitState.Distracted))
return;
// Only alert for hostiles!
@@ -214,7 +202,7 @@ namespace Game.AI
public bool UpdateVictimWithGaze()
{
if (!me.IsInCombat())
if (!me.IsEngaged())
return false;
if (me.HasReactState(ReactStates.Passive))
@@ -237,7 +225,7 @@ namespace Game.AI
public bool UpdateVictim()
{
if (!me.IsInCombat())
if (!me.IsEngaged())
return false;
if (!me.HasReactState(ReactStates.Passive))
@@ -266,7 +254,7 @@ namespace Game.AI
me.RemoveAurasOnEvade();
// sometimes bosses stuck in combat?
me.DeleteThreatList();
me.GetThreatManager().ClearAllThreat();
me.CombatStop(true);
me.SetLootRecipient(null);
me.ResetPlayerDamageReq();
@@ -410,7 +398,10 @@ namespace Game.AI
me.DoImmediateBoundaryCheck();
}
// Called for reaction at enter to combat if not in combat yet (enemy can be NULL)
/// <summary>
/// Called for reaction when initially engaged
/// </summary>
/// <param name="victim"></param>
public virtual void EnterCombat(Unit victim) { }
// Called when the creature is killed
+5 -8
View File
@@ -34,12 +34,9 @@ namespace Game.AI
public override bool CanSeeAlways(WorldObject obj)
{
if (!obj.IsTypeMask(TypeMask.Unit))
return false;
var threatList = me.GetThreatManager().GetThreatList();
foreach (var refe in threatList)
if (refe.GetUnitGuid() == obj.GetGUID())
Unit unit = obj.ToUnit();
if (unit != null)
if (unit.IsControlledByPlayer() && me.IsEngagedBy(unit))
return true;
return false;
@@ -51,14 +48,14 @@ namespace Game.AI
{
me.GetMotionMaster().MoveIdle();
me.CombatStop(true);
me.DeleteThreatList();
me.GetThreatManager().ClearAllThreat();
return;
}
Log.outDebug(LogFilter.Unit, "Guard entry: {0} enters evade mode.", me.GetEntry());
me.RemoveAllAuras();
me.DeleteThreatList();
me.GetThreatManager().ClearAllThreat();
me.CombatStop(true);
// Remove ChaseMovementGenerator from MotionMaster stack list, and add HomeMovementGenerator instead
+163 -71
View File
@@ -57,9 +57,9 @@ namespace Game.AI
return me.GetThreatManager();
}
void SortByDistanceTo(Unit reference, List<Unit> targets)
void SortByDistance(List<Unit> targets, bool ascending)
{
targets.Sort(new ObjectDistanceOrderPred(reference));
targets.Sort(new ObjectDistanceOrderPred(me, true));
}
public void DoMeleeAttackIfReady()
@@ -113,100 +113,187 @@ namespace Game.AI
return false;
}
public Unit SelectTarget(SelectAggroTarget targetType, uint position = 0, float dist = 0.0f, bool playerOnly = false, int aura = 0)
/// <summary>
/// Select the best target (in <targetType> order) from the threat list that fulfill the following:
/// - Not among the first <offset> entries in <targetType> order (or MAXTHREAT order, if <targetType> is RANDOM).
/// - Within at most <dist> yards (if dist > 0.0f)
/// - At least -<dist> yards away (if dist < 0.0f)
/// - Is a player (if playerOnly = true)
/// - Not the current tank (if withTank = false)
/// - Has aura with ID <aura> (if aura > 0)
/// - Does not have aura with ID -<aura> (if aura < 0)
/// </summary>
public Unit SelectTarget(SelectAggroTarget targetType, uint offset = 0, float dist = 0.0f, bool playerOnly = false, bool withTank = true, int aura = 0)
{
return SelectTarget(targetType, position, new DefaultTargetSelector(me, dist, playerOnly, aura));
return SelectTarget(targetType, offset, new DefaultTargetSelector(me, dist, playerOnly, withTank, aura));
}
// Select the targets satifying the predicate.
public Unit SelectTarget(SelectAggroTarget targetType, uint position, ISelector selector)
/// <summary>
/// Select the best target (in <targetType> order) satisfying <predicate> from the threat list.
/// If <offset> is nonzero, the first <offset> entries in <targetType> order (or MAXTHREAT order, if <targetType> is RANDOM) are skipped.
/// </summary>
public Unit SelectTarget(SelectAggroTarget targetType, uint offset, ISelector selector)
{
var threatlist = GetThreatManager().GetThreatList();
if (position >= threatlist.Count)
ThreatManager mgr = GetThreatManager();
// shortcut: if we ignore the first <offset> elements, and there are at most <offset> elements, then we ignore ALL elements
if (mgr.GetThreatListSize() <= offset)
return null;
List<Unit> targetList = new List<Unit>();
Unit currentVictim = null;
var currentVictimReference = GetThreatManager().GetCurrentVictim();
if (currentVictimReference != null)
if (targetType == SelectAggroTarget.MaxDistance || targetType == SelectAggroTarget.MinDistance)
{
currentVictim = currentVictimReference.GetTarget();
foreach (HostileReference refe in mgr.GetThreatList())
{
if (!refe.IsOnline())
continue;
// Current victim always goes first
if (currentVictim && selector.Check(currentVictim))
targetList.Add(refe.GetTarget());
}
}
else
{
Unit currentVictim = mgr.GetCurrentVictim();
if (currentVictim)
targetList.Add(currentVictim);
foreach (HostileReference refe in mgr.GetThreatList())
{
if (!refe.IsOnline())
continue;
Unit thisTarget = refe.GetTarget();
if (thisTarget != currentVictim)
targetList.Add(thisTarget);
}
}
foreach (var hostileRef in threatlist)
{
if (currentVictim != null && hostileRef.GetTarget() != currentVictim && selector.Check(hostileRef.GetTarget()))
targetList.Add(hostileRef.GetTarget());
else if (currentVictim == null && selector.Check(hostileRef.GetTarget()))
targetList.Add(hostileRef.GetTarget());
}
if (position >= targetList.Count)
// filter by predicate
targetList.RemoveAll(target => { return !selector.Check(target); });
// shortcut: the list certainly isn't gonna get any larger after this point
if (targetList.Count <= offset)
return null;
if (targetType == SelectAggroTarget.Nearest || targetType == SelectAggroTarget.Farthest)
SortByDistanceTo(me, targetList);
// right now, list is unsorted for DISTANCE types - re-sort by MAXDISTANCE
if (targetType == SelectAggroTarget.MaxDistance || targetType == SelectAggroTarget.MinDistance)
SortByDistance(targetList, targetType == SelectAggroTarget.MinDistance);
// then reverse the sorting for MIN sortings
if (targetType == SelectAggroTarget.MinThreat)
targetList.Reverse();
// now pop the first <offset> elements
while (offset != 0)
{
targetList.RemoveAt(0);
--offset;
}
// maybe nothing fulfills the predicate
if (targetList.Empty())
return null;
switch (targetType)
{
case SelectAggroTarget.Nearest:
case SelectAggroTarget.TopAggro:
{
return targetList.First();
}
case SelectAggroTarget.Farthest:
case SelectAggroTarget.BottomAggro:
{
return targetList.Last();
}
case SelectAggroTarget.MaxThreat:
case SelectAggroTarget.MinThreat:
case SelectAggroTarget.MaxDistance:
case SelectAggroTarget.MinDistance:
return targetList[0];
case SelectAggroTarget.Random:
{
return targetList.SelectRandom();
}
default:
break;
return null;
}
return null;
}
public List<Unit> SelectTargetList(uint num, SelectAggroTarget targetType, float dist, bool playerOnly, int aura = 0)
/// <summary>
/// Select the best (up to) <num> targets (in <targetType> order) from the threat list that fulfill the following:
/// - Not among the first <offset> entries in <targetType> order (or MAXTHREAT order, if <targetType> is RANDOM).
/// - Within at most <dist> yards (if dist > 0.0f)
/// - At least -<dist> yards away (if dist < 0.0f)
/// - Is a player (if playerOnly = true)
/// - Not the current tank (if withTank = false)
/// - Has aura with ID <aura> (if aura > 0)
/// - Does not have aura with ID -<aura> (if aura < 0)
/// The resulting targets are stored in <targetList> (which is cleared first).
/// </summary>
public List<Unit> SelectTargetList(uint num, SelectAggroTarget targetType, uint offset, float dist, bool playerOnly, bool withTank, int aura = 0)
{
return SelectTargetList(new DefaultTargetSelector(me, dist, playerOnly, aura), num, targetType);
return SelectTargetList(num, targetType, offset, new DefaultTargetSelector(me, dist, playerOnly, withTank, aura));
}
// Select the targets satifying the predicate.
// predicate shall extend std.unary_function<Unit*, bool>
public List<Unit> SelectTargetList(ISelector selector, uint maxTargets, SelectAggroTarget targetType)
/// <summary>
/// Select the best (up to) <num> targets (in <targetType> order) satisfying <predicate> from the threat list and stores them in <targetList> (which is cleared first).
/// If <offset> is nonzero, the first <offset> entries in <targetType> order (or MAXTHREAT order, if <targetType> is RANDOM) are skipped.
/// </summary>
public List<Unit> SelectTargetList(uint num, SelectAggroTarget targetType, uint offset, ISelector selector)
{
var targetList = new List<Unit>();
var threatlist = GetThreatManager().GetThreatList();
if (threatlist.Empty())
ThreatManager mgr = GetThreatManager();
// shortcut: we're gonna ignore the first <offset> elements, and there's at most <offset> elements, so we ignore them all - nothing to do here
if (mgr.GetThreatListSize() <= offset)
return targetList;
foreach (var hostileRef in threatlist)
if (selector.Check(hostileRef.GetTarget()))
targetList.Add(hostileRef.GetTarget());
if (targetType == SelectAggroTarget.MaxDistance || targetType == SelectAggroTarget.MinDistance)
{
foreach (HostileReference refe in mgr.GetThreatList())
{
if (!refe.IsOnline())
continue;
if (targetList.Count < maxTargets)
targetList.Add(refe.GetTarget());
}
}
else
{
Unit currentVictim = mgr.GetCurrentVictim();
if (currentVictim != null)
targetList.Add(currentVictim);
foreach (HostileReference refe in mgr.GetThreatList())
{
if (!refe.IsOnline())
continue;
Unit thisTarget = refe.GetTarget();
if (thisTarget != currentVictim)
targetList.Add(thisTarget);
}
}
// filter by predicate
targetList.RemoveAll(target => { return !selector.Check(target); });
// shortcut: the list isn't gonna get any larger
if (targetList.Count <= offset)
{
targetList.Clear();
return targetList;
}
if (targetType == SelectAggroTarget.Nearest || targetType == SelectAggroTarget.Farthest)
SortByDistanceTo(me, targetList);
// right now, list is unsorted for DISTANCE types - re-sort by MAXDISTANCE
if (targetType == SelectAggroTarget.MaxDistance || targetType == SelectAggroTarget.MinDistance)
SortByDistance(targetList, targetType == SelectAggroTarget.MinDistance);
if (targetType == SelectAggroTarget.Farthest || targetType == SelectAggroTarget.BottomAggro)
// now the list is MAX sorted, reverse for MIN types
if (targetType == SelectAggroTarget.MinThreat)
targetList.Reverse();
// ignore the first <offset> elements
while (offset != 0)
{
targetList.RemoveAt(0);
--offset;
}
if (targetList.Count <= num)
return targetList;
if (targetType == SelectAggroTarget.Random)
targetList = targetList.SelectRandom(maxTargets).ToList();
targetList = targetList.SelectRandom(num).ToList();
else
targetList.Resize(maxTargets);
targetList.Resize(num);
return targetList;
}
@@ -247,7 +334,7 @@ namespace Game.AI
bool playerOnly = spellInfo.HasAttribute(SpellAttr3.OnlyTargetPlayers);
float range = spellInfo.GetMaxRange(false);
DefaultTargetSelector targetSelector = new DefaultTargetSelector(me, range, playerOnly, -(int)spellId);
DefaultTargetSelector targetSelector = new DefaultTargetSelector(me, range, playerOnly, true, -(int)spellId);
if (!spellInfo.HasAuraInterruptFlag(SpellAuraInterruptFlags.NotVictim)
&& targetSelector.Check(me.GetVictim()))
target = me.GetVictim();
@@ -498,11 +585,11 @@ namespace Game.AI
public enum SelectAggroTarget
{
Random = 0, //Just selects a random target
TopAggro, //Selects targes from top aggro to bottom
BottomAggro, //Selects targets from bottom aggro to top
Nearest,
Farthest
Random = 0, // just pick a random target
MaxThreat, // prefer targets higher in the threat list
MinThreat, // prefer targets lower in the threat list
MaxDistance, // prefer targets further from us
MinDistance // prefer targets closer to us
}
public interface ISelector
@@ -516,29 +603,34 @@ namespace Game.AI
Unit me;
float m_dist;
bool m_playerOnly;
Unit except;
int m_aura;
// unit: the reference unit
// dist: if 0: ignored, if > 0: maximum distance to the reference unit, if < 0: minimum distance to the reference unit
// playerOnly: self explaining
// aura: if 0: ignored, if > 0: the target shall have the aura, if < 0, the target shall NOT have the aura
public DefaultTargetSelector(Unit unit, float dist, bool playerOnly, int aura)
/// <param name="unit">the reference unit</param>
/// <param name="dist">if 0: ignored, if > 0: maximum distance to the reference unit, if < 0: minimum distance to the reference unit</param>
/// <param name="playerOnly">self explaining</param>
/// <param name="withMainTank">allow current tank to be selected</param>
/// <param name="aura">if 0: ignored, if > 0: the target shall have the aura, if < 0, the target shall NOT have the aura</param>
public DefaultTargetSelector(Unit unit, float dist, bool playerOnly, bool withMainTank, int aura)
{
me = unit;
m_dist = dist;
m_playerOnly = playerOnly;
except = withMainTank ? me.GetThreatManager().GetCurrentVictim() : null;
m_aura = aura;
}
public bool Check(Unit target)
{
if (me == null)
return false;
if (target == null)
return false;
if (target == except)
return false;
if (m_playerOnly && !target.IsTypeId(TypeId.Player))
return false;
@@ -665,9 +757,9 @@ namespace Game.AI
if (_playerOnly && !target.IsTypeId(TypeId.Player))
return false;
HostileReference currentVictim = _source.GetThreatManager().GetCurrentVictim();
Unit currentVictim = _source.GetThreatManager().GetCurrentVictim();
if (currentVictim != null)
return target.GetGUID() != currentVictim.GetUnitGuid();
return target != currentVictim;
return target != _source.GetVictim();
}
+1 -1
View File
@@ -1243,7 +1243,7 @@ namespace Game.AI
}
}
if (charmer.IsInCombat())
if (charmer.IsEngaged())
{
Unit target = me.GetVictim();
if (!target || !charmer.IsValidAttackTarget(target) || target.HasBreakableByDamageCrowdControlAura())
+80 -34
View File
@@ -111,6 +111,85 @@ namespace Game.AI
source.PlayDirectSound(soundId);
}
/// <summary>
/// Add specified amount of threat directly to victim (ignores redirection effects) - also puts victim in combat and engages them if necessary
/// </summary>
/// <param name="victim"></param>
/// <param name="amount"></param>
/// <param name="who"></param>
public void AddThreat(Unit victim, float amount, Unit who = null)
{
if (!victim)
return;
if (!who)
who = me;
who.GetThreatManager().AddThreat(victim, amount, null, true, true);
}
/// <summary>
/// Adds/removes the specified percentage from the specified victim's threat (to who, or me if not specified)
/// </summary>
/// <param name="victim"></param>
/// <param name="pct"></param>
/// <param name="who"></param>
public void ModifyThreatByPercent(Unit victim, int pct, Unit who = null)
{
if (!victim)
return;
if (!who)
who = me;
who.GetThreatManager().ModifyThreatByPercent(victim, pct);
}
/// <summary>
/// Resets the victim's threat level to who (or me if not specified) to zero
/// </summary>
/// <param name="victim"></param>
/// <param name="who"></param>
void ResetThreat(Unit victim, Unit who)
{
if (!victim)
return;
if (!who)
who = me;
who.GetThreatManager().ResetThreat(victim);
}
/// <summary>
/// Resets the specified unit's threat list (me if not specified) - does not delete entries, just sets their threat to zero
/// </summary>
/// <param name="who"></param>
public void ResetThreatList(Unit who = null)
{
if (!who)
who = me;
who.GetThreatManager().ResetAllThreat();
}
/// <summary>
/// Returns the threat level of victim towards who (or me if not specified)
/// </summary>
/// <param name="victim"></param>
/// <param name="who"></param>
/// <returns></returns>
public float GetThreat(Unit victim, Unit who = null)
{
if (!victim)
return 0.0f;
if (!who)
who = me;
return who.GetThreatManager().GetThreat(victim);
}
//Spawns a creature relative to me
public Creature DoSpawnCreature(uint entry, float offsetX, float offsetY, float offsetZ, float angle, TempSummonType type, uint despawntime)
{
@@ -182,39 +261,6 @@ namespace Game.AI
return apSpell[RandomHelper.IRand(0, (int)(spellCount - 1))];
}
//Drops all threat to 0%. Does not remove players from the threat list
public void DoResetThreat()
{
if (!me.CanHaveThreatList() || me.GetThreatManager().IsThreatListEmpty())
{
Log.outError(LogFilter.Scripts, "DoResetThreat called for creature that either cannot have threat list or has empty threat list (me entry = {0})", me.GetEntry());
return;
}
var threatlist = me.GetThreatManager().GetThreatList();
foreach (var refe in threatlist)
{
Unit unit = Global.ObjAccessor.GetUnit(me, refe.GetUnitGuid());
if (unit != null && DoGetThreat(unit) != 0)
DoModifyThreatPercent(unit, -100);
}
}
public float DoGetThreat(Unit unit)
{
if (unit == null)
return 0.0f;
return me.GetThreatManager().GetThreat(unit);
}
public void DoModifyThreatPercent(Unit unit, int pct)
{
if (unit == null)
return;
me.GetThreatManager().ModifyThreatPercent(unit, pct);
}
void DoTeleportTo(float x, float y, float z, uint time = 0)
{
me.Relocate(x, y, z);
@@ -499,7 +545,7 @@ namespace Game.AI
public override void JustSummoned(Creature summon)
{
summons.Summon(summon);
if (me.IsInCombat())
if (me.IsEngaged())
DoZoneInCombat(summon);
}
+3 -21
View File
@@ -88,8 +88,7 @@ namespace Game.AI
}
else
{
who.SetInCombatWith(me);
me.AddThreat(who, 0.0f);
me.EngageWithTarget(who);
return true;
}
}
@@ -111,24 +110,7 @@ namespace Game.AI
{
float fAttackRadius = me.GetAttackDistance(who);
if (me.IsWithinDistInMap(who, fAttackRadius) && me.IsWithinLOSInMap(who))
{
if (!me.GetVictim())
{
// Clear distracted state on combat
if (me.HasUnitState(UnitState.Distracted))
{
me.ClearUnitState(UnitState.Distracted);
me.GetMotionMaster().Clear();
}
AttackStart(who);
}
else if (me.GetMap().IsDungeon())
{
who.SetInCombatWith(me);
me.AddThreat(who, 0.0f);
}
}
me.EngageWithTarget(who);
}
}
}
@@ -181,7 +163,7 @@ namespace Game.AI
public override void EnterEvadeMode(EvadeReason why = EvadeReason.Other)
{
me.RemoveAllAuras();
me.DeleteThreatList();
me.GetThreatManager().ClearAllThreat();
me.CombatStop(true);
me.SetLootRecipient(null);
@@ -49,9 +49,7 @@ namespace Game.AI
if (me.Attack(who, true))
{
me.AddThreat(who, 0.0f);
me.SetInCombatWith(who);
who.SetInCombatWith(me);
me.EngageWithTarget(who); // in case it doesn't have threat+combat yet
if (me.HasUnitState(UnitState.Follow))
me.ClearUnitState(UnitState.Follow);
@@ -84,18 +82,8 @@ namespace Game.AI
//too far away and no free sight?
if (me.IsWithinDistInMap(who, 100.0f) && me.IsWithinLOSInMap(who))
{
//already fighting someone?
if (!me.GetVictim())
{
AttackStart(who);
return true;
}
else
{
who.SetInCombatWith(me);
me.AddThreat(who, 0.0f);
return true;
}
me.EngageWithTarget(who);
return true;
}
return false;
@@ -128,10 +116,7 @@ namespace Game.AI
AttackStart(who);
}
else if (me.GetMap().IsDungeon())
{
who.SetInCombatWith(me);
me.AddThreat(who, 0.0f);
}
me.EngageWithTarget(who);
}
}
}
@@ -178,7 +163,7 @@ namespace Game.AI
public override void EnterEvadeMode(EvadeReason why)
{
me.RemoveAllAuras();
me.DeleteThreatList();
me.GetThreatManager().ClearAllThreat();
me.CombatStop(true);
me.SetLootRecipient(null);
+2 -12
View File
@@ -544,18 +544,8 @@ namespace Game.AI
//too far away and no free sight?
if (me.IsWithinDistInMap(who, SMART_MAX_AID_DIST) && me.IsWithinLOSInMap(who))
{
//already fighting someone?
if (me.GetVictim() == null)
{
AttackStart(who);
return true;
}
else
{
who.SetInCombatWith(me);
me.AddThreat(who, 0.0f);
return true;
}
me.EngageWithTarget(who);
return true;
}
return false;
+31 -47
View File
@@ -334,16 +334,10 @@ namespace Game.AI
if (me == null)
break;
var threatList = me.GetThreatManager().GetThreatList();
foreach (var refe in threatList)
foreach (var refe in me.GetThreatManager().GetThreatList())
{
Unit target = Global.ObjAccessor.GetUnit(me, refe.GetUnitGuid());
if (target != null)
{
me.GetThreatManager().ModifyThreatPercent(target, e.Action.threatPCT.threatINC != 0 ? (int)e.Action.threatPCT.threatINC : -(int)e.Action.threatPCT.threatDEC);
Log.outDebug(LogFilter.ScriptsAi, "SmartScript.ProcessAction. SMART_ACTION_THREAT_ALL_PCT: Creature guidLow {0} modify threat for unit {1}, value {2}",
me.GetGUID().ToString(), target.GetGUID().ToString(), e.Action.threatPCT.threatINC != 0 ? (int)e.Action.threatPCT.threatINC : -(int)e.Action.threatPCT.threatDEC);
}
refe.AddThreatPercent(Math.Max(-100, (int)(e.Action.threatPCT.threatINC - e.Action.threatPCT.threatDEC)));
Log.outDebug(LogFilter.ScriptsAi, $"SmartScript.ProcessAction: SMART_ACTION_THREAT_ALL_PCT: Creature {me.GetGUID()} modify threat for {refe.GetTarget().GetGUID()}, value {e.Action.threatPCT.threatINC - e.Action.threatPCT.threatDEC}");
}
break;
}
@@ -356,9 +350,8 @@ namespace Game.AI
{
if (IsUnit(target))
{
me.GetThreatManager().ModifyThreatPercent(target.ToUnit(), e.Action.threatPCT.threatINC != 0 ? (int)e.Action.threatPCT.threatINC : -(int)e.Action.threatPCT.threatDEC);
Log.outDebug(LogFilter.ScriptsAi, "SmartScript.ProcessAction. SMART_ACTION_THREAT_SINGLE_PCT: Creature guidLow {0} modify threat for unit {1}, value {2}",
me.GetGUID().ToString(), target.GetGUID().ToString(), e.Action.threatPCT.threatINC != 0 ? (int)e.Action.threatPCT.threatINC : -(int)e.Action.threatPCT.threatDEC);
me.GetThreatManager().ModifyThreatByPercent(target.ToUnit(), Math.Max(-100, (int)(e.Action.threatPCT.threatINC - e.Action.threatPCT.threatDEC)));
Log.outDebug(LogFilter.ScriptsAi, $"SmartScript.ProcessAction: SMART_ACTION_THREAT_SINGLE_PCT: Creature {me.GetGUID()} modify threat for {target.GetGUID()}, value {e.Action.threatPCT.threatINC - e.Action.threatPCT.threatDEC}");
}
}
break;
@@ -2094,7 +2087,7 @@ namespace Game.AI
{
foreach (var target in targets)
if (IsUnit(target))
me.AddThreat(target.ToUnit(), (float)e.Action.threatPCT.threatINC - (float)e.Action.threatPCT.threatDEC);
me.GetThreatManager().AddThreat(target.ToUnit(), (float)(e.Action.threatPCT.threatINC - (float)e.Action.threatPCT.threatDEC), null, true, true);
break;
}
@@ -2377,13 +2370,13 @@ namespace Game.AI
{
if (e.Target.hostilRandom.powerType != 0)
{
Unit u = me.GetAI().SelectTarget(SelectAggroTarget.TopAggro, 1, new PowerUsersSelector(me, (PowerType)(e.Target.hostilRandom.powerType - 1), (float)e.Target.hostilRandom.maxDist, e.Target.hostilRandom.playerOnly != 0));
Unit u = me.GetAI().SelectTarget(SelectAggroTarget.MaxThreat, 1, new PowerUsersSelector(me, (PowerType)(e.Target.hostilRandom.powerType - 1), (float)e.Target.hostilRandom.maxDist, e.Target.hostilRandom.playerOnly != 0));
if (u != null)
targets.Add(u);
}
else
{
Unit u = me.GetAI().SelectTarget(SelectAggroTarget.TopAggro, 1, (float)e.Target.hostilRandom.maxDist, e.Target.hostilRandom.playerOnly != 0);
Unit u = me.GetAI().SelectTarget(SelectAggroTarget.MaxThreat, 1, (float)e.Target.hostilRandom.maxDist, e.Target.hostilRandom.playerOnly != 0);
if (u != null)
targets.Add(u);
}
@@ -2394,13 +2387,13 @@ namespace Game.AI
{
if (e.Target.hostilRandom.powerType != 0)
{
Unit u = me.GetAI().SelectTarget(SelectAggroTarget.BottomAggro, 1, new PowerUsersSelector(me, (PowerType)(e.Target.hostilRandom.powerType - 1), (float)e.Target.hostilRandom.maxDist, e.Target.hostilRandom.playerOnly != 0));
Unit u = me.GetAI().SelectTarget(SelectAggroTarget.MinThreat, 1, new PowerUsersSelector(me, (PowerType)(e.Target.hostilRandom.powerType - 1), (float)e.Target.hostilRandom.maxDist, e.Target.hostilRandom.playerOnly != 0));
if (u != null)
targets.Add(u);
}
else
{
Unit u = me.GetAI().SelectTarget(SelectAggroTarget.BottomAggro, 1, (float)e.Target.hostilRandom.maxDist, e.Target.hostilRandom.playerOnly != 0);
Unit u = me.GetAI().SelectTarget(SelectAggroTarget.MinThreat, 1, (float)e.Target.hostilRandom.maxDist, e.Target.hostilRandom.playerOnly != 0);
if (u != null)
targets.Add(u);
}
@@ -2443,7 +2436,7 @@ namespace Game.AI
case SmartTargets.Farthest:
if (me)
{
Unit u = me.GetAI().SelectTarget(SelectAggroTarget.Farthest, 0, new FarthestTargetSelector(me, (float)e.Target.farthest.maxDist, e.Target.farthest.playerOnly != 0, e.Target.farthest.isInLos != 0));
Unit u = me.GetAI().SelectTarget(SelectAggroTarget.MaxDistance, 0, new FarthestTargetSelector(me, (float)e.Target.farthest.maxDist, e.Target.farthest.playerOnly != 0, e.Target.farthest.isInLos != 0));
if (u != null)
targets.Add(u);
}
@@ -2674,16 +2667,12 @@ namespace Game.AI
}
case SmartTargets.ThreatList:
{
if (me != null)
if (me != null && me.CanHaveThreatList())
{
var threatList = me.GetThreatManager().GetThreatList();
foreach (var refe in threatList)
{
Unit temp = Global.ObjAccessor.GetUnit(me, refe.GetUnitGuid());
if (temp != null)
if (e.Target.hostilRandom.maxDist == 0 || me.IsWithinCombatRange(temp, (float)e.Target.hostilRandom.maxDist))
targets.Add(temp);
}
foreach (var refe in me.GetThreatManager().GetThreatList())
if (e.Target.hostilRandom.maxDist == 0 || me.IsWithinCombatRange(refe.GetTarget(), e.Target.hostilRandom.maxDist))
targets.Add(refe.GetTarget());
}
break;
}
@@ -2790,18 +2779,18 @@ namespace Game.AI
ProcessTimedAction(e, e.Event.minMaxRepeat.repeatMin, e.Event.minMaxRepeat.repeatMax);
break;
case SmartEvents.UpdateOoc:
if (me != null && me.IsInCombat())
if (me != null && me.IsEngaged())
return;
ProcessTimedAction(e, e.Event.minMaxRepeat.repeatMin, e.Event.minMaxRepeat.repeatMax);
break;
case SmartEvents.UpdateIc:
if (me == null || !me.IsInCombat())
if (me == null || !me.IsEngaged())
return;
ProcessTimedAction(e, e.Event.minMaxRepeat.repeatMin, e.Event.minMaxRepeat.repeatMax);
break;
case SmartEvents.HealthPct:
{
if (me == null || !me.IsInCombat() || me.GetMaxHealth() == 0)
if (me == null || !me.IsEngaged() || me.GetMaxHealth() == 0)
return;
uint perc = (uint)me.GetHealthPct();
if (perc > e.Event.minMaxRepeat.max || perc < e.Event.minMaxRepeat.min)
@@ -2811,7 +2800,7 @@ namespace Game.AI
}
case SmartEvents.TargetHealthPct:
{
if (me == null || !me.IsInCombat() || me.GetVictim() == null || me.GetVictim().GetMaxHealth() == 0)
if (me == null || !me.IsEngaged() || me.GetVictim() == null || me.GetVictim().GetMaxHealth() == 0)
return;
uint perc = (uint)me.GetVictim().GetHealthPct();
if (perc > e.Event.minMaxRepeat.max || perc < e.Event.minMaxRepeat.min)
@@ -2821,7 +2810,7 @@ namespace Game.AI
}
case SmartEvents.ManaPct:
{
if (me == null || !me.IsInCombat() || me.GetMaxPower(PowerType.Mana) == 0)
if (me == null || !me.IsEngaged() || me.GetMaxPower(PowerType.Mana) == 0)
return;
uint perc = (uint)me.GetPowerPct(PowerType.Mana);
if (perc > e.Event.minMaxRepeat.max || perc < e.Event.minMaxRepeat.min)
@@ -2831,7 +2820,7 @@ namespace Game.AI
}
case SmartEvents.TargetManaPct:
{
if (me == null || !me.IsInCombat() || me.GetVictim() == null || me.GetVictim().GetMaxPower(PowerType.Mana) == 0)
if (me == null || !me.IsEngaged() || me.GetVictim() == null || me.GetVictim().GetMaxPower(PowerType.Mana) == 0)
return;
uint perc = (uint)me.GetVictim().GetPowerPct(PowerType.Mana);
if (perc > e.Event.minMaxRepeat.max || perc < e.Event.minMaxRepeat.min)
@@ -2841,7 +2830,7 @@ namespace Game.AI
}
case SmartEvents.Range:
{
if (me == null || !me.IsInCombat() || me.GetVictim() == null)
if (me == null || !me.IsEngaged() || me.GetVictim() == null)
return;
if (me.IsInRange(me.GetVictim(), e.Event.minMaxRepeat.min, e.Event.minMaxRepeat.max))
@@ -2852,7 +2841,7 @@ namespace Game.AI
}
case SmartEvents.VictimCasting:
{
if (me == null || !me.IsInCombat())
if (me == null || !me.IsEngaged())
return;
Unit victim = me.GetVictim();
@@ -2873,7 +2862,7 @@ namespace Game.AI
}
case SmartEvents.FriendlyHealth:
{
if (me == null || !me.IsInCombat())
if (me == null || !me.IsEngaged())
return;
Unit target = DoSelectLowestHpFriendly(e.Event.friendlyHealth.radius, e.Event.friendlyHealth.hpDeficit);
@@ -2889,7 +2878,7 @@ namespace Game.AI
}
case SmartEvents.FriendlyIsCc:
{
if (me == null || !me.IsInCombat())
if (me == null || !me.IsEngaged())
return;
List<Creature> creatures = new List<Creature>();
@@ -3016,7 +3005,7 @@ namespace Game.AI
}
case SmartEvents.OocLos:
{
if (me == null || me.IsInCombat())
if (me == null || me.IsEngaged())
return;
//can trigger if closer than fMaxAllowedRange
float range = e.Event.los.maxDist;
@@ -3036,7 +3025,7 @@ namespace Game.AI
}
case SmartEvents.IcLos:
{
if (me == null || !me.IsInCombat())
if (me == null || !me.IsEngaged())
return;
//can trigger if closer than fMaxAllowedRange
float range = e.Event.los.maxDist;
@@ -3226,7 +3215,7 @@ namespace Game.AI
}
case SmartEvents.FriendlyHealthPCT:
{
if (me == null || !me.IsInCombat())
if (me == null || !me.IsEngaged())
return;
List<WorldObject> targets;
@@ -3401,10 +3390,10 @@ namespace Game.AI
if (e.Event.event_phase_mask != 0 && !IsInPhase(e.Event.event_phase_mask))
return;
if (e.GetEventType() == SmartEvents.UpdateIc && (me == null || !me.IsInCombat()))
if (e.GetEventType() == SmartEvents.UpdateIc && (me == null || !me.IsEngaged()))
return;
if (e.GetEventType() == SmartEvents.UpdateOoc && (me != null && me.IsInCombat()))//can be used with me=NULL (go script)
if (e.GetEventType() == SmartEvents.UpdateOoc && (me != null && me.IsEngaged()))//can be used with me=NULL (go script)
return;
if (e.timer < diff)
@@ -3741,15 +3730,10 @@ namespace Game.AI
public void OnMoveInLineOfSight(Unit who)
{
ProcessEventsFor(SmartEvents.OocLos, who);
if (me == null)
return;
if (me.GetVictim() != null)
return;
ProcessEventsFor(SmartEvents.IcLos, who);
ProcessEventsFor(me.IsEngaged() ? SmartEvents.IcLos : SmartEvents.OocLos, who);
}
Unit DoSelectLowestHpFriendly(float range, uint MinHPDiff)