TrinityCore
CombatManager.cpp
Go to the documentation of this file.
1/*
2 * This file is part of the TrinityCore Project. See AUTHORS file for Copyright information
3 *
4 * This program is free software; you can redistribute it and/or modify it
5 * under the terms of the GNU General Public License as published by the
6 * Free Software Foundation; either version 2 of the License, or (at your
7 * option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful, but WITHOUT
10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
12 * more details.
13 *
14 * You should have received a copy of the GNU General Public License along
15 * with this program. If not, see <http://www.gnu.org/licenses/>.
16 */
17
18#include "CombatManager.h"
19#include "Containers.h"
20#include "Creature.h"
21#include "CreatureAI.h"
22#include "Player.h"
23
24/*static*/ bool CombatManager::CanBeginCombat(Unit const* a, Unit const* b)
25{
26 // Checks combat validity before initial reference creation.
27 // For the combat to be valid...
28 // ...the two units need to be different
29 if (a == b)
30 return false;
31 // ...the two units need to be in the world
32 if (!a->IsInWorld() || !b->IsInWorld())
33 return false;
34 // ...the two units need to both be alive
35 if (!a->IsAlive() || !b->IsAlive())
36 return false;
37 // ...the two units need to be on the same map
38 if (a->GetMap() != b->GetMap())
39 return false;
40 // ...the two units need to be in the same phase
41 if (!WorldObject::InSamePhase(a, b))
42 return false;
44 return false;
46 return false;
47 // ... both units must be allowed to enter combat
49 return false;
50 if (a->IsFriendlyTo(b) || b->IsFriendlyTo(a))
51 return false;
54 // ...neither of the two units must be (owned by) a player with .gm on
55 if ((playerA && playerA->IsGameMaster()) || (playerB && playerB->IsGameMaster()))
56 return false;
57 return true;
58}
59
61{
62 // sequencing matters here - AI might do nasty stuff, so make sure refs are in a consistent state before you hand off!
63
64 // first, get rid of any threat that still exists...
67
68 // ...then, remove the references from both managers...
71
72 // ...update the combat state, which will potentially remove IN_COMBAT...
73 bool const needFirstAI = first->GetCombatManager().UpdateOwnerCombatState();
74 bool const needSecondAI = second->GetCombatManager().UpdateOwnerCombatState();
75
76 // ...and if that happened, also notify the AI of it...
77 if (needFirstAI)
78 if (UnitAI* firstAI = first->GetAI())
79 firstAI->JustExitedCombat();
80 if (needSecondAI)
81 if (UnitAI* secondAI = second->GetAI())
82 secondAI->JustExitedCombat();
83
84 // ...and finally clean up the reference object
85 delete this;
86}
87
89{
90 bool needFirstAI = false, needSecondAI = false;
92 {
93 _suppressFirst = false;
95 }
97 {
98 _suppressSecond = false;
100 }
101
102 if (needFirstAI)
104 if (needSecondAI)
106}
107
109{
110 Suppress(who);
112 if (UnitAI* ai = who->GetAI())
113 ai->JustExitedCombat();
114}
115
117{
118 if (_combatTimer <= tdiff)
119 return false;
120 _combatTimer -= tdiff;
121 return true;
122}
123
125{
127}
128
130{
131 ASSERT(_pveRefs.empty(), "CombatManager::~CombatManager - %s: we still have %zu PvE combat references, one of them is with %s", _owner->GetGUID().ToString().c_str(), _pveRefs.size(), _pveRefs.begin()->first.ToString().c_str());
132 ASSERT(_pvpRefs.empty(), "CombatManager::~CombatManager - %s: we still have %zu PvP combat references, one of them is with %s", _owner->GetGUID().ToString().c_str(), _pvpRefs.size(), _pvpRefs.begin()->first.ToString().c_str());
133}
134
136{
137 auto it = _pvpRefs.begin(), end = _pvpRefs.end();
138 while (it != end)
139 {
140 PvPCombatReference* const ref = it->second;
141 if (ref->first == _owner && !ref->Update(tdiff)) // only update if we're the first unit involved (otherwise double decrement)
142 {
143 it = _pvpRefs.erase(it), end = _pvpRefs.end(); // remove it from our refs first to prevent invalidation
144 ref->EndCombat(); // this will remove it from the other side
145 }
146 else
147 ++it;
148 }
149}
150
152{
153 for (auto const& [guid, ref] : _pveRefs)
154 if (!ref->IsSuppressedFor(_owner))
155 return true;
156 return false;
157}
158
160{
161 for (std::pair<ObjectGuid const, CombatReference*> const& reference : _pveRefs)
162 if (!reference.second->IsSuppressedFor(_owner) && reference.second->GetOther(_owner)->GetTypeId() == TYPEID_PLAYER)
163 return true;
164
165 return false;
166}
167
169{
170 for (auto const& pair : _pvpRefs)
171 if (!pair.second->IsSuppressedFor(_owner))
172 return true;
173 return false;
174}
175
177{
178 for (auto const& pair : _pveRefs)
179 if (!pair.second->IsSuppressedFor(_owner))
180 return pair.second->GetOther(_owner);
181 for (auto const& pair : _pvpRefs)
182 if (!pair.second->IsSuppressedFor(_owner))
183 return pair.second->GetOther(_owner);
184 return nullptr;
185}
186
187bool CombatManager::SetInCombatWith(Unit* who, bool addSecondUnitSuppressed)
188{
189 // Are we already in combat? If yes, refresh pvp combat
191 {
192 existingPvpRef->RefreshTimer();
193 existingPvpRef->Refresh();
194 return true;
195 }
197 {
198 existingPveRef->Refresh();
199 return true;
200 }
201
202 // Otherwise, check validity...
204 return false;
205
206 // ...then create new reference
207 CombatReference* ref;
209 ref = new PvPCombatReference(_owner, who);
210 else
211 ref = new CombatReference(_owner, who);
212
213 if (addSecondUnitSuppressed)
214 ref->Suppress(who);
215
216 // ...and insert it into both managers
217 PutReference(who->GetGUID(), ref);
219
220 // now, sequencing is important - first we update the combat state, which will set both units in combat and do non-AI combat start stuff
221 bool const needSelfAI = UpdateOwnerCombatState();
222 bool const needOtherAI = who->GetCombatManager().UpdateOwnerCombatState();
223
224 // then, we finally notify the AI (if necessary) and let it safely do whatever it feels like
225 if (needSelfAI)
227 if (needOtherAI)
229 return IsInCombatWith(who);
230}
231
233{
234 return (_pveRefs.find(guid) != _pveRefs.end()) || (_pvpRefs.find(guid) != _pvpRefs.end());
235}
236
238{
239 return IsInCombatWith(who->GetGUID());
240}
241
243{
244 CombatManager const& mgr = who->GetCombatManager();
245 for (auto& ref : mgr._pveRefs)
246 {
247 if (!IsInCombatWith(ref.first))
248 {
249 Unit* target = ref.second->GetOther(who);
252 continue;
253 SetInCombatWith(target);
254 }
255 }
256 for (auto& ref : mgr._pvpRefs)
257 {
258 Unit* target = ref.second->GetOther(who);
261 continue;
262 SetInCombatWith(target);
263 }
264}
265
266void CombatManager::EndCombatBeyondRange(float range, bool includingPvP)
267{
268 auto it = _pveRefs.begin(), end = _pveRefs.end();
269 while (it != end)
270 {
271 CombatReference* const ref = it->second;
272 if (!ref->first->IsWithinDistInMap(ref->second, range))
273 {
274 it = _pveRefs.erase(it), end = _pveRefs.end(); // erase manually here to avoid iterator invalidation
275 ref->EndCombat();
276 }
277 else
278 ++it;
279 }
280
281 if (!includingPvP)
282 return;
283
284 auto it2 = _pvpRefs.begin(), end2 = _pvpRefs.end();
285 while (it2 != end2)
286 {
287 CombatReference* const ref = it2->second;
288 if (!ref->first->IsWithinDistInMap(ref->second, range))
289 {
290 it2 = _pvpRefs.erase(it2), end2 = _pvpRefs.end(); // erase manually here to avoid iterator invalidation
291 ref->EndCombat();
292 }
293 else
294 ++it2;
295 }
296}
297
298void CombatManager::SuppressPvPCombat(UnitFilter* unitFilter /*= nullptr*/)
299{
300 for (auto const& [guid, combatRef] : _pvpRefs)
301 if (!unitFilter || unitFilter(combatRef->GetOther(_owner)))
302 combatRef->Suppress(_owner);
303
305 if (UnitAI* ownerAI = _owner->GetAI())
306 ownerAI->JustExitedCombat();
307}
308
309void CombatManager::EndAllPvECombat(UnitFilter* unitFilter /*= nullptr*/)
310{
311 // cannot have threat without combat
314
315 std::vector<CombatReference*> combatReferencesToRemove;
316 combatReferencesToRemove.reserve(_pveRefs.size());
317 for (auto const& [guid, combatRef] : _pveRefs)
318 if (!unitFilter || unitFilter(combatRef->GetOther(_owner)))
319 combatReferencesToRemove.push_back(combatRef);
320
321 for (CombatReference* combatRef : combatReferencesToRemove)
322 combatRef->EndCombat();
323}
324
326{
327 auto it = _pveRefs.begin(), end = _pveRefs.end();
328 while (it != end)
329 {
330 CombatReference* const ref = it->second;
332 {
333 it = _pveRefs.erase(it), end = _pveRefs.end(); // erase manually here to avoid iterator invalidation
334 ref->EndCombat();
335 }
336 else
337 ++it;
338 }
339
340 auto it2 = _pvpRefs.begin(), end2 = _pvpRefs.end();
341 while (it2 != end2)
342 {
343 CombatReference* const ref = it2->second;
345 {
346 it2 = _pvpRefs.erase(it2), end2 = _pvpRefs.end(); // erase manually here to avoid iterator invalidation
347 ref->EndCombat();
348 }
349 else
350 ++it2;
351 }
352}
353
354void CombatManager::EndAllPvPCombat(UnitFilter* unitFilter /*= nullptr*/)
355{
356 std::vector<CombatReference*> combatReferencesToRemove;
357 combatReferencesToRemove.reserve(_pvpRefs.size());
358 for (auto const& [guid, combatRef] : _pvpRefs)
359 if (!unitFilter || unitFilter(combatRef->GetOther(_owner)))
360 combatReferencesToRemove.push_back(combatRef);
361
362 for (CombatReference* combatRef : combatReferencesToRemove)
363 combatRef->EndCombat();
364}
365
366/*static*/ void CombatManager::NotifyAICombat(Unit* me, Unit* other)
367{
368 if (UnitAI* ai = me->GetAI())
369 ai->JustEnteredCombat(other);
370}
371
373{
374 if (ref->_isPvP)
375 {
376 auto& inMap = _pvpRefs[guid];
377 ASSERT(!inMap, "Duplicate combat state at %p being inserted for %s vs %s - memory leak!", ref, _owner->GetGUID().ToString().c_str(), guid.ToString().c_str());
378 inMap = static_cast<PvPCombatReference*>(ref);
379 }
380 else
381 {
382 auto& inMap = _pveRefs[guid];
383 ASSERT(!inMap, "Duplicate combat state at %p being inserted for %s vs %s - memory leak!", ref, _owner->GetGUID().ToString().c_str(), guid.ToString().c_str());
384 inMap = ref;
385 }
386}
387
388void CombatManager::PurgeReference(ObjectGuid const& guid, bool pvp)
389{
390 if (pvp)
391 _pvpRefs.erase(guid);
392 else
393 _pveRefs.erase(guid);
394}
395
397{
398 bool const combatState = HasCombat();
399 if (combatState == _owner->IsInCombat())
400 return false;
401
402 if (combatState)
403 {
406 if (_owner->GetTypeId() != TYPEID_UNIT)
408 }
409 else
410 {
413 if (_owner->GetTypeId() != TYPEID_UNIT)
415 }
416
417 if (Unit* master = _owner->GetCharmerOrOwner())
418 master->UpdatePetCombatState();
419
420 return true;
421}
uint32_t uint32
Definition: Define.h:142
#define ASSERT
Definition: Errors.h:68
@ TYPEID_UNIT
Definition: ObjectGuid.h:40
@ TYPEID_PLAYER
Definition: ObjectGuid.h:41
@ UNIT_FLAG_IN_COMBAT
Definition: UnitDefines.h:163
@ UNIT_FLAG_PLAYER_CONTROLLED
Definition: UnitDefines.h:147
@ UNIT_STATE_EVADE
Definition: Unit.h:277
@ UNIT_STATE_IN_FLIGHT
Definition: Unit.h:263
void SuppressPvPCombat(UnitFilter *unitFilter=nullptr)
void EndAllPvECombat(UnitFilter *unitFilter=nullptr)
std::unordered_map< ObjectGuid, PvPCombatReference * > _pvpRefs
bool HasCombat() const
void PutReference(ObjectGuid const &guid, CombatReference *ref)
static bool CanBeginCombat(Unit const *a, Unit const *b)
Unit * GetAnyTarget() const
Unit *const _owner
void InheritCombatStatesFrom(Unit const *who)
void RevalidateCombat()
friend struct CombatReference
std::unordered_map< ObjectGuid, CombatReference * > _pveRefs
void Update(uint32 tdiff)
void EndCombatBeyondRange(float range, bool includingPvP=false)
bool HasPvECombat() const
static void NotifyAICombat(Unit *me, Unit *other)
bool HasPvECombatWithPlayers() const
bool UpdateOwnerCombatState() const
bool HasPvPCombat() const
bool(Unit const *otherUnit) UnitFilter
friend struct PvPCombatReference
bool SetInCombatWith(Unit *who, bool addSecondUnitSuppressed=false)
bool IsInCombatWith(ObjectGuid const &who) const
void EndAllPvPCombat(UnitFilter *unitFilter=nullptr)
void PurgeReference(ObjectGuid const &guid, bool pvp)
std::string ToString() const
Definition: ObjectGuid.cpp:554
bool IsInWorld() const
Definition: Object.h:154
TypeID GetTypeId() const
Definition: Object.h:173
static ObjectGuid GetGUID(Object const *o)
Definition: Object.h:159
bool IsGameMaster() const
Definition: Player.h:1178
void RemoveMeFromThreatLists(bool(*unitFilter)(Unit const *otherUnit))
void ClearThreat(Unit *target)
Definition: UnitAI.h:50
Definition: Unit.h:627
ThreatManager & GetThreatManager()
Definition: Unit.h:1063
virtual void AtExitCombat()
Definition: Unit.cpp:8670
virtual void AtEngage(Unit *)
Definition: Unit.h:1930
bool HasUnitFlag(UnitFlags flags) const
Definition: Unit.h:832
virtual void AtEnterCombat()
Definition: Unit.cpp:8647
virtual void AtDisengage()
Definition: Unit.h:1931
bool IsAlive() const
Definition: Unit.h:1164
UnitAI * GetAI() const
Definition: Unit.h:660
bool IsImmuneToNPC() const
Definition: Unit.h:1033
Unit * GetCharmerOrOwner() const
Definition: Unit.h:1200
bool HasUnitState(const uint32 f) const
Definition: Unit.h:732
bool IsControlledByPlayer() const
Definition: Unit.h:1193
bool IsCombatDisallowed() const
Definition: Unit.h:1796
CombatManager & GetCombatManager()
Definition: Unit.h:1023
void SetUnitFlag(UnitFlags flags)
Definition: Unit.h:833
bool IsImmuneToPC() const
Definition: Unit.h:1030
bool IsInCombat() const
Definition: Unit.h:1043
void RemoveUnitFlag(UnitFlags flags)
Definition: Unit.h:834
bool InSamePhase(PhaseShift const &phaseShift) const
Definition: Object.h:527
Map * GetMap() const
Definition: Object.h:624
Player * GetCharmerOrOwnerPlayerOrPlayerItself() const
Definition: Object.cpp:2252
bool IsWithinDistInMap(WorldObject const *obj, float dist2compare, bool is3D=true, bool incOwnRadius=true, bool incTargetRadius=true) const
Definition: Object.cpp:1147
bool IsFriendlyTo(WorldObject const *target) const
Definition: Object.cpp:2865
auto MapGetValuePtr(M &map, typename M::key_type const &key)
Definition: MapUtils.h:29
bool const _isPvP
Definition: CombatManager.h:56
Unit *const second
Definition: CombatManager.h:55
Unit *const first
Definition: CombatManager.h:54
void SuppressFor(Unit *who)
Unit * GetOther(Unit const *me) const
Definition: CombatManager.h:57
void Suppress(Unit *who)
Definition: CombatManager.h:73
bool Update(uint32 tdiff)
static const uint32 PVP_COMBAT_TIMEOUT
Definition: CombatManager.h:84