Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
181 changes: 138 additions & 43 deletions src/server/game/Entities/Player/Player.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -22283,7 +22283,7 @@ void Player::SendRemoveControlBar() const
SendDirectMessage(packet.Write());
}

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;
Expand Down Expand Up @@ -22326,13 +22326,28 @@ void Player::GetSpellModValues(SpellInfo const* spellInfo, SpellModOp op, Spell*
if (!IsAffectedBySpellmod(spellInfo, mod, spell))
continue;

if (base < T(10000) && mod->value <= -100)
if (base < T(10000) && static_cast<SpellModifierByClassMask*>(mod)->value <= -100)
{
modInstantSpell = mod;
break;
}
}

if (!modInstantSpell)
{
for (SpellModifier* mod : m_spellMods[AsUnderlyingType(op)][SPELLMOD_LABEL_PCT])
{
if (!IsAffectedBySpellmod(spellInfo, mod, spell))
continue;

if (base < T(10000) && static_cast<SpellPctModifierByLabel*>(mod)->value.ModifierValue <= -1.0f)
{
modInstantSpell = mod;
break;
}
}
}

if (modInstantSpell)
{
Player::ApplyModToSpell(modInstantSpell, spell);
Expand All @@ -22350,13 +22365,28 @@ void Player::GetSpellModValues(SpellInfo const* spellInfo, SpellModOp op, Spell*
if (!IsAffectedBySpellmod(spellInfo, mod, spell))
continue;

if (mod->value >= 100)
if (static_cast<SpellModifierByClassMask*>(mod)->value >= 100)
{
modCritical = mod;
break;
}
}

if (!modCritical)
{
for (SpellModifier* mod : m_spellMods[AsUnderlyingType(op)][SPELLMOD_LABEL_FLAT])
{
if (!IsAffectedBySpellmod(spellInfo, mod, spell))
continue;

if (static_cast<SpellFlatModifierByLabel*>(mod)->value.ModifierValue >= 100)
{
modCritical = mod;
break;
}
}
}

if (modCritical)
{
Player::ApplyModToSpell(modCritical, spell);
Expand All @@ -22374,7 +22404,16 @@ void Player::GetSpellModValues(SpellInfo const* spellInfo, SpellModOp op, Spell*
if (!IsAffectedBySpellmod(spellInfo, mod, spell))
continue;

*flat += mod->value;
*flat += static_cast<SpellModifierByClassMask*>(mod)->value;
Player::ApplyModToSpell(mod, spell);
}

for (SpellModifier* mod : m_spellMods[AsUnderlyingType(op)][SPELLMOD_LABEL_FLAT])
{
if (!IsAffectedBySpellmod(spellInfo, mod, spell))
continue;

*flat += static_cast<SpellFlatModifierByLabel*>(mod)->value.ModifierValue;
Player::ApplyModToSpell(mod, spell);
}

Expand All @@ -22390,11 +22429,31 @@ void Player::GetSpellModValues(SpellInfo const* spellInfo, SpellModOp op, Spell*
// special case (skip > 10sec spell casts for instant cast setting)
if (op == SpellModOp::ChangeCastTime)
{
if (base >= T(10000) && mod->value <= -100)
if (base >= T(10000) && static_cast<SpellModifierByClassMask*>(mod)->value <= -100)
continue;
}

*pct *= 1.0f + CalculatePct(1.0f, mod->value);
*pct *= 1.0f + CalculatePct(1.0f, static_cast<SpellModifierByClassMask*>(mod)->value);
Player::ApplyModToSpell(mod, spell);
}

for (SpellModifier* mod : m_spellMods[AsUnderlyingType(op)][SPELLMOD_LABEL_PCT])
{
if (!IsAffectedBySpellmod(spellInfo, mod, spell))
continue;

// skip percent mods for null basevalue (most important for spell mods with charges)
if (base + *flat == T(0))
continue;

// special case (skip > 10sec spell casts for instant cast setting)
if (op == SpellModOp::ChangeCastTime)
{
if (base >= T(10000) && static_cast<SpellPctModifierByLabel*>(mod)->value.ModifierValue <= -1.0f)
continue;
}

*pct *= static_cast<SpellPctModifierByLabel*>(mod)->value.ModifierValue;
Player::ApplyModToSpell(mod, spell);
}
}
Expand Down Expand Up @@ -22429,48 +22488,84 @@ void Player::AddSpellMod(SpellModifier* mod, bool apply)
m_spellMods[AsUnderlyingType(mod->op)][mod->type].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;
case SPELLMOD_FLAT:
case SPELLMOD_PCT:
if (!IsLoading())
{
OpcodeServer opcode = (mod->type == SPELLMOD_FLAT) ? SMSG_SET_FLAT_SPELL_MODIFIER : SMSG_SET_PCT_SPELL_MODIFIER;

WorldPackets::Spells::SetSpellModifier packet(opcode);
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];
/// @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);
spellMod.ModIndex = AsUnderlyingType(mod->op);

for (int eff = 0; eff < 128; ++eff)
{
flag128 mask;
mask[eff / 32] = 1u << (eff % 32);
if (mod->mask & mask)
{
WorldPackets::Spells::SpellModifierData modData;

if (mod->type == SPELLMOD_FLAT)
for (int eff = 0; eff < 128; ++eff)
{
modData.ModifierValue = 0.0f;
for (SpellModifier* spellMod : m_spellMods[AsUnderlyingType(mod->op)][SPELLMOD_FLAT])
if (spellMod->mask & mask)
modData.ModifierValue += spellMod->value;
}
else
{
modData.ModifierValue = 1.0f;
for (SpellModifier* spellMod : m_spellMods[AsUnderlyingType(mod->op)][SPELLMOD_PCT])
if (spellMod->mask & mask)
modData.ModifierValue *= 1.0f + CalculatePct(1.0f, spellMod->value);
}
flag128 mask;
mask[eff / 32] = 1u << (eff % 32);
if (static_cast<SpellModifierByClassMask const*>(mod)->mask & mask)
{
WorldPackets::Spells::SpellModifierData modData;

modData.ClassIndex = eff;
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);
}

spellMod.ModifierData.push_back(modData);
}
}
modData.ClassIndex = eff;

