Added checking global cooldown for pet autocast spells.

Added checking global cooldown for pet spell casting triggered by master.
Optimized PetAI::UpdateAI:
 - Removed obsolete victim_guid as combat check.
 - Stop pet combat when pet die in JustDied(Unit*) method.
 - Optimized autospells selection and casting.

Signed-off-by: ApoC <apoc@nymfe.net>
This commit is contained in:
ApoC 2008-10-27 22:36:42 +01:00 committed by tomrus88
parent ca6e456da1
commit 8bea43063e
4 changed files with 106 additions and 104 deletions

View file

@ -570,6 +570,8 @@ class MANGOS_DLL_SPEC Creature : public Unit
void SetCombatStartPosition(float x, float y, float z) { CombatStartX = x; CombatStartY = y; CombatStartZ = z; }
void GetCombatStartPosition(float &x, float &y, float &z) { x = CombatStartX; y = CombatStartY; z = CombatStartZ; }
uint32 GetGlobalCooldown() const { return m_GlobalCooldown; }
protected:
bool CreateFromProto(uint32 guidlow,uint32 Entry,uint32 team, const CreatureData *data = NULL);
bool InitEntry(uint32 entry, uint32 team=ALLIANCE, const CreatureData* data=NULL);

View file

@ -36,7 +36,7 @@ int PetAI::Permissible(const Creature *creature)
return PERMIT_BASE_NO;
}
PetAI::PetAI(Creature &c) : i_pet(c), i_victimGuid(0), i_tracker(TIME_INTERVAL_LOOK)
PetAI::PetAI(Creature &c) : i_pet(c), i_tracker(TIME_INTERVAL_LOOK), inCombat(false)
{
m_AllySet.clear();
UpdateAllies();
@ -63,7 +63,7 @@ void PetAI::MoveInLineOfSight(Unit *u)
void PetAI::AttackStart(Unit *u)
{
if( i_pet.getVictim() || !u || i_pet.isPet() && ((Pet&)i_pet).getPetType()==MINI_PET )
if( inCombat || !u || (i_pet.isPet() && ((Pet&)i_pet).getPetType() == MINI_PET) )
return;
if(i_pet.Attack(u,true))
@ -73,8 +73,8 @@ void PetAI::AttackStart(Unit *u)
// thus with the following clear the original TMG gets invalidated and crash, doh
// hope it doesn't start to leak memory without this :-/
//i_pet->Clear();
i_victimGuid = u->GetGUID();
i_pet.GetMotionMaster()->MoveChase(u);
inCombat = true;
}
}
@ -89,9 +89,6 @@ bool PetAI::IsVisible(Unit *pl) const
bool PetAI::_needToStop() const
{
if(!i_pet.getVictim() || !i_pet.isAlive())
return true;
// This is needed for charmed creatures, as once their target was reset other effects can trigger threat
if(i_pet.isCharmed() && i_pet.getVictim() == i_pet.GetCharmer())
return true;
@ -101,48 +98,18 @@ bool PetAI::_needToStop() const
void PetAI::_stopAttack()
{
if( !i_victimGuid )
return;
Unit* victim = ObjectAccessor::GetUnit(i_pet, i_victimGuid );
if ( !victim )
return;
assert(!i_pet.getVictim() || i_pet.getVictim() == victim);
inCombat = false;
if( !i_pet.isAlive() )
{
DEBUG_LOG("Creature stoped attacking cuz his dead [guid=%u]", i_pet.GetGUIDLow());
i_pet.StopMoving();
i_pet.GetMotionMaster()->Clear();
i_pet.GetMotionMaster()->MoveIdle();
i_victimGuid = 0;
i_pet.CombatStop();
i_pet.getHostilRefManager().deleteReferences();
return;
}
else if( !victim )
{
DEBUG_LOG("Creature stopped attacking because victim is non exist [guid=%u]", i_pet.GetGUIDLow());
}
else if( !victim->isAlive() )
{
DEBUG_LOG("Creature stopped attacking cuz his victim is dead [guid=%u]", i_pet.GetGUIDLow());
}
else if( victim->HasStealthAura() )
{
DEBUG_LOG("Creature stopped attacking cuz his victim is stealth [guid=%u]", i_pet.GetGUIDLow());
}
else if( victim->isInFlight() )
{
DEBUG_LOG("Creature stopped attacking cuz his victim is fly away [guid=%u]", i_pet.GetGUIDLow());
}
else
{
DEBUG_LOG("Creature stopped attacking due to target out run him [guid=%u]", i_pet.GetGUIDLow());
}
Unit* owner = i_pet.GetCharmerOrOwner();
@ -156,15 +123,13 @@ void PetAI::_stopAttack()
i_pet.GetMotionMaster()->Clear();
i_pet.GetMotionMaster()->MoveIdle();
}
i_victimGuid = 0;
i_pet.AttackStop();
}
void PetAI::UpdateAI(const uint32 diff)
{
// update i_victimGuid if i_pet.getVictim() !=0 and changed
if(i_pet.getVictim())
i_victimGuid = i_pet.getVictim()->GetGUID();
if (!i_pet.isAlive())
return;
Unit* owner = i_pet.GetCharmerOrOwner();
@ -174,13 +139,16 @@ void PetAI::UpdateAI(const uint32 diff)
else
m_updateAlliesTimer -= diff;
if (inCombat && i_pet.getVictim() == NULL)
_stopAttack();
// i_pet.getVictim() can't be used for check in case stop fighting, i_pet.getVictim() clear at Unit death etc.
if( i_victimGuid )
if( i_pet.getVictim() != NULL )
{
if( _needToStop() )
{
DEBUG_LOG("Pet AI stoped attacking [guid=%u]", i_pet.GetGUIDLow());
_stopAttack(); // i_victimGuid == 0 && i_pet.getVictim() == NULL now
_stopAttack();
return;
}
else if( i_pet.IsStopped() || i_pet.IsWithinDistInMap(i_pet.getVictim(), ATTACK_DISTANCE))
@ -226,75 +194,96 @@ void PetAI::UpdateAI(const uint32 diff)
}
}
//Autocast
HM_NAMESPACE::hash_map<uint32, Unit*> targetMap;
targetMap.clear();
SpellCastTargets NULLtargets;
for (uint8 i = 0; i < i_pet.GetPetAutoSpellSize(); i++)
if (i_pet.GetGlobalCooldown() == 0 && !i_pet.IsNonMeleeSpellCasted(false))
{
uint32 spellID = i_pet.GetPetAutoSpellOnPos(i);
if (!spellID)
continue;
SpellEntry const *spellInfo = sSpellStore.LookupEntry(spellID);
if (!spellInfo)
continue;
Spell *spell = new Spell(&i_pet, spellInfo, false, 0);
if(!IsPositiveSpell(spellInfo->Id) && i_pet.getVictim() && !_needToStop() && !i_pet.hasUnitState(UNIT_STAT_FOLLOW) && spell->CanAutoCast(i_pet.getVictim()))
targetMap[spellID] = i_pet.getVictim();
else
//Autocast
for (uint8 i = 0; i < i_pet.GetPetAutoSpellSize(); i++)
{
spell->m_targets = NULLtargets;
for(std::set<uint64>::iterator tar = m_AllySet.begin(); tar != m_AllySet.end(); ++tar)
{
Unit* Target = ObjectAccessor::GetUnit(i_pet,*tar);
uint32 spellID = i_pet.GetPetAutoSpellOnPos(i);
if (!spellID)
continue;
//only buff targets that are in combat, unless the spell can only be cast while out of combat
if(!Target || (!Target->isInCombat() && !IsNonCombatSpell(spellInfo)))
SpellEntry const *spellInfo = sSpellStore.LookupEntry(spellID);
if (!spellInfo)
continue;
// ignore some combinations of combat state and combat/noncombat spells
if (!inCombat)
{
if (!IsPositiveSpell(spellInfo->Id))
continue;
if(spell->CanAutoCast(Target))
targetMap[spellID] = Target;
}
else
{
if (IsNonCombatSpell(spellInfo))
continue;
}
Spell *spell = new Spell(&i_pet, spellInfo, false, 0);
if(inCombat && !i_pet.hasUnitState(UNIT_STAT_FOLLOW) && spell->CanAutoCast(i_pet.getVictim()))
{
m_targetSpellStore.push_back(std::make_pair<Unit*, Spell*>(i_pet.getVictim(), spell));
continue;
}
else
{
bool spellUsed = false;
for(std::set<uint64>::iterator tar = m_AllySet.begin(); tar != m_AllySet.end(); ++tar)
{
Unit* Target = ObjectAccessor::GetUnit(i_pet,*tar);
//only buff targets that are in combat, unless the spell can only be cast while out of combat
if(!Target)
continue;
if(spell->CanAutoCast(Target))
{
m_targetSpellStore.push_back(std::make_pair<Unit*, Spell*>(Target, spell));
spellUsed = true;
break;
}
}
if (!spellUsed)
delete spell;
}
}
delete spell;
}
//found units to cast on to
if(!targetMap.empty())
{
uint32 index = urand(1, targetMap.size());
HM_NAMESPACE::hash_map<uint32, Unit*>::iterator itr;
uint32 i;
for(itr = targetMap.begin(), i = 1; i < index; ++itr, ++i);
SpellEntry const *spellInfo = sSpellStore.LookupEntry(itr->first);
Spell *spell = new Spell(&i_pet, spellInfo, false);
SpellCastTargets targets;
targets.setUnitTarget( itr->second );
if(!i_pet.HasInArc(M_PI, itr->second))
//found units to cast on to
if(!m_targetSpellStore.empty())
{
i_pet.SetInFront(itr->second);
if( itr->second->GetTypeId() == TYPEID_PLAYER )
i_pet.SendUpdateToPlayer( (Player*)itr->second );
uint32 index = urand(0, m_targetSpellStore.size() - 1);
if(owner && owner->GetTypeId() == TYPEID_PLAYER)
i_pet.SendUpdateToPlayer( (Player*)owner );
Spell* spell = m_targetSpellStore[index].second;
Unit* target = m_targetSpellStore[index].first;
m_targetSpellStore.erase(m_targetSpellStore.begin() + index);
SpellCastTargets targets;
targets.setUnitTarget( target );
if( !i_pet.HasInArc(M_PI, target) )
{
i_pet.SetInFront(target);
if( target->GetTypeId() == TYPEID_PLAYER )
i_pet.SendUpdateToPlayer( (Player*)target );
if(owner && owner->GetTypeId() == TYPEID_PLAYER)
i_pet.SendUpdateToPlayer( (Player*)owner );
}
i_pet.AddCreatureSpellCooldown(spell->m_spellInfo->Id);
if(i_pet.isPet())
((Pet*)&i_pet)->CheckLearning(spell->m_spellInfo->Id);
spell->prepare(&targets);
}
while (!m_targetSpellStore.empty())
{
delete m_targetSpellStore.begin()->second;
m_targetSpellStore.erase(m_targetSpellStore.begin());
}
i_pet.AddCreatureSpellCooldown(itr->first);
if(i_pet.isPet())
((Pet*)&i_pet)->CheckLearning(itr->first);
spell->prepare(&targets);
}
targetMap.clear();
}
bool PetAI::_isVisible(Unit *u) const

