Merge pull request #8416 from MrSmite/PetAI_2.0
Core/PetAI: Several changes. Closes #7455 Closes #8398
This commit is contained in:
@@ -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,32 +452,32 @@ 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);
|
||||
|
||||
// The following conditions are true if chase == true
|
||||
// (Follow && (Aggressive || Defensive))
|
||||
// ((Stay || Follow) && (Passive && player clicked attack))
|
||||
|
||||
if (chase)
|
||||
if (me->Attack(target, true))
|
||||
{
|
||||
if (me->Attack(target, true))
|
||||
if (Unit* owner = me->GetOwner())
|
||||
owner->SetInCombatWith(target);
|
||||
|
||||
// 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)
|
||||
{
|
||||
me->GetCharmInfo()->SetIsAtStay(false);
|
||||
me->GetCharmInfo()->SetIsFollowing(false);
|
||||
me->GetCharmInfo()->SetIsReturning(false);
|
||||
me->GetMotionMaster()->Clear();
|
||||
me->GetMotionMaster()->MoveChase(target);
|
||||
ClearCharmInfoFlags();
|
||||
me->GetMotionMaster()->Clear();
|
||||
me->GetMotionMaster()->MoveChase(target);
|
||||
}
|
||||
else // (Stay && ((Aggressive || Defensive) && In Melee Range)))
|
||||
{
|
||||
ClearCharmInfoFlags();
|
||||
me->GetCharmInfo()->SetIsAtStay(true);
|
||||
me->GetMotionMaster()->Clear();
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
// Hmmm...
|
||||
if (!target)
|
||||
return false;
|
||||
|
||||
// Can't attack dead targets...
|
||||
if (!target->isAlive())
|
||||
{
|
||||
// Clear target to prevent getting stuck on dead targets
|
||||
me->AttackStop();
|
||||
me->InterruptNonMeleeSpells(false);
|
||||
me->SendMeleeAttackStop();
|
||||
return false;
|
||||
}
|
||||
|
||||
// Returning - check first since pets returning ignore attacks
|
||||
if (me->GetCharmInfo()->IsReturning())
|
||||
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);
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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*/) {}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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;
|
||||
ReactStates _oldReactState;
|
||||
|
||||
bool m_isCommandAttack;
|
||||
bool m_isAtStay;
|
||||
bool m_isFollowing;
|
||||
bool m_isReturning;
|
||||
float m_stayX;
|
||||
float m_stayY;
|
||||
float m_stayZ;
|
||||
bool _isCommandAttack;
|
||||
bool _isCommandFollow;
|
||||
bool _isAtStay;
|
||||
bool _isFollowing;
|
||||
bool _isReturning;
|
||||
float _stayX;
|
||||
float _stayY;
|
||||
float _stayZ;
|
||||
|
||||
GlobalCooldownMgr m_GlobalCooldownMgr;
|
||||
};
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user