SendDirectMessage(packet.Write());
spellMod.ModifierData.push_back(modData);
}
}

SendDirectMessage(packet.Write());
}
break;
case SPELLMOD_LABEL_FLAT:
if (apply)
{
AddDynamicUpdateFieldValue(m_values.ModifyValue(&Player::m_activePlayerData)
.ModifyValue(&UF::ActivePlayerData::SpellFlatModByLabel)) = static_cast<SpellFlatModifierByLabel const*>(mod)->value;
}
else
{
int32 firstIndex = m_activePlayerData->SpellFlatModByLabel.FindIndex(static_cast<SpellFlatModifierByLabel const*>(mod)->value);
if (firstIndex >= 0)
RemoveDynamicUpdateFieldValue(m_values.ModifyValue(&Player::m_activePlayerData)
.ModifyValue(&UF::ActivePlayerData::SpellFlatModByLabel), firstIndex);
}
break;
case SPELLMOD_LABEL_PCT:
if (apply)
{
AddDynamicUpdateFieldValue(m_values.ModifyValue(&Player::m_activePlayerData)
.ModifyValue(&UF::ActivePlayerData::SpellPctModByLabel)) = static_cast<SpellPctModifierByLabel const*>(mod)->value;
}
else
{
int32 firstIndex = m_activePlayerData->SpellPctModByLabel.FindIndex(static_cast<SpellPctModifierByLabel const*>(mod)->value);
if (firstIndex >= 0)
RemoveDynamicUpdateFieldValue(m_values.ModifyValue(&Player::m_activePlayerData)
.ModifyValue(&UF::ActivePlayerData::SpellPctModByLabel), firstIndex);
}
break;
default:
break;
}
}

Expand Down Expand Up @@ -22520,12 +22615,12 @@ void Player::SendSpellModifiers() const
pctMod.ModifierData[j].ModifierValue = 1.0f;

for (SpellModifier* mod : m_spellMods[i][SPELLMOD_FLAT])
if (mod->mask & mask)
flatMod.ModifierData[j].ModifierValue += mod->value;
if (static_cast<SpellModifierByClassMask const*>(mod)->mask & mask)
flatMod.ModifierData[j].ModifierValue += static_cast<SpellModifierByClassMask const*>(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);
if (static_cast<SpellModifierByClassMask const*>(mod)->mask & mask)
pctMod.ModifierData[j].ModifierValue *= 1.0f + CalculatePct(1.0f, static_cast<SpellModifierByClassMask const*>(mod)->value);
}

