mirror of
https://github.com/TrinityCore/TrinityCore.git
synced 2026-01-15 23:20:36 +01:00
Pet/Guardian AI hook re-organizing (#19824)
* Pet/Guardian AI hook re-organizing: - Adjust OwnerAttacked/OwnerAttackedBy hooks on CreatureAI to fire for all owned units, not just player pets. This should allow guardians to more reliably recognize valid targets. - Kill off the AttackedBy hook. While it was defined in CreatureAI.h as virtual, it was only ever invoked for player pets in specific situations. This makes it classic developer bait. - Adjust PetAI to use DamageTaken instead of AttackedBy. - Adjust behavior of AttackStart on PetAI to compensate.
This commit is contained in:
@@ -126,7 +126,7 @@ void PetAI::UpdateAI(uint32 diff)
|
||||
// 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.
|
||||
// All other cases (ie: defensive) - Targets are assigned by DamageTaken(), OwnerAttackedBy(), OwnerAttacked(), etc.
|
||||
Unit* nextTarget = SelectNextTarget(me->HasReactState(REACT_AGGRESSIVE));
|
||||
|
||||
if (nextTarget)
|
||||
@@ -320,8 +320,18 @@ void PetAI::KilledUnit(Unit* victim)
|
||||
|
||||
void PetAI::AttackStart(Unit* target)
|
||||
{
|
||||
// Overrides Unit::AttackStart to correctly evaluate Pet states
|
||||
// Overrides Unit::AttackStart to prevent pet from switching off its assigned target
|
||||
if (!target || target == me)
|
||||
return;
|
||||
|
||||
if (me->GetVictim() && me->EnsureVictim()->IsAlive())
|
||||
return;
|
||||
|
||||
_AttackStart(target);
|
||||
}
|
||||
|
||||
void PetAI::_AttackStart(Unit* target)
|
||||
{
|
||||
// Check all pet states to decide if we can attack this target
|
||||
if (!CanAttack(target))
|
||||
return;
|
||||
@@ -335,7 +345,7 @@ void PetAI::OwnerAttackedBy(Unit* attacker)
|
||||
// Called when owner takes damage. This function helps keep pets from running off
|
||||
// simply due to owner gaining aggro.
|
||||
|
||||
if (!attacker)
|
||||
if (!attacker || !me->IsAlive())
|
||||
return;
|
||||
|
||||
// Passive pets don't do anything
|
||||
@@ -356,7 +366,7 @@ void PetAI::OwnerAttacked(Unit* target)
|
||||
// that they need to assist
|
||||
|
||||
// Target might be NULL if called from spell with invalid cast targets
|
||||
if (!target)
|
||||
if (!target || !me->IsAlive())
|
||||
return;
|
||||
|
||||
// Passive pets don't do anything
|
||||
@@ -622,23 +632,3 @@ void PetAI::ClearCharmInfoFlags()
|
||||
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->EnsureVictim()->IsAlive())
|
||||
return;
|
||||
|
||||
// Continue to evaluate and attack if necessary
|
||||
AttackStart(attacker);
|
||||
}
|
||||
|
||||
@@ -37,11 +37,12 @@ class TC_GAME_API PetAI : public CreatureAI
|
||||
static int32 Permissible(Creature const* creature);
|
||||
|
||||
void KilledUnit(Unit* /*victim*/) override;
|
||||
void AttackStart(Unit* target) override;
|
||||
void AttackStart(Unit* target) override; // only start attacking if not attacking something else already
|
||||
void _AttackStart(Unit* target); // always start attacking if possible
|
||||
void MovementInform(uint32 moveType, uint32 data) override;
|
||||
void OwnerAttackedBy(Unit* attacker) override;
|
||||
void OwnerAttacked(Unit* target) override;
|
||||
void AttackedBy(Unit* attacker) override;
|
||||
void DamageTaken(Unit* attacker, uint32& /*damage*/) override { AttackStart(attacker); }
|
||||
void ReceiveEmote(Player* player, uint32 textEmote) override;
|
||||
|
||||
// The following aren't used by the PetAI but need to be defined to override
|
||||
@@ -54,7 +55,6 @@ class TC_GAME_API PetAI : public CreatureAI
|
||||
void OnCharmed(bool /*apply*/) override;
|
||||
|
||||
private:
|
||||
bool _isVisible(Unit*) const;
|
||||
bool _needToStop(void);
|
||||
void _stopAttack(void);
|
||||
|
||||
|
||||
@@ -266,7 +266,6 @@ class TC_GAME_API UnitAI
|
||||
|
||||
// Called at any Damage from any attacker (before damage apply)
|
||||
// Note: it for recalculation damage or special reaction at damage
|
||||
// for attack reaction use AttackedBy called for not DOT damage in Unit::DealDamage also
|
||||
virtual void DamageTaken(Unit* /*attacker*/, uint32& /*damage*/) { }
|
||||
|
||||
// Called when the creature receives heal
|
||||
|
||||
@@ -132,15 +132,22 @@ void CreatureAI::MoveInLineOfSight(Unit* who)
|
||||
if (me->GetVictim())
|
||||
return;
|
||||
|
||||
if (me->GetCreatureType() == CREATURE_TYPE_NON_COMBAT_PET) // non-combat pets should just stand there and look good;)
|
||||
return;
|
||||
|
||||
if (me->HasReactState(REACT_AGGRESSIVE) && me->CanStartAttack(who, false))
|
||||
AttackStart(who);
|
||||
//else if (who->GetVictim() && me->IsFriendlyTo(who)
|
||||
// && me->IsWithinDistInMap(who, sWorld->getIntConfig(CONFIG_CREATURE_FAMILY_ASSISTANCE_RADIUS))
|
||||
// && me->CanStartAttack(who->GetVictim(), true)) /// @todo if we use true, it will not attack it when it arrives
|
||||
// me->GetMotionMaster()->MoveChase(who->GetVictim());
|
||||
}
|
||||
|
||||
void CreatureAI::_OnOwnerCombatInteraction(Unit* target)
|
||||
{
|
||||
if (!target || !me->IsAlive())
|
||||
return;
|
||||
|
||||
if (!me->HasReactState(REACT_PASSIVE) && me->CanStartAttack(target, true))
|
||||
{
|
||||
if (me->IsInCombat())
|
||||
me->AddThreat(target, 0.0f);
|
||||
else
|
||||
AttackStart(target);
|
||||
}
|
||||
}
|
||||
|
||||
// Distract creature, if player gets too close while stealthed/prowling
|
||||
@@ -196,12 +203,6 @@ void CreatureAI::EnterEvadeMode(EvadeReason why)
|
||||
me->GetVehicleKit()->Reset(true);
|
||||
}
|
||||
|
||||
/*void CreatureAI::AttackedBy(Unit* attacker)
|
||||
{
|
||||
if (!me->GetVictim())
|
||||
AttackStart(attacker);
|
||||
}*/
|
||||
|
||||
void CreatureAI::SetGazeOn(Unit* target)
|
||||
{
|
||||
if (me->IsValidAttackTarget(target))
|
||||
|
||||
@@ -131,8 +131,6 @@ class TC_GAME_API CreatureAI : public UnitAI
|
||||
// Called when spell hits a target
|
||||
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 bool IsEscorted() const { return false; }
|
||||
|
||||
// Called when creature is spawned or respawned
|
||||
@@ -152,15 +150,14 @@ class TC_GAME_API CreatureAI : public UnitAI
|
||||
virtual void ReceiveEmote(Player* /*player*/, uint32 /*emoteId*/) { }
|
||||
|
||||
// Called when owner takes damage
|
||||
virtual void OwnerAttackedBy(Unit* /*attacker*/) { }
|
||||
virtual void OwnerAttackedBy(Unit* attacker) { _OnOwnerCombatInteraction(attacker); }
|
||||
|
||||
// Called when owner attacks something
|
||||
virtual void OwnerAttacked(Unit* /*target*/) { }
|
||||
virtual void OwnerAttacked(Unit* target) { _OnOwnerCombatInteraction(target); }
|
||||
|
||||
/// == Triggered Actions Requested ==================
|
||||
|
||||
// Called when creature attack expected (if creature can and no have current victim)
|
||||
// Note: for reaction at hostile action must be called AttackedBy function.
|
||||
//virtual void AttackStart(Unit*) { }
|
||||
|
||||
// Called at World update tick
|
||||
@@ -208,6 +205,7 @@ class TC_GAME_API CreatureAI : public UnitAI
|
||||
|
||||
private:
|
||||
bool m_MoveInLineOfSight_locked;
|
||||
void _OnOwnerCombatInteraction(Unit* target);
|
||||
};
|
||||
|
||||
enum Permitions : int32
|
||||
|
||||
@@ -683,20 +683,16 @@ uint32 Unit::DealDamage(Unit* victim, uint32 damage, CleanDamage const* cleanDam
|
||||
// Signal to pets that their owner was attacked - except when DOT.
|
||||
if (damagetype != DOT)
|
||||
{
|
||||
Pet* pet = victim->ToPlayer()->GetPet();
|
||||
|
||||
if (pet && pet->IsAlive())
|
||||
pet->AI()->OwnerAttackedBy(this);
|
||||
for (Unit* controlled : victim->m_Controlled)
|
||||
if (Creature* cControlled = controlled->ToCreature())
|
||||
if (cControlled->IsAIEnabled)
|
||||
cControlled->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)
|
||||
@@ -5894,10 +5890,10 @@ bool Unit::Attack(Unit* victim, bool meleeAttack)
|
||||
// Spells such as auto-shot and others handled in WorldSession::HandleCastSpellOpcode
|
||||
if (GetTypeId() == TYPEID_PLAYER)
|
||||
{
|
||||
Pet* playerPet = this->ToPlayer()->GetPet();
|
||||
|
||||
if (playerPet && playerPet->IsAlive())
|
||||
playerPet->AI()->OwnerAttacked(victim);
|
||||
for (Unit* controlled : m_Controlled)
|
||||
if (Creature* cControlled = controlled->ToCreature())
|
||||
if (cControlled->IsAIEnabled)
|
||||
cControlled->AI()->OwnerAttacked(victim);
|
||||
}
|
||||
|
||||
return true;
|
||||
@@ -8682,12 +8678,7 @@ void Unit::CombatStart(Unit* target, bool initialAggro)
|
||||
|
||||
if (!target->IsInCombat() && target->GetTypeId() != TYPEID_PLAYER
|
||||
&& !target->ToCreature()->HasReactState(REACT_PASSIVE) && target->ToCreature()->IsAIEnabled)
|
||||
{
|
||||
if (target->IsPet())
|
||||
target->ToCreature()->AI()->AttackedBy(this); // PetAI has special handler before AttackStart()
|
||||
else
|
||||
target->ToCreature()->AI()->AttackStart(this);
|
||||
}
|
||||
target->ToCreature()->AI()->AttackStart(this);
|
||||
|
||||
SetInCombatWith(target);
|
||||
target->SetInCombatWith(this);
|
||||
|
||||
@@ -26,6 +26,7 @@
|
||||
#include "Spell.h"
|
||||
#include "ObjectAccessor.h"
|
||||
#include "CreatureAI.h"
|
||||
#include "PetAI.h"
|
||||
#include "Util.h"
|
||||
#include "Pet.h"
|
||||
#include "World.h"
|
||||
@@ -216,7 +217,11 @@ void WorldSession::HandlePetActionHelper(Unit* pet, ObjectGuid guid1, uint32 spe
|
||||
charmInfo->SetIsCommandFollow(false);
|
||||
charmInfo->SetIsReturning(false);
|
||||
|
||||
pet->ToCreature()->AI()->AttackStart(TargetUnit);
|
||||
CreatureAI* AI = pet->ToCreature()->AI();
|
||||
if (PetAI* petAI = dynamic_cast<PetAI*>(AI))
|
||||
petAI->_AttackStart(TargetUnit); // force target switch
|
||||
else
|
||||
AI->AttackStart(TargetUnit);
|
||||
|
||||
//10% chance to play special pet attack talk, else growl
|
||||
if (pet->IsPet() && ((Pet*)pet)->getPetType() == SUMMON_PET && pet != TargetUnit && urand(0, 100) < 10)
|
||||
@@ -365,7 +370,13 @@ void WorldSession::HandlePetActionHelper(Unit* pet, ObjectGuid guid1, uint32 spe
|
||||
{
|
||||
pet->GetMotionMaster()->Clear();
|
||||
if (pet->ToCreature()->IsAIEnabled)
|
||||
pet->ToCreature()->AI()->AttackStart(unit_target);
|
||||
{
|
||||
CreatureAI* AI = pet->ToCreature()->AI();
|
||||
if (PetAI* petAI = dynamic_cast<PetAI*>(AI))
|
||||
petAI->_AttackStart(unit_target); // force victim switch
|
||||
else
|
||||
AI->AttackStart(unit_target);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -3116,9 +3116,11 @@ void Spell::_cast(bool skipCheck)
|
||||
// Let any pets know we've attacked something. Check DmgClass for harmful spells only
|
||||
// This prevents spells such as Hunter's Mark from triggering pet attack
|
||||
if (this->GetSpellInfo()->DmgClass != SPELL_DAMAGE_CLASS_NONE)
|
||||
if (Pet* playerPet = playerCaster->GetPet())
|
||||
if (playerPet->IsAlive() && playerPet->isControlled() && (m_targets.GetTargetMask() & TARGET_FLAG_UNIT))
|
||||
playerPet->AI()->OwnerAttacked(m_targets.GetUnitTarget());
|
||||
if (Unit* unitTarget = m_targets.GetUnitTarget())
|
||||
for (Unit* controlled : playerCaster->m_Controlled)
|
||||
if (Creature* cControlled = controlled->ToCreature())
|
||||
if (cControlled->IsAIEnabled)
|
||||
cControlled->AI()->OwnerAttacked(unitTarget);
|
||||
}
|
||||
|
||||
SetExecutedCurrently(true);
|
||||
|
||||
@@ -66,17 +66,6 @@ public:
|
||||
me->SetFaction(m_uiNormalFaction);
|
||||
}
|
||||
|
||||
void AttackedBy(Unit* pAttacker) override
|
||||
{
|
||||
if (me->GetVictim())
|
||||
return;
|
||||
|
||||
if (me->IsFriendlyTo(pAttacker))
|
||||
return;
|
||||
|
||||
AttackStart(pAttacker);
|
||||
}
|
||||
|
||||
void DamageTaken(Unit* pDoneBy, uint32 &uiDamage) override
|
||||
{
|
||||
if (uiDamage > me->GetHealth() || me->HealthBelowPctDamaged(15, uiDamage))
|
||||
|
||||
@@ -80,14 +80,6 @@ public:
|
||||
|
||||
void EnterCombat(Unit* /*who*/) override { }
|
||||
|
||||
void AttackedBy(Unit* pAttacker) override
|
||||
{
|
||||
if (me->GetVictim() || me->IsFriendlyTo(pAttacker))
|
||||
return;
|
||||
|
||||
AttackStart(pAttacker);
|
||||
}
|
||||
|
||||
void DamageTaken(Unit* pDoneBy, uint32 &uiDamage) override
|
||||
{
|
||||
if (uiDamage > me->GetHealth() || me->HealthBelowPctDamaged(15, uiDamage))
|
||||
|
||||
@@ -106,17 +106,6 @@ public:
|
||||
summoned->AI()->AttackStart(player);
|
||||
}
|
||||
|
||||
void AttackedBy(Unit* pAttacker) override
|
||||
{
|
||||
if (me->GetVictim())
|
||||
return;
|
||||
|
||||
if (me->IsFriendlyTo(pAttacker))
|
||||
return;
|
||||
|
||||
AttackStart(pAttacker);
|
||||
}
|
||||
|
||||
void DamageTaken(Unit* /*pDoneBy*/, uint32& uiDamage) override
|
||||
{
|
||||
if (HealthBelowPct(20))
|
||||
|
||||
@@ -122,17 +122,6 @@ public:
|
||||
me->RestoreFaction();
|
||||
}
|
||||
|
||||
void AttackedBy(Unit* pAttacker) override
|
||||
{
|
||||
if (me->GetVictim())
|
||||
return;
|
||||
|
||||
if (me->IsFriendlyTo(pAttacker))
|
||||
return;
|
||||
|
||||
AttackStart(pAttacker);
|
||||
}
|
||||
|
||||
void DamageTaken(Unit* pDoneBy, uint32 &Damage) override
|
||||
{
|
||||
if (Damage > me->GetHealth() || me->HealthBelowPctDamaged(20, Damage))
|
||||
|
||||
Reference in New Issue
Block a user