TrinityCore
Loading...
Searching...
No Matches
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 "Unit.h"
22#include "CreatureAI.h"
23#include "Player.h"
24
25/*static*/ bool CombatManager::CanBeginCombat(Unit const* a, Unit const* b)
26{
27 // Checks combat validity before initial reference creation.
28 // For the combat to be valid...
29 // ...the two units need to be different
30 if (a == b)
31 return false;
32 // ...the two units need to be in the world
33 if (!a->IsInWorld() || !b->IsInWorld())
34 return false;
35 // ...the two units need to both be alive
36 if (!a->IsAlive() || !b->IsAlive())
37 return false;
38 // ...the two units need to be on the same map
39 if (a->GetMap() != b->GetMap())
40 return false;
41 // ...the two units need to be in the same phase
42 if (!WorldObject::InSamePhase(a, b))
43 return false;
45 return false;
47 return false;
48 // ... both units must be allowed to enter combat
50 return false;
51 if (a->IsFriendlyTo(b) || b->IsFriendlyTo(a))
52 return false;
55 // ...neither of the two units must be (owned by) a player with .gm on
56 if ((playerA && playerA->IsGameMaster()) || (playerB && playerB->IsGameMaster()))
57 return false;
58 return true;
59}
60
62{
63 // sequencing matters here - AI might do nasty stuff, so make sure refs are in a consistent state before you hand off!
64
65 // first, get rid of any threat that still exists...
68
69 // ...then, remove the references from both managers...
72
73 // ...update the combat state, which will potentially remove IN_COMBAT...
74 bool const needFirstAI = first->GetCombatManager().UpdateOwnerCombatState();
75 bool const needSecondAI = second->GetCombatManager().UpdateOwnerCombatState();
76
77 // ...and if that happened, also notify the AI of it...
78 if (needFirstAI)
79 if (UnitAI* firstAI = first->GetAI())
80 firstAI->JustExitedCombat();
81 if (needSecondAI)
82 if (UnitAI* secondAI = second->GetAI())
83 secondAI->JustExitedCombat();
84
85 // ...and finally clean up the reference object
86 delete this;
87}
88
90{
91 bool needFirstAI = false, needSecondAI = false;
93 {
94 _suppressFirst = false;
96 }
98 {
99 _suppressSecond = false;
101 }
102
103 if (needFirstAI)
105 if (needSecondAI)
107}
108
110{
111 Suppress(who);
113 if (UnitAI* ai = who->GetAI())
114 ai->JustExitedCombat();
115}
116
118{
119 if (_combatTimer <= tdiff)
120 return false;
121 _combatTimer -= tdiff;
122 return true;
123}
124
129
131{
132 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());
133 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());
134}
135
137{
138 auto it = _pvpRefs.begin(), end = _pvpRefs.end();
139 while (it != end)
140 {
141 PvPCombatReference* const ref = it->second;
142 if (ref->first == _owner && !ref->Update(tdiff)) // only update if we're the first unit involved (otherwise double decrement)
143 {
144 it = _pvpRefs.erase(it), end = _pvpRefs.end(); // remove it from our refs first to prevent invalidation
145 ref->EndCombat(); // this will remove it from the other side
146 }
147 else
148 ++it;
149 }
150}
151
153{
154 for (auto const& [guid, ref] : _pveRefs)
155 if (!ref->IsSuppressedFor(_owner))
156 return true;
157 return false;
158}
159
161{
162 for (std::pair<ObjectGuid const, CombatReference*> const& reference : _pveRefs)
163 if (!reference.second->IsSuppressedFor(_owner) && reference.second->GetOther(_owner)->GetTypeId() == TYPEID_PLAYER)
164 return true;
165
166 return false;
167}
168
170{
171 for (auto const& pair : _pvpRefs)
172 if (!pair.second->IsSuppressedFor(_owner))
173 return true;
174 return false;
175}
176
178{
179 for (auto const& pair : _pveRefs)
180 if (!pair.second->IsSuppressedFor(_owner))
181 return pair.second->GetOther(_owner);
182 for (auto const& pair : _pvpRefs)
183 if (!pair.second->IsSuppressedFor(_owner))
184 return pair.second->GetOther(_owner);
185 return nullptr;
186}
187
188bool CombatManager::SetInCombatWith(Unit* who, bool addSecondUnitSuppressed)
189{
190 // Are we already in combat? If yes, refresh pvp combat
192 {
193 existingPvpRef->RefreshTimer();
194 existingPvpRef->Refresh();
195 return true;
196 }
198 {
199 existingPveRef->Refresh();
200 return true;
201 }
202
203 // Otherwise, check validity...
205 return false;
206
207 // ...then create new reference
208 CombatReference* ref;
210 ref = new PvPCombatReference(_owner, who);
211 else
212 ref = new CombatReference(_owner, who);
213
214 if (addSecondUnitSuppressed)
215 ref->Suppress(who);
216
217 // ...and insert it into both managers
218 PutReference(who->GetGUID(), ref);
220
221 // now, sequencing is important - first we update the combat state, which will set both units in combat and do non-AI combat start stuff
222 bool const needSelfAI = UpdateOwnerCombatState();
223 bool const needOtherAI = who->GetCombatManager().UpdateOwnerCombatState();
224
225 // then, we finally notify the AI (if necessary) and let it safely do whatever it feels like
226 if (needSelfAI)
228 if (needOtherAI)
230 return IsInCombatWith(who);
231}
232
234{
235 return (_pveRefs.find(guid) != _pveRefs.end()) || (_pvpRefs.find(guid) != _pvpRefs.end());
236}
237
239{
240 return IsInCombatWith(who->GetGUID());
241}
242
244{
245 CombatManager const& mgr = who->GetCombatManager();
246 for (auto& ref : mgr._pveRefs)
247 {
248 if (!IsInCombatWith(ref.first))
249 {
250 Unit* target = ref.second->GetOther(who);
253 continue;
254 SetInCombatWith(target);
255 }
256 }
257 for (auto& ref : mgr._pvpRefs)
258 {
259 Unit* target = ref.second->GetOther(who);
262 continue;
263 SetInCombatWith(target);
264 }
265}
266
267void CombatManager::EndCombatBeyondRange(float range, bool includingPvP)
268{
269 auto it = _pveRefs.begin(), end = _pveRefs.end();
270 while (it != end)
271 {
272 CombatReference* const ref = it->second;
273 if (!ref->first->IsWithinDistInMap(ref->second, range))
274 {
275 it = _pveRefs.erase(it), end = _pveRefs.end(); // erase manually here to avoid iterator invalidation
276 ref->EndCombat();
277 }
278 else
279 ++it;
280 }
281
282 if (!includingPvP)
283 return;
284
285 auto it2 = _pvpRefs.begin(), end2 = _pvpRefs.end();
286 while (it2 != end2)
287 {
288 CombatReference* const ref = it2->second;
289 if (!ref->first->IsWithinDistInMap(ref->second, range))
290 {
291 it2 = _pvpRefs.erase(it2), end2 = _pvpRefs.end(); // erase manually here to avoid iterator invalidation
292 ref->EndCombat();
293 }
294 else
295 ++it2;
296 }
297}
298
300{
301 for (auto const& pair : _pvpRefs)
302 pair.second->Suppress(_owner);
304 if (UnitAI* ownerAI = _owner->GetAI())
305 ownerAI->JustExitedCombat();
306}
307
309{
310 // cannot have threat without combat
313 while (!_pveRefs.empty())
314 _pveRefs.begin()->second->EndCombat();
315}
316
318{
319 auto it = _pveRefs.begin(), end = _pveRefs.end();
320 while (it != end)
321 {
322 CombatReference* const ref = it->second;
324 {
325 it = _pveRefs.erase(it), end = _pveRefs.end(); // erase manually here to avoid iterator invalidation
326 ref->EndCombat();
327 }
328 else
329 ++it;
330 }
331
332 auto it2 = _pvpRefs.begin(), end2 = _pvpRefs.end();
333 while (it2 != end2)
334 {
335 CombatReference* const ref = it2->second;
337 {
338 it2 = _pvpRefs.erase(it2), end2 = _pvpRefs.end(); // erase manually here to avoid iterator invalidation
339 ref->EndCombat();
340 }
341 else
342 ++it2;
343 }
344}
345
347{
348 while (!_pvpRefs.empty())
349 _pvpRefs.begin()->second->EndCombat();
350}
351
352/*static*/ void CombatManager::NotifyAICombat(Unit* me, Unit* other)
353{
354 if (UnitAI* ai = me->GetAI())
355 ai->JustEnteredCombat(other);
356}
357
359{
360 if (ref->_isPvP)
361 {
362 auto& inMap = _pvpRefs[guid];
363 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());
364 inMap = static_cast<PvPCombatReference*>(ref);
365 }
366 else
367 {
368 auto& inMap = _pveRefs[guid];
369 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());
370 inMap = ref;
371 }
372}
373
374void CombatManager::PurgeReference(ObjectGuid const& guid, bool pvp)
375{
376 if (pvp)
377 _pvpRefs.erase(guid);
378 else
379 _pveRefs.erase(guid);
380}
381
383{
384 bool const combatState = HasCombat();
385 if (combatState == _owner->IsInCombat())
386 return false;
387
388 if (combatState)
389 {
392 if (_owner->GetTypeId() != TYPEID_UNIT)
394 }
395 else
396 {
399 if (_owner->GetTypeId() != TYPEID_UNIT)
401 }
402
403 if (Unit* master = _owner->GetCharmerOrOwner())
404 master->UpdatePetCombatState();
405
406 return true;
407}
uint32_t uint32
Definition Define.h:133
#define ASSERT
Definition Errors.h:68
@ TYPEID_UNIT
Definition ObjectGuid.h:38
@ TYPEID_PLAYER
Definition ObjectGuid.h:39
@ UNIT_FLAG_IN_COMBAT
@ UNIT_FLAG_PLAYER_CONTROLLED
@ UNIT_STATE_EVADE
Definition Unit.h:242
@ UNIT_STATE_IN_FLIGHT
Definition Unit.h:228
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)
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
friend struct PvPCombatReference
bool SetInCombatWith(Unit *who, bool addSecondUnitSuppressed=false)
bool IsInCombatWith(ObjectGuid const &who) const
void PurgeReference(ObjectGuid const &guid, bool pvp)
std::string ToString() const
bool IsInWorld() const
Definition Object.h:73
TypeID GetTypeId() const
Definition Object.h:93
static ObjectGuid GetGUID(Object const *o)
Definition Object.h:78
bool IsGameMaster() const
Definition Player.h:998
void RemoveMeFromThreatLists()
void ClearThreat(Unit *target)
Definition Unit.h:769
ThreatManager & GetThreatManager()
Definition Unit.h:1155
virtual void AtExitCombat()
Definition Unit.cpp:8780
bool HasUnitFlag(UnitFlags flags) const
Definition Unit.h:953
virtual void AtDisengage()
Definition Unit.h:1917
bool IsAlive() const
Definition Unit.h:1234
UnitAI * GetAI() const
Definition Unit.h:800
bool IsImmuneToNPC() const
Definition Unit.h:1140
Unit * GetCharmerOrOwner() const
Definition Unit.h:1265
virtual void AtEnterCombat()
Definition Unit.h:1913
bool HasUnitState(const uint32 f) const
Definition Unit.h:876
bool IsControlledByPlayer() const
Definition Unit.h:1258
bool IsCombatDisallowed() const
Definition Unit.h:1826
virtual void AtEngage(Unit *target)
Definition Unit.cpp:8785
CombatManager & GetCombatManager()
Definition Unit.h:1130
void SetUnitFlag(UnitFlags flags)
Definition Unit.h:954
bool IsImmuneToPC() const
Definition Unit.h:1137
bool IsInCombat() const
Definition Unit.h:1144
void RemoveUnitFlag(UnitFlags flags)
Definition Unit.h:955
Map * GetMap() const
Definition Object.h:449
bool InSamePhase(uint32 phasemask) const
Definition Object.h:369
Player * GetCharmerOrOwnerPlayerOrPlayerItself() const
Definition Object.cpp:2203
bool IsWithinDistInMap(WorldObject const *obj, float dist2compare, bool is3D=true, bool incOwnRadius=true, bool incTargetRadius=true) const
Definition Object.cpp:1192
bool IsFriendlyTo(WorldObject const *target) const
Definition Object.cpp:2801
auto MapGetValuePtr(M &map, typename M::key_type const &key)
Definition MapUtils.h:29
bool const _isPvP
Unit *const second
Unit *const first
void SuppressFor(Unit *who)
Unit * GetOther(Unit const *me) const
void Suppress(Unit *who)
bool Update(uint32 tdiff)
static const uint32 PVP_COMBAT_TIMEOUT