aboutsummaryrefslogtreecommitdiff
path: root/src/server/game
diff options
context:
space:
mode:
authorTreeston <treeston.mmoc@gmail.com>2017-07-01 20:18:02 +0200
committerShauren <shauren.trinity@gmail.com>2017-07-01 20:18:02 +0200
commite2a1ccd118d129b96e09ff1a15ed0adb1d4a3897 (patch)
treebbe6600c4066078bb7c64a117df457dce0d00b26 /src/server/game
parent5879eb2198fdb976b9fff136757bf8187adb6cf0 (diff)
[3.3.5] Combat/Threat rewrite - prep & refactor (#19966)
* Combat/Threat rewrite (PR #19930) prep work. Mostly refactors, and a compatibility layer on ThreatManager/HostileReference that allows scripts to be changed already.
Diffstat (limited to 'src/server/game')
-rw-r--r--src/server/game/AI/CoreAI/GuardAI.cpp13
-rw-r--r--src/server/game/AI/CoreAI/UnitAI.cpp29
-rw-r--r--src/server/game/AI/CoreAI/UnitAI.h203
-rw-r--r--src/server/game/AI/CreatureAI.cpp37
-rw-r--r--src/server/game/AI/CreatureAI.h2
-rw-r--r--src/server/game/AI/PlayerAI/PlayerAI.cpp2
-rw-r--r--src/server/game/AI/ScriptedAI/ScriptedCreature.cpp79
-rw-r--r--src/server/game/AI/ScriptedAI/ScriptedCreature.h15
-rw-r--r--src/server/game/AI/ScriptedAI/ScriptedEscortAI.cpp24
-rw-r--r--src/server/game/AI/ScriptedAI/ScriptedFollowerAI.cpp25
-rw-r--r--src/server/game/AI/SmartScripts/SmartAI.cpp14
-rw-r--r--src/server/game/AI/SmartScripts/SmartScript.cpp72
-rw-r--r--src/server/game/Combat/ThreatManager.cpp35
-rw-r--r--src/server/game/Combat/ThreatManager.h42
-rw-r--r--src/server/game/Combat/UnitEvents.h2
-rw-r--r--src/server/game/Entities/Creature/Creature.cpp29
-rw-r--r--src/server/game/Entities/Creature/CreatureGroups.cpp11
-rw-r--r--src/server/game/Entities/Creature/CreatureGroups.h2
-rw-r--r--src/server/game/Entities/Object/Object.cpp2
-rw-r--r--src/server/game/Entities/Object/Object.h5
-rw-r--r--src/server/game/Entities/Player/Player.cpp30
-rw-r--r--src/server/game/Entities/Player/Player.h1
-rw-r--r--src/server/game/Entities/Unit/Unit.cpp103
-rw-r--r--src/server/game/Entities/Unit/Unit.h19
-rw-r--r--src/server/game/Grids/ObjectGridLoader.cpp4
-rw-r--r--src/server/game/Spells/Auras/SpellAuraEffects.cpp14
-rw-r--r--src/server/game/Spells/Spell.cpp19
-rw-r--r--src/server/game/Spells/SpellEffects.cpp18
28 files changed, 460 insertions, 391 deletions
diff --git a/src/server/game/AI/CoreAI/GuardAI.cpp b/src/server/game/AI/CoreAI/GuardAI.cpp
index f216059e492..7f65af41d63 100644
--- a/src/server/game/AI/CoreAI/GuardAI.cpp
+++ b/src/server/game/AI/CoreAI/GuardAI.cpp
@@ -45,14 +45,9 @@ void GuardAI::UpdateAI(uint32 /*diff*/)
bool GuardAI::CanSeeAlways(WorldObject const* obj)
{
- if (!obj->isType(TYPEMASK_UNIT))
- return false;
-
- ThreatContainer::StorageType threatList = me->getThreatManager().getThreatList();
- for (ThreatContainer::StorageType::const_iterator itr = threatList.begin(); itr != threatList.end(); ++itr)
- if ((*itr)->getUnitGuid() == obj->GetGUID())
+ if (Unit const* unit = obj->ToUnit())
+ if (unit->IsControlledByPlayer() && me->IsEngagedBy(unit))
return true;
-
return false;
}
@@ -62,14 +57,14 @@ void GuardAI::EnterEvadeMode(EvadeReason /*why*/)
{
me->GetMotionMaster()->MoveIdle();
me->CombatStop(true);
- me->DeleteThreatList();
+ me->GetThreatManager().ClearAllThreat();
return;
}
TC_LOG_DEBUG("entities.unit", "Guard entry: %u 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/src/server/game/AI/CoreAI/UnitAI.cpp b/src/server/game/AI/CoreAI/UnitAI.cpp
index 9f13f7901bc..50a94caa1fa 100644
--- a/src/server/game/AI/CoreAI/UnitAI.cpp
+++ b/src/server/game/AI/CoreAI/UnitAI.cpp
@@ -103,14 +103,14 @@ bool UnitAI::DoSpellAttackIfReady(uint32 spell)
return false;
}
-Unit* UnitAI::SelectTarget(SelectAggroTarget targetType, uint32 position, float dist, bool playerOnly, int32 aura)
+Unit* UnitAI::SelectTarget(SelectAggroTarget targetType, uint32 position, float dist, bool playerOnly, bool withTank, int32 aura)
{
- return SelectTarget(targetType, position, DefaultTargetSelector(me, dist, playerOnly, aura));
+ return SelectTarget(targetType, position, DefaultTargetSelector(me, dist, playerOnly, withTank, aura));
}
-void UnitAI::SelectTargetList(std::list<Unit*>& targetList, uint32 num, SelectAggroTarget targetType, float dist, bool playerOnly, int32 aura)
+void UnitAI::SelectTargetList(std::list<Unit*>& targetList, uint32 num, SelectAggroTarget targetType, uint32 offset, float dist, bool playerOnly, bool withTank, int32 aura)
{
- SelectTargetList(targetList, DefaultTargetSelector(me, dist, playerOnly, aura), num, targetType);
+ SelectTargetList(targetList, num, targetType, offset, DefaultTargetSelector(me, dist, playerOnly, withTank, aura));
}
float UnitAI::DoGetSpellMaxRange(uint32 spellId, bool positive)
@@ -154,7 +154,7 @@ void UnitAI::DoCast(uint32 spellId)
bool playerOnly = spellInfo->HasAttribute(SPELL_ATTR3_ONLY_TARGET_PLAYERS);
float range = spellInfo->GetMaxRange(false);
- DefaultTargetSelector targetSelector(me, range, playerOnly, -(int32)spellId);
+ DefaultTargetSelector targetSelector(me, range, playerOnly, true, -(int32)spellId);
if (!(spellInfo->AuraInterruptFlags & AURA_INTERRUPT_FLAG_NOT_VICTIM)
&& targetSelector(me->GetVictim()))
target = me->GetVictim();
@@ -251,7 +251,12 @@ void UnitAI::FillAISpellInfo()
ThreatManager& UnitAI::GetThreatManager()
{
- return me->getThreatManager();
+ return me->GetThreatManager();
+}
+
+void UnitAI::SortByDistance(std::list<Unit*> list, bool ascending)
+{
+ list.sort(Trinity::ObjectDistanceOrderPred(me, ascending));
}
bool DefaultTargetSelector::operator()(Unit const* target) const
@@ -262,6 +267,9 @@ bool DefaultTargetSelector::operator()(Unit const* target) const
if (!target)
return false;
+ if (target == except)
+ return false;
+
if (m_playerOnly && (target->GetTypeId() != TYPEID_PLAYER))
return false;
@@ -363,8 +371,8 @@ bool NonTankTargetSelector::operator()(Unit const* target) const
if (_playerOnly && target->GetTypeId() != TYPEID_PLAYER)
return false;
- if (HostileReference* currentVictim = _source->getThreatManager().getCurrentVictim())
- return target->GetGUID() != currentVictim->getUnitGuid();
+ if (Unit* currentVictim = _source->GetThreatManager().GetCurrentVictim())
+ return target != currentVictim;
return target != _source->GetVictim();
}
@@ -405,8 +413,3 @@ bool FarthestTargetSelector::operator()(Unit const* target) const
return true;
}
-
-void SortByDistanceTo(Unit* reference, std::list<Unit*>& targets)
-{
- targets.sort(Trinity::ObjectDistanceOrderPred(reference));
-}
diff --git a/src/server/game/AI/CoreAI/UnitAI.h b/src/server/game/AI/CoreAI/UnitAI.h
index d164b9fe6d5..27542ebd3d1 100644
--- a/src/server/game/AI/CoreAI/UnitAI.h
+++ b/src/server/game/AI/CoreAI/UnitAI.h
@@ -46,11 +46,11 @@ enum SpellEffIndex : uint8;
//Selection method used by SelectTarget
enum SelectAggroTarget
{
- SELECT_TARGET_RANDOM = 0, //Just selects a random target
- SELECT_TARGET_TOPAGGRO, //Selects targes from top aggro to bottom
- SELECT_TARGET_BOTTOMAGGRO, //Selects targets from bottom aggro to top
- SELECT_TARGET_NEAREST,
- SELECT_TARGET_FARTHEST
+ SELECT_TARGET_RANDOM = 0, // just pick a random target
+ SELECT_TARGET_MAXTHREAT, // prefer targets higher in the threat list
+ SELECT_TARGET_MINTHREAT, // prefer targets lower in the threat list
+ SELECT_TARGET_MAXDISTANCE, // prefer targets further from us
+ SELECT_TARGET_MINDISTANCE // prefer targets closer to us
};
// default predicate function to select target based on distance, player and/or aura criteria
@@ -59,13 +59,15 @@ struct TC_GAME_API DefaultTargetSelector
Unit const* me;
float m_dist;
bool m_playerOnly;
+ Unit const* except;
int32 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
+ // withMainTank: allow current tank to be selected
// aura: if 0: ignored, if > 0: the target shall have the aura, if < 0, the target shall NOT have the aura
- DefaultTargetSelector(Unit const* unit, float dist, bool playerOnly, int32 aura) : me(unit), m_dist(dist), m_playerOnly(playerOnly), m_aura(aura) { }
+ DefaultTargetSelector(Unit const* unit, float dist, bool playerOnly, bool withMainTank, int32 aura) : me(unit), m_dist(dist), m_playerOnly(playerOnly), except(withMainTank ? me->GetThreatManager().GetCurrentVictim() : nullptr), m_aura(aura) { }
bool operator()(Unit const* target) const;
};
@@ -124,8 +126,6 @@ struct TC_GAME_API FarthestTargetSelector
bool _inLos;
};
-TC_GAME_API void SortByDistanceTo(Unit* reference, std::list<Unit*>& targets);
-
class TC_GAME_API UnitAI
{
protected:
@@ -152,94 +152,174 @@ class TC_GAME_API UnitAI
virtual void SetGUID(ObjectGuid /*guid*/, int32 /*id*/ = 0) { }
virtual ObjectGuid GetGUID(int32 /*id*/ = 0) const { return ObjectGuid::Empty; }
- Unit* SelectTarget(SelectAggroTarget targetType, uint32 position = 0, float dist = 0.0f, bool playerOnly = false, int32 aura = 0);
- // Select the targets satisfying the predicate.
- // predicate shall extend std::unary_function<Unit*, bool>
+ // 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)
+ Unit* SelectTarget(SelectAggroTarget targetType, uint32 offset = 0, float dist = 0.0f, bool playerOnly = false, bool withTank = true, int32 aura = 0);
+ // 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.
template<class PREDICATE>
- Unit* SelectTarget(SelectAggroTarget targetType, uint32 position, PREDICATE const& predicate)
+ Unit* SelectTarget(SelectAggroTarget targetType, uint32 offset, PREDICATE const& predicate)
{
- ThreatContainer::StorageType const& threatlist = GetThreatManager().getThreatList();
- if (position >= threatlist.size())
+ 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 nullptr;
std::list<Unit*> targetList;
- Unit* currentVictim = nullptr;
- if (auto currentVictimReference = GetThreatManager().getCurrentVictim())
+ if (targetType == SELECT_TARGET_MAXDISTANCE || targetType == SELECT_TARGET_MINDISTANCE)
{
- currentVictim = currentVictimReference->getTarget();
+ for (ThreatReference* ref : mgr.GetUnsortedThreatList())
+ {
+ if (ref->IsOffline())
+ continue;
- // Current victim always goes first
- if (currentVictim && predicate(currentVictim))
+ targetList.push_back(ref->GetVictim());
+ }
+ }
+ else
+ {
+ Unit* currentVictim = mgr.GetCurrentVictim();
+ if (currentVictim)
targetList.push_back(currentVictim);
+
+ for (ThreatReference* ref : mgr.GetSortedThreatList())
+ {
+ if (ref->IsOffline())
+ continue;
+
+ Unit* thisTarget = ref->GetVictim();
+ if (thisTarget != currentVictim)
+ targetList.push_back(thisTarget);
+ }
}
- for (HostileReference* hostileRef : threatlist)
+ // filter by predicate
+ targetList.remove_if([&predicate](Unit* target) { return !predicate(target); });
+
+ // shortcut: the list certainly isn't gonna get any larger after this point
+ if (targetList.size() <= offset)
+ return nullptr;
+
+ // right now, list is unsorted for DISTANCE types - re-sort by MAXDISTANCE
+ if (targetType == SELECT_TARGET_MAXDISTANCE || targetType == SELECT_TARGET_MINDISTANCE)
+ SortByDistance(targetList, targetType == SELECT_TARGET_MINDISTANCE);
+
+ // then reverse the sorting for MIN sortings
+ if (targetType == SELECT_TARGET_MINTHREAT)
+ targetList.reverse();
+
+ // now pop the first <offset> elements
+ while (offset)
{
- if (currentVictim != nullptr && hostileRef->getTarget() != currentVictim && predicate(hostileRef->getTarget()))
- targetList.push_back(hostileRef->getTarget());
- else if (currentVictim == nullptr && predicate(hostileRef->getTarget()))
- targetList.push_back(hostileRef->getTarget());
+ targetList.pop_front();
+ --offset;
}
- if (position >= targetList.size())
+ // maybe nothing fulfills the predicate
+ if (targetList.empty())
return nullptr;
- if (targetType == SELECT_TARGET_NEAREST || targetType == SELECT_TARGET_FARTHEST)
- SortByDistanceTo(me, targetList);
-
switch (targetType)
{
- case SELECT_TARGET_NEAREST:
- case SELECT_TARGET_TOPAGGRO:
- {
- auto itr = targetList.begin();
- std::advance(itr, position);
- return *itr;
- }
- case SELECT_TARGET_FARTHEST:
- case SELECT_TARGET_BOTTOMAGGRO:
- {
- auto ritr = targetList.rbegin();
- std::advance(ritr, position);
- return *ritr;
- }
+ case SELECT_TARGET_MAXTHREAT:
+ case SELECT_TARGET_MINTHREAT:
+ case SELECT_TARGET_MAXDISTANCE:
+ case SELECT_TARGET_MINDISTANCE:
+ return targetList.front();
case SELECT_TARGET_RANDOM:
return Trinity::Containers::SelectRandomContainerElement(targetList);
default:
- break;
+ return nullptr;
}
-
- return nullptr;
}
- void SelectTargetList(std::list<Unit*>& targetList, uint32 num, SelectAggroTarget targetType, float dist = 0.0f, bool playerOnly = false, int32 aura = 0);
-
- // Select the targets satifying the predicate.
- // predicate shall extend std::unary_function<Unit*, bool>
+ // 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).
+ void SelectTargetList(std::list<Unit*>& targetList, uint32 num, SelectAggroTarget targetType, uint32 offset = 0, float dist = 0.0f, bool playerOnly = false, bool withTank = true, int32 aura = 0);
+
+ // 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.
template <class PREDICATE>
- void SelectTargetList(std::list<Unit*>& targetList, PREDICATE const& predicate, uint32 maxTargets, SelectAggroTarget targetType)
+ void SelectTargetList(std::list<Unit*>& targetList, uint32 num, SelectAggroTarget targetType, uint32 offset, PREDICATE const& predicate)
{
- ThreatContainer::StorageType const& threatlist = GetThreatManager().getThreatList();
- if (threatlist.empty())
+ targetList.clear();
+ 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;
- for (HostileReference* hostileRef : threatlist)
- if (predicate(hostileRef->getTarget()))
- targetList.push_back(hostileRef->getTarget());
+ if (targetType == SELECT_TARGET_MAXDISTANCE || targetType == SELECT_TARGET_MINDISTANCE)
+ {
+ for (ThreatReference* ref : mgr.GetUnsortedThreatList())
+ {
+ if (ref->IsOffline())
+ continue;
+
+ targetList.push_back(ref->GetVictim());
+ }
+ }
+ else
+ {
+ Unit* currentVictim = mgr.GetCurrentVictim();
+ if (currentVictim)
+ targetList.push_back(currentVictim);
+
+ for (ThreatReference* ref : mgr.GetSortedThreatList())
+ {
+ if (ref->IsOffline())
+ continue;
+
+ Unit* thisTarget = ref->GetVictim();
+ if (thisTarget != currentVictim)
+ targetList.push_back(thisTarget);
+ }
+ }
+
+ // filter by predicate
+ targetList.remove_if([&predicate](Unit* target) { return !predicate(target); });
- if (targetList.size() < maxTargets)
+ // shortcut: the list isn't gonna get any larger
+ if (targetList.size() <= offset)
+ {
+ targetList.clear();
return;
+ }
- if (targetType == SELECT_TARGET_NEAREST || targetType == SELECT_TARGET_FARTHEST)
- SortByDistanceTo(me, targetList);
+ // right now, list is unsorted for DISTANCE types - re-sort by MAXDISTANCE
+ if (targetType == SELECT_TARGET_MAXDISTANCE || targetType == SELECT_TARGET_MINDISTANCE)
+ SortByDistance(targetList, targetType == SELECT_TARGET_MINDISTANCE);
- if (targetType == SELECT_TARGET_FARTHEST || targetType == SELECT_TARGET_BOTTOMAGGRO)
+ // now the list is MAX sorted, reverse for MIN types
+ if (targetType == SELECT_TARGET_MINTHREAT)
targetList.reverse();
+ // ignore the first <offset> elements
+ while (offset)
+ {
+ targetList.pop_front();
+ --offset;
+ }
+
+ if (targetList.size() <= num)
+ return;
+
if (targetType == SELECT_TARGET_RANDOM)
- Trinity::Containers::RandomResize(targetList, maxTargets);
+ Trinity::Containers::RandomResize(targetList, num);
else
- targetList.resize(maxTargets);
+ targetList.resize(num);
}
// Called at any Damage to any victim (before damage apply)
@@ -303,6 +383,7 @@ class TC_GAME_API UnitAI
UnitAI& operator=(UnitAI const& right) = delete;
ThreatManager& GetThreatManager();
+ void SortByDistance(std::list<Unit*> list, bool ascending = true);
};
#endif
diff --git a/src/server/game/AI/CreatureAI.cpp b/src/server/game/AI/CreatureAI.cpp
index 7ecee7cc14c..cd073d3fe39 100644
--- a/src/server/game/AI/CreatureAI.cpp
+++ b/src/server/game/AI/CreatureAI.cpp
@@ -82,8 +82,8 @@ void CreatureAI::DoZoneInCombat(Creature* creature /*= nullptr*/, float maxRange
if (Unit* summoner = creature->ToTempSummon()->GetSummoner())
{
Unit* target = summoner->getAttackerForHelper();
- if (!target && summoner->CanHaveThreatList() && !summoner->getThreatManager().isThreatListEmpty())
- target = summoner->getThreatManager().getHostilTarget();
+ if (!target && summoner->CanHaveThreatList() && !summoner->GetThreatManager().IsThreatListEmpty())
+ target = summoner->GetThreatManager().GetAnyTarget();
if (target && (creature->IsFriendlyTo(summoner) || creature->IsHostileTo(target)))
creature->AI()->AttackStart(target);
}
@@ -114,16 +114,8 @@ void CreatureAI::DoZoneInCombat(Creature* creature /*= nullptr*/, float maxRange
{
creature->SetInCombatWith(player);
player->SetInCombatWith(creature);
- creature->AddThreat(player, 0.0f);
+ creature->GetThreatManager().AddThreat(player, 0.0f, nullptr, true, true);
}
-
- /* Causes certain things to never leave the threat list (Priest Lightwell, etc):
- for (Unit::ControlList::const_iterator itr = player->m_Controlled.begin(); itr != player->m_Controlled.end(); ++itr)
- {
- creature->SetInCombatWith(*itr);
- (*itr)->SetInCombatWith(creature);
- creature->AddThreat(*itr, 0.0f);
- }*/
}
}
}
@@ -141,11 +133,11 @@ void CreatureAI::MoveInLineOfSight_Safe(Unit* who)
void CreatureAI::MoveInLineOfSight(Unit* who)
{
- if (me->GetVictim())
+ if (me->IsEngaged())
return;
if (me->HasReactState(REACT_AGGRESSIVE) && me->CanStartAttack(who, false))
- AttackStart(who);
+ me->EngageWithTarget(who);
}
void CreatureAI::_OnOwnerCombatInteraction(Unit* target)
@@ -154,12 +146,7 @@ void CreatureAI::_OnOwnerCombatInteraction(Unit* target)
return;
if (!me->HasReactState(REACT_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
@@ -169,8 +156,8 @@ void CreatureAI::TriggerAlert(Unit const* who) const
if (!who || who->GetTypeId() != 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->GetTypeId() != TYPEID_UNIT || me->IsInCombat() || me->HasUnitState(UNIT_STATE_CONFUSED | UNIT_STATE_STUNNED | UNIT_STATE_FLEEING | UNIT_STATE_DISTRACTED))
+ // If this unit isn't an NPC, is already distracted, is fighting, is confused, stunned or fleeing, do nothing
+ if (me->GetTypeId() != TYPEID_UNIT || me->IsEngaged() || me->HasUnitState(UNIT_STATE_CONFUSED | UNIT_STATE_STUNNED | UNIT_STATE_FLEEING | UNIT_STATE_DISTRACTED))
return;
// Only alert for hostiles!
@@ -227,7 +214,7 @@ void CreatureAI::SetGazeOn(Unit* target)
bool CreatureAI::UpdateVictimWithGaze()
{
- if (!me->IsInCombat())
+ if (!me->IsEngaged())
return false;
if (me->HasReactState(REACT_PASSIVE))
@@ -247,7 +234,7 @@ bool CreatureAI::UpdateVictimWithGaze()
bool CreatureAI::UpdateVictim()
{
- if (!me->IsInCombat())
+ if (!me->IsEngaged())
return false;
if (!me->HasReactState(REACT_PASSIVE))
@@ -258,7 +245,7 @@ bool CreatureAI::UpdateVictim()
return me->GetVictim() != nullptr;
}
- else if (me->getThreatManager().isThreatListEmpty())
+ else if (me->GetThreatManager().IsThreatListEmpty())
{
EnterEvadeMode(EVADE_REASON_NO_HOSTILES);
return false;
@@ -275,7 +262,7 @@ bool CreatureAI::_EnterEvadeMode(EvadeReason /*why*/)
me->RemoveAurasOnEvade();
// sometimes bosses stuck in combat?
- me->DeleteThreatList();
+ me->GetThreatManager().ClearAllThreat();
me->CombatStop(true);
me->LoadCreaturesAddon();
me->SetLootRecipient(nullptr);
diff --git a/src/server/game/AI/CreatureAI.h b/src/server/game/AI/CreatureAI.h
index a787082bf9d..bd7a6efab22 100644
--- a/src/server/game/AI/CreatureAI.h
+++ b/src/server/game/AI/CreatureAI.h
@@ -110,7 +110,7 @@ class TC_GAME_API CreatureAI : public UnitAI
// Called for reaction at stopping attack at no attackers or targets
virtual void EnterEvadeMode(EvadeReason why = EVADE_REASON_OTHER);
- // Called for reaction at enter to combat if not in combat yet (enemy can be nullptr)
+ // Called for reaction when initially engaged
virtual void EnterCombat(Unit* /*victim*/) { }
// Called when the creature is killed
diff --git a/src/server/game/AI/PlayerAI/PlayerAI.cpp b/src/server/game/AI/PlayerAI/PlayerAI.cpp
index 34b98cd3fe7..92f1c6f06dc 100644
--- a/src/server/game/AI/PlayerAI/PlayerAI.cpp
+++ b/src/server/game/AI/PlayerAI/PlayerAI.cpp
@@ -1313,7 +1313,7 @@ void SimpleCharmedPlayerAI::UpdateAI(const uint32 diff)
}
}
- if (charmer->IsInCombat())
+ if (charmer->IsEngaged())
{
Unit* target = me->GetVictim();
if (!target || !charmer->IsValidAttackTarget(target) || target->HasBreakableByDamageCrowdControlAura())
diff --git a/src/server/game/AI/ScriptedAI/ScriptedCreature.cpp b/src/server/game/AI/ScriptedAI/ScriptedCreature.cpp
index d32ac53cb9f..e9e252bcf35 100644
--- a/src/server/game/AI/ScriptedAI/ScriptedCreature.cpp
+++ b/src/server/game/AI/ScriptedAI/ScriptedCreature.cpp
@@ -200,6 +200,49 @@ void ScriptedAI::DoPlaySoundToSet(WorldObject* source, uint32 soundId)
source->PlayDirectSound(soundId);
}
+void ScriptedAI::AddThreat(Unit* victim, float amount, Unit* who)
+{
+ if (!victim)
+ return;
+ if (!who)
+ who = me;
+ who->GetThreatManager().AddThreat(victim, amount, nullptr, true, true);
+}
+
+void ScriptedAI::ModifyThreatByPercent(Unit* victim, int32 pct, Unit* who)
+{
+ if (!victim)
+ return;
+ if (!who)
+ who = me;
+ who->GetThreatManager().ModifyThreatByPercent(victim, pct);
+}
+
+void ScriptedAI::ResetThreat(Unit* victim, Unit* who)
+{
+ if (!victim)
+ return;
+ if (!who)
+ who = me;
+ who->GetThreatManager().ResetThreat(victim);
+}
+
+void ScriptedAI::ResetThreatList(Unit* who)
+{
+ if (!who)
+ who = me;
+ who->GetThreatManager().ResetAllThreat();
+}
+
+float ScriptedAI::GetThreat(Unit const* victim, Unit const* who)
+{
+ if (!victim)
+ return 0.0f;
+ if (!who)
+ who = me;
+ return who->GetThreatManager().GetThreat(victim);
+}
+
Creature* ScriptedAI::DoSpawnCreature(uint32 entry, float offsetX, float offsetY, float offsetZ, float angle, uint32 type, uint32 despawntime)
{
return me->SummonCreature(entry, me->GetPositionX() + offsetX, me->GetPositionY() + offsetY, me->GetPositionZ() + offsetZ, angle, TempSummonType(type), despawntime);
@@ -292,38 +335,6 @@ SpellInfo const* ScriptedAI::SelectSpell(Unit* target, uint32 school, uint32 mec
return apSpell[urand(0, spellCount - 1)];
}
-void ScriptedAI::DoResetThreat()
-{
- if (!me->CanHaveThreatList() || me->getThreatManager().isThreatListEmpty())
- {
- TC_LOG_ERROR("scripts", "DoResetThreat called for creature that either cannot have threat list or has empty threat list (me entry = %d)", me->GetEntry());
- return;
- }
-
- ThreatContainer::StorageType threatlist = me->getThreatManager().getThreatList();
-
- for (ThreatContainer::StorageType::const_iterator itr = threatlist.begin(); itr != threatlist.end(); ++itr)
- {
- Unit* unit = ObjectAccessor::GetUnit(*me, (*itr)->getUnitGuid());
- if (unit && DoGetThreat(unit))
- DoModifyThreatPercent(unit, -100);
- }
-}
-
-float ScriptedAI::DoGetThreat(Unit* unit)
-{
- if (!unit)
- return 0.0f;
- return me->getThreatManager().getThreat(unit);
-}
-
-void ScriptedAI::DoModifyThreatPercent(Unit* unit, int32 pct)
-{
- if (!unit)
- return;
- me->getThreatManager().modifyThreatPercent(unit, pct);
-}
-
void ScriptedAI::DoTeleportTo(float x, float y, float z, uint32 time)
{
me->Relocate(x, y, z);
@@ -509,7 +520,7 @@ void BossAI::TeleportCheaters()
float x, y, z;
me->GetPosition(x, y, z);
- ThreatContainer::StorageType threatList = me->getThreatManager().getThreatList();
+ ThreatContainer::StorageType threatList = me->GetThreatManager().getThreatList();
for (ThreatContainer::StorageType::const_iterator itr = threatList.begin(); itr != threatList.end(); ++itr)
if (Unit* target = (*itr)->getTarget())
if (target->GetTypeId() == TYPEID_PLAYER && !CheckBoundary(target))
@@ -519,7 +530,7 @@ void BossAI::TeleportCheaters()
void BossAI::JustSummoned(Creature* summon)
{
summons.Summon(summon);
- if (me->IsInCombat())
+ if (me->IsEngaged())
DoZoneInCombat(summon);
}
diff --git a/src/server/game/AI/ScriptedAI/ScriptedCreature.h b/src/server/game/AI/ScriptedAI/ScriptedCreature.h
index 3b24203a146..6fc85b6a81a 100644
--- a/src/server/game/AI/ScriptedAI/ScriptedCreature.h
+++ b/src/server/game/AI/ScriptedAI/ScriptedCreature.h
@@ -210,11 +210,16 @@ struct TC_GAME_API ScriptedAI : public CreatureAI
//Plays a sound to all nearby players
void DoPlaySoundToSet(WorldObject* source, uint32 soundId);
- //Drops all threat to 0%. Does not remove players from the threat list
- void DoResetThreat();
-
- float DoGetThreat(Unit* unit);
- void DoModifyThreatPercent(Unit* unit, int32 pct);
+ // Add specified amount of threat directly to victim (ignores redirection effects) - also puts victim in combat and engages them if necessary
+ void AddThreat(Unit* victim, float amount, Unit* who = nullptr);
+ // Adds/removes the specified percentage from the specified victim's threat (to who, or me if not specified)
+ void ModifyThreatByPercent(Unit* victim, int32 pct, Unit* who = nullptr);
+ // Resets the victim's threat level to who (or me if not specified) to zero
+ void ResetThreat(Unit* victim, Unit* who = nullptr);
+ // Resets the specified unit's threat list (me if not specified) - does not delete entries, just sets their threat to zero
+ void ResetThreatList(Unit* who = nullptr);
+ // Returns the threat level of victim towards who (or me if not specified)
+ float GetThreat(Unit const* victim, Unit const* who = nullptr);
void DoTeleportTo(float x, float y, float z, uint32 time = 0);
void DoTeleportTo(float const pos[4]);
diff --git a/src/server/game/AI/ScriptedAI/ScriptedEscortAI.cpp b/src/server/game/AI/ScriptedAI/ScriptedEscortAI.cpp
index 6f5b7f1aa71..30a59109e44 100644
--- a/src/server/game/AI/ScriptedAI/ScriptedEscortAI.cpp
+++ b/src/server/game/AI/ScriptedAI/ScriptedEscortAI.cpp
@@ -103,8 +103,7 @@ bool npc_escortAI::AssistPlayerInCombatAgainst(Unit* who)
}
else
{
- who->SetInCombatWith(me);
- me->AddThreat(who, 0.0f);
+ me->EngageWithTarget(who);
return true;
}
}
@@ -126,24 +125,7 @@ void npc_escortAI::MoveInLineOfSight(Unit* who)
{
float fAttackRadius = me->GetAttackDistance(who);
if (me->IsWithinDistInMap(who, fAttackRadius) && me->IsWithinLOSInMap(who))
- {
- if (!me->GetVictim())
- {
- // Clear distracted state on combat
- if (me->HasUnitState(UNIT_STATE_DISTRACTED))
- {
- me->ClearUnitState(UNIT_STATE_DISTRACTED);
- me->GetMotionMaster()->Clear();
- }
-
- AttackStart(who);
- }
- else if (me->GetMap()->IsDungeon())
- {
- who->SetInCombatWith(me);
- me->AddThreat(who, 0.0f);
- }
- }
+ me->EngageWithTarget(who);
}
}
}
@@ -193,7 +175,7 @@ void npc_escortAI::ReturnToLastPoint()
void npc_escortAI::EnterEvadeMode(EvadeReason /*why*/)
{
me->RemoveAllAuras();
- me->DeleteThreatList();
+ me->GetThreatManager().ClearAllThreat();
me->CombatStop(true);
me->SetLootRecipient(nullptr);
diff --git a/src/server/game/AI/ScriptedAI/ScriptedFollowerAI.cpp b/src/server/game/AI/ScriptedAI/ScriptedFollowerAI.cpp
index d18876fc439..b0b332afecd 100644
--- a/src/server/game/AI/ScriptedAI/ScriptedFollowerAI.cpp
+++ b/src/server/game/AI/ScriptedAI/ScriptedFollowerAI.cpp
@@ -52,9 +52,7 @@ void FollowerAI::AttackStart(Unit* who)
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(UNIT_STATE_FOLLOW))
me->ClearUnitState(UNIT_STATE_FOLLOW);
@@ -87,18 +85,8 @@ bool FollowerAI::AssistPlayerInCombatAgainst(Unit* who)
//too far away and no free sight?
if (me->IsWithinDistInMap(who, MAX_PLAYER_DISTANCE) && 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;
@@ -131,10 +119,7 @@ void FollowerAI::MoveInLineOfSight(Unit* who)
AttackStart(who);
}
else if (me->GetMap()->IsDungeon())
- {
- who->SetInCombatWith(me);
- me->AddThreat(who, 0.0f);
- }
+ me->EngageWithTarget(who);
}
}
}
@@ -176,7 +161,7 @@ void FollowerAI::JustRespawned()
void FollowerAI::EnterEvadeMode(EvadeReason /*why*/)
{
me->RemoveAllAuras();
- me->DeleteThreatList();
+ me->GetThreatManager().ClearAllThreat();
me->CombatStop(true);
me->SetLootRecipient(nullptr);
diff --git a/src/server/game/AI/SmartScripts/SmartAI.cpp b/src/server/game/AI/SmartScripts/SmartAI.cpp
index 682f92f1ee6..5aa2550a704 100644
--- a/src/server/game/AI/SmartScripts/SmartAI.cpp
+++ b/src/server/game/AI/SmartScripts/SmartAI.cpp
@@ -559,18 +559,8 @@ bool SmartAI::AssistPlayerInCombatAgainst(Unit* who)
//too far away and no free sight?
if (me->IsWithinDistInMap(who, SMART_MAX_AID_DIST) && 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;
diff --git a/src/server/game/AI/SmartScripts/SmartScript.cpp b/src/server/game/AI/SmartScripts/SmartScript.cpp
index fe86978b0db..3072ef31425 100644
--- a/src/server/game/AI/SmartScripts/SmartScript.cpp
+++ b/src/server/game/AI/SmartScripts/SmartScript.cpp
@@ -459,15 +459,11 @@ void SmartScript::ProcessAction(SmartScriptHolder& e, Unit* unit, uint32 var0, u
if (!me)
break;
- ThreatContainer::StorageType threatList = me->getThreatManager().getThreatList();
- for (auto i = threatList.begin(); i != threatList.end(); ++i)
+ for (auto* ref : me->GetThreatManager().GetUnsortedThreatList())
{
- if (Unit* target = ObjectAccessor::GetUnit(*me, (*i)->getUnitGuid()))
- {
- me->getThreatManager().modifyThreatPercent(target, e.action.threatPCT.threatINC ? (int32)e.action.threatPCT.threatINC : -(int32)e.action.threatPCT.threatDEC);
- TC_LOG_DEBUG("scripts.ai", "SmartScript::ProcessAction:: SMART_ACTION_THREAT_ALL_PCT: Creature guidLow %u modify threat for unit %u, value %i",
- me->GetGUID().GetCounter(), target->GetGUID().GetCounter(), e.action.threatPCT.threatINC ? (int32)e.action.threatPCT.threatINC : -(int32)e.action.threatPCT.threatDEC);
- }
+ ref->ModifyThreatByPercent(std::max<int32>(-100,int32(e.action.threatPCT.threatINC) - int32(e.action.threatPCT.threatDEC)));
+ TC_LOG_DEBUG("scripts.ai", "SmartScript::ProcessAction:: SMART_ACTION_THREAT_ALL_PCT: Creature guidLow %u modify threat for unit %u, value %i",
+ me->GetGUID().GetCounter(), ref->GetVictim()->GetGUID().GetCounter(), int32(e.action.threatPCT.threatINC)-int32(e.action.threatPCT.threatDEC));
}
break;
}
@@ -480,9 +476,9 @@ void SmartScript::ProcessAction(SmartScriptHolder& e, Unit* unit, uint32 var0, u
{
if (IsUnit(target))
{
- me->getThreatManager().modifyThreatPercent(target->ToUnit(), e.action.threatPCT.threatINC ? (int32)e.action.threatPCT.threatINC : -(int32)e.action.threatPCT.threatDEC);
+ me->GetThreatManager().ModifyThreatByPercent(target->ToUnit(), std::max<int32>(-100, int32(e.action.threatPCT.threatINC) - int32(e.action.threatPCT.threatDEC)));
TC_LOG_DEBUG("scripts.ai", "SmartScript::ProcessAction:: SMART_ACTION_THREAT_SINGLE_PCT: Creature guidLow %u modify threat for unit %u, value %i",
- me->GetGUID().GetCounter(), target->GetGUID().GetCounter(), e.action.threatPCT.threatINC ? (int32)e.action.threatPCT.threatINC : -(int32)e.action.threatPCT.threatDEC);
+ me->GetGUID().GetCounter(), target->GetGUID().GetCounter(), int32(e.action.threatPCT.threatINC) - int32(e.action.threatPCT.threatDEC));
}
}
break;
@@ -2091,7 +2087,7 @@ void SmartScript::ProcessAction(SmartScriptHolder& e, Unit* unit, uint32 var0, u
{
for (WorldObject* const target : 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), nullptr, true, true);
break;
}
case SMART_ACTION_LOAD_EQUIPMENT:
@@ -2292,10 +2288,10 @@ void SmartScript::GetTargets(ObjectVector& targets, SmartScriptHolder const& e,
{
if (e.target.hostilRandom.powerType)
{
- if (Unit* u = me->AI()->SelectTarget(SELECT_TARGET_TOPAGGRO, 1, PowerUsersSelector(me, Powers(e.target.hostilRandom.powerType - 1), float(e.target.hostilRandom.maxDist), e.target.hostilRandom.playerOnly != 0)))
+ if (Unit* u = me->AI()->SelectTarget(SELECT_TARGET_MAXTHREAT, 1, PowerUsersSelector(me, Powers(e.target.hostilRandom.powerType - 1), float(e.target.hostilRandom.maxDist), e.target.hostilRandom.playerOnly != 0)))
targets.push_back(u);
}
- else if (Unit* u = me->AI()->SelectTarget(SELECT_TARGET_TOPAGGRO, 1, float(e.target.hostilRandom.maxDist), e.target.hostilRandom.playerOnly != 0))
+ else if (Unit* u = me->AI()->SelectTarget(SELECT_TARGET_MAXTHREAT, 1, float(e.target.hostilRandom.maxDist), e.target.hostilRandom.playerOnly != 0))
targets.push_back(u);
}
break;
@@ -2304,10 +2300,10 @@ void SmartScript::GetTargets(ObjectVector& targets, SmartScriptHolder const& e,
{
if (e.target.hostilRandom.powerType)
{
- if (Unit* u = me->AI()->SelectTarget(SELECT_TARGET_BOTTOMAGGRO, 0, PowerUsersSelector(me, Powers(e.target.hostilRandom.powerType - 1), float(e.target.hostilRandom.maxDist), e.target.hostilRandom.playerOnly != 0)))
+ if (Unit* u = me->AI()->SelectTarget(SELECT_TARGET_MINTHREAT, 0, PowerUsersSelector(me, Powers(e.target.hostilRandom.powerType - 1), float(e.target.hostilRandom.maxDist), e.target.hostilRandom.playerOnly != 0)))
targets.push_back(u);
}
- else if (Unit* u = me->AI()->SelectTarget(SELECT_TARGET_BOTTOMAGGRO, 0, float(e.target.hostilRandom.maxDist), e.target.hostilRandom.playerOnly != 0))
+ else if (Unit* u = me->AI()->SelectTarget(SELECT_TARGET_MINTHREAT, 0, float(e.target.hostilRandom.maxDist), e.target.hostilRandom.playerOnly != 0))
targets.push_back(u);
}
break;
@@ -2338,7 +2334,7 @@ void SmartScript::GetTargets(ObjectVector& targets, SmartScriptHolder const& e,
case SMART_TARGET_FARTHEST:
if (me)
{
- if (Unit* u = me->AI()->SelectTarget(SELECT_TARGET_FARTHEST, 0, FarthestTargetSelector(me, float(e.target.farthest.maxDist), e.target.farthest.playerOnly != 0, e.target.farthest.isInLos != 0)))
+ if (Unit* u = me->AI()->SelectTarget(SELECT_TARGET_MAXDISTANCE, 0, FarthestTargetSelector(me, float(e.target.farthest.maxDist), e.target.farthest.playerOnly != 0, e.target.farthest.isInLos != 0)))
targets.push_back(u);
}
break;
@@ -2555,14 +2551,10 @@ void SmartScript::GetTargets(ObjectVector& targets, SmartScriptHolder const& e,
}
case SMART_TARGET_THREAT_LIST:
{
- if (me)
- {
- ThreatContainer::StorageType const& threatList = me->getThreatManager().getThreatList();
- for (HostileReference const* ref : threatList)
- if (Unit* temp = ObjectAccessor::GetUnit(*me, ref->getUnitGuid()))
- if (e.target.hostilRandom.maxDist == 0 || me->IsWithinCombatRange(temp, float(e.target.hostilRandom.maxDist)))
- targets.push_back(temp);
- }
+ if (me && me->CanHaveThreatList())
+ for (auto* ref : me->GetThreatManager().GetUnsortedThreatList())
+ if (!e.target.hostilRandom.maxDist || me->IsWithinCombatRange(ref->GetVictim(), float(e.target.hostilRandom.maxDist)))
+ targets.push_back(ref->GetVictim());
break;
}
case SMART_TARGET_CLOSEST_ENEMY:
@@ -2646,18 +2638,18 @@ void SmartScript::ProcessEvent(SmartScriptHolder& e, Unit* unit, uint32 var0, ui
ProcessTimedAction(e, e.event.minMaxRepeat.repeatMin, e.event.minMaxRepeat.repeatMax);
break;
case SMART_EVENT_UPDATE_OOC:
- if (me && me->IsInCombat())
+ if (me && me->IsEngaged())
return;
ProcessTimedAction(e, e.event.minMaxRepeat.repeatMin, e.event.minMaxRepeat.repeatMax);
break;
case SMART_EVENT_UPDATE_IC:
- if (!me || !me->IsInCombat())
+ if (!me || !me->IsEngaged())
return;
ProcessTimedAction(e, e.event.minMaxRepeat.repeatMin, e.event.minMaxRepeat.repeatMax);
break;
case SMART_EVENT_HEALT_PCT:
{
- if (!me || !me->IsInCombat() || !me->GetMaxHealth())
+ if (!me || !me->IsEngaged() || !me->GetMaxHealth())
return;
uint32 perc = (uint32)me->GetHealthPct();
if (perc > e.event.minMaxRepeat.max || perc < e.event.minMaxRepeat.min)
@@ -2667,7 +2659,7 @@ void SmartScript::ProcessEvent(SmartScriptHolder& e, Unit* unit, uint32 var0, ui
}
case SMART_EVENT_TARGET_HEALTH_PCT:
{
- if (!me || !me->IsInCombat() || !me->GetVictim() || !me->EnsureVictim()->GetMaxHealth())
+ if (!me || !me->IsEngaged() || !me->GetVictim() || !me->EnsureVictim()->GetMaxHealth())
return;
uint32 perc = (uint32)me->EnsureVictim()->GetHealthPct();
if (perc > e.event.minMaxRepeat.max || perc < e.event.minMaxRepeat.min)
@@ -2677,7 +2669,7 @@ void SmartScript::ProcessEvent(SmartScriptHolder& e, Unit* unit, uint32 var0, ui
}
case SMART_EVENT_MANA_PCT:
{
- if (!me || !me->IsInCombat() || !me->GetMaxPower(POWER_MANA))
+ if (!me || !me->IsEngaged() || !me->GetMaxPower(POWER_MANA))
return;
uint32 perc = uint32(100.0f * me->GetPower(POWER_MANA) / me->GetMaxPower(POWER_MANA));
if (perc > e.event.minMaxRepeat.max || perc < e.event.minMaxRepeat.min)
@@ -2687,7 +2679,7 @@ void SmartScript::ProcessEvent(SmartScriptHolder& e, Unit* unit, uint32 var0, ui
}
case SMART_EVENT_TARGET_MANA_PCT:
{
- if (!me || !me->IsInCombat() || !me->GetVictim() || !me->EnsureVictim()->GetMaxPower(POWER_MANA))
+ if (!me || !me->IsEngaged() || !me->GetVictim() || !me->EnsureVictim()->GetMaxPower(POWER_MANA))
return;
uint32 perc = uint32(100.0f * me->EnsureVictim()->GetPower(POWER_MANA) / me->EnsureVictim()->GetMaxPower(POWER_MANA));
if (perc > e.event.minMaxRepeat.max || perc < e.event.minMaxRepeat.min)
@@ -2697,7 +2689,7 @@ void SmartScript::ProcessEvent(SmartScriptHolder& e, Unit* unit, uint32 var0, ui
}
case SMART_EVENT_RANGE:
{
- if (!me || !me->IsInCombat() || !me->GetVictim())
+ if (!me || !me->IsEngaged() || !me->GetVictim())
return;
if (me->IsInRange(me->GetVictim(), (float)e.event.minMaxRepeat.min, (float)e.event.minMaxRepeat.max))
@@ -2708,7 +2700,7 @@ void SmartScript::ProcessEvent(SmartScriptHolder& e, Unit* unit, uint32 var0, ui
}
case SMART_EVENT_VICTIM_CASTING:
{
- if (!me || !me->IsInCombat())
+ if (!me || !me->IsEngaged())
return;
Unit* victim = me->GetVictim();
@@ -2726,7 +2718,7 @@ void SmartScript::ProcessEvent(SmartScriptHolder& e, Unit* unit, uint32 var0, ui
}
case SMART_EVENT_FRIENDLY_HEALTH:
{
- if (!me || !me->IsInCombat())
+ if (!me || !me->IsEngaged())
return;
Unit* target = DoSelectLowestHpFriendly((float)e.event.friendlyHealth.radius, e.event.friendlyHealth.hpDeficit);
@@ -2742,7 +2734,7 @@ void SmartScript::ProcessEvent(SmartScriptHolder& e, Unit* unit, uint32 var0, ui
}
case SMART_EVENT_FRIENDLY_IS_CC:
{
- if (!me || !me->IsInCombat())
+ if (!me || !me->IsEngaged())
return;
std::vector<Creature*> creatures;
@@ -2865,7 +2857,7 @@ void SmartScript::ProcessEvent(SmartScriptHolder& e, Unit* unit, uint32 var0, ui
}
case SMART_EVENT_OOC_LOS:
{
- if (!me || me->IsInCombat())
+ if (!me || me->IsEngaged())
return;
//can trigger if closer than fMaxAllowedRange
float range = (float)e.event.los.maxDist;
@@ -2885,7 +2877,7 @@ void SmartScript::ProcessEvent(SmartScriptHolder& e, Unit* unit, uint32 var0, ui
}
case SMART_EVENT_IC_LOS:
{
- if (!me || !me->IsInCombat())
+ if (!me || !me->IsEngaged())
return;
//can trigger if closer than fMaxAllowedRange
float range = (float)e.event.los.maxDist;
@@ -3075,7 +3067,7 @@ void SmartScript::ProcessEvent(SmartScriptHolder& e, Unit* unit, uint32 var0, ui
}
case SMART_EVENT_FRIENDLY_HEALTH_PCT:
{
- if (!me || !me->IsInCombat())
+ if (!me || !me->IsEngaged())
return;
ObjectVector targets;
@@ -3220,10 +3212,10 @@ void SmartScript::UpdateTimer(SmartScriptHolder& e, uint32 const diff)
if (e.event.event_phase_mask && !IsInPhase(e.event.event_phase_mask))
return;
- if (e.GetEventType() == SMART_EVENT_UPDATE_IC && (!me || !me->IsInCombat()))
+ if (e.GetEventType() == SMART_EVENT_UPDATE_IC && (!me || !me->IsEngaged()))
return;
- if (e.GetEventType() == SMART_EVENT_UPDATE_OOC && (me && me->IsInCombat())) //can be used with me=nullptr (go script)
+ if (e.GetEventType() == SMART_EVENT_UPDATE_OOC && (me && me->IsEngaged())) //can be used with me=nullptr (go script)
return;
if (e.timer < diff)
@@ -3531,7 +3523,7 @@ void SmartScript::OnMoveInLineOfSight(Unit* who)
if (!me)
return;
- ProcessEventsFor(me->IsInCombat() ? SMART_EVENT_IC_LOS : SMART_EVENT_OOC_LOS, who);
+ ProcessEventsFor(me->IsEngaged() ? SMART_EVENT_IC_LOS : SMART_EVENT_OOC_LOS, who);
}
// SmartScript end
diff --git a/src/server/game/Combat/ThreatManager.cpp b/src/server/game/Combat/ThreatManager.cpp
index 005e8fad2a8..5a1207b82ca 100644
--- a/src/server/game/Combat/ThreatManager.cpp
+++ b/src/server/game/Combat/ThreatManager.cpp
@@ -130,6 +130,9 @@ void HostileReference::fireStatusChanged(ThreatRefStatusChangeEvent& threatRefSt
GetSource()->processThreatEvent(&threatRefStatusChangeEvent);
}
+// -- compatibility layer for combat rewrite (PR #19930)
+Unit* HostileReference::GetOwner() const { return GetSource()->GetOwner(); }
+
//============================================================
void HostileReference::addThreat(float modThreat)
@@ -263,7 +266,7 @@ void ThreatContainer::clearReferences()
//============================================================
// Return the HostileReference of NULL, if not found
-HostileReference* ThreatContainer::getReferenceByTarget(Unit* victim) const
+HostileReference* ThreatContainer::getReferenceByTarget(Unit const* victim) const
{
if (!victim)
return nullptr;
@@ -292,7 +295,7 @@ HostileReference* ThreatContainer::addThreat(Unit* victim, float threat)
//============================================================
-void ThreatContainer::modifyThreatPercent(Unit* victim, int32 percent)
+void ThreatContainer::ModifyThreatByPercent(Unit* victim, int32 percent)
{
if (HostileReference* ref = getReferenceByTarget(victim))
ref->addThreatPercent(percent);
@@ -391,6 +394,30 @@ HostileReference* ThreatContainer::selectNextVictim(Creature* attacker, HostileR
ThreatManager::ThreatManager(Unit* owner) : iCurrentVictim(nullptr), iOwner(owner), iUpdateTimer(THREAT_UPDATE_INTERVAL) { }
+// -- compatibility layer for combat rewrite (PR #19930)
+void ThreatManager::ForwardThreatForAssistingMe(Unit* victim, float amount, SpellInfo const* spell, bool ignoreModifiers, bool ignoreRedirection)
+{
+ (void)ignoreModifiers; (void)ignoreRedirection;
+ GetOwner()->getHostileRefManager().threatAssist(victim, amount, spell);
+}
+
+void ThreatManager::AddThreat(Unit* victim, float amount, SpellInfo const* spell, bool ignoreModifiers, bool ignoreRedirection)
+{
+ (void)ignoreModifiers; (void)ignoreRedirection;
+ if (!iOwner->CanHaveThreatList() || iOwner->HasUnitState(UNIT_STATE_EVADE))
+ return;
+ iOwner->SetInCombatWith(victim);
+ victim->SetInCombatWith(iOwner);
+ addThreat(victim, amount, spell ? spell->GetSchoolMask() : victim->GetMeleeDamageSchoolMask(), spell);
+}
+
+void ThreatManager::ClearAllThreat()
+{
+ if (iOwner->CanHaveThreatList(true) && !isThreatListEmpty())
+ iOwner->SendClearThreatListOpcode();
+ clearReferences();
+}
+
//============================================================
void ThreatManager::clearReferences()
@@ -453,9 +480,9 @@ void ThreatManager::_addThreat(Unit* victim, float threat)
//============================================================
-void ThreatManager::modifyThreatPercent(Unit* victim, int32 percent)
+void ThreatManager::ModifyThreatByPercent(Unit* victim, int32 percent)
{
- iThreatContainer.modifyThreatPercent(victim, percent);
+ iThreatContainer.ModifyThreatByPercent(victim, percent);
}
//============================================================
diff --git a/src/server/game/Combat/ThreatManager.h b/src/server/game/Combat/ThreatManager.h
index c500663214e..fcc0f82a220 100644
--- a/src/server/game/Combat/ThreatManager.h
+++ b/src/server/game/Combat/ThreatManager.h
@@ -24,6 +24,7 @@
#include "LinkedReference/Reference.h"
#include "UnitEvents.h"
#include "ObjectGuid.h"
+#include "Containers.h"
#include <list>
@@ -51,6 +52,19 @@ class TC_GAME_API HostileReference : public Reference<Unit, ThreatManager>
public:
HostileReference(Unit* refUnit, ThreatManager* threatManager, float threat);
+ // -- compatibility layer for combat rewrite (PR #19930)
+ Unit* GetOwner() const;
+ Unit* GetVictim() const { return getTarget(); }
+ void AddThreat(float amt) { addThreat(amt); }
+ void SetThreat(float amt) { setThreat(amt); }
+ void ModifyThreatByPercent(int32 pct) { addThreatPercent(pct); }
+ void ScaleThreat(float factor) { setThreat(iThreat*factor); }
+ bool IsOnline() const { return iOnline; }
+ bool IsAvailable() const { return iOnline; }
+ bool IsOffline() const { return !iOnline; }
+ float GetThreat() const { return getThreat(); }
+ void ClearThreat() { removeReference(); }
+
//=================================================
void addThreat(float modThreat);
@@ -157,7 +171,7 @@ class TC_GAME_API ThreatContainer
HostileReference* addThreat(Unit* victim, float threat);
- void modifyThreatPercent(Unit* victim, int32 percent);
+ void ModifyThreatByPercent(Unit* victim, int32 percent);
HostileReference* selectNextVictim(Creature* attacker, HostileReference* currentVictim) const;
@@ -175,7 +189,7 @@ class TC_GAME_API ThreatContainer
return iThreatList.empty() ? nullptr : iThreatList.front();
}
- HostileReference* getReferenceByTarget(Unit* victim) const;
+ HostileReference* getReferenceByTarget(Unit const* victim) const;
StorageType const & getThreatList() const { return iThreatList; }
@@ -201,9 +215,31 @@ class TC_GAME_API ThreatContainer
//=================================================
+typedef HostileReference ThreatReference;
class TC_GAME_API ThreatManager
{
public:
+ // -- compatibility layer for combat rewrite (PR #19930)
+ Trinity::Containers::IteratorPair<std::list<ThreatReference*>::const_iterator> GetSortedThreatList() const { auto& list = iThreatContainer.getThreatList(); return { list.cbegin(), list.cend() }; }
+ Trinity::Containers::IteratorPair<std::list<ThreatReference*>::const_iterator> GetUnsortedThreatList() const { return GetSortedThreatList(); }
+ Unit* SelectVictim() { return getHostilTarget(); }
+ Unit* GetCurrentVictim() const { if (ThreatReference* ref = getCurrentVictim()) return ref->GetVictim(); else return nullptr; }
+ bool IsThreatListEmpty(bool includeOffline = false) const { return includeOffline ? areThreatListsEmpty() : isThreatListEmpty(); }
+ bool IsThreatenedBy(Unit const* who, bool includeOffline = false) const { return (FindReference(who, includeOffline) != nullptr); }
+ size_t GetThreatListSize() const { return iThreatContainer.iThreatList.size(); }
+ void ForwardThreatForAssistingMe(Unit* victim, float amount, SpellInfo const* spell, bool ignoreModifiers = false, bool ignoreRedirection = false);
+ Unit* GetAnyTarget() const { auto const& list = getThreatList(); if (!list.empty()) return list.front()->getTarget(); return nullptr; }
+ void ResetThreat(Unit const* who) { if (auto* ref = FindReference(who, true)) ref->setThreat(0.0f); }
+ void ResetAllThreat() { resetAllAggro(); }
+ float GetThreat(Unit const* who, bool includeOffline = false) const { if (auto* ref = FindReference(who, includeOffline)) return ref->GetThreat(); return 0.0f; }
+ void ClearThreat(Unit const* who) { if (auto* ref = FindReference(who, true)) ref->removeReference(); }
+ void ClearAllThreat();
+ void AddThreat(Unit* victim, float amount, SpellInfo const* spell = nullptr, bool ignoreModifiers = false, bool ignoreRedirection = false);
+ private:
+ HostileReference* FindReference(Unit const* who, bool includeOffline) const { if (auto* ref = iThreatContainer.getReferenceByTarget(who)) return ref; if (includeOffline) if (auto* ref = iThreatOfflineContainer.getReferenceByTarget(who)) return ref; return nullptr; }
+
+ public:
+
friend class HostileReference;
explicit ThreatManager(Unit* owner);
@@ -216,7 +252,7 @@ class TC_GAME_API ThreatManager
void doAddThreat(Unit* victim, float threat);
- void modifyThreatPercent(Unit* victim, int32 percent);
+ void ModifyThreatByPercent(Unit* victim, int32 percent);
float getThreat(Unit* victim, bool alsoSearchOfflineList = false);
diff --git a/src/server/game/Combat/UnitEvents.h b/src/server/game/Combat/UnitEvents.h
index 35b7e7ecb5f..5e7a00f049d 100644
--- a/src/server/game/Combat/UnitEvents.h
+++ b/src/server/game/Combat/UnitEvents.h
@@ -116,7 +116,7 @@ class TC_GAME_API ThreatRefStatusChangeEvent : public UnitBaseEvent
void setThreatManager(ThreatManager* pThreatManager) { iThreatManager = pThreatManager; }
- ThreatManager* getThreatManager() const { return iThreatManager; }
+ ThreatManager* GetThreatManager() const { return iThreatManager; }
};
#endif
diff --git a/src/server/game/Entities/Creature/Creature.cpp b/src/server/game/Entities/Creature/Creature.cpp
index ef17eaa07bd..3b274c3b67f 100644
--- a/src/server/game/Entities/Creature/Creature.cpp
+++ b/src/server/game/Entities/Creature/Creature.cpp
@@ -215,9 +215,7 @@ bool AssistDelayEvent::Execute(uint64 /*e_time*/, uint32 /*p_time*/)
if (assistant && assistant->CanAssistTo(&m_owner, victim))
{
assistant->SetNoCallAssistance(true);
- assistant->CombatStart(victim);
- if (assistant->IsAIEnabled)
- assistant->AI()->AttackStart(victim);
+ assistant->EngageWithTarget(victim);
}
}
}
@@ -682,7 +680,7 @@ void Creature::Update(uint32 diff)
}
// periodic check to see if the creature has passed an evade boundary
- if (IsAIEnabled && !IsInEvadeMode() && IsInCombat())
+ if (IsAIEnabled && !IsInEvadeMode() && IsEngaged())
{
if (diff >= m_boundaryCheckTime)
{
@@ -693,7 +691,7 @@ void Creature::Update(uint32 diff)
}
// 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;
@@ -712,12 +710,7 @@ void Creature::Update(uint32 diff)
continue;
if (player->IsAlive() && IsHostileTo(player))
- {
- if (CanHaveThreatList())
- AddThreat(player, 0.0f);
- SetInCombatWith(player);
- player->SetInCombatWith(this);
- }
+ EngageWithTarget(player);
}
}
@@ -1662,7 +1655,7 @@ bool Creature::CanStartAttack(Unit const* who, bool force) const
if (!_IsTargetAcceptable(who))
return false;
- if (who->IsInCombat() && IsWithinDist(who, ATTACK_DISTANCE))
+ if (who->IsEngaged() && IsWithinDist(who, ATTACK_DISTANCE))
if (Unit* victim = who->getAttackerForHelper())
if (IsWithinDistInMap(victim, sWorld->getFloatConfig(CONFIG_CREATURE_FAMILY_ASSISTANCE_RADIUS)))
force = true;
@@ -1803,8 +1796,6 @@ void Creature::setDeathState(DeathState s)
SetUInt32Value(UNIT_FIELD_FLAGS, unit_flags);
SetUInt32Value(UNIT_DYNAMIC_FLAGS, dynamicflags);
- RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_IN_COMBAT);
-
SetMeleeDamageSchool(SpellSchools(cinfo->dmgschool));
if (creatureData && GetPhaseMask() != creatureData->phaseMask)
@@ -2193,7 +2184,7 @@ bool Creature::CanAssistTo(Unit const* u, Unit const* enemy, bool checkfaction /
return false;
// skip fighting creature
- if (IsInCombat())
+ if (IsEngaged())
return false;
// only free creature
@@ -2243,7 +2234,7 @@ bool Creature::_IsTargetAcceptable(Unit const* target) const
Unit const* 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
@@ -2441,11 +2432,7 @@ void Creature::SetInCombatWithZone()
continue;
if (player->IsAlive())
- {
- SetInCombatWith(player);
- player->SetInCombatWith(this);
- AddThreat(player, 0.0f);
- }
+ EngageWithTarget(player);
}
}
}
diff --git a/src/server/game/Entities/Creature/CreatureGroups.cpp b/src/server/game/Entities/Creature/CreatureGroups.cpp
index f534db8759c..b7de0c874fa 100644
--- a/src/server/game/Entities/Creature/CreatureGroups.cpp
+++ b/src/server/game/Entities/Creature/CreatureGroups.cpp
@@ -173,7 +173,7 @@ void CreatureGroup::RemoveMember(Creature* member)
member->SetFormation(nullptr);
}
-void CreatureGroup::MemberAttackStart(Creature* member, Unit* target)
+void CreatureGroup::MemberEngagingTarget(Creature* member, Unit* target)
{
uint8 groupAI = sFormationMgr->CreatureGroupMap[member->GetSpawnId()]->groupAI;
if (!groupAI)
@@ -181,11 +181,7 @@ void CreatureGroup::MemberAttackStart(Creature* member, Unit* target)
for (CreatureGroupMemberType::iterator itr = m_members.begin(); itr != m_members.end(); ++itr)
{
- if (m_leader) // avoid crash if leader was killed and reset.
- TC_LOG_DEBUG("entities.unit", "GROUP ATTACK: group instance id %u calls member instid %u", m_leader->GetInstanceId(), member->GetInstanceId());
-
Creature* other = itr->first;
-
// Skip self
if (other == member)
continue;
@@ -193,11 +189,8 @@ void CreatureGroup::MemberAttackStart(Creature* member, Unit* target)
if (!other->IsAlive())
continue;
- if (other->GetVictim())
- continue;
-
if (((other != m_leader && groupAI & FLAG_AGGRO_ON_AGGRO) || (other == m_leader && groupAI & FLAG_TO_AGGRO_ON_AGGRO)) && other->IsValidAttackTarget(target))
- other->AI()->AttackStart(target);
+ other->EngageWithTarget(target);
}
}
diff --git a/src/server/game/Entities/Creature/CreatureGroups.h b/src/server/game/Entities/Creature/CreatureGroups.h
index f0ce2677dc8..3f8e466fca5 100644
--- a/src/server/game/Entities/Creature/CreatureGroups.h
+++ b/src/server/game/Entities/Creature/CreatureGroups.h
@@ -89,7 +89,7 @@ class TC_GAME_API CreatureGroup
void FormationReset(bool dismiss);
void LeaderMoveTo(Position const& destination, uint32 id = 0, uint32 moveType = 0, bool orientation = false);
- void MemberAttackStart(Creature* member, Unit* target);
+ void MemberEngagingTarget(Creature* member, Unit* target);
};
#define sFormationMgr FormationMgr::instance()
diff --git a/src/server/game/Entities/Object/Object.cpp b/src/server/game/Entities/Object/Object.cpp
index ba48a64d719..508a89a86c8 100644
--- a/src/server/game/Entities/Object/Object.cpp
+++ b/src/server/game/Entities/Object/Object.cpp
@@ -1000,7 +1000,7 @@ void MovementInfo::OutDebug()
WorldObject::WorldObject(bool isWorldObject) : WorldLocation(), LastUsedScriptID(0),
m_name(""), m_isActive(false), m_isWorldObject(isWorldObject), m_zoneScript(nullptr),
m_transport(nullptr), m_zoneId(0), m_areaId(0), m_staticFloorZ(VMAP_INVALID_HEIGHT), m_currMap(nullptr), m_InstanceId(0),
-m_phaseMask(PHASEMASK_NORMAL), m_notifyflags(0), m_executed_notifies(0)
+m_phaseMask(PHASEMASK_NORMAL), m_notifyflags(0)
{
m_serverSideVisibility.SetValue(SERVERSIDE_VISIBILITY_GHOST, GHOST_VISIBILITY_ALIVE | GHOST_VISIBILITY_GHOST);
m_serverSideVisibilityDetect.SetValue(SERVERSIDE_VISIBILITY_GHOST, GHOST_VISIBILITY_ALIVE);
diff --git a/src/server/game/Entities/Object/Object.h b/src/server/game/Entities/Object/Object.h
index f5ff5100b78..ca1b547f68b 100644
--- a/src/server/game/Entities/Object/Object.h
+++ b/src/server/game/Entities/Object/Object.h
@@ -410,9 +410,7 @@ class TC_GAME_API WorldObject : public Object, public WorldLocation
void AddToNotify(uint16 f) { m_notifyflags |= f;}
bool isNeedNotify(uint16 f) const { return (m_notifyflags & f) != 0; }
uint16 GetNotifyFlags() const { return m_notifyflags; }
- bool NotifyExecuted(uint16 f) const { return (m_executed_notifies & f) != 0; }
- void SetNotified(uint16 f) { m_executed_notifies |= f;}
- void ResetAllNotifies() { m_notifyflags = 0; m_executed_notifies = 0; }
+ void ResetAllNotifies() { m_notifyflags = 0; }
bool isActiveObject() const { return m_isActive; }
void setActive(bool isActiveObject);
@@ -476,7 +474,6 @@ class TC_GAME_API WorldObject : public Object, public WorldLocation
uint32 m_phaseMask; // in area phase state
uint16 m_notifyflags;
- uint16 m_executed_notifies;
virtual bool _IsWithinDist(WorldObject const* obj, float dist2compare, bool is3D, bool incOwnRadius = true, bool incTargetRadius = true) const;
bool CanNeverSee(WorldObject const* obj) const;
diff --git a/src/server/game/Entities/Player/Player.cpp b/src/server/game/Entities/Player/Player.cpp
index b4d93f01d09..33ce50c1053 100644
--- a/src/server/game/Entities/Player/Player.cpp
+++ b/src/server/game/Entities/Player/Player.cpp
@@ -20285,14 +20285,38 @@ void Player::UpdateAfkReport(time_t currTime)
}
}
+void Player::SetContestedPvP(Player* attackedPlayer)
+{
+ if (attackedPlayer && (attackedPlayer == this || (duel && duel->opponent == attackedPlayer)))
+ return;
+
+ SetContestedPvPTimer(30000);
+ if (!HasUnitState(UNIT_STATE_ATTACK_PLAYER))
+ {
+ AddUnitState(UNIT_STATE_ATTACK_PLAYER);
+ SetFlag(PLAYER_FLAGS, PLAYER_FLAGS_CONTESTED_PVP);
+ // call MoveInLineOfSight for nearby contested guards
+ Trinity::AIRelocationNotifier notifier(*this);
+ Cell::VisitWorldObjects(this, notifier, GetVisibilityRange());
+ }
+ for (Unit* unit : m_Controlled)
+ {
+ if (!unit->HasUnitState(UNIT_STATE_ATTACK_PLAYER))
+ {
+ unit->AddUnitState(UNIT_STATE_ATTACK_PLAYER);
+ Trinity::AIRelocationNotifier notifier(*unit);
+ Cell::VisitWorldObjects(this, notifier, GetVisibilityRange());
+ }
+ }
+}
+
void Player::UpdateContestedPvP(uint32 diff)
{
- if (!m_contestedPvPTimer||IsInCombat())
+ if (!m_contestedPvPTimer || IsInCombat())
return;
+
if (m_contestedPvPTimer <= diff)
- {
ResetContestedPvP();
- }
else
m_contestedPvPTimer -= diff;
}
diff --git a/src/server/game/Entities/Player/Player.h b/src/server/game/Entities/Player/Player.h
index 210909db73f..4a2a82000b4 100644
--- a/src/server/game/Entities/Player/Player.h
+++ b/src/server/game/Entities/Player/Player.h
@@ -1502,6 +1502,7 @@ class TC_GAME_API Player : public Unit, public GridObject<Player>
void UpdateAfkReport(time_t currTime);
void UpdatePvPFlag(time_t currTime);
+ void SetContestedPvP(Player* attackedPlayer = nullptr);
void UpdateContestedPvP(uint32 currTime);
void SetContestedPvPTimer(uint32 newTime) {m_contestedPvPTimer = newTime;}
void ResetContestedPvP();
diff --git a/src/server/game/Entities/Unit/Unit.cpp b/src/server/game/Entities/Unit/Unit.cpp
index bb43c34182a..daa9d5bdefa 100644
--- a/src/server/game/Entities/Unit/Unit.cpp
+++ b/src/server/game/Entities/Unit/Unit.cpp
@@ -445,7 +445,7 @@ void Unit::Update(uint32 p_time)
// Having this would prevent spells from being proced, so let's crash
ASSERT(!m_procDeep);
- if (CanHaveThreatList() && getThreatManager().isNeedUpdateToClient(p_time))
+ if (CanHaveThreatList() && GetThreatManager().isNeedUpdateToClient(p_time))
SendThreatListUpdate();
// update combat timer only for players and pets (only pets with PetAI)
@@ -880,7 +880,7 @@ uint32 Unit::DealDamage(Unit* victim, uint32 damage, CleanDamage const* cleanDam
if (damagetype != DOT && damage > 0 && !victim->GetOwnerGUID().IsPlayer() && (!spellProto || !spellProto->HasAura(SPELL_AURA_DAMAGE_SHIELD)))
victim->ToCreature()->SetLastDamagedTime(GameTime::GetGameTime() + MAX_AGGRO_RESET_TIME);
- victim->AddThreat(this, float(damage), damageSchoolMask, spellProto);
+ victim->GetThreatManager().AddThreat(this, float(damage), spellProto);
}
else // victim is a player
{
@@ -5763,6 +5763,9 @@ void Unit::_removeAttacker(Unit* pAttacker)
Unit* Unit::getAttackerForHelper() const // If someone wants to help, who to give them
{
+ if (!IsEngaged())
+ return nullptr;
+
if (Unit* victim = GetVictim())
if ((!IsPet() && !GetPlayerMovingMe()) || IsInCombatWith(victim) || victim->IsInCombatWith(this))
return victim;
@@ -5866,14 +5869,14 @@ bool Unit::Attack(Unit* victim, bool meleeAttack)
if (creature && !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->GetTypeId() == TYPEID_PLAYER)
victim->SetInCombatWith(this);
if (Unit* owner = victim->GetOwner())
{
- AddThreat(owner, 0.0f);
+ GetThreatManager().AddThreat(owner, 0.0f);
SetInCombatWith(owner);
if (owner->GetTypeId() == TYPEID_PLAYER)
owner->SetInCombatWith(this);
@@ -6768,12 +6771,16 @@ void Unit::SendEnergizeSpellLog(Unit* victim, uint32 spellId, int32 damage, Powe
void Unit::EnergizeBySpell(Unit* victim, uint32 spellId, int32 damage, Powers powerType)
{
- SendEnergizeSpellLog(victim, spellId, damage, powerType);
+ if (SpellInfo const* info = sSpellMgr->GetSpellInfo(spellId))
+ EnergizeBySpell(victim, info, damage, powerType);
+}
+
+void Unit::EnergizeBySpell(Unit* victim, SpellInfo const* spellInfo, int32 damage, Powers powerType)
+{
+ SendEnergizeSpellLog(victim, spellInfo->Id, damage, powerType);
// needs to be called after sending spell log
victim->ModifyPower(powerType, damage);
-
- SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(spellId);
- victim->getHostileRefManager().threatAssist(this, float(damage) * 0.5f, spellInfo);
+ victim->GetThreatManager().ForwardThreatForAssistingMe(this, float(damage)/2, spellInfo, true);
}
uint32 Unit::SpellDamageBonusDone(Unit* victim, SpellInfo const* spellProto, uint32 pdamage, DamageEffectType damagetype, uint32 stack) const
@@ -8690,11 +8697,11 @@ void Unit::CombatStart(Unit* target, bool initialAggro)
SetInCombatWith(target);
target->SetInCombatWith(this);
}
- Unit* who = target->GetCharmerOrOwnerOrSelf();
- if (who->GetTypeId() == TYPEID_PLAYER)
- SetContestedPvP(who->ToPlayer());
Player* me = GetCharmerOrOwnerPlayerOrPlayerItself();
+ Unit* who = target->GetCharmerOrOwnerOrSelf();
+ if (me && who->GetTypeId() == TYPEID_PLAYER)
+ me->SetContestedPvP(who->ToPlayer());
if (me && who->IsPvP()
&& (who->GetTypeId() != TYPEID_PLAYER
|| !me->duel || me->duel->opponent != who))
@@ -8732,7 +8739,7 @@ void Unit::SetInCombatState(bool PvP, Unit* enemy)
creature->AI()->EnterCombat(enemy);
if (creature->GetFormation())
- creature->GetFormation()->MemberAttackStart(creature, enemy);
+ creature->GetFormation()->MemberEngagingTarget(creature, enemy);
}
if (IsPet())
@@ -9419,7 +9426,7 @@ void Unit::setDeathState(DeathState s)
if (s != ALIVE && s != JUST_RESPAWNED)
{
CombatStop();
- DeleteThreatList();
+ GetThreatManager().ClearAllThreat();
getHostileRefManager().deleteReferences();
ClearComboPointHolders(); // any combo points pointed to unit lost at it death
@@ -9489,7 +9496,7 @@ bool Unit::CanHaveThreatList(bool skipAliveCheck) const
// return false;
// summons can not have a threat list, unless they are controlled by a creature
- if (HasUnitTypeMask(UNIT_MASK_MINION | UNIT_MASK_GUARDIAN | UNIT_MASK_CONTROLABLE_GUARDIAN) && ((Pet*)this)->GetOwnerGUID().IsPlayer())
+ if (HasUnitTypeMask(UNIT_MASK_MINION | UNIT_MASK_GUARDIAN | UNIT_MASK_CONTROLABLE_GUARDIAN) && GetOwnerGUID().IsPlayer())
return false;
return true;
@@ -9509,24 +9516,6 @@ float Unit::ApplyTotalThreatModifier(float fThreat, SpellSchoolMask schoolMask)
//======================================================================
-void Unit::AddThreat(Unit* victim, float fThreat, SpellSchoolMask schoolMask, SpellInfo const* threatSpell)
-{
- // Only mobs can manage threat lists
- if (CanHaveThreatList() && !HasUnitState(UNIT_STATE_EVADE))
- m_ThreatManager.addThreat(victim, fThreat, schoolMask, threatSpell);
-}
-
-//======================================================================
-
-void Unit::DeleteThreatList()
-{
- if (CanHaveThreatList(true) && !m_ThreatManager.isThreatListEmpty())
- SendClearThreatListOpcode();
- m_ThreatManager.clearReferences();
-}
-
-//======================================================================
-
void Unit::TauntApply(Unit* taunter)
{
ASSERT(GetTypeId() == TYPEID_UNIT);
@@ -10678,7 +10667,7 @@ void Unit::CleanupBeforeRemoveFromMap(bool finalCleanup)
CombatStop();
ClearComboPoints();
ClearComboPointHolders();
- DeleteThreatList();
+ GetThreatManager().ClearAllThreat();
getHostileRefManager().deleteReferences();
}
@@ -11338,8 +11327,7 @@ Player* Unit::GetSpellModOwner() const
if (HasUnitTypeMask(UNIT_MASK_PET | UNIT_MASK_TOTEM | UNIT_MASK_GUARDIAN))
{
if (Unit* owner = GetOwner())
- if (Player* player = owner->ToPlayer())
- return player;
+ return owner->ToPlayer();
}
return nullptr;
}
@@ -11904,29 +11892,6 @@ bool Unit::IsUnderLastManaUseEffect() const
return getMSTimeDiff(m_lastManaUse, GameTime::GetGameTimeMS()) < 5000;
}
-void Unit::SetContestedPvP(Player* attackedPlayer)
-{
- Player* player = GetCharmerOrOwnerPlayerOrPlayerItself();
-
- if (!player || (attackedPlayer && (attackedPlayer == player || (player->duel && player->duel->opponent == attackedPlayer))))
- return;
-
- player->SetContestedPvPTimer(30000);
- if (!player->HasUnitState(UNIT_STATE_ATTACK_PLAYER))
- {
- player->AddUnitState(UNIT_STATE_ATTACK_PLAYER);
- player->SetFlag(PLAYER_FLAGS, PLAYER_FLAGS_CONTESTED_PVP);
- // call MoveInLineOfSight for nearby contested guards
- UpdateObjectVisibility();
- }
- if (!HasUnitState(UNIT_STATE_ATTACK_PLAYER))
- {
- AddUnitState(UNIT_STATE_ATTACK_PLAYER);
- // call MoveInLineOfSight for nearby contested guards
- UpdateObjectVisibility();
- }
-}
-
Pet* Unit::CreateTamedPetFrom(Creature* creatureTarget, uint32 spell_id)
{
if (GetTypeId() != TYPEID_PLAYER)
@@ -12184,7 +12149,7 @@ void Unit::Kill(Unit* victim, bool durabilityLoss)
if (!creature->IsPet())
{
- creature->DeleteThreatList();
+ creature->GetThreatManager().ClearAllThreat();
// must be after setDeathState which resets dynamic flags
if (!creature->loot.isLooted())
@@ -12576,7 +12541,7 @@ bool Unit::SetCharmedBy(Unit* charmer, CharmType type, AuraApplication const* au
CastStop();
CombatStop(); /// @todo CombatStop(true) may cause crash (interrupt spells)
- DeleteThreatList();
+ GetThreatManager().ClearAllThreat();
Player* playerCharmer = charmer->ToPlayer();
@@ -12719,7 +12684,7 @@ void Unit::RemoveCharmedBy(Unit* charmer)
CastStop();
CombatStop(); /// @todo CombatStop(true) may cause crash (interrupt spells)
getHostileRefManager().deleteReferences();
- DeleteThreatList();
+ GetThreatManager().ClearAllThreat();
if (_oldFactionId)
{
@@ -13177,8 +13142,8 @@ void Unit::SetPhaseMask(uint32 newPhaseMask, bool update)
// modify threat lists for new phasemask
if (GetTypeId() != TYPEID_PLAYER)
{
- std::list<HostileReference*> threatList = getThreatManager().getThreatList();
- std::list<HostileReference*> offlineThreatList = getThreatManager().getOfflineThreatList();
+ std::list<HostileReference*> threatList = GetThreatManager().getThreatList();
+ std::list<HostileReference*> offlineThreatList = GetThreatManager().getOfflineThreatList();
// merge expects sorted lists
threatList.sort();
@@ -14027,15 +13992,15 @@ void Unit::UpdateHeight(float newZ)
void Unit::SendThreatListUpdate()
{
- if (!getThreatManager().isThreatListEmpty())
+ if (!GetThreatManager().isThreatListEmpty())
{
- uint32 count = getThreatManager().getThreatList().size();
+ uint32 count = GetThreatManager().getThreatList().size();
//TC_LOG_DEBUG("entities.unit", "WORLD: Send SMSG_THREAT_UPDATE Message");
WorldPacket data(SMSG_THREAT_UPDATE, 8 + count * 8);
data << GetPackGUID();
data << uint32(count);
- ThreatContainer::StorageType const& tlist = getThreatManager().getThreatList();
+ ThreatContainer::StorageType const& tlist = GetThreatManager().getThreatList();
for (ThreatContainer::StorageType::const_iterator itr = tlist.begin(); itr != tlist.end(); ++itr)
{
data << (*itr)->getUnitGuid().WriteAsPacked();
@@ -14047,16 +14012,16 @@ void Unit::SendThreatListUpdate()
void Unit::SendChangeCurrentVictimOpcode(HostileReference* pHostileReference)
{
- if (!getThreatManager().isThreatListEmpty())
+ if (!GetThreatManager().isThreatListEmpty())
{
- uint32 count = getThreatManager().getThreatList().size();
+ uint32 count = GetThreatManager().getThreatList().size();
TC_LOG_DEBUG("entities.unit", "WORLD: Send SMSG_HIGHEST_THREAT_UPDATE Message");
WorldPacket data(SMSG_HIGHEST_THREAT_UPDATE, 8 + 8 + count * 8);
data << GetPackGUID();
data << pHostileReference->getUnitGuid().WriteAsPacked();
data << uint32(count);
- ThreatContainer::StorageType const& tlist = getThreatManager().getThreatList();
+ ThreatContainer::StorageType const& tlist = GetThreatManager().getThreatList();
for (ThreatContainer::StorageType::const_iterator itr = tlist.begin(); itr != tlist.end(); ++itr)
{
data << (*itr)->getUnitGuid().WriteAsPacked();
diff --git a/src/server/game/Entities/Unit/Unit.h b/src/server/game/Entities/Unit/Unit.h
index 73ca8add151..1a32512a01b 100644
--- a/src/server/game/Entities/Unit/Unit.h
+++ b/src/server/game/Entities/Unit/Unit.h
@@ -1000,6 +1000,7 @@ class TC_GAME_API Unit : public WorldObject
bool IsWithinCombatRange(Unit const* obj, float dist2compare) const;
bool IsWithinMeleeRange(Unit const* obj) const;
float GetMeleeRange(Unit const* target) const;
+ virtual SpellSchoolMask GetMeleeDamageSchoolMask() const;
void GetRandomContactPoint(Unit const* target, float& x, float& y, float& z, float distance2dMin, float distance2dMax) const;
uint32 m_extraAttacks;
bool m_canDualWield;
@@ -1222,6 +1223,12 @@ class TC_GAME_API Unit : public WorldObject
bool IsInFlight() const { return HasUnitState(UNIT_STATE_IN_FLIGHT); }
+ bool IsEngaged() const { return IsInCombat(); }
+ bool IsEngagedBy(Unit const* who) const { return IsInCombatWith(who); }
+ void EngageWithTarget(Unit* who) { SetInCombatWith(who); who->SetInCombatWith(this); GetThreatManager().AddThreat(who, 0.0f); }
+ bool IsThreatened() const { return CanHaveThreatList() && !GetThreatManager().IsThreatListEmpty(); }
+ bool IsThreatenedBy(Unit const* who) const { return who && CanHaveThreatList() && GetThreatManager().IsThreatenedBy(who); }
+
bool IsInCombat() const { return HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_IN_COMBAT); }
bool IsPetInCombat() const { return HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_PET_IN_COMBAT); }
bool IsInCombatWith(Unit const* who) const;
@@ -1259,8 +1266,9 @@ class TC_GAME_API Unit : public WorldObject
void SendHealSpellLog(HealInfo& healInfo, bool critical = false);
int32 HealBySpell(HealInfo& healInfo, bool critical = false);
- void SendEnergizeSpellLog(Unit* victim, uint32 spellID, int32 damage, Powers powerType);
- void EnergizeBySpell(Unit* victim, uint32 SpellID, int32 Damage, Powers powertype);
+ void SendEnergizeSpellLog(Unit* victim, uint32 spellId, int32 damage, Powers powerType);
+ void EnergizeBySpell(Unit* victim, uint32 spellId, int32 damage, Powers powerType);
+ void EnergizeBySpell(Unit* victim, SpellInfo const* spellInfo, int32 damage, Powers powerType);
void CastSpell(SpellCastTargets const& targets, SpellInfo const* spellInfo, CustomSpellValues const* value, TriggerCastFlags triggerFlags = TRIGGERED_NONE, Item* castItem = nullptr, AuraEffect const* triggeredByAura = nullptr, ObjectGuid originalCaster = ObjectGuid::Empty);
void CastSpell(Unit* victim, uint32 spellId, bool triggered, Item* castItem = nullptr, AuraEffect const* triggeredByAura = nullptr, ObjectGuid originalCaster = ObjectGuid::Empty);
@@ -1661,12 +1669,11 @@ class TC_GAME_API Unit : public WorldObject
// Threat related methods
bool CanHaveThreatList(bool skipAliveCheck = false) const;
- void AddThreat(Unit* victim, float fThreat, SpellSchoolMask schoolMask = SPELL_SCHOOL_MASK_NORMAL, SpellInfo const* threatSpell = nullptr);
float ApplyTotalThreatModifier(float fThreat, SpellSchoolMask schoolMask = SPELL_SCHOOL_MASK_NORMAL);
- void DeleteThreatList();
void TauntApply(Unit* victim);
void TauntFadeOut(Unit* taunter);
- ThreatManager& getThreatManager() { return m_ThreatManager; }
+ ThreatManager& GetThreatManager() { return m_ThreatManager; }
+ ThreatManager const& GetThreatManager() const { return m_ThreatManager; }
void addHatedBy(HostileReference* pHostileReference) { m_HostileRefManager.insertFirst(pHostileReference); }
void removeHatedBy(HostileReference* /*pHostileReference*/) { /* nothing to do yet */ }
HostileRefManager& getHostileRefManager() { return m_HostileRefManager; }
@@ -1974,8 +1981,6 @@ class TC_GAME_API Unit : public WorldObject
CharmInfo* m_charmInfo;
SharedVisionList m_sharedVision;
- virtual SpellSchoolMask GetMeleeDamageSchoolMask() const;
-
MotionMaster* i_motionMaster;
uint32 m_reactiveTimer[MAX_REACTIVE];
diff --git a/src/server/game/Grids/ObjectGridLoader.cpp b/src/server/game/Grids/ObjectGridLoader.cpp
index 848df6f60dc..653c9d51d11 100644
--- a/src/server/game/Grids/ObjectGridLoader.cpp
+++ b/src/server/game/Grids/ObjectGridLoader.cpp
@@ -217,10 +217,10 @@ void ObjectGridStoper::Visit(CreatureMapType &m)
for (CreatureMapType::iterator iter = m.begin(); iter != m.end(); ++iter)
{
iter->GetSource()->RemoveAllDynObjects();
- if (iter->GetSource()->IsInCombat() || !iter->GetSource()->getThreatManager().areThreatListsEmpty())
+ if (iter->GetSource()->IsInCombat() || !iter->GetSource()->GetThreatManager().areThreatListsEmpty())
{
iter->GetSource()->CombatStop();
- iter->GetSource()->DeleteThreatList();
+ iter->GetSource()->GetThreatManager().ClearAllThreat();
iter->GetSource()->AI()->EnterEvadeMode();
}
}
diff --git a/src/server/game/Spells/Auras/SpellAuraEffects.cpp b/src/server/game/Spells/Auras/SpellAuraEffects.cpp
index eedbd4f90b9..86713a4efd6 100644
--- a/src/server/game/Spells/Auras/SpellAuraEffects.cpp
+++ b/src/server/game/Spells/Auras/SpellAuraEffects.cpp
@@ -39,6 +39,7 @@
#include "Spell.h"
#include "SpellHistory.h"
#include "SpellMgr.h"
+#include "ThreatManager.h"
#include "Unit.h"
#include "Util.h"
#include "Vehicle.h"
@@ -4371,7 +4372,7 @@ void AuraEffect::HandleAuraDummy(AuraApplication const* aurApp, uint8 mode, bool
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 && 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
@@ -5775,7 +5776,7 @@ void AuraEffect::HandlePeriodicHealthLeechAuraTick(Unit* target, Unit* caster) c
HealInfo 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, PROC_FLAG_DONE_PERIODIC, PROC_FLAG_TAKEN_PERIODIC, PROC_SPELL_TYPE_HEAL, PROC_SPELL_PHASE_NONE, hitMask, nullptr, nullptr, &healInfo);
}
}
@@ -5901,7 +5902,7 @@ void AuraEffect::HandlePeriodicHealAurasTick(Unit* target, Unit* caster) const
SpellPeriodicAuraLogInfo pInfo(this, heal, heal - healInfo.GetEffectiveHeal(), healInfo.GetAbsorb(), 0, 0.0f, crit);
target->SendPeriodicAuraLog(&pInfo);
- target->getHostileRefManager().threatAssist(caster, float(healInfo.GetEffectiveHeal()) * 0.5f, GetSpellInfo());
+ target->GetThreatManager().ForwardThreatForAssistingMe(caster, float(healInfo.GetEffectiveHeal())*0.5f, GetSpellInfo());
bool haveCastItem = !GetBase()->GetCastItemGUID().IsEmpty();
@@ -5984,7 +5985,8 @@ void AuraEffect::HandlePeriodicManaLeechAuraTick(Unit* target, Unit* caster) con
if (gainAmount)
{
gainedAmount = caster->ModifyPower(powerType, gainAmount);
- target->AddThreat(caster, float(gainedAmount) * 0.5f, GetSpellInfo()->GetSchoolMask(), GetSpellInfo());
+ // energize is not modified by threat modifiers
+ target->GetThreatManager().AddThreat(caster, float(gainedAmount) * 0.5f, GetSpellInfo(), true);
}
// Drain Mana
@@ -6035,7 +6037,7 @@ void AuraEffect::HandleObsModPowerAuraTick(Unit* target, Unit* caster) const
int32 gain = target->ModifyPower(powerType, amount);
if (caster)
- target->getHostileRefManager().threatAssist(caster, float(gain) * 0.5f, GetSpellInfo());
+ target->GetThreatManager().ForwardThreatForAssistingMe(caster, float(gain)*0.5f, GetSpellInfo(), true);
}
void AuraEffect::HandlePeriodicEnergizeAuraTick(Unit* target, Unit* caster) const
@@ -6070,7 +6072,7 @@ void AuraEffect::HandlePeriodicEnergizeAuraTick(Unit* target, Unit* caster) cons
int32 gain = target->ModifyPower(powerType, amount);
if (caster)
- target->getHostileRefManager().threatAssist(caster, float(gain) * 0.5f, GetSpellInfo());
+ target->GetThreatManager().ForwardThreatForAssistingMe(caster, float(gain)*0.5f, GetSpellInfo(), true);
}
void AuraEffect::HandlePeriodicPowerBurnAuraTick(Unit* target, Unit* caster) const
diff --git a/src/server/game/Spells/Spell.cpp b/src/server/game/Spells/Spell.cpp
index 8309bf00fd3..d9990dfb033 100644
--- a/src/server/game/Spells/Spell.cpp
+++ b/src/server/game/Spells/Spell.cpp
@@ -2385,7 +2385,7 @@ void Spell::DoAllEffectOnTarget(TargetInfo* target)
HealInfo healInfo(caster, unitTarget, addhealth, m_spellInfo, m_spellInfo->GetSchoolMask());
caster->HealBySpell(healInfo, crit);
- unitTarget->getHostileRefManager().threatAssist(caster, float(healInfo.GetEffectiveHeal()) * 0.5f, m_spellInfo);
+ unitTarget->GetThreatManager().ForwardThreatForAssistingMe(caster, float(healInfo.GetEffectiveHeal())*0.5f, m_spellInfo);
m_healing = healInfo.GetEffectiveHeal();
// Do triggers for unit
@@ -2454,8 +2454,7 @@ void Spell::DoAllEffectOnTarget(TargetInfo* target)
if (missInfo == SPELL_MISS_RESIST && m_spellInfo->HasAttribute(SPELL_ATTR0_CU_PICKPOCKET) && unitTarget->GetTypeId() == TYPEID_UNIT)
{
m_caster->RemoveAurasWithInterruptFlags(AURA_INTERRUPT_FLAG_TALK);
- if (unitTarget->ToCreature()->IsAIEnabled)
- unitTarget->ToCreature()->AI()->AttackStart(m_caster);
+ unitTarget->ToCreature()->EngageWithTarget(m_caster);
}
}
@@ -2558,14 +2557,16 @@ SpellMissInfo Spell::DoSpellHitOnUnit(Unit* unit, uint32 effectMask, bool scaleA
// assisting case, healing and resurrection
if (unit->HasUnitState(UNIT_STATE_ATTACK_PLAYER))
{
- m_caster->SetContestedPvP();
- if (m_caster->GetTypeId() == TYPEID_PLAYER)
- m_caster->ToPlayer()->UpdatePvP(true);
+ if (Player* playerOwner = m_caster->GetCharmerOrOwnerPlayerOrPlayerItself())
+ {
+ 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, nullptr, true);
}
}
}
@@ -4786,14 +4787,14 @@ void Spell::HandleThreatSpells()
// 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);
}
}
TC_LOG_DEBUG("spells", "Spell %u, added an additional %f threat for %s %u target(s)", m_spellInfo->Id, threat, m_spellInfo->IsPositive() ? "assisting" : "harming", uint32(m_UniqueTargetInfo.size()));
diff --git a/src/server/game/Spells/SpellEffects.cpp b/src/server/game/Spells/SpellEffects.cpp
index 2201e39059b..72a39a6073b 100644
--- a/src/server/game/Spells/SpellEffects.cpp
+++ b/src/server/game/Spells/SpellEffects.cpp
@@ -2476,7 +2476,7 @@ void Spell::EffectDistract(SpellEffIndex /*effIndex*/)
return;
// Check for possible target
- if (!unitTarget || unitTarget->IsInCombat())
+ if (!unitTarget || unitTarget->IsEngaged())
return;
// target must be OK to do this
@@ -3074,17 +3074,17 @@ void Spell::EffectTaunt(SpellEffIndex /*effIndex*/)
if (m_spellInfo->Id == 62124 && (!unitTarget->IsPet() || !unitTarget->GetOwnerGUID().IsPlayer()))
m_caster->CastSpell(unitTarget, 67485, true);
- if (!unitTarget->getThreatManager().getOnlineContainer().empty())
+ if (!unitTarget->GetThreatManager().getOnlineContainer().empty())
{
// Also use this effect to set the taunter's threat to the taunted creature's highest value
- float myThreat = unitTarget->getThreatManager().getThreat(m_caster);
- float topThreat = unitTarget->getThreatManager().getOnlineContainer().getMostHated()->getThreat();
+ float myThreat = unitTarget->GetThreatManager().getThreat(m_caster);
+ float topThreat = unitTarget->GetThreatManager().getOnlineContainer().getMostHated()->getThreat();
if (topThreat > myThreat)
- unitTarget->getThreatManager().doAddThreat(m_caster, topThreat - myThreat);
+ unitTarget->GetThreatManager().doAddThreat(m_caster, topThreat - myThreat);
//Set aggro victim to caster
- if (HostileReference* forcedVictim = unitTarget->getThreatManager().getOnlineContainer().getReferenceByTarget(m_caster))
- unitTarget->getThreatManager().setCurrentVictim(forcedVictim);
+ if (HostileReference* forcedVictim = unitTarget->GetThreatManager().getOnlineContainer().getReferenceByTarget(m_caster))
+ unitTarget->GetThreatManager().setCurrentVictim(forcedVictim);
}
if (unitTarget->ToCreature()->IsAIEnabled && !unitTarget->ToCreature()->HasReactState(REACT_PASSIVE))
@@ -3385,7 +3385,7 @@ void Spell::EffectThreat(SpellEffIndex /*effIndex*/)
if (!unitTarget->CanHaveThreatList())
return;
- unitTarget->AddThreat(m_caster, float(damage));
+ unitTarget->GetThreatManager().AddThreat(m_caster, float(damage));
}
void Spell::EffectHealMaxHealth(SpellEffIndex /*effIndex*/)
@@ -4990,7 +4990,7 @@ void Spell::EffectModifyThreatPercent(SpellEffIndex /*effIndex*/)
if (!unitTarget)
return;
- unitTarget->getThreatManager().modifyThreatPercent(m_caster, damage);
+ unitTarget->GetThreatManager().ModifyThreatByPercent(m_caster, damage);
}
void Spell::EffectTransmitted(SpellEffIndex effIndex)