Skip to content

Commit

Permalink
Core/Auras: Merge all SpellModifier sets into one sorted set
Browse files Browse the repository at this point in the history
  • Loading branch information
Shauren committed Jun 18, 2024
1 parent 90572c4 commit a7e4126
Show file tree
Hide file tree
Showing 2 changed files with 144 additions and 71 deletions.
195 changes: 126 additions & 69 deletions src/server/game/Entities/Player/Player.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,7 @@
#include "WorldSession.h"
#include "WorldStateMgr.h"
#include "WorldStatePackets.h"
#include <boost/dynamic_bitset.hpp>
#include <G3D/g3dmath.h>
#include <sstream>

Expand Down Expand Up @@ -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;
Expand All @@ -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;
Expand All @@ -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;
Expand All @@ -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;
Expand All @@ -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;
Expand All @@ -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;
Expand All @@ -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;
Expand All @@ -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;
Expand Down Expand Up @@ -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)
Expand All @@ -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());
Expand Down Expand Up @@ -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())
Expand Down
20 changes: 18 additions & 2 deletions src/server/game/Entities/Player/Player.h
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -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;
Expand Down Expand Up @@ -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;
Expand Down

0 comments on commit a7e4126

Please sign in to comment.