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
@@ -235,9 +235,10 @@ struct boss_hadronox : public BossAI
bool IsInCombatWithPlayer() const
{
return std::ranges::any_of(me->GetThreatMgr().GetThreatList(), [](auto const& ref) {
return ref->getTarget()->IsControlledByPlayer();
});
for (ThreatReference const* ref : me->GetThreatMgr().GetUnsortedThreatList())
if (ref->GetVictim()->IsControlledByPlayer())
return true;
return false;
}
void DamageTaken(Unit* who, uint32& damage, DamageEffectType /*damageType*/, SpellSchoolMask /*damageSchoolMask*/) override
@@ -1080,7 +1080,7 @@ class spell_halion_twilight_phasing_aura : public AuraScript
GetTarget()->SetUnitFlag(UNIT_FLAG_NOT_SELECTABLE);
GetTarget()->ToCreature()->SetReactState(REACT_DEFENSIVE);
GetTarget()->GetMotionMaster()->Clear();
GetTarget()->GetThreatMgr().clearReferences();
GetTarget()->GetThreatMgr().ClearAllThreat();
GetTarget()->RemoveAllAttackers();
GetTarget()->AttackStop();
}
@@ -1090,7 +1090,7 @@ class spell_halion_twilight_phasing_aura : public AuraScript
GetTarget()->RemoveUnitFlag(UNIT_FLAG_NOT_SELECTABLE);
GetTarget()->ToCreature()->SetReactState(REACT_DEFENSIVE);
GetTarget()->GetMotionMaster()->Clear();
GetTarget()->GetThreatMgr().clearReferences();
GetTarget()->GetThreatMgr().ClearAllThreat();
GetTarget()->RemoveAllAttackers();
GetTarget()->AttackStop();
}
@@ -138,7 +138,7 @@ public:
me->SetFaction(FACTION_FRIENDLY);
events.Reset();
Talk(SAY_EADRIC_DEFEATED);
me->GetThreatMgr().clearReferences();
me->GetThreatMgr().ClearAllThreat();
me->SetRegeneratingHealth(false);
_EnterEvadeMode();
me->SetUnitFlag(UNIT_FLAG_NON_ATTACKABLE);
@@ -281,7 +281,7 @@ public:
me->SetFaction(FACTION_FRIENDLY);
events.Reset();
Talk(SAY_PALETRESS_DEFEATED);
me->GetThreatMgr().clearReferences();
me->GetThreatMgr().ClearAllThreat();
me->SetRegeneratingHealth(false);
_EnterEvadeMode();
me->SetUnitFlag(UNIT_FLAG_NON_ATTACKABLE);
@@ -143,7 +143,6 @@ public:
void Reset() override
{
me->SetReactState(REACT_PASSIVE);
me->getHostileRefMgr().setOnlineOfflineState(false);
}
void OnCharmed(bool apply) override
@@ -103,17 +103,14 @@ struct boss_faction_championsAI : public ScriptedAI
void RecalculateThreat()
{
ThreatContainer::StorageType const& tList = me->GetThreatMgr().GetThreatList();
for( ThreatContainer::StorageType::const_iterator itr = tList.begin(); itr != tList.end(); ++itr )
for (ThreatReference const* ref : me->GetThreatMgr().GetUnsortedThreatList())
{
Unit* pUnit = ObjectAccessor::GetUnit(*me, (*itr)->getUnitGuid());
Unit* pUnit = ref->GetVictim();
if (pUnit && pUnit->IsPlayer() && me->GetThreatMgr().GetThreat(pUnit))
{
float threatMod = GetThreatMod(me->GetDistance2d(pUnit), (float)pUnit->GetArmor(), pUnit->GetHealth(), pUnit->GetMaxHealth(), pUnit);
me->GetThreatMgr().ModifyThreatByPercent(pUnit, -100);
//me->getThreatMgr().DoAddThreat(pUnit, 10000000.0f * threatMod);
if (HostileReference* ref = me->GetThreatMgr().GetOnlineContainer().getReferenceByTarget(pUnit))
ref->AddThreat(10000000.0f * threatMod);
me->GetThreatMgr().ResetThreat(pUnit);
me->GetThreatMgr().AddThreat(pUnit, 10000000.0f * threatMod, nullptr, true, true);
}
}
}
@@ -179,27 +176,23 @@ struct boss_faction_championsAI : public ScriptedAI
uint32 EnemiesInRange(float distance)
{
ThreatContainer::StorageType const& tList = me->GetThreatMgr().GetThreatList();
uint32 count = 0;
Unit* target;
for( ThreatContainer::StorageType::const_iterator iter = tList.begin(); iter != tList.end(); ++iter )
for (ThreatReference const* ref : me->GetThreatMgr().GetUnsortedThreatList())
{
target = ObjectAccessor::GetUnit((*me), (*iter)->getUnitGuid());
if (target && me->GetDistance2d(target) < distance )
++count;
if (Unit* target = ref->GetVictim())
if (me->GetDistance2d(target) < distance)
++count;
}
return count;
}
Unit* SelectEnemyCaster(bool casting, float range)
{
ThreatContainer::StorageType const& tList = me->GetThreatMgr().GetThreatList();
Unit* target;
for( ThreatContainer::StorageType::const_iterator iter = tList.begin(); iter != tList.end(); ++iter )
for (ThreatReference const* ref : me->GetThreatMgr().GetUnsortedThreatList())
{
target = ObjectAccessor::GetUnit((*me), (*iter)->getUnitGuid());
if (target && target->getPowerType() == POWER_MANA && (!casting || target->HasUnitState(UNIT_STATE_CASTING)) && me->GetExactDist(target) <= range )
return target;
if (Unit* target = ref->GetVictim())
if (target->getPowerType() == POWER_MANA && (!casting || target->HasUnitState(UNIT_STATE_CASTING)) && me->GetExactDist(target) <= range)
return target;
}
return nullptr;
}
@@ -980,7 +980,7 @@ public:
for (Map::PlayerList::const_iterator itr = pl.begin(); itr != pl.end(); ++itr)
if (Player* p = itr->GetSource())
if (!p->IsGameMaster())
p->SetInCombatState(true);
p->SetInCombatWith(me);
_events.ScheduleEvent(EVENT_KEEP_PLAYER_IN_COMBAT, 4s);
}
break;
@@ -1319,7 +1319,7 @@ public:
for (Map::PlayerList::const_iterator itr = pl.begin(); itr != pl.end(); ++itr)
if (Player* p = itr->GetSource())
if (!p->IsGameMaster())
p->SetInCombatState(true);
p->SetInCombatWith(me);
_events.ScheduleEvent(EVENT_KEEP_PLAYER_IN_COMBAT, 4s);
}
break;
@@ -301,13 +301,13 @@ public:
if (events.GetPhaseMask() & PHASE_ONE_MASK && damage >= me->GetPower(POWER_MANA))
{
// reset threat
ThreatContainer::StorageType const& threatlist = me->GetThreatMgr().GetThreatList();
for (ThreatContainer::StorageType::const_iterator itr = threatlist.begin(); itr != threatlist.end(); ++itr)
for (ThreatReference const* ref : me->GetThreatMgr().GetUnsortedThreatList())
{
Unit* unit = ObjectAccessor::GetUnit((*me), (*itr)->getUnitGuid());
if (unit && DoGetThreat(unit))
DoModifyThreatByPercent(unit, -100);
if (Unit* unit = ref->GetVictim())
{
if (DoGetThreat(unit))
DoModifyThreatByPercent(unit, -100);
}
}
Talk(SAY_PHASE_2);
@@ -209,7 +209,7 @@ public:
bool operator()(Unit const* target) const
{
if (!me || !target || !target->IsPlayer() || target == me->GetThreatMgr().GetCurrentVictim())
if (!me || !target || !target->IsPlayer() || target == me->GetThreatMgr().GetLastVictim())
return false;
if (me->IsWithinCombatRange(target, 7.0f))
@@ -498,7 +498,7 @@ public:
bool Execute(uint64 /*time*/, uint32 /*diff*/) override
{
_owner->SetReactState(REACT_AGGRESSIVE);
if (!_owner->GetThreatMgr().isThreatListEmpty())
if (!_owner->GetThreatMgr().IsThreatListEmpty())
if (Unit* target = _owner->SelectVictim())
_owner->AI()->AttackStart(target);
if (!_owner->GetVictim())
@@ -1180,7 +1180,7 @@ public:
{
if (summon->GetEntry() == NPC_VILE_SPIRIT)
{
summon->m_Events.KillAllEvents(true);
summon->m_Events.KillAllEvents(false);
summon->m_Events.AddEventAtOffset(new VileSpiritActivateEvent(summon), 55s);
summon->GetMotionMaster()->Clear(true);
summon->StopMoving();
@@ -2273,7 +2273,7 @@ public:
{
me->SetControlled(false, UNIT_STATE_ROOT);
if (!me->GetThreatMgr().isThreatListEmpty())
if (!me->GetThreatMgr().IsThreatListEmpty())
if (Unit* target = me->SelectVictim())
AttackStart(target);
if (!me->GetVictim())
@@ -593,12 +593,12 @@ public:
{
checkTimer = 3000;
me->SetInCombatWithZone();
ThreatContainer::StorageType const& threatList = me->GetThreatMgr().GetThreatList();
if (!threatList.empty())
for (ThreatContainer::StorageType::const_iterator itr = threatList.begin(); itr != threatList.end(); ++itr)
if (Unit* target = (*itr)->getTarget())
if (target->IsAlive() && target->IsPlayer() && me->GetExactDist(target) < 200.0f && !target->IsImmunedToDamageOrSchool(SPELL_SCHOOL_MASK_ALL))
return;
for (ThreatReference const* ref : me->GetThreatMgr().GetUnsortedThreatList())
{
if (Unit* target = ref->GetVictim())
if (target->IsAlive() && target->IsPlayer() && me->GetExactDist(target) < 200.0f && !target->IsImmunedToDamageOrSchool(SPELL_SCHOOL_MASK_ALL))
return;
}
EnterEvadeMode();
}
else
@@ -1762,15 +1762,26 @@ public:
Position myPos = me->GetPosition();
me->NearTeleportTo(c->GetPositionX(), c->GetPositionY(), c->GetPositionZ(), c->GetOrientation());
c->NearTeleportTo(myPos.GetPositionX(), myPos.GetPositionY(), myPos.GetPositionZ(), myPos.GetOrientation());
const ThreatContainer::StorageType me_tl = me->GetThreatMgr().GetThreatList();
const ThreatContainer::StorageType target_tl = c->GetThreatMgr().GetThreatList();
// Store threat values before reset
std::vector<std::pair<Unit*, float>> myThreats;
std::vector<std::pair<Unit*, float>> targetThreats;
for (ThreatReference const* ref : me->GetThreatMgr().GetUnsortedThreatList())
if (Unit* victim = ref->GetVictim())
myThreats.push_back({victim, ref->GetThreat()});
for (ThreatReference const* ref : c->GetThreatMgr().GetUnsortedThreatList())
if (Unit* victim = ref->GetVictim())
targetThreats.push_back({victim, ref->GetThreat()});
DoResetThreatList();
for (ThreatContainer::StorageType::const_iterator iter = target_tl.begin(); iter != target_tl.end(); ++iter)
me->GetThreatMgr().AddThreat((*iter)->getTarget(), (*iter)->GetThreat());
for (auto const& pair : targetThreats)
me->GetThreatMgr().AddThreat(pair.first, pair.second);
c->GetThreatMgr().ResetAllThreat();
for (ThreatContainer::StorageType::const_iterator iter = me_tl.begin(); iter != me_tl.end(); ++iter)
c->GetThreatMgr().AddThreat((*iter)->getTarget(), (*iter)->GetThreat());
for (auto const& pair : myThreats)
c->GetThreatMgr().AddThreat(pair.first, pair.second);
}
}
@@ -406,16 +406,19 @@ public:
events.Repeat(15s);
break;
case EVENT_TELEPORT:
{
me->AttackStop();
if (IN_LIVE_SIDE(me))
{
me->CastSpell(me, SPELL_TELEPORT_DEAD, false);
}
else
{
me->CastSpell(me, SPELL_TELEPORT_LIVE, false);
}
me->GetThreatMgr().resetAggro(NotOnSameSide(me));
// Clear threat from targets not on the same side as Gothik
NotOnSameSide notOnSameSide(me);
for (ThreatReference const* ref : me->GetThreatMgr().GetUnsortedThreatList())
if (notOnSameSide(ref->GetVictim()))
me->GetThreatMgr().ClearThreat(ref->GetVictim());
if (Unit* pTarget = SelectTarget(SelectTargetMethod::MaxDistance, 0))
{
me->GetThreatMgr().AddThreat(pTarget, 100.0f);
@@ -423,6 +426,7 @@ public:
}
events.Repeat(20s);
break;
}
case EVENT_CHECK_HEALTH:
if (me->HealthBelowPct(30))
{
@@ -401,15 +401,17 @@ public:
case EVENT_DETONATE_MANA:
{
std::vector<Unit*> unitList;
ThreatContainer::StorageType const& threatList = me->GetThreatMgr().GetThreatList();
for (auto itr : threatList)
for (ThreatReference const* ref : me->GetThreatMgr().GetUnsortedThreatList())
{
if (itr->getTarget()->IsPlayer()
&& itr->getTarget()->getPowerType() == POWER_MANA
&& itr->getTarget()->GetPower(POWER_MANA))
{
unitList.push_back(itr->getTarget());
}
if (Unit* target = ref->GetVictim())
{
if (target->IsPlayer()
&& target->getPowerType() == POWER_MANA
&& target->GetPower(POWER_MANA))
{
unitList.push_back(target);
}
}
}
if (!unitList.empty())
{
@@ -117,11 +117,11 @@ public:
std::list<Unit*> meleeRangeTargets;
Unit* finalTarget = nullptr;
uint8 counter = 0;
auto i = me->GetThreatMgr().GetThreatList().begin();
for (; i != me->GetThreatMgr().GetThreatList().end(); ++i, ++counter)
auto threatList = me->GetThreatMgr().GetSortedThreatList();
for (auto i = threatList.begin(); i != threatList.end(); ++i, ++counter)
{
// Gather all units with melee range
Unit* target = (*i)->getTarget();
Unit* target = (*i)->GetVictim();
if (me->IsWithinMeleeRange(target))
{
meleeRangeTargets.push_back(target);
@@ -315,17 +315,17 @@ public:
}
std::vector<Unit*> targets;
auto i = me->GetThreatMgr().GetThreatList().begin();
for (; i != me->GetThreatMgr().GetThreatList().end(); ++i)
auto i = me->GetThreatMgr().GetUnsortedThreatList().begin();
for (; i != me->GetThreatMgr().GetUnsortedThreatList().end(); ++i)
{
if ((*i)->getTarget()->IsPlayer())
if ((*i)->GetVictim()->IsPlayer())
{
bool inList = false;
if (!blockList.empty())
{
for (GuidList::const_iterator itr = blockList.begin(); itr != blockList.end(); ++itr)
{
if ((*i)->getTarget()->GetGUID() == *itr)
if ((*i)->GetVictim()->GetGUID() == *itr)
{
inList = true;
break;
@@ -334,7 +334,7 @@ public:
}
if (!inList)
{
targets.push_back((*i)->getTarget());
targets.push_back((*i)->GetVictim());
}
}
}
@@ -128,8 +128,8 @@ public:
bool IsAnyPlayerInMeleeRange() const
{
for (auto const& ref : me->GetThreatMgr().GetThreatList())
if (Unit* target = ref->getTarget())
for (auto const* ref : me->GetThreatMgr().GetUnsortedThreatList())
if (Unit* target = ref->GetVictim())
if (target->IsPlayer() && me->IsWithinMeleeRange(target))
return true;
return false;
@@ -1419,7 +1419,7 @@ public:
{
me->CastSpell(me, SPELL_SAC_GHOUL_EXPLODE, true);
me->KillSelf();
me->m_Events.KillAllEvents(true);
me->m_Events.KillAllEvents(false);
Deactivate();
}
}