mirror of
https://github.com/mangosfour/server.git
synced 2025-12-17 16:37:00 +00:00
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:
parent
ca6e456da1
commit
8bea43063e
4 changed files with 106 additions and 104 deletions
|
|
@ -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 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; }
|
void GetCombatStartPosition(float &x, float &y, float &z) { x = CombatStartX; y = CombatStartY; z = CombatStartZ; }
|
||||||
|
|
||||||
|
uint32 GetGlobalCooldown() const { return m_GlobalCooldown; }
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
bool CreateFromProto(uint32 guidlow,uint32 Entry,uint32 team, const CreatureData *data = NULL);
|
bool CreateFromProto(uint32 guidlow,uint32 Entry,uint32 team, const CreatureData *data = NULL);
|
||||||
bool InitEntry(uint32 entry, uint32 team=ALLIANCE, const CreatureData* data=NULL);
|
bool InitEntry(uint32 entry, uint32 team=ALLIANCE, const CreatureData* data=NULL);
|
||||||
|
|
|
||||||
|
|
@ -36,7 +36,7 @@ int PetAI::Permissible(const Creature *creature)
|
||||||
return PERMIT_BASE_NO;
|
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();
|
m_AllySet.clear();
|
||||||
UpdateAllies();
|
UpdateAllies();
|
||||||
|
|
@ -63,7 +63,7 @@ void PetAI::MoveInLineOfSight(Unit *u)
|
||||||
|
|
||||||
void PetAI::AttackStart(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;
|
return;
|
||||||
|
|
||||||
if(i_pet.Attack(u,true))
|
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
|
// thus with the following clear the original TMG gets invalidated and crash, doh
|
||||||
// hope it doesn't start to leak memory without this :-/
|
// hope it doesn't start to leak memory without this :-/
|
||||||
//i_pet->Clear();
|
//i_pet->Clear();
|
||||||
i_victimGuid = u->GetGUID();
|
|
||||||
i_pet.GetMotionMaster()->MoveChase(u);
|
i_pet.GetMotionMaster()->MoveChase(u);
|
||||||
|
inCombat = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -89,9 +89,6 @@ bool PetAI::IsVisible(Unit *pl) const
|
||||||
|
|
||||||
bool PetAI::_needToStop() 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
|
// 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())
|
if(i_pet.isCharmed() && i_pet.getVictim() == i_pet.GetCharmer())
|
||||||
return true;
|
return true;
|
||||||
|
|
@ -101,48 +98,18 @@ bool PetAI::_needToStop() const
|
||||||
|
|
||||||
void PetAI::_stopAttack()
|
void PetAI::_stopAttack()
|
||||||
{
|
{
|
||||||
if( !i_victimGuid )
|
inCombat = false;
|
||||||
return;
|
|
||||||
|
|
||||||
Unit* victim = ObjectAccessor::GetUnit(i_pet, i_victimGuid );
|
|
||||||
|
|
||||||
if ( !victim )
|
|
||||||
return;
|
|
||||||
|
|
||||||
assert(!i_pet.getVictim() || i_pet.getVictim() == victim);
|
|
||||||
|
|
||||||
if( !i_pet.isAlive() )
|
if( !i_pet.isAlive() )
|
||||||
{
|
{
|
||||||
DEBUG_LOG("Creature stoped attacking cuz his dead [guid=%u]", i_pet.GetGUIDLow());
|
DEBUG_LOG("Creature stoped attacking cuz his dead [guid=%u]", i_pet.GetGUIDLow());
|
||||||
i_pet.StopMoving();
|
i_pet.StopMoving();
|
||||||
i_pet.GetMotionMaster()->Clear();
|
i_pet.GetMotionMaster()->Clear();
|
||||||
i_pet.GetMotionMaster()->MoveIdle();
|
i_pet.GetMotionMaster()->MoveIdle();
|
||||||
i_victimGuid = 0;
|
|
||||||
i_pet.CombatStop();
|
i_pet.CombatStop();
|
||||||
i_pet.getHostilRefManager().deleteReferences();
|
i_pet.getHostilRefManager().deleteReferences();
|
||||||
|
|
||||||
return;
|
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();
|
Unit* owner = i_pet.GetCharmerOrOwner();
|
||||||
|
|
||||||
|
|
@ -156,15 +123,13 @@ void PetAI::_stopAttack()
|
||||||
i_pet.GetMotionMaster()->Clear();
|
i_pet.GetMotionMaster()->Clear();
|
||||||
i_pet.GetMotionMaster()->MoveIdle();
|
i_pet.GetMotionMaster()->MoveIdle();
|
||||||
}
|
}
|
||||||
i_victimGuid = 0;
|
|
||||||
i_pet.AttackStop();
|
i_pet.AttackStop();
|
||||||
}
|
}
|
||||||
|
|
||||||
void PetAI::UpdateAI(const uint32 diff)
|
void PetAI::UpdateAI(const uint32 diff)
|
||||||
{
|
{
|
||||||
// update i_victimGuid if i_pet.getVictim() !=0 and changed
|
if (!i_pet.isAlive())
|
||||||
if(i_pet.getVictim())
|
return;
|
||||||
i_victimGuid = i_pet.getVictim()->GetGUID();
|
|
||||||
|
|
||||||
Unit* owner = i_pet.GetCharmerOrOwner();
|
Unit* owner = i_pet.GetCharmerOrOwner();
|
||||||
|
|
||||||
|
|
@ -174,13 +139,16 @@ void PetAI::UpdateAI(const uint32 diff)
|
||||||
else
|
else
|
||||||
m_updateAlliesTimer -= diff;
|
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.
|
// 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() )
|
if( _needToStop() )
|
||||||
{
|
{
|
||||||
DEBUG_LOG("Pet AI stoped attacking [guid=%u]", i_pet.GetGUIDLow());
|
DEBUG_LOG("Pet AI stoped attacking [guid=%u]", i_pet.GetGUIDLow());
|
||||||
_stopAttack(); // i_victimGuid == 0 && i_pet.getVictim() == NULL now
|
_stopAttack();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
else if( i_pet.IsStopped() || i_pet.IsWithinDistInMap(i_pet.getVictim(), ATTACK_DISTANCE))
|
else if( i_pet.IsStopped() || i_pet.IsWithinDistInMap(i_pet.getVictim(), ATTACK_DISTANCE))
|
||||||
|
|
@ -226,75 +194,96 @@ void PetAI::UpdateAI(const uint32 diff)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//Autocast
|
if (i_pet.GetGlobalCooldown() == 0 && !i_pet.IsNonMeleeSpellCasted(false))
|
||||||
HM_NAMESPACE::hash_map<uint32, Unit*> targetMap;
|
|
||||||
targetMap.clear();
|
|
||||||
SpellCastTargets NULLtargets;
|
|
||||||
|
|
||||||
for (uint8 i = 0; i < i_pet.GetPetAutoSpellSize(); i++)
|
|
||||||
{
|
{
|
||||||
uint32 spellID = i_pet.GetPetAutoSpellOnPos(i);
|
//Autocast
|
||||||
if (!spellID)
|
for (uint8 i = 0; i < i_pet.GetPetAutoSpellSize(); i++)
|
||||||
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
|
|
||||||
{
|
{
|
||||||
spell->m_targets = NULLtargets;
|
uint32 spellID = i_pet.GetPetAutoSpellOnPos(i);
|
||||||
for(std::set<uint64>::iterator tar = m_AllySet.begin(); tar != m_AllySet.end(); ++tar)
|
if (!spellID)
|
||||||
{
|
continue;
|
||||||
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
|
SpellEntry const *spellInfo = sSpellStore.LookupEntry(spellID);
|
||||||
if(!Target || (!Target->isInCombat() && !IsNonCombatSpell(spellInfo)))
|
if (!spellInfo)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
// ignore some combinations of combat state and combat/noncombat spells
|
||||||
|
if (!inCombat)
|
||||||
|
{
|
||||||
|
if (!IsPositiveSpell(spellInfo->Id))
|
||||||
continue;
|
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(!m_targetSpellStore.empty())
|
||||||
|
|
||||||
//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))
|
|
||||||
{
|
{
|
||||||
i_pet.SetInFront(itr->second);
|
uint32 index = urand(0, m_targetSpellStore.size() - 1);
|
||||||
if( itr->second->GetTypeId() == TYPEID_PLAYER )
|
|
||||||
i_pet.SendUpdateToPlayer( (Player*)itr->second );
|
|
||||||
|
|
||||||
if(owner && owner->GetTypeId() == TYPEID_PLAYER)
|
Spell* spell = m_targetSpellStore[index].second;
|
||||||
i_pet.SendUpdateToPlayer( (Player*)owner );
|
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
|
bool PetAI::_isVisible(Unit *u) const
|
||||||
|
|
|
||||||
|
|
@ -23,6 +23,7 @@
|
||||||
#include "Timer.h"
|
#include "Timer.h"
|
||||||
|
|
||||||
class Creature;
|
class Creature;
|
||||||
|
class Spell;
|
||||||
|
|
||||||
class MANGOS_DLL_DECL PetAI : public CreatureAI
|
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 DamageTaken(Unit *done_by, uint32& /*damage*/) { AttackedBy(done_by); }
|
||||||
void AttackedBy(Unit*);
|
void AttackedBy(Unit*);
|
||||||
bool IsVisible(Unit *) const;
|
bool IsVisible(Unit *) const;
|
||||||
|
void JustDied(Unit* who) { _stopAttack(); }
|
||||||
|
|
||||||
void UpdateAI(const uint32);
|
void UpdateAI(const uint32);
|
||||||
void UpdateAllies();
|
|
||||||
static int Permissible(const Creature *);
|
static int Permissible(const Creature *);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
@ -46,11 +47,15 @@ class MANGOS_DLL_DECL PetAI : public CreatureAI
|
||||||
bool _needToStop(void) const;
|
bool _needToStop(void) const;
|
||||||
void _stopAttack(void);
|
void _stopAttack(void);
|
||||||
|
|
||||||
|
void UpdateAllies();
|
||||||
|
|
||||||
Creature &i_pet;
|
Creature &i_pet;
|
||||||
uint64 i_victimGuid;
|
bool inCombat;
|
||||||
TimeTracker i_tracker;
|
TimeTracker i_tracker;
|
||||||
//uint32 i_RepeatAction;
|
|
||||||
std::set<uint64> m_AllySet;
|
std::set<uint64> m_AllySet;
|
||||||
uint32 m_updateAlliesTimer;
|
uint32 m_updateAlliesTimer;
|
||||||
|
|
||||||
|
typedef std::pair<Unit*, Spell*> TargetSpellPair;
|
||||||
|
std::vector<TargetSpellPair> m_targetSpellStore;
|
||||||
};
|
};
|
||||||
#endif
|
#endif
|
||||||
|
|
|
||||||
|
|
@ -164,6 +164,9 @@ void WorldSession::HandlePetAction( WorldPacket & recv_data )
|
||||||
else
|
else
|
||||||
unit_target = NULL;
|
unit_target = NULL;
|
||||||
|
|
||||||
|
if (((Creature*)pet)->GetGlobalCooldown() > 0)
|
||||||
|
return;
|
||||||
|
|
||||||
// do not cast unknown spells
|
// do not cast unknown spells
|
||||||
SpellEntry const *spellInfo = sSpellStore.LookupEntry(spellid );
|
SpellEntry const *spellInfo = sSpellStore.LookupEntry(spellid );
|
||||||
if(!spellInfo)
|
if(!spellInfo)
|
||||||
|
|
@ -612,6 +615,9 @@ void WorldSession::HandlePetCastSpellOpcode( WorldPacket& recvPacket )
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (pet->GetGlobalCooldown() > 0)
|
||||||
|
return;
|
||||||
|
|
||||||
SpellEntry const *spellInfo = sSpellStore.LookupEntry(spellid);
|
SpellEntry const *spellInfo = sSpellStore.LookupEntry(spellid);
|
||||||
if(!spellInfo)
|
if(!spellInfo)
|
||||||
{
|
{
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue