diff --git a/src/server/game/Combat/CombatManager.cpp b/src/server/game/Combat/CombatManager.cpp index 9d35fb6992..aac44a396c 100644 --- a/src/server/game/Combat/CombatManager.cpp +++ b/src/server/game/Combat/CombatManager.cpp @@ -335,11 +335,15 @@ bool CombatManager::UpdateOwnerCombatState() const { _owner->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_IN_COMBAT); _owner->AtEnterCombat(); + if (!_owner->CanHaveThreatList() && !_owner->IsEngaged()) + _owner->AtEngage(GetAnyTarget()); } else { _owner->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_IN_COMBAT); _owner->AtExitCombat(); + if (_owner->IsEngaged() && !(_owner->ToCreature() && _owner->ToCreature()->IsAIEnabled())) + _owner->AtDisengage(); } if (Unit* master = _owner->GetCharmerOrOwner()) diff --git a/src/server/game/Combat/ThreatManager.cpp b/src/server/game/Combat/ThreatManager.cpp index 852708d4f9..42172e802d 100644 --- a/src/server/game/Combat/ThreatManager.cpp +++ b/src/server/game/Combat/ThreatManager.cpp @@ -166,7 +166,7 @@ void ThreatReference::UnregisterAndFree() return true; } -ThreatManager::ThreatManager(Unit* owner) : _owner(owner), _ownerCanHaveThreatList(false), _ownerEngaged(false), _needClientUpdate(false), _updateTimer(THREAT_UPDATE_INTERVAL), _currentVictimRef(nullptr), _fixateRef(nullptr) +ThreatManager::ThreatManager(Unit* owner) : _owner(owner), _ownerCanHaveThreatList(false), _needClientUpdate(false), _updateTimer(THREAT_UPDATE_INTERVAL), _currentVictimRef(nullptr), _fixateRef(nullptr) { for (int8 i = 0; i < MAX_SPELL_SCHOOL; ++i) _singleSchoolModifiers[i] = 1.0f; @@ -186,7 +186,7 @@ void ThreatManager::Initialize() void ThreatManager::Update(uint32 tdiff) { - if (!CanHaveThreatList() || !IsEngaged()) + if (!CanHaveThreatList() || IsThreatListEmpty()) return; if (_updateTimer <= tdiff) { @@ -398,11 +398,9 @@ void ThreatManager::AddThreat(Unit* target, float amount, SpellInfo const* spell if (ref->IsOnline()) // ...and if the ref is online it also gets the threat it should have ref->AddThreat(amount); - if (!_ownerEngaged) + if (!_owner->IsEngaged()) { - Creature* cOwner = ASSERT_NOTNULL(_owner->ToCreature()); // if we got here the owner can have a threat list, and must be a creature! - _ownerEngaged = true; - + _owner->AtEngage(target); UpdateVictim(); SaveCreatureHomePositionIfNeed(cOwner); @@ -486,14 +484,17 @@ void ThreatManager::ClearThreat(ThreatReference* ref) void ThreatManager::ClearAllThreat() { - _ownerEngaged = false; - if (_myThreatListEntries.empty()) - return; - - SendClearAllThreatToClients(); - do - _myThreatListEntries.begin()->second->UnregisterAndFree(); - while (!_myThreatListEntries.empty()); + if (!_myThreatListEntries.empty()) + { + SendClearAllThreatToClients(); + do + _myThreatListEntries.begin()->second->UnregisterAndFree(); + while (!_myThreatListEntries.empty()); + } + // note: i don't really like having this here + // (maybe engage flag should be in creature ai? it's inherently an AI property...) + if (_owner->IsEngaged()) + _owner->AtDisengage(); } void ThreatManager::FixateTarget(Unit* target) diff --git a/src/server/game/Combat/ThreatManager.h b/src/server/game/Combat/ThreatManager.h index 1060e41fe5..7d7fda27e9 100644 --- a/src/server/game/Combat/ThreatManager.h +++ b/src/server/game/Combat/ThreatManager.h @@ -43,11 +43,6 @@ class SpellInfo; * - Both owner and victim are valid units, which are currently in the world. Neither can be nullptr. * * - There is an active combat reference between owner and victim. * * * - * ThreatManager also keeps track of whether its owner is engaged (a boolean flag). * - * - If a (non-offline) threat list entry is added to a not-yet-engaged ThreatManager, it calls JustEngagedWith on its owner's AI. * - * - The engaged state is cleared in ClearAllThreat (which is invoked on evade). * - * - This flag can be accessed through the IsEngaged method. For creatures that can have a threat list, this is equal to Unit::IsEngaged. * - * * * Note that (threat => combat) is a strong guarantee provided in conjunction with CombatManager. Thus: * * - Adding threat will also create a combat reference between the units if one doesn't exist yet (even if the owner can't have a threat list!) * * - Ending combat between two units will also delete any threat references that may exist between them. * @@ -112,7 +107,6 @@ class TC_GAME_API ThreatManager // returns an arbitrary non-offline victim from owner's threat list if one exists, nullptr otherwise Unit* GetAnyTarget() const; - bool IsEngaged() const { return _ownerEngaged; } // are there any entries in owner's threat list? bool IsThreatListEmpty(bool includeOffline = false) const; // is there a threat list entry on owner's threat list with victim == who? @@ -188,7 +182,6 @@ class TC_GAME_API ThreatManager private: Unit* const _owner; bool _ownerCanHaveThreatList; - bool _ownerEngaged; static const CompareThreatLessThan CompareThreat; static bool CompareReferencesLT(ThreatReference const* a, ThreatReference const* b, float aWeight); diff --git a/src/server/game/Entities/Creature/Creature.cpp b/src/server/game/Entities/Creature/Creature.cpp index 2aefd64993..3f4020f8bb 100644 --- a/src/server/game/Entities/Creature/Creature.cpp +++ b/src/server/game/Entities/Creature/Creature.cpp @@ -3205,17 +3205,26 @@ bool Creature::CanGiveExperience() const && !(GetCreatureTemplate()->flags_extra & CREATURE_FLAG_EXTRA_NO_XP_AT_KILL); } -void Creature::AtEnterCombat() +void Creature::AtEngage(Unit* target) { - Unit::AtEnterCombat(); + Unit::AtEngage(target); if (!(GetCreatureTemplate()->type_flags & CREATURE_TYPE_FLAG_MOUNTED_COMBAT_ALLOWED)) Dismount(); + + MovementGeneratorType const movetype = GetMotionMaster()->GetCurrentMovementGeneratorType(); + if (movetype == WAYPOINT_MOTION_TYPE || movetype == POINT_MOTION_TYPE || (IsAIEnabled() && AI()->IsEscorted())) + SetHomePosition(GetPosition()); + + if (CreatureAI* ai = AI()) + ai->JustEngagedWith(target); + if (CreatureGroup* formation = GetFormation()) + formation->MemberEngagingTarget(this, target); } -void Creature::AtExitCombat() +void Creature::AtDisengage() { - Unit::AtExitCombat(); + Unit::AtDisengage(); ClearUnitState(UNIT_STATE_ATTACK_PLAYER); if (HasFlag(UNIT_DYNAMIC_FLAGS, UNIT_DYNFLAG_TAPPED)) diff --git a/src/server/game/Entities/Creature/Creature.h b/src/server/game/Entities/Creature/Creature.h index a7e9871026..cf5ea1dd16 100644 --- a/src/server/game/Entities/Creature/Creature.h +++ b/src/server/game/Entities/Creature/Creature.h @@ -358,8 +358,8 @@ class TC_GAME_API Creature : public Unit, public GridObject, public Ma void MakeInterruptable(bool apply); - void AtEnterCombat() override; - void AtExitCombat() override; + void AtEngage(Unit* target) override; + void AtDisengage() override; protected: bool CreateFromProto(ObjectGuid::LowType guidlow, uint32 entry, CreatureData const* data = nullptr, uint32 vehId = 0); diff --git a/src/server/scripts/Commands/cs_debug.cpp b/src/server/scripts/Commands/cs_debug.cpp index ac8e54153b..6e03c5079c 100644 --- a/src/server/scripts/Commands/cs_debug.cpp +++ b/src/server/scripts/Commands/cs_debug.cpp @@ -859,17 +859,17 @@ public: ThreatManager& mgr = target->GetThreatManager(); if (!target->IsAlive()) { - handler->PSendSysMessage("%s (guid %u) is not alive.", target->GetName().c_str(), target->GetGUID().GetCounter()); + handler->PSendSysMessage("%s (GUID %u) is not alive.", target->GetName().c_str(), target->GetGUID().GetCounter()); return true; } uint32 count = 0; auto const& threatenedByMe = target->GetThreatManager().GetThreatenedByMeList(); if (threatenedByMe.empty()) - handler->PSendSysMessage("%s (guid %u) does not threaten any units.", target->GetName().c_str(), target->GetGUID().GetCounter()); + handler->PSendSysMessage("%s (GUID %u) does not threaten any units.", target->GetName().c_str(), target->GetGUID().GetCounter()); else { - handler->PSendSysMessage("List of units threatened by %s (guid %u)", target->GetName().c_str(), target->GetGUID().GetCounter()); + handler->PSendSysMessage("List of units threatened by %s (GUID %u)", target->GetName().c_str(), target->GetGUID().GetCounter()); for (auto const& pair : threatenedByMe) { Unit* unit = pair.second->GetOwner(); @@ -882,7 +882,7 @@ public: { if (!mgr.IsThreatListEmpty(true)) { - if (mgr.IsEngaged()) + if (target->IsEngaged()) handler->PSendSysMessage("Threat list of %s (GUID %u, SpawnID %u):", target->GetName().c_str(), target->GetGUID().GetCounter(), target->GetTypeId() == TYPEID_UNIT ? target->ToCreature()->GetSpawnId() : 0); else handler->PSendSysMessage("%s (GUID %u, SpawnID %u) is not engaged, but still has a threat list? Well, here it is:", target->GetName().c_str(), target->GetGUID().GetCounter(), target->GetTypeId() == TYPEID_UNIT ? target->ToCreature()->GetSpawnId() : 0); @@ -923,13 +923,15 @@ public: } handler->SendSysMessage("End of threat list."); } - else if (!mgr.IsEngaged()) + else if (!target->IsEngaged()) handler->PSendSysMessage("%s (GUID %u, SpawnID %u) is not currently engaged.", target->GetName().c_str(), target->GetGUID().GetCounter(), target->GetTypeId() == TYPEID_UNIT ? target->ToCreature()->GetSpawnId() : 0); else handler->PSendSysMessage("%s (GUID %u, SpawnID %u) seems to be engaged, but does not have a threat list??", target->GetName().c_str(), target->GetGUID().GetCounter(), target->GetTypeId() == TYPEID_UNIT ? target->ToCreature()->GetSpawnId() : 0); } + else if (target->IsEngaged()) + handler->PSendSysMessage("%s (GUID %u) is currently engaged. (This unit cannot have a threat list.)", target->GetName().c_str(), target->GetGUID().GetCounter()); else - handler->PSendSysMessage("%s (GUID %u) cannot have a threat list.", target->GetName().c_str(), target->GetGUID().GetCounter()); + handler->PSendSysMessage("%s (GUID %u) is not currently engaged. (This unit cannot have a threat list.)", target->GetName().c_str(), target->GetGUID().GetCounter()); return true; }