[7770] Prevent access to possible deleted aura's spell proto in Aura::HandleModStealth.
[AHbot.git] / src / game / ThreatManager.cpp
blobb7dd52d2aa0d684c3ca2b4c6b3c8f2505a3752f9
1 /*
2 * Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/>
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version.
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
19 #include "ThreatManager.h"
20 #include "Unit.h"
21 #include "Creature.h"
22 #include "CreatureAI.h"
23 #include "Map.h"
24 #include "Player.h"
25 #include "ObjectAccessor.h"
26 #include "UnitEvents.h"
28 //==============================================================
29 //================= ThreatCalcHelper ===========================
30 //==============================================================
32 // The pHatingUnit is not used yet
33 float ThreatCalcHelper::calcThreat(Unit* pHatedUnit, Unit* pHatingUnit, float pThreat, SpellSchoolMask schoolMask, SpellEntry const *pThreatSpell)
35 if(pThreatSpell)
37 if( Player* modOwner = pHatingUnit->GetSpellModOwner() )
38 modOwner->ApplySpellMod(pThreatSpell->Id, SPELLMOD_THREAT, pThreat);
41 float threat = pHatedUnit->ApplyTotalThreatModifier(pThreat, schoolMask);
42 return threat;
45 //============================================================
46 //================= HostilReference ==========================
47 //============================================================
49 HostilReference::HostilReference(Unit* pUnit, ThreatManager *pThreatManager, float pThreat)
51 iThreat = pThreat;
52 iTempThreatModifyer = 0.0f;
53 link(pUnit, pThreatManager);
54 iUnitGuid = pUnit->GetGUID();
55 iOnline = true;
56 iAccessible = true;
59 //============================================================
60 // Tell our refTo (target) object that we have a link
61 void HostilReference::targetObjectBuildLink()
63 getTarget()->addHatedBy(this);
66 //============================================================
67 // Tell our refTo (taget) object, that the link is cut
68 void HostilReference::targetObjectDestroyLink()
70 getTarget()->removeHatedBy(this);
73 //============================================================
74 // Tell our refFrom (source) object, that the link is cut (Target destroyed)
76 void HostilReference::sourceObjectDestroyLink()
78 setOnlineOfflineState(false);
81 //============================================================
82 // Inform the source, that the status of the reference changed
84 void HostilReference::fireStatusChanged(ThreatRefStatusChangeEvent& pThreatRefStatusChangeEvent)
86 if(getSource())
87 getSource()->processThreatEvent(&pThreatRefStatusChangeEvent);
90 //============================================================
92 void HostilReference::addThreat(float pMod)
94 iThreat += pMod;
95 // the threat is changed. Source and target unit have to be availabe
96 // if the link was cut before relink it again
97 if(!isOnline())
98 updateOnlineStatus();
99 if(pMod != 0.0f)
101 ThreatRefStatusChangeEvent event(UEV_THREAT_REF_THREAT_CHANGE, this, pMod);
102 fireStatusChanged(event);
105 if(isValid() && pMod >= 0)
107 Unit* victim_owner = getTarget()->GetOwner();
108 if(victim_owner && victim_owner->isAlive())
109 getSource()->addThreat(victim_owner, 0.0f); // create a threat to the owner of a pet, if the pet attacks
113 //============================================================
114 // check, if source can reach target and set the status
116 void HostilReference::updateOnlineStatus()
118 bool online = false;
119 bool accessible = false;
121 if(!isValid())
123 Unit* target = ObjectAccessor::GetUnit(*getSourceUnit(), getUnitGuid());
124 if(target)
125 link(target, getSource());
127 // only check for online status if
128 // ref is valid
129 // target is no player or not gamemaster
130 // target is not in flight
131 if(isValid() &&
132 ((getTarget()->GetTypeId() != TYPEID_PLAYER || !((Player*)getTarget())->isGameMaster()) ||
133 !getTarget()->hasUnitState(UNIT_STAT_IN_FLIGHT)))
135 Creature* creature = (Creature* ) getSourceUnit();
136 online = getTarget()->isInAccessablePlaceFor(creature);
137 if(!online)
139 if(creature->AI()->canReachByRangeAttack(getTarget()))
140 online = true; // not accessable but stays online
142 else
143 accessible = true;
146 setAccessibleState(accessible);
147 setOnlineOfflineState(online);
150 //============================================================
151 // set the status and fire the event on status change
153 void HostilReference::setOnlineOfflineState(bool pIsOnline)
155 if(iOnline != pIsOnline)
157 iOnline = pIsOnline;
158 if(!iOnline)
159 setAccessibleState(false); // if not online that not accessable as well
161 ThreatRefStatusChangeEvent event(UEV_THREAT_REF_ONLINE_STATUS, this);
162 fireStatusChanged(event);
166 //============================================================
168 void HostilReference::setAccessibleState(bool pIsAccessible)
170 if(iAccessible != pIsAccessible)
172 iAccessible = pIsAccessible;
174 ThreatRefStatusChangeEvent event(UEV_THREAT_REF_ASSECCIBLE_STATUS, this);
175 fireStatusChanged(event);
179 //============================================================
180 // prepare the reference for deleting
181 // this is called be the target
183 void HostilReference::removeReference()
185 invalidate();
187 ThreatRefStatusChangeEvent event(UEV_THREAT_REF_REMOVE_FROM_LIST, this);
188 fireStatusChanged(event);
191 //============================================================
193 Unit* HostilReference::getSourceUnit()
195 return (getSource()->getOwner());
198 //============================================================
199 //================ ThreatContainer ===========================
200 //============================================================
202 void ThreatContainer::clearReferences()
204 for(std::list<HostilReference*>::const_iterator i = iThreatList.begin(); i != iThreatList.end(); ++i)
206 (*i)->unlink();
207 delete (*i);
209 iThreatList.clear();
212 //============================================================
213 // Return the HostilReference of NULL, if not found
214 HostilReference* ThreatContainer::getReferenceByTarget(Unit* pVictim)
216 HostilReference* result = NULL;
217 uint64 guid = pVictim->GetGUID();
218 for(std::list<HostilReference*>::const_iterator i = iThreatList.begin(); i != iThreatList.end(); ++i)
220 if((*i)->getUnitGuid() == guid)
222 result = (*i);
223 break;
227 return result;
230 //============================================================
231 // Add the threat, if we find the reference
233 HostilReference* ThreatContainer::addThreat(Unit* pVictim, float pThreat)
235 HostilReference* ref = getReferenceByTarget(pVictim);
236 if(ref)
237 ref->addThreat(pThreat);
238 return ref;
241 //============================================================
243 void ThreatContainer::modifyThreatPercent(Unit *pVictim, int32 pPercent)
245 if(HostilReference* ref = getReferenceByTarget(pVictim))
246 ref->addThreatPercent(pPercent);
249 //============================================================
251 bool HostilReferenceSortPredicate(const HostilReference* lhs, const HostilReference* rhs)
253 // std::list::sort ordering predicate must be: (Pred(x,y)&&Pred(y,x))==false
254 return lhs->getThreat() > rhs->getThreat(); // reverse sorting
257 //============================================================
258 // Check if the list is dirty and sort if necessary
260 void ThreatContainer::update()
262 if(iDirty && iThreatList.size() >1)
264 iThreatList.sort(HostilReferenceSortPredicate);
266 iDirty = false;
269 //============================================================
270 // return the next best victim
271 // could be the current victim
273 HostilReference* ThreatContainer::selectNextVictim(Creature* pAttacker, HostilReference* pCurrentVictim)
275 HostilReference* currentRef = NULL;
276 bool found = false;
277 bool noPriorityTargetFound = false;
279 std::list<HostilReference*>::const_iterator lastRef = iThreatList.end();
280 lastRef--;
282 for(std::list<HostilReference*>::const_iterator iter = iThreatList.begin(); iter != iThreatList.end() && !found;)
284 currentRef = (*iter);
286 Unit* target = currentRef->getTarget();
287 assert(target); // if the ref has status online the target must be there !
289 // some units are prefered in comparison to others
290 if(!noPriorityTargetFound && (target->IsImmunedToDamage(pAttacker->GetMeleeDamageSchoolMask()) || target->hasNegativeAuraWithInterruptFlag(AURA_INTERRUPT_FLAG_DAMAGE)) )
292 if(iter != lastRef)
294 // current victim is a second choice target, so don't compare threat with it below
295 if(currentRef == pCurrentVictim)
296 pCurrentVictim = NULL;
297 ++iter;
298 continue;
300 else
302 // 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.
303 noPriorityTargetFound = true;
304 iter = iThreatList.begin();
305 continue;
309 if(!pAttacker->IsOutOfThreatArea(target)) // skip non attackable currently targets
311 if(pCurrentVictim) // select 1.3/1.1 better target in comparison current target
313 // list sorted and and we check current target, then this is best case
314 if(pCurrentVictim == currentRef || currentRef->getThreat() <= 1.1f * pCurrentVictim->getThreat() )
316 currentRef = pCurrentVictim; // for second case
317 found = true;
318 break;
321 if ((currentRef->getThreat() > 1.3f * pCurrentVictim->getThreat() ||
322 currentRef->getThreat() > 1.1f * pCurrentVictim->getThreat()) &&
323 pAttacker->IsWithinDistInMap(target, ATTACK_DISTANCE))
324 { //implement 110% threat rule for targets in melee range
325 found = true; //and 130% rule for targets in ranged distances
326 break; //for selecting alive targets
329 else // select any
331 found = true;
332 break;
335 ++iter;
337 if(!found)
338 currentRef = NULL;
340 return currentRef;
343 //============================================================
344 //=================== ThreatManager ==========================
345 //============================================================
347 ThreatManager::ThreatManager(Unit* owner) : iCurrentVictim(NULL), iOwner(owner)
351 //============================================================
353 void ThreatManager::clearReferences()
355 iThreatContainer.clearReferences();
356 iThreatOfflineContainer.clearReferences();
357 iCurrentVictim = NULL;
360 //============================================================
362 void ThreatManager::addThreat(Unit* pVictim, float pThreat, SpellSchoolMask schoolMask, SpellEntry const *pThreatSpell)
364 //function deals with adding threat and adding players and pets into ThreatList
365 //mobs, NPCs, guards have ThreatList and HateOfflineList
366 //players and pets have only InHateListOf
367 //HateOfflineList is used co contain unattackable victims (in-flight, in-water, GM etc.)
369 // not to self
370 if (pVictim == getOwner())
371 return;
373 // not to GM
374 if(!pVictim || (pVictim->GetTypeId() == TYPEID_PLAYER && ((Player*)pVictim)->isGameMaster()) )
375 return;
377 // not to dead and not for dead
378 if(!pVictim->isAlive() || !getOwner()->isAlive() )
379 return;
381 assert(getOwner()->GetTypeId()== TYPEID_UNIT);
383 float threat = ThreatCalcHelper::calcThreat(pVictim, iOwner, pThreat, schoolMask, pThreatSpell);
385 HostilReference* ref = iThreatContainer.addThreat(pVictim, threat);
386 // Ref is not in the online refs, search the offline refs next
387 if(!ref)
388 ref = iThreatOfflineContainer.addThreat(pVictim, threat);
390 if(!ref) // there was no ref => create a new one
392 // threat has to be 0 here
393 HostilReference* hostilReference = new HostilReference(pVictim, this, 0);
394 iThreatContainer.addReference(hostilReference);
395 hostilReference->addThreat(threat); // now we add the real threat
396 if(pVictim->GetTypeId() == TYPEID_PLAYER && ((Player*)pVictim)->isGameMaster())
397 hostilReference->setOnlineOfflineState(false); // GM is always offline
401 //============================================================
403 void ThreatManager::modifyThreatPercent(Unit *pVictim, int32 pPercent)
405 iThreatContainer.modifyThreatPercent(pVictim, pPercent);
408 //============================================================
410 Unit* ThreatManager::getHostilTarget()
412 iThreatContainer.update();
413 HostilReference* nextVictim = iThreatContainer.selectNextVictim((Creature*) getOwner(), getCurrentVictim());
414 setCurrentVictim(nextVictim);
415 return getCurrentVictim() != NULL ? getCurrentVictim()->getTarget() : NULL;
418 //============================================================
420 float ThreatManager::getThreat(Unit *pVictim, bool pAlsoSearchOfflineList)
422 float threat = 0.0f;
423 HostilReference* ref = iThreatContainer.getReferenceByTarget(pVictim);
424 if(!ref && pAlsoSearchOfflineList)
425 ref = iThreatOfflineContainer.getReferenceByTarget(pVictim);
426 if(ref)
427 threat = ref->getThreat();
428 return threat;
431 //============================================================
433 void ThreatManager::tauntApply(Unit* pTaunter)
435 HostilReference* ref = iThreatContainer.getReferenceByTarget(pTaunter);
436 if(getCurrentVictim() && ref && (ref->getThreat() < getCurrentVictim()->getThreat()))
438 if(ref->getTempThreatModifyer() == 0.0f)
439 // Ok, temp threat is unused
440 ref->setTempThreat(getCurrentVictim()->getThreat());
444 //============================================================
446 void ThreatManager::tauntFadeOut(Unit *pTaunter)
448 HostilReference* ref = iThreatContainer.getReferenceByTarget(pTaunter);
449 if(ref)
450 ref->resetTempThreat();
453 //============================================================
455 void ThreatManager::setCurrentVictim(HostilReference* pHostilReference)
457 iCurrentVictim = pHostilReference;
460 //============================================================
461 // The hated unit is gone, dead or deleted
462 // return true, if the event is consumed
464 void ThreatManager::processThreatEvent(ThreatRefStatusChangeEvent* threatRefStatusChangeEvent)
466 threatRefStatusChangeEvent->setThreatManager(this); // now we can set the threat manager
468 HostilReference* hostilReference = threatRefStatusChangeEvent->getReference();
470 switch(threatRefStatusChangeEvent->getType())
472 case UEV_THREAT_REF_THREAT_CHANGE:
473 if((getCurrentVictim() == hostilReference && threatRefStatusChangeEvent->getFValue()<0.0f) ||
474 (getCurrentVictim() != hostilReference && threatRefStatusChangeEvent->getFValue()>0.0f))
475 setDirty(true); // the order in the threat list might have changed
476 break;
477 case UEV_THREAT_REF_ONLINE_STATUS:
478 if(!hostilReference->isOnline())
480 if (hostilReference == getCurrentVictim())
482 setCurrentVictim(NULL);
483 setDirty(true);
485 iThreatContainer.remove(hostilReference);
486 iThreatOfflineContainer.addReference(hostilReference);
488 else
490 if(getCurrentVictim() && hostilReference->getThreat() > (1.1f * getCurrentVictim()->getThreat()))
491 setDirty(true);
492 iThreatContainer.addReference(hostilReference);
493 iThreatOfflineContainer.remove(hostilReference);
495 break;
496 case UEV_THREAT_REF_REMOVE_FROM_LIST:
497 if (hostilReference == getCurrentVictim())
499 setCurrentVictim(NULL);
500 setDirty(true);
502 if(hostilReference->isOnline())
503 iThreatContainer.remove(hostilReference);
504 else
505 iThreatOfflineContainer.remove(hostilReference);
506 break;