diff options
author | Treeston <treeston.mmoc@gmail.com> | 2019-07-21 01:49:58 +0200 |
---|---|---|
committer | Shauren <shauren.trinity@gmail.com> | 2021-12-17 00:23:33 +0100 |
commit | c13d83796f7b2111c5dcf8546bdd84eccd232ae3 (patch) | |
tree | 03c4220a955113e46b3aa2f9cc270574ba3a79c4 | |
parent | f9fe00bf8c4c913bcfedd8df7d8e1001129962a0 (diff) |
Core/AI: Finally move the "is creature engaged" flag to be a property of the creature AI, where it honestly always belonged. Fixes #17981 and #23602 for real this time.
(cherry picked from commit 0e7c66cb4c7ff7d44e232d0b50703a48605ffd24)
34 files changed, 153 insertions, 101 deletions
diff --git a/src/server/game/AI/CoreAI/GuardAI.cpp b/src/server/game/AI/CoreAI/GuardAI.cpp index 214c8df86f7..aa6ea0e93c4 100644 --- a/src/server/game/AI/CoreAI/GuardAI.cpp +++ b/src/server/game/AI/CoreAI/GuardAI.cpp @@ -52,7 +52,7 @@ void GuardAI::EnterEvadeMode(EvadeReason /*why*/) { me->GetMotionMaster()->MoveIdle(); me->CombatStop(true); - me->GetThreatManager().NotifyDisengaged(); + EngagementOver(); return; } @@ -60,7 +60,7 @@ void GuardAI::EnterEvadeMode(EvadeReason /*why*/) me->RemoveAllAuras(); me->CombatStop(true); - me->GetThreatManager().NotifyDisengaged(); + EngagementOver(); me->GetMotionMaster()->MoveTargetedHome(); } diff --git a/src/server/game/AI/CoreAI/PassiveAI.h b/src/server/game/AI/CoreAI/PassiveAI.h index 582fb737d41..039a5293036 100644 --- a/src/server/game/AI/CoreAI/PassiveAI.h +++ b/src/server/game/AI/CoreAI/PassiveAI.h @@ -39,6 +39,9 @@ class TC_GAME_API PossessedAI : public CreatureAI void MoveInLineOfSight(Unit*) override { } void AttackStart(Unit* target) override; + void JustEnteredCombat(Unit* who) override { EngagementStart(who); } + void JustExitedCombat() override { EngagementOver(); } + void JustStartedThreateningMe(Unit*) override { } void UpdateAI(uint32) override; void EnterEvadeMode(EvadeReason /*why*/) override { } @@ -55,6 +58,8 @@ class TC_GAME_API NullCreatureAI : public CreatureAI void MoveInLineOfSight(Unit*) override { } void AttackStart(Unit*) override { } + void JustStartedThreateningMe(Unit*) override { } + void JustEnteredCombat(Unit*) override { } void UpdateAI(uint32) override { } void JustAppeared() override { } void EnterEvadeMode(EvadeReason /*why*/) override { } diff --git a/src/server/game/AI/CoreAI/TotemAI.cpp b/src/server/game/AI/CoreAI/TotemAI.cpp index 48940a1412b..64741e5993a 100644 --- a/src/server/game/AI/CoreAI/TotemAI.cpp +++ b/src/server/game/AI/CoreAI/TotemAI.cpp @@ -33,16 +33,9 @@ int32 TotemAI::Permissible(Creature const* creature) return PERMIT_BASE_NO; } -TotemAI::TotemAI(Creature* creature, uint32 scriptId) : CreatureAI(creature, scriptId), _victimGUID() +TotemAI::TotemAI(Creature* creature, uint32 scriptId) : NullCreatureAI(creature, scriptId), _victimGUID() { - ASSERT(creature->IsTotem(), "TotemAI: AI assigned to a no-totem creature (%s)!", creature->GetGUID().ToString().c_str()); -} - -void TotemAI::MoveInLineOfSight(Unit* /*who*/) { } - -void TotemAI::EnterEvadeMode(EvadeReason /*why*/) -{ - me->CombatStop(true); + ASSERT(creature->IsTotem(), "TotemAI: AI assigned to a non-totem creature (%s)!", creature->GetGUID().ToString().c_str()); } void TotemAI::UpdateAI(uint32 /*diff*/) diff --git a/src/server/game/AI/CoreAI/TotemAI.h b/src/server/game/AI/CoreAI/TotemAI.h index dcc14e963ee..80f387f6a22 100644 --- a/src/server/game/AI/CoreAI/TotemAI.h +++ b/src/server/game/AI/CoreAI/TotemAI.h @@ -19,20 +19,18 @@ #define TRINITY_TOTEMAI_H #include "CreatureAI.h" +#include "PassiveAI.h" #include "Timer.h" class Creature; class Totem; -class TC_GAME_API TotemAI : public CreatureAI +class TC_GAME_API TotemAI : public NullCreatureAI { public: explicit TotemAI(Creature* creature, uint32 scriptId = {}); - void MoveInLineOfSight(Unit* who) override; void AttackStart(Unit* victim) override; - void JustAppeared() override { } - void EnterEvadeMode(EvadeReason /*why*/) override; void UpdateAI(uint32 diff) override; static int32 Permissible(Creature const* creature); diff --git a/src/server/game/AI/CoreAI/UnitAI.cpp b/src/server/game/AI/CoreAI/UnitAI.cpp index df37aa94f03..642ac34a675 100644 --- a/src/server/game/AI/CoreAI/UnitAI.cpp +++ b/src/server/game/AI/CoreAI/UnitAI.cpp @@ -318,7 +318,7 @@ std::string UnitAI::GetDebugInfo() const } DefaultTargetSelector::DefaultTargetSelector(Unit const* unit, float dist, bool playerOnly, bool withTank, int32 aura) - : _me(unit), _dist(dist), _playerOnly(playerOnly), _exception(!withTank ? _me->GetThreatManager().GetCurrentVictim() : nullptr), _aura(aura) + : _me(unit), _dist(dist), _playerOnly(playerOnly), _exception(!withTank ? unit->GetThreatManager().GetLastVictim() : nullptr), _aura(aura) { } diff --git a/src/server/game/AI/CreatureAI.cpp b/src/server/game/AI/CreatureAI.cpp index e1d53238c89..7d617a9c384 100644 --- a/src/server/game/AI/CreatureAI.cpp +++ b/src/server/game/AI/CreatureAI.cpp @@ -43,7 +43,7 @@ AISpellInfoType* GetAISpellInfo(uint32 spellId, Difficulty difficulty) CreatureAI::CreatureAI(Creature* creature, uint32 scriptId) : UnitAI(creature), me(creature), _boundary(nullptr), - _negateBoundary(false), _scriptId(scriptId ? scriptId : creature->GetScriptId()), _moveInLOSLocked(false) + _negateBoundary(false), _scriptId(scriptId ? scriptId : creature->GetScriptId()), _isEngaged(false), _moveInLOSLocked(false) { ASSERT(_scriptId, "A CreatureAI was initialized with an invalid scriptId!"); } @@ -207,6 +207,12 @@ void CreatureAI::JustAppeared() } } +void CreatureAI::JustEnteredCombat(Unit* who) +{ + if (!IsEngaged() && !me->CanHaveThreatList()) + EngagementStart(who); +} + void CreatureAI::EnterEvadeMode(EvadeReason why) { if (!_EnterEvadeMode(why)) @@ -235,7 +241,7 @@ void CreatureAI::EnterEvadeMode(EvadeReason why) bool CreatureAI::UpdateVictim() { - if (!me->IsEngaged()) + if (!IsEngaged()) return false; if (!me->HasReactState(REACT_PASSIVE)) @@ -257,22 +263,52 @@ bool CreatureAI::UpdateVictim() return true; } +void CreatureAI::EngagementStart(Unit* who) +{ + if (_isEngaged) + { + TC_LOG_ERROR("scripts.ai", "CreatureAI::EngagementStart called even though creature is already engaged. Creature debug info:\n%s", me->GetDebugInfo().c_str()); + return; + } + _isEngaged = true; + + me->AtEngage(who); +} + +void CreatureAI::EngagementOver() +{ + if (!_isEngaged) + { + TC_LOG_ERROR("scripts.ai", "CreatureAI::EngagementOver called even though creature is not currently engaged. Creature debug info:\n%s", me->GetDebugInfo().c_str()); + return; + } + _isEngaged = false; + + me->AtDisengage(); +} + bool CreatureAI::_EnterEvadeMode(EvadeReason /*why*/) { + if (me->IsInEvadeMode()) + return false; + if (!me->IsAlive()) + { + EngagementOver(); return false; + } me->RemoveAurasOnEvade(); me->CombatStop(true); - me->GetThreatManager().NotifyDisengaged(); me->SetLootRecipient(nullptr); me->ResetPlayerDamageReq(); me->SetLastDamagedTime(0); me->SetCannotReachTarget(false); me->DoNotReacquireTarget(); + EngagementOver(); - return !me->IsInEvadeMode(); + return true; } Optional<QuestGiverStatus> CreatureAI::GetDialogStatus(Player* /*player*/) diff --git a/src/server/game/AI/CreatureAI.h b/src/server/game/AI/CreatureAI.h index 165b905803e..eef2cc0bd6b 100644 --- a/src/server/game/AI/CreatureAI.h +++ b/src/server/game/AI/CreatureAI.h @@ -84,6 +84,8 @@ class TC_GAME_API CreatureAI : public UnitAI // Gets the id of the AI (script id) uint32 GetId() const { return _scriptId; } + bool IsEngaged() const { return _isEngaged; } + void Talk(uint8 id, WorldObject const* whisperTarget = nullptr); /// == Reactions At ================================= @@ -97,11 +99,17 @@ 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 whenever we start being in combat (overridden from base UnitAI) + void JustEnteredCombat(Unit* /*who*/) override; + + // Called for reaction whenever a new non-offline unit is added to the threat list + virtual void JustStartedThreateningMe(Unit* who) { if (!IsEngaged()) EngagementStart(who); } + // Called for reaction when initially engaged - this will always happen _after_ JustEnteredCombat virtual void JustEngagedWith(Unit* /*who*/) { } // Called when the creature is killed - virtual void JustDied(Unit* /*killer*/) { } + virtual void JustDied(Unit* /*killer*/) { if (IsEngaged()) EngagementOver(); } // Called when the creature kills a unit virtual void KilledUnit(Unit* /*victim*/) { } @@ -227,6 +235,8 @@ class TC_GAME_API CreatureAI : public UnitAI // intended for encounter design/debugging. do not use for other purposes. expensive. int32 VisualizeBoundary(uint32 duration, Unit* owner = nullptr, bool fill = false) const; + + // boundary system methods virtual bool CheckInRoom(); CreatureBoundary const* GetBoundary() const { return _boundary; } void SetBoundary(CreatureBoundary const* boundary, bool negativeBoundaries = false); @@ -235,6 +245,8 @@ class TC_GAME_API CreatureAI : public UnitAI bool IsInBoundary(Position const* who = nullptr) const; protected: + void EngagementStart(Unit* who); + void EngagementOver(); virtual void MoveInLineOfSight(Unit* /*who*/); bool _EnterEvadeMode(EvadeReason why = EVADE_REASON_OTHER); @@ -246,6 +258,7 @@ class TC_GAME_API CreatureAI : public UnitAI void OnOwnerCombatInteraction(Unit* target); uint32 const _scriptId; + bool _isEngaged; bool _moveInLOSLocked; }; diff --git a/src/server/game/AI/ScriptedAI/ScriptedEscortAI.cpp b/src/server/game/AI/ScriptedAI/ScriptedEscortAI.cpp index 91efe5d5312..63592dd30ba 100644 --- a/src/server/game/AI/ScriptedAI/ScriptedEscortAI.cpp +++ b/src/server/game/AI/ScriptedAI/ScriptedEscortAI.cpp @@ -94,8 +94,9 @@ void EscortAI::EnterEvadeMode(EvadeReason /*why*/) { me->RemoveAllAuras(); me->CombatStop(true); - me->GetThreatManager().NotifyDisengaged(); me->SetLootRecipient(nullptr); + + EngagementOver(); if (HasEscortState(STATE_ESCORT_ESCORTING)) { diff --git a/src/server/game/AI/ScriptedAI/ScriptedFollowerAI.cpp b/src/server/game/AI/ScriptedAI/ScriptedFollowerAI.cpp index 669022d8420..d8f0d972cd1 100644 --- a/src/server/game/AI/ScriptedAI/ScriptedFollowerAI.cpp +++ b/src/server/game/AI/ScriptedAI/ScriptedFollowerAI.cpp @@ -103,14 +103,18 @@ void FollowerAI::JustAppeared() void FollowerAI::EnterEvadeMode(EvadeReason /*why*/) { if (!me->IsAlive()) + { + EngagementOver(); return; + } me->RemoveAllAuras(); me->CombatStop(true); - me->GetThreatManager().NotifyDisengaged(); me->SetLootRecipient(nullptr); me->SetCannotReachTarget(false); me->DoNotReacquireTarget(); + + EngagementOver(); if (HasFollowState(STATE_FOLLOW_INPROGRESS)) { diff --git a/src/server/game/Combat/CombatManager.cpp b/src/server/game/Combat/CombatManager.cpp index d6f7a55665b..a43bb72eca4 100644 --- a/src/server/game/Combat/CombatManager.cpp +++ b/src/server/game/Combat/CombatManager.cpp @@ -293,13 +293,8 @@ void CombatManager::EndAllPvPCombat() /*static*/ void CombatManager::NotifyAICombat(Unit* me, Unit* other) { - if (!me->IsAIEnabled()) - return; - me->GetAI()->JustEnteredCombat(other); - - if (Creature* cMe = me->ToCreature()) - if (!cMe->CanHaveThreatList()) - cMe->AI()->JustEngagedWith(other); + if (UnitAI* ai = me->GetAI()) + ai->JustEnteredCombat(other); } void CombatManager::PutReference(ObjectGuid const& guid, CombatReference* ref) @@ -336,14 +331,14 @@ bool CombatManager::UpdateOwnerCombatState() const { _owner->AddUnitFlag(UNIT_FLAG_IN_COMBAT); _owner->AtEnterCombat(); - if (!_owner->CanHaveThreatList() && !_owner->IsEngaged()) + if (_owner->GetTypeId() == TYPEID_UNIT) _owner->AtEngage(GetAnyTarget()); } else { _owner->RemoveUnitFlag(UNIT_FLAG_IN_COMBAT); _owner->AtExitCombat(); - if (_owner->IsEngaged() && !(_owner->ToCreature() && _owner->CanHaveThreatList() && _owner->ToCreature()->IsAIEnabled())) + if (_owner->GetTypeId() != TYPEID_UNIT) _owner->AtDisengage(); } diff --git a/src/server/game/Combat/ThreatManager.cpp b/src/server/game/Combat/ThreatManager.cpp index 183142f1cd7..6ad8de0aabb 100644 --- a/src/server/game/Combat/ThreatManager.cpp +++ b/src/server/game/Combat/ThreatManager.cpp @@ -71,6 +71,7 @@ void ThreatReference::UpdateOffline() { _online = ShouldBeSuppressed() ? ONLINE_STATE_SUPPRESSED : ONLINE_STATE_ONLINE; HeapNotifyIncreased(); + _mgr.RegisterForAIUpdate(this); } } @@ -199,10 +200,11 @@ Unit* ThreatManager::GetCurrentVictim() { if (!_currentVictimRef || _currentVictimRef->ShouldBeOffline()) UpdateVictim(); - return const_cast<ThreatManager const*>(this)->GetCurrentVictim(); + ASSERT(!_currentVictimRef || _currentVictimRef->IsAvailable()); + return _currentVictimRef ? _currentVictimRef->GetVictim() : nullptr; } -Unit* ThreatManager::GetCurrentVictim() const +Unit* ThreatManager::GetLastVictim() const { if (_currentVictimRef && !_currentVictimRef->ShouldBeOffline()) return _currentVictimRef->GetVictim(); @@ -244,7 +246,7 @@ float ThreatManager::GetThreat(Unit const* who, bool includeOffline) const return (includeOffline || it->second->IsAvailable()) ? it->second->GetThreat() : 0.0f; } -std::vector<ThreatReference*> ThreatManager::GetModifiableThreatList() const +std::vector<ThreatReference*> ThreatManager::GetModifiableThreatList() { std::vector<ThreatReference*> list; list.reserve(_myThreatListEntries.size()); @@ -394,16 +396,14 @@ void ThreatManager::AddThreat(Unit* target, float amount, SpellInfo const* spell PutThreatListRef(target->GetGUID(), ref); target->GetThreatManager().PutThreatenedByMeRef(_owner->GetGUID(), ref); - // afterwards, we evaluate whether this is an online reference (it might not be an acceptable target, but we need to add it to our threat list before we check!) ref->UpdateOffline(); - if (ref->IsOnline()) // ...and if the ref is online it also gets the threat it should have + if (ref->IsOnline()) // we only add the threat if the ref is currently available ref->AddThreat(amount); - if (!_owner->IsEngaged()) - { - _owner->AtEngage(target); + if (!_currentVictimRef) UpdateVictim(); - } + else + ProcessAIUpdates(); } void ThreatManager::ScaleThreat(Unit* target, float factor) @@ -488,14 +488,6 @@ void ThreatManager::ClearAllThreat() } } -void ThreatManager::NotifyDisengaged() -{ - // 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) { if (target) @@ -530,6 +522,7 @@ void ThreatManager::UpdateVictim() _needClientUpdate = false; } + ProcessAIUpdates(); } ThreatReference const* ThreatManager::ReselectVictim() @@ -538,7 +531,7 @@ ThreatReference const* ThreatManager::ReselectVictim() return nullptr; for (auto const& pair : _myThreatListEntries) - pair.second->UpdateOffline(); + pair.second->UpdateOffline(); // AI notifies are processed in ::UpdateVictim caller // fixated target is always preferred if (_fixateRef && _fixateRef->IsAvailable()) @@ -587,6 +580,16 @@ ThreatReference const* ThreatManager::ReselectVictim() return nullptr; } +void ThreatManager::ProcessAIUpdates() +{ + CreatureAI* ai = ASSERT_NOTNULL(_owner->ToCreature())->AI(); + std::vector<ThreatReference const*> v(std::move(_needsAIUpdate)); // _needClientUpdate is now empty in case this triggers a recursive call + if (!ai) + return; + for (ThreatReference const* ref : v) + ai->JustStartedThreateningMe(ref->GetVictim()); +} + // returns true if a is LOWER on the threat list than b /*static*/ bool ThreatManager::CompareReferencesLT(ThreatReference const* a, ThreatReference const* b, float aWeight) { diff --git a/src/server/game/Combat/ThreatManager.h b/src/server/game/Combat/ThreatManager.h index 6e4f20c55a9..8e722707f91 100644 --- a/src/server/game/Combat/ThreatManager.h +++ b/src/server/game/Combat/ThreatManager.h @@ -103,7 +103,7 @@ class TC_GAME_API ThreatManager bool CanHaveThreatList() const { return _ownerCanHaveThreatList; } // returns the current victim - this can be nullptr if owner's threat list is empty, or has only offline targets Unit* GetCurrentVictim(); - Unit* GetCurrentVictim() const; + Unit* GetLastVictim() const; // returns an arbitrary non-offline victim from owner's threat list if one exists, nullptr otherwise Unit* GetAnyTarget() const; @@ -121,10 +121,10 @@ class TC_GAME_API ThreatManager Trinity::IteratorPair<ThreatListIterator> GetUnsortedThreatList() const { return { _myThreatListEntries.begin(), _myThreatListEntries.end() }; } // slightly slower than GetUnsorted, but, well...sorted - only use it if you need the sorted property, of course // this iterator pair will invalidate on any modification (even indirect) of the threat list; spell casts and similar can all induce this! - // note: current tank is NOT guaranteed to be the first entry in this list - check GetCurrentVictim separately if you want that! + // note: current tank is NOT guaranteed to be the first entry in this list - check GetLastVictim separately if you want that! Trinity::IteratorPair<threat_list_heap::ordered_iterator> GetSortedThreatList() const { return { _sortedThreatList.ordered_begin(), _sortedThreatList.ordered_end() }; } // slowest of the three threat list getters (by far), but lets you modify the threat references - this is also sorted - std::vector<ThreatReference*> GetModifiableThreatList() const; + std::vector<ThreatReference*> GetModifiableThreatList(); // does any unit have a threat list entry with victim == this.owner? bool IsThreateningAnyone(bool includeOffline = false) const; @@ -154,9 +154,6 @@ class TC_GAME_API ThreatManager void ClearThreat(ThreatReference* ref); // Removes all targets from the threat list (will cause evade in UpdateVictim if called) void ClearAllThreat(); - // THIS SHOULD ONLY BE CALLED FROM A CREATURE'S AI (typically in EnterEvadeMode) - // notify the unit that the AI has disengaged - void NotifyDisengaged(); // Fixate on the passed target; this target will always be selected until the fixate is cleared // (if the target is not in the threat list, does nothing) @@ -204,6 +201,12 @@ class TC_GAME_API ThreatManager threat_list_heap _sortedThreatList; std::unordered_map<ObjectGuid, ThreatReference*> _myThreatListEntries; + // AI notifies are delayed to ensure we are in a consistent state before we call out to arbitrary logic + // threat references might register themselves here when ::UpdateOffline() is called - MAKE SURE THIS IS PROCESSED JUST BEFORE YOU EXIT THREATMANAGER LOGIC + void ProcessAIUpdates(); + void RegisterForAIUpdate(ThreatReference const* ref) { _needsAIUpdate.push_back(ref); } + std::vector<ThreatReference const*> _needsAIUpdate; + // picks a new victim - called from ::Update periodically void UpdateVictim(); ThreatReference const* ReselectVictim(); @@ -278,7 +281,7 @@ class TC_GAME_API ThreatReference _owner(reinterpret_cast<Creature*>(mgr->_owner)), _mgr(*mgr), _victim(victim), _baseAmount(0.0f), _tempModifier(0), _taunted(TAUNT_STATE_NONE) { - _online = ShouldBeSuppressed() ? ONLINE_STATE_SUPPRESSED : ONLINE_STATE_ONLINE; + _online = ONLINE_STATE_OFFLINE; } void UnregisterAndFree(); diff --git a/src/server/game/Entities/Creature/Creature.cpp b/src/server/game/Entities/Creature/Creature.cpp index d966adbdd66..007664ec134 100644 --- a/src/server/game/Entities/Creature/Creature.cpp +++ b/src/server/game/Entities/Creature/Creature.cpp @@ -3397,6 +3397,13 @@ bool Creature::CanGiveExperience() const && !(GetCreatureTemplate()->flags_extra & CREATURE_FLAG_EXTRA_NO_XP_AT_KILL); } +bool Creature::IsEngaged() const +{ + if (CreatureAI const* ai = AI()) + return ai->IsEngaged(); + return false; +} + void Creature::AtEngage(Unit* target) { Unit::AtEngage(target); diff --git a/src/server/game/Entities/Creature/Creature.h b/src/server/game/Entities/Creature/Creature.h index 119408a2f2e..a13dff72238 100644 --- a/src/server/game/Entities/Creature/Creature.h +++ b/src/server/game/Entities/Creature/Creature.h @@ -367,6 +367,7 @@ class TC_GAME_API Creature : public Unit, public GridObject<Creature>, public Ma bool CanGiveExperience() const; + bool IsEngaged() const override; void AtEngage(Unit* target) override; void AtDisengage() override; diff --git a/src/server/game/Entities/Unit/Unit.cpp b/src/server/game/Entities/Unit/Unit.cpp index cb059be8d7a..2b1dbfdf74e 100644 --- a/src/server/game/Entities/Unit/Unit.cpp +++ b/src/server/game/Entities/Unit/Unit.cpp @@ -304,7 +304,7 @@ Unit::Unit(bool isWorldObject) : m_removedAurasCount(0), m_interruptMask(SpellAuraInterruptFlags::None), m_interruptMask2(SpellAuraInterruptFlags2::None), m_unitMovedByMe(nullptr), m_playerMovingMe(nullptr), m_charmer(nullptr), m_charmed(nullptr), i_motionMaster(new MotionMaster(this)), m_regenTimer(0), m_vehicle(nullptr), - m_vehicleKit(nullptr), m_unitTypeMask(UNIT_MASK_NONE), m_Diminishing(), m_isEngaged(false), m_combatManager(this), + m_vehicleKit(nullptr), m_unitTypeMask(UNIT_MASK_NONE), m_Diminishing(), m_combatManager(this), m_threatManager(this), m_aiLocked(false), _playHoverAnim(false), _aiAnimKitId(0), _movementAnimKitId(0), _meleeAnimKitId(0), _spellHistory(new SpellHistory(this)) { diff --git a/src/server/game/Entities/Unit/Unit.h b/src/server/game/Entities/Unit/Unit.h index e42cce1e913..e88bcf953eb 100644 --- a/src/server/game/Entities/Unit/Unit.h +++ b/src/server/game/Entities/Unit/Unit.h @@ -1099,10 +1099,10 @@ class TC_GAME_API Unit : public WorldObject /// ====================== THREAT & COMBAT ==================== bool CanHaveThreatList() const { return m_threatManager.CanHaveThreatList(); } - // This value can be different from IsInCombat: + // This value can be different from IsInCombat, for example: // - when a projectile spell is midair against a creature (combat on launch - threat+aggro on impact) // - when the creature has no targets left, but the AI has not yet ceased engaged logic - bool IsEngaged() const { return m_isEngaged; } + virtual bool IsEngaged() const { return IsInCombat(); } bool IsEngagedBy(Unit const* who) const { return CanHaveThreatList() ? IsThreatenedBy(who) : IsInCombatWith(who); } void EngageWithTarget(Unit* who); // Adds target to threat list if applicable, otherwise just sets combat state // Combat handling @@ -1938,11 +1938,12 @@ class TC_GAME_API Unit : public WorldObject virtual void ProcessTerrainStatusUpdate(ZLiquidStatus status, Optional<LiquidData> const& liquidData); virtual void SetInWater(bool inWater); + // notifiers virtual void AtEnterCombat(); virtual void AtExitCombat(); - virtual void AtEngage(Unit* /*target*/) { m_isEngaged = true; } - virtual void AtDisengage() { m_isEngaged = false; } + virtual void AtEngage(Unit* /*target*/) {} + virtual void AtDisengage() {} private: @@ -1971,7 +1972,6 @@ class TC_GAME_API Unit : public WorldObject Diminishing m_Diminishing; // Threat+combat management - bool m_isEngaged; friend class CombatManager; CombatManager m_combatManager; friend class ThreatManager; diff --git a/src/server/scripts/Commands/cs_debug.cpp b/src/server/scripts/Commands/cs_debug.cpp index 6e58a7ef96c..956400e1814 100644 --- a/src/server/scripts/Commands/cs_debug.cpp +++ b/src/server/scripts/Commands/cs_debug.cpp @@ -872,7 +872,7 @@ public: ThreatManager& mgr = target->GetThreatManager(); if (!target->IsAlive()) { - handler->PSendSysMessage("%s (%s) is not alive.", target->GetName().c_str(), target->GetGUID().ToString().c_str()); + handler->PSendSysMessage("%s (%s) is not alive.%s", target->GetName().c_str(), target->GetGUID().ToString().c_str(), target->IsEngaged() ? " (It is, however, engaged. Huh?)" : ""); return true; } diff --git a/src/server/scripts/EasternKingdoms/BlackrockMountain/BlackrockDepths/instance_blackrock_depths.cpp b/src/server/scripts/EasternKingdoms/BlackrockMountain/BlackrockDepths/instance_blackrock_depths.cpp index a326d9c2323..2057c55a29b 100644 --- a/src/server/scripts/EasternKingdoms/BlackrockMountain/BlackrockDepths/instance_blackrock_depths.cpp +++ b/src/server/scripts/EasternKingdoms/BlackrockMountain/BlackrockDepths/instance_blackrock_depths.cpp @@ -372,16 +372,9 @@ public: if (Creature* boss = instance->GetCreature(TombBossGUIDs[i])) { if (!boss->IsAlive()) - {//do not call EnterEvadeMode(), it will create infinit loops boss->Respawn(); - boss->RemoveAllAuras(); - boss->CombatStop(true); - boss->GetThreatManager().NotifyDisengaged(); - boss->LoadCreaturesAddon(); - boss->GetMotionMaster()->MoveTargetedHome(); - boss->SetLootRecipient(nullptr); - } - boss->SetFaction(FACTION_FRIENDLY); + else + boss->SetFaction(FACTION_FRIENDLY); } } GhostKillCount = 0; diff --git a/src/server/scripts/EasternKingdoms/Karazhan/bosses_opera.cpp b/src/server/scripts/EasternKingdoms/Karazhan/bosses_opera.cpp index 68400643091..7c6fb0e4689 100644 --- a/src/server/scripts/EasternKingdoms/Karazhan/bosses_opera.cpp +++ b/src/server/scripts/EasternKingdoms/Karazhan/bosses_opera.cpp @@ -1257,7 +1257,6 @@ public: Julianne->GetMotionMaster()->Clear(); Julianne->setDeathState(JUST_DIED); Julianne->CombatStop(true); - Julianne->GetThreatManager().NotifyDisengaged(); Julianne->SetDynamicFlags(UNIT_DYNFLAG_LOOTABLE); } return; @@ -1527,7 +1526,6 @@ void boss_julianne::boss_julianneAI::DamageTaken(Unit* /*done_by*/, uint32 &dama Romulo->GetMotionMaster()->Clear(); Romulo->setDeathState(JUST_DIED); Romulo->CombatStop(true); - Romulo->GetThreatManager().NotifyDisengaged(); Romulo->SetDynamicFlags(UNIT_DYNFLAG_LOOTABLE); } diff --git a/src/server/scripts/EasternKingdoms/ScarletEnclave/chapter5.cpp b/src/server/scripts/EasternKingdoms/ScarletEnclave/chapter5.cpp index 64c8497712c..f62c84c7dc0 100644 --- a/src/server/scripts/EasternKingdoms/ScarletEnclave/chapter5.cpp +++ b/src/server/scripts/EasternKingdoms/ScarletEnclave/chapter5.cpp @@ -1388,9 +1388,10 @@ public: me->RemoveAurasDueToSpell(SPELL_THE_MIGHT_OF_MOGRAINE); me->RemoveAllAuras(); me->CombatStop(true); - me->GetThreatManager().NotifyDisengaged(); me->InterruptNonMeleeSpells(false); me->SetWalk(false); + + EngagementOver(); for (uint8 i = 0; i < ENCOUNTER_DEFENDER_NUMBER; ++i) DespawnNPC(uiDefenderGUID[i]); @@ -1407,9 +1408,9 @@ public: if (Creature* temp = ObjectAccessor::GetCreature(*me, uiKorfaxGUID)) { + temp->AI()->EnterEvadeMode(); temp->RemoveAllAuras(); temp->CombatStop(true); - temp->GetThreatManager().NotifyDisengaged(); temp->AttackStop(); temp->SetFaction(me->GetFaction()); temp->SetWalk(false); @@ -1428,9 +1429,9 @@ public: if (Creature* temp = ObjectAccessor::GetCreature(*me, uiEligorGUID)) { + temp->AI()->EnterEvadeMode(); temp->RemoveAllAuras(); temp->CombatStop(true); - temp->GetThreatManager().NotifyDisengaged(); temp->AttackStop(); temp->SetFaction(me->GetFaction()); temp->SetWalk(false); @@ -1440,9 +1441,9 @@ public: if (Creature* temp = ObjectAccessor::GetCreature(*me, uiKoltiraGUID)) { + temp->AI()->EnterEvadeMode(); temp->RemoveAllAuras(); temp->CombatStop(true); - temp->GetThreatManager().NotifyDisengaged(); temp->AttackStop(); temp->SetFaction(me->GetFaction()); temp->SetWalk(false); @@ -1455,9 +1456,9 @@ public: if (Creature* temp = ObjectAccessor::GetCreature(*me, uiThassarianGUID)) { + temp->AI()->EnterEvadeMode(); temp->RemoveAllAuras(); temp->CombatStop(true); - temp->GetThreatManager().NotifyDisengaged(); temp->AttackStop(); temp->SetFaction(me->GetFaction()); temp->SetWalk(false); diff --git a/src/server/scripts/Kalimdor/CavernsOfTime/BattleForMountHyjal/hyjalAI.cpp b/src/server/scripts/Kalimdor/CavernsOfTime/BattleForMountHyjal/hyjalAI.cpp index a8788d5da97..1ea1ccf835a 100644 --- a/src/server/scripts/Kalimdor/CavernsOfTime/BattleForMountHyjal/hyjalAI.cpp +++ b/src/server/scripts/Kalimdor/CavernsOfTime/BattleForMountHyjal/hyjalAI.cpp @@ -423,9 +423,10 @@ void hyjalAI::EnterEvadeMode(EvadeReason /*why*/) if (me->GetEntry() != JAINA) me->RemoveAllAuras(); me->CombatStop(true); - me->GetThreatManager().NotifyDisengaged(); + + EngagementOver(); + me->LoadCreaturesAddon(); - if (me->IsAlive()) me->GetMotionMaster()->MoveTargetedHome(); diff --git a/src/server/scripts/Kalimdor/zone_desolace.cpp b/src/server/scripts/Kalimdor/zone_desolace.cpp index 5a5e91e491c..46c6102e340 100644 --- a/src/server/scripts/Kalimdor/zone_desolace.cpp +++ b/src/server/scripts/Kalimdor/zone_desolace.cpp @@ -86,9 +86,11 @@ public: me->UpdateEntry(NPC_TAMED_KODO); me->CombatStop(); - me->GetThreatManager().NotifyDisengaged(); me->SetFaction(FACTION_FRIENDLY); me->SetSpeedRate(MOVE_RUN, 0.6f); + + EngagementOver(); + me->GetMotionMaster()->MoveFollow(caster, PET_FOLLOW_DIST, me->GetFollowAngle()); me->setActive(true); me->RemoveNpcFlag(UNIT_NPC_FLAG_GOSSIP); diff --git a/src/server/scripts/Kalimdor/zone_the_barrens.cpp b/src/server/scripts/Kalimdor/zone_the_barrens.cpp index 422be2b9b15..5b5add35f17 100644 --- a/src/server/scripts/Kalimdor/zone_the_barrens.cpp +++ b/src/server/scripts/Kalimdor/zone_the_barrens.cpp @@ -215,11 +215,11 @@ public: { me->RemoveAllAuras(); me->CombatStop(true); - me->GetThreatManager().NotifyDisengaged(); - me->StopMoving(); + + EngagementOver(); + me->GetMotionMaster()->MoveIdle(); - me->SetFaction(FACTION_FRIENDLY_F); me->HandleEmoteCommand(EMOTE_ONESHOT_SALUTE); } diff --git a/src/server/scripts/Maelstrom/Stonecore/stonecore.cpp b/src/server/scripts/Maelstrom/Stonecore/stonecore.cpp index 962ce4bda86..ed9ee7f953e 100644 --- a/src/server/scripts/Maelstrom/Stonecore/stonecore.cpp +++ b/src/server/scripts/Maelstrom/Stonecore/stonecore.cpp @@ -173,7 +173,7 @@ class npc_sc_millhouse_manastorm : public CreatureScript me->RemoveAllAuras(); me->CombatStop(true); - me->GetThreatManager().NotifyDisengaged(); + EngagementOver(); switch (pointId) { diff --git a/src/server/scripts/Northrend/IcecrownCitadel/boss_icecrown_gunship_battle.cpp b/src/server/scripts/Northrend/IcecrownCitadel/boss_icecrown_gunship_battle.cpp index 84015ecb287..fc5fdbceb4d 100644 --- a/src/server/scripts/Northrend/IcecrownCitadel/boss_icecrown_gunship_battle.cpp +++ b/src/server/scripts/Northrend/IcecrownCitadel/boss_icecrown_gunship_battle.cpp @@ -580,7 +580,7 @@ struct gunship_npc_AI : public ScriptedAI return; me->CombatStop(true); - me->GetThreatManager().NotifyDisengaged(); + EngagementOver(); me->GetMotionMaster()->MoveTargetedHome(); } @@ -725,7 +725,6 @@ class npc_gunship : public CreatureScript Creature* stalker = *itr; stalker->RemoveAllAuras(); stalker->CombatStop(true); - stalker->GetThreatManager().NotifyDisengaged(); } uint32 explosionSpell = isVictory ? SPELL_EXPLOSION_VICTORY : SPELL_EXPLOSION_WIPE; @@ -878,7 +877,7 @@ class npc_high_overlord_saurfang_igb : public CreatureScript return; me->CombatStop(true); - me->GetThreatManager().NotifyDisengaged(); + EngagementOver(); me->GetMotionMaster()->MoveTargetedHome(); Reset(); @@ -1142,7 +1141,7 @@ class npc_muradin_bronzebeard_igb : public CreatureScript return; me->CombatStop(true); - me->GetThreatManager().NotifyDisengaged(); + EngagementOver(); me->GetMotionMaster()->MoveTargetedHome(); Reset(); diff --git a/src/server/scripts/Northrend/IcecrownCitadel/boss_the_lich_king.cpp b/src/server/scripts/Northrend/IcecrownCitadel/boss_the_lich_king.cpp index f7a1878b6e0..b4f61407615 100644 --- a/src/server/scripts/Northrend/IcecrownCitadel/boss_the_lich_king.cpp +++ b/src/server/scripts/Northrend/IcecrownCitadel/boss_the_lich_king.cpp @@ -1778,7 +1778,7 @@ class npc_terenas_menethil : public CreatureScript return; me->CombatStop(false); - me->GetThreatManager().NotifyDisengaged(); + EngagementOver(); } void DamageTaken(Unit* /*attacker*/, uint32& damage) override diff --git a/src/server/scripts/Northrend/Naxxramas/boss_patchwerk.cpp b/src/server/scripts/Northrend/Naxxramas/boss_patchwerk.cpp index bf58171cfb7..e16da9ab00f 100644 --- a/src/server/scripts/Northrend/Naxxramas/boss_patchwerk.cpp +++ b/src/server/scripts/Northrend/Naxxramas/boss_patchwerk.cpp @@ -122,7 +122,7 @@ public: ThreatReference* secondThreat = nullptr; ThreatReference* thirdThreat = nullptr; - ThreatManager const& mgr = me->GetThreatManager(); + ThreatManager& mgr = me->GetThreatManager(); Unit* currentVictim = mgr.GetCurrentVictim(); auto list = mgr.GetModifiableThreatList(); auto it = list.begin(), end = list.end(); diff --git a/src/server/scripts/Northrend/Nexus/Oculus/boss_urom.cpp b/src/server/scripts/Northrend/Nexus/Oculus/boss_urom.cpp index c1e6a515863..4775540bd6b 100644 --- a/src/server/scripts/Northrend/Nexus/Oculus/boss_urom.cpp +++ b/src/server/scripts/Northrend/Nexus/Oculus/boss_urom.cpp @@ -293,7 +293,7 @@ class boss_urom : public CreatureScript { me->RemoveAllAuras(); me->CombatStop(false); - me->GetThreatManager().NotifyDisengaged(); + EngagementOver(); } void SpellHit(Unit* /*caster*/, SpellInfo const* spellInfo) override diff --git a/src/server/scripts/Northrend/Ulduar/Ulduar/boss_freya.cpp b/src/server/scripts/Northrend/Ulduar/Ulduar/boss_freya.cpp index 28c983e3ab6..700ab396c65 100644 --- a/src/server/scripts/Northrend/Ulduar/Ulduar/boss_freya.cpp +++ b/src/server/scripts/Northrend/Ulduar/Ulduar/boss_freya.cpp @@ -634,7 +634,7 @@ class boss_freya : public CreatureScript Elder->RemoveAllAuras(); Elder->AttackStop(); Elder->CombatStop(true); - Elder->GetThreatManager().NotifyDisengaged(); + EngagementOver(); Elder->AI()->DoAction(ACTION_ELDER_FREYA_KILLED); } } diff --git a/src/server/scripts/Northrend/zone_borean_tundra.cpp b/src/server/scripts/Northrend/zone_borean_tundra.cpp index 6d488fd5112..2677be16d0b 100644 --- a/src/server/scripts/Northrend/zone_borean_tundra.cpp +++ b/src/server/scripts/Northrend/zone_borean_tundra.cpp @@ -751,7 +751,6 @@ public: Creature* owner = GetOwner()->ToCreature(); owner->RemoveAllAurasExceptType(SPELL_AURA_DUMMY); owner->CombatStop(true); - owner->GetThreatManager().NotifyDisengaged(); owner->GetMotionMaster()->Clear(); owner->GetMotionMaster()->MoveFollow(GetCaster(), 4.0f, 0.0f); owner->CastSpell(owner, SPELL_SUBDUED, true); diff --git a/src/server/scripts/Outland/CoilfangReservoir/SerpentShrine/boss_leotheras_the_blind.cpp b/src/server/scripts/Outland/CoilfangReservoir/SerpentShrine/boss_leotheras_the_blind.cpp index aea89d9ed17..a7d7b2cc303 100644 --- a/src/server/scripts/Outland/CoilfangReservoir/SerpentShrine/boss_leotheras_the_blind.cpp +++ b/src/server/scripts/Outland/CoilfangReservoir/SerpentShrine/boss_leotheras_the_blind.cpp @@ -524,7 +524,7 @@ public: { ThreatManager const& mgr = me->GetThreatManager(); std::list<Unit*> TargetList; - Unit* currentVictim = mgr.GetCurrentVictim(); + Unit* currentVictim = mgr.GetLastVictim(); for (ThreatReference const* ref : mgr.GetSortedThreatList()) { if (Player* tempTarget = ref->GetVictim()->ToPlayer()) diff --git a/src/server/scripts/Outland/zone_hellfire_peninsula.cpp b/src/server/scripts/Outland/zone_hellfire_peninsula.cpp index df90cedc591..9e9df90c9d6 100644 --- a/src/server/scripts/Outland/zone_hellfire_peninsula.cpp +++ b/src/server/scripts/Outland/zone_hellfire_peninsula.cpp @@ -100,7 +100,7 @@ public: me->AddNpcFlag(UNIT_NPC_FLAG_QUESTGIVER); me->RemoveAllAuras(); me->CombatStop(true); - me->GetThreatManager().NotifyDisengaged(); + EngagementOver(); Talk(SAY_FREE); return; } @@ -973,7 +973,7 @@ public: me->RestoreFaction(); me->RemoveAllAuras(); me->CombatStop(true); - me->GetThreatManager().NotifyDisengaged(); + EngagementOver(); me->AddNpcFlag(UNIT_NPC_FLAG_QUESTGIVER); me->SetImmuneToPC(true); Talk(SAY_DEFEATED); diff --git a/src/server/scripts/Outland/zone_terokkar_forest.cpp b/src/server/scripts/Outland/zone_terokkar_forest.cpp index db507e8df63..e1cab1ce464 100644 --- a/src/server/scripts/Outland/zone_terokkar_forest.cpp +++ b/src/server/scripts/Outland/zone_terokkar_forest.cpp @@ -91,7 +91,7 @@ public: me->SetStandState(UNIT_STAND_STATE_SIT); me->RemoveAllAuras(); me->CombatStop(true); - me->GetThreatManager().NotifyDisengaged(); + EngagementOver(); UnkorUnfriendly_Timer = 60000; } diff --git a/src/server/scripts/Pet/pet_priest.cpp b/src/server/scripts/Pet/pet_priest.cpp index 83541959304..57a5e4ea979 100644 --- a/src/server/scripts/Pet/pet_priest.cpp +++ b/src/server/scripts/Pet/pet_priest.cpp @@ -50,7 +50,7 @@ class npc_pet_pri_lightwell : public CreatureScript return; me->CombatStop(true); - me->GetThreatManager().NotifyDisengaged(); + EngagementOver(); me->ResetPlayerDamageReq(); } }; |