[7710] Fixed problem with stuck in combat creatures with EventAI. Remove redundent...
[AHbot.git] / src / game / ThreatManager.cpp
blobb94ce41716bfac4b14fbb2007f38253d7b47217d
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*>::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*>::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*>::iterator lastRef = iThreatList.end();
280 lastRef--;
282 for(std::list<HostilReference*>::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() && pAttacker->IsWithinDistInMap(target, ATTACK_DISTANCE) )
323 { //implement 110% threat rule for targets in melee range
324 found = true; //and 130% rule for targets in ranged distances
325 break; //for selecting alive targets
328 else // select any
330 found = true;
331 break;
334 ++iter;
336 if(!found)
337 currentRef = NULL;
339 return currentRef;
342 //============================================================
343 //=================== ThreatManager ==========================
344 //============================================================
346 ThreatManager::ThreatManager(Unit* owner) : iCurrentVictim(NULL), iOwner(owner)
350 //============================================================
352 void ThreatManager::clearReferences()
354 iThreatContainer.clearReferences();
355 iThreatOfflineContainer.clearReferences();
356 iCurrentVictim = NULL;
359 //============================================================
361 void ThreatManager::addThreat(Unit* pVictim, float pThreat, SpellSchoolMask schoolMask, SpellEntry const *pThreatSpell)
363 //function deals with adding threat and adding players and pets into ThreatList
364 //mobs, NPCs, guards have ThreatList and HateOfflineList
365 //players and pets have only InHateListOf
366 //HateOfflineList is used co contain unattackable victims (in-flight, in-water, GM etc.)
368 // not to self
369 if (pVictim == getOwner())
370 return;
372 // not to GM
373 if(!pVictim || (pVictim->GetTypeId() == TYPEID_PLAYER && ((Player*)pVictim)->isGameMaster()) )
374 return;
376 // not to dead and not for dead
377 if(!pVictim->isAlive() || !getOwner()->isAlive() )
378 return;
380 assert(getOwner()->GetTypeId()== TYPEID_UNIT);
382 float threat = ThreatCalcHelper::calcThreat(pVictim, iOwner, pThreat, schoolMask, pThreatSpell);
384 HostilReference* ref = iThreatContainer.addThreat(pVictim, threat);
385 // Ref is not in the online refs, search the offline refs next
386 if(!ref)
387 ref = iThreatOfflineContainer.addThreat(pVictim, threat);
389 if(!ref) // there was no ref => create a new one
391 // threat has to be 0 here
392 HostilReference* hostilReference = new HostilReference(pVictim, this, 0);
393 iThreatContainer.addReference(hostilReference);
394 hostilReference->addThreat(threat); // now we add the real threat
395 if(pVictim->GetTypeId() == TYPEID_PLAYER && ((Player*)pVictim)->isGameMaster())
396 hostilReference->setOnlineOfflineState(false); // GM is always offline
400 //============================================================
402 void ThreatManager::modifyThreatPercent(Unit *pVictim, int32 pPercent)
404 iThreatContainer.modifyThreatPercent(pVictim, pPercent);
407 //============================================================
409 Unit* ThreatManager::getHostilTarget()
411 iThreatContainer.update();
412 HostilReference* nextVictim = iThreatContainer.selectNextVictim((Creature*) getOwner(), getCurrentVictim());
413 setCurrentVictim(nextVictim);
414 return getCurrentVictim() != NULL ? getCurrentVictim()->getTarget() : NULL;
417 //============================================================
419 float ThreatManager::getThreat(Unit *pVictim, bool pAlsoSearchOfflineList)
421 float threat = 0.0f;
422 HostilReference* ref = iThreatContainer.getReferenceByTarget(pVictim);
423 if(!ref && pAlsoSearchOfflineList)
424 ref = iThreatOfflineContainer.getReferenceByTarget(pVictim);
425 if(ref)
426 threat = ref->getThreat();
427 return threat;
430 //============================================================
432 void ThreatManager::tauntApply(Unit* pTaunter)
434 HostilReference* ref = iThreatContainer.getReferenceByTarget(pTaunter);
435 if(getCurrentVictim() && ref && (ref->getThreat() < getCurrentVictim()->getThreat()))
437 if(ref->getTempThreatModifyer() == 0.0f)
438 // Ok, temp threat is unused
439 ref->setTempThreat(getCurrentVictim()->getThreat());
443 //============================================================
445 void ThreatManager::tauntFadeOut(Unit *pTaunter)
447 HostilReference* ref = iThreatContainer.getReferenceByTarget(pTaunter);
448 if(ref)
449 ref->resetTempThreat();
452 //============================================================
454 void ThreatManager::setCurrentVictim(HostilReference* pHostilReference)
456 iCurrentVictim = pHostilReference;
459 //============================================================
460 // The hated unit is gone, dead or deleted
461 // return true, if the event is consumed
463 void ThreatManager::processThreatEvent(ThreatRefStatusChangeEvent* threatRefStatusChangeEvent)
465 threatRefStatusChangeEvent->setThreatManager(this); // now we can set the threat manager
467 HostilReference* hostilReference = threatRefStatusChangeEvent->getReference();
469 switch(threatRefStatusChangeEvent->getType())
471 case UEV_THREAT_REF_THREAT_CHANGE:
472 if((getCurrentVictim() == hostilReference && threatRefStatusChangeEvent->getFValue()<0.0f) ||
473 (getCurrentVictim() != hostilReference && threatRefStatusChangeEvent->getFValue()>0.0f))
474 setDirty(true); // the order in the threat list might have changed
475 break;
476 case UEV_THREAT_REF_ONLINE_STATUS:
477 if(!hostilReference->isOnline())
479 if (hostilReference == getCurrentVictim())
481 setCurrentVictim(NULL);
482 setDirty(true);
484 iThreatContainer.remove(hostilReference);
485 iThreatOfflineContainer.addReference(hostilReference);
487 else
489 if(getCurrentVictim() && hostilReference->getThreat() > (1.1f * getCurrentVictim()->getThreat()))
490 setDirty(true);
491 iThreatContainer.addReference(hostilReference);
492 iThreatOfflineContainer.remove(hostilReference);
494 break;
495 case UEV_THREAT_REF_REMOVE_FROM_LIST:
496 if (hostilReference == getCurrentVictim())
498 setCurrentVictim(NULL);
499 setDirty(true);
501 if(hostilReference->isOnline())
502 iThreatContainer.remove(hostilReference);
503 else
504 iThreatOfflineContainer.remove(hostilReference);
505 break;