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

Revision 2, 16.2 kB (checked in by yumileroy, 17 years ago)

[svn] * Proper SVN structure

Original author: Neo2003
Date: 2008-10-02 16:23:55-05:00

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