diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/server/game/Entities/Player/Player.cpp | 195 | ||||
-rw-r--r-- | src/server/game/Entities/Player/Player.h | 20 |
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; |