root/trunk/src/game/PetAI.cpp @ 9

Revision 2, 11.3 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 "PetAI.h"
20#include "Errors.h"
21#include "Pet.h"
22#include "Player.h"
23#include "Database/DBCStores.h"
24#include "Spell.h"
25#include "ObjectAccessor.h"
26#include "SpellMgr.h"
27#include "Creature.h"
28#include "World.h"
29#include "Util.h"
30
31int PetAI::Permissible(const Creature *creature)
32{
33    if( creature->isPet())
34        return PERMIT_BASE_SPECIAL;
35
36    return PERMIT_BASE_NO;
37}
38
39PetAI::PetAI(Creature &c) : i_pet(c), i_victimGuid(0), i_tracker(TIME_INTERVAL_LOOK)
40{
41    m_AllySet.clear();
42    UpdateAllies();
43}
44
45void PetAI::MoveInLineOfSight(Unit *u)
46{
47    if( !i_pet.getVictim() && i_pet.GetCharmInfo() &&
48        i_pet.GetCharmInfo()->HasReactState(REACT_AGGRESSIVE) &&
49        u->isTargetableForAttack() && i_pet.IsHostileTo( u ) &&
50        u->isInAccessablePlaceFor(&i_pet))
51    {
52        float attackRadius = i_pet.GetAttackDistance(u);
53        if(i_pet.IsWithinDistInMap(u, attackRadius) && i_pet.GetDistanceZ(u) <= CREATURE_Z_ATTACK_RANGE)
54        {
55            if(i_pet.IsWithinLOSInMap(u))
56            {
57                AttackStart(u);
58                u->RemoveSpellsCausingAura(SPELL_AURA_MOD_STEALTH);
59            }
60        }
61    }
62}
63
64void PetAI::AttackStart(Unit *u)
65{
66    if( i_pet.getVictim() || !u || i_pet.isPet() && ((Pet&)i_pet).getPetType()==MINI_PET )
67        return;
68
69    if(i_pet.Attack(u,true))
70    {
71        i_pet.clearUnitState(UNIT_STAT_FOLLOW);
72        // TMGs call CreatureRelocation which via MoveInLineOfSight can call this function
73        // thus with the following clear the original TMG gets invalidated and crash, doh
74        // hope it doesn't start to leak memory without this :-/
75        //i_pet->Clear();
76        i_victimGuid = u->GetGUID();
77        i_pet.GetMotionMaster()->MoveChase(u);
78    }
79}
80
81void PetAI::EnterEvadeMode()
82{
83}
84
85bool PetAI::IsVisible(Unit *pl) const
86{
87    return _isVisible(pl);
88}
89
90bool PetAI::_needToStop() const
91{
92    if(!i_pet.getVictim() || !i_pet.isAlive())
93        return true;
94
95    // This is needed for charmed creatures, as once their target was reset other effects can trigger threat
96    if(i_pet.isCharmed() && i_pet.getVictim() == i_pet.GetCharmer())
97        return true;
98
99    return !i_pet.getVictim()->isTargetableForAttack();
100}
101
102void PetAI::_stopAttack()
103{
104    if( !i_victimGuid )
105        return;
106
107    Unit* victim = ObjectAccessor::GetUnit(i_pet, i_victimGuid );
108
109    if ( !victim )
110        return;
111
112    assert(!i_pet.getVictim() || i_pet.getVictim() == victim);
113
114    if( !i_pet.isAlive() )
115    {
116        DEBUG_LOG("Creature stoped attacking cuz his dead [guid=%u]", i_pet.GetGUIDLow());
117        i_pet.StopMoving();
118        i_pet.GetMotionMaster()->Clear();
119        i_pet.GetMotionMaster()->MoveIdle();
120        i_victimGuid = 0;
121        i_pet.CombatStop();
122        i_pet.getHostilRefManager().deleteReferences();
123
124        return;
125    }
126    else if( !victim  )
127    {
128        DEBUG_LOG("Creature stopped attacking because victim is non exist [guid=%u]", i_pet.GetGUIDLow());
129    }
130    else if( !victim->isAlive() )
131    {
132        DEBUG_LOG("Creature stopped attacking cuz his victim is dead [guid=%u]", i_pet.GetGUIDLow());
133    }
134    else if( victim->HasStealthAura() )
135    {
136        DEBUG_LOG("Creature stopped attacking cuz his victim is stealth [guid=%u]", i_pet.GetGUIDLow());
137    }
138    else if( victim->isInFlight() )
139    {
140        DEBUG_LOG("Creature stopped attacking cuz his victim is fly away [guid=%u]", i_pet.GetGUIDLow());
141    }
142    else
143    {
144        DEBUG_LOG("Creature stopped attacking due to target out run him [guid=%u]", i_pet.GetGUIDLow());
145    }
146
147    Unit* owner = i_pet.GetCharmerOrOwner();
148
149    if(owner && i_pet.GetCharmInfo() && i_pet.GetCharmInfo()->HasCommandState(COMMAND_FOLLOW))
150    {
151        i_pet.GetMotionMaster()->MoveFollow(owner,PET_FOLLOW_DIST,PET_FOLLOW_ANGLE);
152    }
153    else
154    {
155        i_pet.clearUnitState(UNIT_STAT_FOLLOW);
156        i_pet.GetMotionMaster()->Clear();
157        i_pet.GetMotionMaster()->MoveIdle();
158    }
159    i_victimGuid = 0;
160    i_pet.AttackStop();
161}
162
163void PetAI::UpdateAI(const uint32 diff)
164{
165    // update i_victimGuid if i_pet.getVictim() !=0 and changed
166    if(i_pet.getVictim())
167        i_victimGuid = i_pet.getVictim()->GetGUID();
168
169    Unit* owner = i_pet.GetCharmerOrOwner();
170
171    if(m_updateAlliesTimer <= diff)
172        // UpdateAllies self set update timer
173        UpdateAllies();
174    else
175        m_updateAlliesTimer -= diff;
176
177    // i_pet.getVictim() can't be used for check in case stop fighting, i_pet.getVictim() clear at Unit death etc.
178    if( i_victimGuid )
179    {
180        if( _needToStop() )
181        {
182            DEBUG_LOG("Pet AI stoped attacking [guid=%u]", i_pet.GetGUIDLow());
183            _stopAttack();                                  // i_victimGuid == 0 && i_pet.getVictim() == NULL now
184            return;
185        }
186        else if( i_pet.IsStopped() || i_pet.IsWithinDistInMap(i_pet.getVictim(), ATTACK_DISTANCE))
187        {
188            // required to be stopped cases
189            if ( i_pet.IsStopped() && i_pet.IsNonMeleeSpellCasted(false) )
190            {
191                if( i_pet.hasUnitState(UNIT_STAT_FOLLOW) )
192                    i_pet.InterruptNonMeleeSpells(false);
193                else
194                    return;
195            }
196            // not required to be stopped case
197            else if( i_pet.isAttackReady() && i_pet.canReachWithAttack(i_pet.getVictim()) )
198            {
199                i_pet.AttackerStateUpdate(i_pet.getVictim());
200
201                i_pet.resetAttackTimer();
202
203                if ( !i_pet.getVictim() )
204                    return;
205
206                //if pet misses its target, it will also be the first in threat list
207                i_pet.getVictim()->AddThreat(&i_pet,0.0f);
208
209                if( _needToStop() )
210                    _stopAttack();
211            }
212        }
213    }
214    else if(owner && i_pet.GetCharmInfo())
215    {
216        if(owner->isInCombat() && !(i_pet.GetCharmInfo()->HasReactState(REACT_PASSIVE) || i_pet.GetCharmInfo()->HasCommandState(COMMAND_STAY)))
217        {
218            AttackStart(owner->getAttackerForHelper());
219        }
220        else if(i_pet.GetCharmInfo()->HasCommandState(COMMAND_FOLLOW))
221        {
222            if (!i_pet.hasUnitState(UNIT_STAT_FOLLOW) )
223            {
224                i_pet.GetMotionMaster()->MoveFollow(owner,PET_FOLLOW_DIST,PET_FOLLOW_ANGLE);
225            }
226        }
227    }
228
229    //Autocast
230    HM_NAMESPACE::hash_map<uint32, Unit*> targetMap;
231    targetMap.clear();
232    SpellCastTargets NULLtargets;
233
234    for (uint8 i = 0; i < i_pet.GetPetAutoSpellSize(); i++)
235    {
236        uint32 spellID = i_pet.GetPetAutoSpellOnPos(i);
237        if (!spellID)
238            continue;
239
240        SpellEntry const *spellInfo = sSpellStore.LookupEntry(spellID);
241        if (!spellInfo)
242            continue;
243
244        Spell *spell = new Spell(&i_pet, spellInfo, false, 0);
245
246        if(!IsPositiveSpell(spellInfo->Id) && i_pet.getVictim() && !_needToStop() && !i_pet.hasUnitState(UNIT_STAT_FOLLOW) && spell->CanAutoCast(i_pet.getVictim()))
247            targetMap[spellID] = i_pet.getVictim();
248        else
249        {
250            spell->m_targets = NULLtargets;
251            for(std::set<uint64>::iterator tar = m_AllySet.begin(); tar != m_AllySet.end(); ++tar)
252            {
253                Unit* Target = ObjectAccessor::GetUnit(i_pet,*tar);
254
255                //only buff targets that are in combat, unless the spell can only be cast while out of combat
256                if(!Target || (!Target->isInCombat() && !IsNonCombatSpell(spellInfo)))
257                    continue;
258                if(spell->CanAutoCast(Target))
259                    targetMap[spellID] = Target;
260            }
261        }
262
263        delete spell;
264    }
265
266    //found units to cast on to
267    if(!targetMap.empty())
268    {
269        uint32 index = urand(1, targetMap.size());
270        HM_NAMESPACE::hash_map<uint32, Unit*>::iterator itr;
271        uint32 i;
272        for(itr = targetMap.begin(), i = 1; i < index; ++itr, ++i);
273
274        SpellEntry const *spellInfo = sSpellStore.LookupEntry(itr->first);
275
276        Spell *spell = new Spell(&i_pet, spellInfo, false);
277
278        SpellCastTargets targets;
279        targets.setUnitTarget( itr->second );
280
281        if(!i_pet.HasInArc(M_PI, itr->second))
282        {
283            i_pet.SetInFront(itr->second);
284            if( itr->second->GetTypeId() == TYPEID_PLAYER )
285                i_pet.SendUpdateToPlayer( (Player*)itr->second );
286
287            if(owner && owner->GetTypeId() == TYPEID_PLAYER)
288                i_pet.SendUpdateToPlayer( (Player*)owner );
289        }
290
291        i_pet.AddCreatureSpellCooldown(itr->first);
292        if(i_pet.isPet())
293            ((Pet*)&i_pet)->CheckLearning(itr->first);
294
295        spell->prepare(&targets);
296    }
297    targetMap.clear();
298}
299
300bool PetAI::_isVisible(Unit *u) const
301{
302    //return false;                                           //( ((Creature*)&i_pet)->GetDistanceSq(u) * 1.0<= sWorld.getConfig(CONFIG_SIGHT_GUARDER) && !u->m_stealth && u->isAlive());
303    return i_pet.GetDistance(u) < sWorld.getConfig(CONFIG_SIGHT_GUARDER)
304        && u->isVisibleForOrDetect(&i_pet,true);
305}
306
307void PetAI::UpdateAllies()
308{
309    Unit* owner = i_pet.GetCharmerOrOwner();
310    Group *pGroup = NULL;
311
312    m_updateAlliesTimer = 10000;                            //update friendly targets every 10 seconds, lesser checks increase performance
313
314    if(!owner)
315        return;
316    else if(owner->GetTypeId() == TYPEID_PLAYER)
317        pGroup = ((Player*)owner)->GetGroup();
318
319    //only pet and owner/not in group->ok
320    if(m_AllySet.size() == 2 && !pGroup)
321        return;
322    //owner is in group; group members filled in already (no raid -> subgroupcount = whole count)
323    if(pGroup && !pGroup->isRaidGroup() && m_AllySet.size() == (pGroup->GetMembersCount() + 2))
324        return;
325
326    m_AllySet.clear();
327    m_AllySet.insert(i_pet.GetGUID());
328    if(pGroup)                                              //add group
329    {
330        for(GroupReference *itr = pGroup->GetFirstMember(); itr != NULL; itr = itr->next())
331        {
332            Player* Target = itr->getSource();
333            if(!Target || !pGroup->SameSubGroup((Player*)owner, Target))
334                continue;
335
336            if(Target->GetGUID() == owner->GetGUID())
337                continue;
338
339            m_AllySet.insert(Target->GetGUID());
340        }
341    }
342    else                                                    //remove group
343        m_AllySet.insert(owner->GetGUID());
344}
345
346void PetAI::AttackedBy(Unit *attacker)
347{
348    //when attacked, fight back in case 1)no victim already AND 2)not set to passive AND 3)not set to stay, unless can it can reach attacker with melee attack anyway
349    if(!i_pet.getVictim() && i_pet.GetCharmInfo() && !i_pet.GetCharmInfo()->HasReactState(REACT_PASSIVE) &&
350        (!i_pet.GetCharmInfo()->HasCommandState(COMMAND_STAY) || i_pet.canReachWithAttack(attacker)))
351        AttackStart(attacker);
352}
Note: See TracBrowser for help on using the browser.