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
23 #include "DBCStores.h"
25 #include "ObjectAccessor.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)
45 void PetAI::MoveInLineOfSight(Unit
*u
)
47 if (m_creature
->getVictim())
50 if (m_creature
->isPet() && ((Pet
*)m_creature
)->GetModeFlags() & PET_MODE_DISABLE_ACTIONS
)
53 if (!m_creature
->GetCharmInfo() || !m_creature
->GetCharmInfo()->HasReactState(REACT_AGGRESSIVE
))
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
))
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
))
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 :-/
82 m_creature
->GetMotionMaster()->MoveChase(u
);
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())
102 return !m_creature
->getVictim()->isTargetableForAttack();
105 void PetAI::_stopAttack()
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();
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
);
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())
139 Unit
* owner
= m_creature
->GetCharmerOrOwner();
141 if(m_updateAlliesTimer
<= diff
)
142 // UpdateAllies self set update timer
145 m_updateAlliesTimer
-= diff
;
147 if (inCombat
&& (!m_creature
->getVictim() || m_creature
->isPet() && ((Pet
*)m_creature
)->GetModeFlags() & PET_MODE_DISABLE_ACTIONS
))
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())
155 DEBUG_LOG("PetAI (guid = %u) is stopping attack.", m_creature
->GetGUIDLow());
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);
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())
179 //if pet misses its target, it will also be the first in threat list
180 m_creature
->getVictim()->AddThreat(m_creature
);
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
);
214 SpellEntry
const *spellInfo
= sSpellStore
.LookupEntry(spellID
);
218 // ignore some combinations of combat state and combat/noncombat spells
221 // ignore attacking spells, and allow only self/around spells
222 if (!IsPositiveSpell(spellInfo
->Id
))
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)
235 // allow only spell without cooldown > duration
236 int32 cooldown
= GetSpellRecoveryTime(spellInfo
);
237 if (cooldown
>= 0 && duration
>= 0 && cooldown
> duration
)
243 // just ignore non-combat spells
244 if (IsNonCombatSpell(spellInfo
))
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
));
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
266 if(spell
->CanAutoCast(Target
))
268 targetSpellStore
.push_back(std::make_pair
<Unit
*, Spell
*>(Target
, 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
)
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
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
)
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))
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
))
347 if(Target
->GetGUID() == owner
->GetGUID())
350 m_AllySet
.insert(Target
->GetGUID());
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
);