[9143] Typos
[getmangos.git] / src / game / PetAI.cpp
blobe42a32b2c4fdcb9b39f4080098bb244cadc898cd
1 /*
2 * Copyright (C) 2005-2010 MaNGOS <http://getmangos.com/>
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.
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.
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
19 #include "PetAI.h"
20 #include "Errors.h"
21 #include "Pet.h"
22 #include "Player.h"
23 #include "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"
31 int PetAI::Permissible(const Creature *creature)
33 if( creature->isPet())
34 return PERMIT_BASE_SPECIAL;
36 return PERMIT_BASE_NO;
39 PetAI::PetAI(Creature *c) : CreatureAI(c), i_tracker(TIME_INTERVAL_LOOK), inCombat(false)
41 m_AllySet.clear();
42 UpdateAllies();
45 void PetAI::MoveInLineOfSight(Unit *u)
47 if (m_creature->getVictim())
48 return;
50 if (m_creature->isPet() && ((Pet*)m_creature)->GetModeFlags() & PET_MODE_DISABLE_ACTIONS)
51 return;
53 if (!m_creature->GetCharmInfo() || !m_creature->GetCharmInfo()->HasReactState(REACT_AGGRESSIVE))
54 return;
56 if (u->isTargetableForAttack() && m_creature->IsHostileTo( u ) &&
57 u->isInAccessablePlaceFor(m_creature))
59 float attackRadius = m_creature->GetAttackDistance(u);
60 if(m_creature->IsWithinDistInMap(u, attackRadius) && m_creature->GetDistanceZ(u) <= CREATURE_Z_ATTACK_RANGE)
62 if(m_creature->IsWithinLOSInMap(u))
64 AttackStart(u);
65 u->RemoveSpellsCausingAura(SPELL_AURA_MOD_STEALTH);
71 void PetAI::AttackStart(Unit *u)
73 if(!u || (m_creature->isPet() && ((Pet*)m_creature)->getPetType() == MINI_PET))
74 return;
76 if(m_creature->Attack(u,true))
78 m_creature->clearUnitState(UNIT_STAT_FOLLOW);
79 // TMGs call CreatureRelocation which via MoveInLineOfSight can call this function
80 // thus with the following clear the original TMG gets invalidated and crash, doh
81 // hope it doesn't start to leak memory without this :-/
82 //i_pet->Clear();
83 m_creature->GetMotionMaster()->MoveChase(u);
84 inCombat = true;
88 void PetAI::EnterEvadeMode()
92 bool PetAI::IsVisible(Unit *pl) const
94 return _isVisible(pl);
97 bool PetAI::_needToStop() const
99 // This is needed for charmed creatures, as once their target was reset other effects can trigger threat
100 if(m_creature->isCharmed() && m_creature->getVictim() == m_creature->GetCharmer())
101 return true;
103 return !m_creature->getVictim()->isTargetableForAttack();
106 void PetAI::_stopAttack()
108 inCombat = false;
109 if( !m_creature->isAlive() )
111 DEBUG_LOG("PetAI (guid = %u) stopped attack, he is dead.", m_creature->GetGUIDLow());
112 m_creature->StopMoving();
113 m_creature->GetMotionMaster()->Clear();
114 m_creature->GetMotionMaster()->MoveIdle();
115 m_creature->CombatStop();
116 m_creature->getHostileRefManager().deleteReferences();
118 return;
121 Unit* owner = m_creature->GetCharmerOrOwner();
123 if(owner && m_creature->GetCharmInfo() && m_creature->GetCharmInfo()->HasCommandState(COMMAND_FOLLOW))
125 m_creature->GetMotionMaster()->MoveFollow(owner,PET_FOLLOW_DIST,PET_FOLLOW_ANGLE);
127 else
129 m_creature->clearUnitState(UNIT_STAT_FOLLOW);
130 m_creature->GetMotionMaster()->Clear();
131 m_creature->GetMotionMaster()->MoveIdle();
133 m_creature->AttackStop();
136 void PetAI::UpdateAI(const uint32 diff)
138 if (!m_creature->isAlive())
139 return;
141 Unit* owner = m_creature->GetCharmerOrOwner();
143 if(m_updateAlliesTimer <= diff)
144 // UpdateAllies self set update timer
145 UpdateAllies();
146 else
147 m_updateAlliesTimer -= diff;
149 if (inCombat && (!m_creature->getVictim() || m_creature->isPet() && ((Pet*)m_creature)->GetModeFlags() & PET_MODE_DISABLE_ACTIONS))
150 _stopAttack();
152 // i_pet.getVictim() can't be used for check in case stop fighting, i_pet.getVictim() clear at Unit death etc.
153 if (m_creature->getVictim())
155 if (_needToStop())
157 DEBUG_LOG("PetAI (guid = %u) is stopping attack.", m_creature->GetGUIDLow());
158 _stopAttack();
159 return;
161 else if (m_creature->IsStopped() || m_creature->IsWithinDistInMap(m_creature->getVictim(), ATTACK_DISTANCE))
163 // required to be stopped cases
164 if (m_creature->IsStopped() && m_creature->IsNonMeleeSpellCasted(false))
166 if (m_creature->hasUnitState(UNIT_STAT_FOLLOW))
167 m_creature->InterruptNonMeleeSpells(false);
168 else
169 return;
171 // not required to be stopped case
172 else if (m_creature->isAttackReady() && m_creature->canReachWithAttack(m_creature->getVictim()))
174 m_creature->AttackerStateUpdate(m_creature->getVictim());
176 m_creature->resetAttackTimer();
178 if (!m_creature->getVictim())
179 return;
181 //if pet misses its target, it will also be the first in threat list
182 m_creature->getVictim()->AddThreat(m_creature);
184 if( _needToStop() )
185 _stopAttack();
189 else if (owner && m_creature->GetCharmInfo())
191 if (owner->isInCombat() && !(m_creature->GetCharmInfo()->HasReactState(REACT_PASSIVE) || m_creature->GetCharmInfo()->HasCommandState(COMMAND_STAY)))
193 AttackStart(owner->getAttackerForHelper());
195 else if(m_creature->GetCharmInfo()->HasCommandState(COMMAND_FOLLOW))
197 if (!m_creature->hasUnitState(UNIT_STAT_FOLLOW) )
199 m_creature->GetMotionMaster()->MoveFollow(owner,PET_FOLLOW_DIST,PET_FOLLOW_ANGLE);
204 // Autocast (casted only in combat or persistent spells in any state)
205 if (m_creature->GetGlobalCooldown() == 0 && !m_creature->IsNonMeleeSpellCasted(false))
207 typedef std::vector<std::pair<Unit*, Spell*> > TargetSpellList;
208 TargetSpellList targetSpellStore;
210 for (uint8 i = 0; i < m_creature->GetPetAutoSpellSize(); ++i)
212 uint32 spellID = m_creature->GetPetAutoSpellOnPos(i);
213 if (!spellID)
214 continue;
216 SpellEntry const *spellInfo = sSpellStore.LookupEntry(spellID);
217 if (!spellInfo)
218 continue;
220 // ignore some combinations of combat state and combat/noncombat spells
221 if (!inCombat)
223 // ignore attacking spells, and allow only self/around spells
224 if (!IsPositiveSpell(spellInfo->Id))
225 continue;
227 // non combat spells allowed
228 // only pet spells have IsNonCombatSpell and not fit this reqs:
229 // Consume Shadows, Lesser Invisibility, so ignore checks for its
230 if (!IsNonCombatSpell(spellInfo))
232 // allow only spell without spell cost or with spell cost but not duration limit
233 int32 duration = GetSpellDuration(spellInfo);
234 if ((spellInfo->manaCost || spellInfo->ManaCostPercentage || spellInfo->manaPerSecond) && duration > 0)
235 continue;
237 // allow only spell without cooldown > duration
238 int32 cooldown = GetSpellRecoveryTime(spellInfo);
239 if (cooldown >= 0 && duration >= 0 && cooldown > duration)
240 continue;
243 else
245 // just ignore non-combat spells
246 if (IsNonCombatSpell(spellInfo))
247 continue;
250 Spell *spell = new Spell(m_creature, spellInfo, false, 0);
252 if (inCombat && !m_creature->hasUnitState(UNIT_STAT_FOLLOW) && spell->CanAutoCast(m_creature->getVictim()))
254 targetSpellStore.push_back(std::make_pair<Unit*, Spell*>(m_creature->getVictim(), spell));
255 continue;
257 else
259 bool spellUsed = false;
260 for(std::set<uint64>::const_iterator tar = m_AllySet.begin(); tar != m_AllySet.end(); ++tar)
262 Unit* Target = ObjectAccessor::GetUnit(*m_creature,*tar);
264 //only buff targets that are in combat, unless the spell can only be cast while out of combat
265 if(!Target)
266 continue;
268 if(spell->CanAutoCast(Target))
270 targetSpellStore.push_back(std::make_pair<Unit*, Spell*>(Target, spell));
271 spellUsed = true;
272 break;
275 if (!spellUsed)
276 delete spell;
280 //found units to cast on to
281 if (!targetSpellStore.empty())
283 uint32 index = urand(0, targetSpellStore.size() - 1);
285 Spell* spell = targetSpellStore[index].second;
286 Unit* target = targetSpellStore[index].first;
288 targetSpellStore.erase(targetSpellStore.begin() + index);
290 SpellCastTargets targets;
291 targets.setUnitTarget( target );
293 if (!m_creature->HasInArc(M_PI, target))
295 m_creature->SetInFront(target);
296 if (target->GetTypeId() == TYPEID_PLAYER)
297 m_creature->SendCreateUpdateToPlayer((Player*)target);
299 if (owner && owner->GetTypeId() == TYPEID_PLAYER)
300 m_creature->SendCreateUpdateToPlayer( (Player*)owner );
303 m_creature->AddCreatureSpellCooldown(spell->m_spellInfo->Id);
305 spell->prepare(&targets);
308 // deleted cached Spell objects
309 for(TargetSpellList::const_iterator itr = targetSpellStore.begin(); itr != targetSpellStore.end(); ++itr)
310 delete itr->second;
314 bool PetAI::_isVisible(Unit *u) const
316 return m_creature->IsWithinDist(u,sWorld.getConfig(CONFIG_SIGHT_GUARDER))
317 && u->isVisibleForOrDetect(m_creature,m_creature,true);
320 void PetAI::UpdateAllies()
322 Unit* owner = m_creature->GetCharmerOrOwner();
323 Group *pGroup = NULL;
325 m_updateAlliesTimer = 10*IN_MILISECONDS; //update friendly targets every 10 seconds, lesser checks increase performance
327 if(!owner)
328 return;
329 else if(owner->GetTypeId() == TYPEID_PLAYER)
330 pGroup = ((Player*)owner)->GetGroup();
332 //only pet and owner/not in group->ok
333 if(m_AllySet.size() == 2 && !pGroup)
334 return;
335 //owner is in group; group members filled in already (no raid -> subgroupcount = whole count)
336 if(pGroup && !pGroup->isRaidGroup() && m_AllySet.size() == (pGroup->GetMembersCount() + 2))
337 return;
339 m_AllySet.clear();
340 m_AllySet.insert(m_creature->GetGUID());
341 if(pGroup) //add group
343 for(GroupReference *itr = pGroup->GetFirstMember(); itr != NULL; itr = itr->next())
345 Player* Target = itr->getSource();
346 if(!Target || !pGroup->SameSubGroup((Player*)owner, Target))
347 continue;
349 if(Target->GetGUID() == owner->GetGUID())
350 continue;
352 m_AllySet.insert(Target->GetGUID());
355 else //remove group
356 m_AllySet.insert(owner->GetGUID());
359 void PetAI::AttackedBy(Unit *attacker)
361 //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
362 if(!m_creature->getVictim() && m_creature->GetCharmInfo() && !m_creature->GetCharmInfo()->HasReactState(REACT_PASSIVE) &&
363 (!m_creature->GetCharmInfo()->HasCommandState(COMMAND_STAY) || m_creature->canReachWithAttack(attacker)))
364 AttackStart(attacker);