From a7644b03d4f8faab1852050f9876ed5f3dae0434 Mon Sep 17 00:00:00 2001 From: Shauren Date: Sat, 15 Mar 2025 07:16:45 +0100 Subject: [PATCH] Core/Creatures: Implemented CREATURE_STATIC_FLAG_2_IGNORE_SANCTUARY (ignore SPELL_EFFECT_SANCTUARY, used by Vanish) --- src/server/game/Combat/CombatManager.cpp | 36 +++++++++++++------ src/server/game/Combat/CombatManager.h | 11 +++--- src/server/game/Combat/ThreatManager.cpp | 13 ++++--- src/server/game/Combat/ThreatManager.h | 2 +- src/server/game/Entities/Creature/Creature.h | 2 ++ .../game/Entities/Creature/CreatureData.h | 2 +- src/server/game/Entities/Unit/Unit.cpp | 25 ++++++++----- src/server/game/Entities/Unit/Unit.h | 2 +- src/server/game/Spells/SpellEffects.cpp | 13 +++++-- 9 files changed, 73 insertions(+), 33 deletions(-) diff --git a/src/server/game/Combat/CombatManager.cpp b/src/server/game/Combat/CombatManager.cpp index 7ae9aa6abc..a9e65ba251 100644 --- a/src/server/game/Combat/CombatManager.cpp +++ b/src/server/game/Combat/CombatManager.cpp @@ -277,22 +277,31 @@ void CombatManager::EndCombatBeyondRange(float range, bool includingPvP) } } -void CombatManager::SuppressPvPCombat() +void CombatManager::SuppressPvPCombat(UnitFilter* unitFilter /*= nullptr*/) { - for (auto const& pair : _pvpRefs) - pair.second->Suppress(_owner); + for (auto const& [guid, combatRef] : _pvpRefs) + if (!unitFilter || unitFilter(combatRef->GetOther(_owner))) + combatRef->Suppress(_owner); + if (UpdateOwnerCombatState()) if (UnitAI* ownerAI = _owner->GetAI()) ownerAI->JustExitedCombat(); } -void CombatManager::EndAllPvECombat() +void CombatManager::EndAllPvECombat(UnitFilter* unitFilter /*= nullptr*/) { // cannot have threat without combat - _owner->GetThreatManager().RemoveMeFromThreatLists(); + _owner->GetThreatManager().RemoveMeFromThreatLists(unitFilter); _owner->GetThreatManager().ClearAllThreat(); - while (!_pveRefs.empty()) - _pveRefs.begin()->second->EndCombat(); + + std::vector combatReferencesToRemove; + combatReferencesToRemove.reserve(_pveRefs.size()); + for (auto const& [guid, combatRef] : _pveRefs) + if (!unitFilter || unitFilter(combatRef->GetOther(_owner))) + combatReferencesToRemove.push_back(combatRef); + + for (CombatReference* combatRef : combatReferencesToRemove) + combatRef->EndCombat(); } void CombatManager::RevalidateCombat() @@ -324,12 +333,19 @@ void CombatManager::RevalidateCombat() } } -void CombatManager::EndAllPvPCombat() +void CombatManager::EndAllPvPCombat(UnitFilter* unitFilter /*= nullptr*/) { - while (!_pvpRefs.empty()) - _pvpRefs.begin()->second->EndCombat(); + std::vector combatReferencesToRemove; + combatReferencesToRemove.reserve(_pvpRefs.size()); + for (auto const& [guid, combatRef] : _pvpRefs) + if (!unitFilter || unitFilter(combatRef->GetOther(_owner))) + combatReferencesToRemove.push_back(combatRef); + + for (CombatReference* combatRef : combatReferencesToRemove) + combatRef->EndCombat(); } + /*static*/ void CombatManager::NotifyAICombat(Unit* me, Unit* other) { if (UnitAI* ai = me->GetAI()) diff --git a/src/server/game/Combat/CombatManager.h b/src/server/game/Combat/CombatManager.h index 46a4467581..57da64ad80 100644 --- a/src/server/game/Combat/CombatManager.h +++ b/src/server/game/Combat/CombatManager.h @@ -119,12 +119,15 @@ class TC_GAME_API CombatManager bool IsInCombatWith(Unit const* who) const; void InheritCombatStatesFrom(Unit const* who); void EndCombatBeyondRange(float range, bool includingPvP = false); + + using UnitFilter = bool(Unit const* otherUnit); + // flags any pvp refs for suppression on owner's side - these refs will not generate combat until refreshed - void SuppressPvPCombat(); - void EndAllPvECombat(); + void SuppressPvPCombat(UnitFilter* unitFilter = nullptr); + void EndAllPvECombat(UnitFilter* unitFilter = nullptr); void RevalidateCombat(); - void EndAllPvPCombat(); - void EndAllCombat() { EndAllPvECombat(); EndAllPvPCombat(); } + void EndAllPvPCombat(UnitFilter* unitFilter = nullptr); + void EndAllCombat(UnitFilter* unitFilter = nullptr) { EndAllPvECombat(unitFilter); EndAllPvPCombat(unitFilter); } CombatManager(CombatManager const&) = delete; CombatManager& operator=(CombatManager const&) = delete; diff --git a/src/server/game/Combat/ThreatManager.cpp b/src/server/game/Combat/ThreatManager.cpp index f7e0bc31bf..6db405ca84 100644 --- a/src/server/game/Combat/ThreatManager.cpp +++ b/src/server/game/Combat/ThreatManager.cpp @@ -752,13 +752,16 @@ void ThreatManager::ForwardThreatForAssistingMe(Unit* assistant, float baseAmoun threatened->GetThreatManager().AddThreat(assistant, 0.0f, spell, true); } -void ThreatManager::RemoveMeFromThreatLists() +void ThreatManager::RemoveMeFromThreatLists(bool (*unitFilter)(Unit const* otherUnit)) { - while (!_threatenedByMe.empty()) - { - auto& ref = _threatenedByMe.begin()->second; + std::vector threatReferencesToRemove; + threatReferencesToRemove.reserve(_threatenedByMe.size()); + for (auto const& [guid, ref] : _threatenedByMe) + if (!unitFilter || unitFilter(ref->GetOwner())) + threatReferencesToRemove.push_back(ref); + + for (ThreatReference* ref : threatReferencesToRemove) ref->_mgr.ClearThreat(_owner); - } } void ThreatManager::UpdateMyTempModifiers() diff --git a/src/server/game/Combat/ThreatManager.h b/src/server/game/Combat/ThreatManager.h index 096e53a77d..4a7b326e1b 100644 --- a/src/server/game/Combat/ThreatManager.h +++ b/src/server/game/Combat/ThreatManager.h @@ -167,7 +167,7 @@ class TC_GAME_API ThreatManager // what it says on the tin - call AddThreat on everything that's threatened by us with the specified params void ForwardThreatForAssistingMe(Unit* assistant, float baseAmount, SpellInfo const* spell = nullptr, bool ignoreModifiers = false); // delete all ThreatReferences with victim == owner - void RemoveMeFromThreatLists(); + void RemoveMeFromThreatLists(bool (*unitFilter)(Unit const* otherUnit)); // re-calculates the temporary threat modifier from auras on myself void UpdateMyTempModifiers(); // re-calculate SPELL_AURA_MOD_THREAT modifiers diff --git a/src/server/game/Entities/Creature/Creature.h b/src/server/game/Entities/Creature/Creature.h index dbf120e498..7d5b973ba4 100644 --- a/src/server/game/Entities/Creature/Creature.h +++ b/src/server/game/Entities/Creature/Creature.h @@ -277,6 +277,8 @@ class TC_GAME_API Creature : public Unit, public GridObject, public Ma bool CanAssistTo(const Unit* u, const Unit* enemy, bool checkfaction = true) const; bool _IsTargetAcceptable(const Unit* target) const; bool CanIgnoreFeignDeath() const { return (GetCreatureTemplate()->flags_extra & CREATURE_FLAG_EXTRA_IGNORE_FEIGN_DEATH) != 0; } + bool IsIgnoringSanctuarySpellEffect() const { return _staticFlags.HasFlag(CREATURE_STATIC_FLAG_2_IGNORE_SANCTUARY); } + void SetIngoreSanctuarySpellEffect(bool ignoreSanctuary) { _staticFlags.ApplyFlag(CREATURE_STATIC_FLAG_2_IGNORE_SANCTUARY, ignoreSanctuary); } MovementGeneratorType GetDefaultMovementType() const { return m_defaultMovementType; } void SetDefaultMovementType(MovementGeneratorType mgt) { m_defaultMovementType = mgt; } diff --git a/src/server/game/Entities/Creature/CreatureData.h b/src/server/game/Entities/Creature/CreatureData.h index 4d381a31ea..e88e053bec 100644 --- a/src/server/game/Entities/Creature/CreatureData.h +++ b/src/server/game/Entities/Creature/CreatureData.h @@ -126,7 +126,7 @@ enum CreatureStaticFlags2 CREATURE_STATIC_FLAG_2_NO_WOUNDED_SLOWDOWN = 0x00000040, CREATURE_STATIC_FLAG_2_USE_CREATOR_BONUSES = 0x00000080, CREATURE_STATIC_FLAG_2_IGNORE_FEIGN_DEATH = 0x00000100, // CREATURE_FLAG_EXTRA_IGNORE_FEIGN_DEATH - CREATURE_STATIC_FLAG_2_IGNORE_SANCTUARY = 0x00000200, + CREATURE_STATIC_FLAG_2_IGNORE_SANCTUARY = 0x00000200, // Ignores SPELL_EFFECT_SANCTUARY CREATURE_STATIC_FLAG_2_ACTION_TRIGGERS_WHILE_CHARMED = 0x00000400, CREATURE_STATIC_FLAG_2_INTERACT_WHILE_DEAD = 0x00000800, // CREATURE_TYPE_FLAG_INTERACT_WHILE_DEAD CREATURE_STATIC_FLAG_2_NO_INTERRUPT_SCHOOL_COOLDOWN = 0x00001000, diff --git a/src/server/game/Entities/Unit/Unit.cpp b/src/server/game/Entities/Unit/Unit.cpp index a11ba0931f..7ffe75b109 100644 --- a/src/server/game/Entities/Unit/Unit.cpp +++ b/src/server/game/Entities/Unit/Unit.cpp @@ -5530,23 +5530,32 @@ void Unit::ValidateAttackersAndOwnTarget() AttackStop(); } -void Unit::CombatStop(bool includingCast, bool mutualPvP) +void Unit::CombatStop(bool includingCast, bool mutualPvP, bool (*unitFilter)(Unit const* otherUnit)) { if (includingCast && IsNonMeleeSpellCast(false)) InterruptNonMeleeSpells(false); AttackStop(); - RemoveAllAttackers(); + + if (!unitFilter) + RemoveAllAttackers(); + else + { + std::vector attackersToRemove; + attackersToRemove.reserve(m_attackers.size()); + std::copy_if(m_attackers.begin(), m_attackers.end(), std::back_inserter(attackersToRemove), unitFilter); + + for (Unit* attacker : attackersToRemove) + attacker->AttackStop(); + } if (GetTypeId() == TYPEID_PLAYER) ToPlayer()->SendAttackSwingCancelAttack(); // melee and ranged forced attack cancel + m_combatManager.EndAllPvECombat(unitFilter); if (mutualPvP) - ClearInCombat(); - else - { // vanish and brethren are weird - m_combatManager.EndAllPvECombat(); - m_combatManager.SuppressPvPCombat(); - } + m_combatManager.EndAllPvPCombat(unitFilter); + else // vanish and brethren are weird + m_combatManager.SuppressPvPCombat(unitFilter); } void Unit::CombatStopWithPets(bool includingCast) diff --git a/src/server/game/Entities/Unit/Unit.h b/src/server/game/Entities/Unit/Unit.h index 68b6878468..f40bd32928 100644 --- a/src/server/game/Entities/Unit/Unit.h +++ b/src/server/game/Entities/Unit/Unit.h @@ -865,7 +865,7 @@ class TC_GAME_API Unit : public WorldObject } void ValidateAttackersAndOwnTarget(); - void CombatStop(bool includingCast = false, bool mutualPvP = true); + void CombatStop(bool includingCast = false, bool mutualPvP = true, bool (*unitFilter)(Unit const* otherUnit) = nullptr); void CombatStopWithPets(bool includingCast = false); void StopAttackFaction(uint32 faction_id); Unit* SelectNearbyTarget(Unit* exclude = nullptr, float dist = NOMINAL_MELEE_RANGE) const; diff --git a/src/server/game/Spells/SpellEffects.cpp b/src/server/game/Spells/SpellEffects.cpp index ed99978beb..b3e079072d 100644 --- a/src/server/game/Spells/SpellEffects.cpp +++ b/src/server/game/Spells/SpellEffects.cpp @@ -3573,16 +3573,23 @@ void Spell::EffectSanctuary(SpellEffIndex /*effIndex*/) if (!unitTarget) return; + auto isAffectedBySanctuary = [](Unit const* attacker) + { + Creature const* attackerCreature = attacker->ToCreature(); + return !attackerCreature || !attackerCreature->IsIgnoringSanctuarySpellEffect(); + }; + if (unitTarget->GetTypeId() == TYPEID_PLAYER && !unitTarget->GetMap()->IsDungeon()) { // stop all pve combat for players outside dungeons, suppress pvp combat - unitTarget->CombatStop(false, false); + unitTarget->CombatStop(false, false, isAffectedBySanctuary); } else { // in dungeons (or for nonplayers), reset this unit on all enemies' threat lists - for (auto const& pair : unitTarget->GetThreatManager().GetThreatenedByMeList()) - pair.second->ScaleThreat(0.0f); + for (auto const& [guid, ref] : unitTarget->GetThreatManager().GetThreatenedByMeList()) + if (isAffectedBySanctuary(ref->GetOwner())) + ref->ScaleThreat(0.0f); } // makes spells cast before this time fizzle