mirror of
https://github.com/mod-playerbots/azerothcore-wotlk.git
synced 2026-02-06 12:27:48 +00:00
fix(Core/AI): Improve creature leashing behavior. (#19751)
* Init. https: //github.com/vmangos/core/commit/7d2f1e2923734ea1b983eeaa0208cfd158d4af02 Co-Authored-By: ratkosrb <35845488+ratkosrb@users.noreply.github.com> * Hackfix for pets. * Whoops. * Correct evade uses. * Conflict. * Actually fix grace timer. * Add visibility distance check. Creatures should always fail combat checks in the open world if they're outside of visibility distance, might not be the case for worldbosses. --------- Co-authored-by: ratkosrb <35845488+ratkosrb@users.noreply.github.com>
This commit is contained in:
@@ -307,7 +307,7 @@ bool CreatureAI::_EnterEvadeMode(EvadeReason /*why*/)
|
|||||||
me->LoadCreaturesAddon(true);
|
me->LoadCreaturesAddon(true);
|
||||||
me->SetLootRecipient(nullptr);
|
me->SetLootRecipient(nullptr);
|
||||||
me->ResetPlayerDamageReq();
|
me->ResetPlayerDamageReq();
|
||||||
me->SetLastDamagedTime(0);
|
me->ClearLastLeashExtensionTimePtr();
|
||||||
me->SetCannotReachTarget();
|
me->SetCannotReachTarget();
|
||||||
|
|
||||||
if (ZoneScript* zoneScript = me->GetZoneScript() ? me->GetZoneScript() : (ZoneScript*)me->GetInstanceScript())
|
if (ZoneScript* zoneScript = me->GetZoneScript() ? me->GetZoneScript() : (ZoneScript*)me->GetInstanceScript())
|
||||||
|
|||||||
@@ -220,9 +220,6 @@ bool AssistDelayEvent::Execute(uint64 /*e_time*/, uint32 /*p_time*/)
|
|||||||
{
|
{
|
||||||
if (Unit* victim = ObjectAccessor::GetUnit(*m_owner, m_victim))
|
if (Unit* victim = ObjectAccessor::GetUnit(*m_owner, m_victim))
|
||||||
{
|
{
|
||||||
// Initialize last damage timer if it doesn't exist
|
|
||||||
m_owner->SetLastDamagedTime(GameTime::GetGameTime().count() + MAX_AGGRO_RESET_TIME);
|
|
||||||
|
|
||||||
while (!m_assistants.empty())
|
while (!m_assistants.empty())
|
||||||
{
|
{
|
||||||
Creature* assistant = ObjectAccessor::GetCreature(*m_owner, *m_assistants.begin());
|
Creature* assistant = ObjectAccessor::GetCreature(*m_owner, *m_assistants.begin());
|
||||||
@@ -233,9 +230,14 @@ bool AssistDelayEvent::Execute(uint64 /*e_time*/, uint32 /*p_time*/)
|
|||||||
assistant->SetNoCallAssistance(true);
|
assistant->SetNoCallAssistance(true);
|
||||||
assistant->CombatStart(victim);
|
assistant->CombatStart(victim);
|
||||||
if (assistant->IsAIEnabled)
|
if (assistant->IsAIEnabled)
|
||||||
|
{
|
||||||
assistant->AI()->AttackStart(victim);
|
assistant->AI()->AttackStart(victim);
|
||||||
|
|
||||||
assistant->SetLastDamagedTimePtr(m_owner->GetLastDamagedTimePtr());
|
// When nearby mobs aggro from another mob's initial call for assistance
|
||||||
|
// their leash timers become linked and attacking one will keep the rest from evading.
|
||||||
|
if (assistant->GetVictim())
|
||||||
|
assistant->SetLastLeashExtensionTimePtr(m_owner->GetLastLeashExtensionTimePtr());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -272,7 +274,7 @@ Creature::Creature(bool isWorldObject): Unit(isWorldObject), MovableMapObject(),
|
|||||||
m_transportCheckTimer(1000), lootPickPocketRestoreTime(0), m_combatPulseTime(0), m_combatPulseDelay(0), m_reactState(REACT_AGGRESSIVE), m_defaultMovementType(IDLE_MOTION_TYPE),
|
m_transportCheckTimer(1000), lootPickPocketRestoreTime(0), m_combatPulseTime(0), m_combatPulseDelay(0), m_reactState(REACT_AGGRESSIVE), m_defaultMovementType(IDLE_MOTION_TYPE),
|
||||||
m_spawnId(0), m_equipmentId(0), m_originalEquipmentId(0), m_AlreadyCallAssistance(false),
|
m_spawnId(0), m_equipmentId(0), m_originalEquipmentId(0), m_AlreadyCallAssistance(false),
|
||||||
m_AlreadySearchedAssistance(false), m_regenHealth(true), m_regenPower(true), m_AI_locked(false), m_meleeDamageSchoolMask(SPELL_SCHOOL_MASK_NORMAL), m_originalEntry(0), m_moveInLineOfSightDisabled(false), m_moveInLineOfSightStrictlyDisabled(false),
|
m_AlreadySearchedAssistance(false), m_regenHealth(true), m_regenPower(true), m_AI_locked(false), m_meleeDamageSchoolMask(SPELL_SCHOOL_MASK_NORMAL), m_originalEntry(0), m_moveInLineOfSightDisabled(false), m_moveInLineOfSightStrictlyDisabled(false),
|
||||||
m_homePosition(), m_transportHomePosition(), m_creatureInfo(nullptr), m_creatureData(nullptr), m_detectionDistance(20.0f), m_waypointID(0), m_path_id(0), m_formation(nullptr), _lastDamagedTime(nullptr), m_cannotReachTimer(0),
|
m_homePosition(), m_transportHomePosition(), m_creatureInfo(nullptr), m_creatureData(nullptr), m_detectionDistance(20.0f), m_waypointID(0), m_path_id(0), m_formation(nullptr), m_lastLeashExtensionTime(nullptr), m_cannotReachTimer(0),
|
||||||
_isMissingSwimmingFlagOutOfCombat(false), m_assistanceTimer(0), _playerDamageReq(0), _damagedByPlayer(false), _isCombatMovementAllowed(true)
|
_isMissingSwimmingFlagOutOfCombat(false), m_assistanceTimer(0), _playerDamageReq(0), _damagedByPlayer(false), _isCombatMovementAllowed(true)
|
||||||
{
|
{
|
||||||
m_regenTimer = CREATURE_REGEN_INTERVAL;
|
m_regenTimer = CREATURE_REGEN_INTERVAL;
|
||||||
@@ -1961,8 +1963,6 @@ void Creature::setDeathState(DeathState state, bool despawn)
|
|||||||
|
|
||||||
if (state == DeathState::JustDied)
|
if (state == DeathState::JustDied)
|
||||||
{
|
{
|
||||||
_lastDamagedTime.reset();
|
|
||||||
|
|
||||||
m_corpseRemoveTime = GameTime::GetGameTime().count() + m_corpseDelay;
|
m_corpseRemoveTime = GameTime::GetGameTime().count() + m_corpseDelay;
|
||||||
m_respawnTime = GameTime::GetGameTime().count() + m_respawnDelay + m_corpseDelay;
|
m_respawnTime = GameTime::GetGameTime().count() + m_respawnDelay + m_corpseDelay;
|
||||||
|
|
||||||
@@ -2640,7 +2640,7 @@ bool Creature::CanCreatureAttack(Unit const* victim, bool skipDistCheck) const
|
|||||||
return false;
|
return false;
|
||||||
|
|
||||||
// cannot attack if is during 5 second grace period, unless being attacked
|
// cannot attack if is during 5 second grace period, unless being attacked
|
||||||
if (m_respawnedTime && (GameTime::GetGameTime().count() - m_respawnedTime) < 5 && !GetLastDamagedTime())
|
if (m_respawnedTime && (GameTime::GetGameTime().count() - m_respawnedTime) < 5 && !IsEngagedBy(victim))
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -2656,9 +2656,15 @@ bool Creature::CanCreatureAttack(Unit const* victim, bool skipDistCheck) const
|
|||||||
if (GetMap()->IsDungeon())
|
if (GetMap()->IsDungeon())
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
|
float visibility = std::max<float>(GetVisibilityRange(), victim->GetVisibilityRange());
|
||||||
|
|
||||||
|
// if outside visibility
|
||||||
|
if (!IsWithinDist(victim, visibility))
|
||||||
|
return false;
|
||||||
|
|
||||||
// pussywizard: don't check distance to home position if recently damaged (allow kiting away from spawnpoint!)
|
// pussywizard: don't check distance to home position if recently damaged (allow kiting away from spawnpoint!)
|
||||||
// xinef: this should include taunt auras
|
// xinef: this should include taunt auras
|
||||||
if (!isWorldBoss() && (GetLastDamagedTime() > GameTime::GetGameTime().count() || HasAuraType(SPELL_AURA_MOD_TAUNT)))
|
if (!isWorldBoss() && (GetLastLeashExtensionTime() + 12 > GameTime::GetGameTime().count() || HasAuraType(SPELL_AURA_MOD_TAUNT)))
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2666,10 +2672,13 @@ bool Creature::CanCreatureAttack(Unit const* victim, bool skipDistCheck) const
|
|||||||
return true;
|
return true;
|
||||||
|
|
||||||
// xinef: added size factor for huge npcs
|
// xinef: added size factor for huge npcs
|
||||||
float dist = std::min<float>(GetMap()->GetVisibilityRange() + GetObjectSize() * 2, 150.0f);
|
float dist = std::min<float>(GetDetectionRange() + GetObjectSize() * 2, 150.0f);
|
||||||
|
|
||||||
if (Unit* unit = GetCharmerOrOwner())
|
if (Unit* unit = GetCharmerOrOwner())
|
||||||
|
{
|
||||||
|
dist = std::min<float>(GetMap()->GetVisibilityRange() + GetObjectSize() * 2, 150.0f);
|
||||||
return victim->IsWithinDist(unit, dist);
|
return victim->IsWithinDist(unit, dist);
|
||||||
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// to prevent creatures in air ignore attacks because distance is already too high...
|
// to prevent creatures in air ignore attacks because distance is already too high...
|
||||||
@@ -3666,35 +3675,31 @@ bool Creature::IsNotReachableAndNeedRegen() const
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
time_t Creature::GetLastDamagedTime() const
|
std::shared_ptr<time_t> const& Creature::GetLastLeashExtensionTimePtr() const
|
||||||
{
|
{
|
||||||
if (!_lastDamagedTime)
|
if (m_lastLeashExtensionTime == nullptr)
|
||||||
return time_t(0);
|
m_lastLeashExtensionTime = std::make_shared<time_t>(time(nullptr));
|
||||||
|
return m_lastLeashExtensionTime;
|
||||||
return *_lastDamagedTime;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
std::shared_ptr<time_t> const& Creature::GetLastDamagedTimePtr() const
|
void Creature::SetLastLeashExtensionTimePtr(std::shared_ptr<time_t> const& timer)
|
||||||
{
|
{
|
||||||
return _lastDamagedTime;
|
m_lastLeashExtensionTime = timer;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Creature::SetLastDamagedTime(time_t val)
|
void Creature::ClearLastLeashExtensionTimePtr()
|
||||||
{
|
{
|
||||||
if (val > 0)
|
m_lastLeashExtensionTime.reset();
|
||||||
{
|
|
||||||
if (_lastDamagedTime)
|
|
||||||
*_lastDamagedTime = val;
|
|
||||||
else
|
|
||||||
_lastDamagedTime = std::make_shared<time_t>(val);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
_lastDamagedTime.reset();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Creature::SetLastDamagedTimePtr(std::shared_ptr<time_t> const& val)
|
time_t Creature::GetLastLeashExtensionTime() const
|
||||||
{
|
{
|
||||||
_lastDamagedTime = val;
|
return *GetLastLeashExtensionTimePtr();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Creature::UpdateLeashExtensionTime()
|
||||||
|
{
|
||||||
|
(*GetLastLeashExtensionTimePtr()) = time(nullptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Creature::CanPeriodicallyCallForAssistance() const
|
bool Creature::CanPeriodicallyCallForAssistance() const
|
||||||
|
|||||||
@@ -381,10 +381,11 @@ public:
|
|||||||
[[nodiscard]] bool IsMovementPreventedByCasting() const override;
|
[[nodiscard]] bool IsMovementPreventedByCasting() const override;
|
||||||
|
|
||||||
// Part of Evade mechanics
|
// Part of Evade mechanics
|
||||||
[[nodiscard]] time_t GetLastDamagedTime() const;
|
std::shared_ptr<time_t> const& GetLastLeashExtensionTimePtr() const;
|
||||||
[[nodiscard]] std::shared_ptr<time_t> const& GetLastDamagedTimePtr() const;
|
void SetLastLeashExtensionTimePtr(std::shared_ptr<time_t> const& timer);
|
||||||
void SetLastDamagedTime(time_t val);
|
void ClearLastLeashExtensionTimePtr();
|
||||||
void SetLastDamagedTimePtr(std::shared_ptr<time_t> const& val);
|
time_t GetLastLeashExtensionTime() const;
|
||||||
|
void UpdateLeashExtensionTime();
|
||||||
|
|
||||||
bool IsFreeToMove();
|
bool IsFreeToMove();
|
||||||
static constexpr uint32 MOVE_CIRCLE_CHECK_INTERVAL = 3000;
|
static constexpr uint32 MOVE_CIRCLE_CHECK_INTERVAL = 3000;
|
||||||
@@ -500,7 +501,9 @@ private:
|
|||||||
CreatureGroup* m_formation;
|
CreatureGroup* m_formation;
|
||||||
bool TriggerJustRespawned;
|
bool TriggerJustRespawned;
|
||||||
|
|
||||||
mutable std::shared_ptr<time_t> _lastDamagedTime; // Part of Evade mechanics
|
// Shared timer between mobs who assist another.
|
||||||
|
// Damaging one extends leash range on all of them.
|
||||||
|
mutable std::shared_ptr<time_t> m_lastLeashExtensionTime;
|
||||||
|
|
||||||
ObjectGuid m_cannotReachTarget;
|
ObjectGuid m_cannotReachTarget;
|
||||||
uint32 m_cannotReachTimer;
|
uint32 m_cannotReachTimer;
|
||||||
|
|||||||
@@ -1041,10 +1041,6 @@ uint32 Unit::DealDamage(Unit* attacker, Unit* victim, uint32 damage, CleanDamage
|
|||||||
|
|
||||||
if (!victim->IsPlayer())
|
if (!victim->IsPlayer())
|
||||||
{
|
{
|
||||||
// Part of Evade mechanics. DoT's and Thorns / Retribution Aura do not contribute to this
|
|
||||||
if (damagetype != DOT && damage > 0 && !victim->GetOwnerGUID().IsPlayer() && (!spellProto || !spellProto->HasAura(SPELL_AURA_DAMAGE_SHIELD)))
|
|
||||||
victim->ToCreature()->SetLastDamagedTime(GameTime::GetGameTime().count() + MAX_AGGRO_RESET_TIME);
|
|
||||||
|
|
||||||
if (attacker)
|
if (attacker)
|
||||||
{
|
{
|
||||||
if (spellProto && victim->CanHaveThreatList() && !victim->HasUnitState(UNIT_STATE_EVADE) && !victim->IsInCombatWith(attacker))
|
if (spellProto && victim->CanHaveThreatList() && !victim->HasUnitState(UNIT_STATE_EVADE) && !victim->IsInCombatWith(attacker))
|
||||||
@@ -10387,6 +10383,8 @@ void Unit::CombatStop(bool includingCast)
|
|||||||
RemoveAllAttackers();
|
RemoveAllAttackers();
|
||||||
if (IsPlayer())
|
if (IsPlayer())
|
||||||
ToPlayer()->SendAttackSwingCancelAttack(); // melee and ranged forced attack cancel
|
ToPlayer()->SendAttackSwingCancelAttack(); // melee and ranged forced attack cancel
|
||||||
|
if (Creature* pCreature = ToCreature())
|
||||||
|
pCreature->ClearLastLeashExtensionTimePtr();
|
||||||
ClearInCombat();
|
ClearInCombat();
|
||||||
|
|
||||||
// xinef: just in case
|
// xinef: just in case
|
||||||
@@ -13539,6 +13537,9 @@ void Unit::SetInCombatWith(Unit* enemy, uint32 duration)
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (Creature* pCreature = ToCreature())
|
||||||
|
pCreature->UpdateLeashExtensionTime();
|
||||||
|
|
||||||
SetInCombatState(false, enemy, duration);
|
SetInCombatState(false, enemy, duration);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -148,6 +148,15 @@ bool ChaseMovementGenerator<T>::DoUpdate(T* owner, uint32 time_diff)
|
|||||||
owner->ClearUnitState(UNIT_STATE_CHASE_MOVE);
|
owner->ClearUnitState(UNIT_STATE_CHASE_MOVE);
|
||||||
owner->SetInFront(target);
|
owner->SetInFront(target);
|
||||||
MovementInform(owner);
|
MovementInform(owner);
|
||||||
|
|
||||||
|
// Mobs should chase you infinitely if you stop and wait every few seconds.
|
||||||
|
i_leashExtensionTimer.Update(time_diff);
|
||||||
|
if (i_leashExtensionTimer.Passed())
|
||||||
|
{
|
||||||
|
i_leashExtensionTimer.Reset(5000);
|
||||||
|
if (Creature* creature = owner->ToCreature())
|
||||||
|
creature->UpdateLeashExtensionTime();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// if the target moved, we have to consider whether to adjust
|
// if the target moved, we have to consider whether to adjust
|
||||||
|
|||||||
@@ -39,7 +39,7 @@ class ChaseMovementGenerator : public MovementGeneratorMedium<T, ChaseMovementGe
|
|||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
ChaseMovementGenerator(Unit* target, Optional<ChaseRange> range = {}, Optional<ChaseAngle> angle = {})
|
ChaseMovementGenerator(Unit* target, Optional<ChaseRange> range = {}, Optional<ChaseAngle> angle = {})
|
||||||
: TargetedMovementGeneratorBase(target), i_path(nullptr), i_recheckDistance(0), i_recalculateTravel(true), _range(range), _angle(angle) {}
|
: TargetedMovementGeneratorBase(target), i_leashExtensionTimer(0), i_path(nullptr), i_recheckDistance(0), i_recalculateTravel(true), _range(range), _angle(angle) {}
|
||||||
~ChaseMovementGenerator() { }
|
~ChaseMovementGenerator() { }
|
||||||
|
|
||||||
MovementGeneratorType GetMovementGeneratorType() { return CHASE_MOTION_TYPE; }
|
MovementGeneratorType GetMovementGeneratorType() { return CHASE_MOTION_TYPE; }
|
||||||
@@ -59,6 +59,7 @@ public:
|
|||||||
bool HasLostTarget(Unit* unit) const { return unit->GetVictim() != this->GetTarget(); }
|
bool HasLostTarget(Unit* unit) const { return unit->GetVictim() != this->GetTarget(); }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
TimeTrackerSmall i_leashExtensionTimer;
|
||||||
std::unique_ptr<PathGenerator> i_path;
|
std::unique_ptr<PathGenerator> i_path;
|
||||||
TimeTrackerSmall i_recheckDistance;
|
TimeTrackerSmall i_recheckDistance;
|
||||||
bool i_recalculateTravel;
|
bool i_recalculateTravel;
|
||||||
|
|||||||
@@ -1130,7 +1130,7 @@ public:
|
|||||||
me->LoadCreaturesAddon(true);
|
me->LoadCreaturesAddon(true);
|
||||||
me->SetLootRecipient(nullptr);
|
me->SetLootRecipient(nullptr);
|
||||||
me->ResetPlayerDamageReq();
|
me->ResetPlayerDamageReq();
|
||||||
me->SetLastDamagedTime(0);
|
me->UpdateLeashExtensionTime();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -58,7 +58,7 @@ struct npc_pet_hunter_snake_trap : public ScriptedAI
|
|||||||
me->LoadCreaturesAddon(true);
|
me->LoadCreaturesAddon(true);
|
||||||
me->SetLootRecipient(nullptr);
|
me->SetLootRecipient(nullptr);
|
||||||
me->ResetPlayerDamageReq();
|
me->ResetPlayerDamageReq();
|
||||||
me->SetLastDamagedTime(0);
|
me->ClearLastLeashExtensionTimePtr();
|
||||||
|
|
||||||
me->AddUnitState(UNIT_STATE_EVADE);
|
me->AddUnitState(UNIT_STATE_EVADE);
|
||||||
me->GetMotionMaster()->MoveTargetedHome();
|
me->GetMotionMaster()->MoveTargetedHome();
|
||||||
|
|||||||
Reference in New Issue
Block a user