root/trunk/src/game/ThreatManager.cpp @ 213

Revision 198, 16.8 kB (checked in by yumileroy, 17 years ago)

[svn] Fixed charmer or possessor not being entered in the threat lists of creatures who are being attacked by the charmed or possessed unit.

Original author: gvcoman
Date: 2008-11-08 17:46:34-06:00

Line 
1/*
2 * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/>
3 *
4 * Copyright (C) 2008 Trinity <http://www.trinitycore.org/>
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
19 */
20
21#include "ThreatManager.h"
22#include "Unit.h"
23#include "Creature.h"
24#include "CreatureAI.h"
25#include "Map.h"
26#include "MapManager.h"
27#include "Player.h"
28#include "ObjectAccessor.h"
29#include "UnitEvents.h"
30
31//==============================================================
32//================= ThreatCalcHelper ===========================
33//==============================================================
34
35// The pHatingUnit is not used yet
36float ThreatCalcHelper::calcThreat(Unit* pHatedUnit, Unit* pHatingUnit, float pThreat, SpellSchoolMask schoolMask, SpellEntry const *pThreatSpell)
37{
38    if(pThreatSpell)
39    {
40        if( Player* modOwner = pHatingUnit->GetSpellModOwner() )
41            modOwner->ApplySpellMod(pThreatSpell->Id, SPELLMOD_THREAT, pThreat);
42    }
43
44    float threat = pHatedUnit->ApplyTotalThreatModifier(pThreat, schoolMask);
45    return threat;
46}
47
48//============================================================
49//================= HostilReference ==========================
50//============================================================
51
52HostilReference::HostilReference(Unit* pUnit, ThreatManager *pThreatManager, float pThreat)
53{
54    iThreat = pThreat;
55    iTempThreatModifyer = 0.0f;
56    link(pUnit, pThreatManager);
57    iUnitGuid = pUnit->GetGUID();
58    iOnline = true;
59    iAccessible = true;
60}
61
62//============================================================
63// Tell our refTo (target) object that we have a link
64void HostilReference::targetObjectBuildLink()
65{
66    getTarget()->addHatedBy(this);
67}
68
69//============================================================
70// Tell our refTo (taget) object, that the link is cut
71void HostilReference::targetObjectDestroyLink()
72{
73    getTarget()->removeHatedBy(this);
74}
75
76//============================================================
77// Tell our refFrom (source) object, that the link is cut (Target destroyed)
78
79void HostilReference::sourceObjectDestroyLink()
80{
81    setOnlineOfflineState(false);
82}
83
84//============================================================
85// Inform the source, that the status of the reference changed
86
87void HostilReference::fireStatusChanged(const ThreatRefStatusChangeEvent& pThreatRefStatusChangeEvent)
88{
89    if(getSource())
90        getSource()->processThreatEvent(&pThreatRefStatusChangeEvent);
91}
92
93//============================================================
94
95void HostilReference::addThreat(float pMod)
96{
97    iThreat += pMod;
98    // the threat is changed. Source and target unit have to be availabe
99    // if the link was cut before relink it again
100    if(!isOnline())
101        updateOnlineStatus();
102    if(pMod != 0.0f)
103        fireStatusChanged(ThreatRefStatusChangeEvent(UEV_THREAT_REF_THREAT_CHANGE, this, pMod));
104    if(isValid() && pMod >= 0)
105    {
106        Unit* victim_owner = getTarget()->GetCharmerOrOwner();
107        if(victim_owner && victim_owner->isAlive())
108            getSource()->addThreat(victim_owner, 0.0f);     // create a threat to the owner of a pet, if the pet attacks
109    }
110}
111
112//============================================================
113// check, if source can reach target and set the status
114
115void HostilReference::updateOnlineStatus()
116{
117    bool online = false;
118    bool accessible = false;
119
120    if(!isValid())
121    {
122        Unit* target = ObjectAccessor::GetUnit(*getSourceUnit(), getUnitGuid());
123        if(target)
124            link(target, getSource());
125    }
126    // only check for online status if
127    // ref is valid
128    // target is no player or not gamemaster
129    // target is not in flight
130    if(isValid() &&
131        ((getTarget()->GetTypeId() != TYPEID_PLAYER || !((Player*)getTarget())->isGameMaster()) ||
132        !getTarget()->hasUnitState(UNIT_STAT_IN_FLIGHT)))
133    {
134        Creature* creature = (Creature* ) getSourceUnit();
135        online = getTarget()->isInAccessablePlaceFor(creature);
136        if(!online)
137        {
138            if(creature->AI()->canReachByRangeAttack(getTarget()))
139                online = true;                              // not accessable but stays online
140        }
141        else
142            accessible = true;
143
144    }
145    setAccessibleState(accessible);
146    setOnlineOfflineState(online);
147}
148
149//============================================================
150// set the status and fire the event on status change
151
152void HostilReference::setOnlineOfflineState(bool pIsOnline)
153{
154    if(iOnline != pIsOnline)
155    {
156        iOnline = pIsOnline;
157        if(!iOnline)
158            setAccessibleState(false);                      // if not online that not accessable as well
159        fireStatusChanged(ThreatRefStatusChangeEvent(UEV_THREAT_REF_ONLINE_STATUS, this));
160    }
161}
162
163//============================================================
164
165void HostilReference::setAccessibleState(bool pIsAccessible)
166{
167    if(iAccessible != pIsAccessible)
168    {
169        iAccessible = pIsAccessible;
170        fireStatusChanged(ThreatRefStatusChangeEvent(UEV_THREAT_REF_ASSECCIBLE_STATUS, this));
171    }
172}
173
174//============================================================
175// prepare the reference for deleting
176// this is called be the target
177
178void HostilReference::removeReference()
179{
180    invalidate();
181    fireStatusChanged(ThreatRefStatusChangeEvent(UEV_THREAT_REF_REMOVE_FROM_LIST, this));
182}
183
184//============================================================
185
186Unit* HostilReference::getSourceUnit()
187{
188    return (getSource()->getOwner());
189}
190
191//============================================================
192//================ ThreatContainer ===========================
193//============================================================
194
195void ThreatContainer::clearReferences()
196{
197    for(std::list<HostilReference*>::iterator i = iThreatList.begin(); i != iThreatList.end(); i++)
198    {
199        (*i)->unlink();
200        delete (*i);
201    }
202    iThreatList.clear();
203}
204
205//============================================================
206// Return the HostilReference of NULL, if not found
207HostilReference* ThreatContainer::getReferenceByTarget(Unit* pVictim)
208{
209    HostilReference* result = NULL;
210    uint64 guid = pVictim->GetGUID();
211    for(std::list<HostilReference*>::iterator i = iThreatList.begin(); i != iThreatList.end(); i++)
212    {
213        if((*i)->getUnitGuid() == guid)
214        {
215            result = (*i);
216            break;
217        }
218    }
219
220    return result;
221}
222
223//============================================================
224// Add the threat, if we find the reference
225
226HostilReference* ThreatContainer::addThreat(Unit* pVictim, float pThreat)
227{
228    HostilReference* ref = getReferenceByTarget(pVictim);
229    if(ref)
230        ref->addThreat(pThreat);
231    return ref;
232}
233
234//============================================================
235
236void ThreatContainer::modifyThreatPercent(Unit *pVictim, int32 pPercent)
237{
238    if(HostilReference* ref = getReferenceByTarget(pVictim))
239        ref->addThreatPercent(pPercent);
240}
241
242//============================================================
243
244bool HostilReferenceSortPredicate(const HostilReference* lhs, const HostilReference* rhs)
245{
246    // std::list::sort ordering predicate must be: (Pred(x,y)&&Pred(y,x))==false
247    return lhs->getThreat() > rhs->getThreat();             // reverse sorting
248}
249
250//============================================================
251// Check if the list is dirty and sort if necessary
252
253void ThreatContainer::update()
254{
255    if(iDirty && iThreatList.size() >1)
256    {
257        iThreatList.sort(HostilReferenceSortPredicate);
258    }
259    iDirty = false;
260}
261
262//============================================================
263// return the next best victim
264// could be the current victim
265
266HostilReference* ThreatContainer::selectNextVictim(Creature* pAttacker, HostilReference* pCurrentVictim)
267{
268    HostilReference* currentRef = NULL;
269    bool found = false;
270   
271    std::list<HostilReference*>::iterator lastRef = iThreatList.end();
272    lastRef--;
273   
274    for(std::list<HostilReference*>::iterator iter = iThreatList.begin(); iter != iThreatList.end() && !found; ++iter)
275    {
276        currentRef = (*iter);
277
278        Unit* target = currentRef->getTarget();
279        assert(target);                                     // if the ref has status online the target must be there !
280       
281        // some units are prefered in comparison to others
282        if(iter != lastRef && (target->IsImmunedToDamage(pAttacker->GetMeleeDamageSchoolMask(), false) ||
283                target->hasUnitState(UNIT_STAT_CONFUSED | UNIT_STAT_FLEEING)
284                ) )
285        {
286            // current victim is a second choice target, so don't compare threat with it below
287            if(currentRef == pCurrentVictim)
288                pCurrentVictim = NULL;
289            continue;
290        }
291
292        if(!pAttacker->IsOutOfThreatArea(target))           // skip non attackable currently targets
293        {
294            if(pCurrentVictim)                              // select 1.3/1.1 better target in comparison current target
295            {
296                // list sorted and and we check current target, then this is best case
297                if(pCurrentVictim == currentRef || currentRef->getThreat() <= 1.1f * pCurrentVictim->getThreat() )
298                {
299                    currentRef = pCurrentVictim;            // for second case
300                    found = true;
301                    break;
302                }
303
304                if( currentRef->getThreat() > 1.3f * pCurrentVictim->getThreat() ||
305                    currentRef->getThreat() > 1.1f * pCurrentVictim->getThreat() && pAttacker->IsWithinDistInMap(target, ATTACK_DISTANCE) )
306                {                                           //implement 110% threat rule for targets in melee range
307                    found = true;                           //and 130% rule for targets in ranged distances
308                    break;                                  //for selecting alive targets
309                }
310            }
311            else                                            // select any
312            {
313                found = true;
314                break;
315            }
316        }
317    }
318    if(!found)
319        currentRef = NULL;
320
321    return currentRef;
322}
323
324//============================================================
325//=================== ThreatManager ==========================
326//============================================================
327
328ThreatManager::ThreatManager(Unit* owner) : iCurrentVictim(NULL), iOwner(owner)
329{
330}
331
332//============================================================
333
334void ThreatManager::clearReferences()
335{
336    iThreatContainer.clearReferences();
337    iThreatOfflineContainer.clearReferences();
338    iCurrentVictim = NULL;
339}
340
341//============================================================
342
343void ThreatManager::addThreat(Unit* pVictim, float pThreat, SpellSchoolMask schoolMask, SpellEntry const *pThreatSpell)
344{
345    //function deals with adding threat and adding players and pets into ThreatList
346    //mobs, NPCs, guards have ThreatList and HateOfflineList
347    //players and pets have only InHateListOf
348    //HateOfflineList is used co contain unattackable victims (in-flight, in-water, GM etc.)
349
350    if (pVictim == getOwner())                              // only for same creatures :)
351        return;
352
353    if(!pVictim || (pVictim->GetTypeId() == TYPEID_PLAYER && ((Player*)pVictim)->isGameMaster()) )
354        return;
355
356    assert(getOwner()->GetTypeId()== TYPEID_UNIT);
357
358    float threat = ThreatCalcHelper::calcThreat(pVictim, iOwner, pThreat, schoolMask, pThreatSpell);
359
360    HostilReference* ref = iThreatContainer.addThreat(pVictim, threat);
361    // Ref is not in the online refs, search the offline refs next
362    if(!ref)
363        ref = iThreatOfflineContainer.addThreat(pVictim, threat);
364
365    if(!ref)                                                // there was no ref => create a new one
366    {
367                                                            // threat has to be 0 here
368        HostilReference* hostilReference = new HostilReference(pVictim, this, 0);
369        iThreatContainer.addReference(hostilReference);
370        hostilReference->addThreat(threat);                 // now we add the real threat
371        if(pVictim->GetTypeId() == TYPEID_PLAYER && ((Player*)pVictim)->isGameMaster())
372            hostilReference->setOnlineOfflineState(false);  // GM is always offline
373    }
374}
375
376//============================================================
377
378void ThreatManager::modifyThreatPercent(Unit *pVictim, int32 pPercent)
379{
380    iThreatContainer.modifyThreatPercent(pVictim, pPercent);
381}
382
383//============================================================
384
385Unit* ThreatManager::getHostilTarget()
386{
387    iThreatContainer.update();
388    HostilReference* nextVictim = iThreatContainer.selectNextVictim((Creature*) getOwner(), getCurrentVictim());
389    setCurrentVictim(nextVictim);
390    return getCurrentVictim() != NULL ? getCurrentVictim()->getTarget() : NULL;
391}
392
393//============================================================
394
395float ThreatManager::getThreat(Unit *pVictim, bool pAlsoSearchOfflineList)
396{
397    float threat = 0.0f;
398    HostilReference* ref = iThreatContainer.getReferenceByTarget(pVictim);
399    if(!ref && pAlsoSearchOfflineList)
400        ref = iThreatOfflineContainer.getReferenceByTarget(pVictim);
401    if(ref)
402        threat = ref->getThreat();
403    return threat;
404}
405
406//============================================================
407
408void ThreatManager::tauntApply(Unit* pTaunter)
409{
410    HostilReference* ref = iThreatContainer.getReferenceByTarget(pTaunter);
411    if(getCurrentVictim() && ref && (ref->getThreat() < getCurrentVictim()->getThreat()))
412    {
413        if(ref->getTempThreatModifyer() == 0.0f)
414                                                            // Ok, temp threat is unused
415            ref->setTempThreat(getCurrentVictim()->getThreat());
416    }
417}
418
419//============================================================
420
421void ThreatManager::tauntFadeOut(Unit *pTaunter)
422{
423    HostilReference* ref = iThreatContainer.getReferenceByTarget(pTaunter);
424    if(ref)
425        ref->resetTempThreat();
426}
427
428//============================================================
429
430void ThreatManager::setCurrentVictim(HostilReference* pHostilReference)
431{
432    iCurrentVictim = pHostilReference;
433}
434
435//============================================================
436// The hated unit is gone, dead or deleted
437// return true, if the event is consumed
438
439bool ThreatManager::processThreatEvent(const UnitBaseEvent* pUnitBaseEvent)
440{
441    bool consumed = false;
442
443    ThreatRefStatusChangeEvent* threatRefStatusChangeEvent;
444    HostilReference* hostilReference;
445
446    threatRefStatusChangeEvent = (ThreatRefStatusChangeEvent*) pUnitBaseEvent;
447    threatRefStatusChangeEvent->setThreatManager(this);     // now we can set the threat manager
448    hostilReference = threatRefStatusChangeEvent->getReference();
449
450    switch(pUnitBaseEvent->getType())
451    {
452        case UEV_THREAT_REF_THREAT_CHANGE:
453            if((getCurrentVictim() == hostilReference && threatRefStatusChangeEvent->getFValue()<0.0f) ||
454                (getCurrentVictim() != hostilReference && threatRefStatusChangeEvent->getFValue()>0.0f))
455                setDirty(true);                             // the order in the threat list might have changed
456            break;
457        case UEV_THREAT_REF_ONLINE_STATUS:
458            if(!hostilReference->isOnline())
459            {
460                if (hostilReference == getCurrentVictim())
461                {
462                    setCurrentVictim(NULL);
463                    setDirty(true);
464                }
465                iThreatContainer.remove(hostilReference);
466                iThreatOfflineContainer.addReference(hostilReference);
467            }
468            else
469            {
470                if(getCurrentVictim() && hostilReference->getThreat() > (1.1f * getCurrentVictim()->getThreat()))
471                    setDirty(true);
472                iThreatContainer.addReference(hostilReference);
473                iThreatOfflineContainer.remove(hostilReference);
474            }
475            break;
476        case UEV_THREAT_REF_REMOVE_FROM_LIST:
477            if (hostilReference == getCurrentVictim())
478            {
479                setCurrentVictim(NULL);
480                setDirty(true);
481            }
482            if(hostilReference->isOnline())
483                iThreatContainer.remove(hostilReference);
484            else
485                iThreatOfflineContainer.remove(hostilReference);
486            break;
487    }
488    return consumed;
489}
Note: See TracBrowser for help on using the browser.