diff options
author | ariel- <ariel-@users.noreply.github.com> | 2016-12-11 15:04:25 -0300 |
---|---|---|
committer | joschiwald <joschiwald.trinity@gmail.com> | 2018-04-21 13:52:37 +0200 |
commit | 76831d16bea0a4fdf7e11656c69949fd706971f9 (patch) | |
tree | 53530b5caee70d3e9d44b4fc167c907f660e1231 /src | |
parent | 4aaf4245646e4f117cb2de7855b5672ce2fff14f (diff) |
Core/Spell: implemented dispel reflection
Closes #18323
(cherry picked from commit f0772eea98c66f7fd2e745c0cfb8599d6c21ef19)
# Conflicts:
# src/server/game/Spells/Spell.cpp
# src/server/game/Spells/Spell.h
# src/server/game/Spells/SpellEffects.cpp
Diffstat (limited to 'src')
-rw-r--r-- | src/server/game/Entities/Unit/Unit.cpp | 25 | ||||
-rw-r--r-- | src/server/game/Entities/Unit/Unit.h | 30 | ||||
-rw-r--r-- | src/server/game/Spells/Auras/SpellAuras.cpp | 2 | ||||
-rw-r--r-- | src/server/game/Spells/Auras/SpellAuras.h | 2 | ||||
-rw-r--r-- | src/server/game/Spells/Spell.cpp | 6 | ||||
-rw-r--r-- | src/server/game/Spells/Spell.h | 23 | ||||
-rw-r--r-- | src/server/game/Spells/SpellEffects.cpp | 140 |
7 files changed, 135 insertions, 93 deletions
diff --git a/src/server/game/Entities/Unit/Unit.cpp b/src/server/game/Entities/Unit/Unit.cpp index 847a8e6f51e..f0af80b721e 100644 --- a/src/server/game/Entities/Unit/Unit.cpp +++ b/src/server/game/Entities/Unit/Unit.cpp @@ -105,6 +105,11 @@ float playerBaseMoveSpeed[MAX_MOVE_TYPE] = 3.14f // MOVE_PITCH_RATE }; +bool DispelableAura::RollDispel() const +{ + return roll_chance_i(_chance); +} + DamageInfo::DamageInfo(Unit* attacker, Unit* victim, uint32 damage, SpellInfo const* spellInfo, SpellSchoolMask schoolMask, DamageEffectType damageType, WeaponAttackType attackType) : m_attacker(attacker), m_victim(victim), m_damage(damage), m_spellInfo(spellInfo), m_schoolMask(schoolMask), m_damageType(damageType), m_attackType(attackType), m_absorb(0), m_resist(0), m_block(0), m_hitMask(0) @@ -4261,13 +4266,13 @@ Aura* Unit::GetAuraOfRankedSpell(uint32 spellId, ObjectGuid casterGUID, ObjectGu return aurApp ? aurApp->GetBase() : NULL; } -void Unit::GetDispellableAuraList(Unit* caster, uint32 dispelMask, DispelChargesList& dispelList) +void Unit::GetDispellableAuraList(Unit* caster, uint32 dispelMask, DispelChargesList& dispelList, bool isReflect /*= false*/) const { AuraMap const& auras = GetOwnedAuras(); - for (AuraMap::const_iterator itr = auras.begin(); itr != auras.end(); ++itr) + for (auto itr = auras.begin(); itr != auras.end(); ++itr) { Aura* aura = itr->second; - AuraApplication * aurApp = aura->GetApplicationOfTarget(GetGUID()); + AuraApplication const* aurApp = aura->GetApplicationOfTarget(GetGUID()); if (!aurApp) continue; @@ -4278,17 +4283,23 @@ void Unit::GetDispellableAuraList(Unit* caster, uint32 dispelMask, DispelCharges if (aura->GetSpellInfo()->GetDispelMask() & dispelMask) { // do not remove positive auras if friendly target - // negative auras if non-friendly target - if (aurApp->IsPositive() == IsFriendlyTo(caster)) + // negative auras if non-friendly + // unless we're reflecting (dispeller eliminates one of it's benefitial buffs) + if (isReflect != (aurApp->IsPositive() == IsFriendlyTo(caster))) + continue; + + // 2.4.3 Patch Notes: "Dispel effects will no longer attempt to remove effects that have 100% dispel resistance." + int32 chance = aura->CalcDispelChance(this, !IsFriendlyTo(caster)); + if (!chance) continue; // The charges / stack amounts don't count towards the total number of auras that can be dispelled. // Ie: A dispel on a target with 5 stacks of Winters Chill and a Polymorph has 1 / (1 + 1) -> 50% chance to dispell // Polymorph instead of 1 / (5 + 1) -> 16%. - bool dispelCharges = aura->GetSpellInfo()->HasAttribute(SPELL_ATTR7_DISPEL_CHARGES); + bool const dispelCharges = aura->GetSpellInfo()->HasAttribute(SPELL_ATTR7_DISPEL_CHARGES); uint8 charges = dispelCharges ? aura->GetCharges() : aura->GetStackAmount(); if (charges > 0) - dispelList.push_back(std::make_pair(aura, charges)); + dispelList.emplace_back(aura, chance, charges); } } } diff --git a/src/server/game/Entities/Unit/Unit.h b/src/server/game/Entities/Unit/Unit.h index b5a023620da..6d8f9c81cbd 100644 --- a/src/server/game/Entities/Unit/Unit.h +++ b/src/server/game/Entities/Unit/Unit.h @@ -228,7 +228,33 @@ namespace WorldPackets } typedef std::list<Unit*> UnitList; -typedef std::list<std::pair<Aura*, uint8>> DispelChargesList; + +class DispelableAura +{ + public: + DispelableAura(Aura* aura, int32 dispelChance, uint8 dispelCharges) : + _aura(aura), _chance(dispelChance), _charges(dispelCharges) { } + + Aura* GetAura() const { return _aura; } + bool RollDispel() const; + uint8 GetDispelCharges() const { return _charges; } + + void IncrementCharges() { ++_charges; } + bool DecrementCharge() + { + if (!_charges) + return false; + + --_charges; + return _charges > 0; + } + + private: + Aura* _aura; + int32 _chance; + uint8 _charges; +}; +typedef std::vector<DispelableAura> DispelChargesList; typedef std::unordered_multimap<uint32 /*type*/, uint32 /*spellId*/> SpellImmuneContainer; @@ -1471,7 +1497,7 @@ class TC_GAME_API Unit : public WorldObject AuraApplication * GetAuraApplicationOfRankedSpell(uint32 spellId, ObjectGuid casterGUID = ObjectGuid::Empty, ObjectGuid itemCasterGUID = ObjectGuid::Empty, uint32 reqEffMask = 0, AuraApplication * except = NULL) const; Aura* GetAuraOfRankedSpell(uint32 spellId, ObjectGuid casterGUID = ObjectGuid::Empty, ObjectGuid itemCasterGUID = ObjectGuid::Empty, uint32 reqEffMask = 0) const; - void GetDispellableAuraList(Unit* caster, uint32 dispelMask, DispelChargesList& dispelList); + void GetDispellableAuraList(Unit* caster, uint32 dispelMask, DispelChargesList& dispelList, bool isReflect = false) const; bool HasAuraEffect(uint32 spellId, uint8 effIndex, ObjectGuid caster = ObjectGuid::Empty) const; uint32 GetAuraCount(uint32 spellId) const; diff --git a/src/server/game/Spells/Auras/SpellAuras.cpp b/src/server/game/Spells/Auras/SpellAuras.cpp index e81659a298a..3d7852e8337 100644 --- a/src/server/game/Spells/Auras/SpellAuras.cpp +++ b/src/server/game/Spells/Auras/SpellAuras.cpp @@ -1086,7 +1086,7 @@ void Aura::UnregisterSingleTarget() SetIsSingleTarget(false); } -int32 Aura::CalcDispelChance(Unit* auraTarget, bool offensive) const +int32 Aura::CalcDispelChance(Unit const* auraTarget, bool offensive) const { // we assume that aura dispel chance is 100% on start // need formula for level difference based chance diff --git a/src/server/game/Spells/Auras/SpellAuras.h b/src/server/game/Spells/Auras/SpellAuras.h index f4d4125aaec..eb20d56ff7b 100644 --- a/src/server/game/Spells/Auras/SpellAuras.h +++ b/src/server/game/Spells/Auras/SpellAuras.h @@ -206,7 +206,7 @@ class TC_GAME_API Aura bool IsSingleTargetWith(Aura const* aura) const; void SetIsSingleTarget(bool val) { m_isSingleTarget = val; } void UnregisterSingleTarget(); - int32 CalcDispelChance(Unit* auraTarget, bool offensive) const; + int32 CalcDispelChance(Unit const* auraTarget, bool offensive) const; /** * @fn AuraKey Aura::GenerateKey(uint32& recalculateMask) const diff --git a/src/server/game/Spells/Spell.cpp b/src/server/game/Spells/Spell.cpp index 5ce5123f762..245b2a64ac7 100644 --- a/src/server/game/Spells/Spell.cpp +++ b/src/server/game/Spells/Spell.cpp @@ -581,6 +581,7 @@ m_spellValue(new SpellValue(caster->GetMap()->GetDifficultyID(), m_spellInfo)) gameObjTarget = NULL; destTarget = NULL; damage = 0; + targetMissInfo = SPELL_MISS_NONE; variance = 0.0f; effectHandleMode = SPELL_EFFECT_HANDLE_LAUNCH; effectInfo = nullptr; @@ -611,7 +612,7 @@ m_spellValue(new SpellValue(caster->GetMap()->GetDifficultyID(), m_spellInfo)) // Patch 1.2 notes: Spell Reflection no longer reflects abilities m_canReflect = m_spellInfo->DmgClass == SPELL_DAMAGE_CLASS_MAGIC && !m_spellInfo->HasAttribute(SPELL_ATTR0_ABILITY) && !m_spellInfo->HasAttribute(SPELL_ATTR1_CANT_BE_REFLECTED) && !m_spellInfo->HasAttribute(SPELL_ATTR0_UNAFFECTED_BY_INVULNERABILITY) - && !m_spellInfo->IsPassive() && !m_spellInfo->IsPositive(); + && !m_spellInfo->IsPassive(); CleanupTargetList(); @@ -2160,7 +2161,7 @@ void Spell::AddUnitTarget(Unit* target, uint32 effectMask, bool checkIfValid /*= // Calculate hit result if (m_originalCaster) { - targetInfo.missCondition = m_originalCaster->SpellHitResult(target, m_spellInfo, m_canReflect); + targetInfo.missCondition = m_originalCaster->SpellHitResult(target, m_spellInfo, m_canReflect && !(m_spellInfo->IsPositive() && m_caster->IsFriendlyTo(target))); if (m_skipCheck && targetInfo.missCondition != SPELL_MISS_IMMUNE) targetInfo.missCondition = SPELL_MISS_NONE; } @@ -2358,6 +2359,7 @@ void Spell::DoAllEffectOnTarget(TargetInfo* target) // Need init unitTarget by default unit (can changed in code on reflect) // Or on missInfo != SPELL_MISS_NONE unitTarget undefined (but need in trigger subsystem) unitTarget = unit; + targetMissInfo = missInfo; // Reset damage/healing counter m_damage = target->damage; diff --git a/src/server/game/Spells/Spell.h b/src/server/game/Spells/Spell.h index 453e1b58543..88ca04bd7a8 100644 --- a/src/server/game/Spells/Spell.h +++ b/src/server/game/Spells/Spell.h @@ -320,7 +320,7 @@ enum SpellEffectHandleMode SPELL_EFFECT_HANDLE_HIT_TARGET }; -typedef std::list<std::pair<uint32, ObjectGuid>> DispelList; +typedef std::vector<std::pair<uint32, ObjectGuid>> DispelList; static const uint32 SPELL_INTERRUPT_NONPLAYER = 32747; @@ -748,6 +748,7 @@ class TC_GAME_API Spell GameObject* gameObjTarget; WorldLocation* destTarget; int32 damage; + SpellMissInfo targetMissInfo; float variance; SpellEffectHandleMode effectHandleMode; SpellEffectInfo const* effectInfo; @@ -775,18 +776,18 @@ class TC_GAME_API Spell // Targets store structures and data struct TargetInfo { - // a bug in gcc-4.7 needs a destructor to call move operator instead of copy operator in std::vector remove - ~TargetInfo() { } ObjectGuid targetGUID; uint64 timeDelay; - SpellMissInfo missCondition:8; - SpellMissInfo reflectResult:8; - uint32 effectMask; - bool processed:1; - bool alive:1; - bool crit:1; - bool scaleAura:1; int32 damage; + + SpellMissInfo missCondition; + SpellMissInfo reflectResult; + + uint32 effectMask; + bool processed; + bool alive; + bool crit; + bool scaleAura; }; std::vector<TargetInfo> m_UniqueTargetInfo; uint32 m_channelTargetEffectMask; // Mask req. alive targets @@ -795,7 +796,7 @@ class TC_GAME_API Spell { ObjectGuid targetGUID; uint64 timeDelay; - uint32 effectMask; + uint32 effectMask; bool processed; }; std::vector<GOTargetInfo> m_UniqueGOTargetInfo; diff --git a/src/server/game/Spells/SpellEffects.cpp b/src/server/game/Spells/SpellEffects.cpp index 0c2e55e7025..1512e39f946 100644 --- a/src/server/game/Spells/SpellEffects.cpp +++ b/src/server/game/Spells/SpellEffects.cpp @@ -2166,62 +2166,63 @@ void Spell::EffectDispel(SpellEffIndex effIndex) uint32 dispel_type = effectInfo->MiscValue; uint32 dispelMask = SpellInfo::GetDispelMask(DispelType(dispel_type)); - DispelChargesList dispel_list; - unitTarget->GetDispellableAuraList(m_caster, dispelMask, dispel_list); - if (dispel_list.empty()) + DispelChargesList dispelList; + unitTarget->GetDispellableAuraList(m_caster, dispelMask, dispelList, targetMissInfo == SPELL_MISS_REFLECT); + if (dispelList.empty()) return; + size_t remaining = dispelList.size(); + // Ok if exist some buffs for dispel try dispel it - DispelChargesList success_list; + uint32 failCount = 0; + DispelChargesList successList; + successList.reserve(damage); + WorldPackets::Spells::DispelFailed dispelFailed; dispelFailed.CasterGUID = m_caster->GetGUID(); dispelFailed.VictimGUID = unitTarget->GetGUID(); dispelFailed.SpellID = m_spellInfo->Id; // dispel N = damage buffs (or while exist buffs for dispel) - for (int32 count = 0; count < damage && !dispel_list.empty();) + for (int32 count = 0; count < damage && remaining > 0;) { // Random select buff for dispel - DispelChargesList::iterator itr = dispel_list.begin(); - std::advance(itr, urand(0, dispel_list.size() - 1)); + auto itr = dispelList.begin(); + std::advance(itr, urand(0, remaining - 1)); - int32 chance = itr->first->CalcDispelChance(unitTarget, !unitTarget->IsFriendlyTo(m_caster)); - // 2.4.3 Patch Notes: "Dispel effects will no longer attempt to remove effects that have 100% dispel resistance." - if (!chance) - { - dispel_list.erase(itr); - continue; - } - else + if (itr->RollDispel()) { - if (roll_chance_i(chance)) + auto successItr = std::find_if(successList.begin(), successList.end(), [&itr](DispelableAura& dispelAura) -> bool { - bool alreadyListed = false; - for (DispelChargesList::iterator successItr = success_list.begin(); successItr != success_list.end(); ++successItr) - { - if (successItr->first->GetId() == itr->first->GetId()) - { - ++successItr->second; - alreadyListed = true; - } - } - if (!alreadyListed) - success_list.push_back(std::make_pair(itr->first, 1)); - --itr->second; - if (itr->second <= 0) - dispel_list.erase(itr); - } + if (dispelAura.GetAura()->GetId() == itr->GetAura()->GetId()) + return true; + + return false; + }); + + if (successItr == successList.end()) + successList.emplace_back(itr->GetAura(), 0, 1); else - dispelFailed.FailedSpells.push_back(int32(itr->first->GetId())); + successItr->IncrementCharges(); - ++count; + if (!itr->DecrementCharge()) + { + --remaining; + std::swap(*itr, dispelList[remaining]); + } } + else + { + ++failCount; + dispelFailed.FailedSpells.push_back(int32(itr->GetAura()->GetId())); + } + ++count; } if (!dispelFailed.FailedSpells.empty()) m_caster->SendMessageToSet(dispelFailed.Write(), true); - if (success_list.empty()) + if (successList.empty()) return; WorldPackets::CombatLog::SpellDispellLog spellDispellLog; @@ -2232,15 +2233,15 @@ void Spell::EffectDispel(SpellEffIndex effIndex) spellDispellLog.CasterGUID = m_caster->GetGUID(); spellDispellLog.DispelledBySpellID = m_spellInfo->Id; - for (std::pair<Aura*, uint8> const& dispellCharge : success_list) + for (DispelableAura const& dispelableAura : successList) { WorldPackets::CombatLog::SpellDispellData dispellData; - dispellData.SpellID = dispellCharge.first->GetId(); + dispellData.SpellID = dispelableAura.GetAura()->GetId(); dispellData.Harmful = false; // TODO: use me dispellData.Rolled = boost::none; // TODO: use me dispellData.Needed = boost::none; // TODO: use me - unitTarget->RemoveAurasDueToSpellByDispel(dispellCharge.first->GetId(), m_spellInfo->Id, dispellCharge.first->GetCasterGUID(), m_caster, dispellCharge.second); + unitTarget->RemoveAurasDueToSpellByDispel(dispelableAura.GetAura()->GetId(), m_spellInfo->Id, dispelableAura.GetAura()->GetCasterGUID(), m_caster, dispelableAura.GetDispelCharges()); spellDispellLog.DispellData.emplace_back(dispellData); } @@ -4426,14 +4427,11 @@ void Spell::EffectDispelMechanic(SpellEffIndex /*effIndex*/) continue; if (roll_chance_i(aura->CalcDispelChance(unitTarget, !unitTarget->IsFriendlyTo(m_caster)))) if ((aura->GetSpellInfo()->GetAllEffectsMechanicMask() & (1 << mechanic))) - dispel_list.push_back(std::make_pair(aura->GetId(), aura->GetCasterGUID())); + dispel_list.emplace_back(aura->GetId(), aura->GetCasterGUID()); } - while (!dispel_list.empty()) - { - unitTarget->RemoveAura(dispel_list.front().first, dispel_list.front().second, 0, AURA_REMOVE_BY_ENEMY_SPELL); - dispel_list.pop_front(); - } + for (auto itr = dispel_list.begin(); itr != dispel_list.end(); ++itr) + unitTarget->RemoveAura(itr->first, itr->second, 0, AURA_REMOVE_BY_ENEMY_SPELL); } void Spell::EffectResurrectPet(SpellEffIndex /*effIndex*/) @@ -4837,7 +4835,7 @@ void Spell::EffectStealBeneficialBuff(SpellEffIndex /*effIndex*/) if (!unitTarget || unitTarget == m_caster) // can't steal from self return; - DispelChargesList steal_list; + DispelChargesList stealList; // Create dispel mask by dispel type uint32 dispelMask = SpellInfo::GetDispelMask(DispelType(effectInfo->MiscValue)); @@ -4845,7 +4843,7 @@ void Spell::EffectStealBeneficialBuff(SpellEffIndex /*effIndex*/) for (Unit::AuraMap::const_iterator itr = auras.begin(); itr != auras.end(); ++itr) { Aura* aura = itr->second; - AuraApplication * aurApp = aura->GetApplicationOfTarget(unitTarget->GetGUID()); + AuraApplication const* aurApp = aura->GetApplicationOfTarget(unitTarget->GetGUID()); if (!aurApp) continue; @@ -4855,60 +4853,64 @@ void Spell::EffectStealBeneficialBuff(SpellEffIndex /*effIndex*/) if (!aurApp->IsPositive() || aura->IsPassive() || aura->GetSpellInfo()->HasAttribute(SPELL_ATTR4_NOT_STEALABLE)) continue; + // 2.4.3 Patch Notes: "Dispel effects will no longer attempt to remove effects that have 100% dispel resistance." + int32 chance = aura->CalcDispelChance(unitTarget, !unitTarget->IsFriendlyTo(m_caster)); + if (!chance) + continue; + // The charges / stack amounts don't count towards the total number of auras that can be dispelled. // Ie: A dispel on a target with 5 stacks of Winters Chill and a Polymorph has 1 / (1 + 1) -> 50% chance to dispell // Polymorph instead of 1 / (5 + 1) -> 16%. bool dispelCharges = aura->GetSpellInfo()->HasAttribute(SPELL_ATTR7_DISPEL_CHARGES); uint8 charges = dispelCharges ? aura->GetCharges() : aura->GetStackAmount(); if (charges > 0) - steal_list.push_back(std::make_pair(aura, charges)); + stealList.emplace_back(aura, chance, charges); } } - if (steal_list.empty()) + if (stealList.empty()) return; + size_t remaining = stealList.size(); + // Ok if exist some buffs for dispel try dispel it - DispelList success_list; + uint32 failCount = 0; + DispelList successList; + successList.reserve(damage); + WorldPackets::Spells::DispelFailed dispelFailed; dispelFailed.CasterGUID = m_caster->GetGUID(); dispelFailed.VictimGUID = unitTarget->GetGUID(); dispelFailed.SpellID = m_spellInfo->Id; // dispel N = damage buffs (or while exist buffs for dispel) - for (int32 count = 0; count < damage && !steal_list.empty();) + for (int32 count = 0; count < damage && remaining > 0;) { // Random select buff for dispel - DispelChargesList::iterator itr = steal_list.begin(); - std::advance(itr, urand(0, steal_list.size() - 1)); + DispelChargesList::iterator itr = stealList.begin(); + std::advance(itr, urand(0, remaining - 1)); - int32 chance = itr->first->CalcDispelChance(unitTarget, !unitTarget->IsFriendlyTo(m_caster)); - // 2.4.3 Patch Notes: "Dispel effects will no longer attempt to remove effects that have 100% dispel resistance." - if (!chance) + if (itr->RollDispel()) { - steal_list.erase(itr); - continue; + successList.emplace_back(itr->GetAura()->GetId(), itr->GetAura()->GetCasterGUID()); + if (!itr->DecrementCharge()) + { + --remaining; + std::swap(*itr, stealList[remaining]); + } } else { - if (roll_chance_i(chance)) - { - success_list.push_back(std::make_pair(itr->first->GetId(), itr->first->GetCasterGUID())); - --itr->second; - if (itr->second <= 0) - steal_list.erase(itr); - } - else - dispelFailed.FailedSpells.push_back(int32(itr->first->GetId())); - - ++count; + ++failCount; + dispelFailed.FailedSpells.push_back(int32(itr->GetAura()->GetId())); } + ++count; } if (!dispelFailed.FailedSpells.empty()) m_caster->SendMessageToSet(dispelFailed.Write(), true); - if (success_list.empty()) + if (successList.empty()) return; WorldPackets::CombatLog::SpellDispellLog spellDispellLog; @@ -4919,7 +4921,7 @@ void Spell::EffectStealBeneficialBuff(SpellEffIndex /*effIndex*/) spellDispellLog.CasterGUID = m_caster->GetGUID(); spellDispellLog.DispelledBySpellID = m_spellInfo->Id; - for (std::pair<uint32, ObjectGuid> const& dispell : success_list) + for (std::pair<uint32, ObjectGuid> const& dispell : successList) { WorldPackets::CombatLog::SpellDispellData dispellData; dispellData.SpellID = dispell.first; |