[9411] More SpellEffectIndex using in apropriate cases
[getmangos.git] / src / game / PetAI.cpp
blob7ddd62503a48efc3b2e84d508a6c9dc2123d49f5
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 // TMGs call CreatureRelocation which via MoveInLineOfSight can call this function
79 // thus with the following clear the original TMG gets invalidated and crash, doh
80 // hope it doesn't start to leak memory without this :-/
81 //i_pet->Clear();
82 m_creature->GetMotionMaster()->MoveChase(u);
83 inCombat = true;
87 void PetAI::EnterEvadeMode()
91 bool PetAI::IsVisible(Unit *pl) const
93 return _isVisible(pl);
96 bool PetAI::_needToStop() const
98 // This is needed for charmed creatures, as once their target was reset other effects can trigger threat
99 if(m_creature->isCharmed() && m_creature->getVictim() == m_creature->GetCharmer())
100 return true;
102 return !m_creature->getVictim()->isTargetableForAttack();
105 void PetAI::_stopAttack()
107 inCombat = false;
108 if( !m_creature->isAlive() )
110 DEBUG_LOG("PetAI (guid = %u) stopped attack, he is dead.", m_creature->GetGUIDLow());
111 m_creature->StopMoving();
112 m_creature->GetMotionMaster()->Clear();
113 m_creature->GetMotionMaster()->MoveIdle();
114 m_creature->CombatStop();
115 m_creature->getHostileRefManager().deleteReferences();
117 return;
120 Unit* owner = m_creature->GetCharmerOrOwner();
122 if(owner && m_creature->GetCharmInfo() && m_creature->GetCharmInfo()->HasCommandState(COMMAND_FOLLOW))
124 m_creature->GetMotionMaster()->MoveFollow(owner,PET_FOLLOW_DIST,PET_FOLLOW_ANGLE);
126 else
128 m_creature->GetMotionMaster()->Clear(false);
129 m_creature->GetMotionMaster()->MoveIdle();
131 m_creature->AttackStop();
134 void PetAI::UpdateAI(const uint32 diff)
136 if (!m_creature->isAlive())
137 return;
139 Unit* owner = m_creature->GetCharmerOrOwner();
141 if(m_updateAlliesTimer <= diff)
142 // UpdateAllies self set update timer
143 UpdateAllies();
144 else
145 m_updateAlliesTimer -= diff;
147 if (inCombat && (!m_creature->getVictim() || m_creature->isPet() && ((Pet*)m_creature)->GetModeFlags() & PET_MODE_DISABLE_ACTIONS))
148 _stopAttack();
150 // i_pet.getVictim() can't be used for check in case stop fighting, i_pet.getVictim() clear at Unit death etc.
151 if (m_creature->getVictim())
153 if (_needToStop())
155 DEBUG_LOG("PetAI (guid = %u) is stopping attack.", m_creature->GetGUIDLow());
156 _stopAttack();
157 return;
159 else if (m_creature->IsStopped() || m_creature->IsWithinDistInMap(m_creature->getVictim(), ATTACK_DISTANCE))
161 // required to be stopped cases
162 if (m_creature->IsStopped() && m_creature->IsNonMeleeSpellCasted(false))
164 if (m_creature->hasUnitState(UNIT_STAT_FOLLOW_MOVE))
165 m_creature->InterruptNonMeleeSpells(false);
166 else
167 return;
169 // not required to be stopped case
170 else if (m_creature->isAttackReady() && m_creature->canReachWithAttack(m_creature->getVictim()))
172 m_creature->AttackerStateUpdate(m_creature->getVictim());
174 m_creature->resetAttackTimer();
176 if (!m_creature->getVictim())
177 return;
179 //if pet misses its target, it will also be the first in threat list
180 m_creature->getVictim()->AddThreat(m_creature);
182 if( _needToStop() )
183 _stopAttack();
187 else if (owner && m_creature->GetCharmInfo())
189 if (owner->isInCombat() && !(m_creature->GetCharmInfo()->HasReactState(REACT_PASSIVE) || m_creature->GetCharmInfo()->HasCommandState(COMMAND_STAY)))
191 AttackStart(owner->getAttackerForHelper());
193 else if(m_creature->GetCharmInfo()->HasCommandState(COMMAND_FOLLOW))
195 if (!m_creature->hasUnitState(UNIT_STAT_FOLLOW) )
197 m_creature->GetMotionMaster()->MoveFollow(owner,PET_FOLLOW_DIST,PET_FOLLOW_ANGLE);
202 // Autocast (casted only in combat or persistent spells in any state)
203 if (m_creature->GetGlobalCooldown() == 0 && !m_creature->IsNonMeleeSpellCasted(false))
205 typedef std::vector<std::pair<Unit*, Spell*> > TargetSpellList;
206 TargetSpellList targetSpellStore;
208 for (uint8 i = 0; i < m_creature->GetPetAutoSpellSize(); ++i)
210 uint32 spellID = m_creature->GetPetAutoSpellOnPos(i);
211 if (!spellID)
212 continue;
214 SpellEntry const *spellInfo = sSpellStore.LookupEntry(spellID);
215 if (!spellInfo)
216 continue;
218 // ignore some combinations of combat state and combat/noncombat spells
219 if (!inCombat)
221 // ignore attacking spells, and allow only self/around spells
222 if (!IsPositiveSpell(spellInfo->Id))
223 continue;
225 // non combat spells allowed
226 // only pet spells have IsNonCombatSpell and not fit this reqs:
227 // Consume Shadows, Lesser Invisibility, so ignore checks for its
228 if (!IsNonCombatSpell(spellInfo))
230 // allow only spell without spell cost or with spell cost but not duration limit
231 int32 duration = GetSpellDuration(spellInfo);
232 if ((spellInfo->manaCost || spellInfo->ManaCostPercentage || spellInfo->manaPerSecond) && duration > 0)
233 continue;
235 // allow only spell without cooldown > duration
236 int32 cooldown = GetSpellRecoveryTime(spellInfo);
237 if (cooldown >= 0 && duration >= 0 && cooldown > duration)
238 continue;
241 else
243 // just ignore non-combat spells
244 if (IsNonCombatSpell(spellInfo))
245 continue;
248 Spell *spell = new Spell(m_creature, spellInfo, false, 0);
250 if (inCombat && !m_creature->hasUnitState(UNIT_STAT_FOLLOW) && spell->CanAutoCast(m_creature->getVictim()))
252 targetSpellStore.push_back(std::make_pair<Unit*, Spell*>(m_creature->getVictim(), spell));
253 continue;
255 else
257 bool spellUsed = false;
258 for(std::set<uint64>::const_iterator tar = m_AllySet.begin(); tar != m_AllySet.end(); ++tar)
260 Unit* Target = ObjectAccessor::GetUnit(*m_creature,*tar);
262 //only buff targets that are in combat, unless the spell can only be cast while out of combat
263 if(!Target)
264 continue;
266 if(spell->CanAutoCast(Target))
268 targetSpellStore.push_back(std::make_pair<Unit*, Spell*>(Target, spell));
269 spellUsed = true;
270 break;
273 if (!spellUsed)
274 delete spell;
278 //found units to cast on to
279 if (!targetSpellStore.empty())
281 uint32 index = urand(0, targetSpellStore.size() - 1);
283 Spell* spell = targetSpellStore[index].second;
284 Unit* target = targetSpellStore[index].first;
286 targetSpellStore.erase(targetSpellStore.begin() + index);
288 SpellCastTargets targets;
289 targets.setUnitTarget( target );
291 if (!m_creature->HasInArc(M_PI_F, target))
293 m_creature->SetInFront(target);
294 if (target->GetTypeId() == TYPEID_PLAYER)
295 m_creature->SendCreateUpdateToPlayer((Player*)target);
297 if (owner && owner->GetTypeId() == TYPEID_PLAYER)
298 m_creature->SendCreateUpdateToPlayer( (Player*)owner );
301 m_creature->AddCreatureSpellCooldown(spell->m_spellInfo->Id);
303 spell->prepare(&targets);
306 // deleted cached Spell objects
307 for(TargetSpellList::const_iterator itr = targetSpellStore.begin(); itr != targetSpellStore.end(); ++itr)
308 delete itr->second;
312 bool PetAI::_isVisible(Unit *u) const
314 return m_creature->IsWithinDist(u,sWorld.getConfig(CONFIG_FLOAT_SIGHT_GUARDER))
315 && u->isVisibleForOrDetect(m_creature,m_creature,true);
318 void PetAI::UpdateAllies()
320 Unit* owner = m_creature->GetCharmerOrOwner();
321 Group *pGroup = NULL;
323 m_updateAlliesTimer = 10*IN_MILISECONDS; //update friendly targets every 10 seconds, lesser checks increase performance
325 if(!owner)
326 return;
327 else if(owner->GetTypeId() == TYPEID_PLAYER)
328 pGroup = ((Player*)owner)->GetGroup();
330 //only pet and owner/not in group->ok
331 if(m_AllySet.size() == 2 && !pGroup)
332 return;
333 //owner is in group; group members filled in already (no raid -> subgroupcount = whole count)
334 if(pGroup && !pGroup->isRaidGroup() && m_AllySet.size() == (pGroup->GetMembersCount() + 2))
335 return;
337 m_AllySet.clear();
338 m_AllySet.insert(m_creature->GetGUID());
339 if(pGroup) //add group
341 for(GroupReference *itr = pGroup->GetFirstMember(); itr != NULL; itr = itr->next())
343 Player* Target = itr->getSource();
344 if(!Target || !pGroup->SameSubGroup((Player*)owner, Target))
345 continue;
347 if(Target->GetGUID() == owner->GetGUID())
348 continue;
350 m_AllySet.insert(Target->GetGUID());
353 else //remove group
354 m_AllySet.insert(owner->GetGUID());
357 void PetAI::AttackedBy(Unit *attacker)
359 //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
360 if(!m_creature->getVictim() && m_creature->GetCharmInfo() && !m_creature->GetCharmInfo()->HasReactState(REACT_PASSIVE) &&
361 (!m_creature->GetCharmInfo()->HasCommandState(COMMAND_STAY) || m_creature->canReachWithAttack(attacker)))
362 AttackStart(attacker);