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 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 :-/
83 m_creature
->GetMotionMaster()->MoveChase(u
);
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())
103 return !m_creature
->getVictim()->isTargetableForAttack();
106 void PetAI::_stopAttack()
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();
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
);
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())
141 Unit
* owner
= m_creature
->GetCharmerOrOwner();
143 if(m_updateAlliesTimer
<= diff
)
144 // UpdateAllies self set update timer
147 m_updateAlliesTimer
-= diff
;
149 if (inCombat
&& (!m_creature
->getVictim() || m_creature
->isPet() && ((Pet
*)m_creature
)->GetModeFlags() & PET_MODE_DISABLE_ACTIONS
))
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())
157 DEBUG_LOG("PetAI (guid = %u) is stopping attack.", m_creature
->GetGUIDLow());
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);
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())
181 //if pet misses its target, it will also be the first in threat list
182 m_creature
->getVictim()->AddThreat(m_creature
);
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
);
216 SpellEntry
const *spellInfo
= sSpellStore
.LookupEntry(spellID
);
220 // ignore some combinations of combat state and combat/noncombat spells
223 // ignore attacking spells, and allow only self/around spells
224 if (!IsPositiveSpell(spellInfo
->Id
))
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)
237 // allow only spell without cooldown > duration
238 int32 cooldown
= GetSpellRecoveryTime(spellInfo
);
239 if (cooldown
>= 0 && duration
>= 0 && cooldown
> duration
)
245 // just ignore non-combat spells
246 if (IsNonCombatSpell(spellInfo
))
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
));
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
268 if(spell
->CanAutoCast(Target
))
270 targetSpellStore
.push_back(std::make_pair
<Unit
*, Spell
*>(Target
, 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
)
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
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
)
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))
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
))
349 if(Target
->GetGUID() == owner
->GetGUID())
352 m_AllySet
.insert(Target
->GetGUID());
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
);