Merge pull request #8416 from MrSmite/PetAI_2.0

Core/PetAI: Several changes.
Closes #7455
Closes #8398
This commit is contained in:
Subv
2012-12-09 07:50:33 -08:00
12 changed files with 343 additions and 162 deletions
+134 -89
View File
@@ -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);
}
+11 -3
View File
@@ -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
+2 -2
View File
@@ -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);
+1
View File
@@ -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);
+58 -44
View File
@@ -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)
+22 -18
View File
@@ -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:
+4
View File
@@ -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);
}
}
}