diff --git a/src/server/game/Spells/Auras/SpellAuraEffects.cpp b/src/server/game/Spells/Auras/SpellAuraEffects.cpp index e04007ce8c..b600511758 100644 --- a/src/server/game/Spells/Auras/SpellAuraEffects.cpp +++ b/src/server/game/Spells/Auras/SpellAuraEffects.cpp @@ -680,30 +680,15 @@ void AuraEffect::CalculatePeriodic(Unit* caster, bool resetPeriodicTimer, bool l GetBase()->CallScriptEffectCalcPeriodicHandlers(this, m_isPeriodic, _period); if (!m_isPeriodic) + { + _period = 0; return; - - if (_period == 0) - _period = 5200; // fallback value found in client - - Player* modOwner = caster ? caster->GetSpellModOwner() : nullptr; + } if (_period) - { - // Apply periodic time mod - if (modOwner) - modOwner->ApplySpellMod(GetSpellInfo()->Id, SpellModOp::Period, _period); + _period = m_spellInfo->CalcPeriod(caster, SpellEffIndex(GetEffIndex()), _period); - if (caster) - { - // Apply haste modifiers - if (m_spellInfo->HasAttribute(SPELL_ATTR5_SPELL_HASTE_AFFECTS_PERIODIC)) - _period = int32(_period * caster->GetFloatValue(UNIT_MOD_CAST_HASTE)); - - if (m_spellInfo->HasAttribute(SPELL_ATTR8_MELEE_HASTE_AFFECTS_PERIODIC) && caster->IsPlayer()) - _period = int32(_period * caster->GetFloatValue(PLAYER_FIELD_MOD_HASTE)); - } - } - else // prevent infinite loop on Update + if (!_period) // prevent infinite loop on Update m_isPeriodic = false; if (load) // aura loaded from db diff --git a/src/server/game/Spells/Auras/SpellAuras.cpp b/src/server/game/Spells/Auras/SpellAuras.cpp index 84f7f01718..2d3533b140 100644 --- a/src/server/game/Spells/Auras/SpellAuras.cpp +++ b/src/server/game/Spells/Auras/SpellAuras.cpp @@ -885,29 +885,7 @@ int32 Aura::CalcMaxDuration(Unit* caster) const /*static*/ int32 Aura::CalcMaxDuration(SpellInfo const* spellInfo, WorldObject* caster) { - Player* modOwner = nullptr; - int32 maxDuration = 0; - - if (caster) - { - modOwner = caster->GetSpellModOwner(); - maxDuration = caster->CalcSpellDuration(spellInfo); - } - else - maxDuration = spellInfo->GetDuration(); - - if (spellInfo->IsPassive() && !spellInfo->DurationEntry) - maxDuration = -1; - - // IsPermanent() checks max duration (which we are supposed to calculate here) - if (maxDuration != -1 && modOwner) - modOwner->ApplySpellMod(spellInfo->Id, SpellModOp::Duration, maxDuration); - - // Calculate duration of periodics affected by haste. - if (caster && caster->IsUnit() && spellInfo->HasAttribute(SPELL_ATTR8_HASTE_AFFECTS_DURATION)) - maxDuration = int32(maxDuration * caster->GetFloatValue(UNIT_MOD_CAST_HASTE)); - - return maxDuration; + return spellInfo->CalcDuration(caster); } void Aura::SetDuration(int32 duration, bool withMods) diff --git a/src/server/game/Spells/Spell.cpp b/src/server/game/Spells/Spell.cpp index 02175b2921..a6d2f52028 100644 --- a/src/server/game/Spells/Spell.cpp +++ b/src/server/game/Spells/Spell.cpp @@ -3037,8 +3037,8 @@ void Spell::DoSpellEffectHit(Unit* unit, uint8 effIndex, TargetInfo& hitInfo) || m_spellInfo->Effects[i].ApplyAuraName == SPELL_AURA_PERIODIC_HEAL)) return false; - if ((m_spellInfo->StackAmount < 2) && !(_triggeredCastFlags & TRIGGERED_DONT_RESET_PERIODIC_TIMER)) - return true; + if (m_spellInfo->StackAmount < 2) + return !(_triggeredCastFlags & TRIGGERED_DONT_RESET_PERIODIC_TIMER); return true; }(); @@ -3074,39 +3074,26 @@ void Spell::DoSpellEffectHit(Unit* unit, uint8 effIndex, TargetInfo& hitInfo) //if (!m_spellValue->Duration) { hitInfo.AuraDuration = caster->ModSpellDuration(hitInfo.AuraSpellInfo, unit, hitInfo.AuraDuration, hitInfo.Positive, hitInfo.HitAura->GetEffectMask()); - if (hitInfo.AuraDuration > 0) { - // Haste modifies duration and periodic of spells so we have to clamp the duration accordingly - if (m_spellInfo->HasAttribute(SPELL_ATTR8_HASTE_AFFECTS_DURATION) - || m_spellInfo->HasAttribute(SPELL_ATTR5_SPELL_HASTE_AFFECTS_PERIODIC) - || m_spellInfo->HasAttribute(SPELL_ATTR8_MELEE_HASTE_AFFECTS_PERIODIC)) + if (m_spellInfo->HasAttribute(SPELL_ATTR5_SPELL_HASTE_AFFECTS_PERIODIC) || m_spellInfo->HasAttribute(SPELL_ATTR8_HASTE_AFFECTS_DURATION)) { - // Haste based auras need their duration adapted. int32 origDuration = hitInfo.AuraDuration; hitInfo.AuraDuration = 0; + int32 period = 0; for (uint8 i = 0; i < MAX_SPELL_EFFECTS; ++i) - { if (AuraEffect const* aurEff = hitInfo.HitAura->GetEffect(effIndex)) - { - if (int32 period = aurEff->GetPeriod()) - { - hitInfo.AuraDuration = std::clamp(origDuration / period * period, 1, origDuration); + if (int32 effectPeriod = aurEff->GetPeriod()) + period = effectPeriod; - // If the duration of the aura could fit in half of a tick, round up - if (origDuration % period >= (period / 2)) - hitInfo.AuraDuration += period; - break; - } - } - } + if (period != 0) + hitInfo.AuraDuration = std::clamp(origDuration / period * period, 1, origDuration); // if there is no periodic effect if (!hitInfo.AuraDuration) hitInfo.AuraDuration = origDuration; } - // Apply rolled over duration if there is any hitInfo.AuraDuration += hitInfo.HitAura->GetRolledOverDuration(); } @@ -4679,7 +4666,7 @@ void Spell::UpdateSpellCastDataTargets(WorldPackets::Spells::SpellHitInfo& data) // possibly SPELL_MISS_IMMUNE2 for this?? targetInfo.MissCondition = SPELL_MISS_IMMUNE2; - if (targetInfo.MissCondition == SPELL_MISS_NONE) // hits + if (targetInfo.MissCondition == SPELL_MISS_NONE || (targetInfo.MissCondition == SPELL_MISS_BLOCK && !m_spellInfo->HasAttribute(SPELL_ATTR3_COMPLETELY_BLOCKED))) // hits { data.HitTargets.push_back(targetInfo.TargetGUID); m_channelTargetEffectMask |= targetInfo.EffectMask; diff --git a/src/server/game/Spells/SpellInfo.cpp b/src/server/game/Spells/SpellInfo.cpp index 405176d3af..2002ebd7ad 100644 --- a/src/server/game/Spells/SpellInfo.cpp +++ b/src/server/game/Spells/SpellInfo.cpp @@ -3353,15 +3353,104 @@ int32 SpellInfo::GetMaxDuration() const int32 SpellInfo::CalcDuration(WorldObject const* caster /*= nullptr*/) const { - int32 duration = GetDuration(); + if (!DurationEntry) + return IsPassive() ? -1 : 0; - if (caster) - if (Player* modOwner = caster->GetSpellModOwner()) - modOwner->ApplySpellMod(Id, SpellModOp::Duration, duration); + int32 duration = GetMaxDuration(); + if (duration == -1) + return -1; + + Unit const* unitCaster = caster ? caster->ToUnit() : nullptr; + if (!unitCaster) + return std::min(GetMaxDuration(), DurationEntry->Duration); + + uint32 level = unitCaster->getLevel(); + if (MaxLevel > 0 && level > MaxLevel) + level = MaxLevel; + if (BaseLevel > 0) + level -= BaseLevel; + + duration = DurationEntry->Duration + DurationEntry->DurationPerLevel * level; + duration = std::min(GetMaxDuration(), duration); + + if (duration == -1) + return -1; + + // Increase duration based on combo points + if (HasAttribute(SPELL_ATTR1_FINISHING_MOVE_DURATION)) + { + if (uint8 comboPoints = unitCaster->IsMovedByClient() ? unitCaster->GetGameClientMovingMe()->GetBasePlayer()->GetComboPoints() : 0) + { + if (GetDuration() != GetMaxDuration() && GetDuration() != -1) + duration += int32((GetMaxDuration() - GetDuration()) * comboPoints / 5); + } + } + + if (Player const* modOwner = unitCaster->GetSpellModOwner()) + modOwner->ApplySpellMod(Id, SpellModOp::Duration, duration); + + if (HasAttribute(SPELL_ATTR8_HASTE_AFFECTS_DURATION) || HasAttribute(SPELL_ATTR5_SPELL_HASTE_AFFECTS_PERIODIC)) + { + SpellEffIndex periodicEffectIndex = EFFECT_0; + bool hasPeriodicEffect = [&]() + { + for (uint8 i = 0; i < MAX_SPELL_EFFECTS; ++i) + if (Effects[i].IsEffect() && Effects[i].AuraPeriod) + { + periodicEffectIndex = SpellEffIndex(i); + return true; + } + + return false; + }(); + + if (hasPeriodicEffect && HasAttribute(SPELL_ATTR5_SPELL_HASTE_AFFECTS_PERIODIC) && !HasAttribute(SPELL_ATTR3_IGNORE_CASTER_MODIFIERS)) + { + float hasteMod = unitCaster->GetFloatValue(UNIT_MOD_CAST_HASTE); + if (hasteMod > 0.001f) + { + if (HasAttribute(SPELL_ATTR8_HASTE_AFFECTS_DURATION)) + return int32(duration * hasteMod); + int32 period = CalcPeriod(caster, periodicEffectIndex); + if (period > 0) + duration = int32(period * (duration / (float)period)); + } + } + } return duration; } +int32 SpellInfo::CalcPeriod(WorldObject const* caster, SpellEffIndex effIndex, Optional periodOverride /*= {}*/) const +{ + int32 period = 0; + if (periodOverride.has_value() && *periodOverride > 0) + period = *periodOverride; + else + { + if (Effects[effIndex].IsEffect() && Effects[effIndex].AuraPeriod != 0) + period = Effects[effIndex].AuraPeriod; + else + period = 5200; + } + + Unit const* unitCaster = caster ? caster->ToUnit() : nullptr; + if (!unitCaster) + return period; + + if (Player const* modOwner = unitCaster->GetSpellModOwner()) + modOwner->ApplySpellMod(Id, SpellModOp::Period, period); + + if (HasAttribute(SPELL_ATTR5_SPELL_HASTE_AFFECTS_PERIODIC) && !HasAttribute(SPELL_ATTR3_IGNORE_CASTER_MODIFIERS)) + { + float hasteMod = unitCaster->GetFloatValue(UNIT_MOD_CAST_HASTE); + if (hasteMod > 0.001f) + period = ((float)period * hasteMod); + } + + return period; +} + uint32 SpellInfo::CalcCastTime(uint8 level, Spell* spell /*= nullptr*/) const { int32 castTime = 0; diff --git a/src/server/game/Spells/SpellInfo.h b/src/server/game/Spells/SpellInfo.h index a1523197dc..988a5c51ed 100644 --- a/src/server/game/Spells/SpellInfo.h +++ b/src/server/game/Spells/SpellInfo.h @@ -578,6 +578,7 @@ class TC_GAME_API SpellInfo int32 GetMaxDuration() const; int32 CalcDuration(WorldObject const* caster = nullptr) const; + int32 CalcPeriod(WorldObject const* caster, SpellEffIndex effIndex, Optional periodOverride = {}) const; uint32 GetMaxTicks() const;