[7512] Implement checks of item bag mask at server startup.
[AHbot.git] / src / game / ThreatManager.cpp
blob66ab9c2a66d88ff6339cf318a1b1a442c12cd299
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(const 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)
100 fireStatusChanged(ThreatRefStatusChangeEvent(UEV_THREAT_REF_THREAT_CHANGE, this, pMod));
101 if(isValid() && pMod >= 0)
103 Unit* victim_owner = getTarget()->GetOwner();
104 if(victim_owner && victim_owner->isAlive())
105 getSource()->addThreat(victim_owner, 0.0f); // create a threat to the owner of a pet, if the pet attacks
109 //============================================================
110 // check, if source can reach target and set the status
112 void HostilReference::updateOnlineStatus()
114 bool online = false;
115 bool accessible = false;
117 if(!isValid())
119 Unit* target = ObjectAccessor::GetUnit(*getSourceUnit(), getUnitGuid());
120 if(target)
121 link(target, getSource());
123 // only check for online status if
124 // ref is valid
125 // target is no player or not gamemaster
126 // target is not in flight
127 if(isValid() &&
128 ((getTarget()->GetTypeId() != TYPEID_PLAYER || !((Player*)getTarget())->isGameMaster()) ||
129 !getTarget()->hasUnitState(UNIT_STAT_IN_FLIGHT)))
131 Creature* creature = (Creature* ) getSourceUnit();
132 online = getTarget()->isInAccessablePlaceFor(creature);
133 if(!online)
135 if(creature->AI()->canReachByRangeAttack(getTarget()))
136 online = true; // not accessable but stays online
138 else
139 accessible = true;
142 setAccessibleState(accessible);
143 setOnlineOfflineState(online);
146 //============================================================
147 // set the status and fire the event on status change
149 void HostilReference::setOnlineOfflineState(bool pIsOnline)
151 if(iOnline != pIsOnline)
153 iOnline = pIsOnline;
154 if(!iOnline)
155 setAccessibleState(false); // if not online that not accessable as well
156 fireStatusChanged(ThreatRefStatusChangeEvent(UEV_THREAT_REF_ONLINE_STATUS, this));
160 //============================================================
162 void HostilReference::setAccessibleState(bool pIsAccessible)
164 if(iAccessible != pIsAccessible)
166 iAccessible = pIsAccessible;
167 fireStatusChanged(ThreatRefStatusChangeEvent(UEV_THREAT_REF_ASSECCIBLE_STATUS, this));
171 //============================================================
172 // prepare the reference for deleting
173 // this is called be the target
175 void HostilReference::removeReference()
177 invalidate();
178 fireStatusChanged(ThreatRefStatusChangeEvent(UEV_THREAT_REF_REMOVE_FROM_LIST, this));
181 //============================================================
183 Unit* HostilReference::getSourceUnit()
185 return (getSource()->getOwner());
188 //============================================================
189 //================ ThreatContainer ===========================
190 //============================================================
192 void ThreatContainer::clearReferences()
194 for(std::list<HostilReference*>::iterator i = iThreatList.begin(); i != iThreatList.end(); ++i)
196 (*i)->unlink();
197 delete (*i);
199 iThreatList.clear();
202 //============================================================
203 // Return the HostilReference of NULL, if not found
204 HostilReference* ThreatContainer::getReferenceByTarget(Unit* pVictim)
206 HostilReference* result = NULL;
207 uint64 guid = pVictim->GetGUID();
208 for(std::list<HostilReference*>::iterator i = iThreatList.begin(); i != iThreatList.end(); ++i)
210 if((*i)->getUnitGuid() == guid)
212 result = (*i);
213 break;
217 return result;
220 //============================================================
221 // Add the threat, if we find the reference
223 HostilReference* ThreatContainer::addThreat(Unit* pVictim, float pThreat)
225 HostilReference* ref = getReferenceByTarget(pVictim);
226 if(ref)
227 ref->addThreat(pThreat);
228 return ref;
231 //============================================================
233 void ThreatContainer::modifyThreatPercent(Unit *pVictim, int32 pPercent)
235 if(HostilReference* ref = getReferenceByTarget(pVictim))
236 ref->addThreatPercent(pPercent);
239 //============================================================
241 bool HostilReferenceSortPredicate(const HostilReference* lhs, const HostilReference* rhs)
243 // std::list::sort ordering predicate must be: (Pred(x,y)&&Pred(y,x))==false
244 return lhs->getThreat() > rhs->getThreat(); // reverse sorting
247 //============================================================
248 // Check if the list is dirty and sort if necessary
250 void ThreatContainer::update()
252 if(iDirty && iThreatList.size() >1)
254 iThreatList.sort(HostilReferenceSortPredicate);
256 iDirty = false;
259 //============================================================
260 // return the next best victim
261 // could be the current victim
263 HostilReference* ThreatContainer::selectNextVictim(Creature* pAttacker, HostilReference* pCurrentVictim)
265 HostilReference* currentRef = NULL;
266 bool found = false;
267 bool noPriorityTargetFound = false;
269 std::list<HostilReference*>::iterator lastRef = iThreatList.end();
270 lastRef--;
272 for(std::list<HostilReference*>::iterator iter = iThreatList.begin(); iter != iThreatList.end() && !found;)
274 currentRef = (*iter);
276 Unit* target = currentRef->getTarget();
277 assert(target); // if the ref has status online the target must be there !
279 // some units are prefered in comparison to others
280 if(!noPriorityTargetFound && (target->IsImmunedToDamage(pAttacker->GetMeleeDamageSchoolMask()) || target->hasNegativeAuraWithInterruptFlag(AURA_INTERRUPT_FLAG_DAMAGE)) )
282 if(iter != lastRef)
284 // current victim is a second choice target, so don't compare threat with it below
285 if(currentRef == pCurrentVictim)
286 pCurrentVictim = NULL;
287 ++iter;
288 continue;
290 else
292 // 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.
293 noPriorityTargetFound = true;
294 iter = iThreatList.begin();
295 continue;
299 if(!pAttacker->IsOutOfThreatArea(target)) // skip non attackable currently targets
301 if(pCurrentVictim) // select 1.3/1.1 better target in comparison current target
303 // list sorted and and we check current target, then this is best case
304 if(pCurrentVictim == currentRef || currentRef->getThreat() <= 1.1f * pCurrentVictim->getThreat() )
306 currentRef = pCurrentVictim; // for second case
307 found = true;
308 break;
311 if( currentRef->getThreat() > 1.3f * pCurrentVictim->getThreat() ||
312 currentRef->getThreat() > 1.1f * pCurrentVictim->getThreat() && pAttacker->IsWithinDistInMap(target, ATTACK_DISTANCE) )
313 { //implement 110% threat rule for targets in melee range
314 found = true; //and 130% rule for targets in ranged distances
315 break; //for selecting alive targets
318 else // select any
320 found = true;
321 break;
324 ++iter;
326 if(!found)
327 currentRef = NULL;
329 return currentRef;
332 //============================================================
333 //=================== ThreatManager ==========================
334 //============================================================
336 ThreatManager::ThreatManager(Unit* owner) : iCurrentVictim(NULL), iOwner(owner)
340 //============================================================
342 void ThreatManager::clearReferences()
344 iThreatContainer.clearReferences();
345 iThreatOfflineContainer.clearReferences();
346 iCurrentVictim = NULL;
349 //============================================================
351 void ThreatManager::addThreat(Unit* pVictim, float pThreat, SpellSchoolMask schoolMask, SpellEntry const *pThreatSpell)
353 //function deals with adding threat and adding players and pets into ThreatList
354 //mobs, NPCs, guards have ThreatList and HateOfflineList
355 //players and pets have only InHateListOf
356 //HateOfflineList is used co contain unattackable victims (in-flight, in-water, GM etc.)
358 if (pVictim == getOwner()) // only for same creatures :)
359 return;
361 if(!pVictim || (pVictim->GetTypeId() == TYPEID_PLAYER && ((Player*)pVictim)->isGameMaster()) )
362 return;
364 assert(getOwner()->GetTypeId()== TYPEID_UNIT);
366 float threat = ThreatCalcHelper::calcThreat(pVictim, iOwner, pThreat, schoolMask, pThreatSpell);
368 HostilReference* ref = iThreatContainer.addThreat(pVictim, threat);
369 // Ref is not in the online refs, search the offline refs next
370 if(!ref)
371 ref = iThreatOfflineContainer.addThreat(pVictim, threat);
373 if(!ref) // there was no ref => create a new one
375 // threat has to be 0 here
376 HostilReference* hostilReference = new HostilReference(pVictim, this, 0);
377 iThreatContainer.addReference(hostilReference);
378 hostilReference->addThreat(threat); // now we add the real threat
379 if(pVictim->GetTypeId() == TYPEID_PLAYER && ((Player*)pVictim)->isGameMaster())
380 hostilReference->setOnlineOfflineState(false); // GM is always offline
384 //============================================================
386 void ThreatManager::modifyThreatPercent(Unit *pVictim, int32 pPercent)
388 iThreatContainer.modifyThreatPercent(pVictim, pPercent);
391 //============================================================
393 Unit* ThreatManager::getHostilTarget()
395 iThreatContainer.update();
396 HostilReference* nextVictim = iThreatContainer.selectNextVictim((Creature*) getOwner(), getCurrentVictim());
397 setCurrentVictim(nextVictim);
398 return getCurrentVictim() != NULL ? getCurrentVictim()->getTarget() : NULL;
401 //============================================================
403 float ThreatManager::getThreat(Unit *pVictim, bool pAlsoSearchOfflineList)
405 float threat = 0.0f;
406 HostilReference* ref = iThreatContainer.getReferenceByTarget(pVictim);
407 if(!ref && pAlsoSearchOfflineList)
408 ref = iThreatOfflineContainer.getReferenceByTarget(pVictim);
409 if(ref)
410 threat = ref->getThreat();
411 return threat;
414 //============================================================
416 void ThreatManager::tauntApply(Unit* pTaunter)
418 HostilReference* ref = iThreatContainer.getReferenceByTarget(pTaunter);
419 if(getCurrentVictim() && ref && (ref->getThreat() < getCurrentVictim()->getThreat()))
421 if(ref->getTempThreatModifyer() == 0.0f)
422 // Ok, temp threat is unused
423 ref->setTempThreat(getCurrentVictim()->getThreat());
427 //============================================================
429 void ThreatManager::tauntFadeOut(Unit *pTaunter)
431 HostilReference* ref = iThreatContainer.getReferenceByTarget(pTaunter);
432 if(ref)
433 ref->resetTempThreat();
436 //============================================================
438 void ThreatManager::setCurrentVictim(HostilReference* pHostilReference)
440 iCurrentVictim = pHostilReference;
443 //============================================================
444 // The hated unit is gone, dead or deleted
445 // return true, if the event is consumed
447 bool ThreatManager::processThreatEvent(const UnitBaseEvent* pUnitBaseEvent)
449 bool consumed = false;
451 ThreatRefStatusChangeEvent* threatRefStatusChangeEvent;
452 HostilReference* hostilReference;
454 threatRefStatusChangeEvent = (ThreatRefStatusChangeEvent*) pUnitBaseEvent;
455 threatRefStatusChangeEvent->setThreatManager(this); // now we can set the threat manager
456 hostilReference = threatRefStatusChangeEvent->getReference();
458 switch(pUnitBaseEvent->getType())
460 case UEV_THREAT_REF_THREAT_CHANGE:
461 if((getCurrentVictim() == hostilReference && threatRefStatusChangeEvent->getFValue()<0.0f) ||
462 (getCurrentVictim() != hostilReference && threatRefStatusChangeEvent->getFValue()>0.0f))
463 setDirty(true); // the order in the threat list might have changed
464 break;
465 case UEV_THREAT_REF_ONLINE_STATUS:
466 if(!hostilReference->isOnline())
468 if (hostilReference == getCurrentVictim())
470 setCurrentVictim(NULL);
471 setDirty(true);
473 iThreatContainer.remove(hostilReference);
474 iThreatOfflineContainer.addReference(hostilReference);
476 else
478 if(getCurrentVictim() && hostilReference->getThreat() > (1.1f * getCurrentVictim()->getThreat()))
479 setDirty(true);
480 iThreatContainer.addReference(hostilReference);
481 iThreatOfflineContainer.remove(hostilReference);
483 break;
484 case UEV_THREAT_REF_REMOVE_FROM_LIST:
485 if (hostilReference == getCurrentVictim())
487 setCurrentVictim(NULL);
488 setDirty(true);
490 if(hostilReference->isOnline())
491 iThreatContainer.remove(hostilReference);
492 else
493 iThreatOfflineContainer.remove(hostilReference);
494 break;
496 return consumed;