Jump to content

Spell Stacking System


Recommended Posts

Posted

So... as many ppl know mangos lack of general stacking system for same-effect spells which makes some spell to apply twice, which means stacked Blessing of Might and Battleshout etc...

Here is something ive already got after some moment spent on trying to get it working

diff --git a/src/game/Spell.cpp b/src/game/Spell.cpp
index 9f141df..d86f888 100644
--- a/src/game/Spell.cpp
+++ b/src/game/Spell.cpp
@@ -4882,6 +4882,34 @@ SpellCastResult Spell::CheckCast(bool strict)
        }
    }

+    Unit::AuraMap::iterator Aurmap,next;
+    for (Aurmap = m_caster->GetAuras().begin(); Aurmap != m_caster->GetAuras().end(); Aurmap = next)
+    {
+        next = Aurmap;
+        ++next;
+        if (!(*Aurmap).second) continue;
+
+        if (!(*Aurmap).second->GetSpellProto())
+            continue;
+
+        if((*Aurmap).second->IsPassive())
+            continue;
+
+        if( IsPassiveSpell((*Aurmap).second->GetId()) )
+            continue;
+
+        SpellEntry const* i_spellProto = (*Aurmap).second->GetSpellProto();
+        if( i_spellProto->Id == (*Aurmap).second->GetId() && m_caster == (*Aurmap).second->GetCaster() )
+            continue;
+
+        Unit * Target = m_targets.getUnitTarget();
+        int32 EffectValue = CalculateDamage(0,Target);
+
+        if( m_spellInfo->EffectApplyAuraName[0] == i_spellProto->EffectApplyAuraName[0] )
+            if( (*Aurmap).second->GetModifier()->m_amount > EffectValue || ( (*Aurmap).second->GetModifier()->m_amount == EffectValue && (*Aurmap).second->GetAuraDuration() > Target->CalculateSpellDuration(m_spellInfo, 0, Target) ) )
+                return SPELL_FAILED_AURA_BOUNCED;
+    }
+
    // all ok
    return SPELL_CAST_OK;
}
@@ -6139,7 +6167,36 @@ void Spell::FillRaidOrPartyTargets( UnitList &TagUnitMap, Unit* member, Unit* ce
            {
                if ((Target==center || center->IsWithinDistInMap(Target, radius)) &&
                    (withcaster || Target != m_caster))
+                    {
+                    bool hasMorePowerful = false;
+                    Unit::AuraMap::iterator Aurmap,next;
+                    for (Aurmap = Target->GetAuras().begin(); Aurmap != Target->GetAuras().end(); Aurmap = next)
+                    {
+                        next = Aurmap;
+                        ++next;
+                        if (!(*Aurmap).second) continue;
+
+                        if (!(*Aurmap).second->GetSpellProto())
+                            continue;
+
+                        if((*Aurmap).second->IsPassive())
+                            continue;
+
+                        if( IsPassiveSpell((*Aurmap).second->GetId()) )
+                            continue;
+
+                        SpellEntry const* i_spellProto = (*Aurmap).second->GetSpellProto();
+
+                        if( m_spellInfo->EffectApplyAuraName[0] == i_spellProto->EffectApplyAuraName[0] )
+                            if( (*Aurmap).second->GetModifier()->m_amount > CalculateDamage(0,Target) || ( (*Aurmap).second->GetModifier()->m_amount == CalculateDamage(0,Target) && (*Aurmap).second->GetAuraDuration() > Target->CalculateSpellDuration(m_spellInfo, 0, Target)) )
+                                hasMorePowerful = true;
+                    }
+
+                    if( hasMorePowerful )
+                        continue;
+
                    TagUnitMap.push_back(Target);
+                }

                if (withPets)
                    if (Pet* pet = Target->GetPet())
diff --git a/src/game/SpellMgr.cpp b/src/game/SpellMgr.cpp
index 5bda5ee..30a3049 100644
--- a/src/game/SpellMgr.cpp
+++ b/src/game/SpellMgr.cpp
@@ -24,6 +24,7 @@
#include "World.h"
#include "Chat.h"
#include "Spell.h"
+#include "SpellAuras.h"
#include "BattleGroundMgr.h"

SpellMgr::SpellMgr()
@@ -1301,6 +1302,18 @@ bool SpellMgr::canStackSpellRanks(SpellEntry const *spellInfo)
    return true;
}

+bool SpellMgr::IsEffectHigherThanSecond(Aura *spellAura_1, Aura *spellAura_2 )
+{
+    SpellEntry const* aurSpellInfo_1 = spellAura_1->GetSpellProto();
+    SpellEntry const* aurSpellInfo_2 = spellAura_2->GetSpellProto();
+
+    if( aurSpellInfo_1->EffectApplyAuraName[0] == aurSpellInfo_2->EffectApplyAuraName[0] )
+        if( (spellAura_1->GetModifier()->m_amount >= spellAura_2->GetModifier()->m_amount) )
+            return true;
+
+    return false;
+}
+
bool SpellMgr::IsNoStackSpellDueToSpell(uint32 spellId_1, uint32 spellId_2) const
{
    SpellEntry const *spellInfo_1 = sSpellStore.LookupEntry(spellId_1);
diff --git a/src/game/SpellMgr.h b/src/game/SpellMgr.h
index 8d8e3d8..7c2f960 100644
--- a/src/game/SpellMgr.h
+++ b/src/game/SpellMgr.h
@@ -34,6 +34,7 @@

class Player;
class Spell;
+class Aura;
struct SpellModifier;

// only used in code
@@ -878,6 +879,7 @@ class SpellMgr
        bool IsRankSpellDueToSpell(SpellEntry const *spellInfo_1,uint32 spellId_2) const;
        static bool canStackSpellRanks(SpellEntry const *spellInfo);
        bool IsNoStackSpellDueToSpell(uint32 spellId_1, uint32 spellId_2) const;
+        bool IsEffectHigherThanSecond(Aura * spellAura_1, Aura * spellAura_2);

        SpellEntry const* SelectAuraRankForPlayerLevel(SpellEntry const* spellInfo, uint32 playerLevel) const;

diff --git a/src/game/Unit.cpp b/src/game/Unit.cpp
index 42d434d..db26549 100644
--- a/src/game/Unit.cpp
+++ b/src/game/Unit.cpp
@@ -3868,6 +3868,28 @@ bool Unit::RemoveNoStackAurasDueToAura(Aura *Aur)
            continue;
        }

+        if(sSpellMgr.IsEffectHigherThanSecond(Aur, ((*i).second)) )
+        {
+            if(Aur->IsPassive() || ((*i).second)->IsPassive() )
+                continue;
+
+            // Its a parent aura (create this aura in ApplyModifier)
+            if ((*i).second->IsInUse())
+            {
+                sLog.outError("Aura (Spell %u Effect %u) is in process but attempt removed at aura (Spell %u Effect %u) adding, need add stack rule for Unit::RemoveNoStackAurasDueToAura", i->second->GetId(), i->second->GetEffIndex(),Aur->GetId(), Aur->GetEffIndex());
+                continue;
+            }
+
+            RemoveAurasDueToSpell(i_spellId);
+
+            if( m_Auras.empty() )
+                break;
+            else
+                next =  m_Auras.begin();
+
+            continue;
+        }
+
        // non single (per caster) per target spell specific (possible single spell per target at caster)
        if( !is_spellSpecPerTargetPerCaster && !is_spellSpecPerTarget && sSpellMgr.IsNoStackSpellDueToSpell(spellId, i_spellId) )
        {

Its just some initial code and may kinda break many things so better don't test it on Your servers

What is already added

- Same effects will not stack, it will remove weaker effect one, also if target has already more powerful buff with same effect it will return proper error.

What must be done to get it more-less "perfect":

- When effect has same value (like untalented battle shout and blessing of might) it should apply the one that has longer duration, as now untalented battle shout (shorter duration) will remove untalented blessing of might and should not

- Some pet casted spells are still applied even tho target selecting code is there, need to find where its handled yet. (in this case i mean Commanding Shout and Blood Pact of Imps)

- Some two-effect spells should be able to be casted even tho more powerful effect is already on target, however only second effect should apply then (as in meaning, Aura should apply but only with effect that differs from effect target already has)

- Some triggered auras (mostly from talents) still may have problems after adding this system.

- There might be a case where specified effect that shouldnt stack is stored in second or third effect of aura which is not already implemented (only first effects are check yet)

Some other things about patch;

+class Aura; is added only becouse without it Debug_NoPCH project would not compile, dunno why

Patch was tested about stability only with few players so cant really tell much about how it will work on high-population servers.

If anyone want to continue work on this, good luck (i will implement duration checks from TODO #1 soon)

  • 3 weeks later...
Posted

Why not loop in Spell::CheckCast aura effects like:

Unit* target = m_targets.GetUnitTarget();
for (int i = 0; target && i < 3; ++i)
{
   if (!m_spellInfo->EffectApplyAuraName[i])
       continue;

   // else
   const AuraList& aurasByName = target->GetAurasByType(m_spellInfo->EffectApplyAuraName[i]);
   AuraList::const_iterator it = aurasByName.begin();
   for ( ; it != aurasByName.end(); ++it)
   {
       // Do your checks here
       // on first hit return SPELL_FAILED_AURA_BOUNCED
   }
}

And may be better to do this as method for avoid code duplication on more places.

×
×
  • Create New...

Important Information

We have placed cookies on your device to help make this website better. You can adjust your cookie settings, otherwise we'll assume you're okay to continue. Privacy Policy Terms of Use