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

Revision 102, 11.4 kB (checked in by yumileroy, 17 years ago)

[svn] Fixed copyright notices to comply with GPL.

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