fix(Core/Combat): safe StopAttackFaction and restore escort evade (#25516)

Co-authored-by: blinkysc <blinkysc@users.noreply.github.com>
This commit is contained in:
blinkysc 2026-04-20 14:18:14 -05:00 committed by GitHub
parent ad3b5a29e1
commit 37b60cd0bc
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 25 additions and 11 deletions

View file

@ -309,12 +309,9 @@ void CreatureAI::EngagementOver()
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);
// No-op: synchronous EnterEvadeMode cascades via MemberEvaded and frees
// refs held by upstream iterators (StopAttackFaction crash). EngagementOver
// here also resets scripted fights on brief combat gaps (Valithria).
}
/*void CreatureAI::AttackedBy(Unit* attacker)

View file

@ -189,6 +189,15 @@ void npc_escortAI::ReturnToLastPoint()
me->GetMotionMaster()->MovePoint(POINT_LAST_POINT, x, y, z, FORCED_MOVEMENT_RUN);
}
void npc_escortAI::JustExitedCombat()
{
// Evade synchronously so UpdateAI does not push a waypoint spline before
// SelectVictim's evade fallback fires; stacked motion intents twitch.
EngagementOver();
if (me->IsAlive() && me->IsInWorld() && !me->IsInEvadeMode())
EnterEvadeMode(EVADE_REASON_NO_HOSTILES);
}
void npc_escortAI::EnterEvadeMode(EvadeReason /*why*/)
{
me->GetThreatMgr().ClearAllThreat();

View file

@ -70,6 +70,8 @@ public:
void EnterEvadeMode(EvadeReason /*why*/ = EVADE_REASON_OTHER) override;
void JustExitedCombat() override;
void UpdateAI(uint32 diff) override; //the "internal" update, calls UpdateEscortAI()
virtual void UpdateEscortAI(uint32 diff); //used when it's needed to add code in update (abilities, scripted events, etc)

View file

@ -16105,13 +16105,19 @@ void Unit::StopAttackFaction(uint32 faction_id)
++itr;
}
// End combat and threat references with creatures in this faction
std::vector<CombatReference*> refsToEnd;
// Collect GUIDs, not pointers: EndCombat can cascade through AI callbacks
// (formation MemberEvaded -> CombatStop) and free other refs in the list.
std::vector<ObjectGuid> guidsToEnd;
for (auto const& pair : m_combatManager.GetPvECombatRefs())
if (pair.second->GetOther(this)->GetFactionTemplateEntry()->faction == faction_id)
refsToEnd.push_back(pair.second);
for (CombatReference* ref : refsToEnd)
ref->EndCombat();
guidsToEnd.push_back(pair.first);
for (ObjectGuid const& guid : guidsToEnd)
{
auto const& refs = m_combatManager.GetPvECombatRefs();
auto it = refs.find(guid);
if (it != refs.end())
it->second->EndCombat();
}
for (ControlSet::const_iterator itr = m_Controlled.begin(); itr != m_Controlled.end(); ++itr)
(*itr)->StopAttackFaction(faction_id);