aboutsummaryrefslogtreecommitdiff
path: root/src/server/game/Spells/Spell.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/server/game/Spells/Spell.cpp')
-rwxr-xr-xsrc/server/game/Spells/Spell.cpp154
1 files changed, 93 insertions, 61 deletions
diff --git a/src/server/game/Spells/Spell.cpp b/src/server/game/Spells/Spell.cpp
index bf444f27e86..f52ce6461b3 100755
--- a/src/server/game/Spells/Spell.cpp
+++ b/src/server/game/Spells/Spell.cpp
@@ -1166,7 +1166,7 @@ void Spell::DoAllEffectOnTarget(TargetInfo *target)
//Spells with this flag cannot trigger if effect is casted on self
// Slice and Dice, relentless strikes, eviscerate
- bool canEffectTrigger = unitTarget->CanProc() && (m_spellInfo->AttributesEx4 & (SPELL_ATTR4_CANT_PROC_FROM_SELFCAST) ? m_caster != unitTarget : true);
+ bool canEffectTrigger = unitTarget->CanProc() && CanExecuteTriggersOnHit(mask);
Unit* spellHitTarget = NULL;
if (missInfo == SPELL_MISS_NONE) // In case spell hit target, do all effect on that target
@@ -1355,7 +1355,7 @@ void Spell::DoAllEffectOnTarget(TargetInfo *target)
m_caster->ToCreature()->AI()->SpellHitTarget(spellHitTarget, m_spellInfo);
// Needs to be called after dealing damage/healing to not remove breaking on damage auras
- DoTriggersOnSpellHit(spellHitTarget);
+ DoTriggersOnSpellHit(spellHitTarget, mask);
// if target is fallged for pvp also flag caster if a player
if (unit->IsPvP())
@@ -1525,9 +1525,10 @@ SpellMissInfo Spell::DoSpellHitOnUnit(Unit *unit, const uint32 effectMask, bool
return SPELL_MISS_NONE;
}
-void Spell::DoTriggersOnSpellHit(Unit *unit)
+void Spell::DoTriggersOnSpellHit(Unit *unit, uint8 effMask)
{
// Apply additional spell effects to target
+ // TODO: move this code to scripts
if (m_preCastSpell)
{
// Paladin immunity shields
@@ -1549,38 +1550,44 @@ void Spell::DoTriggersOnSpellHit(Unit *unit)
m_caster->AddAura(m_preCastSpell, unit);
}
- // spells with this flag can trigger only if not selfcast (eviscerate for example)
- if (m_ChanceTriggerSpells.size() && (!((m_spellInfo->AttributesEx4 & SPELL_ATTR4_CANT_PROC_FROM_SELFCAST) && unit == m_caster)))
+ // handle SPELL_AURA_ADD_TARGET_TRIGGER auras
+ // this is executed after spell proc spells on target hit
+ // spells are triggered for each hit spell target
+ // info confirmed with retail sniffs of permafrost and shadow weaving
+ if (!m_hitTriggerSpells.empty() && CanExecuteTriggersOnHit(effMask))
{
- int _duration=0;
- for (ChanceTriggerSpells::const_iterator i = m_ChanceTriggerSpells.begin(); i != m_ChanceTriggerSpells.end(); ++i)
+ int _duration = 0;
+ for (HitTriggerSpells::const_iterator i = m_hitTriggerSpells.begin(); i != m_hitTriggerSpells.end(); ++i)
{
- // SPELL_AURA_ADD_TARGET_TRIGGER auras shouldn't trigger auras without duration
- // set duration equal to triggering spell
if (roll_chance_i(i->second))
{
m_caster->CastSpell(unit, i->first, true);
sLog->outDebug(LOG_FILTER_SPELLS_AURAS, "Spell %d triggered spell %d by SPELL_AURA_ADD_TARGET_TRIGGER aura", m_spellInfo->Id, i->first->Id);
- }
- if (GetSpellDuration(i->first) == -1)
- {
- if (Aura * triggeredAur = unit->GetAura(i->first->Id, m_caster->GetGUID()))
+
+ // SPELL_AURA_ADD_TARGET_TRIGGER auras shouldn't trigger auras without duration
+ // set duration of current aura to the triggered spell
+ if (GetSpellDuration(i->first) == -1)
{
- // get duration from aura-only once
- if (!_duration)
+ if (Aura * triggeredAur = unit->GetAura(i->first->Id, m_caster->GetGUID()))
{
- Aura * aur = unit->GetAura(m_spellInfo->Id, m_caster->GetGUID());
- _duration = aur ? aur->GetDuration() : -1;
+ // get duration from aura-only once
+ if (!_duration)
+ {
+ Aura * aur = unit->GetAura(m_spellInfo->Id, m_caster->GetGUID());
+ _duration = aur ? aur->GetDuration() : -1;
+ }
+ triggeredAur->SetDuration(_duration);
}
- triggeredAur->SetDuration(_duration);
}
}
}
}
+ // trigger linked auras remove/apply
+ // TODO: remove/cleanup this, as this table is not documented and people are doing stupid things with it
if (m_customAttr & SPELL_ATTR0_CU_LINK_HIT)
- if (const std::vector<int32> *spell_triggered = sSpellMgr->GetSpellLinked(m_spellInfo->Id + SPELL_LINK_HIT))
- for (std::vector<int32>::const_iterator i = spell_triggered->begin(); i != spell_triggered->end(); ++i)
+ if (std::vector<int32> const* spellTriggered = sSpellMgr->GetSpellLinked(m_spellInfo->Id + SPELL_LINK_HIT))
+ for (std::vector<int32>::const_iterator i = spellTriggered->begin(); i != spellTriggered->end(); ++i)
if (*i < 0)
unit->RemoveAurasDueToSpell(-(*i));
else
@@ -3135,30 +3142,7 @@ void Spell::cast(bool skipCheck)
return;
}
- if (m_spellInfo->SpellFamilyName)
- {
- if (m_spellInfo->excludeCasterAuraSpell && !IsPositiveSpell(m_spellInfo->excludeCasterAuraSpell))
- m_preCastSpell = m_spellInfo->excludeCasterAuraSpell;
- else if (m_spellInfo->excludeTargetAuraSpell && !IsPositiveSpell(m_spellInfo->excludeTargetAuraSpell))
- m_preCastSpell = m_spellInfo->excludeTargetAuraSpell;
- }
-
- switch (m_spellInfo->SpellFamilyName)
- {
- case SPELLFAMILY_GENERIC:
- {
- if (m_spellInfo->Mechanic == MECHANIC_BANDAGE) // Bandages
- m_preCastSpell = 11196; // Recently Bandaged
- break;
- }
- case SPELLFAMILY_MAGE:
- {
- // Permafrost
- if (m_spellInfo->SpellFamilyFlags[1] & 0x00001000 || m_spellInfo->SpellFamilyFlags[0] & 0x00100220)
- m_preCastSpell = 68391;
- break;
- }
- }
+ PrepareTriggersExecutedOnHit();
// traded items have trade slot instead of guid in m_itemTargetGUID
// set to real guid to be sent later to the client
@@ -3188,23 +3172,6 @@ void Spell::cast(bool skipCheck)
TakeReagents();
}
- // are there any spells need to be triggered after hit?
- // handle SPELL_AURA_ADD_TARGET_TRIGGER auras
- Unit::AuraEffectList const& targetTriggers = m_caster->GetAuraEffectsByType(SPELL_AURA_ADD_TARGET_TRIGGER);
- for (Unit::AuraEffectList::const_iterator i = targetTriggers.begin(); i != targetTriggers.end(); ++i)
- {
- if (!(*i)->IsAffectedOnSpell(m_spellInfo))
- continue;
- SpellEntry const *auraSpellInfo = (*i)->GetSpellProto();
- uint32 auraSpellIdx = (*i)->GetEffIndex();
- if (SpellEntry const *spellInfo = sSpellStore.LookupEntry(auraSpellInfo->EffectTriggerSpell[auraSpellIdx]))
- {
- int32 auraBaseAmount = (*i)->GetBaseAmount();
- int32 chance = m_caster->CalculateSpellDamage(NULL, auraSpellInfo, auraSpellIdx, &auraBaseAmount);
- m_ChanceTriggerSpells.push_back(std::make_pair(spellInfo, chance * (*i)->GetBase()->GetStackAmount()));
- }
- }
-
if (m_customAttr & SPELL_ATTR0_CU_DIRECT_DAMAGE)
CalculateDamageDoneForAllTargets();
@@ -3482,6 +3449,7 @@ void Spell::_handle_finish_phase()
if (m_comboPointGain)
m_caster->m_movedPlayer->GainSpellComboPoints(m_comboPointGain);
}
+ // TODO: trigger proc phase finish here
}
void Spell::SendSpellCooldown()
@@ -7302,6 +7270,70 @@ void Spell::CallScriptAfterUnitTargetSelectHandlers(std::list<Unit*>& unitTarget
}
}
+bool Spell::CanExecuteTriggersOnHit(uint8 effMask) const
+{
+ // check which effects can trigger proc
+ // don't allow to proc for dummy-only spell target hits
+ // prevents triggering/procing effects twice from spells like Eviscerate
+ for (uint8 i = 0;effMask && i < MAX_SPELL_EFFECTS; ++i)
+ {
+ if (m_spellInfo->Effect[i] == SPELL_EFFECT_DUMMY)
+ effMask &= ~(1<<i);
+ }
+ return effMask;
+}
+
+void Spell::PrepareTriggersExecutedOnHit()
+{
+ // todo: move this to scripts
+ if (m_spellInfo->SpellFamilyName)
+ {
+ if (m_spellInfo->excludeCasterAuraSpell && !IsPositiveSpell(m_spellInfo->excludeCasterAuraSpell))
+ m_preCastSpell = m_spellInfo->excludeCasterAuraSpell;
+ else if (m_spellInfo->excludeTargetAuraSpell && !IsPositiveSpell(m_spellInfo->excludeTargetAuraSpell))
+ m_preCastSpell = m_spellInfo->excludeTargetAuraSpell;
+ }
+
+ // todo: move this to scripts
+ switch (m_spellInfo->SpellFamilyName)
+ {
+ case SPELLFAMILY_GENERIC:
+ {
+ if (m_spellInfo->Mechanic == MECHANIC_BANDAGE) // Bandages
+ m_preCastSpell = 11196; // Recently Bandaged
+ break;
+ }
+ case SPELLFAMILY_MAGE:
+ {
+ // Permafrost
+ if (m_spellInfo->SpellFamilyFlags[1] & 0x00001000 || m_spellInfo->SpellFamilyFlags[0] & 0x00100220)
+ m_preCastSpell = 68391;
+ break;
+ }
+ }
+
+ // handle SPELL_AURA_ADD_TARGET_TRIGGER auras:
+ // save auras which were present on spell caster on cast, to prevent triggered auras from affecting caster
+ // and to correctly calculate proc chance when combopoints are present
+ Unit::AuraEffectList const& targetTriggers = m_caster->GetAuraEffectsByType(SPELL_AURA_ADD_TARGET_TRIGGER);
+ for (Unit::AuraEffectList::const_iterator i = targetTriggers.begin(); i != targetTriggers.end(); ++i)
+ {
+ if (!(*i)->IsAffectedOnSpell(m_spellInfo))
+ continue;
+ SpellEntry const *auraSpellInfo = (*i)->GetSpellProto();
+ uint32 auraSpellIdx = (*i)->GetEffIndex();
+ if (SpellEntry const *spellInfo = sSpellStore.LookupEntry(auraSpellInfo->EffectTriggerSpell[auraSpellIdx]))
+ {
+ // calculate the chance using spell base amount, because aura amount is not updated on combo-points change
+ // this possibly needs fixing
+ int32 auraBaseAmount = (*i)->GetBaseAmount();
+ int32 chance = m_caster->CalculateSpellDamage(NULL, auraSpellInfo, auraSpellIdx, &auraBaseAmount);
+ // proc chance is stored in effect amount
+ m_hitTriggerSpells.push_back(std::make_pair(spellInfo, chance * (*i)->GetBase()->GetStackAmount()));
+ }
+ }
+}
+
// Global cooldowns management
enum GCDLimits
{