[7297] Fixed profession spells sorting in trainer spell list at client.
[getmangos.git] / src / game / ThreatManager.cpp
blob7932d46c4a313ac665797dc6fff900663fb94b29
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 "MapManager.h"
25 #include "Player.h"
26 #include "ObjectAccessor.h"
27 #include "UnitEvents.h"
29 //==============================================================
30 //================= ThreatCalcHelper ===========================
31 //==============================================================
33 // The pHatingUnit is not used yet
34 float ThreatCalcHelper::calcThreat(Unit* pHatedUnit, Unit* pHatingUnit, float pThreat, SpellSchoolMask schoolMask, SpellEntry const *pThreatSpell)
36 if(pThreatSpell)
38 if( Player* modOwner = pHatingUnit->GetSpellModOwner() )
39 modOwner->ApplySpellMod(pThreatSpell->Id, SPELLMOD_THREAT, pThreat);
42 float threat = pHatedUnit->ApplyTotalThreatModifier(pThreat, schoolMask);
43 return threat;
46 //============================================================
47 //================= HostilReference ==========================
48 //============================================================
50 HostilReference::HostilReference(Unit* pUnit, ThreatManager *pThreatManager, float pThreat)
52 iThreat = pThreat;
53 iTempThreatModifyer = 0.0f;
54 link(pUnit, pThreatManager);
55 iUnitGuid = pUnit->GetGUID();
56 iOnline = true;
57 iAccessible = true;
60 //============================================================
61 // Tell our refTo (target) object that we have a link
62 void HostilReference::targetObjectBuildLink()
64 getTarget()->addHatedBy(this);
67 //============================================================
68 // Tell our refTo (taget) object, that the link is cut
69 void HostilReference::targetObjectDestroyLink()
71 getTarget()->removeHatedBy(this);
74 //============================================================
75 // Tell our refFrom (source) object, that the link is cut (Target destroyed)
77 void HostilReference::sourceObjectDestroyLink()
79 setOnlineOfflineState(false);
82 //============================================================
83 // Inform the source, that the status of the reference changed
85 void HostilReference::fireStatusChanged(const ThreatRefStatusChangeEvent& pThreatRefStatusChangeEvent)
87 if(getSource())
88 getSource()->processThreatEvent(&pThreatRefStatusChangeEvent);
91 //============================================================
93 void HostilReference::addThreat(float pMod)
95 iThreat += pMod;
96 // the threat is changed. Source and target unit have to be availabe
97 // if the link was cut before relink it again
98 if(!isOnline())
99 updateOnlineStatus();
100 if(pMod != 0.0f)
101 fireStatusChanged(ThreatRefStatusChangeEvent(UEV_THREAT_REF_THREAT_CHANGE, this, pMod));
102 if(isValid() && pMod >= 0)
104 Unit* victim_owner = getTarget()->GetOwner();
105 if(victim_owner && victim_owner->isAlive())
106 getSource()->addThreat(victim_owner, 0.0f); // create a threat to the owner of a pet, if the pet attacks
110 //============================================================
111 // check, if source can reach target and set the status
113 void HostilReference::updateOnlineStatus()
115 bool online = false;
116 bool accessible = false;
118 if(!isValid())
120 Unit* target = ObjectAccessor::GetUnit(*getSourceUnit(), getUnitGuid());
121 if(target)
122 link(target, getSource());
124 // only check for online status if
125 // ref is valid
126 // target is no player or not gamemaster
127 // target is not in flight
128 if(isValid() &&
129 ((getTarget()->GetTypeId() != TYPEID_PLAYER || !((Player*)getTarget())->isGameMaster()) ||
130 !getTarget()->hasUnitState(UNIT_STAT_IN_FLIGHT)))
132 Creature* creature = (Creature* ) getSourceUnit();
133 online = getTarget()->isInAccessablePlaceFor(creature);
134 if(!online)
136 if(creature->AI()->canReachByRangeAttack(getTarget()))
137 online = true; // not accessable but stays online
139 else
140 accessible = true;
143 setAccessibleState(accessible);
144 setOnlineOfflineState(online);
147 //============================================================
148 // set the status and fire the event on status change
150 void HostilReference::setOnlineOfflineState(bool pIsOnline)
152 if(iOnline != pIsOnline)
154 iOnline = pIsOnline;
155 if(!iOnline)
156 setAccessibleState(false); // if not online that not accessable as well
157 fireStatusChanged(ThreatRefStatusChangeEvent(UEV_THREAT_REF_ONLINE_STATUS, this));
161 //============================================================
163 void HostilReference::setAccessibleState(bool pIsAccessible)
165 if(iAccessible != pIsAccessible)
167 iAccessible = pIsAccessible;
168 fireStatusChanged(ThreatRefStatusChangeEvent(UEV_THREAT_REF_ASSECCIBLE_STATUS, this));
172 //============================================================
173 // prepare the reference for deleting
174 // this is called be the target
176 void HostilReference::removeReference()
178 invalidate();
179 fireStatusChanged(ThreatRefStatusChangeEvent(UEV_THREAT_REF_REMOVE_FROM_LIST, this));
182 //============================================================
184 Unit* HostilReference::getSourceUnit()
186 return (getSource()->getOwner());
189 //============================================================
190 //================ ThreatContainer ===========================
191 //============================================================
193 void ThreatContainer::clearReferences()
195 for(std::list<HostilReference*>::iterator i = iThreatList.begin(); i != iThreatList.end(); ++i)
197 (*i)->unlink();
198 delete (*i);
200 iThreatList.clear();
203 //============================================================
204 // Return the HostilReference of NULL, if not found
205 HostilReference* ThreatContainer::getReferenceByTarget(Unit* pVictim)
207 HostilReference* result = NULL;
208 uint64 guid = pVictim->GetGUID();
209 for(std::list<HostilReference*>::iterator i = iThreatList.begin(); i != iThreatList.end(); ++i)
211 if((*i)->getUnitGuid() == guid)
213 result = (*i);
214 break;
218 return result;
221 //============================================================
222 // Add the threat, if we find the reference
224 HostilReference* ThreatContainer::addThreat(Unit* pVictim, float pThreat)
226 HostilReference* ref = getReferenceByTarget(pVictim);
227 if(ref)
228 ref->addThreat(pThreat);
229 return ref;
232 //============================================================
234 void ThreatContainer::modifyThreatPercent(Unit *pVictim, int32 pPercent)
236 if(HostilReference* ref = getReferenceByTarget(pVictim))
237 ref->addThreatPercent(pPercent);
240 //============================================================
242 bool HostilReferenceSortPredicate(const HostilReference* lhs, const HostilReference* rhs)
244 // std::list::sort ordering predicate must be: (Pred(x,y)&&Pred(y,x))==false
245 return lhs->getThreat() > rhs->getThreat(); // reverse sorting
248 //============================================================
249 // Check if the list is dirty and sort if necessary
251 void ThreatContainer::update()
253 if(iDirty && iThreatList.size() >1)
255 iThreatList.sort(HostilReferenceSortPredicate);
257 iDirty = false;
260 //============================================================
261 // return the next best victim
262 // could be the current victim
264 HostilReference* ThreatContainer::selectNextVictim(Creature* pAttacker, HostilReference* pCurrentVictim)
266 HostilReference* currentRef = NULL;
267 bool found = false;
268 bool noPriorityTargetFound = false;
270 std::list<HostilReference*>::iterator lastRef = iThreatList.end();
271 lastRef--;
273 for(std::list<HostilReference*>::iterator iter = iThreatList.begin(); iter != iThreatList.end() && !found;)
275 currentRef = (*iter);
277 Unit* target = currentRef->getTarget();
278 assert(target); // if the ref has status online the target must be there !
280 // some units are prefered in comparison to others
281 if(!noPriorityTargetFound && (target->IsImmunedToDamage(pAttacker->GetMeleeDamageSchoolMask()) || target->hasNegativeAuraWithInterruptFlag(AURA_INTERRUPT_FLAG_DAMAGE)) )
283 if(iter != lastRef)
285 // current victim is a second choice target, so don't compare threat with it below
286 if(currentRef == pCurrentVictim)
287 pCurrentVictim = NULL;
288 ++iter;
289 continue;
291 else
293 // 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.
294 noPriorityTargetFound = true;
295 iter = iThreatList.begin();
296 continue;
300 if(!pAttacker->IsOutOfThreatArea(target)) // skip non attackable currently targets
302 if(pCurrentVictim) // select 1.3/1.1 better target in comparison current target
304 // list sorted and and we check current target, then this is best case
305 if(pCurrentVictim == currentRef || currentRef->getThreat() <= 1.1f * pCurrentVictim->getThreat() )
307 currentRef = pCurrentVictim; // for second case
308 found = true;
309 break;
312 if( currentRef->getThreat() > 1.3f * pCurrentVictim->getThreat() ||
313 currentRef->getThreat() > 1.1f * pCurrentVictim->getThreat() && pAttacker->IsWithinDistInMap(target, ATTACK_DISTANCE) )
314 { //implement 110% threat rule for targets in melee range
315 found = true; //and 130% rule for targets in ranged distances
316 break; //for selecting alive targets
319 else // select any
321 found = true;
322 break;
325 ++iter;
327 if(!found)
328 currentRef = NULL;
330 return currentRef;
333 //============================================================
334 //=================== ThreatManager ==========================
335 //============================================================
337 ThreatManager::ThreatManager(Unit* owner) : iCurrentVictim(NULL), iOwner(owner)
341 //============================================================
343 void ThreatManager::clearReferences()
345 iThreatContainer.clearReferences();
346 iThreatOfflineContainer.clearReferences();
347 iCurrentVictim = NULL;
350 //============================================================
352 void ThreatManager::addThreat(Unit* pVictim, float pThreat, SpellSchoolMask schoolMask, SpellEntry const *pThreatSpell)
354 //function deals with adding threat and adding players and pets into ThreatList
355 //mobs, NPCs, guards have ThreatList and HateOfflineList
356 //players and pets have only InHateListOf
357 //HateOfflineList is used co contain unattackable victims (in-flight, in-water, GM etc.)
359 if (pVictim == getOwner()) // only for same creatures :)
360 return;
362 if(!pVictim || (pVictim->GetTypeId() == TYPEID_PLAYER && ((Player*)pVictim)->isGameMaster()) )
363 return;
365 assert(getOwner()->GetTypeId()== TYPEID_UNIT);
367 float threat = ThreatCalcHelper::calcThreat(pVictim, iOwner, pThreat, schoolMask, pThreatSpell);
369 HostilReference* ref = iThreatContainer.addThreat(pVictim, threat);
370 // Ref is not in the online refs, search the offline refs next
371 if(!ref)
372 ref = iThreatOfflineContainer.addThreat(pVictim, threat);
374 if(!ref) // there was no ref => create a new one
376 // threat has to be 0 here
377 HostilReference* hostilReference = new HostilReference(pVictim, this, 0);
378 iThreatContainer.addReference(hostilReference);
379 hostilReference->addThreat(threat); // now we add the real threat
380 if(pVictim->GetTypeId() == TYPEID_PLAYER && ((Player*)pVictim)->isGameMaster())
381 hostilReference->setOnlineOfflineState(false); // GM is always offline
385 //============================================================
387 void ThreatManager::modifyThreatPercent(Unit *pVictim, int32 pPercent)
389 iThreatContainer.modifyThreatPercent(pVictim, pPercent);
392 //============================================================
394 Unit* ThreatManager::getHostilTarget()
396 iThreatContainer.update();
397 HostilReference* nextVictim = iThreatContainer.selectNextVictim((Creature*) getOwner(), getCurrentVictim());
398 setCurrentVictim(nextVictim);
399 return getCurrentVictim() != NULL ? getCurrentVictim()->getTarget() : NULL;
402 //============================================================
404 float ThreatManager::getThreat(Unit *pVictim, bool pAlsoSearchOfflineList)
406 float threat = 0.0f;
407 HostilReference* ref = iThreatContainer.getReferenceByTarget(pVictim);
408 if(!ref && pAlsoSearchOfflineList)
409 ref = iThreatOfflineContainer.getReferenceByTarget(pVictim);
410 if(ref)
411 threat = ref->getThreat();
412 return threat;
415 //============================================================
417 void ThreatManager::tauntApply(Unit* pTaunter)
419 HostilReference* ref = iThreatContainer.getReferenceByTarget(pTaunter);
420 if(getCurrentVictim() && ref && (ref->getThreat() < getCurrentVictim()->getThreat()))
422 if(ref->getTempThreatModifyer() == 0.0f)
423 // Ok, temp threat is unused
424 ref->setTempThreat(getCurrentVictim()->getThreat());
428 //============================================================
430 void ThreatManager::tauntFadeOut(Unit *pTaunter)
432 HostilReference* ref = iThreatContainer.getReferenceByTarget(pTaunter);
433 if(ref)
434 ref->resetTempThreat();
437 //============================================================
439 void ThreatManager::setCurrentVictim(HostilReference* pHostilReference)
441 iCurrentVictim = pHostilReference;
444 //============================================================
445 // The hated unit is gone, dead or deleted
446 // return true, if the event is consumed
448 bool ThreatManager::processThreatEvent(const UnitBaseEvent* pUnitBaseEvent)
450 bool consumed = false;
452 ThreatRefStatusChangeEvent* threatRefStatusChangeEvent;
453 HostilReference* hostilReference;
455 threatRefStatusChangeEvent = (ThreatRefStatusChangeEvent*) pUnitBaseEvent;
456 threatRefStatusChangeEvent->setThreatManager(this); // now we can set the threat manager
457 hostilReference = threatRefStatusChangeEvent->getReference();
459 switch(pUnitBaseEvent->getType())
461 case UEV_THREAT_REF_THREAT_CHANGE:
462 if((getCurrentVictim() == hostilReference && threatRefStatusChangeEvent->getFValue()<0.0f) ||
463 (getCurrentVictim() != hostilReference && threatRefStatusChangeEvent->getFValue()>0.0f))
464 setDirty(true); // the order in the threat list might have changed
465 break;
466 case UEV_THREAT_REF_ONLINE_STATUS:
467 if(!hostilReference->isOnline())
469 if (hostilReference == getCurrentVictim())
471 setCurrentVictim(NULL);
472 setDirty(true);
474 iThreatContainer.remove(hostilReference);
475 iThreatOfflineContainer.addReference(hostilReference);
477 else
479 if(getCurrentVictim() && hostilReference->getThreat() > (1.1f * getCurrentVictim()->getThreat()))
480 setDirty(true);
481 iThreatContainer.addReference(hostilReference);
482 iThreatOfflineContainer.remove(hostilReference);
484 break;
485 case UEV_THREAT_REF_REMOVE_FROM_LIST:
486 if (hostilReference == getCurrentVictim())
488 setCurrentVictim(NULL);
489 setDirty(true);
491 if(hostilReference->isOnline())
492 iThreatContainer.remove(hostilReference);
493 else
494 iThreatOfflineContainer.remove(hostilReference);
495 break;
497 return consumed;