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.

(cherry picked from commit 1660bb7d27)
This commit is contained in:
Treeston
2017-06-07 02:33:47 +02:00
committed by Carbenium
parent 5903a10a83
commit f7a7d02a7f
10 changed files with 61 additions and 91 deletions

View File

@@ -128,7 +128,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)
@@ -322,8 +322,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;
@@ -337,7 +347,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
@@ -358,7 +368,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
@@ -626,23 +636,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);
}

View File

@@ -36,11 +36,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
@@ -53,7 +54,6 @@ class TC_GAME_API PetAI : public CreatureAI
void OnCharmed(bool /*apply*/) override;
private:
bool _isVisible(Unit*) const;
bool _needToStop(void);
void _stopAttack(void);

View File

@@ -246,7 +246,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

View File

@@ -147,15 +147,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
@@ -211,12 +218,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))

View File

@@ -119,8 +119,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
@@ -146,15 +144,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
@@ -202,6 +199,7 @@ class TC_GAME_API CreatureAI : public UnitAI
private:
bool m_MoveInLineOfSight_locked;
void _OnOwnerCombatInteraction(Unit* target);
};
enum Permitions : int32

View File

@@ -730,20 +730,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)
@@ -5762,10 +5758,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;
@@ -8001,12 +7997,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);

View File

@@ -35,6 +35,7 @@
#include "SpellInfo.h"
#include "SpellMgr.h"
#include "SpellPackets.h"
#include "PetAI.h"
#include "Util.h"
#include "World.h"
#include "WorldPacket.h"
@@ -211,7 +212,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)
@@ -372,7 +377,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);
}
}
}

View File

@@ -3170,9 +3170,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);

View File

@@ -105,17 +105,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))

View File

@@ -120,17 +120,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))