aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/server/game/Entities/Player/Player.cpp195
-rw-r--r--src/server/game/Entities/Player/Player.h20
2 files changed, 144 insertions, 71 deletions
diff --git a/src/server/game/Entities/Player/Player.cpp b/src/server/game/Entities/Player/Player.cpp
index 2643add23db..e89fa205ac6 100644
--- a/src/server/game/Entities/Player/Player.cpp
+++ b/src/server/game/Entities/Player/Player.cpp
@@ -137,6 +137,7 @@
#include "WorldSession.h"
#include "WorldStateMgr.h"
#include "WorldStatePackets.h"
+#include <boost/dynamic_bitset.hpp>
#include <G3D/g3dmath.h>
#include <sstream>
@@ -22067,17 +22068,56 @@ void Player::GetSpellModValues(SpellInfo const* spellInfo, SpellModOp op, Spell*
*flat = 0;
*pct = 1.0f;
+ auto spellModOpBegin = std::ranges::lower_bound(m_spellMods, op, std::ranges::less(), &SpellModifier::op);
+ if (spellModOpBegin == m_spellMods.end() || (*spellModOpBegin)->op != op)
+ return;
+
// Drop charges for triggering spells instead of triggered ones
if (m_spellModTakingSpell)
spell = m_spellModTakingSpell;
+ auto spellModTypeBegin = [&](SpellModType type)
+ {
+ auto typeBegin = spellModOpBegin;
+ auto end = m_spellMods.end();
+ while (typeBegin != end && (*typeBegin)->op == op)
+ {
+ if ((*typeBegin)->type == type)
+ return typeBegin;
+
+ ++typeBegin;
+ }
+
+ return end;
+ };
+
+ // end of our iterable range will be when we reach a spellmod with different op or type than expected
+ // we can do this because m_spellMods is sorted by op and type
+ auto spellModTypeEnd = [&](SpellModType type)
+ {
+ struct EndSentinel
+ {
+ bool operator==(std::vector<SpellModifier*>::const_iterator const& itr) const
+ {
+ return itr == end || (*itr)->op != op || (*itr)->type != type;
+ }
+
+ std::vector<SpellModifier*>::const_iterator end;
+ SpellModOp op;
+ SpellModType type;
+ };
+ return EndSentinel{ .end = m_spellMods.end(), .op = op, .type = type };
+ };
+
+ auto spellModTypeRange = [&](SpellModType type) { return Trinity::IteratorPair(spellModTypeBegin(type), spellModTypeEnd(type)); };
+
switch (op)
{
// special case, if a mod makes spell instant, only consume that mod
case SpellModOp::ChangeCastTime:
{
SpellModifier* modInstantSpell = nullptr;
- for (SpellModifier* mod : m_spellMods[AsUnderlyingType(op)][SPELLMOD_PCT])
+ for (SpellModifier* mod : spellModTypeRange(SPELLMOD_PCT))
{
if (!IsAffectedBySpellmod(spellInfo, mod, spell))
continue;
@@ -22091,7 +22131,7 @@ void Player::GetSpellModValues(SpellInfo const* spellInfo, SpellModOp op, Spell*
if (!modInstantSpell)
{
- for (SpellModifier* mod : m_spellMods[AsUnderlyingType(op)][SPELLMOD_LABEL_PCT])
+ for (SpellModifier* mod : spellModTypeRange(SPELLMOD_LABEL_PCT))
{
if (!IsAffectedBySpellmod(spellInfo, mod, spell))
continue;
@@ -22116,7 +22156,7 @@ void Player::GetSpellModValues(SpellInfo const* spellInfo, SpellModOp op, Spell*
case SpellModOp::CritChance:
{
SpellModifier* modCritical = nullptr;
- for (SpellModifier* mod : m_spellMods[AsUnderlyingType(op)][SPELLMOD_FLAT])
+ for (SpellModifier* mod : spellModTypeRange(SPELLMOD_FLAT))
{
if (!IsAffectedBySpellmod(spellInfo, mod, spell))
continue;
@@ -22130,7 +22170,7 @@ void Player::GetSpellModValues(SpellInfo const* spellInfo, SpellModOp op, Spell*
if (!modCritical)
{
- for (SpellModifier* mod : m_spellMods[AsUnderlyingType(op)][SPELLMOD_LABEL_FLAT])
+ for (SpellModifier* mod : spellModTypeRange(SPELLMOD_LABEL_FLAT))
{
if (!IsAffectedBySpellmod(spellInfo, mod, spell))
continue;
@@ -22155,7 +22195,7 @@ void Player::GetSpellModValues(SpellInfo const* spellInfo, SpellModOp op, Spell*
break;
}
- for (SpellModifier* mod : m_spellMods[AsUnderlyingType(op)][SPELLMOD_FLAT])
+ for (SpellModifier* mod : spellModTypeRange(SPELLMOD_FLAT))
{
if (!IsAffectedBySpellmod(spellInfo, mod, spell))
continue;
@@ -22168,7 +22208,7 @@ void Player::GetSpellModValues(SpellInfo const* spellInfo, SpellModOp op, Spell*
Player::ApplyModToSpell(mod, spell);
}
- for (SpellModifier* mod : m_spellMods[AsUnderlyingType(op)][SPELLMOD_LABEL_FLAT])
+ for (SpellModifier* mod : spellModTypeRange(SPELLMOD_LABEL_FLAT))
{
if (!IsAffectedBySpellmod(spellInfo, mod, spell))
continue;
@@ -22181,7 +22221,7 @@ void Player::GetSpellModValues(SpellInfo const* spellInfo, SpellModOp op, Spell*
Player::ApplyModToSpell(mod, spell);
}
- for (SpellModifier* mod : m_spellMods[AsUnderlyingType(op)][SPELLMOD_PCT])
+ for (SpellModifier* mod : spellModTypeRange(SPELLMOD_PCT))
{
if (!IsAffectedBySpellmod(spellInfo, mod, spell))
continue;
@@ -22205,7 +22245,7 @@ void Player::GetSpellModValues(SpellInfo const* spellInfo, SpellModOp op, Spell*
Player::ApplyModToSpell(mod, spell);
}
- for (SpellModifier* mod : m_spellMods[AsUnderlyingType(op)][SPELLMOD_LABEL_PCT])
+ for (SpellModifier* mod : spellModTypeRange(SPELLMOD_LABEL_PCT))
{
if (!IsAffectedBySpellmod(spellInfo, mod, spell))
continue;
@@ -22257,9 +22297,9 @@ void Player::AddSpellMod(SpellModifier* mod, bool apply)
/// First, manipulate our spellmodifier container
if (apply)
- m_spellMods[AsUnderlyingType(mod->op)][mod->type].insert(mod);
+ m_spellMods.insert(mod);
else
- m_spellMods[AsUnderlyingType(mod->op)][mod->type].erase(mod);
+ m_spellMods.erase(mod);
/// Now, send spellmodifier packet
switch (mod->type)
@@ -22274,37 +22314,37 @@ void Player::AddSpellMod(SpellModifier* mod, bool apply)
/// @todo Implement sending of bulk modifiers instead of single
packet.Modifiers.resize(1);
- WorldPackets::Spells::SpellModifier& spellMod = packet.Modifiers[0];
+ WorldPackets::Spells::SpellModifier& spellModifier = packet.Modifiers[0];
- spellMod.ModIndex = AsUnderlyingType(mod->op);
+ spellModifier.ModIndex = AsUnderlyingType(mod->op);
- for (int eff = 0; eff < 128; ++eff)
- {
- flag128 mask;
- mask[eff / 32] = 1u << (eff % 32);
- if (static_cast<SpellModifierByClassMask const*>(mod)->mask & mask)
- {
- WorldPackets::Spells::SpellModifierData modData;
-
- if (mod->type == SPELLMOD_FLAT)
- {
- modData.ModifierValue = 0.0f;
- for (SpellModifier* spellMod : m_spellMods[AsUnderlyingType(mod->op)][SPELLMOD_FLAT])
- if (static_cast<SpellModifierByClassMask const*>(spellMod)->mask & mask)
- modData.ModifierValue += static_cast<SpellModifierByClassMask const*>(spellMod)->value;
- }
- else
- {
- modData.ModifierValue = 1.0f;
- for (SpellModifier* spellMod : m_spellMods[AsUnderlyingType(mod->op)][SPELLMOD_PCT])
- if (static_cast<SpellModifierByClassMask const*>(spellMod)->mask & mask)
- modData.ModifierValue *= 1.0f + CalculatePct(1.0f, static_cast<SpellModifierByClassMask const*>(spellMod)->value);
- }
+ boost::dynamic_bitset<uint32> mask;
+ mask.resize(128);
- modData.ClassIndex = eff;
+ boost::from_block_range(
+ &static_cast<SpellModifierByClassMask const*>(mod)->mask[0],
+ &static_cast<SpellModifierByClassMask const*>(mod)->mask[0] + 4,
+ mask);
- spellMod.ModifierData.push_back(modData);
+ for (std::size_t classIndex = mask.find_first(); classIndex != decltype(mask)::npos; classIndex = mask.find_next(classIndex))
+ {
+ WorldPackets::Spells::SpellModifierData& modData = spellModifier.ModifierData.emplace_back();
+ if (mod->type == SPELLMOD_FLAT)
+ {
+ modData.ModifierValue = 0.0f;
+ for (SpellModifier* spellMod : std::ranges::equal_range(m_spellMods, std::make_pair(mod->op, SPELLMOD_FLAT), std::ranges::less(), [](SpellModifier const* sm) { return std::make_pair(sm->op, sm->type); }))
+ if (static_cast<SpellModifierByClassMask const*>(spellMod)->mask[classIndex / 32] & (1u << (classIndex % 32)))
+ modData.ModifierValue += static_cast<SpellModifierByClassMask const*>(spellMod)->value;
+ }
+ else
+ {
+ modData.ModifierValue = 1.0f;
+ for (SpellModifier* spellMod : std::ranges::equal_range(m_spellMods, std::make_pair(mod->op, SPELLMOD_PCT), std::ranges::less(), [](SpellModifier const* sm) { return std::make_pair(sm->op, sm->type); }))
+ if (static_cast<SpellModifierByClassMask const*>(spellMod)->mask[classIndex / 32] & (1u << (classIndex % 32)))
+ modData.ModifierValue *= 1.0f + CalculatePct(1.0f, static_cast<SpellModifierByClassMask const*>(spellMod)->value);
}
+
+ modData.ClassIndex = classIndex;
}
SendDirectMessage(packet.Write());
@@ -22369,46 +22409,63 @@ void Player::SetSpellModTakingSpell(Spell* spell, bool apply)
void Player::SendSpellModifiers() const
{
- WorldPackets::Spells::SetSpellModifier flatMods(SMSG_SET_FLAT_SPELL_MODIFIER);
- WorldPackets::Spells::SetSpellModifier pctMods(SMSG_SET_PCT_SPELL_MODIFIER);
- for (uint8 i = 0; i < MAX_SPELLMOD; ++i)
+ auto getOrCreateModifierData = [](std::vector<WorldPackets::Spells::SpellModifierData>& datas, uint8 classIndex, float defaultValue) -> float&
{
- WorldPackets::Spells::SpellModifier flatMod;
- flatMod.ModifierData.resize(128);
- WorldPackets::Spells::SpellModifier pctMod;
- pctMod.ModifierData.resize(128);
- flatMod.ModIndex = pctMod.ModIndex = i;
- for (uint8 j = 0; j < 128; ++j)
- {
- flag128 mask;
- mask[j / 32] = 1u << (j % 32);
+ auto itr = std::ranges::find(datas, classIndex, &WorldPackets::Spells::SpellModifierData::ClassIndex);
+ if (itr != datas.end())
+ return itr->ModifierValue;
- flatMod.ModifierData[j].ClassIndex = j;
- flatMod.ModifierData[j].ModifierValue = 0.0f;
- pctMod.ModifierData[j].ClassIndex = j;
- pctMod.ModifierData[j].ModifierValue = 1.0f;
+ WorldPackets::Spells::SpellModifierData& data = datas.emplace_back();
+ data.ModifierValue = defaultValue;
+ data.ClassIndex = classIndex;
+ return data.ModifierValue;
+ };
- for (SpellModifier* mod : m_spellMods[i][SPELLMOD_FLAT])
- if (static_cast<SpellModifierByClassMask const*>(mod)->mask & mask)
- flatMod.ModifierData[j].ModifierValue += static_cast<SpellModifierByClassMask const*>(mod)->value;
+ boost::dynamic_bitset<uint32> mask;
+ mask.resize(128);
- for (SpellModifier* mod : m_spellMods[i][SPELLMOD_PCT])
- if (static_cast<SpellModifierByClassMask const*>(mod)->mask & mask)
- pctMod.ModifierData[j].ModifierValue *= 1.0f + CalculatePct(1.0f, static_cast<SpellModifierByClassMask const*>(mod)->value);
- }
+ WorldPackets::Spells::SetSpellModifier flatMods(SMSG_SET_FLAT_SPELL_MODIFIER);
+ WorldPackets::Spells::SetSpellModifier pctMods(SMSG_SET_PCT_SPELL_MODIFIER);
- flatMod.ModifierData.erase(std::remove_if(flatMod.ModifierData.begin(), flatMod.ModifierData.end(), [](WorldPackets::Spells::SpellModifierData const& mod)
- {
- return G3D::fuzzyEq(mod.ModifierValue, 0.0f);
- }), flatMod.ModifierData.end());
+ WorldPackets::Spells::SpellModifier* flatModifier = nullptr;
+ WorldPackets::Spells::SpellModifier* pctModifier = nullptr;
- pctMod.ModifierData.erase(std::remove_if(pctMod.ModifierData.begin(), pctMod.ModifierData.end(), [](WorldPackets::Spells::SpellModifierData const& mod)
- {
- return G3D::fuzzyEq(mod.ModifierValue, 1.0f);
- }), pctMod.ModifierData.end());
+ for (SpellModifier const* mod : m_spellMods)
+ {
+ if (mod->type != SPELLMOD_FLAT && mod->type != SPELLMOD_PCT)
+ continue;
- flatMods.Modifiers.emplace_back(std::move(flatMod));
- pctMods.Modifiers.emplace_back(std::move(pctMod));
+ switch (mod->type)
+ {
+ case SPELLMOD_FLAT:
+ if (!flatModifier || flatModifier->ModIndex != uint8(mod->op))
+ {
+ flatModifier = &flatMods.Modifiers.emplace_back();
+ flatModifier->ModIndex = uint8(mod->op);
+ }
+ boost::from_block_range(&static_cast<SpellModifierByClassMask const*>(mod)->mask[0], &static_cast<SpellModifierByClassMask const*>(mod)->mask[0] + 4, mask);
+ for (std::size_t classIndex = mask.find_first(); classIndex != decltype(mask)::npos; classIndex = mask.find_next(classIndex))
+ {
+ float& modifierValue = getOrCreateModifierData(flatModifier->ModifierData, classIndex, 0.0f);
+ modifierValue += static_cast<SpellModifierByClassMask const*>(mod)->value;
+ }
+ break;
+ case SPELLMOD_PCT:
+ if (!pctModifier || pctModifier->ModIndex != uint8(mod->op))
+ {
+ pctModifier = &flatMods.Modifiers.emplace_back();
+ pctModifier->ModIndex = uint8(mod->op);
+ }
+ boost::from_block_range(&static_cast<SpellModifierByClassMask const*>(mod)->mask[0], &static_cast<SpellModifierByClassMask const*>(mod)->mask[0] + 4, mask);
+ for (std::size_t classIndex = mask.find_first(); classIndex != decltype(mask)::npos; classIndex = mask.find_next(classIndex))
+ {
+ float& modifierValue = getOrCreateModifierData(pctModifier->ModifierData, classIndex, 0.0f);
+ modifierValue *= 1.0f + CalculatePct(1.0f, static_cast<SpellModifierByClassMask const*>(mod)->value);
+ }
+ break;
+ default:
+ break;
+ }
}
if (!flatMods.Modifiers.empty())
diff --git a/src/server/game/Entities/Player/Player.h b/src/server/game/Entities/Player/Player.h
index e427a2639d4..c9e3ca5a0d3 100644
--- a/src/server/game/Entities/Player/Player.h
+++ b/src/server/game/Entities/Player/Player.h
@@ -258,6 +258,22 @@ struct SpellModifierByLabel : SpellModifier
using SpellFlatModifierByLabel = SpellModifierByLabel<UF::SpellFlatModByLabel>;
using SpellPctModifierByLabel = SpellModifierByLabel<UF::SpellPctModByLabel>;
+struct SpellModifierCompare
+{
+ bool operator()(SpellModifier const* left, SpellModifier const* right) const
+ {
+ // first sort by SpellModOp
+ if (left->op != right->op)
+ return left->op < right->op;
+
+ // then by type (flat/pct)
+ if (left->type != right->type)
+ return left->type < right->type;
+
+ return left < right;
+ }
+};
+
enum PlayerCurrencyState
{
PLAYERCURRENCY_UNCHANGED = 0,
@@ -280,7 +296,7 @@ struct PlayerCurrency
typedef std::unordered_map<uint32, PlayerSpellState> PlayerTalentMap;
typedef std::array<uint32, MAX_PVP_TALENT_SLOTS> PlayerPvpTalentMap;
typedef std::unordered_map<uint32, PlayerSpell> PlayerSpellMap;
-typedef std::unordered_set<SpellModifier*> SpellModContainer;
+typedef Trinity::Containers::FlatSet<SpellModifier*, SpellModifierCompare> SpellModContainer;
typedef std::unordered_map<uint32, PlayerCurrency> PlayerCurrenciesMap;
typedef std::unordered_map<uint32 /*instanceId*/, time_t/*releaseTime*/> InstanceTimeMap;
@@ -3118,7 +3134,7 @@ class TC_GAME_API Player final : public Unit, public GridObject<Player>
uint32 m_baseHealthRegen;
int32 m_spellPenetrationItemMod;
- SpellModContainer m_spellMods[MAX_SPELLMOD][SPELLMOD_END];
+ SpellModContainer m_spellMods;
EnchantDurationList m_enchantDuration;
ItemDurationList m_itemDuration;