[8449] Deprecate healing/damage item mods and merge internal data in to spell power.
[getmangos.git] / src / game / ThreatManager.cpp
bloba058a8fc76853c99071b867519a1cac956fdf4d3
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)
36 if (Player* modOwner = pHatedUnit->GetSpellModOwner())
37 modOwner->ApplySpellMod(pThreatSpell->Id, SPELLMOD_THREAT, pThreat);
39 float threat = pHatedUnit->ApplyTotalThreatModifier(pThreat, schoolMask);
40 return threat;
43 //============================================================
44 //================= HostilReference ==========================
45 //============================================================
47 HostilReference::HostilReference(Unit* pUnit, ThreatManager *pThreatManager, float pThreat)
49 iThreat = pThreat;
50 iTempThreatModifyer = 0.0f;
51 link(pUnit, pThreatManager);
52 iUnitGuid = pUnit->GetGUID();
53 iOnline = true;
54 iAccessible = true;
57 //============================================================
58 // Tell our refTo (target) object that we have a link
59 void HostilReference::targetObjectBuildLink()
61 getTarget()->addHatedBy(this);
64 //============================================================
65 // Tell our refTo (taget) object, that the link is cut
66 void HostilReference::targetObjectDestroyLink()
68 getTarget()->removeHatedBy(this);
71 //============================================================
72 // Tell our refFrom (source) object, that the link is cut (Target destroyed)
74 void HostilReference::sourceObjectDestroyLink()
76 setOnlineOfflineState(false);
79 //============================================================
80 // Inform the source, that the status of the reference changed
82 void HostilReference::fireStatusChanged(ThreatRefStatusChangeEvent& pThreatRefStatusChangeEvent)
84 if(getSource())
85 getSource()->processThreatEvent(&pThreatRefStatusChangeEvent);
88 //============================================================
90 void HostilReference::addThreat(float pMod)
92 iThreat += pMod;
93 // the threat is changed. Source and target unit have to be availabe
94 // if the link was cut before relink it again
95 if(!isOnline())
96 updateOnlineStatus();
97 if(pMod != 0.0f)
99 ThreatRefStatusChangeEvent event(UEV_THREAT_REF_THREAT_CHANGE, this, pMod);
100 fireStatusChanged(event);
103 if(isValid() && pMod >= 0)
105 Unit* victim_owner = getTarget()->GetOwner();
106 if(victim_owner && victim_owner->isAlive())
107 getSource()->addThreat(victim_owner, 0.0f); // create a threat to the owner of a pet, if the pet attacks
111 //============================================================
112 // check, if source can reach target and set the status
114 void HostilReference::updateOnlineStatus()
116 bool online = false;
117 bool accessible = false;
119 if(!isValid())
121 Unit* target = ObjectAccessor::GetUnit(*getSourceUnit(), getUnitGuid());
122 if(target)
123 link(target, getSource());
125 // only check for online status if
126 // ref is valid
127 // target is no player or not gamemaster
128 // target is not in flight
129 if(isValid() &&
130 ((getTarget()->GetTypeId() != TYPEID_PLAYER || !((Player*)getTarget())->isGameMaster()) ||
131 !getTarget()->hasUnitState(UNIT_STAT_IN_FLIGHT)))
133 Creature* creature = (Creature* ) getSourceUnit();
134 online = getTarget()->isInAccessablePlaceFor(creature);
135 if(!online)
137 if(creature->AI()->canReachByRangeAttack(getTarget()))
138 online = true; // not accessable but stays online
140 else
141 accessible = true;
144 setAccessibleState(accessible);
145 setOnlineOfflineState(online);
148 //============================================================
149 // set the status and fire the event on status change
151 void HostilReference::setOnlineOfflineState(bool pIsOnline)
153 if(iOnline != pIsOnline)
155 iOnline = pIsOnline;
156 if(!iOnline)
157 setAccessibleState(false); // if not online that not accessable as well
159 ThreatRefStatusChangeEvent event(UEV_THREAT_REF_ONLINE_STATUS, this);
160 fireStatusChanged(event);
164 //============================================================
166 void HostilReference::setAccessibleState(bool pIsAccessible)
168 if(iAccessible != pIsAccessible)
170 iAccessible = pIsAccessible;
172 ThreatRefStatusChangeEvent event(UEV_THREAT_REF_ASSECCIBLE_STATUS, this);
173 fireStatusChanged(event);
177 //============================================================
178 // prepare the reference for deleting
179 // this is called be the target
181 void HostilReference::removeReference()
183 invalidate();
185 ThreatRefStatusChangeEvent event(UEV_THREAT_REF_REMOVE_FROM_LIST, this);
186 fireStatusChanged(event);
189 //============================================================
191 Unit* HostilReference::getSourceUnit()
193 return (getSource()->getOwner());
196 //============================================================
197 //================ ThreatContainer ===========================
198 //============================================================
200 void ThreatContainer::clearReferences()
202 for(std::list<HostilReference*>::const_iterator i = iThreatList.begin(); i != iThreatList.end(); ++i)
204 (*i)->unlink();
205 delete (*i);
207 iThreatList.clear();
210 //============================================================
211 // Return the HostilReference of NULL, if not found
212 HostilReference* ThreatContainer::getReferenceByTarget(Unit* pVictim)
214 HostilReference* result = NULL;
215 uint64 guid = pVictim->GetGUID();
216 for(std::list<HostilReference*>::const_iterator i = iThreatList.begin(); i != iThreatList.end(); ++i)
218 if((*i)->getUnitGuid() == guid)
220 result = (*i);
221 break;
225 return result;
228 //============================================================
229 // Add the threat, if we find the reference
231 HostilReference* ThreatContainer::addThreat(Unit* pVictim, float pThreat)
233 HostilReference* ref = getReferenceByTarget(pVictim);
234 if(ref)
235 ref->addThreat(pThreat);
236 return ref;
239 //============================================================
241 void ThreatContainer::modifyThreatPercent(Unit *pVictim, int32 pPercent)
243 if(HostilReference* ref = getReferenceByTarget(pVictim))
244 ref->addThreatPercent(pPercent);
247 //============================================================
249 bool HostilReferenceSortPredicate(const HostilReference* lhs, const HostilReference* rhs)
251 // std::list::sort ordering predicate must be: (Pred(x,y)&&Pred(y,x))==false
252 return lhs->getThreat() > rhs->getThreat(); // reverse sorting
255 //============================================================
256 // Check if the list is dirty and sort if necessary
258 void ThreatContainer::update()
260 if(iDirty && iThreatList.size() >1)
262 iThreatList.sort(HostilReferenceSortPredicate);
264 iDirty = false;
267 //============================================================
268 // return the next best victim
269 // could be the current victim
271 HostilReference* ThreatContainer::selectNextVictim(Creature* pAttacker, HostilReference* pCurrentVictim)
273 HostilReference* currentRef = NULL;
274 bool found = false;
275 bool noPriorityTargetFound = false;
277 std::list<HostilReference*>::const_iterator lastRef = iThreatList.end();
278 lastRef--;
280 for(std::list<HostilReference*>::const_iterator iter = iThreatList.begin(); iter != iThreatList.end() && !found;)
282 currentRef = (*iter);
284 Unit* target = currentRef->getTarget();
285 assert(target); // if the ref has status online the target must be there !
287 // some units are prefered in comparison to others
288 if(!noPriorityTargetFound && (target->IsImmunedToDamage(pAttacker->GetMeleeDamageSchoolMask()) || target->hasNegativeAuraWithInterruptFlag(AURA_INTERRUPT_FLAG_DAMAGE)) )
290 if(iter != lastRef)
292 // current victim is a second choice target, so don't compare threat with it below
293 if(currentRef == pCurrentVictim)
294 pCurrentVictim = NULL;
295 ++iter;
296 continue;
298 else
300 // 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.
301 noPriorityTargetFound = true;
302 iter = iThreatList.begin();
303 continue;
307 if(!pAttacker->IsOutOfThreatArea(target)) // skip non attackable currently targets
309 if(pCurrentVictim) // select 1.3/1.1 better target in comparison current target
311 // list sorted and and we check current target, then this is best case
312 if(pCurrentVictim == currentRef || currentRef->getThreat() <= 1.1f * pCurrentVictim->getThreat() )
314 currentRef = pCurrentVictim; // for second case
315 found = true;
316 break;
319 if (currentRef->getThreat() > 1.3f * pCurrentVictim->getThreat() ||
320 (currentRef->getThreat() > 1.1f * pCurrentVictim->getThreat() &&
321 pAttacker->IsWithinDistInMap(target, ATTACK_DISTANCE)) )
322 { //implement 110% threat rule for targets in melee range
323 found = true; //and 130% rule for targets in ranged distances
324 break; //for selecting alive targets
327 else // select any
329 found = true;
330 break;
333 ++iter;
335 if(!found)
336 currentRef = NULL;
338 return currentRef;
341 //============================================================
342 //=================== ThreatManager ==========================
343 //============================================================
345 ThreatManager::ThreatManager(Unit* owner) : iCurrentVictim(NULL), iOwner(owner)
349 //============================================================
351 void ThreatManager::clearReferences()
353 iThreatContainer.clearReferences();
354 iThreatOfflineContainer.clearReferences();
355 iCurrentVictim = NULL;
358 //============================================================
360 void ThreatManager::addThreat(Unit* pVictim, float pThreat, SpellSchoolMask schoolMask, SpellEntry const *pThreatSpell)
362 //function deals with adding threat and adding players and pets into ThreatList
363 //mobs, NPCs, guards have ThreatList and HateOfflineList
364 //players and pets have only InHateListOf
365 //HateOfflineList is used co contain unattackable victims (in-flight, in-water, GM etc.)
367 // not to self
368 if (pVictim == getOwner())
369 return;
371 // not to GM
372 if(!pVictim || (pVictim->GetTypeId() == TYPEID_PLAYER && ((Player*)pVictim)->isGameMaster()) )
373 return;
375 // not to dead and not for dead
376 if(!pVictim->isAlive() || !getOwner()->isAlive() )
377 return;
379 assert(getOwner()->GetTypeId()== TYPEID_UNIT);
381 float threat = ThreatCalcHelper::calcThreat(pVictim, iOwner, pThreat, schoolMask, pThreatSpell);
383 HostilReference* ref = iThreatContainer.addThreat(pVictim, threat);
384 // Ref is not in the online refs, search the offline refs next
385 if(!ref)
386 ref = iThreatOfflineContainer.addThreat(pVictim, threat);
388 if(!ref) // there was no ref => create a new one
390 // threat has to be 0 here
391 HostilReference* hostilReference = new HostilReference(pVictim, this, 0);
392 iThreatContainer.addReference(hostilReference);
393 hostilReference->addThreat(threat); // now we add the real threat
394 if(pVictim->GetTypeId() == TYPEID_PLAYER && ((Player*)pVictim)->isGameMaster())
395 hostilReference->setOnlineOfflineState(false); // GM is always offline
399 //============================================================
401 void ThreatManager::modifyThreatPercent(Unit *pVictim, int32 pPercent)
403 iThreatContainer.modifyThreatPercent(pVictim, pPercent);
406 //============================================================
408 Unit* ThreatManager::getHostilTarget()
410 iThreatContainer.update();
411 HostilReference* nextVictim = iThreatContainer.selectNextVictim((Creature*) getOwner(), getCurrentVictim());
412 setCurrentVictim(nextVictim);
413 return getCurrentVictim() != NULL ? getCurrentVictim()->getTarget() : NULL;
416 //============================================================
418 float ThreatManager::getThreat(Unit *pVictim, bool pAlsoSearchOfflineList)
420 float threat = 0.0f;
421 HostilReference* ref = iThreatContainer.getReferenceByTarget(pVictim);
422 if(!ref && pAlsoSearchOfflineList)
423 ref = iThreatOfflineContainer.getReferenceByTarget(pVictim);
424 if(ref)
425 threat = ref->getThreat();
426 return threat;
429 //============================================================
431 void ThreatManager::tauntApply(Unit* pTaunter)
433 HostilReference* ref = iThreatContainer.getReferenceByTarget(pTaunter);
434 if(getCurrentVictim() && ref && (ref->getThreat() < getCurrentVictim()->getThreat()))
436 if(ref->getTempThreatModifyer() == 0.0f)
437 // Ok, temp threat is unused
438 ref->setTempThreat(getCurrentVictim()->getThreat());
442 //============================================================
444 void ThreatManager::tauntFadeOut(Unit *pTaunter)
446 HostilReference* ref = iThreatContainer.getReferenceByTarget(pTaunter);
447 if(ref)
448 ref->resetTempThreat();
451 //============================================================
453 void ThreatManager::setCurrentVictim(HostilReference* pHostilReference)
455 iCurrentVictim = pHostilReference;
458 //============================================================
459 // The hated unit is gone, dead or deleted
460 // return true, if the event is consumed
462 void ThreatManager::processThreatEvent(ThreatRefStatusChangeEvent* threatRefStatusChangeEvent)
464 threatRefStatusChangeEvent->setThreatManager(this); // now we can set the threat manager
466 HostilReference* hostilReference = threatRefStatusChangeEvent->getReference();
468 switch(threatRefStatusChangeEvent->getType())
470 case UEV_THREAT_REF_THREAT_CHANGE:
471 if((getCurrentVictim() == hostilReference && threatRefStatusChangeEvent->getFValue()<0.0f) ||
472 (getCurrentVictim() != hostilReference && threatRefStatusChangeEvent->getFValue()>0.0f))
473 setDirty(true); // the order in the threat list might have changed
474 break;
475 case UEV_THREAT_REF_ONLINE_STATUS:
476 if(!hostilReference->isOnline())
478 if (hostilReference == getCurrentVictim())
480 setCurrentVictim(NULL);
481 setDirty(true);
483 iThreatContainer.remove(hostilReference);
484 iThreatOfflineContainer.addReference(hostilReference);
486 else
488 if(getCurrentVictim() && hostilReference->getThreat() > (1.1f * getCurrentVictim()->getThreat()))
489 setDirty(true);
490 iThreatContainer.addReference(hostilReference);
491 iThreatOfflineContainer.remove(hostilReference);
493 break;
494 case UEV_THREAT_REF_REMOVE_FROM_LIST:
495 if (hostilReference == getCurrentVictim())
497 setCurrentVictim(NULL);
498 setDirty(true);
500 if(hostilReference->isOnline())
501 iThreatContainer.remove(hostilReference);
502 else
503 iThreatOfflineContainer.remove(hostilReference);
504 break;