diff options
-rw-r--r-- | src/server/game/Spells/Spell.cpp | 101 | ||||
-rw-r--r-- | src/server/game/Spells/Spell.h | 2 |
2 files changed, 51 insertions, 52 deletions
diff --git a/src/server/game/Spells/Spell.cpp b/src/server/game/Spells/Spell.cpp index 59cde35ec96..50483d7b714 100644 --- a/src/server/game/Spells/Spell.cpp +++ b/src/server/game/Spells/Spell.cpp @@ -9108,74 +9108,73 @@ bool WorldObjectSpellLineTargetCheck::operator()(WorldObject* target) const return WorldObjectSpellTargetCheck::operator ()(target); } -void SelectRandomInjuredTargets(std::list<WorldObject*>& targets, size_t maxTargets, bool prioritizePlayers) +void SelectRandomInjuredTargets(std::list<WorldObject*>& targets, size_t maxTargets, bool prioritizePlayers, Unit const* prioritizeGroupMembersOf /*= nullptr*/) { if (targets.size() <= maxTargets) return; - std::vector<WorldObject*> tempTargets(targets.begin(), targets.end()); + // Target priority states (bit indices) + // higher value means lower selection priority + // current list: + // * injured player group members + // * injured other players + // * injured pets of group members + // * injured other pets + // * full health player group members + // * full health other players + // * full health pets of group members + // * full health other pets + enum + { + NOT_GROUPED, + NOT_PLAYER, + NOT_INJURED, + END + }; - auto begin = tempTargets.begin(); - auto end = tempTargets.end(); + std::array<std::ptrdiff_t, 1 << END> countsByPriority = {}; + std::vector<std::pair<WorldObject*, int32>> tempTargets; + tempTargets.resize(targets.size()); - if (prioritizePlayers) + // categorize each target + std::transform(targets.begin(), targets.end(), tempTargets.begin(), [&](WorldObject* target) { - auto playersEnd = std::stable_partition(begin, end, [](WorldObject const* target) - { - return target->IsPlayer(); - }); + int32 negativePoints = 0; + if (prioritizeGroupMembersOf && (!target->IsUnit() || target->ToUnit()->IsInRaidWith(prioritizeGroupMembersOf))) + negativePoints |= 1 << NOT_GROUPED; - size_t playerCount = std::distance(begin, playersEnd); - if (playerCount < maxTargets) - { - // not enough players, add nonplayer targets - // prioritize injured nonplayers over full health nonplayers - auto injuredNonPlayersEnd = std::stable_partition(playersEnd, end, [](WorldObject const* target) - { - return target->IsUnit() && !target->ToUnit()->IsFullHealth(); - }); + if (prioritizePlayers && !target->IsPlayer() && (!target->IsCreature() || !target->ToCreature()->HasFlag(CREATURE_STATIC_FLAG_4_TREAT_AS_RAID_UNIT_FOR_HELPFUL_SPELLS))) + negativePoints |= 1 << NOT_PLAYER; - size_t injuredNonPlayersCount = std::distance(playersEnd, injuredNonPlayersEnd); - if (playerCount + injuredNonPlayersCount < maxTargets) - { - // not enough players + injured nonplayers - // fill remainder with random full health nonplayers - Containers::RandomShuffle(injuredNonPlayersEnd, end); - } - else if (playerCount + injuredNonPlayersCount > maxTargets) - { - // randomize injured nonplayers order - // final list will contain all players + random injured nonplayers - Containers::RandomShuffle(playersEnd, injuredNonPlayersEnd); - } + if (!target->IsUnit() || target->ToUnit()->IsFullHealth()) + negativePoints |= 1 << NOT_INJURED; - targets.assign(tempTargets.begin(), tempTargets.begin() + maxTargets); - return; - } - - // We have more players than we requested, proceed checking injured targets - end = playersEnd; - } + ++countsByPriority[negativePoints]; + return std::make_pair(target, negativePoints); + }); - auto injuredUnitsEnd = std::stable_partition(begin, end, [](WorldObject const* target) + std::sort(tempTargets.begin(), tempTargets.end(), [](std::pair<WorldObject*, int32> const& left, std::pair<WorldObject*, int32> const& right) { - return target->IsUnit() && !target->ToUnit()->IsFullHealth(); + return left.second < right.second; }); - size_t injuredUnitsCount = std::distance(begin, injuredUnitsEnd); - if (injuredUnitsCount < maxTargets) - { - // not enough injured units - // fill remainder with full health units - Containers::RandomShuffle(injuredUnitsEnd, end); - } - else if (injuredUnitsCount > maxTargets) + std::size_t foundTargets = 0; + for (std::ptrdiff_t countForPriority : countsByPriority) { - // select random injured units - Containers::RandomShuffle(begin, injuredUnitsEnd); + if (foundTargets + countForPriority >= maxTargets) + { + // shuffle only the lower priority extras + // example: our initial target list had 5 injured and 5 full health units and we want to select 7 targets + // we always want to select 5 injured and 2 random full health ones + Containers::RandomShuffle(tempTargets.begin() + foundTargets, tempTargets.begin() + foundTargets + countForPriority); + break; + } + + foundTargets += countForPriority; } - targets.assign(tempTargets.begin(), tempTargets.begin() + maxTargets); + targets.resize(maxTargets); + std::transform(tempTargets.begin(), tempTargets.begin() + maxTargets, targets.begin(), std::mem_fn(&std::pair<WorldObject*, int32>::first)); } } //namespace Trinity diff --git a/src/server/game/Spells/Spell.h b/src/server/game/Spells/Spell.h index e173a978391..6494d4c3d70 100644 --- a/src/server/game/Spells/Spell.h +++ b/src/server/game/Spells/Spell.h @@ -978,7 +978,7 @@ namespace Trinity bool operator()(WorldObject* target) const; }; - TC_GAME_API void SelectRandomInjuredTargets(std::list<WorldObject*>& targets, size_t maxTargets, bool prioritizePlayers); + TC_GAME_API void SelectRandomInjuredTargets(std::list<WorldObject*>& targets, size_t maxTargets, bool prioritizePlayers, Unit const* prioritizeGroupMembersOf = nullptr); } using SpellEffectHandlerFn = void(Spell::*)(); |