Project SkyFire Core
SkyFire 5.4.8 server core API documentation
Loading...
Searching...
No Matches
ThreatManager.cpp
Go to the documentation of this file.
1/*
2* This file is part of Project SkyFire https://www.projectskyfire.org.
3* See LICENSE.md file for Copyright information
4*/
5
6#include "Creature.h"
7#include "CreatureAI.h"
8#include "Map.h"
9#include "ObjectAccessor.h"
10#include "Player.h"
11#include "SpellAuras.h"
12#include "SpellMgr.h"
13#include "ThreatManager.h"
14#include "Unit.h"
15#include "UnitEvents.h"
16
17//==============================================================
18//================= ThreatCalcHelper ===========================
19//==============================================================
20
21// The hatingUnit is not used yet
22float ThreatCalcHelper::calcThreat(Unit* hatedUnit, Unit* /*hatingUnit*/, float threat, uint32 schoolMask, SpellInfo const* threatSpell)
23{
24 if (threatSpell)
25 {
26 float pctMod = 1.0f;
27 if (SpellThreatEntry const* threatEntry = sSpellMgr->GetSpellThreatEntry(threatSpell->Id))
28 if (threatEntry->pctMod != 1.0f)
29 pctMod = threatEntry->pctMod;
30
31 bool hasEnergizeEffect = false;
32 for (uint8 i = 0; i < MAX_SPELL_EFFECTS; i++)
33 {
34 if (threatSpell->Effects[i].Effect == SPELL_EFFECT_ENERGIZE || threatSpell->Effects[i].ApplyAuraName == SPELL_AURA_PERIODIC_ENERGIZE)
35 {
36 hasEnergizeEffect = true;
37 break;
38 }
39 }
40
41 SpellThreatCalculation const calculation = ApplySpellThreatModifiers(threat, pctMod, hasEnergizeEffect);
42 threat = calculation.threat;
43
44 // Energize is not affected by Mods
45 if (calculation.ignoresUnitModifiers)
46 return threat;
47
48 if (Player* modOwner = hatedUnit->GetSpellModOwner())
49 modOwner->ApplySpellMod(threatSpell->Id, SPELLMOD_THREAT, threat);
50 }
51
52 return hatedUnit->ApplyTotalThreatModifier(threat, SpellSchoolMask(schoolMask));
53}
54
55bool ThreatCalcHelper::isValidProcess(Unit* hatedUnit, Unit* hatingUnit, SpellInfo const* threatSpell)
56{
57 //function deals with adding threat and adding players and pets into ThreatList
58 //mobs, NPCs, guards have ThreatList and HateOfflineList
59 //players and pets have only InHateListOf
60 //HateOfflineList is used co contain unattackable victims (in-flight, in-water, GM etc.)
61
62 if (!hatedUnit || !hatingUnit)
63 return false;
64
65 // not to self
66 if (hatedUnit == hatingUnit)
67 return false;
68
69 // not to GM
70 if (hatedUnit->GetTypeId() == TypeID::TYPEID_PLAYER && hatedUnit->ToPlayer()->IsGameMaster())
71 return false;
72
73 // not to dead and not for dead
74 if (!hatedUnit->IsAlive() || !hatingUnit->IsAlive())
75 return false;
76
77 // not in same map or phase
78 if (!hatedUnit->IsInMap(hatingUnit) || !hatedUnit->InSamePhase(hatingUnit))
79 return false;
80
81 // spell not causing threat
82 if (threatSpell && threatSpell->AttributesEx & SPELL_ATTR1_NO_THREAT)
83 return false;
84
85 ASSERT(hatingUnit->GetTypeId() == TypeID::TYPEID_UNIT);
86
87 return true;
88}
89
90//============================================================
91//================= HostileReference ==========================
92//============================================================
93
94HostileReference::HostileReference(Unit* refUnit, ThreatManager* threatManager, float threat)
95{
96 iThreat = threat;
98 link(refUnit, threatManager);
99 iUnitGuid = refUnit->GetGUID();
100 iOnline = true;
101 iAccessible = true;
102}
103
104//============================================================
105// Tell our refTo (target) object that we have a link
110
111//============================================================
112// Tell our refTo (taget) object, that the link is cut
117
118//============================================================
119// Tell our refFrom (source) object, that the link is cut (Target destroyed)
120
125
126//============================================================
127// Inform the source, that the status of the reference changed
128
130{
131 if (GetSource())
132 GetSource()->processThreatEvent(&threatRefStatusChangeEvent);
133}
134
135//============================================================
136
137void HostileReference::addThreat(float modThreat)
138{
139 iThreat += modThreat;
140 // the threat is changed. Source and target unit have to be available
141 // if the link was cut before relink it again
142 if (!isOnline())
144 if (modThreat != 0.0f)
145 {
147 fireStatusChanged(event);
148 }
149
150 if (isValid() && modThreat >= 0.0f)
151 {
152 Unit* victimOwner = getTarget()->GetCharmerOrOwner();
153 if (victimOwner && victimOwner->IsAlive())
154 GetSource()->addThreat(victimOwner, 0.0f); // create a threat to the owner of a pet, if the pet attacks
155 }
156}
157
159{
160 float tmpThreat = iThreat;
161 AddPct(tmpThreat, percent);
162 addThreat(tmpThreat - iThreat);
163}
164
165//============================================================
166// check, if source can reach target and set the status
167
169{
170 bool online = false;
171 bool accessible = false;
172
173 if (!isValid())
175 link(target, GetSource());
176
177 // only check for online status if
178 // ref is valid
179 // target is no player or not gamemaster
180 // target is not in flight
181 if (isValid()
182 && (getTarget()->GetTypeId() != TypeID::TYPEID_PLAYER || !getTarget()->ToPlayer()->IsGameMaster())
183 && !getTarget()->HasUnitState(UNIT_STATE_IN_FLIGHT)
184 && getTarget()->IsInMap(GetSourceUnit())
185 && getTarget()->InSamePhase(GetSourceUnit())
186 )
187 {
188 Creature* creature = GetSourceUnit()->ToCreature();
189 online = getTarget()->isInAccessiblePlaceFor(creature);
190 if (!online)
191 {
192 if (creature->IsWithinCombatRange(getTarget(), creature->m_CombatDistance))
193 online = true; // not accessible but stays online
194 }
195 else
196 accessible = true;
197 }
198 setAccessibleState(accessible);
199 setOnlineOfflineState(online);
200}
201
202//============================================================
203// set the status and fire the event on status change
204
206{
207 if (iOnline != isOnline)
208 {
210 if (!iOnline)
211 setAccessibleState(false); // if not online that not accessable as well
212
214 fireStatusChanged(event);
215 }
216}
217
218//============================================================
219
230
231//============================================================
232// prepare the reference for deleting
233// this is called be the target
234
242
243//============================================================
244
246{
247 return (GetSource()->GetOwner());
248}
249
250//============================================================
251//================ ThreatContainer ===========================
252//============================================================
253
255{
256 for (ThreatContainer::StorageType::const_iterator i = iThreatList.begin(); i != iThreatList.end(); ++i)
257 {
258 (*i)->unlink();
259 delete (*i);
260 }
261
262 iThreatList.clear();
263}
264
265//============================================================
266// Return the HostileReference of NULL, if not found
268{
269 if (!victim)
270 return NULL;
271
272 uint64 const guid = victim->GetGUID();
273 for (ThreatContainer::StorageType::const_iterator i = iThreatList.begin(); i != iThreatList.end(); ++i)
274 {
275 HostileReference* ref = (*i);
276 if (ref && ref->getUnitGuid() == guid)
277 return ref;
278 }
279
280 return NULL;
281}
282
283//============================================================
284// Add the threat, if we find the reference
285
287{
289 if (ref)
290 ref->addThreat(threat);
291 return ref;
292}
293
294//============================================================
295
297{
298 if (HostileReference* ref = getReferenceByTarget(victim))
299 ref->addThreatPercent(percent);
300}
301
302//============================================================
303// Check if the list is dirty and sort if necessary
304
306{
307 if (iDirty && iThreatList.size() > 1)
309
310 iDirty = false;
311}
312
313//============================================================
314// return the next best victim
315// could be the current victim
316
318{
319 HostileReference* currentRef = NULL;
320 bool found = false;
321 bool noPriorityTargetFound = false;
322
323 ThreatContainer::StorageType::const_iterator lastRef = iThreatList.end();
324 --lastRef;
325
326 for (ThreatContainer::StorageType::const_iterator iter = iThreatList.begin(); iter != iThreatList.end() && !found;)
327 {
328 currentRef = (*iter);
329
330 Unit* target = currentRef->getTarget();
331 ASSERT(target); // if the ref has status online the target must be there !
332
333 // some units are prefered in comparison to others
334 if (!noPriorityTargetFound && (target->IsImmunedToDamage(attacker->GetMeleeDamageSchoolMask()) || target->HasNegativeAuraWithInterruptFlag(AURA_INTERRUPT_FLAG_TAKE_DAMAGE)))
335 {
336 if (iter != lastRef)
337 {
338 // current victim is a second choice target, so don't compare threat with it below
339 if (currentRef == currentVictim)
340 currentVictim = NULL;
341 ++iter;
342 continue;
343 }
344 else
345 {
346 // if we reached to this point, everyone in the threatlist is a second choice target. In such a situation the target with the highest threat should be attacked.
347 noPriorityTargetFound = true;
348 iter = iThreatList.begin();
349 continue;
350 }
351 }
352
353 if (attacker->CanCreatureAttack(target)) // skip non attackable currently targets
354 {
355 if (currentVictim) // select 1.3/1.1 better target in comparison current target
356 {
357 // list sorted and and we check current target, then this is best case
358 if (currentVictim == currentRef || currentRef->getThreat() <= 1.1f * currentVictim->getThreat())
359 {
360 if (currentVictim != currentRef && attacker->CanCreatureAttack(currentVictim->getTarget()))
361 currentRef = currentVictim; // for second case, if currentvictim is attackable
362
363 found = true;
364 break;
365 }
366
367 if (currentRef->getThreat() > 1.3f * currentVictim->getThreat() ||
368 (currentRef->getThreat() > 1.1f * currentVictim->getThreat() &&
369 attacker->IsWithinMeleeRange(target)))
370 { //implement 110% threat rule for targets in melee range
371 found = true; //and 130% rule for targets in ranged distances
372 break; //for selecting alive targets
373 }
374 }
375 else // select any
376 {
377 found = true;
378 break;
379 }
380 }
381 ++iter;
382 }
383 if (!found)
384 currentRef = NULL;
385
386 return currentRef;
387}
388
389//============================================================
390//=================== ThreatManager ==========================
391//============================================================
392
394
395//============================================================
396
398{
399 iThreatContainer.clearReferences();
400 iThreatOfflineContainer.clearReferences();
401 iCurrentVictim = NULL;
403}
404
405//============================================================
406
407void ThreatManager::addThreat(Unit* victim, float threat, SpellSchoolMask schoolMask, SpellInfo const* threatSpell)
408{
409 if (!ThreatCalcHelper::isValidProcess(victim, GetOwner(), threatSpell))
410 return;
411
412 doAddThreat(victim, ThreatCalcHelper::calcThreat(victim, iOwner, threat, schoolMask, threatSpell));
413}
414
415void ThreatManager::doAddThreat(Unit* victim, float threat)
416{
417 uint32 redirectThreadPct = victim->GetRedirectThreatPercent();
418
419 // must check > 0.0f, otherwise dead loop
420 if (threat > 0.0f && redirectThreadPct)
421 {
422 if (Unit* redirectTarget = victim->GetRedirectThreatTarget())
423 {
424 float redirectThreat = CalculatePct(threat, redirectThreadPct);
425 threat -= redirectThreat;
426 _addThreat(redirectTarget, redirectThreat);
427 }
428 }
429
430 _addThreat(victim, threat);
431}
432
433void ThreatManager::_addThreat(Unit* victim, float threat)
434{
435 HostileReference* ref = iThreatContainer.addThreat(victim, threat);
436 // Ref is not in the online refs, search the offline refs next
437 if (!ref)
438 ref = iThreatOfflineContainer.addThreat(victim, threat);
439
440 if (!ref) // there was no ref => create a new one
441 {
442 // threat has to be 0 here
443 HostileReference* hostileRef = new HostileReference(victim, this, 0);
444 iThreatContainer.addReference(hostileRef);
445 hostileRef->addThreat(threat); // now we add the real threat
446 if (victim->GetTypeId() == TypeID::TYPEID_PLAYER && victim->ToPlayer()->IsGameMaster())
447 hostileRef->setOnlineOfflineState(false); // GM is always offline
448 }
449}
450
451//============================================================
452
454{
455 iThreatContainer.modifyThreatPercent(victim, percent);
456}
457
458//============================================================
459
461{
462 iThreatContainer.update();
463 HostileReference* nextVictim = iThreatContainer.selectNextVictim(GetOwner()->ToCreature(), getCurrentVictim());
464 setCurrentVictim(nextVictim);
465 return getCurrentVictim() != NULL ? getCurrentVictim()->getTarget() : NULL;
466}
467
468//============================================================
469
470float ThreatManager::getThreat(Unit* victim, bool alsoSearchOfflineList)
471{
472 float threat = 0.0f;
473 HostileReference* ref = iThreatContainer.getReferenceByTarget(victim);
474 if (!ref && alsoSearchOfflineList)
475 ref = iThreatOfflineContainer.getReferenceByTarget(victim);
476 if (ref)
477 threat = ref->getThreat();
478 return threat;
479}
480
481//============================================================
482
484{
485 HostileReference* ref = iThreatContainer.getReferenceByTarget(taunter);
486 if (getCurrentVictim() && ref && (ref->getThreat() < getCurrentVictim()->getThreat()))
487 {
488 if (ref->getTempThreatModifier() == 0.0f) // Ok, temp threat is unused
490 }
491}
492
493//============================================================
494
496{
497 HostileReference* ref = iThreatContainer.getReferenceByTarget(taunter);
498 if (ref)
499 ref->resetTempThreat();
500}
501
502//============================================================
503
505{
506 if (pHostileReference && pHostileReference != iCurrentVictim)
507 {
508 iOwner->SendChangeCurrentVictimOpcode(pHostileReference);
509 }
510 iCurrentVictim = pHostileReference;
511}
512
513//============================================================
514// The hated unit is gone, dead or deleted
515// return true, if the event is consumed
516
518{
519 threatRefStatusChangeEvent->setThreatManager(this); // now we can set the threat manager
520
521 HostileReference* hostilRef = threatRefStatusChangeEvent->getReference();
522
523 switch (threatRefStatusChangeEvent->getType())
524 {
526 if ((getCurrentVictim() == hostilRef && threatRefStatusChangeEvent->getFValue() < 0.0f) ||
527 (getCurrentVictim() != hostilRef && threatRefStatusChangeEvent->getFValue() > 0.0f))
528 setDirty(true); // the order in the threat list might have changed
529 break;
531 if (!hostilRef->isOnline())
532 {
533 if (hostilRef == getCurrentVictim())
534 {
535 setCurrentVictim(NULL);
536 setDirty(true);
537 }
538 iOwner->SendRemoveFromThreatListOpcode(hostilRef);
539 iThreatContainer.remove(hostilRef);
540 iThreatOfflineContainer.addReference(hostilRef);
541 }
542 else
543 {
544 if (getCurrentVictim() && hostilRef->getThreat() > (1.1f * getCurrentVictim()->getThreat()))
545 setDirty(true);
546 iThreatContainer.addReference(hostilRef);
547 iThreatOfflineContainer.remove(hostilRef);
548 }
549 break;
551 if (hostilRef == getCurrentVictim())
552 {
553 setCurrentVictim(NULL);
554 setDirty(true);
555 }
556 iOwner->SendRemoveFromThreatListOpcode(hostilRef);
557 if (hostilRef->isOnline())
558 iThreatContainer.remove(hostilRef);
559 else
560 iThreatOfflineContainer.remove(hostilRef);
561 break;
562 }
563}
564
566{
567 if (isThreatListEmpty())
568 return false;
569
570 if (time >= iUpdateTimer)
571 {
573 return true;
574 }
575 iUpdateTimer -= time;
576 return false;
577}
578
579// Reset all aggro without modifying the threatlist.
581{
582 ThreatContainer::StorageType& threatList = iThreatContainer.iThreatList;
583 if (threatList.empty())
584 return;
585
586 for (ThreatContainer::StorageType::iterator itr = threatList.begin(); itr != threatList.end(); ++itr)
587 (*itr)->setThreat(0);
588
589 setDirty(true);
590}
#define MAX_SPELL_EFFECTS
std::int32_t int32
Definition Define.h:73
std::uint8_t uint8
Definition Define.h:79
std::uint32_t uint32
Definition Define.h:77
std::uint64_t uint64
Definition Define.h:76
#define ASSERT
Definition Errors.h:29
@ TYPEID_PLAYER
Definition Object.h:55
@ TYPEID_UNIT
Definition Object.h:54
@ SPELL_EFFECT_ENERGIZE
@ SPELL_ATTR1_NO_THREAT
SpellSchoolMask
@ SPELL_AURA_PERIODIC_ENERGIZE
#define sSpellMgr
Definition SpellMgr.h:744
#define THREAT_UPDATE_INTERVAL
@ AURA_INTERRUPT_FLAG_TAKE_DAMAGE
Definition Unit.h:42
@ SPELLMOD_THREAT
Definition Unit.h:75
@ UNIT_STATE_IN_FLIGHT
Definition Unit.h:520
@ UEV_THREAT_REF_THREAT_CHANGE
Definition UnitEvents.h:24
@ UEV_THREAT_REF_ONLINE_STATUS
Definition UnitEvents.h:21
@ UEV_THREAT_REF_REMOVE_FROM_LIST
Definition UnitEvents.h:27
@ UEV_THREAT_REF_ASSECCIBLE_STATUS
Definition UnitEvents.h:30
T AddPct(T &base, U pct)
Definition Util.h:109
T CalculatePct(T base, U pct)
Definition Util.h:103
SpellSchoolMask GetMeleeDamageSchoolMask() const OVERRIDE
Definition Creature.h:485
float m_CombatDistance
Definition Creature.h:660
bool CanCreatureAttack(Unit const *victim, bool force=true) const
bool isOnline() const
HostileReference(Unit *refUnit, ThreatManager *threatManager, float threat)
void setOnlineOfflineState(bool isOnline)
void setAccessibleState(bool isAccessible)
void setTempThreat(float threat)
bool isAccessible() const
void addThreat(float modThreat)
uint64 getUnitGuid() const
float getThreat() const
void fireStatusChanged(ThreatRefStatusChangeEvent &threatRefStatusChangeEvent)
void addThreatPercent(int32 percent)
float getTempThreatModifier() const
static Unit * GetUnit(WorldObject const &, uint64 guid)
uint64 GetGUID() const
Definition Object.h:119
Player * ToPlayer()
Definition Object.h:204
TypeID GetTypeId() const
Definition Object.h:131
Creature * ToCreature()
Definition Object.h:207
bool IsGameMaster() const
Definition Player.h:1369
void link(Unit *toObj, ThreatManager *fromObj)
Definition Reference.h:33
ThreatManager * GetSource() const
Definition Reference.h:83
uint32 ApplyAuraName
Definition SpellInfo.h:90
SpellEffectInfo Effects[MAX_SPELL_EFFECTS]
Definition SpellInfo.h:255
uint32 Id
Definition SpellInfo.h:160
uint32 AttributesEx
Definition SpellInfo.h:165
HostileReference * getReferenceByTarget(Unit *victim) const
HostileReference * addThreat(Unit *victim, float threat)
StorageType iThreatList
void modifyThreatPercent(Unit *victim, int32 percent)
std::list< HostileReference * > StorageType
HostileReference * selectNextVictim(Creature *attacker, HostileReference *currentVictim) const
ThreatManager(Unit *owner)
void tauntFadeOut(Unit *taunter)
Unit * getHostilTarget()
float getThreat(Unit *victim, bool alsoSearchOfflineList=false)
void setDirty(bool isDirty)
HostileReference * getCurrentVictim() const
ThreatContainer iThreatOfflineContainer
ThreatContainer iThreatContainer
void modifyThreatPercent(Unit *victim, int32 percent)
HostileReference * iCurrentVictim
void addThreat(Unit *victim, float threat, SpellSchoolMask schoolMask=SPELL_SCHOOL_MASK_NORMAL, SpellInfo const *threatSpell=NULL)
void setCurrentVictim(HostileReference *hostileRef)
bool isThreatListEmpty() const
void processThreatEvent(ThreatRefStatusChangeEvent *threatRefStatusChangeEvent)
void tauntApply(Unit *taunter)
void _addThreat(Unit *victim, float threat)
friend class HostileReference
bool isNeedUpdateToClient(uint32 time)
Unit * GetOwner() const
void doAddThreat(Unit *victim, float threat)
void setThreatManager(ThreatManager *pThreatManager)
Definition UnitEvents.h:92
HostileReference * getReference() const
Definition UnitEvents.h:90
uint32 getType() const
Definition UnitEvents.h:63
Definition Unit.h:1367
Player * GetSpellModOwner() const
Definition Unit.cpp:5818
float ApplyTotalThreatModifier(float fThreat, SpellSchoolMask schoolMask=SPELL_SCHOOL_MASK_NORMAL)
Definition Unit.cpp:4451
bool IsAlive() const
Definition Unit.h:2032
bool IsWithinCombatRange(const Unit *obj, float dist2compare) const
Definition Unit.cpp:447
bool isInAccessiblePlaceFor(Creature const *c) const
Definition Unit.cpp:1473
Unit * GetRedirectThreatTarget()
Definition Unit.cpp:7158
Unit * GetCharmerOrOwner() const
Definition Unit.cpp:2419
uint32 GetRedirectThreatPercent() const
Definition Unit.h:2728
void removeHatedBy(HostileReference *)
Definition Unit.h:2463
bool IsWithinMeleeRange(const Unit *obj, float dist=MELEE_RANGE) const
Definition Unit.cpp:463
bool IsImmunedToDamage(SpellSchoolMask meleeSchoolMask) const
Definition Unit.cpp:3415
void addHatedBy(HostileReference *pHostileReference)
Definition Unit.h:2459
bool HasNegativeAuraWithInterruptFlag(uint32 flag, uint64 guid=0) const
bool InSamePhase(WorldObject const *obj) const
Definition Object.cpp:3412
bool IsInMap(WorldObject const *obj) const
Definition Object.cpp:1703
Definition SpellMgr.h:346
static float calcThreat(Unit *hatedUnit, Unit *hatingUnit, float threat, uint32 schoolMask=NormalSchoolMask, SpellInfo const *threatSpell=NULL)
static bool isValidProcess(Unit *hatedUnit, Unit *hatingUnit, SpellInfo const *threatSpell=NULL)
static SpellThreatCalculation ApplySpellThreatModifiers(float threat, float pctMod, bool hasEnergizeEffect)