diff --git a/Source/Game/AI/CoreAI/CreatureAI.cs b/Source/Game/AI/CoreAI/CreatureAI.cs
index 4e7868418..a9e6dd4f4 100644
--- a/Source/Game/AI/CoreAI/CreatureAI.cs
+++ b/Source/Game/AI/CoreAI/CreatureAI.cs
@@ -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)
+ ///
+ /// Called for reaction when initially engaged
+ ///
+ ///
public virtual void EnterCombat(Unit victim) { }
// Called when the creature is killed
diff --git a/Source/Game/AI/CoreAI/GuardAI.cs b/Source/Game/AI/CoreAI/GuardAI.cs
index 6a360455c..3d5c34764 100644
--- a/Source/Game/AI/CoreAI/GuardAI.cs
+++ b/Source/Game/AI/CoreAI/GuardAI.cs
@@ -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
diff --git a/Source/Game/AI/CoreAI/UnitAI.cs b/Source/Game/AI/CoreAI/UnitAI.cs
index 8553846db..014b43fe6 100644
--- a/Source/Game/AI/CoreAI/UnitAI.cs
+++ b/Source/Game/AI/CoreAI/UnitAI.cs
@@ -57,9 +57,9 @@ namespace Game.AI
return me.GetThreatManager();
}
- void SortByDistanceTo(Unit reference, List targets)
+ void SortByDistance(List 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)
+ ///
+ /// Select the best target (in order) from the threat list that fulfill the following:
+ /// - Not among the first entries in order (or MAXTHREAT order, if is RANDOM).
+ /// - Within at most yards (if dist > 0.0f)
+ /// - At least - yards away (if dist < 0.0f)
+ /// - Is a player (if playerOnly = true)
+ /// - Not the current tank (if withTank = false)
+ /// - Has aura with ID (if aura > 0)
+ /// - Does not have aura with ID - (if aura < 0)
+ ///
+ 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)
+ ///
+ /// Select the best target (in order) satisfying from the threat list.
+ /// If is nonzero, the first entries in order (or MAXTHREAT order, if is RANDOM) are skipped.
+ ///
+ 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 elements, and there are at most elements, then we ignore ALL elements
+ if (mgr.GetThreatListSize() <= offset)
return null;
List targetList = new List();
- 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 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 SelectTargetList(uint num, SelectAggroTarget targetType, float dist, bool playerOnly, int aura = 0)
+ ///
+ /// Select the best (up to) targets (in order) from the threat list that fulfill the following:
+ /// - Not among the first entries in order (or MAXTHREAT order, if is RANDOM).
+ /// - Within at most yards (if dist > 0.0f)
+ /// - At least - yards away (if dist < 0.0f)
+ /// - Is a player (if playerOnly = true)
+ /// - Not the current tank (if withTank = false)
+ /// - Has aura with ID (if aura > 0)
+ /// - Does not have aura with ID - (if aura < 0)
+ /// The resulting targets are stored in (which is cleared first).
+ ///
+ public List 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
- public List SelectTargetList(ISelector selector, uint maxTargets, SelectAggroTarget targetType)
+ ///
+ /// Select the best (up to) targets (in order) satisfying from the threat list and stores them in (which is cleared first).
+ /// If is nonzero, the first entries in order (or MAXTHREAT order, if is RANDOM) are skipped.
+ ///
+ public List SelectTargetList(uint num, SelectAggroTarget targetType, uint offset, ISelector selector)
{
var targetList = new List();
- var threatlist = GetThreatManager().GetThreatList();
- if (threatlist.Empty())
+ ThreatManager mgr = GetThreatManager();
+ // shortcut: we're gonna ignore the first elements, and there's at most 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 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)
+ /// the reference unit
+ /// if 0: ignored, if > 0: maximum distance to the reference unit, if < 0: minimum distance to the reference unit
+ /// self explaining
+ /// allow current tank to be selected
+ /// 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, 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();
}
diff --git a/Source/Game/AI/PlayerAI/PlayerAI.cs b/Source/Game/AI/PlayerAI/PlayerAI.cs
index dbb833a81..344ed83d5 100644
--- a/Source/Game/AI/PlayerAI/PlayerAI.cs
+++ b/Source/Game/AI/PlayerAI/PlayerAI.cs
@@ -1243,7 +1243,7 @@ namespace Game.AI
}
}
- if (charmer.IsInCombat())
+ if (charmer.IsEngaged())
{
Unit target = me.GetVictim();
if (!target || !charmer.IsValidAttackTarget(target) || target.HasBreakableByDamageCrowdControlAura())
diff --git a/Source/Game/AI/ScriptedAI/ScriptedAI.cs b/Source/Game/AI/ScriptedAI/ScriptedAI.cs
index 4ad76ae80..557e5f681 100644
--- a/Source/Game/AI/ScriptedAI/ScriptedAI.cs
+++ b/Source/Game/AI/ScriptedAI/ScriptedAI.cs
@@ -111,6 +111,85 @@ namespace Game.AI
source.PlayDirectSound(soundId);
}
+ ///
+ /// Add specified amount of threat directly to victim (ignores redirection effects) - also puts victim in combat and engages them if necessary
+ ///
+ ///
+ ///
+ ///
+ 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);
+ }
+
+ ///
+ /// Adds/removes the specified percentage from the specified victim's threat (to who, or me if not specified)
+ ///
+ ///
+ ///
+ ///
+ public void ModifyThreatByPercent(Unit victim, int pct, Unit who = null)
+ {
+ if (!victim)
+ return;
+
+ if (!who)
+ who = me;
+
+ who.GetThreatManager().ModifyThreatByPercent(victim, pct);
+ }
+
+ ///
+ /// Resets the victim's threat level to who (or me if not specified) to zero
+ ///
+ ///
+ ///
+ void ResetThreat(Unit victim, Unit who)
+ {
+ if (!victim)
+ return;
+
+ if (!who)
+ who = me;
+
+ who.GetThreatManager().ResetThreat(victim);
+ }
+
+ ///
+ /// Resets the specified unit's threat list (me if not specified) - does not delete entries, just sets their threat to zero
+ ///
+ ///
+ public void ResetThreatList(Unit who = null)
+ {
+ if (!who)
+ who = me;
+
+ who.GetThreatManager().ResetAllThreat();
+ }
+
+ ///
+ /// Returns the threat level of victim towards who (or me if not specified)
+ ///
+ ///
+ ///
+ ///
+ 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);
}
diff --git a/Source/Game/AI/ScriptedAI/ScriptedEscortAI.cs b/Source/Game/AI/ScriptedAI/ScriptedEscortAI.cs
index ecb16a65d..2efd91687 100644
--- a/Source/Game/AI/ScriptedAI/ScriptedEscortAI.cs
+++ b/Source/Game/AI/ScriptedAI/ScriptedEscortAI.cs
@@ -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);
diff --git a/Source/Game/AI/ScriptedAI/ScriptedFollowerAI.cs b/Source/Game/AI/ScriptedAI/ScriptedFollowerAI.cs
index 920a25b1d..e4918eae7 100644
--- a/Source/Game/AI/ScriptedAI/ScriptedFollowerAI.cs
+++ b/Source/Game/AI/ScriptedAI/ScriptedFollowerAI.cs
@@ -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);
diff --git a/Source/Game/AI/SmartScripts/SmartAI.cs b/Source/Game/AI/SmartScripts/SmartAI.cs
index 340f53862..99cc58c73 100644
--- a/Source/Game/AI/SmartScripts/SmartAI.cs
+++ b/Source/Game/AI/SmartScripts/SmartAI.cs
@@ -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;
diff --git a/Source/Game/AI/SmartScripts/SmartScript.cs b/Source/Game/AI/SmartScripts/SmartScript.cs
index 58616c69b..dffc900ac 100644
--- a/Source/Game/AI/SmartScripts/SmartScript.cs
+++ b/Source/Game/AI/SmartScripts/SmartScript.cs
@@ -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 creatures = new List();
@@ -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 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)
diff --git a/Source/Game/Combat/HostileRegManager.cs b/Source/Game/Combat/HostileRegManager.cs
index bb72adfe5..9f4b858fb 100644
--- a/Source/Game/Combat/HostileRegManager.cs
+++ b/Source/Game/Combat/HostileRegManager.cs
@@ -209,10 +209,12 @@ namespace Game.Combat
{
GetTarget().AddHatedBy(this);
}
+
public override void TargetObjectDestroyLink()
{
GetTarget().RemoveHatedBy(this);
}
+
public override void SourceObjectDestroyLink()
{
SetOnlineOfflineState(false);
@@ -391,4 +393,4 @@ namespace Game.Combat
bool iOnline;
bool iAccessible;
}
-}
+}
\ No newline at end of file
diff --git a/Source/Game/Combat/ThreatManager.cs b/Source/Game/Combat/ThreatManager.cs
index 2cd600d08..7139ed92a 100644
--- a/Source/Game/Combat/ThreatManager.cs
+++ b/Source/Game/Combat/ThreatManager.cs
@@ -36,6 +36,28 @@ namespace Game.Combat
const int ThreatUpdateInternal = 1 * Time.InMilliseconds;
+ public void ForwardThreatForAssistingMe(Unit victim, float amount, SpellInfo spell, bool ignoreModifiers = false, bool ignoreRedirection = false)
+ {
+ GetOwner().GetHostileRefManager().ThreatAssist(victim, amount, spell);
+ }
+
+ public void AddThreat(Unit victim, float amount, SpellInfo spell, bool ignoreModifiers = false, bool ignoreRedirection = false)
+ {
+ if (!Owner.CanHaveThreatList() || Owner.HasUnitState(UnitState.Evade))
+ return;
+
+ Owner.SetInCombatWith(victim);
+ victim.SetInCombatWith(Owner);
+ AddThreat(victim, amount, spell != null ? spell.GetSchoolMask() : victim.GetMeleeDamageSchoolMask(), spell);
+ }
+
+ public void ClearAllThreat()
+ {
+ if (Owner.CanHaveThreatList(true) && !IsThreatListEmpty())
+ Owner.SendClearThreatList();
+ ClearReferences();
+ }
+
public void ClearReferences()
{
threatContainer.ClearReferences();
@@ -111,17 +133,17 @@ namespace Game.Combat
}
}
- public void ModifyThreatPercent(Unit victim, int percent)
+ public void ModifyThreatByPercent(Unit victim, int percent)
{
- threatContainer.ModifyThreatPercent(victim, percent);
+ threatContainer.ModifyThreatByPercent(victim, percent);
}
public Unit GetHostilTarget()
{
threatContainer.Update();
- HostileReference nextVictim = threatContainer.SelectNextVictim(GetOwner().ToCreature(), GetCurrentVictim());
+ HostileReference nextVictim = threatContainer.SelectNextVictim(GetOwner().ToCreature(), getCurrentVictim());
SetCurrentVictim(nextVictim);
- return GetCurrentVictim() != null ? GetCurrentVictim().GetTarget() : null;
+ return GetCurrentVictim() != null ? GetCurrentVictim() : null;
}
public float GetThreat(Unit victim, bool alsoSearchOfflineList = false)
@@ -138,10 +160,10 @@ namespace Game.Combat
void TauntApply(Unit taunter)
{
HostileReference refe = threatContainer.GetReferenceByTarget(taunter);
- if (GetCurrentVictim() != null && refe != null && (refe.GetThreat() < GetCurrentVictim().GetThreat()))
+ if (GetCurrentVictim() != null && refe != null && (refe.GetThreat() < getCurrentVictim().GetThreat()))
{
if (refe.GetTempThreatModifier() == 0.0f) // Ok, temp threat is unused
- refe.SetTempThreat(GetCurrentVictim().GetThreat());
+ refe.SetTempThreat(getCurrentVictim().GetThreat());
}
}
@@ -170,14 +192,14 @@ namespace Game.Combat
switch (threatRefStatusChangeEvent.GetEventType())
{
case UnitEventTypes.ThreatRefThreatChange:
- if ((GetCurrentVictim() == hostilRef && threatRefStatusChangeEvent.GetFValue() < 0.0f) ||
- (GetCurrentVictim() != hostilRef && threatRefStatusChangeEvent.GetFValue() > 0.0f))
+ if ((getCurrentVictim() == hostilRef && threatRefStatusChangeEvent.GetFValue() < 0.0f) ||
+ (getCurrentVictim() != hostilRef && threatRefStatusChangeEvent.GetFValue() > 0.0f))
SetDirty(true); // the order in the threat list might have changed
break;
case UnitEventTypes.ThreatRefOnlineStatus:
if (!hostilRef.IsOnline())
{
- if (hostilRef == GetCurrentVictim())
+ if (hostilRef == getCurrentVictim())
{
SetCurrentVictim(null);
SetDirty(true);
@@ -188,14 +210,14 @@ namespace Game.Combat
}
else
{
- if (GetCurrentVictim() != null && hostilRef.GetThreat() > (1.1f * GetCurrentVictim().GetThreat()))
+ if (GetCurrentVictim() != null && hostilRef.GetThreat() > (1.1f * getCurrentVictim().GetThreat()))
SetDirty(true);
threatContainer.AddReference(hostilRef);
threatOfflineContainer.Remove(hostilRef);
}
break;
case UnitEventTypes.ThreatRefRemoveFromList:
- if (hostilRef == GetCurrentVictim())
+ if (hostilRef == getCurrentVictim())
{
SetCurrentVictim(null);
SetDirty(true);
@@ -209,7 +231,6 @@ namespace Game.Combat
}
}
-
public bool IsNeedUpdateToClient(uint time)
{
if (IsThreatListEmpty())
@@ -245,11 +266,8 @@ namespace Game.Combat
return threatContainer.Empty() && threatOfflineContainer.Empty();
}
- public HostileReference GetCurrentVictim()
- {
- return currentVictim;
- }
-
+ public HostileReference getCurrentVictim() { return currentVictim; }
+
public Unit GetOwner()
{
return Owner;
@@ -264,8 +282,53 @@ namespace Game.Combat
public List GetOfflineThreatList() { return threatOfflineContainer.GetThreatList(); }
public ThreatContainer GetOnlineContainer() { return threatContainer; }
- // The hatingUnit is not used yet
- public static float CalcThreat(Unit hatedUnit, Unit hatingUnit, float threat, SpellSchoolMask schoolMask = SpellSchoolMask.Normal, SpellInfo threatSpell = null)
+ public Unit SelectVictim() { return GetHostilTarget(); }
+ public Unit GetCurrentVictim()
+ {
+ var refe = getCurrentVictim();
+ if (refe != null)
+ return refe.GetTarget();
+ else
+ return null;
+ }
+ public bool IsThreatListEmpty(bool includeOffline = false) { return includeOffline ? IsThreatListsEmpty() : IsThreatListEmpty(); }
+ public bool IsThreatenedBy(Unit who, bool includeOffline = false) { return FindReference(who, includeOffline) != null; }
+ public int GetThreatListSize() { return threatContainer.threatList.Count; }
+ public Unit GetAnyTarget()
+ {
+ var list = GetThreatList();
+ if (!list.Empty())
+ return list[0].GetTarget();
+
+ return null;
+ }
+ public void ResetThreat(Unit who)
+ {
+ var refe = FindReference(who, true);
+ if (refe != null)
+ refe.SetThreat(0.0f);
+
+ }
+ public void ResetAllThreat() { ResetAllAggro(); }
+
+ HostileReference FindReference(Unit who, bool includeOffline)
+ {
+ var refe = threatContainer.GetReferenceByTarget(who);
+ if (refe != null)
+ return refe;
+
+ if (includeOffline)
+ {
+ var offlineRefe = threatOfflineContainer.GetReferenceByTarget(who);
+ if (offlineRefe != null)
+ return offlineRefe;
+ }
+
+ return null;
+ }
+
+ // The hatingUnit is not used yet
+ public static float CalcThreat(Unit hatedUnit, Unit hatingUnit, float threat, SpellSchoolMask schoolMask = SpellSchoolMask.Normal, SpellInfo threatSpell = null)
{
if (threatSpell != null)
{
@@ -370,7 +433,7 @@ namespace Game.Combat
return reff;
}
- public void ModifyThreatPercent(Unit victim, int percent)
+ public void ModifyThreatByPercent(Unit victim, int percent)
{
HostileReference refe = GetReferenceByTarget(victim);
if (refe != null)
diff --git a/Source/Game/Entities/Creature/Creature.cs b/Source/Game/Entities/Creature/Creature.cs
index ff693b112..1783a4364 100644
--- a/Source/Game/Entities/Creature/Creature.cs
+++ b/Source/Game/Entities/Creature/Creature.cs
@@ -475,7 +475,7 @@ namespace Game.Entities
}
// periodic check to see if the creature has passed an evade boundary
- if (IsAIEnabled && !IsInEvadeMode() && IsInCombat())
+ if (IsAIEnabled && !IsInEvadeMode() && IsEngaged())
{
if (diff >= m_boundaryCheckTime)
{
@@ -487,7 +487,7 @@ namespace Game.Entities
}
// if periodic combat pulse is enabled and we are both in combat and in a dungeon, do this now
- if (m_combatPulseDelay > 0 && IsInCombat() && GetMap().IsDungeon())
+ if (m_combatPulseDelay > 0 && IsEngaged() && GetMap().IsDungeon())
{
if (diff > m_combatPulseTime)
m_combatPulseTime = 0;
@@ -503,12 +503,7 @@ namespace Game.Entities
continue;
if (player.IsAlive() && IsHostileTo(player))
- {
- if (CanHaveThreatList())
- AddThreat(player, 0.0f);
- SetInCombatWith(player);
- player.SetInCombatWith(this);
- }
+ EngageWithTarget(player);
}
m_combatPulseTime = m_combatPulseDelay * Time.InMilliseconds;
}
@@ -1484,7 +1479,7 @@ namespace Game.Entities
if (!_IsTargetAcceptable(who))
return false;
- if (who.IsInCombat() && IsWithinDist(who, SharedConst.AttackDistance))
+ if (who.IsEngaged() && IsWithinDist(who, SharedConst.AttackDistance))
{
Unit victim = who.GetAttackerForHelper();
if (victim != null)
@@ -2022,7 +2017,7 @@ namespace Game.Entities
return false;
// skip fighting creature
- if (IsInCombat())
+ if (IsEngaged())
return false;
// only free creature
@@ -2067,7 +2062,7 @@ namespace Game.Entities
Unit targetVictim = target.GetAttackerForHelper();
// if I'm already fighting target, or I'm hostile towards the target, the target is acceptable
- if (IsInCombatWith(target) || IsHostileTo(target))
+ if (IsEngagedBy(target) || IsHostileTo(target))
return true;
// if the target's victim is friendly, and the target is neutral, the target is acceptable
@@ -2256,7 +2251,6 @@ namespace Game.Entities
}
var PlList = map.GetPlayers();
-
if (PlList.Empty())
return;
@@ -2266,12 +2260,7 @@ namespace Game.Entities
continue;
if (player.IsAlive())
- {
- SetInCombatWith(player);
- player.SetInCombatWith(this);
- AddThreat(player, 0.0f);
- }
-
+ EngageWithTarget(player);
}
}
@@ -3270,9 +3259,7 @@ namespace Game.Entities
if (assistant != null && assistant.CanAssistTo(m_owner, victim))
{
assistant.SetNoCallAssistance(true);
- assistant.CombatStart(victim);
- if (assistant.IsAIEnabled)
- assistant.GetAI().AttackStart(victim);
+ assistant.EngageWithTarget(victim);
}
}
}
diff --git a/Source/Game/Entities/Creature/CreatureGroups.cs b/Source/Game/Entities/Creature/CreatureGroups.cs
index 5758c8252..4f49dfa78 100644
--- a/Source/Game/Entities/Creature/CreatureGroups.cs
+++ b/Source/Game/Entities/Creature/CreatureGroups.cs
@@ -170,7 +170,7 @@ namespace Game.Entities
member.SetFormation(null);
}
- public void MemberAttackStart(Creature member, Unit target)
+ public void MemberEngagingTarget(Creature member, Unit target)
{
GroupAIFlags groupAI = (GroupAIFlags)FormationMgr.CreatureGroupMap[member.GetSpawnId()].groupAI;
if (groupAI == 0)
@@ -186,9 +186,6 @@ namespace Game.Entities
foreach (var pair in m_members)
{
- if (m_leader) // avoid crash if leader was killed and reset.
- Log.outDebug(LogFilter.Unit, "GROUP ATTACK: group instance id {0} calls member instid {1}", m_leader.GetInstanceId(), member.GetInstanceId());
-
Creature other = pair.Key;
// Skip self
@@ -198,11 +195,8 @@ namespace Game.Entities
if (!other.IsAlive())
continue;
- if (other.GetVictim())
- continue;
-
if (((other != m_leader && groupAI.HasAnyFlag(GroupAIFlags.MembersAssistLeader)) || (other == m_leader && groupAI.HasAnyFlag(GroupAIFlags.LeaderAssistsMember))) && other.IsValidAttackTarget(target))
- other.GetAI().AttackStart(target);
+ other.EngageWithTarget(target);
}
}
diff --git a/Source/Game/Entities/Object/WorldObject.cs b/Source/Game/Entities/Object/WorldObject.cs
index 16d00fc2c..26e817f67 100644
--- a/Source/Game/Entities/Object/WorldObject.cs
+++ b/Source/Game/Entities/Object/WorldObject.cs
@@ -1738,9 +1738,7 @@ namespace Game.Entities
public void AddToNotify(NotifyFlags f) { m_notifyflags |= f; }
public bool IsNeedNotify(NotifyFlags f) { return Convert.ToBoolean(m_notifyflags & f); }
NotifyFlags GetNotifyFlags() { return m_notifyflags; }
- bool NotifyExecuted(NotifyFlags f) { return Convert.ToBoolean(m_executed_notifies & f); }
- void SetNotified(NotifyFlags f) { m_executed_notifies |= f; }
- public void ResetAllNotifies() { m_notifyflags = 0; m_executed_notifies = 0; }
+ public void ResetAllNotifies() { m_notifyflags = 0; }
public bool IsActiveObject() { return m_isActive; }
public bool IsPermanentWorldObject() { return m_isWorldObject; }
@@ -2363,7 +2361,6 @@ namespace Game.Entities
public bool IsInWorld { get; set; }
NotifyFlags m_notifyflags;
- NotifyFlags m_executed_notifies;
public FlaggedArray m_stealth = new FlaggedArray(2);
public FlaggedArray m_stealthDetect = new FlaggedArray(2);
diff --git a/Source/Game/Entities/Player/Player.Combat.cs b/Source/Game/Entities/Player/Player.Combat.cs
index 31c9cd275..923e451ad 100644
--- a/Source/Game/Entities/Player/Player.Combat.cs
+++ b/Source/Game/Entities/Player/Player.Combat.cs
@@ -22,6 +22,7 @@ using Game.Networking.Packets;
using Game.Spells;
using System;
using System.Collections.Generic;
+using Game.Maps;
namespace Game.Entities
{
@@ -485,15 +486,38 @@ namespace Game.Entities
}
}
+ public void SetContestedPvP(Player attackedPlayer = null)
+ {
+ if (attackedPlayer != null && (attackedPlayer == this || (duel != null && duel.opponent == attackedPlayer)))
+ return;
+
+ SetContestedPvPTimer(30000);
+ if (!HasUnitState(UnitState.AttackPlayer))
+ {
+ AddUnitState(UnitState.AttackPlayer);
+ AddPlayerFlag(PlayerFlags.ContestedPVP);
+ // call MoveInLineOfSight for nearby contested guards
+ AIRelocationNotifier notifier = new AIRelocationNotifier(this);
+ Cell.VisitWorldObjects(this, notifier, GetVisibilityRange());
+ }
+ foreach (Unit unit in m_Controlled)
+ {
+ if (!unit.HasUnitState(UnitState.AttackPlayer))
+ {
+ unit.AddUnitState(UnitState.AttackPlayer);
+ AIRelocationNotifier notifier = new AIRelocationNotifier(unit);
+ Cell.VisitWorldObjects(this, notifier, GetVisibilityRange());
+ }
+ }
+ }
+
public void UpdateContestedPvP(uint diff)
{
if (m_contestedPvPTimer == 0 || IsInCombat())
return;
if (m_contestedPvPTimer <= diff)
- {
ResetContestedPvP();
- }
else
m_contestedPvPTimer -= diff;
}
diff --git a/Source/Game/Entities/Unit/Unit.Combat.cs b/Source/Game/Entities/Unit/Unit.Combat.cs
index 51f88d9ea..adaec067d 100644
--- a/Source/Game/Entities/Unit/Unit.Combat.cs
+++ b/Source/Game/Entities/Unit/Unit.Combat.cs
@@ -125,7 +125,7 @@ namespace Game.Entities
public virtual void SetCanDualWield(bool value) { m_canDualWield = value; }
- void SendClearThreatList()
+ public void SendClearThreatList()
{
ThreatClear packet = new ThreatClear();
packet.UnitGUID = GetGUID();
@@ -158,13 +158,6 @@ namespace Game.Entities
}
}
- public void DeleteThreatList()
- {
- if (CanHaveThreatList(true) && !threatManager.IsThreatListEmpty())
- SendClearThreatList();
- threatManager.ClearReferences();
- }
-
public void TauntApply(Unit taunter)
{
Cypher.Assert(IsTypeId(TypeId.Unit));
@@ -325,12 +318,6 @@ namespace Game.Entities
}
public void RemoveHatedBy(HostileReference pHostileReference) { } //nothing to do yet
- public void AddThreat(Unit victim, float fThreat, SpellSchoolMask schoolMask = SpellSchoolMask.Normal, SpellInfo threatSpell = null)
- {
- // Only mobs can manage threat lists
- if (CanHaveThreatList() && !HasUnitState(UnitState.Evade))
- threatManager.AddThreat(victim, fThreat, schoolMask, threatSpell);
- }
public float ApplyTotalThreatModifier(float fThreat, SpellSchoolMask schoolMask = SpellSchoolMask.Normal)
{
if (!HasAuraType(AuraType.ModThreat) || fThreat < 0)
@@ -360,6 +347,17 @@ namespace Game.Entities
return m_deathState;
}
+ public bool IsEngaged() { return IsInCombat(); }
+ public bool IsEngagedBy(Unit who) { return IsInCombatWith(who); }
+ public void EngageWithTarget(Unit who)
+ {
+ SetInCombatWith(who);
+ who.SetInCombatWith(this);
+ GetThreatManager().AddThreat(who, 0.0f);
+ }
+ public bool IsThreatened() { return CanHaveThreatList() && !GetThreatManager().IsThreatListEmpty(); }
+ public bool IsThreatenedBy(Unit who) { return who != null && CanHaveThreatList() && GetThreatManager().IsThreatenedBy(who); }
+
public bool IsInCombat() { return HasUnitFlag(UnitFlags.InCombat); }
public bool IsPetInCombat() { return HasUnitFlag(UnitFlags.PetInCombat); }
@@ -444,7 +442,7 @@ namespace Game.Entities
if (creature != null && !IsPet())
{
// should not let player enter combat by right clicking target - doesn't helps
- AddThreat(victim, 0.0f);
+ GetThreatManager().AddThreat(victim, 0.0f);
SetInCombatWith(victim);
if (victim.IsTypeId(TypeId.Player))
@@ -453,7 +451,7 @@ namespace Game.Entities
Unit owner = victim.GetOwner();
if (owner != null)
{
- AddThreat(owner, 0.0f);
+ GetThreatManager().AddThreat(owner, 0.0f);
SetInCombatWith(owner);
if (owner.GetTypeId() == TypeId.Player)
owner.SetInCombatWith(this);
@@ -553,6 +551,9 @@ namespace Game.Entities
}
public Unit GetAttackerForHelper()
{
+ if (!IsEngaged())
+ return null;
+
Unit victim = GetVictim();
if (victim != null)
if ((!IsPet() && GetPlayerMovingMe() == null) || IsInCombatWith(victim) || victim.IsInCombatWith(this))
@@ -1143,7 +1144,7 @@ namespace Game.Entities
if (damagetype != DamageEffectType.DOT && damage > 0 && !victim.GetOwnerGUID().IsPlayer() && (spellProto == null || !spellProto.HasAura(AuraType.DamageShield)))
victim.ToCreature().SetLastDamagedTime(GameTime.GetGameTime() + SharedConst.MaxAggroResetTime);
- victim.AddThreat(this, damage, damageSchoolMask, spellProto);
+ victim.GetThreatManager().AddThreat(this, damage, spellProto);
}
else // victim is a player
{
@@ -1338,11 +1339,12 @@ namespace Game.Entities
SetInCombatWith(target);
target.SetInCombatWith(this);
}
- Unit who = target.GetCharmerOrOwnerOrSelf();
- if (who.IsTypeId(TypeId.Player))
- SetContestedPvP(who.ToPlayer());
Player me = GetCharmerOrOwnerPlayerOrPlayerItself();
+ Unit who = target.GetCharmerOrOwnerOrSelf();
+ if (me != null && who.IsTypeId(TypeId.Player))
+ me.SetContestedPvP(who.ToPlayer());
+
if (me != null && who.IsPvP() && (!who.IsTypeId(TypeId.Player) || me.duel == null || me.duel.opponent != who))
{
me.UpdatePvP(true);
@@ -1403,7 +1405,7 @@ namespace Game.Entities
creature.GetAI().EnterCombat(enemy);
if (creature.GetFormation() != null)
- creature.GetFormation().MemberAttackStart(creature, enemy);
+ creature.GetFormation().MemberEngagingTarget(creature, enemy);
}
if (IsPet())
@@ -1595,7 +1597,7 @@ namespace Game.Entities
if (!creature.IsPet())
{
- creature.DeleteThreatList();
+ creature.GetThreatManager().ClearAllThreat();
// must be after setDeathState which resets dynamic flags
if (!creature.loot.IsLooted())
@@ -1709,29 +1711,6 @@ namespace Game.Entities
public virtual uint GetBlockPercent() { return 30; }
- public void SetContestedPvP(Player attackedPlayer = null)
- {
- Player player = GetCharmerOrOwnerPlayerOrPlayerItself();
-
- if (player == null || (attackedPlayer != null && (attackedPlayer == player || (player.duel != null && player.duel.opponent == attackedPlayer))))
- return;
-
- player.SetContestedPvPTimer(30000);
- if (!player.HasUnitState(UnitState.AttackPlayer))
- {
- player.AddUnitState(UnitState.AttackPlayer);
- player.AddPlayerFlag(PlayerFlags.ContestedPVP);
- // call MoveInLineOfSight for nearby contested guards
- UpdateObjectVisibility();
- }
- if (!HasUnitState(UnitState.AttackPlayer))
- {
- AddUnitState(UnitState.AttackPlayer);
- // call MoveInLineOfSight for nearby contested guards
- UpdateObjectVisibility();
- }
- }
-
void UpdateReactives(uint p_time)
{
for (ReactiveType reactive = 0; reactive < ReactiveType.Max; ++reactive)
diff --git a/Source/Game/Entities/Unit/Unit.Pets.cs b/Source/Game/Entities/Unit/Unit.Pets.cs
index b50d54805..ea2f3f545 100644
--- a/Source/Game/Entities/Unit/Unit.Pets.cs
+++ b/Source/Game/Entities/Unit/Unit.Pets.cs
@@ -313,7 +313,7 @@ namespace Game.Entities
CastStop();
CombatStop(); // @todo CombatStop(true) may cause crash (interrupt spells)
- DeleteThreatList();
+ GetThreatManager().ClearAllThreat();
Player playerCharmer = charmer.ToPlayer();
@@ -456,7 +456,7 @@ namespace Game.Entities
CastStop();
CombatStop(); // @todo CombatStop(true) may cause crash (interrupt spells)
GetHostileRefManager().DeleteReferences();
- DeleteThreatList();
+ GetThreatManager().ClearAllThreat();
if (_oldFactionId != 0)
{
diff --git a/Source/Game/Entities/Unit/Unit.Spells.cs b/Source/Game/Entities/Unit/Unit.Spells.cs
index d75ed6b8a..232eedf00 100644
--- a/Source/Game/Entities/Unit/Unit.Spells.cs
+++ b/Source/Game/Entities/Unit/Unit.Spells.cs
@@ -1508,14 +1508,19 @@ namespace Game.Entities
}
public void EnergizeBySpell(Unit victim, uint spellId, int damage, PowerType powerType)
+ {
+ SpellInfo info = Global.SpellMgr.GetSpellInfo(spellId, GetMap().GetDifficultyID());
+ if (info != null)
+ EnergizeBySpell(victim, info, damage, powerType);
+ }
+
+ void EnergizeBySpell(Unit victim, SpellInfo spellInfo, int damage, PowerType powerType)
{
int gain = victim.ModifyPower(powerType, damage);
int overEnergize = damage - gain;
- SpellInfo spellInfo = Global.SpellMgr.GetSpellInfo(spellId, GetMap().GetDifficultyID());
- victim.GetHostileRefManager().ThreatAssist(this, damage * 0.5f, spellInfo);
-
- SendEnergizeSpellLog(victim, spellId, damage, overEnergize, powerType);
+ victim.GetThreatManager().ForwardThreatForAssistingMe(this, damage / 2, spellInfo, true);
+ SendEnergizeSpellLog(victim, spellInfo.Id, damage, overEnergize, powerType);
}
public void ApplySpellImmune(uint spellId, SpellImmunity op, SpellSchoolMask type, bool apply)
diff --git a/Source/Game/Entities/Unit/Unit.cs b/Source/Game/Entities/Unit/Unit.cs
index 1c7311f96..ff4dc43cc 100644
--- a/Source/Game/Entities/Unit/Unit.cs
+++ b/Source/Game/Entities/Unit/Unit.cs
@@ -491,7 +491,7 @@ namespace Game.Entities
m_Events.KillAllEvents(false); // non-delatable (currently casted spells) will not deleted now but it will deleted at call in Map.RemoveAllObjectsInRemoveList
CombatStop();
- DeleteThreatList();
+ GetThreatManager().ClearAllThreat();
GetHostileRefManager().DeleteReferences();
}
public override void CleanupsBeforeDelete(bool finalCleanup = true)
@@ -1609,7 +1609,7 @@ namespace Game.Entities
if (s != DeathState.Alive && s != DeathState.JustRespawned)
{
CombatStop();
- DeleteThreatList();
+ GetThreatManager().ClearAllThreat();
GetHostileRefManager().DeleteReferences();
if (IsNonMeleeSpellCast(false))
diff --git a/Source/Game/Maps/ObjectGridLoader.cs b/Source/Game/Maps/ObjectGridLoader.cs
index 2f4eed9ad..71cce97f2 100644
--- a/Source/Game/Maps/ObjectGridLoader.cs
+++ b/Source/Game/Maps/ObjectGridLoader.cs
@@ -168,7 +168,7 @@ namespace Game.Maps
if (creature.IsInCombat() || !creature.GetThreatManager().IsThreatListsEmpty())
{
creature.CombatStop();
- creature.DeleteThreatList();
+ creature.GetThreatManager().ClearAllThreat();
if (creature.IsAIEnabled)
creature.GetAI().EnterEvadeMode();
}
diff --git a/Source/Game/Spells/Auras/AuraEffect.cs b/Source/Game/Spells/Auras/AuraEffect.cs
index f745fde55..49c26aefc 100644
--- a/Source/Game/Spells/Auras/AuraEffect.cs
+++ b/Source/Game/Spells/Auras/AuraEffect.cs
@@ -4239,7 +4239,7 @@ namespace Game.Spells
case 1515: // Tame beast
// FIX_ME: this is 2.0.12 threat effect replaced in 2.1.x by dummy aura, must be checked for correctness
if (caster != null && target.CanHaveThreatList())
- target.AddThreat(caster, 10.0f);
+ target.GetThreatManager().AddThreat(caster, 10.0f);
break;
case 13139: // net-o-matic
// root to self part of (root_target.charge.root_self sequence
@@ -5597,7 +5597,7 @@ namespace Game.Spells
HealInfo healInfo = new HealInfo(caster, caster, heal, GetSpellInfo(), GetSpellInfo().GetSchoolMask());
caster.HealBySpell(healInfo);
- caster.GetHostileRefManager().ThreatAssist(caster, healInfo.GetEffectiveHeal() * 0.5f, GetSpellInfo());
+ caster.GetThreatManager().ForwardThreatForAssistingMe(caster, healInfo.GetEffectiveHeal() * 0.5f, GetSpellInfo());
caster.ProcSkillsAndAuras(caster, ProcFlags.DonePeriodic, ProcFlags.TakenPeriodic, ProcFlagsSpellType.Heal, ProcFlagsSpellPhase.None, hitMask, null, null, healInfo);
}
@@ -5707,7 +5707,7 @@ namespace Game.Spells
SpellPeriodicAuraLogInfo pInfo = new SpellPeriodicAuraLogInfo(this, heal, (uint)damage, heal - healInfo.GetEffectiveHeal(), healInfo.GetAbsorb(), 0, 0.0f, crit);
target.SendPeriodicAuraLog(pInfo);
- target.GetHostileRefManager().ThreatAssist(caster, healInfo.GetEffectiveHeal() * 0.5f, GetSpellInfo());
+ target.GetThreatManager().ForwardThreatForAssistingMe(caster, healInfo.GetEffectiveHeal() * 0.5f, GetSpellInfo());
// %-based heal - does not proc auras
if (GetAuraType() == AuraType.ObsModHealth)
@@ -5751,7 +5751,8 @@ namespace Game.Spells
if (gainAmount != 0)
{
gainedAmount = caster.ModifyPower(powerType, gainAmount);
- target.AddThreat(caster, gainedAmount * 0.5f, GetSpellInfo().GetSchoolMask(), GetSpellInfo());
+ // energize is not modified by threat modifiers
+ target.GetThreatManager().AddThreat(caster, gainedAmount * 0.5f, GetSpellInfo(), true);
}
// Drain Mana
@@ -5801,7 +5802,7 @@ namespace Game.Spells
int gain = target.ModifyPower(powerType, amount);
if (caster != null)
- target.GetHostileRefManager().ThreatAssist(caster, gain * 0.5f, GetSpellInfo());
+ target.GetThreatManager().ForwardThreatForAssistingMe(caster, gain * 0.5f, GetSpellInfo(), true);
target.SendPeriodicAuraLog(pInfo);
}
@@ -5829,7 +5830,7 @@ namespace Game.Spells
int gain = target.ModifyPower(powerType, amount);
if (caster != null)
- target.GetHostileRefManager().ThreatAssist(caster, gain * 0.5f, GetSpellInfo());
+ target.GetThreatManager().ForwardThreatForAssistingMe(caster, gain * 0.5f, GetSpellInfo(), true);
target.SendPeriodicAuraLog(pInfo);
}
diff --git a/Source/Game/Spells/Spell.cs b/Source/Game/Spells/Spell.cs
index 4291d1b94..68aa8f735 100644
--- a/Source/Game/Spells/Spell.cs
+++ b/Source/Game/Spells/Spell.cs
@@ -1938,7 +1938,7 @@ namespace Game.Spells
HealInfo healInfo = new HealInfo(caster, unitTarget, addhealth, m_spellInfo, m_spellInfo.GetSchoolMask());
caster.HealBySpell(healInfo, crit);
- unitTarget.GetHostileRefManager().ThreatAssist(caster, healInfo.GetEffectiveHeal() * 0.5f, m_spellInfo);
+ unitTarget.GetThreatManager().ForwardThreatForAssistingMe(caster, healInfo.GetEffectiveHeal() * 0.5f, m_spellInfo);
m_healing = (int)healInfo.GetEffectiveHeal();
// Do triggers for unit
@@ -2007,8 +2007,7 @@ namespace Game.Spells
if (missInfo == SpellMissInfo.Resist && m_spellInfo.HasAttribute(SpellCustomAttributes.PickPocket) && unitTarget.IsTypeId(TypeId.Unit))
{
m_caster.RemoveAurasWithInterruptFlags(SpellAuraInterruptFlags.Talk);
- if (unitTarget.ToCreature().IsAIEnabled)
- unitTarget.ToCreature().GetAI().AttackStart(m_caster);
+ unitTarget.ToCreature().EngageWithTarget(m_caster);
}
}
@@ -2108,14 +2107,17 @@ namespace Game.Spells
// assisting case, healing and resurrection
if (unit.HasUnitState(UnitState.AttackPlayer))
{
- m_caster.SetContestedPvP();
- if (m_caster.IsTypeId(TypeId.Player))
- m_caster.ToPlayer().UpdatePvP(true);
+ Player playerOwner = m_caster.GetCharmerOrOwnerPlayerOrPlayerItself();
+ if (playerOwner != null)
+ {
+ playerOwner.SetContestedPvP();
+ playerOwner.UpdatePvP(true);
+ }
}
if (unit.IsInCombat() && m_spellInfo.HasInitialAggro())
{
m_caster.SetInCombatState(unit.GetCombatTimer() > 0, unit);
- unit.GetHostileRefManager().ThreatAssist(m_caster, 0.0f);
+ unit.GetThreatManager().ForwardThreatForAssistingMe(m_caster, 0.0f, null, true);
}
}
}
@@ -4377,14 +4379,14 @@ namespace Game.Spells
// positive spells distribute threat among all units that are in combat with target, like healing
if (m_spellInfo.IsPositive())
- target.GetHostileRefManager().ThreatAssist(m_caster, threatToAdd, m_spellInfo);
+ target.GetThreatManager().ForwardThreatForAssistingMe(m_caster, threatToAdd, m_spellInfo);
// for negative spells threat gets distributed among affected targets
else
{
if (!target.CanHaveThreatList())
continue;
- target.AddThreat(m_caster, threatToAdd, m_spellInfo.GetSchoolMask(), m_spellInfo);
+ target.GetThreatManager().AddThreat(m_caster, threatToAdd, m_spellInfo, true);
}
}
Log.outDebug(LogFilter.Spells, "Spell {0}, added an additional {1} threat for {2} {3} target(s)", m_spellInfo.Id, threat, m_spellInfo.IsPositive() ? "assisting" : "harming", m_UniqueTargetInfo.Count);
diff --git a/Source/Game/Spells/SpellEffects.cs b/Source/Game/Spells/SpellEffects.cs
index 12c40a1eb..14e923ae9 100644
--- a/Source/Game/Spells/SpellEffects.cs
+++ b/Source/Game/Spells/SpellEffects.cs
@@ -1942,7 +1942,7 @@ namespace Game.Spells
return;
// Check for possible target
- if (unitTarget == null || unitTarget.IsInCombat())
+ if (unitTarget == null || unitTarget.IsEngaged())
return;
// target must be OK to do this
@@ -2701,7 +2701,7 @@ namespace Game.Spells
if (!unitTarget.CanHaveThreatList())
return;
- unitTarget.AddThreat(m_caster, damage);
+ unitTarget.GetThreatManager().AddThreat(m_caster, damage);
}
[SpellEffectHandler(SpellEffectName.HealMaxHealth)]
@@ -4408,7 +4408,7 @@ namespace Game.Spells
if (unitTarget == null)
return;
- unitTarget.GetThreatManager().ModifyThreatPercent(m_caster, damage);
+ unitTarget.GetThreatManager().ModifyThreatByPercent(m_caster, damage);
}
[SpellEffectHandler(SpellEffectName.TransDoor)]
diff --git a/Source/Scripts/EasternKingdoms/Karazhan/Curator.cs b/Source/Scripts/EasternKingdoms/Karazhan/Curator.cs
index f6fba2fc5..49f7f07f8 100644
--- a/Source/Scripts/EasternKingdoms/Karazhan/Curator.cs
+++ b/Source/Scripts/EasternKingdoms/Karazhan/Curator.cs
@@ -102,7 +102,7 @@ namespace Scripts.EasternKingdoms.Karazhan.Curator
switch (eventId)
{
case EventIds.HatefulBolt:
- Unit target = SelectTarget(SelectAggroTarget.TopAggro, 1);
+ Unit target = SelectTarget(SelectAggroTarget.MaxThreat, 1);
if (target != null)
DoCast(target, SpellIds.HatefulBolt);
_events.Repeat(TimeSpan.FromSeconds(7), TimeSpan.FromSeconds(15));
diff --git a/Source/Scripts/EasternKingdoms/Karazhan/Moroes.cs b/Source/Scripts/EasternKingdoms/Karazhan/Moroes.cs
index dd05930d1..6c3e1fe3c 100644
--- a/Source/Scripts/EasternKingdoms/Karazhan/Moroes.cs
+++ b/Source/Scripts/EasternKingdoms/Karazhan/Moroes.cs
@@ -278,16 +278,9 @@ namespace Scripts.EasternKingdoms.Karazhan.Moroes
if (Blind_Timer <= diff)
{
- List targets = SelectTargetList(5, SelectAggroTarget.Random, me.GetCombatReach() * 5, true);
- foreach (var i in targets)
- {
-
- if (!me.IsWithinMeleeRange(i))
- {
- DoCast(i, SpellIds.Blind);
- break;
- }
- }
+ Unit target = SelectTarget(SelectAggroTarget.MinDistance, 0, 0.0f, true, false);
+ if (target != null)
+ DoCast(target, SpellIds.Blind);
Blind_Timer = 40000;
}
else
diff --git a/Source/Scripts/EasternKingdoms/Karazhan/OperaEvent.cs b/Source/Scripts/EasternKingdoms/Karazhan/OperaEvent.cs
index 10e59cb1f..c76909db8 100644
--- a/Source/Scripts/EasternKingdoms/Karazhan/OperaEvent.cs
+++ b/Source/Scripts/EasternKingdoms/Karazhan/OperaEvent.cs
@@ -765,11 +765,11 @@ namespace Scripts.EasternKingdoms.Karazhan.OperaEvent
{
Talk(RedRidingHood.SayWolfHood);
DoCast(target, RedRidingHood.SpellLittleRedRidingHood, true);
- TempThreat = DoGetThreat(target);
+ TempThreat = GetThreat(target);
if (TempThreat != 0.0f)
- DoModifyThreatPercent(target, -100);
+ ModifyThreatByPercent(target, -100);
HoodGUID = target.GetGUID();
- me.AddThreat(target, 1000000.0f);
+ AddThreat(target, 1000000.0f);
ChaseTimer = 20000;
IsChasing = true;
}
@@ -781,9 +781,9 @@ namespace Scripts.EasternKingdoms.Karazhan.OperaEvent
if (target)
{
HoodGUID.Clear();
- if (DoGetThreat(target) != 0f)
- DoModifyThreatPercent(target, -100);
- me.AddThreat(target, TempThreat);
+ if (GetThreat(target) != 0f)
+ ModifyThreatByPercent(target, -100);
+ AddThreat(target, TempThreat);
TempThreat = 0;
}
@@ -1023,7 +1023,7 @@ namespace Scripts.EasternKingdoms.Karazhan.OperaEvent
Romulo.GetMotionMaster().Clear();
Romulo.SetDeathState(DeathState.JustDied);
Romulo.CombatStop(true);
- Romulo.DeleteThreatList();
+ Romulo.GetThreatManager().ClearAllThreat();
Romulo.SetDynamicFlags(UnitDynFlags.Lootable);
}
@@ -1261,7 +1261,7 @@ namespace Scripts.EasternKingdoms.Karazhan.OperaEvent
Julianne.GetMotionMaster().Clear();
Julianne.SetDeathState(DeathState.JustDied);
Julianne.CombatStop(true);
- Julianne.DeleteThreatList();
+ Julianne.GetThreatManager().ClearAllThreat();
Julianne.SetDynamicFlags(UnitDynFlags.Lootable);
}
return;
@@ -1290,7 +1290,7 @@ namespace Scripts.EasternKingdoms.Karazhan.OperaEvent
Creature Julianne = (ObjectAccessor.GetCreature(me, JulianneGUID));
if (Julianne && Julianne.GetVictim())
{
- me.AddThreat(Julianne.GetVictim(), 1.0f);
+ AddThreat(Julianne.GetVictim(), 1.0f);
AttackStart(Julianne.GetVictim());
}
}
diff --git a/Source/Scripts/Northrend/AzjolNerub/Ahnkahet/BossJedogaShadowseeker.cs b/Source/Scripts/Northrend/AzjolNerub/Ahnkahet/BossJedogaShadowseeker.cs
index 47f0b5b08..f74c3fa14 100644
--- a/Source/Scripts/Northrend/AzjolNerub/Ahnkahet/BossJedogaShadowseeker.cs
+++ b/Source/Scripts/Northrend/AzjolNerub/Ahnkahet/BossJedogaShadowseeker.cs
@@ -170,7 +170,7 @@ namespace Scripts.Northrend.AzjolNerub.Ahnkahet.JedogaShadowseeker
else
{
who.SetInCombatWith(me);
- me.AddThreat(who, 0.0f);
+ AddThreat(who, 0.0f);
}
}
}
diff --git a/Source/Scripts/Northrend/CrusadersColiseum/TrialOfTheChampion/GrandChampions.cs b/Source/Scripts/Northrend/CrusadersColiseum/TrialOfTheChampion/GrandChampions.cs
index 4b8599d12..6bd1e6de9 100644
--- a/Source/Scripts/Northrend/CrusadersColiseum/TrialOfTheChampion/GrandChampions.cs
+++ b/Source/Scripts/Northrend/CrusadersColiseum/TrialOfTheChampion/GrandChampions.cs
@@ -175,8 +175,8 @@ namespace Scripts.Northrend.CrusadersColiseum.TrialOfTheChampion
{
if (player && !player.IsGameMaster() && me.IsInRange(player, 8.0f, 25.0f, false))
{
- DoResetThreat();
- me.AddThreat(player, 1.0f);
+ ResetThreatList();
+ me.GetThreatManager().AddThreat(player, 1.0f);
DoCast(player, TrialOfChampionSpells.CHARGE);
break;
}
@@ -324,7 +324,7 @@ namespace Scripts.Northrend.CrusadersColiseum.TrialOfTheChampion
temp.SetReactState(ReactStates.Aggressive);
temp.SetInCombatWith(player);
player.SetInCombatWith(temp);
- temp.AddThreat(player, 0.0f);
+ temp.GetThreatManager().AddThreat(player, 0.0f);
}
}
}
@@ -386,8 +386,8 @@ namespace Scripts.Northrend.CrusadersColiseum.TrialOfTheChampion
{
if (player && !player.IsGameMaster() && me.IsInRange(player, 8.0f, 25.0f, false))
{
- DoResetThreat();
- me.AddThreat(player, 5.0f);
+ ResetThreatList();
+ me.GetThreatManager().AddThreat(player, 5.0f);
DoCast(player, TrialOfChampionSpells.INTERCEPT);
break;
}
@@ -549,7 +549,7 @@ namespace Scripts.Northrend.CrusadersColiseum.TrialOfTheChampion
_scheduler.Schedule(TimeSpan.FromSeconds(12), task =>
{
ObjectGuid uiTargetGUID = ObjectGuid.Empty;
- Unit target = SelectTarget(SelectAggroTarget.Farthest, 0, 30.0f);
+ Unit target = SelectTarget(SelectAggroTarget.MaxDistance, 0, 30.0f);
if (target)
{
uiTargetGUID = target.GetGUID();
diff --git a/Source/Scripts/Northrend/CrusadersColiseum/TrialOfTheChampion/TrialOfTheChampion.cs b/Source/Scripts/Northrend/CrusadersColiseum/TrialOfTheChampion/TrialOfTheChampion.cs
index 65e58d9c8..3602d7ba0 100644
--- a/Source/Scripts/Northrend/CrusadersColiseum/TrialOfTheChampion/TrialOfTheChampion.cs
+++ b/Source/Scripts/Northrend/CrusadersColiseum/TrialOfTheChampion/TrialOfTheChampion.cs
@@ -448,7 +448,7 @@ namespace Scripts.Northrend.CrusadersColiseum.TrialOfTheChampion
temp.SetReactState(ReactStates.Aggressive);
temp.SetInCombatWith(player);
player.SetInCombatWith(temp);
- temp.AddThreat(player, 0.0f);
+ AddThreat(player, 0.0f, temp);
}
}
}
diff --git a/Source/Scripts/Northrend/CrusadersColiseum/TrialOfTheCrusader/LordJaraxxus.cs b/Source/Scripts/Northrend/CrusadersColiseum/TrialOfTheCrusader/LordJaraxxus.cs
index c9f73c79e..0097beb41 100644
--- a/Source/Scripts/Northrend/CrusadersColiseum/TrialOfTheCrusader/LordJaraxxus.cs
+++ b/Source/Scripts/Northrend/CrusadersColiseum/TrialOfTheCrusader/LordJaraxxus.cs
@@ -155,7 +155,7 @@ namespace Scripts.Northrend.CrusadersColiseum.TrialOfTheCrusader
return;
case Jaraxxus.EventFelLightning:
{
- Unit target = SelectTarget(SelectAggroTarget.Random, 0, 0.0f, true, -Jaraxxus.SpellLordHittin);
+ Unit target = SelectTarget(SelectAggroTarget.Random, 0, 0.0f, true, true, -Jaraxxus.SpellLordHittin);
if (target)
DoCast(target, Jaraxxus.SpellFelLighting);
_events.ScheduleEvent(Jaraxxus.EventFelLightning, RandomHelper.URand(10 * Time.InMilliseconds, 15 * Time.InMilliseconds));
@@ -163,7 +163,7 @@ namespace Scripts.Northrend.CrusadersColiseum.TrialOfTheCrusader
}
case Jaraxxus.EventIncinerateFlesh:
{
- Unit target = SelectTarget(SelectAggroTarget.Random, 1, 0.0f, true, -Jaraxxus.SpellLordHittin);
+ Unit target = SelectTarget(SelectAggroTarget.Random, 1, 0.0f, true, true, -Jaraxxus.SpellLordHittin);
if (target)
{
Talk(Jaraxxus.EmoteIncinerate, target);
@@ -179,7 +179,7 @@ namespace Scripts.Northrend.CrusadersColiseum.TrialOfTheCrusader
return;
case Jaraxxus.EventLegionFlame:
{
- Unit target = SelectTarget(SelectAggroTarget.Random, 1, 0.0f, true, -Jaraxxus.SpellLordHittin);
+ Unit target = SelectTarget(SelectAggroTarget.Random, 1, 0.0f, true, true, -Jaraxxus.SpellLordHittin);
if (target)
{
Talk(Jaraxxus.EmoteLegionFlame, target);
diff --git a/Source/Scripts/Northrend/CrusadersColiseum/TrialOfTheCrusader/TrialOfTheCrusader.cs b/Source/Scripts/Northrend/CrusadersColiseum/TrialOfTheCrusader/TrialOfTheCrusader.cs
index 7cf97307e..8f9e55e79 100644
--- a/Source/Scripts/Northrend/CrusadersColiseum/TrialOfTheCrusader/TrialOfTheCrusader.cs
+++ b/Source/Scripts/Northrend/CrusadersColiseum/TrialOfTheCrusader/TrialOfTheCrusader.cs
@@ -1174,8 +1174,7 @@ namespace Scripts.Northrend.CrusadersColiseum.TrialOfTheCrusader
{
//1-shot Fizzlebang
temp.CastSpell(me, 67888, false);
- me.SetInCombatWith(temp);
- temp.AddThreat(me, 1000.0f);
+ AddThreat(me, 1000.0f, temp);
temp.GetAI().AttackStart(me);
}
_instance.SetData(DataTypes.Event, 1160);
diff --git a/Source/Scripts/Northrend/FrozenHalls/PitOfSaron/BossKrickAndIck.cs b/Source/Scripts/Northrend/FrozenHalls/PitOfSaron/BossKrickAndIck.cs
index c61351dbf..69d27ecc3 100644
--- a/Source/Scripts/Northrend/FrozenHalls/PitOfSaron/BossKrickAndIck.cs
+++ b/Source/Scripts/Northrend/FrozenHalls/PitOfSaron/BossKrickAndIck.cs
@@ -174,8 +174,8 @@ namespace Scripts.Northrend.FrozenHalls.PitOfSaron.BossKrickAndIck
public void _ResetThreat(Unit target)
{
- DoModifyThreatPercent(target, -100);
- me.AddThreat(target, _tempThreat);
+ ModifyThreatByPercent(target, -100);
+ AddThreat(target, _tempThreat);
}
public override void UpdateAI(uint diff)
@@ -617,8 +617,8 @@ namespace Scripts.Northrend.FrozenHalls.PitOfSaron.BossKrickAndIck
ick.GetAI().Talk(TextIds.SayIckChase1, target);
ick.AddAura(GetSpellInfo().Id, target);
ick.GetAI().SetTempThreat(ick.GetThreatManager().GetThreat(target));
- ick.AddThreat(target, GetEffectValue());
- target.AddThreat(ick, GetEffectValue());
+ ick.GetThreatManager().AddThreat(target, GetEffectValue(), GetSpellInfo(), true, true);
+ target.GetThreatManager().AddThreat(ick, GetEffectValue(), GetSpellInfo(), true, true);
}
}
}
diff --git a/Source/Scripts/Northrend/IcecrownCitadel/GunshipBattle.cs b/Source/Scripts/Northrend/IcecrownCitadel/GunshipBattle.cs
index 0f4a79ff5..1824c4f87 100644
--- a/Source/Scripts/Northrend/IcecrownCitadel/GunshipBattle.cs
+++ b/Source/Scripts/Northrend/IcecrownCitadel/GunshipBattle.cs
@@ -587,7 +587,7 @@ namespace Scripts.Northrend.IcecrownCitadel
if (!me.IsAlive() || !me.IsInCombat())
return;
- me.DeleteThreatList();
+ me.GetThreatManager().ClearAllThreat();
me.CombatStop(true);
me.GetMotionMaster().MoveTargetedHome();
}
@@ -733,11 +733,10 @@ namespace Scripts.Northrend.IcecrownCitadel
foreach (var stalker in creatures)
{
stalker.RemoveAllAuras();
- stalker.DeleteThreatList();
+ stalker.GetThreatManager().ClearAllThreat();
stalker.CombatStop(true);
}
-
uint explosionSpell = isVictory ? GunshipSpells.ExplosionVictory : GunshipSpells.ExplosionWipe;
creatures.Clear();
me.GetCreatureListWithEntryInGrid(creatures, CreatureIds.GunshipHull, 200.0f);
@@ -872,7 +871,7 @@ namespace Scripts.Northrend.IcecrownCitadel
if (!me.IsAlive())
return;
- me.DeleteThreatList();
+ me.GetThreatManager().ClearAllThreat();
me.CombatStop(true);
me.GetMotionMaster().MoveTargetedHome();
@@ -1132,7 +1131,7 @@ namespace Scripts.Northrend.IcecrownCitadel
if (!me.IsAlive())
return;
- me.DeleteThreatList();
+ me.GetThreatManager().ClearAllThreat();
me.CombatStop(true);
me.GetMotionMaster().MoveTargetedHome();
@@ -1442,7 +1441,7 @@ namespace Scripts.Northrend.IcecrownCitadel
{
players.Sort(new ObjectDistanceOrderPred(me));
foreach (var pl in players)
- me.AddThreat(pl, 1.0f);
+ AddThreat(pl, 1.0f);
AttackStart(players.First());
}
diff --git a/Source/Scripts/Northrend/IcecrownCitadel/IcecrownCitadel.cs b/Source/Scripts/Northrend/IcecrownCitadel/IcecrownCitadel.cs
index cd55cfa24..89c5f175b 100644
--- a/Source/Scripts/Northrend/IcecrownCitadel/IcecrownCitadel.cs
+++ b/Source/Scripts/Northrend/IcecrownCitadel/IcecrownCitadel.cs
@@ -646,7 +646,7 @@ namespace Scripts.Northrend.IcecrownCitadel
Talk(Texts.SaySvalnaAggro);
break;
case EventTypes.ImpalingSpear:
- Unit target = SelectTarget(SelectAggroTarget.Random, 1, 0.0f, true, -(int)InstanceSpells.ImpalingSpear);
+ Unit target = SelectTarget(SelectAggroTarget.Random, 1, 0.0f, true, true, -(int)InstanceSpells.ImpalingSpear);
if (target)
{
DoCast(me, InstanceSpells.AetherShield);
diff --git a/Source/Scripts/Northrend/IcecrownCitadel/InstanceIcecrownCitadel.cs b/Source/Scripts/Northrend/IcecrownCitadel/InstanceIcecrownCitadel.cs
index a7b48aaf9..446d34023 100644
--- a/Source/Scripts/Northrend/IcecrownCitadel/InstanceIcecrownCitadel.cs
+++ b/Source/Scripts/Northrend/IcecrownCitadel/InstanceIcecrownCitadel.cs
@@ -1509,7 +1509,7 @@ namespace Scripts.Northrend.IcecrownCitadel
if (warden)
{
terenas.GetAI().AttackStart(warden);
- warden.AddThreat(terenas, 300000.0f);
+ warden.GetThreatManager().AddThreat(terenas, 300000.0f);
}
}
break;
diff --git a/Source/Scripts/Northrend/IcecrownCitadel/LadyDeathwhisper.cs b/Source/Scripts/Northrend/IcecrownCitadel/LadyDeathwhisper.cs
index 47490adc4..102f35497 100644
--- a/Source/Scripts/Northrend/IcecrownCitadel/LadyDeathwhisper.cs
+++ b/Source/Scripts/Northrend/IcecrownCitadel/LadyDeathwhisper.cs
@@ -313,7 +313,7 @@ namespace Scripts.Northrend.IcecrownCitadel
Talk(TextIds.SayDominateMind);
for (byte i = 0; i < _dominateMindCount; i++)
{
- Unit target = SelectTarget(SelectAggroTarget.Random, 1, 0.0f, true, -(int)SpellIds.DominateMind);
+ Unit target = SelectTarget(SelectAggroTarget.Random, 1, 0.0f, true, false, -(int)SpellIds.DominateMind);
if (target != null)
DoCast(target, SpellIds.DominateMind);
}
@@ -423,7 +423,7 @@ namespace Scripts.Northrend.IcecrownCitadel
Talk(TextIds.SayPhase2);
Talk(TextIds.EmotePhase2);
DoStartMovement(me.GetVictim());
- DoResetThreat();
+ ResetThreatList();
damage -= (uint)me.GetPower(PowerType.Mana);
me.SetPower(PowerType.Mana, 0);
diff --git a/Source/Scripts/Northrend/IcecrownCitadel/LordMarrowgar.cs b/Source/Scripts/Northrend/IcecrownCitadel/LordMarrowgar.cs
index 87d6c6625..ae656c957 100644
--- a/Source/Scripts/Northrend/IcecrownCitadel/LordMarrowgar.cs
+++ b/Source/Scripts/Northrend/IcecrownCitadel/LordMarrowgar.cs
@@ -470,8 +470,8 @@ namespace Scripts.Northrend.IcecrownCitadel
void SelectTarget(List targets)
{
targets.Clear();
- // select any unit but not the tank (by owners threatlist)
- Unit target = GetCaster().GetAI().SelectTarget(SelectAggroTarget.Random, 1, -GetCaster().GetCombatReach(), true, -(int)Spells.Impaled);
+ // select any unit but not the tank
+ Unit target = GetCaster().GetAI().SelectTarget(SelectAggroTarget.Random, 1, -GetCaster().GetCombatReach(), true, false, -(int)Spells.Impaled);
if (!target)
target = GetCaster().GetAI().SelectTarget(SelectAggroTarget.Random, 0, 0.0f, true); // or the tank if its solo
if (!target)
@@ -565,7 +565,7 @@ namespace Scripts.Northrend.IcecrownCitadel
CreatureAI marrowgarAI = marrowgar.GetAI();
byte boneSpikeCount = (byte)(Convert.ToBoolean((int)GetCaster().GetMap().GetDifficultyID() & 1) ? 3 : 1);
- List targets = marrowgarAI.SelectTargetList(new BoneSpikeTargetSelector(marrowgarAI), boneSpikeCount, SelectAggroTarget.Random);
+ List targets = marrowgarAI.SelectTargetList(boneSpikeCount, SelectAggroTarget.Random, 1, new BoneSpikeTargetSelector(marrowgarAI));
if (targets.Empty())
return;
diff --git a/Source/Scripts/Northrend/Ulduar/Mimiron.cs b/Source/Scripts/Northrend/Ulduar/Mimiron.cs
index 61982e07e..f6584f06d 100644
--- a/Source/Scripts/Northrend/Ulduar/Mimiron.cs
+++ b/Source/Scripts/Northrend/Ulduar/Mimiron.cs
@@ -1352,10 +1352,10 @@ namespace Scripts.Northrend.Ulduar
if (me.HasUnitState(UnitState.Root))
{
- Unit newTarget = SelectTarget(SelectAggroTarget.Nearest, 0, 30.0f, true);
+ Unit newTarget = SelectTarget(SelectAggroTarget.MinDistance, 0, 30.0f, true);
if (newTarget)
{
- me.DeleteThreatList();
+ me.GetThreatManager().ClearAllThreat();
AttackStart(newTarget);
}
}
diff --git a/Source/Scripts/Outlands/HellfirePeninsula.cs b/Source/Scripts/Outlands/HellfirePeninsula.cs
index d01a13969..aa79cbef5 100644
--- a/Source/Scripts/Outlands/HellfirePeninsula.cs
+++ b/Source/Scripts/Outlands/HellfirePeninsula.cs
@@ -73,7 +73,7 @@ namespace Scripts.Outlands
me.SetFaction(Aeranas.FactionFriendly);
me.AddNpcFlag(NPCFlags.QuestGiver);
me.RemoveAllAuras();
- me.DeleteThreatList();
+ me.GetThreatManager().ClearAllThreat();
me.CombatStop(true);
Talk(Aeranas.SayFree);
return;
diff --git a/Source/Scripts/Pets/Priest.cs b/Source/Scripts/Pets/Priest.cs
index c2001da9c..41a2ca698 100644
--- a/Source/Scripts/Pets/Priest.cs
+++ b/Source/Scripts/Pets/Priest.cs
@@ -42,7 +42,7 @@ namespace Scripts.Pets.Priest
if (!me.IsAlive())
return;
- me.DeleteThreatList();
+ me.GetThreatManager().ClearAllThreat();
me.CombatStop(true);
me.ResetPlayerDamageReq();
}
diff --git a/Source/Scripts/World/BossEmeraldDragons.cs b/Source/Scripts/World/BossEmeraldDragons.cs
index af29e22de..e80a71c16 100644
--- a/Source/Scripts/World/BossEmeraldDragons.cs
+++ b/Source/Scripts/World/BossEmeraldDragons.cs
@@ -146,7 +146,7 @@ namespace Scripts.World.BossEmeraldDragons
_scheduler.Update(diff);
- Unit target = SelectTarget(SelectAggroTarget.TopAggro, 0, -50.0f, true);
+ Unit target = SelectTarget(SelectAggroTarget.MaxThreat, 0, -50.0f, true);
if (target)
DoCast(target, Spells.SummonPlayer);
diff --git a/Source/Scripts/World/NpcSpecial.cs b/Source/Scripts/World/NpcSpecial.cs
index a3894a01a..005898afe 100644
--- a/Source/Scripts/World/NpcSpecial.cs
+++ b/Source/Scripts/World/NpcSpecial.cs
@@ -1559,7 +1559,7 @@ namespace Scripts.World.NpcSpecial
public override void DamageTaken(Unit doneBy, ref uint damage)
{
- me.AddThreat(doneBy, damage); // just to create threat reference
+ AddThreat(doneBy, damage); // just to create threat reference
_damageTimes[doneBy.GetGUID()] = Time.UnixTime;
damage = 0;
}