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

Revision 229, 11.1 kB (checked in by yumileroy, 17 years ago)

[svn] *** Source: MaNGOS ***
* Fixed build extractor at Windows Vista. Author: Vladimir
* Fixed comment text and code indentifiers spelling. Author: Vladimir & Paradox.
* Access cached member lists in guild handlers instead of querying the DB. Author: Hunuza
* Small fixes in send/received packet and simple code cleanup also. Author: Vladimir
* Not output error at loading empty character_ticket table. Author: Vladimir
* Not reset display model at shapeshift aura remove if it not set at apply. Author: Arthorius
* Applied props to few files.

Original author: visagalis
Date: 2008-11-14 16:28:45-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 "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_tracker(TIME_INTERVAL_LOOK), inCombat(false)
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        i_pet.IsHostileTo( u ) && i_pet.canAttack(u) &&
52        u->isInAccessiblePlaceFor(&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( !u || (i_pet.isPet() && ((Pet&)i_pet).getPetType() == MINI_PET) )
69        return;
70
71    if (inCombat && i_pet.getVictim() && u != i_pet.getVictim())
72        i_pet.AttackStop();
73
74    if(i_pet.Attack(u,true))
75    {
76        i_pet.clearUnitState(UNIT_STAT_FOLLOW);
77        // TMGs call CreatureRelocation which via MoveInLineOfSight can call this function
78        // thus with the following clear the original TMG gets invalidated and crash, doh
79        // hope it doesn't start to leak memory without this :-/
80        //i_pet->Clear();
81        i_pet.GetMotionMaster()->MoveChase(u);
82        inCombat = true;
83    }
84}
85
86void PetAI::EnterEvadeMode()
87{
88}
89
90bool PetAI::IsVisible(Unit *pl) const
91{
92    return _isVisible(pl);
93}
94
95bool PetAI::_needToStop() const
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.canAttack(i_pet.getVictim());
102}
103
104void PetAI::_stopAttack()
105{
106    inCombat = false;
107    if( !i_pet.isAlive() )
108    {
109        DEBUG_LOG("Creature stoped attacking cuz his dead [guid=%u]", i_pet.GetGUIDLow());
110        i_pet.StopMoving();
111        i_pet.GetMotionMaster()->Clear();
112        i_pet.GetMotionMaster()->MoveIdle();
113        i_pet.CombatStop();
114        i_pet.getHostilRefManager().deleteReferences();
115
116        return;
117    }
118
119    Unit* owner = i_pet.GetCharmerOrOwner();
120
121    if(owner && i_pet.GetCharmInfo() && i_pet.GetCharmInfo()->HasCommandState(COMMAND_FOLLOW))
122    {
123        i_pet.GetMotionMaster()->MoveFollow(owner,PET_FOLLOW_DIST,PET_FOLLOW_ANGLE);
124    }
125    else
126    {
127        i_pet.clearUnitState(UNIT_STAT_FOLLOW);
128        i_pet.GetMotionMaster()->Clear();
129        i_pet.GetMotionMaster()->MoveIdle();
130    }
131    i_pet.AttackStop();
132}
133
134void PetAI::UpdateAI(const uint32 diff)
135{
136    if (!i_pet.isAlive())
137        return;
138
139    Unit* owner = i_pet.GetCharmerOrOwner();
140
141    if(m_updateAlliesTimer <= diff)
142        // UpdateAllies self set update timer
143        UpdateAllies();
144    else
145        m_updateAlliesTimer -= diff;
146
147    if (inCombat && i_pet.getVictim() == NULL)
148        _stopAttack();
149
150    // i_pet.getVictim() can't be used for check in case stop fighting, i_pet.getVictim() clear at Unit death etc.
151    if( i_pet.getVictim() != NULL )
152    {
153        if( _needToStop() )
154        {
155            DEBUG_LOG("Pet AI stoped attacking [guid=%u]", i_pet.GetGUIDLow());
156            _stopAttack();
157            return;
158        }
159        else if( i_pet.IsStopped() || i_pet.IsWithinDistInMap(i_pet.getVictim(), ATTACK_DISTANCE))
160        {
161            // required to be stopped cases
162            if ( i_pet.IsStopped() && i_pet.IsNonMeleeSpellCasted(false) )
163            {
164                if( i_pet.hasUnitState(UNIT_STAT_FOLLOW) )
165                    i_pet.InterruptNonMeleeSpells(false);
166                else
167                    return;
168            }
169            // not required to be stopped case
170            else if( i_pet.isAttackReady() && i_pet.IsWithinCombatDist(i_pet.getVictim(), ATTACK_DISTANCE) )
171            {
172                i_pet.AttackerStateUpdate(i_pet.getVictim());
173
174                i_pet.resetAttackTimer();
175
176                if ( !i_pet.getVictim() )
177                    return;
178
179                //if pet misses its target, it will also be the first in threat list
180                i_pet.getVictim()->AddThreat(&i_pet,0.0f);
181
182                if( _needToStop() )
183                    _stopAttack();
184            }
185        }
186    }
187    else if(owner && i_pet.GetCharmInfo())
188    {
189        if(owner->isInCombat() && !(i_pet.GetCharmInfo()->HasReactState(REACT_PASSIVE) || i_pet.GetCharmInfo()->HasCommandState(COMMAND_STAY)))
190        {
191            AttackStart(owner->getAttackerForHelper());
192        }
193        else if(i_pet.GetCharmInfo()->HasCommandState(COMMAND_FOLLOW))
194        {
195            if (!i_pet.hasUnitState(UNIT_STAT_FOLLOW) )
196            {
197                i_pet.GetMotionMaster()->MoveFollow(owner,PET_FOLLOW_DIST,PET_FOLLOW_ANGLE);
198            }
199        }
200    }
201
202    if (i_pet.GetGlobalCooldown() == 0 && !i_pet.IsNonMeleeSpellCasted(false))
203    {
204        //Autocast
205        for (uint8 i = 0; i < i_pet.GetPetAutoSpellSize(); i++)
206        {
207            uint32 spellID = i_pet.GetPetAutoSpellOnPos(i);
208            if (!spellID)
209                continue;
210
211            SpellEntry const *spellInfo = sSpellStore.LookupEntry(spellID);
212            if (!spellInfo)
213                continue;
214
215            // ignore some combinations of combat state and combat/noncombat spells
216            if (!inCombat)
217            {
218                if (!IsPositiveSpell(spellInfo->Id))
219                    continue;
220            }
221            else
222            {
223                if (IsNonCombatSpell(spellInfo))
224                    continue;
225            }
226
227            Spell *spell = new Spell(&i_pet, spellInfo, false, 0);
228
229            if(inCombat && !i_pet.hasUnitState(UNIT_STAT_FOLLOW) && spell->CanAutoCast(i_pet.getVictim()))
230            {
231                m_targetSpellStore.push_back(std::make_pair<Unit*, Spell*>(i_pet.getVictim(), spell));
232                continue;
233            }
234            else
235            {
236                bool spellUsed = false;
237                for(std::set<uint64>::iterator tar = m_AllySet.begin(); tar != m_AllySet.end(); ++tar)
238                {
239                    Unit* Target = ObjectAccessor::GetUnit(i_pet,*tar);
240
241                    //only buff targets that are in combat, unless the spell can only be cast while out of combat
242                    if(!Target)
243                        continue;
244
245                    if(spell->CanAutoCast(Target))
246                    {
247                        m_targetSpellStore.push_back(std::make_pair<Unit*, Spell*>(Target, spell));
248                        spellUsed = true;
249                        break;
250                    }
251                }
252                if (!spellUsed)
253                    delete spell;
254            }
255        }
256
257        //found units to cast on to
258        if(!m_targetSpellStore.empty())
259        {
260            uint32 index = urand(0, m_targetSpellStore.size() - 1);
261
262            Spell* spell  = m_targetSpellStore[index].second;
263            Unit*  target = m_targetSpellStore[index].first;
264
265            m_targetSpellStore.erase(m_targetSpellStore.begin() + index);
266
267            SpellCastTargets targets;
268            targets.setUnitTarget( target );
269
270            if( !i_pet.HasInArc(M_PI, target) )
271            {
272                i_pet.SetInFront(target);
273                if( target->GetTypeId() == TYPEID_PLAYER )
274                    i_pet.SendUpdateToPlayer( (Player*)target );
275
276                if(owner && owner->GetTypeId() == TYPEID_PLAYER)
277                    i_pet.SendUpdateToPlayer( (Player*)owner );
278            }
279
280            i_pet.AddCreatureSpellCooldown(spell->m_spellInfo->Id);
281            if(i_pet.isPet())
282                ((Pet*)&i_pet)->CheckLearning(spell->m_spellInfo->Id);
283
284            spell->prepare(&targets);
285        }
286        while (!m_targetSpellStore.empty())
287        {
288            delete m_targetSpellStore.begin()->second;
289            m_targetSpellStore.erase(m_targetSpellStore.begin());
290        }
291    }
292}
293
294bool PetAI::_isVisible(Unit *u) const
295{
296    //return false;                                           //( ((Creature*)&i_pet)->GetDistanceSq(u) * 1.0<= sWorld.getConfig(CONFIG_SIGHT_GUARDER) && !u->m_stealth && u->isAlive());
297    return i_pet.GetDistance(u) < sWorld.getConfig(CONFIG_SIGHT_GUARDER)
298        && u->isVisibleForOrDetect(&i_pet,true);
299}
300
301void PetAI::UpdateAllies()
302{
303    Unit* owner = i_pet.GetCharmerOrOwner();
304    Group *pGroup = NULL;
305
306    m_updateAlliesTimer = 10000;                            //update friendly targets every 10 seconds, lesser checks increase performance
307
308    if(!owner)
309        return;
310    else if(owner->GetTypeId() == TYPEID_PLAYER)
311        pGroup = ((Player*)owner)->GetGroup();
312
313    //only pet and owner/not in group->ok
314    if(m_AllySet.size() == 2 && !pGroup)
315        return;
316    //owner is in group; group members filled in already (no raid -> subgroupcount = whole count)
317    if(pGroup && !pGroup->isRaidGroup() && m_AllySet.size() == (pGroup->GetMembersCount() + 2))
318        return;
319
320    m_AllySet.clear();
321    m_AllySet.insert(i_pet.GetGUID());
322    if(pGroup)                                              //add group
323    {
324        for(GroupReference *itr = pGroup->GetFirstMember(); itr != NULL; itr = itr->next())
325        {
326            Player* Target = itr->getSource();
327            if(!Target || !pGroup->SameSubGroup((Player*)owner, Target))
328                continue;
329
330            if(Target->GetGUID() == owner->GetGUID())
331                continue;
332
333            m_AllySet.insert(Target->GetGUID());
334        }
335    }
336    else                                                    //remove group
337        m_AllySet.insert(owner->GetGUID());
338}
339
340void PetAI::AttackedBy(Unit *attacker)
341{
342    //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
343    if(!i_pet.getVictim() && i_pet.GetCharmInfo() && !i_pet.GetCharmInfo()->HasReactState(REACT_PASSIVE) &&
344        (!i_pet.GetCharmInfo()->HasCommandState(COMMAND_STAY) || i_pet.canReachWithAttack(attacker)))
345        AttackStart(attacker);
346}
Note: See TracBrowser for help on using the browser.