diff options
-rw-r--r-- | src/server/game/Combat/CombatManager.cpp | 35 | ||||
-rw-r--r-- | src/server/game/Combat/CombatManager.h | 11 | ||||
-rw-r--r-- | src/server/game/Combat/ThreatManager.cpp | 13 | ||||
-rw-r--r-- | src/server/game/Combat/ThreatManager.h | 2 | ||||
-rw-r--r-- | src/server/game/Entities/Creature/Creature.h | 2 | ||||
-rw-r--r-- | src/server/game/Entities/Creature/CreatureData.h | 2 | ||||
-rw-r--r-- | src/server/game/Entities/Unit/Unit.cpp | 25 | ||||
-rw-r--r-- | src/server/game/Entities/Unit/Unit.h | 2 | ||||
-rw-r--r-- | src/server/game/Spells/SpellEffects.cpp | 13 |
9 files changed, 72 insertions, 33 deletions
diff --git a/src/server/game/Combat/CombatManager.cpp b/src/server/game/Combat/CombatManager.cpp index 721b8d5c3f5..18e084f8e0d 100644 --- a/src/server/game/Combat/CombatManager.cpp +++ b/src/server/game/Combat/CombatManager.cpp @@ -295,22 +295,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<CombatReference*> 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() @@ -342,10 +351,16 @@ void CombatManager::RevalidateCombat() } } -void CombatManager::EndAllPvPCombat() +void CombatManager::EndAllPvPCombat(UnitFilter* unitFilter /*= nullptr*/) { - while (!_pvpRefs.empty()) - _pvpRefs.begin()->second->EndCombat(); + std::vector<CombatReference*> 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) diff --git a/src/server/game/Combat/CombatManager.h b/src/server/game/Combat/CombatManager.h index 1dfd926fa0f..d44aff87e1c 100644 --- a/src/server/game/Combat/CombatManager.h +++ b/src/server/game/Combat/CombatManager.h @@ -120,12 +120,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 9376d42b010..04ff7e36da1 100644 --- a/src/server/game/Combat/ThreatManager.cpp +++ b/src/server/game/Combat/ThreatManager.cpp @@ -743,13 +743,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<ThreatReference*> 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 b78a2e59b37..8681d6af9d1 100644 --- a/src/server/game/Combat/ThreatManager.h +++ b/src/server/game/Combat/ThreatManager.h @@ -165,7 +165,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 723b58afeb4..237b5f45310 100644 --- a/src/server/game/Entities/Creature/Creature.h +++ b/src/server/game/Entities/Creature/Creature.h @@ -320,6 +320,8 @@ class TC_GAME_API Creature : public Unit, public GridObject<Creature>, public Ma bool CanAssistTo(Unit const* u, Unit const* enemy, bool checkfaction = true) const; bool _IsTargetAcceptable(Unit const* 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); } void RemoveCorpse(bool setSpawnTime = true, bool destroyForNearbyPlayers = true); diff --git a/src/server/game/Entities/Creature/CreatureData.h b/src/server/game/Entities/Creature/CreatureData.h index 79cdc6bdb8d..1e386ff2da5 100644 --- a/src/server/game/Entities/Creature/CreatureData.h +++ b/src/server/game/Entities/Creature/CreatureData.h @@ -82,7 +82,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 8639c8e458b..ad8ebc33e94 100644 --- a/src/server/game/Entities/Unit/Unit.cpp +++ b/src/server/game/Entities/Unit/Unit.cpp @@ -5822,23 +5822,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<Unit*> 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 47be2b81751..0c17b38a2fe 100644 --- a/src/server/game/Entities/Unit/Unit.h +++ b/src/server/game/Entities/Unit/Unit.h @@ -716,7 +716,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 72d0271d656..8c694c29f89 100644 --- a/src/server/game/Spells/SpellEffects.cpp +++ b/src/server/game/Spells/SpellEffects.cpp @@ -3130,16 +3130,23 @@ void Spell::EffectSanctuary() 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 |