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:
blinkysc
2026-03-18 13:36:59 -05:00
committed by GitHub
parent c80a0f1fad
commit 984baa92dd
101 changed files with 7045 additions and 2659 deletions
+1 -1
View File
@@ -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);
}
+5
View File
@@ -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 {}
+2 -2
View File
@@ -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;
}
+66 -11
View File
@@ -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)
{
+11 -8
View File
@@ -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);
};
+96 -24
View File
@@ -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;
}
+24 -4
View File
@@ -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)
+28 -4
View File
@@ -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);
+19 -25
View File
@@ -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;