diff --git a/src/server/game/Entities/Unit/Unit.h b/src/server/game/Entities/Unit/Unit.h index c8dff2f94..4144c9ecd 100644 --- a/src/server/game/Entities/Unit/Unit.h +++ b/src/server/game/Entities/Unit/Unit.h @@ -2310,6 +2310,32 @@ namespace Acore private: bool const _ascending; }; + + // Binary predicate for sorting Units based on current value of health + class HealthOrderPred + { + public: + HealthOrderPred(bool ascending = true) : _ascending(ascending) { } + + bool operator()(WorldObject const* objA, WorldObject const* objB) const + { + Unit const* a = objA->ToUnit(); + Unit const* b = objB->ToUnit(); + uint32 rA = a ? a->GetHealth() : 0; + uint32 rB = b ? b->GetHealth() : 0; + return _ascending ? rA < rB : rA > rB; + } + + bool operator() (Unit const* a, Unit const* b) const + { + uint32 rA = a ? a->GetHealth() : 0; + uint32 rB = b ? b->GetHealth() : 0; + return _ascending ? rA < rB : rA > rB; + } + + private: + bool const _ascending; + }; } class RedirectSpellEvent : public BasicEvent diff --git a/src/server/scripts/Northrend/Naxxramas/boss_patchwerk.cpp b/src/server/scripts/Northrend/Naxxramas/boss_patchwerk.cpp index 25d46fcbd..78be3def6 100644 --- a/src/server/scripts/Northrend/Naxxramas/boss_patchwerk.cpp +++ b/src/server/scripts/Northrend/Naxxramas/boss_patchwerk.cpp @@ -113,49 +113,37 @@ public: { case EVENT_HATEFUL_STRIKE: { - // Cast Hateful strike on the player with the highest amount of HP within melee distance, and second threat amount - std::list meleeRangeTargets; - Unit* finalTarget = nullptr; - uint8 counter = 0; - auto threatList = me->GetThreatMgr().GetSortedThreatList(); - for (auto i = threatList.begin(); i != threatList.end(); ++i, ++counter) + // Scan melee targets (melee range), choose target according to mechanics: + // - If only 1 melee target exists, hit the tank (current victim). + // - Otherwise select the top RAID_MODE(1,2) threat targets (excluding current victim) + // among melee targets and hit the one with the highest HP. + // - The target hit gains 500 flat threat. + + Unit* victim = me->GetVictim(); + if (!victim) { - // Gather all units with melee range - Unit* target = (*i)->GetVictim(); - if (me->IsWithinMeleeRange(target)) - { - meleeRangeTargets.push_back(target); - } - // and add threat to most hated - if (counter < RAID_MODE(2, 3)) - { - me->AddThreat(target, 500.0f); - } + events.RescheduleEvent(EVENT_HATEFUL_STRIKE, 1s); + break; } - counter = 0; - std::list>::iterator itr; - for (itr = meleeRangeTargets.begin(); itr != meleeRangeTargets.end(); ++itr, ++counter) + + Unit* hatefulTarget = victim; // default to current victim if no other target is available + + std::list meleeTargets; + constexpr float HATEFUL_STRIKE_RANGE = 5.0f; + SelectTargetList(meleeTargets, RAID_MODE(1, 2), SelectTargetMethod::MaxThreat, 0, [&](Unit const* target) { - // if there is only one target available - if (meleeRangeTargets.size() == 1) - { - finalTarget = (*itr); - } - else if (counter > 0) // skip first target - { - if (!finalTarget || (*itr)->GetHealth() > finalTarget->GetHealth()) - { - finalTarget = (*itr); - } - // third loop - if (counter >= 2) - break; - } - } - if (finalTarget) + return target && target->IsPlayer() && target != victim && me->IsWithinCombatRange(target, HATEFUL_STRIKE_RANGE); + }); + + if (!meleeTargets.empty()) { - me->CastSpell(finalTarget, SPELL_HATEFUL_STRIKE, false); + meleeTargets.sort(Acore::HealthOrderPred(false)); + hatefulTarget = meleeTargets.front(); } + + me->CastSpell(hatefulTarget, SPELL_HATEFUL_STRIKE, false); + me->AddThreat(hatefulTarget, 500.0f); + events.Repeat(1s); break; }