TrinityCore
Loading...
Searching...
No Matches
ThreatManager.h
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 #ifndef TRINITY_THREATMANAGER_H
19 #define TRINITY_THREATMANAGER_H
20
21#include "Common.h"
22#include "IteratorPair.h"
23#include "ObjectGuid.h"
24#include "SharedDefines.h"
25#include <array>
26#include <memory>
27#include <unordered_map>
28#include <vector>
29
30class Creature;
31class Unit;
32class SpellInfo;
33
34/********************************************************************************************************************************************************\
35 * DEV DOCUMENTATION: THREAT SYSTEM *
36 * (future devs: please keep this up-to-date if you change the system) *
37 * The threat system works based on dynamically allocated threat list entries. *
38 * *
39 * Each such entry is a ThreatReference object, which is always stored in exactly three places: *
40 * - The threatened unit's (from now: reference "owner") sorted and unsorted threat lists *
41 * - The threatening unit's (from now: reference "victim") threatened-by-me list *
42 * A ThreatReference object carries the following implicit guarantees: *
43 * - Both owner and victim are valid units, which are currently in the world. Neither can be nullptr. *
44 * - There is an active combat reference between owner and victim. *
45 * *
46 * Note that (threat => combat) is a strong guarantee provided in conjunction with CombatManager. Thus: *
47 * - 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!) *
48 * - Ending combat between two units will also delete any threat references that may exist between them. *
49 * *
50 * To manage a creature's threat list, ThreatManager maintains a heap of threat reference const pointers. *
51 * This heap is kept well-structured in all methods that modify ThreatReference, and is used to select the next target. *
52 * *
53 * Selection uses the following properties on ThreatReference, in order: *
54 * - Online state (one of ONLINE, SUPPRESSED, OFFLINE): *
55 * - ONLINE: Normal threat state, target is valid and attackable *
56 * - SUPPRESSED: Target is attackable, but inopportune. This is used for targets under immunity effects and damage-breaking CC. *
57 * Targets with SUPPRESSED threat can still be valid targets, but any target with ONLINE threat will be preferred. *
58 * - OFFLINE: The target is, for whatever reason, not valid at this time (for example, IMMUNE_TO_X flags or game master state). *
59 * These targets can never be selected, and GetCurrentVictim will return nullptr if all targets are OFFLINE (typically causing evade). *
60 * - Related methods: GetOnlineState, IsOnline, IsAvailable, IsOffline *
61 * - Taunt state (one of TAUNT, NONE, DETAUNT), the names speak for themselves *
62 * - Related methods: GetTauntState, IsTaunting, IsDetaunted *
63 * - Actual threat value (GetThreat) *
64 * *
65 * The current (= last selected) victim can be accessed using GetCurrentVictim. *
66 * Beyond that, ThreatManager has a variety of helpers and notifiers, which are documented inline below. *
67 * *
68 * SPECIAL NOTE: Please be aware that any iterator may be invalidated if you modify a ThreatReference. The heap holds const pointers for a reason, but *
69 * that doesn't mean you're scot free. A variety of actions (casting spells, teleporting units, and so forth) can cause changes to *
70 * the threat list. Use with care - or default to GetModifiableThreatList(), which inherently copies entries. *
71\********************************************************************************************************************************************************/
72
73class ThreatReference;
75{
77 bool operator()(ThreatReference const* a, ThreatReference const* b) const;
78};
79
81{
82 None = 0x0,
83 UpdateNeeded = 0x1,
85};
86
88
89// Please check Game/Combat/ThreatManager.h for documentation on how this class works!
91{
92 public:
93 class Heap;
95 static const uint32 THREAT_UPDATE_INTERVAL = 1000u;
96
97 static bool CanHaveThreatList(Unit const* who);
98
99 ThreatManager(Unit* owner);
101 // called from ::Create methods just after construction (once all fields on owner have been populated)
102 // should not be called from anywhere else
103 void Initialize();
104 // called from Creature::Update (only creatures can have their own threat list)
105 // should not be called from anywhere else
106 void Update(uint32 tdiff);
107
108 // never nullptr
109 Unit* GetOwner() const { return _owner; }
110 // can our owner have a threat list?
111 // identical to ThreatManager::CanHaveThreatList(GetOwner())
112 bool CanHaveThreatList() const { return _ownerCanHaveThreatList; }
113 // returns the current victim - this can be nullptr if owner's threat list is empty, or has only offline targets
114 Unit* GetCurrentVictim();
115 Unit* GetLastVictim() const;
116 // returns an arbitrary non-offline victim from owner's threat list if one exists, nullptr otherwise
117 Unit* GetAnyTarget() const;
118
119 // are there any entries in owner's threat list?
120 bool IsThreatListEmpty(bool includeOffline = false) const;
121 // is there a threat list entry on owner's threat list with victim == who?
122 bool IsThreatenedBy(ObjectGuid const& who, bool includeOffline = false) const;
123 // is there a threat list entry on owner's threat list with victim == who?
124 bool IsThreatenedBy(Unit const* who, bool includeOffline = false) const;
125 // returns ThreatReference amount if a ref exists, 0.0f otherwise
126 float GetThreat(Unit const* who, bool includeOffline = false) const;
127 size_t GetThreatListSize() const;
128 uint32 GetThreatListPlayerCount(bool includeOffline = false) const;
129 // fastest of the three threat list getters - gets the threat list in "arbitrary" order
130 // iterators will invalidate on adding/removing entries from the threat list; slightly less finicky than GetSorted.
132 // slightly slower than GetUnsorted, but, well...sorted - only use it if you need the sorted property, of course
133 // this iterator pair will invalidate on any modification (even indirect) of the threat list; spell casts and similar can all induce this!
134 // note: current tank is NOT guaranteed to be the first entry in this list - check GetLastVictim separately if you want that!
136 // slowest of the three threat list getters (by far), but lets you modify the threat references - this is also sorted
137 std::vector<ThreatReference*> GetModifiableThreatList();
138
139 // does any unit have a threat list entry with victim == this.owner?
140 bool IsThreateningAnyone(bool includeOffline = false) const;
141 // is there a threat list entry on who's threat list for this.owner?
142 bool IsThreateningTo(ObjectGuid const& who, bool includeOffline = false) const;
143 // is there a threat list entry on who's threat list for this.owner?
144 bool IsThreateningTo(Unit const* who, bool includeOffline = false) const;
145 auto const& GetThreatenedByMeList() const { return _threatenedByMe; }
146
147 // Notify the ThreatManager that its owner may now be suppressed on others' threat lists (immunity or damage-breakable CC being applied)
148 void EvaluateSuppressed(bool canExpire = false);
150 void AddThreat(Unit* target, float amount, SpellInfo const* spell = nullptr, bool ignoreModifiers = false, bool ignoreRedirects = false);
151 void ScaleThreat(Unit* target, float factor);
152 // Modify target's threat by +percent%
153 void ModifyThreatByPercent(Unit* target, int32 percent) { if (percent) ScaleThreat(target, 0.01f*float(100 + percent)); }
154 // Resets the specified unit's threat to zero
155 void ResetThreat(Unit* target) { ScaleThreat(target, 0.0f); }
156 // Sets the specified unit's threat to be equal to the highest entry on the threat list
157 void MatchUnitThreatToHighestThreat(Unit* target);
158 // Notify the ThreatManager that we have a new taunt aura (or a taunt aura expired)
159 void TauntUpdate();
160 // Sets all threat refs in owner's threat list to have zero threat
161 void ResetAllThreat();
162 // Removes specified target from the threat list
163 void ClearThreat(Unit* target);
164 void ClearThreat(ThreatReference* ref);
165 // Removes all targets from the threat list (will cause evade in UpdateVictim if called)
166 void ClearAllThreat();
167
168 // Fixate on the passed target; this target will always be selected until the fixate is cleared
169 // (if the target is not in the threat list, does nothing)
170 void FixateTarget(Unit* target);
171 void ClearFixate() { FixateTarget(nullptr); }
172 Unit* GetFixateTarget() const;
173
175 // what it says on the tin - call AddThreat on everything that's threatened by us with the specified params
176 void ForwardThreatForAssistingMe(Unit* assistant, float baseAmount, SpellInfo const* spell = nullptr, bool ignoreModifiers = false);
177 // delete all ThreatReferences with victim == owner
178 void RemoveMeFromThreatLists();
179 // re-calculates the temporary threat modifier from auras on myself
180 void UpdateMyTempModifiers();
181 // re-calculate SPELL_AURA_MOD_THREAT modifiers
182 void UpdateMySpellSchoolModifiers();
183
185 // Register a redirection effect that redirects pct% of threat generated by owner to victim
186 void RegisterRedirectThreat(uint32 spellId, ObjectGuid const& victim, uint32 pct);
187 // Unregister a redirection effort for all victims
188 void UnregisterRedirectThreat(uint32 spellId);
189 // Unregister a redirection effect for a specific victim
190 void UnregisterRedirectThreat(uint32 spellId, ObjectGuid const& victim);
191
192 private:
193 Unit* const _owner;
195
197 static bool CompareReferencesLT(ThreatReference const* a, ThreatReference const* b, float aWeight);
198 static float CalculateModifiedThreat(float threat, Unit const* victim, SpellInfo const* spell);
199
200 // send opcodes (all for my own threat list)
201 void SendClearAllThreatToClients() const;
202 void SendRemoveToClients(Unit const* victim) const;
203 void SendThreatListToClients(bool newHighest) const;
204
206 void PutThreatListRef(ObjectGuid const& guid, ThreatReference* ref);
207 void PurgeThreatListRef(ObjectGuid const& guid);
208
211 std::unique_ptr<Heap> _sortedThreatList;
212 std::unordered_map<ObjectGuid, ThreatReference*> _myThreatListEntries;
213
214 // AI notifies are delayed to ensure we are in a consistent state before we call out to arbitrary logic
215 // threat references might register themselves here when ::UpdateOffline() is called - MAKE SURE THIS IS PROCESSED JUST BEFORE YOU EXIT THREATMANAGER LOGIC
216 void ProcessAIUpdates();
217 void RegisterForAIUpdate(ObjectGuid const& guid) { _needsAIUpdate.push_back(guid); }
218 std::vector<ObjectGuid> _needsAIUpdate;
219
220 // picks a new victim - called from ::Update periodically
221 void UpdateVictim();
222 ThreatReference const* ReselectVictim();
225
227 void PutThreatenedByMeRef(ObjectGuid const& guid, ThreatReference* ref);
228 void PurgeThreatenedByMeRef(ObjectGuid const& guid);
229 std::unordered_map<ObjectGuid, ThreatReference*> _threatenedByMe; // these refs are entries for myself on other units' threat lists
230 std::array<float, MAX_SPELL_SCHOOL> _singleSchoolModifiers; // most spells are single school - we pre-calculate these and store them
231 mutable std::unordered_map<std::underlying_type<SpellSchoolMask>::type, float> _multiSchoolModifiers; // these are calculated on demand
232
233 // redirect system (is kind of dumb, but that's because none of the redirection spells actually have any aura effect associated with them, so spellscript needs to deal with it)
234 void UpdateRedirectInfo();
235 std::vector<std::pair<ObjectGuid, uint32>> _redirectInfo; // current redirection targets and percentages (updated from registry in ThreatManager::UpdateRedirectInfo)
236 std::unordered_map<uint32, std::unordered_map<ObjectGuid, uint32>> _redirectRegistry; // spellid -> (victim -> pct); all redirection effects on us (removal individually managed by spell scripts because blizzard is dumb)
237
238 public:
239 ThreatManager(ThreatManager const&) = delete;
241
243 {
244 private:
245 std::function<ThreatReference const* ()> _generator;
247
249 explicit ThreatListIterator(std::function<ThreatReference const* ()>&& generator)
250 : _generator(std::move(generator)), _current(_generator()) {}
251
252 public:
253 ThreatReference const* operator*() const { return _current; }
254 ThreatReference const* operator->() const { return _current; }
255 ThreatListIterator& operator++() { _current = _generator(); return *this; }
256 bool operator==(ThreatListIterator const& o) const { return _current == o._current; }
257 bool operator!=(ThreatListIterator const& o) const { return _current != o._current; }
258 bool operator==(std::nullptr_t) const { return _current == nullptr; }
259 bool operator!=(std::nullptr_t) const { return _current != nullptr; }
260 };
261
262 friend class ThreatReference;
266};
267
268// Please check Game/Combat/ThreatManager.h for documentation on how this class works!
270{
271 public:
272 enum TauntState : uint32 { TAUNT_STATE_DETAUNT = 0, TAUNT_STATE_NONE = 1, TAUNT_STATE_TAUNT = 2 };
273 enum OnlineState { ONLINE_STATE_ONLINE = 2, ONLINE_STATE_SUPPRESSED = 1, ONLINE_STATE_OFFLINE = 0 };
274
275 Creature* GetOwner() const { return _owner; }
276 Unit* GetVictim() const { return _victim; }
277 float GetThreat() const { return std::max<float>(_baseAmount + (float)_tempModifier, 0.0f); }
278 OnlineState GetOnlineState() const { return _online; }
279 bool IsOnline() const { return (_online >= ONLINE_STATE_ONLINE); }
280 bool IsAvailable() const { return (_online > ONLINE_STATE_OFFLINE); }
281 bool IsSuppressed() const { return (_online == ONLINE_STATE_SUPPRESSED); }
282 bool IsOffline() const { return (_online <= ONLINE_STATE_OFFLINE); }
283 TauntState GetTauntState() const { return IsTaunting() ? TAUNT_STATE_TAUNT : _taunted; }
284 bool IsTaunting() const { return _taunted >= TAUNT_STATE_TAUNT; }
285 bool IsDetaunted() const { return _taunted == TAUNT_STATE_DETAUNT; }
286
287 void AddThreat(float amount);
288 void ScaleThreat(float factor);
289 void ModifyThreatByPercent(int32 percent) { if (percent) ScaleThreat(0.01f*float(100 + percent)); }
290 void UpdateOffline();
291
292 void ClearThreat(); // dealloc's this
293
294 protected:
295 static bool FlagsAllowFighting(Unit const* a, Unit const* b);
296
297 explicit ThreatReference(ThreatManager* mgr, Unit* victim) :
298 _owner(reinterpret_cast<Creature*>(mgr->_owner)), _mgr(*mgr), _victim(victim),
299 _baseAmount(0.0f), _tempModifier(0), _taunted(TAUNT_STATE_NONE)
300 {
301 _online = ONLINE_STATE_OFFLINE;
302 }
303
304 virtual ~ThreatReference() = default;
305
306 void UnregisterAndFree();
307
308 bool ShouldBeOffline() const;
309 bool ShouldBeSuppressed() const;
310 void UpdateTauntState(TauntState state = TAUNT_STATE_NONE);
313 void HeapNotifyIncreased();
314 void HeapNotifyDecreased();
315 Unit* const _victim;
318 int32 _tempModifier; // Temporary effects (auras with SPELL_AURA_MOD_TOTAL_THREAT) - set from victim's threatmanager in ThreatManager::UpdateMyTempModifiers
320
321 public:
324
325 friend class ThreatManager;
327};
328
330
331 #endif
#define TC_GAME_API
Definition Define.h:114
uint8_t uint8
Definition Define.h:135
int32_t int32
Definition Define.h:129
uint32_t uint32
Definition Define.h:133
#define DEFINE_ENUM_FLAG(enumType)
Definition EnumFlag.h:26
ThreatListClientUpdateFlags
ThreatReference const * operator*() const
ThreatListIterator(std::function< ThreatReference const *()> &&generator)
ThreatReference const * operator->() const
ThreatReference const * _current
bool operator==(ThreatListIterator const &o) const
bool operator!=(std::nullptr_t) const
bool operator==(std::nullptr_t) const
bool operator!=(ThreatListIterator const &o) const
ThreatListIterator & operator++()
std::unordered_map< ObjectGuid, ThreatReference * > _threatenedByMe
bool CanHaveThreatList() const
void ModifyThreatByPercent(Unit *target, int32 percent)
EnumFlag< ThreatListClientUpdateFlags > _updateFlags
ThreatManager & operator=(ThreatManager const &)=delete
std::vector< std::pair< ObjectGuid, uint32 > > _redirectInfo
void RegisterForAIUpdate(ObjectGuid const &guid)
void ResetThreat(Unit *target)
std::array< float, MAX_SPELL_SCHOOL > _singleSchoolModifiers
static bool CompareReferencesLT(ThreatReference const *a, ThreatReference const *b, float aWeight)
std::unordered_map< uint32, std::unordered_map< ObjectGuid, uint32 > > _redirectRegistry
auto const & GetThreatenedByMeList() const
static const CompareThreatLessThan CompareThreat
std::unique_ptr< Heap > _sortedThreatList
ThreatReference const * _currentVictimRef
std::unordered_map< ObjectGuid, ThreatReference * > _myThreatListEntries
bool _ownerCanHaveThreatList
std::vector< ObjectGuid > _needsAIUpdate
ThreatManager(ThreatManager const &)=delete
Unit *const _owner
ThreatReference const * _fixateRef
std::unordered_map< std::underlying_type< SpellSchoolMask >::type, float > _multiSchoolModifiers
Unit * GetOwner() const
void ModifyThreatByPercent(int32 percent)
bool IsSuppressed() const
bool IsTaunting() const
Creature * GetOwner() const
ThreatManager & _mgr
bool IsDetaunted() const
OnlineState GetOnlineState() const
Creature *const _owner
bool IsOnline() const
Unit *const _victim
TauntState _taunted
bool IsOffline() const
ThreatReference(ThreatReference const &)=delete
float GetThreat() const
TauntState GetTauntState() const
ThreatReference(ThreatManager *mgr, Unit *victim)
OnlineState _online
virtual ~ThreatReference()=default
ThreatReference & operator=(ThreatReference const &)=delete
Unit * GetVictim() const
bool IsAvailable() const
Utility class to enable range for loop syntax for multimap.equal_range uses.
Definition Unit.h:783
STL namespace.
bool operator()(ThreatReference const *a, ThreatReference const *b) const