View file

@ -23,6 +23,7 @@
#include "Timer.h"
class Creature;
class Spell;
class MANGOS_DLL_DECL PetAI : public CreatureAI
{
@ -36,9 +37,9 @@ class MANGOS_DLL_DECL PetAI : public CreatureAI
void DamageTaken(Unit *done_by, uint32& /*damage*/) { AttackedBy(done_by); }
void AttackedBy(Unit*);
bool IsVisible(Unit *) const;
void JustDied(Unit* who) { _stopAttack(); }
void UpdateAI(const uint32);
void UpdateAllies();
static int Permissible(const Creature *);
private:
@ -46,11 +47,15 @@ class MANGOS_DLL_DECL PetAI : public CreatureAI
bool _needToStop(void) const;
void _stopAttack(void);
void UpdateAllies();
Creature &i_pet;
uint64 i_victimGuid;
bool inCombat;
TimeTracker i_tracker;
//uint32 i_RepeatAction;
std::set<uint64> m_AllySet;
uint32 m_updateAlliesTimer;
typedef std::pair<Unit*, Spell*> TargetSpellPair;
std::vector<TargetSpellPair> m_targetSpellStore;
};
#endif

View file

@ -164,6 +164,9 @@ void WorldSession::HandlePetAction( WorldPacket & recv_data )
else
unit_target = NULL;
if (((Creature*)pet)->GetGlobalCooldown() > 0)
return;
// do not cast unknown spells
SpellEntry const *spellInfo = sSpellStore.LookupEntry(spellid );
if(!spellInfo)
@ -612,6 +615,9 @@ void WorldSession::HandlePetCastSpellOpcode( WorldPacket& recvPacket )
return;
}
if (pet->GetGlobalCooldown() > 0)
return;
SpellEntry const *spellInfo = sSpellStore.LookupEntry(spellid);
if(!spellInfo)
{