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"
22 #include "CreatureAI.h"
24 #include "MapManager.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
)
38 if( Player
* modOwner
= pHatingUnit
->GetSpellModOwner() )
39 modOwner
->ApplySpellMod(pThreatSpell
->Id
, SPELLMOD_THREAT
, pThreat
);
42 float threat
= pHatedUnit
->ApplyTotalThreatModifier(pThreat
, schoolMask
);
46 //============================================================
47 //================= HostilReference ==========================
48 //============================================================
50 HostilReference::HostilReference(Unit
* pUnit
, ThreatManager
*pThreatManager
, float pThreat
)
53 iTempThreatModifyer
= 0.0f
;
54 link(pUnit
, pThreatManager
);
55 iUnitGuid
= pUnit
->GetGUID();
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
)
88 getSource()->processThreatEvent(&pThreatRefStatusChangeEvent
);
91 //============================================================
93 void HostilReference::addThreat(float pMod
)
96 // the threat is changed. Source and target unit have to be availabe
97 // if the link was cut before relink it again
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()
116 bool accessible
= false;
120 Unit
* target
= ObjectAccessor::GetUnit(*getSourceUnit(), getUnitGuid());
122 link(target
, getSource());
124 // only check for online status if
126 // target is no player or not gamemaster
127 // target is not in flight
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
);
136 if(creature
->AI()->canReachByRangeAttack(getTarget()))
137 online
= true; // not accessable but stays online
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
)
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()
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
)
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
)
221 //============================================================
222 // Add the threat, if we find the reference
224 HostilReference
* ThreatContainer::addThreat(Unit
* pVictim
, float pThreat
)
226 HostilReference
* ref
= getReferenceByTarget(pVictim
);
228 ref
->addThreat(pThreat
);
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
);
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
;
268 bool noPriorityTargetFound
= false;
270 std::list
<HostilReference
*>::iterator lastRef
= iThreatList
.end();
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
)) )
285 // current victim is a second choice target, so don't compare threat with it below
286 if(currentRef
== pCurrentVictim
)
287 pCurrentVictim
= NULL
;
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();
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
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
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 :)
362 if(!pVictim
|| (pVictim
->GetTypeId() == TYPEID_PLAYER
&& ((Player
*)pVictim
)->isGameMaster()) )
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
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
)
407 HostilReference
* ref
= iThreatContainer
.getReferenceByTarget(pVictim
);
408 if(!ref
&& pAlsoSearchOfflineList
)
409 ref
= iThreatOfflineContainer
.getReferenceByTarget(pVictim
);
411 threat
= ref
->getThreat();
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
);
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
466 case UEV_THREAT_REF_ONLINE_STATUS
:
467 if(!hostilReference
->isOnline())
469 if (hostilReference
== getCurrentVictim())
471 setCurrentVictim(NULL
);
474 iThreatContainer
.remove(hostilReference
);
475 iThreatOfflineContainer
.addReference(hostilReference
);
479 if(getCurrentVictim() && hostilReference
->getThreat() > (1.1f
* getCurrentVictim()->getThreat()))
481 iThreatContainer
.addReference(hostilReference
);
482 iThreatOfflineContainer
.remove(hostilReference
);
485 case UEV_THREAT_REF_REMOVE_FROM_LIST
:
486 if (hostilReference
== getCurrentVictim())
488 setCurrentVictim(NULL
);
491 if(hostilReference
->isOnline())
492 iThreatContainer
.remove(hostilReference
);
494 iThreatOfflineContainer
.remove(hostilReference
);