Core/Spells: ported spellMod handling from master branch

This commit is contained in:
Ovahlord committed 2025-03-09 13:20:40 +01:00
1 parent b390f0fec9
commit 83958342d9
16 files changed
+374 -177

No files matched your search

+218 -93
View File
@@ -116,6 +116,7 @@
#include "WorldSession.h"
#include "WorldStateMgr.h"
#include "WorldStatePackets.h"
#include <boost/dynamic_bitset.hpp>
#include <G3D/g3dmath.h>
// corpse reclaim times
@@ -7973,7 +7974,7 @@ void Player::CastItemCombatSpell(DamageInfo const& damageInfo, Item* item, ItemT
}
// Apply spell mods
ApplySpellMod(pEnchant->EffectArg[s], SpellModOp::ProcChance, chance);
ApplySpellMod(spellInfo, SpellModOp::ProcChance, chance);
// Shiv has 100% chance to apply the poison
if (FindCurrentSpellBySpellId(5938) && e_slot == TEMP_ENCHANTMENT_SLOT)
@@ -20728,7 +20729,7 @@ void Player::SendRemoveControlBar() const
SendDirectMessage(&data);
}
bool Player::IsAffectedBySpellmod(SpellInfo const* spellInfo, SpellModifier* mod, Spell* spell)
bool Player::IsAffectedBySpellmod(SpellInfo const* spellInfo, SpellModifier const* mod, Spell* spell)
{
if (!mod || !spellInfo)
return false;
@@ -20737,38 +20738,99 @@ bool Player::IsAffectedBySpellmod(SpellInfo const* spellInfo, SpellModifier* mod
if (spell && mod->ownerAura->IsUsingCharges() && !mod->ownerAura->GetCharges() && !spell->m_appliedMods.count(mod->ownerAura))
return false;
// +duration to infinite duration spells making them limited
if (mod->op == SpellModOp::Duration && spellInfo->GetDuration() == -1)
return false;
switch (mod->op)
{
case SpellModOp::Duration: // +duration to infinite duration spells making them limited
if (spellInfo->GetDuration() == -1)
return false;
break;
case SpellModOp::CritChance: // mod crit to spells that can't crit
if (!spellInfo->HasAttribute(SPELL_ATTR0_CU_CAN_CRIT))
return false;
break;
case SpellModOp::PointsIndex0: // check if spell has any effect at that index
case SpellModOp::Points:
if (!spellInfo->Effects[EFFECT_0].IsEffect())
return false;
break;
case SpellModOp::PointsIndex1: // check if spell has any effect at that index
if (!spellInfo->Effects[EFFECT_1].IsEffect())
return false;
break;
case SpellModOp::PointsIndex2: // check if spell has any effect at that index
if (!spellInfo->Effects[EFFECT_2].IsEffect())
return false;
break;
default:
break;
}
return spellInfo->IsAffectedBySpellMod(mod);
}
template <class T>
void Player::ApplySpellMod(uint32 spellId, SpellModOp op, T& basevalue, Spell* spell /*= nullptr*/) const
void Player::GetSpellModValues(SpellInfo const* spellInfo, SpellModOp op, Spell* spell, T base, int32* flat, float* pct) const
{
SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(spellId);
if (!spellInfo)
ASSERT(flat && pct);
*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;
float totalmul = 1.0f;
int32 totalflat = 0;
// 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;
if (mod->type == SPELLMOD_PCT && basevalue < T(10000) && mod->value <= -100)
if (base < T(10000) && static_cast<SpellModifierByClassMask*>(mod)->value <= -100)
{
modInstantSpell = mod;
break;
@@ -20778,7 +20840,7 @@ void Player::ApplySpellMod(uint32 spellId, SpellModOp op, T& basevalue, Spell* s
if (modInstantSpell)
{
Player::ApplyModToSpell(modInstantSpell, spell);
basevalue = T(0);
*pct = 0.0f;
return;
}
break;
@@ -20787,12 +20849,12 @@ void Player::ApplySpellMod(uint32 spellId, SpellModOp op, T& basevalue, Spell* s
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;
if (mod->type == SPELLMOD_FLAT && mod->value >= 100)
if (static_cast<SpellModifierByClassMask*>(mod)->value >= 100)
{
modCritical = mod;
break;
@@ -20802,7 +20864,7 @@ void Player::ApplySpellMod(uint32 spellId, SpellModOp op, T& basevalue, Spell* s
if (modCritical)
{
Player::ApplyModToSpell(modCritical, spell);
basevalue = T(100);
*flat = 100;
return;
}
break;
@@ -20811,44 +20873,64 @@ void Player::ApplySpellMod(uint32 spellId, SpellModOp op, T& basevalue, Spell* s
break;
}
for (SpellModifier* mod : m_spellMods[AsUnderlyingType(op)][SPELLMOD_FLAT])
for (SpellModifier* mod : spellModTypeRange(SPELLMOD_FLAT))
{
if (!IsAffectedBySpellmod(spellInfo, mod, spell))
continue;
if (mod->value == 0)
int32 value = static_cast<SpellModifierByClassMask*>(mod)->value;
if (value == 0)
continue;
totalflat += mod->value;
*flat += value;
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;
// skip percent mods for null basevalue (most important for spell mods with charges)
if (basevalue + totalflat == T(0))
if (base + *flat == T(0))
continue;
if (mod->value == 0)
int32 value = static_cast<SpellModifierByClassMask*>(mod)->value;
if (value == 0)
continue;
// special case (skip > 10sec spell casts for instant cast setting)
if (mod->op == SpellModOp::ChangeCastTime && basevalue >= T(10000) && mod->value <= -100)
continue;
if (op == SpellModOp::ChangeCastTime)
{
if (base >= T(10000) && value <= -100)
continue;
}
totalmul *= 1.0f + CalculatePct(1.0f, mod->value);
*pct += CalculatePct(1.0f, value);
Player::ApplyModToSpell(mod, spell);
}
basevalue = T(float(basevalue + totalflat) * totalmul);
}
template TC_GAME_API void Player::ApplySpellMod(uint32 spellId, SpellModOp op, int32& basevalue, Spell* spell) const;
template TC_GAME_API void Player::ApplySpellMod(uint32 spellId, SpellModOp op, uint32& basevalue, Spell* spell) const;
template TC_GAME_API void Player::ApplySpellMod(uint32 spellId, SpellModOp op, float& basevalue, Spell* spell) const;
template TC_GAME_API void Player::GetSpellModValues(SpellInfo const* spellInfo, SpellModOp op, Spell* spell, int32 base, int32* flat, float* pct) const;
template TC_GAME_API void Player::GetSpellModValues(SpellInfo const* spellInfo, SpellModOp op, Spell* spell, uint32 base, int32* flat, float* pct) const;
template TC_GAME_API void Player::GetSpellModValues(SpellInfo const* spellInfo, SpellModOp op, Spell* spell, float base, int32* flat, float* pct) const;
template TC_GAME_API void Player::GetSpellModValues(SpellInfo const* spellInfo, SpellModOp op, Spell* spell, double base, int32* flat, float* pct) const;
template <class T>
void Player::ApplySpellMod(SpellInfo const* spellInfo, SpellModOp op, T& basevalue, Spell* spell /*= nullptr*/) const
{
float totalmul = 1.0f;
int32 totalflat = 0;
GetSpellModValues(spellInfo, op, spell, basevalue, &totalflat, &totalmul);
basevalue = T(double(basevalue + totalflat) * totalmul);
}
template TC_GAME_API void Player::ApplySpellMod(SpellInfo const* spellInfo, SpellModOp op, int32& basevalue, Spell* spell) const;
template TC_GAME_API void Player::ApplySpellMod(SpellInfo const* spellInfo, SpellModOp op, uint32& basevalue, Spell* spell) const;
template TC_GAME_API void Player::ApplySpellMod(SpellInfo const* spellInfo, SpellModOp op, float& basevalue, Spell* spell) const;
template TC_GAME_API void Player::ApplySpellMod(SpellInfo const* spellInfo, SpellModOp op, double& basevalue, Spell* spell) const;
void Player::AddSpellMod(SpellModifier* mod, bool apply)
{
@@ -20856,43 +20938,69 @@ 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
if (!IsLoading())
switch (mod->type)
{
OpcodeServer opcode = (mod->type == SPELLMOD_FLAT) ? SMSG_SET_FLAT_SPELL_MODIFIER : SMSG_SET_PCT_SPELL_MODIFIER;
WorldPackets::Spells::SetSpellModifier packet(opcode);
/// @todo Implement sending of bulk modifiers instead of single
packet.Modifiers.resize(1);
WorldPackets::Spells::SpellModifier& spellMod = packet.Modifiers[0];
spellMod.ModIndex = AsUnderlyingType(mod->op);
for (uint8 eff = 0; eff < 96; ++eff)
{
flag96 mask;
mask[eff / 32] = 1u << (eff % 32);
if (mod->mask & mask)
case SPELLMOD_FLAT:
case SPELLMOD_PCT:
if (!IsLoading())
{
WorldPackets::Spells::SpellModifierData modData;
OpcodeServer opcode = (mod->type == SPELLMOD_FLAT) ? SMSG_SET_FLAT_SPELL_MODIFIER : SMSG_SET_PCT_SPELL_MODIFIER;
modData.ModifierValue = 0.0f;
for (SpellModifier* spellMod : m_spellMods[AsUnderlyingType(mod->op)][AsUnderlyingType(mod->type)])
if (spellMod->mask & mask)
modData.ModifierValue += spellMod->value;
WorldPackets::Spells::SetSpellModifier packet(opcode);
modData.ClassIndex = eff;
/// @todo Implement sending of bulk modifiers instead of single
packet.Modifiers.resize(1);
WorldPackets::Spells::SpellModifier& spellModifier = packet.Modifiers[0];
spellMod.ModifierData.push_back(modData);
spellModifier.ModIndex = AsUnderlyingType(mod->op);
boost::dynamic_bitset<uint32> mask;
mask.resize(96);
boost::from_block_range(
&static_cast<SpellModifierByClassMask const*>(mod)->mask[0],
&static_cast<SpellModifierByClassMask const*>(mod)->mask[0] + 3,
mask);
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;
auto itr = std::ranges::lower_bound(m_spellMods, std::make_pair(mod->op, SPELLMOD_FLAT), std::ranges::less(), [](SpellModifier const* sm) { return std::make_pair(sm->op, sm->type); });
while (itr != m_spellMods.end() && (*itr)->op == mod->op && (*itr)->type == SPELLMOD_FLAT)
{
SpellModifierByClassMask const* spellMod = static_cast<SpellModifierByClassMask const*>(*itr++);
if (spellMod->mask[classIndex / 32] & (1u << (classIndex % 32)))
modData.ModifierValue += spellMod->value;
}
}
else
{
modData.ModifierValue = 0;
auto itr = std::ranges::lower_bound(m_spellMods, std::make_pair(mod->op, SPELLMOD_PCT), std::ranges::less(), [](SpellModifier const* sm) { return std::make_pair(sm->op, sm->type); });
while (itr != m_spellMods.end() && (*itr)->op == mod->op && (*itr)->type == SPELLMOD_PCT)
{
SpellModifierByClassMask const* spellMod = static_cast<SpellModifierByClassMask const*>(*itr++);
if (spellMod->mask[classIndex / 32] & (1u << (classIndex % 32)))
modData.ModifierValue += spellMod->value;
}
}
modData.ClassIndex = classIndex;
}
SendDirectMessage(packet.Write());
}
}
SendDirectMessage(packet.Write());
break;
default:
break;
}
}
@@ -20922,46 +21030,63 @@ void Player::SetSpellModTakingSpell(Spell* spell, bool apply)
void Player::SendSpellModifiers() const
{
auto getOrCreateModifierData = [](std::vector<WorldPackets::Spells::SpellModifierData>& datas, uint8 classIndex, float defaultValue) -> float&
{
auto itr = std::ranges::find(datas, classIndex, &WorldPackets::Spells::SpellModifierData::ClassIndex);
if (itr != datas.end())
return itr->ModifierValue;
WorldPackets::Spells::SpellModifierData& data = datas.emplace_back();
data.ModifierValue = defaultValue;
data.ClassIndex = classIndex;
return data.ModifierValue;
};
boost::dynamic_bitset<uint32> mask;
mask.resize(96);
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)
WorldPackets::Spells::SpellModifier* flatModifier = nullptr;
WorldPackets::Spells::SpellModifier* pctModifier = nullptr;
for (SpellModifier const* mod : m_spellMods)
{
WorldPackets::Spells::SpellModifier flatMod;
flatMod.ModifierData.resize(96);
WorldPackets::Spells::SpellModifier pctMod;
pctMod.ModifierData.resize(96);
flatMod.ModIndex = pctMod.ModIndex = i;
for (uint8 j = 0; j < 96; ++j)
if (mod->type != SPELLMOD_FLAT && mod->type != SPELLMOD_PCT)
continue;
switch (mod->type)
{
flag96 mask;
mask[j / 32] = 1u << (j % 32);
flatMod.ModifierData[j].ClassIndex = j;
flatMod.ModifierData[j].ModifierValue = 0.0f;
pctMod.ModifierData[j].ClassIndex = j;
pctMod.ModifierData[j].ModifierValue = 1.0f;
for (SpellModifier* mod : m_spellMods[i][SPELLMOD_FLAT])
if (mod->mask & mask)
flatMod.ModifierData[j].ModifierValue += mod->value;
for (SpellModifier* mod : m_spellMods[i][SPELLMOD_PCT])
if (mod->mask & mask)
pctMod.ModifierData[j].ModifierValue *= 1.0f + CalculatePct(1.0f, mod->value);
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] + 3, 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);
modifierValue += static_cast<SpellModifierByClassMask const*>(mod)->value;
}
break;
case SPELLMOD_PCT:
if (!pctModifier || pctModifier->ModIndex != uint8(mod->op))
{
pctModifier = &pctMods.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] + 3, 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);
modifierValue += static_cast<SpellModifierByClassMask const*>(mod)->value;
}
break;
default:
break;
}
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());
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());
flatMods.Modifiers.emplace_back(std::move(flatMod));
pctMods.Modifiers.emplace_back(std::move(pctMod));
}
if (!flatMods.Modifiers.empty())
+32 -7
View File
@@ -185,18 +185,41 @@ uint32 const ArmorSpecializationIds[MAX_ARMOR_SPECIALIZATION_IDS] = { 86530, 865
// Spell modifier (used for modify other spells)
struct SpellModifier
{
SpellModifier(Aura* _ownerAura) : op(SpellModOp::HealingAndDamage), type(SPELLMOD_FLAT), value(0), mask(), spellId(0), ownerAura(_ownerAura) { }
SpellModifier(Aura* _ownerAura) : op(SpellModOp::HealingAndDamage), type(SPELLMOD_FLAT), spellId(0), ownerAura(_ownerAura) { }
virtual ~SpellModifier() = default;
SpellModOp op;
SpellModType type;
int32 value;
flag96 mask;
uint32 spellId;
Aura* const ownerAura;
};
struct SpellModifierByClassMask : SpellModifier
{
SpellModifierByClassMask(Aura* _ownerAura) : SpellModifier(_ownerAura), value(0), mask() { }
int32 value;
flag96 mask;
};
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,
@@ -218,7 +241,7 @@ struct PlayerCurrency
typedef std::unordered_map<uint32, PlayerTalent*> PlayerTalentMap;
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;
@@ -1649,9 +1672,11 @@ class TC_GAME_API Player : public Unit, public GridObject<Player>
PlayerSpellMap & GetSpellMap() { return m_spells; }
void AddSpellMod(SpellModifier* mod, bool apply);
static bool IsAffectedBySpellmod(SpellInfo const* spellInfo, SpellModifier* mod, Spell* spell = nullptr);
static bool IsAffectedBySpellmod(SpellInfo const* spellInfo, SpellModifier const* mod, Spell* spell = nullptr);
template <class T>
void ApplySpellMod(uint32 spellId, SpellModOp op, T& basevalue, Spell* spell = nullptr) const;
void GetSpellModValues(SpellInfo const* spellInfo, SpellModOp op, Spell* spell, T base, int32* flat, float* pct) const;
template <class T>
void ApplySpellMod(SpellInfo const* spellInfo, SpellModOp op, T& basevalue, Spell* spell = nullptr) const;
static void ApplyModToSpell(SpellModifier* mod, Spell* spell);
void SetSpellModTakingSpell(Spell* spell, bool apply);
void SendSpellModifiers() const;
@@ -2592,7 +2617,7 @@ class TC_GAME_API Player : 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;