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

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

[svn] * Merge Temp dev SVN with Assembla.
* Changes include:

  • Implementation of w12x's Outdoor PvP and Game Event Systems.
  • Temporary removal of IRC Chat Bot (until infinite loop when disabled is fixed).
  • All mangos -> trinity (to convert your mangos_string table, please run mangos_string_to_trinity_string.sql).
  • Improved Config cleanup.
  • And many more changes.

Original author: Seline
Date: 2008-10-14 11:57:03-05:00

Line 
1/*
2 * Copyright (C) 2008 Trinity <http://www.trinitycore.org/>
3 *
4 * Thanks to the original authors: MaNGOS <http://www.mangosproject.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()->GetOwner();
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    for(std::list<HostilReference*>::iterator iter = iThreatList.begin(); iter != iThreatList.end() && !found; ++iter)
271    {
272        currentRef = (*iter);
273
274        Unit* target = currentRef->getTarget();
275        assert(target);                                     // if the ref has status online the target must be there !
276
277        if(!pAttacker->IsOutOfThreatArea(target))           // skip non attackable currently targets
278        {
279            if(pCurrentVictim)                              // select 1.3/1.1 better target in comparison current target
280            {
281                // list sorted and and we check current target, then this is best case
282                if(pCurrentVictim == currentRef || currentRef->getThreat() <= 1.1f * pCurrentVictim->getThreat() )
283                {
284                    currentRef = pCurrentVictim;            // for second case
285                    found = true;
286                    break;
287                }
288
289                if( currentRef->getThreat() > 1.3f * pCurrentVictim->getThreat() ||
290                    currentRef->getThreat() > 1.1f * pCurrentVictim->getThreat() && pAttacker->IsWithinDistInMap(target, ATTACK_DISTANCE) )
291                {                                           //implement 110% threat rule for targets in melee range
292                    found = true;                           //and 130% rule for targets in ranged distances
293                    break;                                  //for selecting alive targets
294                }
295            }
296            else                                            // select any
297            {
298                found = true;
299                break;
300            }
301        }
302    }
303    if(!found)
304        currentRef = NULL;
305
306    return currentRef;
307}
308
309//============================================================
310//=================== ThreatManager ==========================
311//============================================================
312
313ThreatManager::ThreatManager(Unit* owner) : iCurrentVictim(NULL), iOwner(owner)
314{
315}
316
317//============================================================
318
319void ThreatManager::clearReferences()
320{
321    iThreatContainer.clearReferences();
322    iThreatOfflineContainer.clearReferences();
323    iCurrentVictim = NULL;
324}
325
326//============================================================
327
328void ThreatManager::addThreat(Unit* pVictim, float pThreat, SpellSchoolMask schoolMask, SpellEntry const *pThreatSpell)
329{
330    //function deals with adding threat and adding players and pets into ThreatList
331    //mobs, NPCs, guards have ThreatList and HateOfflineList
332    //players and pets have only InHateListOf
333    //HateOfflineList is used co contain unattackable victims (in-flight, in-water, GM etc.)
334
335    if (pVictim == getOwner())                              // only for same creatures :)
336        return;
337
338    if(!pVictim || (pVictim->GetTypeId() == TYPEID_PLAYER && ((Player*)pVictim)->isGameMaster()) )
339        return;
340
341    assert(getOwner()->GetTypeId()== TYPEID_UNIT);
342
343    float threat = ThreatCalcHelper::calcThreat(pVictim, iOwner, pThreat, schoolMask, pThreatSpell);
344
345    HostilReference* ref = iThreatContainer.addThreat(pVictim, threat);
346    // Ref is not in the online refs, search the offline refs next
347    if(!ref)
348        ref = iThreatOfflineContainer.addThreat(pVictim, threat);
349
350    if(!ref)                                                // there was no ref => create a new one
351    {
352                                                            // threat has to be 0 here
353        HostilReference* hostilReference = new HostilReference(pVictim, this, 0);
354        iThreatContainer.addReference(hostilReference);
355        hostilReference->addThreat(threat);                 // now we add the real threat
356        if(pVictim->GetTypeId() == TYPEID_PLAYER && ((Player*)pVictim)->isGameMaster())
357            hostilReference->setOnlineOfflineState(false);  // GM is always offline
358    }
359}
360
361//============================================================
362
363void ThreatManager::modifyThreatPercent(Unit *pVictim, int32 pPercent)
364{
365    iThreatContainer.modifyThreatPercent(pVictim, pPercent);
366}
367
368//============================================================
369
370Unit* ThreatManager::getHostilTarget()
371{
372    iThreatContainer.update();
373    HostilReference* nextVictim = iThreatContainer.selectNextVictim((Creature*) getOwner(), getCurrentVictim());
374    setCurrentVictim(nextVictim);
375    return getCurrentVictim() != NULL ? getCurrentVictim()->getTarget() : NULL;
376}
377
378//============================================================
379
380float ThreatManager::getThreat(Unit *pVictim, bool pAlsoSearchOfflineList)
381{
382    float threat = 0.0f;
383    HostilReference* ref = iThreatContainer.getReferenceByTarget(pVictim);
384    if(!ref && pAlsoSearchOfflineList)
385        ref = iThreatOfflineContainer.getReferenceByTarget(pVictim);
386    if(ref)
387        threat = ref->getThreat();
388    return threat;
389}
390
391//============================================================
392
393void ThreatManager::tauntApply(Unit* pTaunter)
394{
395    HostilReference* ref = iThreatContainer.getReferenceByTarget(pTaunter);
396    if(getCurrentVictim() && ref && (ref->getThreat() < getCurrentVictim()->getThreat()))
397    {
398        if(ref->getTempThreatModifyer() == 0.0f)
399                                                            // Ok, temp threat is unused
400            ref->setTempThreat(getCurrentVictim()->getThreat());
401    }
402}
403
404//============================================================
405
406void ThreatManager::tauntFadeOut(Unit *pTaunter)
407{
408    HostilReference* ref = iThreatContainer.getReferenceByTarget(pTaunter);
409    if(ref)
410        ref->resetTempThreat();
411}
412
413//============================================================
414
415void ThreatManager::setCurrentVictim(HostilReference* pHostilReference)
416{
417    iCurrentVictim = pHostilReference;
418}
419
420//============================================================
421// The hated unit is gone, dead or deleted
422// return true, if the event is consumed
423
424bool ThreatManager::processThreatEvent(const UnitBaseEvent* pUnitBaseEvent)
425{
426    bool consumed = false;
427
428    ThreatRefStatusChangeEvent* threatRefStatusChangeEvent;
429    HostilReference* hostilReference;
430
431    threatRefStatusChangeEvent = (ThreatRefStatusChangeEvent*) pUnitBaseEvent;
432    threatRefStatusChangeEvent->setThreatManager(this);     // now we can set the threat manager
433    hostilReference = threatRefStatusChangeEvent->getReference();
434
435    switch(pUnitBaseEvent->getType())
436    {
437        case UEV_THREAT_REF_THREAT_CHANGE:
438            if((getCurrentVictim() == hostilReference && threatRefStatusChangeEvent->getFValue()<0.0f) ||
439                (getCurrentVictim() != hostilReference && threatRefStatusChangeEvent->getFValue()>0.0f))
440                setDirty(true);                             // the order in the threat list might have changed
441            break;
442        case UEV_THREAT_REF_ONLINE_STATUS:
443            if(!hostilReference->isOnline())
444            {
445                if (hostilReference == getCurrentVictim())
446                {
447                    setCurrentVictim(NULL);
448                    setDirty(true);
449                }
450                iThreatContainer.remove(hostilReference);
451                iThreatOfflineContainer.addReference(hostilReference);
452            }
453            else
454            {
455                if(getCurrentVictim() && hostilReference->getThreat() > (1.1f * getCurrentVictim()->getThreat()))
456                    setDirty(true);
457                iThreatContainer.addReference(hostilReference);
458                iThreatOfflineContainer.remove(hostilReference);
459            }
460            break;
461        case UEV_THREAT_REF_REMOVE_FROM_LIST:
462            if (hostilReference == getCurrentVictim())
463            {
464                setCurrentVictim(NULL);
465                setDirty(true);
466            }
467            if(hostilReference->isOnline())
468                iThreatContainer.remove(hostilReference);
469            else
470                iThreatOfflineContainer.remove(hostilReference);
471            break;
472    }
473    return consumed;
474}
Note: See TracBrowser for help on using the browser.