flatMod.ModifierData.erase(std::remove_if(flatMod.ModifierData.begin(), flatMod.ModifierData.end(), [](WorldPackets::Spells::SpellModifierData const& mod)
Expand Down
27 changes: 23 additions & 4 deletions src/server/game/Entities/Player/Player.h
Original file line number Diff line number Diff line change
Expand Up @@ -147,6 +147,8 @@ enum SpellModType : uint8
{
SPELLMOD_FLAT = 0, // SPELL_AURA_ADD_FLAT_MODIFIER
SPELLMOD_PCT = 1, // SPELL_AURA_ADD_PCT_MODIFIER
SPELLMOD_LABEL_FLAT = 2, // SPELL_AURA_ADD_FLAT_MODIFIER_BY_SPELL_LABEL
SPELLMOD_LABEL_PCT = 3, // SPELL_AURA_ADD_PCT_MODIFIER_BY_SPELL_LABEL
SPELLMOD_END
};

Expand Down Expand Up @@ -250,17 +252,34 @@ enum SpecResetType
// 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) { }

SpellModOp op;
SpellModType type;

int32 value;
flag128 mask;
uint32 spellId;
Aura* const ownerAura;
};

struct SpellModifierByClassMask : SpellModifier
{
SpellModifierByClassMask(Aura* _ownerAura) : SpellModifier(_ownerAura), value(0), mask() { }

int32 value;
flag128 mask;
};

template<typename T>
struct SpellModifierByLabel : SpellModifier
{
SpellModifierByLabel(Aura* _ownerAura) : SpellModifier(_ownerAura) { }

T value;
};

using SpellFlatModifierByLabel = SpellModifierByLabel<UF::SpellFlatModByLabel>;
using SpellPctModifierByLabel = SpellModifierByLabel<UF::SpellPctModByLabel>;

enum PlayerCurrencyState
{
PLAYERCURRENCY_UNCHANGED = 0,
Expand Down Expand Up @@ -1804,7 +1823,7 @@ 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 GetSpellModValues(SpellInfo const* spellInfo, SpellModOp op, Spell* spell, T base, int32* flat, float* pct) const;
template <class T>
Expand Down
23 changes: 21 additions & 2 deletions src/server/game/Entities/Unit/Unit.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2745,7 +2745,7 @@ void Unit::_UpdateAutoRepeatSpell()
// check "realtime" interrupts
// don't cancel spells which are affected by a SPELL_AURA_CAST_WHILE_WALKING effect
if (((GetTypeId() == TYPEID_PLAYER && ToPlayer()->isMoving()) || IsNonMeleeSpellCast(false, false, true, autoRepeatSpellInfo->Id == 75)) &&
!HasAuraTypeWithAffectMask(SPELL_AURA_CAST_WHILE_WALKING, autoRepeatSpellInfo))
!CanCastSpellWhileMoving(autoRepeatSpellInfo))
{
// cancel wand shoot
if (autoRepeatSpellInfo->Id != 75)
Expand Down Expand Up @@ -2967,6 +2967,10 @@ bool Unit::IsMovementPreventedByCasting() const
if (!HasUnitState(UNIT_STATE_CASTING))
return false;

if (Spell* spell = m_currentSpells[CURRENT_GENERIC_SPELL])
if (CanCastSpellWhileMoving(spell->GetSpellInfo()))
return false;

// channeled spells during channel stage (after the initial cast timer) allow movement with a specific spell attribute
if (Spell* spell = m_currentSpells[CURRENT_CHANNELED_SPELL])
if (spell->getState() != SPELL_STATE_FINISHED && spell->IsChannelActive())
Expand All @@ -2977,6 +2981,21 @@ bool Unit::IsMovementPreventedByCasting() const
return true;
}

bool Unit::CanCastSpellWhileMoving(SpellInfo const* spellInfo) const
{
if (HasAuraTypeWithAffectMask(SPELL_AURA_CAST_WHILE_WALKING, spellInfo))
return true;

if (HasAuraType(SPELL_AURA_CAST_WHILE_WALKING_ALL))
return true;

for (uint32 label : spellInfo->Labels)
if (HasAuraTypeWithMiscvalue(SPELL_AURA_CAST_WHILE_WALKING_BY_SPELL_LABEL, label))
return true;

return false;
}

bool Unit::isInFrontInMap(Unit const* target, float distance, float arc) const
{
return IsWithinDistInMap(target, distance) && HasInArc(arc, target);
Expand Down Expand Up @@ -3874,7 +3893,7 @@ bool IsInterruptFlagIgnoredForSpell(InterruptFlag /*flag*/, Unit const* /*unit*/
template<>
bool IsInterruptFlagIgnoredForSpell(SpellAuraInterruptFlags flag, Unit const* unit, SpellInfo const* spellInfo)
{
return flag == SpellAuraInterruptFlags::Moving && unit->HasAuraTypeWithAffectMask(SPELL_AURA_CAST_WHILE_WALKING, spellInfo);
return flag == SpellAuraInterruptFlags::Moving && unit->CanCastSpellWhileMoving(spellInfo);
}

template <typename InterruptFlags>
Expand Down
1 change: 1 addition & 0 deletions src/server/game/Entities/Unit/Unit.h
Original file line number Diff line number Diff line change
Expand Up @@ -1498,6 +1498,7 @@ class TC_GAME_API Unit : public WorldObject

virtual bool IsFocusing(Spell const* /*focusSpell*/ = nullptr, bool /*withDelay*/ = false) { return false; }
virtual bool IsMovementPreventedByCasting() const;
bool CanCastSpellWhileMoving(SpellInfo const* spellInfo) const;

SpellHistory* GetSpellHistory() { return _spellHistory; }
SpellHistory const* GetSpellHistory() const { return _spellHistory; }
Expand Down
8 changes: 4 additions & 4 deletions src/server/game/Spells/Auras/SpellAuraDefines.h
Original file line number Diff line number Diff line change
Expand Up @@ -303,8 +303,8 @@ enum AuraType : uint32
SPELL_AURA_ARENA_PREPARATION = 215,
SPELL_AURA_HASTE_SPELLS = 216,
SPELL_AURA_MOD_MELEE_HASTE_2 = 217,
SPELL_AURA_ADD_PCT_MODIFIER_BY_SPELL_LABEL = 218, // NYI
SPELL_AURA_ADD_FLAT_MODIFIER_BY_SPELL_LABEL = 219, // NYI
SPELL_AURA_ADD_PCT_MODIFIER_BY_SPELL_LABEL = 218,
SPELL_AURA_ADD_FLAT_MODIFIER_BY_SPELL_LABEL = 219,
SPELL_AURA_MOD_ABILITY_SCHOOL_MASK = 220, // NYI
SPELL_AURA_MOD_DETAUNT = 221,
SPELL_AURA_REMOVE_TRANSMOG_COST = 222,
Expand Down Expand Up @@ -392,7 +392,7 @@ enum AuraType : uint32
SPELL_AURA_MOD_FAKE_INEBRIATE = 304,
SPELL_AURA_MOD_MINIMUM_SPEED = 305,
SPELL_AURA_MOD_CRIT_CHANCE_FOR_CASTER = 306,
SPELL_AURA_CAST_WHILE_WALKING_BY_SPELL_LABEL = 307, // NYI
SPELL_AURA_CAST_WHILE_WALKING_BY_SPELL_LABEL = 307,
SPELL_AURA_MOD_CRIT_CHANCE_FOR_CASTER_WITH_ABILITIES = 308,
SPELL_AURA_MOD_RESILIENCE = 309, // NYI
SPELL_AURA_MOD_CREATURE_AOE_DAMAGE_AVOIDANCE = 310,
Expand Down Expand Up @@ -462,7 +462,7 @@ enum AuraType : uint32
SPELL_AURA_MODIFY_FALL_DAMAGE_PCT = 374, // NYI
SPELL_AURA_HIDE_MODEL_AND_EQUIPEMENT_SLOTS = 375,
SPELL_AURA_MOD_CURRENCY_GAIN_FROM_SOURCE = 376, // NYI
SPELL_AURA_CAST_WHILE_WALKING_2 = 377, // NYI
SPELL_AURA_CAST_WHILE_WALKING_ALL = 377, // Enables casting all spells while moving
SPELL_AURA_MOD_POSSESS_PET = 378,
SPELL_AURA_MOD_MANA_REGEN_PCT = 379,
SPELL_AURA_380 = 380,
Expand Down
Loading