diff options
author | Shauren <shauren.trinity@gmail.com> | 2025-09-06 12:28:12 +0200 |
---|---|---|
committer | Shauren <shauren.trinity@gmail.com> | 2025-09-06 13:19:38 +0200 |
commit | 03d072da463769ff02090e08d12b4da3f6a49222 (patch) | |
tree | 3b67edcd4c0e7df18de425489f688eb621a32235 /src | |
parent | 2e3f3fda3fc533daa4064739b633dbb28f5115d3 (diff) |
Core/Spells: Simplify SortTargetsWithPriorityRules
* Remove manul weight assignment
* Removed std::vector alloc
Diffstat (limited to 'src')
-rw-r--r-- | src/server/game/Spells/Spell.cpp | 58 | ||||
-rw-r--r-- | src/server/game/Spells/Spell.h | 32 | ||||
-rw-r--r-- | src/server/scripts/Spells/spell_priest.cpp | 22 |
3 files changed, 54 insertions, 58 deletions
diff --git a/src/server/game/Spells/Spell.cpp b/src/server/game/Spells/Spell.cpp index bce4b676490..111d2914c57 100644 --- a/src/server/game/Spells/Spell.cpp +++ b/src/server/game/Spells/Spell.cpp @@ -9633,67 +9633,41 @@ void SelectRandomInjuredTargets(std::list<WorldObject*>& targets, size_t maxTarg std::ranges::transform(tempTargets.begin(), tempTargets.begin() + maxTargets, targets.begin(), Trinity::TupleElement<0>); } -void SortTargetsWithPriorityRules(std::list<WorldObject*>& targets, size_t maxTargets, std::vector<PriorityRules> const& rules) +void SortTargetsWithPriorityRules(std::list<WorldObject*>& targets, size_t maxTargets, std::span<TargetPriorityRule const> rules) { if (targets.size() <= maxTargets) return; - std::vector<std::pair<WorldObject*, int32>> prioritizedTargets; + std::vector<std::pair<WorldObject*, int32>> prioritizedTargets(targets.size()); // score each target based on how many rules they satisfy. - for (WorldObject* object : targets) + std::ranges::transform(targets, prioritizedTargets.begin(), [&](WorldObject* target) { - Unit* unit = object ? object->ToUnit() : nullptr; - if (!unit) - continue; - int32 score = 0; + for (std::size_t i = 0; i < rules.size(); ++i) + if (rules[i].Rule(target)) + score |= 1 << (rules.size() - 1 - i); - for (PriorityRules const& rule : rules) - { - if (rule.condition(unit)) - score += rule.weight; - } - - prioritizedTargets.emplace_back(object, score); - } + return std::make_pair(target, score); + }); // the higher the value, the higher the priority is. - std::ranges::sort(prioritizedTargets, [](const std::pair<WorldObject*, int32>& left, const std::pair<WorldObject*, int32>& right) - { - return left.second > right.second; - }); + std::ranges::sort(prioritizedTargets, std::ranges::greater(), Trinity::TupleElement<1>); - size_t cutOff = std::min(maxTargets, prioritizedTargets.size()); + int32 tieScore = prioritizedTargets[maxTargets - 1].second; // if there are ties at the cutoff, shuffle them to avoid selection bias. - if (cutOff < prioritizedTargets.size()) + if (prioritizedTargets[maxTargets].second == tieScore) { - int32 tieScore = prioritizedTargets[cutOff - 1].second; - - auto const isTieScore = [tieScore](const std::pair<WorldObject*, int32>& entry) - { return entry.second == tieScore; }; - - // scan backwards to include tied entries before the cutoff. - std::ptrdiff_t tieStart = static_cast<std::ptrdiff_t>(cutOff - 1); - while (tieStart > 0 && isTieScore(prioritizedTargets[tieStart - 1])) - --tieStart; - - // scan forward to include tied entries after the cutoff. - std::ptrdiff_t tieEnd = static_cast<std::ptrdiff_t>(cutOff); - while (tieEnd < static_cast<std::ptrdiff_t>(prioritizedTargets.size()) && isTieScore(prioritizedTargets[tieEnd])) - ++tieEnd; + auto toShuffle = std::equal_range(prioritizedTargets.begin(), prioritizedTargets.end(), std::pair<WorldObject*, int32>(nullptr, tieScore), + [](std::pair<WorldObject*, int32> const& target1, std::pair<WorldObject*, int32> const& target2) { return target1.second > target2.second; }); // shuffle only the tied range to randomize final selection. - Containers::RandomShuffle( - std::ranges::next(prioritizedTargets.begin(), tieStart), - std::ranges::next(prioritizedTargets.begin(), tieEnd)); + Containers::RandomShuffle(toShuffle.first, toShuffle.second); } - targets.clear(); - - for (size_t i = 0; i < cutOff; ++i) - targets.push_back(prioritizedTargets[i].first); + targets.resize(maxTargets); + std::ranges::transform(prioritizedTargets.begin(), prioritizedTargets.begin() + maxTargets, targets.begin(), Trinity::TupleElement<0>); } } //namespace Trinity diff --git a/src/server/game/Spells/Spell.h b/src/server/game/Spells/Spell.h index d6f968420d8..8555151235b 100644 --- a/src/server/game/Spells/Spell.h +++ b/src/server/game/Spells/Spell.h @@ -18,6 +18,7 @@ #ifndef __SPELL_H #define __SPELL_H +#include "Concepts.h" #include "ConditionMgr.h" #include "DBCEnums.h" #include "Duration.h" @@ -27,6 +28,7 @@ #include "Position.h" #include "SharedDefines.h" #include "SpellDefines.h" +#include "Types.h" #include "UniqueTrackablePtr.h" #include <memory> @@ -1055,15 +1057,35 @@ namespace Trinity TC_GAME_API void SelectRandomInjuredTargets(std::list<WorldObject*>& targets, size_t maxTargets, bool prioritizePlayers, Unit const* prioritizeGroupMembersOf = nullptr); - struct PriorityRules + struct TargetPriorityRule { - int32 weight; - std::function<bool(Unit*)> condition; + template <typename Func> requires (!std::same_as<Func, TargetPriorityRule>) + TargetPriorityRule(Func&& func) : Rule([func = std::forward<Func>(func)]<typename T = WorldObject /*template to avoid Object.h dependency*/>(T* target) + { + if constexpr (invocable_r<Func, bool, WorldObject*>) + return std::invoke(func, target); + else if constexpr (invocable_r<Func, bool, Unit*>) + return target->IsUnit() && std::invoke(func, target->ToUnit()); + else if constexpr (invocable_r<Func, bool, Player*>) + return target->IsPlayer() && std::invoke(func, target->ToPlayer()); + else + static_assert(dependant_false_v<T>, "Unsupported object type, use WorldObject* as your rule argument"); + }) + { + } + + std::function<bool(WorldObject*)> Rule; }; - inline std::vector<PriorityRules> CreatePriorityRules(std::initializer_list<PriorityRules> rules) { return { rules }; } + TC_GAME_API void SortTargetsWithPriorityRules(std::list<WorldObject*>& targets, size_t maxTargets, std::span<TargetPriorityRule const> rules); + + template <std::size_t N> + inline void SortTargetsWithPriorityRules(std::list<WorldObject*>& targets, size_t maxTargets, std::array<TargetPriorityRule, N> const& rules) + { + static_assert(N <= 31); - TC_GAME_API void SortTargetsWithPriorityRules(std::list<WorldObject*>& targets, size_t maxTargets, std::vector<PriorityRules> const& rules); + SortTargetsWithPriorityRules(targets, maxTargets, std::span(rules)); + } } extern template void Spell::SearchTargets<Trinity::WorldObjectListSearcher<Trinity::WorldObjectSpellAreaTargetCheck>>(Trinity::WorldObjectListSearcher<Trinity::WorldObjectSpellAreaTargetCheck>& searcher, uint32 containerMask, WorldObject* referer, Position const* pos, float radius); diff --git a/src/server/scripts/Spells/spell_priest.cpp b/src/server/scripts/Spells/spell_priest.cpp index ddd3928ab0f..6aa96c48d20 100644 --- a/src/server/scripts/Spells/spell_priest.cpp +++ b/src/server/scripts/Spells/spell_priest.cpp @@ -2119,9 +2119,9 @@ class spell_pri_power_word_radiance : public SpellScript Unit* explTarget = GetExplTargetUnit(); // we must add one since explicit target is always chosen. - uint32 maxTargets = GetEffectInfo(EFFECT_2).CalcValue(GetCaster()) + 1; + uint32 maxTargets = GetEffectInfo(EFFECT_2).CalcValue(caster) + 1; - SortTargetsWithPriorityRules(targets, maxTargets, GetRadianceRules(caster, explTarget)); + Trinity::SortTargetsWithPriorityRules(targets, maxTargets, GetRadianceRules(caster, explTarget)); for (WorldObject const* target : targets) { @@ -2139,16 +2139,16 @@ class spell_pri_power_word_radiance : public SpellScript GetHitUnit()->SendPlaySpellVisual(target, SPELL_VISUAL_PRIEST_POWER_WORD_RADIANCE, 0, 0, 70.0f); } - static std::vector<Trinity::PriorityRules> GetRadianceRules(Unit const* caster, Unit const* explTarget) + static std::array<Trinity::TargetPriorityRule, 5> GetRadianceRules(Unit const* caster, Unit const* explTarget) { - return Trinity::CreatePriorityRules - ({ - { .weight = 1, .condition = [caster](Unit const* target) { return target->IsInRaidWith(caster); }}, - { .weight = 2, .condition = [](Unit const* target) { return target->IsPlayer() || (target->IsCreature() && target->ToCreature()->IsTreatedAsRaidUnit()); }}, - { .weight = 4, .condition = [](Unit const* target) { return !target->IsFullHealth(); }}, - { .weight = 8, .condition = [caster](Unit const* target) { return !target->HasAura(SPELL_PRIEST_ATONEMENT_EFFECT, caster->GetGUID()); }}, - { .weight = 16, .condition = [explTarget](Unit const* target) { return target == explTarget; }} - }); + return + { + [explTarget](WorldObject const* target) { return target == explTarget; }, + [caster](Unit const* target) { return !target->HasAura(SPELL_PRIEST_ATONEMENT_EFFECT, caster->GetGUID()); }, + [](Unit const* target) { return !target->IsFullHealth(); }, + [](WorldObject const* target) { return target->IsPlayer() || (target->IsCreature() && target->ToCreature()->IsTreatedAsRaidUnit()); }, + [caster](Unit const* target) { return target->IsInRaidWith(caster); } + }; } void Register() override |