refactor(Core/Combat): Port TrinityCore heap-based threat system (#24715)
Co-authored-by: blinkysc <blinkysc@users.noreply.github.com> Co-authored-by: Treeston <treeston.mmoc@gmail.com> Co-authored-by: killerwife <killerwife@gmail.com> Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -35,7 +35,7 @@ int32 NullCreatureAI::Permissible(Creature const* creature)
|
||||
|
||||
void PassiveAI::UpdateAI(uint32)
|
||||
{
|
||||
if (me->IsInCombat() && me->getAttackers().empty())
|
||||
if (me->IsEngaged() && !me->IsInCombat())
|
||||
EnterEvadeMode(EVADE_REASON_NO_HOSTILES);
|
||||
}
|
||||
|
||||
|
||||
@@ -40,6 +40,9 @@ public:
|
||||
|
||||
void MoveInLineOfSight(Unit*) override {}
|
||||
void AttackStart(Unit* target) override;
|
||||
void JustEnteredCombat(Unit* who) override { EngagementStart(who); }
|
||||
void JustExitedCombat() override { EngagementOver(); }
|
||||
void JustStartedThreateningMe(Unit*) override {}
|
||||
void UpdateAI(uint32) override;
|
||||
void EnterEvadeMode(EvadeReason /*why*/) override {}
|
||||
|
||||
@@ -56,6 +59,8 @@ public:
|
||||
|
||||
void MoveInLineOfSight(Unit*) override {}
|
||||
void AttackStart(Unit*) override {}
|
||||
void JustStartedThreateningMe(Unit*) override {}
|
||||
void JustEnteredCombat(Unit*) override {}
|
||||
void UpdateAI(uint32) override {}
|
||||
void EnterEvadeMode(EvadeReason /*why*/) override {}
|
||||
void OnCharmed(bool /*apply*/) override {}
|
||||
|
||||
@@ -77,7 +77,7 @@ void PetAI::_stopAttack()
|
||||
me->GetMotionMaster()->Clear();
|
||||
me->GetMotionMaster()->MoveIdle();
|
||||
me->CombatStop();
|
||||
me->getHostileRefMgr().deleteReferences();
|
||||
me->GetThreatMgr().RemoveMeFromThreatLists();
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -498,7 +498,7 @@ Unit* PetAI::SelectNextTarget(bool allowAutoSelect) const
|
||||
if (!tauntAuras.empty())
|
||||
for (Unit::AuraEffectList::const_reverse_iterator itr = tauntAuras.rbegin(); itr != tauntAuras.rend(); ++itr)
|
||||
if (Unit* caster = (*itr)->GetCaster())
|
||||
if (me->CanCreatureAttack(caster) && !caster->HasAuraTypeWithCaster(SPELL_AURA_IGNORED, me->GetGUID()))
|
||||
if (me->CanCreatureAttack(caster) && !caster->HasAuraTypeWithCaster(SPELL_AURA_MOD_DETAUNT, me->GetGUID()))
|
||||
return caster;
|
||||
}
|
||||
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
|
||||
#include "UnitAI.h"
|
||||
#include "Creature.h"
|
||||
#include "CreatureAI.h"
|
||||
#include "CreatureAIImpl.h"
|
||||
#include "Player.h"
|
||||
#include "Spell.h"
|
||||
@@ -135,10 +136,12 @@ SpellCastResult UnitAI::DoAddAuraToAllHostilePlayers(uint32 spellid)
|
||||
{
|
||||
if (me->IsInCombat())
|
||||
{
|
||||
ThreatContainer::StorageType threatlist = me->GetThreatMgr().GetThreatList();
|
||||
for (ThreatContainer::StorageType::const_iterator itr = threatlist.begin(); itr != threatlist.end(); ++itr)
|
||||
for (ThreatReference const* ref : me->GetThreatMgr().GetUnsortedThreatList())
|
||||
{
|
||||
if (Unit* unit = ObjectAccessor::GetUnit(*me, (*itr)->getUnitGuid()))
|
||||
if (ref->IsOffline())
|
||||
continue;
|
||||
|
||||
if (Unit* unit = ref->GetVictim())
|
||||
{
|
||||
if (unit->IsPlayer())
|
||||
{
|
||||
@@ -146,8 +149,6 @@ SpellCastResult UnitAI::DoAddAuraToAllHostilePlayers(uint32 spellid)
|
||||
return SPELL_CAST_OK;
|
||||
}
|
||||
}
|
||||
else
|
||||
return SPELL_FAILED_BAD_TARGETS;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -158,16 +159,16 @@ SpellCastResult UnitAI::DoCastToAllHostilePlayers(uint32 spellid, bool triggered
|
||||
{
|
||||
if (me->IsInCombat())
|
||||
{
|
||||
ThreatContainer::StorageType threatlist = me->GetThreatMgr().GetThreatList();
|
||||
for (ThreatContainer::StorageType::const_iterator itr = threatlist.begin(); itr != threatlist.end(); ++itr)
|
||||
for (ThreatReference const* ref : me->GetThreatMgr().GetUnsortedThreatList())
|
||||
{
|
||||
if (Unit* unit = ObjectAccessor::GetUnit(*me, (*itr)->getUnitGuid()))
|
||||
if (ref->IsOffline())
|
||||
continue;
|
||||
|
||||
if (Unit* unit = ref->GetVictim())
|
||||
{
|
||||
if (unit->IsPlayer())
|
||||
return me->CastSpell(unit, spellid, triggered);
|
||||
}
|
||||
else
|
||||
return SPELL_FAILED_BAD_TARGETS;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -376,7 +377,7 @@ void UnitAI::FillAISpellInfo()
|
||||
}
|
||||
}
|
||||
|
||||
ThreatMgr& UnitAI::GetThreatMgr()
|
||||
ThreatManager& UnitAI::GetThreatMgr()
|
||||
{
|
||||
return me->GetThreatMgr();
|
||||
}
|
||||
@@ -386,6 +387,60 @@ void UnitAI::SortByDistance(std::list<Unit*>& list, bool ascending)
|
||||
list.sort(Acore::ObjectDistanceOrderPred(me, ascending));
|
||||
}
|
||||
|
||||
void UnitAI::EvadeTimerExpired()
|
||||
{
|
||||
Creature* creature = me->ToCreature();
|
||||
if (!creature)
|
||||
return;
|
||||
|
||||
CreatureAI* ai = creature->AI();
|
||||
if (!ai)
|
||||
return;
|
||||
|
||||
// Check if we can teleport an unreachable player first
|
||||
if (ObjectGuid targetGuid = creature->GetCannotReachTarget())
|
||||
{
|
||||
if (Player* player = ObjectAccessor::GetPlayer(*creature, targetGuid))
|
||||
{
|
||||
if (creature->IsEngagedBy(player) && ai->OnTeleportUnreacheablePlayer(player))
|
||||
{
|
||||
creature->SetCannotReachTarget();
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (creature->GetMap()->IsRaid())
|
||||
{
|
||||
creature->GetCombatManager().ContinueEvadeRegen();
|
||||
return;
|
||||
}
|
||||
|
||||
// If only one target, enter evade mode
|
||||
if (creature->GetThreatMgr().GetThreatListSize() <= 1)
|
||||
{
|
||||
ai->EnterEvadeMode(CreatureAI::EVADE_REASON_NO_PATH);
|
||||
return;
|
||||
}
|
||||
|
||||
// Multiple targets - clear threat on unreachable target and try another
|
||||
if (ObjectGuid targetGuid = creature->GetCannotReachTarget())
|
||||
{
|
||||
if (Unit* target = ObjectAccessor::GetUnit(*creature, targetGuid))
|
||||
{
|
||||
if (creature->GetThreatMgr().IsThreatenedBy(target))
|
||||
{
|
||||
creature->GetThreatMgr().ClearThreat(target);
|
||||
creature->SetCannotReachTarget();
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Fallback - enter evade
|
||||
ai->EnterEvadeMode(CreatureAI::EVADE_REASON_NO_PATH);
|
||||
}
|
||||
|
||||
//Enable PlayerAI when charmed
|
||||
void PlayerAI::OnCharmed(bool apply)
|
||||
{
|
||||
|
||||
@@ -63,7 +63,7 @@ struct DefaultTargetSelector : public Acore::unary_function<Unit*, bool>
|
||||
// playerOnly: self explaining
|
||||
// withMainTank: allow current tank to be selected
|
||||
// aura: if 0: ignored, if > 0: the target shall have the aura, if < 0, the target shall NOT have the aura
|
||||
DefaultTargetSelector(Unit const* unit, float dist, bool playerOnly, bool withMainTank, int32 aura) : me(unit), m_dist(dist), except(!withMainTank ? me->GetThreatMgr().GetCurrentVictim() : nullptr), m_playerOnly(playerOnly), m_aura(aura) {}
|
||||
DefaultTargetSelector(Unit const* unit, float dist, bool playerOnly, bool withMainTank, int32 aura) : me(unit), m_dist(dist), except(!withMainTank ? me->GetThreatMgr().GetLastVictim() : nullptr), m_playerOnly(playerOnly), m_aura(aura) {}
|
||||
|
||||
bool operator()(Unit const* target) const
|
||||
{
|
||||
@@ -126,7 +126,7 @@ public:
|
||||
bool operator()(Unit const* target) const;
|
||||
|
||||
private:
|
||||
Creature const* _source;
|
||||
Creature* _source;
|
||||
bool _playerOnly;
|
||||
};
|
||||
|
||||
@@ -146,7 +146,7 @@ struct PowerUsersSelector : public Acore::unary_function<Unit*, bool>
|
||||
if (!_me || !target)
|
||||
return false;
|
||||
|
||||
if (!_withTank && target == _me->GetThreatMgr().GetCurrentVictim())
|
||||
if (!_withTank && target == _me->GetThreatMgr().GetLastVictim())
|
||||
return false;
|
||||
|
||||
if (target->getPowerType() != _power)
|
||||
@@ -237,7 +237,7 @@ public:
|
||||
template <class PREDICATE>
|
||||
Unit* SelectTarget(SelectTargetMethod targetType, uint32 position, PREDICATE const& predicate)
|
||||
{
|
||||
ThreatMgr& mgr = GetThreatMgr();
|
||||
ThreatManager& mgr = GetThreatMgr();
|
||||
// shortcut: if we ignore the first <offset> elements, and there are at most <offset> elements, then we ignore ALL elements
|
||||
if (mgr.GetThreatListSize() <= position)
|
||||
return nullptr;
|
||||
@@ -282,7 +282,7 @@ public:
|
||||
void SelectTargetList(std::list<Unit*>& targetList, uint32 num, SelectTargetMethod targetType, uint32 position, PREDICATE const& predicate)
|
||||
{
|
||||
targetList.clear();
|
||||
ThreatMgr& mgr = GetThreatMgr();
|
||||
ThreatManager& mgr = GetThreatMgr();
|
||||
// shortcut: we're gonna ignore the first <offset> elements, and there's at most <offset> elements, so we ignore them all - nothing to do here
|
||||
if (mgr.GetThreatListSize() <= position)
|
||||
return;
|
||||
@@ -352,11 +352,14 @@ public:
|
||||
|
||||
/**
|
||||
* @brief Called when the unit leaves combat
|
||||
*
|
||||
* @todo Never invoked right now. Preparation for Combat Threat refactor
|
||||
*/
|
||||
virtual void JustExitedCombat() { }
|
||||
|
||||
/**
|
||||
* @brief Called when evade timer expires (target unreachable for too long)
|
||||
*/
|
||||
virtual void EvadeTimerExpired();
|
||||
|
||||
/// @brief Called at any Damage to any victim (before damage apply)
|
||||
virtual void DamageDealt(Unit* /*victim*/, uint32& /*damage*/, DamageEffectType /*damageType*/, SpellSchoolMask /*damageSchoolMask*/) {}
|
||||
|
||||
@@ -421,7 +424,7 @@ public:
|
||||
virtual std::string GetDebugInfo() const;
|
||||
|
||||
private:
|
||||
ThreatMgr& GetThreatMgr();
|
||||
ThreatManager& GetThreatMgr();
|
||||
void SortByDistance(std::list<Unit*>& list, bool ascending = true);
|
||||
};
|
||||
|
||||
|
||||
@@ -33,9 +33,23 @@
|
||||
#include <functional>
|
||||
|
||||
//Disable CreatureAI when charmed
|
||||
void CreatureAI::OnCharmed(bool /*apply*/)
|
||||
void CreatureAI::OnCharmed(bool /* apply */)
|
||||
{
|
||||
//me->IsAIEnabled = !apply;*/
|
||||
if (!me->IsCharmed() && !me->LastCharmerGUID.IsEmpty())
|
||||
{
|
||||
if (!me->HasReactState(REACT_PASSIVE))
|
||||
{
|
||||
if (Unit* lastCharmer = ObjectAccessor::GetUnit(*me, me->LastCharmerGUID))
|
||||
me->EngageWithTarget(lastCharmer);
|
||||
}
|
||||
|
||||
me->LastCharmerGUID.Clear();
|
||||
|
||||
if (!me->IsInCombat())
|
||||
EnterEvadeMode(EVADE_REASON_NO_HOSTILES);
|
||||
}
|
||||
|
||||
// trigger AI switch
|
||||
me->NeedChangeAI = true;
|
||||
me->IsAIEnabled = false;
|
||||
}
|
||||
@@ -139,13 +153,13 @@ void CreatureAI::DoZoneInCombat(Creature* creature /*= nullptr*/, float maxRange
|
||||
continue;
|
||||
}
|
||||
|
||||
creature->SetInCombatWith(player);
|
||||
player->SetInCombatWith(creature);
|
||||
creature->EngageWithTarget(player);
|
||||
|
||||
if (creature->CanHaveThreatList())
|
||||
{
|
||||
creature->AddThreat(player, 0.0f);
|
||||
}
|
||||
for (Unit* pet : player->m_Controlled)
|
||||
creature->EngageWithTarget(pet);
|
||||
|
||||
if (Unit* vehicle = player->GetVehicleBase())
|
||||
creature->EngageWithTarget(vehicle);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -179,6 +193,15 @@ void CreatureAI::MoveInLineOfSight(Unit* who)
|
||||
AttackStart(who);
|
||||
}
|
||||
|
||||
void CreatureAI::OnOwnerCombatInteraction(Unit* target)
|
||||
{
|
||||
if (!target || !me->IsAlive())
|
||||
return;
|
||||
|
||||
if (!me->HasReactState(REACT_PASSIVE) && me->CanStartAttack(target))
|
||||
me->EngageWithTarget(target);
|
||||
}
|
||||
|
||||
// Distract creature, if player gets too close while stealthed/prowling
|
||||
void CreatureAI::TriggerAlert(Unit const* who) const
|
||||
{
|
||||
@@ -241,6 +264,47 @@ void CreatureAI::EnterEvadeMode(EvadeReason why)
|
||||
}
|
||||
}
|
||||
|
||||
void CreatureAI::JustEnteredCombat(Unit* who)
|
||||
{
|
||||
// Creatures without threat list use JustEnteredCombat to trigger engagement
|
||||
if (!IsEngaged() && !me->CanHaveThreatList())
|
||||
EngagementStart(who);
|
||||
}
|
||||
|
||||
void CreatureAI::EngagementStart(Unit* who)
|
||||
{
|
||||
if (_isEngaged)
|
||||
{
|
||||
LOG_ERROR("scripts.ai", "CreatureAI::EngagementStart called even though creature is already engaged. Creature debug info:\n{}", me->GetDebugInfo());
|
||||
return;
|
||||
}
|
||||
_isEngaged = true;
|
||||
|
||||
me->AtEngage(who);
|
||||
}
|
||||
|
||||
void CreatureAI::EngagementOver()
|
||||
{
|
||||
if (!_isEngaged)
|
||||
{
|
||||
LOG_DEBUG("scripts.ai", "CreatureAI::EngagementOver called even though creature is not currently engaged. Creature debug info:\n{}", me->GetDebugInfo());
|
||||
return;
|
||||
}
|
||||
_isEngaged = false;
|
||||
|
||||
me->AtDisengage();
|
||||
}
|
||||
|
||||
void CreatureAI::JustExitedCombat()
|
||||
{
|
||||
EngagementOver();
|
||||
|
||||
// If creature is alive, in world, and not already evading, trigger evade to return home
|
||||
// Check IsInWorld to avoid evade during server shutdown/cleanup
|
||||
if (me->IsAlive() && me->IsInWorld() && !me->IsInEvadeMode())
|
||||
EnterEvadeMode(EVADE_REASON_NO_HOSTILES);
|
||||
}
|
||||
|
||||
/*void CreatureAI::AttackedBy(Unit* attacker)
|
||||
{
|
||||
if (!me->GetVictim())
|
||||
@@ -279,38 +343,50 @@ bool CreatureAI::UpdateVictim()
|
||||
if (!me->IsEngaged())
|
||||
return false;
|
||||
|
||||
if (!me->IsAlive())
|
||||
{
|
||||
EngagementOver();
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!me->HasReactState(REACT_PASSIVE))
|
||||
{
|
||||
if (Unit* victim = me->SelectVictim())
|
||||
AttackStart(victim);
|
||||
return me->GetVictim();
|
||||
if (victim != me->GetVictim())
|
||||
AttackStart(victim);
|
||||
return me->GetVictim() != nullptr;
|
||||
}
|
||||
// xinef: if we have any victim, just return true
|
||||
else if (me->GetVictim() && me->GetExactDist(me->GetVictim()) < 30.0f)
|
||||
return true;
|
||||
else if (me->GetThreatMgr().isThreatListEmpty())
|
||||
else if (!me->IsInCombat())
|
||||
{
|
||||
EnterEvadeMode();
|
||||
EnterEvadeMode(EVADE_REASON_NO_HOSTILES);
|
||||
return false;
|
||||
}
|
||||
else if (me->GetVictim())
|
||||
me->AttackStop();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CreatureAI::_EnterEvadeMode(EvadeReason /*why*/)
|
||||
{
|
||||
if (me->IsInEvadeMode())
|
||||
return false;
|
||||
|
||||
if (!me->IsAlive())
|
||||
{
|
||||
EngagementOver();
|
||||
return false;
|
||||
}
|
||||
|
||||
// Set evade state early to prevent recursion when CombatStop triggers JustExitedCombat
|
||||
// This will be cleared in MoveTargetedHome for pets/guardians following their owner
|
||||
me->AddUnitState(UNIT_STATE_EVADE);
|
||||
|
||||
// don't remove vehicle auras, passengers aren't supposed to drop off the vehicle
|
||||
// don't remove clone caster on evade (to be verified)
|
||||
me->RemoveEvadeAuras();
|
||||
|
||||
me->ClearComboPointHolders(); // Remove all combo points targeting this unit
|
||||
// sometimes bosses stuck in combat?
|
||||
me->GetThreatMgr().ClearAllThreat();
|
||||
me->CombatStop(true);
|
||||
me->LoadCreaturesAddon(true);
|
||||
me->SetLootRecipient(nullptr);
|
||||
@@ -321,14 +397,8 @@ bool CreatureAI::_EnterEvadeMode(EvadeReason /*why*/)
|
||||
if (ZoneScript* zoneScript = me->GetZoneScript() ? me->GetZoneScript() : (ZoneScript*)me->GetInstanceScript())
|
||||
zoneScript->OnCreatureEvade(me);
|
||||
|
||||
if (me->IsInEvadeMode())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
else if (CreatureGroup* formation = me->GetFormation())
|
||||
{
|
||||
if (CreatureGroup* formation = me->GetFormation())
|
||||
formation->MemberEvaded(me);
|
||||
}
|
||||
|
||||
if (TempSummon* summon = me->ToTempSummon())
|
||||
{
|
||||
@@ -345,6 +415,8 @@ bool CreatureAI::_EnterEvadeMode(EvadeReason /*why*/)
|
||||
}
|
||||
}
|
||||
|
||||
EngagementOver();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@@ -97,10 +97,13 @@ public:
|
||||
|
||||
WorldObject* GetSummoner() const;
|
||||
|
||||
explicit CreatureAI(Creature* creature) : UnitAI(creature), me(creature), _boundary(nullptr), _negateBoundary(false), m_MoveInLineOfSight_locked(false) { }
|
||||
explicit CreatureAI(Creature* creature) : UnitAI(creature), me(creature), _boundary(nullptr), _negateBoundary(false), _isEngaged(false), m_MoveInLineOfSight_locked(false) { }
|
||||
|
||||
~CreatureAI() override {}
|
||||
|
||||
/// @brief Check if creature is currently engaged in combat
|
||||
bool IsEngaged() const { return _isEngaged; }
|
||||
|
||||
void MoveCircleChecks();
|
||||
void MoveBackwardsChecks();
|
||||
|
||||
@@ -118,11 +121,24 @@ public:
|
||||
// Called for reaction at stopping attack at no attackers or targets
|
||||
virtual void EnterEvadeMode(EvadeReason why = EVADE_REASON_OTHER);
|
||||
|
||||
// Called for reaction whenever we start being in combat (overridden from UnitAI)
|
||||
void JustEnteredCombat(Unit* who) override;
|
||||
|
||||
// Called for reaction when a new non-offline unit is added to the threat list
|
||||
virtual void JustStartedThreateningMe(Unit* who) { if (!IsEngaged()) EngagementStart(who); }
|
||||
|
||||
/**
|
||||
* @brief Called for reaction when initially engaged
|
||||
* @brief Called for reaction when initially engaged - this happens _after_ JustEnteredCombat
|
||||
*/
|
||||
virtual void JustEngagedWith(Unit* /*who*/) {}
|
||||
|
||||
// Engagement handling
|
||||
void EngagementStart(Unit* who);
|
||||
void EngagementOver();
|
||||
|
||||
// Called when combat ends - clears engagement state and triggers evade if needed
|
||||
void JustExitedCombat() override;
|
||||
|
||||
// Called when the creature is killed
|
||||
virtual void JustDied(Unit* /*killer*/) {}
|
||||
|
||||
@@ -181,10 +197,13 @@ public:
|
||||
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); }
|
||||
|
||||
// Default handler for owner combat interactions — makes controlled creatures auto-engage
|
||||
void OnOwnerCombatInteraction(Unit* target);
|
||||
|
||||
/// == Triggered Actions Requested ==================
|
||||
|
||||
@@ -248,6 +267,7 @@ protected:
|
||||
|
||||
CreatureBoundary const* _boundary;
|
||||
bool _negateBoundary;
|
||||
bool _isEngaged;
|
||||
|
||||
private:
|
||||
bool m_MoveInLineOfSight_locked;
|
||||
|
||||
@@ -472,7 +472,7 @@ void ScriptedAI::DoResetThreat(Unit* unit)
|
||||
|
||||
void ScriptedAI::DoResetThreatList()
|
||||
{
|
||||
if (!me->CanHaveThreatList() || me->GetThreatMgr().isThreatListEmpty())
|
||||
if (!me->CanHaveThreatList() || me->GetThreatMgr().IsThreatListEmpty())
|
||||
{
|
||||
LOG_ERROR("entities.unit.ai", "DoResetThreatList called for creature that either cannot have threat list or has empty threat list (me entry = {})", me->GetEntry());
|
||||
return;
|
||||
@@ -714,11 +714,12 @@ void BossAI::TeleportCheaters()
|
||||
float x, y, z;
|
||||
me->GetPosition(x, y, z);
|
||||
|
||||
ThreatContainer::StorageType threatList = me->GetThreatMgr().GetThreatList();
|
||||
for (ThreatContainer::StorageType::const_iterator itr = threatList.begin(); itr != threatList.end(); ++itr)
|
||||
if (Unit* target = (*itr)->getTarget())
|
||||
if (target->IsPlayer() && !IsInBoundary(target))
|
||||
target->NearTeleportTo(x, y, z, 0);
|
||||
for (auto const& pair : me->GetCombatManager().GetPvECombatRefs())
|
||||
{
|
||||
Unit* target = pair.second->GetOther(me);
|
||||
if (target->IsControlledByPlayer() && !IsInBoundary(target))
|
||||
target->NearTeleportTo(x, y, z, 0);
|
||||
}
|
||||
}
|
||||
|
||||
void BossAI::JustSummoned(Creature* summon)
|
||||
|
||||
@@ -523,8 +523,16 @@ void SmartAI::CheckConditions(const uint32 diff)
|
||||
|
||||
void SmartAI::UpdateAI(uint32 diff)
|
||||
{
|
||||
bool hasVictim = UpdateVictim();
|
||||
if (!me->IsAlive())
|
||||
{
|
||||
if (IsEngaged())
|
||||
EngagementOver();
|
||||
return;
|
||||
}
|
||||
|
||||
CheckConditions(diff);
|
||||
|
||||
bool hasVictim = UpdateVictim();
|
||||
GetScript()->OnUpdate(diff);
|
||||
UpdatePath(diff);
|
||||
UpdateDespawn(diff);
|
||||
@@ -689,7 +697,12 @@ void SmartAI::EnterEvadeMode(EvadeReason /*why*/)
|
||||
|
||||
GetScript()->ProcessEventsFor(SMART_EVENT_EVADE); //must be after aura clear so we can cast spells from db
|
||||
|
||||
if (HasEscortState(SMART_ESCORT_ESCORTING))
|
||||
if (Unit* owner = me->GetCharmerOrOwner())
|
||||
{
|
||||
me->GetMotionMaster()->MoveFollow(owner, PET_FOLLOW_DIST, me->GetFollowAngle());
|
||||
me->ClearUnitState(UNIT_STATE_EVADE);
|
||||
}
|
||||
else if (HasEscortState(SMART_ESCORT_ESCORTING))
|
||||
{
|
||||
AddEscortState(SMART_ESCORT_RETURNING);
|
||||
ReturnToLastOOCPos();
|
||||
@@ -1003,13 +1016,24 @@ void SmartAI::OnCharmed(bool /* apply */)
|
||||
|
||||
mIsCharmed = charmed;
|
||||
|
||||
if (charmed && !me->isPossessed() && !me->IsVehicle())
|
||||
me->GetMotionMaster()->MoveFollow(me->GetCharmer(), PET_FOLLOW_DIST, me->GetFollowAngle());
|
||||
|
||||
if (!charmed && !me->IsInEvadeMode())
|
||||
{
|
||||
if (mCanRepeatPath)
|
||||
StartPath(mForcedMovement, GetScript()->GetPathId(), true);
|
||||
|
||||
if (Unit* charmer = me->GetCharmer())
|
||||
AttackStart(charmer);
|
||||
if (!me->LastCharmerGUID.IsEmpty())
|
||||
{
|
||||
if (!me->HasReactState(REACT_PASSIVE))
|
||||
if (Unit* lastCharmer = ObjectAccessor::GetUnit(*me, me->LastCharmerGUID))
|
||||
me->EngageWithTarget(lastCharmer);
|
||||
me->LastCharmerGUID.Clear();
|
||||
|
||||
if (!me->IsInCombat())
|
||||
EnterEvadeMode(EVADE_REASON_NO_HOSTILES);
|
||||
}
|
||||
}
|
||||
|
||||
GetScript()->ProcessEventsFor(SMART_EVENT_CHARMED, nullptr, 0, 0, charmed);
|
||||
|
||||
@@ -585,15 +585,11 @@ void SmartScript::ProcessAction(SmartScriptHolder& e, Unit* unit, uint32 var0, u
|
||||
if (!me)
|
||||
break;
|
||||
|
||||
ThreatContainer::StorageType threatList = me->GetThreatMgr().GetThreatList();
|
||||
for (ThreatContainer::StorageType::const_iterator i = threatList.begin(); i != threatList.end(); ++i)
|
||||
for (ThreatReference* ref : me->GetThreatMgr().GetModifiableThreatList())
|
||||
{
|
||||
if (Unit* target = ObjectAccessor::GetUnit(*me, (*i)->getUnitGuid()))
|
||||
{
|
||||
me->GetThreatMgr().ModifyThreatByPercent(target, e.action.threatPCT.threatINC ? (int32)e.action.threatPCT.threatINC : -(int32)e.action.threatPCT.threatDEC);
|
||||
LOG_DEBUG("sql.sql", "SmartScript::ProcessAction:: SMART_ACTION_THREAT_ALL_PCT: Creature {} modify threat for unit {}, value {}",
|
||||
me->GetGUID().ToString(), target->GetGUID().ToString(), e.action.threatPCT.threatINC ? (int32)e.action.threatPCT.threatINC : -(int32)e.action.threatPCT.threatDEC);
|
||||
}
|
||||
ref->ModifyThreatByPercent(std::max<int32>(-100, int32(e.action.threatPCT.threatINC) - int32(e.action.threatPCT.threatDEC)));
|
||||
LOG_DEBUG("sql.sql", "SmartScript::ProcessAction:: SMART_ACTION_THREAT_ALL_PCT: Creature {} modify threat for unit {}, value {}",
|
||||
me->GetGUID().ToString(), ref->GetVictim()->GetGUID().ToString(), int32(e.action.threatPCT.threatINC) - int32(e.action.threatPCT.threatDEC));
|
||||
}
|
||||
break;
|
||||
}
|
||||
@@ -606,9 +602,9 @@ void SmartScript::ProcessAction(SmartScriptHolder& e, Unit* unit, uint32 var0, u
|
||||
{
|
||||
if (IsUnit(target))
|
||||
{
|
||||
me->GetThreatMgr().ModifyThreatByPercent(target->ToUnit(), e.action.threatPCT.threatINC ? (int32)e.action.threatPCT.threatINC : -(int32)e.action.threatPCT.threatDEC);
|
||||
LOG_DEBUG("scripts.ai", "SmartScript::ProcessAction:: SMART_ACTION_THREAT_SINGLE_PCT: Creature guidLow {} modify threat for unit {}, value {}",
|
||||
me->GetGUID().ToString(), target->GetGUID().ToString(), e.action.threatPCT.threatINC ? (int32)e.action.threatPCT.threatINC : -(int32)e.action.threatPCT.threatDEC);
|
||||
me->GetThreatMgr().ModifyThreatByPercent(target->ToUnit(), std::max<int32>(-100, int32(e.action.threatPCT.threatINC) - int32(e.action.threatPCT.threatDEC)));
|
||||
LOG_DEBUG("scripts.ai", "SmartScript::ProcessAction:: SMART_ACTION_THREAT_SINGLE_PCT: Creature {} modify threat for unit {}, value {}",
|
||||
me->GetGUID().ToString(), target->GetGUID().ToString(), int32(e.action.threatPCT.threatINC) - int32(e.action.threatPCT.threatDEC));
|
||||
}
|
||||
}
|
||||
break;
|
||||
@@ -1294,9 +1290,7 @@ void SmartScript::ProcessAction(SmartScriptHolder& e, Unit* unit, uint32 var0, u
|
||||
for (WorldObject* unit : units)
|
||||
if (IsPlayer(unit) && !unit->ToPlayer()->isDead())
|
||||
{
|
||||
me->SetInCombatWith(unit->ToPlayer());
|
||||
unit->ToPlayer()->SetInCombatWith(me);
|
||||
me->AddThreat(unit->ToPlayer(), 0.0f);
|
||||
me->EngageWithTarget(unit->ToPlayer());
|
||||
}
|
||||
}
|
||||
else
|
||||
@@ -2611,9 +2605,11 @@ void SmartScript::ProcessAction(SmartScriptHolder& e, Unit* unit, uint32 var0, u
|
||||
}
|
||||
case SMART_ACTION_ADD_THREAT:
|
||||
{
|
||||
if (!me->CanHaveThreatList())
|
||||
break;
|
||||
for (WorldObject* const target : targets)
|
||||
if (IsUnit(target))
|
||||
me->AddThreat(target->ToUnit(), float(e.action.threatPCT.threatINC) - float(e.action.threatPCT.threatDEC));
|
||||
me->GetThreatMgr().AddThreat(target->ToUnit(), float(e.action.threat.threatINC) - float(e.action.threat.threatDEC), nullptr, true, true);
|
||||
break;
|
||||
}
|
||||
case SMART_ACTION_LOAD_EQUIPMENT:
|
||||
@@ -3900,9 +3896,8 @@ void SmartScript::GetTargets(ObjectVector& targets, SmartScriptHolder const& e,
|
||||
{
|
||||
if (me)
|
||||
{
|
||||
ThreatContainer::StorageType threatList = me->GetThreatMgr().GetThreatList();
|
||||
for (ThreatContainer::StorageType::const_iterator i = threatList.begin(); i != threatList.end(); ++i)
|
||||
if (Unit* temp = ObjectAccessor::GetUnit(*me, (*i)->getUnitGuid()))
|
||||
for (ThreatReference const* ref : me->GetThreatMgr().GetUnsortedThreatList())
|
||||
if (Unit* temp = ref->GetVictim())
|
||||
// Xinef: added distance check
|
||||
if (e.target.threatList.maxDist == 0 || me->IsWithinCombatRange(temp, (float)e.target.threatList.maxDist))
|
||||
targets.push_back(temp);
|
||||
@@ -4848,12 +4843,11 @@ void SmartScript::ProcessEvent(SmartScriptHolder& e, Unit* unit, uint32 var0, ui
|
||||
if (!me || !me->IsEngaged())
|
||||
return;
|
||||
|
||||
ThreatContainer::StorageType threatList = me->GetThreatMgr().GetThreatList();
|
||||
for (ThreatContainer::StorageType::const_iterator i = threatList.begin(); i != threatList.end(); ++i)
|
||||
for (ThreatReference const* ref : me->GetThreatMgr().GetUnsortedThreatList())
|
||||
{
|
||||
if (Unit* target = ObjectAccessor::GetUnit(*me, (*i)->getUnitGuid()))
|
||||
if (Unit* target = ref->GetVictim())
|
||||
{
|
||||
if (!target || !IsPlayer(target) || !target->IsNonMeleeSpellCast(false, false, true))
|
||||
if (!IsPlayer(target) || !target->IsNonMeleeSpellCast(false, false, true))
|
||||
continue;
|
||||
|
||||
if (!(me->IsInRange(target, (float)e.event.minMaxRepeat.rangeMin, (float)e.event.minMaxRepeat.rangeMax)))
|
||||
@@ -4867,6 +4861,7 @@ void SmartScript::ProcessEvent(SmartScriptHolder& e, Unit* unit, uint32 var0, ui
|
||||
|
||||
// No targets found
|
||||
RecalcTimer(e, e.event.minMaxRepeat.repeatMin, e.event.minMaxRepeat.repeatMax);
|
||||
|
||||
break;
|
||||
}
|
||||
case SMART_EVENT_AREA_RANGE:
|
||||
@@ -4874,10 +4869,9 @@ void SmartScript::ProcessEvent(SmartScriptHolder& e, Unit* unit, uint32 var0, ui
|
||||
if (!me || !me->IsEngaged())
|
||||
return;
|
||||
|
||||
ThreatContainer::StorageType threatList = me->GetThreatMgr().GetThreatList();
|
||||
for (ThreatContainer::StorageType::const_iterator i = threatList.begin(); i != threatList.end(); ++i)
|
||||
for (ThreatReference const* ref : me->GetThreatMgr().GetUnsortedThreatList())
|
||||
{
|
||||
if (Unit* target = ObjectAccessor::GetUnit(*me, (*i)->getUnitGuid()))
|
||||
if (Unit* target = ref->GetVictim())
|
||||
{
|
||||
if (!(me->IsInRange(target, (float)e.event.minMaxRepeat.rangeMin, (float)e.event.minMaxRepeat.rangeMax)))
|
||||
continue;
|
||||
|
||||
Reference in New Issue
Block a user