fix(Core/Spells): Spellmods should not drop charges if not consumed b… (#13498)
This commit is contained in:
@@ -9598,6 +9598,137 @@ bool Player::IsAffectedBySpellmod(SpellInfo const* spellInfo, SpellModifier* mod
|
|||||||
return spellInfo->IsAffectedBySpellMod(mod);
|
return spellInfo->IsAffectedBySpellMod(mod);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template <class T>
|
||||||
|
void Player::ApplySpellMod(uint32 spellId, SpellModOp op, T& basevalue, Spell* spell, bool temporaryPet)
|
||||||
|
{
|
||||||
|
SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(spellId);
|
||||||
|
if (!spellInfo)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
float totalmul = 1.0f;
|
||||||
|
int32 totalflat = 0;
|
||||||
|
|
||||||
|
auto calculateSpellMod = [&](SpellModifier* mod)
|
||||||
|
{
|
||||||
|
// xinef: temporary pets cannot use charged mods of owner, needed for mirror image QQ they should use their own auras
|
||||||
|
if (temporaryPet && mod->charges != 0)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mod->type == SPELLMOD_FLAT)
|
||||||
|
{
|
||||||
|
// xinef: do not allow to consume more than one 100% crit increasing spell
|
||||||
|
if (mod->op == SPELLMOD_CRITICAL_CHANCE && totalflat >= 100)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
int32 flatValue = mod->value;
|
||||||
|
|
||||||
|
// SPELL_MOD_THREAT - divide by 100 (in packets we send threat * 100)
|
||||||
|
if (mod->op == SPELLMOD_THREAT)
|
||||||
|
{
|
||||||
|
flatValue /= 100;
|
||||||
|
}
|
||||||
|
|
||||||
|
totalflat += flatValue;
|
||||||
|
}
|
||||||
|
else if (mod->type == SPELLMOD_PCT)
|
||||||
|
{
|
||||||
|
// skip percent mods for null basevalue (most important for spell mods with charges)
|
||||||
|
if (basevalue == T(0) || totalmul == 0.0f)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// special case (skip > 10sec spell casts for instant cast setting)
|
||||||
|
if (mod->op == SPELLMOD_CASTING_TIME && basevalue >= T(10000) && mod->value <= -100)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// xinef: special exception for surge of light, dont affect crit chance if previous mods were not applied
|
||||||
|
else if (mod->op == SPELLMOD_CRITICAL_CHANCE && spell && !HasSpellMod(mod, spell))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// xinef: special case for backdraft gcd reduce with backlast time reduction, dont affect gcd if cast time was not applied
|
||||||
|
else if (mod->op == SPELLMOD_GLOBAL_COOLDOWN && spell && !HasSpellMod(mod, spell))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// xinef: those two mods should be multiplicative (Glyph of Renew)
|
||||||
|
if (mod->op == SPELLMOD_DAMAGE || mod->op == SPELLMOD_DOT)
|
||||||
|
{
|
||||||
|
totalmul *= CalculatePct(1.0f, 100.0f + mod->value);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
totalmul += CalculatePct(1.0f, mod->value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
DropModCharge(mod, spell);
|
||||||
|
};
|
||||||
|
|
||||||
|
// Drop charges for triggering spells instead of triggered ones
|
||||||
|
if (m_spellModTakingSpell)
|
||||||
|
{
|
||||||
|
spell = m_spellModTakingSpell;
|
||||||
|
}
|
||||||
|
|
||||||
|
SpellModifier* chargedMod = nullptr;
|
||||||
|
for (auto mod : m_spellMods[op])
|
||||||
|
{
|
||||||
|
// Charges can be set only for mods with auras
|
||||||
|
if (!mod->ownerAura)
|
||||||
|
{
|
||||||
|
ASSERT(!mod->charges);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!IsAffectedBySpellmod(spellInfo, mod, spell))
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mod->ownerAura->IsUsingCharges())
|
||||||
|
{
|
||||||
|
if (!chargedMod || (chargedMod->ownerAura->GetSpellInfo()->SpellPriority < mod->ownerAura->GetSpellInfo()->SpellPriority))
|
||||||
|
{
|
||||||
|
chargedMod = mod;
|
||||||
|
}
|
||||||
|
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
calculateSpellMod(mod);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (chargedMod)
|
||||||
|
{
|
||||||
|
calculateSpellMod(chargedMod);
|
||||||
|
}
|
||||||
|
|
||||||
|
float diff = 0.0f;
|
||||||
|
if (op == SPELLMOD_CASTING_TIME || op == SPELLMOD_DURATION)
|
||||||
|
{
|
||||||
|
diff = ((float)basevalue + totalflat) * (totalmul - 1.0f) + (float)totalflat;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
diff = (float)basevalue * (totalmul - 1.0f) + (float)totalflat;
|
||||||
|
}
|
||||||
|
|
||||||
|
basevalue = T((float)basevalue + diff);
|
||||||
|
}
|
||||||
|
|
||||||
|
template AC_GAME_API void Player::ApplySpellMod(uint32 spellId, SpellModOp op, int32& basevalue, Spell* spell, bool temporaryPet);
|
||||||
|
template AC_GAME_API void Player::ApplySpellMod(uint32 spellId, SpellModOp op, uint32& basevalue, Spell* spell, bool temporaryPet);
|
||||||
|
template AC_GAME_API void Player::ApplySpellMod(uint32 spellId, SpellModOp op, float& basevalue, Spell* spell, bool temporaryPet);
|
||||||
|
|
||||||
// Binary predicate for sorting SpellModifiers
|
// Binary predicate for sorting SpellModifiers
|
||||||
class SpellModPred
|
class SpellModPred
|
||||||
{
|
{
|
||||||
@@ -9765,6 +9896,13 @@ void Player::RemoveSpellMods(Spell* spell)
|
|||||||
SpellModifier* mod = *itr;
|
SpellModifier* mod = *itr;
|
||||||
++itr;
|
++itr;
|
||||||
|
|
||||||
|
// don't handle spells with proc_event entry defined
|
||||||
|
// this is a temporary workaround, because all spellmods should be handled like that
|
||||||
|
if (sSpellMgr->GetSpellProcEvent(mod->spellId))
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
// spellmods without aura set cannot be charged
|
// spellmods without aura set cannot be charged
|
||||||
if (!mod->ownerAura || !mod->ownerAura->IsUsingCharges())
|
if (!mod->ownerAura || !mod->ownerAura->IsUsingCharges())
|
||||||
continue;
|
continue;
|
||||||
@@ -9800,11 +9938,6 @@ void Player::RemoveSpellMods(Spell* spell)
|
|||||||
|
|
||||||
void Player::DropModCharge(SpellModifier* mod, Spell* spell)
|
void Player::DropModCharge(SpellModifier* mod, Spell* spell)
|
||||||
{
|
{
|
||||||
// don't handle spells with proc_event entry defined
|
|
||||||
// this is a temporary workaround, because all spellmods should be handled like that
|
|
||||||
if (sSpellMgr->GetSpellProcEvent(mod->spellId))
|
|
||||||
return;
|
|
||||||
|
|
||||||
if (spell && mod->ownerAura && mod->charges > 0)
|
if (spell && mod->ownerAura && mod->charges > 0)
|
||||||
{
|
{
|
||||||
if (--mod->charges == 0)
|
if (--mod->charges == 0)
|
||||||
|
|||||||
@@ -1732,7 +1732,8 @@ public:
|
|||||||
void AddSpellMod(SpellModifier* mod, bool apply);
|
void AddSpellMod(SpellModifier* mod, bool apply);
|
||||||
bool IsAffectedBySpellmod(SpellInfo const* spellInfo, SpellModifier* mod, Spell* spell = nullptr);
|
bool IsAffectedBySpellmod(SpellInfo const* spellInfo, SpellModifier* mod, Spell* spell = nullptr);
|
||||||
bool HasSpellMod(SpellModifier* mod, Spell* spell);
|
bool HasSpellMod(SpellModifier* mod, Spell* spell);
|
||||||
template <class T> T ApplySpellMod(uint32 spellId, SpellModOp op, T& basevalue, Spell* spell = nullptr, bool temporaryPet = false);
|
template <class T>
|
||||||
|
void ApplySpellMod(uint32 spellId, SpellModOp op, T& basevalue, Spell* spell = nullptr, bool temporaryPet = false);
|
||||||
void RemoveSpellMods(Spell* spell);
|
void RemoveSpellMods(Spell* spell);
|
||||||
void RestoreSpellMods(Spell* spell, uint32 ownerAuraId = 0, Aura* aura = nullptr);
|
void RestoreSpellMods(Spell* spell, uint32 ownerAuraId = 0, Aura* aura = nullptr);
|
||||||
void RestoreAllSpellMods(uint32 ownerAuraId = 0, Aura* aura = nullptr);
|
void RestoreAllSpellMods(uint32 ownerAuraId = 0, Aura* aura = nullptr);
|
||||||
@@ -2941,131 +2942,4 @@ private:
|
|||||||
void AddItemsSetItem(Player* player, Item* item);
|
void AddItemsSetItem(Player* player, Item* item);
|
||||||
void RemoveItemsSetItem(Player* player, ItemTemplate const* proto);
|
void RemoveItemsSetItem(Player* player, ItemTemplate const* proto);
|
||||||
|
|
||||||
// "the bodies of template functions must be made available in a header file"
|
|
||||||
template <class T> T Player::ApplySpellMod(uint32 spellId, SpellModOp op, T& basevalue, Spell* spell, bool temporaryPet)
|
|
||||||
{
|
|
||||||
SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(spellId);
|
|
||||||
if (!spellInfo)
|
|
||||||
{
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
float totalmul = 1.0f;
|
|
||||||
int32 totalflat = 0;
|
|
||||||
|
|
||||||
auto calculateSpellMod = [&](SpellModifier* mod)
|
|
||||||
{
|
|
||||||
// xinef: temporary pets cannot use charged mods of owner, needed for mirror image QQ they should use their own auras
|
|
||||||
if (temporaryPet && mod->charges != 0)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (mod->type == SPELLMOD_FLAT)
|
|
||||||
{
|
|
||||||
// xinef: do not allow to consume more than one 100% crit increasing spell
|
|
||||||
if (mod->op == SPELLMOD_CRITICAL_CHANCE && totalflat >= 100)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
int32 flatValue = mod->value;
|
|
||||||
|
|
||||||
// SPELL_MOD_THREAT - divide by 100 (in packets we send threat * 100)
|
|
||||||
if (mod->op == SPELLMOD_THREAT)
|
|
||||||
{
|
|
||||||
flatValue /= 100;
|
|
||||||
}
|
|
||||||
|
|
||||||
totalflat += flatValue;
|
|
||||||
}
|
|
||||||
else if (mod->type == SPELLMOD_PCT)
|
|
||||||
{
|
|
||||||
// skip percent mods for null basevalue (most important for spell mods with charges)
|
|
||||||
if (basevalue == T(0) || totalmul == 0.0f)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// special case (skip > 10sec spell casts for instant cast setting)
|
|
||||||
if (mod->op == SPELLMOD_CASTING_TIME && basevalue >= T(10000) && mod->value <= -100)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
// xinef: special exception for surge of light, dont affect crit chance if previous mods were not applied
|
|
||||||
else if (mod->op == SPELLMOD_CRITICAL_CHANCE && spell && !HasSpellMod(mod, spell))
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
// xinef: special case for backdraft gcd reduce with backlast time reduction, dont affect gcd if cast time was not applied
|
|
||||||
else if (mod->op == SPELLMOD_GLOBAL_COOLDOWN && spell && !HasSpellMod(mod, spell))
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// xinef: those two mods should be multiplicative (Glyph of Renew)
|
|
||||||
if (mod->op == SPELLMOD_DAMAGE || mod->op == SPELLMOD_DOT)
|
|
||||||
{
|
|
||||||
totalmul *= CalculatePct(1.0f, 100.0f + mod->value);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
totalmul += CalculatePct(1.0f, mod->value);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
DropModCharge(mod, spell);
|
|
||||||
};
|
|
||||||
|
|
||||||
// Drop charges for triggering spells instead of triggered ones
|
|
||||||
if (m_spellModTakingSpell)
|
|
||||||
{
|
|
||||||
spell = m_spellModTakingSpell;
|
|
||||||
}
|
|
||||||
|
|
||||||
SpellModifier* chargedMod = nullptr;
|
|
||||||
for (auto mod : m_spellMods[op])
|
|
||||||
{
|
|
||||||
// Charges can be set only for mods with auras
|
|
||||||
if (!mod->ownerAura)
|
|
||||||
{
|
|
||||||
ASSERT(!mod->charges);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!IsAffectedBySpellmod(spellInfo, mod, spell))
|
|
||||||
{
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (mod->ownerAura->IsUsingCharges())
|
|
||||||
{
|
|
||||||
if (!chargedMod || (chargedMod->ownerAura->GetSpellInfo()->SpellPriority < mod->ownerAura->GetSpellInfo()->SpellPriority))
|
|
||||||
{
|
|
||||||
chargedMod = mod;
|
|
||||||
}
|
|
||||||
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
calculateSpellMod(mod);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (chargedMod)
|
|
||||||
{
|
|
||||||
calculateSpellMod(chargedMod);
|
|
||||||
}
|
|
||||||
|
|
||||||
float diff = 0.0f;
|
|
||||||
if (op == SPELLMOD_CASTING_TIME || op == SPELLMOD_DURATION)
|
|
||||||
{
|
|
||||||
diff = ((float)basevalue + totalflat) * (totalmul - 1.0f) + (float)totalflat;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
diff = (float)basevalue * (totalmul - 1.0f) + (float)totalflat;
|
|
||||||
}
|
|
||||||
|
|
||||||
basevalue = T((float)basevalue + diff);
|
|
||||||
return T(diff);
|
|
||||||
}
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -16590,18 +16590,14 @@ void Unit::ProcDamageAndSpellFor(bool isVictim, Unit* target, uint32 procFlag, u
|
|||||||
case SPELL_AURA_ADD_FLAT_MODIFIER:
|
case SPELL_AURA_ADD_FLAT_MODIFIER:
|
||||||
case SPELL_AURA_ADD_PCT_MODIFIER:
|
case SPELL_AURA_ADD_PCT_MODIFIER:
|
||||||
{
|
{
|
||||||
if (SpellModifier* mod = triggeredByAura->GetSpellModifier())
|
if (triggeredByAura->GetSpellModifier())
|
||||||
{
|
{
|
||||||
if (mod->op == SPELLMOD_CASTING_TIME && mod->value < 0 && procSpell)
|
// Do proc if mod is consumed by spell
|
||||||
|
if (!procSpell || procSpell->m_appliedMods.find(i->aura) != procSpell->m_appliedMods.end())
|
||||||
{
|
{
|
||||||
// Skip instant spells
|
|
||||||
if (procSpellInfo->CalcCastTime() <= 0 || (procSpell->GetTriggeredCastFlags() & TRIGGERED_CAST_DIRECTLY) != 0)
|
|
||||||
{
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
takeCharges = true;
|
takeCharges = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
|
|||||||
Reference in New Issue
Block a user