From 66e0f98fcabc4af17ad7e60eba9be3878b31e2e5 Mon Sep 17 00:00:00 2001 From: hater Date: Mon, 22 Jan 2024 16:01:14 -0500 Subject: [PATCH 1/2] threat rework --- src/server/game/AI/CoreAI/GuardAI.cpp | 4 +- src/server/game/AI/CoreAI/PetAI.cpp | 15 +- src/server/game/AI/CoreAI/UnitAI.cpp | 18 +- src/server/game/AI/CoreAI/UnitAI.h | 8 +- src/server/game/AI/CreatureAI.cpp | 62 +- .../game/AI/ScriptedAI/ScriptedCreature.cpp | 18 +- .../game/AI/ScriptedAI/ScriptedEscortAI.cpp | 2 +- .../game/AI/ScriptedAI/ScriptedFollowerAI.cpp | 4 +- .../game/AI/SmartScripts/SmartScript.cpp | 38 +- .../game/Battlegrounds/Battleground.cpp | 3 - src/server/game/Combat/CombatManager.cpp | 351 ++ src/server/game/Combat/CombatManager.h | 143 + src/server/game/Combat/HostileRefMgr.cpp | 250 -- src/server/game/Combat/HostileRefMgr.h | 75 - src/server/game/Combat/ThreatMgr.cpp | 1018 +++--- src/server/game/Combat/ThreatMgr.h | 483 ++- src/server/game/Combat/UnitEvents.h | 134 - .../game/Entities/Creature/Creature.cpp | 163 +- src/server/game/Entities/Creature/Creature.h | 17 +- src/server/game/Entities/Object/Object.h | 3 +- src/server/game/Entities/Pet/Pet.cpp | 2 + src/server/game/Entities/Player/Player.cpp | 15 +- src/server/game/Entities/Player/Player.h | 2 + .../game/Entities/Player/PlayerMisc.cpp | 20 + .../game/Entities/Player/PlayerUpdates.cpp | 15 +- src/server/game/Entities/Unit/Unit.cpp | 865 +---- src/server/game/Entities/Unit/Unit.h | 139 +- src/server/game/Handlers/CharacterHandler.cpp | 2 +- src/server/game/Handlers/MovementHandler.cpp | 3 - src/server/game/Handlers/PetHandler.cpp | 3 - src/server/game/Maps/Map.cpp | 20 +- .../WaypointMovementGenerator.cpp | 3 +- src/server/game/Server/WorldSession.cpp | 2 +- .../game/Spells/Auras/SpellAuraDefines.h | 2 +- .../game/Spells/Auras/SpellAuraEffects.cpp | 77 +- .../game/Spells/Auras/SpellAuraEffects.h | 1 + src/server/game/Spells/Spell.cpp | 37 +- src/server/game/Spells/SpellEffects.cpp | 2865 ++++++++--------- src/server/game/Spells/SpellInfo.cpp | 7 +- src/server/game/Spells/SpellInfo.h | 2 + src/server/scripts/Commands/cs_debug.cpp | 108 +- src/server/scripts/Commands/cs_misc.cpp | 1 - .../BlackrockDepths/boss_tomb_of_seven.cpp | 2 +- .../instance_blackrock_depths.cpp | 2 +- .../BlackrockSpire/boss_drakkisath.cpp | 6 +- .../BlackrockSpire/boss_mor_grayhoof.cpp | 6 +- .../instance_blackrock_spire.cpp | 4 +- .../BlackwingLair/boss_nefarian.cpp | 6 +- .../BlackwingLair/boss_razorgore.cpp | 7 +- .../MoltenCore/boss_majordomo_executus.cpp | 2 +- .../MoltenCore/boss_ragnaros.cpp | 2 +- .../MoltenCore/boss_shazzrah.cpp | 4 +- .../EasternKingdoms/Karazhan/boss_curator.cpp | 2 +- .../Karazhan/boss_midnight.cpp | 32 +- .../Karazhan/boss_netherspite.cpp | 2 +- .../Karazhan/boss_nightbane.cpp | 6 +- .../Karazhan/boss_prince_malchezaar.cpp | 2 +- .../Karazhan/boss_shade_of_aran.cpp | 2 +- .../EasternKingdoms/Karazhan/bosses_opera.cpp | 2 +- .../Karazhan/instance_karazhan.cpp | 2 +- .../boss_priestess_delrissa.cpp | 22 +- .../ScarletEnclave/chapter2.cpp | 4 +- .../ScarletEnclave/chapter5.cpp | 4 +- .../Scholomance/boss_darkmaster_gandling.cpp | 4 +- .../Scholomance/instance_scholomance.cpp | 45 +- .../SunwellPlateau/boss_eredar_twins.cpp | 2 +- .../SunwellPlateau/boss_kiljaeden.cpp | 2 +- .../EasternKingdoms/ZulAman/boss_akilzon.cpp | 2 +- .../EasternKingdoms/ZulAman/boss_hexlord.cpp | 6 +- .../ZulGurub/boss_gahzranka.cpp | 4 +- .../EasternKingdoms/ZulGurub/boss_grilek.cpp | 4 +- .../EasternKingdoms/ZulGurub/boss_hakkar.cpp | 6 +- .../ZulGurub/boss_hazzarah.cpp | 18 - .../EasternKingdoms/ZulGurub/boss_jeklik.cpp | 2 +- .../EasternKingdoms/ZulGurub/boss_jindo.cpp | 11 +- .../ZulGurub/boss_mandokir.cpp | 8 +- .../EasternKingdoms/ZulGurub/boss_marli.cpp | 2 +- .../ZulGurub/boss_renataki.cpp | 13 +- .../zone_isle_of_queldanas.cpp | 2 +- .../zone_silverpine_forest.cpp | 2 +- .../EasternKingdoms/zone_undercity.cpp | 50 +- .../scripts/EasternKingdoms/zone_westfall.cpp | 2 +- .../scripts/EasternKingdoms/zone_wetlands.cpp | 2 +- .../BattleForMountHyjal/boss_anetheron.cpp | 2 +- .../BattleForMountHyjal/boss_archimonde.cpp | 17 +- .../BattleForMountHyjal/boss_azgalor.cpp | 2 +- .../BattleForMountHyjal/boss_kazrogal.cpp | 2 +- .../boss_rage_winterchill.cpp | 2 +- .../BattleForMountHyjal/hyjalAI.cpp | 2 +- .../BattleForMountHyjal/hyjal_trash.cpp | 32 +- .../culling_of_stratholme.cpp | 12 +- .../Kalimdor/RuinsOfAhnQiraj/boss_buru.cpp | 4 +- .../Kalimdor/RuinsOfAhnQiraj/boss_rajaxx.cpp | 2 +- .../TempleOfAhnQiraj/boss_bug_trio.cpp | 4 +- .../Kalimdor/TempleOfAhnQiraj/boss_ouro.cpp | 6 +- .../TempleOfAhnQiraj/boss_sartura.cpp | 20 +- .../TempleOfAhnQiraj/boss_twinemperors.cpp | 4 +- src/server/scripts/Kalimdor/boss_azuregos.cpp | 2 +- src/server/scripts/Kalimdor/zone_durotar.cpp | 2 +- .../scripts/Kalimdor/zone_moonglade.cpp | 2 +- .../scripts/Kalimdor/zone_the_barrens.cpp | 2 +- .../ahnkahet/boss_herald_volazj.cpp | 2 +- .../ahnkahet/boss_prince_taldaram.cpp | 16 +- .../RubySanctum/boss_halion.cpp | 4 +- .../boss_argent_challenge.cpp | 4 +- .../TrialOfTheChampion/boss_black_knight.cpp | 2 +- .../boss_grand_champions.cpp | 15 +- .../boss_anubarak_trial.cpp | 2 +- .../boss_faction_champions.cpp | 26 +- .../TrialOfTheCrusader/boss_lord_jaraxxus.cpp | 2 +- .../boss_northrend_beasts.cpp | 2 +- .../DraktharonKeep/boss_tharon_ja.cpp | 4 +- .../ForgeOfSouls/boss_devourer_of_souls.cpp | 2 +- .../HallsOfReflection/halls_of_reflection.cpp | 4 +- .../instance_halls_of_reflection.cpp | 8 +- .../FrozenHalls/PitOfSaron/pit_of_saron.cpp | 6 +- .../scripts/Northrend/Gundrak/boss_eck.cpp | 4 +- .../boss_blood_prince_council.cpp | 12 +- .../boss_blood_queen_lana_thel.cpp | 2 +- .../boss_icecrown_gunship_battle.cpp | 30 +- .../boss_lady_deathwhisper.cpp | 12 +- .../boss_professor_putricide.cpp | 4 +- .../IcecrownCitadel/boss_rotface.cpp | 4 +- .../IcecrownCitadel/boss_sindragosa.cpp | 4 +- .../IcecrownCitadel/boss_the_lich_king.cpp | 10 +- .../boss_valithria_dreamwalker.cpp | 11 +- .../IcecrownCitadel/icecrown_citadel.cpp | 20 +- .../instance_icecrown_citadel.cpp | 2 +- .../Northrend/Naxxramas/boss_gothik.cpp | 4 +- .../Northrend/Naxxramas/boss_kelthuzad.cpp | 14 +- .../Northrend/Naxxramas/boss_patchwerk.cpp | 8 +- .../Northrend/Naxxramas/boss_sapphiron.cpp | 12 +- .../Northrend/Naxxramas/boss_thaddius.cpp | 14 +- .../Nexus/EyeOfEternity/boss_malygos.cpp | 2 +- .../Northrend/Nexus/Nexus/instance_nexus.cpp | 2 +- .../Northrend/Nexus/Oculus/boss_urom.cpp | 2 +- .../Ulduar/HallsOfLightning/boss_ionar.cpp | 2 +- .../Ulduar/HallsOfStone/brann_bronzebeard.cpp | 2 +- .../Ulduar/boss_algalon_the_observer.cpp | 2 +- .../Ulduar/Ulduar/boss_flame_leviathan.cpp | 4 +- .../Northrend/Ulduar/Ulduar/boss_ignis.cpp | 2 +- .../Northrend/Ulduar/Ulduar/boss_kologarn.cpp | 2 +- .../Northrend/Ulduar/Ulduar/boss_thorim.cpp | 6 +- .../Ulduar/Ulduar/boss_yoggsaron.cpp | 2 +- .../UtgardeKeep/UtgardeKeep/boss_keleseth.cpp | 2 +- .../UtgardeKeep/boss_skarvald_dalronn.cpp | 10 +- .../UtgardePinnacle/boss_svala.cpp | 2 +- .../VaultOfArchavon/boss_toravon.cpp | 4 +- .../Northrend/VioletHold/violet_hold.cpp | 2 +- .../scripts/Northrend/zone_borean_tundra.cpp | 2 +- .../scripts/Northrend/zone_dragonblight.cpp | 2 +- .../scripts/Northrend/zone_sholazar_basin.cpp | 2 +- .../SethekkHalls/boss_talon_king_ikiss.cpp | 2 +- .../boss_blackheart_the_inciter.cpp | 6 +- .../ShadowLabyrinth/boss_murmur.cpp | 2 +- .../Outland/BlackTemple/boss_bloodboil.cpp | 2 +- .../Outland/BlackTemple/boss_illidan.cpp | 10 +- .../BlackTemple/boss_shade_of_akama.cpp | 4 +- .../Outland/BlackTemple/boss_supremus.cpp | 8 +- .../BlackTemple/boss_teron_gorefiend.cpp | 2 +- .../BlackTemple/instance_black_temple.cpp | 6 +- .../boss_leotheras_the_blind.cpp | 4 +- .../SerpentShrine/boss_lurker_below.cpp | 6 +- .../boss_morogrim_tidewalker.cpp | 4 +- .../ShatteredHalls/boss_warbringer_omrogg.cpp | 4 +- .../Outland/TempestKeep/Eye/boss_alar.cpp | 6 +- .../Outland/TempestKeep/Eye/boss_kaelthas.cpp | 6 +- .../Mechanar/boss_nethermancer_sepethrea.cpp | 13 +- .../botanica/instance_the_botanica.cpp | 6 +- .../Outland/zone_blades_edge_mountains.cpp | 1 - .../Outland/zone_hellfire_peninsula.cpp | 4 +- .../scripts/Outland/zone_netherstorm.cpp | 6 +- .../Outland/zone_shadowmoon_valley.cpp | 2 +- .../scripts/Outland/zone_terokkar_forest.cpp | 2 +- src/server/scripts/Pet/pet_hunter.cpp | 8 +- src/server/scripts/Pet/pet_mage.cpp | 14 +- src/server/scripts/Spells/spell_generic.cpp | 4 +- src/server/scripts/Spells/spell_hunter.cpp | 15 +- src/server/scripts/Spells/spell_priest.cpp | 38 - src/server/scripts/Spells/spell_rogue.cpp | 26 +- src/server/scripts/Spells/spell_shaman.cpp | 2 +- src/server/scripts/Spells/spell_warlock.cpp | 2 +- src/server/scripts/Spells/spell_warrior.cpp | 14 +- .../scripts/World/boss_emerald_dragons.cpp | 4 +- .../scripts/World/npc_stave_of_ancients.cpp | 16 +- .../scripts/World/npc_stave_of_ancients.h | 3 +- 186 files changed, 3762 insertions(+), 4237 deletions(-) create mode 100644 src/server/game/Combat/CombatManager.cpp create mode 100644 src/server/game/Combat/CombatManager.h delete mode 100644 src/server/game/Combat/HostileRefMgr.cpp delete mode 100644 src/server/game/Combat/HostileRefMgr.h delete mode 100644 src/server/game/Combat/UnitEvents.h diff --git a/src/server/game/AI/CoreAI/GuardAI.cpp b/src/server/game/AI/CoreAI/GuardAI.cpp index 86c6fc1f0bcb16..42ce9154982884 100644 --- a/src/server/game/AI/CoreAI/GuardAI.cpp +++ b/src/server/game/AI/CoreAI/GuardAI.cpp @@ -43,14 +43,14 @@ void GuardAI::EnterEvadeMode(EvadeReason /*why*/) { me->GetMotionMaster()->MoveIdle(); me->CombatStop(true); - me->GetThreatMgr().ClearAllThreat(); + me->GetThreatManager().ClearAllThreat(); return; } LOG_DEBUG("entities.unit", "Guard entry: {} enters evade mode.", me->GetEntry()); me->RemoveAllAuras(); - me->GetThreatMgr().ClearAllThreat(); + me->GetThreatManager().ClearAllThreat(); me->CombatStop(true); // Remove ChaseMovementGenerator from MotionMaster stack list, and add HomeMovementGenerator instead diff --git a/src/server/game/AI/CoreAI/PetAI.cpp b/src/server/game/AI/CoreAI/PetAI.cpp index 8e86d465851030..479ac095471487 100644 --- a/src/server/game/AI/CoreAI/PetAI.cpp +++ b/src/server/game/AI/CoreAI/PetAI.cpp @@ -76,7 +76,6 @@ void PetAI::_stopAttack() me->GetMotionMaster()->Clear(); me->GetMotionMaster()->MoveIdle(); me->CombatStop(); - me->getHostileRefMgr().deleteReferences(); return; } @@ -256,7 +255,7 @@ void PetAI::UpdateAI(uint32 diff) if (spellInfo->CanBeUsedInCombat()) { // Check if we're in combat or commanded to attack (exlude auras with infinity duration) - if (!me->IsInCombat() && spellInfo->GetMaxDuration() != -1 && !me->IsPetInCombat()) + if (!me->IsInCombat() && spellInfo->GetMaxDuration() != -1) { continue; } @@ -489,7 +488,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; } @@ -567,8 +566,8 @@ void PetAI::HandleReturnMovement() me->GetCharmInfo()->SetForcedTargetGUID(); // xinef: remember that npcs summoned by npcs can also be pets - me->GetThreatMgr().ClearAllThreat(); - me->ClearInPetCombat(); + me->GetThreatManager().ClearAllThreat(); + me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_PET_IN_COMBAT); // on player pets, this flag indicates that we're actively going after a target - we're returning, so remove it } void PetAI::SpellHit(Unit* caster, SpellInfo const* spellInfo) @@ -594,11 +593,7 @@ void PetAI::DoAttack(Unit* target, bool chase) if (me->Attack(target, true)) { - // xinef: properly fix fake combat after pet is sent to attack - if (Unit* owner = me->GetOwner()) - owner->SetUnitFlag(UNIT_FLAG_PET_IN_COMBAT); - - me->SetUnitFlag(UNIT_FLAG_PET_IN_COMBAT); + me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_PET_IN_COMBAT); // on player pets, this flag indicates we're actively going after a target - that's what we're doing, so set it // Play sound to let the player know the pet is attacking something it picked on its own if (me->HasReactState(REACT_AGGRESSIVE) && !me->GetCharmInfo()->IsCommandAttack()) diff --git a/src/server/game/AI/CoreAI/UnitAI.cpp b/src/server/game/AI/CoreAI/UnitAI.cpp index f57e7a2b290d99..87fe38b3559e61 100644 --- a/src/server/game/AI/CoreAI/UnitAI.cpp +++ b/src/server/game/AI/CoreAI/UnitAI.cpp @@ -135,10 +135,10 @@ 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) + auto list = me->GetThreatManager().GetUnsortedThreatList(); + for (auto item : list) { - if (Unit* unit = ObjectAccessor::GetUnit(*me, (*itr)->getUnitGuid())) + if (Unit* unit = ObjectAccessor::GetUnit(*me, item->GetVictim()->GetGUID())) { if (unit->GetTypeId() == TYPEID_PLAYER) { @@ -158,10 +158,10 @@ 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) + auto list = me->GetThreatManager().GetUnsortedThreatList(); + for (auto item : list) { - if (Unit* unit = ObjectAccessor::GetUnit(*me, (*itr)->getUnitGuid())) + if (Unit* unit = ObjectAccessor::GetUnit(*me, item->GetVictim()->GetGUID())) { if (unit->GetTypeId() == TYPEID_PLAYER) return me->CastSpell(unit, spellid, triggered); @@ -337,9 +337,9 @@ void UnitAI::FillAISpellInfo() } } -ThreatMgr& UnitAI::GetThreatMgr() +ThreatMgr& UnitAI::GetThreatManager() { - return me->GetThreatMgr(); + return me->GetThreatManager(); } void UnitAI::SortByDistance(std::list& list, bool ascending) @@ -427,7 +427,7 @@ bool NonTankTargetSelector::operator()(Unit const* target) const if (_playerOnly && target->GetTypeId() != TYPEID_PLAYER) return false; - if (Unit* currentVictim = _source->GetThreatMgr().GetCurrentVictim()) + if (Unit* currentVictim = _source->GetThreatManager().GetCurrentVictim()) return target != currentVictim; return target != _source->GetVictim(); diff --git a/src/server/game/AI/CoreAI/UnitAI.h b/src/server/game/AI/CoreAI/UnitAI.h index 48190137c5d359..d8f8a543bf37a9 100644 --- a/src/server/game/AI/CoreAI/UnitAI.h +++ b/src/server/game/AI/CoreAI/UnitAI.h @@ -52,7 +52,7 @@ struct DefaultTargetSelector : public Acore::unary_function // 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->GetThreatManager().GetCurrentVictim() : nullptr), m_playerOnly(playerOnly), m_aura(aura) {} bool operator()(Unit const* target) const { @@ -221,7 +221,7 @@ class UnitAI template Unit* SelectTarget(SelectTargetMethod targetType, uint32 position, PREDICATE const& predicate) { - ThreatMgr& mgr = GetThreatMgr(); + ThreatMgr& mgr = GetThreatManager(); // shortcut: if we ignore the first elements, and there are at most elements, then we ignore ALL elements if (mgr.GetThreatListSize() <= position) return nullptr; @@ -266,7 +266,7 @@ class UnitAI void SelectTargetList(std::list& targetList, uint32 num, SelectTargetMethod targetType, uint32 position, PREDICATE const& predicate) { targetList.clear(); - ThreatMgr& mgr = GetThreatMgr(); + ThreatMgr& mgr = GetThreatManager(); // shortcut: we're gonna ignore the first elements, and there's at most elements, so we ignore them all - nothing to do here if (mgr.GetThreatListSize() <= position) return; @@ -409,7 +409,7 @@ class UnitAI virtual std::string GetDebugInfo() const; private: - ThreatMgr& GetThreatMgr(); + ThreatMgr& GetThreatManager(); void SortByDistance(std::list& list, bool ascending = true); }; diff --git a/src/server/game/AI/CreatureAI.cpp b/src/server/game/AI/CreatureAI.cpp index bb014a03472869..ec83a0266046ac 100644 --- a/src/server/game/AI/CreatureAI.cpp +++ b/src/server/game/AI/CreatureAI.cpp @@ -105,10 +105,37 @@ void CreatureAI::DoZoneInCombat(Creature* creature /*= nullptr*/, float maxRange } Map* map = creature->GetMap(); - if (!map->IsDungeon()) //use IsDungeon instead of Instanceable, in case battlegrounds will be instantiated + if (creature->CanHaveThreatList()) { - LOG_ERROR("entities.unit.ai", "DoZoneInCombat call for map {} that isn't a dungeon (creature entry = {})", map->GetId(), creature->GetTypeId() == TYPEID_UNIT ? creature->ToCreature()->GetEntry() : 0); - return; + if (!map->IsDungeon()) //use IsDungeon instead of Instanceable, in case battlegrounds will be instantiated + { + LOG_ERROR("entities.unit.ai", "DoZoneInCombat call for map {} that isn't a dungeon (creature entry = {})", map->GetId(), creature->GetTypeId() == TYPEID_UNIT ? creature->ToCreature()->GetEntry() : 0); + return; + } + + if (!creature->HasReactState(REACT_PASSIVE) && !creature->GetVictim()) + { + if (Unit* nearTarget = creature->SelectNearestTarget(maxRangeToNearestTarget)) + creature->AI()->AttackStart(nearTarget); + else if (creature->IsSummon()) + { + if (Unit* summoner = creature->ToTempSummon()->GetSummoner()->ToUnit()) + { + Unit* target = summoner->getAttackerForHelper(); + if (!target && !summoner->GetThreatManager().IsThreatListEmpty()) + target = summoner->GetThreatManager().GetAnyTarget(); + if (target && (creature->IsFriendlyTo(summoner) || creature->IsHostileTo(target))) + creature->AI()->AttackStart(target); + } + } + } + // Intended duplicated check, the code above this should select a victim + // If it can't find a suitable attack target then we should error out. + if (!creature->HasReactState(REACT_PASSIVE) && !creature->GetVictim()) + { + LOG_ERROR("misc.dozoneincombat", "DoZoneInCombat called for creature that has empty threat list (creature entry = %u)", creature->GetEntry()); + return; + } } Map::PlayerList const& playerList = map->GetPlayers(); @@ -121,22 +148,19 @@ void CreatureAI::DoZoneInCombat(Creature* creature /*= nullptr*/, float maxRange { if (Player* player = itr->GetSource()) { - if (!IsValidCombatTarget(creature, player)) + if (player->IsAlive()) { - continue; - } + if (!IsValidCombatTarget(creature, player)) + { + continue; + } - if (!creature->IsWithinDistInMap(player, maxRangeToNearestTarget)) - { - continue; - } - - creature->SetInCombatWith(player); - player->SetInCombatWith(creature); + if (!creature->IsWithinDistInMap(player, maxRangeToNearestTarget)) + { + continue; + } - if (creature->CanHaveThreatList()) - { - creature->AddThreat(player, 0.0f); + creature->SetInCombatWith(player); } } } @@ -280,11 +304,13 @@ bool CreatureAI::UpdateVictim() // 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->GetThreatManager().GetThreatListSize() <= 1) { EnterEvadeMode(); return false; } + else + me->AttackStop(); return true; } @@ -302,7 +328,7 @@ bool CreatureAI::_EnterEvadeMode(EvadeReason /*why*/) me->ClearComboPointHolders(); // Remove all combo points targeting this unit // sometimes bosses stuck in combat? - me->GetThreatMgr().ClearAllThreat(); + me->GetThreatManager().ClearAllThreat(); me->CombatStop(true); me->LoadCreaturesAddon(true); me->SetLootRecipient(nullptr); diff --git a/src/server/game/AI/ScriptedAI/ScriptedCreature.cpp b/src/server/game/AI/ScriptedAI/ScriptedCreature.cpp index 3073939204b1bf..05af7dd476314f 100644 --- a/src/server/game/AI/ScriptedAI/ScriptedCreature.cpp +++ b/src/server/game/AI/ScriptedAI/ScriptedCreature.cpp @@ -416,7 +416,7 @@ void ScriptedAI::DoAddThreat(Unit* unit, float amount) if (!unit) return; - me->GetThreatMgr().AddThreat(unit, amount); + me->GetThreatManager().AddThreat(unit, amount); } void ScriptedAI::DoModifyThreatByPercent(Unit* unit, int32 pct) @@ -424,7 +424,7 @@ void ScriptedAI::DoModifyThreatByPercent(Unit* unit, int32 pct) if (!unit) return; - me->GetThreatMgr().ModifyThreatByPercent(unit, pct); + me->GetThreatManager().ModifyThreatByPercent(unit, pct); } void ScriptedAI::DoResetThreat(Unit* unit) @@ -432,18 +432,18 @@ void ScriptedAI::DoResetThreat(Unit* unit) if (!unit) return; - me->GetThreatMgr().ResetThreat(unit); + me->GetThreatManager().ResetThreat(unit); } void ScriptedAI::DoResetThreatList() { - if (!me->CanHaveThreatList() || me->GetThreatMgr().isThreatListEmpty()) + if (!me->CanHaveThreatList() || me->GetThreatManager().GetThreatListSize() > 0) { 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; } - me->GetThreatMgr().ResetAllThreat(); + me->GetThreatManager().ResetAllThreat(); } float ScriptedAI::DoGetThreat(Unit* unit) @@ -451,7 +451,7 @@ float ScriptedAI::DoGetThreat(Unit* unit) if (!unit) return 0.0f; - return me->GetThreatMgr().GetThreat(unit); + return me->GetThreatManager().GetThreat(unit); } void ScriptedAI::DoTeleportPlayer(Unit* unit, float x, float y, float z, float o) @@ -653,9 +653,9 @@ 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()) + auto list = me->GetThreatManager().GetUnsortedThreatList(); + for (auto item : list) + if (Unit* target = item->GetVictim()) if (target->GetTypeId() == TYPEID_PLAYER && !IsInBoundary(target)) target->NearTeleportTo(x, y, z, 0); } diff --git a/src/server/game/AI/ScriptedAI/ScriptedEscortAI.cpp b/src/server/game/AI/ScriptedAI/ScriptedEscortAI.cpp index 88e33c93cbaf80..df924ef4dd60e9 100644 --- a/src/server/game/AI/ScriptedAI/ScriptedEscortAI.cpp +++ b/src/server/game/AI/ScriptedAI/ScriptedEscortAI.cpp @@ -200,7 +200,7 @@ void npc_escortAI::ReturnToLastPoint() void npc_escortAI::EnterEvadeMode(EvadeReason /*why*/) { - me->GetThreatMgr().ClearAllThreat(); + me->GetThreatManager().ClearAllThreat(); me->CombatStop(true); me->SetLootRecipient(nullptr); diff --git a/src/server/game/AI/ScriptedAI/ScriptedFollowerAI.cpp b/src/server/game/AI/ScriptedAI/ScriptedFollowerAI.cpp index c53a0e6f1266fb..14aea7345ae7eb 100644 --- a/src/server/game/AI/ScriptedAI/ScriptedFollowerAI.cpp +++ b/src/server/game/AI/ScriptedAI/ScriptedFollowerAI.cpp @@ -48,7 +48,7 @@ void FollowerAI::AttackStart(Unit* who) if (me->Attack(who, true)) { // This is done in Unit::Attack function which wont bug npcs by not adding threat upon combat start... - //me->AddThreat(who, 0.0f); + //me->GetThreatManager().AddThreat(who, 0.0f); //me->SetInCombatWith(who); //who->SetInCombatWith(me); @@ -153,7 +153,7 @@ void FollowerAI::JustRespawned() void FollowerAI::EnterEvadeMode(EvadeReason /*why*/) { me->RemoveAllAuras(); - me->GetThreatMgr().ClearAllThreat(); + me->GetThreatManager().ClearAllThreat(); me->CombatStop(true); me->SetLootRecipient(nullptr); diff --git a/src/server/game/AI/SmartScripts/SmartScript.cpp b/src/server/game/AI/SmartScripts/SmartScript.cpp index 41e3f9ce3940e7..64bb4cc059968f 100644 --- a/src/server/game/AI/SmartScripts/SmartScript.cpp +++ b/src/server/game/AI/SmartScripts/SmartScript.cpp @@ -527,12 +527,12 @@ 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) + auto list = me->GetThreatManager().GetUnsortedThreatList(); + for (auto item : list) { - if (Unit* target = ObjectAccessor::GetUnit(*me, (*i)->getUnitGuid())) + if (Unit* target = ObjectAccessor::GetUnit(*me, item->GetVictim()->GetGUID())) { - me->GetThreatMgr().ModifyThreatByPercent(target, e.action.threatPCT.threatINC ? (int32)e.action.threatPCT.threatINC : -(int32)e.action.threatPCT.threatDEC); + me->GetThreatManager().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); } @@ -548,7 +548,7 @@ 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); + me->GetThreatManager().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); } @@ -610,7 +610,7 @@ void SmartScript::ProcessAction(SmartScriptHolder& e, Unit* unit, uint32 var0, u // If the threatlist is a singleton, cancel if (e.action.cast.castFlags & SMARTCAST_THREATLIST_NOT_SINGLE) - if (me->GetThreatMgr().GetThreatListSize() <= 1) + if (me->GetThreatManager().GetThreatListSize() <= 1) break; // If target does not use mana, skip @@ -1183,7 +1183,7 @@ void SmartScript::ProcessAction(SmartScriptHolder& e, Unit* unit, uint32 var0, u { me->SetInCombatWith(unit->ToPlayer()); unit->ToPlayer()->SetInCombatWith(me); - me->AddThreat(unit->ToPlayer(), 0.0f); + me->GetThreatManager().AddThreat(unit->ToUnit(), 0.f); } } else @@ -2452,9 +2452,12 @@ 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->GetThreatManager().AddThreat(target->ToUnit(), float(e.action.threatPCT.threatINC) - float(e.action.threatPCT.threatDEC), nullptr, true, true); break; } case SMART_ACTION_LOAD_EQUIPMENT: @@ -3569,9 +3572,9 @@ 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())) + auto list = me->GetThreatManager().GetUnsortedThreatList(); + for (auto item : list) + if (Unit* temp = ObjectAccessor::GetUnit(*me, item->GetVictim()->GetGUID())) // Xinef: added distance check if (e.target.threatList.maxDist == 0 || me->IsWithinCombatRange(temp, (float)e.target.threatList.maxDist)) targets.push_back(temp); @@ -4463,10 +4466,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) - { - if (Unit* target = ObjectAccessor::GetUnit(*me, (*i)->getUnitGuid())) + auto list = me->GetThreatManager().GetUnsortedThreatList(); + for (auto item : list) { + if (Unit* target = ObjectAccessor::GetUnit(*me, item->GetVictim()->GetGUID())) { if (!target || !IsPlayer(target) || !target->IsNonMeleeSpellCast(false, false, true)) continue; @@ -4491,10 +4493,10 @@ 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) + auto list = me->GetThreatManager().GetUnsortedThreatList(); + for (auto item : list) { - if (Unit* target = ObjectAccessor::GetUnit(*me, (*i)->getUnitGuid())) + if (Unit* target = ObjectAccessor::GetUnit(*me, item->GetVictim()->GetGUID())) { if (!(me->IsInRange(target, (float)e.event.minMaxRepeat.rangeMin, (float)e.event.minMaxRepeat.rangeMax))) continue; diff --git a/src/server/game/Battlegrounds/Battleground.cpp b/src/server/game/Battlegrounds/Battleground.cpp index a315ba93514782..645d003f35984e 100644 --- a/src/server/game/Battlegrounds/Battleground.cpp +++ b/src/server/game/Battlegrounds/Battleground.cpp @@ -844,11 +844,8 @@ void Battleground::EndBattleground(PvPTeamId winnerTeamId) player->SpawnCorpseBones(); } else - { //needed cause else in av some creatures will kill the players at the end player->CombatStop(); - player->getHostileRefMgr().deleteReferences(); - } uint32 winner_kills = player->GetRandomWinner() ? sWorld->getIntConfig(CONFIG_BG_REWARD_WINNER_HONOR_LAST) : sWorld->getIntConfig(CONFIG_BG_REWARD_WINNER_HONOR_FIRST); uint32 loser_kills = player->GetRandomWinner() ? sWorld->getIntConfig(CONFIG_BG_REWARD_LOSER_HONOR_LAST) : sWorld->getIntConfig(CONFIG_BG_REWARD_LOSER_HONOR_FIRST); diff --git a/src/server/game/Combat/CombatManager.cpp b/src/server/game/Combat/CombatManager.cpp new file mode 100644 index 00000000000000..9303a494a784b4 --- /dev/null +++ b/src/server/game/Combat/CombatManager.cpp @@ -0,0 +1,351 @@ +/* + * Copyright (C) 2008-2018 TrinityCore + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see . + */ + +#include "CombatManager.h" +#include "Creature.h" +#include "Unit.h" +#include "CreatureAI.h" +#include "Player.h" + +/*static*/ bool CombatManager::CanBeginCombat(Unit const* a, Unit const* b) +{ + // Checks combat validity before initial reference creation. + // For the combat to be valid... + // ...the two units need to be different + if (a == b) + return false; + // ...the two units need to be in the world + if (!a->IsInWorld() || !b->IsInWorld()) + return false; + // ...the two units need to both be alive + if (!a->IsAlive() || !b->IsAlive()) + return false; + // ...the two units need to be on the same map + if (a->GetMap() != b->GetMap()) + return false; + // ...the two units need to be in the same phase + if (!WorldObject::InSamePhase(a, b)) + return false; + if (a->HasUnitState(UNIT_STATE_EVADE) || b->HasUnitState(UNIT_STATE_EVADE)) + return false; + if (a->HasUnitState(UNIT_STATE_IN_FLIGHT) || b->HasUnitState(UNIT_STATE_IN_FLIGHT)) + return false; + if (a->IsControlledByPlayer() || b->IsControlledByPlayer()) + { // PvSomething, only block friendly fire + if (a->IsFriendlyTo(b) || b->IsFriendlyTo(a)) + return false; + } + else + { // CvC, need hostile reaction to start a fight + if (!a->IsHostileTo(b) && !b->IsHostileTo(a)) + return false; + } + Player const* playerA = a->GetCharmerOrOwnerPlayerOrPlayerItself(); + Player const* playerB = b->GetCharmerOrOwnerPlayerOrPlayerItself(); + // ...neither of the two units must be (owned by) a player with .gm on + if ((playerA && playerA->IsGameMaster()) || (playerB && playerB->IsGameMaster())) + return false; + return true; +} + +void CombatReference::EndCombat() +{ + // sequencing matters here - AI might do nasty stuff, so make sure refs are in a consistent state before you hand off! + + // first, get rid of any threat that still exists... + first->GetThreatManager().ClearThreat(second); + second->GetThreatManager().ClearThreat(first); + + // ...then, remove the references from both managers... + first->GetCombatManager().PurgeReference(second->GetGUID(), _isPvP); + second->GetCombatManager().PurgeReference(first->GetGUID(), _isPvP); + + // ...update the combat state, which will potentially remove IN_COMBAT... + bool const needFirstAI = first->GetCombatManager().UpdateOwnerCombatState(); + bool const needSecondAI = second->GetCombatManager().UpdateOwnerCombatState(); + + // ...and if that happened, also notify the AI of it... + if (needFirstAI && first->IsAIEnabled) + first->GetAI()->JustExitedCombat(); + if (needSecondAI && second->IsAIEnabled) + second->GetAI()->JustExitedCombat(); + + // ...and finally clean up the reference object + delete this; +} + +bool PvPCombatReference::Update(uint32 tdiff) +{ + if (_combatTimer <= tdiff) + return false; + _combatTimer -= tdiff; + return true; +} + +void PvPCombatReference::Refresh() +{ + _combatTimer = PVP_COMBAT_TIMEOUT; + + bool needFirstAI = false, needSecondAI = false; + if (_suppressFirst) + { + _suppressFirst = false; + needFirstAI = first->GetCombatManager().UpdateOwnerCombatState(); + } + if (_suppressSecond) + { + _suppressSecond = false; + needSecondAI = second->GetCombatManager().UpdateOwnerCombatState(); + } + + if (needFirstAI) + CombatManager::NotifyAICombat(first, second); + if (needSecondAI) + CombatManager::NotifyAICombat(second, first); +} + +void PvPCombatReference::SuppressFor(Unit* who) +{ + Suppress(who); + if (who->GetCombatManager().UpdateOwnerCombatState()) + if (who->IsAIEnabled) + who->GetAI()->JustExitedCombat(); +} + +void CombatManager::Update(uint32 tdiff) +{ + auto it = _pvpRefs.begin(), end = _pvpRefs.end(); + while (it != end) + { + PvPCombatReference* const ref = it->second; + if (ref->first == _owner && !ref->Update(tdiff)) // only update if we're the first unit involved (otherwise double decrement) + { + it = _pvpRefs.erase(it), end = _pvpRefs.end(); // remove it from our refs first to prevent invalidation + ref->EndCombat(); // this will remove it from the other side + } + else + ++it; + } +} + +bool CombatManager::HasPvPCombat() const +{ + for (auto const& pair : _pvpRefs) + if (!pair.second->IsSuppressedFor(_owner)) + return true; + return false; +} + +Unit* CombatManager::GetAnyTarget() const +{ + if (!_pveRefs.empty()) + return _pveRefs.begin()->second->GetOther(_owner); + for (auto const& pair : _pvpRefs) + if (!pair.second->IsSuppressedFor(_owner)) + return pair.second->GetOther(_owner); + return nullptr; +} + +bool CombatManager::SetInCombatWith(Unit* who) +{ + // Are we already in combat? If yes, refresh pvp combat + auto it = _pvpRefs.find(who->GetGUID()); + if (it != _pvpRefs.end()) + { + it->second->Refresh(); + return true; + } + else if (_pveRefs.find(who->GetGUID()) != _pveRefs.end()) + return true; + + // Otherwise, check validity... + if (!CombatManager::CanBeginCombat(_owner, who)) + return false; + + // ...then create new reference + CombatReference* ref; + if (_owner->IsControlledByPlayer() && who->IsControlledByPlayer()) + ref = new PvPCombatReference(_owner, who); + else + ref = new CombatReference(_owner, who); + + // ...and insert it into both managers + PutReference(who->GetGUID(), ref); + who->GetCombatManager().PutReference(_owner->GetGUID(), ref); + + // now, sequencing is important - first we update the combat state, which will set both units in combat and do non-AI combat start stuff + bool const needSelfAI = UpdateOwnerCombatState(); + bool const needOtherAI = who->GetCombatManager().UpdateOwnerCombatState(); + + // then, we finally notify the AI (if necessary) and let it safely do whatever it feels like + if (needSelfAI) + NotifyAICombat(_owner, who); + if (needOtherAI) + NotifyAICombat(who, _owner); + return true; +} + +bool CombatManager::IsInCombatWith(ObjectGuid const& guid) const +{ + return (_pveRefs.find(guid) != _pveRefs.end()) || (_pvpRefs.find(guid) != _pvpRefs.end()); +} + +bool CombatManager::IsInCombatWith(Unit const* who) const +{ + return IsInCombatWith(who->GetGUID()); +} + +void CombatManager::InheritCombatStatesFrom(Unit const* who) +{ + CombatManager const& mgr = who->GetCombatManager(); + for (auto& ref : mgr._pveRefs) + { + if (!IsInCombatWith(ref.first)) + { + Unit* target = ref.second->GetOther(who); + if ((_owner->IsImmuneToPC() && target->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_PVP)) || + (_owner->IsImmuneToNPC() && !target->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_PVP))) + continue; + SetInCombatWith(target); + } + } + for (auto& ref : mgr._pvpRefs) + { + if (!IsInCombatWith(ref.first)) + { + Unit* target = ref.second->GetOther(who); + if ((_owner->IsImmuneToPC() && target->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_PVP)) || + (_owner->IsImmuneToNPC() && !target->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_PVP))) + continue; + SetInCombatWith(target); + } + } +} + +void CombatManager::EndCombatBeyondRange(float range, bool includingPvP) +{ + auto it = _pveRefs.begin(), end = _pveRefs.end(); + while (it != end) + { + CombatReference* const ref = it->second; + if (!ref->first->IsWithinDistInMap(ref->second, range)) + { + it = _pveRefs.erase(it), end = _pveRefs.end(); // erase manually here to avoid iterator invalidation + ref->EndCombat(); + } + else + ++it; + } + + if (!includingPvP) + return; + + auto it2 = _pvpRefs.begin(), end2 = _pvpRefs.end(); + while (it2 != end2) + { + CombatReference* const ref = it2->second; + if (!ref->first->IsWithinDistInMap(ref->second, range)) + { + it2 = _pvpRefs.erase(it2), end2 = _pvpRefs.end(); // erase manually here to avoid iterator invalidation + ref->EndCombat(); + } + else + ++it2; + } +} + +void CombatManager::SuppressPvPCombat() +{ + for (auto const& pair : _pvpRefs) + pair.second->Suppress(_owner); + if (UpdateOwnerCombatState()) + if (_owner->IsAIEnabled) + _owner->GetAI()->JustExitedCombat(); +} + +void CombatManager::EndAllPvECombat() +{ + // cannot have threat without combat + _owner->GetThreatManager().RemoveMeFromThreatLists(); + _owner->GetThreatManager().ClearAllThreat(); + while (!_pveRefs.empty()) + _pveRefs.begin()->second->EndCombat(); +} + +void CombatManager::EndAllPvPCombat() +{ + while (!_pvpRefs.empty()) + _pvpRefs.begin()->second->EndCombat(); +} + +/*static*/ void CombatManager::NotifyAICombat(Unit* me, Unit* other) +{ + if (!me->IsAIEnabled) + return; + me->GetAI()->JustEnteredCombat(other); + + if (Creature* cMe = me->ToCreature()) + if (!cMe->CanHaveThreatList()) + cMe->AI()->JustEngagedWith(other); +} + +void CombatManager::PutReference(ObjectGuid const& guid, CombatReference* ref) +{ + if (ref->_isPvP) + { + auto& inMap = _pvpRefs[guid]; + ASSERT(!inMap && "Duplicate combat state detected - memory leak!"); + inMap = static_cast(ref); + } + else + { + auto& inMap = _pveRefs[guid]; + ASSERT(!inMap && "Duplicate combat state detected - memory leak!"); + inMap = ref; + } +} + +void CombatManager::PurgeReference(ObjectGuid const& guid, bool pvp) +{ + if (pvp) + _pvpRefs.erase(guid); + else + _pveRefs.erase(guid); +} + +bool CombatManager::UpdateOwnerCombatState() const +{ + bool const combatState = HasCombat(); + if (combatState == _owner->IsInCombat()) + return false; + + if (combatState) + { + _owner->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_IN_COMBAT); + _owner->AtEnterCombat(); + } + else + { + _owner->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_IN_COMBAT); + _owner->AtExitCombat(); + } + + if (Unit* master = _owner->GetCharmerOrOwner()) + master->UpdatePetCombatState(); + + return true; +} diff --git a/src/server/game/Combat/CombatManager.h b/src/server/game/Combat/CombatManager.h new file mode 100644 index 00000000000000..380db4e3682c94 --- /dev/null +++ b/src/server/game/Combat/CombatManager.h @@ -0,0 +1,143 @@ +/* + * Copyright (C) 2008-2018 TrinityCore + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see . + */ + +#ifndef TRINITY_COMBATMANAGER_H +#define TRINITY_COMBATMANAGER_H + +#include "Common.h" +#include "ObjectGuid.h" +#include + +class Unit; + +/********************************************************************************************************************************************************\ + * DEV DOCUMENTATION: COMBAT SYSTEM * + * (future devs: please keep this up-to-date if you change the system) * + * CombatManager maintains a list of dynamically allocated CombatReference entries. Each entry represents a combat state between two distinct units. * + * A unit is "in combat" iff it has one or more non-suppressed CombatReference entries in its CombatManager. No exceptions. * + * - Note: Only PvP combat references can be suppressed, and only because Vanish is a very silly spell. Sue Blizzard. * + * * + * A CombatReference object carries the following implicit guarantees by existing: * + * - Both CombatReference.first and CombatReference.second are valid Units, distinct, not nullptr and currently in the world. * + * - If the CombatReference was retrieved from the CombatManager of Unit* A, then exactly one of .first and .second is equal to A. * + * - Note: Use CombatReference::GetOther to quickly get the other unit for a given reference. * + * - Both .first and .second are currently in combat (IsInCombat will always be true) if either of the following hold: * + * - The reference is a PvE combat reference (_isPvP is false) * + * - IsSuppressedFor returns false for the respective unit * + * * + * To end combat between two units, find their CombatReference and call EndCombat. * + * - Keep in mind that this modifies the CombatRefs maps on both ends, which may cause iterators to be invalidated. * + * * + * To put two units in combat with each other, call SetInCombatWith. Note that this is not guaranteed to succeed. * + * - The return value of SetInCombatWith is the new combat state between the units (identical to calling IsInCombatWith at that time). * + * * + * Note that (threat => combat) is a strong guarantee provided in conjunction with ThreatManager. Thus: * + * - Ending combat between two units will also delete any threat references that may exist between them. * + * - Adding threat will also create a combat reference between the units if one doesn't exist yet. * +\********************************************************************************************************************************************************/ + +// Please check Game/Combat/CombatManager.h for documentation on how this class works! +struct CombatReference +{ + Unit* const first; + Unit* const second; + bool const _isPvP; + Unit* GetOther(Unit const* me) const { return (first == me) ? second : first; } + + void EndCombat(); + + CombatReference(CombatReference const&) = delete; + CombatReference& operator=(CombatReference const&) = delete; + +protected: + CombatReference(Unit* a, Unit* b, bool pvp = false) : first(a), second(b), _isPvP(pvp) { } + + friend class CombatManager; +}; + +// Please check Game/Combat/CombatManager.h for documentation on how this class works! +struct PvPCombatReference : public CombatReference +{ + static const uint32 PVP_COMBAT_TIMEOUT = 5 * IN_MILLISECONDS; + + // suppressed combat refs do not generate a combat state for one side of the relation + // (used by: vanish, feign death) + void SuppressFor(Unit* who); + bool IsSuppressedFor(Unit const* who) const { return (who == first) ? _suppressFirst : _suppressSecond; } + +private: + PvPCombatReference(Unit* first, Unit* second) : CombatReference(first, second, true) { } + + bool Update(uint32 tdiff); + void Refresh(); + void Suppress(Unit* who) { (who == first ? _suppressFirst : _suppressSecond) = true; } + + uint32 _combatTimer = PVP_COMBAT_TIMEOUT; + bool _suppressFirst = false; + bool _suppressSecond = false; + + friend class CombatManager; +}; + +// please check Game/Combat/CombatManager.h for documentation on how this class works! +class CombatManager +{ + public: + static bool CanBeginCombat(Unit const* a, Unit const* b); + + CombatManager(Unit* owner) : _owner(owner) { } + void Update(uint32 tdiff); // called from Unit::Update + + Unit* GetOwner() const { return _owner; } + bool HasCombat() const { return HasPvECombat() || HasPvPCombat(); } + bool HasPvECombat() const { return !_pveRefs.empty(); } + std::unordered_map const& GetPvECombatRefs() const { return _pveRefs; } + bool HasPvPCombat() const; + std::unordered_map const& GetPvPCombatRefs() const { return _pvpRefs; } + // If the Unit is in combat, returns an arbitrary Unit that it's in combat with. Otherwise, returns nullptr. + Unit* GetAnyTarget() const; + + // return value is the same as calling IsInCombatWith immediately after this returns + bool SetInCombatWith(Unit* who); + bool IsInCombatWith(ObjectGuid const& who) const; + bool IsInCombatWith(Unit const* who) const; + void InheritCombatStatesFrom(Unit const* who); + void EndCombatBeyondRange(float range, bool includingPvP = false); + // flags any pvp refs for suppression on owner's side - these refs will not generate combat until refreshed + void SuppressPvPCombat(); + void EndAllPvECombat(); + void EndAllPvPCombat(); + void EndAllCombat() { EndAllPvECombat(); EndAllPvPCombat(); } + + CombatManager(CombatManager const&) = delete; + CombatManager& operator=(CombatManager const&) = delete; + + private: + static void NotifyAICombat(Unit* me, Unit* other); + void PutReference(ObjectGuid const& guid, CombatReference* ref); + void PurgeReference(ObjectGuid const& guid, bool pvp); + bool UpdateOwnerCombatState() const; + Unit* const _owner; + std::unordered_map _pveRefs; + std::unordered_map _pvpRefs; + + + friend struct CombatReference; + friend struct PvPCombatReference; +}; + +#endif \ No newline at end of file diff --git a/src/server/game/Combat/HostileRefMgr.cpp b/src/server/game/Combat/HostileRefMgr.cpp deleted file mode 100644 index 16fc944a5218e6..00000000000000 --- a/src/server/game/Combat/HostileRefMgr.cpp +++ /dev/null @@ -1,250 +0,0 @@ -/* - * This file is part of the AzerothCore Project. See AUTHORS file for Copyright information - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU Affero General Public License as published by the - * Free Software Foundation; either version 3 of the License, or (at your - * option) any later version. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License along - * with this program. If not, see . - */ - -#include "HostileRefMgr.h" -#include "CreatureAI.h" -#include "SpellInfo.h" -#include "SpellMgr.h" -#include "ThreatMgr.h" -#include "Unit.h" - -HostileRefMgr::~HostileRefMgr() -{ - deleteReferences(); -} - -//================================================= -// send threat to all my hateres for the victim -// The victim is hated than by them as well -// use for buffs and healing threat functionality - -void HostileRefMgr::threatAssist(Unit* victim, float baseThreat, SpellInfo const* threatSpell) -{ - if (getSize() == 0) - return; - - HostileReference* ref = getFirst(); - float threat = ThreatCalcHelper::calcThreat(victim, baseThreat, (threatSpell ? threatSpell->GetSchoolMask() : SPELL_SCHOOL_MASK_NORMAL), threatSpell); - threat /= getSize(); - while (ref) - { - Unit* refOwner = ref->GetSource()->GetOwner(); - if (ThreatCalcHelper::isValidProcess(victim, refOwner, threatSpell)) - { - if (Creature* hatingCreature = refOwner->ToCreature()) - { - if (hatingCreature->IsAIEnabled) - { - hatingCreature->AI()->CalculateThreat(victim, threat, threatSpell); - } - } - - ref->GetSource()->DoAddThreat(victim, threat); - } - - ref = ref->next(); - } -} - -//================================================= - -void HostileRefMgr::addTempThreat(float threat, bool apply) -{ - HostileReference* ref = getFirst(); - - while (ref) - { - if (apply) - { - if (ref->getTempThreatModifier() == 0.0f) - ref->addTempThreat(threat); - } - else - ref->resetTempThreat(); - - ref = ref->next(); - } -} - -//================================================= - -void HostileRefMgr::addThreatPercent(int32 percent) -{ - HostileReference* ref = getFirst(); - while (ref) - { - ref->addThreatPercent(percent); - ref = ref->next(); - } -} - -//================================================= -// The online / offline status is given to the method. The calculation has to be done before - -void HostileRefMgr::setOnlineOfflineState(bool isOnline) -{ - HostileReference* ref = getFirst(); - while (ref) - { - ref->setOnlineOfflineState(isOnline); - ref = ref->next(); - } -} - -//================================================= -// The online / offline status is calculated and set - -void HostileRefMgr::updateThreatTables() -{ - HostileReference* ref = getFirst(); - while (ref) - { - ref->updateOnlineStatus(); - ref = ref->next(); - } -} - -//================================================= -// The references are not needed anymore -// tell the source to remove them from the list and free the mem - -void HostileRefMgr::deleteReferences(bool removeFromMap /*= false*/) -{ - std::vector creaturesToEvade; - - HostileReference* ref = getFirst(); - while (ref) - { - HostileReference* nextRef = ref->next(); - ref->removeReference(); - - if (removeFromMap) - { - if (ThreatMgr const* threatMgr = ref->GetSource()) - { - if (threatMgr->areThreatListsEmpty()) - { - if (Creature* creature = threatMgr->GetOwner()->ToCreature()) - { - creaturesToEvade.push_back(creature); - } - } - } - } - - delete ref; - ref = nextRef; - } - - for (Creature* creature : creaturesToEvade) - { - creature->AI()->EnterEvadeMode(); - } -} - -//================================================= -// delete one reference, defined by faction - -void HostileRefMgr::deleteReferencesForFaction(uint32 faction) -{ - HostileReference* ref = getFirst(); - while (ref) - { - HostileReference* nextRef = ref->next(); - if (ref->GetSource()->GetOwner()->GetFactionTemplateEntry()->faction == faction) - { - ref->removeReference(); - delete ref; - } - ref = nextRef; - } -} - -//================================================= -// delete one reference, defined by Unit - -void HostileRefMgr::deleteReference(Unit* creature) -{ - HostileReference* ref = getFirst(); - while (ref) - { - HostileReference* nextRef = ref->next(); - if (ref->GetSource()->GetOwner() == creature) - { - ref->removeReference(); - delete ref; - break; - } - ref = nextRef; - } -} - -//================================================= -// delete all references out of specified range - -void HostileRefMgr::deleteReferencesOutOfRange(float range) -{ - HostileReference* ref = getFirst(); - range = range * range; - while (ref) - { - HostileReference* nextRef = ref->next(); - Unit* owner = ref->GetSource()->GetOwner(); - if (!owner->isActiveObject() && owner->GetExactDist2dSq(GetOwner()) > range) - { - ref->removeReference(); - delete ref; - } - ref = nextRef; - } -} - -//================================================= -// set state for one reference, defined by Unit - -void HostileRefMgr::setOnlineOfflineState(Unit* creature, bool isOnline) -{ - HostileReference* ref = getFirst(); - while (ref) - { - HostileReference* nextRef = ref->next(); - if (ref->GetSource()->GetOwner() == creature) - { - ref->setOnlineOfflineState(isOnline); - break; - } - ref = nextRef; - } -} - -//================================================= - -void HostileRefMgr::UpdateVisibility(bool checkThreat) -{ - HostileReference* ref = getFirst(); - while (ref) - { - HostileReference* nextRef = ref->next(); - if ((!checkThreat || ref->GetSource()->GetThreatListSize() <= 1)) - { - nextRef = ref->next(); - ref->removeReference(); - delete ref; - } - ref = nextRef; - } -} diff --git a/src/server/game/Combat/HostileRefMgr.h b/src/server/game/Combat/HostileRefMgr.h deleted file mode 100644 index 8cbdfef7b32a24..00000000000000 --- a/src/server/game/Combat/HostileRefMgr.h +++ /dev/null @@ -1,75 +0,0 @@ -/* - * This file is part of the AzerothCore Project. See AUTHORS file for Copyright information - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU Affero General Public License as published by the - * Free Software Foundation; either version 3 of the License, or (at your - * option) any later version. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License along - * with this program. If not, see . - */ - -#ifndef _HOSTILEREFMANAGER -#define _HOSTILEREFMANAGER - -#include "Common.h" -#include "RefMgr.h" - -class Unit; -class ThreatMgr; -class HostileReference; -class SpellInfo; - -//================================================= - -class HostileRefMgr : public RefMgr -{ -private: - Unit* iOwner; -public: - explicit HostileRefMgr(Unit* owner) { iOwner = owner; } - ~HostileRefMgr() override; - - Unit* GetOwner() { return iOwner; } - - // send threat to all my hateres for the victim - // The victim is hated than by them as well - // use for buffs and healing threat functionality - void threatAssist(Unit* victim, float baseThreat, SpellInfo const* threatSpell = nullptr); - - void addTempThreat(float threat, bool apply); - - void addThreatPercent(int32 percent); - - // The references are not needed anymore - // tell the source to remove them from the list and free the mem - void deleteReferences(bool removeFromMap = false); - - // Remove specific faction references - void deleteReferencesForFaction(uint32 faction); - - // pussywizard: for combat bugs - void deleteReferencesOutOfRange(float range); - - HostileReference* getFirst() { return ((HostileReference*) RefMgr::getFirst()); } - - void updateThreatTables(); - - void setOnlineOfflineState(bool isOnline); - - // set state for one reference, defined by Unit - void setOnlineOfflineState(Unit* creature, bool isOnline); - - // delete one reference, defined by Unit - void deleteReference(Unit* creature); - - void UpdateVisibility(bool checkThreat); -}; -//================================================= -#endif diff --git a/src/server/game/Combat/ThreatMgr.cpp b/src/server/game/Combat/ThreatMgr.cpp index c1041da4967d0e..62ced3dc902099 100644 --- a/src/server/game/Combat/ThreatMgr.cpp +++ b/src/server/game/Combat/ThreatMgr.cpp @@ -15,647 +15,727 @@ * with this program. If not, see . */ -#include "ThreatMgr.h" #include "Creature.h" #include "CreatureAI.h" +#include "MotionMaster.h" #include "Map.h" -#include "ObjectAccessor.h" #include "Player.h" +#include "ThreatMgr.h" +#include "Unit.h" +#include "UnitAI.h" +#include "SpellAuraEffects.h" #include "SpellInfo.h" #include "SpellMgr.h" #include "Unit.h" -#include "UnitEvents.h" +#include "ObjectAccessor.h" +#include "WorldPacket.h" +#include //============================================================== //================= ThreatCalcHelper =========================== //============================================================== +const CompareThreatLessThan ThreatMgr::CompareThreat; // The hatingUnit is not used yet -float ThreatCalcHelper::calcThreat(Unit* hatedUnit, float threat, SpellSchoolMask schoolMask, SpellInfo const* threatSpell) +void ThreatReference::AddThreat(float amount) { - if (threatSpell) - { - if (SpellThreatEntry const* threatEntry = sSpellMgr->GetSpellThreatEntry(threatSpell->Id)) - if (threatEntry->pctMod != 1.0f) - threat *= threatEntry->pctMod; + if (amount == 0.0f) + return; + _baseAmount = std::max(_baseAmount + amount, 0.0f); + if (amount > 0.0f) + HeapNotifyIncreased(); + else + HeapNotifyDecreased(); +} - // Energize is not affected by Mods - for (uint8 i = 0; i < MAX_SPELL_EFFECTS; i++) - if (threatSpell->Effects[i].Effect == SPELL_EFFECT_ENERGIZE || threatSpell->Effects[i].ApplyAuraName == SPELL_AURA_PERIODIC_ENERGIZE) - return threat; +void ThreatReference::ScaleThreat(float factor) +{ + if (factor == 1.0f) + return; + _baseAmount *= factor; + if (factor > 1.0f) + HeapNotifyIncreased(); + else + HeapNotifyDecreased(); +} + +void ThreatReference::UpdateOnlineState() +{ + OnlineState onlineState = SelectOnlineState(); + if (onlineState == _online) + return; + bool increase = (onlineState > _online); + _online = onlineState; + if (increase) + HeapNotifyIncreased(); + else + HeapNotifyDecreased(); - if (Player* modOwner = hatedUnit->GetSpellModOwner()) - modOwner->ApplySpellMod(threatSpell->Id, SPELLMOD_THREAT, threat); + if (!IsAvailable()) + _owner->GetThreatManager().SendRemoveToClients(_victim); +} + +/*static*/ bool ThreatReference::FlagsAllowFighting(Unit const* a, Unit const* b) +{ + if (a->GetTypeId() == TYPEID_UNIT && a->ToCreature()->IsTrigger()) + return false; + if (a->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_PVP)) + { + if (b->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_IMMUNE_TO_PC)) + return false; + } + else + { + if (b->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_IMMUNE_TO_NPC)) + return false; } + return true; +} - return hatedUnit->ApplyTotalThreatModifier(threat, schoolMask); +ThreatReference::OnlineState ThreatReference::SelectOnlineState() +{ + // first, check all offline conditions + if (!_owner->CanSeeOrDetect(_victim)) // not in map/phase, or stealth/invis + return ONLINE_STATE_OFFLINE; + if (_victim->HasUnitState(UNIT_STATE_DIED)) // feign death + return ONLINE_STATE_OFFLINE; + if (!FlagsAllowFighting(_owner, _victim) || !FlagsAllowFighting(_victim, _owner)) + return ONLINE_STATE_OFFLINE; + // next, check suppression (immunity to chosen melee attack school) + if (_victim->IsImmunedToDamage(_owner->GetMeleeDamageSchoolMask())) + return ONLINE_STATE_SUPPRESSED; + // or any form of CC that will break on damage - disorient, polymorph, blind etc + if (_victim->HasBreakableByDamageCrowdControlAura()) + return ONLINE_STATE_SUPPRESSED; + // no suppression - we're online + return ONLINE_STATE_ONLINE; } -bool ThreatCalcHelper::isValidProcess(Unit* hatedUnit, Unit* hatingUnit, SpellInfo const* threatSpell) +void ThreatReference::UpdateTauntState(bool victimIsTaunting) { - //function deals with adding threat and adding players and pets into ThreatList - //mobs, NPCs, guards have ThreatList and HateOfflineList - //players and pets have only InHateListOf - //HateOfflineList is used co contain unattackable victims (in-flight, in-water, GM etc.) + if (victimIsTaunting) + { + _taunted = TAUNT_STATE_TAUNT; + HeapNotifyIncreased(); + return; + } - if (!hatedUnit || !hatingUnit) - return false; + // Check for SPELL_AURA_MOD_DETAUNT (applied from owner to victim) + for (AuraEffect const* eff : _victim->GetAuraEffectsByType(SPELL_AURA_MOD_DETAUNT)) + if (eff->GetCasterGUID() == _owner->GetGUID()) + { + _taunted = TAUNT_STATE_DETAUNT; + HeapNotifyDecreased(); + return; + } - // not to self - if (hatedUnit == hatingUnit) - return false; + _taunted = TAUNT_STATE_NONE; + HeapNotifyChanged(); +} - // not to GM - if (hatedUnit->GetTypeId() == TYPEID_PLAYER && hatedUnit->ToPlayer()->IsGameMaster()) - return false; +void ThreatReference::ClearThreat(bool sendRemove) +{ + _owner->GetThreatManager().PurgeThreatListRef(_victim->GetGUID(), sendRemove); + _victim->GetThreatManager().PurgeThreatenedByMeRef(_owner->GetGUID()); + delete this; +} - // not to dead and not for dead - if (!hatedUnit->IsAlive() || !hatingUnit->IsAlive()) +/*static*/ bool ThreatMgr::CanHaveThreatList(Unit const* who) +{ + // only creatures can have threat list + if (who->GetTypeId() != TYPEID_UNIT) return false; - // not in same map or phase - if (!hatedUnit->IsInMap(hatingUnit) || !hatedUnit->InSamePhase(hatingUnit)) + // pets and totems cannot have threat list + if (who->IsPet() || who->IsTotem()) return false; - // spell not causing threat - if (threatSpell && threatSpell->HasAttribute(SPELL_ATTR1_NO_THREAT)) + // summons cannot have a threat list, unless they are controlled by a creature + if (who->HasUnitTypeMask(UNIT_MASK_MINION | UNIT_MASK_GUARDIAN) && !who->GetOwnerGUID().IsCreature()) return false; - ASSERT(hatingUnit->GetTypeId() == TYPEID_UNIT); - return true; } -//============================================================ -//================= HostileReference ========================== -//============================================================ - -HostileReference::HostileReference(Unit* refUnit, ThreatMgr* threatMgr, float threat) +ThreatMgr::ThreatMgr(Unit* owner) : _owner(owner), _ownerCanHaveThreatList(false), _ownerEngaged(false), _updateClientTimer(CLIENT_THREAT_UPDATE_INTERVAL), _currentVictimRef(nullptr) { - iThreat = threat; - iTempThreatModifier = 0.0f; - link(refUnit, threatMgr); - iUnitGuid = refUnit->GetGUID(); - iOnline = true; + for (int8 i = 0; i < MAX_SPELL_SCHOOL; ++i) + _singleSchoolModifiers[i] = 1.0f; } -//============================================================ -// Tell our refTo (target) object that we have a link -void HostileReference::targetObjectBuildLink() +void ThreatMgr::Initialize() { - getTarget()->addHatedBy(this); + _ownerCanHaveThreatList = ThreatMgr::CanHaveThreatList(_owner); } -//============================================================ -// Tell our refTo (taget) object, that the link is cut -void HostileReference::targetObjectDestroyLink() +void ThreatMgr::Update(uint32 tdiff) { - getTarget()->removeHatedBy(this); + if (_updateClientTimer <= tdiff) + { + _updateClientTimer = CLIENT_THREAT_UPDATE_INTERVAL; + SendThreatListToClients(); + } + else + _updateClientTimer -= tdiff; } -//============================================================ -// Tell our refFrom (source) object, that the link is cut (Target destroyed) - -void HostileReference::sourceObjectDestroyLink() +Unit* ThreatMgr::GetCurrentVictim() const { - setOnlineOfflineState(false); + if (_currentVictimRef) + return _currentVictimRef->GetVictim(); + return nullptr; } -//============================================================ -// Inform the source, that the status of the reference changed - -void HostileReference::fireStatusChanged(ThreatRefStatusChangeEvent& threatRefStatusChangeEvent) +Unit* ThreatMgr::GetAnyTarget() const { - if (GetSource()) - GetSource()->processThreatEvent(&threatRefStatusChangeEvent); + for (ThreatReference const* ref : _sortedThreatList) + if (!ref->IsOffline()) + return ref->GetVictim(); + return nullptr; } -// -- compatibility layer for combat rewrite -Unit* HostileReference::GetOwner() const { return GetSource()->GetOwner(); } - -//============================================================ - -void HostileReference::AddThreat(float modThreat) +Unit* ThreatMgr::SelectVictim() { - iThreat += modThreat; - // the threat is changed. Source and target unit have to be available - // if the link was cut before relink it again - if (!IsOnline()) - updateOnlineStatus(); - if (modThreat != 0.0f) - { - ThreatRefStatusChangeEvent event(UEV_THREAT_REF_THREAT_CHANGE, this, modThreat); - fireStatusChanged(event); - } + if (_sortedThreatList.empty()) + return nullptr; - if (isValid() && modThreat >= 0.0f) + ThreatReference const* newVictimRef = ReselectVictim(); + if (newVictimRef != _currentVictimRef) { - Unit* target = getTarget(); - if (target->GetEntry() != NPC_EYE_OF_KILROGG) // Excluded Eye of Kilrogg - { - Unit* victimOwner = target->GetCharmerOrOwner(); - if (victimOwner && victimOwner->IsAlive()) - { - GetSource()->AddThreat(victimOwner, 0.0f); // create a threat to the owner of a pet, if the pet attacks - } - } + if (newVictimRef) + SendNewVictimToClients(newVictimRef); + + _currentVictimRef = newVictimRef; } + return newVictimRef ? newVictimRef->GetVictim() : nullptr; } -void HostileReference::addThreatPercent(int32 percent) +bool ThreatMgr::IsThreatListEmpty(bool includeOffline) const { - // Xinef: Do not allow to modify threat by percent if threat is negative (forced to big value < 0 by spells adding temporary threat) - // Xinef: When the temporary effect ends, temporary threat is added back which results in huge additional amount of threat - if (iThreat <= 0) - return; - - float tmpThreat = iThreat; - AddPct(tmpThreat, percent); - AddThreat(tmpThreat - iThreat); + if (includeOffline) + return _sortedThreatList.empty(); + for (ThreatReference const* ref : _sortedThreatList) + if (ref->IsAvailable()) + return false; + return true; } -//============================================================ -// check, if source can reach target and set the status - -void HostileReference::updateOnlineStatus() +bool ThreatMgr::IsThreatenedBy(ObjectGuid const& who, bool includeOffline) const { - bool online = false; - - if (!isValid()) - if (Unit* target = ObjectAccessor::GetUnit(*GetSourceUnit(), getUnitGuid())) - link(target, GetSource()); - - // only check for online status if - // ref is valid - // target is no player or not gamemaster - // target is not in flight - if (isValid() - && (getTarget()->GetTypeId() != TYPEID_PLAYER || !getTarget()->ToPlayer()->IsGameMaster()) - && !getTarget()->IsInFlight() - && getTarget()->IsInMap(GetSourceUnit()) - && getTarget()->InSamePhase(GetSourceUnit()) - ) - { - Creature* creature = GetSourceUnit()->ToCreature(); - online = getTarget()->isInAccessiblePlaceFor(creature); - if (!online) - { - if (creature->IsWithinCombatRange(getTarget(), creature->m_CombatDistance)) - online = true; // not accessible but stays online - } - } - - setOnlineOfflineState(online); + auto it = _myThreatListEntries.find(who); + if (it == _myThreatListEntries.end()) + return false; + return (includeOffline || it->second->IsAvailable()); } +bool ThreatMgr::IsThreatenedBy(Unit const* who, bool includeOffline) const { return IsThreatenedBy(who->GetGUID(), includeOffline); } -//============================================================ -// set the status and fire the event on status change - -void HostileReference::setOnlineOfflineState(bool isOnline) +float ThreatMgr::GetThreat(Unit const* who, bool includeOffline) const { - if (iOnline != isOnline) - { - iOnline = isOnline; - - ThreatRefStatusChangeEvent event(UEV_THREAT_REF_ONLINE_STATUS, this); - fireStatusChanged(event); - } + auto it = _myThreatListEntries.find(who->GetGUID()); + if (it == _myThreatListEntries.end()) + return 0.0f; + return (includeOffline || it->second->IsAvailable()) ? it->second->GetThreat() : 0.0f; } -//============================================================ -// prepare the reference for deleting -// this is called be the target - -void HostileReference::removeReference() +std::vector ThreatMgr::GetModifiableThreatList() const { - invalidate(); + std::vector list; + list.reserve(_myThreatListEntries.size()); + for (auto it = _sortedThreatList.ordered_begin(), end = _sortedThreatList.ordered_end(); it != end; ++it) + list.push_back(const_cast(*it)); + return list; +} - ThreatRefStatusChangeEvent event(UEV_THREAT_REF_REMOVE_FROM_LIST, this); - fireStatusChanged(event); +bool ThreatMgr::IsThreateningAnyone(bool includeOffline) const +{ + if (includeOffline) + return !_threatenedByMe.empty(); + for (auto const& pair : _threatenedByMe) + if (pair.second->IsAvailable()) + return true; + return false; } -//============================================================ +bool ThreatMgr::IsThreateningTo(ObjectGuid const& who, bool includeOffline) const +{ + auto it = _threatenedByMe.find(who); + if (it == _threatenedByMe.end()) + return false; + return (includeOffline || it->second->IsAvailable()); +} +bool ThreatMgr::IsThreateningTo(Unit const* who, bool includeOffline) const { return IsThreateningTo(who->GetGUID(), includeOffline); } -Unit* HostileReference::GetSourceUnit() +void ThreatMgr::UpdateOnlineStates(bool meThreateningOthers, bool othersThreateningMe) { - return (GetSource()->GetOwner()); + if (othersThreateningMe) + for (auto const& pair : _myThreatListEntries) + pair.second->UpdateOnlineState(); + if (meThreateningOthers) + for (auto const& pair : _threatenedByMe) + pair.second->UpdateOnlineState(); } -//============================================================ -//================ ThreatContainer =========================== -//============================================================ +static void SaveCreatureHomePositionIfNeed(Creature* c) +{ + MovementGeneratorType const movetype = c->GetMotionMaster()->GetCurrentMovementGeneratorType(); + if (movetype == WAYPOINT_MOTION_TYPE || movetype == POINT_MOTION_TYPE || (c->IsAIEnabled && c->AI()->IsEscorted())) + c->SetHomePosition(c->GetPosition()); +} -void ThreatContainer::clearReferences() +void ThreatMgr::AddThreat(Unit* target, float amount, SpellInfo const* spell, bool ignoreModifiers, bool ignoreRedirects) { - for (ThreatContainer::StorageType::const_iterator i = iThreatList.begin(); i != iThreatList.end(); ++i) + // step 1: we can shortcut if the spell has one of the NO_THREAT attrs set - nothing will happen + if (spell) { - (*i)->unlink(); - delete (*i); + if (spell->HasAttribute(SPELL_ATTR1_NO_THREAT)) + return; + if (!_owner->IsEngaged() && spell->HasAttribute(SPELL_ATTR3_SUPRESS_TARGET_PROCS)) + return; } - iThreatList.clear(); -} + // while riding a vehicle, all threat goes to the vehicle, not the pilot + if (Unit* vehicle = target->GetVehicleBase()) + { -//============================================================ -// Return the HostileReference of nullptr, if not found -HostileReference* ThreatContainer::getReferenceByTarget(Unit const* victim) const -{ - if (!victim) - return nullptr; + AddThreat(vehicle, amount, spell, ignoreModifiers, ignoreRedirects); + if (target->HasUnitTypeMask(UNIT_MASK_ACCESSORY)) // accessories are fully treated as components of the parent and cannot have threat + return; + amount = 0.0f; + } - return getReferenceByTarget(victim->GetGUID()); -} + // if we cannot actually have a threat list, we instead just set combat state and avoid creating threat refs altogether + if (!CanHaveThreatList()) + { + CombatManager& combatMgr = _owner->GetCombatManager(); + if (!combatMgr.SetInCombatWith(target)) + return; + // traverse redirects and put them in combat, too + for (auto const& pair : target->GetThreatManager()._redirectInfo) + if (!combatMgr.IsInCombatWith(pair.first)) + if (Unit* redirTarget = ObjectAccessor::GetUnit(*_owner, pair.first)) + combatMgr.SetInCombatWith(redirTarget); + return; + } -HostileReference* ThreatContainer::getReferenceByTarget(ObjectGuid const& guid) const -{ - for (ThreatContainer::StorageType::const_iterator i = iThreatList.begin(); i != iThreatList.end(); ++i) + // apply threat modifiers to the amount + if (!ignoreModifiers) + amount = CalculateModifiedThreat(amount, target, spell); + + // if we're increasing threat, send some/all of it to redirection targets instead if applicable + if (!ignoreRedirects && amount > 0.0f) { - HostileReference* ref = (*i); - if (ref && ref->getUnitGuid() == guid) + auto const& redirInfo = target->GetThreatManager()._redirectInfo; + if (!redirInfo.empty()) { - return ref; + float const origAmount = amount; + for (auto const& pair : redirInfo) // (victim,pct) + { + Unit* redirTarget = nullptr; + auto it = _myThreatListEntries.find(pair.first); // try to look it up in our threat list first (faster) + if (it != _myThreatListEntries.end()) + redirTarget = it->second->_victim; + else + redirTarget = ObjectAccessor::GetUnit(*_owner, pair.first); + + if (redirTarget) + { + float amountRedirected = CalculatePct(origAmount, pair.second); + AddThreat(redirTarget, amountRedirected, spell, true, true); + amount -= amountRedirected; + } + } } } - return nullptr; -} - -//============================================================ -// Add the threat, if we find the reference + // ok, now we actually apply threat + // check if we already have an entry - if we do, just increase threat for that entry and we're done + auto it = _myThreatListEntries.find(target->GetGUID()); + if (it != _myThreatListEntries.end()) + { + it->second->AddThreat(amount); + return; + } -HostileReference* ThreatContainer::AddThreat(Unit* victim, float threat) -{ - HostileReference* ref = getReferenceByTarget(victim); - if (ref) - ref->AddThreat(threat); - return ref; -} + // otherwise, ensure we're in combat (threat implies combat!) + if (!_owner->GetCombatManager().SetInCombatWith(target)) // if this returns false, we're not actually in combat, and thus cannot have threat! + return; // typical causes: bad scripts trying to add threat to GMs, dead targets etc -//============================================================ + // ok, we're now in combat - create the threat list reference and push it to the respective managers + ThreatReference* ref = new ThreatReference(this, target, amount); + PutThreatListRef(target->GetGUID(), ref); + target->GetThreatManager().PutThreatenedByMeRef(_owner->GetGUID(), ref); + if (!ref->IsOffline() && !_ownerEngaged) + { + _ownerEngaged = true; -void ThreatContainer::ModifyThreatByPercent(Unit* victim, int32 percent) -{ - if (HostileReference* ref = getReferenceByTarget(victim)) - ref->addThreatPercent(percent); + Creature* cOwner = _owner->ToCreature(); + assert(cOwner); // if we got here the owner can have a threat list, and must be a creature! + SaveCreatureHomePositionIfNeed(cOwner); + if (cOwner->IsAIEnabled) + cOwner->AI()->JustEngagedWith(target); + } } -//============================================================ -// Check if the list is dirty and sort if necessary - -void ThreatContainer::update() +void ThreatMgr::ScaleThreat(Unit* target, float factor) { - if (iDirty && iThreatList.size() > 1) - iThreatList.sort(Acore::ThreatOrderPred()); - - iDirty = false; + auto it = _myThreatListEntries.find(target->GetGUID()); + if (it != _myThreatListEntries.end()) + it->second->ScaleThreat(std::max(factor,0.0f)); } -//============================================================ -// return the next best victim -// could be the current victim - -HostileReference* ThreatContainer::SelectNextVictim(Creature* attacker, HostileReference* currentVictim) const +void ThreatMgr::MatchUnitThreatToHighestThreat(Unit* target) { - // pussywizard: pretty much remade this whole function - - HostileReference* currentRef = nullptr; - bool found = false; - bool noPriorityTargetFound = false; - - // pussywizard: currentVictim is needed to compare if threat was exceeded by 10%/30% for melee/range targets (only then switching current target) - if (currentVictim) - { - Unit* cvUnit = currentVictim->getTarget(); - if (!attacker->CanCreatureAttack(cvUnit)) // pussywizard: if currentVictim is not valid => don't compare the threat with it, just take the highest threat valid target - currentVictim = nullptr; - else if (cvUnit->IsImmunedToDamageOrSchool(attacker->GetMeleeDamageSchoolMask()) || cvUnit->HasNegativeAuraWithInterruptFlag(AURA_INTERRUPT_FLAG_TAKE_DAMAGE)) // pussywizard: no 10%/30% if currentVictim is immune to damage or has auras breakable by damage - currentVictim = nullptr; - } + if (_sortedThreatList.empty()) + return; - ThreatContainer::StorageType::const_iterator lastRef = iThreatList.end(); - --lastRef; + auto it = _sortedThreatList.begin(), end = _sortedThreatList.end(); + ThreatReference const* highest = *it; + if (!highest->IsOnline()) + return; - // pussywizard: iterate from highest to lowest threat - for (ThreatContainer::StorageType::const_iterator iter = iThreatList.begin(); iter != iThreatList.end() && !found;) + if (highest->_taunted) // might need to skip this - new max could be one of the preceding elements (heap property) since there is only one taunt element { - currentRef = (*iter); - - Unit* target = currentRef->getTarget(); - ASSERT(target); // if the ref has status online the target must be there ! - - // pussywizard: don't go to threat comparison if this ref is immune to damage or has aura breakable on damage (second choice target) - // pussywizard: if this is the last entry on the threat list, then all targets are second choice, set bool to true and loop threat list again, ignoring this section - if (!noPriorityTargetFound && (target->IsImmunedToDamageOrSchool(attacker->GetMeleeDamageSchoolMask()) || target->HasNegativeAuraWithInterruptFlag(AURA_INTERRUPT_FLAG_TAKE_DAMAGE) || target->HasAuraTypeWithCaster(SPELL_AURA_IGNORED, attacker->GetGUID()))) - { - if (iter != lastRef) - { - ++iter; - continue; - } - else - { - noPriorityTargetFound = true; - iter = iThreatList.begin(); - continue; - } - } - - // pussywizard: skip not valid targets - if (attacker->CanCreatureAttack(target)) + if ((++it) != end) { - if (currentVictim) // pussywizard: if not nullptr then target must have 10%/30% more threat - { - if (currentVictim == currentRef) // pussywizard: nothing found previously was good and enough, currentRef passed all necessary tests, so end now - { - found = true; - break; - } + ThreatReference const* a = *it; + if (a->IsOnline() && a->GetThreat() > highest->GetThreat()) + highest = a; - // pussywizard: implement 110% threat rule for targets in melee range and 130% rule for targets in ranged distances - if (currentRef->GetThreat() > 1.3f * currentVictim->GetThreat()) // pussywizard: enough in all cases, end - { - found = true; - break; - } - else if (currentRef->GetThreat() > 1.1f * currentVictim->GetThreat()) // pussywizard: enought only if target in melee range - { - if (attacker->IsWithinMeleeRange(target)) - { - found = true; - break; - } - } - else // pussywizard: nothing found previously was good and enough, this and next entries on the list have less than 110% threat, and currentVictim is present and valid as checked before the loop (otherwise it's nullptr), so end now - { - currentRef = currentVictim; - found = true; - break; - } - } - else // pussywizard: no currentVictim, first passing all checks is chosen (highest threat, list is sorted) + if ((++it) != end) { - found = true; - break; + ThreatReference const* a = *it; + if (a->IsOnline() && a->GetThreat() > highest->GetThreat()) + highest = a; } } - ++iter; } - if (!found) - currentRef = nullptr; - - return currentRef; + AddThreat(target, highest->GetThreat() - GetThreat(target, true), nullptr, true, true); } -//============================================================ -//=================== ThreatMgr ========================== -//============================================================ - -ThreatMgr::ThreatMgr(Unit* owner) : iCurrentVictim(nullptr), iOwner(owner), iUpdateTimer(THREAT_UPDATE_INTERVAL) +void ThreatMgr::TauntUpdate() { + std::list const& tauntEffects = _owner->GetAuraEffectsByType(SPELL_AURA_MOD_TAUNT); + auto threatEnd = _myThreatListEntries.end(); + ThreatReference* tauntRef = nullptr; + // Only the last taunt effect applied by something still on our threat list is considered + for (auto it = tauntEffects.rbegin(), end = tauntEffects.rend(); it != end; ++it) + { + auto threatIt = _myThreatListEntries.find((*it)->GetCasterGUID()); + if (threatIt == threatEnd) + continue; + if (!threatIt->second->IsOnline()) + continue; + tauntRef = threatIt->second; + break; + } + for (auto const& pair : _myThreatListEntries) + pair.second->UpdateTauntState(pair.second == tauntRef); } -void ThreatMgr::ClearAllThreat() +void ThreatMgr::ResetAllThreat() { - if (iOwner->CanHaveThreatList() && !isThreatListEmpty()) - iOwner->SendClearThreatListOpcode(); - clearReferences(); + for (auto const& pair : _myThreatListEntries) + pair.second->SetThreat(0.0f); } -//============================================================ - -void ThreatMgr::clearReferences() +void ThreatMgr::ClearThreat(Unit* target) { - iThreatContainer.clearReferences(); - iThreatOfflineContainer.clearReferences(); - iCurrentVictim = nullptr; - iUpdateTimer = THREAT_UPDATE_INTERVAL; + auto it = _myThreatListEntries.find(target->GetGUID()); + if (it != _myThreatListEntries.end()) + it->second->ClearThreat(); } -//============================================================ - -void ThreatMgr::AddThreat(Unit* victim, float threat, SpellSchoolMask schoolMask, SpellInfo const* threatSpell) +void ThreatMgr::ClearAllThreat() { - if (!ThreatCalcHelper::isValidProcess(victim, iOwner, threatSpell)) + _ownerEngaged = false; + if (_myThreatListEntries.empty()) return; - threat = ThreatCalcHelper::calcThreat(victim, threat, schoolMask, threatSpell); - if (Creature* hatingCreature = iOwner->ToCreature()) + SendClearAllThreatToClients(); + do + _myThreatListEntries.begin()->second->ClearThreat(false); + while (!_myThreatListEntries.empty()); +} + +ThreatReference const* ThreatMgr::ReselectVictim() +{ + ThreatReference const* oldVictimRef = _currentVictimRef; + if (oldVictimRef && !oldVictimRef->IsAvailable()) + oldVictimRef = nullptr; + // in 99% of cases - we won't need to actually look at anything beyond the first element + ThreatReference const* highest = _sortedThreatList.top(); + // if the highest reference is offline, the entire list is offline, and we indicate this + if (!highest->IsAvailable()) + return nullptr; + // if we have no old victim, or old victim is still highest, then highest is our target and we're done + if (!oldVictimRef || highest == oldVictimRef) + return highest; + // if highest threat doesn't break 110% of old victim, nothing below it is going to do so either; new victim = old victim and done + if (!ThreatMgr::CompareReferencesLT(oldVictimRef, highest, 1.1f)) + return oldVictimRef; + // if highest threat breaks 130%, it's our new target regardless of range (and we're done) + if (ThreatMgr::CompareReferencesLT(oldVictimRef, highest, 1.3f)) + return highest; + // if it doesn't break 130%, we need to check if it's melee - if yes, it breaks 110% (we checked earlier) and is our new target + if (_owner->IsWithinMeleeRange(highest->_victim)) + return highest; + // If we get here, highest threat is ranged, but below 130% of current - there might be a melee that breaks 110% below us somewhere, so now we need to actually look at the next highest element + // luckily, this is a heap, so getting the next highest element is O(log n), and we're just gonna do that repeatedly until we've seen enough targets (or find a target) + auto it = _sortedThreatList.ordered_begin(), end = _sortedThreatList.ordered_end(); + while (it != end) { - if (hatingCreature->IsAIEnabled) - { - hatingCreature->AI()->CalculateThreat(victim, threat, threatSpell); - } + ThreatReference const* next = *it; + // if we've found current victim, we're done (nothing above is higher, and nothing below can be higher) + if (next == oldVictimRef) + return next; + // if next isn't above 110% threat, then nothing below it can be either - we're done, old victim stays + if (!ThreatMgr::CompareReferencesLT(oldVictimRef, next, 1.1f)) + return oldVictimRef; + // if next is melee, he's above 110% and our new victim + if (_owner->IsWithinMeleeRange(next->_victim)) + return next; + // otherwise the next highest target may still be a melee above 110% and we need to look further + ++it; } - - DoAddThreat(victim, threat); + ASSERT(false && "Current victim not found in sorted threat list even though it has a reference - manager desync!"); + return nullptr; } -void ThreatMgr::DoAddThreat(Unit* victim, float threat) +// returns true if a is LOWER on the threat list than b +bool ThreatMgr::CompareReferencesLT(ThreatReference const* a, ThreatReference const* b, float aWeight) { - uint32 redirectThreadPct = victim->GetRedirectThreatPercent(); - Unit* redirectTarget = victim->GetRedirectThreatTarget(); + if (a->_online != b->_online) // online state precedence (ONLINE > SUPPRESSED > OFFLINE) + return a->_online < b->_online; + if (a->_taunted != b->_taunted) // taunt state precedence (TAUNT > NONE > DETAUNT) + return a->_taunted < b->_taunted; + return (a->GetThreat()*aWeight < b->GetThreat()); +} - // Personal Spawns from same summoner can aggro each other - if (TempSummon* tempSummonVictim = victim->ToTempSummon()) +/*static*/ float ThreatMgr::CalculateModifiedThreat(float threat, Unit const* victim, SpellInfo const* spell) +{ + // modifiers by spell + if (spell) { - if (tempSummonVictim->IsVisibleBySummonerOnly()) - { - if (!GetOwner()->ToTempSummon() || - !GetOwner()->ToTempSummon()->IsVisibleBySummonerOnly() || - tempSummonVictim->GetSummonerGUID() != GetOwner()->ToTempSummon()->GetSummonerGUID()) - { - redirectThreadPct = 100; - redirectTarget = tempSummonVictim->GetSummonerUnit(); - } - } + if (SpellThreatEntry const* threatEntry = sSpellMgr->GetSpellThreatEntry(spell->Id)) + if (threatEntry->pctMod != 1.0f) // flat/AP modifiers handled in Spell::HandleThreatSpells + threat *= threatEntry->pctMod; + + if (Player* modOwner = victim->GetSpellModOwner()) + modOwner->ApplySpellMod(spell->Id, SPELLMOD_THREAT, threat); } - // must check > 0.0f, otherwise dead loop - if (threat > 0.0f && redirectThreadPct) + // modifiers by effect school + ThreatMgr const& victimMgr = victim->GetThreatManager(); + SpellSchoolMask const mask = spell ? spell->GetSchoolMask() : SPELL_SCHOOL_MASK_NORMAL; + switch (mask) { - if (redirectTarget) + case SPELL_SCHOOL_MASK_NORMAL: + threat *= victimMgr._singleSchoolModifiers[SPELL_SCHOOL_NORMAL]; + break; + case SPELL_SCHOOL_MASK_HOLY: + threat *= victimMgr._singleSchoolModifiers[SPELL_SCHOOL_HOLY]; + break; + case SPELL_SCHOOL_MASK_FIRE: + threat *= victimMgr._singleSchoolModifiers[SPELL_SCHOOL_FIRE]; + break; + case SPELL_SCHOOL_MASK_NATURE: + threat *= victimMgr._singleSchoolModifiers[SPELL_SCHOOL_NATURE]; + break; + case SPELL_SCHOOL_MASK_FROST: + threat *= victimMgr._singleSchoolModifiers[SPELL_SCHOOL_FROST]; + break; + case SPELL_SCHOOL_MASK_SHADOW: + threat *= victimMgr._singleSchoolModifiers[SPELL_SCHOOL_SHADOW]; + break; + case SPELL_SCHOOL_MASK_ARCANE: + threat *= victimMgr._singleSchoolModifiers[SPELL_SCHOOL_ARCANE]; + break; + default: { - float redirectThreat = CalculatePct(threat, redirectThreadPct); - threat -= redirectThreat; - if (ThreatCalcHelper::isValidProcess(redirectTarget, GetOwner())) - _addThreat(redirectTarget, redirectThreat); + auto it = victimMgr._multiSchoolModifiers.find(mask); + if (it != victimMgr._multiSchoolModifiers.end()) + { + threat *= it->second; + break; + } + float mod = victim->GetTotalAuraMultiplierByMiscMask(SPELL_AURA_MOD_THREAT, mask); + victimMgr._multiSchoolModifiers[mask] = mod; + threat *= mod; + break; } } - - _addThreat(victim, threat); + return threat; } -void ThreatMgr::_addThreat(Unit* victim, float threat) +void ThreatMgr::SendClearAllThreatToClients() const { - HostileReference* ref = iThreatContainer.AddThreat(victim, threat); - // Ref is not in the online refs, search the offline refs next - if (!ref) - ref = iThreatOfflineContainer.AddThreat(victim, threat); + WorldPacket data(SMSG_THREAT_CLEAR, 8); + data << _owner->GetPackGUID(); + _owner->SendMessageToSet(&data, false); +} - if (!ref) // there was no ref => create a new one +void ThreatMgr::SendThreatListToClients() const +{ + WorldPacket data(SMSG_THREAT_UPDATE, (_sortedThreatList.size() + 1) * 8); // guess + data << _owner->GetPackGUID(); + size_t countPos = data.wpos(); + data << uint32(0); // placeholder + uint32 count = 0; + for (ThreatReference const* ref : _sortedThreatList) { - // threat has to be 0 here - HostileReference* hostileRef = new HostileReference(victim, this, 0); - iThreatContainer.addReference(hostileRef); - hostileRef->AddThreat(threat); // now we add the real threat - if (victim->GetTypeId() == TYPEID_PLAYER && victim->ToPlayer()->IsGameMaster()) - hostileRef->setOnlineOfflineState(false); // GM is always offline + if (!ref->IsAvailable()) // @todo check if suppressed threat should get sent for bubble/iceblock/hop etc + continue; + data << ref->GetVictim()->GetPackGUID(); + data << uint32(ref->GetThreat() * 100); + ++count; } + data.put(countPos, count); + _owner->SendMessageToSet(&data, false); } -//============================================================ - -void ThreatMgr::ModifyThreatByPercent(Unit* victim, int32 percent) +void ThreatMgr::ForwardThreatForAssistingMe(Unit* assistant, float baseAmount, SpellInfo const* spell, bool ignoreModifiers) { - iThreatContainer.ModifyThreatByPercent(victim, percent); + if (spell && spell->HasAttribute(SPELL_ATTR1_NO_THREAT)) // shortcut, none of the calls would do anything + return; + for (auto const& pair : _threatenedByMe) + pair.second->GetOwner()->GetThreatManager().AddThreat(assistant, baseAmount, spell, ignoreModifiers); } -//============================================================ - -Unit* ThreatMgr::getHostileTarget() +void ThreatMgr::RemoveMeFromThreatLists() { - iThreatContainer.update(); - HostileReference* nextVictim = iThreatContainer.SelectNextVictim(GetOwner()->ToCreature(), getCurrentVictim()); - setCurrentVictim(nextVictim); - return getCurrentVictim() != nullptr ? getCurrentVictim()->getTarget() : nullptr; + while (!_threatenedByMe.empty()) + _threatenedByMe.begin()->second->ClearThreat(); } -//============================================================ - -float ThreatMgr::GetThreat(Unit* victim, bool alsoSearchOfflineList) +void ThreatMgr::UpdateMyTempModifiers() { - float threat = 0.0f; - HostileReference* ref = iThreatContainer.getReferenceByTarget(victim); - if (!ref && alsoSearchOfflineList) - ref = iThreatOfflineContainer.getReferenceByTarget(victim); - if (ref) - threat = ref->GetThreat(); - return threat; -} + int32 mod = 0; + for (AuraEffect const* eff : _owner->GetAuraEffectsByType(SPELL_AURA_MOD_TOTAL_THREAT)) + mod += eff->GetAmount(); -//============================================================ + for (auto const& pair : _threatenedByMe) + { + pair.second->_tempModifier = mod; + pair.second->HeapNotifyChanged(); + } +} -float ThreatMgr::getThreatWithoutTemp(Unit* victim, bool alsoSearchOfflineList) +void ThreatMgr::UpdateMySpellSchoolModifiers() { - float threat = 0.0f; - HostileReference* ref = iThreatContainer.getReferenceByTarget(victim); - if (!ref && alsoSearchOfflineList) - ref = iThreatOfflineContainer.getReferenceByTarget(victim); - if (ref) - threat = ref->GetThreat() - ref->getTempThreatModifier(); - return threat; + for (uint8 i = 0; i < MAX_SPELL_SCHOOL; ++i) + _singleSchoolModifiers[i] = _owner->GetTotalAuraMultiplierByMiscMask(SPELL_AURA_MOD_THREAT, 1 << i); + _multiSchoolModifiers.clear(); } -//============================================================ - -void ThreatMgr::tauntApply(Unit* taunter) +void ThreatMgr::RegisterRedirectThreat(uint32 spellId, ObjectGuid const& victim, uint32 pct) { - HostileReference* ref = iThreatContainer.getReferenceByTarget(taunter); - if (getCurrentVictim() && ref && (ref->GetThreat() < getCurrentVictim()->GetThreat())) - { - if (ref->getTempThreatModifier() == 0.0f) // Ok, temp threat is unused - ref->setTempThreat(getCurrentVictim()->GetThreat()); - } + _redirectRegistry[spellId][victim] = pct; + UpdateRedirectInfo(); } -//============================================================ +void ThreatMgr::UnregisterRedirectThreat(uint32 spellId) +{ + auto it = _redirectRegistry.find(spellId); + if (it == _redirectRegistry.end()) + return; + _redirectRegistry.erase(it); + UpdateRedirectInfo(); +} -void ThreatMgr::tauntFadeOut(Unit* taunter) +void ThreatMgr::UnregisterRedirectThreat(uint32 spellId, ObjectGuid const& victim) { - HostileReference* ref = iThreatContainer.getReferenceByTarget(taunter); - if (ref) - ref->resetTempThreat(); + auto it = _redirectRegistry.find(spellId); + if (it == _redirectRegistry.end()) + return; + auto& victimMap = it->second; + auto it2 = victimMap.find(victim); + if (it2 == victimMap.end()) + return; + victimMap.erase(it2); + UpdateRedirectInfo(); } -//============================================================ +void ThreatMgr::SendRemoveToClients(Unit const* victim) const +{ + WorldPacket data(SMSG_THREAT_REMOVE, 16); + data << _owner->GetPackGUID(); + data << victim->GetPackGUID(); + _owner->SendMessageToSet(&data, false); +} -void ThreatMgr::setCurrentVictim(HostileReference* pHostileReference) +void ThreatMgr::SendNewVictimToClients(ThreatReference const* victimRef) const { - if (pHostileReference && pHostileReference != iCurrentVictim) + WorldPacket data(SMSG_HIGHEST_THREAT_UPDATE, (_sortedThreatList.size() + 2) * 8); + data << _owner->GetPackGUID(); + data << victimRef->_victim->GetPackGUID(); + size_t countPos = data.wpos(); + data << uint32(0); // placeholder + uint32 count = 0; + for (ThreatReference const* ref : _sortedThreatList) { - iOwner->SendChangeCurrentVictimOpcode(pHostileReference); + if (!ref->IsAvailable()) + continue; + data << ref->GetVictim()->GetPackGUID(); + data << uint32(ref->GetThreat() * 100); + ++count; } - iCurrentVictim = pHostileReference; + data.put(countPos, count); + _owner->SendMessageToSet(&data, false); } -//============================================================ -// The hated unit is gone, dead or deleted -// return true, if the event is consumed +void ThreatMgr::PutThreatListRef(ObjectGuid const& guid, ThreatReference* ref) +{ + auto& inMap = _myThreatListEntries[guid]; + ASSERT(!inMap && "Duplicate threat list entry being inserted - memory leak!"); + inMap = ref; + ref->_handle = _sortedThreatList.push(ref); +} -void ThreatMgr::processThreatEvent(ThreatRefStatusChangeEvent* threatRefStatusChangeEvent) +void ThreatMgr::PurgeThreatListRef(ObjectGuid const& guid, bool sendRemove) { - threatRefStatusChangeEvent->setThreatMgr(this); // now we can set the threat manager + auto it = _myThreatListEntries.find(guid); + if (it == _myThreatListEntries.end()) + return; + ThreatReference* ref = it->second; + _myThreatListEntries.erase(it); - HostileReference* hostileRef = threatRefStatusChangeEvent->getReference(); + if (_currentVictimRef == ref) + _currentVictimRef = nullptr; - switch (threatRefStatusChangeEvent->getType()) - { - case UEV_THREAT_REF_THREAT_CHANGE: - if ((getCurrentVictim() == hostileRef && threatRefStatusChangeEvent->getFValue() < 0.0f) || - (getCurrentVictim() != hostileRef && threatRefStatusChangeEvent->getFValue() > 0.0f)) - setDirty(true); // the order in the threat list might have changed - break; - case UEV_THREAT_REF_ONLINE_STATUS: - if (!hostileRef->IsOnline()) - { - if (hostileRef == getCurrentVictim()) - { - setCurrentVictim(nullptr); - setDirty(true); - } - if (GetOwner() && GetOwner()->IsInWorld()) - if (Unit* target = ObjectAccessor::GetUnit(*GetOwner(), hostileRef->getUnitGuid())) - if (GetOwner()->IsInMap(target)) - GetOwner()->SendRemoveFromThreatListOpcode(hostileRef); - iThreatContainer.remove(hostileRef); - iThreatOfflineContainer.addReference(hostileRef); - } - else - { - if (getCurrentVictim() && hostileRef->GetThreat() > (1.1f * getCurrentVictim()->GetThreat())) - setDirty(true); - iThreatContainer.addReference(hostileRef); - iThreatOfflineContainer.remove(hostileRef); - } - break; - case UEV_THREAT_REF_REMOVE_FROM_LIST: - if (hostileRef == getCurrentVictim()) - { - setCurrentVictim(nullptr); - setDirty(true); - } - iOwner->SendRemoveFromThreatListOpcode(hostileRef); - if (hostileRef->IsOnline()) - iThreatContainer.remove(hostileRef); - else - iThreatOfflineContainer.remove(hostileRef); - break; - } + _sortedThreatList.erase(ref->_handle); + if (sendRemove && ref->IsOnline()) + SendRemoveToClients(ref->_victim); } -bool ThreatMgr::isNeedUpdateToClient(uint32 time) +void ThreatMgr::PutThreatenedByMeRef(ObjectGuid const& guid, ThreatReference* ref) { - if (isThreatListEmpty()) - return false; - - if (time >= iUpdateTimer) - { - iUpdateTimer = THREAT_UPDATE_INTERVAL; - return true; - } - iUpdateTimer -= time; - return false; + auto& inMap = _threatenedByMe[guid]; + ASSERT(!inMap && "Duplicate entry being inserted into threatened by me list - potential memory leak!"); + inMap = ref; } -// Reset all aggro without modifying the threatlist. -void ThreatMgr::ResetAllThreat() +void ThreatMgr::PurgeThreatenedByMeRef(ObjectGuid const& guid) { - ThreatContainer::StorageType& threatList = iThreatContainer.iThreatList; - if (threatList.empty()) - return; - - for (ThreatContainer::StorageType::iterator itr = threatList.begin(); itr != threatList.end(); ++itr) - (*itr)->SetThreat(0); + auto it = _threatenedByMe.find(guid); + if (it != _threatenedByMe.end()) + _threatenedByMe.erase(it); +} - setDirty(true); +void ThreatMgr::UpdateRedirectInfo() +{ + _redirectInfo.clear(); + uint32 totalPct = 0; + for (auto const& pair : _redirectRegistry) // (spellid, victim -> pct) + for (auto const& victimPair : pair.second) // (victim,pct) + { + uint32 thisPct = std::min(100 - totalPct, victimPair.second); + if (thisPct > 0) + { + _redirectInfo.push_back({ victimPair.first, thisPct }); + totalPct += thisPct; + ASSERT(totalPct <= 100); + if (totalPct == 100) + return; + } + } } diff --git a/src/server/game/Combat/ThreatMgr.h b/src/server/game/Combat/ThreatMgr.h index bcc6d161421baf..b8ef8bfd817e9e 100644 --- a/src/server/game/Combat/ThreatMgr.h +++ b/src/server/game/Combat/ThreatMgr.h @@ -21,288 +21,255 @@ #include "Common.h" #include "IteratorPair.h" #include "ObjectGuid.h" -#include "Reference.h" #include "SharedDefines.h" -#include "UnitEvents.h" -#include +#include +#include +#include //============================================================== class Unit; -class Creature; -class ThreatMgr; class SpellInfo; -#define THREAT_UPDATE_INTERVAL 2 * IN_MILLISECONDS // Server should send threat update to client periodically each second - -//============================================================== -// Class to calculate the real threat based - -struct ThreatCalcHelper +/********************************************************************************************************************************************************\ + * DEV DOCUMENTATION: THREAT SYSTEM * + * (future devs: please keep this up-to-date if you change the system) * + * The threat system works based on dynamically allocated threat list entries. * + * * + * Each such entry is a ThreatReference object, which is always stored in exactly three places: * + * - The threatened unit's (from now: reference "owner") sorted and unsorted threat lists * + * - The threatening unit's (from now: reference "victim") threatened-by-me list * + * A ThreatReference object carries the following implicit guarantees: * + * - Both owner and victim are valid units, which are currently in the world. Neither can be nullptr. * + * - There is an active combat reference between owner and victim. * + * * + * ThreatManager also keeps track of whether its owner is engaged (a boolean flag). * + * - If a (non-offline) threat list entry is added to a not-yet-engaged ThreatManager, it calls JustEngagedWith on its owner's AI. * + * - The engaged state is cleared in ClearAllThreat (which is invoked on evade). * + * - This flag can be accessed through the IsEngaged method. For creatures that can have a threat list, this is equal to Unit::IsEngaged. * + * * + * Note that (threat => combat) is a strong guarantee provided in conjunction with CombatManager. Thus: * + * - Adding threat will also create a combat reference between the units if one doesn't exist yet (even if the owner can't have a threat list!) * + * - Ending combat between two units will also delete any threat references that may exist between them. * + * * + * To manage a creature's threat list, ThreatManager maintains a heap of threat reference const pointers. * + * This heap is kept well-structured in all methods that modify ThreatReference, and is used to select the next target. * + * * + * Selection uses the following properties on ThreatReference, in order: * + * - Online state (one of ONLINE, SUPPRESSED, OFFLINE): * + * - ONLINE: Normal threat state, target is valid and attackable * + * - SUPPRESSED: Target is attackable, but fully immuned. This is used for targets under HoP, Divine Shield, Ice Block etc. * + * Targets with SUPPRESSED threat can still be valid targets, but any target with ONLINE threat will be preferred. * + * - OFFLINE: The target is, for whatever reason, not valid at this time (for example, IMMUNE_TO_X flags or game master state). * + * These targets can never be selected by SelectVictim, which will return nullptr if all targets are OFFLINE (typically causing evade). * + * - Related methods: GetOnlineState, IsOnline, IsAvailable, IsOffline * + * - Taunt state (one of TAUNT, NONE, DETAUNT), the names speak for themselves * + * - Related methods: GetTauntState, IsTaunting, IsDetaunted * + * - Actual threat value (GetThreat) * + * * + * The current (= last selected) victim can be accessed using GetCurrentVictim. SelectVictim selects a (potentially new) victim. * + * Beyond that, ThreatManager has a variety of helpers and notifiers, which are documented inline below. * + * * + * SPECIAL NOTE: Please be aware that any heap iterator may be invalidated if you modify a ThreatReference. The heap holds const pointers for a reason. * + * If you need to modify multiple ThreatReference objects, then use GetModifiableThreatList(), which is safe to modify! * +\********************************************************************************************************************************************************/ + +class ThreatReference; +struct CompareThreatLessThan { - static float calcThreat(Unit* hatedUnit, float threat, SpellSchoolMask schoolMask = SPELL_SCHOOL_MASK_NORMAL, SpellInfo const* threatSpell = nullptr); - static bool isValidProcess(Unit* hatedUnit, Unit* hatingUnit, SpellInfo const* threatSpell = nullptr); + CompareThreatLessThan() {} + bool operator()(ThreatReference const* a, ThreatReference const* b) const; }; -//============================================================== -class HostileReference : public Reference +// Please check Game/Combat/ThreatManager.h for documentation on how this class works! +class ThreatMgr { public: - HostileReference(Unit* refUnit, ThreatMgr* threatMgr, float threat); - - Unit* GetOwner() const; - Unit* GetVictim() const { return getTarget(); } - - //================================================= - void AddThreat(float modThreat); - - void SetThreat(float threat) { AddThreat(threat - GetThreat()); } - - void addThreatPercent(int32 percent); - - [[nodiscard]] float GetThreat() const { return iThreat; } - - void ClearThreat() { removeReference(); } - - [[nodiscard]] bool IsOnline() const { return iOnline; } - [[nodiscard]] bool IsAvailable() const { return iOnline; } // unused for now - [[nodiscard]] bool IsOffline() const { return !iOnline; } // unused for now - - // used for temporary setting a threat and reducting it later again. - // the threat modification is stored - void setTempThreat(float threat) - { - addTempThreat(threat - GetThreat()); - } - - void addTempThreat(float threat) - { - iTempThreatModifier = threat; - if (iTempThreatModifier != 0.0f) - AddThreat(iTempThreatModifier); - } - - void resetTempThreat() - { - if (iTempThreatModifier != 0.0f) - { - AddThreat(-iTempThreatModifier); - iTempThreatModifier = 0.0f; - } - } - - float getTempThreatModifier() { return iTempThreatModifier; } - - //================================================= - // check, if source can reach target and set the status - void updateOnlineStatus(); - - void setOnlineOfflineState(bool isOnline); - //================================================= - - bool operator == (const HostileReference& hostileRef) const { return hostileRef.getUnitGuid() == getUnitGuid(); } - - //================================================= - - [[nodiscard]] ObjectGuid getUnitGuid() const { return iUnitGuid; } - - //================================================= - // reference is not needed anymore. realy delete it ! - - void removeReference(); - - //================================================= - - HostileReference* next() { return ((HostileReference*) Reference::next()); } - - //================================================= - - // Tell our refTo (target) object that we have a link - void targetObjectBuildLink() override; - - // Tell our refTo (taget) object, that the link is cut - void targetObjectDestroyLink() override; + typedef boost::heap::fibonacci_heap> threat_list_heap; + static const uint32 CLIENT_THREAT_UPDATE_INTERVAL = 1000u; + + static bool CanHaveThreatList(Unit const* who); + + ThreatMgr(Unit* owner); + // called from ::Create methods just after construction (once all fields on owner have been populated) + // should not be called from anywhere else + void Initialize(); + // called from Creature::Update (only creatures can have their own threat list) + // should not be called from anywhere else + void Update(uint32 tdiff); + + // never nullptr + Unit* GetOwner() const { return _owner; } + // can our owner have a threat list? + // identical to ThreatManager::CanHaveThreatList(GetOwner()) + bool CanHaveThreatList() const { return _ownerCanHaveThreatList; } + // returns the victim selected by the last SelectVictim call - this can be nullptr + Unit* GetCurrentVictim() const; + // returns an arbitrary non-offline victim from owner's threat list if one exists, nullptr otherwise + Unit* GetAnyTarget() const; + // selects a (potentially new) victim from the threat list and returns it - this can be nullptr + Unit* SelectVictim(); + + bool IsEngaged() const { return _ownerEngaged; } + // are there any entries in owner's threat list? + bool IsThreatListEmpty(bool includeOffline = false) const; + // is there a threat list entry on owner's threat list with victim == who? + bool IsThreatenedBy(ObjectGuid const& who, bool includeOffline = false) const; + // is there a threat list entry on owner's threat list with victim == who? + bool IsThreatenedBy(Unit const* who, bool includeOffline = false) const; + // returns ThreatReference amount if a ref exists, 0.0f otherwise + float GetThreat(Unit const* who, bool includeOffline = false) const; + size_t GetThreatListSize() const { return _sortedThreatList.size(); } + bool isThreatListEmpty() { return _sortedThreatList.empty(); } + // fastest of the three threat list getters - gets the threat list in "arbitrary" order + Acore::IteratorPair GetUnsortedThreatList() const { return { _sortedThreatList.begin(), _sortedThreatList.end() }; } + // slightly slower than GetUnsorted, but, well...sorted - only use it if you need the sorted property, of course + // note: current tank is NOT guaranteed to be the first entry in this list - check GetCurrentVictim separately if you want that! + Acore::IteratorPair GetSortedThreatList() const { return { _sortedThreatList.ordered_begin(), _sortedThreatList.ordered_end() }; } + // slowest of the three threat list getters (by far), but lets you modify the threat references + std::vector GetModifiableThreatList() const; + + // does any unit have a threat list entry with victim == this.owner? + bool IsThreateningAnyone(bool includeOffline = false) const; + // is there a threat list entry on who's threat list for this.owner? + bool IsThreateningTo(ObjectGuid const& who, bool includeOffline = false) const; + // is there a threat list entry on who's threat list for this.owner? + bool IsThreateningTo(Unit const* who, bool includeOffline = false) const; + auto const& GetThreatenedByMeList() const { return _threatenedByMe; } + + // Notify the ThreatManager that a condition changed that may impact refs' online state so it can re-evaluate + void UpdateOnlineStates(bool meThreateningOthers = true, bool othersThreateningMe = true); + ///== AFFECT MY THREAT LIST == + void AddThreat(Unit* target, float amount, SpellInfo const* spell = nullptr, bool ignoreModifiers = false, bool ignoreRedirects = false); + void ScaleThreat(Unit* target, float factor); + // Modify target's threat by +percent% + void ModifyThreatByPercent(Unit* target, int32 percent) { if (percent) ScaleThreat(target, 0.01f*float(100 + percent)); } + // Resets the specified unit's threat to zero + void ResetThreat(Unit* target) { ScaleThreat(target, 0.0f); } + // Sets the specified unit's threat to be equal to the highest entry on the threat list + void MatchUnitThreatToHighestThreat(Unit* target); + // Notify the ThreatManager that we have a new taunt aura (or a taunt aura expired) + void TauntUpdate(); + // Sets all threat refs in owner's threat list to have zero threat + void ResetAllThreat(); + // Removes specified target from the threat list + void ClearThreat(Unit* target); + // Removes all targets from the threat list (will cause evade in UpdateVictim if called) + void ClearAllThreat(); - // Tell our refFrom (source) object, that the link is cut (Target destroyed) - void sourceObjectDestroyLink() override; + // sends SMSG_THREAT_UPDATE to all nearby clients (used by client to forward threat list info to addons) + void SendThreatListToClients() const; + + ///== AFFECT OTHERS' THREAT LISTS == + // 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(); + // re-calculates the temporary threat modifier from auras on myself + void UpdateMyTempModifiers(); + // re-calculate SPELL_AURA_MOD_THREAT modifiers + void UpdateMySpellSchoolModifiers(); + + ///== REDIRECT SYSTEM == + // Register a redirection effect that redirects pct% of threat generated by owner to victim + void RegisterRedirectThreat(uint32 spellId, ObjectGuid const& victim, uint32 pct); + // Unregister a redirection effort for all victims + void UnregisterRedirectThreat(uint32 spellId); + // Unregister a redirection effect for a specific victim + void UnregisterRedirectThreat(uint32 spellId, ObjectGuid const& victim); private: - // Inform the source, that the status of that reference was changed - void fireStatusChanged(ThreatRefStatusChangeEvent& threatRefStatusChangeEvent); - - Unit* GetSourceUnit(); -private: - float iThreat; - float iTempThreatModifier; // used for taunt - ObjectGuid iUnitGuid; - bool iOnline; -}; - -//============================================================== -class ThreatMgr; - -class ThreatContainer -{ - friend class ThreatMgr; + Unit* const _owner; + bool _ownerCanHaveThreatList; + bool _ownerEngaged; + + static const CompareThreatLessThan CompareThreat; + static bool CompareReferencesLT(ThreatReference const* a, ThreatReference const* b, float aWeight); + static float CalculateModifiedThreat(float threat, Unit const* victim, SpellInfo const* spell); + + // send opcodes (all for my own threat list) + void SendClearAllThreatToClients() const; + void SendRemoveToClients(Unit const* victim) const; + void SendNewVictimToClients(ThreatReference const* victimRef) const; + + ///== MY THREAT LIST == + void PutThreatListRef(ObjectGuid const& guid, ThreatReference* ref); + void PurgeThreatListRef(ObjectGuid const& guid, bool sendRemove); + + uint32 _updateClientTimer; + threat_list_heap _sortedThreatList; + std::unordered_map _myThreatListEntries; + ThreatReference const* _currentVictimRef; + ThreatReference const* ReselectVictim(); + + ///== OTHERS' THREAT LISTS == + void PutThreatenedByMeRef(ObjectGuid const& guid, ThreatReference* ref); + void PurgeThreatenedByMeRef(ObjectGuid const& guid); + std::unordered_map _threatenedByMe; // these refs are entries for myself on other units' threat lists + float _singleSchoolModifiers[MAX_SPELL_SCHOOL]; // most spells are single school - we pre-calculate these and store them + mutable std::unordered_map::type, float> _multiSchoolModifiers; // these are calculated on demand + + // redirect system (is kind of dumb, but that's because none of the redirection spells actually have any aura effect associated with them, so spellscript needs to deal with it) + void UpdateRedirectInfo(); + std::vector> _redirectInfo; // current redirection targets and percentages (updated from registry in ThreatManager::UpdateRedirectInfo) + std::unordered_map> _redirectRegistry; // spellid -> (victim -> pct); all redirection effects on us (removal individually managed by spell scripts because blizzard is dumb) public: - typedef std::list StorageType; - - ThreatContainer() = default; - - ~ThreatContainer() { clearReferences(); } - - HostileReference* AddThreat(Unit* victim, float threat); - - void ModifyThreatByPercent(Unit* victim, int32 percent); + ThreatMgr(ThreatMgr const&) = delete; + ThreatMgr& operator=(ThreatMgr const&) = delete; - HostileReference* SelectNextVictim(Creature* attacker, HostileReference* currentVictim) const; - - void setDirty(bool isDirty) { iDirty = isDirty; } - - [[nodiscard]] bool isDirty() const { return iDirty; } - - [[nodiscard]] bool empty() const - { - return iThreatList.empty(); - } - - [[nodiscard]] HostileReference* getMostHated() const - { - return iThreatList.empty() ? nullptr : iThreatList.front(); - } - - HostileReference* getReferenceByTarget(Unit const* victim) const; - HostileReference* getReferenceByTarget(ObjectGuid const& guid) const; - - [[nodiscard]] StorageType const& GetThreatList() const { return iThreatList; } - -private: - void remove(HostileReference* hostileRef) - { - iThreatList.remove(hostileRef); - } - - void addReference(HostileReference* hostileRef) - { - iThreatList.push_back(hostileRef); - } - - void clearReferences(); - - // Sort the list if necessary - void update(); - - StorageType iThreatList; - bool iDirty{false}; + friend class ThreatReference; + friend struct CompareThreatLessThan; }; -//================================================= - -typedef HostileReference ThreatReference; - -class ThreatMgr +// Please check Game/Combat/ThreatManager.h for documentation on how this class works! +class ThreatReference { public: - friend class HostileReference; - - explicit ThreatMgr(Unit* owner); - - ~ThreatMgr() { clearReferences(); } - - Unit* SelectVictim() { return getHostileTarget(); } - Unit* GetCurrentVictim() const { if (ThreatReference* ref = getCurrentVictim()) return ref->GetVictim(); else return nullptr; } - Unit* GetAnyTarget() const { auto const& list = GetThreatList(); if (!list.empty()) return list.front()->getTarget(); return nullptr; } - - void clearReferences(); - - void AddThreat(Unit* victim, float threat, SpellSchoolMask schoolMask = SPELL_SCHOOL_MASK_NORMAL, SpellInfo const* threatSpell = nullptr); - void DoAddThreat(Unit* victim, float threat); - void ModifyThreatByPercent(Unit* victim, int32 percent); - float GetThreat(Unit* victim, bool alsoSearchOfflineList = false); - float GetThreatListSize() const { return GetThreatList().size(); } - float getThreatWithoutTemp(Unit* victim, bool alsoSearchOfflineList = false); - - [[nodiscard]] bool isThreatListEmpty() const { return iThreatContainer.empty(); } - [[nodiscard]] bool areThreatListsEmpty() const { return iThreatContainer.empty() && iThreatOfflineContainer.empty(); } - - Acore::IteratorPair::const_iterator> GetSortedThreatList() const { auto& list = iThreatContainer.GetThreatList(); return { list.cbegin(), list.cend() }; } - Acore::IteratorPair::const_iterator> GetUnsortedThreatList() const { return GetSortedThreatList(); } - - void processThreatEvent(ThreatRefStatusChangeEvent* threatRefStatusChangeEvent); - - bool isNeedUpdateToClient(uint32 time); - - [[nodiscard]] HostileReference* getCurrentVictim() const { return iCurrentVictim; } - - [[nodiscard]] Unit* GetOwner() const { return iOwner; } - - Unit* getHostileTarget(); - - void tauntApply(Unit* taunter); - void tauntFadeOut(Unit* taunter); - - void setCurrentVictim(HostileReference* hostileRef); - - void setDirty(bool isDirty) { iThreatContainer.setDirty(isDirty); } - - // Reset all aggro without modifying the threadlist. - void ResetThreat(Unit const* who) { if (auto* ref = FindReference(who, true)) ref->SetThreat(0.0f); } - void ResetAllThreat(); - - void ClearThreat(Unit const* who) { if (auto* ref = FindReference(who, true)) ref->removeReference(); } - void ClearAllThreat(); - - // Reset all aggro of unit in threadlist satisfying the predicate. - template void resetAggro(PREDICATE predicate) - { - ThreatContainer::StorageType& threatList = iThreatContainer.iThreatList; - if (threatList.empty()) - return; - - for (auto& ref : threatList) - { - if (predicate(ref->getTarget())) - { - ref->SetThreat(0); - setDirty(true); - } - } - } - - // methods to access the lists from the outside to do some dirty manipulation (scriping and such) - // I hope they are used as little as possible. - [[nodiscard]] ThreatContainer::StorageType const& GetThreatList() const { return iThreatContainer.GetThreatList(); } - [[nodiscard]] ThreatContainer::StorageType const& GetOfflineThreatList() const { return iThreatOfflineContainer.GetThreatList(); } - ThreatContainer& GetOnlineContainer() { return iThreatContainer; } - ThreatContainer& GetOfflineContainer() { return iThreatOfflineContainer; } - + enum TauntState { TAUNT_STATE_DETAUNT = -1, TAUNT_STATE_NONE = 0, TAUNT_STATE_TAUNT = 1 }; + enum OnlineState { ONLINE_STATE_ONLINE = 2, ONLINE_STATE_SUPPRESSED = 1, ONLINE_STATE_OFFLINE = 0 }; + + Unit* GetOwner() const { return _owner; } + Unit* GetVictim() const { return _victim; } + float GetThreat() const { return std::max(_baseAmount + (float)_tempModifier, 0.0f); } + OnlineState GetOnlineState() const { return _online; } + bool IsOnline() const { return (_online >= ONLINE_STATE_ONLINE); } + bool IsAvailable() const { return (_online > ONLINE_STATE_OFFLINE); } + bool IsOffline() const { return (_online <= ONLINE_STATE_OFFLINE); } + TauntState GetTauntState() const { return _taunted; } + bool IsTaunting() const { return _taunted == TAUNT_STATE_TAUNT; } + bool IsDetaunted() const { return _taunted == TAUNT_STATE_DETAUNT; } + + void SetThreat(float amount) { _baseAmount = amount; HeapNotifyChanged(); } + void AddThreat(float amount); + void ScaleThreat(float factor); + void ModifyThreatByPercent(int32 percent) { if (percent) ScaleThreat(0.01f*float(100 + percent)); } + void UpdateOnlineState(); + + void ClearThreat(bool sendRemove = true); // dealloc's this private: - HostileReference* FindReference(Unit const* who, bool includeOffline) const { if (auto* ref = iThreatContainer.getReferenceByTarget(who)) return ref; if (includeOffline) if (auto* ref = iThreatOfflineContainer.getReferenceByTarget(who)) return ref; return nullptr; } - - void _addThreat(Unit* victim, float threat); + ThreatReference(ThreatMgr* mgr, Unit* victim, float amount) : _owner(mgr->_owner), _mgr(mgr), _victim(victim), _baseAmount(amount), _tempModifier(0), _online(SelectOnlineState()), _taunted(TAUNT_STATE_NONE) { } + static bool FlagsAllowFighting(Unit const* a, Unit const* b); + OnlineState SelectOnlineState(); + void UpdateTauntState(bool victimIsTaunting); + Unit* const _owner; + ThreatMgr* const _mgr; + void HeapNotifyIncreased() { _mgr->_sortedThreatList.increase(_handle); } + void HeapNotifyDecreased() { _mgr->_sortedThreatList.decrease(_handle); } + void HeapNotifyChanged() { _mgr->_sortedThreatList.update(_handle); } + Unit* const _victim; + float _baseAmount; + int32 _tempModifier; // Temporary effects (auras with SPELL_AURA_MOD_TOTAL_THREAT) - set from victim's threatmanager in ThreatManager::UpdateMyTempModifiers + OnlineState _online; + TauntState _taunted; + ThreatMgr::threat_list_heap::handle_type _handle; +public: + ThreatReference(ThreatReference const&) = delete; + ThreatReference& operator=(ThreatReference const&) = delete; - HostileReference* iCurrentVictim; - Unit* iOwner; - uint32 iUpdateTimer; - ThreatContainer iThreatContainer; - ThreatContainer iThreatOfflineContainer; + friend class ThreatMgr; + friend struct CompareThreatLessThan; }; -//================================================= +inline bool CompareThreatLessThan::operator()(ThreatReference const* a, ThreatReference const* b) const { return ThreatMgr::CompareReferencesLT(a, b, 1.0f); } -namespace Acore -{ - // Binary predicate for sorting HostileReferences based on threat value - class ThreatOrderPred - { - public: - ThreatOrderPred(bool ascending = false) : m_ascending(ascending) {} - bool operator() (HostileReference const* a, HostileReference const* b) const - { - return m_ascending ? a->GetThreat() < b->GetThreat() : a->GetThreat() > b->GetThreat(); - } - private: - const bool m_ascending; - }; -} #endif diff --git a/src/server/game/Combat/UnitEvents.h b/src/server/game/Combat/UnitEvents.h deleted file mode 100644 index 1f106898fb5dcf..00000000000000 --- a/src/server/game/Combat/UnitEvents.h +++ /dev/null @@ -1,134 +0,0 @@ -/* - * This file is part of the AzerothCore Project. See AUTHORS file for Copyright information - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU Affero General Public License as published by the - * Free Software Foundation; either version 3 of the License, or (at your - * option) any later version. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License along - * with this program. If not, see . - */ - -#ifndef _UNITEVENTS -#define _UNITEVENTS - -#include "Common.h" - -class ThreatContainer; -class ThreatMgr; -class HostileReference; - -//============================================================== -//============================================================== - -enum UNIT_EVENT_TYPE -{ - // Player/Pet changed on/offline status - UEV_THREAT_REF_ONLINE_STATUS = 1 << 0, - - // Threat for Player/Pet changed - UEV_THREAT_REF_THREAT_CHANGE = 1 << 1, - - // Player/Pet will be removed from list (dead) [for internal use] - UEV_THREAT_REF_REMOVE_FROM_LIST = 1 << 2, - - // Player/Pet entered/left water or some other place where it is/was not accessible for the creature - UEV_THREAT_REF_ASSECCIBLE_STATUS = 1 << 3, - - // Threat list is going to be sorted (if dirty flag is set) - UEV_THREAT_SORT_LIST = 1 << 4, - - // New target should be fetched, could tbe the current target as well - UEV_THREAT_SET_NEXT_TARGET = 1 << 5, - - // A new victim (target) was set. Could be nullptr - UEV_THREAT_VICTIM_CHANGED = 1 << 6, - - // Future use - //UEV_UNIT_KILLED = 1<<7, - - //Future use - //UEV_UNIT_HEALTH_CHANGE = 1<<8, -}; - -#define UEV_THREAT_REF_EVENT_MASK (UEV_THREAT_REF_ONLINE_STATUS | UEV_THREAT_REF_THREAT_CHANGE | UEV_THREAT_REF_REMOVE_FROM_LIST | UEV_THREAT_REF_ASSECCIBLE_STATUS) -#define UEV_THREAT_MANAGER_EVENT_MASK (UEV_THREAT_SORT_LIST | UEV_THREAT_SET_NEXT_TARGET | UEV_THREAT_VICTIM_CHANGED) -#define UEV_ALL_EVENT_MASK (0xffffffff) - -// Future use -//#define UEV_UNIT_EVENT_MASK (UEV_UNIT_KILLED | UEV_UNIT_HEALTH_CHANGE) - -//============================================================== - -class UnitBaseEvent -{ -private: - uint32 iType; -public: - UnitBaseEvent(uint32 pType) { iType = pType; } - [[nodiscard]] uint32 getType() const { return iType; } - [[nodiscard]] bool matchesTypeMask(uint32 pMask) const { return iType & pMask; } - - void setType(uint32 pType) { iType = pType; } -}; - -//============================================================== - -class ThreatRefStatusChangeEvent : public UnitBaseEvent -{ -private: - HostileReference* iHostileReference; - union - { - float iFValue; - int32 iIValue; - bool iBValue; - }; - ThreatMgr* iThreatMgr; -public: - ThreatRefStatusChangeEvent(uint32 pType) : UnitBaseEvent(pType), iThreatMgr(nullptr) { iHostileReference = nullptr; } - - ThreatRefStatusChangeEvent(uint32 pType, HostileReference* pHostileReference) : UnitBaseEvent(pType), iThreatMgr(nullptr) { iHostileReference = pHostileReference; } - - ThreatRefStatusChangeEvent(uint32 pType, HostileReference* pHostileReference, float pValue) : UnitBaseEvent(pType), iThreatMgr(nullptr) { iHostileReference = pHostileReference; iFValue = pValue; } - - ThreatRefStatusChangeEvent(uint32 pType, HostileReference* pHostileReference, bool pValue) : UnitBaseEvent(pType), iThreatMgr(nullptr) { iHostileReference = pHostileReference; iBValue = pValue; } - - [[nodiscard]] int32 getIValue() const { return iIValue; } - - [[nodiscard]] float getFValue() const { return iFValue; } - - [[nodiscard]] bool getBValue() const { return iBValue; } - - void setBValue(bool pValue) { iBValue = pValue; } - - [[nodiscard]] HostileReference* getReference() const { return iHostileReference; } - - void setThreatMgr(ThreatMgr* pThreatMgr) { iThreatMgr = pThreatMgr; } - - [[nodiscard]] ThreatMgr* GetThreatMgr() const { return iThreatMgr; } -}; - -//============================================================== - -class ThreatMgrEvent : public ThreatRefStatusChangeEvent -{ -private: - ThreatContainer* iThreatContainer; -public: - ThreatMgrEvent(uint32 pType) : ThreatRefStatusChangeEvent(pType), iThreatContainer(nullptr) {} - ThreatMgrEvent(uint32 pType, HostileReference* pHostileReference) : ThreatRefStatusChangeEvent(pType, pHostileReference), iThreatContainer(nullptr) {} - - void setThreatContainer(ThreatContainer* pThreatContainer) { iThreatContainer = pThreatContainer; } - - [[nodiscard]] ThreatContainer* getThreatContainer() const { return iThreatContainer; } -}; - -//============================================================== -#endif diff --git a/src/server/game/Entities/Creature/Creature.cpp b/src/server/game/Entities/Creature/Creature.cpp index 0af7906a650b53..3f6548a0d62ebb 100644 --- a/src/server/game/Entities/Creature/Creature.cpp +++ b/src/server/game/Entities/Creature/Creature.cpp @@ -180,11 +180,7 @@ bool AssistDelayEvent::Execute(uint64 /*e_time*/, uint32 /*p_time*/) if (assistant && assistant->CanAssistTo(m_owner, victim)) { assistant->SetNoCallAssistance(true); - assistant->CombatStart(victim); - if (assistant->IsAIEnabled) - assistant->AI()->AttackStart(victim); - - assistant->SetLastDamagedTimePtr(m_owner->GetLastDamagedTimePtr()); + assistant->EngageWithTarget(victim); } } } @@ -208,8 +204,8 @@ bool TemporaryThreatModifierEvent::Execute(uint64 /*e_time*/, uint32 /*p_time*/) { if (m_owner.IsInCombatWith(victim)) { - m_owner.GetThreatMgr().ModifyThreatByPercent(victim, -100); // Reset threat to zero. - m_owner.GetThreatMgr().AddThreat(victim, m_threatValue); // Set to the previous value it had, first before modification. + m_owner.GetThreatManager().ModifyThreatByPercent(victim, -100); // Reset threat to zero. + m_owner.GetThreatManager().AddThreat(victim, m_threatValue); // Set to the previous value it had, first before modification. } } @@ -651,6 +647,7 @@ bool Creature::UpdateEntry(uint32 Entry, const CreatureData* data, bool changele AIM_Initialize(); } + GetThreatManager().UpdateOnlineStates(true, true); return true; } @@ -728,6 +725,8 @@ void Creature::Update(uint32 diff) if (!IsAlive()) break; + GetThreatManager().Update(diff); + // if creature is charmed, switch to charmed AI if (NeedChangeAI) { @@ -885,22 +884,7 @@ void Creature::Update(uint32 diff) } }; - if (GetThreatMgr().GetThreatListSize() <= 1) - { - EnterEvade(); - } - else - { - if (HostileReference* ref = GetThreatMgr().GetOnlineContainer().getReferenceByTarget(m_cannotReachTarget)) - { - ref->removeReference(); - SetCannotReachTarget(); - } - else - { - EnterEvade(); - } - } + EnterEvade(); } } } @@ -1213,9 +1197,138 @@ bool Creature::Create(ObjectGuid::LowType guidlow, Map* map, uint32 phaseMask, u if (GetCreatureTemplate()->flags_extra & CREATURE_FLAG_EXTRA_IGNORE_PATHFINDING) AddUnitState(UNIT_STATE_IGNORE_PATHFINDING); + GetThreatManager().Initialize(); return true; } +Unit* Creature::SelectVictim() +{ + Unit* target = nullptr; + + ThreatMgr& mgr = GetThreatManager(); + + if (mgr.CanHaveThreatList()) + { + target = mgr.SelectVictim(); + while (!target) + { + Unit* newTarget = nullptr; + // nothing found to attack - try to find something we're in combat with (but don't have a threat entry for yet) and start attacking it + for (auto const& pair : GetCombatManager().GetPvECombatRefs()) + { + newTarget = pair.second->GetOther(this); + if (!mgr.IsThreatenedBy(newTarget, true)) + { + mgr.AddThreat(newTarget, 0.0f, nullptr, true, true); + break; + } + else + newTarget = nullptr; + } + if (!newTarget) + break; + target = mgr.SelectVictim(); + } + } + else if (!HasReactState(REACT_PASSIVE)) + { + // We're a player pet, probably + target = getAttackerForHelper(); + if (!target && IsSummon()) + { + if (Unit* owner = ToTempSummon()->GetOwner()) + { + if (owner->IsInCombat()) + target = owner->getAttackerForHelper(); + if (!target) + { + for (ControlSet::const_iterator itr = owner->m_Controlled.begin(); itr != owner->m_Controlled.end(); ++itr) + { + if ((*itr)->IsInCombat()) + { + target = (*itr)->getAttackerForHelper(); + if (target) + break; + } + } + } + } + } + } + else + return nullptr; + + if (target && _IsTargetAcceptable(target) && CanCreatureAttack(target)) + { + if (!HasSpellFocus(nullptr)) + SetInFront(target); + return target; + } + + /// @todo a vehicle may eat some mob, so mob should not evade + if (GetVehicle()) + return nullptr; + + // search nearby enemy before enter evade mode + if (HasReactState(REACT_AGGRESSIVE)) + { + target = SelectNearestTargetInAttackDistance(m_CombatDistance ? m_CombatDistance : ATTACK_DISTANCE); + + if (target && _IsTargetAcceptable(target) && CanCreatureAttack(target)) + return target; + } + + Unit::AuraEffectList const& iAuras = GetAuraEffectsByType(SPELL_AURA_MOD_INVISIBILITY); + if (!iAuras.empty()) + { + for (Unit::AuraEffectList::const_iterator itr = iAuras.begin(); itr != iAuras.end(); ++itr) + { + if ((*itr)->GetBase()->IsPermanent()) + { + AI()->EnterEvadeMode(CreatureAI::EVADE_REASON_OTHER); + break; + } + } + return nullptr; + } + + // enter in evade mode in other case + AI()->EnterEvadeMode(CreatureAI::EVADE_REASON_NO_HOSTILES); + + return nullptr; +} + +void Creature::AtEnterCombat() +{ + Unit::AtEnterCombat(); + + if (!(GetCreatureTemplate()->type_flags & CREATURE_TYPE_FLAG_ALLOW_MOUNTED_COMBAT)) + Dismount(); + + if (IsPet() || IsGuardian()) // update pets' speed for catchup OOC speed + { + UpdateSpeed(MOVE_RUN, true); + UpdateSpeed(MOVE_SWIM, true); + UpdateSpeed(MOVE_FLIGHT, true); + } +} + +void Creature::AtExitCombat() +{ + Unit::AtExitCombat(); + + ClearUnitState(UNIT_STATE_ATTACK_PLAYER); + if (HasFlag(UNIT_DYNAMIC_FLAGS, UNIT_DYNFLAG_TAPPED)) + SetUInt32Value(UNIT_DYNAMIC_FLAGS, GetCreatureTemplate()->dynamicflags); + + if (IsPet() || IsGuardian()) // update pets' speed for catchup OOC speed + { + UpdateSpeed(MOVE_RUN, true); + UpdateSpeed(MOVE_SWIM, true); + UpdateSpeed(MOVE_FLIGHT, true); + } +} + bool Creature::isCanInteractWithBattleMaster(Player* player, bool msg) const { if (!IsBattleMaster()) @@ -3811,11 +3924,11 @@ void Creature::ModifyThreatPercentTemp(Unit* victim, int32 percent, Milliseconds { if (victim) { - float currentThreat = GetThreatMgr().GetThreat(victim); + float currentThreat = GetThreatManager().GetThreat(victim); if (percent != 0.0f) { - GetThreatMgr().ModifyThreatByPercent(victim, percent); + GetThreatManager().ModifyThreatByPercent(victim, percent); } TemporaryThreatModifierEvent* pEvent = new TemporaryThreatModifierEvent(*this, victim->GetGUID(), currentThreat); diff --git a/src/server/game/Entities/Creature/Creature.h b/src/server/game/Entities/Creature/Creature.h index 696dd169947167..f9127a3797e680 100644 --- a/src/server/game/Entities/Creature/Creature.h +++ b/src/server/game/Entities/Creature/Creature.h @@ -135,6 +135,18 @@ class Creature : public Unit, public GridObject, public MovableMapObje [[nodiscard]] bool IsImmuneToKnockback() const; [[nodiscard]] bool IsAvoidingAOE() const { return GetCreatureTemplate()->flags_extra & CREATURE_FLAG_EXTRA_AVOID_AOE; } + Unit* SelectVictim(); + + using Unit::IsImmuneToAll; + using Unit::SetImmuneToAll; + void SetImmuneToAll(bool apply) override { Unit::SetImmuneToAll(apply, HasReactState(REACT_PASSIVE)); } + using Unit::IsImmuneToPC; + using Unit::SetImmuneToPC; + void SetImmuneToPC(bool apply) override { Unit::SetImmuneToPC(apply, HasReactState(REACT_PASSIVE)); } + using Unit::IsImmuneToNPC; + using Unit::SetImmuneToNPC; + void SetImmuneToNPC(bool apply) override { Unit::SetImmuneToNPC(apply, HasReactState(REACT_PASSIVE)); } + uint8 getLevelForTarget(WorldObject const* target) const override; // overwrite Unit::getLevelForTarget for boss level support [[nodiscard]] bool IsInEvadeMode() const { return HasUnitState(UNIT_STATE_EVADE); } @@ -363,8 +375,6 @@ class Creature : public Unit, public GridObject, public MovableMapObje [[nodiscard]] CreatureGroup* GetFormation() { return m_formation; } void SetFormation(CreatureGroup* formation) { m_formation = formation; } - Unit* SelectVictim(); - void SetDisableReputationGain(bool disable) { DisableReputationGain = disable; } [[nodiscard]] bool IsReputationGainDisabled() const { return DisableReputationGain; } [[nodiscard]] bool IsDamageEnoughForLootingAndReward() const; @@ -430,6 +440,9 @@ class Creature : public Unit, public GridObject, public MovableMapObje std::string GetDebugInfo() const override; + void AtEnterCombat() override; + void AtExitCombat() override; + protected: bool CreateFromProto(ObjectGuid::LowType guidlow, uint32 Entry, uint32 vehId, const CreatureData* data = nullptr); bool InitEntry(uint32 entry, const CreatureData* data = nullptr); diff --git a/src/server/game/Entities/Object/Object.h b/src/server/game/Entities/Object/Object.h index 682d0e8bddf290..b16ca150aa7915 100644 --- a/src/server/game/Entities/Object/Object.h +++ b/src/server/game/Entities/Object/Object.h @@ -434,8 +434,9 @@ class WorldObject : public Object, public WorldLocation virtual void SetPhaseMask(uint32 newPhaseMask, bool update); [[nodiscard]] uint32 GetPhaseMask() const { return m_phaseMask; } - bool InSamePhase(WorldObject const* obj) const { return InSamePhase(obj->GetPhaseMask()); } [[nodiscard]] bool InSamePhase(uint32 phasemask) const { return m_useCombinedPhases ? GetPhaseMask() & phasemask : GetPhaseMask() == phasemask; } + bool InSamePhase(WorldObject const* obj) const { return obj && InSamePhase(obj->GetPhaseMask()); } + static bool InSamePhase(WorldObject const* a, WorldObject const* b) { return a && a->InSamePhase(b); } [[nodiscard]] uint32 GetZoneId() const; [[nodiscard]] uint32 GetAreaId() const; diff --git a/src/server/game/Entities/Pet/Pet.cpp b/src/server/game/Entities/Pet/Pet.cpp index 88cf34aa7a2897..5b3eea979783bd 100644 --- a/src/server/game/Entities/Pet/Pet.cpp +++ b/src/server/game/Entities/Pet/Pet.cpp @@ -2329,6 +2329,8 @@ bool Pet::Create(ObjectGuid::LowType guidlow, Map* map, uint32 phaseMask, uint32 SetUnitFlag2(UNIT_FLAG2_REGENERATE_POWER); SetSheath(SHEATH_STATE_MELEE); + GetThreatManager().Initialize(); + return true; } diff --git a/src/server/game/Entities/Player/Player.cpp b/src/server/game/Entities/Player/Player.cpp index deb94a504b32db..7810256266e7a2 100644 --- a/src/server/game/Entities/Player/Player.cpp +++ b/src/server/game/Entities/Player/Player.cpp @@ -704,6 +704,8 @@ bool Player::Create(ObjectGuid::LowType guidlow, CharacterCreateInfo* createInfo UpdateThorns(); CheckAllAchievementCriteria(); + GetThreatManager().Initialize(); + return true; } @@ -2256,8 +2258,6 @@ void Player::SetInWater(bool apply) // remove auras that need water/land RemoveAurasWithInterruptFlags(apply ? AURA_INTERRUPT_FLAG_NOT_ABOVEWATER : AURA_INTERRUPT_FLAG_NOT_UNDERWATER); - - getHostileRefMgr().updateThreatTables(); } bool Player::IsInAreaTriggerRadius(AreaTrigger const* trigger, float delta) const @@ -2293,11 +2293,9 @@ void Player::SetGameMaster(bool on) SetUnitFlag2(UNIT_FLAG2_ALLOW_CHEAT_SPELLS); if (Pet* pet = GetPet()) - { if (GetSession()->IsGMAccount()) pet->SetFaction(FACTION_FRIENDLY); - pet->getHostileRefMgr().setOnlineOfflineState(false); - } + if (HasByteFlag(UNIT_FIELD_BYTES_2, 1, UNIT_BYTE2_FLAG_FFA_PVP)) { RemoveByteFlag(UNIT_FIELD_BYTES_2, 1, UNIT_BYTE2_FLAG_FFA_PVP); @@ -2305,7 +2303,6 @@ void Player::SetGameMaster(bool on) } ResetContestedPvP(); - getHostileRefMgr().setOnlineOfflineState(false); CombatStopWithPets(); SetPhaseMask(uint32(PHASEMASK_ANYWHERE), false); // see and visible in all phases @@ -2329,7 +2326,7 @@ void Player::SetGameMaster(bool on) if (Pet* pet = GetPet()) { pet->SetFaction(GetFaction()); - pet->getHostileRefMgr().setOnlineOfflineState(true); + pet->GetThreatManager().UpdateOnlineStates(); } // restore FFA PvP Server state @@ -2344,7 +2341,6 @@ void Player::SetGameMaster(bool on) // restore FFA PvP area state, remove not allowed for GM mounts UpdateArea(m_areaUpdateId); - getHostileRefMgr().setOnlineOfflineState(true); m_serverSideVisibilityDetect.SetValue(SERVERSIDE_VISIBILITY_GM, SEC_PLAYER); } @@ -2360,8 +2356,6 @@ void Player::SetGMVisible(bool on) RemoveAurasDueToSpell(VISUAL_AURA); m_ExtraFlags &= ~PLAYER_EXTRA_GM_INVISIBLE; m_serverSideVisibility.SetValue(SERVERSIDE_VISIBILITY_GM, SEC_PLAYER); - - getHostileRefMgr().setOnlineOfflineState(false); CombatStopWithPets(); } else @@ -10785,7 +10779,6 @@ void Player::CleanupAfterTaxiFlight() m_taxi.ClearTaxiDestinations(); // not destinations, clear source node Dismount(); RemoveUnitFlag(UNIT_FLAG_DISABLE_MOVE | UNIT_FLAG_TAXI_FLIGHT); - getHostileRefMgr().setOnlineOfflineState(true); } void Player::ContinueTaxiFlight() diff --git a/src/server/game/Entities/Player/Player.h b/src/server/game/Entities/Player/Player.h index 3077a22741829a..5530d5cad253da 100644 --- a/src/server/game/Entities/Player/Player.h +++ b/src/server/game/Entities/Player/Player.h @@ -1893,6 +1893,7 @@ class Player : public Unit, public GridObject void UpdateAfkReport(time_t currTime); void UpdatePvPFlag(time_t currTime); + void SetContestedPvP(Player* attackedPlayer = nullptr); void UpdateFFAPvPFlag(time_t currTime); void UpdateContestedPvP(uint32 currTime); void SetContestedPvPTimer(uint32 newTime) {m_contestedPvPTimer = newTime;} @@ -2057,6 +2058,7 @@ class Player : public Unit, public GridObject bool UpdatePosition(const Position& pos, bool teleport = false) { return UpdatePosition(pos.GetPositionX(), pos.GetPositionY(), pos.GetPositionZ(), pos.GetOrientation(), teleport); } void ProcessTerrainStatusUpdate() override; + void AtExitCombat() override; void SendMessageToSet(WorldPacket const* data, bool self) const override { SendMessageToSetInRange(data, GetVisibilityRange(), self, true); } // pussywizard! void SendMessageToSetInRange(WorldPacket const* data, float dist, bool self, bool includeMargin = false, Player const* skipped_rcvr = nullptr) const override; // pussywizard! diff --git a/src/server/game/Entities/Player/PlayerMisc.cpp b/src/server/game/Entities/Player/PlayerMisc.cpp index 8c32b2cae0f410..0e75ff4550e9aa 100644 --- a/src/server/game/Entities/Player/PlayerMisc.cpp +++ b/src/server/game/Entities/Player/PlayerMisc.cpp @@ -357,6 +357,26 @@ void Player::UpdateAfkReport(time_t currTime) } } +void Player::SetContestedPvP(Player* attackedPlayer) +{ + if (attackedPlayer && (attackedPlayer == this || (duel && duel->Opponent == attackedPlayer))) + return; + + SetContestedPvPTimer(30000); + if (!HasUnitState(UNIT_STATE_ATTACK_PLAYER)) + { + AddUnitState(UNIT_STATE_ATTACK_PLAYER); + SetFlag(PLAYER_FLAGS, PLAYER_FLAGS_CONTESTED_PVP); + } + for (Unit* unit : m_Controlled) + { + if (!unit->HasUnitState(UNIT_STATE_ATTACK_PLAYER)) + { + unit->AddUnitState(UNIT_STATE_ATTACK_PLAYER); + } + } +} + void Player::UpdateContestedPvP(uint32 diff) { if (!m_contestedPvPTimer || IsInCombat()) diff --git a/src/server/game/Entities/Player/PlayerUpdates.cpp b/src/server/game/Entities/Player/PlayerUpdates.cpp index 1189df5dac42ec..48f8802e8a95a9 100644 --- a/src/server/game/Entities/Player/PlayerUpdates.cpp +++ b/src/server/game/Entities/Player/PlayerUpdates.cpp @@ -399,8 +399,7 @@ void Player::Update(uint32 p_time) { m_hostileReferenceCheckTimer = 15000; if (!GetMap()->IsDungeon()) - getHostileRefMgr().deleteReferencesOutOfRange( - GetVisibilityRange()); + GetCombatManager().EndCombatBeyondRange(GetVisibilityRange(), true); } else m_hostileReferenceCheckTimer -= p_time; @@ -2287,3 +2286,15 @@ void Player::ProcessTerrainStatusUpdate() else m_MirrorTimerFlags &= ~(UNDERWATER_INWATER | UNDERWATER_INLAVA | UNDERWATER_INSLIME | UNDERWATER_INDARKWATER); } + +void Player::AtExitCombat() +{ + Unit::AtExitCombat(); + UpdatePotionCooldown(); + + if (getClass() == CLASS_DEATH_KNIGHT) + for (uint8 i = 0; i < MAX_RUNES; ++i) + { + SetGracePeriod(i, 0xFFFFFFFF); + } +} diff --git a/src/server/game/Entities/Unit/Unit.cpp b/src/server/game/Entities/Unit/Unit.cpp index 48465c639a0dad..f39a4cc107c9cd 100644 --- a/src/server/game/Entities/Unit/Unit.cpp +++ b/src/server/game/Entities/Unit/Unit.cpp @@ -276,27 +276,12 @@ SpellInfo const* ProcEventInfo::GetSpellInfo() const Unit::Unit(bool isWorldObject) : WorldObject(isWorldObject), m_movedByPlayer(nullptr), m_lastSanctuaryTime(0), - IsAIEnabled(false), - NeedChangeAI(false), - m_ControlledByPlayer(false), - m_CreatedByPlayer(false), - movespline(new Movement::MoveSpline()), - i_AI(nullptr), - i_disabledAI(nullptr), - m_realRace(0), - m_race(0), - m_AutoRepeatFirstCast(false), - m_procDeep(0), - m_removedAurasCount(0), - i_motionMaster(new MotionMaster(this)), - m_regenTimer(0), - m_ThreatMgr(this), - m_vehicle(nullptr), - m_vehicleKit(nullptr), - m_unitTypeMask(UNIT_MASK_NONE), - m_HostileRefMgr(this), - m_comboTarget(nullptr), - m_comboPoints(0) + IsAIEnabled(false), NeedChangeAI(false), m_ControlledByPlayer(false), + movespline(new Movement::MoveSpline()), i_AI(nullptr), i_disabledAI(nullptr), + m_AutoRepeatFirstCast(false), m_procDeep(0), m_removedAurasCount(0), + i_motionMaster(new MotionMaster(this)), m_regenTimer(0), m_vehicle(nullptr), + m_vehicleKit(nullptr), m_unitTypeMask(UNIT_MASK_NONE), m_Diminishing(), m_combatManager(this), + m_threatManager(this), m_comboTarget(nullptr), m_comboPoints(0) { #ifdef _MSC_VER #pragma warning(default:4355) @@ -366,19 +351,13 @@ Unit::Unit(bool isWorldObject) : WorldObject(isWorldObject), m_modSpellHitChance = 17.0f; m_baseSpellCritChance = 5; - m_CombatTimer = 0; m_lastManaUse = 0; - for (uint8 i = 0; i < MAX_SPELL_SCHOOL; ++i) - m_threatModifier[i] = 1.0f; - for (uint8 i = 0; i < MAX_MOVE_TYPE; ++i) m_speed_rate[i] = 1.0f; m_charmInfo = nullptr; - _redirectThreatInfo = RedirectThreatInfo(); - // remove aurastates allowing special moves for (uint8 i = 0; i < MAX_REACTIVE; ++i) m_reactiveTimer[i] = 0; @@ -508,24 +487,7 @@ void Unit::Update(uint32 p_time) _UpdateSpells( p_time ); - if (CanHaveThreatList() && GetThreatMgr().isNeedUpdateToClient(p_time)) - SendThreatListUpdate(); - - // update combat timer only for players and pets (only pets with PetAI) - if (IsInCombat() && (GetTypeId() == TYPEID_PLAYER || ((IsPet() || HasUnitTypeMask(UNIT_MASK_CONTROLABLE_GUARDIAN)) && IsControlledByPlayer()))) - { - // Check UNIT_STATE_MELEE_ATTACKING or UNIT_STATE_CHASE (without UNIT_STATE_FOLLOW in this case) so pets can reach far away - // targets without stopping half way there and running off. - // These flags are reset after target dies or another command is given. - if (m_HostileRefMgr.IsEmpty()) - { - // m_CombatTimer set at aura start and it will be freeze until aura removing - if (m_CombatTimer <= p_time) - ClearInCombat(); - else - m_CombatTimer -= p_time; - } - } + m_combatManager.Update(p_time); // not implemented before 3.0.2 // xinef: if attack time > 0, reduce by diff @@ -1113,12 +1075,7 @@ uint32 Unit::DealDamage(Unit* attacker, Unit* victim, uint32 damage, CleanDamage if (attacker) { - if (spellProto && victim->CanHaveThreatList() && !victim->HasUnitState(UNIT_STATE_EVADE) && !victim->IsInCombatWith(attacker)) - { - victim->CombatStart(attacker, !(spellProto->AttributesEx3 & SPELL_ATTR3_SUPRESS_TARGET_PROCS)); - } - - victim->AddThreat(attacker, float(damage), damageSchoolMask, spellProto); + victim->GetThreatManager().AddThreat(attacker, float(damage), spellProto); } } else // victim is a player @@ -2101,15 +2058,15 @@ ThornsDamage Unit::CalculateThorns(Unit* attacker, Unit* victim, bool calcMisses //auto otherThorns = shieldOwner->GetUInt32Value(UNIT_FIELD_THORNS); //thornsDmg.ThornsDamageMap[SPELL_SCHOOL_MASK_NORMAL] += otherThorns; - SpellInfo const* thornsSpellInfo = sSpellMgr->GetSpellInfo(1570001); - - for (auto tDamage : thornsDmg.ThornsDamageMap) - { - if (Player* player = shieldOwner->ToPlayer()) - player->ApplySpellMod(thornsSpellInfo->Id, SPELLMOD_THORNS_DAMAGE_DONE, tDamage.second); + if (SpellInfo const* thornsSpellInfo = sSpellMgr->GetSpellInfo(1570001)) { + for (auto tDamage : thornsDmg.ThornsDamageMap) + { + if (Player* player = shieldOwner->ToPlayer()) + player->ApplySpellMod(thornsSpellInfo->Id, SPELLMOD_THORNS_DAMAGE_DONE, tDamage.second); - thornsDmg.TotalDamage += tDamage.second; + thornsDmg.TotalDamage += tDamage.second; } + } return thornsDmg; } @@ -2690,7 +2647,7 @@ void Unit::AttackerStateUpdate(Unit* victim, WeaponAttackType attType /*= BASE_A // CombatStart puts the target into stand state, so we need to cache sit state here to know if we should crit later const bool sittingVictim = victim->GetTypeId() == TYPEID_PLAYER && (victim->IsSitState() || victim->getStandState() == UNIT_STAND_STATE_SLEEP); - CombatStart(victim); + AttackedTarget(victim, true); RemoveAurasWithInterruptFlags(AURA_INTERRUPT_FLAG_MELEE_ATTACK); if (attType != BASE_ATTACK && attType != OFF_ATTACK) @@ -7276,11 +7233,7 @@ bool Unit::Attack(Unit* victim, bool meleeAttack) // ToCreature()->SetCombatStartPosition(GetPositionX(), GetPositionY(), GetPositionZ()); if (creature && !(IsControllableGuardian() && IsControlledByPlayer())) { - // should not let player enter combat by right clicking target - doesn't helps - SetInCombatWith(victim); - if (victim->GetTypeId() == TYPEID_PLAYER) - victim->SetInCombatWith(this); - AddThreat(victim, 0.0f); + EngageWithTarget(victim); // ensure that anything we're attacking has threat creature->SendAIReaction(AI_REACTION_HOSTILE); @@ -7338,7 +7291,25 @@ bool Unit::AttackStop() return true; } -void Unit::CombatStop(bool includingCast) +void Unit::ValidateAttackersAndOwnTarget() +{ + // iterate attackers + std::vector toRemove; + AttackerSet const& attackers = getAttackers(); + for (Unit* attacker : attackers) + if (!attacker->IsValidAttackTarget(this)) + toRemove.push_back(attacker); + + for (Unit* attacker : toRemove) + attacker->AttackStop(); + + // remove our own victim + if (Unit* victim = GetVictim()) + if (!IsValidAttackTarget(victim)) + AttackStop(); +} + +void Unit::CombatStop(bool includingCast, bool mutualPvP) { if (includingCast && IsNonMeleeSpellCast(false)) InterruptNonMeleeSpells(false); @@ -7347,20 +7318,23 @@ void Unit::CombatStop(bool includingCast) RemoveAllAttackers(); if (GetTypeId() == TYPEID_PLAYER) ToPlayer()->SendAttackSwingCancelAttack(); // melee and ranged forced attack cancel - ClearInCombat(); AddComboPoints(0); - // xinef: just in case - if (IsPetInCombat() && GetTypeId() != TYPEID_PLAYER) - ClearInPetCombat(); + if (mutualPvP) + ClearInCombat(); + else + { // vanish and brethren are weird + m_combatManager.EndAllPvECombat(); + m_combatManager.SuppressPvPCombat(); + } } void Unit::CombatStopWithPets(bool includingCast) { CombatStop(includingCast); - for (ControlSet::const_iterator itr = m_Controlled.begin(); itr != m_Controlled.end(); ++itr) - (*itr)->CombatStop(includingCast); + for (Unit* minion : m_Controlled) + minion->CombatStop(includingCast); } bool Unit::isAttackingPlayer() const @@ -7742,6 +7716,7 @@ void Unit::SetMinion(Minion* minion, bool apply) } } } + UpdatePetCombatState(); } void Unit::GetAllMinionsByEntry(std::list& Minions, uint32 entry) @@ -7852,6 +7827,7 @@ void Unit::SetCharm(Unit* charm, bool apply) m_Controlled.erase(charm); } + UpdatePetCombatState(); } void Unit::DealHeal(HealInfo& healInfo) @@ -8021,6 +7997,9 @@ void Unit::RemoveAllControlled(bool onDeath /*= false*/) LOG_ERROR("entities.unit", "Unit {} is trying to release unit {} which is neither charmed nor owned by it", GetEntry(), target->GetEntry()); } } + + if (!IsPet()) // pets don't use the flag for this + RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_PET_IN_COMBAT); // m_controlled is now empty, so we know none of our minions are in combat } Unit* Unit::GetNextRandomRaidMemberOrPet(float radius) @@ -8198,10 +8177,18 @@ void Unit::EnergizeBySpell(Unit* victim, uint32 spellID, uint32 damage, Powers p if (powerType != POWER_HAPPINESS && gainedPower) { SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(spellID); - victim->getHostileRefMgr().threatAssist(this, float(gainedPower) * 0.5f, spellInfo); + victim->GetThreatManager().ForwardThreatForAssistingMe(this, float(damage) / 2, spellInfo, true); } } +void Unit::EnergizeBySpell(Unit* victim, SpellInfo const* spellInfo, int32 damage, Powers powerType) +{ + SendEnergizeSpellLog(victim, spellInfo->Id, damage, powerType); + // needs to be called after sending spell log + victim->ModifyPower(powerType, damage); + victim->GetThreatManager().ForwardThreatForAssistingMe(this, float(damage) / 2, spellInfo, true); +} + float Unit::SpellPctDamageModsDone(Unit* victim, SpellInfo const* spellProto, DamageEffectType damagetype) { if (!spellProto || !victim || damagetype == DIRECT_DAMAGE) @@ -10710,291 +10697,124 @@ void Unit::Dismount() } } -void Unit::SetInCombatWith(Unit* enemy, uint32 duration) +void Unit::EngageWithTarget(Unit* enemy) { // Xinef: Dont allow to start combat with triggers if (enemy->GetTypeId() == TYPEID_UNIT && enemy->ToCreature()->IsTrigger()) return; - Unit* eOwner = enemy->GetCharmerOrOwnerOrSelf(); - if (eOwner->IsPvP() || eOwner->IsFFAPvP()) - { - SetInCombatState(true, enemy, duration); + if (!enemy) return; - } - // check for duel - if (eOwner->GetTypeId() == TYPEID_PLAYER && eOwner->ToPlayer()->duel) - { - Unit const* myOwner = GetCharmerOrOwnerOrSelf(); - if (((Player const*)eOwner)->duel->Opponent == myOwner) - { - SetInCombatState(true, enemy, duration); - return; - } - } - SetInCombatState(false, enemy, duration); -} + if (IsEngagedBy(enemy)) + return; -void Unit::SetImmuneToPC(bool apply, bool keepCombat) -{ - (void)keepCombat; - if (apply) - SetUnitFlag(UNIT_FLAG_IMMUNE_TO_PC); + if (CanHaveThreatList()) + m_threatManager.AddThreat(enemy, 0.0f, nullptr, true, true); else RemoveUnitFlag(UNIT_FLAG_IMMUNE_TO_PC); -} -void Unit::SetImmuneToNPC(bool apply, bool keepCombat) -{ - (void)keepCombat; - if (apply) - SetUnitFlag(UNIT_FLAG_IMMUNE_TO_NPC); - else - RemoveUnitFlag(UNIT_FLAG_IMMUNE_TO_NPC); + SetInCombatWith(enemy); + + if (Creature* creature = ToCreature()) + if (CreatureGroup* formation = creature->GetFormation()) + formation->MemberEngagingTarget(creature, enemy); } -void Unit::CombatStart(Unit* victim, bool initialAggro) +void Unit::AttackedTarget(Unit* target, bool canInitialAggro) { - // Xinef: Dont allow to start combat with triggers - if (victim->GetTypeId() == TYPEID_UNIT && victim->ToCreature()->IsTrigger()) + if (!target->IsEngaged() && !canInitialAggro) return; + target->EngageWithTarget(this); + if (Unit* targetOwner = target->GetCharmerOrOwner()) + targetOwner->EngageWithTarget(this); - if (initialAggro) + Player* myPlayerOwner = GetCharmerOrOwnerPlayerOrPlayerItself(); + Player* targetPlayerOwner = target->GetCharmerOrOwnerPlayerOrPlayerItself(); + if (myPlayerOwner && targetPlayerOwner && !(myPlayerOwner->duel && myPlayerOwner->duel->Opponent == targetPlayerOwner)) { - // Make player victim stand up automatically - if (victim->getStandState() && victim->IsPlayer()) - { - victim->SetStandState(UNIT_STAND_STATE_STAND); - } - - if (!victim->IsInCombat() && victim->GetTypeId() != TYPEID_PLAYER && !victim->ToCreature()->HasReactState(REACT_PASSIVE) && victim->ToCreature()->IsAIEnabled) - { - if (victim->IsPet()) - victim->ToCreature()->AI()->AttackedBy(this); // PetAI has special handler before AttackStart() - else - { - victim->ToCreature()->AI()->AttackStart(this); - // if the target is an NPC with a pet or minion, pet should react. - if (Unit* victimControlledUnit = victim->GetFirstControlled()) - { - victimControlledUnit->SetInCombatWith(this); - SetInCombatWith(victimControlledUnit); - victimControlledUnit->AddThreat(this, 0.0f); - } - } - - // if unit has an owner, put owner in combat. - if (Unit* victimOwner = victim->GetOwner()) - { - if (!(victimOwner->IsInCombatWith(this))) - { - /* warding off to not take over aggro for no reason - Using only AddThreat causes delay in attack */ - if (!victimOwner->IsInCombat() && victimOwner->IsAIEnabled) - { - victimOwner->ToCreature()->AI()->AttackStart(this); - } - victimOwner->SetInCombatWith(this); - SetInCombatWith(victimOwner); - victimOwner->AddThreat(this, 0.0f); - } - } - } - - bool alreadyInCombat = IsInCombat(); - - SetInCombatWith(victim); - victim->SetInCombatWith(this); - - // Xinef: If pet started combat - put owner in combat - if (!alreadyInCombat && IsInCombat()) - { - if (Unit* owner = GetOwner()) - { - owner->SetInCombatWith(victim); - victim->SetInCombatWith(owner); - } - } - - bool isPlayer = IsPlayer(); - ToggleCombatAuras(true); - } - - Unit* who = victim->GetCharmerOrOwnerOrSelf(); - if (who->GetTypeId() == TYPEID_PLAYER) - SetContestedPvP(who->ToPlayer()); - - Player* player = GetCharmerOrOwnerPlayerOrPlayerItself(); - if (player && who->IsPvP() && (who->GetTypeId() != TYPEID_PLAYER || !player->duel || player->duel->Opponent != who)) - { - player->UpdatePvP(true); - player->RemoveAurasWithInterruptFlags(AURA_INTERRUPT_FLAG_ENTER_PVP_COMBAT); + myPlayerOwner->UpdatePvP(true); + myPlayerOwner->RemoveAurasWithInterruptFlags(AURA_INTERRUPT_FLAG_ENTER_PVP_COMBAT); } - - AddComboPoints(victim, 0); } -void Unit::CombatStartOnCast(Unit* target, bool initialAggro, uint32 duration) +void Unit::SetImmuneToAll(bool apply, bool keepCombat) { - // Xinef: Dont allow to start combat with triggers - if (target->GetTypeId() == TYPEID_UNIT && target->ToCreature()->IsTrigger()) - return; - - if (initialAggro) + if (apply) { - SetInCombatWith(target, duration); - - // Xinef: If pet started combat - put owner in combat - if (Unit* owner = GetOwner()) - owner->SetInCombatWith(target, duration); - - ToggleCombatAuras(true); + SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_IMMUNE_TO_PC | UNIT_FLAG_IMMUNE_TO_NPC); + ValidateAttackersAndOwnTarget(); + if (keepCombat) + m_threatManager.UpdateOnlineStates(true, true); + else + m_combatManager.EndAllCombat(); } - - Unit* who = target->GetCharmerOrOwnerOrSelf(); - if (who->GetTypeId() == TYPEID_PLAYER) - SetContestedPvP(who->ToPlayer()); - - Player* player = GetCharmerOrOwnerPlayerOrPlayerItself(); - if (player && who->IsPvP() && (who->GetTypeId() != TYPEID_PLAYER || !player->duel || player->duel->Opponent != who)) + else { - player->UpdatePvP(true); - player->RemoveAurasWithInterruptFlags(AURA_INTERRUPT_FLAG_ENTER_PVP_COMBAT); + RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_IMMUNE_TO_PC | UNIT_FLAG_IMMUNE_TO_NPC); + m_threatManager.UpdateOnlineStates(true, true); } } -void Unit::SetInCombatState(bool PvP, Unit* enemy, uint32 duration) +void Unit::SetImmuneToPC(bool apply, bool keepCombat) { - // only alive units can be in combat - if (!IsAlive()) - return; - - if (PvP) - m_CombatTimer = std::max(GetCombatTimer(), std::max(5500, duration)); - else if (duration) - m_CombatTimer = std::max(GetCombatTimer(), duration); - - if (HasUnitState(UNIT_STATE_EVADE) || GetCreatureType() == CREATURE_TYPE_NON_COMBAT_PET) - return; - - // xinef: if we somehow engage in combat (scripts, dunno) with player, remove this flag so he can fight back - if (GetTypeId() == TYPEID_UNIT && enemy && IsImmuneToPC() && enemy->GetCharmerOrOwnerPlayerOrPlayerItself()) - SetImmuneToPC(false); // unit has engaged in combat, remove immunity so players can fight back - - if (IsInCombat()) - return; - - AddComboPoints(enemy, 0); - SetUnitFlag(UNIT_FLAG_IN_COMBAT); - - if (Creature* creature = ToCreature()) + if (apply) { - // Set home position at place of engaging combat for escorted creatures - if ((IsAIEnabled && creature->AI()->IsEscorted()) || - GetMotionMaster()->GetCurrentMovementGeneratorType() == WAYPOINT_MOTION_TYPE || - GetMotionMaster()->GetCurrentMovementGeneratorType() == ESCORT_MOTION_TYPE) - creature->SetHomePosition(GetPositionX(), GetPositionY(), GetPositionZ(), GetOrientation()); - - if (enemy) - { - if (IsAIEnabled) - creature->AI()->JustEngagedWith(enemy); - - if (creature->GetFormation()) - creature->GetFormation()->MemberEngagingTarget(creature, enemy); - - sScriptMgr->OnUnitEnterCombat(creature, enemy); - } - - creature->RefreshSwimmingFlag(); - - if (IsPet()) + SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_IMMUNE_TO_PC); + ValidateAttackersAndOwnTarget(); + if (keepCombat) + m_threatManager.UpdateOnlineStates(true, true); + else { - UpdateSpeed(MOVE_RUN, true); - UpdateSpeed(MOVE_SWIM, true); - UpdateSpeed(MOVE_FLIGHT, true); + std::list toEnd; + for (auto const& pair : m_combatManager.GetPvECombatRefs()) + if (pair.second->GetOther(this)->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_PVP)) + toEnd.push_back(pair.second); + for (auto const& pair : m_combatManager.GetPvPCombatRefs()) + if (pair.second->GetOther(this)->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_PVP)) + toEnd.push_back(pair.second); + for (CombatReference* ref : toEnd) + ref->EndCombat(); } - - if (!(creature->GetCreatureTemplate()->type_flags & CREATURE_TYPE_FLAG_ALLOW_MOUNTED_COMBAT)) - Dismount(); - if (!IsStandState()) // pussywizard: already done in CombatStart(target, initialAggro) for the target, but when aggro'ing from MoveInLOS CombatStart is not called! - SetStandState(UNIT_STAND_STATE_STAND); } - - for (Unit::ControlSet::iterator itr = m_Controlled.begin(); itr != m_Controlled.end();) - { - Unit* controlled = *itr; - ++itr; - - // Xinef: Dont set combat for passive units, they will evade in next update... - if (controlled->GetTypeId() == TYPEID_UNIT && controlled->ToCreature()->HasReactState(REACT_PASSIVE)) - continue; - - controlled->SetInCombatState(PvP, enemy, duration); - } - - if (Player* player = this->ToPlayer()) + else { - sScriptMgr->OnPlayerEnterCombat(player, enemy); + RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_IMMUNE_TO_PC); + m_threatManager.UpdateOnlineStates(true, true); } - - Unit::m_ComboPointDegenTimer = 0; - AddComboPoints(0); - ToggleCombatAuras(false); } -void Unit::ClearInCombat() +void Unit::SetImmuneToNPC(bool apply, bool keepCombat) { - m_CombatTimer = 0; - RemoveUnitFlag(UNIT_FLAG_IN_COMBAT); - - // Player's state will be cleared in Player::UpdateContestedPvP - if (Creature* creature = ToCreature()) - { - if (creature->GetCreatureTemplate() && creature->GetCreatureTemplate()->unit_flags & UNIT_FLAG_IMMUNE_TO_PC) - SetImmuneToPC(true); // set immunity state to the one from db on evade - - ClearUnitState(UNIT_STATE_ATTACK_PLAYER); - if (HasDynamicFlag(UNIT_DYNFLAG_TAPPED)) - ReplaceAllDynamicFlags(creature->GetCreatureTemplate()->dynamicflags); - - creature->SetAssistanceTimer(0); - - // Xinef: will be recalculated at follow movement generator initialization - if (!IsPet() && !IsCharmed()) - return; - } - else if (Player* player = ToPlayer()) + if (apply) { - player->UpdatePotionCooldown(); - if (player->getClass() == CLASS_DEATH_KNIGHT) - for (uint8 i = 0; i < MAX_RUNES; ++i) - player->SetGracePeriod(i, 0); + SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_IMMUNE_TO_NPC); + ValidateAttackersAndOwnTarget(); + if (keepCombat) + m_threatManager.UpdateOnlineStates(true, true); + else + { + std::list toEnd; + for (auto const& pair : m_combatManager.GetPvECombatRefs()) + if (!pair.second->GetOther(this)->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_PVP)) + toEnd.push_back(pair.second); + for (auto const& pair : m_combatManager.GetPvPCombatRefs()) + if (!pair.second->GetOther(this)->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_PVP)) + toEnd.push_back(pair.second); + for (CombatReference* ref : toEnd) + ref->EndCombat(); + } } - - if (Player* player = this->ToPlayer()) + else { - sScriptMgr->OnPlayerLeaveCombat(player); + RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_IMMUNE_TO_NPC); + m_threatManager.UpdateOnlineStates(true, true); } - - Unit::m_ComboPointDegenTimer = 0; - AddComboPoints(0); - - ToggleCombatAuras(false); - } -void Unit::ClearInPetCombat() +bool Unit::IsThreatened() const { - RemoveUnitFlag(UNIT_FLAG_PET_IN_COMBAT); - RemoveAurasWithInterruptFlags(AURA_INTERRUPT_FLAG_LEAVE_COMBAT); - if (Unit* owner = GetOwner()) - { - owner->RemoveUnitFlag(UNIT_FLAG_PET_IN_COMBAT); - } - - ToggleCombatAuras(false); + return !m_threatManager.IsThreatListEmpty(); } bool Unit::isTargetableForAttack(bool checkFakeDeath, Unit const* byWho) const @@ -11041,7 +10861,7 @@ bool Unit::_IsValidAttackTarget(Unit const* target, SpellInfo const* bySpell, Wo // can't attack invisible (ignore stealth for aoe spells) also if the area being looked at is from a spell use the dynamic object created instead of the casting unit. //Ignore stealth if target is player and unit in combat with same player - if (GetEntry() != WORLD_TRIGGER && (!bySpell || !bySpell->HasAttribute(SPELL_ATTR6_IGNORE_PHASE_SHIFT)) && (obj ? !obj->CanSeeOrDetect(target, bySpell && bySpell->IsAffectingArea()) : !CanSeeOrDetect(target, (bySpell && bySpell->IsAffectingArea()) || (target->GetTypeId() == TYPEID_PLAYER && target->HasStealthAura() && target->IsInCombat() && IsInCombatWith(target))))) + if (GetEntry() != WORLD_TRIGGER && (!bySpell || !bySpell->HasAttribute(SPELL_ATTR6_IGNORE_PHASE_SHIFT)) && (obj ? !obj->CanSeeOrDetect(target, bySpell && bySpell->IsAffectingArea()) : !CanSeeOrDetect(target, (bySpell && bySpell->IsAffectingArea()) || (target->GetTypeId() == TYPEID_PLAYER && target->HasStealthAura() && IsInCombatWith(target))))) return false; // can't attack dead @@ -11704,8 +11524,7 @@ void Unit::setDeathState(DeathState s, bool despawn) if (s != DeathState::Alive && s != DeathState::JustRespawned) { CombatStop(); - GetThreatMgr().ClearAllThreat(); - getHostileRefMgr().deleteReferences(); + GetThreatManager().ClearAllThreat(); ClearComboPointHolders(false); // any combo points pointed to unit lost at it death if (IsNonMeleeSpellCast(false)) @@ -11758,224 +11577,32 @@ void Unit::setDeathState(DeathState s, bool despawn) m_deathState = s; } -/*######################################## -######## ######## -######## AGGRO SYSTEM ######## -######## ######## -########################################*/ -bool Unit::CanHaveThreatList() const -{ - // only creatures can have threat list - if (GetTypeId() != TYPEID_UNIT) - return false; - - // only alive units can have threat list - if (!IsAlive() || isDying()) - return false; - - // totems can not have threat list - if (ToCreature()->IsTotem()) - return false; - - // vehicles can not have threat list - if (ToCreature()->IsVehicle() && GetMap()->IsBattlegroundOrArena()) - return false; - - // summons can not have a threat list, unless they are controlled by a creature - if (HasUnitTypeMask(UNIT_MASK_MINION | UNIT_MASK_GUARDIAN | UNIT_MASK_CONTROLABLE_GUARDIAN) && ((Pet*)this)->GetOwnerGUID().IsPlayer()) - return false; - - return true; -} - -//====================================================================== - -float Unit::ApplyTotalThreatModifier(float fThreat, SpellSchoolMask schoolMask) -{ - if (!HasAuraType(SPELL_AURA_MOD_THREAT) || fThreat < 0) - return fThreat; - - SpellSchools school = GetFirstSchoolInMask(schoolMask); - - return fThreat * m_threatModifier[school]; -} - -//====================================================================== - -void Unit::AddThreat(Unit* victim, float fThreat, SpellSchoolMask schoolMask, SpellInfo const* threatSpell) -{ - // Only mobs can manage threat lists - if (CanHaveThreatList() && !HasUnitState(UNIT_STATE_EVADE)) - { - m_ThreatMgr.AddThreat(victim, fThreat, schoolMask, threatSpell); - } -} - -//====================================================================== - -void Unit::TauntApply(Unit* taunter) -{ - ASSERT(GetTypeId() == TYPEID_UNIT); - - if (!taunter || (taunter->GetTypeId() == TYPEID_PLAYER && taunter->ToPlayer()->IsGameMaster())) - return; - - if (!CanHaveThreatList()) - return; - - Creature* creature = ToCreature(); - - if (creature->HasReactState(REACT_PASSIVE)) - return; - - Unit* target = GetVictim(); - if (target && target == taunter) - return; - - SetInFront(taunter); - SetGuidValue(UNIT_FIELD_TARGET, taunter->GetGUID()); - - if (creature->IsAIEnabled) - creature->AI()->AttackStart(taunter); - - //m_ThreatMgr.tauntApply(taunter); -} - //====================================================================== -void Unit::TauntFadeOut(Unit* taunter) +void Unit::AtExitCombat() { - ASSERT(GetTypeId() == TYPEID_UNIT); - - if (!taunter || (taunter->GetTypeId() == TYPEID_PLAYER && taunter->ToPlayer()->IsGameMaster())) - return; - - if (!CanHaveThreatList()) - return; - - Creature* creature = ToCreature(); - - if (creature->HasReactState(REACT_PASSIVE)) - return; - - Unit* target = GetVictim(); - if (!target || target != taunter) - return; - - if (m_ThreatMgr.isThreatListEmpty()) - { - if (creature->IsAIEnabled) - creature->AI()->EnterEvadeMode(CreatureAI::EVADE_REASON_NO_HOSTILES); - return; - } - - target = creature->SelectVictim(); // might have more taunt auras remaining - - if (target && target != taunter) - { - SetGuidValue(UNIT_FIELD_TARGET, target->GetGUID()); - SetInFront(target); - if (creature->IsAIEnabled) - creature->AI()->AttackStart(target); - } + RemoveAurasWithInterruptFlags(AURA_INTERRUPT_FLAG_LEAVE_COMBAT); } //====================================================================== -Unit* Creature::SelectVictim() +void Unit::UpdatePetCombatState() { - // function provides main threat functionality - // next-victim-selection algorithm and evade mode are called - // threat list sorting etc. - - Unit* target = nullptr; - - // First checking if we have some taunt on us - AuraEffectList const& tauntAuras = GetAuraEffectsByType(SPELL_AURA_MOD_TAUNT); - if (!tauntAuras.empty()) - for (Unit::AuraEffectList::const_reverse_iterator itr = tauntAuras.rbegin(); itr != tauntAuras.rend(); ++itr) - if (Unit* caster = (*itr)->GetCaster()) - if (CanCreatureAttack(caster) && !caster->HasAuraTypeWithCaster(SPELL_AURA_IGNORED, GetGUID())) - { - target = caster; - break; - } - - if (CanHaveThreatList()) - { - if (!target && !m_ThreatMgr.isThreatListEmpty()) - target = m_ThreatMgr.getHostileTarget(); - } - else if (!HasReactState(REACT_PASSIVE)) - { - // we have player pet probably - target = getAttackerForHelper(); - if (!target && IsSummon()) - if (Unit* owner = ToTempSummon()->GetOwner()) - { - if (owner->IsInCombat()) - target = owner->getAttackerForHelper(); - if (!target) - for (ControlSet::const_iterator itr = owner->m_Controlled.begin(); itr != owner->m_Controlled.end(); ++itr) - if ((*itr)->IsInCombat()) - { - target = (*itr)->getAttackerForHelper(); - if (target) - break; - } - } - } - else - return nullptr; - - if (target && CanCreatureAttack(target)) - { - SetInFront(target); - return target; - } - - // last case when creature must not go to evade mode: - // it in combat but attacker not make any damage and not enter to aggro radius to have record in threat list - // Note: creature does not have targeted movement generator but has attacker in this case - for (AttackerSet::const_iterator itr = m_attackers.begin(); itr != m_attackers.end(); ++itr) - if ((*itr) && CanCreatureAttack(*itr) && (*itr)->GetTypeId() != TYPEID_PLAYER && !(*itr)->ToCreature()->HasUnitTypeMask(UNIT_MASK_CONTROLABLE_GUARDIAN)) - return nullptr; - - if (GetVehicle()) - return nullptr; - - // pussywizard: not sure why it's here - // pussywizard: can't evade when having invisibility aura with duration? o_O - Unit::AuraEffectList const& iAuras = GetAuraEffectsByType(SPELL_AURA_MOD_INVISIBILITY); - if (!iAuras.empty()) - { - for (Unit::AuraEffectList::const_iterator itr = iAuras.begin(); itr != iAuras.end(); ++itr) - if ((*itr)->GetBase()->IsPermanent()) - { - AI()->EnterEvadeMode(CreatureAI::EVADE_REASON_NO_HOSTILES); - break; - } - return nullptr; - } - - // Last chance: creature group - if (CreatureGroup* group = GetFormation()) - { - if (Unit* groupTarget = group->GetNewTargetForMember(this)) + ASSERT(!IsPet()); // player pets do not use UNIT_FLAG_PET_IN_COMBAT for this purpose - but player pets should also never have minions of their own to call this + bool state = false; + for (Unit* minion : m_Controlled) + if (minion->IsInCombat()) { - SetInFront(groupTarget); - return groupTarget; + state = true; + break; } - } - - // enter in evade mode in other case - AI()->EnterEvadeMode(); - return nullptr; + if (state) + SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_PET_IN_COMBAT); + else + RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_PET_IN_COMBAT); } -//====================================================================== -//====================================================================== //====================================================================== float Unit::ApplyEffectModifiers(SpellInfo const* spellProto, uint8 effect_index, float value) const @@ -12912,8 +12539,7 @@ void Unit::CleanupBeforeRemoveFromMap(bool finalCleanup) CombatStop(); ClearComboPoints(); ClearComboPointHolders(); - GetThreatMgr().ClearAllThreat(); - getHostileRefMgr().deleteReferences(); + GetThreatManager().ClearAllThreat(); GetMotionMaster()->Clear(false); // remove different non-standard movement generators. } @@ -14419,44 +14045,6 @@ bool Unit::IsUnderLastManaUseEffect() const return getMSTimeDiff(m_lastManaUse, GameTime::GetGameTimeMS().count()) < 5000; } -void Unit::SetContestedPvP(Player* attackedPlayer, bool lookForNearContestedGuards) -{ - Player* player = GetCharmerOrOwnerPlayerOrPlayerItself(); - - if (!player || ((attackedPlayer && (attackedPlayer == player || (player->duel && player->duel->Opponent == attackedPlayer))) || player->InBattleground())) - return; - - // check if there any guards that should care about the contested flag on player - if (lookForNearContestedGuards) - { - std::list targets; - Acore::NearestVisibleDetectableContestedGuardUnitCheck u_check(this); - Acore::UnitListSearcher searcher(this, targets, u_check); - Cell::VisitAllObjects(this, searcher, MAX_AGGRO_RADIUS); - - // return if there are no contested guards found - if (!targets.size()) - { - return; - } - } - - player->SetContestedPvPTimer(30000); - if (!player->HasUnitState(UNIT_STATE_ATTACK_PLAYER)) - { - player->AddUnitState(UNIT_STATE_ATTACK_PLAYER); - player->SetPlayerFlag(PLAYER_FLAGS_CONTESTED_PVP); - // call MoveInLineOfSight for nearby contested guards - AddToNotify(NOTIFY_AI_RELOCATION); - } - if (!HasUnitState(UNIT_STATE_ATTACK_PLAYER)) - { - AddUnitState(UNIT_STATE_ATTACK_PLAYER); - // call MoveInLineOfSight for nearby contested guards - AddToNotify(NOTIFY_AI_RELOCATION); - } -} - void Unit::AddPetAura(PetAura const* petSpell) { if (GetTypeId() != TYPEID_PLAYER) @@ -14750,7 +14338,6 @@ void Unit::Kill(Unit* killer, Unit* victim, bool durabilityLoss, WeaponAttackTyp // Stop attacks victim->CombatStop(); - victim->getHostileRefMgr().deleteReferences(); // restore for use at real death victim->SetUInt32Value(PLAYER_SELF_RES_SPELL, ressSpellId); @@ -14779,7 +14366,6 @@ void Unit::Kill(Unit* killer, Unit* victim, bool durabilityLoss, WeaponAttackTyp } // 10% durability loss on death - // clean InHateListOf if (Player* plrVictim = victim->ToPlayer()) { // remember victim PvP death for corpse type and corpse reclaim delay @@ -14812,7 +14398,7 @@ void Unit::Kill(Unit* killer, Unit* victim, bool durabilityLoss, WeaponAttackTyp if (!creature->IsPet() && creature->GetLootMode() > 0) { - creature->GetThreatMgr().ClearAllThreat(); + creature->GetThreatManager().ClearAllThreat(); // must be after setDeathState which resets dynamic flags if (!creature->loot.isLooted()) @@ -15285,10 +14871,6 @@ bool Unit::SetCharmedBy(Unit* charmer, CharmType type, AuraApplication const* au CastStop(); AttackStop(); - // Xinef: dont reset threat and combat, put them on offline list, moved down after faction changes - // CombatStop(); /// @todo: CombatStop(true) may cause crash (interrupt spells) - // DeleteThreatList(); - Player* playerCharmer = charmer->ToPlayer(); // Charmer stop charming @@ -15543,14 +15125,6 @@ void Unit::RemoveCharmedBy(Unit* charmer) sScriptMgr->AnticheatSetUnderACKmount(player); } - // xinef: restore threat - for (CharmThreatMap::const_iterator itr = _charmThreatInfo.begin(); itr != _charmThreatInfo.end(); ++itr) - { - if (Unit* target = ObjectAccessor::GetUnit(*this, itr->first)) - if (!IsFriendlyTo(target)) - AddThreat(target, itr->second); - } - _charmThreatInfo.clear(); if (Creature* creature = ToCreature()) @@ -15570,6 +15144,8 @@ void Unit::RemoveCharmedBy(Unit* charmer) else ToPlayer()->SetClientControl(this, true); // verified + EngageWithTarget(charmer); + // a guardian should always have charminfo if (playerCharmer && this != charmer->GetFirstControlled()) playerCharmer->SendRemoveControlBar(); @@ -15939,46 +15515,7 @@ void Unit::SetPhaseMask(uint32 newPhaseMask, bool update) return; if (IsInWorld()) - { - // xinef: to comment, bellow line should be removed - // pussywizard: goign to other phase (valithria, algalon) should not remove such auras - //RemoveNotOwnSingleTargetAuras(newPhaseMask, true); // we can lost access to caster or target - - if (!sScriptMgr->CanSetPhaseMask(this, newPhaseMask, update)) - return; - - // modify hostile references for new phasemask, some special cases deal with hostile references themselves - if (GetTypeId() == TYPEID_UNIT || (!ToPlayer()->IsGameMaster() && !ToPlayer()->GetSession()->PlayerLogout())) - { - HostileRefMgr& refMgr = getHostileRefMgr(); - HostileReference* ref = refMgr.getFirst(); - - while (ref) - { - if (Unit* unit = ref->GetSource()->GetOwner()) - if (Creature* creature = unit->ToCreature()) - refMgr.setOnlineOfflineState(creature, creature->InSamePhase(newPhaseMask)); - - ref = ref->next(); - } - - // modify threat lists for new phasemask - if (GetTypeId() != TYPEID_PLAYER) - { - ThreatContainer::StorageType threatList = GetThreatMgr().GetThreatList(); - ThreatContainer::StorageType offlineThreatList = GetThreatMgr().GetOfflineThreatList(); - - // merge expects sorted lists - threatList.sort(); - offlineThreatList.sort(); - threatList.merge(offlineThreatList); - - for (ThreatContainer::StorageType::const_iterator itr = threatList.begin(); itr != threatList.end(); ++itr) - if (Unit* unit = (*itr)->getTarget()) - unit->getHostileRefMgr().setOnlineOfflineState(ToCreature(), unit->InSamePhase(newPhaseMask)); - } - } - } + m_threatManager.UpdateOnlineStates(true, true); WorldObject::SetPhaseMask(newPhaseMask, false); @@ -16020,6 +15557,7 @@ void Unit::UpdateObjectVisibility(bool forced, bool /*fromUpdate*/) AddToNotify(NOTIFY_VISIBILITY_CHANGED); else { + m_threatManager.UpdateOnlineStates(true, true); WorldObject::UpdateObjectVisibility(true); Acore::AIRelocationNotifier notifier(*this); float radius = 60.0f; @@ -16393,11 +15931,6 @@ uint32 Unit::GetModelForTotem(PlayerTotemType totemType) return 0; } -Unit* Unit::GetRedirectThreatTarget() const -{ - return _redirectThreatInfo.GetTargetGUID() ? ObjectAccessor::GetUnit(*this, _redirectThreatInfo.GetTargetGUID()) : nullptr; -} - void Unit::JumpTo(float speedXY, float speedZ, bool forward, int32 directional) { float angle = forward ? 0 : M_PI; @@ -16883,7 +16416,7 @@ bool Unit::CanSwim() const return true; if (HasUnitFlag2(UNIT_FLAG2_UNUSED_6)) return false; - if (HasUnitFlag(UNIT_FLAG_PET_IN_COMBAT)) + if (IsPet() && HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_PET_IN_COMBAT)) return true; return HasUnitFlag(UNIT_FLAG_RENAME | UNIT_FLAG_SWIMMING); } @@ -16987,64 +16520,6 @@ void Unit::UpdateHeight(float newZ) GetVehicleKit()->RelocatePassengers(); } -void Unit::SendThreatListUpdate() -{ - if (!GetThreatMgr().isThreatListEmpty()) - { - uint32 count = GetThreatMgr().GetThreatList().size(); - - //LOG_DEBUG("entities.unit", "WORLD: Send SMSG_THREAT_UPDATE Message"); - WorldPacket data(SMSG_THREAT_UPDATE, 8 + count * 8); - data << GetPackGUID(); - data << uint32(count); - ThreatContainer::StorageType const& tlist = GetThreatMgr().GetThreatList(); - for (ThreatContainer::StorageType::const_iterator itr = tlist.begin(); itr != tlist.end(); ++itr) - { - data << (*itr)->getUnitGuid().WriteAsPacked(); - data << uint32((*itr)->GetThreat() * 100); - } - SendMessageToSet(&data, false); - } -} - -void Unit::SendChangeCurrentVictimOpcode(HostileReference* pHostileReference) -{ - if (!GetThreatMgr().isThreatListEmpty()) - { - uint32 count = GetThreatMgr().GetThreatList().size(); - - LOG_DEBUG("entities.unit", "WORLD: Send SMSG_HIGHEST_THREAT_UPDATE Message"); - WorldPacket data(SMSG_HIGHEST_THREAT_UPDATE, 8 + 8 + count * 8); - data << GetPackGUID(); - data << pHostileReference->getUnitGuid().WriteAsPacked(); - data << uint32(count); - ThreatContainer::StorageType const& tlist = GetThreatMgr().GetThreatList(); - for (ThreatContainer::StorageType::const_iterator itr = tlist.begin(); itr != tlist.end(); ++itr) - { - data << (*itr)->getUnitGuid().WriteAsPacked(); - data << uint32((*itr)->GetThreat() * 100); - } - SendMessageToSet(&data, false); - } -} - -void Unit::SendClearThreatListOpcode() -{ - LOG_DEBUG("entities.unit", "WORLD: Send SMSG_THREAT_CLEAR Message"); - WorldPacket data(SMSG_THREAT_CLEAR, 8); - data << GetPackGUID(); - SendMessageToSet(&data, false); -} - -void Unit::SendRemoveFromThreatListOpcode(HostileReference* pHostileReference) -{ - LOG_DEBUG("entities.unit", "WORLD: Send SMSG_THREAT_REMOVE Message"); - WorldPacket data(SMSG_THREAT_REMOVE, 8 + 8); - data << GetPackGUID(); - data << pHostileReference->getUnitGuid().WriteAsPacked(); - SendMessageToSet(&data, false); -} - void Unit::RewardRage(uint32 damage, uint32 weaponSpeedHitFactor, bool attacker) { if (getClass() == CLASS_BARD) @@ -17107,10 +16582,15 @@ void Unit::StopAttackFaction(uint32 faction_id) ++itr; } - getHostileRefMgr().deleteReferencesForFaction(faction_id); + std::vector refsToEnd; + 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(); - for (ControlSet::const_iterator itr = m_Controlled.begin(); itr != m_Controlled.end(); ++itr) - (*itr)->StopAttackFaction(faction_id); + for (Unit* minion : m_Controlled) + minion->StopAttackFaction(faction_id); } void Unit::StopAttackingInvalidTarget() @@ -17962,25 +17442,6 @@ DisplayRace Unit::GetDisplayRaceFromModelId(uint32 modelId) const return DisplayRace::None; } -// Check if unit in combat with specific unit -bool Unit::IsInCombatWith(Unit const* who) const -{ - // Check target exists - if (!who) - return false; - // Search in threat list - ObjectGuid guid = who->GetGUID(); - for (ThreatContainer::StorageType::const_iterator i = m_ThreatMgr.GetThreatList().begin(); i != m_ThreatMgr.GetThreatList().end(); ++i) - { - HostileReference* ref = (*i); - // Return true if the unit matches - if (ref && ref->getUnitGuid() == guid) - return true; - } - // Nothing found, false. - return false; -} - /** * @brief this method gets the diameter of a Unit by DB if any value is defined, otherwise it gets the value by the DBC * diff --git a/src/server/game/Entities/Unit/Unit.h b/src/server/game/Entities/Unit/Unit.h index 847079d664e2a1..85bbee15b7a533 100644 --- a/src/server/game/Entities/Unit/Unit.h +++ b/src/server/game/Entities/Unit/Unit.h @@ -24,7 +24,7 @@ #include "EventProcessor.h" #include "FollowerRefMgr.h" #include "FollowerReference.h" -#include "HostileRefMgr.h" +#include "CombatManager.h" #include "ItemTemplate.h" #include "MotionMaster.h" #include "Object.h" @@ -960,28 +960,6 @@ struct SpellPeriodicAuraLogInfo void createProcFlags(SpellInfo const* spellInfo, WeaponAttackType attackType, bool positive, uint32& procAttacker, uint32& procVictim); uint32 createProcHitMask(SpellNonMeleeDamage* damageInfo, SpellMissInfo missCondition); -struct RedirectThreatInfo -{ - RedirectThreatInfo() = default; - ObjectGuid _targetGUID; - uint32 _threatPct{0}; - - [[nodiscard]] ObjectGuid GetTargetGUID() const { return _targetGUID; } - [[nodiscard]] uint32 GetThreatPct() const { return _threatPct; } - - void Set(ObjectGuid guid, uint32 pct) - { - _targetGUID = guid; - _threatPct = pct; - } - - void ModifyThreatPct(int32 amount) - { - amount += _threatPct; - _threatPct = uint32(std::max(0, amount)); - } -}; - #define MAX_DECLINED_NAME_CASES 5 struct DeclinedName @@ -1393,14 +1371,24 @@ class Unit : public WorldObject } [[nodiscard]] Unit* getAttackerForHelper() const // If someone wants to help, who to give them { - if (GetVictim() != nullptr) - return GetVictim(); + if (Unit* victim = GetVictim()) + if ((!IsPet() && !m_movedByPlayer) || IsInCombatWith(victim)) + return GetVictim(); if (!IsEngaged()) return nullptr; - if (!m_attackers.empty()) - return *(m_attackers.begin()); + CombatManager const& mgr = GetCombatManager(); + // pick arbitrary targets; our pvp combat > owner's pvp combat > our pve combat > owner's pve combat + Unit* owner = GetCharmerOrOwner(); + if (mgr.HasPvPCombat()) + return mgr.GetPvPCombatRefs().begin()->second->GetOther(this); + if (owner && (owner->GetCombatManager().HasPvPCombat())) + return owner->GetCombatManager().GetPvPCombatRefs().begin()->second->GetOther(owner); + if (mgr.HasPvECombat()) + return mgr.GetPvECombatRefs().begin()->second->GetOther(this); + if (owner && (owner->GetCombatManager().HasPvECombat())) + return owner->GetCombatManager().GetPvECombatRefs().begin()->second->GetOther(owner); return nullptr; } @@ -1413,7 +1401,8 @@ class Unit : public WorldObject [[nodiscard]] bool isAttackingPlayer() const; [[nodiscard]] Unit* GetVictim() const { return m_attacking; } - void CombatStop(bool includingCast = false); + void ValidateAttackersAndOwnTarget(); + void CombatStop(bool includingCast = false, bool mutualPvP = true); void CombatStopWithPets(bool includingCast = false); void StopAttackFaction(uint32 faction_id); void StopAttackingInvalidTarget(); @@ -1711,28 +1700,45 @@ class Unit : public WorldObject [[nodiscard]] bool IsInFlight() const { return HasUnitState(UNIT_STATE_IN_FLIGHT); } - void SetImmuneToAll(bool apply, bool keepCombat = false) { SetImmuneToPC(apply, keepCombat); SetImmuneToNPC(apply, keepCombat); } + /// ====================== THREAT & COMBAT ==================== + bool CanHaveThreatList() const { return m_threatManager.CanHaveThreatList(); } + // For NPCs with threat list: Whether there are any enemies on our threat list + // For other units: Whether we're in combat + // This value is different from IsInCombat when a projectile spell is midair (combat on launch - threat+aggro on impact) + bool IsEngaged() const { return CanHaveThreatList() ? m_threatManager.IsEngaged() : IsInCombat(); } + bool IsEngagedBy(Unit const* who) const { return CanHaveThreatList() ? IsThreatenedBy(who) : IsInCombatWith(who); } + void EngageWithTarget(Unit* who); // Adds target to threat list if applicable, otherwise just sets combat state + // Combat handling + CombatManager& GetCombatManager() { return m_combatManager; } + CombatManager const& GetCombatManager() const { return m_combatManager; } + void AttackedTarget(Unit* target, bool canInitialAggro); + bool IsImmuneToAll() const { return IsImmuneToPC() && IsImmuneToNPC(); } - void SetImmuneToPC(bool apply, bool keepCombat = false); + void SetImmuneToAll(bool apply, bool keepCombat); + virtual void SetImmuneToAll(bool apply) { SetImmuneToAll(apply, false); } bool IsImmuneToPC() const { return HasUnitFlag(UNIT_FLAG_IMMUNE_TO_PC); } - void SetImmuneToNPC(bool apply, bool keepCombat = false); + void SetImmuneToPC(bool apply, bool keepCombat); + virtual void SetImmuneToPC(bool apply) { SetImmuneToPC(apply, false); } bool IsImmuneToNPC() const { return HasUnitFlag(UNIT_FLAG_IMMUNE_TO_NPC); } + void SetImmuneToNPC(bool apply, bool keepCombat); + virtual void SetImmuneToNPC(bool apply) { SetImmuneToNPC(apply, false); } + + bool IsInCombat() const { return HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_IN_COMBAT); } + bool IsInCombatWith(Unit const* who) const { return who && m_combatManager.IsInCombatWith(who); } + void SetInCombatWith(Unit* enemy) { if (enemy) m_combatManager.SetInCombatWith(enemy); } + void ClearInCombat() { m_combatManager.EndAllCombat(); } + void UpdatePetCombatState(); + // Threat handling + bool IsThreatened() const; + bool IsThreatenedBy(Unit const* who) const { return who && m_threatManager.IsThreatenedBy(who, true); } + // Exposes the threat manager directly - be careful when interfacing with this + // As a general rule of thumb, any unit pointer MUST be null checked BEFORE passing it to threatmanager methods + // threatmanager will NOT null check your pointers for you - misuse = crash + ThreatMgr& GetThreatManager() { return m_threatManager; } + ThreatMgr const& GetThreatManager() const { return m_threatManager; } - bool IsEngaged() const { return IsInCombat(); } - bool IsEngagedBy(Unit const* who) const { return IsInCombatWith(who); } - - [[nodiscard]] bool IsInCombat() const { return HasUnitFlag(UNIT_FLAG_IN_COMBAT); } - bool IsInCombatWith(Unit const* who) const; - - [[nodiscard]] bool IsPetInCombat() const { return HasUnitFlag(UNIT_FLAG_PET_IN_COMBAT); } - void CombatStart(Unit* target, bool initialAggro = true); - void CombatStartOnCast(Unit* target, bool initialAggro = true, uint32 duration = 0); - void SetInCombatState(bool PvP, Unit* enemy = nullptr, uint32 duration = 0); - void SetInCombatWith(Unit* enemy, uint32 duration = 0); - void ClearInCombat(); - void ClearInPetCombat(); - [[nodiscard]] uint32 GetCombatTimer() const { return m_CombatTimer; } - void SetCombatTimer(uint32 timer) { m_CombatTimer = timer; } + void SendClearTarget(); + void SendThreatListUpdate() { m_threatManager.SendThreatListToClients(); } void ToggleCombatAuras(bool startingCombat); void ToggleOnPowerPctAuras(); @@ -1769,6 +1775,7 @@ class Unit : public WorldObject int32 HealBySpell(HealInfo& healInfo, bool critical = false); void SendEnergizeSpellLog(Unit* victim, uint32 SpellID, uint32 Damage, Powers powertype); void EnergizeBySpell(Unit* victim, uint32 SpellID, uint32 Damage, Powers powertype); + void EnergizeBySpell(Unit* victim, SpellInfo const* spellInfo, int32 damage, Powers powerType); SpellCastResult CastSpell(SpellCastTargets const& targets, SpellInfo const* spellInfo, CustomSpellValues const* value, TriggerCastFlags triggerFlags = TRIGGERED_NONE, Item* castItem = nullptr, AuraEffect const* triggeredByAura = nullptr, ObjectGuid originalCaster = ObjectGuid::Empty); SpellCastResult CastSpell(Unit* victim, uint32 spellId, bool triggered, Item* castItem = nullptr, AuraEffect const* triggeredByAura = nullptr, ObjectGuid originalCaster = ObjectGuid::Empty); @@ -1838,13 +1845,6 @@ class Unit : public WorldObject void SetFacingTo(float ori); void SetFacingToObject(WorldObject* object); - void SendChangeCurrentVictimOpcode(HostileReference* pHostileReference); - void SendClearThreatListOpcode(); - void SendRemoveFromThreatListOpcode(HostileReference* pHostileReference); - void SendThreatListUpdate(); - - void SendClearTarget(); - void BuildHeartBeatMsg(WorldPacket* data) const; [[nodiscard]] bool IsAlive() const { return (m_deathState == DeathState::Alive); }; @@ -2143,7 +2143,6 @@ class Unit : public WorldObject float m_modSpellHitChance; int32 m_baseSpellCritChance; - float m_threatModifier[MAX_SPELL_SCHOOL]; float m_modAttackSpeedPct[3]; uint32 m_ComboPointDegenTimer; uint32 m_focusRegen; @@ -2195,18 +2194,6 @@ class Unit : public WorldObject SpellImmuneList m_spellImmune[MAX_SPELL_IMMUNITY]; uint32 m_lastSanctuaryTime; - // Threat related methods - [[nodiscard]] bool CanHaveThreatList() const; - void AddThreat(Unit* victim, float fThreat, SpellSchoolMask schoolMask = SPELL_SCHOOL_MASK_NORMAL, SpellInfo const* threatSpell = nullptr); - float ApplyTotalThreatModifier(float fThreat, SpellSchoolMask schoolMask = SPELL_SCHOOL_MASK_NORMAL); - void TauntApply(Unit* victim); - void TauntFadeOut(Unit* taunter); - ThreatMgr& GetThreatMgr() { return m_ThreatMgr; } - ThreatMgr const& GetThreatMgr() const { return m_ThreatMgr; } - void addHatedBy(HostileReference* pHostileReference) { m_HostileRefMgr.insertFirst(pHostileReference); }; - void removeHatedBy(HostileReference* /*pHostileReference*/) { /* nothing to do yet */ } - HostileRefMgr& getHostileRefMgr() { return m_HostileRefMgr; } - VisibleAuraMap const* GetVisibleAuras() { return &m_visibleAuras; } AuraApplication* GetVisibleAura(uint8 slot) { @@ -2288,8 +2275,6 @@ class Unit : public WorldObject void SetLastManaUse(uint32 spellCastTime) { m_lastManaUse = spellCastTime; } [[nodiscard]] bool IsUnderLastManaUseEffect() const; - void SetContestedPvP(Player* attackedPlayer = nullptr, bool lookForNearContestedGuards = true); - uint32 GetCastingTimeForBonus(SpellInfo const* spellProto, DamageEffectType damagetype, uint32 CastingTime) const; float CalculateDefaultCoefficient(SpellInfo const* spellInfo, DamageEffectType damagetype, Unit* caster) const; @@ -2411,13 +2396,6 @@ class Unit : public WorldObject [[nodiscard]] uint32 GetModelForForm(ShapeshiftForm form, uint32 spellId) const; uint32 GetModelForTotem(PlayerTotemType totemType); - // Redirect Threat - void SetRedirectThreat(ObjectGuid guid, uint32 pct) { _redirectThreatInfo.Set(guid, pct); } - void ResetRedirectThreat() { SetRedirectThreat(ObjectGuid::Empty, 0); } - void ModifyRedirectThreat(int32 amount) { _redirectThreatInfo.ModifyThreatPct(amount); } - uint32 GetRedirectThreatPercent() { return _redirectThreatInfo.GetThreatPct(); } - [[nodiscard]] Unit* GetRedirectThreatTarget() const; - bool IsAIEnabled, NeedChangeAI; bool CreateVehicleKit(uint32 id, uint32 creatureEntry); void RemoveVehicleKit(); @@ -2605,7 +2583,6 @@ class Unit : public WorldObject uint32 m_reactiveTimer[MAX_REACTIVE]; int32 m_regenTimer; - ThreatMgr m_ThreatMgr; typedef std::map CharmThreatMap; CharmThreatMap _charmThreatInfo; @@ -2622,6 +2599,8 @@ class Unit : public WorldObject bool _instantCast; ForgeStatMap m_forgestats; + virtual void AtEnterCombat() { } + virtual void AtExitCombat(); private: void UpdateSplineMovement(uint32 t_diff); @@ -2643,13 +2622,15 @@ class Unit : public WorldObject private: uint32 m_state; // Even derived shouldn't modify - uint32 m_CombatTimer; uint32 m_lastManaUse; // msecs //TimeTrackerSmall m_movesplineTimer; Diminishing m_Diminishing; // Manage all Units that are threatened by us - HostileRefMgr m_HostileRefMgr; + friend class CombatManager; + CombatManager m_combatManager; + friend class ThreatManager; + ThreatMgr m_threatManager; FollowerRefMgr m_FollowingRefMgr; @@ -2657,8 +2638,6 @@ class Unit : public WorldObject int8 m_comboPoints; std::unordered_set m_ComboPointHolders; - RedirectThreatInfo _redirectThreatInfo; - bool m_cleanupDone; // lock made to not add stuff after cleanup before delete bool m_duringRemoveFromWorld; // lock made to not add stuff after begining removing from world diff --git a/src/server/game/Handlers/CharacterHandler.cpp b/src/server/game/Handlers/CharacterHandler.cpp index 7f16aa4ecbb178..a47146c37fc852 100644 --- a/src/server/game/Handlers/CharacterHandler.cpp +++ b/src/server/game/Handlers/CharacterHandler.cpp @@ -969,7 +969,7 @@ void WorldSession::HandlePlayerLoginFromDB(LoginQueryHolder const& holder) if (pCurrChar->HasPlayerFlag(PLAYER_FLAGS_CONTESTED_PVP)) { - pCurrChar->SetContestedPvP(nullptr, false); + pCurrChar->SetContestedPvP(nullptr); } // Apply at_login requests diff --git a/src/server/game/Handlers/MovementHandler.cpp b/src/server/game/Handlers/MovementHandler.cpp index 5ca058eb690df5..07bbb39410ad45 100644 --- a/src/server/game/Handlers/MovementHandler.cpp +++ b/src/server/game/Handlers/MovementHandler.cpp @@ -127,9 +127,6 @@ void WorldSession::HandleMoveWorldportAck() _player->m_movementInfo.RemoveMovementFlag(MOVEMENTFLAG_ONTRANSPORT); } - if (!_player->getHostileRefMgr().IsEmpty()) - _player->getHostileRefMgr().deleteReferences(true); // pussywizard: multithreading crashfix - CellCoord pair(Acore::ComputeCellCoord(GetPlayer()->GetPositionX(), GetPlayer()->GetPositionY())); Cell cell(pair); if (!GridCoord(cell.GridX(), cell.GridY()).IsCoordValid()) diff --git a/src/server/game/Handlers/PetHandler.cpp b/src/server/game/Handlers/PetHandler.cpp index 3d283f5ed5cc3b..2d9882aa476228 100644 --- a/src/server/game/Handlers/PetHandler.cpp +++ b/src/server/game/Handlers/PetHandler.cpp @@ -144,7 +144,6 @@ void WorldSession::HandlePetStopAttack(WorldPackets::Pet::PetStopAttack& packet) return; pet->AttackStop(); - pet->ClearInPetCombat(); } void WorldSession::HandlePetActionHelper(Unit* pet, ObjectGuid guid1, uint32 spellId, uint16 flag, ObjectGuid guid2) @@ -190,7 +189,6 @@ void WorldSession::HandlePetActionHelper(Unit* pet, ObjectGuid guid1, uint32 spe { pet->AttackStop(); pet->InterruptNonMeleeSpells(false); - pet->ClearInPetCombat(); pet->GetMotionMaster()->MoveFollow(_player, PET_FOLLOW_DIST, pet->GetFollowAngle()); if (pet->ToPet()) pet->ToPet()->ClearCastWhenWillAvailable(); @@ -320,7 +318,6 @@ void WorldSession::HandlePetActionHelper(Unit* pet, ObjectGuid guid1, uint32 spe pet->AttackStop(); if (pet->ToPet()) pet->ToPet()->ClearCastWhenWillAvailable(); - pet->ClearInPetCombat(); [[fallthrough]]; /// @todo: Not sure whether the fallthrough was a mistake (forgetting a break) or intended. This should be double-checked. case REACT_DEFENSIVE: //recovery diff --git a/src/server/game/Maps/Map.cpp b/src/server/game/Maps/Map.cpp index d9c24bfcf4f91f..b15be116dc996b 100644 --- a/src/server/game/Maps/Map.cpp +++ b/src/server/game/Maps/Map.cpp @@ -892,20 +892,10 @@ void Map::Update(const uint32 t_diff, const uint32 s_diff, bool /*thread*/) // handle updates for creatures in combat with player and are more than X yards away if (player->IsInCombat()) { - updateList.clear(); - float rangeSq = player->GetGridActivationRange() - 1.0f; - rangeSq = rangeSq * rangeSq; - HostileReference* ref = player->getHostileRefMgr().getFirst(); - while (ref) - { - if (Unit* unit = ref->GetSource()->GetOwner()) - if (Creature* cre = unit->ToCreature()) - if (cre->FindMap() == player->FindMap() && cre->GetExactDist2dSq(player) > rangeSq) - updateList.push_back(cre); - ref = ref->next(); - } - for (std::vector::const_iterator itr = updateList.begin(); itr != updateList.end(); ++itr) - VisitNearbyCellsOf(*itr, grid_object_update, world_object_update, grid_large_object_update, world_large_object_update); + for (auto const& pair : player->GetCombatManager().GetPvECombatRefs()) + if (Creature* unit = pair.second->GetOther(player)->ToCreature()) + if (unit->GetMapId() == player->GetMapId() && !unit->IsWithinDistInMap(player, GetVisibilityRange(), false)) + VisitNearbyCellsOf(unit, grid_object_update, world_object_update, grid_large_object_update, world_large_object_update); } } @@ -970,7 +960,7 @@ struct ResetNotifier void Map::RemovePlayerFromMap(Player* player, bool remove) { - player->getHostileRefMgr().deleteReferences(true); // pussywizard: multithreading crashfix + player->CombatStop(); bool inWorld = player->IsInWorld(); player->RemoveFromWorld(); diff --git a/src/server/game/Movement/MovementGenerators/WaypointMovementGenerator.cpp b/src/server/game/Movement/MovementGenerators/WaypointMovementGenerator.cpp index da637ef503f2f8..a6ed09541fc454 100644 --- a/src/server/game/Movement/MovementGenerators/WaypointMovementGenerator.cpp +++ b/src/server/game/Movement/MovementGenerators/WaypointMovementGenerator.cpp @@ -366,7 +366,6 @@ void FlightPathMovementGenerator::DoFinalize(Player* player) if (player->m_taxi.empty()) { - player->getHostileRefMgr().setOnlineOfflineState(true); // update z position to ground and orientation for landing point // this prevent cheating with landing point at lags // when client side flight end early in comparison server side @@ -392,7 +391,7 @@ void FlightPathMovementGenerator::DoReset(Player* player) return; } - player->getHostileRefMgr().setOnlineOfflineState(false); + player->CombatStopWithPets(); player->AddUnitState(UNIT_STATE_IN_FLIGHT); player->SetUnitFlag(UNIT_FLAG_DISABLE_MOVE | UNIT_FLAG_TAXI_FLIGHT); diff --git a/src/server/game/Server/WorldSession.cpp b/src/server/game/Server/WorldSession.cpp index 144bd5500af6d1..06f36940e26ca6 100644 --- a/src/server/game/Server/WorldSession.cpp +++ b/src/server/game/Server/WorldSession.cpp @@ -586,7 +586,7 @@ void WorldSession::LogoutPlayer(bool save) //FIXME: logout must be delayed in case lost connection with client in time of combat if (_player->GetDeathTimer()) { - _player->getHostileRefMgr().deleteReferences(true); + _player->CombatStop(); _player->BuildPlayerRepop(); _player->RepopAtGraveyard(); } diff --git a/src/server/game/Spells/Auras/SpellAuraDefines.h b/src/server/game/Spells/Auras/SpellAuraDefines.h index d054ab82682a37..f6e711d1394ec0 100644 --- a/src/server/game/Spells/Auras/SpellAuraDefines.h +++ b/src/server/game/Spells/Auras/SpellAuraDefines.h @@ -281,7 +281,7 @@ enum AuraType SPELL_AURA_HASTE_RANGED = 218, SPELL_AURA_MOD_MANA_REGEN_FROM_STAT = 219, SPELL_AURA_MOD_RATING_FROM_STAT = 220, - SPELL_AURA_IGNORED = 221, + SPELL_AURA_MOD_DETAUNT = 221, SPELL_AURA_222 = 222, SPELL_AURA_RAID_PROC_FROM_CHARGE = 223, SPELL_AURA_224 = 224, diff --git a/src/server/game/Spells/Auras/SpellAuraEffects.cpp b/src/server/game/Spells/Auras/SpellAuraEffects.cpp index ceca6c22f897be..c61a24c84b6114 100644 --- a/src/server/game/Spells/Auras/SpellAuraEffects.cpp +++ b/src/server/game/Spells/Auras/SpellAuraEffects.cpp @@ -281,7 +281,7 @@ pAuraEffectHandler AuraEffectHandler[TOTAL_AURAS] = &AuraEffect::HandleAuraModRangedHaste, //218 SPELL_AURA_HASTE_RANGED &AuraEffect::HandleModManaRegen, //219 SPELL_AURA_MOD_MANA_REGEN_FROM_STAT &AuraEffect::HandleModRatingFromStat, //220 SPELL_AURA_MOD_RATING_FROM_STAT - &AuraEffect::HandleNoImmediateEffect, //221 SPELL_AURA_IGNORED + &AuraEffect::HandleModDetaunt, //221 SPELL_AURA_MOD_DETAUNT &AuraEffect::HandleUnused, //222 unused (3.2.0) only for spell 44586 that not used in real spell cast &AuraEffect::HandleNoImmediateEffect, //223 SPELL_AURA_RAID_PROC_FROM_CHARGE &AuraEffect::HandleUnused, //224 unused (3.0.8a) @@ -2974,6 +2974,8 @@ void AuraEffect::HandleAuraTransform(AuraApplication const* aurApp, uint8 mode, } } } + + target->GetThreatManager().UpdateOnlineStates(true, false); } void AuraEffect::HandleAuraModScale(AuraApplication const* aurApp, uint8 mode, bool /*apply*/) const @@ -3069,19 +3071,25 @@ void AuraEffect::HandleFeignDeath(AuraApplication const* aurApp, uint8 mode, boo } } } + if (target->GetMap()->IsDungeon()) // feign death does not remove combat in dungeons + { + target->AttackStop(); + if (Player* targetPlayer = target->ToPlayer()) + targetPlayer->SendAttackSwingCancelAttack(); + } + else + target->CombatStop(false, false); if (target->GetInstanceScript() && target->GetInstanceScript()->IsEncounterInProgress()) { // Xinef: replaced with CombatStop(false) target->AttackStop(); target->RemoveAllAttackers(); - target->getHostileRefMgr().addThreatPercent(-100); target->ToPlayer()->SendAttackSwingCancelAttack(); // melee and ranged forced attack cancel } else { target->CombatStop(); - target->getHostileRefMgr().deleteReferences(); } target->RemoveAurasWithInterruptFlags(AURA_INTERRUPT_FLAG_IMMUNE_OR_LOST_SELECTION); @@ -3131,6 +3139,7 @@ void AuraEffect::HandleFeignDeath(AuraApplication const* aurApp, uint8 mode, boo target->ClearUnitState(UNIT_STATE_DIED); } + target->GetThreatManager().UpdateOnlineStates(true, false); } void AuraEffect::HandleModUnattackable(AuraApplication const* aurApp, uint8 mode, bool apply) const @@ -3150,7 +3159,14 @@ void AuraEffect::HandleModUnattackable(AuraApplication const* aurApp, uint8 mode if (apply && (mode & AURA_EFFECT_HANDLE_REAL)) { // xinef: this aura should not stop combat (movie with sindragosa proves that) - //target->CombatStop(); + if (target->GetMap()->IsDungeon()) + { + target->AttackStop(); + if (Player* targetPlayer = target->ToPlayer()) + targetPlayer->SendAttackSwingCancelAttack(); + } + else + target->CombatStop(); target->RemoveAurasWithInterruptFlags(AURA_INTERRUPT_FLAG_IMMUNE_OR_LOST_SELECTION); } } @@ -3640,18 +3656,15 @@ void AuraEffect::HandleForceMoveForward(AuraApplication const* aurApp, uint8 mod /*** THREAT ***/ /****************************/ -void AuraEffect::HandleModThreat(AuraApplication const* aurApp, uint8 mode, bool apply) const +void AuraEffect::HandleModThreat(AuraApplication const* aurApp, uint8 mode, bool /*apply*/) const { if (!(mode & AURA_EFFECT_HANDLE_CHANGE_AMOUNT_MASK)) return; - Unit* target = aurApp->GetTarget(); - for (int8 i = 0; i < MAX_SPELL_SCHOOL; ++i) - if (GetMiscValue() & (1 << i)) - ApplyPercentModFloatVar(target->m_threatModifier[i], float(GetAmount()), apply); + aurApp->GetTarget()->GetThreatManager().UpdateMySpellSchoolModifiers(); } -void AuraEffect::HandleAuraModTotalThreat(AuraApplication const* aurApp, uint8 mode, bool apply) const +void AuraEffect::HandleAuraModTotalThreat(AuraApplication const* aurApp, uint8 mode, bool /*apply*/) const { if (!(mode & AURA_EFFECT_HANDLE_CHANGE_AMOUNT_MASK)) return; @@ -3663,7 +3676,7 @@ void AuraEffect::HandleAuraModTotalThreat(AuraApplication const* aurApp, uint8 m Unit* caster = GetCaster(); if (caster && caster->IsAlive()) - target->getHostileRefMgr().addTempThreat((float)GetAmount(), apply); + caster->GetThreatManager().UpdateMyTempModifiers(); } void AuraEffect::HandleModTaunt(AuraApplication const* aurApp, uint8 mode, bool apply) const @@ -3676,17 +3689,21 @@ void AuraEffect::HandleModTaunt(AuraApplication const* aurApp, uint8 mode, bool if (!target->IsAlive() || !target->CanHaveThreatList()) return; + target->GetThreatManager().TauntUpdate(); +} + +void AuraEffect::HandleModDetaunt(AuraApplication const* aurApp, uint8 mode, bool /*apply*/) const +{ + if (!(mode & AURA_EFFECT_HANDLE_REAL)) + return; + Unit* caster = GetCaster(); - if (!caster || !caster->IsAlive()) + Unit* target = aurApp->GetTarget(); + + if (!caster || !caster->IsAlive() || !target->IsAlive() || !caster->CanHaveThreatList()) return; - if (apply) - target->TauntApply(caster); - else - { - // When taunt aura fades out, mob will switch to previous target if current has less than 1.1 * secondthreat - target->TauntFadeOut(caster); - } + caster->GetThreatManager().TauntUpdate(); } /*****************************/ @@ -3701,6 +3718,7 @@ void AuraEffect::HandleModConfuse(AuraApplication const* aurApp, uint8 mode, boo Unit* target = aurApp->GetTarget(); target->SetControlled(apply, UNIT_STATE_CONFUSED); + target->GetThreatManager().UpdateOnlineStates(true, false); } void AuraEffect::HandleModFear(AuraApplication const* aurApp, uint8 mode, bool apply) const @@ -3711,6 +3729,7 @@ void AuraEffect::HandleModFear(AuraApplication const* aurApp, uint8 mode, bool a Unit* target = aurApp->GetTarget(); target->SetControlled(apply, UNIT_STATE_FLEEING, GetCaster(), true); + target->GetThreatManager().UpdateOnlineStates(true, false); } void AuraEffect::HandleAuraModStun(AuraApplication const* aurApp, uint8 mode, bool apply) const @@ -3731,6 +3750,7 @@ void AuraEffect::HandleAuraModRoot(AuraApplication const* aurApp, uint8 mode, bo Unit* target = aurApp->GetTarget(); target->SetControlled(apply, UNIT_STATE_ROOT); + target->GetThreatManager().UpdateOnlineStates(true, false); } void AuraEffect::HandlePreventFleeing(AuraApplication const* aurApp, uint8 mode, bool apply) const @@ -4501,6 +4521,7 @@ void AuraEffect::HandleAuraModSchoolImmunity(AuraApplication const* aurApp, uint ++iter; } } + target->GetThreatManager().UpdateOnlineStates(true, false); } void AuraEffect::HandleAuraModDmgImmunity(AuraApplication const* aurApp, uint8 mode, bool apply) const @@ -4511,6 +4532,8 @@ void AuraEffect::HandleAuraModDmgImmunity(AuraApplication const* aurApp, uint8 m Unit* target = aurApp->GetTarget(); target->ApplySpellImmune(GetId(), IMMUNITY_DAMAGE, GetMiscValue(), apply); + + target->GetThreatManager().UpdateOnlineStates(true, false); } void AuraEffect::HandleAuraModDispelImmunity(AuraApplication const* aurApp, uint8 mode, bool apply) const @@ -5827,7 +5850,7 @@ void AuraEffect::HandleAuraDummy(AuraApplication const* aurApp, uint8 mode, bool case 1515: // Tame beast // FIX_ME: this is 2.0.12 threat effect replaced in 2.1.x by dummy aura, must be checked for correctness if (caster && target->CanHaveThreatList()) - target->AddThreat(caster, 10.0f); + target->GetThreatManager().AddThreat(caster, 10.0f); break; case 34026: // kill command { @@ -6338,7 +6361,7 @@ void AuraEffect::HandleForceReaction(AuraApplication const* aurApp, uint8 mode, player->GetReputationMgr().ApplyForceReaction(faction_id, faction_rank, apply); player->GetReputationMgr().SendForceReactions(); - // stop fighting if at apply forced rank friendly or at remove real rank friendly + // stop fighting at apply (if forced rank friendly) or at remove (if real rank friendly) if ((apply && faction_rank >= REP_FRIENDLY) || (!apply && player->GetReputationRank(faction_id) >= REP_FRIENDLY)) player->StopAttackFaction(faction_id); } @@ -7273,7 +7296,7 @@ void AuraEffect::HandlePeriodicHealthLeechAuraTick(Unit* target, Unit* caster) c HealInfo healInfo(caster, caster, heal, GetSpellInfo(), GetSpellInfo()->GetSchoolMask()); int32 gain = caster->HealBySpell(healInfo); - caster->getHostileRefMgr().threatAssist(caster, gain * 0.5f, GetSpellInfo()); + caster->GetThreatManager().ForwardThreatForAssistingMe(caster, healInfo.GetEffectiveHeal() * 0.5f, GetSpellInfo()); caster->ProcSkillsAndAuras(caster, PROC_FLAG_DONE_PERIODIC, PROC_FLAG_TAKEN_PERIODIC, PROC_SPELL_TYPE_HEAL, PROC_SPELL_PHASE_NONE, hitMask, nullptr, nullptr, &healInfo); } @@ -7425,7 +7448,7 @@ void AuraEffect::HandlePeriodicHealAurasTick(Unit* target, Unit* caster) const target->SendPeriodicAuraLog(&pInfo); if (caster) - target->getHostileRefMgr().threatAssist(caster, float(healInfo.GetEffectiveHeal()) * 0.5f, GetSpellInfo()); + target->GetThreatManager().ForwardThreatForAssistingMe(caster, float(healInfo.GetEffectiveHeal()) * 0.5f, GetSpellInfo()); bool haveCastItem = GetBase()->GetCastItemGUID(); @@ -7507,10 +7530,10 @@ void AuraEffect::HandlePeriodicManaLeechAuraTick(Unit* target, Unit* caster) con if (gainAmount) { gainedAmount = caster->ModifyPower(PowerType, gainAmount); - target->AddThreat(caster, float(gainedAmount) * 0.5f, GetSpellInfo()->GetSchoolMask(), GetSpellInfo()); + target->GetThreatManager().AddThreat(caster, float(gainedAmount) * 0.5f, GetSpellInfo(), true); } - target->AddThreat(caster, float(gainedAmount) * 0.5f, GetSpellInfo()->GetSchoolMask(), GetSpellInfo()); + target->GetThreatManager().AddThreat(caster, float(gainedAmount) * 0.5f, GetSpellInfo(), true); // remove CC auras target->RemoveAurasWithInterruptFlags(AURA_INTERRUPT_FLAG_TAKE_DAMAGE); @@ -7562,7 +7585,7 @@ void AuraEffect::HandleObsModPowerAuraTick(Unit* target, Unit* caster) const int32 gain = target->ModifyPower(PowerType, amount); if (caster) - target->getHostileRefMgr().threatAssist(caster, float(gain) * 0.5f, GetSpellInfo()); + target->GetThreatManager().ForwardThreatForAssistingMe(caster, float(gain) * 0.5f, GetSpellInfo(), true); } void AuraEffect::HandlePeriodicEnergizeAuraTick(Unit* target, Unit* caster) const @@ -7596,7 +7619,7 @@ void AuraEffect::HandlePeriodicEnergizeAuraTick(Unit* target, Unit* caster) cons int32 gain = target->ModifyPower(PowerType, amount); if (caster) - target->getHostileRefMgr().threatAssist(caster, float(gain) * 0.5f, GetSpellInfo()); + target->GetThreatManager().ForwardThreatForAssistingMe(caster, float(gain) * 0.5f, GetSpellInfo(), true); } void AuraEffect::HandlePeriodicPowerBurnAuraTick(Unit* target, Unit* caster) const diff --git a/src/server/game/Spells/Auras/SpellAuraEffects.h b/src/server/game/Spells/Auras/SpellAuraEffects.h index 22f95958be0bf1..1fdfb17dddc89c 100644 --- a/src/server/game/Spells/Auras/SpellAuraEffects.h +++ b/src/server/game/Spells/Auras/SpellAuraEffects.h @@ -215,6 +215,7 @@ class AuraEffect void HandleModThreat(AuraApplication const* aurApp, uint8 mode, bool apply) const; void HandleAuraModTotalThreat(AuraApplication const* aurApp, uint8 mode, bool apply) const; void HandleModTaunt(AuraApplication const* aurApp, uint8 mode, bool apply) const; + void HandleModDetaunt(AuraApplication const* aurApp, uint8 mode, bool apply) const; // control void HandleModConfuse(AuraApplication const* aurApp, uint8 mode, bool apply) const; void HandleModFear(AuraApplication const* aurApp, uint8 mode, bool apply) const; diff --git a/src/server/game/Spells/Spell.cpp b/src/server/game/Spells/Spell.cpp index 173963723ef452..9abfad65f285d3 100644 --- a/src/server/game/Spells/Spell.cpp +++ b/src/server/game/Spells/Spell.cpp @@ -2807,7 +2807,7 @@ void Spell::DoAllEffectOnTarget(TargetInfo* target) } int32 gain = caster->HealBySpell(healInfo, crit); - unitTarget->getHostileRefMgr().threatAssist(caster, float(gain) * 0.5f, m_spellInfo); + unitTarget->GetThreatManager().ForwardThreatForAssistingMe(m_caster, 0.0f, m_spellInfo, true); m_healing = gain; // Do triggers for unit @@ -2933,7 +2933,7 @@ void Spell::DoAllEffectOnTarget(TargetInfo* target) { if (missInfo != SPELL_MISS_EVADE && !m_caster->IsFriendlyTo(effectUnit) && (!m_spellInfo->IsPositive() || m_spellInfo->HasEffect(SPELL_EFFECT_DISPEL))) { - m_caster->CombatStart(effectUnit, !(m_spellInfo->AttributesEx3 & SPELL_ATTR3_SUPRESS_TARGET_PROCS)); + m_caster->AttackedTarget(effectUnit, m_spellInfo->HasInitialAggro()); // Patch 3.0.8: All player spells which cause a creature to become aggressive to you will now also immediately cause the creature to be tapped. if (effectUnit->IsInCombatWith(m_caster)) @@ -3076,16 +3076,18 @@ SpellMissInfo Spell::DoSpellHitOnUnit(Unit* unit, uint32 effectMask, bool scaleA // assisting case, healing and resurrection if (unit->HasUnitState(UNIT_STATE_ATTACK_PLAYER)) { - m_caster->SetContestedPvP(); - if (m_caster->GetTypeId() == TYPEID_PLAYER && !m_spellInfo->HasAttribute(SPELL_ATTR0_CU_NO_PVP_FLAG)) - m_caster->ToPlayer()->UpdatePvP(true); + if (Player* playerOwner = m_caster->GetCharmerOrOwnerPlayerOrPlayerItself()) + { + playerOwner->SetContestedPvP(); + playerOwner->UpdatePvP(true); + } } - // xinef: triggered spells should not prolong combat - if (unit->IsInCombat() && !m_spellInfo->HasAttribute(SPELL_ATTR3_SUPRESS_TARGET_PROCS) && !m_triggeredByAuraSpell) + if (unit->IsInCombat() && m_spellInfo->HasInitialAggro()) { - m_caster->SetInCombatState(unit->GetCombatTimer() > 0, unit); - unit->getHostileRefMgr().threatAssist(m_caster, 0.0f); + if (m_caster->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_PVP)) // only do explicit combat forwarding for PvP enabled units + m_caster->GetCombatManager().InheritCombatStatesFrom(unit); // for creature v creature combat, the threat forward does it for us + unit->GetThreatManager().ForwardThreatForAssistingMe(m_caster, 0.0f, nullptr, true); } } } @@ -4070,12 +4072,6 @@ void Spell::_cast(bool skipCheck) if (target->GetTypeId() == TYPEID_UNIT) m_caster->CastSpell(target, 32747, true); - // xinef: start combat at cast for delayed spells, only for explicit target - if (Unit* target = m_targets.GetUnitTarget()) - if (m_caster->GetTypeId() == TYPEID_PLAYER || (m_caster->IsPet() && m_caster->IsControlledByPlayer())) - if (GetDelayMoment() > 0 && !m_caster->IsFriendlyTo(target) && !m_spellInfo->HasAura(SPELL_AURA_BIND_SIGHT) && (!m_spellInfo->IsPositive() || m_spellInfo->HasEffect(SPELL_EFFECT_DISPEL))) - m_caster->CombatStartOnCast(target, !m_spellInfo->HasAttribute(SPELL_ATTR3_SUPRESS_TARGET_PROCS), GetDelayMoment() + 500); // xinef: increase this time so we dont leave and enter combat in a moment - if (m_caster->GetTypeId() == TYPEID_PLAYER) if (m_caster->ToPlayer()->GetCommandStatus(CHEAT_COOLDOWN)) m_caster->ToPlayer()->RemoveSpellCooldown(m_spellInfo->Id, true); @@ -5596,10 +5592,10 @@ void Spell::HandleThreatSpells() bool IsFriendly = m_caster->IsFriendlyTo(target); // positive spells distribute threat among all units that are in combat with target, like healing if (m_spellInfo->_IsPositiveSpell() && IsFriendly) - target->getHostileRefMgr().threatAssist(m_caster, threatToAdd, m_spellInfo); + target->GetThreatManager().ForwardThreatForAssistingMe(m_caster, threatToAdd, m_spellInfo); // for negative spells threat gets distributed among affected targets else if (!m_spellInfo->_IsPositiveSpell() && !IsFriendly && target->CanHaveThreatList()) - target->AddThreat(m_caster, threatToAdd, m_spellInfo->GetSchoolMask(), m_spellInfo); + target->GetThreatManager().AddThreat(m_caster, threatToAdd, m_spellInfo, true); } LOG_DEBUG("spells.aura", "Spell {}, added an additional {} threat for {} {} target(s)", m_spellInfo->Id, threat, m_spellInfo->_IsPositiveSpell() ? "assisting" : "harming", uint32(m_UniqueTargetInfo.size())); } @@ -5777,8 +5773,7 @@ SpellCastResult Spell::CheckCast(bool strict) if (Unit* victim = member->GetVictim()) if (victim->IsInCombat() && m_caster->GetDistance(victim) < m_caster->GetVisibilityRange()) { - m_caster->CombatStart(victim); - victim->AddThreat(m_caster, 1.0f); + m_caster->EngageWithTarget(victim); break; } return SPELL_FAILED_TARGET_CANNOT_BE_RESURRECTED; @@ -8267,6 +8262,10 @@ void Spell::DoAllEffectOnLaunchTarget(TargetInfo& targetInfo, float* multiplier) if (!unit) return; + // This will only cause combat - the target will engage once the projectile hits (in DoAllEffectOnTarget) + if (targetInfo.missCondition != SPELL_MISS_EVADE && !m_caster->IsFriendlyTo(unit) && (!m_spellInfo->IsPositive() || m_spellInfo->HasEffect(SPELL_EFFECT_DISPEL)) && (m_spellInfo->HasInitialAggro() || unit->IsEngaged())) + m_caster->SetInCombatWith(unit); + for (uint32 i = 0; i < MAX_SPELL_EFFECTS; ++i) { if (targetInfo.effectMask & (1 << i)) diff --git a/src/server/game/Spells/SpellEffects.cpp b/src/server/game/Spells/SpellEffects.cpp index e04e73a5f45134..9cf988c710beb5 100644 --- a/src/server/game/Spells/SpellEffects.cpp +++ b/src/server/game/Spells/SpellEffects.cpp @@ -1,19 +1,19 @@ - /* - * This file is part of the AzerothCore Project. See AUTHORS file for Copyright information - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU Affero General Public License as published by the - * Free Software Foundation; either version 3 of the License, or (at your - * option) any later version. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License along - * with this program. If not, see . - */ +/* +* This file is part of the AzerothCore Project. See AUTHORS file for Copyright information +* +* This program is free software; you can redistribute it and/or modify it +* under the terms of the GNU Affero General Public License as published by the +* Free Software Foundation; either version 3 of the License, or (at your +* option) any later version. +* +* This program is distributed in the hope that it will be useful, but WITHOUT +* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for +* more details. +* +* You should have received a copy of the GNU General Public License along +* with this program. If not, see . +*/ #include "Battleground.h" #include "BattlegroundEY.h" @@ -56,6 +56,7 @@ #include "TemporarySummon.h" #include "Totem.h" #include "Transport.h" +#include "ThreatMgr.h" #include "Unit.h" #include "UpdateData.h" #include "UpdateMask.h" @@ -65,7 +66,7 @@ #include "WorldPacket.h" #include "Transmogrification.h" - /// @todo: this import is not necessary for compilation and marked as unused by the IDE +/// @todo: this import is not necessary for compilation and marked as unused by the IDE // however, for some reasons removing it would cause a damn linking issue // there is probably some underlying problem with imports which should properly addressed // see: https://github.com/azerothcore/azerothcore-wotlk/issues/9766 @@ -367,313 +368,313 @@ void Spell::EffectSchoolDMG(SpellEffIndex effIndex) bool apply_direct_bonus = true; switch (m_spellInfo->SpellFamilyName) { - case SPELLFAMILY_GENERIC: - { - // Meteor like spells (divided damage to targets) - if (m_spellInfo->HasAttribute(SPELL_ATTR0_CU_SHARE_DAMAGE)) - { - uint32 count = 0; - for (std::list::iterator ihit = m_UniqueTargetInfo.begin(); ihit != m_UniqueTargetInfo.end(); ++ihit) - if (ihit->effectMask & (1 << effIndex)) - ++count; + case SPELLFAMILY_GENERIC: + { + // Meteor like spells (divided damage to targets) + if (m_spellInfo->HasAttribute(SPELL_ATTR0_CU_SHARE_DAMAGE)) + { + uint32 count = 0; + for (std::list::iterator ihit = m_UniqueTargetInfo.begin(); ihit != m_UniqueTargetInfo.end(); ++ihit) + if (ihit->effectMask & (1 << effIndex)) + ++count; - damageDivisor += count; // divide to all targets - } - break; - } - case SPELLFAMILY_WARRIOR: + damageDivisor += count; // divide to all targets + } + break; + } + case SPELLFAMILY_WARRIOR: + { + // Victory Rush + if (m_spellInfo->SpellFamilyFlags[1] & 0x100) + ApplyPct(damage, m_caster->GetTotalAttackPowerValue(BASE_ATTACK)); + // Shockwave + else if (m_spellInfo->Id == 46968) + { + int32 pct = m_caster->CalculateSpellDamage(unitTarget, m_spellInfo, 2); + if (pct > 0) + damage += int32(CalculatePct(m_caster->GetTotalAttackPowerValue(BASE_ATTACK), pct)); + break; + } + break; + } + case SPELLFAMILY_WARLOCK: + { + // Incinerate Rank 1 & 2 + if ((m_spellInfo->SpellFamilyFlags[1] & 0x000040) && m_spellInfo->SpellIconID == 2128) + { + // Incinerate does more dmg (dmg*0.25) if the target have Immolate debuff. + // Check aura state for speed but aura state set not only for Immolate spell + if (unitTarget->HasAuraState(AURA_STATE_CONFLAGRATE)) { - // Victory Rush - if (m_spellInfo->SpellFamilyFlags[1] & 0x100) - ApplyPct(damage, m_caster->GetTotalAttackPowerValue(BASE_ATTACK)); - // Shockwave - else if (m_spellInfo->Id == 46968) - { - int32 pct = m_caster->CalculateSpellDamage(unitTarget, m_spellInfo, 2); - if (pct > 0) - damage += int32(CalculatePct(m_caster->GetTotalAttackPowerValue(BASE_ATTACK), pct)); - break; - } - break; + if (unitTarget->GetAuraEffect(SPELL_AURA_PERIODIC_DAMAGE, SPELLFAMILY_WARLOCK, 0x4, 0, 0)) + damage += damage / 4; } - case SPELLFAMILY_WARLOCK: + } + // Immolate - hidden delay for conflagrate + else if (m_spellInfo->SpellFamilyFlags[0] & 0x4) + { + } + // Conflagrate - consumes Immolate or Shadowflame + else if (m_spellInfo->TargetAuraState == AURA_STATE_CONFLAGRATE) + { + AuraEffect const* aura = nullptr; // found req. aura for damage calculation + + Unit::AuraEffectList const& mPeriodic = unitTarget->GetAuraEffectsByType(SPELL_AURA_PERIODIC_DAMAGE); + for (Unit::AuraEffectList::const_iterator i = mPeriodic.begin(); i != mPeriodic.end(); ++i) { - // Incinerate Rank 1 & 2 - if ((m_spellInfo->SpellFamilyFlags[1] & 0x000040) && m_spellInfo->SpellIconID == 2128) - { - // Incinerate does more dmg (dmg*0.25) if the target have Immolate debuff. - // Check aura state for speed but aura state set not only for Immolate spell - if (unitTarget->HasAuraState(AURA_STATE_CONFLAGRATE)) - { - if (unitTarget->GetAuraEffect(SPELL_AURA_PERIODIC_DAMAGE, SPELLFAMILY_WARLOCK, 0x4, 0, 0)) - damage += damage / 4; - } - } - // Immolate - hidden delay for conflagrate - else if (m_spellInfo->SpellFamilyFlags[0] & 0x4) + // for caster applied auras only + if ((*i)->GetSpellInfo()->SpellFamilyName != SPELLFAMILY_WARLOCK || + (*i)->GetCasterGUID() != m_caster->GetGUID()) + continue; + + // Immolate + if ((*i)->GetSpellInfo()->SpellFamilyFlags[0] & 0x4) { + aura = *i; // it selected always if exist + break; } - // Conflagrate - consumes Immolate or Shadowflame - else if (m_spellInfo->TargetAuraState == AURA_STATE_CONFLAGRATE) - { - AuraEffect const* aura = nullptr; // found req. aura for damage calculation - Unit::AuraEffectList const& mPeriodic = unitTarget->GetAuraEffectsByType(SPELL_AURA_PERIODIC_DAMAGE); - for (Unit::AuraEffectList::const_iterator i = mPeriodic.begin(); i != mPeriodic.end(); ++i) - { - // for caster applied auras only - if ((*i)->GetSpellInfo()->SpellFamilyName != SPELLFAMILY_WARLOCK || - (*i)->GetCasterGUID() != m_caster->GetGUID()) - continue; - - // Immolate - if ((*i)->GetSpellInfo()->SpellFamilyFlags[0] & 0x4) - { - aura = *i; // it selected always if exist - break; - } - - // Shadowflame - if ((*i)->GetSpellInfo()->SpellFamilyFlags[2] & 0x00000002) - aura = *i; // remember but wait possible Immolate as primary priority - } + // Shadowflame + if ((*i)->GetSpellInfo()->SpellFamilyFlags[2] & 0x00000002) + aura = *i; // remember but wait possible Immolate as primary priority + } - // found Immolate or Shadowflame - if (aura) - { - uint32 pdamage = uint32(std::max(aura->GetAmount(), 0)); - pdamage = unitTarget->SpellDamageBonusTaken(m_caster, aura->GetSpellInfo(), pdamage, DOT, aura->GetBase()->GetStackAmount()); - uint32 pct_dir = m_caster->CalculateSpellDamage(unitTarget, m_spellInfo, (effIndex + 1)); - uint8 baseTotalTicks = uint8(m_caster->CalcSpellDuration(aura->GetSpellInfo()) / aura->GetSpellInfo()->Effects[EFFECT_0].Amplitude); + // found Immolate or Shadowflame + if (aura) + { + uint32 pdamage = uint32(std::max(aura->GetAmount(), 0)); + pdamage = unitTarget->SpellDamageBonusTaken(m_caster, aura->GetSpellInfo(), pdamage, DOT, aura->GetBase()->GetStackAmount()); + uint32 pct_dir = m_caster->CalculateSpellDamage(unitTarget, m_spellInfo, (effIndex + 1)); + uint8 baseTotalTicks = uint8(m_caster->CalcSpellDuration(aura->GetSpellInfo()) / aura->GetSpellInfo()->Effects[EFFECT_0].Amplitude); - damage += int32(CalculatePct(pdamage * baseTotalTicks, pct_dir)); + damage += int32(CalculatePct(pdamage * baseTotalTicks, pct_dir)); - uint32 pct_dot = m_caster->CalculateSpellDamage(unitTarget, m_spellInfo, (effIndex + 2)) / 3; - m_spellValue->EffectBasePoints[1] = m_spellInfo->Effects[EFFECT_1].CalcBaseValue(int32(CalculatePct(pdamage * baseTotalTicks, pct_dot))); + uint32 pct_dot = m_caster->CalculateSpellDamage(unitTarget, m_spellInfo, (effIndex + 2)) / 3; + m_spellValue->EffectBasePoints[1] = m_spellInfo->Effects[EFFECT_1].CalcBaseValue(int32(CalculatePct(pdamage * baseTotalTicks, pct_dot))); - apply_direct_bonus = false; - // Glyph of Conflagrate - if (!m_caster->HasAura(56235)) - unitTarget->RemoveAurasDueToSpell(aura->GetId(), m_caster->GetGUID()); + apply_direct_bonus = false; + // Glyph of Conflagrate + if (!m_caster->HasAura(56235)) + unitTarget->RemoveAurasDueToSpell(aura->GetId(), m_caster->GetGUID()); - break; - } - } - // Shadow Bite - else if (m_spellInfo->SpellFamilyFlags[1] & 0x400000) - { - if (m_caster->GetTypeId() == TYPEID_UNIT && m_caster->IsPet()) - { - if (Player* owner = m_caster->GetOwner()->ToPlayer()) - { - if (AuraEffect* aurEff = owner->GetAuraEffect(SPELL_AURA_ADD_FLAT_MODIFIER, SPELLFAMILY_WARLOCK, 214, 0)) - { - int32 bp0 = aurEff->GetId() == 54037 ? 4 : 8; - m_caster->CastCustomSpell(m_caster, 54425, &bp0, nullptr, nullptr, true); - } - } - } - } break; } - case SPELLFAMILY_PRIEST: + } + // Shadow Bite + else if (m_spellInfo->SpellFamilyFlags[1] & 0x400000) + { + if (m_caster->GetTypeId() == TYPEID_UNIT && m_caster->IsPet()) { - // Improved Mind Blast (Mind Blast in shadow form bonus) - if (m_caster->GetShapeshiftForm() == FORM_SHADOW && (m_spellInfo->SpellFamilyFlags[0] & 0x00002000)) + if (Player* owner = m_caster->GetOwner()->ToPlayer()) { - Unit::AuraEffectList const& ImprMindBlast = m_caster->GetAuraEffectsByType(SPELL_AURA_ADD_FLAT_MODIFIER); - for (Unit::AuraEffectList::const_iterator i = ImprMindBlast.begin(); i != ImprMindBlast.end(); ++i) + if (AuraEffect* aurEff = owner->GetAuraEffect(SPELL_AURA_ADD_FLAT_MODIFIER, SPELLFAMILY_WARLOCK, 214, 0)) { - if ((*i)->GetSpellInfo()->SpellFamilyName == SPELLFAMILY_PRIEST && - ((*i)->GetSpellInfo()->SpellIconID == 95)) - { - int chance = (*i)->GetSpellInfo()->Effects[EFFECT_1].CalcValue(m_caster); - if (roll_chance_i(chance)) - // Mind Trauma - m_caster->CastSpell(unitTarget, 48301, true, 0); - break; - } + int32 bp0 = aurEff->GetId() == 54037 ? 4 : 8; + m_caster->CastCustomSpell(m_caster, 54425, &bp0, nullptr, nullptr, true); } } - break; } - case SPELLFAMILY_DRUID: + } + break; + } + case SPELLFAMILY_PRIEST: + { + // Improved Mind Blast (Mind Blast in shadow form bonus) + if (m_caster->GetShapeshiftForm() == FORM_SHADOW && (m_spellInfo->SpellFamilyFlags[0] & 0x00002000)) + { + Unit::AuraEffectList const& ImprMindBlast = m_caster->GetAuraEffectsByType(SPELL_AURA_ADD_FLAT_MODIFIER); + for (Unit::AuraEffectList::const_iterator i = ImprMindBlast.begin(); i != ImprMindBlast.end(); ++i) { - // Ferocious Bite - if (m_caster->GetTypeId() == TYPEID_PLAYER && (m_spellInfo->SpellFamilyFlags[0] & 0x000800000) && m_spellInfo->SpellVisual[0] == 6587) - { - // converts each extra point of energy into ($f1+$AP/410) additional damage - float ap = m_caster->GetTotalAttackPowerValue(BASE_ATTACK); - float multiple = ap / 410 + (damageMultiplier / 100); - int32 energy = -(m_caster->ModifyPower(POWER_ENERGY, -30)); - damage += int32(energy * multiple); - damage += int32(CalculatePct(m_caster->GetComboPoints() * ap, 7)); - } - // Wrath - else if (m_spellInfo->SpellFamilyFlags[0] & 0x00000001) + if ((*i)->GetSpellInfo()->SpellFamilyName == SPELLFAMILY_PRIEST && + ((*i)->GetSpellInfo()->SpellIconID == 95)) { - // Improved Insect Swarm - if (AuraEffect const* aurEff = m_caster->GetDummyAuraEffect(SPELLFAMILY_DRUID, 1771, 0)) - if (unitTarget->GetAuraEffect(SPELL_AURA_PERIODIC_DAMAGE, SPELLFAMILY_DRUID, 0x00200000, 0, 0)) - AddPct(damage, aurEff->GetAmount()); + int chance = (*i)->GetSpellInfo()->Effects[EFFECT_1].CalcValue(m_caster); + if (roll_chance_i(chance)) + // Mind Trauma + m_caster->CastSpell(unitTarget, 48301, true, 0); + break; } - break; } - case SPELLFAMILY_ROGUE: + } + break; + } + case SPELLFAMILY_DRUID: + { + // Ferocious Bite + if (m_caster->GetTypeId() == TYPEID_PLAYER && (m_spellInfo->SpellFamilyFlags[0] & 0x000800000) && m_spellInfo->SpellVisual[0] == 6587) + { + // converts each extra point of energy into ($f1+$AP/410) additional damage + float ap = m_caster->GetTotalAttackPowerValue(BASE_ATTACK); + float multiple = ap / 410 + (damageMultiplier / 100); + int32 energy = -(m_caster->ModifyPower(POWER_ENERGY, -30)); + damage += int32(energy * multiple); + damage += int32(CalculatePct(m_caster->GetComboPoints() * ap, 7)); + } + // Wrath + else if (m_spellInfo->SpellFamilyFlags[0] & 0x00000001) + { + // Improved Insect Swarm + if (AuraEffect const* aurEff = m_caster->GetDummyAuraEffect(SPELLFAMILY_DRUID, 1771, 0)) + if (unitTarget->GetAuraEffect(SPELL_AURA_PERIODIC_DAMAGE, SPELLFAMILY_DRUID, 0x00200000, 0, 0)) + AddPct(damage, aurEff->GetAmount()); + } + break; + } + case SPELLFAMILY_ROGUE: + { + // Envenom + if (m_spellInfo->SpellFamilyFlags[1] & 0x00000008) + { + if (Player* player = m_caster->ToPlayer()) { - // Envenom - if (m_spellInfo->SpellFamilyFlags[1] & 0x00000008) + // consume from stack dozes not more that have combo-points + if (uint32 combo = player->GetComboPoints()) { - if (Player* player = m_caster->ToPlayer()) + // Lookup for Deadly poison (only attacker applied) + if (AuraEffect const* aurEff = unitTarget->GetAuraEffect(SPELL_AURA_PERIODIC_DAMAGE, SPELLFAMILY_ROGUE, 0x00010000, 0, 0, m_caster->GetGUID())) { - // consume from stack dozes not more that have combo-points - if (uint32 combo = player->GetComboPoints()) + // count consumed deadly poison doses at target + bool needConsume = true; + uint32 spellId = aurEff->GetId(); + + uint32 doses = aurEff->GetBase()->GetStackAmount(); + if (doses > combo) + doses = combo; + + // Master Poisoner + Unit::AuraEffectList const& auraList = player->GetAuraEffectsByType(SPELL_AURA_MOD_AURA_DURATION_BY_DISPEL_NOT_STACK); + for (Unit::AuraEffectList::const_iterator iter = auraList.begin(); iter != auraList.end(); ++iter) { - // Lookup for Deadly poison (only attacker applied) - if (AuraEffect const* aurEff = unitTarget->GetAuraEffect(SPELL_AURA_PERIODIC_DAMAGE, SPELLFAMILY_ROGUE, 0x00010000, 0, 0, m_caster->GetGUID())) + if ((*iter)->GetSpellInfo()->SpellFamilyName == SPELLFAMILY_ROGUE && (*iter)->GetSpellInfo()->SpellIconID == 1960) { - // count consumed deadly poison doses at target - bool needConsume = true; - uint32 spellId = aurEff->GetId(); - - uint32 doses = aurEff->GetBase()->GetStackAmount(); - if (doses > combo) - doses = combo; - - // Master Poisoner - Unit::AuraEffectList const& auraList = player->GetAuraEffectsByType(SPELL_AURA_MOD_AURA_DURATION_BY_DISPEL_NOT_STACK); - for (Unit::AuraEffectList::const_iterator iter = auraList.begin(); iter != auraList.end(); ++iter) - { - if ((*iter)->GetSpellInfo()->SpellFamilyName == SPELLFAMILY_ROGUE && (*iter)->GetSpellInfo()->SpellIconID == 1960) - { - uint32 chance = (*iter)->GetSpellInfo()->Effects[EFFECT_2].CalcValue(m_caster); - - if (chance && roll_chance_i(chance)) - needConsume = false; - - break; - } - } - - if (needConsume) - for (uint32 i = 0; i < doses; ++i) - unitTarget->RemoveAuraFromStack(spellId, m_caster->GetGUID()); - - AddPct(damageMultiplier, doses*100); - damage += int32(player->GetTotalAttackPowerValue(BASE_ATTACK) * 0.09f * combo); - } + uint32 chance = (*iter)->GetSpellInfo()->Effects[EFFECT_2].CalcValue(m_caster); + + if (chance && roll_chance_i(chance)) + needConsume = false; - // Eviscerate and Envenom Bonus Damage (item set effect) - if (m_caster->HasAura(37169)) - damage += combo * 40; + break; + } } + + if (needConsume) + for (uint32 i = 0; i < doses; ++i) + unitTarget->RemoveAuraFromStack(spellId, m_caster->GetGUID()); + + AddPct(damageMultiplier, doses * 100); + damage += int32(player->GetTotalAttackPowerValue(BASE_ATTACK) * 0.09f * combo); } + + // Eviscerate and Envenom Bonus Damage (item set effect) + if (m_caster->HasAura(37169)) + damage += combo * 40; } - // Eviscerate - else if (m_spellInfo->SpellFamilyFlags[0] & 0x00020000) + } + } + // Eviscerate + else if (m_spellInfo->SpellFamilyFlags[0] & 0x00020000) + { + if (m_caster->GetTypeId() == TYPEID_PLAYER) + { + if (uint32 combo = m_caster->ToPlayer()->GetComboPoints()) { - if (m_caster->GetTypeId() == TYPEID_PLAYER) - { - if (uint32 combo = m_caster->ToPlayer()->GetComboPoints()) - { - float ap = m_caster->GetTotalAttackPowerValue(BASE_ATTACK); - damage += int32(ap * combo * 0.07f); + float ap = m_caster->GetTotalAttackPowerValue(BASE_ATTACK); + damage += int32(ap * combo * 0.07f); - // Eviscerate and Envenom Bonus Damage (item set effect) - if (m_caster->HasAura(37169)) - damage += combo * 40; - } - } + // Eviscerate and Envenom Bonus Damage (item set effect) + if (m_caster->HasAura(37169)) + damage += combo * 40; } - break; } - case SPELLFAMILY_HUNTER: + } + break; + } + case SPELLFAMILY_HUNTER: + { + //Gore + if (m_spellInfo->SpellIconID == 1578) + { + if (m_caster->HasAura(57627)) // Charge 6 sec post-affect + AddPct(damageMultiplier, 200); + } + // Steady Shot + else if (m_spellInfo->SpellFamilyFlags[1] & 0x1) + { + bool found = false; + // check dazed affect + Unit::AuraEffectList const& decSpeedList = unitTarget->GetAuraEffectsByType(SPELL_AURA_MOD_DECREASE_SPEED); + for (Unit::AuraEffectList::const_iterator iter = decSpeedList.begin(); iter != decSpeedList.end(); ++iter) { - //Gore - if (m_spellInfo->SpellIconID == 1578) + if ((*iter)->GetSpellInfo()->SpellIconID == 15 && (*iter)->GetSpellInfo()->Dispel == 0) { - if (m_caster->HasAura(57627)) // Charge 6 sec post-affect - AddPct(damageMultiplier, 200); + found = true; + break; } - // Steady Shot - else if (m_spellInfo->SpellFamilyFlags[1] & 0x1) - { - bool found = false; - // check dazed affect - Unit::AuraEffectList const& decSpeedList = unitTarget->GetAuraEffectsByType(SPELL_AURA_MOD_DECREASE_SPEED); - for (Unit::AuraEffectList::const_iterator iter = decSpeedList.begin(); iter != decSpeedList.end(); ++iter) - { - if ((*iter)->GetSpellInfo()->SpellIconID == 15 && (*iter)->GetSpellInfo()->Dispel == 0) - { - found = true; - break; - } - } - - /// @todo: should this be put on taken but not done? - if (found) - damage += m_spellInfo->Effects[EFFECT_1].CalcValue(); + } - if (Player* caster = m_caster->ToPlayer()) - { - // Add Ammo and Weapon damage plus RAP * 0.1 - float dmg_min = 0.f; - float dmg_max = 0.f; - for (uint8 i = 0; i < MAX_ITEM_PROTO_DAMAGES; ++i) - { - dmg_min += caster->GetWeaponDamageRange(RANGED_ATTACK, MINDAMAGE, i); - dmg_max += caster->GetWeaponDamageRange(RANGED_ATTACK, MAXDAMAGE, i); - } + /// @todo: should this be put on taken but not done? + if (found) + damage += m_spellInfo->Effects[EFFECT_1].CalcValue(); - if (dmg_max == 0.0f && dmg_min > dmg_max) - { - damage += int32(dmg_min); - } - else - { - damage += irand(int32(dmg_min), int32(dmg_max)); - } - damage += int32(caster->GetAmmoDPS() * caster->GetAttackTime(RANGED_ATTACK) * 0.001f); - } - } - break; - } - case SPELLFAMILY_PALADIN: + if (Player* caster = m_caster->ToPlayer()) { - // Hammer of the Righteous - if (m_spellInfo->SpellFamilyFlags[1] & 0x00040000) + // Add Ammo and Weapon damage plus RAP * 0.1 + float dmg_min = 0.f; + float dmg_max = 0.f; + for (uint8 i = 0; i < MAX_ITEM_PROTO_DAMAGES; ++i) { - // Add main hand dps * effect[2] amount - if (Player* player = m_caster->ToPlayer()) - { - float minTotal = 0.f; - float maxTotal = 0.f; - for (uint8 i = 0; i < MAX_ITEM_PROTO_DAMAGES; ++i) - { - float tmpMin, tmpMax; - player->CalculateMinMaxDamage(BASE_ATTACK, false, false, tmpMin, tmpMax, i); - minTotal += tmpMin; - maxTotal += tmpMax; - } + dmg_min += caster->GetWeaponDamageRange(RANGED_ATTACK, MINDAMAGE, i); + dmg_max += caster->GetWeaponDamageRange(RANGED_ATTACK, MAXDAMAGE, i); + } - float average = (minTotal + maxTotal) / 2; - int32 count = m_caster->CalculateSpellDamage(unitTarget, m_spellInfo, EFFECT_2); - damage += count * int32(average * IN_MILLISECONDS) / m_caster->GetAttackTime(BASE_ATTACK); - } - break; + if (dmg_max == 0.0f && dmg_min > dmg_max) + { + damage += int32(dmg_min); } - // Shield of Righteousness - if (m_spellInfo->SpellFamilyFlags[EFFECT_1] & 0x100000) + else { - uint8 level = m_caster->GetLevel(); - uint32 block_value = m_caster->GetShieldBlockValue(uint32(float(level) * 29.5f), uint32(float(level) * 34.5f)); - if (m_caster->GetAuraEffect(64882, EFFECT_0)) - block_value += 225; - damage += CalculatePct(block_value, m_spellInfo->Effects[EFFECT_1].CalcValue()); - break; + damage += irand(int32(dmg_min), int32(dmg_max)); } - break; + damage += int32(caster->GetAmmoDPS() * caster->GetAttackTime(RANGED_ATTACK) * 0.001f); } + } + break; + } + case SPELLFAMILY_PALADIN: + { + // Hammer of the Righteous + if (m_spellInfo->SpellFamilyFlags[1] & 0x00040000) + { + // Add main hand dps * effect[2] amount + if (Player* player = m_caster->ToPlayer()) + { + float minTotal = 0.f; + float maxTotal = 0.f; + for (uint8 i = 0; i < MAX_ITEM_PROTO_DAMAGES; ++i) + { + float tmpMin, tmpMax; + player->CalculateMinMaxDamage(BASE_ATTACK, false, false, tmpMin, tmpMax, i); + minTotal += tmpMin; + maxTotal += tmpMax; + } + + float average = (minTotal + maxTotal) / 2; + int32 count = m_caster->CalculateSpellDamage(unitTarget, m_spellInfo, EFFECT_2); + damage += count * int32(average * IN_MILLISECONDS) / m_caster->GetAttackTime(BASE_ATTACK); + } + break; + } + // Shield of Righteousness + if (m_spellInfo->SpellFamilyFlags[EFFECT_1] & 0x100000) + { + uint8 level = m_caster->GetLevel(); + uint32 block_value = m_caster->GetShieldBlockValue(uint32(float(level) * 29.5f), uint32(float(level) * 34.5f)); + if (m_caster->GetAuraEffect(64882, EFFECT_0)) + block_value += 225; + damage += CalculatePct(block_value, m_spellInfo->Effects[EFFECT_1].CalcValue()); + break; + } + break; + } } if (m_originalCaster /*&& damage > 0 Xinef: this can be increased from 0*/ && apply_direct_bonus) @@ -705,91 +706,91 @@ void Spell::EffectDummy(SpellEffIndex effIndex) // selection by spell family switch (m_spellInfo->SpellFamilyName) { - case SPELLFAMILY_GENERIC: - { - switch (m_spellInfo->Id) - { - // Trial of the Champion, Trample - case 67866: - { - if( unitTarget && !unitTarget->IsVehicle() && !unitTarget->GetUInt32Value(UNIT_FIELD_MOUNTDISPLAYID) ) - unitTarget->CastSpell(unitTarget, 67867, false); - return; - } - // Trial of the Champion, Hammer of the Righteous - case 66867: - { - if( !unitTarget ) - return; - if( unitTarget->HasAura(66940) ) - m_caster->CastSpell(unitTarget, 66903, true); - else - m_caster->CastSpell(unitTarget, 66904, true); - return; - } - case 17731: - case 69294: - { - if( !gameObjTarget || gameObjTarget->GetRespawnTime() > GameTime::GetGameTime().count() ) - return; + case SPELLFAMILY_GENERIC: + { + switch (m_spellInfo->Id) + { + // Trial of the Champion, Trample + case 67866: + { + if (unitTarget && !unitTarget->IsVehicle() && !unitTarget->GetUInt32Value(UNIT_FIELD_MOUNTDISPLAYID)) + unitTarget->CastSpell(unitTarget, 67867, false); + return; + } + // Trial of the Champion, Hammer of the Righteous + case 66867: + { + if (!unitTarget) + return; + if (unitTarget->HasAura(66940)) + m_caster->CastSpell(unitTarget, 66903, true); + else + m_caster->CastSpell(unitTarget, 66904, true); + return; + } + case 17731: + case 69294: + { + if (!gameObjTarget || gameObjTarget->GetRespawnTime() > GameTime::GetGameTime().count()) + return; - gameObjTarget->SetRespawnTime(10); - gameObjTarget->SendCustomAnim(gameObjTarget->GetGoAnimProgress()); - if( Creature* trigger = gameObjTarget->SummonCreature(12758, *gameObjTarget, TEMPSUMMON_TIMED_DESPAWN, 1000) ) - trigger->CastSpell(trigger, 17731, false); + gameObjTarget->SetRespawnTime(10); + gameObjTarget->SendCustomAnim(gameObjTarget->GetGoAnimProgress()); + if (Creature* trigger = gameObjTarget->SummonCreature(12758, *gameObjTarget, TEMPSUMMON_TIMED_DESPAWN, 1000)) + trigger->CastSpell(trigger, 17731, false); - return; - } - // HoL, Arc Weld - case 59086: - { - if( m_caster && m_caster->GetTypeId() == TYPEID_PLAYER && m_caster->ToPlayer()->isMoving() ) - m_caster->CastSpell(m_caster, 59097, true); + return; + } + // HoL, Arc Weld + case 59086: + { + if (m_caster && m_caster->GetTypeId() == TYPEID_PLAYER && m_caster->ToPlayer()->isMoving()) + m_caster->CastSpell(m_caster, 59097, true); - return; - } - } - break; - } - case SPELLFAMILY_PALADIN: - switch (m_spellInfo->Id) - { - case 31789: // Righteous Defense (step 1) - { - if (!unitTarget) - return; - // not empty (checked), copy - Unit::AttackerSet attackers = unitTarget->getAttackers(); - - // remove invalid attackers - for (Unit::AttackerSet::iterator aItr = attackers.begin(); aItr != attackers.end();) - if (!(*aItr)->IsValidAttackTarget(m_caster)) - aItr = attackers.erase(aItr); - else - ++aItr; - - // selected from list 3 - uint32 maxTargets = std::min(3, attackers.size()); - for (uint32 i = 0; i < maxTargets; ++i) - { - Unit::AttackerSet::iterator aItr = attackers.begin(); - std::advance(aItr, urand(0, attackers.size() - 1)); - m_caster->CastSpell((*aItr), 31790, true); - attackers.erase(aItr); - } + return; + } + } + break; + } + case SPELLFAMILY_PALADIN: + switch (m_spellInfo->Id) + { + case 31789: // Righteous Defense (step 1) + { + if (!unitTarget) + return; + // not empty (checked), copy + Unit::AttackerSet attackers = unitTarget->getAttackers(); - return; - } - } - break; - case SPELLFAMILY_ROGUE: - // Hunger for Blood - if( m_spellInfo->Id == 51662 ) + // remove invalid attackers + for (Unit::AttackerSet::iterator aItr = attackers.begin(); aItr != attackers.end();) + if (!(*aItr)->IsValidAttackTarget(m_caster)) + aItr = attackers.erase(aItr); + else + ++aItr; + + // selected from list 3 + uint32 maxTargets = std::min(3, attackers.size()); + for (uint32 i = 0; i < maxTargets; ++i) { - m_caster->CastSpell(m_caster, 63848, true); - return; + Unit::AttackerSet::iterator aItr = attackers.begin(); + std::advance(aItr, urand(0, attackers.size() - 1)); + m_caster->CastSpell((*aItr), 31790, true); + attackers.erase(aItr); } - break; + + return; + } + } + break; + case SPELLFAMILY_ROGUE: + // Hunger for Blood + if (m_spellInfo->Id == 51662) + { + m_caster->CastSpell(m_caster, 63848, true); + return; + } + break; } // pet auras @@ -820,125 +821,125 @@ void Spell::EffectDummy(SpellEffIndex effIndex) void Spell::EffectTriggerSpell(SpellEffIndex effIndex) { if (effectHandleMode != SPELL_EFFECT_HANDLE_LAUNCH_TARGET - && effectHandleMode != SPELL_EFFECT_HANDLE_LAUNCH) + && effectHandleMode != SPELL_EFFECT_HANDLE_LAUNCH) return; uint32 triggered_spell_id = m_spellInfo->Effects[effIndex].TriggerSpell; /// @todo: move those to spell scripts if (m_spellInfo->Effects[effIndex].Effect == SPELL_EFFECT_TRIGGER_SPELL - && effectHandleMode == SPELL_EFFECT_HANDLE_LAUNCH_TARGET) + && effectHandleMode == SPELL_EFFECT_HANDLE_LAUNCH_TARGET) { // special cases switch (triggered_spell_id) { // Mirror Image - case 58832: - { - // Glyph of Mirror Image - if (m_caster->HasAura(63093)) - m_caster->CastSpell(m_caster, 65047, true); // Mirror Image + case 58832: + { + // Glyph of Mirror Image + if (m_caster->HasAura(63093)) + m_caster->CastSpell(m_caster, 65047, true); // Mirror Image - break; - } - // Vanish (not exist) - case 18461: - { - unitTarget->RemoveMovementImpairingAuras(true); - unitTarget->RemoveAurasByType(SPELL_AURA_MOD_STALKED); + break; + } + // Vanish (not exist) + case 18461: + { + unitTarget->RemoveMovementImpairingAuras(true); + unitTarget->RemoveAurasByType(SPELL_AURA_MOD_STALKED); - // See if we already are stealthed. If so, we're done. - if (unitTarget->HasAura(1784)) - return; + // See if we already are stealthed. If so, we're done. + if (unitTarget->HasAura(1784)) + return; - // Reset cooldown on stealth if needed - if (unitTarget->GetTypeId() == TYPEID_PLAYER && unitTarget->ToPlayer()->HasSpellCooldown(1784)) - unitTarget->ToPlayer()->RemoveSpellCooldown(1784); + // Reset cooldown on stealth if needed + if (unitTarget->GetTypeId() == TYPEID_PLAYER && unitTarget->ToPlayer()->HasSpellCooldown(1784)) + unitTarget->ToPlayer()->RemoveSpellCooldown(1784); - unitTarget->CastSpell(unitTarget, 1784, true); - return; - } - // Demonic Empowerment -- succubus - case 54437: - { - unitTarget->RemoveMovementImpairingAuras(true); - unitTarget->RemoveAurasByType(SPELL_AURA_MOD_STALKED); - unitTarget->RemoveAurasByType(SPELL_AURA_MOD_STUN); + unitTarget->CastSpell(unitTarget, 1784, true); + return; + } + // Demonic Empowerment -- succubus + case 54437: + { + unitTarget->RemoveMovementImpairingAuras(true); + unitTarget->RemoveAurasByType(SPELL_AURA_MOD_STALKED); + unitTarget->RemoveAurasByType(SPELL_AURA_MOD_STUN); - // Cast Lesser Invisibility - unitTarget->CastSpell(unitTarget, 7870, true); - return; - } - // just skip - case 23770: // Sayge's Dark Fortune of * - // not exist, common cooldown can be implemented in scripts if need. - return; + // Cast Lesser Invisibility + unitTarget->CastSpell(unitTarget, 7870, true); + return; + } + // just skip + case 23770: // Sayge's Dark Fortune of * + // not exist, common cooldown can be implemented in scripts if need. + return; // Brittle Armor - (need add max stack of 24575 Brittle Armor) - case 29284: - { - // Brittle Armor - SpellInfo const* spell = sSpellMgr->GetSpellInfo(24575); - if (!spell) - return; - - for (uint32 j = 0; j < spell->StackAmount; ++j) - m_caster->CastSpell(unitTarget, spell->Id, true); - return; - } - // Mercurial Shield - (need add max stack of 26464 Mercurial Shield) - case 29286: - { - // Mercurial Shield - SpellInfo const* spell = sSpellMgr->GetSpellInfo(26464); - if (!spell) - return; + case 29284: + { + // Brittle Armor + SpellInfo const* spell = sSpellMgr->GetSpellInfo(24575); + if (!spell) + return; - for (uint32 j = 0; j < spell->StackAmount; ++j) - m_caster->CastSpell(unitTarget, spell->Id, true); - return; - } - // Cloak of Shadows - case 35729: - { - uint32 dispelMask = SpellInfo::GetDispelMask(DISPEL_ALL); - Unit::AuraApplicationMap& Auras = unitTarget->GetAppliedAuras(); - for (Unit::AuraApplicationMap::iterator iter = Auras.begin(); iter != Auras.end();) - { - // remove all harmful spells on you... - SpellInfo const* spell = iter->second->GetBase()->GetSpellInfo(); + for (uint32 j = 0; j < spell->StackAmount; ++j) + m_caster->CastSpell(unitTarget, spell->Id, true); + return; + } + // Mercurial Shield - (need add max stack of 26464 Mercurial Shield) + case 29286: + { + // Mercurial Shield + SpellInfo const* spell = sSpellMgr->GetSpellInfo(26464); + if (!spell) + return; - // Pounce Bleed shouldn't be removed by Cloak of Shadows. - if (spell->GetAllEffectsMechanicMask() & 1 << MECHANIC_BLEED) - return; + for (uint32 j = 0; j < spell->StackAmount; ++j) + m_caster->CastSpell(unitTarget, spell->Id, true); + return; + } + // Cloak of Shadows + case 35729: + { + uint32 dispelMask = SpellInfo::GetDispelMask(DISPEL_ALL); + Unit::AuraApplicationMap& Auras = unitTarget->GetAppliedAuras(); + for (Unit::AuraApplicationMap::iterator iter = Auras.begin(); iter != Auras.end();) + { + // remove all harmful spells on you... + SpellInfo const* spell = iter->second->GetBase()->GetSpellInfo(); - bool dmgClassNone = false; - if (spell->DmgClass == SPELL_DAMAGE_CLASS_NONE && spell->SpellFamilyName == SPELLFAMILY_GENERIC) - for (uint8 i = EFFECT_0; i < MAX_SPELL_EFFECTS; ++i) - { - if ((iter->second->GetEffectMask() & (1 << i)) && - spell->Effects[i].ApplyAuraName != SPELL_AURA_PERIODIC_DAMAGE && - spell->Effects[i].ApplyAuraName != SPELL_AURA_PERIODIC_TRIGGER_SPELL && - spell->Effects[i].ApplyAuraName != SPELL_AURA_DUMMY) - { - dmgClassNone = false; - break; - } - dmgClassNone = true; - } + // Pounce Bleed shouldn't be removed by Cloak of Shadows. + if (spell->GetAllEffectsMechanicMask() & 1 << MECHANIC_BLEED) + return; - if ((spell->DmgClass == SPELL_DAMAGE_CLASS_MAGIC || (spell->GetDispelMask() & dispelMask) || dmgClassNone) && - // ignore positive and passive auras - !iter->second->IsPositive() && !iter->second->GetBase()->IsPassive() && - // Xinef: Ignore NPC spells having INVULNERABILITY attribute - (!spell->HasAttribute(SPELL_ATTR0_NO_IMMUNITIES) || spell->SpellFamilyName != SPELLFAMILY_GENERIC)) + bool dmgClassNone = false; + if (spell->DmgClass == SPELL_DAMAGE_CLASS_NONE && spell->SpellFamilyName == SPELLFAMILY_GENERIC) + for (uint8 i = EFFECT_0; i < MAX_SPELL_EFFECTS; ++i) + { + if ((iter->second->GetEffectMask() & (1 << i)) && + spell->Effects[i].ApplyAuraName != SPELL_AURA_PERIODIC_DAMAGE && + spell->Effects[i].ApplyAuraName != SPELL_AURA_PERIODIC_TRIGGER_SPELL && + spell->Effects[i].ApplyAuraName != SPELL_AURA_DUMMY) { - m_caster->RemoveAura(iter); + dmgClassNone = false; + break; } - else - ++iter; + dmgClassNone = true; } - return; + + if ((spell->DmgClass == SPELL_DAMAGE_CLASS_MAGIC || (spell->GetDispelMask() & dispelMask) || dmgClassNone) && + // ignore positive and passive auras + !iter->second->IsPositive() && !iter->second->GetBase()->IsPassive() && + // Xinef: Ignore NPC spells having INVULNERABILITY attribute + (!spell->HasAttribute(SPELL_ATTR0_NO_IMMUNITIES) || spell->SpellFamilyName != SPELLFAMILY_GENERIC)) + { + m_caster->RemoveAura(iter); } + else + ++iter; + } + return; + } } } @@ -982,32 +983,32 @@ void Spell::EffectTriggerSpell(SpellEffIndex effIndex) for (uint32 i = 0; i < repeat; ++i) { m_caster->m_Events.AddEventAtOffset([caster = m_caster, targets, originalCaster = m_originalCasterGUID, spellInfo, effectIndex = effIndex, dmg = damage, sInfo = m_spellInfo]() - { - CustomSpellValues values; - // set basepoints for trigger with value effect - if (sInfo->Effects[effectIndex].Effect == SPELL_EFFECT_TRIGGER_SPELL_WITH_VALUE) { - // maybe need to set value only when basepoints == 0? - for (uint32 i = 0; i < MAX_SPELL_EFFECTS; ++i) - values.AddSpellMod(SpellValueMod(SPELLVALUE_BASE_POINT0 + i), dmg); - } + CustomSpellValues values; + // set basepoints for trigger with value effect + if (sInfo->Effects[effectIndex].Effect == SPELL_EFFECT_TRIGGER_SPELL_WITH_VALUE) + { + // maybe need to set value only when basepoints == 0? + for (uint32 i = 0; i < MAX_SPELL_EFFECTS; ++i) + values.AddSpellMod(SpellValueMod(SPELLVALUE_BASE_POINT0 + i), dmg); + } - // Remove spell cooldown (not category) if spell triggering spell with cooldown and same category - if (caster->GetTypeId() == TYPEID_PLAYER && spellInfo->CategoryRecoveryTime && sInfo->GetCategory() == spellInfo->GetCategory()) - { - caster->ToPlayer()->RemoveSpellCooldown(spellInfo->Id); - } + // Remove spell cooldown (not category) if spell triggering spell with cooldown and same category + if (caster->GetTypeId() == TYPEID_PLAYER && spellInfo->CategoryRecoveryTime && sInfo->GetCategory() == spellInfo->GetCategory()) + { + caster->ToPlayer()->RemoveSpellCooldown(spellInfo->Id); + } - // original caster guid only for GO cast - caster->CastSpell(targets, spellInfo, &values, TriggerCastFlags(TRIGGERED_FULL_MASK & ~TRIGGERED_NO_PERIODIC_RESET), nullptr, nullptr, originalCaster); - }, delay + (delay * i)); + // original caster guid only for GO cast + caster->CastSpell(targets, spellInfo, &values, TriggerCastFlags(TRIGGERED_FULL_MASK & ~TRIGGERED_NO_PERIODIC_RESET), nullptr, nullptr, originalCaster); + }, delay + (delay * i)); } } void Spell::EffectTriggerMissileSpell(SpellEffIndex effIndex) { if (effectHandleMode != SPELL_EFFECT_HANDLE_HIT_TARGET - && effectHandleMode != SPELL_EFFECT_HANDLE_HIT) + && effectHandleMode != SPELL_EFFECT_HANDLE_HIT) return; uint32 triggered_spell_id = m_spellInfo->Effects[effIndex].TriggerSpell; @@ -1081,18 +1082,18 @@ void Spell::EffectForceCast(SpellEffIndex effIndex) { switch (m_spellInfo->Id) { - case 52588: // Skeletal Gryphon Escape - case 48598: // Ride Flamebringer Cue - unitTarget->RemoveAura(damage); - break; - case 52463: // Hide In Mine Car - case 52349: // Overtake - unitTarget->CastCustomSpell(unitTarget, spellInfo->Id, &damage, nullptr, nullptr, true, nullptr, nullptr, m_originalCasterGUID); - return; - case 72378: // Blood Nova - case 73058: // Blood Nova - m_caster->CastSpell(unitTarget, damage, true); // additional spell cast - break; + case 52588: // Skeletal Gryphon Escape + case 48598: // Ride Flamebringer Cue + unitTarget->RemoveAura(damage); + break; + case 52463: // Hide In Mine Car + case 52349: // Overtake + unitTarget->CastCustomSpell(unitTarget, spellInfo->Id, &damage, nullptr, nullptr, true, nullptr, nullptr, m_originalCasterGUID); + return; + case 72378: // Blood Nova + case 73058: // Blood Nova + m_caster->CastSpell(unitTarget, damage, true); // additional spell cast + break; } } @@ -1242,15 +1243,15 @@ void Spell::EffectTeleportUnits(SpellEffIndex /*effIndex*/) // Pre effects switch (m_spellInfo->Id) { - case 70746: // Teleport Into Sunwell (for Battered Hilt) - if (Player* target = unitTarget->ToPlayer()) - { - uint32 mapid = destTarget->GetMapId(); - float x, y, z, orientation; - destTarget->GetPosition(x, y, z, orientation); - target->TeleportTo(mapid, x, y, z, orientation, TELE_TO_GM_MODE); // skip PlayerCannotEnter check - } - return; + case 70746: // Teleport Into Sunwell (for Battered Hilt) + if (Player* target = unitTarget->ToPlayer()) + { + uint32 mapid = destTarget->GetMapId(); + float x, y, z, orientation; + destTarget->GetPosition(x, y, z, orientation); + target->TeleportTo(mapid, x, y, z, orientation, TELE_TO_GM_MODE); // skip PlayerCannotEnter check + } + return; } // If not exist data for dest location - return @@ -1294,63 +1295,63 @@ void Spell::EffectTeleportUnits(SpellEffIndex /*effIndex*/) switch (m_spellInfo->Id) { // Dimensional Ripper - Everlook - case 23442: - { - int32 r = irand(0, 119); - if (r >= 70) // 7/12 success - { - if (r < 100) // 4/12 evil twin - m_caster->CastSpell(m_caster, 23445, true); - else // 1/12 fire - m_caster->CastSpell(m_caster, 23449, true); - } - return; - } - // Ultrasafe Transporter: Toshley's Station - case 36941: - { - if (roll_chance_i(50)) // 50% success - { - int32 rand_eff = urand(1, 7); - switch (rand_eff) - { - case 1: - // soul split - evil - m_caster->CastSpell(m_caster, 36900, true); - break; - case 2: - // soul split - good - m_caster->CastSpell(m_caster, 36901, true); - break; - case 3: - // Increase the size - m_caster->CastSpell(m_caster, 36895, true); - break; - case 4: - // Decrease the size - m_caster->CastSpell(m_caster, 36893, true); - break; - case 5: - // Transform - { - if (m_caster->ToPlayer()->GetTeamId() == TEAM_ALLIANCE) - m_caster->CastSpell(m_caster, 36897, true); - else - m_caster->CastSpell(m_caster, 36899, true); - break; - } - case 6: - // chicken - m_caster->CastSpell(m_caster, 36940, true); - break; - case 7: - // evil twin - m_caster->CastSpell(m_caster, 23445, true); - break; - } - } - return; + case 23442: + { + int32 r = irand(0, 119); + if (r >= 70) // 7/12 success + { + if (r < 100) // 4/12 evil twin + m_caster->CastSpell(m_caster, 23445, true); + else // 1/12 fire + m_caster->CastSpell(m_caster, 23449, true); + } + return; + } + // Ultrasafe Transporter: Toshley's Station + case 36941: + { + if (roll_chance_i(50)) // 50% success + { + int32 rand_eff = urand(1, 7); + switch (rand_eff) + { + case 1: + // soul split - evil + m_caster->CastSpell(m_caster, 36900, true); + break; + case 2: + // soul split - good + m_caster->CastSpell(m_caster, 36901, true); + break; + case 3: + // Increase the size + m_caster->CastSpell(m_caster, 36895, true); + break; + case 4: + // Decrease the size + m_caster->CastSpell(m_caster, 36893, true); + break; + case 5: + // Transform + { + if (m_caster->ToPlayer()->GetTeamId() == TEAM_ALLIANCE) + m_caster->CastSpell(m_caster, 36897, true); + else + m_caster->CastSpell(m_caster, 36899, true); + break; + } + case 6: + // chicken + m_caster->CastSpell(m_caster, 36940, true); + break; + case 7: + // evil twin + m_caster->CastSpell(m_caster, 23445, true); + break; } + } + return; + } } } @@ -1372,7 +1373,7 @@ void Spell::EffectApplyAreaAura(SpellEffIndex effIndex) if (!m_spellAura || !unitTarget) return; - ASSERT (unitTarget == m_spellAura->GetOwner()); + ASSERT(unitTarget == m_spellAura->GetOwner()); m_spellAura->_ApplyEffectForTargets(effIndex); } @@ -1430,7 +1431,7 @@ void Spell::EffectPowerDrain(SpellEffIndex effIndex) int32 gain = int32(newDamage * gainMultiplier); - m_caster->EnergizeBySpell(m_caster, m_spellInfo->Id, gain, PowerType); + m_caster->EnergizeBySpell(m_caster, m_spellInfo, gain, PowerType); } ExecuteLogEffectTakeTargetPower(effIndex, unitTarget, PowerType, newDamage, gainMultiplier); } @@ -1439,7 +1440,7 @@ void Spell::EffectSendEvent(SpellEffIndex effIndex) { // we do not handle a flag dropping or clicking on flag in battleground by sendevent system if (effectHandleMode != SPELL_EFFECT_HANDLE_HIT_TARGET - && effectHandleMode != SPELL_EFFECT_HANDLE_HIT) + && effectHandleMode != SPELL_EFFECT_HANDLE_HIT) return; WorldObject* target = nullptr; @@ -1557,7 +1558,7 @@ void Spell::EffectHeal(SpellEffIndex effIndex) for (Unit::AuraEffectList::const_iterator i = RejorRegr.begin(); i != RejorRegr.end(); ++i) { if ((*i)->GetSpellInfo()->SpellFamilyName == SPELLFAMILY_DRUID - && (*i)->GetSpellInfo()->SpellFamilyFlags[0] & 0x50) + && (*i)->GetSpellInfo()->SpellFamilyFlags[0] & 0x50) { if (m_caster->GetGUID() == (*i)->GetCasterGUID()) { @@ -1719,24 +1720,24 @@ void Spell::DoCreateItem(uint8 /*effIndex*/, uint32 itemId) bool SelfCast = true; switch (m_spellInfo->Id) { - case SPELL_AV_MARK_WINNER: - case SPELL_AV_MARK_LOSER: - case SPELL_WS_MARK_WINNER: - case SPELL_WS_MARK_LOSER: - case SPELL_WS_MARK_TIE: - case SPELL_AB_MARK_WINNER: - case SPELL_AB_MARK_LOSER: - SelfCast = true; - break; - case SPELL_WG_MARK_WINNER: - if (player->HasAura(55629 /*SPELL_LIEUTENANT*/)) - addNumber = 3; - else if (player->HasAura(33280 /*SPELL_CORPORAL*/)) - addNumber = 2; - else - addNumber = 1; - SelfCast = true; - break; + case SPELL_AV_MARK_WINNER: + case SPELL_AV_MARK_LOSER: + case SPELL_WS_MARK_WINNER: + case SPELL_WS_MARK_LOSER: + case SPELL_WS_MARK_TIE: + case SPELL_AB_MARK_WINNER: + case SPELL_AB_MARK_LOSER: + SelfCast = true; + break; + case SPELL_WG_MARK_WINNER: + if (player->HasAura(55629 /*SPELL_LIEUTENANT*/)) + addNumber = 3; + else if (player->HasAura(33280 /*SPELL_CORPORAL*/)) + addNumber = 2; + else + addNumber = 1; + SelfCast = true; + break; } if (addNumber < 1) @@ -1940,7 +1941,7 @@ void Spell::EffectEnergize(SpellEffIndex effIndex) Powers power = Powers(m_spellInfo->Effects[effIndex].MiscValue); if (unitTarget->GetTypeId() == TYPEID_PLAYER && unitTarget->getPowerType() != power && m_spellInfo->SpellFamilyName != SPELLFAMILY_POTION - && !m_spellInfo->HasAttribute(SPELL_ATTR7_ONLY_IN_SPELLBOOK_UNTIL_LEARNED)) + && !m_spellInfo->HasAttribute(SPELL_ATTR7_ONLY_IN_SPELLBOOK_UNTIL_LEARNED)) return; if (unitTarget->GetMaxPower(power) == 0) @@ -1951,31 +1952,31 @@ void Spell::EffectEnergize(SpellEffIndex effIndex) int level_diff = 0; switch (m_spellInfo->Id) { - case 9512: // Restore Energy - level_diff = m_caster->GetLevel() - 40; - level_multiplier = 2; - break; - case 24571: // Blood Fury - level_diff = m_caster->GetLevel() - 60; - level_multiplier = 10; - break; - case 24532: // Burst of Energy - level_diff = m_caster->GetLevel() - 60; - level_multiplier = 4; - break; - case 31930: // Judgements of the Wise - case 63375: // Improved Stormstrike - case 68082: // Glyph of Seal of Command - damage = int32(CalculatePct(unitTarget->GetCreateMana(), damage)); - break; - case 48542: // Revitalize - damage = int32(CalculatePct(unitTarget->GetMaxPower(power), damage)); - break; - case 71132: // Glyph of Shadow Word: Pain - damage = int32(CalculatePct(unitTarget->GetCreateMana(), 1)); // set 1 as value, missing in dbc - break; - default: - break; + case 9512: // Restore Energy + level_diff = m_caster->GetLevel() - 40; + level_multiplier = 2; + break; + case 24571: // Blood Fury + level_diff = m_caster->GetLevel() - 60; + level_multiplier = 10; + break; + case 24532: // Burst of Energy + level_diff = m_caster->GetLevel() - 60; + level_multiplier = 4; + break; + case 31930: // Judgements of the Wise + case 63375: // Improved Stormstrike + case 68082: // Glyph of Seal of Command + damage = int32(CalculatePct(unitTarget->GetCreateMana(), damage)); + break; + case 48542: // Revitalize + damage = int32(CalculatePct(unitTarget->GetMaxPower(power), damage)); + break; + case 71132: // Glyph of Shadow Word: Pain + damage = int32(CalculatePct(unitTarget->GetCreateMana(), 1)); // set 1 as value, missing in dbc + break; + default: + break; } if (level_diff > 0) @@ -1984,7 +1985,7 @@ void Spell::EffectEnergize(SpellEffIndex effIndex) if (damage < 0) return; - m_caster->EnergizeBySpell(unitTarget, m_spellInfo->Id, damage, power); + m_caster->EnergizeBySpell(unitTarget, m_spellInfo, damage, power); // Mad Alchemist's Potion if (m_spellInfo->Id == 45051) @@ -2052,7 +2053,7 @@ void Spell::EffectEnergizePct(SpellEffIndex effIndex) return; uint32 gain = CalculatePct(maxPower, damage); - m_caster->EnergizeBySpell(unitTarget, m_spellInfo->Id, gain, power); + m_caster->EnergizeBySpell(unitTarget, m_spellInfo, gain, power); } void Spell::SendLoot(ObjectGuid guid, LootType loottype) @@ -2085,35 +2086,35 @@ void Spell::SendLoot(ObjectGuid guid, LootType loottype) switch (gameObjTarget->GetGoType()) { - case GAMEOBJECT_TYPE_DOOR: - gameObjTarget->UseDoorOrButton(0, false, player); - return; - case GAMEOBJECT_TYPE_BUTTON: - gameObjTarget->UseDoorOrButton(0, false, player); + case GAMEOBJECT_TYPE_DOOR: + gameObjTarget->UseDoorOrButton(0, false, player); + return; + case GAMEOBJECT_TYPE_BUTTON: + gameObjTarget->UseDoorOrButton(0, false, player); - // Xinef: properly link possible traps - if (uint32 trapEntry = gameObjTarget->GetGOInfo()->button.linkedTrap) - gameObjTarget->TriggeringLinkedGameObject(trapEntry, player); - return; - case GAMEOBJECT_TYPE_QUESTGIVER: - player->PrepareGossipMenu(gameObjTarget, gameObjTarget->GetGOInfo()->questgiver.gossipID, true); - player->SendPreparedGossip(gameObjTarget); - return; + // Xinef: properly link possible traps + if (uint32 trapEntry = gameObjTarget->GetGOInfo()->button.linkedTrap) + gameObjTarget->TriggeringLinkedGameObject(trapEntry, player); + return; + case GAMEOBJECT_TYPE_QUESTGIVER: + player->PrepareGossipMenu(gameObjTarget, gameObjTarget->GetGOInfo()->questgiver.gossipID, true); + player->SendPreparedGossip(gameObjTarget); + return; - case GAMEOBJECT_TYPE_SPELL_FOCUS: - // triggering linked GO - if (uint32 trapEntry = gameObjTarget->GetGOInfo()->spellFocus.linkedTrapId) - gameObjTarget->TriggeringLinkedGameObject(trapEntry, m_caster); - return; + case GAMEOBJECT_TYPE_SPELL_FOCUS: + // triggering linked GO + if (uint32 trapEntry = gameObjTarget->GetGOInfo()->spellFocus.linkedTrapId) + gameObjTarget->TriggeringLinkedGameObject(trapEntry, m_caster); + return; - case GAMEOBJECT_TYPE_CHEST: - // triggering linked GO - if (uint32 trapEntry = gameObjTarget->GetGOInfo()->chest.linkedTrapId) - gameObjTarget->TriggeringLinkedGameObject(trapEntry, m_caster); + case GAMEOBJECT_TYPE_CHEST: + // triggering linked GO + if (uint32 trapEntry = gameObjTarget->GetGOInfo()->chest.linkedTrapId) + gameObjTarget->TriggeringLinkedGameObject(trapEntry, m_caster); // Don't return, let loots been taken - default: - break; + default: + break; } } @@ -2144,7 +2145,7 @@ void Spell::EffectOpenLock(SpellEffIndex effIndex) goInfo = gameObjTarget->GetGOInfo(); // Arathi Basin banner opening. /// @todo: Verify correctness of this check if ((goInfo->type == GAMEOBJECT_TYPE_BUTTON && goInfo->button.noDamageImmune) || - (goInfo->type == GAMEOBJECT_TYPE_GOOBER && goInfo->goober.losOK)) + (goInfo->type == GAMEOBJECT_TYPE_GOOBER && goInfo->goober.losOK)) { //CanUseBattlegroundObject() already called in CheckCast() // in battleground check @@ -2413,170 +2414,170 @@ void Spell::EffectSummonType(SpellEffIndex effIndex) // so here's a list of MiscValueB values, which is currently most generic check switch (properties->Id) { - case 64: - case 61: - case 1101: - case 66: - case 648: - case 2301: - case 1061: - case 1261: - case 629: - case 181: - case 715: - case 1562: - case 833: - case 1161: - case 713: // xinef, bloodworms - numSummons = (damage > 0) ? damage : 1; - break; - default: - numSummons = 1; - break; + case 64: + case 61: + case 1101: + case 66: + case 648: + case 2301: + case 1061: + case 1261: + case 629: + case 181: + case 715: + case 1562: + case 833: + case 1161: + case 713: // xinef, bloodworms + numSummons = (damage > 0) ? damage : 1; + break; + default: + numSummons = 1; + break; } switch (properties->Category) { - case SUMMON_CATEGORY_WILD: - case SUMMON_CATEGORY_ALLY: - case SUMMON_CATEGORY_UNK: - if (properties->Flags & 512) - { - SummonGuardian(effIndex, entry, properties, numSummons, personalSpawn); - break; - } - switch (properties->Type) - { - case SUMMON_TYPE_PET: - case SUMMON_TYPE_GUARDIAN: - case SUMMON_TYPE_GUARDIAN2: - case SUMMON_TYPE_MINION: - SummonGuardian(effIndex, entry, properties, numSummons, personalSpawn); - break; - // Summons a vehicle, but doesn't force anyone to enter it (see SUMMON_CATEGORY_VEHICLE) - case SUMMON_TYPE_VEHICLE: - case SUMMON_TYPE_VEHICLE2: - summon = m_caster->GetMap()->SummonCreature(entry, *destTarget, properties, duration, m_originalCaster, m_spellInfo->Id, 0, personalSpawn); - break; - case SUMMON_TYPE_LIGHTWELL: - case SUMMON_TYPE_TOTEM: - { - // protection code - summon = m_caster->GetMap()->SummonCreature(entry, *destTarget, properties, duration, m_originalCaster, m_spellInfo->Id, 0, personalSpawn); - if (!summon || !summon->IsTotem()) - return; + case SUMMON_CATEGORY_WILD: + case SUMMON_CATEGORY_ALLY: + case SUMMON_CATEGORY_UNK: + if (properties->Flags & 512) + { + SummonGuardian(effIndex, entry, properties, numSummons, personalSpawn); + break; + } + switch (properties->Type) + { + case SUMMON_TYPE_PET: + case SUMMON_TYPE_GUARDIAN: + case SUMMON_TYPE_GUARDIAN2: + case SUMMON_TYPE_MINION: + SummonGuardian(effIndex, entry, properties, numSummons, personalSpawn); + break; + // Summons a vehicle, but doesn't force anyone to enter it (see SUMMON_CATEGORY_VEHICLE) + case SUMMON_TYPE_VEHICLE: + case SUMMON_TYPE_VEHICLE2: + summon = m_caster->GetMap()->SummonCreature(entry, *destTarget, properties, duration, m_originalCaster, m_spellInfo->Id, 0, personalSpawn); + break; + case SUMMON_TYPE_LIGHTWELL: + case SUMMON_TYPE_TOTEM: + { + // protection code + summon = m_caster->GetMap()->SummonCreature(entry, *destTarget, properties, duration, m_originalCaster, m_spellInfo->Id, 0, personalSpawn); + if (!summon || !summon->IsTotem()) + return; - // Mana Tide Totem - if (m_spellInfo->Id == 16190) - damage = m_caster->CountPctFromMaxHealth(10); + // Mana Tide Totem + if (m_spellInfo->Id == 16190) + damage = m_caster->CountPctFromMaxHealth(10); - if (damage && properties->Type != SUMMON_TYPE_LIGHTWELL) // Health set in script for lightwell - { - summon->SetMaxHealth(damage); - summon->SetHealth(damage); - } - break; - } - case SUMMON_TYPE_JEEVES: - case SUMMON_TYPE_MINIPET: - { - summon = m_caster->GetMap()->SummonCreature(entry, *destTarget, properties, duration, m_originalCaster, m_spellInfo->Id, 0, personalSpawn); - if (!summon || !summon->HasUnitTypeMask(UNIT_MASK_MINION)) - return; + if (damage && properties->Type != SUMMON_TYPE_LIGHTWELL) // Health set in script for lightwell + { + summon->SetMaxHealth(damage); + summon->SetHealth(damage); + } + break; + } + case SUMMON_TYPE_JEEVES: + case SUMMON_TYPE_MINIPET: + { + summon = m_caster->GetMap()->SummonCreature(entry, *destTarget, properties, duration, m_originalCaster, m_spellInfo->Id, 0, personalSpawn); + if (!summon || !summon->HasUnitTypeMask(UNIT_MASK_MINION)) + return; - summon->SelectLevel(); // some summoned creaters have different from 1 DB data for level/hp - summon->ReplaceAllNpcFlags(NPCFlags(summon->GetCreatureTemplate()->npcflag)); + summon->SelectLevel(); // some summoned creaters have different from 1 DB data for level/hp + summon->ReplaceAllNpcFlags(NPCFlags(summon->GetCreatureTemplate()->npcflag)); - summon->SetImmuneToAll(true); - summon->SetReactState(REACT_PASSIVE); + summon->SetImmuneToAll(true); + summon->SetReactState(REACT_PASSIVE); - // Xinef: Pet can have some auras in creature_addon or in scripts, do not remove them instantly - //summon->AI()->EnterEvadeMode(); - if (properties->Type != SUMMON_TYPE_JEEVES) - { - summon->GetMotionMaster()->Clear(false); - summon->GetMotionMaster()->MoveFollow(m_originalCaster, PET_FOLLOW_DIST, summon->GetFollowAngle(), MOTION_SLOT_ACTIVE); - } - break; - } - default: - { - float radius = m_spellInfo->Effects[effIndex].CalcRadius(); + // Xinef: Pet can have some auras in creature_addon or in scripts, do not remove them instantly + //summon->AI()->EnterEvadeMode(); + if (properties->Type != SUMMON_TYPE_JEEVES) + { + summon->GetMotionMaster()->Clear(false); + summon->GetMotionMaster()->MoveFollow(m_originalCaster, PET_FOLLOW_DIST, summon->GetFollowAngle(), MOTION_SLOT_ACTIVE); + } + break; + } + default: + { + float radius = m_spellInfo->Effects[effIndex].CalcRadius(); - TempSummonType summonType = (duration <= 0) ? TEMPSUMMON_DEAD_DESPAWN : TEMPSUMMON_TIMED_DESPAWN; + TempSummonType summonType = (duration <= 0) ? TEMPSUMMON_DEAD_DESPAWN : TEMPSUMMON_TIMED_DESPAWN; - for (uint32 count = 0; count < numSummons; ++count) - { - Position pos; - if (count == 0) - pos = *destTarget; - else - // randomize position for multiple summons - pos = m_caster->GetRandomPoint(*destTarget, radius); - - summon = m_originalCaster->SummonCreature(entry, pos, summonType, duration, 0, nullptr, personalSpawn); - if (!summon) - continue; + for (uint32 count = 0; count < numSummons; ++count) + { + Position pos; + if (count == 0) + pos = *destTarget; + else + // randomize position for multiple summons + pos = m_caster->GetRandomPoint(*destTarget, radius); - summon->SetTempSummonType(summonType); + summon = m_originalCaster->SummonCreature(entry, pos, summonType, duration, 0, nullptr, personalSpawn); + if (!summon) + continue; - if (properties->Category == SUMMON_CATEGORY_ALLY) - { - summon->SetOwnerGUID(m_originalCaster->GetGUID()); - summon->SetFaction(m_originalCaster->GetFaction()); - } + summon->SetTempSummonType(summonType); - ExecuteLogEffectSummonObject(effIndex, summon); - } - return; - } - }//switch - break; - case SUMMON_CATEGORY_PET: - // Xinef: SummonGuardian function can summon a few npcs of same type, remove old summons with same entry here - if (m_originalCaster) - m_originalCaster->RemoveAllMinionsByEntry(entry); - SummonGuardian(effIndex, entry, properties, numSummons, personalSpawn); - break; - case SUMMON_CATEGORY_PUPPET: - summon = m_caster->GetMap()->SummonCreature(entry, *destTarget, properties, duration, m_originalCaster, m_spellInfo->Id, 0, personalSpawn); - break; - case SUMMON_CATEGORY_VEHICLE: - // Summoning spells (usually triggered by npc_spellclick) that spawn a vehicle and that cause the clicker - // to cast a ride vehicle spell on the summoned unit. - //float x, y, z; - //m_caster->GetClosePoint(x, y, z, DEFAULT_WORLD_OBJECT_SIZE); - // xinef: vehicles summoned in air, eg. Cold Hearted quest - if (std::fabs(m_caster->GetPositionZ() - destTarget->GetPositionZ()) > 6.0f) - destTarget->m_positionZ = m_caster->GetPositionZ(); - - summon = m_originalCaster->GetMap()->SummonCreature(entry, *destTarget, properties, duration, m_caster, m_spellInfo->Id, 0, personalSpawn); - if (!summon || !summon->IsVehicle()) - return; + if (properties->Category == SUMMON_CATEGORY_ALLY) + { + summon->SetOwnerGUID(m_originalCaster->GetGUID()); + summon->SetFaction(m_originalCaster->GetFaction()); + } - // The spell that this effect will trigger. It has SPELL_AURA_CONTROL_VEHICLE - uint32 spellId = VEHICLE_SPELL_RIDE_HARDCODED; - int32 basePoints = m_spellInfo->Effects[effIndex].CalcValue(); - if (basePoints > 1) // xinef: some summoning spells have value 1 - indicates vehicle seat - { - SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(basePoints); - if (spellInfo && spellInfo->HasAura(SPELL_AURA_CONTROL_VEHICLE)) - spellId = spellInfo->Id; + ExecuteLogEffectSummonObject(effIndex, summon); } + return; + } + }//switch + break; + case SUMMON_CATEGORY_PET: + // Xinef: SummonGuardian function can summon a few npcs of same type, remove old summons with same entry here + if (m_originalCaster) + m_originalCaster->RemoveAllMinionsByEntry(entry); + SummonGuardian(effIndex, entry, properties, numSummons, personalSpawn); + break; + case SUMMON_CATEGORY_PUPPET: + summon = m_caster->GetMap()->SummonCreature(entry, *destTarget, properties, duration, m_originalCaster, m_spellInfo->Id, 0, personalSpawn); + break; + case SUMMON_CATEGORY_VEHICLE: + // Summoning spells (usually triggered by npc_spellclick) that spawn a vehicle and that cause the clicker + // to cast a ride vehicle spell on the summoned unit. + //float x, y, z; + //m_caster->GetClosePoint(x, y, z, DEFAULT_WORLD_OBJECT_SIZE); + // xinef: vehicles summoned in air, eg. Cold Hearted quest + if (std::fabs(m_caster->GetPositionZ() - destTarget->GetPositionZ()) > 6.0f) + destTarget->m_positionZ = m_caster->GetPositionZ(); + + summon = m_originalCaster->GetMap()->SummonCreature(entry, *destTarget, properties, duration, m_caster, m_spellInfo->Id, 0, personalSpawn); + if (!summon || !summon->IsVehicle()) + return; - // xinef: if we have small value, it indicates seat position - if (basePoints > 0 && basePoints < MAX_VEHICLE_SEATS) - m_originalCaster->CastCustomSpell(spellId, SPELLVALUE_BASE_POINT0, basePoints, summon, true); - else - m_originalCaster->CastSpell(summon, spellId, true); + // The spell that this effect will trigger. It has SPELL_AURA_CONTROL_VEHICLE + uint32 spellId = VEHICLE_SPELL_RIDE_HARDCODED; + int32 basePoints = m_spellInfo->Effects[effIndex].CalcValue(); + if (basePoints > 1) // xinef: some summoning spells have value 1 - indicates vehicle seat + { + SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(basePoints); + if (spellInfo && spellInfo->HasAura(SPELL_AURA_CONTROL_VEHICLE)) + spellId = spellInfo->Id; + } + + // xinef: if we have small value, it indicates seat position + if (basePoints > 0 && basePoints < MAX_VEHICLE_SEATS) + m_originalCaster->CastCustomSpell(spellId, SPELLVALUE_BASE_POINT0, basePoints, summon, true); + else + m_originalCaster->CastSpell(summon, spellId, true); - // xinef: i think this is wrong, found only 2 vehicles with faction override and one of them should inherit caster faction... - //uint32 faction = properties->Faction; - //if (!faction) - uint32 faction = m_originalCaster->GetFaction(); + // xinef: i think this is wrong, found only 2 vehicles with faction override and one of them should inherit caster faction... + //uint32 faction = properties->Faction; + //if (!faction) + uint32 faction = m_originalCaster->GetFaction(); - summon->SetFaction(faction); - break; + summon->SetFaction(faction); + break; } if (summon) @@ -2621,7 +2622,7 @@ void Spell::EffectDispel(SpellEffIndex effIndex) // Create dispel mask by dispel type uint32 dispel_type = m_spellInfo->Effects[effIndex].MiscValue; - uint32 dispelMask = SpellInfo::GetDispelMask(DispelType(dispel_type)); + uint32 dispelMask = SpellInfo::GetDispelMask(DispelType(dispel_type)); DispelChargesList dispel_list; unitTarget->GetDispellableAuraList(m_caster, dispelMask, dispel_list, m_spellInfo); @@ -2684,10 +2685,6 @@ void Spell::EffectDispel(SpellEffIndex effIndex) if (failCount) m_caster->SendMessageToSet(&dataFail, true); - // put in combat - if (unitTarget->IsFriendlyTo(m_caster)) - unitTarget->getHostileRefMgr().threatAssist(m_caster, 0.0f, m_spellInfo); - if (success_list.empty()) return; @@ -2725,7 +2722,7 @@ void Spell::EffectDispel(SpellEffIndex effIndex) if (Unit* owner = m_caster->GetOwner()) if (owner->GetAura(1600017)) owner->CastSpell(owner, 1600018, TRIGGERED_FULL_MASK); - } + } } void Spell::EffectDualWield(SpellEffIndex /*effIndex*/) @@ -2939,7 +2936,7 @@ void Spell::EffectEnchantItemPerm(SpellEffIndex effIndex) item_owner->RemoveTradeableItem(itemTarget); itemTarget->ClearSoulboundTradeable(item_owner); - + Transmogrification::instance().AddToCollection(item_owner, itemTarget); } } @@ -2976,7 +2973,7 @@ void Spell::EffectEnchantItemPrismatic(SpellEffIndex effIndex) if (!add_socket) { LOG_ERROR("spells.effect", "Spell::EffectEnchantItemPrismatic: attempt apply enchant spell {} with SPELL_EFFECT_ENCHANT_ITEM_PRISMATIC ({}) but without ITEM_ENCHANTMENT_TYPE_PRISMATIC_SOCKET ({}), not suppoted yet.", - m_spellInfo->Id, SPELL_EFFECT_ENCHANT_ITEM_PRISMATIC, ITEM_ENCHANTMENT_TYPE_PRISMATIC_SOCKET); + m_spellInfo->Id, SPELL_EFFECT_ENCHANT_ITEM_PRISMATIC, ITEM_ENCHANTMENT_TYPE_PRISMATIC_SOCKET); return; } } @@ -3021,36 +3018,36 @@ void Spell::EffectEnchantItemTmp(SpellEffIndex effIndex) switch (damage) { // Rank 1 - case 2: - spell_id = 36744; - break; // 0% [ 7% == 2, 14% == 2, 20% == 2] + case 2: + spell_id = 36744; + break; // 0% [ 7% == 2, 14% == 2, 20% == 2] // Rank 2 - case 4: - spell_id = 36753; - break; // 0% [ 7% == 4, 14% == 4] - case 5: - spell_id = 36751; - break; // 20% + case 4: + spell_id = 36753; + break; // 0% [ 7% == 4, 14% == 4] + case 5: + spell_id = 36751; + break; // 20% // Rank 3 - case 6: - spell_id = 36754; - break; // 0% [ 7% == 6, 14% == 6] - case 7: - spell_id = 36755; - break; // 20% + case 6: + spell_id = 36754; + break; // 0% [ 7% == 6, 14% == 6] + case 7: + spell_id = 36755; + break; // 20% // Rank 4 - case 9: - spell_id = 36761; - break; // 0% [ 7% == 6] - case 10: - spell_id = 36758; - break; // 14% - case 11: - spell_id = 36760; - break; // 20% - default: - LOG_ERROR("spells.effect", "Spell::EffectEnchantItemTmp: Damage {} not handled in S'RW", damage); - return; + case 9: + spell_id = 36761; + break; // 0% [ 7% == 6] + case 10: + spell_id = 36758; + break; // 14% + case 11: + spell_id = 36760; + break; // 20% + default: + LOG_ERROR("spells.effect", "Spell::EffectEnchantItemTmp: Damage {} not handled in S'RW", damage); + return; } SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(spell_id); @@ -3206,7 +3203,7 @@ void Spell::EffectSummonPet(SpellEffIndex effIndex) uint32 petentry = m_spellInfo->Effects[effIndex].MiscValue; int32 duration = m_spellInfo->GetDuration(); - if(Player* modOwner = m_originalCaster->GetSpellModOwner()) + if (Player* modOwner = m_originalCaster->GetSpellModOwner()) modOwner->ApplySpellMod(m_spellInfo, SPELLMOD_DURATION, duration); Player* owner = m_originalCaster->ToPlayer(); @@ -3343,33 +3340,27 @@ void Spell::EffectTaunt(SpellEffIndex /*effIndex*/) if (!unitTarget) return; - // xinef: Hand of Reckoning, cast before checing canhavethreatlist. fixes damage against pets - if (m_spellInfo->Id == 62124 && unitTarget->GetVictim() != m_caster) - { - m_caster->CastSpell(unitTarget, 67485, true); - unitTarget->CombatStart(m_caster); - } - // this effect use before aura Taunt apply for prevent taunt already attacking target // for spell as marked "non effective at already attacking target" - if (!unitTarget->CanHaveThreatList() || (unitTarget->GetVictim() == m_caster && !m_spellInfo->HasAura(SPELL_AURA_MOD_TAUNT))) + if (!unitTarget->CanHaveThreatList() && !m_spellInfo->HasAura(SPELL_AURA_MOD_TAUNT)) { SendCastResult(SPELL_FAILED_DONT_REPORT); return; } - if (!unitTarget->GetThreatMgr().GetOnlineContainer().empty()) + if (unitTarget->GetThreatManager().GetCurrentVictim() == m_caster) { - // Also use this effect to set the taunter's threat to the taunted creature's highest value - float myThreat = unitTarget->GetThreatMgr().GetThreat(m_caster); - float topThreat = unitTarget->GetThreatMgr().GetOnlineContainer().getMostHated()->GetThreat(); - if (topThreat > myThreat) - unitTarget->GetThreatMgr().DoAddThreat(m_caster, topThreat - myThreat); - - //Set aggro victim to caster - if (HostileReference* forcedVictim = unitTarget->GetThreatMgr().GetOnlineContainer().getReferenceByTarget(m_caster)) - unitTarget->GetThreatMgr().setCurrentVictim(forcedVictim); + SendCastResult(SPELL_FAILED_DONT_REPORT); + return; } + + // Hand of Reckoning + if (m_spellInfo->Id == 62124) + m_caster->CastSpell(unitTarget, 67485, true); + + if (!unitTarget->GetThreatManager().IsThreatListEmpty()) + // Set threat equal to highest threat currently on target + unitTarget->GetThreatManager().MatchUnitThreatToHighestThreat(m_caster); } void Spell::EffectWeaponDmg(SpellEffIndex effIndex) @@ -3387,229 +3378,229 @@ void Spell::EffectWeaponDmg(SpellEffIndex effIndex) { switch (m_spellInfo->Effects[j].Effect) { - case SPELL_EFFECT_WEAPON_DAMAGE: - case SPELL_EFFECT_WEAPON_DAMAGE_NOSCHOOL: - case SPELL_EFFECT_NORMALIZED_WEAPON_DMG: - case SPELL_EFFECT_WEAPON_PERCENT_DAMAGE: - return; // we must calculate only at last weapon effect - break; + case SPELL_EFFECT_WEAPON_DAMAGE: + case SPELL_EFFECT_WEAPON_DAMAGE_NOSCHOOL: + case SPELL_EFFECT_NORMALIZED_WEAPON_DMG: + case SPELL_EFFECT_WEAPON_PERCENT_DAMAGE: + return; // we must calculate only at last weapon effect + break; } } // some spell specific modifiers - float totalDamagePercentMod = m_spellInfo->GetEffect(effIndex).DamageMultiplier * 100; // applied to final bonus+weapon damage + float totalDamagePercentMod = m_spellInfo->GetEffect(effIndex).DamageMultiplier * 100; // applied to final bonus+weapon damage int32 spell_bonus = 0; // bonus specific for spell switch (m_spellInfo->SpellFamilyName) { - case SPELLFAMILY_GENERIC: + case SPELLFAMILY_GENERIC: + { + switch (m_spellInfo->Id) + { + // Trial of the Champion, Black Knight, Obliterate + case 67725: + case 67883: + { + AddPct(totalDamagePercentMod, unitTarget->GetDiseasesByCaster(m_caster->GetGUID(), 1) * 30.0f); + break; + } + } + break; + } + case SPELLFAMILY_WARRIOR: + { + // Devastate (player ones) + if (m_spellInfo->SpellFamilyFlags[1] & 0x40) + { + m_caster->CastSpell(unitTarget, 58567, true); + + if (Aura* aur = unitTarget->GetAura(58567)) { - switch (m_spellInfo->Id) - { - // Trial of the Champion, Black Knight, Obliterate - case 67725: - case 67883: - { - AddPct(totalDamagePercentMod, unitTarget->GetDiseasesByCaster(m_caster->GetGUID(), 1) * 30.0f); - break; - } - } - break; + // 58388 - Glyph of Devastate dummy aura. + if (m_caster->HasAura(58388)) + aur->ModStackAmount(1); + + spell_bonus += (aur->GetStackAmount() - 1) * CalculateSpellDamage(2, unitTarget); } - case SPELLFAMILY_WARRIOR: + } + break; + } + case SPELLFAMILY_ROGUE: + { + // Fan of Knives, Hemorrhage, Ghostly Strike + if ((m_spellInfo->SpellFamilyFlags[1] & 0x40000) + || (m_spellInfo->SpellFamilyFlags[0] & 0x6000000)) + { + // Hemorrhage + if (m_spellInfo->SpellFamilyFlags[0] & 0x2000000) { - // Devastate (player ones) - if (m_spellInfo->SpellFamilyFlags[1] & 0x40) - { - m_caster->CastSpell(unitTarget, 58567, true); - - if (Aura* aur = unitTarget->GetAura(58567)) - { - // 58388 - Glyph of Devastate dummy aura. - if (m_caster->HasAura(58388)) - aur->ModStackAmount(1); - - spell_bonus += (aur->GetStackAmount() - 1) * CalculateSpellDamage(2, unitTarget); - } - } - break; + AddComboPointGain(unitTarget, 1); } - case SPELLFAMILY_ROGUE: + // 50% more damage with daggers + if (m_caster->GetTypeId() == TYPEID_PLAYER) + if (Item* item = m_caster->ToPlayer()->GetWeaponForAttack(m_attackType, true)) + if (item->GetTemplate()->SubClass == ITEM_SUBCLASS_WEAPON_DAGGER) + AddPct(totalDamagePercentMod, 50.0f); + } + // Mutilate (for each hand) + else if (m_spellInfo->SpellFamilyFlags[1] & 0x6) + { + bool found = false; + // fast check + if (unitTarget->HasAuraState(AURA_STATE_DEADLY_POISON, m_spellInfo, m_caster)) + found = true; + // full aura scan + else { - // Fan of Knives, Hemorrhage, Ghostly Strike - if ((m_spellInfo->SpellFamilyFlags[1] & 0x40000) - || (m_spellInfo->SpellFamilyFlags[0] & 0x6000000)) + Unit::AuraApplicationMap const& auras = unitTarget->GetAppliedAuras(); + for (Unit::AuraApplicationMap::const_iterator itr = auras.begin(); itr != auras.end(); ++itr) { - // Hemorrhage - if (m_spellInfo->SpellFamilyFlags[0] & 0x2000000) + if (itr->second->GetBase()->GetSpellInfo()->Dispel == DISPEL_POISON) { - AddComboPointGain(unitTarget, 1); - } - // 50% more damage with daggers - if (m_caster->GetTypeId() == TYPEID_PLAYER) - if (Item* item = m_caster->ToPlayer()->GetWeaponForAttack(m_attackType, true)) - if (item->GetTemplate()->SubClass == ITEM_SUBCLASS_WEAPON_DAGGER) - AddPct(totalDamagePercentMod, 50.0f); - } - // Mutilate (for each hand) - else if (m_spellInfo->SpellFamilyFlags[1] & 0x6) - { - bool found = false; - // fast check - if (unitTarget->HasAuraState(AURA_STATE_DEADLY_POISON, m_spellInfo, m_caster)) found = true; - // full aura scan - else - { - Unit::AuraApplicationMap const& auras = unitTarget->GetAppliedAuras(); - for (Unit::AuraApplicationMap::const_iterator itr = auras.begin(); itr != auras.end(); ++itr) - { - if (itr->second->GetBase()->GetSpellInfo()->Dispel == DISPEL_POISON) - { - found = true; - break; - } - } - } - - if (found) - AddPct(totalDamagePercentMod, 20.0f); // 120% if poisoned - } - break; - } - case SPELLFAMILY_PALADIN: - { - switch (m_spellInfo->Id) - { - case 20467: // Seal of Command Unleashed - spell_bonus += int32(0.08f * m_caster->GetTotalAttackPowerValue(BASE_ATTACK)); - spell_bonus += int32(0.13f * m_caster->SpellBaseDamageBonusDone(m_spellInfo->GetSchoolMask())); - break; - case 42463: // Seals of the Pure for Seal of Vengeance/Corruption - case 53739: - if (AuraEffect const* sealsOfPure = m_caster->GetAuraEffect(SPELL_AURA_ADD_PCT_MODIFIER, SPELLFAMILY_PALADIN, 25, 0)) - AddPct(totalDamagePercentMod, sealsOfPure->GetAmount()); break; - default: - break; - } - break; - } - case SPELLFAMILY_SHAMAN: - { - // Skyshatter Harness item set bonus - // Stormstrike - if (AuraEffect* aurEff = m_caster->IsScriptOverriden(m_spellInfo, 5634)) - m_caster->CastSpell(m_caster, 38430, true, nullptr, aurEff); - // Lava lash damage increased by Flametongue weapon - if (m_caster->GetAuraEffect(SPELL_AURA_DUMMY, SPELLFAMILY_SHAMAN, 688, EFFECT_0)) - AddPct(totalDamagePercentMod, 25.0f); - break; - } - case SPELLFAMILY_DRUID: - { - // Mangle (Cat): CP - if (m_spellInfo->SpellFamilyFlags[1] & 0x400) - { - AddComboPointGain(unitTarget, 1); - } - // Shred, Maul - Rend and Tear - else if (m_spellInfo->SpellFamilyFlags[0] & 0x00008800 && unitTarget->HasAuraState(AURA_STATE_BLEEDING)) - { - if (AuraEffect const* rendAndTear = m_caster->GetDummyAuraEffect(SPELLFAMILY_DRUID, 2859, 0)) - AddPct(totalDamagePercentMod, rendAndTear->GetAmount()); - } - break; - } - case SPELLFAMILY_HUNTER: - { - // Kill Shot - if( m_spellInfo->SpellFamilyFlags[1] & 0x800000 ) - { - spell_bonus += int32(m_caster->GetTotalAttackPowerValue(RANGED_ATTACK) * 0.4f); + } } - break; } - case SPELLFAMILY_DEATHKNIGHT: - { - // Plague Strike - if (m_spellInfo->SpellFamilyFlags[0] & 0x1) - { - // Glyph of Plague Strike - if (AuraEffect const* aurEff = m_caster->GetAuraEffect(58657, EFFECT_0)) - AddPct(totalDamagePercentMod, aurEff->GetAmount()); - break; - } - // Blood Strike - if (m_spellInfo->SpellFamilyFlags[0] & 0x400000) - { - float disease_amt = m_spellInfo->Effects[EFFECT_2].CalcValue(); - //Death Knight T8 Melee 4P Bonus - if (AuraEffect* aurEff = m_caster->GetAuraEffectDummy(64736) ) - AddPct(disease_amt, aurEff->GetAmount()); - AddPct(totalDamagePercentMod, disease_amt * unitTarget->GetDiseasesByCaster(m_caster->GetGUID()) / 2.0f); + if (found) + AddPct(totalDamagePercentMod, 20.0f); // 120% if poisoned + } + break; + } + case SPELLFAMILY_PALADIN: + { + switch (m_spellInfo->Id) + { + case 20467: // Seal of Command Unleashed + spell_bonus += int32(0.08f * m_caster->GetTotalAttackPowerValue(BASE_ATTACK)); + spell_bonus += int32(0.13f * m_caster->SpellBaseDamageBonusDone(m_spellInfo->GetSchoolMask())); + break; + case 42463: // Seals of the Pure for Seal of Vengeance/Corruption + case 53739: + if (AuraEffect const* sealsOfPure = m_caster->GetAuraEffect(SPELL_AURA_ADD_PCT_MODIFIER, SPELLFAMILY_PALADIN, 25, 0)) + AddPct(totalDamagePercentMod, sealsOfPure->GetAmount()); + break; + default: + break; + } + break; + } + case SPELLFAMILY_SHAMAN: + { + // Skyshatter Harness item set bonus + // Stormstrike + if (AuraEffect* aurEff = m_caster->IsScriptOverriden(m_spellInfo, 5634)) + m_caster->CastSpell(m_caster, 38430, true, nullptr, aurEff); + // Lava lash damage increased by Flametongue weapon + if (m_caster->GetAuraEffect(SPELL_AURA_DUMMY, SPELLFAMILY_SHAMAN, 688, EFFECT_0)) + AddPct(totalDamagePercentMod, 25.0f); + break; + } + case SPELLFAMILY_DRUID: + { + // Mangle (Cat): CP + if (m_spellInfo->SpellFamilyFlags[1] & 0x400) + { + AddComboPointGain(unitTarget, 1); + } + // Shred, Maul - Rend and Tear + else if (m_spellInfo->SpellFamilyFlags[0] & 0x00008800 && unitTarget->HasAuraState(AURA_STATE_BLEEDING)) + { + if (AuraEffect const* rendAndTear = m_caster->GetDummyAuraEffect(SPELLFAMILY_DRUID, 2859, 0)) + AddPct(totalDamagePercentMod, rendAndTear->GetAmount()); + } + break; + } + case SPELLFAMILY_HUNTER: + { + // Kill Shot + if (m_spellInfo->SpellFamilyFlags[1] & 0x800000) + { + spell_bonus += int32(m_caster->GetTotalAttackPowerValue(RANGED_ATTACK) * 0.4f); + } + break; + } + case SPELLFAMILY_DEATHKNIGHT: + { + // Plague Strike + if (m_spellInfo->SpellFamilyFlags[0] & 0x1) + { + // Glyph of Plague Strike + if (AuraEffect const* aurEff = m_caster->GetAuraEffect(58657, EFFECT_0)) + AddPct(totalDamagePercentMod, aurEff->GetAmount()); + break; + } + // Blood Strike + if (m_spellInfo->SpellFamilyFlags[0] & 0x400000) + { + float disease_amt = m_spellInfo->Effects[EFFECT_2].CalcValue(); + //Death Knight T8 Melee 4P Bonus + if (AuraEffect* aurEff = m_caster->GetAuraEffectDummy(64736)) + AddPct(disease_amt, aurEff->GetAmount()); + + AddPct(totalDamagePercentMod, disease_amt * unitTarget->GetDiseasesByCaster(m_caster->GetGUID()) / 2.0f); - // Glyph of Blood Strike - if (m_caster->GetAuraEffect(59332, EFFECT_0)) - if (unitTarget->HasAuraType(SPELL_AURA_MOD_DECREASE_SPEED)) - AddPct(totalDamagePercentMod, 20.0f); - break; - } - // Death Strike - if (m_spellInfo->SpellFamilyFlags[0] & 0x10) - { - // Glyph of Death Strike - if (AuraEffect const* aurEff = m_caster->GetAuraEffect(59336, EFFECT_0)) - if (uint32 runic = std::min(m_caster->GetPower(POWER_RUNIC_POWER), aurEff->GetSpellInfo()->Effects[EFFECT_1].CalcValue())) - AddPct(totalDamagePercentMod, runic); - break; - } - // Obliterate (12.5% more damage per disease) - if (m_spellInfo->SpellFamilyFlags[1] & 0x20000) - { - bool consumeDiseases = true; - // Annihilation - if (AuraEffect const* aurEff = m_caster->GetDummyAuraEffect(SPELLFAMILY_DEATHKNIGHT, 2710, EFFECT_0)) - // Do not consume diseases if roll sucesses - if (roll_chance_i(aurEff->GetAmount())) - consumeDiseases = false; - - float disease_amt = m_spellInfo->Effects[EFFECT_2].CalcValue(); - //Death Knight T8 Melee 4P Bonus - if (AuraEffect* aurEff = m_caster->GetAuraEffectDummy(64736) ) - AddPct(disease_amt, aurEff->GetAmount()); - - AddPct(totalDamagePercentMod, disease_amt * unitTarget->GetDiseasesByCaster(m_caster->GetGUID(), consumeDiseases) / 2.0f); - break; - } - // Blood-Caked Strike - Blood-Caked Blade - if (m_spellInfo->SpellIconID == 1736) - { - int32 weaponDamage = m_caster->CalculateDamage(m_attackType, false, true); - ApplyPct(weaponDamage, std::min(uint32(3), unitTarget->GetDiseasesByCaster(m_caster->GetGUID())) * 12.5f); - spell_bonus = weaponDamage; - break; - } - // Heart Strike - if (m_spellInfo->SpellFamilyFlags[0] & 0x1000000) - { - float disease_amt = m_spellInfo->Effects[EFFECT_2].CalcValue(); - //Death Knight T8 Melee 4P Bonus - if (AuraEffect* aurEff = m_caster->GetAuraEffectDummy(64736) ) - AddPct(disease_amt, aurEff->GetAmount()); + // Glyph of Blood Strike + if (m_caster->GetAuraEffect(59332, EFFECT_0)) + if (unitTarget->HasAuraType(SPELL_AURA_MOD_DECREASE_SPEED)) + AddPct(totalDamagePercentMod, 20.0f); + break; + } + // Death Strike + if (m_spellInfo->SpellFamilyFlags[0] & 0x10) + { + // Glyph of Death Strike + if (AuraEffect const* aurEff = m_caster->GetAuraEffect(59336, EFFECT_0)) + if (uint32 runic = std::min(m_caster->GetPower(POWER_RUNIC_POWER), aurEff->GetSpellInfo()->Effects[EFFECT_1].CalcValue())) + AddPct(totalDamagePercentMod, runic); + break; + } + // Obliterate (12.5% more damage per disease) + if (m_spellInfo->SpellFamilyFlags[1] & 0x20000) + { + bool consumeDiseases = true; + // Annihilation + if (AuraEffect const* aurEff = m_caster->GetDummyAuraEffect(SPELLFAMILY_DEATHKNIGHT, 2710, EFFECT_0)) + // Do not consume diseases if roll sucesses + if (roll_chance_i(aurEff->GetAmount())) + consumeDiseases = false; + + float disease_amt = m_spellInfo->Effects[EFFECT_2].CalcValue(); + //Death Knight T8 Melee 4P Bonus + if (AuraEffect* aurEff = m_caster->GetAuraEffectDummy(64736)) + AddPct(disease_amt, aurEff->GetAmount()); + + AddPct(totalDamagePercentMod, disease_amt * unitTarget->GetDiseasesByCaster(m_caster->GetGUID(), consumeDiseases) / 2.0f); + break; + } + // Blood-Caked Strike - Blood-Caked Blade + if (m_spellInfo->SpellIconID == 1736) + { + int32 weaponDamage = m_caster->CalculateDamage(m_attackType, false, true); + ApplyPct(weaponDamage, std::min(uint32(3), unitTarget->GetDiseasesByCaster(m_caster->GetGUID())) * 12.5f); + spell_bonus = weaponDamage; + break; + } + // Heart Strike + if (m_spellInfo->SpellFamilyFlags[0] & 0x1000000) + { + float disease_amt = m_spellInfo->Effects[EFFECT_2].CalcValue(); + //Death Knight T8 Melee 4P Bonus + if (AuraEffect* aurEff = m_caster->GetAuraEffectDummy(64736)) + AddPct(disease_amt, aurEff->GetAmount()); - AddPct(totalDamagePercentMod, disease_amt * unitTarget->GetDiseasesByCaster(m_caster->GetGUID())); - break; - } - // Rune Strike - if (m_spellInfo->SpellFamilyFlags[1] & 0x20000000) - { - spell_bonus += int32(0.15f * m_caster->GetTotalAttackPowerValue(BASE_ATTACK)); - } + AddPct(totalDamagePercentMod, disease_amt * unitTarget->GetDiseasesByCaster(m_caster->GetGUID())); + break; + } + // Rune Strike + if (m_spellInfo->SpellFamilyFlags[1] & 0x20000000) + { + spell_bonus += int32(0.15f * m_caster->GetTotalAttackPowerValue(BASE_ATTACK)); + } - break; - } + break; + } } bool normalized = false; @@ -3620,19 +3611,19 @@ void Spell::EffectWeaponDmg(SpellEffIndex effIndex) { switch (m_spellInfo->Effects[j].Effect) { - case SPELL_EFFECT_WEAPON_DAMAGE: - case SPELL_EFFECT_WEAPON_DAMAGE_NOSCHOOL: - fixed_bonus += CalculateSpellDamage(j, unitTarget); - break; - case SPELL_EFFECT_NORMALIZED_WEAPON_DMG: - fixed_bonus += CalculateSpellDamage(j, unitTarget); - normalized = true; - break; - case SPELL_EFFECT_WEAPON_PERCENT_DAMAGE: - ApplyPct(weaponDamagePercentMod, CalculateSpellDamage(j, unitTarget)); - break; - default: - break; // not weapon damage effect, just skip + case SPELL_EFFECT_WEAPON_DAMAGE: + case SPELL_EFFECT_WEAPON_DAMAGE_NOSCHOOL: + fixed_bonus += CalculateSpellDamage(j, unitTarget); + break; + case SPELL_EFFECT_NORMALIZED_WEAPON_DMG: + fixed_bonus += CalculateSpellDamage(j, unitTarget); + normalized = true; + break; + case SPELL_EFFECT_WEAPON_PERCENT_DAMAGE: + ApplyPct(weaponDamagePercentMod, CalculateSpellDamage(j, unitTarget)); + break; + default: + break; // not weapon damage effect, just skip } } @@ -3642,16 +3633,16 @@ void Spell::EffectWeaponDmg(SpellEffIndex effIndex) UnitMods unitMod; switch (m_attackType) { - default: - case BASE_ATTACK: - unitMod = UNIT_MOD_DAMAGE_MAINHAND; - break; - case OFF_ATTACK: - unitMod = UNIT_MOD_DAMAGE_OFFHAND; - break; - case RANGED_ATTACK: - unitMod = UNIT_MOD_DAMAGE_RANGED; - break; + default: + case BASE_ATTACK: + unitMod = UNIT_MOD_DAMAGE_MAINHAND; + break; + case OFF_ATTACK: + unitMod = UNIT_MOD_DAMAGE_OFFHAND; + break; + case RANGED_ATTACK: + unitMod = UNIT_MOD_DAMAGE_RANGED; + break; } if (m_spellSchoolMask & SPELL_SCHOOL_MASK_NORMAL) @@ -3685,22 +3676,22 @@ void Spell::EffectWeaponDmg(SpellEffIndex effIndex) // and at most one weaponDamagePercentMod switch (m_spellInfo->Effects[j].Effect) { - case SPELL_EFFECT_WEAPON_DAMAGE: - case SPELL_EFFECT_WEAPON_DAMAGE_NOSCHOOL: - case SPELL_EFFECT_NORMALIZED_WEAPON_DMG: - weaponDamage += fixed_bonus; - break; - case SPELL_EFFECT_WEAPON_PERCENT_DAMAGE: - ApplyPct(weaponDamage, weaponDamagePercentMod); - default: - break; // not weapon damage effect, just skip + case SPELL_EFFECT_WEAPON_DAMAGE: + case SPELL_EFFECT_WEAPON_DAMAGE_NOSCHOOL: + case SPELL_EFFECT_NORMALIZED_WEAPON_DMG: + weaponDamage += fixed_bonus; + break; + case SPELL_EFFECT_WEAPON_PERCENT_DAMAGE: + ApplyPct(weaponDamage, weaponDamagePercentMod); + default: + break; // not weapon damage effect, just skip } } weaponDamage += spell_bonus; - + ProcessWeaponDamageSchoolEffects(weaponDamage, totalDamagePercentMod, effIndex); - + totalDamagePercentMod = totalDamagePercentMod / 100; // adjust to a 100% = 1.0f value m_caster->MeleeDamageBonus(unitTarget, weaponDamage, totalDamagePercentMod, m_attackType, m_spellInfo); totalDamagePercentMod = totalDamagePercentMod * 100; // return to a 100% = 100.0f value @@ -3732,14 +3723,14 @@ void Spell::EffectThreat(SpellEffIndex /*effIndex*/) if (effectHandleMode != SPELL_EFFECT_HANDLE_HIT_TARGET) return; - if (!unitTarget || !unitTarget->IsAlive() || !m_caster->IsAlive()) + if (!unitTarget || !m_caster->IsAlive()) return; // xinef: skip if target cannot have threat list or caster is friendly (ghoul leap) if (!unitTarget->CanHaveThreatList() || m_caster->IsFriendlyTo(unitTarget)) return; - unitTarget->AddThreat(m_caster, float(damage)); + unitTarget->GetThreatManager().AddThreat(m_caster, float(damage), m_spellInfo, true); } void Spell::EffectHealMaxHealth(SpellEffIndex /*effIndex*/) @@ -3779,10 +3770,10 @@ void Spell::EffectInterruptCast(SpellEffIndex effIndex) SpellInfo const* curSpellInfo = spell->m_spellInfo; // check if we can interrupt spell if ((spell->getState() == SPELL_STATE_CASTING - || (spell->getState() == SPELL_STATE_PREPARING && spell->GetCastTime() > 0.0f)) - && curSpellInfo->PreventionType == SPELL_PREVENTION_TYPE_SILENCE - && ((i == CURRENT_GENERIC_SPELL && curSpellInfo->InterruptFlags & SPELL_INTERRUPT_FLAG_INTERRUPT) - || (i == CURRENT_CHANNELED_SPELL && curSpellInfo->ChannelInterruptFlags & CHANNEL_INTERRUPT_FLAG_INTERRUPT))) + || (spell->getState() == SPELL_STATE_PREPARING && spell->GetCastTime() > 0.0f)) + && curSpellInfo->PreventionType == SPELL_PREVENTION_TYPE_SILENCE + && ((i == CURRENT_GENERIC_SPELL && curSpellInfo->InterruptFlags & SPELL_INTERRUPT_FLAG_INTERRUPT) + || (i == CURRENT_CHANNELED_SPELL && curSpellInfo->ChannelInterruptFlags & CHANNEL_INTERRUPT_FLAG_INTERRUPT))) { if (m_originalCaster) { @@ -3841,7 +3832,7 @@ void Spell::EffectSummonObjectWild(SpellEffIndex effIndex) if (GameObject* linkedTrap = pGameObj->GetLinkedTrap()) { - linkedTrap->SetRespawnTime(duration > 0 ? duration/IN_MILLISECONDS :0); + linkedTrap->SetRespawnTime(duration > 0 ? duration / IN_MILLISECONDS : 0); linkedTrap->SetSpellId(m_spellInfo->Id); ExecuteLogEffectSummonObject(effIndex, linkedTrap); } @@ -3856,226 +3847,226 @@ void Spell::EffectScriptEffect(SpellEffIndex effIndex) switch (m_spellInfo->SpellFamilyName) { - case SPELLFAMILY_GENERIC: - { - switch (m_spellInfo->Id) - { - // Shadow Flame (All script effects, not just end ones to prevent player from dodging the last triggered spell) - case 22539: - case 22972: - case 22975: - case 22976: - case 22977: - case 22978: - case 22979: - case 22980: - case 22981: - case 22982: - case 22983: - case 22984: - case 22985: - { - if (!unitTarget || !unitTarget->IsAlive()) - return; + case SPELLFAMILY_GENERIC: + { + switch (m_spellInfo->Id) + { + // Shadow Flame (All script effects, not just end ones to prevent player from dodging the last triggered spell) + case 22539: + case 22972: + case 22975: + case 22976: + case 22977: + case 22978: + case 22979: + case 22980: + case 22981: + case 22982: + case 22983: + case 22984: + case 22985: + { + if (!unitTarget || !unitTarget->IsAlive()) + return; - // Onyxia Scale Cloak - if (unitTarget->HasAura(22683)) - return; + // Onyxia Scale Cloak + if (unitTarget->HasAura(22683)) + return; - // Shadow Flame - m_caster->CastSpell(unitTarget, 22682, true); - return; - } - // Plant Warmaul Ogre Banner - case 32307: - if (Player* caster = m_caster->ToPlayer()) - { - caster->RewardPlayerAndGroupAtEvent(18388, unitTarget); - if (Creature* target = unitTarget->ToCreature()) - { - target->setDeathState(DeathState::Corpse); - target->RemoveCorpse(); - } - } - break; - // SOTA defender teleport - case 54640: - { - if (Player* player = unitTarget->ToPlayer()) - if (player->GetBattleground() && player->GetBattleground()->GetBgTypeID(true) == BATTLEGROUND_SA) - { - if (GameObject* dportal = player->FindNearestGameObject(192819, 10.0f)) - { - BattlegroundSA* bg = ((BattlegroundSA*)player->GetBattleground()); - bg->DefendersPortalTeleport(dportal, player); - } - } - return; - } - /*// Mug Transformation - case 41931: + // Shadow Flame + m_caster->CastSpell(unitTarget, 22682, true); + return; + } + // Plant Warmaul Ogre Banner + case 32307: + if (Player* caster = m_caster->ToPlayer()) + { + caster->RewardPlayerAndGroupAtEvent(18388, unitTarget); + if (Creature* target = unitTarget->ToCreature()) + { + target->setDeathState(DeathState::Corpse); + target->RemoveCorpse(); + } + } + break; + // SOTA defender teleport + case 54640: + { + if (Player* player = unitTarget->ToPlayer()) + if (player->GetBattleground() && player->GetBattleground()->GetBgTypeID(true) == BATTLEGROUND_SA) + { + if (GameObject* dportal = player->FindNearestGameObject(192819, 10.0f)) { - if (m_caster->GetTypeId() != TYPEID_PLAYER) - return; - - uint8 bag = 19; - uint8 slot = 0; - Item* item = nullptr; + BattlegroundSA* bg = ((BattlegroundSA*)player->GetBattleground()); + bg->DefendersPortalTeleport(dportal, player); + } + } + return; + } + /*// Mug Transformation + case 41931: + { + if (m_caster->GetTypeId() != TYPEID_PLAYER) + return; - while (bag) // 256 = 0 due to var type - { - item = m_caster->ToPlayer()->GetItemByPos(bag, slot); - if (item && item->GetEntry() == 38587) - break; + uint8 bag = 19; + uint8 slot = 0; + Item* item = nullptr; - ++slot; - if (slot == 39) - { - slot = 0; - ++bag; - } - } - if (bag) - { - if (m_caster->ToPlayer()->GetItemByPos(bag, slot)->GetCount() == 1) m_caster->ToPlayer()->RemoveItem(bag, slot, true); - else m_caster->ToPlayer()->GetItemByPos(bag, slot)->SetCount(m_caster->ToPlayer()->GetItemByPos(bag, slot)->GetCount()-1); - // Spell 42518 (Braufest - Gratisprobe des Braufest herstellen) - m_caster->CastSpell(m_caster, 42518, true); - return; - } - break; - }*/ - // Roll Dice - Decahedral Dwarven Dice - case 47770: - { - char buf[128]; - const char* gender = "his"; - if (m_caster->getGender() > 0) - gender = "her"; - snprintf(buf, sizeof(buf), "%s rubs %s [Decahedral Dwarven Dice] between %s hands and rolls. One %u and one %u.", m_caster->GetName().c_str(), gender, gender, urand(1, 10), urand(1, 10)); - m_caster->TextEmote(buf); - break; - } - case 52173: // Coyote Spirit Despawn - case 60243: // Blood Parrot Despawn - if (unitTarget->GetTypeId() == TYPEID_UNIT && unitTarget->ToCreature()->IsSummon()) - unitTarget->ToTempSummon()->UnSummon(); - return; - case 57347: // Retrieving (Wintergrasp RP-GG pickup spell) - { - if (!unitTarget || unitTarget->GetTypeId() != TYPEID_UNIT || m_caster->GetTypeId() != TYPEID_PLAYER) - return; + while (bag) // 256 = 0 due to var type + { + item = m_caster->ToPlayer()->GetItemByPos(bag, slot); + if (item && item->GetEntry() == 38587) + break; - unitTarget->ToCreature()->DespawnOrUnsummon(); + ++slot; + if (slot == 39) + { + slot = 0; + ++bag; + } + } + if (bag) + { + if (m_caster->ToPlayer()->GetItemByPos(bag, slot)->GetCount() == 1) m_caster->ToPlayer()->RemoveItem(bag, slot, true); + else m_caster->ToPlayer()->GetItemByPos(bag, slot)->SetCount(m_caster->ToPlayer()->GetItemByPos(bag, slot)->GetCount()-1); + // Spell 42518 (Braufest - Gratisprobe des Braufest herstellen) + m_caster->CastSpell(m_caster, 42518, true); + return; + } + break; + }*/ + // Roll Dice - Decahedral Dwarven Dice + case 47770: + { + char buf[128]; + const char* gender = "his"; + if (m_caster->getGender() > 0) + gender = "her"; + snprintf(buf, sizeof(buf), "%s rubs %s [Decahedral Dwarven Dice] between %s hands and rolls. One %u and one %u.", m_caster->GetName().c_str(), gender, gender, urand(1, 10), urand(1, 10)); + m_caster->TextEmote(buf); + break; + } + case 52173: // Coyote Spirit Despawn + case 60243: // Blood Parrot Despawn + if (unitTarget->GetTypeId() == TYPEID_UNIT && unitTarget->ToCreature()->IsSummon()) + unitTarget->ToTempSummon()->UnSummon(); + return; + case 57347: // Retrieving (Wintergrasp RP-GG pickup spell) + { + if (!unitTarget || unitTarget->GetTypeId() != TYPEID_UNIT || m_caster->GetTypeId() != TYPEID_PLAYER) + return; - return; - } - case 57349: // Drop RP-GG (Wintergrasp RP-GG at death drop spell) - { - if (m_caster->GetTypeId() != TYPEID_PLAYER) - return; + unitTarget->ToCreature()->DespawnOrUnsummon(); - // Delete item from inventory at death - m_caster->ToPlayer()->DestroyItemCount(damage, 5, true); + return; + } + case 57349: // Drop RP-GG (Wintergrasp RP-GG at death drop spell) + { + if (m_caster->GetTypeId() != TYPEID_PLAYER) + return; - return; - } - case 58418: // Portal to Orgrimmar - case 58420: // Portal to Stormwind - { - if (!unitTarget || unitTarget->GetTypeId() != TYPEID_PLAYER || effIndex != 0) - return; + // Delete item from inventory at death + m_caster->ToPlayer()->DestroyItemCount(damage, 5, true); - uint32 spellID = m_spellInfo->Effects[EFFECT_0].CalcValue(); - uint32 questID = m_spellInfo->Effects[EFFECT_1].CalcValue(); + return; + } + case 58418: // Portal to Orgrimmar + case 58420: // Portal to Stormwind + { + if (!unitTarget || unitTarget->GetTypeId() != TYPEID_PLAYER || effIndex != 0) + return; - if (unitTarget->ToPlayer()->GetQuestStatus(questID) == QUEST_STATUS_COMPLETE) - unitTarget->CastSpell(unitTarget, spellID, true); + uint32 spellID = m_spellInfo->Effects[EFFECT_0].CalcValue(); + uint32 questID = m_spellInfo->Effects[EFFECT_1].CalcValue(); - return; - } - // Stoneclaw Totem - case 55328: // Rank 1 - case 55329: // Rank 2 - case 55330: // Rank 3 - case 55332: // Rank 4 - case 55333: // Rank 5 - case 55335: // Rank 6 - case 55278: // Rank 7 - case 58589: // Rank 8 - case 58590: // Rank 9 - case 58591: // Rank 10 - { - int32 basepoints0 = CalculatePct(m_caster->GetHealth(), 10); // old: damage; new: 10% HP - // Cast Absorb on totems - for (uint8 slot = SUMMON_SLOT_TOTEM; slot < MAX_TOTEM_SLOT; ++slot) - { - if (!unitTarget->m_SummonSlot[slot]) - continue; + if (unitTarget->ToPlayer()->GetQuestStatus(questID) == QUEST_STATUS_COMPLETE) + unitTarget->CastSpell(unitTarget, spellID, true); - Creature* totem = unitTarget->GetMap()->GetCreature(unitTarget->m_SummonSlot[slot]); - if (totem && totem->IsTotem()) - { - m_caster->CastCustomSpell(totem, 55277, &basepoints0, nullptr, nullptr, true); - } - } - // Glyph of Stoneclaw Totem - if (AuraEffect* aur = unitTarget->GetAuraEffect(63298, 0)) - { - basepoints0 *= aur->GetAmount(); - m_caster->CastCustomSpell(unitTarget, 55277, &basepoints0, nullptr, nullptr, true); - } - break; - } - case 61263: // for item Intravenous Healing Potion (44698) - { - if( !m_caster || !unitTarget ) - return; + return; + } + // Stoneclaw Totem + case 55328: // Rank 1 + case 55329: // Rank 2 + case 55330: // Rank 3 + case 55332: // Rank 4 + case 55333: // Rank 5 + case 55335: // Rank 6 + case 55278: // Rank 7 + case 58589: // Rank 8 + case 58590: // Rank 9 + case 58591: // Rank 10 + { + int32 basepoints0 = CalculatePct(m_caster->GetHealth(), 10); // old: damage; new: 10% HP + // Cast Absorb on totems + for (uint8 slot = SUMMON_SLOT_TOTEM; slot < MAX_TOTEM_SLOT; ++slot) + { + if (!unitTarget->m_SummonSlot[slot]) + continue; - m_caster->CastSpell(m_caster, 61267, true); - m_caster->CastSpell(m_caster, 61268, true); - return; - } + Creature* totem = unitTarget->GetMap()->GetCreature(unitTarget->m_SummonSlot[slot]); + if (totem && totem->IsTotem()) + { + m_caster->CastCustomSpell(totem, 55277, &basepoints0, nullptr, nullptr, true); } - break; } - case SPELLFAMILY_ROGUE: + // Glyph of Stoneclaw Totem + if (AuraEffect* aur = unitTarget->GetAuraEffect(63298, 0)) { - switch( m_spellInfo->Id ) - { - // Master of Subtlety - case 31666: - { - if( !unitTarget ) - return; + basepoints0 *= aur->GetAmount(); + m_caster->CastCustomSpell(unitTarget, 55277, &basepoints0, nullptr, nullptr, true); + } + break; + } + case 61263: // for item Intravenous Healing Potion (44698) + { + if (!m_caster || !unitTarget) + return; - Aura* mos = unitTarget->GetAura(31665); - if( mos ) - { - mos->SetMaxDuration(6000); - mos->SetDuration(6000, true); - } + m_caster->CastSpell(m_caster, 61267, true); + m_caster->CastSpell(m_caster, 61268, true); + return; + } + } + break; + } + case SPELLFAMILY_ROGUE: + { + switch (m_spellInfo->Id) + { + // Master of Subtlety + case 31666: + { + if (!unitTarget) + return; - break; - } - // Overkill - case 58428: - { - if( !unitTarget ) - return; + Aura* mos = unitTarget->GetAura(31665); + if (mos) + { + mos->SetMaxDuration(6000); + mos->SetDuration(6000, true); + } - Aura* overkill = unitTarget->GetAura(58427); - if( overkill ) - { - overkill->SetMaxDuration(20000); - overkill->SetDuration(20000, true); - } + break; + } + // Overkill + case 58428: + { + if (!unitTarget) + return; - break; - } - } - break; + Aura* overkill = unitTarget->GetAura(58427); + if (overkill) + { + overkill->SetMaxDuration(20000); + overkill->SetDuration(20000, true); } + + break; + } + } + break; + } } // normal DB scripted effect @@ -4093,22 +4084,14 @@ void Spell::EffectSanctuary(SpellEffIndex /*effIndex*/) if (unitTarget->GetInstanceScript() && unitTarget->GetInstanceScript()->IsEncounterInProgress()) { - unitTarget->getHostileRefMgr().UpdateVisibility(true); - // Xinef: replaced with CombatStop(false) - unitTarget->AttackStop(); - unitTarget->RemoveAllAttackers(); - - // Night Elf: Shadowmeld only resets threat temporarily - if (m_spellInfo->Id != 59646) - unitTarget->getHostileRefMgr().addThreatPercent(-100); - - if (unitTarget->GetTypeId() == TYPEID_PLAYER) - unitTarget->ToPlayer()->SendAttackSwingCancelAttack(); // melee and ranged forced attack cancel + // in dungeons (or for nonplayers), reset this unit on all enemies' threat lists + for (auto const& pair : unitTarget->GetThreatManager().GetThreatenedByMeList()) + pair.second->SetThreat(0.0f); } else { - unitTarget->getHostileRefMgr().UpdateVisibility(m_spellInfo->Id == 59646); // Night Elf: Shadowmeld - unitTarget->CombatStop(true); + // stop all pve combat for players outside dungeons, suppress pvp combat + unitTarget->CombatStop(false, false); } UnitList targets; @@ -4145,13 +4128,13 @@ void Spell::EffectSanctuary(SpellEffIndex /*effIndex*/) } } - // Xinef: Set last sanctuary time + // makes spells cast before this time fizzle unitTarget->m_lastSanctuaryTime = GameTime::GetGameTimeMS().count(); // Vanish allows to remove all threat and cast regular stealth so other spells can be used if (m_caster->GetTypeId() == TYPEID_PLAYER - && m_spellInfo->SpellFamilyName == SPELLFAMILY_ROGUE - && (m_spellInfo->SpellFamilyFlags[0] & SPELLFAMILYFLAG_ROGUE_VANISH)) + && m_spellInfo->SpellFamilyName == SPELLFAMILY_ROGUE + && (m_spellInfo->SpellFamilyFlags[0] & SPELLFAMILYFLAG_ROGUE_VANISH)) { m_caster->ToPlayer()->RemoveAurasByType(SPELL_AURA_MOD_ROOT); @@ -4212,11 +4195,11 @@ void Spell::EffectDuel(SpellEffIndex effIndex) Map* map = m_caster->GetMap(); if (!pGameObj->Create(map->GenerateLowGuid(), gameobject_id, - map, m_caster->GetPhaseMask(), - m_caster->GetPositionX() + (unitTarget->GetPositionX() - m_caster->GetPositionX()) / 2, - m_caster->GetPositionY() + (unitTarget->GetPositionY() - m_caster->GetPositionY()) / 2, - m_caster->GetPositionZ(), - m_caster->GetOrientation(), G3D::Quat(), 0, GO_STATE_READY)) + map, m_caster->GetPhaseMask(), + m_caster->GetPositionX() + (unitTarget->GetPositionX() - m_caster->GetPositionX()) / 2, + m_caster->GetPositionY() + (unitTarget->GetPositionY() - m_caster->GetPositionY()) / 2, + m_caster->GetPositionZ(), + m_caster->GetOrientation(), G3D::Quat(), 0, GO_STATE_READY)) { delete pGameObj; return; @@ -4335,70 +4318,70 @@ void Spell::EffectActivateObject(SpellEffIndex effIndex) GameObjectActions action = GameObjectActions(m_spellInfo->Effects[effIndex].MiscValue); switch (action) { - case GameObjectActions::AnimateCustom0: - case GameObjectActions::AnimateCustom1: - case GameObjectActions::AnimateCustom2: - case GameObjectActions::AnimateCustom3: - gameObjTarget->SendCustomAnim(uint32(action) - uint32(GameObjectActions::AnimateCustom0)); - break; - case GameObjectActions::Disturb: // What's the difference with Open? - case GameObjectActions::Open: - if (Unit* unitCaster = m_caster->ToUnit()) - gameObjTarget->Use(unitCaster); - break; - case GameObjectActions::OpenAndUnlock: - if (Unit* unitCaster = m_caster->ToUnit()) - gameObjTarget->UseDoorOrButton(0, false, unitCaster); - [[fallthrough]]; - case GameObjectActions::Unlock: - case GameObjectActions::Lock: - gameObjTarget->ApplyModFlag(GAMEOBJECT_FLAGS, GO_FLAG_LOCKED, action == GameObjectActions::Lock); - break; - case GameObjectActions::Close: - case GameObjectActions::Rebuild: - gameObjTarget->ResetDoorOrButton(); - break; - case GameObjectActions::Despawn: - gameObjTarget->DespawnOrUnsummon(); - break; - case GameObjectActions::MakeInert: - case GameObjectActions::MakeActive: - gameObjTarget->ApplyModFlag(GAMEOBJECT_FLAGS, GO_FLAG_NOT_SELECTABLE, action == GameObjectActions::MakeInert); - break; - case GameObjectActions::CloseAndLock: - gameObjTarget->ResetDoorOrButton(); - gameObjTarget->SetFlag(GAMEOBJECT_FLAGS, GO_FLAG_LOCKED); - break; - case GameObjectActions::Destroy: - if (Unit* unitCaster = m_caster->ToUnit()) - gameObjTarget->UseDoorOrButton(0, true, unitCaster); - break; - case GameObjectActions::UseArtKit0: - case GameObjectActions::UseArtKit1: - case GameObjectActions::UseArtKit2: - case GameObjectActions::UseArtKit3: - { - GameObjectTemplateAddon const* templateAddon = gameObjTarget->GetTemplateAddon(); - - uint32 artKitIndex = uint32(action) - uint32(GameObjectActions::UseArtKit0); - - uint32 artKitValue = 0; - if (templateAddon) - artKitValue = templateAddon->artKits[artKitIndex]; - - if (artKitValue == 0) - LOG_ERROR("sql.sql", "GameObject {} hit by spell {} needs `artkit{}` in `gameobject_template_addon`", gameObjTarget->GetEntry(), m_spellInfo->Id, artKitIndex); - else - gameObjTarget->SetGoArtKit(artKitValue); + case GameObjectActions::AnimateCustom0: + case GameObjectActions::AnimateCustom1: + case GameObjectActions::AnimateCustom2: + case GameObjectActions::AnimateCustom3: + gameObjTarget->SendCustomAnim(uint32(action) - uint32(GameObjectActions::AnimateCustom0)); + break; + case GameObjectActions::Disturb: // What's the difference with Open? + case GameObjectActions::Open: + if (Unit* unitCaster = m_caster->ToUnit()) + gameObjTarget->Use(unitCaster); + break; + case GameObjectActions::OpenAndUnlock: + if (Unit* unitCaster = m_caster->ToUnit()) + gameObjTarget->UseDoorOrButton(0, false, unitCaster); + [[fallthrough]]; + case GameObjectActions::Unlock: + case GameObjectActions::Lock: + gameObjTarget->ApplyModFlag(GAMEOBJECT_FLAGS, GO_FLAG_LOCKED, action == GameObjectActions::Lock); + break; + case GameObjectActions::Close: + case GameObjectActions::Rebuild: + gameObjTarget->ResetDoorOrButton(); + break; + case GameObjectActions::Despawn: + gameObjTarget->DespawnOrUnsummon(); + break; + case GameObjectActions::MakeInert: + case GameObjectActions::MakeActive: + gameObjTarget->ApplyModFlag(GAMEOBJECT_FLAGS, GO_FLAG_NOT_SELECTABLE, action == GameObjectActions::MakeInert); + break; + case GameObjectActions::CloseAndLock: + gameObjTarget->ResetDoorOrButton(); + gameObjTarget->SetFlag(GAMEOBJECT_FLAGS, GO_FLAG_LOCKED); + break; + case GameObjectActions::Destroy: + if (Unit* unitCaster = m_caster->ToUnit()) + gameObjTarget->UseDoorOrButton(0, true, unitCaster); + break; + case GameObjectActions::UseArtKit0: + case GameObjectActions::UseArtKit1: + case GameObjectActions::UseArtKit2: + case GameObjectActions::UseArtKit3: + { + GameObjectTemplateAddon const* templateAddon = gameObjTarget->GetTemplateAddon(); + + uint32 artKitIndex = uint32(action) - uint32(GameObjectActions::UseArtKit0); + + uint32 artKitValue = 0; + if (templateAddon) + artKitValue = templateAddon->artKits[artKitIndex]; + + if (artKitValue == 0) + LOG_ERROR("sql.sql", "GameObject {} hit by spell {} needs `artkit{}` in `gameobject_template_addon`", gameObjTarget->GetEntry(), m_spellInfo->Id, artKitIndex); + else + gameObjTarget->SetGoArtKit(artKitValue); - break; - } - case GameObjectActions::None: - LOG_FATAL("spell", "Spell {} has action type NONE in effect {}", m_spellInfo->Id, int32(effIndex)); - break; - default: - LOG_ERROR("spell", "Spell {} has unhandled action {} in effect {}", m_spellInfo->Id, int32(action), int32(effIndex)); - break; + break; + } + case GameObjectActions::None: + LOG_FATAL("spell", "Spell {} has action type NONE in effect {}", m_spellInfo->Id, int32(effIndex)); + break; + default: + LOG_ERROR("spell", "Spell {} has unhandled action {} in effect {}", m_spellInfo->Id, int32(action), int32(effIndex)); + break; } } @@ -4643,20 +4626,20 @@ void Spell::EffectSummonObject(SpellEffIndex effIndex) uint8 slot = 0; switch (m_spellInfo->Effects[effIndex].Effect) { - case SPELL_EFFECT_SUMMON_OBJECT_SLOT1: - slot = 0; - break; - case SPELL_EFFECT_SUMMON_OBJECT_SLOT2: - slot = 1; - break; - case SPELL_EFFECT_SUMMON_OBJECT_SLOT3: - slot = 2; - break; - case SPELL_EFFECT_SUMMON_OBJECT_SLOT4: - slot = 3; - break; - default: - return; + case SPELL_EFFECT_SUMMON_OBJECT_SLOT1: + slot = 0; + break; + case SPELL_EFFECT_SUMMON_OBJECT_SLOT2: + slot = 1; + break; + case SPELL_EFFECT_SUMMON_OBJECT_SLOT3: + slot = 2; + break; + case SPELL_EFFECT_SUMMON_OBJECT_SLOT4: + slot = 3; + break; + default: + return; } if (m_caster) @@ -4729,7 +4712,7 @@ void Spell::EffectResurrect(SpellEffIndex effIndex) return; uint32 health = target->CountPctFromMaxHealth(damage); - uint32 mana = CalculatePct(target->GetMaxPower(POWER_MANA), damage); + uint32 mana = CalculatePct(target->GetMaxPower(POWER_MANA), damage); ExecuteLogEffectResurrect(effIndex, target); @@ -5272,10 +5255,6 @@ void Spell::EffectDispelMechanic(SpellEffIndex effIndex) { unitTarget->RemoveAura(dispel_list.front().first, dispel_list.front().second, 0, AURA_REMOVE_BY_ENEMY_SPELL); } - - // put in combat - if (unitTarget->IsFriendlyTo(m_caster)) - unitTarget->getHostileRefMgr().threatAssist(m_caster, 0.0f, m_spellInfo); } void Spell::EffectResurrectPet(SpellEffIndex /*effIndex*/) @@ -5437,7 +5416,7 @@ void Spell::EffectModifyThreatPercent(SpellEffIndex /*effIndex*/) if (!unitTarget) return; - unitTarget->GetThreatMgr().ModifyThreatByPercent(m_caster, damage); + unitTarget->GetThreatManager().ModifyThreatByPercent(m_caster, damage); } void Spell::EffectTransmitted(SpellEffIndex effIndex) @@ -5500,48 +5479,48 @@ void Spell::EffectTransmitted(SpellEffIndex effIndex) switch (goinfo->type) { - case GAMEOBJECT_TYPE_FISHINGNODE: - { - m_caster->SetGuidValue(UNIT_FIELD_CHANNEL_OBJECT, pGameObj->GetGUID()); - m_caster->AddGameObject(pGameObj); // will removed at spell cancel - - // end time of range when possible catch fish (FISHING_BOBBER_READY_TIME..GetDuration(m_spellInfo)) - // start time == fish-FISHING_BOBBER_READY_TIME (0..GetDuration(m_spellInfo)-FISHING_BOBBER_READY_TIME) - int32 lastSec = 0; - switch (urand(0, 2)) - { - case 0: - lastSec = 3; - break; - case 1: - lastSec = 7; - break; - case 2: - lastSec = 13; - break; - } - - // Duration of the fishing bobber can't be higher than the Fishing channeling duration - duration = std::min(duration, duration - lastSec*IN_MILLISECONDS + FISHING_BOBBER_READY_TIME*IN_MILLISECONDS); + case GAMEOBJECT_TYPE_FISHINGNODE: + { + m_caster->SetGuidValue(UNIT_FIELD_CHANNEL_OBJECT, pGameObj->GetGUID()); + m_caster->AddGameObject(pGameObj); // will removed at spell cancel - break; - } - case GAMEOBJECT_TYPE_SUMMONING_RITUAL: - { - if (m_caster->GetTypeId() == TYPEID_PLAYER) - { - pGameObj->AddUniqueUse(m_caster->ToPlayer()); - m_caster->AddGameObject(pGameObj); // will be removed at spell cancel - } - break; - } - case GAMEOBJECT_TYPE_DUEL_ARBITER: // 52991 - m_caster->AddGameObject(pGameObj); + // end time of range when possible catch fish (FISHING_BOBBER_READY_TIME..GetDuration(m_spellInfo)) + // start time == fish-FISHING_BOBBER_READY_TIME (0..GetDuration(m_spellInfo)-FISHING_BOBBER_READY_TIME) + int32 lastSec = 0; + switch (urand(0, 2)) + { + case 0: + lastSec = 3; break; - case GAMEOBJECT_TYPE_FISHINGHOLE: - case GAMEOBJECT_TYPE_CHEST: - default: + case 1: + lastSec = 7; + break; + case 2: + lastSec = 13; break; + } + + // Duration of the fishing bobber can't be higher than the Fishing channeling duration + duration = std::min(duration, duration - lastSec * IN_MILLISECONDS + FISHING_BOBBER_READY_TIME * IN_MILLISECONDS); + + break; + } + case GAMEOBJECT_TYPE_SUMMONING_RITUAL: + { + if (m_caster->GetTypeId() == TYPEID_PLAYER) + { + pGameObj->AddUniqueUse(m_caster->ToPlayer()); + m_caster->AddGameObject(pGameObj); // will be removed at spell cancel + } + break; + } + case GAMEOBJECT_TYPE_DUEL_ARBITER: // 52991 + m_caster->AddGameObject(pGameObj); + break; + case GAMEOBJECT_TYPE_FISHINGHOLE: + case GAMEOBJECT_TYPE_CHEST: + default: + break; } pGameObj->SetRespawnTime(duration > 0 ? duration / IN_MILLISECONDS : 0); @@ -5680,7 +5659,7 @@ void Spell::EffectStealBeneficialBuff(SpellEffIndex effIndex) DispelChargesList steal_list; // Create dispel mask by dispel type - uint32 dispelMask = SpellInfo::GetDispelMask(DispelType(m_spellInfo->Effects[effIndex].MiscValue)); + uint32 dispelMask = SpellInfo::GetDispelMask(DispelType(m_spellInfo->Effects[effIndex].MiscValue)); Unit::AuraMap const& auras = unitTarget->GetOwnedAuras(); for (Unit::AuraMap::const_iterator itr = auras.begin(); itr != auras.end(); ++itr) { @@ -5990,7 +5969,7 @@ void Spell::EffectRedirectThreat(SpellEffIndex /*effIndex*/) return; if (unitTarget) - m_caster->SetRedirectThreat(unitTarget->GetGUID(), uint32(damage)); + m_caster->GetThreatManager().RegisterRedirectThreat(m_spellInfo->Id, unitTarget->GetGUID(), uint32(damage)); } void Spell::EffectGameObjectDamage(SpellEffIndex /*effIndex*/) @@ -6061,46 +6040,46 @@ void Spell::SummonGuardian(uint32 i, uint32 entry, SummonPropertiesEntry const* switch (m_spellInfo->Id) { // Dragon's Call - case 13049: - summonLevel = 55; - break; + case 13049: + summonLevel = 55; + break; // Cleansed Timberling Heart: Summon Timberling - case 5666: - summonLevel = 7; - break; + case 5666: + summonLevel = 7; + break; // Glowing Cat Figurine: Summon Ghost Saber - case 6084: - // minLevel 19, maxLevel 20 - summonLevel = 20; - break; + case 6084: + // minLevel 19, maxLevel 20 + summonLevel = 20; + break; // Spiked Collar: Summon Felhunter - case 8176: - summonLevel = 30; - break; + case 8176: + summonLevel = 30; + break; // Dog Whistle: Summon Tracking Hound - case 9515: - summonLevel = 30; - break; + case 9515: + summonLevel = 30; + break; // Barov Peasant Caller: Death by Peasant - case 18307: - case 18308: - summonLevel = 60; - break; + case 18307: + case 18308: + summonLevel = 60; + break; // Thornling Seed: Plant Thornling - case 22792: - summonLevel = 60; - break; + case 22792: + summonLevel = 60; + break; // Cannonball Runner: Summon Crimson Cannon - case 6251: - summonLevel = 61; - break; + case 6251: + summonLevel = 61; + break; } } @@ -6128,7 +6107,7 @@ void Spell::SummonGuardian(uint32 i, uint32 entry, SummonPropertiesEntry const* // target position is specified in the DB AND the effect has no or zero radius if ((totalNumGuardians == 1 && GetSpellInfo()->Effects[i].Effect != SPELL_EFFECT_SUMMON_PET) || (GetSpellInfo()->Effects[i].TargetA.GetTarget() == TARGET_DEST_DB && - (!GetSpellInfo()->Effects[i].HasRadius() || GetSpellInfo()->Effects[i].RadiusEntry->RadiusMax == 0))) + (!GetSpellInfo()->Effects[i].HasRadius() || GetSpellInfo()->Effects[i].RadiusEntry->RadiusMax == 0))) { pos = *destTarget; } @@ -6178,13 +6157,13 @@ void Spell::SummonGuardian(uint32 i, uint32 entry, SummonPropertiesEntry const* switch (m_spellInfo->Id) { // Inferno, Warlock summon spell - case 1122: - caster->AddAura(61191, summon); - break; + case 1122: + caster->AddAura(61191, summon); + break; // Ritual of Doom, Warlock summon spell - case 60478: - caster->AddAura(SPELL_RITUAL_ENSLAVEMENT, summon); - break; + case 60478: + caster->AddAura(SPELL_RITUAL_ENSLAVEMENT, summon); + break; } } } @@ -6195,7 +6174,7 @@ void Spell::EffectRenamePet(SpellEffIndex /*effIndex*/) return; if (!unitTarget || unitTarget->GetTypeId() != TYPEID_UNIT || - !unitTarget->IsPet() || unitTarget->ToPet()->getPetType() != HUNTER_PET) + !unitTarget->IsPet() || unitTarget->ToPet()->getPetType() != HUNTER_PET) return; unitTarget->SetByteFlag(UNIT_FIELD_BYTES_2, 2, UNIT_CAN_BE_RENAMED); @@ -6267,12 +6246,12 @@ void Spell::EffectPlaySound(SpellEffIndex effIndex) switch (m_spellInfo->Id) { - case 58730: // Restricted Flight Area - case 58600: // Restricted Flight Area - player->GetSession()->SendNotification(LANG_ZONE_NOFLYZONE); - break; - default: - break; + case 58730: // Restricted Flight Area + case 58600: // Restricted Flight Area + player->GetSession()->SendNotification(LANG_ZONE_NOFLYZONE); + break; + default: + break; } uint32 soundId = m_spellInfo->Effects[effIndex].MiscValue; @@ -6306,8 +6285,8 @@ void Spell::EffectCreateAreaTrigger(SpellEffIndex effIndex) return; int32 duration = GetSpellInfo()->GetDuration(); - - AreaTrigger::CreateAreaTrigger(m_spellInfo->GetEffect(effIndex).MiscValue, GetCaster(), nullptr, GetSpellInfo(), destTarget->GetPosition(), duration, { m_spellInfo->SpellVisual[0], m_spellInfo->SpellVisual[1]}, this); + + AreaTrigger::CreateAreaTrigger(m_spellInfo->GetEffect(effIndex).MiscValue, GetCaster(), nullptr, GetSpellInfo(), destTarget->GetPosition(), duration, { m_spellInfo->SpellVisual[0], m_spellInfo->SpellVisual[1] }, this); } void Spell::EffectLearnTransmogSet(SpellEffIndex effIndex) @@ -6641,7 +6620,7 @@ void Spell::EffectBind(SpellEffIndex effIndex) player->SendDirectMessage(&data); LOG_DEBUG("spells.aura", "EffectBind: New homebind X: {}, Y: {}, Z: {}, MapId: {}, AreaId: {}", - homeLoc.GetPositionX(), homeLoc.GetPositionY(), homeLoc.GetPositionZ(), homeLoc.GetMapId(), areaId); + homeLoc.GetPositionX(), homeLoc.GetPositionY(), homeLoc.GetPositionZ(), homeLoc.GetMapId(), areaId); // zone update data.Initialize(SMSG_PLAYERBOUND, 8 + 4); data << m_caster->GetGUID(); diff --git a/src/server/game/Spells/SpellInfo.cpp b/src/server/game/Spells/SpellInfo.cpp index 8633264e9f6ec5..18d31bb24769e8 100644 --- a/src/server/game/Spells/SpellInfo.cpp +++ b/src/server/game/Spells/SpellInfo.cpp @@ -1809,7 +1809,7 @@ SpellCastResult SpellInfo::CheckTarget(Unit const* caster, WorldObject const* ta if (unitTarget) { // xinef: spells cannot be cast if player is in fake combat also - if (AttributesEx & SPELL_ATTR1_ONLY_PEACEFUL_TARGETS && (unitTarget->IsInCombat() || unitTarget->IsPetInCombat())) + if (HasAttribute(SPELL_ATTR1_ONLY_PEACEFUL_TARGETS) && (unitTarget->IsInCombat() || unitTarget->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_PET_IN_COMBAT))) return SPELL_FAILED_TARGET_AFFECTING_COMBAT; // only spells with SPELL_ATTR3_ONLY_ON_GHOSTS can target ghosts @@ -3075,6 +3075,11 @@ bool SpellInfo::CheckElixirStacking(Unit const* caster) const return true; } +bool SpellInfo::HasInitialAggro() const +{ + return !(HasAttribute(SPELL_ATTR1_NO_THREAT) || HasAttribute(SPELL_ATTR3_SUPRESS_TARGET_PROCS)); +} + WeaponAttackType SpellInfo::GetAttackType() const { WeaponAttackType result; diff --git a/src/server/game/Spells/SpellInfo.h b/src/server/game/Spells/SpellInfo.h index 3a0745ce1ddb15..e03bf8bece54bf 100644 --- a/src/server/game/Spells/SpellInfo.h +++ b/src/server/game/Spells/SpellInfo.h @@ -476,6 +476,8 @@ friend class SpellMgr; WeaponAttackType GetAttackType() const; + bool HasInitialAggro() const; + bool IsAffected(uint32 familyName, flag96 const& familyFlags) const; bool IsAffectedBySpellMods() const; diff --git a/src/server/scripts/Commands/cs_debug.cpp b/src/server/scripts/Commands/cs_debug.cpp index 36bac6800c0fee..5b4040f4717e22 100644 --- a/src/server/scripts/Commands/cs_debug.cpp +++ b/src/server/scripts/Commands/cs_debug.cpp @@ -39,6 +39,7 @@ #include "PoolMgr.h" #include "ScriptMgr.h" #include "SpellMgr.h" +#include "ThreatMgr.h" #include "Transport.h" #include "Warden.h" #include "World.h" @@ -794,42 +795,74 @@ class debug_commandscript : public CommandScript static bool HandleDebugThreatListCommand(ChatHandler* handler) { - Creature* target = handler->getSelectedCreature(); - if (!target || target->IsTotem() || target->IsPet()) - return false; + Unit* target = handler->getSelectedUnit(); + if (!target) + target = handler->GetSession()->GetPlayer(); - auto const& threatList = target->GetThreatMgr().GetThreatList(); - ThreatContainer::StorageType::const_iterator itr; - uint32 count = 0; + ThreatMgr& mgr = target->GetThreatManager(); + if (!target->IsAlive()) + { + handler->PSendSysMessage("%s (guid %u) is not alive.", target->GetName().c_str(), target->GetGUID().GetCounter()); + return true; + } + if (!target->CanHaveThreatList()) + handler->PSendSysMessage("%s (guid %u) cannot have a threat list.", target->GetName().c_str(), target->GetGUID().GetCounter()); - handler->PSendSysMessage("Threat list of %s (%s)", target->GetName().c_str(), target->GetGUID().ToString().c_str()); + uint32 count = 0; - for (itr = threatList.begin(); itr != threatList.end(); ++itr) + auto const& threatenedByMe = target->GetThreatManager().GetThreatenedByMeList(); + if (threatenedByMe.empty()) + handler->PSendSysMessage("%s (guid %u) does not threaten any units.", target->GetName().c_str(), target->GetGUID().GetCounter()); + else { - Unit* unit = (*itr)->getTarget(); - if (!unit) + handler->PSendSysMessage("List of units threatened by %s (guid %u)", target->GetName().c_str(), target->GetGUID().GetCounter()); + for (auto const& pair : threatenedByMe) { - handler->PSendSysMessage(" %u. No Unit - threat %f", ++count, (*itr)->GetThreat()); - continue; + Unit* unit = pair.second->GetOwner(); + handler->PSendSysMessage(" %u. %s (current guid %u, DB guid %u) - threat %f", ++count, unit->GetName().c_str(), unit->GetGUID().GetCounter(), unit->GetTypeId() == TYPEID_UNIT ? unit->ToCreature()->GetSpawnId() : 0, pair.second->GetThreat()); } - - handler->PSendSysMessage(" %u. %s (%s) - threat %f", ++count, unit->GetName().c_str(), unit->GetGUID().ToString().c_str(), (*itr)->GetThreat()); + handler->SendSysMessage("End of threatened-by-me list."); } - auto const& threatList2 = target->GetThreatMgr().GetOfflineThreatList(); - for (itr = threatList2.begin(); itr != threatList2.end(); ++itr) + if (!mgr.CanHaveThreatList()) + return true; + if (mgr.IsEngaged()) { - Unit* unit = (*itr)->getTarget(); - if (!unit) + count = 0; + handler->PSendSysMessage("Threat list of %s (guid %u, DB GUID %u)", target->GetName().c_str(), target->GetGUID().GetCounter(), target->GetTypeId() == TYPEID_UNIT ? target->ToCreature()->GetSpawnId() : 0); + for (ThreatReference const* ref : mgr.GetSortedThreatList()) { - handler->PSendSysMessage(" %u. [offline] No Unit - threat %f", ++count, (*itr)->GetThreat()); - continue; + Unit* unit = ref->GetVictim(); + char const* onlineStr; + switch (ref->GetOnlineState()) + { + case ThreatReference::ONLINE_STATE_SUPPRESSED: + onlineStr = " [SUPPRESSED]"; + break; + case ThreatReference::ONLINE_STATE_OFFLINE: + onlineStr = " [OFFLINE]"; + break; + default: + onlineStr = ""; + } + char const* tauntStr; + switch (ref->GetTauntState()) + { + case ThreatReference::TAUNT_STATE_TAUNT: + tauntStr = " [TAUNT]"; + break; + case ThreatReference::TAUNT_STATE_DETAUNT: + tauntStr = " [DETAUNT]"; + break; + default: + tauntStr = ""; + } + handler->PSendSysMessage(" %u. %s (guid %u) - threat %f%s%s", ++count, unit->GetName().c_str(), unit->GetGUID().GetCounter(), ref->GetThreat(), tauntStr, onlineStr); } - - handler->PSendSysMessage(" %u. [offline] %s (%s) - threat %f", ++count, unit->GetName().c_str(), unit->GetGUID().ToString().c_str(), (*itr)->GetThreat()); + handler->SendSysMessage("End of threat list."); } - - handler->SendSysMessage("End of threat list."); + else + handler->PSendSysMessage("%s (guid %u, DB GUID %u) is not currently engaged.", target->GetName().c_str(), target->GetGUID().GetCounter(), target->GetTypeId() == TYPEID_UNIT ? target->ToCreature()->GetSpawnId() : 0); return true; } @@ -840,27 +873,18 @@ class debug_commandscript : public CommandScript if (!target) target = handler->GetSession()->GetPlayer(); - HostileReference* ref = target->getHostileRefMgr().getFirst(); - uint32 count = 0; - - handler->PSendSysMessage("Hostile reference list of %s (%s)", target->GetName().c_str(), target->GetGUID().ToString().c_str()); - - while (ref) + handler->PSendSysMessage("Combat refs: (Combat state: %d | Manager state: %d)", target->IsInCombat(), target->GetCombatManager().HasCombat()); + for (auto const& ref : target->GetCombatManager().GetPvPCombatRefs()) { - if (Unit* unit = ref->GetSource()->GetOwner()) - { - handler->PSendSysMessage(" %u. %s %s (%s) - threat %f", ++count, (ref->IsOnline() ? "" : "[offline]"), - unit->GetName().c_str(), unit->GetGUID().ToString().c_str(), ref->GetThreat()); - } - else - { - handler->PSendSysMessage(" %u. No Owner - threat %f", ++count, ref->GetThreat()); - } - - ref = ref->next(); + Unit* unit = ref.second->GetOther(target); + handler->PSendSysMessage("[PvP] %s (DBGUID %u)", unit->GetName().c_str(), unit->GetTypeId() == TYPEID_UNIT ? unit->ToCreature()->GetSpawnId() : 0); + } + for (auto const& ref : target->GetCombatManager().GetPvECombatRefs()) + { + Unit* unit = ref.second->GetOther(target); + handler->PSendSysMessage("[PvE] %s (DBGUID %u)", unit->GetName().c_str(), unit->GetTypeId() == TYPEID_UNIT ? unit->ToCreature()->GetSpawnId() : 0); } - handler->SendSysMessage("End of hostile reference list."); return true; } diff --git a/src/server/scripts/Commands/cs_misc.cpp b/src/server/scripts/Commands/cs_misc.cpp index 8dabb668d29506..b3243292f7d7e7 100644 --- a/src/server/scripts/Commands/cs_misc.cpp +++ b/src/server/scripts/Commands/cs_misc.cpp @@ -2873,7 +2873,6 @@ class misc_commandscript : public CommandScript } playerTarget->CombatStop(); - playerTarget->getHostileRefMgr().deleteReferences(); return true; } diff --git a/src/server/scripts/EasternKingdoms/BlackrockMountain/BlackrockDepths/boss_tomb_of_seven.cpp b/src/server/scripts/EasternKingdoms/BlackrockMountain/BlackrockDepths/boss_tomb_of_seven.cpp index cfecdbfdcb6d55..d31a8e6c3da96f 100644 --- a/src/server/scripts/EasternKingdoms/BlackrockMountain/BlackrockDepths/boss_tomb_of_seven.cpp +++ b/src/server/scripts/EasternKingdoms/BlackrockMountain/BlackrockDepths/boss_tomb_of_seven.cpp @@ -203,7 +203,7 @@ class boss_doomrel : public CreatureScript void EnterEvadeMode(EvadeReason /*why*/) override { me->RemoveAllAuras(); - me->GetThreatMgr().ClearAllThreat(); + me->GetThreatManager().ClearAllThreat(); me->CombatStop(true); me->LoadCreaturesAddon(true); if (me->IsAlive()) diff --git a/src/server/scripts/EasternKingdoms/BlackrockMountain/BlackrockDepths/instance_blackrock_depths.cpp b/src/server/scripts/EasternKingdoms/BlackrockMountain/BlackrockDepths/instance_blackrock_depths.cpp index 014531fa26f4d8..95c0c17082d4f7 100644 --- a/src/server/scripts/EasternKingdoms/BlackrockMountain/BlackrockDepths/instance_blackrock_depths.cpp +++ b/src/server/scripts/EasternKingdoms/BlackrockMountain/BlackrockDepths/instance_blackrock_depths.cpp @@ -782,7 +782,7 @@ class instance_blackrock_depths : public InstanceMapScript //do not call EnterEvadeMode(), it will create infinit loops boss->Respawn(); boss->RemoveAllAuras(); - boss->GetThreatMgr().ClearAllThreat(); + boss->GetThreatManager().ClearAllThreat(); boss->CombatStop(true); boss->LoadCreaturesAddon(true); boss->GetMotionMaster()->MoveTargetedHome(); diff --git a/src/server/scripts/EasternKingdoms/BlackrockMountain/BlackrockSpire/boss_drakkisath.cpp b/src/server/scripts/EasternKingdoms/BlackrockMountain/BlackrockSpire/boss_drakkisath.cpp index 02e6196276aae8..b90fec1ed309bf 100644 --- a/src/server/scripts/EasternKingdoms/BlackrockMountain/BlackrockSpire/boss_drakkisath.cpp +++ b/src/server/scripts/EasternKingdoms/BlackrockMountain/BlackrockSpire/boss_drakkisath.cpp @@ -91,8 +91,8 @@ class boss_drakkisath : public CreatureScript if (Unit* target = me->GetVictim()) { _conflagrateTarget = me->GetVictim()->GetGUID(); - _conflagrateThreat = me->GetThreatMgr().GetThreat(me->GetVictim()); - me->GetThreatMgr().ModifyThreatByPercent(target, -100); + _conflagrateThreat = me->GetThreatManager().GetThreat(me->GetVictim()); + me->GetThreatManager().ModifyThreatByPercent(target, -100); } events.ScheduleEvent(EVENT_CONFLAGRATION, 10s, 13s); events.ScheduleEvent(EVENT_CHECK_CONFLAGRATION_TARGET, 10s); @@ -112,7 +112,7 @@ class boss_drakkisath : public CreatureScript case EVENT_CHECK_CONFLAGRATION_TARGET: if (Unit* target = ObjectAccessor::GetUnit(*me, _conflagrateTarget)) { - me->GetThreatMgr().AddThreat(target, _conflagrateThreat); + me->GetThreatManager().AddThreat(target, _conflagrateThreat); } break; } diff --git a/src/server/scripts/EasternKingdoms/BlackrockMountain/BlackrockSpire/boss_mor_grayhoof.cpp b/src/server/scripts/EasternKingdoms/BlackrockMountain/BlackrockSpire/boss_mor_grayhoof.cpp index c416371cd6048f..f1384521b4ec85 100644 --- a/src/server/scripts/EasternKingdoms/BlackrockMountain/BlackrockSpire/boss_mor_grayhoof.cpp +++ b/src/server/scripts/EasternKingdoms/BlackrockMountain/BlackrockSpire/boss_mor_grayhoof.cpp @@ -122,13 +122,13 @@ struct boss_mor_grayhoof : public BossAI // Sleep can target tank, we need to drop threat temporarily on the target. _sleepTargetGUID = target->GetGUID(); - _sleepTargetThreat = me->GetThreatMgr().GetThreat(target); - me->GetThreatMgr().ModifyThreatByPercent(target, -100); + _sleepTargetThreat = me->GetThreatManager().GetThreat(target); + me->GetThreatManager().ModifyThreatByPercent(target, -100); _scheduler.Schedule(10s, [this](TaskContext /*context*/) { if (Unit* sleepTarget = ObjectAccessor::GetUnit(*me, _sleepTargetGUID)) { - me->GetThreatMgr().AddThreat(sleepTarget, _sleepTargetThreat); + me->GetThreatManager().AddThreat(sleepTarget, _sleepTargetThreat); } }); } diff --git a/src/server/scripts/EasternKingdoms/BlackrockMountain/BlackrockSpire/instance_blackrock_spire.cpp b/src/server/scripts/EasternKingdoms/BlackrockMountain/BlackrockSpire/instance_blackrock_spire.cpp index 47f66c506ff086..629b145dee9de3 100644 --- a/src/server/scripts/EasternKingdoms/BlackrockMountain/BlackrockSpire/instance_blackrock_spire.cpp +++ b/src/server/scripts/EasternKingdoms/BlackrockMountain/BlackrockSpire/instance_blackrock_spire.cpp @@ -986,14 +986,14 @@ class npc_vaelastrasz_the_red : public CreatureScript me->SetImmuneToNPC(false); if (Creature* gyth = me->FindNearestCreature(NPC_GYTH, 100.0f, true)) { - me->AddThreat(gyth, 1000000.f); + me->GetThreatManager().AddThreat(gyth, 1000000.f); me->AI()->AttackStart(gyth); } if (Creature* rend = me->FindNearestCreature(NPC_WARCHIEF_REND_BLACKHAND, 100.0f, true)) { if (!rend->IsImmuneToNPC() && rend->isTargetableForAttack()) { - me->AddThreat(rend, 100000.f); + me->GetThreatManager().AddThreat(rend, 100000.f); if (!me->FindNearestCreature(NPC_GYTH, 100.0f, true)) { me->AI()->AttackStart(rend); diff --git a/src/server/scripts/EasternKingdoms/BlackrockMountain/BlackwingLair/boss_nefarian.cpp b/src/server/scripts/EasternKingdoms/BlackrockMountain/BlackwingLair/boss_nefarian.cpp index 7f73b5b3702dd0..60fb94d7baa8c4 100644 --- a/src/server/scripts/EasternKingdoms/BlackrockMountain/BlackwingLair/boss_nefarian.cpp +++ b/src/server/scripts/EasternKingdoms/BlackrockMountain/BlackwingLair/boss_nefarian.cpp @@ -681,11 +681,11 @@ struct boss_nefarian : public BossAI case EVENT_CLASSCALL: if (classesPresent.empty()) { - for (auto& ref : me->GetThreatMgr().GetThreatList()) + for (auto& ref : me->GetThreatManager().GetSortedThreatList()) { - if (ref->getTarget() && ref->getTarget()->GetTypeId() == TYPEID_PLAYER) + if (ref->GetVictim() && ref->GetVictim()->GetTypeId() == TYPEID_PLAYER) { - classesPresent.insert(ref->getTarget()->getClass()); + classesPresent.insert(ref->GetVictim()->getClass()); } } } diff --git a/src/server/scripts/EasternKingdoms/BlackrockMountain/BlackwingLair/boss_razorgore.cpp b/src/server/scripts/EasternKingdoms/BlackrockMountain/BlackwingLair/boss_razorgore.cpp index 98f656c3c70d34..29086a1d5b5424 100644 --- a/src/server/scripts/EasternKingdoms/BlackrockMountain/BlackwingLair/boss_razorgore.cpp +++ b/src/server/scripts/EasternKingdoms/BlackrockMountain/BlackwingLair/boss_razorgore.cpp @@ -115,11 +115,11 @@ class boss_razorgore : public CreatureScript return false; } - if (me->GetThreatMgr().GetThreatListSize() > 1) + if (me->GetThreatManager().GetThreatListSize() > 1) { - ThreatContainer::StorageType::const_iterator lastRef = me->GetThreatMgr().GetOnlineContainer().GetThreatList().end(); + auto lastRef = me->GetThreatManager().GetUnsortedThreatList().end(); --lastRef; - if (Unit* lastTarget = (*lastRef)->getTarget()) + if (Unit* lastTarget = (*lastRef)->GetVictim()) { if (lastTarget != target) { @@ -190,7 +190,6 @@ class boss_razorgore : public CreatureScript if (Unit* charmer = ObjectAccessor::GetUnit(*me, _charmerGUID)) { charmer->RemoveAurasDueToSpell(SPELL_MINDCONTROL_VISUAL); - me->TauntApply(charmer); } } } diff --git a/src/server/scripts/EasternKingdoms/BlackrockMountain/MoltenCore/boss_majordomo_executus.cpp b/src/server/scripts/EasternKingdoms/BlackrockMountain/MoltenCore/boss_majordomo_executus.cpp index 3c12600f314c85..7ffe13d112f235 100644 --- a/src/server/scripts/EasternKingdoms/BlackrockMountain/MoltenCore/boss_majordomo_executus.cpp +++ b/src/server/scripts/EasternKingdoms/BlackrockMountain/MoltenCore/boss_majordomo_executus.cpp @@ -601,7 +601,7 @@ class spell_hate_to_zero : public SpellScriptLoader { if (Creature* creatureCaster = caster->ToCreature()) { - creatureCaster->GetThreatMgr().ResetAllThreat(); + creatureCaster->GetThreatManager().ResetAllThreat(); } } } diff --git a/src/server/scripts/EasternKingdoms/BlackrockMountain/MoltenCore/boss_ragnaros.cpp b/src/server/scripts/EasternKingdoms/BlackrockMountain/MoltenCore/boss_ragnaros.cpp index 95049716c4fa0b..c34b41d3c877a4 100644 --- a/src/server/scripts/EasternKingdoms/BlackrockMountain/MoltenCore/boss_ragnaros.cpp +++ b/src/server/scripts/EasternKingdoms/BlackrockMountain/MoltenCore/boss_ragnaros.cpp @@ -237,7 +237,7 @@ class boss_ragnaros : public CreatureScript void EnterEvadeMode(EvadeReason why) override { - if (!me->GetThreatMgr().GetThreatList().empty()) + if (!me->GetThreatManager().GetThreatListSize() > 0) { if (!_processingMagmaBurst) { diff --git a/src/server/scripts/EasternKingdoms/BlackrockMountain/MoltenCore/boss_shazzrah.cpp b/src/server/scripts/EasternKingdoms/BlackrockMountain/MoltenCore/boss_shazzrah.cpp index 6ef919b528e23c..e5491a0a09a375 100644 --- a/src/server/scripts/EasternKingdoms/BlackrockMountain/MoltenCore/boss_shazzrah.cpp +++ b/src/server/scripts/EasternKingdoms/BlackrockMountain/MoltenCore/boss_shazzrah.cpp @@ -171,8 +171,8 @@ class spell_shazzrah_gate_dummy : public SpellScriptLoader if (Creature* creatureCaster = caster->ToCreature()) { - creatureCaster->GetThreatMgr().ResetAllThreat(); - creatureCaster->GetThreatMgr().AddThreat(target, 1); + creatureCaster->GetThreatManager().ResetAllThreat(); + creatureCaster->GetThreatManager().AddThreat(target, 1); creatureCaster->AI()->AttackStart(target); // Attack the target which caster will teleport to. } } diff --git a/src/server/scripts/EasternKingdoms/Karazhan/boss_curator.cpp b/src/server/scripts/EasternKingdoms/Karazhan/boss_curator.cpp index 59dfb299052bfd..a58e91b7bff4e8 100644 --- a/src/server/scripts/EasternKingdoms/Karazhan/boss_curator.cpp +++ b/src/server/scripts/EasternKingdoms/Karazhan/boss_curator.cpp @@ -134,7 +134,7 @@ struct boss_curator : public BossAI if (Unit* target = summon->SelectNearbyTarget(nullptr, 40.0f)) { summon->AI()->AttackStart(target); - summon->AddThreat(target, 1000.0f); + summon->GetThreatManager().AddThreat(target, 1000.0f); } summon->SetInCombatWithZone(); } diff --git a/src/server/scripts/EasternKingdoms/Karazhan/boss_midnight.cpp b/src/server/scripts/EasternKingdoms/Karazhan/boss_midnight.cpp index d2d361fe024779..5b3f927b510b73 100644 --- a/src/server/scripts/EasternKingdoms/Karazhan/boss_midnight.cpp +++ b/src/server/scripts/EasternKingdoms/Karazhan/boss_midnight.cpp @@ -179,7 +179,7 @@ struct boss_attumen : public BossAI { Unit* target = nullptr; std::vector target_list; - for (auto* ref : me->GetThreatMgr().GetUnsortedThreatList()) + for (auto* ref : me->GetThreatManager().GetUnsortedThreatList()) { target = ref->GetVictim(); if (target && !target->IsWithinDist(me, 8.00f, false) && target->IsWithinDist(me, 25.0f, false)) @@ -381,39 +381,9 @@ struct boss_midnight : public BossAI uint8 _phase; }; -class spell_midnight_fixate : public AuraScript -{ - PrepareAuraScript(spell_midnight_fixate) - - void HandleEffectApply(AuraEffect const* /*aurEff*/, AuraEffectHandleModes /*mode*/) - { - Unit* target = GetTarget(); - if (Unit* caster = GetCaster()) - { - caster->TauntApply(target); - } - } - - void HandleEffectRemove(AuraEffect const* /*aurEff*/, AuraEffectHandleModes /*mode*/) - { - Unit* target = GetTarget(); - if (Unit* caster = GetCaster()) - { - caster->TauntFadeOut(target); - } - } - - void Register() override - { - OnEffectApply += AuraEffectApplyFn(spell_midnight_fixate::HandleEffectApply, EFFECT_0, SPELL_AURA_DUMMY, AURA_EFFECT_HANDLE_REAL); - OnEffectRemove += AuraEffectRemoveFn(spell_midnight_fixate::HandleEffectRemove, EFFECT_0, SPELL_AURA_DUMMY, AURA_EFFECT_HANDLE_REAL); - } -}; - void AddSC_boss_attumen() { RegisterKarazhanCreatureAI(boss_midnight); RegisterKarazhanCreatureAI(boss_attumen); - RegisterSpellScript(spell_midnight_fixate); } diff --git a/src/server/scripts/EasternKingdoms/Karazhan/boss_netherspite.cpp b/src/server/scripts/EasternKingdoms/Karazhan/boss_netherspite.cpp index 77c0f379b62acf..9c2ecd1d7273e8 100644 --- a/src/server/scripts/EasternKingdoms/Karazhan/boss_netherspite.cpp +++ b/src/server/scripts/EasternKingdoms/Karazhan/boss_netherspite.cpp @@ -181,7 +181,7 @@ struct boss_netherspite : public BossAI // aggro target if Red Beam if (j == RED_PORTAL && me->GetVictim() != target && target->GetTypeId() == TYPEID_PLAYER) { - me->GetThreatMgr().AddThreat(target, 100000.0f + DoGetThreat(me->GetVictim())); + me->GetThreatManager().AddThreat(target, 100000.0f + DoGetThreat(me->GetVictim())); } } } diff --git a/src/server/scripts/EasternKingdoms/Karazhan/boss_nightbane.cpp b/src/server/scripts/EasternKingdoms/Karazhan/boss_nightbane.cpp index e53e28c3239198..13732b4c357869 100644 --- a/src/server/scripts/EasternKingdoms/Karazhan/boss_nightbane.cpp +++ b/src/server/scripts/EasternKingdoms/Karazhan/boss_nightbane.cpp @@ -249,10 +249,10 @@ struct boss_nightbane : public BossAI void DoCastOnFarAwayPlayers(uint32 spellid, bool triggered, float tresholddistance) { //resembles DoCastToAllHostilePlayers a bit/lot - ThreatContainer::StorageType targets = me->GetThreatMgr().GetThreatList(); - for (ThreatContainer::StorageType::const_iterator itr = targets.begin(); itr != targets.end(); ++itr) + auto tList = me->GetThreatManager().GetUnsortedThreatList(); + for (auto t : tList) { - if (Unit* unit = ObjectAccessor::GetUnit(*me, (*itr)->getUnitGuid())) + if (Unit* unit = ObjectAccessor::GetUnit(*me, t->GetVictim()->GetGUID())) { if (unit->IsPlayer() && !unit->IsWithinDist(me, tresholddistance, false)) { diff --git a/src/server/scripts/EasternKingdoms/Karazhan/boss_prince_malchezaar.cpp b/src/server/scripts/EasternKingdoms/Karazhan/boss_prince_malchezaar.cpp index 10d03fa8324f95..a5f11cd8d4b3a6 100644 --- a/src/server/scripts/EasternKingdoms/Karazhan/boss_prince_malchezaar.cpp +++ b/src/server/scripts/EasternKingdoms/Karazhan/boss_prince_malchezaar.cpp @@ -340,7 +340,7 @@ struct npc_malchezaar_axe : public ScriptedAI DoModifyThreatByPercent(me->GetVictim(), -100); } - me->AddThreat(target, 1000000.0f); + me->GetThreatManager().AddThreat(target, 1000000.0f); } context.Repeat(7500ms, 20s); diff --git a/src/server/scripts/EasternKingdoms/Karazhan/boss_shade_of_aran.cpp b/src/server/scripts/EasternKingdoms/Karazhan/boss_shade_of_aran.cpp index c63797afaeb704..d396afd6a77eef 100644 --- a/src/server/scripts/EasternKingdoms/Karazhan/boss_shade_of_aran.cpp +++ b/src/server/scripts/EasternKingdoms/Karazhan/boss_shade_of_aran.cpp @@ -162,7 +162,7 @@ struct boss_shade_of_aran : public BossAI if (me->Attack(who, false)) { me->GetMotionMaster()->MoveChase(who, 45.0f, 0); - me->AddThreat(who, 0.0f); + me->GetThreatManager().AddThreat(who, 0.0f); } } } diff --git a/src/server/scripts/EasternKingdoms/Karazhan/bosses_opera.cpp b/src/server/scripts/EasternKingdoms/Karazhan/bosses_opera.cpp index 42af93dd6f0343..2f429bda9bd115 100644 --- a/src/server/scripts/EasternKingdoms/Karazhan/bosses_opera.cpp +++ b/src/server/scripts/EasternKingdoms/Karazhan/bosses_opera.cpp @@ -1203,7 +1203,7 @@ struct boss_romulo : public ScriptedAI { if (Julianne->GetVictim()) { - me->AddThreat(Julianne->GetVictim(), 1.0f); + me->GetThreatManager().AddThreat(Julianne->GetVictim(), 1.0f); AttackStart(Julianne->GetVictim()); } } diff --git a/src/server/scripts/EasternKingdoms/Karazhan/instance_karazhan.cpp b/src/server/scripts/EasternKingdoms/Karazhan/instance_karazhan.cpp index 447c6a3750b18c..72fe86562c2329 100644 --- a/src/server/scripts/EasternKingdoms/Karazhan/instance_karazhan.cpp +++ b/src/server/scripts/EasternKingdoms/Karazhan/instance_karazhan.cpp @@ -603,7 +603,7 @@ class spell_karazhan_blink : public SpellScriptLoader void HandleDummy(SpellEffIndex effIndex) { PreventHitDefaultEffect(effIndex); - GetCaster()->GetThreatMgr().ResetAllThreat(); + GetCaster()->GetThreatManager().ResetAllThreat(); if (Unit* target = GetHitUnit()) GetCaster()->CastSpell(target, SPELL_BLINK, true); } diff --git a/src/server/scripts/EasternKingdoms/MagistersTerrace/boss_priestess_delrissa.cpp b/src/server/scripts/EasternKingdoms/MagistersTerrace/boss_priestess_delrissa.cpp index 2fa74afae42f77..6d6fe01ee0ec44 100644 --- a/src/server/scripts/EasternKingdoms/MagistersTerrace/boss_priestess_delrissa.cpp +++ b/src/server/scripts/EasternKingdoms/MagistersTerrace/boss_priestess_delrissa.cpp @@ -288,16 +288,16 @@ struct boss_priestess_lackey_commonAI : public ScriptedAI void RecalculateThreat() { - ThreatContainer::StorageType const& tList = me->GetThreatMgr().GetThreatList(); - for (ThreatContainer::StorageType::const_iterator itr = tList.begin(); itr != tList.end(); ++itr) + + auto tList = me->GetThreatManager().GetUnsortedThreatList(); + for (auto t : tList) { - Unit* pUnit = ObjectAccessor::GetUnit(*me, (*itr)->getUnitGuid()); - if (pUnit && pUnit->GetTypeId() == TYPEID_PLAYER && me->GetThreatMgr().GetThreat(pUnit)) + Unit* pUnit = ObjectAccessor::GetUnit(*me, t->GetVictim()->GetGUID()); + if (pUnit && pUnit->GetTypeId() == TYPEID_PLAYER && me->GetThreatManager().GetThreat(pUnit)) { float threatMod = GetThreatMod(me->GetDistance2d(pUnit), (float)pUnit->GetArmor(), pUnit->GetHealth(), pUnit->GetMaxHealth(), pUnit); - me->GetThreatMgr().ModifyThreatByPercent(pUnit, -100); - if (HostileReference* ref = me->GetThreatMgr().GetOnlineContainer().getReferenceByTarget(pUnit)) - ref->AddThreat(10000000.0f * threatMod); + me->GetThreatManager().ModifyThreatByPercent(pUnit, -100); + me->GetThreatManager().AddThreat(pUnit, 10000000.0f * threatMod); } } } @@ -443,7 +443,7 @@ struct boss_kagani_nightstrike : public boss_priestess_lackey_commonAI me->CastSpell(me, SPELL_VANISH, false); DoResetThreatList(); if (Unit* unit = SelectTarget(SelectTargetMethod::Random, 0)) - me->AddThreat(unit, 1000.0f); + me->GetThreatManager().AddThreat(unit, 1000.0f); events.ScheduleEvent(EVENT_SPELL_VANISH, 30000); break; @@ -687,9 +687,9 @@ struct boss_yazzai : public boss_priestess_lackey_commonAI case EVENT_SPELL_BLINK: { bool InMeleeRange = false; - ThreatContainer::StorageType const& t_list = me->GetThreatMgr().GetThreatList(); - for (ThreatContainer::StorageType::const_iterator itr = t_list.begin(); itr != t_list.end(); ++itr) - if (Unit* target = ObjectAccessor::GetUnit(*me, (*itr)->getUnitGuid())) + auto tList = me->GetThreatManager().GetUnsortedThreatList(); + for (auto t : tList) + if (Unit* target = ObjectAccessor::GetUnit(*me, t->GetVictim()->GetGUID())) if (target->IsWithinMeleeRange(me)) { InMeleeRange = true; diff --git a/src/server/scripts/EasternKingdoms/ScarletEnclave/chapter2.cpp b/src/server/scripts/EasternKingdoms/ScarletEnclave/chapter2.cpp index 8e936fddccabcc..bc92f921f98c18 100644 --- a/src/server/scripts/EasternKingdoms/ScarletEnclave/chapter2.cpp +++ b/src/server/scripts/EasternKingdoms/ScarletEnclave/chapter2.cpp @@ -242,7 +242,7 @@ class npc_koltira_deathweaver : public CreatureScript void EnterEvadeMode(EvadeReason /*why*/) override { - me->GetThreatMgr().ClearAllThreat(); + me->GetThreatManager().ClearAllThreat(); me->CombatStop(false); me->SetLootRecipient(nullptr); @@ -312,7 +312,7 @@ class npc_koltira_deathweaver : public CreatureScript if (summoned->GetEntry() == NPC_HIGH_INQUISITOR_VALROTH) m_uiValrothGUID = summoned->GetGUID(); - summoned->AddThreat(me, 0.0f); + summoned->GetThreatManager().AddThreat(me, 0.0f); summoned->SetImmuneToPC(false); summons.Summon(summoned); } diff --git a/src/server/scripts/EasternKingdoms/ScarletEnclave/chapter5.cpp b/src/server/scripts/EasternKingdoms/ScarletEnclave/chapter5.cpp index e324dd7f85df2f..3b6620d9fef0e6 100644 --- a/src/server/scripts/EasternKingdoms/ScarletEnclave/chapter5.cpp +++ b/src/server/scripts/EasternKingdoms/ScarletEnclave/chapter5.cpp @@ -698,13 +698,13 @@ class npc_highlord_darion_mograine : public CreatureScript if (Creature* summon = ObjectAccessor::GetCreature(*me, *itr)) { summon->CombatStop(true); - summon->GetThreatMgr().ClearAllThreat(); + summon->GetThreatManager().ClearAllThreat(); summon->SetImmuneToAll(true); summon->SetReactState(REACT_PASSIVE); summon->GetMotionMaster()->Clear(false); } me->CombatStop(true); - me->GetThreatMgr().ClearAllThreat(); + me->GetThreatManager().ClearAllThreat(); me->SetImmuneToAll(true); me->SetReactState(REACT_PASSIVE); me->GetMotionMaster()->Clear(false); diff --git a/src/server/scripts/EasternKingdoms/Scholomance/boss_darkmaster_gandling.cpp b/src/server/scripts/EasternKingdoms/Scholomance/boss_darkmaster_gandling.cpp index 87a22ba7c12401..cad7b5e9d5937f 100644 --- a/src/server/scripts/EasternKingdoms/Scholomance/boss_darkmaster_gandling.cpp +++ b/src/server/scripts/EasternKingdoms/Scholomance/boss_darkmaster_gandling.cpp @@ -306,10 +306,10 @@ class boss_darkmaster_gandling : public CreatureScript auto victim = me->GetVictim(); if (victim && (target->GetGUID() == victim->GetGUID())) { - me->AddThreat(victim, -1000000); // drop current player, add a ton to second. This should guarantee that we don't end up with both 1 and 2 in a cage... + me->GetThreatManager().AddThreat(victim, -1000000); // drop current player, add a ton to second. This should guarantee that we don't end up with both 1 and 2 in a cage... if (Unit* newTarget = SelectTarget(SelectTargetMethod::MaxThreat, 1, 200.0f)) // search in whole room { - me->AddThreat(newTarget, 1000000); + me->GetThreatManager().AddThreat(newTarget, 1000000); } } } diff --git a/src/server/scripts/EasternKingdoms/Scholomance/instance_scholomance.cpp b/src/server/scripts/EasternKingdoms/Scholomance/instance_scholomance.cpp index d13d6bccba975d..de3dc2ea83064a 100644 --- a/src/server/scripts/EasternKingdoms/Scholomance/instance_scholomance.cpp +++ b/src/server/scripts/EasternKingdoms/Scholomance/instance_scholomance.cpp @@ -246,42 +246,6 @@ class instance_scholomance : public InstanceMapScript }; }; -class spell_scholomance_fixate : public SpellScriptLoader -{ -public: - spell_scholomance_fixate() : SpellScriptLoader("spell_scholomance_fixate") { } - - class spell_scholomance_fixate_AuraScript : public AuraScript - { - PrepareAuraScript(spell_scholomance_fixate_AuraScript); - - void HandleEffectApply(AuraEffect const* /*aurEff*/, AuraEffectHandleModes /*mode*/) - { - Unit* target = GetTarget(); - if (Unit* caster = GetCaster()) - caster->TauntApply(target); - } - - void HandleEffectRemove(AuraEffect const* /*aurEff*/, AuraEffectHandleModes /*mode*/) - { - Unit* target = GetTarget(); - if (Unit* caster = GetCaster()) - caster->TauntFadeOut(target); - } - - void Register() override - { - OnEffectApply += AuraEffectApplyFn(spell_scholomance_fixate_AuraScript::HandleEffectApply, EFFECT_0, SPELL_AURA_DUMMY, AURA_EFFECT_HANDLE_REAL); - OnEffectRemove += AuraEffectRemoveFn(spell_scholomance_fixate_AuraScript::HandleEffectRemove, EFFECT_0, SPELL_AURA_DUMMY, AURA_EFFECT_HANDLE_REAL); - } - }; - - AuraScript* GetAuraScript() const override - { - return new spell_scholomance_fixate_AuraScript(); - } -}; - class spell_scholomance_boon_of_life : public SpellScriptLoader { public: @@ -298,7 +262,7 @@ class spell_scholomance_boon_of_life : public SpellScriptLoader if (Creature* creature = target->ToCreature()) { creature->AI()->AttackStart(caster); - creature->AddThreat(caster, 10000.0f); + creature->GetThreatManager().AddThreat(caster, 10000.0f); } } @@ -361,10 +325,10 @@ class npc_scholomance_occultist : public CreatureScript Unit* SelectUnitCasting() { - ThreatContainer::StorageType threatlist = me->GetThreatMgr().GetThreatList(); - for (ThreatContainer::StorageType::const_iterator itr = threatlist.begin(); itr != threatlist.end(); ++itr) + auto tList = me->GetThreatManager().GetUnsortedThreatList(); + for (auto t : tList) { - if (Unit* unit = ObjectAccessor::GetUnit(*me, (*itr)->getUnitGuid())) + if (Unit* unit = ObjectAccessor::GetUnit(*me, t->GetVictim()->GetGUID())) { if (unit->HasUnitState(UNIT_STATE_CASTING)) { @@ -460,7 +424,6 @@ class npc_scholomance_occultist : public CreatureScript void AddSC_instance_scholomance() { new instance_scholomance(); - new spell_scholomance_fixate(); new spell_scholomance_boon_of_life(); new npc_scholomance_occultist(); } diff --git a/src/server/scripts/EasternKingdoms/SunwellPlateau/boss_eredar_twins.cpp b/src/server/scripts/EasternKingdoms/SunwellPlateau/boss_eredar_twins.cpp index 63e0f862b3cd51..6665403369128f 100644 --- a/src/server/scripts/EasternKingdoms/SunwellPlateau/boss_eredar_twins.cpp +++ b/src/server/scripts/EasternKingdoms/SunwellPlateau/boss_eredar_twins.cpp @@ -165,7 +165,7 @@ class boss_sacrolash : public CreatureScript if (Unit* target = SelectTarget(SelectTargetMethod::Random, 0, 50.0f, true)) { summon->AI()->AttackStart(target); - summon->AddThreat(target, 10000000); + summon->GetThreatManager().AddThreat(target, 10000000); } } diff --git a/src/server/scripts/EasternKingdoms/SunwellPlateau/boss_kiljaeden.cpp b/src/server/scripts/EasternKingdoms/SunwellPlateau/boss_kiljaeden.cpp index 954ff836dfa9bb..c8ad81e997077e 100644 --- a/src/server/scripts/EasternKingdoms/SunwellPlateau/boss_kiljaeden.cpp +++ b/src/server/scripts/EasternKingdoms/SunwellPlateau/boss_kiljaeden.cpp @@ -330,7 +330,7 @@ class boss_kiljaeden : public CreatureScript me->SetTarget(); me->SetReactState(REACT_PASSIVE); me->RemoveAllAuras(); - me->GetThreatMgr().ClearAllThreat(); + me->GetThreatManager().ClearAllThreat(); me->SetRegeneratingHealth(false); me->SetUnitFlag(UNIT_FLAG_NON_ATTACKABLE); me->HandleEmoteCommand(EMOTE_ONESHOT_DROWN); diff --git a/src/server/scripts/EasternKingdoms/ZulAman/boss_akilzon.cpp b/src/server/scripts/EasternKingdoms/ZulAman/boss_akilzon.cpp index 25f92a7e84d79f..9e500c5fefdb5b 100644 --- a/src/server/scripts/EasternKingdoms/ZulAman/boss_akilzon.cpp +++ b/src/server/scripts/EasternKingdoms/ZulAman/boss_akilzon.cpp @@ -335,7 +335,7 @@ class boss_akilzon : public CreatureScript Creature* creature = me->SummonCreature(NPC_SOARING_EAGLE, x, y, z, 0, TEMPSUMMON_CORPSE_DESPAWN, 0); if (creature) { - creature->AddThreat(me->GetVictim(), 1.0f); + creature->GetThreatManager().AddThreat(me->GetVictim(), 1.0f); creature->AI()->AttackStart(me->GetVictim()); BirdGUIDs[i] = creature->GetGUID(); } diff --git a/src/server/scripts/EasternKingdoms/ZulAman/boss_hexlord.cpp b/src/server/scripts/EasternKingdoms/ZulAman/boss_hexlord.cpp index 8d357de1ebff97..369c27ad5a24b6 100644 --- a/src/server/scripts/EasternKingdoms/ZulAman/boss_hexlord.cpp +++ b/src/server/scripts/EasternKingdoms/ZulAman/boss_hexlord.cpp @@ -602,7 +602,7 @@ class boss_alyson_antille : public CreatureScript if (me->Attack(who, false)) { me->GetMotionMaster()->MoveChase(who, 20); - me->AddThreat(who, 0.0f); + me->GetThreatManager().AddThreat(who, 0.0f); } } } @@ -690,7 +690,7 @@ struct boss_gazakrothAI : public boss_hexlord_addAI if (me->Attack(who, false)) { me->GetMotionMaster()->MoveChase(who, 20); - me->AddThreat(who, 0.0f); + me->GetThreatManager().AddThreat(who, 0.0f); } } } @@ -833,7 +833,7 @@ class boss_slither : public CreatureScript if (me->Attack(who, false)) { me->GetMotionMaster()->MoveChase(who, 20); - me->AddThreat(who, 0.0f); + me->GetThreatManager().AddThreat(who, 0.0f); } } } diff --git a/src/server/scripts/EasternKingdoms/ZulGurub/boss_gahzranka.cpp b/src/server/scripts/EasternKingdoms/ZulGurub/boss_gahzranka.cpp index 2da3f6f0bbfc95..daf7a3eeb4fb81 100644 --- a/src/server/scripts/EasternKingdoms/ZulGurub/boss_gahzranka.cpp +++ b/src/server/scripts/EasternKingdoms/ZulGurub/boss_gahzranka.cpp @@ -130,7 +130,7 @@ class spell_gahzranka_slam : public SpellScript { if (Unit* caster = GetCaster()) { - _wipeThreat = targets.size() < caster->GetThreatMgr().GetThreatListSize(); + _wipeThreat = targets.size() < caster->GetThreatManager().GetThreatListSize(); } } @@ -142,7 +142,7 @@ class spell_gahzranka_slam : public SpellScript { if (Unit* target = GetHitUnit()) { - caster->GetThreatMgr().ModifyThreatByPercent(target, -100); + caster->GetThreatManager().ModifyThreatByPercent(target, -100); } } } diff --git a/src/server/scripts/EasternKingdoms/ZulGurub/boss_grilek.cpp b/src/server/scripts/EasternKingdoms/ZulGurub/boss_grilek.cpp index 782730498d9e6b..c1dc4448339916 100644 --- a/src/server/scripts/EasternKingdoms/ZulGurub/boss_grilek.cpp +++ b/src/server/scripts/EasternKingdoms/ZulGurub/boss_grilek.cpp @@ -104,14 +104,14 @@ class boss_grilek : public CreatureScript // grilek me->SetReactState(REACT_AGGRESSIVE); if (Unit* pursuitTarget = ObjectAccessor::GetUnit(*me, _pursuitTargetGUID)) { - me->GetThreatMgr().AddThreat(pursuitTarget, 1000000.f); + me->GetThreatManager().AddThreat(pursuitTarget, 1000000.f); } break; case EVENT_STOP_PURSUIT: if (Unit* pursuitTarget = ObjectAccessor::GetUnit(*me, _pursuitTargetGUID)) { _pursuitTargetGUID.Clear(); - me->GetThreatMgr().AddThreat(pursuitTarget, -1000000.f); + me->GetThreatManager().AddThreat(pursuitTarget, -1000000.f); } break; case EVENT_ENTANGLING_ROOTS: diff --git a/src/server/scripts/EasternKingdoms/ZulGurub/boss_hakkar.cpp b/src/server/scripts/EasternKingdoms/ZulGurub/boss_hakkar.cpp index 466c8e14371f66..838ca299e79d05 100644 --- a/src/server/scripts/EasternKingdoms/ZulGurub/boss_hakkar.cpp +++ b/src/server/scripts/EasternKingdoms/ZulGurub/boss_hakkar.cpp @@ -163,7 +163,7 @@ class boss_hakkar : public CreatureScript events.ScheduleEvent(EVENT_CORRUPTED_BLOOD, 30s, 40s); break; case EVENT_CAUSE_INSANITY: - if (me->GetThreatMgr().GetThreatListSize() > 1) + if (me->GetThreatManager().GetThreatListSize() > 1) { if (Unit* victim = SelectTarget(SelectTargetMethod::MaxThreat, 0, 30.f, true)) { @@ -189,7 +189,7 @@ class boss_hakkar : public CreatureScript if (Unit* victim = SelectTarget(SelectTargetMethod::MaxThreat, 0, 5.f, true)) { DoCast(victim, SPELL_ASPECT_OF_MARLI, true); - me->GetThreatMgr().ModifyThreatByPercent(victim, -100.f); + me->GetThreatManager().ModifyThreatByPercent(victim, -100.f); } events.ScheduleEvent(EVENT_ASPECT_OF_MARLI, 45s); break; @@ -201,7 +201,7 @@ class boss_hakkar : public CreatureScript if (Unit* victim = SelectTarget(SelectTargetMethod::MaxThreat, 0, 5.f, true)) { DoCast(victim, SPELL_ASPECT_OF_ARLOKK, true); - me->GetThreatMgr().ModifyThreatByPercent(victim, -100.f); + me->GetThreatManager().ModifyThreatByPercent(victim, -100.f); } events.ScheduleEvent(EVENT_ASPECT_OF_ARLOKK, 10s, 15s); break; diff --git a/src/server/scripts/EasternKingdoms/ZulGurub/boss_hazzarah.cpp b/src/server/scripts/EasternKingdoms/ZulGurub/boss_hazzarah.cpp index 280012a9173a60..5d2c560c2b5b48 100644 --- a/src/server/scripts/EasternKingdoms/ZulGurub/boss_hazzarah.cpp +++ b/src/server/scripts/EasternKingdoms/ZulGurub/boss_hazzarah.cpp @@ -79,24 +79,6 @@ struct boss_hazzarah : public BossAI events.ScheduleEvent(EVENT_ILLUSIONS, 16s, 24s); } - bool CanAIAttack(Unit const* target) const override - { - if (me->GetThreatMgr().GetThreatListSize() > 1) - { - ThreatContainer::StorageType::const_iterator lastRef = me->GetThreatMgr().GetOnlineContainer().GetThreatList().end(); - --lastRef; - if (Unit* lastTarget = (*lastRef)->getTarget()) - { - if (lastTarget != target) - { - return !target->HasAura(SPELL_SLEEP); - } - } - } - - return true; - } - void UpdateAI(uint32 diff) override { if (!UpdateVictim()) diff --git a/src/server/scripts/EasternKingdoms/ZulGurub/boss_jeklik.cpp b/src/server/scripts/EasternKingdoms/ZulGurub/boss_jeklik.cpp index 09fb2de49057c9..924675451b4cf0 100644 --- a/src/server/scripts/EasternKingdoms/ZulGurub/boss_jeklik.cpp +++ b/src/server/scripts/EasternKingdoms/ZulGurub/boss_jeklik.cpp @@ -229,7 +229,7 @@ struct boss_jeklik : public BossAI context.Repeat(25s); }).Schedule(10s, PHASE_TWO, [this](TaskContext context) { - if (me->GetThreatMgr().GetThreatListSize()) + if (me->GetThreatManager().GetThreatListSize()) { // summon up to 2 bat riders if (batRidersCount < 2) diff --git a/src/server/scripts/EasternKingdoms/ZulGurub/boss_jindo.cpp b/src/server/scripts/EasternKingdoms/ZulGurub/boss_jindo.cpp index c9b6f89b4f230c..7641e29d393bf8 100644 --- a/src/server/scripts/EasternKingdoms/ZulGurub/boss_jindo.cpp +++ b/src/server/scripts/EasternKingdoms/ZulGurub/boss_jindo.cpp @@ -82,7 +82,7 @@ struct boss_jindo : public BossAI { case NPC_BRAIN_WASH_TOTEM: summon->SetReactState(REACT_PASSIVE); - if (Unit* target = SelectTarget(SelectTargetMethod::Random, me->GetThreatMgr().GetThreatListSize() > 1 ? 1 : 0)) + if (Unit* target = SelectTarget(SelectTargetMethod::Random, me->GetThreatManager().GetThreatListSize() > 1 ? 1 : 0)) { summon->CastSpell(target, summon->m_spells[0], true); } @@ -133,7 +133,7 @@ struct boss_jindo : public BossAI events.ScheduleEvent(EVENT_POWERFULL_HEALING_WARD, 14s, 20s); break; case EVENT_HEX: - if (me->GetThreatMgr().GetThreatListSize() > 1) + if (me->GetThreatManager().GetThreatListSize() > 1) DoCastVictim(SPELL_HEX, true); events.ScheduleEvent(EVENT_HEX, 12s, 20s); break; @@ -155,11 +155,10 @@ struct boss_jindo : public BossAI bool CanAIAttack(Unit const* target) const override { - if (me->GetThreatMgr().GetThreatListSize() > 1) + if (me->GetThreatManager().GetThreatListSize() > 1) { - ThreatContainer::StorageType::const_iterator lastRef = me->GetThreatMgr().GetOnlineContainer().GetThreatList().end(); - --lastRef; - if (Unit* lastTarget = (*lastRef)->getTarget()) + auto lastRef = me->GetThreatManager().GetCurrentVictim(); + if (Unit* lastTarget = lastRef) { if (lastTarget != target) { diff --git a/src/server/scripts/EasternKingdoms/ZulGurub/boss_mandokir.cpp b/src/server/scripts/EasternKingdoms/ZulGurub/boss_mandokir.cpp index 4b5049da482d8a..cc973a9a947927 100644 --- a/src/server/scripts/EasternKingdoms/ZulGurub/boss_mandokir.cpp +++ b/src/server/scripts/EasternKingdoms/ZulGurub/boss_mandokir.cpp @@ -458,7 +458,7 @@ class boss_mandokir : public CreatureScript events.DelayEvents(1500ms); if (Unit* mainTarget = SelectTarget(SelectTargetMethod::MaxThreat, 0, 100.0f)) { - me->GetThreatMgr().ModifyThreatByPercent(mainTarget, -100); + me->GetThreatManager().ModifyThreatByPercent(mainTarget, -100); } } events.ScheduleEvent(EVENT_CHARGE_PLAYER, 30s, 40s); @@ -470,10 +470,10 @@ class boss_mandokir : public CreatureScript case EVENT_CLEAVE: { std::list meleeRangeTargets; - auto i = me->GetThreatMgr().GetThreatList().begin(); - for (; i != me->GetThreatMgr().GetThreatList().end(); ++i) + auto i = me->GetThreatManager().GetUnsortedThreatList().begin(); + for (; i != me->GetThreatManager().GetUnsortedThreatList().end(); ++i) { - Unit* target = (*i)->getTarget(); + Unit* target = (*i)->GetVictim(); if (me->IsWithinMeleeRange(target)) { meleeRangeTargets.push_back(target); diff --git a/src/server/scripts/EasternKingdoms/ZulGurub/boss_marli.cpp b/src/server/scripts/EasternKingdoms/ZulGurub/boss_marli.cpp index 0b1368092b1b0f..8fd0bed5f78e69 100644 --- a/src/server/scripts/EasternKingdoms/ZulGurub/boss_marli.cpp +++ b/src/server/scripts/EasternKingdoms/ZulGurub/boss_marli.cpp @@ -301,7 +301,7 @@ class spell_enveloping_webs : public SpellScript Unit* hitUnit = GetHitUnit(); if (caster && hitUnit && hitUnit->GetTypeId() == TYPEID_PLAYER) { - caster->GetThreatMgr().ModifyThreatByPercent(hitUnit, -100); + caster->GetThreatManager().ModifyThreatByPercent(hitUnit, -100); } } diff --git a/src/server/scripts/EasternKingdoms/ZulGurub/boss_renataki.cpp b/src/server/scripts/EasternKingdoms/ZulGurub/boss_renataki.cpp index 6445a79fd2ca8d..e2e784c0512a68 100644 --- a/src/server/scripts/EasternKingdoms/ZulGurub/boss_renataki.cpp +++ b/src/server/scripts/EasternKingdoms/ZulGurub/boss_renataki.cpp @@ -85,11 +85,10 @@ class boss_renataki : public CreatureScript bool CanAIAttack(Unit const* target) const override { - if (me->GetThreatMgr().GetThreatListSize() > 1) + if (me->GetThreatManager().GetThreatListSize() > 1) { - ThreatContainer::StorageType::const_iterator lastRef = me->GetThreatMgr().GetOnlineContainer().GetThreatList().end(); - --lastRef; - if (Unit* lastTarget = (*lastRef)->getTarget()) + auto lastRef = me->GetThreatManager().GetSortedThreatList().begin(); + if (Unit* lastTarget = (*lastRef)->GetVictim()) { if (lastTarget != target) { @@ -168,10 +167,10 @@ class boss_renataki : public CreatureScript if (_thousandBladesTargets.empty()) { std::vector targetList; - ThreatContainer::StorageType const& threatlist = me->GetThreatMgr().GetThreatList(); - for (ThreatContainer::StorageType::const_iterator itr = threatlist.begin(); itr != threatlist.end(); ++itr) + auto tList = me->GetThreatManager().GetUnsortedThreatList(); + for (auto t : tList) { - if (Unit* target = (*itr)->getTarget()) + if (Unit* target = t->GetVictim()) { if (target->IsAlive() && target->IsWithinDist2d(me, 100.f)) { diff --git a/src/server/scripts/EasternKingdoms/zone_isle_of_queldanas.cpp b/src/server/scripts/EasternKingdoms/zone_isle_of_queldanas.cpp index 54d31091a1c3c0..b4ba14b9d6cfd0 100644 --- a/src/server/scripts/EasternKingdoms/zone_isle_of_queldanas.cpp +++ b/src/server/scripts/EasternKingdoms/zone_isle_of_queldanas.cpp @@ -675,7 +675,7 @@ class npc_greengill_slave : public CreatureScript if (Creature* Myrmidon = me->FindNearestCreature(DM, 70)) { - me->AddThreat(Myrmidon, 100000.0f); + me->GetThreatManager().AddThreat(Myrmidon, 100000.0f); AttackStart(Myrmidon); } } diff --git a/src/server/scripts/EasternKingdoms/zone_silverpine_forest.cpp b/src/server/scripts/EasternKingdoms/zone_silverpine_forest.cpp index f11a572ab0e6ca..42663ee77be99d 100644 --- a/src/server/scripts/EasternKingdoms/zone_silverpine_forest.cpp +++ b/src/server/scripts/EasternKingdoms/zone_silverpine_forest.cpp @@ -242,7 +242,7 @@ class pyrewood_ambush : public CreatureScript target = me; summoned->SetFaction(FACTION_STORMWIND); - summoned->AddThreat(target, 32.0f); + summoned->GetThreatManager().AddThreat(target, 32.0f); summoned->AI()->AttackStart(target); } } diff --git a/src/server/scripts/EasternKingdoms/zone_undercity.cpp b/src/server/scripts/EasternKingdoms/zone_undercity.cpp index b53a079bdd8981..4bf39670d900f9 100644 --- a/src/server/scripts/EasternKingdoms/zone_undercity.cpp +++ b/src/server/scripts/EasternKingdoms/zone_undercity.cpp @@ -1013,7 +1013,7 @@ class npc_varian_wrynn : public CreatureScript void EnterEvadeMode(EvadeReason /*why*/) override { - me->GetThreatMgr().ClearAllThreat(); + me->GetThreatManager().ClearAllThreat(); me->CombatStop(true); me->SetLootRecipient(nullptr); @@ -1289,12 +1289,12 @@ class npc_varian_wrynn : public CreatureScript if (Unit* temp = me->SummonCreature(NPC_BLIGHTWORM, AllianceSpawn[7].x, AllianceSpawn[7].y, AllianceSpawn[7].z, TEMPSUMMON_MANUAL_DESPAWN)) { blightWormGUID = temp->GetGUID(); - temp->AddThreat(me, 100.0f); - me->AddThreat(temp, 100.0f); + temp->GetThreatManager().AddThreat(me, 100.0f); + me->GetThreatManager().AddThreat(temp, 100.0f); if (Creature* jaina = ObjectAccessor::GetCreature(*me, jainaGUID)) { - temp->AddThreat(jaina, 100.0f); - jaina->AddThreat(temp, 100.0f); + temp->GetThreatManager().AddThreat(jaina, 100.0f); + jaina->GetThreatManager().AddThreat(temp, 100.0f); } } if (Unit* temp = me->SummonCreature(NPC_KHANOK, HordeSpawn[0].x, HordeSpawn[0].y, HordeSpawn[0].z, TEMPSUMMON_MANUAL_DESPAWN)) @@ -1817,8 +1817,8 @@ class npc_varian_wrynn : public CreatureScript if (Creature* putress = ObjectAccessor::GetCreature(*me, putressGUID)) { putress->SetImmuneToAll(false); - putress->AddThreat(me, 100.0f); - me->AddThreat(putress, 100.0f); + putress->GetThreatManager().AddThreat(me, 100.0f); + me->GetThreatManager().AddThreat(putress, 100.0f); putress->RemoveAura(SPELL_PUTRESS_CASTING_STATE); } bStepping = false; @@ -1933,8 +1933,8 @@ class npc_varian_wrynn : public CreatureScript thrall->SetReactState(REACT_AGGRESSIVE); thrall->SetImmuneToNPC(false); thrall->SetImmuneToPC(true); - thrall->AddThreat(me, 100.0f); - me->AddThreat(thrall, 100.0f); + thrall->GetThreatManager().AddThreat(me, 100.0f); + me->GetThreatManager().AddThreat(thrall, 100.0f); thrall->AI()->AttackStart(me); } if (Creature* sylvanas = ObjectAccessor::GetCreature(*me, sylvanasGUID)) @@ -1942,9 +1942,9 @@ class npc_varian_wrynn : public CreatureScript sylvanas->SetReactState(REACT_AGGRESSIVE); sylvanas->SetImmuneToNPC(false); sylvanas->SetImmuneToPC(true); - sylvanas->AddThreat(me, 100.0f); + sylvanas->GetThreatManager().AddThreat(me, 100.0f); sylvanas->AI()->AttackStart(me); - me->AddThreat(sylvanas, 100.0f); + me->GetThreatManager().AddThreat(sylvanas, 100.0f); } for (uint8 i = 0; i < HORDE_FORCE_MAXCOUNT; ++i) { @@ -1965,9 +1965,9 @@ class npc_varian_wrynn : public CreatureScript temp2->SetReactState(REACT_AGGRESSIVE); temp->SetImmuneToAll(false); temp2->SetImmuneToAll(false); - temp->AddThreat(temp2, 100.0f); + temp->GetThreatManager().AddThreat(temp2, 100.0f); temp->AI()->AttackStart(temp2); - temp2->AddThreat(temp, 100.0f); + temp2->GetThreatManager().AddThreat(temp, 100.0f); } } } @@ -2350,7 +2350,7 @@ class npc_thrall_bfu : public CreatureScript void EnterEvadeMode(EvadeReason /*why*/) override { me->RemoveAura(SPELL_HEROIC_VANGUARD); - me->GetThreatMgr().ClearAllThreat(); + me->GetThreatManager().ClearAllThreat(); me->CombatStop(true); me->SetLootRecipient(nullptr); @@ -2457,8 +2457,8 @@ class npc_thrall_bfu : public CreatureScript summoned->ApplySpellImmune(0, IMMUNITY_ID, SPELL_SYLVANAS_BUFF, true); if (!EnableAttack) summoned->SetFaction(FACTION_FRIENDLY); - summoned->AddThreat(me, 100.0f); - me->AddThreat(summoned, 100.0f); + summoned->GetThreatManager().AddThreat(me, 100.0f); + me->GetThreatManager().AddThreat(summoned, 100.0f); summoned->AI()->AttackStart(me); break; case NPC_LEGION_INVADER: @@ -2469,8 +2469,8 @@ class npc_thrall_bfu : public CreatureScript case NPC_FELBEAST_H: summoned->ApplySpellImmune(0, IMMUNITY_EFFECT, SPELL_EFFECT_KNOCK_BACK, true); summoned->ApplySpellImmune(0, IMMUNITY_EFFECT, SPELL_EFFECT_KNOCK_BACK_DEST, true); - summoned->AddThreat(me, 100.0f); - me->AddThreat(summoned, 100.0f); + summoned->GetThreatManager().AddThreat(me, 100.0f); + me->GetThreatManager().AddThreat(summoned, 100.0f); summoned->AI()->AttackStart(me); break; default: @@ -2630,8 +2630,8 @@ class npc_thrall_bfu : public CreatureScript if (Creature* temp = me->SummonCreature(NPC_BLIGHT_ABBERATION, ThrallSpawn[28].x, ThrallSpawn[28].y, ThrallSpawn[28].z, ThrallSpawn[28].o, TEMPSUMMON_TIMED_OR_DEAD_DESPAWN, 900 * IN_MILLISECONDS)) { temp->GetMotionMaster()->MoveJump(ThrallSpawn[62].x, ThrallSpawn[62].y, ThrallSpawn[62].z, 10.0f, 20.0f, 0); - temp->AddThreat(me, 100.0f); - me->AddThreat(temp, 100.0f); + temp->GetThreatManager().AddThreat(me, 100.0f); + me->GetThreatManager().AddThreat(temp, 100.0f); temp->AI()->AttackStart(me); } break; @@ -3669,8 +3669,8 @@ class npc_thrall_bfu : public CreatureScript valimathras->RemoveAura(SPELL_AURA_OF_VARIMATHRAS); valimathras->RemoveAura(SPELL_OPENING_LEGION_PORTALS); valimathras->AI()->Talk(SAY_VALIMATHRAS_ATTACK); - valimathras->AddThreat(me, 100.0f); - me->AddThreat(valimathras, 100.0f); + valimathras->GetThreatManager().AddThreat(me, 100.0f); + me->GetThreatManager().AddThreat(valimathras, 100.0f); valimathras->AI()->AttackStart(me); } bStepping = false; @@ -3785,8 +3785,8 @@ class npc_thrall_bfu : public CreatureScript wrynn->SetImmuneToNPC(false); wrynn->SetImmuneToPC(true); wrynn->SetReactState(REACT_AGGRESSIVE); - wrynn->AddThreat(me, 100.0f); - me->AddThreat(wrynn, 100.0f); + wrynn->GetThreatManager().AddThreat(me, 100.0f); + me->GetThreatManager().AddThreat(wrynn, 100.0f); wrynn->AI()->AttackStart(me); } @@ -3796,7 +3796,7 @@ class npc_thrall_bfu : public CreatureScript { temp->SetImmuneToAll(false); temp->SetReactState(REACT_AGGRESSIVE); - temp->AddThreat(me, 100.0f); + temp->GetThreatManager().AddThreat(me, 100.0f); temp->AI()->AttackStart(me); } } diff --git a/src/server/scripts/EasternKingdoms/zone_westfall.cpp b/src/server/scripts/EasternKingdoms/zone_westfall.cpp index 4b022c7a7d46e3..bced7dee44903a 100644 --- a/src/server/scripts/EasternKingdoms/zone_westfall.cpp +++ b/src/server/scripts/EasternKingdoms/zone_westfall.cpp @@ -162,7 +162,7 @@ class npc_daphne_stilwell : public CreatureScript creature->GetMotionMaster()->MoveChase(me); creature->GetMotionMaster()->MovePoint(0, me->GetPositionX(), me->GetPositionY(), me->GetPositionZ()); creature->AI()->AttackStart(me); - creature->AddThreat(me, 0.0f); + creature->GetThreatManager().AddThreat(me, 0.0f); summons.Summon(creature); } diff --git a/src/server/scripts/EasternKingdoms/zone_wetlands.cpp b/src/server/scripts/EasternKingdoms/zone_wetlands.cpp index 6575b2dfba9053..d9cea99882c2a8 100644 --- a/src/server/scripts/EasternKingdoms/zone_wetlands.cpp +++ b/src/server/scripts/EasternKingdoms/zone_wetlands.cpp @@ -118,7 +118,7 @@ class npc_tapoke_slim_jahn : public CreatureScript me->RestoreFaction(); me->RemoveAllAuras(); - me->GetThreatMgr().ClearAllThreat(); + me->GetThreatManager().ClearAllThreat(); me->CombatStop(true); SetRun(false); diff --git a/src/server/scripts/Kalimdor/CavernsOfTime/BattleForMountHyjal/boss_anetheron.cpp b/src/server/scripts/Kalimdor/CavernsOfTime/BattleForMountHyjal/boss_anetheron.cpp index a69dc8144d4ac3..b64e8722e9b4ec 100644 --- a/src/server/scripts/Kalimdor/CavernsOfTime/BattleForMountHyjal/boss_anetheron.cpp +++ b/src/server/scripts/Kalimdor/CavernsOfTime/BattleForMountHyjal/boss_anetheron.cpp @@ -97,7 +97,7 @@ class boss_anetheron : public CreatureScript { Unit* target = ObjectAccessor::GetUnit(*me, instance->GetGuidData(DATA_JAINAPROUDMOORE)); if (target && target->IsAlive()) - me->AddThreat(target, 0.0f); + me->GetThreatManager().AddThreat(target, 0.0f); } } diff --git a/src/server/scripts/Kalimdor/CavernsOfTime/BattleForMountHyjal/boss_archimonde.cpp b/src/server/scripts/Kalimdor/CavernsOfTime/BattleForMountHyjal/boss_archimonde.cpp index df544d93542f0c..1e2185f86256eb 100644 --- a/src/server/scripts/Kalimdor/CavernsOfTime/BattleForMountHyjal/boss_archimonde.cpp +++ b/src/server/scripts/Kalimdor/CavernsOfTime/BattleForMountHyjal/boss_archimonde.cpp @@ -326,14 +326,14 @@ class boss_archimonde : public CreatureScript return; // Now lets get archimode threat list - ThreatContainer::StorageType const& t_list = me->GetThreatMgr().GetThreatList(); + auto tlist = me->GetThreatManager().GetUnsortedThreatList(); - if (t_list.empty()) + if (me->GetThreatManager().GetThreatListSize()) return; - ThreatContainer::StorageType::const_iterator itr = t_list.begin(); + auto itr = tlist.begin(); - if (Unit* target = ObjectAccessor::GetUnit(*me, (*itr)->getUnitGuid())) + if (Unit* target = ObjectAccessor::GetUnit(*me, (*itr)->GetVictim()->GetGUID())) if (target->IsAlive() && target->GetTypeId() == TYPEID_PLAYER) spellEffectTargets.push_back(target); @@ -436,14 +436,13 @@ class boss_archimonde : public CreatureScript if (victim && me->IsWithinMeleeRange(victim)) return false; - ThreatContainer::StorageType const& threatlist = me->GetThreatMgr().GetThreatList(); - if (threatlist.empty()) + if (me->GetThreatManager().GetThreatListSize() > 0) return false; - auto itr = threatlist.begin(); - for (; itr != threatlist.end(); ++itr) + auto tlist = me->GetThreatManager().GetUnsortedThreatList(); + for (auto t : tlist) { - Unit* unit = ObjectAccessor::GetUnit(*me, (*itr)->getUnitGuid()); + Unit* unit = ObjectAccessor::GetUnit(*me, t->GetVictim()->GetGUID()); if (unit && unit->IsAlive() && me->IsWithinMeleeRange(unit)) fingerOfDeathTargets.push_back(unit); } diff --git a/src/server/scripts/Kalimdor/CavernsOfTime/BattleForMountHyjal/boss_azgalor.cpp b/src/server/scripts/Kalimdor/CavernsOfTime/BattleForMountHyjal/boss_azgalor.cpp index cc3e8fc357addc..ef52ce7a01e4c2 100644 --- a/src/server/scripts/Kalimdor/CavernsOfTime/BattleForMountHyjal/boss_azgalor.cpp +++ b/src/server/scripts/Kalimdor/CavernsOfTime/BattleForMountHyjal/boss_azgalor.cpp @@ -101,7 +101,7 @@ class boss_azgalor : public CreatureScript { Unit* target = ObjectAccessor::GetUnit(*me, instance->GetGuidData(DATA_THRALL)); if (target && target->IsAlive()) - me->AddThreat(target, 0.0f); + me->GetThreatManager().AddThreat(target, 0.0f); } } diff --git a/src/server/scripts/Kalimdor/CavernsOfTime/BattleForMountHyjal/boss_kazrogal.cpp b/src/server/scripts/Kalimdor/CavernsOfTime/BattleForMountHyjal/boss_kazrogal.cpp index b5b3b63a863c87..2f50c9079c14bf 100644 --- a/src/server/scripts/Kalimdor/CavernsOfTime/BattleForMountHyjal/boss_kazrogal.cpp +++ b/src/server/scripts/Kalimdor/CavernsOfTime/BattleForMountHyjal/boss_kazrogal.cpp @@ -97,7 +97,7 @@ class boss_kazrogal : public CreatureScript { Unit* target = ObjectAccessor::GetUnit(*me, instance->GetGuidData(DATA_THRALL)); if (target && target->IsAlive()) - me->AddThreat(target, 0.0f); + me->GetThreatManager().AddThreat(target, 0.0f); } } diff --git a/src/server/scripts/Kalimdor/CavernsOfTime/BattleForMountHyjal/boss_rage_winterchill.cpp b/src/server/scripts/Kalimdor/CavernsOfTime/BattleForMountHyjal/boss_rage_winterchill.cpp index 49590d28006c61..7c99d65af4b309 100644 --- a/src/server/scripts/Kalimdor/CavernsOfTime/BattleForMountHyjal/boss_rage_winterchill.cpp +++ b/src/server/scripts/Kalimdor/CavernsOfTime/BattleForMountHyjal/boss_rage_winterchill.cpp @@ -91,7 +91,7 @@ class boss_rage_winterchill : public CreatureScript { Unit* target = ObjectAccessor::GetUnit(*me, instance->GetGuidData(DATA_JAINAPROUDMOORE)); if (target && target->IsAlive()) - me->AddThreat(target, 0.0f); + me->GetThreatManager().AddThreat(target, 0.0f); } } diff --git a/src/server/scripts/Kalimdor/CavernsOfTime/BattleForMountHyjal/hyjalAI.cpp b/src/server/scripts/Kalimdor/CavernsOfTime/BattleForMountHyjal/hyjalAI.cpp index 42817e7950fe28..ebebddb2251813 100644 --- a/src/server/scripts/Kalimdor/CavernsOfTime/BattleForMountHyjal/hyjalAI.cpp +++ b/src/server/scripts/Kalimdor/CavernsOfTime/BattleForMountHyjal/hyjalAI.cpp @@ -410,7 +410,7 @@ void hyjalAI::EnterEvadeMode(EvadeReason /*why*/) { if (me->GetEntry() != JAINA) me->RemoveAllAuras(); - me->GetThreatMgr().ClearAllThreat(); + me->GetThreatManager().ClearAllThreat(); me->CombatStop(true); me->LoadCreaturesAddon(true); diff --git a/src/server/scripts/Kalimdor/CavernsOfTime/BattleForMountHyjal/hyjal_trash.cpp b/src/server/scripts/Kalimdor/CavernsOfTime/BattleForMountHyjal/hyjal_trash.cpp index e883f1c20a1f3d..f542d6cbb5282d 100644 --- a/src/server/scripts/Kalimdor/CavernsOfTime/BattleForMountHyjal/hyjal_trash.cpp +++ b/src/server/scripts/Kalimdor/CavernsOfTime/BattleForMountHyjal/hyjal_trash.cpp @@ -447,7 +447,7 @@ class npc_giant_infernal : public CreatureScript { Unit* target = ObjectAccessor::GetUnit(*me, instance->GetGuidData(DATA_THRALL)); if (target && target->IsAlive()) - me->AddThreat(target, 0.0f); + me->GetThreatManager().AddThreat(target, 0.0f); } } } @@ -490,7 +490,7 @@ class npc_giant_infernal : public CreatureScript { Unit* target = ObjectAccessor::GetUnit(*me, instance->GetGuidData(DATA_THRALL)); if (target && target->IsAlive()) - me->AddThreat(target, 0.0f); + me->GetThreatManager().AddThreat(target, 0.0f); } else if (instance->GetData(DATA_ALLIANCE_RETREAT) && instance->GetData(DATA_HORDE_RETREAT)) { @@ -571,13 +571,13 @@ class npc_abomination : public CreatureScript { Unit* target = ObjectAccessor::GetUnit(*me, instance->GetGuidData(DATA_THRALL)); if (target && target->IsAlive()) - me->AddThreat(target, 0.0f); + me->GetThreatManager().AddThreat(target, 0.0f); } else { Unit* target = ObjectAccessor::GetUnit(*me, instance->GetGuidData(DATA_JAINAPROUDMOORE)); if (target && target->IsAlive()) - me->AddThreat(target, 0.0f); + me->GetThreatManager().AddThreat(target, 0.0f); } } if (waypointId == LastOverronPos && IsOverrun) @@ -671,13 +671,13 @@ class npc_ghoul : public CreatureScript { Unit* target = ObjectAccessor::GetUnit(*me, instance->GetGuidData(DATA_THRALL)); if (target && target->IsAlive()) - me->AddThreat(target, 0.0f); + me->GetThreatManager().AddThreat(target, 0.0f); } else { Unit* target = ObjectAccessor::GetUnit(*me, instance->GetGuidData(DATA_JAINAPROUDMOORE)); if (target && target->IsAlive()) - me->AddThreat(target, 0.0f); + me->GetThreatManager().AddThreat(target, 0.0f); } } if (waypointId == LastOverronPos && IsOverrun) @@ -783,13 +783,13 @@ class npc_necromancer : public CreatureScript { Unit* target = ObjectAccessor::GetUnit(*me, instance->GetGuidData(DATA_THRALL)); if (target && target->IsAlive()) - me->AddThreat(target, 0.0f); + me->GetThreatManager().AddThreat(target, 0.0f); } else { Unit* target = ObjectAccessor::GetUnit(*me, instance->GetGuidData(DATA_JAINAPROUDMOORE)); if (target && target->IsAlive()) - me->AddThreat(target, 0.0f); + me->GetThreatManager().AddThreat(target, 0.0f); } } } @@ -897,13 +897,13 @@ class npc_banshee : public CreatureScript { Unit* target = ObjectAccessor::GetUnit(*me, instance->GetGuidData(DATA_THRALL)); if (target && target->IsAlive()) - me->AddThreat(target, 0.0f); + me->GetThreatManager().AddThreat(target, 0.0f); } else { Unit* target = ObjectAccessor::GetUnit(*me, instance->GetGuidData(DATA_JAINAPROUDMOORE)); if (target && target->IsAlive()) - me->AddThreat(target, 0.0f); + me->GetThreatManager().AddThreat(target, 0.0f); } } } @@ -996,13 +996,13 @@ class npc_crypt_fiend : public CreatureScript { Unit* target = ObjectAccessor::GetUnit(*me, instance->GetGuidData(DATA_THRALL)); if (target && target->IsAlive()) - me->AddThreat(target, 0.0f); + me->GetThreatManager().AddThreat(target, 0.0f); } else { Unit* target = ObjectAccessor::GetUnit(*me, instance->GetGuidData(DATA_JAINAPROUDMOORE)); if (target && target->IsAlive()) - me->AddThreat(target, 0.0f); + me->GetThreatManager().AddThreat(target, 0.0f); } } } @@ -1083,13 +1083,13 @@ class npc_fel_stalker : public CreatureScript { Unit* target = ObjectAccessor::GetUnit(*me, instance->GetGuidData(DATA_THRALL)); if (target && target->IsAlive()) - me->AddThreat(target, 0.0f); + me->GetThreatManager().AddThreat(target, 0.0f); } else { Unit* target = ObjectAccessor::GetUnit(*me, instance->GetGuidData(DATA_JAINAPROUDMOORE)); if (target && target->IsAlive()) - me->AddThreat(target, 0.0f); + me->GetThreatManager().AddThreat(target, 0.0f); } } } @@ -1172,7 +1172,7 @@ class npc_frost_wyrm : public CreatureScript Unit* target = ObjectAccessor::GetUnit(*me, instance->GetGuidData(DATA_THRALL)); if (target && target->IsAlive()) { - me->AddThreat(target, 0.0f); + me->GetThreatManager().AddThreat(target, 0.0f); DoCast(target, SPELL_FROST_BREATH, true); } } @@ -1296,7 +1296,7 @@ class npc_gargoyle : public CreatureScript Unit* target = ObjectAccessor::GetUnit(*me, instance->GetGuidData(DATA_THRALL)); if (target && target->IsAlive()) { - me->AddThreat(target, 0.0f); + me->GetThreatManager().AddThreat(target, 0.0f); DoCast(target, SPELL_GARGOYLE_STRIKE, true); } } diff --git a/src/server/scripts/Kalimdor/CavernsOfTime/CullingOfStratholme/culling_of_stratholme.cpp b/src/server/scripts/Kalimdor/CavernsOfTime/CullingOfStratholme/culling_of_stratholme.cpp index cc94d7dc068d75..3ee756863eb8b3 100644 --- a/src/server/scripts/Kalimdor/CavernsOfTime/CullingOfStratholme/culling_of_stratholme.cpp +++ b/src/server/scripts/Kalimdor/CavernsOfTime/CullingOfStratholme/culling_of_stratholme.cpp @@ -992,21 +992,21 @@ class npc_arthas : public CreatureScript cr->UpdateEntry(NPC_INFINITE_ADVERSARY, nullptr, false); cr->SetReactState(REACT_AGGRESSIVE); cr->SetInCombatWithZone(); - cr->AddThreat(me, 0.0f); + cr->GetThreatManager().AddThreat(me, 0.0f); } if (Creature* cr = GetEventNpc(NPC_INFINITE_AGENT)) // it is infinite agent now :) { cr->SetImmuneToAll(false); cr->SetReactState(REACT_AGGRESSIVE); cr->SetInCombatWithZone(); - cr->AddThreat(me, 0.0f); + cr->GetThreatManager().AddThreat(me, 0.0f); } if (Creature* cr = GetEventNpc(NPC_INFINITE_HUNTER)) // it is infinite hunter now :) { cr->SetImmuneToAll(false); cr->SetReactState(REACT_AGGRESSIVE); cr->SetInCombatWithZone(); - cr->AddThreat(me, 0.0f); + cr->GetThreatManager().AddThreat(me, 0.0f); } ScheduleNextEvent(currentEvent, 2000); break; @@ -1100,7 +1100,7 @@ class npc_arthas : public CreatureScript cr->SetImmuneToAll(false); cr->RemoveUnitFlag(UNIT_FLAG_NON_ATTACKABLE); cr->SetReactState(REACT_AGGRESSIVE); - cr->AddThreat(me, 0.0f); + cr->GetThreatManager().AddThreat(me, 0.0f); cr->SetInCombatWithZone(); } ScheduleNextEvent(currentEvent, 1000); @@ -1125,7 +1125,7 @@ class npc_arthas : public CreatureScript cr->SetImmuneToAll(false); cr->RemoveUnitFlag(UNIT_FLAG_NON_ATTACKABLE); cr->SetInCombatWithZone(); - cr->AddThreat(me, 0.0f); + cr->GetThreatManager().AddThreat(me, 0.0f); AttackStart(cr); } eventInRun = false; @@ -1352,7 +1352,7 @@ void npc_arthas::npc_arthasAI::SpawnTimeRift() AttackStart(cr); cr->SetInCombatWithZone(); - cr->AddThreat(me, 0.0f); + cr->GetThreatManager().AddThreat(me, 0.0f); cr->SetReactState(REACT_AGGRESSIVE); cr->GetMotionMaster()->MovePoint(POINT_CHRONOS, RiftAndSpawnsLocations[timeRiftId][i][1], RiftAndSpawnsLocations[timeRiftId][i][2], RiftAndSpawnsLocations[timeRiftId][i][3]); } diff --git a/src/server/scripts/Kalimdor/RuinsOfAhnQiraj/boss_buru.cpp b/src/server/scripts/Kalimdor/RuinsOfAhnQiraj/boss_buru.cpp index bcfc285409334c..21a32e4ed0e202 100644 --- a/src/server/scripts/Kalimdor/RuinsOfAhnQiraj/boss_buru.cpp +++ b/src/server/scripts/Kalimdor/RuinsOfAhnQiraj/boss_buru.cpp @@ -81,7 +81,7 @@ struct boss_buru : public BossAI void JustEngagedWith(Unit* who) override { BossAI::JustEngagedWith(who); - me->AddThreat(who, 1000000.f); + me->GetThreatManager().AddThreat(who, 1000000.f); Talk(EMOTE_TARGET, who); DoCastSelf(SPELL_THORNS); _phase = PHASE_EGG; @@ -122,7 +122,7 @@ struct boss_buru : public BossAI { DoResetThreatList(); AttackStart(victim); - me->AddThreat(victim, 1000000.f); + me->GetThreatManager().AddThreat(victim, 1000000.f); Talk(EMOTE_TARGET, victim); } } diff --git a/src/server/scripts/Kalimdor/RuinsOfAhnQiraj/boss_rajaxx.cpp b/src/server/scripts/Kalimdor/RuinsOfAhnQiraj/boss_rajaxx.cpp index 8b66464860a0ab..72907a6f0d2206 100644 --- a/src/server/scripts/Kalimdor/RuinsOfAhnQiraj/boss_rajaxx.cpp +++ b/src/server/scripts/Kalimdor/RuinsOfAhnQiraj/boss_rajaxx.cpp @@ -114,7 +114,7 @@ struct boss_rajaxx : public BossAI break; case EVENT_THUNDERCRASH: DoCastSelf(SPELL_THUNDERCRASH); - me->GetThreatMgr().ResetAllThreat(); + me->GetThreatManager().ResetAllThreat(); events.ScheduleEvent(EVENT_THUNDERCRASH, 21s); break; default: diff --git a/src/server/scripts/Kalimdor/TempleOfAhnQiraj/boss_bug_trio.cpp b/src/server/scripts/Kalimdor/TempleOfAhnQiraj/boss_bug_trio.cpp index 323d4af42bfbdc..3ffd02e862f4c1 100644 --- a/src/server/scripts/Kalimdor/TempleOfAhnQiraj/boss_bug_trio.cpp +++ b/src/server/scripts/Kalimdor/TempleOfAhnQiraj/boss_bug_trio.cpp @@ -165,7 +165,7 @@ struct boss_bug_trio : public BossAI me->GetMotionMaster()->MoveIdle(); me->SetSpeed(MOVE_RUN, 15.f/7.f); // From sniffs DoCastSelf(SPELL_FULL_HEAL, true); - if (me->GetThreatMgr().GetThreatListSize()) + if (me->GetThreatManager().GetThreatListSize()) DoResetThreatList(); if (Creature* dying = instance->GetCreature(_creatureDying)) { @@ -453,7 +453,7 @@ class spell_vem_knockback : public SpellScript { if (Creature* cCaster = GetCaster()->ToCreature()) { - cCaster->GetThreatMgr().ModifyThreatByPercent(target, -80); + cCaster->GetThreatManager().ModifyThreatByPercent(target, -80); } } } diff --git a/src/server/scripts/Kalimdor/TempleOfAhnQiraj/boss_ouro.cpp b/src/server/scripts/Kalimdor/TempleOfAhnQiraj/boss_ouro.cpp index 4468cab45621e8..1a277a99724f93 100644 --- a/src/server/scripts/Kalimdor/TempleOfAhnQiraj/boss_ouro.cpp +++ b/src/server/scripts/Kalimdor/TempleOfAhnQiraj/boss_ouro.cpp @@ -194,7 +194,7 @@ struct boss_ouro : public BossAI { if (spellInfo->Id == SPELL_SAND_BLAST && target) { - me->GetThreatMgr().ModifyThreatByPercent(target, -100); + me->GetThreatManager().ModifyThreatByPercent(target, -100); } } @@ -270,7 +270,7 @@ struct boss_ouro : public BossAI void EnterEvadeMode(EvadeReason /*why*/) override { - if (me->GetThreatMgr().GetThreatList().empty()) + if (me->GetThreatManager().GetThreatListSize() <= 1) { DoCastSelf(SPELL_OURO_SUBMERGE_VISUAL); me->DespawnOrUnsummon(1000); @@ -348,7 +348,7 @@ struct npc_dirt_mound : ScriptedAI DoResetThreatList(); if (Unit* target = SelectTarget(SelectTargetMethod::Random, 0, 200.f, true)) { - me->AddThreat(target, 1000000.f); + me->GetThreatManager().AddThreat(target, 1000000.f); AttackStart(target); } } diff --git a/src/server/scripts/Kalimdor/TempleOfAhnQiraj/boss_sartura.cpp b/src/server/scripts/Kalimdor/TempleOfAhnQiraj/boss_sartura.cpp index 4b427722445b6a..27c7e37c3a795a 100644 --- a/src/server/scripts/Kalimdor/TempleOfAhnQiraj/boss_sartura.cpp +++ b/src/server/scripts/Kalimdor/TempleOfAhnQiraj/boss_sartura.cpp @@ -117,8 +117,8 @@ struct boss_sartura : public BossAI case EVENT_SARTURA_WHIRLWIND: if (Unit* target = SelectTarget(SelectTargetMethod::Random, 1, 100.0f, true)) { - me->GetThreatMgr().ResetAllThreat(); - me->AddThreat(target, 1000.0f); + me->GetThreatManager().ResetAllThreat(); + me->GetThreatManager().AddThreat(target, 1000.0f); } DoCastSelf(SPELL_WHIRLWIND); events.ScheduleEvent(EVENT_SARTURA_WHIRLWIND_RANDOM, 2s, 7s); @@ -127,13 +127,13 @@ struct boss_sartura : public BossAI case EVENT_SARTURA_WHIRLWIND_RANDOM: if (Unit* target = SelectTarget(SelectTargetMethod::Random, 0, 100.0f, true)) { - me->GetThreatMgr().ResetAllThreat(); - me->AddThreat(target, 1000.0f); + me->GetThreatManager().ResetAllThreat(); + me->GetThreatManager().AddThreat(target, 1000.0f); } events.Repeat(2s, 7s); break; case EVENT_SARTURA_WHIRLWIND_END: - me->GetThreatMgr().ResetAllThreat(); + me->GetThreatManager().ResetAllThreat(); me->SetReactState(REACT_AGGRESSIVE); events.CancelEvent(EVENT_SARTURA_WHIRLWIND_RANDOM); events.ScheduleEvent(EVENT_SARTURA_WHIRLWIND, 5s, 11s); @@ -201,8 +201,8 @@ struct npc_sartura_royal_guard : public ScriptedAI case EVENT_GUARD_WHIRLWIND: if (Unit* target = SelectTarget(SelectTargetMethod::Random, 1, 100.0f, true)) { - me->GetThreatMgr().ResetAllThreat(); - me->AddThreat(target, 1000.0f); + me->GetThreatManager().ResetAllThreat(); + me->GetThreatManager().AddThreat(target, 1000.0f); } DoCastSelf(SPELL_GUARD_WHIRLWIND); events.ScheduleEvent(EVENT_GUARD_WHIRLWIND_RANDOM, 2s, 7s); @@ -211,13 +211,13 @@ struct npc_sartura_royal_guard : public ScriptedAI case EVENT_GUARD_WHIRLWIND_RANDOM: if (Unit* target = SelectTarget(SelectTargetMethod::Random, 0, 100.0f, true)) { - me->GetThreatMgr().ResetAllThreat(); - me->AddThreat(target, 1000.0f); + me->GetThreatManager().ResetAllThreat(); + me->GetThreatManager().AddThreat(target, 1000.0f); } events.Repeat(2s, 7s); break; case EVENT_GUARD_WHIRLWIND_END: - me->GetThreatMgr().ResetAllThreat(); + me->GetThreatManager().ResetAllThreat(); me->SetReactState(REACT_AGGRESSIVE); events.CancelEvent(EVENT_GUARD_WHIRLWIND_RANDOM); events.ScheduleEvent(EVENT_GUARD_WHIRLWIND, 500ms, 9s); diff --git a/src/server/scripts/Kalimdor/TempleOfAhnQiraj/boss_twinemperors.cpp b/src/server/scripts/Kalimdor/TempleOfAhnQiraj/boss_twinemperors.cpp index 1d83e1047bb773..9893228c1cf0a8 100644 --- a/src/server/scripts/Kalimdor/TempleOfAhnQiraj/boss_twinemperors.cpp +++ b/src/server/scripts/Kalimdor/TempleOfAhnQiraj/boss_twinemperors.cpp @@ -164,7 +164,7 @@ struct boss_twinemperorsAI : public BossAI me->SetControlled(false, UNIT_STATE_ROOT); if (Unit* victim = me->SelectNearestTarget()) { - me->AddThreat(victim, 2000.f); + me->GetThreatManager().AddThreat(victim, 2000.f); AttackStart(victim); } }); @@ -400,7 +400,7 @@ struct boss_veklor : public boss_twinemperorsAI if (me->Attack(who, false)) { me->GetMotionMaster()->MoveChase(who, 45.0f, 0); - me->AddThreat(who, 0.0f); + me->GetThreatManager().AddThreat(who, 0.0f); } } } diff --git a/src/server/scripts/Kalimdor/boss_azuregos.cpp b/src/server/scripts/Kalimdor/boss_azuregos.cpp index 3ca98843a2a854..c87f1533827fa7 100644 --- a/src/server/scripts/Kalimdor/boss_azuregos.cpp +++ b/src/server/scripts/Kalimdor/boss_azuregos.cpp @@ -186,7 +186,7 @@ class spell_arcane_vacuum : public SpellScript Unit* hitUnit = GetHitUnit(); if (caster && hitUnit) { - caster->GetThreatMgr().ModifyThreatByPercent(hitUnit, -100); + caster->GetThreatManager().ModifyThreatByPercent(hitUnit, -100); caster->CastSpell(hitUnit, SPELL_ARCANE_VACUUM_TP, true); } } diff --git a/src/server/scripts/Kalimdor/zone_durotar.cpp b/src/server/scripts/Kalimdor/zone_durotar.cpp index ffca73f50b7054..7448bed767bc21 100644 --- a/src/server/scripts/Kalimdor/zone_durotar.cpp +++ b/src/server/scripts/Kalimdor/zone_durotar.cpp @@ -171,7 +171,7 @@ class npc_tiger_matriarch : public CreatureScript _tigerGuid = vehicle->GetBase()->GetGUID(); if (Unit* tiger = ObjectAccessor::GetUnit(*me, _tigerGuid)) { - me->AddThreat(tiger, 500000.0f); + me->GetThreatManager().AddThreat(tiger, 500000.0f); DoCast(me, SPELL_FURIOUS_BITE); } } diff --git a/src/server/scripts/Kalimdor/zone_moonglade.cpp b/src/server/scripts/Kalimdor/zone_moonglade.cpp index c574da421cc7a4..5d29c66d9445fb 100644 --- a/src/server/scripts/Kalimdor/zone_moonglade.cpp +++ b/src/server/scripts/Kalimdor/zone_moonglade.cpp @@ -475,7 +475,7 @@ class npc_clintar_spirit : public CreatureScript case 0: if (Creature* mob = me->SummonCreature(ASPECT_RAVEN, AspectRavenSummon, TEMPSUMMON_CORPSE_TIMED_DESPAWN, 2000)) { - mob->AddThreat(me, 10000.0f); + mob->GetThreatManager().AddThreat(me, 10000.0f); mob->AI()->AttackStart(me); } EventTimer = 2000; diff --git a/src/server/scripts/Kalimdor/zone_the_barrens.cpp b/src/server/scripts/Kalimdor/zone_the_barrens.cpp index 6d4fc105e6c402..6ab4157bcaaa33 100644 --- a/src/server/scripts/Kalimdor/zone_the_barrens.cpp +++ b/src/server/scripts/Kalimdor/zone_the_barrens.cpp @@ -177,7 +177,7 @@ class npc_taskmaster_fizzule : public CreatureScript void DoFriend() { me->RemoveAllAuras(); - me->GetThreatMgr().ClearAllThreat(); + me->GetThreatManager().ClearAllThreat(); me->CombatStop(true); me->StopMoving(); diff --git a/src/server/scripts/Northrend/AzjolNerub/ahnkahet/boss_herald_volazj.cpp b/src/server/scripts/Northrend/AzjolNerub/ahnkahet/boss_herald_volazj.cpp index b35c579024f270..2831ca20a96c11 100644 --- a/src/server/scripts/Northrend/AzjolNerub/ahnkahet/boss_herald_volazj.cpp +++ b/src/server/scripts/Northrend/AzjolNerub/ahnkahet/boss_herald_volazj.cpp @@ -413,7 +413,7 @@ class spell_herald_volzaj_insanity : public SpellScript if (Unit* summon = caster->SummonCreature(NPC_TWISTED_VISAGE, plrClone->GetPosition(), TEMPSUMMON_CORPSE_DESPAWN, 0)) { - summon->AddThreat(plrTarget, 0.0f); + summon->GetThreatManager().AddThreat(plrTarget, 0.0f); summon->SetInCombatWith(plrTarget); plrTarget->SetInCombatWith(summon); diff --git a/src/server/scripts/Northrend/AzjolNerub/ahnkahet/boss_prince_taldaram.cpp b/src/server/scripts/Northrend/AzjolNerub/ahnkahet/boss_prince_taldaram.cpp index 9cff13a2be5786..72426ed90e8d2f 100644 --- a/src/server/scripts/Northrend/AzjolNerub/ahnkahet/boss_prince_taldaram.cpp +++ b/src/server/scripts/Northrend/AzjolNerub/ahnkahet/boss_prince_taldaram.cpp @@ -412,19 +412,13 @@ struct boss_taldaram : public BossAI { //Count alive players uint8 count = 0; - std::list const t_list = me->GetThreatMgr().GetThreatList(); - if (!t_list.empty()) + auto tList = me->GetThreatManager().GetUnsortedThreatList(); + for (auto t : tList) { - for (HostileReference const* reference : t_list) + Unit const* pTarget = ObjectAccessor::GetUnit(*me, t->GetVictim()->GetGUID()); + if (pTarget && pTarget->GetTypeId() == TYPEID_PLAYER && pTarget->IsAlive()) { - if (reference) - { - Unit const* pTarget = ObjectAccessor::GetUnit(*me, reference->getUnitGuid()); - if (pTarget && pTarget->GetTypeId() == TYPEID_PLAYER && pTarget->IsAlive()) - { - ++count; - } - } + ++count; } } diff --git a/src/server/scripts/Northrend/ChamberOfAspects/RubySanctum/boss_halion.cpp b/src/server/scripts/Northrend/ChamberOfAspects/RubySanctum/boss_halion.cpp index 3a5cdd6cf9b8eb..69de0b51b6e254 100644 --- a/src/server/scripts/Northrend/ChamberOfAspects/RubySanctum/boss_halion.cpp +++ b/src/server/scripts/Northrend/ChamberOfAspects/RubySanctum/boss_halion.cpp @@ -1153,7 +1153,7 @@ class spell_halion_twilight_phasing : public SpellScriptLoader GetTarget()->SetUnitFlag(UNIT_FLAG_NOT_SELECTABLE); GetTarget()->ToCreature()->SetReactState(REACT_DEFENSIVE); GetTarget()->GetMotionMaster()->Clear(); - GetTarget()->GetThreatMgr().clearReferences(); + GetTarget()->GetThreatManager().ClearAllThreat(); GetTarget()->RemoveAllAttackers(); GetTarget()->AttackStop(); } @@ -1163,7 +1163,7 @@ class spell_halion_twilight_phasing : public SpellScriptLoader GetTarget()->RemoveUnitFlag(UNIT_FLAG_NOT_SELECTABLE); GetTarget()->ToCreature()->SetReactState(REACT_DEFENSIVE); GetTarget()->GetMotionMaster()->Clear(); - GetTarget()->GetThreatMgr().clearReferences(); + GetTarget()->GetThreatManager().ClearAllThreat(); GetTarget()->RemoveAllAttackers(); GetTarget()->AttackStop(); } diff --git a/src/server/scripts/Northrend/CrusadersColiseum/TrialOfTheChampion/boss_argent_challenge.cpp b/src/server/scripts/Northrend/CrusadersColiseum/TrialOfTheChampion/boss_argent_challenge.cpp index 2cbe8078c30997..860897bd3a2b6e 100644 --- a/src/server/scripts/Northrend/CrusadersColiseum/TrialOfTheChampion/boss_argent_challenge.cpp +++ b/src/server/scripts/Northrend/CrusadersColiseum/TrialOfTheChampion/boss_argent_challenge.cpp @@ -151,7 +151,7 @@ class boss_eadric : public CreatureScript me->SetFaction(FACTION_FRIENDLY); events.Reset(); Talk(SAY_EADRIC_DEFEATED); - me->GetThreatMgr().clearReferences(); + me->GetThreatManager().ClearAllThreat(); me->SetRegeneratingHealth(false); _EnterEvadeMode(); me->SetUnitFlag(UNIT_FLAG_NON_ATTACKABLE); @@ -294,7 +294,7 @@ class boss_paletress : public CreatureScript me->SetFaction(FACTION_FRIENDLY); events.Reset(); Talk(SAY_PALETRESS_DEFEATED); - me->GetThreatMgr().clearReferences(); + me->GetThreatManager().ClearAllThreat(); me->SetRegeneratingHealth(false); _EnterEvadeMode(); me->SetUnitFlag(UNIT_FLAG_NON_ATTACKABLE); diff --git a/src/server/scripts/Northrend/CrusadersColiseum/TrialOfTheChampion/boss_black_knight.cpp b/src/server/scripts/Northrend/CrusadersColiseum/TrialOfTheChampion/boss_black_knight.cpp index dec97a49edcc92..6233b9a76dee42 100644 --- a/src/server/scripts/Northrend/CrusadersColiseum/TrialOfTheChampion/boss_black_knight.cpp +++ b/src/server/scripts/Northrend/CrusadersColiseum/TrialOfTheChampion/boss_black_knight.cpp @@ -429,7 +429,7 @@ class npc_black_knight_ghoul : public CreatureScript DoResetThreatList(); if (Unit* target = SelectTarget(SelectTargetMethod::Random, 0, 30.0f)) { - me->AddThreat(target, 100.0f); + me->GetThreatManager().AddThreat(target, 100.0f); AttackStart(target); } break; diff --git a/src/server/scripts/Northrend/CrusadersColiseum/TrialOfTheChampion/boss_grand_champions.cpp b/src/server/scripts/Northrend/CrusadersColiseum/TrialOfTheChampion/boss_grand_champions.cpp index e48f714c265b7d..3ba58ec49591f9 100644 --- a/src/server/scripts/Northrend/CrusadersColiseum/TrialOfTheChampion/boss_grand_champions.cpp +++ b/src/server/scripts/Northrend/CrusadersColiseum/TrialOfTheChampion/boss_grand_champions.cpp @@ -164,7 +164,6 @@ class npc_toc5_player_vehicle : public CreatureScript void Reset() override { me->SetReactState(REACT_PASSIVE); - me->getHostileRefMgr().setOnlineOfflineState(false); } void OnCharmed(bool apply) override @@ -301,8 +300,8 @@ class npc_toc5_grand_champion_minion : public CreatureScript uint8 rnd = LIST.size() > 1 ? urand(0, LIST.size() - 1) : 0; if( Unit* target = ObjectAccessor::GetUnit(*me, LIST.at(rnd)) ) { - me->GetThreatMgr().ResetAllThreat(); - me->AddThreat(target, 10000.0f); + me->GetThreatManager().ResetAllThreat(); + me->GetThreatManager().AddThreat(target, 10000.0f); AttackStart(target); me->CastSpell(target, SPELL_MINIONS_CHARGE, false); } @@ -555,7 +554,7 @@ class boss_grand_champion : public CreatureScript me->SetReactState(REACT_PASSIVE); me->RemoveAllAuras(); AddCreatureAddonAuras(); - me->GetThreatMgr().ClearAllThreat(); + me->GetThreatManager().ClearAllThreat(); me->CombatStop(true); me->GetMotionMaster()->Clear(); me->StopMoving(); @@ -586,7 +585,7 @@ class boss_grand_champion : public CreatureScript me->SetReactState(REACT_PASSIVE); me->RemoveAllAuras(); AddCreatureAddonAuras(); - me->GetThreatMgr().ClearAllThreat(); + me->GetThreatManager().ClearAllThreat(); me->CombatStop(true); me->GetMotionMaster()->Clear(); me->SetRegeneratingHealth(false); @@ -750,8 +749,8 @@ class boss_grand_champion : public CreatureScript uint8 rnd = LIST.size() > 1 ? urand(0, LIST.size() - 1) : 0; if( Unit* target = ObjectAccessor::GetUnit(*me, LIST.at(rnd)) ) { - me->GetThreatMgr().ResetAllThreat(); - me->AddThreat(target, 10000.0f); + me->GetThreatManager().ResetAllThreat(); + me->GetThreatManager().AddThreat(target, 10000.0f); AttackStart(target); me->CastSpell(target, SPELL_MINIONS_CHARGE, false); } @@ -923,7 +922,7 @@ class boss_grand_champion : public CreatureScript if( player && me->IsInRange(player, 8.0f, 25.0f, false) ) { DoResetThreatList(); - me->AddThreat(player, 5.0f); + me->GetThreatManager().AddThreat(player, 5.0f); me->CastSpell(player, SPELL_INTERCEPT, false); break; } diff --git a/src/server/scripts/Northrend/CrusadersColiseum/TrialOfTheCrusader/boss_anubarak_trial.cpp b/src/server/scripts/Northrend/CrusadersColiseum/TrialOfTheCrusader/boss_anubarak_trial.cpp index 45a189ad9b5431..872b95cd96f3d4 100644 --- a/src/server/scripts/Northrend/CrusadersColiseum/TrialOfTheCrusader/boss_anubarak_trial.cpp +++ b/src/server/scripts/Northrend/CrusadersColiseum/TrialOfTheCrusader/boss_anubarak_trial.cpp @@ -457,7 +457,7 @@ class npc_swarm_scarab : public CreatureScript DoZoneInCombat(); if( Unit* t = SelectTarget(SelectTargetMethod::Random, 0, 250.0f, true) ) { - me->AddThreat(t, 20000.0f); + me->GetThreatManager().AddThreat(t, 20000.0f); AttackStart(t); } } diff --git a/src/server/scripts/Northrend/CrusadersColiseum/TrialOfTheCrusader/boss_faction_champions.cpp b/src/server/scripts/Northrend/CrusadersColiseum/TrialOfTheCrusader/boss_faction_champions.cpp index 26a49edc7462ca..99666a5c3f9f86 100644 --- a/src/server/scripts/Northrend/CrusadersColiseum/TrialOfTheCrusader/boss_faction_champions.cpp +++ b/src/server/scripts/Northrend/CrusadersColiseum/TrialOfTheCrusader/boss_faction_champions.cpp @@ -103,17 +103,15 @@ 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 ) + auto tlist = me->GetThreatManager().GetUnsortedThreatList(); + for (auto t : tlist) { - Unit* pUnit = ObjectAccessor::GetUnit(*me, (*itr)->getUnitGuid()); - if( pUnit && pUnit->GetTypeId() == TYPEID_PLAYER && me->GetThreatMgr().GetThreat(pUnit) ) + Unit* pUnit = ObjectAccessor::GetUnit(*me, t->GetVictim()->GetGUID()); + if( pUnit && pUnit->GetTypeId() == TYPEID_PLAYER && me->GetThreatManager().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->GetThreatManager().ModifyThreatByPercent(pUnit, -100); + me->GetThreatManager().AddThreat(pUnit, 10000000.0f * threatMod); } } } @@ -179,12 +177,12 @@ 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 ) + auto tlist = me->GetThreatManager().GetUnsortedThreatList(); + for (auto t : tlist) { - target = ObjectAccessor::GetUnit((*me), (*iter)->getUnitGuid()); + target = ObjectAccessor::GetUnit((*me), t->GetVictim()->GetGUID()); if( target && me->GetDistance2d(target) < distance ) ++count; } @@ -193,11 +191,11 @@ struct boss_faction_championsAI : public ScriptedAI 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 ) + auto tlist = me->GetThreatManager().GetUnsortedThreatList(); + for (auto t : tlist) { - target = ObjectAccessor::GetUnit((*me), (*iter)->getUnitGuid()); + target = ObjectAccessor::GetUnit((*me), t->GetVictim()->GetGUID()); if( target && target->getPowerType() == POWER_MANA && (!casting || target->HasUnitState(UNIT_STATE_CASTING)) && me->GetExactDist(target) <= range ) return target; } diff --git a/src/server/scripts/Northrend/CrusadersColiseum/TrialOfTheCrusader/boss_lord_jaraxxus.cpp b/src/server/scripts/Northrend/CrusadersColiseum/TrialOfTheCrusader/boss_lord_jaraxxus.cpp index 21be9eb91698ff..a44ac9c3b8af91 100644 --- a/src/server/scripts/Northrend/CrusadersColiseum/TrialOfTheCrusader/boss_lord_jaraxxus.cpp +++ b/src/server/scripts/Northrend/CrusadersColiseum/TrialOfTheCrusader/boss_lord_jaraxxus.cpp @@ -338,7 +338,7 @@ class npc_fel_infernal : public CreatureScript if( Unit* target = SelectTarget(SelectTargetMethod::Random, 0, 44.0f, true) ) { DoResetThreatList(); - me->AddThreat(target, 50000.0f); + me->GetThreatManager().AddThreat(target, 50000.0f); me->CastSpell(target, SPELL_FEL_STEAK_MORPH, true); me->CastSpell(target, SPELL_FEL_STEAK, true); events.Repeat(30s); diff --git a/src/server/scripts/Northrend/CrusadersColiseum/TrialOfTheCrusader/boss_northrend_beasts.cpp b/src/server/scripts/Northrend/CrusadersColiseum/TrialOfTheCrusader/boss_northrend_beasts.cpp index 0beb019b3d4e9e..887b9fd85f4345 100644 --- a/src/server/scripts/Northrend/CrusadersColiseum/TrialOfTheCrusader/boss_northrend_beasts.cpp +++ b/src/server/scripts/Northrend/CrusadersColiseum/TrialOfTheCrusader/boss_northrend_beasts.cpp @@ -133,7 +133,7 @@ class npc_snobold_vassal : public CreatureScript { t->RemoveAura(SPELL_CHANGE_VEHICLE); me->RemoveAllAuras(); - me->GetThreatMgr().ClearAllThreat(); + me->GetThreatManager().ClearAllThreat(); me->CombatStop(true); me->SetHealth(me->GetMaxHealth()); if( pInstance ) diff --git a/src/server/scripts/Northrend/DraktharonKeep/boss_tharon_ja.cpp b/src/server/scripts/Northrend/DraktharonKeep/boss_tharon_ja.cpp index 45987b50501256..3f1b5b66fe8a02 100644 --- a/src/server/scripts/Northrend/DraktharonKeep/boss_tharon_ja.cpp +++ b/src/server/scripts/Northrend/DraktharonKeep/boss_tharon_ja.cpp @@ -162,7 +162,7 @@ class boss_tharon_ja : public CreatureScript if (me->HealthBelowPct(50)) { Talk(SAY_FLESH); - me->GetThreatMgr().ResetAllThreat(); + me->GetThreatManager().ResetAllThreat(); me->CastSpell((Unit*)nullptr, SPELL_TURN_FLESH, false); events.Reset(); @@ -258,7 +258,7 @@ class spell_tharon_ja_dummy : public SpellScriptLoader void HandleEffectRemove(AuraEffect const* /*aurEff*/, AuraEffectHandleModes /*mode*/) { PreventDefaultAction(); - GetUnitOwner()->GetThreatMgr().ResetAllThreat(); + GetUnitOwner()->GetThreatManager().ResetAllThreat(); GetUnitOwner()->GetMotionMaster()->Clear(); GetUnitOwner()->CastSpell((Unit*)nullptr, SPELL_TURN_BONES, false); GetUnitOwner()->GetAI()->DoAction(ACTION_TURN_BONES); diff --git a/src/server/scripts/Northrend/FrozenHalls/ForgeOfSouls/boss_devourer_of_souls.cpp b/src/server/scripts/Northrend/FrozenHalls/ForgeOfSouls/boss_devourer_of_souls.cpp index e2759b624c21c7..5ff07d59e04093 100644 --- a/src/server/scripts/Northrend/FrozenHalls/ForgeOfSouls/boss_devourer_of_souls.cpp +++ b/src/server/scripts/Northrend/FrozenHalls/ForgeOfSouls/boss_devourer_of_souls.cpp @@ -289,7 +289,7 @@ class boss_devourer_of_souls : public CreatureScript if (summon->GetEntry() == 36595) if (Player* plr = summon->SelectNearestPlayer(100.0f)) { - summon->AddThreat(plr, 100000.0f); + summon->GetThreatManager().AddThreat(plr, 100000.0f); summon->AI()->AttackStart(plr); } } diff --git a/src/server/scripts/Northrend/FrozenHalls/HallsOfReflection/halls_of_reflection.cpp b/src/server/scripts/Northrend/FrozenHalls/HallsOfReflection/halls_of_reflection.cpp index 0d814ddda674f6..7265860aa4c11d 100644 --- a/src/server/scripts/Northrend/FrozenHalls/HallsOfReflection/halls_of_reflection.cpp +++ b/src/server/scripts/Northrend/FrozenHalls/HallsOfReflection/halls_of_reflection.cpp @@ -1223,7 +1223,7 @@ class npc_shadowy_mercenary : public CreatureScript if (Unit* target = SelectTargetFromPlayerList(100.0f, 0, true)) { DoResetThreatList(); - me->AddThreat(target, 5000.0f); + me->GetThreatManager().AddThreat(target, 5000.0f); AttackStart(target); me->CastSpell(target, SPELL_SHADOW_STEP, false); } @@ -1735,7 +1735,7 @@ class npc_hor_lich_king : public CreatureScript s->SetInCombatWithZone(); if (Unit* target = s->SelectNearestPlayer(350.0f)) { - s->AddThreat(target, 1000.0f); + s->GetThreatManager().AddThreat(target, 1000.0f); s->AI()->AttackStart(target); } s->SetHomePosition(PathWaypoints[WP_STOP[currentWall + 1]]); diff --git a/src/server/scripts/Northrend/FrozenHalls/HallsOfReflection/instance_halls_of_reflection.cpp b/src/server/scripts/Northrend/FrozenHalls/HallsOfReflection/instance_halls_of_reflection.cpp index f37361ebbf8ce1..56372b5d4348e4 100644 --- a/src/server/scripts/Northrend/FrozenHalls/HallsOfReflection/instance_halls_of_reflection.cpp +++ b/src/server/scripts/Northrend/FrozenHalls/HallsOfReflection/instance_halls_of_reflection.cpp @@ -537,7 +537,7 @@ class instance_halls_of_reflection : public InstanceMapScript if (TeamIdInInstance == TEAM_ALLIANCE) c->UpdateEntry(NPC_JAINA_PART2); } - c->GetThreatMgr().ClearAllThreat(); + c->GetThreatManager().ClearAllThreat(); c->CombatStop(true); c->InterruptNonMeleeSpells(true); c->GetMotionMaster()->Clear(); @@ -554,7 +554,7 @@ class instance_halls_of_reflection : public InstanceMapScript } if (Creature* c = instance->GetCreature(NPC_LichKingGUID)) { - c->GetThreatMgr().ClearAllThreat(); + c->GetThreatManager().ClearAllThreat(); c->CombatStop(true); c->InterruptNonMeleeSpells(true); c->GetMotionMaster()->Clear(); @@ -898,7 +898,7 @@ class instance_halls_of_reflection : public InstanceMapScript if (TrashActive[i]) if (Creature* c = instance->GetCreature(NPC_TrashGUID[i])) { - c->GetThreatMgr().ClearAllThreat(); + c->GetThreatManager().ClearAllThreat(); c->CombatStop(true); c->InterruptNonMeleeSpells(true); c->SetUnitFlag(UNIT_FLAG_NOT_SELECTABLE); @@ -1119,7 +1119,7 @@ class instance_halls_of_reflection : public InstanceMapScript if (Creature* c = instance->GetCreature(NPC_LichKingGUID)) { c->SetUInt32Value(UNIT_NPC_EMOTESTATE, EMOTE_ONESHOT_NONE); - c->GetThreatMgr().ClearAllThreat(); + c->GetThreatManager().ClearAllThreat(); c->CombatStop(true); c->InterruptNonMeleeSpells(true); c->SetVisible(false); diff --git a/src/server/scripts/Northrend/FrozenHalls/PitOfSaron/pit_of_saron.cpp b/src/server/scripts/Northrend/FrozenHalls/PitOfSaron/pit_of_saron.cpp index 68f159bc4261bc..7354385759c36d 100644 --- a/src/server/scripts/Northrend/FrozenHalls/PitOfSaron/pit_of_saron.cpp +++ b/src/server/scripts/Northrend/FrozenHalls/PitOfSaron/pit_of_saron.cpp @@ -1078,8 +1078,8 @@ class npc_pos_martin_or_gorkun_second : public CreatureScript continue; c->SetInCombatWith(s); s->SetInCombatWith(c); - c->AddThreat(s, 0.0f); - s->AddThreat(c, 0.0f); + c->GetThreatManager().AddThreat(s, 0.0f); + s->GetThreatManager().AddThreat(c, 0.0f); } } events.RescheduleEvent(10, 3000); @@ -1125,7 +1125,7 @@ class npc_pos_freed_slave : public CreatureScript return; me->RemoveEvadeAuras(); - me->GetThreatMgr().ClearAllThreat(); + me->GetThreatManager().ClearAllThreat(); me->CombatStop(true); me->LoadCreaturesAddon(true); me->SetLootRecipient(nullptr); diff --git a/src/server/scripts/Northrend/Gundrak/boss_eck.cpp b/src/server/scripts/Northrend/Gundrak/boss_eck.cpp index 45b3029a2fe3bf..93d84aae87a25d 100644 --- a/src/server/scripts/Northrend/Gundrak/boss_eck.cpp +++ b/src/server/scripts/Northrend/Gundrak/boss_eck.cpp @@ -125,8 +125,8 @@ class boss_eck : public CreatureScript case EVENT_ECK_SPRING: if (Unit* target = SelectTarget(SelectTargetMethod::Random, 1, 30.0f, true)) { - me->GetThreatMgr().ResetAllThreat(); - me->AddThreat(target, 500.0f); + me->GetThreatManager().ResetAllThreat(); + me->GetThreatManager().AddThreat(target, 500.0f); me->CastSpell(target, SPELL_ECK_SPRING, false); } diff --git a/src/server/scripts/Northrend/IcecrownCitadel/boss_blood_prince_council.cpp b/src/server/scripts/Northrend/IcecrownCitadel/boss_blood_prince_council.cpp index fe29caec115ce5..2cf1a6b015f35c 100644 --- a/src/server/scripts/Northrend/IcecrownCitadel/boss_blood_prince_council.cpp +++ b/src/server/scripts/Northrend/IcecrownCitadel/boss_blood_prince_council.cpp @@ -336,7 +336,7 @@ class boss_prince_keleseth_icc : public CreatureScript if (!_isEmpowered) { if (attacker) - me->AddThreat(attacker, float(damage)); + me->GetThreatManager().AddThreat(attacker, float(damage)); damage = 0; } } @@ -606,7 +606,7 @@ class boss_prince_taldaram_icc : public CreatureScript if (!_isEmpowered) { if (attacker) - me->AddThreat(attacker, float(damage)); + me->GetThreatManager().AddThreat(attacker, float(damage)); damage = 0; } } @@ -900,7 +900,7 @@ class boss_prince_valanar_icc : public CreatureScript if (!_isEmpowered) { if (attacker) - me->AddThreat(attacker, float(damage)); + me->GetThreatManager().AddThreat(attacker, float(damage)); damage = 0; } } @@ -1206,8 +1206,8 @@ class npc_dark_nucleus : public CreatureScript if (!attacker || attacker == me || attacker == me->GetVictim() || (det != DIRECT_DAMAGE && det != SPELL_DIRECT_DAMAGE)) return; - me->GetThreatMgr().ClearAllThreat(); - me->AddThreat(attacker, 500000000.0f); + me->GetThreatManager().ClearAllThreat(); + me->GetThreatManager().AddThreat(attacker, 500000000.0f); } void JustDied(Unit* /*killer*/) override @@ -1321,7 +1321,7 @@ class npc_ball_of_flame : public CreatureScript if (me->GetEntry() == NPC_BALL_OF_INFERNO_FLAME) me->CastSpell(me, SPELL_BALL_OF_FLAMES_PERIODIC, true); me->ClearUnitState(UNIT_STATE_CASTING | UNIT_STATE_STUNNED); - me->AddThreat(target, 1000.0f); + me->GetThreatManager().AddThreat(target, 1000.0f); me->SetCanFly(true); me->SetDisableGravity(true); me->SetHover(true); diff --git a/src/server/scripts/Northrend/IcecrownCitadel/boss_blood_queen_lana_thel.cpp b/src/server/scripts/Northrend/IcecrownCitadel/boss_blood_queen_lana_thel.cpp index 275b5f25bf0481..c414136d8a3cd7 100644 --- a/src/server/scripts/Northrend/IcecrownCitadel/boss_blood_queen_lana_thel.cpp +++ b/src/server/scripts/Northrend/IcecrownCitadel/boss_blood_queen_lana_thel.cpp @@ -334,7 +334,7 @@ class boss_blood_queen_lana_thel : public CreatureScript if (Player* p = itr->GetSource()) if (p->IsAlive() && p != me->GetVictim() && p->GetGUID() != _offtankGUID && !p->IsGameMaster() && p->GetDistance(me) < 70.0f) { - float th = me->GetThreatMgr().getThreatWithoutTemp(p); + float th = me->GetThreatManager().GetThreat(p); if (!target || th > maxThreat) { target = p; diff --git a/src/server/scripts/Northrend/IcecrownCitadel/boss_icecrown_gunship_battle.cpp b/src/server/scripts/Northrend/IcecrownCitadel/boss_icecrown_gunship_battle.cpp index 3f151ab85d2bd1..f8c4f37019839b 100644 --- a/src/server/scripts/Northrend/IcecrownCitadel/boss_icecrown_gunship_battle.cpp +++ b/src/server/scripts/Northrend/IcecrownCitadel/boss_icecrown_gunship_battle.cpp @@ -786,7 +786,7 @@ class npc_high_overlord_saurfang_igb : public CreatureScript { if (!me->IsAlive()) return; - me->GetThreatMgr().ClearAllThreat(); + me->GetThreatManager().ClearAllThreat(); me->CombatStop(true); me->GetMotionMaster()->MoveTargetedHome(); Reset(); @@ -907,7 +907,7 @@ class npc_high_overlord_saurfang_igb : public CreatureScript { me->SetInCombatWith(p); p->SetInCombatWith(me); - me->AddThreat(p, 0.0f); + me->GetThreatManager().AddThreat(p, 0.0f); } } else @@ -947,11 +947,7 @@ class npc_high_overlord_saurfang_igb : public CreatureScript if (_instance->GetBossState(DATA_ICECROWN_GUNSHIP_BATTLE) == IN_PROGRESS) { //_instance->DoCastSpellOnPlayers(SPELL_LOCK_PLAYERS_AND_TAP_CHEST); - Map::PlayerList const& pl = me->GetMap()->GetPlayers(); - for (Map::PlayerList::const_iterator itr = pl.begin(); itr != pl.end(); ++itr) - if (Player* p = itr->GetSource()) - if (!p->IsGameMaster()) - p->SetInCombatState(true); + _instance->DoCastSpellOnPlayers(SPELL_LOCK_PLAYERS_AND_TAP_CHEST); _events.ScheduleEvent(EVENT_KEEP_PLAYER_IN_COMBAT, 4s); } break; @@ -1122,7 +1118,7 @@ class npc_muradin_bronzebeard_igb : public CreatureScript { if (!me->IsAlive()) return; - me->GetThreatMgr().ClearAllThreat(); + me->GetThreatManager().ClearAllThreat(); me->CombatStop(true); me->GetMotionMaster()->MoveTargetedHome(); Reset(); @@ -1243,7 +1239,7 @@ class npc_muradin_bronzebeard_igb : public CreatureScript { me->SetInCombatWith(p); p->SetInCombatWith(me); - me->AddThreat(p, 0.0f); + me->GetThreatManager().AddThreat(p, 0.0f); } } else @@ -1286,11 +1282,7 @@ class npc_muradin_bronzebeard_igb : public CreatureScript if (_instance->GetBossState(DATA_ICECROWN_GUNSHIP_BATTLE) == IN_PROGRESS) { //_instance->DoCastSpellOnPlayers(SPELL_LOCK_PLAYERS_AND_TAP_CHEST); - Map::PlayerList const& pl = me->GetMap()->GetPlayers(); - for (Map::PlayerList::const_iterator itr = pl.begin(); itr != pl.end(); ++itr) - if (Player* p = itr->GetSource()) - if (!p->IsGameMaster()) - p->SetInCombatState(true); + _instance->DoCastSpellOnPlayers(SPELL_LOCK_PLAYERS_AND_TAP_CHEST); _events.ScheduleEvent(EVENT_KEEP_PLAYER_IN_COMBAT, 4s); } break; @@ -1498,7 +1490,7 @@ struct gunship_npc_AI : public ScriptedAI { if (!me->IsAlive() || !me->IsInCombat()) return; - me->GetThreatMgr().ClearAllThreat(); + me->GetThreatManager().ClearAllThreat(); me->CombatStop(true); me->GetMotionMaster()->MoveTargetedHome(); Reset(); @@ -1559,7 +1551,7 @@ struct npc_gunship_boarding_addAI : public ScriptedAI { if (!me->IsAlive() || !me->IsInCombat()) return; - me->GetThreatMgr().ClearAllThreat(); + me->GetThreatManager().ClearAllThreat(); me->CombatStop(true); me->GetMotionMaster()->MoveTargetedHome(); Reset(); @@ -1622,7 +1614,7 @@ struct npc_gunship_boarding_addAI : public ScriptedAI anyValid = true; me->SetInCombatWith(p); p->SetInCombatWith(me); - me->AddThreat(p, 0.0f); + me->GetThreatManager().AddThreat(p, 0.0f); } } else @@ -1783,7 +1775,7 @@ class npc_gunship_mage : public CreatureScript if (Player* player = me->SelectNearestPlayer(50.0f)) { me->SetInCombatWithZone(); - me->AddThreat(player, 1.0f); + me->GetThreatManager().AddThreat(player, 1.0f); } me->CastSpell((Unit*)nullptr, SPELL_BELOW_ZERO, false); break; @@ -1864,7 +1856,7 @@ class npc_gunship_gunner : public CreatureScript anyValid = true; me->SetInCombatWith(p); p->SetInCombatWith(me); - me->AddThreat(p, 0.0f); + me->GetThreatManager().AddThreat(p, 0.0f); } } else diff --git a/src/server/scripts/Northrend/IcecrownCitadel/boss_lady_deathwhisper.cpp b/src/server/scripts/Northrend/IcecrownCitadel/boss_lady_deathwhisper.cpp index cc62e50a951e2b..0d94eac1f49220 100644 --- a/src/server/scripts/Northrend/IcecrownCitadel/boss_lady_deathwhisper.cpp +++ b/src/server/scripts/Northrend/IcecrownCitadel/boss_lady_deathwhisper.cpp @@ -301,10 +301,10 @@ class boss_lady_deathwhisper : public CreatureScript 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) + auto tlist = me->GetThreatManager().GetUnsortedThreatList(); + for (auto t : tlist) { - Unit* unit = ObjectAccessor::GetUnit((*me), (*itr)->getUnitGuid()); + Unit* unit = ObjectAccessor::GetUnit((*me), t->GetVictim()->GetGUID()); if (unit && DoGetThreat(unit)) DoModifyThreatByPercent(unit, -100); @@ -517,7 +517,7 @@ class boss_lady_deathwhisper : public CreatureScript { darnavan->RemoveAllAuras(); darnavan->SetFaction(FACTION_FRIENDLY); - darnavan->GetThreatMgr().ClearAllThreat(); + darnavan->GetThreatManager().ClearAllThreat(); darnavan->CombatStop(true); darnavan->GetMotionMaster()->MoveIdle(); darnavan->StopMoving(); @@ -932,8 +932,8 @@ class npc_vengeful_shade : public CreatureScript ScriptedAI::AttackStart(who); if (!targetGUID) { - me->GetThreatMgr().ResetAllThreat(); - me->AddThreat(who, 1000000.0f); + me->GetThreatManager().ResetAllThreat(); + me->GetThreatManager().AddThreat(who, 1000000.0f); targetGUID = who->GetGUID(); } } diff --git a/src/server/scripts/Northrend/IcecrownCitadel/boss_professor_putricide.cpp b/src/server/scripts/Northrend/IcecrownCitadel/boss_professor_putricide.cpp index 5a063c3c3aef27..a961bf3890892d 100644 --- a/src/server/scripts/Northrend/IcecrownCitadel/boss_professor_putricide.cpp +++ b/src/server/scripts/Northrend/IcecrownCitadel/boss_professor_putricide.cpp @@ -1151,10 +1151,10 @@ class spell_putricide_ooze_channel : public SpellScriptLoader void StartAttack() { GetCaster()->ClearUnitState(UNIT_STATE_CASTING); - GetCaster()->GetThreatMgr().ClearAllThreat(); + GetCaster()->GetThreatManager().ClearAllThreat(); GetCaster()->ToCreature()->SetInCombatWithZone(); GetCaster()->ToCreature()->AI()->AttackStart(GetHitUnit()); - GetCaster()->AddThreat(GetHitUnit(), 500000000.0f); // value seen in sniff + GetCaster()->GetThreatManager().AddThreat(GetHitUnit(), 500000000.0f); // value seen in sniff if (Creature* c = GetCaster()->ToCreature()) c->AI()->SetGUID(GetHitUnit()->GetGUID(), -1); } diff --git a/src/server/scripts/Northrend/IcecrownCitadel/boss_rotface.cpp b/src/server/scripts/Northrend/IcecrownCitadel/boss_rotface.cpp index 9fa5a9354d3b2f..626ffa72d2a7f2 100644 --- a/src/server/scripts/Northrend/IcecrownCitadel/boss_rotface.cpp +++ b/src/server/scripts/Northrend/IcecrownCitadel/boss_rotface.cpp @@ -365,7 +365,7 @@ class npc_little_ooze : public CreatureScript return; } - me->AddThreat(summoner->ToUnit(), 500000.0f); + me->GetThreatManager().AddThreat(summoner->ToUnit(), 500000.0f); AttackStart(summoner->ToUnit()); } @@ -390,7 +390,7 @@ class npc_little_ooze : public CreatureScript if (TempSummon* ts = me->ToTempSummon()) if (Unit* summoner = ts->GetSummonerUnit()) { - me->AddThreat(summoner, 500000.0f); + me->GetThreatManager().AddThreat(summoner, 500000.0f); AttackStart(summoner); } } diff --git a/src/server/scripts/Northrend/IcecrownCitadel/boss_sindragosa.cpp b/src/server/scripts/Northrend/IcecrownCitadel/boss_sindragosa.cpp index b1ab5028ee199e..753ae73eae3dff 100644 --- a/src/server/scripts/Northrend/IcecrownCitadel/boss_sindragosa.cpp +++ b/src/server/scripts/Northrend/IcecrownCitadel/boss_sindragosa.cpp @@ -1921,7 +1921,7 @@ class spell_frostwarden_handler_focus_fire : public SpellScriptLoader void HandleScript(SpellEffIndex effIndex) { PreventHitDefaultEffect(effIndex); - GetCaster()->AddThreat(GetHitUnit(), float(GetEffectValue())); + GetCaster()->GetThreatManager().AddThreat(GetHitUnit(), float(GetEffectValue())); GetCaster()->GetAI()->SetData(DATA_WHELP_MARKER, 1); } @@ -1940,7 +1940,7 @@ class spell_frostwarden_handler_focus_fire : public SpellScriptLoader PreventDefaultAction(); if (Unit* caster = GetCaster()) { - caster->AddThreat(GetTarget(), -float(GetSpellInfo()->Effects[EFFECT_1].CalcValue())); + caster->GetThreatManager().AddThreat(GetTarget(), -float(GetSpellInfo()->Effects[EFFECT_1].CalcValue())); caster->GetAI()->SetData(DATA_WHELP_MARKER, 0); } } diff --git a/src/server/scripts/Northrend/IcecrownCitadel/boss_the_lich_king.cpp b/src/server/scripts/Northrend/IcecrownCitadel/boss_the_lich_king.cpp index 96ec7df6b33564..7093521f63868e 100644 --- a/src/server/scripts/Northrend/IcecrownCitadel/boss_the_lich_king.cpp +++ b/src/server/scripts/Northrend/IcecrownCitadel/boss_the_lich_king.cpp @@ -504,7 +504,7 @@ class StartMovementEvent : public BasicEvent bool Execute(uint64 /*time*/, uint32 /*diff*/) override { _owner->SetReactState(REACT_AGGRESSIVE); - if (!_owner->GetThreatMgr().isThreatListEmpty()) + if (!_owner->GetThreatManager().isThreatListEmpty()) if (Unit* target = _owner->SelectVictim()) _owner->AI()->AttackStart(target); if (!_owner->GetVictim()) @@ -1655,7 +1655,7 @@ class npc_tirion_fordring_tft : public CreatureScript return; } - me->GetThreatMgr().ClearAllThreat(); + me->GetThreatManager().ClearAllThreat(); me->CombatStop(false); } @@ -2386,7 +2386,7 @@ class npc_raging_spirit : public CreatureScript { me->SetControlled(false, UNIT_STATE_ROOT); - if (!me->GetThreatMgr().isThreatListEmpty()) + if (!me->GetThreatManager().isThreatListEmpty()) if (Unit* target = me->SelectVictim()) AttackStart(target); if (!me->GetVictim()) @@ -3025,7 +3025,7 @@ class spell_the_lich_king_vile_spirit_move_target_search : public SpellScriptLoa GetCaster()->ToCreature()->SetInCombatWithZone(); GetCaster()->ToCreature()->AI()->AttackStart(GetHitUnit()); - GetCaster()->AddThreat(GetHitUnit(), GetCaster()->GetMaxHealth() * 0.2f); + GetCaster()->GetThreatManager().AddThreat(GetHitUnit(), GetCaster()->GetMaxHealth() * 0.2f); } void Register() override @@ -3378,7 +3378,7 @@ class npc_terenas_menethil : public CreatureScript if (!me->IsAlive()) return; - me->GetThreatMgr().ClearAllThreat(); + me->GetThreatManager().ClearAllThreat(); me->CombatStop(false); } }; diff --git a/src/server/scripts/Northrend/IcecrownCitadel/boss_valithria_dreamwalker.cpp b/src/server/scripts/Northrend/IcecrownCitadel/boss_valithria_dreamwalker.cpp index 035d4bf34be0f6..2178904103c307 100644 --- a/src/server/scripts/Northrend/IcecrownCitadel/boss_valithria_dreamwalker.cpp +++ b/src/server/scripts/Northrend/IcecrownCitadel/boss_valithria_dreamwalker.cpp @@ -590,12 +590,11 @@ class npc_green_dragon_combat_trigger : public CreatureScript { 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->GetTypeId() == TYPEID_PLAYER && me->GetExactDist(target) < 200.0f && !target->IsImmunedToDamageOrSchool(SPELL_SCHOOL_MASK_ALL)) - return; + auto tlist = me->GetThreatManager().GetUnsortedThreatList(); + for (auto t : tlist) + if (Unit* target = t->GetVictim()) + if (target->IsAlive() && target->GetTypeId() == TYPEID_PLAYER && me->GetExactDist(target) < 200.0f && !target->IsImmunedToDamageOrSchool(SPELL_SCHOOL_MASK_ALL)) + return; EnterEvadeMode(); } else diff --git a/src/server/scripts/Northrend/IcecrownCitadel/icecrown_citadel.cpp b/src/server/scripts/Northrend/IcecrownCitadel/icecrown_citadel.cpp index 9a0fcb967c6106..3a3751fca85d57 100644 --- a/src/server/scripts/Northrend/IcecrownCitadel/icecrown_citadel.cpp +++ b/src/server/scripts/Northrend/IcecrownCitadel/icecrown_citadel.cpp @@ -771,7 +771,7 @@ class npc_crok_scourgebane : public CreatureScript _handledWP4 = false; me->CombatStop(); - me->GetThreatMgr().ClearAllThreat(); + me->GetThreatManager().ClearAllThreat(); } } @@ -1765,15 +1765,15 @@ class npc_frostwing_vrykul : public CreatureScript 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(); + DoResetThreatList(); - for (ThreatContainer::StorageType::const_iterator iter = target_tl.begin(); iter != target_tl.end(); ++iter) - me->GetThreatMgr().AddThreat((*iter)->getTarget(), (*iter)->GetThreat()); + auto ttlist = c->GetThreatManager().GetUnsortedThreatList(); + for (auto t : ttlist) + me->GetThreatManager().AddThreat(t->GetVictim(), t->GetThreat()); - c->GetThreatMgr().ResetAllThreat(); - for (ThreatContainer::StorageType::const_iterator iter = me_tl.begin(); iter != me_tl.end(); ++iter) - c->GetThreatMgr().AddThreat((*iter)->getTarget(), (*iter)->GetThreat()); + auto me_tl = me->GetThreatManager().GetUnsortedThreatList(); + for (auto m : me_tl) + c->GetThreatManager().AddThreat(m->GetVictim(), m->GetThreat()); } } @@ -2028,7 +2028,7 @@ class spell_icc_geist_alarm : public SpellScriptLoader } l->AI()->Talk(0); l->AI()->AttackStart(target); - l->AddThreat(target, 1.0f); + l->GetThreatManager().AddThreat(target, 1.0f); for (uint8 i = 0; i < 5; ++i) { float dist = 2.0f + rand_norm() * 4.0f; @@ -2038,7 +2038,7 @@ class spell_icc_geist_alarm : public SpellScriptLoader if (Creature* c = l->SummonCreature(NPC_VENGEFUL_FLESHREAPER, pos, TEMPSUMMON_TIMED_DESPAWN_OUT_OF_COMBAT, 30 * MINUTE * IN_MILLISECONDS)) { c->AI()->AttackStart(l->GetVictim()); - c->AddThreat(l->GetVictim(), 1.0f); + c->GetThreatManager().AddThreat(l->GetVictim(), 1.0f); if (!hasTarget) c->GetMotionMaster()->MoveJump(c->GetPositionX(), c->GetPositionY() + 55.0f, c->GetPositionZ(), 20.0f, 6.0f); } diff --git a/src/server/scripts/Northrend/IcecrownCitadel/instance_icecrown_citadel.cpp b/src/server/scripts/Northrend/IcecrownCitadel/instance_icecrown_citadel.cpp index ac4c460d3650f3..a0d3010c2d9824 100644 --- a/src/server/scripts/Northrend/IcecrownCitadel/instance_icecrown_citadel.cpp +++ b/src/server/scripts/Northrend/IcecrownCitadel/instance_icecrown_citadel.cpp @@ -1864,7 +1864,7 @@ class instance_icecrown_citadel : public InstanceMapScript if (Creature* warden = instance->SummonCreature(NPC_SPIRIT_WARDEN, SpiritWardenSpawn, nullptr, 65000)) { terenas->AI()->AttackStart(warden); - warden->AddThreat(terenas, 300000.0f); + warden->GetThreatManager().AddThreat(terenas, 300000.0f); } } break; diff --git a/src/server/scripts/Northrend/Naxxramas/boss_gothik.cpp b/src/server/scripts/Northrend/Naxxramas/boss_gothik.cpp index 43bc0090f03e53..d7da491dc7bc74 100644 --- a/src/server/scripts/Northrend/Naxxramas/boss_gothik.cpp +++ b/src/server/scripts/Northrend/Naxxramas/boss_gothik.cpp @@ -472,10 +472,10 @@ class boss_gothik : public CreatureScript { me->CastSpell(me, SPELL_TELEPORT_LIVE, false); } - me->GetThreatMgr().resetAggro(NotOnSameSide(me)); + DoResetThreatList(); if (Unit* pTarget = SelectTarget(SelectTargetMethod::MaxDistance, 0)) { - me->GetThreatMgr().AddThreat(pTarget, 100.0f); + me->GetThreatManager().AddThreat(pTarget, 100.0f); AttackStart(pTarget); } events.Repeat(20s); diff --git a/src/server/scripts/Northrend/Naxxramas/boss_kelthuzad.cpp b/src/server/scripts/Northrend/Naxxramas/boss_kelthuzad.cpp index 4ec9cf9762568b..b253180b191221 100644 --- a/src/server/scripts/Northrend/Naxxramas/boss_kelthuzad.cpp +++ b/src/server/scripts/Northrend/Naxxramas/boss_kelthuzad.cpp @@ -439,14 +439,14 @@ class boss_kelthuzad : public CreatureScript case EVENT_DETONATE_MANA: { std::vector unitList; - ThreatContainer::StorageType const& threatList = me->GetThreatMgr().GetThreatList(); - for (auto itr : threatList) + auto tList = me->GetThreatManager().GetUnsortedThreatList(); + for (auto t : tList) { - if (itr->getTarget()->GetTypeId() == TYPEID_PLAYER - && itr->getTarget()->getPowerType() == POWER_MANA - && itr->getTarget()->GetPower(POWER_MANA)) + if (t->GetVictim()->GetTypeId() == TYPEID_PLAYER + && t->GetVictim()->getPowerType() == POWER_MANA + && t->GetVictim()->GetPower(POWER_MANA)) { - unitList.push_back(itr->getTarget()); + unitList.push_back(t->GetVictim()); } } if (!unitList.empty()) @@ -598,7 +598,7 @@ class boss_kelthuzad_minion : public CreatureScript if (me->GetEntry() != NPC_UNSTOPPABLE_ABOMINATION && me->GetEntry() != NPC_GUARDIAN_OF_ICECROWN) { - me->AddThreat(who, 1000000.0f); + me->GetThreatManager().AddThreat(who, 1000000.0f); } } diff --git a/src/server/scripts/Northrend/Naxxramas/boss_patchwerk.cpp b/src/server/scripts/Northrend/Naxxramas/boss_patchwerk.cpp index 0ae9feaf574bfa..d626d80015299e 100644 --- a/src/server/scripts/Northrend/Naxxramas/boss_patchwerk.cpp +++ b/src/server/scripts/Northrend/Naxxramas/boss_patchwerk.cpp @@ -128,11 +128,11 @@ class boss_patchwerk : public CreatureScript std::list meleeRangeTargets; Unit* finalTarget = nullptr; uint8 counter = 0; - auto i = me->GetThreatMgr().GetThreatList().begin(); - for (; i != me->GetThreatMgr().GetThreatList().end(); ++i, ++counter) + auto tlist = me->GetThreatManager().GetUnsortedThreatList(); + for (auto t : tlist) { // Gather all units with melee range - Unit* target = (*i)->getTarget(); + Unit* target = t->GetVictim(); if (me->IsWithinMeleeRange(target)) { meleeRangeTargets.push_back(target); @@ -140,7 +140,7 @@ class boss_patchwerk : public CreatureScript // and add threat to most hated if (counter < RAID_MODE(2, 3)) { - me->AddThreat(target, 500.0f); + me->GetThreatManager().AddThreat(target, 500.0f); } } counter = 0; diff --git a/src/server/scripts/Northrend/Naxxramas/boss_sapphiron.cpp b/src/server/scripts/Northrend/Naxxramas/boss_sapphiron.cpp index 47cccd7e2c885a..4e953394bf268c 100644 --- a/src/server/scripts/Northrend/Naxxramas/boss_sapphiron.cpp +++ b/src/server/scripts/Northrend/Naxxramas/boss_sapphiron.cpp @@ -153,7 +153,7 @@ class boss_sapphiron : public CreatureScript { me->SetInCombatWith(player); player->SetInCombatWith(me); - me->AddThreat(player, 0.0f); + me->GetThreatManager().AddThreat(player, 0.0f); } } } @@ -322,17 +322,17 @@ class boss_sapphiron : public CreatureScript } std::vector targets; - auto i = me->GetThreatMgr().GetThreatList().begin(); - for (; i != me->GetThreatMgr().GetThreatList().end(); ++i) + auto tlist = me->GetThreatManager().GetUnsortedThreatList(); + for (auto t : tlist) { - if ((*i)->getTarget()->GetTypeId() == TYPEID_PLAYER) + if (t->GetVictim()->GetTypeId() == TYPEID_PLAYER) { bool inList = false; if (!blockList.empty()) { for (GuidList::const_iterator itr = blockList.begin(); itr != blockList.end(); ++itr) { - if ((*i)->getTarget()->GetGUID() == *itr) + if (t->GetVictim()->GetGUID() == *itr) { inList = true; break; @@ -341,7 +341,7 @@ class boss_sapphiron : public CreatureScript } if (!inList) { - targets.push_back((*i)->getTarget()); + targets.push_back(t->GetVictim()); } } } diff --git a/src/server/scripts/Northrend/Naxxramas/boss_thaddius.cpp b/src/server/scripts/Northrend/Naxxramas/boss_thaddius.cpp index b2bf2a7273e3dc..f851d8521edca8 100644 --- a/src/server/scripts/Northrend/Naxxramas/boss_thaddius.cpp +++ b/src/server/scripts/Northrend/Naxxramas/boss_thaddius.cpp @@ -448,7 +448,7 @@ class boss_thaddius_summon : public CreatureScript if (Creature* cr = ObjectAccessor::GetCreature(*me, pInstance->GetGuidData(DATA_THADDIUS_BOSS))) { cr->AI()->AttackStart(pWho); - cr->AddThreat(pWho, 10.0f); + cr->GetThreatManager().AddThreat(pWho, 10.0f); } } } @@ -555,18 +555,18 @@ class boss_thaddius_summon : public CreatureScript if (!feugen->IsAlive() || !feugen->GetVictim() || !me->GetVictim()) return; - float threatFeugen = feugen->GetThreatMgr().GetThreat(feugen->GetVictim()); - float threatStalagg = me->GetThreatMgr().GetThreat(me->GetVictim()); + float threatFeugen = feugen->GetThreatManager().GetThreat(feugen->GetVictim()); + float threatStalagg = me->GetThreatManager().GetThreat(me->GetVictim()); Unit* tankFeugen = feugen->GetVictim(); Unit* tankStalagg = me->GetVictim(); - feugen->GetThreatMgr().ModifyThreatByPercent(tankFeugen, -100); - feugen->AddThreat(tankStalagg, threatFeugen); + feugen->GetThreatManager().ModifyThreatByPercent(tankFeugen, -100); + feugen->GetThreatManager().AddThreat(tankStalagg, threatFeugen); feugen->CastSpell(tankStalagg, SPELL_MAGNETIC_PULL, true); feugen->AI()->DoAction(ACTION_MAGNETIC_PULL); - me->GetThreatMgr().ModifyThreatByPercent(tankStalagg, -100); - me->AddThreat(tankFeugen, threatStalagg); + me->GetThreatManager().ModifyThreatByPercent(tankStalagg, -100); + me->GetThreatManager().AddThreat(tankFeugen, threatStalagg); me->CastSpell(tankFeugen, SPELL_MAGNETIC_PULL, true); DoAction(ACTION_MAGNETIC_PULL); } diff --git a/src/server/scripts/Northrend/Nexus/EyeOfEternity/boss_malygos.cpp b/src/server/scripts/Northrend/Nexus/EyeOfEternity/boss_malygos.cpp index dbb3413b638411..21133714a00ea0 100644 --- a/src/server/scripts/Northrend/Nexus/EyeOfEternity/boss_malygos.cpp +++ b/src/server/scripts/Northrend/Nexus/EyeOfEternity/boss_malygos.cpp @@ -710,7 +710,7 @@ class boss_malygos : public CreatureScript me->SendMeleeAttackStop(me->GetVictim()); me->GetMotionMaster()->MoveTakeoff(MI_POINT_PH_3_FIGHT_POSITION, CenterPos.GetPositionX(), CenterPos.GetPositionY(), CenterPos.GetPositionZ() - 5.0f, me->GetSpeed(MOVE_RUN)); - me->GetThreatMgr().ClearAllThreat(); // players on vehicle are unattackable -> leads to EnterEvadeMode() because target is not acceptable! + me->GetThreatManager().ClearAllThreat(); // players on vehicle are unattackable -> leads to EnterEvadeMode() because target is not acceptable! // mount players: Map::PlayerList const& PlayerList = me->GetMap()->GetPlayers(); diff --git a/src/server/scripts/Northrend/Nexus/Nexus/instance_nexus.cpp b/src/server/scripts/Northrend/Nexus/Nexus/instance_nexus.cpp index 14e3acddb86089..6cdb5da3eb049e 100644 --- a/src/server/scripts/Northrend/Nexus/Nexus/instance_nexus.cpp +++ b/src/server/scripts/Northrend/Nexus/Nexus/instance_nexus.cpp @@ -213,7 +213,7 @@ class npc_crystalline_frayer : public CreatureScript if (!_allowDeath) { me->RemoveAllAuras(); - me->GetThreatMgr().ClearAllThreat(); + me->GetThreatManager().ClearAllThreat(); me->CombatStop(true); damage = 0; diff --git a/src/server/scripts/Northrend/Nexus/Oculus/boss_urom.cpp b/src/server/scripts/Northrend/Nexus/Oculus/boss_urom.cpp index f5547129e5f876..c1e59b94e13071 100644 --- a/src/server/scripts/Northrend/Nexus/Oculus/boss_urom.cpp +++ b/src/server/scripts/Northrend/Nexus/Oculus/boss_urom.cpp @@ -208,7 +208,7 @@ class boss_urom : public CreatureScript void LeaveCombat() { me->RemoveAllAuras(); - me->GetThreatMgr().ClearAllThreat(); + me->GetThreatManager().ClearAllThreat(); me->CombatStop(true); me->LoadCreaturesAddon(true); me->SetLootRecipient(nullptr); diff --git a/src/server/scripts/Northrend/Ulduar/HallsOfLightning/boss_ionar.cpp b/src/server/scripts/Northrend/Ulduar/HallsOfLightning/boss_ionar.cpp index 2624f55ad79aea..fc4eb2eeb019b8 100644 --- a/src/server/scripts/Northrend/Ulduar/HallsOfLightning/boss_ionar.cpp +++ b/src/server/scripts/Northrend/Ulduar/HallsOfLightning/boss_ionar.cpp @@ -255,7 +255,7 @@ class npc_spark_of_ionar : public CreatureScript if (param == ACTION_CALLBACK) { me->SetSpeed(MOVE_RUN, 2.5f); - me->GetThreatMgr().ClearAllThreat(); + me->GetThreatManager().ClearAllThreat(); me->CombatStop(true); me->GetMotionMaster()->MoveTargetedHome(); returning = true; diff --git a/src/server/scripts/Northrend/Ulduar/HallsOfStone/brann_bronzebeard.cpp b/src/server/scripts/Northrend/Ulduar/HallsOfStone/brann_bronzebeard.cpp index 59a264650752f2..5d78ee9e9dfa20 100644 --- a/src/server/scripts/Northrend/Ulduar/HallsOfStone/brann_bronzebeard.cpp +++ b/src/server/scripts/Northrend/Ulduar/HallsOfStone/brann_bronzebeard.cpp @@ -589,7 +589,7 @@ class brann_bronzebeard : public CreatureScript if(cr) { cr->AI()->AttackStart(me); - cr->AddThreat(me, 100.0f); + cr->GetThreatManager().AddThreat(me, 100.0f); cr->SetInCombatWithZone(); } } diff --git a/src/server/scripts/Northrend/Ulduar/Ulduar/boss_algalon_the_observer.cpp b/src/server/scripts/Northrend/Ulduar/Ulduar/boss_algalon_the_observer.cpp index 98b1784802b412..4c3c00714a273c 100644 --- a/src/server/scripts/Northrend/Ulduar/Ulduar/boss_algalon_the_observer.cpp +++ b/src/server/scripts/Northrend/Ulduar/Ulduar/boss_algalon_the_observer.cpp @@ -1005,7 +1005,7 @@ class npc_living_constellation : public CreatureScript if (Player* target = SelectTargetFromPlayerList(250.0f)) { AttackStart(target); - me->AddThreat(target, 100.0f); + me->GetThreatManager().AddThreat(target, 100.0f); } me->SetInCombatWithZone(); break; diff --git a/src/server/scripts/Northrend/Ulduar/Ulduar/boss_flame_leviathan.cpp b/src/server/scripts/Northrend/Ulduar/Ulduar/boss_flame_leviathan.cpp index 02b7569b726882..d7d1ea220e03f6 100644 --- a/src/server/scripts/Northrend/Ulduar/Ulduar/boss_flame_leviathan.cpp +++ b/src/server/scripts/Northrend/Ulduar/Ulduar/boss_flame_leviathan.cpp @@ -1659,9 +1659,9 @@ class spell_pursue : public SpellScriptLoader if (!target || !caster) return; - caster->GetThreatMgr().ResetAllThreat(); + caster->GetThreatManager().ResetAllThreat(); caster->GetAI()->AttackStart(target); // Chase target - caster->AddThreat(target, 10000000.0f); + caster->GetThreatManager().AddThreat(target, 10000000.0f); } void Register() override diff --git a/src/server/scripts/Northrend/Ulduar/Ulduar/boss_ignis.cpp b/src/server/scripts/Northrend/Ulduar/Ulduar/boss_ignis.cpp index 38f9e657a92afe..9a0825f8dc6694 100644 --- a/src/server/scripts/Northrend/Ulduar/Ulduar/boss_ignis.cpp +++ b/src/server/scripts/Northrend/Ulduar/Ulduar/boss_ignis.cpp @@ -140,7 +140,7 @@ class npc_ulduar_iron_construct : public CreatureScript heat->ModStackAmount(-1); } me->CastSpell(me, SPELL_MOLTEN, true); - me->GetThreatMgr().ResetAllThreat(); + me->GetThreatManager().ResetAllThreat(); } } } diff --git a/src/server/scripts/Northrend/Ulduar/Ulduar/boss_kologarn.cpp b/src/server/scripts/Northrend/Ulduar/Ulduar/boss_kologarn.cpp index 6af801f5439721..d98aa3fbd264df 100644 --- a/src/server/scripts/Northrend/Ulduar/Ulduar/boss_kologarn.cpp +++ b/src/server/scripts/Northrend/Ulduar/Ulduar/boss_kologarn.cpp @@ -672,7 +672,7 @@ class StoneGripTargetSelector bool operator() (WorldObject* target) const { - if (target == _victim && _me->GetThreatMgr().GetThreatListSize() > 1) + if (target == _victim && _me->GetThreatManager().GetThreatListSize() > 1) return true; if (target->GetTypeId() != TYPEID_PLAYER) diff --git a/src/server/scripts/Northrend/Ulduar/Ulduar/boss_thorim.cpp b/src/server/scripts/Northrend/Ulduar/Ulduar/boss_thorim.cpp index 5894409bef73f0..e70b415c56728e 100644 --- a/src/server/scripts/Northrend/Ulduar/Ulduar/boss_thorim.cpp +++ b/src/server/scripts/Northrend/Ulduar/Ulduar/boss_thorim.cpp @@ -557,7 +557,7 @@ class boss_thorim : public CreatureScript DoResetThreatList(); if (Player* player = GetArenaPlayer()) - me->AddThreat(player, 1000.0f); + me->GetThreatManager().AddThreat(player, 1000.0f); } if (damage >= me->GetHealth()) @@ -1116,7 +1116,7 @@ class boss_thorim_start_npcs : public CreatureScript } } _playerAttack = true; - me->GetThreatMgr().ResetAllThreat(); + me->GetThreatManager().ResetAllThreat(); me->CallForHelp(40.0f); AttackStart(who); } @@ -1636,7 +1636,7 @@ class boss_thorim_arena_npcs : public CreatureScript if (target) { AttackStart(target); - me->AddThreat(target, 500.0f); + me->GetThreatManager().AddThreat(target, 500.0f); if (me->GetEntry() == NPC_DARK_RUNE_EVOKER && urand(0, 1)) me->CastSpell(me, SPELL_RUNIC_SHIELD, false); else if (me->GetEntry() == NPC_DARK_RUNE_CHAMPION && !urand(0, 2)) diff --git a/src/server/scripts/Northrend/Ulduar/Ulduar/boss_yoggsaron.cpp b/src/server/scripts/Northrend/Ulduar/Ulduar/boss_yoggsaron.cpp index 3516427115c550..7d700592510018 100644 --- a/src/server/scripts/Northrend/Ulduar/Ulduar/boss_yoggsaron.cpp +++ b/src/server/scripts/Northrend/Ulduar/Ulduar/boss_yoggsaron.cpp @@ -1541,7 +1541,7 @@ class boss_yoggsaron_crusher_tentacle : public CreatureScript if (who && damagetype == DIRECT_DAMAGE) { DoResetThreatList(); - me->AddThreat(who, 100000); + me->GetThreatManager().AddThreat(who, 100000); AttackStart(who); me->InterruptNonMeleeSpells(false); } diff --git a/src/server/scripts/Northrend/UtgardeKeep/UtgardeKeep/boss_keleseth.cpp b/src/server/scripts/Northrend/UtgardeKeep/UtgardeKeep/boss_keleseth.cpp index c4b99d7831ee56..a2ffde962e1443 100644 --- a/src/server/scripts/Northrend/UtgardeKeep/UtgardeKeep/boss_keleseth.cpp +++ b/src/server/scripts/Northrend/UtgardeKeep/UtgardeKeep/boss_keleseth.cpp @@ -218,7 +218,7 @@ class boss_keleseth : public CreatureScript if( Creature* c = me->SummonCreature(NPC_SKELETON, 156.2f + cos(angle) * dist, 259.1f + std::sin(angle) * dist, 42.9f, 0, TEMPSUMMON_CORPSE_TIMED_DESPAWN, 20000) ) if( Unit* target = c->SelectNearestTarget(250.0f) ) { - c->AddThreat(target, 5.0f); + c->GetThreatManager().AddThreat(target, 5.0f); DoZoneInCombat(c); } } diff --git a/src/server/scripts/Northrend/UtgardeKeep/UtgardeKeep/boss_skarvald_dalronn.cpp b/src/server/scripts/Northrend/UtgardeKeep/UtgardeKeep/boss_skarvald_dalronn.cpp index 4acb81dbd96390..2148f70924caec 100644 --- a/src/server/scripts/Northrend/UtgardeKeep/UtgardeKeep/boss_skarvald_dalronn.cpp +++ b/src/server/scripts/Northrend/UtgardeKeep/UtgardeKeep/boss_skarvald_dalronn.cpp @@ -101,7 +101,7 @@ class boss_skarvald_the_constructor : public CreatureScript { if (Unit* target = me->SelectNearestTarget(50.0f)) { - me->AddThreat(target, 0.0f); + me->GetThreatManager().AddThreat(target, 0.0f); AttackStart(target); } } @@ -137,7 +137,7 @@ class boss_skarvald_the_constructor : public CreatureScript { if (!dalronn->IsInCombat() && who) { - dalronn->AddThreat(who, 0.0f); + dalronn->GetThreatManager().AddThreat(who, 0.0f); dalronn->AI()->AttackStart(who); } } @@ -199,7 +199,7 @@ class boss_skarvald_the_constructor : public CreatureScript if (Unit* target = SelectTarget(SelectTargetMethod::Random, 0, (IsHeroic() ? 100.0f : 30.0f), true)) { DoResetThreatList(); - me->AddThreat(target, 10000.0f); + me->GetThreatManager().AddThreat(target, 10000.0f); me->CastSpell(target, SPELL_CHARGE, false); } events.Repeat(5s, 10s); @@ -266,7 +266,7 @@ class boss_dalronn_the_controller : public CreatureScript { if (Unit* target = me->SelectNearestTarget(50.0f)) { - me->AddThreat(target, 0.0f); + me->GetThreatManager().AddThreat(target, 0.0f); AttackStart(target); } } @@ -305,7 +305,7 @@ class boss_dalronn_the_controller : public CreatureScript { if (!skarvald->IsInCombat() && who) { - skarvald->AddThreat(who, 0.0f); + skarvald->GetThreatManager().AddThreat(who, 0.0f); skarvald->AI()->AttackStart(who); } } diff --git a/src/server/scripts/Northrend/UtgardeKeep/UtgardePinnacle/boss_svala.cpp b/src/server/scripts/Northrend/UtgardeKeep/UtgardePinnacle/boss_svala.cpp index 3dc2daa593fa23..8326a89481b86f 100644 --- a/src/server/scripts/Northrend/UtgardeKeep/UtgardePinnacle/boss_svala.cpp +++ b/src/server/scripts/Northrend/UtgardeKeep/UtgardePinnacle/boss_svala.cpp @@ -389,7 +389,7 @@ class npc_ritual_channeler : public CreatureScript if (pWho) { - me->AddThreat(pWho, 10000000.0f); + me->GetThreatManager().AddThreat(pWho, 10000000.0f); me->CastSpell(pWho, SPELL_PARALYZE, false); } } diff --git a/src/server/scripts/Northrend/VaultOfArchavon/boss_toravon.cpp b/src/server/scripts/Northrend/VaultOfArchavon/boss_toravon.cpp index b5a749b3242e67..2a4f7d156ab0d0 100644 --- a/src/server/scripts/Northrend/VaultOfArchavon/boss_toravon.cpp +++ b/src/server/scripts/Northrend/VaultOfArchavon/boss_toravon.cpp @@ -191,9 +191,9 @@ class npc_frozen_orb : public CreatureScript if (switchTimer >= 10000) { switchTimer = 0; - me->GetThreatMgr().ResetAllThreat(); + me->GetThreatManager().ResetAllThreat(); if (Player* player = SelectTargetFromPlayerList(100.0f)) - me->AddThreat(player, 100000.0f); + me->GetThreatManager().AddThreat(player, 100000.0f); } } }; diff --git a/src/server/scripts/Northrend/VioletHold/violet_hold.cpp b/src/server/scripts/Northrend/VioletHold/violet_hold.cpp index cad20b90aa8bfb..9b51c21635bf32 100644 --- a/src/server/scripts/Northrend/VioletHold/violet_hold.cpp +++ b/src/server/scripts/Northrend/VioletHold/violet_hold.cpp @@ -397,7 +397,7 @@ struct violet_hold_trashAI : public npc_escortAI me->SetHomePosition(1845.577759f + rand_norm() * 5 - 2.5f, 800.681152f + rand_norm() * 5 - 2.5f, 44.104248f, M_PI); } - me->GetThreatMgr().ClearAllThreat(); + me->GetThreatManager().ClearAllThreat(); me->CombatStop(true); if (HasEscortState(STATE_ESCORT_ESCORTING)) { diff --git a/src/server/scripts/Northrend/zone_borean_tundra.cpp b/src/server/scripts/Northrend/zone_borean_tundra.cpp index 43deb22d0f7a27..83ea9e0eff5234 100644 --- a/src/server/scripts/Northrend/zone_borean_tundra.cpp +++ b/src/server/scripts/Northrend/zone_borean_tundra.cpp @@ -56,7 +56,7 @@ class spell_q11919_q11940_drake_hunt : public SpellScriptLoader Creature* owner = GetOwner()->ToCreature(); owner->RemoveAllAurasExceptType(SPELL_AURA_DUMMY); owner->CombatStop(true); - owner->GetThreatMgr().ClearAllThreat(); + owner->GetThreatManager().ClearAllThreat(); owner->GetMotionMaster()->Clear(false); owner->GetMotionMaster()->MoveFollow(GetCaster(), 4.0f, M_PI, MOTION_SLOT_ACTIVE); owner->CastSpell(owner, SPELL_SUBDUED, true); diff --git a/src/server/scripts/Northrend/zone_dragonblight.cpp b/src/server/scripts/Northrend/zone_dragonblight.cpp index 5bf65ef68c6406..cb4751effcd14d 100644 --- a/src/server/scripts/Northrend/zone_dragonblight.cpp +++ b/src/server/scripts/Northrend/zone_dragonblight.cpp @@ -445,7 +445,7 @@ class npc_hourglass_of_eternity : public CreatureScript { cr->CastSpell(cr, SPELL_TELEPORT_EFFECT, true); cr->AI()->AttackStart(me); - cr->AddThreat(me, 100.0f); + cr->GetThreatManager().AddThreat(me, 100.0f); } } } diff --git a/src/server/scripts/Northrend/zone_sholazar_basin.cpp b/src/server/scripts/Northrend/zone_sholazar_basin.cpp index 24e92b9d2e28d1..7cce7d6ffff2d5 100644 --- a/src/server/scripts/Northrend/zone_sholazar_basin.cpp +++ b/src/server/scripts/Northrend/zone_sholazar_basin.cpp @@ -204,7 +204,7 @@ class npc_artruis_the_hearthless : public CreatureScript minion->AI()->Talk(SAY_TURNED_FRIENDLY); minion->RemoveAurasDueToSpell(SPELL_ARTRUIS_BINDING); minion->SetFaction(me->GetVictim()->GetFaction()); - minion->AddThreat(me, 100000.0f); + minion->GetThreatManager().AddThreat(me, 100000.0f); minion->AI()->AttackStart(me); minion->DespawnOrUnsummon(900000); events.RescheduleEvent(EVENT_ARTRUIS_TALK3, 5s); diff --git a/src/server/scripts/Outland/Auchindoun/SethekkHalls/boss_talon_king_ikiss.cpp b/src/server/scripts/Outland/Auchindoun/SethekkHalls/boss_talon_king_ikiss.cpp index 7a8962da977931..96cd99275c6f2a 100644 --- a/src/server/scripts/Outland/Auchindoun/SethekkHalls/boss_talon_king_ikiss.cpp +++ b/src/server/scripts/Outland/Auchindoun/SethekkHalls/boss_talon_king_ikiss.cpp @@ -66,7 +66,7 @@ struct boss_talon_king_ikiss : public BossAI DoCastAOE(SPELL_ARCANE_EXPLOSION); }).Schedule(6500ms, [this](TaskContext /*context*/) { - me->GetThreatMgr().ResetAllThreat(); + me->GetThreatManager().ResetAllThreat(); }); }); diff --git a/src/server/scripts/Outland/Auchindoun/ShadowLabyrinth/boss_blackheart_the_inciter.cpp b/src/server/scripts/Outland/Auchindoun/ShadowLabyrinth/boss_blackheart_the_inciter.cpp index 76717e97a3875a..c77b72fb8d5382 100644 --- a/src/server/scripts/Outland/Auchindoun/ShadowLabyrinth/boss_blackheart_the_inciter.cpp +++ b/src/server/scripts/Outland/Auchindoun/ShadowLabyrinth/boss_blackheart_the_inciter.cpp @@ -85,10 +85,10 @@ struct boss_blackheart_the_inciter : public BossAI DoCastAOE(SPELL_INCITE_CHAOS); DoCastSelf(SPELL_LAUGHTER, true); uint32 inciteTriggerID = NPC_INCITE_TRIGGER; - std::list t_list = me->GetThreatMgr().GetThreatList(); - for (std::list::const_iterator itr = t_list.begin(); itr != t_list.end(); ++itr) + auto tList = me->GetThreatManager().GetUnsortedThreatList(); + for (auto t : tList) { - Unit* target = ObjectAccessor::GetUnit(*me, (*itr)->getUnitGuid()); + Unit* target = ObjectAccessor::GetUnit(*me, t->GetVictim()->GetGUID()); if (target && target->IsPlayer()) { if (Creature* inciteTrigger = me->SummonCreature(inciteTriggerID++, *target, TEMPSUMMON_TIMED_DESPAWN, 15 * IN_MILLISECONDS)) diff --git a/src/server/scripts/Outland/Auchindoun/ShadowLabyrinth/boss_murmur.cpp b/src/server/scripts/Outland/Auchindoun/ShadowLabyrinth/boss_murmur.cpp index 3024cc6d9ebfcb..69582fdd7dccdf 100644 --- a/src/server/scripts/Outland/Auchindoun/ShadowLabyrinth/boss_murmur.cpp +++ b/src/server/scripts/Outland/Auchindoun/ShadowLabyrinth/boss_murmur.cpp @@ -91,7 +91,7 @@ struct boss_murmur : public BossAI void EnterEvadeMode(EvadeReason why) override { - if (me->GetThreatMgr().GetThreatList().empty()) + if (me->GetThreatManager().GetThreatListSize() < 1) { BossAI::EnterEvadeMode(why); } diff --git a/src/server/scripts/Outland/BlackTemple/boss_bloodboil.cpp b/src/server/scripts/Outland/BlackTemple/boss_bloodboil.cpp index 6d97a0fe699c5d..0125eba4c0df92 100644 --- a/src/server/scripts/Outland/BlackTemple/boss_bloodboil.cpp +++ b/src/server/scripts/Outland/BlackTemple/boss_bloodboil.cpp @@ -246,7 +246,7 @@ class spell_gurtogg_eject : public SpellScriptLoader { PreventHitEffect(effIndex); if (Unit* target = GetHitUnit()) - GetCaster()->GetThreatMgr().ModifyThreatByPercent(target, -20); + GetCaster()->GetThreatManager().ModifyThreatByPercent(target, -20); } void Register() override diff --git a/src/server/scripts/Outland/BlackTemple/boss_illidan.cpp b/src/server/scripts/Outland/BlackTemple/boss_illidan.cpp index c860ef94d3c442..cbb197f6c56f0e 100644 --- a/src/server/scripts/Outland/BlackTemple/boss_illidan.cpp +++ b/src/server/scripts/Outland/BlackTemple/boss_illidan.cpp @@ -619,7 +619,7 @@ class boss_illidan_stormrage : public CreatureScript me->SetDisableGravity(false); break; case EVENT_START_PHASE_3_LAND: - me->GetThreatMgr().ResetAllThreat(); + me->GetThreatManager().ResetAllThreat(); me->RemoveUnitFlag(UNIT_FLAG_NOT_SELECTABLE); me->SetTarget(me->GetVictim()->GetGUID()); AttackStart(me->GetVictim()); @@ -632,7 +632,7 @@ class boss_illidan_stormrage : public CreatureScript // /////////////////////////// case EVENT_PHASE_4_START: me->CastSpell(me, SPELL_DEMON_TRANSFORM_1, true); - me->GetThreatMgr().ResetAllThreat(); + me->GetThreatManager().ResetAllThreat(); me->GetMotionMaster()->MoveChase(me->GetVictim(), 35.0f); events.Reset(); events.ScheduleEvent(EVENT_SPELL_SHADOW_BLAST, 11000); @@ -655,7 +655,7 @@ class boss_illidan_stormrage : public CreatureScript break; case EVENT_REMOVE_DEMON_FORM: me->CastSpell(me, SPELL_DEMON_TRANSFORM_1, true); - me->GetThreatMgr().ResetAllThreat(); + me->GetThreatManager().ResetAllThreat(); events.Reset(); if (summons.HasEntry(NPC_MAIEV_SHADOWSONG)) { @@ -836,8 +836,8 @@ class npc_akama_illidan : public CreatureScript summon->ReplaceAllNpcFlags(UNIT_NPC_FLAG_NONE); if (summon->GetEntry() == NPC_ILLIDARI_ELITE) { - me->AddThreat(summon, 1000000.0f); - summon->AddThreat(me, 1000000.0f); + me->GetThreatManager().AddThreat(summon, 1000000.0f); + summon->GetThreatManager().AddThreat(me, 1000000.0f); summon->AI()->AttackStart(me); AttackStart(summon); } diff --git a/src/server/scripts/Outland/BlackTemple/boss_shade_of_akama.cpp b/src/server/scripts/Outland/BlackTemple/boss_shade_of_akama.cpp index 691c345e09357c..f41af07086c063 100644 --- a/src/server/scripts/Outland/BlackTemple/boss_shade_of_akama.cpp +++ b/src/server/scripts/Outland/BlackTemple/boss_shade_of_akama.cpp @@ -240,7 +240,7 @@ struct boss_shade_of_akama : public BossAI me->SetReactState(REACT_AGGRESSIVE); DoResetThreatList(); me->GetVictim()->InterruptNonMeleeSpells(false); - me->AddThreat(me->GetVictim(), 1000000.0f); + me->GetThreatManager().AddThreat(me->GetVictim(), 1000000.0f); me->RemoveUnitFlag(UNIT_FLAG_NOT_SELECTABLE); me->SetImmuneToAll(false); summonsGenerator.DoAction(ACTION_STOP_SPAWNING); @@ -448,7 +448,7 @@ struct npc_creature_generator_akama : public NullCreatureAI summon->SetInCombatWithZone(); if (Unit* akama = ObjectAccessor::GetCreature(*me, instance->GetGuidData(NPC_AKAMA_SHADE))) { - summon->AddThreat(akama, 500.0f); + summon->GetThreatManager().AddThreat(akama, 500.0f); summon->AI()->AttackStart(akama); } } diff --git a/src/server/scripts/Outland/BlackTemple/boss_supremus.cpp b/src/server/scripts/Outland/BlackTemple/boss_supremus.cpp index d5259faec497ce..c079dbfd036e12 100644 --- a/src/server/scripts/Outland/BlackTemple/boss_supremus.cpp +++ b/src/server/scripts/Outland/BlackTemple/boss_supremus.cpp @@ -104,10 +104,10 @@ struct boss_supremus : public BossAI Unit* FindHatefulStrikeTarget() { Unit* target = nullptr; - ThreatContainer::StorageType const& threatlist = me->GetThreatMgr().GetThreatList(); - for (ThreatContainer::StorageType::const_iterator i = threatlist.begin(); i != threatlist.end(); ++i) + auto tlist = me->GetThreatManager().GetUnsortedThreatList(); + for (auto t : tlist) { - Unit* unit = ObjectAccessor::GetUnit(*me, (*i)->getUnitGuid()); + Unit* unit = ObjectAccessor::GetUnit(*me, t->GetVictim()->GetGUID()); if (unit && me->IsWithinMeleeRange(unit)) if (!target || unit->GetHealth() > target->GetHealth()) target = unit; @@ -146,7 +146,7 @@ struct boss_supremus : public BossAI if (Unit* target = SelectTarget(SelectTargetMethod::Random, 0, 100, true)) { DoResetThreatList(); - me->AddThreat(target, 5000000.0f); + me->GetThreatManager().AddThreat(target, 5000000.0f); Talk(EMOTE_NEW_TARGET); } events.ScheduleEvent(EVENT_SWITCH_TARGET, 10000, EVENT_GROUP_ABILITIES); diff --git a/src/server/scripts/Outland/BlackTemple/boss_teron_gorefiend.cpp b/src/server/scripts/Outland/BlackTemple/boss_teron_gorefiend.cpp index 00034a60f2d313..5b6f8cb06e678e 100644 --- a/src/server/scripts/Outland/BlackTemple/boss_teron_gorefiend.cpp +++ b/src/server/scripts/Outland/BlackTemple/boss_teron_gorefiend.cpp @@ -261,7 +261,7 @@ class spell_teron_gorefiend_shadowy_construct : public AuraScript if (Player* player = i->GetSource()) { if (GetUnitOwner()->IsValidAttackTarget(player)) - GetUnitOwner()->AddThreat(player, 1000000.0f); + GetUnitOwner()->GetThreatManager().AddThreat(player, 1000000.0f); } GetUnitOwner()->CastSpell(GetUnitOwner(), SPELL_BRIEF_STUN, true); diff --git a/src/server/scripts/Outland/BlackTemple/instance_black_temple.cpp b/src/server/scripts/Outland/BlackTemple/instance_black_temple.cpp index ab447cdcbe71f6..687fbc1c2a58c4 100644 --- a/src/server/scripts/Outland/BlackTemple/instance_black_temple.cpp +++ b/src/server/scripts/Outland/BlackTemple/instance_black_temple.cpp @@ -274,8 +274,7 @@ class spell_black_template_harpooners_mark : public SpellScriptLoader GetUnitOwner()->GetCreaturesWithEntryInRange(creatureList, 80.0f, NPC_DRAGON_TURTLE); for (std::list::const_iterator itr = creatureList.begin(); itr != creatureList.end(); ++itr) { - (*itr)->TauntApply(GetUnitOwner()); - (*itr)->AddThreat(GetUnitOwner(), 10000000.0f); + (*itr)->GetThreatManager().AddThreat(GetUnitOwner(), 10000000.0f); _turtleSet.insert((*itr)->GetGUID()); } } @@ -285,8 +284,7 @@ class spell_black_template_harpooners_mark : public SpellScriptLoader for (ObjectGuid const& guid : _turtleSet) if (Creature* turtle = ObjectAccessor::GetCreature(*GetUnitOwner(), guid)) { - turtle->TauntFadeOut(GetUnitOwner()); - turtle->AddThreat(GetUnitOwner(), -10000000.0f); + turtle->GetThreatManager().AddThreat(GetUnitOwner(), -10000000.0f); } } diff --git a/src/server/scripts/Outland/CoilfangReservoir/SerpentShrine/boss_leotheras_the_blind.cpp b/src/server/scripts/Outland/CoilfangReservoir/SerpentShrine/boss_leotheras_the_blind.cpp index 816c1f35e813c9..7de25f2e56e3fc 100644 --- a/src/server/scripts/Outland/CoilfangReservoir/SerpentShrine/boss_leotheras_the_blind.cpp +++ b/src/server/scripts/Outland/CoilfangReservoir/SerpentShrine/boss_leotheras_the_blind.cpp @@ -173,7 +173,7 @@ struct boss_leotheras_the_blind : public BossAI if (me->GetDistance2d(target) > 40.0f) { me->GetMotionMaster()->MoveChase(target, 5.0f, 0); - me->AddThreat(target, 0.0f); + me->GetThreatManager().AddThreat(target, 0.0f); } else { @@ -332,7 +332,7 @@ class spell_leotheras_whirlwind : public SpellScript void HandleScriptEffect(SpellEffIndex effIndex) { PreventHitDefaultEffect(effIndex); - GetCaster()->GetThreatMgr().ResetAllThreat(); + GetCaster()->GetThreatManager().ResetAllThreat(); if (roll_chance_i(33)) if (Unit* target = GetCaster()->GetAI()->SelectTarget(SelectTargetMethod::Random, 0, 30.0f, true)) diff --git a/src/server/scripts/Outland/CoilfangReservoir/SerpentShrine/boss_lurker_below.cpp b/src/server/scripts/Outland/CoilfangReservoir/SerpentShrine/boss_lurker_below.cpp index 4b27f312b8279b..37fd2c8036e64d 100644 --- a/src/server/scripts/Outland/CoilfangReservoir/SerpentShrine/boss_lurker_below.cpp +++ b/src/server/scripts/Outland/CoilfangReservoir/SerpentShrine/boss_lurker_below.cpp @@ -207,10 +207,10 @@ struct boss_the_lurker_below : public BossAI } else { - ThreatContainer::StorageType const& t_list = me->GetThreatMgr().GetThreatList(); - for (ThreatReference const* ref : t_list) + auto tList = me->GetThreatManager().GetUnsortedThreatList(); + for (auto t : tList) { - if (Unit* threatTarget = ObjectAccessor::GetUnit(*me, ref->getUnitGuid())) + if (Unit* threatTarget = ObjectAccessor::GetUnit(*me, t->GetVictim()->GetGUID())) { if (me->IsWithinMeleeRange(threatTarget)) { diff --git a/src/server/scripts/Outland/CoilfangReservoir/SerpentShrine/boss_morogrim_tidewalker.cpp b/src/server/scripts/Outland/CoilfangReservoir/SerpentShrine/boss_morogrim_tidewalker.cpp index b365495ec85382..e14412e5dcba76 100644 --- a/src/server/scripts/Outland/CoilfangReservoir/SerpentShrine/boss_morogrim_tidewalker.cpp +++ b/src/server/scripts/Outland/CoilfangReservoir/SerpentShrine/boss_morogrim_tidewalker.cpp @@ -181,13 +181,13 @@ class spell_morogrim_tidewalker_water_globule_new_target : public SpellScript // Xinef: if we have target we currently follow, return if (Unit* target = GetCaster()->GetVictim()) - if (GetCaster()->GetThreatMgr().GetThreat(target) >= 100000.0f) + if (GetCaster()->GetThreatManager().GetThreat(target) >= 100000.0f) return; // Xinef: acquire new target // TODO: sniffs to see how this actually happens if (Unit* target = GetHitUnit()) - GetCaster()->AddThreat(target, 1000000.0f); + GetCaster()->GetThreatManager().AddThreat(target, 1000000.0f); } void Register() override diff --git a/src/server/scripts/Outland/HellfireCitadel/ShatteredHalls/boss_warbringer_omrogg.cpp b/src/server/scripts/Outland/HellfireCitadel/ShatteredHalls/boss_warbringer_omrogg.cpp index af9c20bca86cd0..339f35ca206b0f 100644 --- a/src/server/scripts/Outland/HellfireCitadel/ShatteredHalls/boss_warbringer_omrogg.cpp +++ b/src/server/scripts/Outland/HellfireCitadel/ShatteredHalls/boss_warbringer_omrogg.cpp @@ -170,7 +170,7 @@ struct boss_warbringer_omrogg : public BossAI { DoResetThreatList(); if (Unit* newTarget = SelectTarget(SelectTargetMethod::Random, 1)) - me->AddThreat(newTarget, 2250.f); + me->GetThreatManager().AddThreat(newTarget, 2250.f); HandleHeadTalk(SAY_ON_BEATDOWN); context.Schedule(1200ms, GROUP_NON_BURNING_PHASE, [this](TaskContext /*context*/) { @@ -200,7 +200,7 @@ struct boss_warbringer_omrogg : public BossAI { DoResetThreatList(); if (Unit* newTarget = SelectTarget(SelectTargetMethod::Random, 1)) - me->AddThreat(newTarget, 2250.f); + me->GetThreatManager().AddThreat(newTarget, 2250.f); me->SetReactState(REACT_AGGRESSIVE); context.Schedule(4850ms, 8500ms, GROUP_BURNING_PHASE, [this](TaskContext context) { diff --git a/src/server/scripts/Outland/TempestKeep/Eye/boss_alar.cpp b/src/server/scripts/Outland/TempestKeep/Eye/boss_alar.cpp index 7264a07ae50b43..5cec7acdf1c06e 100644 --- a/src/server/scripts/Outland/TempestKeep/Eye/boss_alar.cpp +++ b/src/server/scripts/Outland/TempestKeep/Eye/boss_alar.cpp @@ -324,9 +324,9 @@ class boss_alar : public CreatureScript else { me->resetAttackTimer(); - ThreatContainer::StorageType const& threatList = me->GetThreatMgr().GetThreatList(); - for (ThreatContainer::StorageType::const_iterator itr = threatList.begin(); itr != threatList.end(); ++itr) - if (Unit* unit = ObjectAccessor::GetUnit(*me, (*itr)->getUnitGuid())) + auto tlist = me->GetThreatManager().GetUnsortedThreatList(); + for (auto t : tlist) + if (Unit* unit = ObjectAccessor::GetUnit(*me, t->GetVictim()->GetGUID())) if (me->IsWithinMeleeRange(unit)) { me->AttackerStateUpdate(unit); diff --git a/src/server/scripts/Outland/TempestKeep/Eye/boss_kaelthas.cpp b/src/server/scripts/Outland/TempestKeep/Eye/boss_kaelthas.cpp index 62a6ee73051e0d..2601388a6dcf11 100644 --- a/src/server/scripts/Outland/TempestKeep/Eye/boss_kaelthas.cpp +++ b/src/server/scripts/Outland/TempestKeep/Eye/boss_kaelthas.cpp @@ -995,11 +995,11 @@ class spell_kaelthas_nether_beam : public SpellScriptLoader { PreventHitEffect(effIndex); - ThreatContainer::StorageType const& ThreatList = GetCaster()-> GetThreatMgr().GetThreatList(); std::list targetList; - for (ThreatContainer::StorageType::const_iterator itr = ThreatList.begin(); itr != ThreatList.end(); ++itr) + auto tList = GetCaster()->GetThreatManager().GetUnsortedThreatList(); + for (auto t : tList) { - Unit* target = ObjectAccessor::GetUnit(*GetCaster(), (*itr)->getUnitGuid()); + Unit* target = ObjectAccessor::GetUnit(*GetCaster(), t->GetVictim()->GetGUID()); if (target && target->GetTypeId() == TYPEID_PLAYER) targetList.push_back(target); } diff --git a/src/server/scripts/Outland/TempestKeep/Mechanar/boss_nethermancer_sepethrea.cpp b/src/server/scripts/Outland/TempestKeep/Mechanar/boss_nethermancer_sepethrea.cpp index 547874097c82cd..48c539daf30f47 100644 --- a/src/server/scripts/Outland/TempestKeep/Mechanar/boss_nethermancer_sepethrea.cpp +++ b/src/server/scripts/Outland/TempestKeep/Mechanar/boss_nethermancer_sepethrea.cpp @@ -51,11 +51,10 @@ struct boss_nethermancer_sepethrea : public BossAI bool CanAIAttack(Unit const* target) const override { - if (me->GetThreatMgr().GetThreatListSize() > 1) + if (me->GetThreatManager().GetThreatListSize() > 1) { - ThreatContainer::StorageType::const_iterator lastRef = me->GetThreatMgr().GetOnlineContainer().GetThreatList().end(); - --lastRef; - if (Unit* lastTarget = (*lastRef)->getTarget()) + auto lastRef = me->GetThreatManager().GetCurrentVictim(); + if (Unit* lastTarget = lastRef) { if (lastTarget != target) { @@ -110,7 +109,7 @@ struct boss_nethermancer_sepethrea : public BossAI if (Unit* victim = me->GetVictim()) { summon->AI()->AttackStart(victim); - summon->AddThreat(victim, 1000.0f); + summon->GetThreatManager().AddThreat(victim, 1000.0f); summon->SetInCombatWithZone(); } } @@ -143,13 +142,13 @@ struct npc_raging_flames : public ScriptedAI // It's more tricky actually void FixateRandomTarget() { - me->GetThreatMgr().ClearAllThreat(); + me->GetThreatManager().ClearAllThreat(); if (TempSummon* summon = me->ToTempSummon()) if (Creature* summoner = summon->GetSummonerCreatureBase()) if (summoner->IsAIEnabled) if (Unit* target = summoner->AI()->SelectTarget(SelectTargetMethod::Random, 0, 100.0f, true, false)) - me->AddThreat(target, 1000000.0f); + me->GetThreatManager().AddThreat(target, 1000000.0f); } void IsSummonedBy(WorldObject* /*summoner*/) override diff --git a/src/server/scripts/Outland/TempestKeep/botanica/instance_the_botanica.cpp b/src/server/scripts/Outland/TempestKeep/botanica/instance_the_botanica.cpp index b4687846b5c9ab..8b26423334cb33 100644 --- a/src/server/scripts/Outland/TempestKeep/botanica/instance_the_botanica.cpp +++ b/src/server/scripts/Outland/TempestKeep/botanica/instance_the_botanica.cpp @@ -63,8 +63,7 @@ class spell_botanica_call_of_the_falcon : public SpellScriptLoader GetUnitOwner()->GetCreaturesWithEntryInRange(creatureList, 80.0f, NPC_BLOODFALCON); for (std::list::const_iterator itr = creatureList.begin(); itr != creatureList.end(); ++itr) { - (*itr)->TauntApply(GetUnitOwner()); - (*itr)->AddThreat(GetUnitOwner(), 10000000.0f); + (*itr)->GetThreatManager().AddThreat(GetUnitOwner(), 10000000.0f); _falconSet.insert((*itr)->GetGUID()); } } @@ -74,8 +73,7 @@ class spell_botanica_call_of_the_falcon : public SpellScriptLoader for (ObjectGuid const& guid : _falconSet) if (Creature* falcon = ObjectAccessor::GetCreature(*GetUnitOwner(), guid)) { - falcon->TauntFadeOut(GetUnitOwner()); - falcon->AddThreat(GetUnitOwner(), -10000000.0f); + falcon->GetThreatManager().AddThreat(GetUnitOwner(), -10000000.0f); } } diff --git a/src/server/scripts/Outland/zone_blades_edge_mountains.cpp b/src/server/scripts/Outland/zone_blades_edge_mountains.cpp index d470ffdf89bdd6..3c9c8cc655653f 100644 --- a/src/server/scripts/Outland/zone_blades_edge_mountains.cpp +++ b/src/server/scripts/Outland/zone_blades_edge_mountains.cpp @@ -209,7 +209,6 @@ class npc_deaths_fel_cannon : public CreatureScript if (Creature* Target = GetClosestCreatureWithEntry(me, NPC_DEATHS_DOOR_FEL_CANNON_TARGET_BUNNY, 200.0f)) { me->SetFacingToObject(Target); - me->TauntFadeOut(Target); me->CombatStop(); // force } diff --git a/src/server/scripts/Outland/zone_hellfire_peninsula.cpp b/src/server/scripts/Outland/zone_hellfire_peninsula.cpp index 66bff74f345714..305fee2fdcabb9 100644 --- a/src/server/scripts/Outland/zone_hellfire_peninsula.cpp +++ b/src/server/scripts/Outland/zone_hellfire_peninsula.cpp @@ -115,7 +115,7 @@ class npc_aeranas : public CreatureScript me->SetFaction(FACTION_FRIENDLY); me->SetNpcFlag(UNIT_NPC_FLAG_QUESTGIVER); me->RemoveAllAuras(); - me->GetThreatMgr().ClearAllThreat(); + me->GetThreatManager().ClearAllThreat(); me->CombatStop(true); Talk(SAY_FREE); return; @@ -484,7 +484,7 @@ struct npc_magister_aledis : public ScriptedAI _events.Reset(); me->RestoreFaction(); me->RemoveAllAuras(); - me->GetThreatMgr().ClearAllThreat(); + me->GetThreatManager().ClearAllThreat(); me->CombatStop(true); me->SetNpcFlag(UNIT_NPC_FLAG_QUESTGIVER); me->SetImmuneToPC(true); diff --git a/src/server/scripts/Outland/zone_netherstorm.cpp b/src/server/scripts/Outland/zone_netherstorm.cpp index bdaa848d8a4ea9..ee6b916df502fb 100644 --- a/src/server/scripts/Outland/zone_netherstorm.cpp +++ b/src/server/scripts/Outland/zone_netherstorm.cpp @@ -667,12 +667,12 @@ class npc_phase_hunter : public CreatureScript // some code to cast spell Mana Burn on random target which has mana if (ManaBurnTimer <= diff) { - std::list AggroList = me->GetThreatMgr().GetThreatList(); std::list UnitsWithMana; - for (std::list::const_iterator itr = AggroList.begin(); itr != AggroList.end(); ++itr) + auto tlist = me->GetThreatManager().GetUnsortedThreatList(); + for (auto t : tlist) { - if (Unit* unit = ObjectAccessor::GetUnit(*me, (*itr)->getUnitGuid())) + if (Unit* unit = ObjectAccessor::GetUnit(*me, t->GetVictim()->GetGUID())) { if (unit->GetCreateMana() > 0) UnitsWithMana.push_back(unit); diff --git a/src/server/scripts/Outland/zone_shadowmoon_valley.cpp b/src/server/scripts/Outland/zone_shadowmoon_valley.cpp index 20450a647a447a..d01e0d747ad06c 100644 --- a/src/server/scripts/Outland/zone_shadowmoon_valley.cpp +++ b/src/server/scripts/Outland/zone_shadowmoon_valley.cpp @@ -976,7 +976,7 @@ class npc_torloth_the_magnificent : public CreatureScript if (Player* AggroTarget = ObjectAccessor::GetPlayer(*me, AggroTargetGUID)) { me->SetTarget(AggroTarget->GetGUID()); - me->AddThreat(AggroTarget, 1); + me->GetThreatManager().AddThreat(AggroTarget, 1); me->HandleEmoteCommand(EMOTE_ONESHOT_POINT); } break; diff --git a/src/server/scripts/Outland/zone_terokkar_forest.cpp b/src/server/scripts/Outland/zone_terokkar_forest.cpp index b9a1835761b87d..eb03a324b4caad 100644 --- a/src/server/scripts/Outland/zone_terokkar_forest.cpp +++ b/src/server/scripts/Outland/zone_terokkar_forest.cpp @@ -359,7 +359,7 @@ class npc_unkor_the_ruthless : public CreatureScript me->SetFaction(FACTION_FRIENDLY); me->SetStandState(UNIT_STAND_STATE_SIT); me->RemoveAllAuras(); - me->GetThreatMgr().ClearAllThreat(); + me->GetThreatManager().ClearAllThreat(); me->CombatStop(true); UnkorUnfriendly_Timer = 60000; } diff --git a/src/server/scripts/Pet/pet_hunter.cpp b/src/server/scripts/Pet/pet_hunter.cpp index b81e5290a710c7..f7baa008fa2934 100644 --- a/src/server/scripts/Pet/pet_hunter.cpp +++ b/src/server/scripts/Pet/pet_hunter.cpp @@ -70,7 +70,7 @@ struct npc_pet_hunter_snake_trap : public ScriptedAI if (!me->GetVictim()) if (Unit* tgt = me->SelectNearestTarget(10.0f)) { - me->AddThreat(tgt, 100000.0f); + me->GetThreatManager().AddThreat(tgt, 100000.0f); AttackStart(tgt); } } @@ -78,7 +78,7 @@ struct npc_pet_hunter_snake_trap : public ScriptedAI void EnterEvadeMode(EvadeReason /*why*/) override { // _EnterEvadeMode(); - me->GetThreatMgr().ClearAllThreat(); + me->GetThreatManager().ClearAllThreat(); me->CombatStop(true); me->LoadCreaturesAddon(true); me->SetLootRecipient(nullptr); @@ -101,7 +101,7 @@ struct npc_pet_hunter_snake_trap : public ScriptedAI if (me->IsWithinDistInMap(who, 10.0f)) { - me->AddThreat(who, 100000.0f); + me->GetThreatManager().AddThreat(who, 100000.0f); AttackStart(who); } } @@ -222,7 +222,7 @@ class spell_pet_guard_dog : public AuraScript caster->CastSpell((Unit*)nullptr, SPELL_PET_GUARD_DOG_HAPPINESS, true, nullptr, aurEff); float addThreat = CalculatePct(eventInfo.GetSpellInfo()->Effects[EFFECT_0].CalcValue(caster), aurEff->GetAmount()); - eventInfo.GetProcTarget()->AddThreat(caster, addThreat); + eventInfo.GetProcTarget()->GetThreatManager().AddThreat(caster, addThreat); } void Register() override diff --git a/src/server/scripts/Pet/pet_mage.cpp b/src/server/scripts/Pet/pet_mage.cpp index 2bb4f209c9d70e..9b35d235cd3178 100644 --- a/src/server/scripts/Pet/pet_mage.cpp +++ b/src/server/scripts/Pet/pet_mage.cpp @@ -97,12 +97,12 @@ struct npc_pet_mage_mirror_image : CasterAI // Xinef: Inherit Master's Threat List (not yet implemented) //owner->CastSpell((Unit*)nullptr, SPELL_MAGE_MASTERS_THREAT_LIST, true); - HostileReference* ref = owner->getHostileRefMgr().getFirst(); - while (ref) + + auto tList = me->GetThreatManager().GetUnsortedThreatList(); + for (auto t : tList) { - if (Unit* unit = ref->GetSource()->GetOwner()) - unit->AddThreat(me, ref->GetThreat() - ref->getTempThreatModifier()); - ref = ref->next(); + if (Unit* unit = t->GetOwner()) + unit->GetThreatManager().AddThreat(me, owner->GetThreatManager().GetThreat(t->GetVictim())); } _ebonGargoyleGUID.Clear(); @@ -164,8 +164,8 @@ struct npc_pet_mage_mirror_image : CasterAI if (selection) { - me->GetThreatMgr().ResetAllThreat(); - me->AddThreat(selection, 1000000.0f); + me->GetThreatManager().ResetAllThreat(); + me->GetThreatManager().AddThreat(selection, 1000000.0f); if (owner->IsInCombat()) AttackStart(selection); diff --git a/src/server/scripts/Spells/spell_generic.cpp b/src/server/scripts/Spells/spell_generic.cpp index e9c48131765187..fda5051290ff15 100644 --- a/src/server/scripts/Spells/spell_generic.cpp +++ b/src/server/scripts/Spells/spell_generic.cpp @@ -914,7 +914,7 @@ class spell_gen_knock_away : public SpellScript PreventHitDefaultEffect(effIndex); if (Unit* target = GetHitUnit()) if (Creature* caster = GetCaster()->ToCreature()) - caster->GetThreatMgr().ModifyThreatByPercent(target, -25); // Xinef: amount confirmed by onyxia and void reaver notes + caster->GetThreatManager().ModifyThreatByPercent(target, -25); // Xinef: amount confirmed by onyxia and void reaver notes } void Register() override @@ -1020,7 +1020,7 @@ class spell_gen_hate_to_zero : public SpellScript PreventHitDefaultEffect(effIndex); if (Unit* target = GetHitUnit()) if (Creature* caster = GetCaster()->ToCreature()) - caster->GetThreatMgr().ModifyThreatByPercent(target, -100); + caster->GetThreatManager().ModifyThreatByPercent(target, -100); } void Register() override diff --git a/src/server/scripts/Spells/spell_hunter.cpp b/src/server/scripts/Spells/spell_hunter.cpp index 4cd552e93b23aa..2b14640297718d 100644 --- a/src/server/scripts/Spells/spell_hunter.cpp +++ b/src/server/scripts/Spells/spell_hunter.cpp @@ -55,6 +55,7 @@ enum HunterSpells SPELL_HUNTER_IMPROVED_MEND_PET = 24406, SPELL_HUNTER_INVIGORATION_TRIGGERED = 53398, SPELL_HUNTER_MASTERS_CALL_TRIGGERED = 62305, + SPELL_HUNTER_MISDIRECTION = 34477, SPELL_HUNTER_MISDIRECTION_PROC = 35079, SPELL_HUNTER_PET_LAST_STAND_TRIGGERED = 53479, SPELL_HUNTER_PET_HEART_OF_THE_PHOENIX = 55709, @@ -318,7 +319,7 @@ class spell_hun_taming_the_beast : public AuraScript { if (Creature* creature = target->ToCreature()) { - creature->GetThreatMgr().ClearAllThreat(); + creature->GetThreatManager().ClearAllThreat(); } } } @@ -941,24 +942,18 @@ class spell_hun_misdirection : public AuraScript void OnRemove(AuraEffect const* /*aurEff*/, AuraEffectHandleModes /*mode*/) { if (GetTargetApplication()->GetRemoveMode() != AURA_REMOVE_BY_DEFAULT || !GetTarget()->HasAura(SPELL_HUNTER_MISDIRECTION_PROC)) - GetTarget()->ResetRedirectThreat(); - } - - bool CheckProc(ProcEventInfo& /*eventInfo*/) - { - return GetTarget()->GetRedirectThreatTarget(); + GetTarget()->GetThreatManager().UnregisterRedirectThreat(SPELL_HUNTER_MISDIRECTION); } void HandleProc(AuraEffect const* aurEff, ProcEventInfo& /*eventInfo*/) { PreventDefaultAction(); - GetTarget()->CastSpell(GetTarget(), SPELL_HUNTER_MISDIRECTION_PROC, true, nullptr, aurEff); + GetTarget()->CastSpell(GetTarget(), SPELL_HUNTER_MISDIRECTION_PROC, aurEff); } void Register() override { AfterEffectRemove += AuraEffectRemoveFn(spell_hun_misdirection::OnRemove, EFFECT_1, SPELL_AURA_DUMMY, AURA_EFFECT_HANDLE_REAL); - DoCheckProc += AuraCheckProcFn(spell_hun_misdirection::CheckProc); OnEffectProc += AuraEffectProcFn(spell_hun_misdirection::HandleProc, EFFECT_1, SPELL_AURA_DUMMY); } }; @@ -970,7 +965,7 @@ class spell_hun_misdirection_proc : public AuraScript void OnRemove(AuraEffect const* /*aurEff*/, AuraEffectHandleModes /*mode*/) { - GetTarget()->ResetRedirectThreat(); + GetTarget()->GetThreatManager().UnregisterRedirectThreat(SPELL_HUNTER_MISDIRECTION); } void Register() override diff --git a/src/server/scripts/Spells/spell_priest.cpp b/src/server/scripts/Spells/spell_priest.cpp index 87530f3cd7a771..cab991f645f79b 100644 --- a/src/server/scripts/Spells/spell_priest.cpp +++ b/src/server/scripts/Spells/spell_priest.cpp @@ -979,43 +979,6 @@ class spell_pri_vampiric_touch : public AuraScript } }; -// 605 - Mind Control -class spell_pri_mind_control : public AuraScript -{ - PrepareAuraScript(spell_pri_mind_control); - - void HandleApplyEffect(AuraEffect const* /*aurEff*/, AuraEffectHandleModes /*mode*/) - { - if (Unit* caster = GetCaster()) - { - if (Unit* target = GetTarget()) - { - uint32 duration = static_cast(GetDuration()); - caster->SetInCombatWith(target, duration); - target->SetInCombatWith(caster, duration); - } - } - } - - void HandleRemoveEffect(AuraEffect const* /*aurEff*/, AuraEffectHandleModes /*mode*/) - { - if (Unit* caster = GetCaster()) - { - if (Unit* target = GetTarget()) - { - caster->SetCombatTimer(0); - target->SetCombatTimer(0); - } - } - } - - void Register() override - { - AfterEffectApply += AuraEffectApplyFn(spell_pri_mind_control::HandleApplyEffect, EFFECT_0, SPELL_AURA_MOD_POSSESS, AURA_EFFECT_HANDLE_REAL); - AfterEffectRemove += AuraEffectRemoveFn(spell_pri_mind_control::HandleRemoveEffect, EFFECT_0, SPELL_AURA_MOD_POSSESS, AURA_EFFECT_HANDLE_REAL); - } -}; - // 37565 - Flexibility | Item - Priest T4 Holy/Discipline 4P Bonus class spell_pri_t4_4p_bonus : public AuraScript { @@ -1496,7 +1459,6 @@ void AddSC_priest_spell_scripts() RegisterSpellScript(spell_pri_renew); RegisterSpellScript(spell_pri_shadow_word_death); RegisterSpellScript(spell_pri_vampiric_touch); - RegisterSpellScript(spell_pri_mind_control); RegisterSpellScript(spell_pri_t4_4p_bonus); RegisterSpellScript(spell_pri_aq_3p_bonus); RegisterSpellScript(spell_pri_body_and_soul); diff --git a/src/server/scripts/Spells/spell_rogue.cpp b/src/server/scripts/Spells/spell_rogue.cpp index fcb80a1aed45e9..fe40583e22ba7b 100644 --- a/src/server/scripts/Spells/spell_rogue.cpp +++ b/src/server/scripts/Spells/spell_rogue.cpp @@ -40,6 +40,7 @@ enum RogueSpells SPELL_ROGUE_KILLING_SPREE_DMG_BUFF = 61851, SPELL_ROGUE_PREY_ON_THE_WEAK = 58670, SPELL_ROGUE_SHIV_TRIGGERED = 5940, + SPELL_ROGUE_TRICKS_OF_THE_TRADE = 57934, SPELL_ROGUE_TRICKS_OF_THE_TRADE_DMG_BOOST = 57933, SPELL_ROGUE_TRICKS_OF_THE_TRADE_PROC = 59628, SPELL_ROGUE_GLYPH_OF_BACKSTAB_TRIGGER = 63975, @@ -632,14 +633,9 @@ class spell_rog_tricks_of_the_trade : public AuraScript void OnRemove(AuraEffect const* /*aurEff*/, AuraEffectHandleModes /*mode*/) { if (GetTargetApplication()->GetRemoveMode() != AURA_REMOVE_BY_DEFAULT) - GetTarget()->ResetRedirectThreat(); + GetTarget()->GetThreatManager().UnregisterRedirectThreat(SPELL_ROGUE_TRICKS_OF_THE_TRADE); } - bool CheckProc(ProcEventInfo& /*eventInfo*/) - { - _redirectTarget = GetTarget()->GetRedirectThreatTarget(); - return _redirectTarget; - } void HandleProc(AuraEffect const* aurEff, ProcEventInfo& /*eventInfo*/) { @@ -654,7 +650,6 @@ class spell_rog_tricks_of_the_trade : public AuraScript void Register() override { AfterEffectRemove += AuraEffectRemoveFn(spell_rog_tricks_of_the_trade::OnRemove, EFFECT_1, SPELL_AURA_DUMMY, AURA_EFFECT_HANDLE_REAL); - DoCheckProc += AuraCheckProcFn(spell_rog_tricks_of_the_trade::CheckProc); OnEffectProc += AuraEffectProcFn(spell_rog_tricks_of_the_trade::HandleProc, EFFECT_1, SPELL_AURA_DUMMY); } @@ -662,22 +657,6 @@ class spell_rog_tricks_of_the_trade : public AuraScript Unit* _redirectTarget; }; -// 59628 - Tricks of the Trade (Proc) -class spell_rog_tricks_of_the_trade_proc : public AuraScript -{ - PrepareAuraScript(spell_rog_tricks_of_the_trade_proc); - - void HandleRemove(AuraEffect const* /*aurEff*/, AuraEffectHandleModes /*mode*/) - { - GetTarget()->ResetRedirectThreat(); - } - - void Register() override - { - AfterEffectRemove += AuraEffectRemoveFn(spell_rog_tricks_of_the_trade_proc::HandleRemove, EFFECT_0, SPELL_AURA_DUMMY, AURA_EFFECT_HANDLE_REAL); - } -}; - class spell_rog_pickpocket : public SpellScript { PrepareSpellScript(spell_rog_pickpocket); @@ -858,7 +837,6 @@ void AddSC_rogue_spell_scripts() RegisterSpellScript(spell_rog_rupture); RegisterSpellScript(spell_rog_shiv); RegisterSpellScript(spell_rog_tricks_of_the_trade); - RegisterSpellScript(spell_rog_tricks_of_the_trade_proc); RegisterSpellScript(spell_rog_pickpocket); RegisterSpellScript(spell_rog_cut_to_the_chase); RegisterSpellScript(spell_rog_deadly_brew); diff --git a/src/server/scripts/Spells/spell_shaman.cpp b/src/server/scripts/Spells/spell_shaman.cpp index ac5058d077e55f..03777eedc09515 100644 --- a/src/server/scripts/Spells/spell_shaman.cpp +++ b/src/server/scripts/Spells/spell_shaman.cpp @@ -1252,7 +1252,7 @@ class spell_sha_nature_guardian : public AuraScript // Threat reduction is around 10% confirmed in retail and from wiki // Unit* attacker = eventInfo.GetActor(); // if (attacker->IsAlive()) -// attacker->getThreatMgr().modifyThreatPercent(target, -10); +// attacker->GetThreatManager().modifyThreatPercent(target, -10); } } } diff --git a/src/server/scripts/Spells/spell_warlock.cpp b/src/server/scripts/Spells/spell_warlock.cpp index b2c97391d15b50..5fc30fd43d0072 100644 --- a/src/server/scripts/Spells/spell_warlock.cpp +++ b/src/server/scripts/Spells/spell_warlock.cpp @@ -915,7 +915,7 @@ class spell_warl_soulshatter : public SpellScript Unit* caster = GetCaster(); if (Unit* target = GetHitUnit()) { - if (target->CanHaveThreatList() && target->GetThreatMgr().GetThreat(caster) > 0.0f) + if (target->CanHaveThreatList() && target->GetThreatManager().GetThreat(caster) > 0.0f) caster->CastSpell(target, SPELL_WARLOCK_SOULSHATTER, true); } } diff --git a/src/server/scripts/Spells/spell_warrior.cpp b/src/server/scripts/Spells/spell_warrior.cpp index 80d2b690bbc4f6..cdabab465f41e3 100644 --- a/src/server/scripts/Spells/spell_warrior.cpp +++ b/src/server/scripts/Spells/spell_warrior.cpp @@ -973,17 +973,6 @@ class spell_warr_vigilance : public AuraScript target->CastSpell(caster, SPELL_WARRIOR_VIGILANCE_REDIRECT_THREAT, true); } - void HandleAfterApply(AuraEffect const* /*aurEff*/, AuraEffectHandleModes /*mode*/) - { - //! WORKAROUND - //! this glyph is a proc - if (Unit* caster = GetCaster()) - { - if (AuraEffect const* glyph = caster->GetAuraEffect(SPELL_WARRIOR_GLYPH_OF_VIGILANCE, EFFECT_0)) - GetTarget()->ModifyRedirectThreat(glyph->GetAmount()); - } - } - void HandleRemove(AuraEffect const* /*aurEff*/, AuraEffectHandleModes /*mode*/) { Unit* target = GetTarget(); @@ -995,7 +984,7 @@ class spell_warr_vigilance : public AuraScript target->RemoveAurasDueToSpell(SPELL_GEN_DAMAGE_REDUCTION_AURA); } - target->ResetRedirectThreat(); + target->GetThreatManager().UnregisterRedirectThreat(SPELL_WARRIOR_VIGILANCE_REDIRECT_THREAT, GetCasterGUID()); } bool CheckProc(ProcEventInfo& /*eventInfo*/) @@ -1013,7 +1002,6 @@ class spell_warr_vigilance : public AuraScript void Register() override { OnEffectApply += AuraEffectApplyFn(spell_warr_vigilance::HandleApply, EFFECT_0, SPELL_AURA_PROC_TRIGGER_SPELL, AURA_EFFECT_HANDLE_REAL_OR_REAPPLY_MASK); - AfterEffectApply += AuraEffectApplyFn(spell_warr_vigilance::HandleAfterApply, EFFECT_0, SPELL_AURA_PROC_TRIGGER_SPELL, AURA_EFFECT_HANDLE_REAL_OR_REAPPLY_MASK); OnEffectRemove += AuraEffectRemoveFn(spell_warr_vigilance::HandleRemove, EFFECT_0, SPELL_AURA_PROC_TRIGGER_SPELL, AURA_EFFECT_HANDLE_REAL_OR_REAPPLY_MASK); DoCheckProc += AuraCheckProcFn(spell_warr_vigilance::CheckProc); OnEffectProc += AuraEffectProcFn(spell_warr_vigilance::HandleProc, EFFECT_0, SPELL_AURA_PROC_TRIGGER_SPELL); diff --git a/src/server/scripts/World/boss_emerald_dragons.cpp b/src/server/scripts/World/boss_emerald_dragons.cpp index 55495c4f00dafc..85b62aeb36fea8 100644 --- a/src/server/scripts/World/boss_emerald_dragons.cpp +++ b/src/server/scripts/World/boss_emerald_dragons.cpp @@ -320,12 +320,12 @@ class boss_ysondre : public CreatureScript { Talk(SAY_YSONDRE_SUMMON_DRUIDS); - auto const& attackers = me->GetThreatMgr().GetThreatList(); + auto const& attackers = me->GetThreatManager().GetUnsortedThreatList(); uint8 attackersCount = 0; for (const auto attacker : attackers) { - if ((*attacker)->ToPlayer() && (*attacker)->IsAlive()) + if (attacker->GetOwner()->ToPlayer() && attacker->GetOwner()->IsAlive()) ++attackersCount; } diff --git a/src/server/scripts/World/npc_stave_of_ancients.cpp b/src/server/scripts/World/npc_stave_of_ancients.cpp index 8793f5b54b0f9e..ae88041782ba03 100644 --- a/src/server/scripts/World/npc_stave_of_ancients.cpp +++ b/src/server/scripts/World/npc_stave_of_ancients.cpp @@ -53,11 +53,12 @@ void NPCStaveQuestAI::StorePlayerGUID() return; } - for (ThreatContainer::StorageType::const_iterator itr = threatList.begin(); itr != threatList.end(); ++itr) + auto tList = me->GetThreatManager().GetUnsortedThreatList(); + for (auto t : tList) { - if ((*itr)->getTarget()->GetTypeId() == TYPEID_PLAYER) + if (t->GetVictim()->GetTypeId() == TYPEID_PLAYER) { - playerGUID = (*itr)->getUnitGuid(); + playerGUID = t->GetVictim()->GetGUID(); } } } @@ -107,11 +108,12 @@ bool NPCStaveQuestAI::UnitIsUnfair(Unit* unit) bool NPCStaveQuestAI::IsFairFight() { - for (ThreatContainer::StorageType::const_iterator itr = threatList.begin(); itr != threatList.end(); ++itr) + auto tList = me->GetThreatManager().GetUnsortedThreatList(); + for (auto t : tList) { - Unit* unit = ObjectAccessor::GetUnit(*me, (*itr)->getUnitGuid()); + Unit* unit = ObjectAccessor::GetUnit(*me, t->GetVictim()->GetGUID()); - if (!(*itr)->GetThreat()) + if (!t->GetThreat()) { // if target threat is 0 its fair, this prevents despawn in the case when // there is a bystander since UpdateVictim adds nearby enemies to the threatlist @@ -129,7 +131,7 @@ bool NPCStaveQuestAI::IsFairFight() bool NPCStaveQuestAI::ValidThreatlist() { - if (threatList.size() == 1) + if (me->GetThreatManager().GetThreatListSize() == 1) { return true; } diff --git a/src/server/scripts/World/npc_stave_of_ancients.h b/src/server/scripts/World/npc_stave_of_ancients.h index fcdc51ef7ac96c..8ee44a456b3e58 100644 --- a/src/server/scripts/World/npc_stave_of_ancients.h +++ b/src/server/scripts/World/npc_stave_of_ancients.h @@ -138,7 +138,8 @@ struct NPCStaveQuestAI : public ScriptedAI ObjectGuid gossipPlayerGUID; ObjectGuid playerGUID; bool encounterStarted; - ThreatContainer::StorageType const& threatList = me->GetThreatMgr().GetThreatList(); + + Acore::IteratorPair threatList = me->GetThreatManager().GetUnsortedThreatList(); std::map entryKeys = { { ARTORIUS_NORMAL_ENTRY, 1 }, From c21e42a4564ea0db47f94468398d89a0ecb4d01f Mon Sep 17 00:00:00 2001 From: hater Date: Mon, 22 Jan 2024 16:22:13 -0500 Subject: [PATCH 2/2] fix power regen --- src/server/game/Entities/Unit/Unit.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/server/game/Entities/Unit/Unit.cpp b/src/server/game/Entities/Unit/Unit.cpp index f39a4cc107c9cd..90b585cb112348 100644 --- a/src/server/game/Entities/Unit/Unit.cpp +++ b/src/server/game/Entities/Unit/Unit.cpp @@ -11132,12 +11132,12 @@ int32 Unit::ModifyPower(Powers power, int32 dVal) if (val < maxPower) { - SetPower(power, 0); + SetPower(power, val); gain = val - curPower; } else if (curPower != maxPower) { - SetPower(power, 0); + SetPower(power, maxPower); gain = maxPower - curPower; }