aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/server/game/AI/CoreAI/PetAI.cpp217
-rw-r--r--src/server/game/AI/CoreAI/PetAI.h14
-rw-r--r--src/server/game/AI/CreatureAI.h4
-rw-r--r--src/server/game/Entities/Creature/Creature.cpp66
-rw-r--r--src/server/game/Entities/Creature/Creature.h2
-rw-r--r--src/server/game/Entities/Pet/Pet.cpp1
-rw-r--r--src/server/game/Entities/Unit/Unit.cpp102
-rw-r--r--src/server/game/Entities/Unit/Unit.h42
-rw-r--r--src/server/game/Grids/Notifiers/GridNotifiers.h29
-rw-r--r--src/server/game/Handlers/PetHandler.cpp4
-rwxr-xr-xsrc/server/game/Movement/MovementGenerators/TargetedMovementGenerator.cpp4
-rw-r--r--src/server/game/Spells/Auras/SpellAuraEffects.cpp16
12 files changed, 341 insertions, 160 deletions
diff --git a/src/server/game/AI/CoreAI/PetAI.cpp b/src/server/game/AI/CoreAI/PetAI.cpp
index 1de5417b820..812d8dc5f68 100644
--- a/src/server/game/AI/CoreAI/PetAI.cpp
+++ b/src/server/game/AI/CoreAI/PetAI.cpp
@@ -44,10 +44,6 @@ PetAI::PetAI(Creature* c) : CreatureAI(c), i_tracker(TIME_INTERVAL_LOOK)
UpdateAllies();
}
-void PetAI::EnterEvadeMode()
-{
-}
-
bool PetAI::_needToStop()
{
// This is needed for charmed creatures, as once their target was reset other effects can trigger threat
@@ -74,12 +70,13 @@ void PetAI::_stopAttack()
me->InterruptNonMeleeSpells(false);
me->SendMeleeAttackStop(); // Should stop pet's attack button from flashing
me->GetCharmInfo()->SetIsCommandAttack(false);
+ ClearCharmInfoFlags();
HandleReturnMovement();
}
void PetAI::UpdateAI(const uint32 diff)
{
- if (!me->isAlive())
+ if (!me->isAlive() || !me->GetCharmInfo())
return;
Unit* owner = me->GetCharmerOrOwner();
@@ -107,39 +104,34 @@ void PetAI::UpdateAI(const uint32 diff)
}
// Check before attacking to prevent pets from leaving stay position
- if (CanAttack(me->getVictim()))
+ if (me->GetCharmInfo()->HasCommandState(COMMAND_STAY))
+ {
+ if (me->GetCharmInfo()->IsCommandAttack() || (me->GetCharmInfo()->IsAtStay() && me->IsWithinMeleeRange(me->getVictim())))
+ DoMeleeAttackIfReady();
+ }
+ else
DoMeleeAttackIfReady();
}
- else if (owner && me->GetCharmInfo()) //no victim
+ else
{
- // Only aggressive pets do target search every update.
- // Defensive pets do target search only in these cases:
- // * Owner attacks something - handled by OwnerAttacked()
- // * Owner receives damage - handled by OwnerDamagedBy()
- // * Pet is in combat and current target dies - handled by KilledUnit()
- if (me->HasReactState(REACT_AGGRESSIVE))
+ if (me->HasReactState(REACT_AGGRESSIVE) || me->GetCharmInfo()->IsAtStay())
{
- Unit* nextTarget = SelectNextTarget();
+ // Every update we need to check targets only in certain cases
+ // Aggressive - Allow auto select if owner or pet don't have a target
+ // Stay - Only pick from pet or owner targets / attackers so targets won't run by
+ // while chasing our owner. Don't do auto select.
+ // All other cases (ie: defensive) - Targets are assigned by AttackedBy(), OwnerAttackedBy(), OwnerAttacked(), etc.
+ Unit* nextTarget = SelectNextTarget(me->HasReactState(REACT_AGGRESSIVE));
if (nextTarget)
AttackStart(nextTarget);
else
- {
- me->GetCharmInfo()->SetIsCommandAttack(false);
HandleReturnMovement();
- }
}
else
- {
- me->GetCharmInfo()->SetIsCommandAttack(false);
HandleReturnMovement();
- }
- }
- else if (owner && !me->HasUnitState(UNIT_STATE_FOLLOW)) // no charm info and no victim
- HandleReturnMovement();
- if (!me->GetCharmInfo())
- return;
+ }
// Autocast (casted only in combat or persistent spells in any state)
if (!me->HasUnitState(UNIT_STATE_CASTING))
@@ -313,15 +305,11 @@ void PetAI::KilledUnit(Unit* victim)
me->InterruptNonMeleeSpells(false);
me->SendMeleeAttackStop(); // Stops the pet's 'Attack' button from flashing
- Unit* nextTarget = SelectNextTarget();
-
- if (nextTarget)
+ // Before returning to owner, see if there are more things to attack
+ if (Unit* nextTarget = SelectNextTarget(false))
AttackStart(nextTarget);
else
- {
- me->GetCharmInfo()->SetIsCommandAttack(false);
HandleReturnMovement(); // Return
- }
}
void PetAI::AttackStart(Unit* target)
@@ -332,17 +320,14 @@ void PetAI::AttackStart(Unit* target)
if (!CanAttack(target))
return;
- if (Unit* owner = me->GetOwner())
- owner->SetInCombatWith(target);
-
// Only chase if not commanded to stay or if stay but commanded to attack
DoAttack(target, (!me->GetCharmInfo()->HasCommandState(COMMAND_STAY) || me->GetCharmInfo()->IsCommandAttack()));
}
-void PetAI::OwnerDamagedBy(Unit* attacker)
+void PetAI::OwnerAttackedBy(Unit* attacker)
{
- // Called when owner takes damage. Allows defensive pets to know
- // that their owner might need help
+ // Called when owner takes damage. This function helps keep pets from running off
+ // simply due to owner gaining aggro.
if (!attacker)
return;
@@ -380,10 +365,12 @@ void PetAI::OwnerAttacked(Unit* target)
AttackStart(target);
}
-Unit* PetAI::SelectNextTarget()
+Unit* PetAI::SelectNextTarget(bool allowAutoSelect) const
{
- // Provides next target selection after current target death
- // Targets are not evaluated here for being valid attack targets
+ // Provides next target selection after current target death.
+ // This function should only be called internally by the AI
+ // Targets are not evaluated here for being valid targets, that is done in _CanAttack()
+ // The parameter: allowAutoSelect lets us disable aggressive pet auto targeting for certain situations
// Passive pets don't do next target selection
if (me->HasReactState(REACT_PASSIVE))
@@ -406,17 +393,17 @@ Unit* PetAI::SelectNextTarget()
// Check owner victim
// 3.0.2 - Pets now start attacking their owners victim in defensive mode as soon as the hunter does
if (Unit* ownerVictim = me->GetCharmerOrOwner()->getVictim())
- if (!ownerVictim->HasBreakableByDamageCrowdControlAura())
return ownerVictim;
// Neither pet or owner had a target and aggressive pets can pick any target
- // Note: Creature::SelectNearestTarget() If no distance is supplied it uses MAX_VISIBILITY_DISTANCE
- // We also want to lock this to LOS so pet doesn't go running through walls and stuff
- if (me->HasReactState(REACT_AGGRESSIVE))
- if (Unit* nearTarget = me->ToCreature()->SelectNearestTarget())
- if (nearTarget->IsHostileTo(me) && !nearTarget->HasBreakableByDamageCrowdControlAura())
- if (nearTarget->IsWithinLOS(me->GetPositionX(), me->GetPositionY(), me->GetPositionZ()))
- return nearTarget;
+ // To prevent aggressive pets from chain selecting targets and running off, we
+ // only select a random target if certain conditions are met.
+ if (me->HasReactState(REACT_AGGRESSIVE) && allowAutoSelect)
+ {
+ if (!me->GetCharmInfo()->IsReturning() || me->GetCharmInfo()->IsFollowing() || me->GetCharmInfo()->IsAtStay())
+ if (Unit* nearTarget = me->ToCreature()->SelectNearestHostileUnitInAggroRange(true))
+ return nearTarget;
+ }
// Default - no valid targets
return NULL;
@@ -426,6 +413,11 @@ void PetAI::HandleReturnMovement()
{
// Handles moving the pet back to stay or owner
+ // Prevent activating movement when under control of spells
+ // such as "Eyes of the Beast"
+ if (me->isCharmed())
+ return;
+
if (me->GetCharmInfo()->HasCommandState(COMMAND_STAY))
{
if (!me->GetCharmInfo()->IsAtStay() && !me->GetCharmInfo()->IsReturning())
@@ -436,6 +428,7 @@ void PetAI::HandleReturnMovement()
float x, y, z;
me->GetCharmInfo()->GetStayPosition(x, y, z);
+ ClearCharmInfoFlags();
me->GetCharmInfo()->SetIsReturning(true);
me->GetMotionMaster()->Clear();
me->GetMotionMaster()->MovePoint(me->GetGUIDLow(), x, y, z);
@@ -448,6 +441,7 @@ void PetAI::HandleReturnMovement()
{
if (!me->GetCharmInfo()->IsCommandAttack())
{
+ ClearCharmInfoFlags();
me->GetCharmInfo()->SetIsReturning(true);
me->GetMotionMaster()->Clear();
me->GetMotionMaster()->MoveFollow(me->GetCharmerOrOwner(), PET_FOLLOW_DIST, me->GetFollowAngle());
@@ -458,33 +452,33 @@ void PetAI::HandleReturnMovement()
void PetAI::DoAttack(Unit* target, bool chase)
{
- // Handles attack with or without chase and also resets all
- // PetAI flags for next update / creature kill
+ // Handles attack with or without chase and also resets flags
+ // for next update / creature kill
- // me->GetCharmInfo()->SetIsCommandAttack(false);
+ if (me->Attack(target, true))
+ {
+ if (Unit* owner = me->GetOwner())
+ owner->SetInCombatWith(target);
- // The following conditions are true if chase == true
- // (Follow && (Aggressive || Defensive))
- // ((Stay || Follow) && (Passive && player clicked attack))
+ // Play sound to let the player know the pet is attacking something it picked on its own
+ if (me->HasReactState(REACT_AGGRESSIVE) && !me->GetCharmInfo()->IsCommandAttack())
+ me->SendPetAIReaction(me->GetGUID());
- if (chase)
- {
- if (me->Attack(target, true))
+
+ if (chase)
+ {
+ ClearCharmInfoFlags();
+ me->GetMotionMaster()->Clear();
+ me->GetMotionMaster()->MoveChase(target);
+ }
+ else // (Stay && ((Aggressive || Defensive) && In Melee Range)))
{
- me->GetCharmInfo()->SetIsAtStay(false);
- me->GetCharmInfo()->SetIsFollowing(false);
- me->GetCharmInfo()->SetIsReturning(false);
+ ClearCharmInfoFlags();
+ me->GetCharmInfo()->SetIsAtStay(true);
me->GetMotionMaster()->Clear();
- me->GetMotionMaster()->MoveChase(target);
+ me->GetMotionMaster()->MoveIdle();
}
}
- else // (Stay && ((Aggressive || Defensive) && In Melee Range)))
- {
- me->GetCharmInfo()->SetIsAtStay(true);
- me->GetCharmInfo()->SetIsFollowing(false);
- me->GetCharmInfo()->SetIsReturning(false);
- me->Attack(target, true);
- }
}
void PetAI::MovementInform(uint32 moveType, uint32 data)
@@ -498,10 +492,8 @@ void PetAI::MovementInform(uint32 moveType, uint32 data)
// pet's GUIDLow since we set that as the waypoint ID
if (data == me->GetGUIDLow() && me->GetCharmInfo()->IsReturning())
{
+ ClearCharmInfoFlags();
me->GetCharmInfo()->SetIsAtStay(true);
- me->GetCharmInfo()->SetIsReturning(false);
- me->GetCharmInfo()->SetIsFollowing(false);
- me->GetCharmInfo()->SetIsCommandAttack(false);
me->GetMotionMaster()->Clear();
me->GetMotionMaster()->MoveIdle();
}
@@ -513,10 +505,8 @@ void PetAI::MovementInform(uint32 moveType, uint32 data)
// otherwise we're probably chasing a creature
if (me->GetCharmerOrOwner() && me->GetCharmInfo() && data == me->GetCharmerOrOwner()->GetGUIDLow() && me->GetCharmInfo()->IsReturning())
{
- me->GetCharmInfo()->SetIsAtStay(false);
- me->GetCharmInfo()->SetIsReturning(false);
+ ClearCharmInfoFlags();
me->GetCharmInfo()->SetIsFollowing(true);
- me->GetCharmInfo()->SetIsCommandAttack(false);
}
break;
}
@@ -527,32 +517,51 @@ void PetAI::MovementInform(uint32 moveType, uint32 data)
bool PetAI::CanAttack(Unit* target)
{
- // Evaluates wether a pet can attack a specific
- // target based on CommandState, ReactState and other flags
+ // Evaluates wether a pet can attack a specific target based on CommandState, ReactState and other flags
+ // IMPORTANT: The order in which things are checked is important, be careful if you add or remove checks
- // Can't attack dead targets...
- if (!target->isAlive())
+ // Hmmm...
+ if (!target)
return false;
- // Returning - check first since pets returning ignore attacks
- if (me->GetCharmInfo()->IsReturning())
+ if (!target->isAlive())
+ {
+ // Clear target to prevent getting stuck on dead targets
+ me->AttackStop();
+ me->InterruptNonMeleeSpells(false);
+ me->SendMeleeAttackStop();
return false;
+ }
- // Passive - check now so we don't have to worry about passive in later checks
+ // Passive - passive pets can attack if told to
if (me->HasReactState(REACT_PASSIVE))
return me->GetCharmInfo()->IsCommandAttack();
- // Follow
- if (me->GetCharmInfo()->HasCommandState(COMMAND_FOLLOW))
- return true;
+ // CC - mobs under crowd control can be attacked if owner commanded
+ if (target->HasBreakableByDamageCrowdControlAura())
+ return me->GetCharmInfo()->IsCommandAttack();
+
+ // Returning - pets ignore attacks only if owner clicked follow
+ if (me->GetCharmInfo()->IsReturning())
+ return !me->GetCharmInfo()->IsCommandFollow();
// Stay - can attack if target is within range or commanded to
if (me->GetCharmInfo()->HasCommandState(COMMAND_STAY))
return (me->IsWithinMeleeRange(target) || me->GetCharmInfo()->IsCommandAttack());
- // Pets commanded to attack should not stop their approach if attacked by another creature
+ // Pets attacking something (or chasing) should only switch targets if owner tells them to
if (me->getVictim() && (me->getVictim() != target))
- return !me->GetCharmInfo()->IsCommandAttack();
+ {
+ // Check if our owner selected this target and clicked "attack"
+ Unit* ownerTarget = me->GetCharmerOrOwner()->ToPlayer()->GetSelectedUnit();
+
+ if (ownerTarget && me->GetCharmInfo()->IsCommandAttack())
+ return (target->GetGUID() == ownerTarget->GetGUID());
+ }
+
+ // Follow
+ if (me->GetCharmInfo()->HasCommandState(COMMAND_FOLLOW))
+ return !me->GetCharmInfo()->IsReturning();
// default, though we shouldn't ever get here
return false;
@@ -573,3 +582,39 @@ void PetAI::ReceiveEmote(Player* player, uint32 emote)
break;
}
}
+
+void PetAI::ClearCharmInfoFlags()
+{
+ // Quick access to set all flags to FALSE
+
+ CharmInfo* ci = me->GetCharmInfo();
+
+ if (ci)
+ {
+ ci->SetIsAtStay(false);
+ ci->SetIsCommandAttack(false);
+ ci->SetIsCommandFollow(false);
+ ci->SetIsFollowing(false);
+ ci->SetIsReturning(false);
+ }
+}
+
+void PetAI::AttackedBy(Unit* attacker)
+{
+ // Called when pet takes damage. This function helps keep pets from running off
+ // simply due to gaining aggro.
+
+ if (!attacker)
+ return;
+
+ // Passive pets don't do anything
+ if (me->HasReactState(REACT_PASSIVE))
+ return;
+
+ // Prevent pet from disengaging from current target
+ if (me->getVictim() && me->getVictim()->isAlive())
+ return;
+
+ // Continue to evaluate and attack if necessary
+ AttackStart(attacker);
+}
diff --git a/src/server/game/AI/CoreAI/PetAI.h b/src/server/game/AI/CoreAI/PetAI.h
index d7f1dca3fbf..1115333fd01 100644
--- a/src/server/game/AI/CoreAI/PetAI.h
+++ b/src/server/game/AI/CoreAI/PetAI.h
@@ -31,17 +31,24 @@ class PetAI : public CreatureAI
explicit PetAI(Creature* c);
- void EnterEvadeMode();
void UpdateAI(const uint32);
static int Permissible(const Creature*);
void KilledUnit(Unit* /*victim*/);
void AttackStart(Unit* target);
void MovementInform(uint32 moveType, uint32 data);
- void OwnerDamagedBy(Unit* attacker);
+ void OwnerAttackedBy(Unit* attacker);
void OwnerAttacked(Unit* target);
+ void AttackedBy(Unit* attacker);
void ReceiveEmote(Player* player, uint32 textEmote);
+ // The following aren't used by the PetAI but need to be defined to override
+ // default CreatureAI functions which interfere with the PetAI
+ //
+ void MoveInLineOfSight(Unit* who) {} // CreatureAI interferes with returning pets
+ void MoveInLineOfSight_Safe(Unit* who) {} // CreatureAI interferes with returning pets
+ void EnterEvadeMode() {} // For fleeing, pets don't use this type of Evade mechanic
+
private:
bool _isVisible(Unit*) const;
bool _needToStop(void);
@@ -54,10 +61,11 @@ class PetAI : public CreatureAI
std::set<uint64> m_AllySet;
uint32 m_updateAlliesTimer;
- Unit* SelectNextTarget();
+ Unit* SelectNextTarget(bool allowAutoSelect) const;
void HandleReturnMovement();
void DoAttack(Unit* target, bool chase);
bool CanAttack(Unit* target);
+ void ClearCharmInfoFlags();
};
#endif
diff --git a/src/server/game/AI/CreatureAI.h b/src/server/game/AI/CreatureAI.h
index d3ad27935ca..e6439ec9920 100644
--- a/src/server/game/AI/CreatureAI.h
+++ b/src/server/game/AI/CreatureAI.h
@@ -117,7 +117,7 @@ class CreatureAI : public UnitAI
virtual void SpellHitTarget(Unit* /*target*/, SpellInfo const* /*spell*/) {}
// Called when the creature is target of hostile action: swing, hostile spell landed, fear/etc)
- //virtual void AttackedBy(Unit* attacker);
+ virtual void AttackedBy(Unit* attacker) {}
virtual bool IsEscorted() { return false; }
// Called when creature is spawned or respawned (for reseting variables)
@@ -137,7 +137,7 @@ class CreatureAI : public UnitAI
virtual void ReceiveEmote(Player* /*player*/, uint32 /*emoteId*/) {}
// Called when owner takes damage
- virtual void OwnerDamagedBy(Unit* /*attacker*/) {}
+ virtual void OwnerAttackedBy(Unit* /*attacker*/) {}
// Called when owner attacks something
virtual void OwnerAttacked(Unit* /*target*/) {}
diff --git a/src/server/game/Entities/Creature/Creature.cpp b/src/server/game/Entities/Creature/Creature.cpp
index 911ccd25728..66da873f9e9 100644
--- a/src/server/game/Entities/Creature/Creature.cpp
+++ b/src/server/game/Entities/Creature/Creature.cpp
@@ -2515,3 +2515,69 @@ bool Creature::SetHover(bool enable)
SendMessageToSet(&data, false);
return true;
}
+
+float Creature::GetAggroRange(Unit const* target) const
+{
+ // Determines the aggro range for creatures (usually pets), used mainly for aggressive pet target selection.
+ // Based on data from wowwiki due to lack of 3.3.5a data
+
+ if (target && this->isPet())
+ {
+ uint32 targetLevel = 0;
+
+ if (target->GetTypeId() == TYPEID_PLAYER)
+ targetLevel = target->getLevelForTarget(this);
+ else if (target->GetTypeId() == TYPEID_UNIT)
+ targetLevel = target->ToCreature()->getLevelForTarget(this);
+
+ uint32 myLevel = getLevelForTarget(target);
+ int32 levelDiff = int32(targetLevel) - int32(myLevel);
+
+ // The maximum Aggro Radius is capped at 45 yards (25 level difference)
+ if (levelDiff < -25)
+ levelDiff = -25;
+
+ // The base aggro radius for mob of same level
+ float aggroRadius = 20;
+
+ // Aggro Radius varies with level difference at a rate of roughly 1 yard/level
+ aggroRadius -= (float)levelDiff;
+
+ // detect range auras
+ aggroRadius += GetTotalAuraModifier(SPELL_AURA_MOD_DETECT_RANGE);
+
+ // detected range auras
+ aggroRadius += target->GetTotalAuraModifier(SPELL_AURA_MOD_DETECTED_RANGE);
+
+ // Just in case, we don't want pets running all over the map
+ if (aggroRadius > MAX_AGGRO_RADIUS)
+ aggroRadius = MAX_AGGRO_RADIUS;
+
+ // Minimum Aggro Radius for a mob seems to be combat range (5 yards)
+ // hunter pets seem to ignore minimum aggro radius so we'll default it a little higher
+ if (aggroRadius < 10)
+ aggroRadius = 10;
+
+ return (aggroRadius);
+ }
+
+ // Default
+ return 0.0f;
+}
+
+Unit* Creature::SelectNearestHostileUnitInAggroRange(bool useLOS) const
+{
+ // Selects nearest hostile target within creature's aggro range. Used primarily by
+ // pets set to aggressive. Will not return neutral or friendly targets.
+
+ Unit* target = NULL;
+
+ {
+ Trinity::NearestHostileUnitInAggroRangeCheck u_check(this, useLOS);
+ Trinity::UnitSearcher<Trinity::NearestHostileUnitInAggroRangeCheck> searcher(this, target, u_check);
+
+ VisitNearbyGridObject(MAX_AGGRO_RADIUS, searcher);
+ }
+
+ return target;
+}
diff --git a/src/server/game/Entities/Creature/Creature.h b/src/server/game/Entities/Creature/Creature.h
index f9b682383f1..8a697dd0ef0 100644
--- a/src/server/game/Entities/Creature/Creature.h
+++ b/src/server/game/Entities/Creature/Creature.h
@@ -621,12 +621,14 @@ class Creature : public Unit, public GridObject<Creature>, public MapCreature
bool canStartAttack(Unit const* u, bool force) const;
float GetAttackDistance(Unit const* player) const;
+ float GetAggroRange(Unit const* target) const;
void SendAIReaction(AiReaction reactionType);
Unit* SelectNearestTarget(float dist = 0) const;
Unit* SelectNearestTargetInAttackDistance(float dist = 0) const;
Player* SelectNearestPlayer(float distance = 0) const;
+ Unit* SelectNearestHostileUnitInAggroRange(bool useLOS = false) const;
void DoFleeToGetAssistance();
void CallForHelp(float fRadius);
diff --git a/src/server/game/Entities/Pet/Pet.cpp b/src/server/game/Entities/Pet/Pet.cpp
index b6417681f4a..5238eed2e15 100644
--- a/src/server/game/Entities/Pet/Pet.cpp
+++ b/src/server/game/Entities/Pet/Pet.cpp
@@ -74,6 +74,7 @@ void Pet::AddToWorld()
if (GetCharmInfo() && GetCharmInfo()->HasCommandState(COMMAND_FOLLOW))
{
GetCharmInfo()->SetIsCommandAttack(false);
+ GetCharmInfo()->SetIsCommandFollow(false);
GetCharmInfo()->SetIsAtStay(false);
GetCharmInfo()->SetIsFollowing(false);
GetCharmInfo()->SetIsReturning(false);
diff --git a/src/server/game/Entities/Unit/Unit.cpp b/src/server/game/Entities/Unit/Unit.cpp
index c469edd1112..f1d421b916a 100644
--- a/src/server/game/Entities/Unit/Unit.cpp
+++ b/src/server/game/Entities/Unit/Unit.cpp
@@ -551,18 +551,22 @@ uint32 Unit::DealDamage(Unit* victim, uint32 damage, CleanDamage const* cleanDam
if (IsAIEnabled)
GetAI()->DamageDealt(victim, damage, damagetype);
- if (victim->GetTypeId() == TYPEID_PLAYER)
+ if (victim->GetTypeId() == TYPEID_PLAYER && this != victim)
{
- if (victim->ToPlayer()->GetCommandStatus(CHEAT_GOD))
- return 0;
-
// Signal to pets that their owner was attacked
Pet* pet = victim->ToPlayer()->GetPet();
if (pet && pet->isAlive())
- pet->AI()->OwnerDamagedBy(this);
+ pet->AI()->OwnerAttackedBy(this);
+
+ if (victim->ToPlayer()->GetCommandStatus(CHEAT_GOD))
+ return 0;
}
+ // Signal the pet it was attacked so the AI can respond if needed
+ if (victim->GetTypeId() == TYPEID_UNIT && this != victim && victim->isPet() && victim->isAlive())
+ victim->ToPet()->AI()->AttackedBy(this);
+
if (damagetype != NODAMAGE)
{
// interrupting auras with AURA_INTERRUPT_FLAG_DAMAGE before checking !damage (absorbed damage breaks that type of auras)
@@ -13948,17 +13952,17 @@ void Unit::DeleteCharmInfo()
}
CharmInfo::CharmInfo(Unit* unit)
-: m_unit(unit), m_CommandState(COMMAND_FOLLOW), m_petnumber(0), m_barInit(false),
- m_isCommandAttack(false), m_isAtStay(false), m_isFollowing(false), m_isReturning(false),
- m_stayX(0.0f), m_stayY(0.0f), m_stayZ(0.0f)
+: _unit(unit), _CommandState(COMMAND_FOLLOW), _petnumber(0), _barInit(false),
+ _isCommandAttack(false), _isAtStay(false), _isFollowing(false), _isReturning(false),
+ _stayX(0.0f), _stayY(0.0f), _stayZ(0.0f)
{
for (uint8 i = 0; i < MAX_SPELL_CHARM; ++i)
- m_charmspells[i].SetActionAndType(0, ACT_DISABLED);
+ _charmspells[i].SetActionAndType(0, ACT_DISABLED);
- if (m_unit->GetTypeId() == TYPEID_UNIT)
+ if (_unit->GetTypeId() == TYPEID_UNIT)
{
- m_oldReactState = m_unit->ToCreature()->GetReactState();
- m_unit->ToCreature()->SetReactState(REACT_PASSIVE);
+ _oldReactState = _unit->ToCreature()->GetReactState();
+ _unit->ToCreature()->SetReactState(REACT_PASSIVE);
}
}
@@ -13968,9 +13972,9 @@ CharmInfo::~CharmInfo()
void CharmInfo::RestoreState()
{
- if (m_unit->GetTypeId() == TYPEID_UNIT)
- if (Creature* creature = m_unit->ToCreature())
- creature->SetReactState(m_oldReactState);
+ if (_unit->GetTypeId() == TYPEID_UNIT)
+ if (Creature* creature = _unit->ToCreature())
+ creature->SetReactState(_oldReactState);
}
void CharmInfo::InitPetActionBar()
@@ -14001,16 +14005,16 @@ void CharmInfo::InitEmptyActionBar(bool withAttack)
void CharmInfo::InitPossessCreateSpells()
{
InitEmptyActionBar();
- if (m_unit->GetTypeId() == TYPEID_UNIT)
+ if (_unit->GetTypeId() == TYPEID_UNIT)
{
for (uint32 i = 0; i < CREATURE_MAX_SPELLS; ++i)
{
- uint32 spellId = m_unit->ToCreature()->m_spells[i];
+ uint32 spellId = _unit->ToCreature()->m_spells[i];
SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(spellId);
if (spellInfo && !(spellInfo->Attributes & SPELL_ATTR0_CASTABLE_WHILE_DEAD))
{
if (spellInfo->IsPassive())
- m_unit->CastSpell(m_unit, spellInfo, true);
+ _unit->CastSpell(_unit, spellInfo, true);
else
AddSpellToActionBar(spellInfo, ACT_PASSIVE);
}
@@ -14020,7 +14024,7 @@ void CharmInfo::InitPossessCreateSpells()
void CharmInfo::InitCharmCreateSpells()
{
- if (m_unit->GetTypeId() == TYPEID_PLAYER) // charmed players don't have spells
+ if (_unit->GetTypeId() == TYPEID_PLAYER) // charmed players don't have spells
{
InitEmptyActionBar();
return;
@@ -14030,23 +14034,23 @@ void CharmInfo::InitCharmCreateSpells()
for (uint32 x = 0; x < MAX_SPELL_CHARM; ++x)
{
- uint32 spellId = m_unit->ToCreature()->m_spells[x];
+ uint32 spellId = _unit->ToCreature()->m_spells[x];
SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(spellId);
if (!spellInfo || spellInfo->Attributes & SPELL_ATTR0_CASTABLE_WHILE_DEAD)
{
- m_charmspells[x].SetActionAndType(spellId, ACT_DISABLED);
+ _charmspells[x].SetActionAndType(spellId, ACT_DISABLED);
continue;
}
if (spellInfo->IsPassive())
{
- m_unit->CastSpell(m_unit, spellInfo, true);
- m_charmspells[x].SetActionAndType(spellId, ACT_PASSIVE);
+ _unit->CastSpell(_unit, spellInfo, true);
+ _charmspells[x].SetActionAndType(spellId, ACT_PASSIVE);
}
else
{
- m_charmspells[x].SetActionAndType(spellId, ACT_DISABLED);
+ _charmspells[x].SetActionAndType(spellId, ACT_DISABLED);
ActiveStates newstate = ACT_PASSIVE;
@@ -14123,17 +14127,17 @@ void CharmInfo::ToggleCreatureAutocast(SpellInfo const* spellInfo, bool apply)
return;
for (uint32 x = 0; x < MAX_SPELL_CHARM; ++x)
- if (spellInfo->Id == m_charmspells[x].GetAction())
- m_charmspells[x].SetType(apply ? ACT_ENABLED : ACT_DISABLED);
+ if (spellInfo->Id == _charmspells[x].GetAction())
+ _charmspells[x].SetType(apply ? ACT_ENABLED : ACT_DISABLED);
}
void CharmInfo::SetPetNumber(uint32 petnumber, bool statwindow)
{
- m_petnumber = petnumber;
+ _petnumber = petnumber;
if (statwindow)
- m_unit->SetUInt32Value(UNIT_FIELD_PETNUMBER, m_petnumber);
+ _unit->SetUInt32Value(UNIT_FIELD_PETNUMBER, _petnumber);
else
- m_unit->SetUInt32Value(UNIT_FIELD_PETNUMBER, 0);
+ _unit->SetUInt32Value(UNIT_FIELD_PETNUMBER, 0);
}
void CharmInfo::LoadPetActionBar(const std::string& data)
@@ -17579,58 +17583,68 @@ uint32 Unit::GetResistance(SpellSchoolMask mask) const
void CharmInfo::SetIsCommandAttack(bool val)
{
- m_isCommandAttack = val;
+ _isCommandAttack = val;
}
bool CharmInfo::IsCommandAttack()
{
- return m_isCommandAttack;
+ return _isCommandAttack;
+}
+
+void CharmInfo::SetIsCommandFollow(bool val)
+{
+ _isCommandFollow = val;
+}
+
+bool CharmInfo::IsCommandFollow()
+{
+ return _isCommandFollow;
}
void CharmInfo::SaveStayPosition()
{
//! At this point a new spline destination is enabled because of Unit::StopMoving()
- G3D::Vector3 const stayPos = m_unit->movespline->FinalDestination();
- m_stayX = stayPos.x;
- m_stayY = stayPos.y;
- m_stayZ = stayPos.z;
+ G3D::Vector3 const stayPos = _unit->movespline->FinalDestination();
+ _stayX = stayPos.x;
+ _stayY = stayPos.y;
+ _stayZ = stayPos.z;
}
void CharmInfo::GetStayPosition(float &x, float &y, float &z)
{
- x = m_stayX;
- y = m_stayY;
- z = m_stayZ;
+ x = _stayX;
+ y = _stayY;
+ z = _stayZ;
}
void CharmInfo::SetIsAtStay(bool val)
{
- m_isAtStay = val;
+ _isAtStay = val;
}
bool CharmInfo::IsAtStay()
{
- return m_isAtStay;
+ return _isAtStay;
}
void CharmInfo::SetIsFollowing(bool val)
{
- m_isFollowing = val;
+ _isFollowing = val;
}
bool CharmInfo::IsFollowing()
{
- return m_isFollowing;
+ return _isFollowing;
}
void CharmInfo::SetIsReturning(bool val)
{
- m_isReturning = val;
+ _isReturning = val;
}
bool CharmInfo::IsReturning()
{
- return m_isReturning;
+ return _isReturning;
}
void Unit::SetInFront(Unit const* target)
diff --git a/src/server/game/Entities/Unit/Unit.h b/src/server/game/Entities/Unit/Unit.h
index 7b2308c2a51..3053d0018d4 100644
--- a/src/server/game/Entities/Unit/Unit.h
+++ b/src/server/game/Entities/Unit/Unit.h
@@ -253,6 +253,7 @@ enum UnitRename
#define MAX_SPELL_CONTROL_BAR 10
#define MAX_AGGRO_RESET_TIME 10 // in seconds
+#define MAX_AGGRO_RADIUS 45.0f // yards
enum Swing
{
@@ -1094,12 +1095,12 @@ struct CharmInfo
explicit CharmInfo(Unit* unit);
~CharmInfo();
void RestoreState();
- uint32 GetPetNumber() const { return m_petnumber; }
+ uint32 GetPetNumber() const { return _petnumber; }
void SetPetNumber(uint32 petnumber, bool statwindow);
- void SetCommandState(CommandStates st) { m_CommandState = st; }
- CommandStates GetCommandState() const { return m_CommandState; }
- bool HasCommandState(CommandStates state) const { return (m_CommandState == state); }
+ void SetCommandState(CommandStates st) { _CommandState = st; }
+ CommandStates GetCommandState() const { return _CommandState; }
+ bool HasCommandState(CommandStates state) const { return (_CommandState == state); }
void InitPossessCreateSpells();
void InitCharmCreateSpells();
@@ -1120,12 +1121,14 @@ struct CharmInfo
void ToggleCreatureAutocast(SpellInfo const* spellInfo, bool apply);
- CharmSpellInfo* GetCharmSpell(uint8 index) { return &(m_charmspells[index]); }
+ CharmSpellInfo* GetCharmSpell(uint8 index) { return &(_charmspells[index]); }
GlobalCooldownMgr& GetGlobalCooldownMgr() { return m_GlobalCooldownMgr; }
void SetIsCommandAttack(bool val);
bool IsCommandAttack();
+ void SetIsCommandFollow(bool val);
+ bool IsCommandFollow();
void SetIsAtStay(bool val);
bool IsAtStay();
void SetIsFollowing(bool val);
@@ -1137,23 +1140,24 @@ struct CharmInfo
private:
- Unit* m_unit;
+ Unit* _unit;
UnitActionBarEntry PetActionBar[MAX_UNIT_ACTION_BAR_INDEX];
- CharmSpellInfo m_charmspells[4];
- CommandStates m_CommandState;
- uint32 m_petnumber;
- bool m_barInit;
+ CharmSpellInfo _charmspells[4];
+ CommandStates _CommandState;
+ uint32 _petnumber;
+ bool _barInit;
//for restoration after charmed
- ReactStates m_oldReactState;
-
- bool m_isCommandAttack;
- bool m_isAtStay;
- bool m_isFollowing;
- bool m_isReturning;
- float m_stayX;
- float m_stayY;
- float m_stayZ;
+ ReactStates _oldReactState;
+
+ bool _isCommandAttack;
+ bool _isCommandFollow;
+ bool _isAtStay;
+ bool _isFollowing;
+ bool _isReturning;
+ float _stayX;
+ float _stayY;
+ float _stayZ;
GlobalCooldownMgr m_GlobalCooldownMgr;
};
diff --git a/src/server/game/Grids/Notifiers/GridNotifiers.h b/src/server/game/Grids/Notifiers/GridNotifiers.h
index 752c6ec068e..24d2a45c4f2 100644
--- a/src/server/game/Grids/Notifiers/GridNotifiers.h
+++ b/src/server/game/Grids/Notifiers/GridNotifiers.h
@@ -1087,6 +1087,35 @@ namespace Trinity
NearestHostileUnitInAttackDistanceCheck(NearestHostileUnitInAttackDistanceCheck const&);
};
+ class NearestHostileUnitInAggroRangeCheck
+ {
+ public:
+ explicit NearestHostileUnitInAggroRangeCheck(Creature const* creature, bool useLOS = false) : _me(creature), _useLOS(useLOS)
+ {
+ }
+ bool operator()(Unit* u)
+ {
+ if (!u->IsHostileTo(_me))
+ return false;
+
+ if (!u->IsWithinDistInMap(_me, _me->GetAggroRange(u)))
+ return false;
+
+ if (!_me->IsValidAttackTarget(u))
+ return false;
+
+ if (_useLOS && !u->IsWithinLOSInMap(_me))
+ return false;
+
+ return true;
+ }
+
+ private:
+ Creature const* _me;
+ bool _useLOS;
+ NearestHostileUnitInAggroRangeCheck(NearestHostileUnitInAggroRangeCheck const&);
+ };
+
class AnyAssistCreatureInRangeCheck
{
public:
diff --git a/src/server/game/Handlers/PetHandler.cpp b/src/server/game/Handlers/PetHandler.cpp
index 36dacaead69..5e43462b6d0 100644
--- a/src/server/game/Handlers/PetHandler.cpp
+++ b/src/server/game/Handlers/PetHandler.cpp
@@ -162,6 +162,7 @@ void WorldSession::HandlePetActionHelper(Unit* pet, uint64 guid1, uint32 spellid
charmInfo->SetIsCommandAttack(false);
charmInfo->SetIsAtStay(true);
+ charmInfo->SetIsCommandFollow(false);
charmInfo->SetIsFollowing(false);
charmInfo->SetIsReturning(false);
charmInfo->SaveStayPosition();
@@ -175,6 +176,7 @@ void WorldSession::HandlePetActionHelper(Unit* pet, uint64 guid1, uint32 spellid
charmInfo->SetIsCommandAttack(false);
charmInfo->SetIsAtStay(false);
charmInfo->SetIsReturning(true);
+ charmInfo->SetIsCommandFollow(true);
charmInfo->SetIsFollowing(false);
break;
case COMMAND_ATTACK: //spellid=1792 //ATTACK
@@ -215,6 +217,7 @@ void WorldSession::HandlePetActionHelper(Unit* pet, uint64 guid1, uint32 spellid
charmInfo->SetIsCommandAttack(true);
charmInfo->SetIsAtStay(false);
charmInfo->SetIsFollowing(false);
+ charmInfo->SetIsCommandFollow(false);
charmInfo->SetIsReturning(false);
pet->ToCreature()->AI()->AttackStart(TargetUnit);
@@ -236,6 +239,7 @@ void WorldSession::HandlePetActionHelper(Unit* pet, uint64 guid1, uint32 spellid
charmInfo->SetIsCommandAttack(true);
charmInfo->SetIsAtStay(false);
charmInfo->SetIsFollowing(false);
+ charmInfo->SetIsCommandFollow(false);
charmInfo->SetIsReturning(false);
pet->Attack(TargetUnit, true);
diff --git a/src/server/game/Movement/MovementGenerators/TargetedMovementGenerator.cpp b/src/server/game/Movement/MovementGenerators/TargetedMovementGenerator.cpp
index 7a669642e7e..8712b2cf527 100755
--- a/src/server/game/Movement/MovementGenerators/TargetedMovementGenerator.cpp
+++ b/src/server/game/Movement/MovementGenerators/TargetedMovementGenerator.cpp
@@ -66,7 +66,9 @@ void TargetedMovementGeneratorMedium<T,D>::_setTargetLocation(T &owner)
// We need to subtract GetObjectSize() because it gets added back further down the chain
// and that makes pets too far away. Subtracting it allows pets to properly
// be (GetCombatReach() + i_offset) away.
- if (owner.isPet())
+ // Only applies when i_target is pet's owner otherwise pets and mobs end up
+ // doing a "dance" while fighting
+ if (owner.isPet() && i_target->GetTypeId() == TYPEID_PLAYER)
{
dist = i_target->GetCombatReach();
size = i_target->GetCombatReach() - i_target->GetObjectSize();
diff --git a/src/server/game/Spells/Auras/SpellAuraEffects.cpp b/src/server/game/Spells/Auras/SpellAuraEffects.cpp
index ce15b918810..17cb16fd6d5 100644
--- a/src/server/game/Spells/Auras/SpellAuraEffects.cpp
+++ b/src/server/game/Spells/Auras/SpellAuraEffects.cpp
@@ -3095,9 +3095,10 @@ void AuraEffect::HandleModPossess(AuraApplication const* aurApp, uint8 mode, boo
target->RemoveCharmedBy(caster);
}
-// only one spell has this aura
void AuraEffect::HandleModPossessPet(AuraApplication const* aurApp, uint8 mode, bool apply) const
{
+ // Used by spell "Eyes of the Beast"
+
if (!(mode & AURA_EFFECT_HANDLE_REAL))
return;
@@ -3120,6 +3121,9 @@ void AuraEffect::HandleModPossessPet(AuraApplication const* aurApp, uint8 mode,
if (caster->ToPlayer()->GetPet() != pet)
return;
+ // Must clear current motion or pet leashes back to owner after a few yards
+ // when under spell 'Eyes of the Beast'
+ pet->GetMotionMaster()->Clear();
pet->SetCharmedBy(caster, CHARM_TYPE_POSSESS, aurApp);
}
else
@@ -3130,13 +3134,15 @@ void AuraEffect::HandleModPossessPet(AuraApplication const* aurApp, uint8 mode,
pet->Remove(PET_SAVE_NOT_IN_SLOT, true);
else
{
- // Reinitialize the pet bar and make the pet come back to the owner
+ // Reinitialize the pet bar or it will appear greyed out
caster->ToPlayer()->PetSpellInitialize();
- if (!pet->getVictim())
+
+ // Follow owner only if not fighting or owner didn't click "stay" at new location
+ // This may be confusing because pet bar shows "stay" when under the spell but it retains
+ // the "follow" flag. Player MUST click "stay" while under the spell.
+ if (!pet->getVictim() && !pet->GetCharmInfo()->HasCommandState(COMMAND_STAY))
{
pet->GetMotionMaster()->MoveFollow(caster, PET_FOLLOW_DIST, pet->GetFollowAngle());
- //if (target->GetCharmInfo())
- // target->GetCharmInfo()->SetCommandState(COMMAND_FOLLOW);
}
}
}