aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/game/Player.cpp3
-rw-r--r--src/game/Spell.cpp357
-rw-r--r--src/game/Spell.h10
-rw-r--r--src/game/SpellAuras.cpp50
-rw-r--r--src/game/SpellEffects.cpp121
-rw-r--r--src/game/SpellMgr.cpp114
-rw-r--r--src/game/SpellMgr.h110
-rw-r--r--src/game/Unit.cpp2026
-rw-r--r--src/game/Unit.h80
9 files changed, 2452 insertions, 419 deletions
diff --git a/src/game/Player.cpp b/src/game/Player.cpp
index aff35ea3da1..c37c20a5ece 100644
--- a/src/game/Player.cpp
+++ b/src/game/Player.cpp
@@ -4849,6 +4849,7 @@ void Player::UpdateWeaponSkill (WeaponAttackType attType)
void Player::UpdateCombatSkills(Unit *pVictim, WeaponAttackType attType, MeleeHitOutcome outcome, bool defence)
{
+/* Not need, this checked on call this func from trigger system
switch(outcome)
{
case MELEE_HIT_CRIT:
@@ -4861,7 +4862,7 @@ void Player::UpdateCombatSkills(Unit *pVictim, WeaponAttackType attType, MeleeHi
default:
break;
}
-
+*/
uint32 plevel = getLevel(); // if defense than pVictim == attacker
uint32 greylevel = Trinity::XP::GetGrayLevel(plevel);
uint32 moblevel = pVictim->getLevelForTarget(this);
diff --git a/src/game/Spell.cpp b/src/game/Spell.cpp
index 5e36ad5dc26..bcec22c8dca 100644
--- a/src/game/Spell.cpp
+++ b/src/game/Spell.cpp
@@ -326,6 +326,8 @@ Spell::Spell( Unit* Caster, SpellEntry const *info, bool triggered, uint64 origi
m_spellSchoolMask = SpellSchoolMask(1 << pItem->GetProto()->Damage->DamageType);
}
}
+ // Set health leech amount to zero
+ m_healthLeech = 0;
if(originalCasterGUID)
m_originalCasterGUID = originalCasterGUID;
@@ -676,6 +678,77 @@ void Spell::FillTargetMap()
}
}
+void Spell::prepareDataForTriggerSystem()
+{
+ //==========================================================================================
+ // Now fill data for trigger system, need know:
+ // Ņan spell trigger another or not ( m_canTrigger )
+ // Create base triggers flags for Attacker and Victim ( m_procAttacker and m_procVictim)
+ //==========================================================================================
+
+ // Fill flag can spell trigger or not
+ if (!m_IsTriggeredSpell)
+ m_canTrigger = true; // Normal cast - can trigger
+ else if (!m_triggeredByAuraSpell)
+ m_canTrigger = true; // Triggered from SPELL_EFFECT_TRIGGER_SPELL - can trigger
+ else // Exceptions (some periodic triggers)
+ {
+ m_canTrigger = false; // Triggered spells can`t trigger another
+ switch (m_spellInfo->SpellFamilyName)
+ {
+ case SPELLFAMILY_MAGE: // Arcane Missles triggers need do it
+ if (m_spellInfo->SpellFamilyFlags & 0x0000000000200000LL) m_canTrigger = true;
+ break;
+ case SPELLFAMILY_WARLOCK: // For Hellfire Effect / Rain of Fire / Seed of Corruption triggers need do it
+ if (m_spellInfo->SpellFamilyFlags & 0x0000800000000060LL) m_canTrigger = true;
+ break;
+ case SPELLFAMILY_HUNTER: // Hunter Explosive Trap Effect/Immolation Trap Effect/Frost Trap Aura/Snake Trap Effect
+ if (m_spellInfo->SpellFamilyFlags & 0x0000200000000014LL) m_canTrigger = true;
+ break;
+ case SPELLFAMILY_PALADIN: // For Holy Shock triggers need do it
+ if (m_spellInfo->SpellFamilyFlags & 0x0001000000200000LL) m_canTrigger = true;
+ break;
+ }
+ }
+ // Do not trigger from item cast spell
+ if (m_CastItem)
+ m_canTrigger = false;
+
+ // Get data for type of attack and fill base info for trigger
+ switch (m_spellInfo->DmgClass)
+ {
+ case SPELL_DAMAGE_CLASS_MELEE:
+ m_procAttacker = PROC_FLAG_SUCCESSFUL_MELEE_SPELL_HIT;
+ m_procVictim = PROC_FLAG_TAKEN_MELEE_SPELL_HIT;
+ break;
+ case SPELL_DAMAGE_CLASS_RANGED:
+ m_procAttacker = PROC_FLAG_SUCCESSFUL_RANGED_SPELL_HIT;
+ m_procVictim = PROC_FLAG_TAKEN_RANGED_SPELL_HIT;
+ break;
+ default:
+ if (IsPositiveSpell(m_spellInfo->Id)) // Check for positive spell
+ {
+ m_procAttacker = PROC_FLAG_SUCCESSFUL_POSITIVE_SPELL;
+ m_procVictim = PROC_FLAG_TAKEN_POSITIVE_SPELL;
+ }
+ else if (m_spellInfo->Id == 5019) // Wands
+ {
+ m_procAttacker = PROC_FLAG_SUCCESSFUL_RANGED_SPELL_HIT;
+ m_procVictim = PROC_FLAG_TAKEN_RANGED_SPELL_HIT;
+ }
+ else
+ {
+ m_procAttacker = PROC_FLAG_SUCCESSFUL_NEGATIVE_SPELL_HIT;
+ m_procVictim = PROC_FLAG_TAKEN_NEGATIVE_SPELL_HIT;
+ }
+ break;
+ }
+ // Hunter traps spells (for Entrapment trigger)
+ // Gives your Immolation Trap, Frost Trap, Explosive Trap, and Snake Trap ....
+ if (m_spellInfo->SpellFamilyName == SPELLFAMILY_HUNTER && m_spellInfo->SpellFamilyFlags & 0x0000200000000014LL)
+ m_procAttacker |= PROC_FLAG_ON_TRAP_ACTIVATION;
+}
+
void Spell::CleanupTargetList()
{
m_UniqueTargetInfo.clear();
@@ -835,7 +908,7 @@ void Spell::AddItemTarget(Item* pitem, uint32 effIndex)
target.effectMask = 1<<effIndex;
m_UniqueItemInfo.push_back(target);
}
-
+/*
void Spell::doTriggers(SpellMissInfo missInfo, uint32 damage, SpellSchoolMask damageSchoolMask, uint32 block, uint32 absorb, bool crit)
{
// Do triggers depends from hit result (triggers on hit do in effects)
@@ -911,7 +984,7 @@ void Spell::doTriggers(SpellMissInfo missInfo, uint32 damage, SpellSchoolMask da
break;
}
}
-}
+}*/
void Spell::DoAllEffectOnTarget(TargetInfo *target)
{
@@ -928,12 +1001,28 @@ void Spell::DoAllEffectOnTarget(TargetInfo *target)
if (!unit)
return;
+ // Get original caster (if exist) and calculate damage/healing from him data
+ Unit *caster = m_originalCasterGUID ? m_originalCaster : m_caster;
+
+ // Skip if m_originalCaster not avaiable
+ if (!caster)
+ return;
+
SpellMissInfo missInfo = target->missCondition;
// 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;
+ // Reset damage/healing counter
+ m_damage = 0;
+ m_healing = 0;
+
+ // Fill base trigger info
+ uint32 procAttacker = m_procAttacker;
+ uint32 procVictim = m_procVictim;
+ uint32 procEx = PROC_EX_NONE;
+
if (missInfo==SPELL_MISS_NONE) // In case spell hit target, do all effect on that target
DoSpellHitOnUnit(unit, mask);
else if (missInfo == SPELL_MISS_REFLECT) // In case spell reflect from target, do all effect on caster (if hit)
@@ -952,9 +1041,103 @@ void Spell::DoAllEffectOnTarget(TargetInfo *target)
DoSpellHitOnUnit(unit, tempMask);
}
- // Do triggers only on miss/resist/parry/dodge
- if (missInfo!=SPELL_MISS_NONE)
- doTriggers(missInfo);
+ // All calculated do it!
+ // Do healing and triggers
+ if (m_healing)
+ {
+ bool crit = caster->isSpellCrit(NULL, m_spellInfo, m_spellSchoolMask);
+ uint32 addhealth = m_healing;
+ if (crit)
+ {
+ procEx |= PROC_EX_CRITICAL_HIT;
+ addhealth = caster->SpellCriticalBonus(m_spellInfo, addhealth, NULL);
+ }
+ else
+ procEx |= PROC_EX_NORMAL_HIT;
+
+ caster->SendHealSpellLog(unitTarget, m_spellInfo->Id, addhealth, crit);
+
+ // Do triggers for unit (reflect triggers passed on hit phase for correct drop charge)
+ if (m_canTrigger && missInfo != SPELL_MISS_REFLECT)
+ caster->ProcDamageAndSpell(unitTarget, procAttacker, procVictim, procEx, addhealth, m_attackType, m_spellInfo);
+
+ int32 gain = unitTarget->ModifyHealth( int32(addhealth) );
+
+ unitTarget->getHostilRefManager().threatAssist(caster, float(gain) * 0.5f, m_spellInfo);
+ if(caster->GetTypeId()==TYPEID_PLAYER)
+ if(BattleGround *bg = ((Player*)caster)->GetBattleGround())
+ bg->UpdatePlayerScore(((Player*)caster), SCORE_HEALING_DONE, gain);
+ }
+ // Do damage and triggers
+ else if (m_damage)
+ {
+ // Fill base damage struct (unitTarget - is real spell target)
+ SpellNonMeleeDamage damageInfo(caster, unitTarget, m_spellInfo->Id, m_spellSchoolMask);
+
+ // Add bonuses and fill damageInfo struct
+ caster->CalculateSpellDamage(&damageInfo, m_damage, m_spellInfo);
+
+ // Send log damage message to client
+ caster->SendSpellNonMeleeDamageLog(&damageInfo);
+
+ procEx = createProcExtendMask(&damageInfo, missInfo);
+ procVictim |= PROC_FLAG_TAKEN_ANY_DAMAGE;
+
+ // Do triggers for unit (reflect triggers passed on hit phase for correct drop charge)
+ if (m_canTrigger && missInfo != SPELL_MISS_REFLECT)
+ caster->ProcDamageAndSpell(unitTarget, procAttacker, procVictim, procEx, damageInfo.damage, m_attackType, m_spellInfo);
+
+ caster->DealSpellDamage(&damageInfo, true);
+
+ // Shadow Word: Death - deals damage equal to damage done to caster if victim is not killed
+ if (m_spellInfo->SpellFamilyName == SPELLFAMILY_PRIEST && m_spellInfo->SpellFamilyFlags&0x0000000200000000LL &&
+ caster != unitTarget && unitTarget->isAlive())
+ {
+ // Redirect damage to caster if victim Alive
+ damageInfo.target = caster;
+ damageInfo.absorb = 0;
+ damageInfo.resist = 0;
+ damageInfo.blocked = 0;
+ // Send log damage message to client
+ caster->SendSpellNonMeleeDamageLog(&damageInfo);
+ caster->DealSpellDamage(&damageInfo, true);
+ }
+ // Judgement of Blood
+ else if (m_spellInfo->SpellFamilyName == SPELLFAMILY_PALADIN && m_spellInfo->SpellFamilyFlags & 0x0000000800000000LL && m_spellInfo->SpellIconID==153)
+ {
+ int32 damagePoint = damageInfo.damage * 33 / 100;
+ m_caster->CastCustomSpell(m_caster, 32220, &damagePoint, NULL, NULL, true);
+ }
+ // Bloodthirst
+ else if (m_spellInfo->SpellFamilyName == SPELLFAMILY_WARRIOR && m_spellInfo->SpellFamilyFlags & 0x40000000000LL)
+ {
+ uint32 BTAura = 0;
+ switch(m_spellInfo->Id)
+ {
+ case 23881: BTAura = 23885; break;
+ case 23892: BTAura = 23886; break;
+ case 23893: BTAura = 23887; break;
+ case 23894: BTAura = 23888; break;
+ case 25251: BTAura = 25252; break;
+ case 30335: BTAura = 30339; break;
+ default:
+ sLog.outError("Spell::EffectSchoolDMG: Spell %u not handled in BTAura",m_spellInfo->Id);
+ break;
+ }
+ if (BTAura)
+ m_caster->CastSpell(m_caster,BTAura,true);
+ }
+ }
+ // Passive spell hits/misses or active spells only misses (only triggers)
+ else
+ {
+ // Fill base damage struct (unitTarget - is real spell target)
+ SpellNonMeleeDamage damageInfo(caster, unitTarget, m_spellInfo->Id, m_spellSchoolMask);
+ procEx = createProcExtendMask(&damageInfo, missInfo);
+ // Do triggers for unit (reflect triggers passed on hit phase for correct drop charge)
+ if (m_canTrigger && missInfo != SPELL_MISS_REFLECT)
+ caster->ProcDamageAndSpell(unit, procAttacker, procVictim, procEx, 0, m_attackType, m_spellInfo);
+ }
// Call scripted function for AI if this spell is casted upon a creature (except pets)
if(IS_CREATURE_GUID(target->targetGUID))
@@ -1076,14 +1259,15 @@ void Spell::DoSpellHitOnUnit(Unit *unit, const uint32 effectMask)
}
}
- if(spellmgr.GetSpellCustomAttr(m_spellInfo->Id) && m_originalCaster)
+ //This is not needed with procflag patch
+ /*if(spellmgr.GetSpellCustomAttr(m_spellInfo->Id) && m_originalCaster)
{
uint32 flag = spellmgr.GetSpellCustomAttr(m_spellInfo->Id);
if(flag & SPELL_ATTR_CU_EFFECT_HEAL)
m_originalCaster->ProcDamageAndSpell(unit, PROC_FLAG_HEAL, PROC_FLAG_NONE, 0, GetSpellSchoolMask(m_spellInfo), m_spellInfo);
if(m_originalCaster != unit && (flag & SPELL_ATTR_CU_EFFECT_DAMAGE))
m_originalCaster->ProcDamageAndSpell(unit, PROC_FLAG_HIT_SPELL, PROC_FLAG_STRUCK_SPELL, 0, GetSpellSchoolMask(m_spellInfo), m_spellInfo);
- }
+ }*/
}
void Spell::DoAllEffectOnTarget(GOTargetInfo *target)
@@ -2150,6 +2334,9 @@ void Spell::prepare(SpellCastTargets * targets, Aura* triggeredByAura)
return;
}
+ // Prepare data for triggers
+ prepareDataForTriggerSystem();
+
// calculate cast time (calculated after first CanCast check to prevent charge counting for first CanCast fail)
m_casttime = GetSpellCastTime(m_spellInfo, this);
@@ -2321,15 +2508,6 @@ void Spell::cast(bool skipCheck)
SendCastResult(castResult);
SendSpellGo(); // we must send smsg_spell_go packet before m_castItem delete in TakeCastItem()...
- // Pass cast spell event to handler (not send triggered by aura spells)
- if (m_spellInfo->DmgClass != SPELL_DAMAGE_CLASS_MELEE && m_spellInfo->DmgClass != SPELL_DAMAGE_CLASS_RANGED && !m_triggeredByAuraSpell)
- {
- m_caster->ProcDamageAndSpell(m_targets.getUnitTarget(), PROC_FLAG_CAST_SPELL, PROC_FLAG_NONE, 0, SPELL_SCHOOL_MASK_NONE, m_spellInfo, m_IsTriggeredSpell);
-
- // update pointers base at GUIDs to prevent access to non-existed already object
- UpdatePointers(); // pointers can be invalidate at triggered spell casting
- }
-
// Okay, everything is prepared. Now we need to distinguish between immediate and evented delayed spells
if (m_spellInfo->speed > 0.0f && !IsChanneledSpell(m_spellInfo))
{
@@ -2745,6 +2923,13 @@ void Spell::finish(bool ok)
}
}
+ // Heal caster for all health leech from all targets
+ if (m_healthLeech)
+ {
+ m_caster->ModifyHealth(m_healthLeech);
+ m_caster->SendHealSpellLog(m_caster, m_spellInfo->Id, uint32(m_healthLeech));
+ }
+
if (IsMeleeAttackResetSpell())
{
m_caster->resetAttackTimer(BASE_ATTACK);
@@ -3574,8 +3759,144 @@ uint8 Spell::CanCast(bool strict)
if(uint8 castResult = CheckItems())
return castResult;
- if(uint8 castResult = CheckRange(strict))
- return castResult;
+ /*//ImpliciteTargetA-B = 38, If fact there is 0 Spell with ImpliciteTargetB=38
+ if(m_UniqueTargetInfo.empty()) // skip second canCast apply (for delayed spells for example)
+ {
+ for(uint8 j = 0; j < 3; j++)
+ {
+ if( m_spellInfo->EffectImplicitTargetA[j] == TARGET_SCRIPT ||
+ m_spellInfo->EffectImplicitTargetB[j] == TARGET_SCRIPT && m_spellInfo->EffectImplicitTargetA[j] != TARGET_SELF ||
+ m_spellInfo->EffectImplicitTargetA[j] == TARGET_SCRIPT_COORDINATES ||
+ m_spellInfo->EffectImplicitTargetB[j] == TARGET_SCRIPT_COORDINATES )
+ {
+ SpellScriptTarget::const_iterator lower = spellmgr.GetBeginSpellScriptTarget(m_spellInfo->Id);
+ SpellScriptTarget::const_iterator upper = spellmgr.GetEndSpellScriptTarget(m_spellInfo->Id);
+ if(lower==upper)
+ sLog.outErrorDb("Spell (ID: %u) has effect EffectImplicitTargetA/EffectImplicitTargetB = TARGET_SCRIPT or TARGET_SCRIPT_COORDINATES, but does not have record in `spell_script_target`",m_spellInfo->Id);
+
+ SpellRangeEntry const* srange = sSpellRangeStore.LookupEntry(m_spellInfo->rangeIndex);
+ float range = GetSpellMaxRange(srange);
+
+ Creature* creatureScriptTarget = NULL;
+ GameObject* goScriptTarget = NULL;
+
+ for(SpellScriptTarget::const_iterator i_spellST = lower; i_spellST != upper; ++i_spellST)
+ {
+ switch(i_spellST->second.type)
+ {
+ case SPELL_TARGET_TYPE_GAMEOBJECT:
+ {
+ GameObject* p_GameObject = NULL;
+
+ if(i_spellST->second.targetEntry)
+ {
+ CellPair p(MaNGOS::ComputeCellPair(m_caster->GetPositionX(), m_caster->GetPositionY()));
+ Cell cell(p);
+ cell.data.Part.reserved = ALL_DISTRICT;
+
+ MaNGOS::NearestGameObjectEntryInObjectRangeCheck go_check(*m_caster,i_spellST->second.targetEntry,range);
+ MaNGOS::GameObjectLastSearcher<MaNGOS::NearestGameObjectEntryInObjectRangeCheck> checker(p_GameObject,go_check);
+
+ TypeContainerVisitor<MaNGOS::GameObjectLastSearcher<MaNGOS::NearestGameObjectEntryInObjectRangeCheck>, GridTypeMapContainer > object_checker(checker);
+ CellLock<GridReadGuard> cell_lock(cell, p);
+ cell_lock->Visit(cell_lock, object_checker, *m_caster->GetMap());
+
+ if(p_GameObject)
+ {
+ // remember found target and range, next attempt will find more near target with another entry
+ creatureScriptTarget = NULL;
+ goScriptTarget = p_GameObject;
+ range = go_check.GetLastRange();
+ }
+ }
+ else if( focusObject ) //Focus Object
+ {
+ float frange = m_caster->GetDistance(focusObject);
+ if(range >= frange)
+ {
+ creatureScriptTarget = NULL;
+ goScriptTarget = focusObject;
+ range = frange;
+ }
+ }
+ break;
+ }
+ case SPELL_TARGET_TYPE_CREATURE:
+ case SPELL_TARGET_TYPE_DEAD:
+ default:
+ {
+ Creature *p_Creature = NULL;
+
+ CellPair p(MaNGOS::ComputeCellPair(m_caster->GetPositionX(), m_caster->GetPositionY()));
+ Cell cell(p);
+ cell.data.Part.reserved = ALL_DISTRICT;
+ cell.SetNoCreate(); // Really don't know what is that???
+
+ MaNGOS::NearestCreatureEntryWithLiveStateInObjectRangeCheck u_check(*m_caster,i_spellST->second.targetEntry,i_spellST->second.type!=SPELL_TARGET_TYPE_DEAD,range);
+ MaNGOS::CreatureLastSearcher<MaNGOS::NearestCreatureEntryWithLiveStateInObjectRangeCheck> searcher(p_Creature, u_check);
+
+ TypeContainerVisitor<MaNGOS::CreatureLastSearcher<MaNGOS::NearestCreatureEntryWithLiveStateInObjectRangeCheck>, GridTypeMapContainer > grid_creature_searcher(searcher);
+
+ CellLock<GridReadGuard> cell_lock(cell, p);
+ cell_lock->Visit(cell_lock, grid_creature_searcher, *m_caster->GetMap());
+
+ if(p_Creature )
+ {
+ creatureScriptTarget = p_Creature;
+ goScriptTarget = NULL;
+ range = u_check.GetLastRange();
+ }
+ break;
+ }
+ }
+ }
+
+ if(creatureScriptTarget)
+ {
+ // store coordinates for TARGET_SCRIPT_COORDINATES
+ if (m_spellInfo->EffectImplicitTargetA[j] == TARGET_SCRIPT_COORDINATES ||
+ m_spellInfo->EffectImplicitTargetB[j] == TARGET_SCRIPT_COORDINATES )
+ {
+ m_targets.setDestination(creatureScriptTarget->GetPositionX(),creatureScriptTarget->GetPositionY(),creatureScriptTarget->GetPositionZ());
+
+ if(m_spellInfo->EffectImplicitTargetA[j] == TARGET_SCRIPT_COORDINATES && m_spellInfo->EffectImplicitTargetB[j] == 0 && m_spellInfo->Effect[j]!=SPELL_EFFECT_PERSISTENT_AREA_AURA)
+ AddUnitTarget(creatureScriptTarget, j);
+ }
+ // store explicit target for TARGET_SCRIPT
+ else
+ AddUnitTarget(creatureScriptTarget, j);
+ }
+ else if(goScriptTarget)
+ {
+ // store coordinates for TARGET_SCRIPT_COORDINATES
+ if (m_spellInfo->EffectImplicitTargetA[j] == TARGET_SCRIPT_COORDINATES ||
+ m_spellInfo->EffectImplicitTargetB[j] == TARGET_SCRIPT_COORDINATES )
+ {
+ m_targets.setDestination(goScriptTarget->GetPositionX(),goScriptTarget->GetPositionY(),goScriptTarget->GetPositionZ());
+
+ if(m_spellInfo->EffectImplicitTargetA[j] == TARGET_SCRIPT_COORDINATES && m_spellInfo->EffectImplicitTargetB[j] == 0 && m_spellInfo->Effect[j]!=SPELL_EFFECT_PERSISTENT_AREA_AURA)
+ AddGOTarget(goScriptTarget, j);
+ }
+ // store explicit target for TARGET_SCRIPT
+ else
+ AddGOTarget(goScriptTarget, j);
+ }
+ //Missing DB Entry or targets for this spellEffect.
+ else
+ {
+ // not report target not existence for triggered spells
+ if(m_triggeredByAuraSpell || m_IsTriggeredSpell)
+ return SPELL_FAILED_DONT_REPORT;
+ else
+ return SPELL_FAILED_BAD_TARGETS;
+ }
+ }
+ }
+ }*/
+
+ if(!m_triggeredByAuraSpell)
+ if(uint8 castResult = CheckRange(strict))
+ return castResult;
{
if(uint8 castResult = CheckPower())
diff --git a/src/game/Spell.h b/src/game/Spell.h
index bd2d198f330..365acb93ce9 100644
--- a/src/game/Spell.h
+++ b/src/game/Spell.h
@@ -470,10 +470,18 @@ class Spell
// -------------------------------------------
GameObject* focusObject;
+ // Damage and healing in effects need just calculate
+ int32 m_damage; // Damge in effects count here
+ int32 m_healing; // Healing in effects count here
+ int32 m_healthLeech; // Health leech in effects for all targets count here
+
//******************************************
// Spell trigger system
//******************************************
- void doTriggers(SpellMissInfo missInfo, uint32 damage=0, SpellSchoolMask damageSchoolMask = SPELL_SCHOOL_MASK_NONE, uint32 block=0, uint32 absorb=0, bool crit=false);
+ bool m_canTrigger; // Can start trigger (m_IsTriggeredSpell can`t use for this)
+ uint32 m_procAttacker; // Attacker trigger flags
+ uint32 m_procVictim; // Victim trigger flags
+ void prepareDataForTriggerSystem();
//*****************************************
// Spell target subsystem
diff --git a/src/game/SpellAuras.cpp b/src/game/SpellAuras.cpp
index 46842bc3ced..9bac84ed555 100644
--- a/src/game/SpellAuras.cpp
+++ b/src/game/SpellAuras.cpp
@@ -257,8 +257,8 @@ pAuraHandler AuraHandler[TOTAL_AURAS]=
&Aura::HandleNoImmediateEffect, //200 SPELL_AURA_MOD_XP_PCT implemented in Player::GiveXP
&Aura::HandleAuraAllowFlight, //201 SPELL_AURA_FLY this aura enable flight mode...
&Aura::HandleNoImmediateEffect, //202 SPELL_AURA_CANNOT_BE_DODGED implemented in Unit::RollPhysicalOutcomeAgainst
- &Aura::HandleNoImmediateEffect, //203 SPELL_AURA_MOD_ATTACKER_MELEE_CRIT_DAMAGE implemented in Unit::DoAttackDamage
- &Aura::HandleNoImmediateEffect, //204 SPELL_AURA_MOD_ATTACKER_RANGED_CRIT_DAMAGE implemented in Unit::DoAttackDamage
+ &Aura::HandleNoImmediateEffect, //203 SPELL_AURA_MOD_ATTACKER_MELEE_CRIT_DAMAGE implemented in Unit::CalculateMeleeDamage and Unit::CalculateSpellDamage
+ &Aura::HandleNoImmediateEffect, //204 SPELL_AURA_MOD_ATTACKER_RANGED_CRIT_DAMAGE implemented in Unit::CalculateMeleeDamage and Unit::CalculateSpellDamage
&Aura::HandleNULL, //205 vulnerable to school dmg?
&Aura::HandleNULL, //206 SPELL_AURA_MOD_SPEED_MOUNTED
&Aura::HandleAuraModIncreaseFlightSpeed, //207 SPELL_AURA_MOD_INCREASE_FLIGHT_SPEED
@@ -5616,11 +5616,15 @@ void Aura::PeriodicTick()
Unit* target = m_target; // aura can be deleted in DealDamage
SpellEntry const* spellProto = GetSpellProto();
- pCaster->DealDamage(m_target, (pdamage <= absorb+resist) ? 0 : (pdamage-absorb-resist), &cleanDamage, DOT, GetSpellSchoolMask(GetSpellProto()), GetSpellProto(), true);
+ // Set trigger flag
+ uint32 procAttacker = PROC_FLAG_ON_DO_PERIODIC;// | PROC_FLAG_SUCCESSFUL_HARMFUL_SPELL_HIT;
+ uint32 procVictim = PROC_FLAG_ON_TAKE_PERIODIC;// | PROC_FLAG_TAKEN_HARMFUL_SPELL_HIT;
+ pdamage = (pdamage <= absorb+resist) ? 0 : (pdamage-absorb-resist);
+ if (pdamage)
+ procVictim|=PROC_FLAG_TAKEN_ANY_DAMAGE;
+ pCaster->ProcDamageAndSpell(target, procAttacker, procVictim, PROC_EX_NORMAL_HIT, pdamage, BASE_ATTACK, spellProto);
- // DO NOT ACCESS MEMBERS OF THE AURA FROM NOW ON (DealDamage can delete aura)
-
- pCaster->ProcDamageAndSpell(target, PROC_FLAG_NONE, PROC_FLAG_TAKE_DAMAGE, (pdamage <= absorb+resist) ? 0 : (pdamage-absorb-resist), GetSpellSchoolMask(spellProto), spellProto);
+ pCaster->DealDamage(target, pdamage, &cleanDamage, DOT, GetSpellSchoolMask(spellProto), spellProto, true);
break;
}
case SPELL_AURA_PERIODIC_LEECH:
@@ -5729,11 +5733,15 @@ void Aura::PeriodicTick()
SpellEntry const* spellProto = GetSpellProto();
float multiplier = spellProto->EffectMultipleValue[GetEffIndex()] > 0 ? spellProto->EffectMultipleValue[GetEffIndex()] : 1;
- uint32 new_damage = pCaster->DealDamage(m_target, (pdamage <= absorb+resist) ? 0 : (pdamage-absorb-resist), &cleanDamage, DOT, GetSpellSchoolMask(GetSpellProto()), GetSpellProto(), false);
-
- // DO NOT ACCESS MEMBERS OF THE AURA FROM NOW ON (DealDamage can delete aura)
+ // Set trigger flag
+ uint32 procAttacker = PROC_FLAG_ON_DO_PERIODIC;// | PROC_FLAG_SUCCESSFUL_HARMFUL_SPELL_HIT;
+ uint32 procVictim = PROC_FLAG_ON_TAKE_PERIODIC;// | PROC_FLAG_TAKEN_HARMFUL_SPELL_HIT;
+ pdamage = (pdamage <= absorb+resist) ? 0 : (pdamage-absorb-resist);
+ if (pdamage)
+ procVictim|=PROC_FLAG_TAKEN_ANY_DAMAGE;
+ pCaster->ProcDamageAndSpell(target, procAttacker, procVictim, PROC_EX_NORMAL_HIT, pdamage, BASE_ATTACK, spellProto);
+ int32 new_damage = pCaster->DealDamage(target, pdamage, &cleanDamage, DOT, GetSpellSchoolMask(spellProto), spellProto, false);
- pCaster->ProcDamageAndSpell(target, PROC_FLAG_HEALED, PROC_FLAG_TAKE_DAMAGE, new_damage, GetSpellSchoolMask(spellProto), spellProto);
if (!target->isAlive() && pCaster->IsNonMeleeSpellCasted(false))
{
for (uint32 i = CURRENT_FIRST_NON_MELEE_SPELL; i < CURRENT_MAX_SPELL; i++)
@@ -5834,9 +5842,11 @@ void Aura::PeriodicTick()
}
}
+ uint32 procAttacker = PROC_FLAG_ON_DO_PERIODIC;// | PROC_FLAG_SUCCESSFUL_HEAL;
+ uint32 procVictim = 0;//ROC_FLAG_ON_TAKE_PERIODIC | PROC_FLAG_TAKEN_HEAL;
// ignore item heals
- if(procSpell && !haveCastItem)
- pCaster->ProcDamageAndSpell(target,PROC_FLAG_NONE, PROC_FLAG_HEALED, pdamage, SPELL_SCHOOL_MASK_NONE, spellProto);
+// if(procSpell && !haveCastItem)
+// pCaster->ProcDamageAndSpell(target, procAttacker, procVictim, PROC_EX_NORMAL_HIT, pdamage, BASE_ATTACK, spellProto);
break;
}
case SPELL_AURA_PERIODIC_MANA_LEECH:
@@ -5995,8 +6005,22 @@ void Aura::PeriodicTick()
gain = uint32(gain * GetSpellProto()->EffectMultipleValue[GetEffIndex()]);
+ SpellEntry const* spellProto = GetSpellProto();
//maybe has to be sent different to client, but not by SMSG_PERIODICAURALOG
- pCaster->SpellNonMeleeDamageLog(m_target, GetId(), gain);
+ SpellNonMeleeDamage damageInfo(pCaster, m_target, spellProto->Id, spellProto->SchoolMask);
+ pCaster->CalculateSpellDamage(&damageInfo, gain, spellProto);
+ pCaster->SendSpellNonMeleeDamageLog(&damageInfo);
+
+ // Set trigger flag
+ uint32 procAttacker = PROC_FLAG_ON_DO_PERIODIC;// | PROC_FLAG_SUCCESSFUL_HARMFUL_SPELL_HIT;
+ uint32 procVictim = PROC_FLAG_ON_TAKE_PERIODIC;// | PROC_FLAG_TAKEN_HARMFUL_SPELL_HIT;
+ uint32 procEx = createProcExtendMask(&damageInfo, SPELL_MISS_NONE);
+ if (damageInfo.damage)
+ procVictim|=PROC_FLAG_TAKEN_ANY_DAMAGE;
+
+ pCaster->ProcDamageAndSpell(damageInfo.target, procAttacker, procVictim, procEx, damageInfo.damage, BASE_ATTACK, spellProto);
+
+ pCaster->DealSpellDamage(&damageInfo, true);
break;
}
// Here tick dummy auras
diff --git a/src/game/SpellEffects.cpp b/src/game/SpellEffects.cpp
index acbdfacdce9..abbd6214203 100644
--- a/src/game/SpellEffects.cpp
+++ b/src/game/SpellEffects.cpp
@@ -559,61 +559,7 @@ void Spell::EffectSchoolDMG(uint32 effect_idx)
}
if(damage >= 0)
- {
- uint32 finalDamage;
- if(m_originalCaster) // m_caster only passive source of cast
- finalDamage = m_originalCaster->SpellNonMeleeDamageLog(unitTarget, m_spellInfo->Id, damage, m_IsTriggeredSpell, true);
- else
- finalDamage = m_caster->SpellNonMeleeDamageLog(unitTarget, m_spellInfo->Id, damage, m_IsTriggeredSpell, true);
-
- // post effects
- switch(m_spellInfo->SpellFamilyName)
- {
- case SPELLFAMILY_WARRIOR:
- {
- // Bloodthirst
- if(m_spellInfo->SpellFamilyFlags & 0x40000000000LL)
- {
- uint32 BTAura = 0;
- switch(m_spellInfo->Id)
- {
- case 23881: BTAura = 23885; break;
- case 23892: BTAura = 23886; break;
- case 23893: BTAura = 23887; break;
- case 23894: BTAura = 23888; break;
- case 25251: BTAura = 25252; break;
- case 30335: BTAura = 30339; break;
- default:
- sLog.outError("Spell::EffectSchoolDMG: Spell %u not handled in BTAura",m_spellInfo->Id);
- break;
- }
-
- if (BTAura)
- m_caster->CastSpell(m_caster,BTAura,true);
- }
- break;
- }
- case SPELLFAMILY_PRIEST:
- {
- // Shadow Word: Death
- if(finalDamage > 0 && (m_spellInfo->SpellFamilyFlags & 0x0000000200000000LL) && unitTarget->isAlive())
- // deals damage equal to damage done to caster if victim is not killed
- m_caster->SpellNonMeleeDamageLog( m_caster, m_spellInfo->Id, finalDamage, m_IsTriggeredSpell, false);
-
- break;
- }
- case SPELLFAMILY_PALADIN:
- {
- // Judgement of Blood
- if(finalDamage > 0 && (m_spellInfo->SpellFamilyFlags & 0x0000000800000000LL) && m_spellInfo->SpellIconID==153)
- {
- int32 damagePoint = finalDamage * 33 / 100;
- m_caster->CastCustomSpell(m_caster, 32220, &damagePoint, NULL, NULL, true);
- }
- break;
- }
- }
- }
+ m_damage+= damage;
}
}
@@ -1452,7 +1398,7 @@ void Spell::EffectDummy(uint32 i)
}
if(found)
- m_caster->SpellNonMeleeDamageLog(unitTarget, m_spellInfo->Id, damage, m_IsTriggeredSpell, true);
+ m_damage+= damage;
return;
}
// Kill command
@@ -2347,7 +2293,7 @@ void Spell::EffectPowerBurn(uint32 i)
modOwner->ApplySpellMod(m_spellInfo->Id, SPELLMOD_MULTIPLE_VALUE, multiplier);
new_damage = int32(new_damage*multiplier);
- m_caster->SpellNonMeleeDamageLog(unitTarget, m_spellInfo->Id, new_damage, m_IsTriggeredSpell, true);
+ m_damage+=new_damage;
}
void Spell::EffectHeal( uint32 /*i*/ )
@@ -2415,27 +2361,7 @@ void Spell::EffectHeal( uint32 /*i*/ )
else
addhealth = caster->SpellHealingBonus(m_spellInfo, addhealth,HEAL, unitTarget);
- bool crit = caster->isSpellCrit(unitTarget, m_spellInfo, m_spellSchoolMask, m_attackType);
- if (crit)
- addhealth = caster->SpellCriticalBonus(m_spellInfo, addhealth, unitTarget);
- caster->SendHealSpellLog(unitTarget, m_spellInfo->Id, addhealth, crit);
-
- int32 gain = unitTarget->ModifyHealth( int32(addhealth) );
- unitTarget->getHostilRefManager().threatAssist(m_caster, float(gain) * 0.5f, m_spellInfo);
-
- if(caster->GetTypeId()==TYPEID_PLAYER)
- if(BattleGround *bg = ((Player*)caster)->GetBattleGround())
- bg->UpdatePlayerScore(((Player*)caster), SCORE_HEALING_DONE, gain);
-
- // ignore item heals
- if(m_CastItem)
- return;
-
- uint32 procHealer = PROC_FLAG_HEAL;
- if (crit)
- procHealer |= PROC_FLAG_CRIT_HEAL;
-
- m_caster->ProcDamageAndSpell(unitTarget,procHealer,PROC_FLAG_HEALED,addhealth,SPELL_SCHOOL_MASK_NONE,m_spellInfo,m_IsTriggeredSpell);
+ m_healing+=addhealth;
}
}
@@ -2512,6 +2438,8 @@ void Spell::EffectHealthLeech(uint32 i)
if(m_caster->GetTypeId() == TYPEID_PLAYER)
m_caster->SendHealSpellLog(m_caster, m_spellInfo->Id, uint32(new_damage));
}
+// m_healthLeech+=tmpvalue;
+// m_damage+=new_damage;
}
void Spell::DoCreateItem(uint32 i, uint32 itemtype)
@@ -4442,35 +4370,9 @@ void Spell::EffectWeaponDmg(uint32 i)
// prevent negative damage
uint32 eff_damage = uint32(bonus > 0 ? bonus : 0);
- const uint32 nohitMask = HITINFO_ABSORB | HITINFO_RESIST | HITINFO_MISS;
-
- uint32 hitInfo = 0;
- VictimState victimState = VICTIMSTATE_NORMAL;
- uint32 blocked_dmg = 0;
- uint32 absorbed_dmg = 0;
- uint32 resisted_dmg = 0;
- CleanDamage cleanDamage = CleanDamage(0, BASE_ATTACK, MELEE_HIT_NORMAL );
-
- m_caster->DoAttackDamage(unitTarget, &eff_damage, &cleanDamage, &blocked_dmg, m_spellSchoolMask, &hitInfo, &victimState, &absorbed_dmg, &resisted_dmg, m_attackType, m_spellInfo, m_IsTriggeredSpell);
-
- if ((hitInfo & nohitMask) && m_attackType != RANGED_ATTACK) // not send ranged miss/etc
- m_caster->SendAttackStateUpdate(hitInfo & nohitMask, unitTarget, 1, m_spellSchoolMask, eff_damage, absorbed_dmg, resisted_dmg, VICTIMSTATE_NORMAL, blocked_dmg);
-
- bool criticalhit = (hitInfo & HITINFO_CRITICALHIT);
- m_caster->SendSpellNonMeleeDamageLog(unitTarget, m_spellInfo->Id, eff_damage, m_spellSchoolMask, absorbed_dmg, resisted_dmg, false, blocked_dmg, criticalhit);
-
- if (eff_damage > (absorbed_dmg + resisted_dmg + blocked_dmg))
- {
- eff_damage -= (absorbed_dmg + resisted_dmg + blocked_dmg);
- }
- else
- {
- cleanDamage.damage += eff_damage;
- eff_damage = 0;
- }
-
- // SPELL_SCHOOL_NORMAL use for weapon-like threat and rage calculation
- m_caster->DealDamage(unitTarget, eff_damage, &cleanDamage, SPELL_DIRECT_DAMAGE, SPELL_SCHOOL_MASK_NORMAL, NULL, true);
+ // Add melee damage bonuses (also check for negative)
+ m_caster->MeleeDamageBonus(unitTarget, &eff_damage, m_attackType, m_spellInfo);
+ m_damage+= eff_damage;
// Hemorrhage
if(m_spellInfo->SpellFamilyName==SPELLFAMILY_ROGUE && (m_spellInfo->SpellFamilyFlags & 0x2000000))
@@ -4534,10 +4436,7 @@ void Spell::EffectHealMaxHealth(uint32 /*i*/)
uint32 heal = m_caster->GetMaxHealth();
- int32 gain = unitTarget->ModifyHealth(heal);
- unitTarget->getHostilRefManager().threatAssist(m_caster, float(gain) * 0.5f, m_spellInfo);
-
- m_caster->SendHealSpellLog(unitTarget, m_spellInfo->Id, heal);
+ m_healing+=heal;
}
void Spell::EffectInterruptCast(uint32 /*i*/)
diff --git a/src/game/SpellMgr.cpp b/src/game/SpellMgr.cpp
index 98585fceaaf..8d842bd63a6 100644
--- a/src/game/SpellMgr.cpp
+++ b/src/game/SpellMgr.cpp
@@ -547,6 +547,17 @@ bool IsSingleTargetSpells(SpellEntry const *spellInfo1, SpellEntry const *spellI
return false;
}
+bool IsAuraAddedBySpell(uint32 auraType, uint32 spellId)
+{
+ SpellEntry const *spellproto = sSpellStore.LookupEntry(spellId);
+ if (!spellproto) return false;
+
+ for (int i = 0; i < 3; i++)
+ if (spellproto->EffectApplyAuraName[i] == auraType)
+ return true;
+ return false;
+}
+
uint8 GetErrorAtShapeshiftedCast (SpellEntry const *spellInfo, uint32 form)
{
// talents that learn spells can have stance requirements that need ignore
@@ -823,8 +834,8 @@ void SpellMgr::LoadSpellProcEvents()
uint32 count = 0;
- // 0 1 2 3 4 5 6 7 8
- QueryResult *result = WorldDatabase.Query("SELECT entry, SchoolMask, Category, SkillID, SpellFamilyName, SpellFamilyMask, procFlags, ppmRate, cooldown FROM spell_proc_event");
+ // 0 1 2 3 4 5 6 7 8
+ QueryResult *result = WorldDatabase.Query("SELECT entry, SchoolMask, SpellFamilyName, SpellFamilyMask, procFlags, procEx, ppmRate, CustomChance, Cooldown FROM spell_proc_event");
if( !result )
{
@@ -838,7 +849,7 @@ void SpellMgr::LoadSpellProcEvents()
}
barGoLink bar( result->GetRowCount() );
-
+ uint32 customProc = 0;
do
{
Field *fields = result->Fetch();
@@ -847,7 +858,8 @@ void SpellMgr::LoadSpellProcEvents()
uint16 entry = fields[0].GetUInt16();
- if (!sSpellStore.LookupEntry(entry))
+ const SpellEntry *spell = sSpellStore.LookupEntry(entry);
+ if (!spell)
{
sLog.outErrorDb("Spell %u listed in `spell_proc_event` does not exist", entry);
continue;
@@ -856,23 +868,35 @@ void SpellMgr::LoadSpellProcEvents()
SpellProcEventEntry spe;
spe.schoolMask = fields[1].GetUInt32();
- spe.category = fields[2].GetUInt32();
- spe.skillId = fields[3].GetUInt32();
- spe.spellFamilyName = fields[4].GetUInt32();
- spe.spellFamilyMask = fields[5].GetUInt64();
- spe.procFlags = fields[6].GetUInt32();
- spe.ppmRate = fields[7].GetFloat();
+ spe.spellFamilyName = fields[2].GetUInt32();
+ spe.spellFamilyMask = fields[3].GetUInt64();
+ spe.procFlags = fields[4].GetUInt32();
+ spe.procEx = fields[5].GetUInt32();
+ spe.ppmRate = fields[6].GetFloat();
+ spe.customChance = fields[7].GetFloat();
spe.cooldown = fields[8].GetUInt32();
mSpellProcEventMap[entry] = spe;
+ if (spell->procFlags==0)
+ {
+ if (spe.procFlags == 0)
+ {
+ sLog.outErrorDb("Spell %u listed in `spell_proc_event` probally not triggered spell", entry);
+ continue;
+ }
+ customProc++;
+ }
++count;
} while( result->NextRow() );
delete result;
sLog.outString();
- sLog.outString( ">> Loaded %u spell proc event conditions", count );
+ if (customProc)
+ sLog.outString( ">> Loaded %u custom spell proc event conditions +%u custom", count, customProc );
+ else
+ sLog.outString( ">> Loaded %u spell proc event conditions", count );
/*
// Commented for now, as it still produces many errors (still quite many spells miss spell_proc_event)
@@ -904,6 +928,7 @@ void SpellMgr::LoadSpellProcEvents()
*/
}
+/*
bool SpellMgr::IsSpellProcEventCanTriggeredBy( SpellProcEventEntry const * spellProcEvent, SpellEntry const * procSpell, uint32 procFlags )
{
if((procFlags & spellProcEvent->procFlags) == 0)
@@ -942,6 +967,73 @@ bool SpellMgr::IsSpellProcEventCanTriggeredBy( SpellProcEventEntry const * spell
return true;
}
+*/
+
+bool SpellMgr::IsSpellProcEventCanTriggeredBy(SpellProcEventEntry const * spellProcEvent, uint32 EventProcFlag, SpellEntry const * procSpell, uint32 procFlags, uint32 procExtra, bool active)
+{
+ // No extra req need
+ uint32 procEvent_procEx = PROC_EX_NONE;
+
+ // check prockFlags for condition
+ if((procFlags & EventProcFlag) == 0)
+ return false;
+
+ // Always trigger for this
+ if (EventProcFlag & (PROC_FLAG_KILLED | PROC_FLAG_KILL_AND_GET_XP))
+ return true;
+
+ if (spellProcEvent) // Exist event data
+ {
+ // Store extra req
+ procEvent_procEx = spellProcEvent->procEx;
+
+ // For melee triggers
+ if (procSpell == NULL)
+ {
+ // Check (if set) for school (melee attack have Normal school)
+ if(spellProcEvent->schoolMask && (spellProcEvent->schoolMask & SPELL_SCHOOL_MASK_NORMAL) == 0)
+ return false;
+ }
+ else // For spells need check school/spell family/family mask
+ {
+ // Check (if set) for school
+ if(spellProcEvent->schoolMask && (spellProcEvent->schoolMask & procSpell->SchoolMask) == 0)
+ return false;
+
+ // Check (if set) for spellFamilyName
+ if(spellProcEvent->spellFamilyName && (spellProcEvent->spellFamilyName != procSpell->SpellFamilyName))
+ return false;
+
+ // spellFamilyName is Ok need check for spellFamilyMask if present
+ if(spellProcEvent->spellFamilyMask)
+ {
+ if ((spellProcEvent->spellFamilyMask & procSpell->SpellFamilyFlags) == 0)
+ return false;
+ active = true; // Spell added manualy -> so its active spell
+ }
+ }
+ }
+ // Check for extra req (if none) and hit/crit
+ if (procEvent_procEx == PROC_EX_NONE)
+ {
+ // No extra req, so can trigger only for active (damage/healing present) and hit/crit
+ if((procExtra & (PROC_EX_NORMAL_HIT|PROC_EX_CRITICAL_HIT)) && active)
+ return true;
+ }
+ else // Passive spells hits here only if resist/reflect/immune/evade
+ {
+ // Exist req for PROC_EX_EX_TRIGGER_ALWAYS
+ if (procEvent_procEx & PROC_EX_EX_TRIGGER_ALWAYS)
+ return true;
+ // Passive spells can`t trigger if need hit
+ if ((procEvent_procEx & PROC_EX_NORMAL_HIT) && !active)
+ return false;
+ // Check Extra Requirement like (hit/crit/miss/resist/parry/dodge/block/immune/reflect/absorb and other)
+ if (procEvent_procEx & procExtra)
+ return true;
+ }
+ return false;
+}
void SpellMgr::LoadSpellElixirs()
{
diff --git a/src/game/SpellMgr.h b/src/game/SpellMgr.h
index 859329a96f2..f5394470ae3 100644
--- a/src/game/SpellMgr.h
+++ b/src/game/SpellMgr.h
@@ -330,6 +330,8 @@ bool IsPositiveTarget(uint32 targetA, uint32 targetB);
bool IsSingleTargetSpell(SpellEntry const *spellInfo);
bool IsSingleTargetSpells(SpellEntry const *spellInfo1, SpellEntry const *spellInfo2);
+bool IsAuraAddedBySpell(uint32 auraType, uint32 spellId);
+
bool IsSpellAllowedInLocation(SpellEntry const *spellInfo,uint32 map_id,uint32 zone_id,uint32 area_id);
inline bool IsAreaEffectTarget( Targets target )
@@ -448,50 +450,86 @@ typedef std::map<uint32, uint64> SpellAffectMap;
// Spell proc event related declarations (accessed using SpellMgr functions)
enum ProcFlags
{
- PROC_FLAG_NONE = 0x00000000, // None
- PROC_FLAG_HIT_MELEE = 0x00000001, // On melee hit
- PROC_FLAG_STRUCK_MELEE = 0x00000002, // On being struck melee
- PROC_FLAG_KILL_XP_GIVER = 0x00000004, // On kill target giving XP or honor
- PROC_FLAG_SPECIAL_DROP = 0x00000008, //
- PROC_FLAG_DODGE = 0x00000010, // On dodge melee attack
- PROC_FLAG_PARRY = 0x00000020, // On parry melee attack
- PROC_FLAG_BLOCK = 0x00000040, // On block attack
- PROC_FLAG_TOUCH = 0x00000080, // On being touched (for bombs, probably?)
- PROC_FLAG_TARGET_LOW_HEALTH = 0x00000100, // On deal damage to enemy with 20% or less health
- PROC_FLAG_LOW_HEALTH = 0x00000200, // On health dropped below 20%
- PROC_FLAG_STRUCK_RANGED = 0x00000400, // On being struck ranged
- PROC_FLAG_HIT_SPECIAL = 0x00000800, // (!)Removed, may be reassigned in future
- PROC_FLAG_CRIT_MELEE = 0x00001000, // On crit melee
- PROC_FLAG_STRUCK_CRIT_MELEE = 0x00002000, // On being critically struck in melee
- PROC_FLAG_CAST_SPELL = 0x00004000, // On cast spell
- PROC_FLAG_TAKE_DAMAGE = 0x00008000, // On take damage
- PROC_FLAG_CRIT_SPELL = 0x00010000, // On crit spell
- PROC_FLAG_HIT_SPELL = 0x00020000, // On hit spell
- PROC_FLAG_STRUCK_CRIT_SPELL = 0x00040000, // On being critically struck by a spell
- PROC_FLAG_HIT_RANGED = 0x00080000, // On getting ranged hit
- PROC_FLAG_STRUCK_SPELL = 0x00100000, // On being struck by a spell
- PROC_FLAG_TRAP = 0x00200000, // On trap activation (?)
- PROC_FLAG_CRIT_RANGED = 0x00400000, // On getting ranged crit
- PROC_FLAG_STRUCK_CRIT_RANGED = 0x00800000, // On being critically struck by a ranged attack
- PROC_FLAG_RESIST_SPELL = 0x01000000, // On resist enemy spell
- PROC_FLAG_TARGET_RESISTS = 0x02000000, // On enemy resisted spell
- PROC_FLAG_TARGET_DODGE_OR_PARRY = 0x04000000, // On enemy dodges/parries
- PROC_FLAG_HEAL = 0x08000000, // On heal
- PROC_FLAG_CRIT_HEAL = 0x10000000, // On critical healing effect
- PROC_FLAG_HEALED = 0x20000000, // On healing
- PROC_FLAG_TARGET_BLOCK = 0x40000000, // On enemy blocks
- PROC_FLAG_MISS = 0x80000000 // On miss melee attack
+ PROC_FLAG_NONE = 0x00000000,
+
+ PROC_FLAG_KILLED = 0x00000001, // 00 Killed by agressor
+ PROC_FLAG_KILL_AND_GET_XP = 0x00000002, // 01 Kill that yields experience or honor
+
+ PROC_FLAG_SUCCESSFUL_MILEE_HIT = 0x00000004, // 02 Successful melee attack
+ PROC_FLAG_TAKEN_MELEE_HIT = 0x00000008, // 03 Taken damage from melee strike hit
+
+ PROC_FLAG_SUCCESSFUL_MELEE_SPELL_HIT = 0x00000010, // 04 Successful attack by Spell that use melee weapon
+ PROC_FLAG_TAKEN_MELEE_SPELL_HIT = 0x00000020, // 05 Taken damage by Spell that use melee weapon
+
+ PROC_FLAG_SUCCESSFUL_RANGED_HIT = 0x00000040, // 06 Successful Ranged attack (all ranged attack deal as spell so newer set :( )
+ PROC_FLAG_TAKEN_RANGED_HIT = 0x00000080, // 07 Taken damage from ranged attack (all ranged attack deal as spell so newer set :( )
+
+ PROC_FLAG_SUCCESSFUL_RANGED_SPELL_HIT = 0x00000100, // 08 Successful Ranged attack by Spell that use ranged weapon
+ PROC_FLAG_TAKEN_RANGED_SPELL_HIT = 0x00000200, // 09 Taken damage by Spell that use ranged weapon
+
+ PROC_FLAG_SUCCESSFUL_POSITIVE_AOE_HIT = 0x00000400, // 10 Successful AoE (not 100% shure unused)
+ PROC_FLAG_TAKEN_POSITIVE_AOE = 0x00000800, // 11 Taken AoE (not 100% shure unused)
+
+ PROC_FLAG_SUCCESSFUL_AOE_SPELL_HIT = 0x00001000, // 12 Successful AoE damage spell hit (not 100% shure unused)
+ PROC_FLAG_TAKEN_AOE_SPELL_HIT = 0x00002000, // 13 Taken AoE damage spell hit (not 100% shure unused)
+
+ PROC_FLAG_SUCCESSFUL_POSITIVE_SPELL = 0x00004000, // 14 Successful cast positive spell (by default only on healing)
+ PROC_FLAG_TAKEN_POSITIVE_SPELL = 0x00008000, // 15 Taken positive spell hit (by default only on healing)
+
+ PROC_FLAG_SUCCESSFUL_NEGATIVE_SPELL_HIT = 0x00010000, // 16 Successful negative spell cast (by default only on damage)
+ PROC_FLAG_TAKEN_NEGATIVE_SPELL_HIT = 0x00020000, // 17 Taken negative spell (by default only on damage)
+
+ PROC_FLAG_ON_DO_PERIODIC = 0x00040000, // 18 Successful do periodic (damage / healing, determined from 14-17 flags)
+ PROC_FLAG_ON_TAKE_PERIODIC = 0x00080000, // 19 Taken spell periodic (damage / healing, determined from 14-17 flags)
+
+ PROC_FLAG_TAKEN_ANY_DAMAGE = 0x00100000, // 20 Taken any damage
+ PROC_FLAG_ON_TRAP_ACTIVATION = 0x00200000, // 21 On trap activation
+
+ PROC_FLAG_TAKEN_OFFHAND_HIT = 0x00400000, // 22 Taken off-hand melee attacks(not used)
+ PROC_FLAG_SUCCESSFUL_OFFHAND_HIT = 0x00800000 // 23 Successful off-hand melee attacks
+};
+
+#define MELEE_BASED_TRIGGER_MASK (PROC_FLAG_SUCCESSFUL_MILEE_HIT | \
+ PROC_FLAG_TAKEN_MELEE_HIT | \
+ PROC_FLAG_SUCCESSFUL_MELEE_SPELL_HIT | \
+ PROC_FLAG_TAKEN_MELEE_SPELL_HIT | \
+ PROC_FLAG_SUCCESSFUL_RANGED_HIT | \
+ PROC_FLAG_TAKEN_RANGED_HIT | \
+ PROC_FLAG_SUCCESSFUL_RANGED_SPELL_HIT | \
+ PROC_FLAG_TAKEN_RANGED_SPELL_HIT)
+
+enum ProcFlagsEx
+{
+ PROC_EX_NONE = 0x0000000, // If none can tigger on Hit/Crit only (passive spells MUST defined by SpellFamily flag)
+ PROC_EX_NORMAL_HIT = 0x0000001, // If set only from normal hit (only damage spells)
+ PROC_EX_CRITICAL_HIT = 0x0000002,
+ PROC_EX_MISS = 0x0000004,
+ PROC_EX_RESIST = 0x0000008,
+ PROC_EX_DODGE = 0x0000010,
+ PROC_EX_PARRY = 0x0000020,
+ PROC_EX_BLOCK = 0x0000040,
+ PROC_EX_EVADE = 0x0000080,
+ PROC_EX_IMMUNE = 0x0000100,
+ PROC_EX_DEFLECT = 0x0000200,
+ PROC_EX_ABSORB = 0x0000400,
+ PROC_EX_REFLECT = 0x0000800,
+ PROC_EX_INTERRUPT = 0x0001000, // Melle hit result can be Interrupt (not used)
+ PROC_EX_RESERVED1 = 0x0002000,
+ PROC_EX_RESERVED2 = 0x0004000,
+ PROC_EX_RESERVED3 = 0x0008000,
+ PROC_EX_EX_TRIGGER_ALWAYS = 0x0010000, // If set trigger always ( no matter another flags) used for drop charges
+ PROC_EX_EX_ONE_TIME_TRIGGER = 0x0020000 // If set trigger always but only one time
};
struct SpellProcEventEntry
{
uint32 schoolMask; // if nonzero - bit mask for matching proc condition based on spell candidate's school: Fire=2, Mask=1<<(2-1)=2
- uint32 category; // if nonzero - match proc condition based on candidate spell's category
- uint32 skillId; // if nonzero - for matching proc condition based on candidate spell's skillId from SkillLineAbility.dbc (Shadow Bolt = Destruction)
uint32 spellFamilyName; // if nonzero - for matching proc condition based on candidate spell's SpellFamilyNamer value
uint64 spellFamilyMask; // if nonzero - for matching proc condition based on candidate spell's SpellFamilyFlags (like auras 107 and 108 do)
uint32 procFlags; // bitmask for matching proc event
+ uint32 procEx; // proc Extend info (see ProcFlagsEx)
float ppmRate; // for melee (ranged?) damage spells - proc rate per minute. if zero, falls back to flat chance from Spell.dbc
+ float customChance; // Owerride chance (in most cases for debug only)
uint32 cooldown; // hidden cooldown used for some spell proc events, applied to _triggered_spell_
};
@@ -701,7 +739,7 @@ class SpellMgr
return NULL;
}
- static bool IsSpellProcEventCanTriggeredBy( SpellProcEventEntry const * spellProcEvent, SpellEntry const * procSpell, uint32 procFlags );
+ static bool IsSpellProcEventCanTriggeredBy( SpellProcEventEntry const * spellProcEvent, uint32 EventProcFlag, SpellEntry const * procSpell, uint32 procFlags, uint32 procExtra, bool active);
// Spell target coordinates
SpellTargetPosition const* GetSpellTargetPosition(uint32 spell_id) const
diff --git a/src/game/Unit.cpp b/src/game/Unit.cpp
index 571ff991147..c40f37ec48f 100644
--- a/src/game/Unit.cpp
+++ b/src/game/Unit.cpp
@@ -61,6 +61,8 @@ float baseMoveSpeed[MAX_MOVE_TYPE] =
4.5f, // MOVE_FLYBACK
};
+void InitTriggerAuraData();
+
// auraTypes contains attacker auras capable of proc'ing cast auras
static Unit::AuraTypeSet GenerateAttakerProcCastAuraTypes()
{
@@ -114,6 +116,8 @@ static Unit::AuraTypeSet victimProcEffectAuraTypes = GenerateVictimProcEffectA
// auraTypes contains auras capable of proc'ing for attacker and victim
static Unit::AuraTypeSet GenerateProcAuraTypes()
{
+ InitTriggerAuraData();
+
Unit::AuraTypeSet auraTypes;
auraTypes.insert(attackerProcCastAuraTypes.begin(),attackerProcCastAuraTypes.end());
auraTypes.insert(attackerProcEffectAuraTypes.begin(),attackerProcEffectAuraTypes.end());
@@ -675,8 +679,12 @@ uint32 Unit::DealDamage(Unit *pVictim, uint32 damage, CleanDamage const* cleanDa
// Reward player, his pets, and group/raid members
// call kill spell proc event (before real die and combat stop to triggering auras removed at death/combat stop)
if(player && player!=pVictim)
+ {
if(player->RewardPlayerAndGroupAtKill(pVictim))
- player->ProcDamageAndSpell(pVictim,PROC_FLAG_KILL_XP_GIVER,PROC_FLAG_NONE);
+ player->ProcDamageAndSpell(pVictim, PROC_FLAG_KILL_AND_GET_XP, PROC_FLAG_KILLED, PROC_EX_NONE, 0);
+ else
+ player->ProcDamageAndSpell(pVictim, PROC_FLAG_NONE, PROC_FLAG_KILLED,PROC_EX_NONE, 0);
+ }
DEBUG_LOG("DealDamageAttackStop");
@@ -847,18 +855,6 @@ uint32 Unit::DealDamage(Unit *pVictim, uint32 damage, CleanDamage const* cleanDa
pVictim->ModifyHealth(- (int32)damage);
- // Check if health is below 20% (apply damage before to prevent case when after ProcDamageAndSpell health < damage
- if(pVictim->GetHealth()*5 < pVictim->GetMaxHealth())
- {
- uint32 procVictim = PROC_FLAG_NONE;
-
- // if just dropped below 20% (for CheatDeath)
- if((pVictim->GetHealth()+damage)*5 > pVictim->GetMaxHealth())
- procVictim = PROC_FLAG_LOW_HEALTH;
-
- ProcDamageAndSpell(pVictim,PROC_FLAG_TARGET_LOW_HEALTH,procVictim);
- }
-
if(damagetype != DOT)
{
if(getVictim())
@@ -875,8 +871,12 @@ uint32 Unit::DealDamage(Unit *pVictim, uint32 damage, CleanDamage const* cleanDa
}
}
- if(damagetype == DIRECT_DAMAGE|| damagetype == SPELL_DIRECT_DAMAGE)
- pVictim->RemoveAurasWithInterruptFlags(AURA_INTERRUPT_FLAG_DIRECT_DAMAGE);
+ if(damagetype == DIRECT_DAMAGE || damagetype == SPELL_DIRECT_DAMAGE)
+ {
+ //TODO: This is from procflag, I do not know which spell needs this
+ //if (!spellProto || !(spellProto->AuraInterruptFlags&AURA_INTERRUPT_FLAG_DIRECT_DAMAGE))
+ pVictim->RemoveAurasWithInterruptFlags(AURA_INTERRUPT_FLAG_DIRECT_DAMAGE);
+ }
if (pVictim->GetTypeId() != TYPEID_PLAYER)
{
@@ -914,10 +914,30 @@ uint32 Unit::DealDamage(Unit *pVictim, uint32 damage, CleanDamage const* cleanDa
if (damagetype != NODAMAGE && damage)// && pVictim->GetTypeId() == TYPEID_PLAYER)
{
- //if (se->procFlags & (1<<3))
pVictim->RemoveAurasWithInterruptFlags(AURA_INTERRUPT_FLAG_DAMAGE);
pVictim->RemoveSpellbyDamageTaken(damage, spellProto ? spellProto->Id : 0);
+ /*const SpellEntry *se = i->second->GetSpellProto();
+ next = i; ++next;
+ if (spellProto && spellProto->Id == se->Id) // Not drop auras added by self
+ continue;
+ if( se->AuraInterruptFlags & AURA_INTERRUPT_FLAG_DAMAGE )
+ {
+ bool remove = true;
+ if (se->procFlags & (1<<3))
+ {
+ if (!roll_chance_i(se->procChance))
+ remove = false;
+ }
+ if (remove)
+ {
+ pVictim->RemoveAurasDueToSpell(i->second->GetId());
+ // FIXME: this may cause the auras with proc chance to be rerolled several times
+ next = vAuras.begin();
+ }
+ }
+ }*/
+
if(pVictim != this && pVictim->GetTypeId() == TYPEID_PLAYER) // does not support creature push_back
{
if(damagetype != DOT)
@@ -1095,6 +1115,7 @@ void Unit::CastSpell(float x, float y, float z, SpellEntry const *spellInfo, boo
spell->prepare(&targets, triggeredByAura);
}
+/*
void Unit::DealFlatDamage(Unit *pVictim, SpellEntry const *spellInfo, uint32 *damage, CleanDamage *cleanDamage, bool *crit, bool isTriggeredSpell)
{
// TODO this in only generic way, check for exceptions
@@ -1505,6 +1526,577 @@ uint32 Unit::SpellNonMeleeDamageLog(Unit *pVictim, uint32 spellID, uint32 damage
return 0;
}
}
+*/
+
+// Obsolete func need remove, here only for comotability vs another patches
+uint32 Unit::SpellNonMeleeDamageLog(Unit *pVictim, uint32 spellID, uint32 damage, bool isTriggeredSpell, bool useSpellDamage)
+{
+ SpellEntry const *spellInfo = sSpellStore.LookupEntry(spellID);
+ SpellNonMeleeDamage damageInfo(this, pVictim, spellInfo->Id, spellInfo->SchoolMask);
+ CalculateSpellDamage(&damageInfo, damage, spellInfo);
+ SendSpellNonMeleeDamageLog(&damageInfo);
+ DealSpellDamage(&damageInfo, true);
+ return damageInfo.damage;
+}
+
+void Unit::CalculateSpellDamage(SpellNonMeleeDamage *damageInfo, int32 damage, SpellEntry const *spellInfo, WeaponAttackType attackType)
+{
+ SpellSchoolMask damageSchoolMask = SpellSchoolMask(damageInfo->schoolMask);
+ Unit *pVictim = damageInfo->target;
+
+ if (damage < 0)
+ return;
+
+ if(!this || !pVictim)
+ return;
+ if(!this->isAlive() || !pVictim->isAlive())
+ return;
+
+ uint32 crTypeMask = pVictim->GetCreatureTypeMask();
+ // Check spell crit chance
+ bool crit = isSpellCrit(pVictim, spellInfo, damageSchoolMask, attackType);
+ bool blocked = false;
+ // Per-school calc
+ switch (spellInfo->DmgClass)
+ {
+ // Melee and Ranged Spells
+ case SPELL_DAMAGE_CLASS_RANGED:
+ case SPELL_DAMAGE_CLASS_MELEE:
+ {
+ // Physical Damage
+ if ( damageSchoolMask & SPELL_SCHOOL_MASK_NORMAL )
+ {
+ //Calculate armor mitigation
+ damage = CalcArmorReducedDamage(pVictim, damage);
+ // Get blocked status
+ blocked = isSpellBlocked(pVictim, spellInfo, attackType);
+ }
+ // Magical Damage
+ else
+ {
+ // Calculate damage bonus
+ damage = SpellDamageBonus(pVictim, spellInfo, damage, SPELL_DIRECT_DAMAGE);
+ }
+ if (crit)
+ {
+ damageInfo->HitInfo|= SPELL_HIT_TYPE_CRIT;
+
+ // Calculate crit bonus
+ uint32 crit_bonus = damage;
+ // Apply crit_damage bonus for melee spells
+ if(Player* modOwner = GetSpellModOwner())
+ modOwner->ApplySpellMod(spellInfo->Id, SPELLMOD_CRIT_DAMAGE_BONUS, crit_bonus);
+ damage += crit_bonus;
+
+ // Apply SPELL_AURA_MOD_ATTACKER_RANGED_CRIT_DAMAGE or SPELL_AURA_MOD_ATTACKER_MELEE_CRIT_DAMAGE
+ int32 critPctDamageMod=0;
+ if(attackType == RANGED_ATTACK)
+ critPctDamageMod += pVictim->GetTotalAuraModifier(SPELL_AURA_MOD_ATTACKER_RANGED_CRIT_DAMAGE);
+ else
+ {
+ critPctDamageMod += pVictim->GetTotalAuraModifier(SPELL_AURA_MOD_ATTACKER_MELEE_CRIT_DAMAGE);
+ critPctDamageMod += GetTotalAuraModifier(SPELL_AURA_MOD_CRIT_DAMAGE_BONUS_MELEE);
+ }
+ // Increase crit damage from SPELL_AURA_MOD_CRIT_PERCENT_VERSUS
+ critPctDamageMod += GetTotalAuraModifierByMiscMask(SPELL_AURA_MOD_CRIT_PERCENT_VERSUS, crTypeMask);
+
+ if (critPctDamageMod!=0)
+ damage = int32((damage) * float((100.0f + critPctDamageMod)/100.0f));
+
+ // Resilience - reduce crit damage
+ if (pVictim->GetTypeId()==TYPEID_PLAYER)
+ damage -= ((Player*)pVictim)->GetMeleeCritDamageReduction(damage);
+ }
+ // Spell weapon based damage CAN BE crit & blocked at same time
+ if (blocked)
+ {
+ damageInfo->blocked = uint32(pVictim->GetShieldBlockValue());
+ if (damage < damageInfo->blocked)
+ damageInfo->blocked = damage;
+ damage-=damageInfo->blocked;
+ }
+ }
+ break;
+ // Magical Attacks
+ case SPELL_DAMAGE_CLASS_NONE:
+ case SPELL_DAMAGE_CLASS_MAGIC:
+ {
+ // Calculate damage bonus
+ damage = SpellDamageBonus(pVictim, spellInfo, damage, SPELL_DIRECT_DAMAGE);
+ // If crit add critical bonus
+ if (crit)
+ {
+ damageInfo->HitInfo|= SPELL_HIT_TYPE_CRIT;
+ damage = SpellCriticalBonus(spellInfo, damage, pVictim);
+ // Resilience - reduce crit damage
+ if (pVictim->GetTypeId()==TYPEID_PLAYER)
+ damage -= ((Player*)pVictim)->GetSpellCritDamageReduction(damage);
+ }
+ }
+ break;
+ }
+
+ // Calculate absorb resist
+ if(damage > 0)
+ {
+ CalcAbsorbResist(pVictim, damageSchoolMask, SPELL_DIRECT_DAMAGE, damage, &damageInfo->absorb, &damageInfo->resist);
+ damage-= damageInfo->absorb + damageInfo->resist;
+ }
+ else
+ damage = 0;
+ damageInfo->damage = damage;
+}
+
+void Unit::DealSpellDamage(SpellNonMeleeDamage *damageInfo, bool durabilityLoss)
+{
+ if (damageInfo==0)
+ return;
+
+ Unit *pVictim = damageInfo->target;
+
+ if(!this || !pVictim)
+ return;
+
+ if (!pVictim->isAlive() || pVictim->isInFlight() || pVictim->GetTypeId() == TYPEID_UNIT && ((Creature*)pVictim)->IsInEvadeMode())
+ return;
+
+ SpellEntry const *spellProto = sSpellStore.LookupEntry(damageInfo->SpellID);
+ if (spellProto == NULL)
+ {
+ sLog.outDebug("Unit::DealSpellDamage have wrong damageInfo->SpellID: %u", damageInfo->SpellID);
+ return;
+ }
+
+ //You don't lose health from damage taken from another player while in a sanctuary
+ //You still see it in the combat log though
+ if(pVictim != this && GetTypeId() == TYPEID_PLAYER && pVictim->GetTypeId() == TYPEID_PLAYER)
+ {
+ const AreaTableEntry *area = GetAreaEntryByAreaID(pVictim->GetAreaId());
+ if(area && area->flags & 0x800) //sanctuary
+ return;
+ }
+
+ // update at damage Judgement aura duration that applied by attacker at victim
+ if(damageInfo->damage && spellProto->Id == 35395)
+ {
+ AuraMap& vAuras = pVictim->GetAuras();
+ for(AuraMap::iterator itr = vAuras.begin(); itr != vAuras.end(); ++itr)
+ {
+ SpellEntry const *spellInfo = (*itr).second->GetSpellProto();
+ if( spellInfo->AttributesEx3 & 0x40000 && spellInfo->SpellFamilyName == SPELLFAMILY_PALADIN && ((*itr).second->GetCasterGUID() == GetGUID()))
+ {
+ (*itr).second->SetAuraDuration((*itr).second->GetAuraMaxDuration());
+ (*itr).second->UpdateAuraDuration();
+ }
+ }
+ }
+ // Call default DealDamage
+ CleanDamage cleanDamage(damageInfo->cleanDamage, BASE_ATTACK, MELEE_HIT_NORMAL);
+ DealDamage(pVictim, damageInfo->damage, &cleanDamage, SPELL_DIRECT_DAMAGE, SpellSchoolMask(damageInfo->schoolMask), spellProto, durabilityLoss);
+}
+
+//TODO for melee need create structure as in
+void Unit::CalculateMeleeDamage(Unit *pVictim, uint32 damage, CalcDamageInfo *damageInfo, WeaponAttackType attackType)
+{
+ damageInfo->attacker = this;
+ damageInfo->target = pVictim;
+ damageInfo->damageSchoolMask = GetMeleeDamageSchoolMask();
+ damageInfo->attackType = attackType;
+ damageInfo->damage = 0;
+ damageInfo->cleanDamage = 0;
+ damageInfo->absorb = 0;
+ damageInfo->resist = 0;
+ damageInfo->blocked_amount = 0;
+
+ damageInfo->TargetState = 0;
+ damageInfo->HitInfo = 0;
+ damageInfo->procAttacker = PROC_FLAG_NONE;
+ damageInfo->procVictim = PROC_FLAG_NONE;
+ damageInfo->procEx = PROC_EX_NONE;
+ damageInfo->hitOutCome = MELEE_HIT_EVADE;
+
+ if(!this || !pVictim)
+ return;
+ if(!this->isAlive() || !pVictim->isAlive())
+ return;
+
+ // Select HitInfo/procAttacker/procVictim flag based on attack type
+ switch (attackType)
+ {
+ case BASE_ATTACK:
+ damageInfo->procAttacker = PROC_FLAG_SUCCESSFUL_MILEE_HIT;
+ damageInfo->procVictim = PROC_FLAG_TAKEN_MELEE_HIT;
+ damageInfo->HitInfo = HITINFO_NORMALSWING2;
+ break;
+ case OFF_ATTACK:
+ damageInfo->procAttacker = PROC_FLAG_SUCCESSFUL_MILEE_HIT | PROC_FLAG_SUCCESSFUL_OFFHAND_HIT;
+ damageInfo->procVictim = PROC_FLAG_TAKEN_MELEE_HIT;//|PROC_FLAG_TAKEN_OFFHAND_HIT // not used
+ damageInfo->HitInfo = HITINFO_LEFTSWING;
+ break;
+ case RANGED_ATTACK:
+ damageInfo->procAttacker = PROC_FLAG_SUCCESSFUL_RANGED_HIT;
+ damageInfo->procVictim = PROC_FLAG_TAKEN_RANGED_HIT;
+ damageInfo->HitInfo = 0x08;// test
+ break;
+ default:
+ break;
+ }
+
+ // Physical Immune check
+ if(damageInfo->target->IsImmunedToDamage(SpellSchoolMask(damageInfo->damageSchoolMask),true))
+ {
+ damageInfo->HitInfo |= HITINFO_NORMALSWING;
+ damageInfo->TargetState = VICTIMSTATE_IS_IMMUNE;
+
+ damageInfo->procEx |=PROC_EX_IMMUNE;
+ damageInfo->damage = 0;
+ damageInfo->cleanDamage = 0;
+ return;
+ }
+ damage += CalculateDamage (damageInfo->attackType, false);
+ // Add melee damage bonus
+ MeleeDamageBonus(damageInfo->target, &damage, damageInfo->attackType);
+ // Calculate armor reduction
+ damageInfo->damage = CalcArmorReducedDamage(damageInfo->target, damage);
+ damageInfo->cleanDamage += damage - damageInfo->damage;
+
+ damageInfo->hitOutCome = RollMeleeOutcomeAgainst(damageInfo->target, damageInfo->attackType);
+
+ // Disable parry or dodge for ranged attack
+ if(damageInfo->attackType == RANGED_ATTACK)
+ {
+ if (damageInfo->hitOutCome == MELEE_HIT_PARRY) damageInfo->hitOutCome = MELEE_HIT_NORMAL;
+ if (damageInfo->hitOutCome == MELEE_HIT_DODGE) damageInfo->hitOutCome = MELEE_HIT_MISS;
+ }
+
+ switch(damageInfo->hitOutCome)
+ {
+ case MELEE_HIT_EVADE:
+ {
+ damageInfo->HitInfo |= HITINFO_MISS|HITINFO_SWINGNOHITSOUND;
+ damageInfo->TargetState = VICTIMSTATE_EVADES;
+
+ damageInfo->procEx|=PROC_EX_EVADE;
+ damageInfo->damage = 0;
+ damageInfo->cleanDamage = 0;
+ return;
+ }
+ case MELEE_HIT_MISS:
+ {
+ damageInfo->HitInfo |= HITINFO_MISS;
+ damageInfo->TargetState = VICTIMSTATE_NORMAL;
+
+ damageInfo->procEx|=PROC_EX_MISS;
+ damageInfo->damage = 0;
+ damageInfo->cleanDamage = 0;
+ break;
+ }
+ case MELEE_HIT_NORMAL:
+ damageInfo->TargetState = VICTIMSTATE_NORMAL;
+ damageInfo->procEx|=PROC_EX_NORMAL_HIT;
+ break;
+ case MELEE_HIT_CRIT:
+ {
+ damageInfo->HitInfo |= HITINFO_CRITICALHIT;
+ damageInfo->TargetState = VICTIMSTATE_NORMAL;
+
+ damageInfo->procEx|=PROC_EX_CRITICAL_HIT;
+ // Crit bonus calc
+ damageInfo->damage += damageInfo->damage;
+ int32 mod=0;
+ // Apply SPELL_AURA_MOD_ATTACKER_RANGED_CRIT_DAMAGE or SPELL_AURA_MOD_ATTACKER_MELEE_CRIT_DAMAGE
+ if(damageInfo->attackType == RANGED_ATTACK)
+ mod += damageInfo->target->GetTotalAuraModifier(SPELL_AURA_MOD_ATTACKER_RANGED_CRIT_DAMAGE);
+ else
+ {
+ mod += damageInfo->target->GetTotalAuraModifier(SPELL_AURA_MOD_ATTACKER_MELEE_CRIT_DAMAGE);
+ mod += GetTotalAuraModifier(SPELL_AURA_MOD_CRIT_DAMAGE_BONUS_MELEE);
+ }
+
+ uint32 crTypeMask = damageInfo->target->GetCreatureTypeMask();
+
+ // Increase crit damage from SPELL_AURA_MOD_CRIT_PERCENT_VERSUS
+ mod += GetTotalAuraModifierByMiscMask(SPELL_AURA_MOD_CRIT_PERCENT_VERSUS, crTypeMask);
+ if (mod!=0)
+ damageInfo->damage = int32((damageInfo->damage) * float((100.0f + mod)/100.0f));
+
+ // Resilience - reduce crit damage
+ if (pVictim->GetTypeId()==TYPEID_PLAYER)
+ {
+ uint32 resilienceReduction = ((Player*)pVictim)->GetMeleeCritDamageReduction(damageInfo->damage);
+ damageInfo->damage -= resilienceReduction;
+ damageInfo->cleanDamage += resilienceReduction;
+ }
+ break;
+ }
+ case MELEE_HIT_PARRY:
+ damageInfo->TargetState = VICTIMSTATE_PARRY;
+ damageInfo->procEx|=PROC_EX_PARRY;
+ damageInfo->cleanDamage += damageInfo->damage;
+ damageInfo->damage = 0;
+ break;
+
+ case MELEE_HIT_DODGE:
+ damageInfo->TargetState = VICTIMSTATE_DODGE;
+ damageInfo->procEx|=PROC_EX_DODGE;
+ damageInfo->cleanDamage += damageInfo->damage;
+ damageInfo->damage = 0;
+ break;
+ case MELEE_HIT_BLOCK:
+ {
+ damageInfo->TargetState = VICTIMSTATE_NORMAL;
+ damageInfo->procEx|=PROC_EX_BLOCK;
+ damageInfo->blocked_amount = damageInfo->target->GetShieldBlockValue();
+ if (damageInfo->blocked_amount >= damageInfo->damage)
+ {
+ damageInfo->TargetState = VICTIMSTATE_BLOCKS;
+ damageInfo->blocked_amount = damageInfo->damage;
+ }
+ damageInfo->damage -= damageInfo->blocked_amount;
+ damageInfo->cleanDamage += damageInfo->blocked_amount;
+ break;
+ }
+ case MELEE_HIT_GLANCING:
+ {
+ damageInfo->HitInfo |= HITINFO_GLANCING;
+ damageInfo->TargetState = VICTIMSTATE_NORMAL;
+ damageInfo->procEx|=PROC_EX_NORMAL_HIT;
+ float reducePercent = 1.0f; //damage factor
+ // calculate base values and mods
+ float baseLowEnd = 1.3;
+ float baseHighEnd = 1.2;
+ switch(getClass()) // lowering base values for casters
+ {
+ case CLASS_SHAMAN:
+ case CLASS_PRIEST:
+ case CLASS_MAGE:
+ case CLASS_WARLOCK:
+ case CLASS_DRUID:
+ baseLowEnd -= 0.7;
+ baseHighEnd -= 0.3;
+ break;
+ }
+
+ float maxLowEnd = 0.6;
+ switch(getClass()) // upper for melee classes
+ {
+ case CLASS_WARRIOR:
+ case CLASS_ROGUE:
+ maxLowEnd = 0.91; //If the attacker is a melee class then instead the lower value of 0.91
+ }
+
+ // calculate values
+ int32 diff = damageInfo->target->GetDefenseSkillValue() - GetWeaponSkillValue(damageInfo->attackType);
+ float lowEnd = baseLowEnd - ( 0.05f * diff );
+ float highEnd = baseHighEnd - ( 0.03f * diff );
+
+ // apply max/min bounds
+ if ( lowEnd < 0.01f ) //the low end must not go bellow 0.01f
+ lowEnd = 0.01f;
+ else if ( lowEnd > maxLowEnd ) //the smaller value of this and 0.6 is kept as the low end
+ lowEnd = maxLowEnd;
+
+ if ( highEnd < 0.2f ) //high end limits
+ highEnd = 0.2f;
+ if ( highEnd > 0.99f )
+ highEnd = 0.99f;
+
+ if(lowEnd > highEnd) // prevent negative range size
+ lowEnd = highEnd;
+
+ reducePercent = lowEnd + rand_norm() * ( highEnd - lowEnd );
+
+ damageInfo->cleanDamage += damageInfo->damage-uint32(reducePercent * damageInfo->damage);
+ damageInfo->damage = uint32(reducePercent * damageInfo->damage);
+ break;
+ }
+ case MELEE_HIT_CRUSHING:
+ {
+ damageInfo->HitInfo |= HITINFO_CRUSHING;
+ damageInfo->TargetState = VICTIMSTATE_NORMAL;
+ damageInfo->procEx|=PROC_EX_NORMAL_HIT;
+ // 150% normal damage
+ damageInfo->damage += (damageInfo->damage / 2);
+ break;
+ }
+ default:
+
+ break;
+ }
+
+ // Calculate absorb resist
+ if(int32(damageInfo->damage) > 0)
+ {
+ damageInfo->procVictim |= PROC_FLAG_TAKEN_ANY_DAMAGE;
+ // Calculate absorb & resists
+ CalcAbsorbResist(damageInfo->target, SpellSchoolMask(damageInfo->damageSchoolMask), DIRECT_DAMAGE, damageInfo->damage, &damageInfo->absorb, &damageInfo->resist);
+ damageInfo->damage-=damageInfo->absorb + damageInfo->resist;
+ if (damageInfo->absorb)
+ {
+ damageInfo->HitInfo|=HITINFO_ABSORB;
+ damageInfo->procEx|=PROC_EX_ABSORB;
+ }
+ if (damageInfo->resist)
+ damageInfo->HitInfo|=HITINFO_RESIST;
+
+ }
+ else // Umpossible get negative result but....
+ damageInfo->damage = 0;
+}
+
+void Unit::DealMeleeDamage(CalcDamageInfo *damageInfo, bool durabilityLoss)
+{
+ if (damageInfo==0) return;
+ Unit *pVictim = damageInfo->target;
+
+ if(!this || !pVictim)
+ return;
+
+ if (!pVictim->isAlive() || pVictim->isInFlight() || pVictim->GetTypeId() == TYPEID_UNIT && ((Creature*)pVictim)->IsInEvadeMode())
+ return;
+
+ //You don't lose health from damage taken from another player while in a sanctuary
+ //You still see it in the combat log though
+ if(pVictim != this && GetTypeId() == TYPEID_PLAYER && pVictim->GetTypeId() == TYPEID_PLAYER)
+ {
+ const AreaTableEntry *area = GetAreaEntryByAreaID(pVictim->GetAreaId());
+ if(area && area->flags & 0x800) //sanctuary
+ return;
+ }
+
+ // Hmmmm dont like this emotes cloent must by self do all animations
+ if (damageInfo->HitInfo&HITINFO_CRITICALHIT)
+ pVictim->HandleEmoteCommand(EMOTE_ONESHOT_WOUNDCRITICAL);
+ if(damageInfo->blocked_amount && damageInfo->TargetState!=VICTIMSTATE_BLOCKS)
+ pVictim->HandleEmoteCommand(EMOTE_ONESHOT_PARRYSHIELD);
+
+ if(damageInfo->TargetState == VICTIMSTATE_PARRY)
+ {
+ // Get attack timers
+ float offtime = float(pVictim->getAttackTimer(OFF_ATTACK));
+ float basetime = float(pVictim->getAttackTimer(BASE_ATTACK));
+ // Reduce attack time
+ if (pVictim->haveOffhandWeapon() && offtime < basetime)
+ {
+ float percent20 = pVictim->GetAttackTime(OFF_ATTACK) * 0.20;
+ float percent60 = 3 * percent20;
+ if(offtime > percent20 && offtime <= percent60)
+ {
+ pVictim->setAttackTimer(OFF_ATTACK, uint32(percent20));
+ }
+ else if(offtime > percent60)
+ {
+ offtime -= 2 * percent20;
+ pVictim->setAttackTimer(OFF_ATTACK, uint32(offtime));
+ }
+ }
+ else
+ {
+ float percent20 = pVictim->GetAttackTime(BASE_ATTACK) * 0.20;
+ float percent60 = 3 * percent20;
+ if(basetime > percent20 && basetime <= percent60)
+ {
+ pVictim->setAttackTimer(BASE_ATTACK, uint32(percent20));
+ }
+ else if(basetime > percent60)
+ {
+ basetime -= 2 * percent20;
+ pVictim->setAttackTimer(BASE_ATTACK, uint32(basetime));
+ }
+ }
+ }
+
+ // Call default DealDamage
+ CleanDamage cleanDamage(damageInfo->cleanDamage,damageInfo->attackType,damageInfo->hitOutCome);
+ DealDamage(pVictim, damageInfo->damage, &cleanDamage, DIRECT_DAMAGE, SpellSchoolMask(damageInfo->damageSchoolMask), NULL, durabilityLoss);
+
+ // If this is a creature and it attacks from behind it has a probability to daze it's victim
+ if( (damageInfo->hitOutCome==MELEE_HIT_CRIT || damageInfo->hitOutCome==MELEE_HIT_CRUSHING || damageInfo->hitOutCome==MELEE_HIT_NORMAL || damageInfo->hitOutCome==MELEE_HIT_GLANCING) &&
+ GetTypeId() != TYPEID_PLAYER && !((Creature*)this)->GetCharmerOrOwnerGUID() && !pVictim->HasInArc(M_PI, this) )
+ {
+ // -probability is between 0% and 40%
+ // 20% base chance
+ float Probability = 20;
+
+ //there is a newbie protection, at level 10 just 7% base chance; assuming linear function
+ if( pVictim->getLevel() < 30 )
+ Probability = 0.65f*pVictim->getLevel()+0.5;
+
+ uint32 VictimDefense=pVictim->GetDefenseSkillValue();
+ uint32 AttackerMeleeSkill=GetUnitMeleeSkill();
+
+ Probability *= AttackerMeleeSkill/(float)VictimDefense;
+
+ if(Probability > 40)
+ Probability = 40;
+
+ if(roll_chance_f(Probability))
+ CastSpell(pVictim, 1604, true);
+ }
+
+ // update at damage Judgement aura duration that applied by attacker at victim
+ if(damageInfo->damage)
+ {
+ AuraMap& vAuras = pVictim->GetAuras();
+ for(AuraMap::iterator itr = vAuras.begin(); itr != vAuras.end(); ++itr)
+ {
+ SpellEntry const *spellInfo = (*itr).second->GetSpellProto();
+ if( spellInfo->AttributesEx3 & 0x40000 && spellInfo->SpellFamilyName == SPELLFAMILY_PALADIN && ((*itr).second->GetCasterGUID() == GetGUID()))
+ {
+ (*itr).second->SetAuraDuration((*itr).second->GetAuraMaxDuration());
+ (*itr).second->UpdateAuraDuration();
+ }
+ }
+ }
+
+ // If not miss
+ if (!(damageInfo->HitInfo & HITINFO_MISS))
+ {
+ if(GetTypeId() == TYPEID_PLAYER && pVictim->isAlive())
+ {
+ for(int i = EQUIPMENT_SLOT_START; i < EQUIPMENT_SLOT_END; i++)
+ ((Player*)this)->CastItemCombatSpell(((Player*)this)->GetItemByPos(INVENTORY_SLOT_BAG_0,i), pVictim, damageInfo->attackType);
+ }
+
+ // victim's damage shield
+ std::set<Aura*> alreadyDone;
+ uint32 removedAuras = pVictim->m_removedAuras;
+ AuraList const& vDamageShields = pVictim->GetAurasByType(SPELL_AURA_DAMAGE_SHIELD);
+ for(AuraList::const_iterator i = vDamageShields.begin(), next = vDamageShields.begin(); i != vDamageShields.end(); i = next)
+ {
+ next++;
+ if (alreadyDone.find(*i) == alreadyDone.end())
+ {
+ alreadyDone.insert(*i);
+ uint32 damage=(*i)->GetModifier()->m_amount;
+ SpellEntry const *spellProto = sSpellStore.LookupEntry((*i)->GetId());
+ if(!spellProto)
+ continue;
+ //Calculate absorb resist ??? no data in opcode for this possibly unable to absorb or resist?
+ //uint32 absorb;
+ //uint32 resist;
+ //CalcAbsorbResist(pVictim, SpellSchools(spellProto->School), SPELL_DIRECT_DAMAGE, damage, &absorb, &resist);
+ //damage-=absorb + resist;
+
+ WorldPacket data(SMSG_SPELLDAMAGESHIELD,(8+8+4+4));
+ data << uint64(pVictim->GetGUID());
+ data << uint64(GetGUID());
+ data << uint32(spellProto->SchoolMask);
+ data << uint32(damage);
+ pVictim->SendMessageToSet(&data, true );
+
+ pVictim->DealDamage(this, damage, 0, SPELL_DIRECT_DAMAGE, GetSpellSchoolMask(spellProto), spellProto, true);
+
+ if (pVictim->m_removedAuras > removedAuras)
+ {
+ removedAuras = pVictim->m_removedAuras;
+ next = vDamageShields.begin();
+ }
+ }
+ }
+ }
+}
+
void Unit::HandleEmoteCommand(uint32 anim_id)
{
@@ -1763,6 +2355,7 @@ void Unit::CalcAbsorbResist(Unit *pVictim,SpellSchoolMask schoolMask, DamageEffe
*absorb = damage - RemainingDamage - *resist;
}
+/*
void Unit::DoAttackDamage (Unit *pVictim, uint32 *damage, CleanDamage *cleanDamage, uint32 *blocked_amount, SpellSchoolMask damageSchoolMask, uint32 *hitInfo, VictimState *victimState, uint32 *absorbDamage, uint32 *resistDamage, WeaponAttackType attType, SpellEntry const *spellCasted, bool isTriggeredSpell)
{
MeleeHitOutcome outcome;
@@ -2163,7 +2756,7 @@ void Unit::DoAttackDamage (Unit *pVictim, uint32 *damage, CleanDamage *cleanDama
}
}
}
-}
+}*/
void Unit::AttackerStateUpdate (Unit *pVictim, WeaponAttackType attType, bool extra )
{
@@ -2207,65 +2800,19 @@ void Unit::AttackerStateUpdate (Unit *pVictim, WeaponAttackType attType, bool ex
return;
}
- VictimState victimState = VICTIMSTATE_NORMAL;
-
- CleanDamage cleanDamage = CleanDamage(0, BASE_ATTACK, MELEE_HIT_NORMAL );
- uint32 blocked_dmg = 0;
- uint32 absorbed_dmg = 0;
- uint32 resisted_dmg = 0;
-
- SpellSchoolMask meleeSchoolMask = GetMeleeDamageSchoolMask();
-
- if(pVictim->IsImmunedToDamage(meleeSchoolMask,true)) // use charges
- {
- SendAttackStateUpdate (HITINFO_NORMALSWING, pVictim, 1, meleeSchoolMask, 0, 0, 0, VICTIMSTATE_IS_IMMUNE, 0);
-
- // not recent extra attack only at any non extra attack (miss case)
- if(!extra && extraAttacks)
- {
- while(m_extraAttacks)
- {
- AttackerStateUpdate(pVictim, BASE_ATTACK, true);
- if(m_extraAttacks > 0)
- --m_extraAttacks;
- }
- }
-
- return;
- }
-
- uint32 damage = CalculateDamage (attType, false);
-
- DoAttackDamage (pVictim, &damage, &cleanDamage, &blocked_dmg, meleeSchoolMask, &hitInfo, &victimState, &absorbed_dmg, &resisted_dmg, attType);
-
- if (hitInfo & HITINFO_MISS)
- //send miss
- SendAttackStateUpdate (hitInfo, pVictim, 1, meleeSchoolMask, damage, absorbed_dmg, resisted_dmg, victimState, blocked_dmg);
- else
- {
- //do animation
- SendAttackStateUpdate (hitInfo, pVictim, 1, meleeSchoolMask, damage, absorbed_dmg, resisted_dmg, victimState, blocked_dmg);
-
- if (damage > (absorbed_dmg + resisted_dmg + blocked_dmg))
- damage -= (absorbed_dmg + resisted_dmg + blocked_dmg);
- else
- damage = 0;
-
- DealDamage (pVictim, damage, &cleanDamage, DIRECT_DAMAGE, meleeSchoolMask, NULL, true);
-
- if(GetTypeId() == TYPEID_PLAYER && pVictim->isAlive())
- {
- for(int i = EQUIPMENT_SLOT_START; i < EQUIPMENT_SLOT_END; i++)
- ((Player*)this)->CastItemCombatSpell(((Player*)this)->GetItemByPos(INVENTORY_SLOT_BAG_0,i),pVictim,attType);
- }
- }
+ CalcDamageInfo damageInfo;
+ CalculateMeleeDamage(pVictim, 0, &damageInfo, attType);
+ // Send log damage message to client
+ SendAttackStateUpdate(&damageInfo);
+ ProcDamageAndSpell(damageInfo.target, damageInfo.procAttacker, damageInfo.procVictim, damageInfo.procEx, damageInfo.damage, damageInfo.attackType);
+ DealMeleeDamage(&damageInfo,true);
if (GetTypeId() == TYPEID_PLAYER)
DEBUG_LOG("AttackerStateUpdate: (Player) %u attacked %u (TypeId: %u) for %u dmg, absorbed %u, blocked %u, resisted %u.",
- GetGUIDLow(), pVictim->GetGUIDLow(), pVictim->GetTypeId(), damage, absorbed_dmg, blocked_dmg, resisted_dmg);
+ GetGUIDLow(), pVictim->GetGUIDLow(), pVictim->GetTypeId(), damageInfo.damage, damageInfo.absorb, damageInfo.blocked_amount, damageInfo.resist);
else
DEBUG_LOG("AttackerStateUpdate: (NPC) %u attacked %u (TypeId: %u) for %u dmg, absorbed %u, blocked %u, resisted %u.",
- GetGUIDLow(), pVictim->GetGUIDLow(), pVictim->GetTypeId(), damage, absorbed_dmg, blocked_dmg, resisted_dmg);
+ GetGUIDLow(), pVictim->GetGUIDLow(), pVictim->GetTypeId(), damageInfo.damage, damageInfo.absorb, damageInfo.blocked_amount, damageInfo.resist);
// extra attack only at any non extra attack (normal case)
if(!extra && extraAttacks)
@@ -2279,6 +2826,7 @@ void Unit::AttackerStateUpdate (Unit *pVictim, WeaponAttackType attType, bool ex
}
}
+/*
MeleeHitOutcome Unit::RollPhysicalOutcomeAgainst (Unit const *pVictim, WeaponAttackType attType, SpellEntry const *spellInfo)
{
// Miss chance based on melee
@@ -2337,7 +2885,7 @@ MeleeHitOutcome Unit::RollPhysicalOutcomeAgainst (Unit const *pVictim, WeaponAtt
DEBUG_LOG("PHYSICAL OUTCOME: miss %f crit %f dodge %f parry %f block %f",miss_chance,crit_chance,dodge_chance,parry_chance, block_chance);
return RollMeleeOutcomeAgainst(pVictim, attType, int32(crit_chance*100), int32(miss_chance*100),int32(dodge_chance*100),int32(parry_chance*100),int32(block_chance*100), true);
-}
+}*/
MeleeHitOutcome Unit::RollMeleeOutcomeAgainst(const Unit *pVictim, WeaponAttackType attType) const
{
@@ -2608,7 +3156,18 @@ void Unit::SendAttackStop(Unit* victim)
((Creature*)victim)->AI().EnterEvadeMode(this);*/
}
-/*
+bool Unit::isSpellBlocked(Unit *pVictim, SpellEntry const *spellProto, WeaponAttackType attackType)
+{
+ if (pVictim->HasInArc(M_PI,this))
+ {
+ float blockChance = GetUnitBlockChance();
+ blockChance += (GetWeaponSkillValue(attackType) - pVictim->GetMaxSkillValueForLevel() )*0.04;
+ if (roll_chance_f(blockChance))
+ return true;
+ }
+ return false;
+}
+
// Melee based spells can be miss, parry or dodge on this step
// Crit or block - determined on damage calculation phase! (and can be both in some time)
float Unit::MeleeSpellMissChance(Unit *pVictim, WeaponAttackType attType, int32 skillDiff, SpellEntry const *spell)
@@ -2732,7 +3291,7 @@ SpellMissInfo Unit::MeleeSpellHitResult(Unit *pVictim, SpellEntry const *spell)
return SPELL_MISS_PARRY;
return SPELL_MISS_NONE;
-}*/
+}
// TODO need use unit spell resistances in calculations
SpellMissInfo Unit::MagicSpellHitResult(Unit *pVictim, SpellEntry const *spell)
@@ -2802,6 +3361,14 @@ SpellMissInfo Unit::MagicSpellHitResult(Unit *pVictim, SpellEntry const *spell)
return SPELL_MISS_NONE;
}
+// Calculate spell hit result can be:
+// Every spell can: Evade/Immune/Reflect/Sucesful hit
+// For melee based spells:
+// Miss
+// Dodge
+// Parry
+// For spells
+// Resist
SpellMissInfo Unit::SpellHitResult(Unit *pVictim, SpellEntry const *spell, bool CanReflect)
{
// Return evade for units in evade mode
@@ -2829,62 +3396,24 @@ SpellMissInfo Unit::SpellHitResult(Unit *pVictim, SpellEntry const *spell, bool
// Try victim reflect spell
if (CanReflect)
{
- // specialized first
+ int32 reflectchance = pVictim->GetTotalAuraModifier(SPELL_AURA_REFLECT_SPELLS);
Unit::AuraList const& mReflectSpellsSchool = pVictim->GetAurasByType(SPELL_AURA_REFLECT_SPELLS_SCHOOL);
for(Unit::AuraList::const_iterator i = mReflectSpellsSchool.begin(); i != mReflectSpellsSchool.end(); ++i)
- {
if((*i)->GetModifier()->m_miscvalue & GetSpellSchoolMask(spell))
- {
- int32 reflectchance = (*i)->GetModifier()->m_amount;
- if (reflectchance > 0 && roll_chance_i(reflectchance))
- {
- if((*i)->m_procCharges > 0)
- {
- --(*i)->m_procCharges;
- if((*i)->m_procCharges==0)
- pVictim->RemoveAurasDueToSpell((*i)->GetId());
- }
- return SPELL_MISS_REFLECT;
- }
- }
- }
-
- // generic reflection
- Unit::AuraList const& mReflectSpells = pVictim->GetAurasByType(SPELL_AURA_REFLECT_SPELLS);
- for(Unit::AuraList::const_iterator i = mReflectSpells.begin(); i != mReflectSpells.end(); ++i)
+ reflectchance += (*i)->GetModifier()->m_amount;
+ if (reflectchance > 0 && roll_chance_i(reflectchance))
{
- int32 reflectchance = (*i)->GetModifier()->m_amount;
- if (reflectchance > 0 && roll_chance_i(reflectchance))
- {
- if((*i)->m_procCharges > 0)
- {
- --(*i)->m_procCharges;
- if((*i)->m_procCharges==0)
- pVictim->RemoveAurasDueToSpell((*i)->GetId());
- }
- return SPELL_MISS_REFLECT;
- }
+ // Start triggers for remove charges if need (trigger only for victim, and mark as active spell)
+ ProcDamageAndSpell(pVictim, PROC_FLAG_NONE, PROC_FLAG_TAKEN_NEGATIVE_SPELL_HIT, PROC_EX_REFLECT, 1, BASE_ATTACK, spell);
+ return SPELL_MISS_REFLECT;
}
}
- // Temporary solution for melee based spells and spells vs SPELL_SCHOOL_NORMAL (hit result calculated after)
- for (int i=0;i<3;i++)
- {
- if (spell->Effect[i] == SPELL_EFFECT_WEAPON_DAMAGE ||
- spell->Effect[i] == SPELL_EFFECT_WEAPON_PERCENT_DAMAGE ||
- spell->Effect[i] == SPELL_EFFECT_NORMALIZED_WEAPON_DMG ||
- spell->Effect[i] == SPELL_EFFECT_WEAPON_DAMAGE_NOSCHOOL)
- return SPELL_MISS_NONE;
- }
-
- // TODO need use this code for spell hit result calculation
- // now code commented for computability
switch (spell->DmgClass)
{
case SPELL_DAMAGE_CLASS_RANGED:
case SPELL_DAMAGE_CLASS_MELEE:
-// return MeleeSpellHitResult(pVictim, spell);
- return SPELL_MISS_NONE;
+ return MeleeSpellHitResult(pVictim, spell);
case SPELL_DAMAGE_CLASS_NONE:
return SPELL_MISS_NONE;
case SPELL_DAMAGE_CLASS_MAGIC:
@@ -4386,6 +4915,24 @@ void Unit::RemoveAllGameObjects()
}
}
+void Unit::SendSpellNonMeleeDamageLog(SpellNonMeleeDamage *log)
+{
+ WorldPacket data(SMSG_SPELLNONMELEEDAMAGELOG, (16+4+4+1+4+4+1+1+4+4+1)); // we guess size
+ data.append(log->target->GetPackGUID());
+ data.append(log->attacker->GetPackGUID());
+ data << uint32(log->SpellID);
+ data << uint32(log->damage); //damage amount
+ data << uint8 (log->schoolMask); //damage school
+ data << uint32(log->absorb); //AbsorbedDamage
+ data << uint32(log->resist); //resist
+ data << uint8 (log->phusicalLog); // damsge type? flag
+ data << uint8 (log->unused); //unused
+ data << uint32(log->blocked); //blocked
+ data << uint32(log->HitInfo);
+ data << uint8 (0); // flag to use extend data
+ SendMessageToSet( &data, true );
+}
+
void Unit::SendSpellNonMeleeDamageLog(Unit *target,uint32 SpellID,uint32 Damage, SpellSchoolMask damageSchoolMask,uint32 AbsorbedDamage, uint32 Resist,bool PhysicalDamage, uint32 Blocked, bool CriticalHit)
{
sLog.outDebug("Sending: SMSG_SPELLNONMELEEDAMAGELOG");
@@ -4405,6 +4952,17 @@ void Unit::SendSpellNonMeleeDamageLog(Unit *target,uint32 SpellID,uint32 Damage,
SendMessageToSet( &data, true );
}
+void Unit::ProcDamageAndSpell(Unit *pVictim, uint32 procAttacker, uint32 procVictim, uint32 procExtra, uint32 amount, WeaponAttackType attType, SpellEntry const *procSpell)
+{
+ // Not much to do if no flags are set.
+ if (procAttacker)
+ ProcDamageAndSpellFor(false,pVictim,procAttacker, procExtra,attType, procSpell, amount);
+ // Now go on with a victim's events'n'auras
+ // Not much to do if no flags are set or there is no victim
+ if(pVictim && pVictim->isAlive() && procVictim)
+ pVictim->ProcDamageAndSpellFor(true,this,procVictim, procExtra, attType, procSpell, amount);
+}
+
void Unit::SendSpellMiss(Unit *target, uint32 spellID, SpellMissInfo missInfo)
{
WorldPacket data(SMSG_SPELLLOGMISS, (4+8+1+4+8+1));
@@ -4419,6 +4977,29 @@ void Unit::SendSpellMiss(Unit *target, uint32 spellID, SpellMissInfo missInfo)
SendMessageToSet(&data, true);
}
+void Unit::SendAttackStateUpdate(CalcDamageInfo *damageInfo)
+{
+ WorldPacket data(SMSG_ATTACKERSTATEUPDATE, (16+84)); // we guess size
+ data << (uint32)damageInfo->HitInfo;
+ data.append(GetPackGUID());
+ data.append(damageInfo->target->GetPackGUID());
+ data << (uint32)(damageInfo->damage); // Full damage
+
+ data << (uint8)1; // Sub damage count
+ //=== Sub damage description
+ data << (uint32)(damageInfo->damageSchoolMask); // School of sub damage
+ data << (float)damageInfo->damage; // sub damage
+ data << (uint32)damageInfo->damage; // Sub Damage
+ data << (uint32)damageInfo->absorb; // Absorb
+ data << (uint32)damageInfo->resist; // Resist
+ //=================================================
+ data << (uint32)damageInfo->TargetState;
+ data << (uint32)0;
+ data << (uint32)0;
+ data << (uint32)damageInfo->blocked_amount;
+ SendMessageToSet( &data, true );/**/
+}
+
void Unit::SendAttackStateUpdate(uint32 HitInfo, Unit *target, uint8 SwingType, SpellSchoolMask damageSchoolMask, uint32 Damage, uint32 AbsorbDamage, uint32 Resist, VictimState TargetState, uint32 BlockedAmount)
{
sLog.outDebug("WORLD: Sending SMSG_ATTACKERSTATEUPDATE");
@@ -4452,7 +5033,7 @@ void Unit::SendAttackStateUpdate(uint32 HitInfo, Unit *target, uint8 SwingType,
SendMessageToSet( &data, true );
}
-
+/*
void Unit::ProcDamageAndSpell(Unit *pVictim, uint32 procAttacker, uint32 procVictim, uint32 damage, SpellSchoolMask damageSchoolMask, SpellEntry const *procSpell, bool isTriggeredSpell, WeaponAttackType attType)
{
sLog.outDebug("ProcDamageAndSpell: attacker flags are 0x%x, victim flags 0x%x", procAttacker, procVictim);
@@ -4586,9 +5167,9 @@ void Unit::CastMeleeProcDamageAndSpell(Unit* pVictim, uint32 damage, SpellSchool
if(procAttacker != PROC_FLAG_NONE || procVictim != PROC_FLAG_NONE)
ProcDamageAndSpell(pVictim, procAttacker, procVictim, damage, damageSchoolMask, spellCasted, isTriggeredSpell, attType);
-}
+}*/
-bool Unit::HandleHasteAuraProc(Unit *pVictim, uint32 damage, Aura* triggeredByAura, SpellEntry const * /*procSpell*/, uint32 /*procFlag*/, uint32 cooldown)
+bool Unit::HandleHasteAuraProc(Unit *pVictim, uint32 damage, Aura* triggeredByAura, SpellEntry const * procSpell, uint32 /*procFlag*/, uint32 /*procEx*/, uint32 cooldown)
{
SpellEntry const *hasteSpell = triggeredByAura->GetSpellProto();
@@ -4651,7 +5232,7 @@ bool Unit::HandleHasteAuraProc(Unit *pVictim, uint32 damage, Aura* triggeredByAu
return true;
}
-bool Unit::HandleDummyAuraProc(Unit *pVictim, uint32 damage, Aura* triggeredByAura, SpellEntry const * procSpell, uint32 procFlag, uint32 cooldown)
+bool Unit::HandleDummyAuraProc(Unit *pVictim, uint32 damage, Aura* triggeredByAura, SpellEntry const * procSpell, uint32 procFlag, uint32 procEx, uint32 cooldown)
{
SpellEntry const *dummySpell = triggeredByAura->GetSpellProto ();
uint32 effIndex = triggeredByAura->GetEffIndex ();
@@ -4764,19 +5345,6 @@ bool Unit::HandleDummyAuraProc(Unit *pVictim, uint32 damage, Aura* triggeredByAu
if(!procSpell)
return false;
- // not from DoT
- bool found = false;
- for(int j = 0; j < 3; ++j)
- {
- if(procSpell->EffectApplyAuraName[j]==SPELL_AURA_PERIODIC_DAMAGE||procSpell->EffectApplyAuraName[j]==SPELL_AURA_PERIODIC_DAMAGE_PERCENT)
- {
- found = true;
- break;
- }
- }
- if(found)
- return false;
-
switch(GetFirstSchoolInMask(GetSpellSchoolMask(procSpell)))
{
case SPELL_SCHOOL_NORMAL:
@@ -5042,6 +5610,16 @@ bool Unit::HandleDummyAuraProc(Unit *pVictim, uint32 damage, Aura* triggeredByAu
triggered_spell_id = 29077;
break;
}
+ // Incanter's Regalia set (add trigger chance to Mana Shield)
+ if (dummySpell->SpellFamilyFlags & 0x0000000000008000LL)
+ {
+ if(GetTypeId() != TYPEID_PLAYER)
+ return false;
+
+ target = this;
+ triggered_spell_id = 37436;
+ break;
+ }
switch(dummySpell->Id)
{
// Ignite
@@ -5070,14 +5648,14 @@ bool Unit::HandleDummyAuraProc(Unit *pVictim, uint32 damage, Aura* triggeredByAu
case 11129:
{
//last charge and crit
- if( triggeredByAura->m_procCharges <= 1 && (procFlag & PROC_FLAG_CRIT_SPELL) )
+ if (triggeredByAura->m_procCharges <= 1 && (procEx & PROC_EX_CRITICAL_HIT) )
{
RemoveAurasDueToSpell(28682); //-> remove Combustion auras
return true; // charge counting (will removed)
}
CastSpell(this, 28682, true, castItem, triggeredByAura);
- return(procFlag & PROC_FLAG_CRIT_SPELL);// charge update only at crit hits, no hidden cooldowns
+ return (procEx & PROC_EX_CRITICAL_HIT);// charge update only at crit hits, no hidden cooldowns
}
}
break;
@@ -5094,6 +5672,34 @@ bool Unit::HandleDummyAuraProc(Unit *pVictim, uint32 damage, Aura* triggeredByAu
triggered_spell_id = 22858;
break;
}
+ else if (dummySpell->SpellIconID == 1697) // Second Wind
+ {
+ // only for spells and hit/crit (trigger start always) and not start from self casted spells (5530 Mace Stun Effect for example)
+ if (procSpell == 0 || !(procEx & (PROC_EX_NORMAL_HIT|PROC_EX_CRITICAL_HIT)) || this == pVictim)
+ return false;
+ // Need stun or root mechanic
+ if (procSpell->Mechanic != MECHANIC_ROOT && procSpell->Mechanic != MECHANIC_STUN)
+ {
+ int32 i;
+ for (i=0; i<3; i++)
+ if (procSpell->EffectMechanic[i] == MECHANIC_ROOT || procSpell->EffectMechanic[i] == MECHANIC_STUN)
+ break;
+ if (i == 3)
+ return false;
+ }
+
+ switch (dummySpell->Id)
+ {
+ case 29838: triggered_spell_id=29842; break;
+ case 29834: triggered_spell_id=29841; break;
+ default:
+ sLog.outError("Unit::HandleDummyAuraProc: non handled spell id: %u (SW)",dummySpell->Id);
+ return false;
+ }
+
+ target = this;
+ break;
+ }
break;
}
case SPELLFAMILY_WARLOCK:
@@ -5103,7 +5709,6 @@ bool Unit::HandleDummyAuraProc(Unit *pVictim, uint32 damage, Aura* triggeredByAu
{
Modifier* mod = triggeredByAura->GetModifier();
// if damage is more than need or target die from damage deal finish spell
- // FIX ME: not triggered currently at death
if( mod->m_amount <= damage || GetHealth() <= damage )
{
// remember guid before aura delete
@@ -5447,16 +6052,14 @@ bool Unit::HandleDummyAuraProc(Unit *pVictim, uint32 damage, Aura* triggeredByAu
switch(triggeredByAura->GetEffIndex())
{
case 0:
- // prevent chain triggering
- if(procSpell && procSpell->Id==31893 )
- return false;
-
triggered_spell_id = 31893;
break;
case 1:
{
// damage
- basepoints0 = triggeredByAura->GetModifier()->m_amount * damage / 100;
+ damage += CalculateDamage(BASE_ATTACK, false) * 35 / 100; // add spell damage from prev effect (35%)
+ basepoints0 = triggeredByAura->GetModifier()->m_amount * damage / 100;
+
target = this;
triggered_spell_id = 32221;
break;
@@ -5804,7 +6407,7 @@ bool Unit::HandleDummyAuraProc(Unit *pVictim, uint32 damage, Aura* triggeredByAu
return true;
}
-
+/*
bool Unit::HandleProcTriggerSpell(Unit *pVictim, uint32 damage, Aura* triggeredByAura, SpellEntry const *procSpell, uint32 procFlags,WeaponAttackType attackType, uint32 cooldown)
{
SpellEntry const* auraSpellInfo = triggeredByAura->GetSpellProto();
@@ -6470,6 +7073,652 @@ bool Unit::HandleProcTriggerSpell(Unit *pVictim, uint32 damage, Aura* triggeredB
return true;
}
+*/
+
+bool Unit::HandleProcTriggerSpell(Unit *pVictim, uint32 damage, Aura* triggeredByAura, SpellEntry const *procSpell, uint32 procFlags, uint32 procEx, uint32 cooldown)
+{
+ // Get triggered aura spell info
+ SpellEntry const* auraSpellInfo = triggeredByAura->GetSpellProto();
+
+ // Basepoints of trigger aura
+ int32 triggerAmount = triggeredByAura->GetModifier()->m_amount;
+
+ // Set trigger spell id, target, custom basepoints
+ uint32 trigger_spell_id = auraSpellInfo->EffectTriggerSpell[triggeredByAura->GetEffIndex()];
+ Unit* target = NULL;
+ int32 basepoints0 = 0;
+
+ Item* castItem = triggeredByAura->GetCastItemGUID() && GetTypeId()==TYPEID_PLAYER
+ ? ((Player*)this)->GetItemByGuid(triggeredByAura->GetCastItemGUID()) : NULL;
+
+ // Try handle uncnown trigger spells
+ if (sSpellStore.LookupEntry(trigger_spell_id)==NULL)
+ switch (auraSpellInfo->SpellFamilyName)
+ {
+ //=====================================================================
+ // Generic class
+ // ====================================================================
+ // .....
+ //=====================================================================
+ case SPELLFAMILY_GENERIC:
+// if (auraSpellInfo->Id==34082) // Advantaged State (DND)
+// trigger_spell_id = ???;
+ if (auraSpellInfo->Id == 23780) // Aegis of Preservation (Aegis of Preservation trinket)
+ trigger_spell_id = 23781;
+// else if (auraSpellInfo->Id==43504) // Alterac Valley OnKill Proc Aura
+// trigger_spell_id = ;
+// else if (auraSpellInfo->Id==37030) // Chaotic Temperament
+// trigger_spell_id = ;
+ else if (auraSpellInfo->Id==43820) // Charm of the Witch Doctor (Amani Charm of the Witch Doctor trinket)
+ {
+ // Pct value stored in dummy
+ basepoints0 = pVictim->GetCreateHealth() * auraSpellInfo->EffectBasePoints[1] / 100;
+ target = pVictim;
+ break;
+ }
+// else if (auraSpellInfo->Id==41248) // Consuming Strikes
+// trigger_spell_id = 41249;
+// else if (auraSpellInfo->Id==41054) // Copy Weapon
+// trigger_spell_id = 41055;
+// else if (auraSpellInfo->Id==31255) // Deadly Swiftness (Rank 1)
+// trigger_spell_id = ;
+// else if (auraSpellInfo->Id==5301) // Defensive State (DND)
+// trigger_spell_id = ;
+// else if (auraSpellInfo->Id==13358) // Defensive State (DND)
+// trigger_spell_id = ;
+// else if (auraSpellInfo->Id==16092) // Defensive State (DND)
+// trigger_spell_id = ;
+// else if (auraSpellInfo->Id==24949) // Defensive State 2 (DND)
+// trigger_spell_id = ;
+// else if (auraSpellInfo->Id==40329) // Demo Shout Sensor
+// trigger_spell_id = ;
+ // Desperate Defense (Stonescythe Whelp, Stonescythe Alpha, Stonescythe Ambusher)
+ else if (auraSpellInfo->Id == 33896)
+ trigger_spell_id = 33898;
+// else if (auraSpellInfo->Id==18943) // Double Attack
+// trigger_spell_id = ;
+// else if (auraSpellInfo->Id==19194) // Double Attack
+// trigger_spell_id = ;
+// else if (auraSpellInfo->Id==19817) // Double Attack
+// trigger_spell_id = ;
+// else if (auraSpellInfo->Id==19818) // Double Attack
+// trigger_spell_id = ;
+// else if (auraSpellInfo->Id==22835) // Drunken Rage
+// trigger_spell_id = 14822;
+ /*
+ else if (auraSpellInfo->SpellIconID==191) // Elemental Response
+ {
+ switch (auraSpellInfo->Id && auraSpellInfo->AttributesEx==0)
+ {
+ case 34191:
+ case 34329:
+ case 34524:
+ case 34582:
+ case 36733:break;
+ default:
+ sLog.outError("Unit::HandleProcTriggerSpell: Spell %u miss posibly Elemental Response",auraSpellInfo->Id);
+ return false;
+ }
+ //This generic aura self-triggers a different spell for each school of magic that lands on the wearer:
+ switch (procSpell->School)
+ {
+ case SPELL_SCHOOL_FIRE: trigger_spell_id = 34192;break;//Fire: 34192
+ case SPELL_SCHOOL_FROST: trigger_spell_id = 34193;break;//Frost: 34193
+ case SPELL_SCHOOL_ARCANE: trigger_spell_id = 34194;break;//Arcane: 34194
+ case SPELL_SCHOOL_NATURE: trigger_spell_id = 34195;break;//Nature: 34195
+ case SPELL_SCHOOL_SHADOW: trigger_spell_id = 34196;break;//Shadow: 34196
+ case SPELL_SCHOOL_HOLY: trigger_spell_id = 34197;break;//Holy: 34197
+ case SPELL_SCHOOL_NORMAL: trigger_spell_id = 34198;break;//Physical: 34198
+ default:
+ sLog.outError("Unit::HandleProcTriggerSpell: Spell %u Elemental Response wrong school",auraSpellInfo->Id);
+ return false;
+ }
+ }*/
+// else if (auraSpellInfo->Id==6542) // Enraged Defense
+// trigger_spell_id = ;
+// else if (auraSpellInfo->Id==40364) // Entangling Roots Sensor
+// trigger_spell_id = ;
+// else if (auraSpellInfo->Id==33207) // Gossip NPC Periodic - Fidget
+// trigger_spell_id = ;
+// else if (auraSpellInfo->Id==35321) // Gushing Wound
+// trigger_spell_id = ;
+// else if (auraSpellInfo->Id==38363) // Gushing Wound
+// trigger_spell_id = ;
+// else if (auraSpellInfo->Id==39215) // Gushing Wound
+// trigger_spell_id = ;
+// else if (auraSpellInfo->Id==40250) // Improved Duration
+// trigger_spell_id = ;
+ else if (auraSpellInfo->Id==27522) // Mana Drain Trigger
+ {
+ // On successful melee or ranged attack gain $29471s1 mana and if possible drain $27526s1 mana from the target.
+ if (this && this->isAlive())
+ CastSpell(this, 29471, true, castItem, triggeredByAura);
+ if (pVictim && pVictim->isAlive())
+ CastSpell(pVictim, 27526, true, castItem, triggeredByAura);
+ return true;
+ }
+ else if (auraSpellInfo->Id==24905) // Moonkin Form (Passive)
+ {
+ // Elune's Touch (instead non-existed triggered spell) 30% from AP
+ trigger_spell_id = 33926;
+ basepoints0 = GetTotalAttackPowerValue(BASE_ATTACK) * 30 / 100;
+ target = this;
+ }
+// else if (auraSpellInfo->Id==43453) // Rune Ward
+// trigger_spell_id = ;
+// else if (auraSpellInfo->Id==7137) // Shadow Charge (Rank 1)
+// trigger_spell_id = ;
+ // Shaleskin (Shaleskin Flayer, Shaleskin Ripper) 30023 trigger
+// else if (auraSpellInfo->Id==36576)
+// trigger_spell_id = ;
+// else if (auraSpellInfo->Id==34783) // Spell Reflection
+// trigger_spell_id = ;
+// else if (auraSpellInfo->Id==36096) // Spell Reflection
+// trigger_spell_id = ;
+// else if (auraSpellInfo->Id==36207) // Steal Weapon
+// trigger_spell_id = ;
+// else if (auraSpellInfo->Id==35205) // Vanish
+ break;
+ //=====================================================================
+ // Mage
+ //=====================================================================
+ // Blazing Speed (Rank 1,2) trigger = 18350
+ //=====================================================================
+ case SPELLFAMILY_MAGE:
+ // Blazing Speed
+ if (auraSpellInfo->SpellIconID == 2127)
+ {
+ switch (auraSpellInfo->Id)
+ {
+ case 31641: // Rank 1
+ case 31642: // Rank 2
+ trigger_spell_id = 31643;
+ break;
+ default:
+ sLog.outError("Unit::HandleProcTriggerSpell: Spell %u miss posibly Blazing Speed",auraSpellInfo->Id);
+ return false;
+ }
+ }
+ break;
+ //=====================================================================
+ // Warrior
+ //=====================================================================
+ // Rampage (Rank 1-3) trigger = 18350
+ //=====================================================================
+ case SPELLFAMILY_WARRIOR:
+ // Rampage
+ if (auraSpellInfo->SpellIconID == 2006 && auraSpellInfo->SpellFamilyFlags==0x100000)
+ {
+ switch(auraSpellInfo->Id)
+ {
+ case 29801: trigger_spell_id = 30029; break; // Rank 1
+ case 30030: trigger_spell_id = 30031; break; // Rank 2
+ case 30033: trigger_spell_id = 30032; break; // Rank 3
+ default:
+ sLog.outError("Unit::HandleProcTriggerSpell: Spell %u not handled in Rampage",auraSpellInfo->Id);
+ return false;
+ }
+ }
+ break;
+ //=====================================================================
+ // Warlock
+ //=====================================================================
+ // Pyroclasm trigger = 18350
+ // Drain Soul (Rank 1-5) trigger = 0
+ //=====================================================================
+ case SPELLFAMILY_WARLOCK:
+ {
+ // Pyroclasm
+ if (auraSpellInfo->SpellIconID == 1137)
+ {
+ if(!pVictim || !pVictim->isAlive() || pVictim == this || procSpell == NULL)
+ return false;
+ // Calculate spell tick count for spells
+ uint32 tick = 1; // Default tick = 1
+
+ // Hellfire have 15 tick
+ if (procSpell->SpellFamilyFlags&0x0000000000000040LL)
+ tick = 15;
+ // Rain of Fire have 4 tick
+ else if (procSpell->SpellFamilyFlags&0x0000000000000020LL)
+ tick = 4;
+ else
+ return false;
+
+ // Calculate chance = baseChance / tick
+ float chance = 0;
+ switch (auraSpellInfo->Id)
+ {
+ case 18096: chance = 13.0f / tick; break;
+ case 18073: chance = 26.0f / tick; break;
+ }
+ // Roll chance
+ if (!roll_chance_f(chance))
+ return false;
+
+ trigger_spell_id = 18093;
+ }
+ // Drain Soul
+ else if (auraSpellInfo->SpellFamilyFlags & 0x0000000000004000LL)
+ {
+ Unit::AuraList const& mAddFlatModifier = GetAurasByType(SPELL_AURA_ADD_FLAT_MODIFIER);
+ for(Unit::AuraList::const_iterator i = mAddFlatModifier.begin(); i != mAddFlatModifier.end(); ++i)
+ {
+ if ((*i)->GetModifier()->m_miscvalue == SPELLMOD_CHANCE_OF_SUCCESS && (*i)->GetSpellProto()->SpellIconID == 113)
+ {
+ int32 value2 = CalculateSpellDamage((*i)->GetSpellProto(),2,(*i)->GetSpellProto()->EffectBasePoints[2],this);
+ basepoints0 = value2 * GetMaxPower(POWER_MANA) / 100;
+ }
+ }
+ if ( basepoints0 == 0 )
+ return false;
+ trigger_spell_id = 18371;
+ }
+ break;
+ }
+ //=====================================================================
+ // Priest
+ //=====================================================================
+ // Greater Heal Refund trigger = 18350
+ // Blessed Recovery (Rank 1-3) trigger = 18350
+ // Shadowguard (1-7) trigger = 28376
+ //=====================================================================
+ case SPELLFAMILY_PRIEST:
+ {
+ // Greater Heal Refund
+ if (auraSpellInfo->Id==37594)
+ trigger_spell_id = 37595;
+ // Shadowguard
+ else if(auraSpellInfo->SpellFamilyFlags==0x100080000000LL && auraSpellInfo->SpellVisual==7958)
+ {
+ switch(auraSpellInfo->Id)
+ {
+ case 18137: trigger_spell_id = 28377; break; // Rank 1
+ case 19308: trigger_spell_id = 28378; break; // Rank 2
+ case 19309: trigger_spell_id = 28379; break; // Rank 3
+ case 19310: trigger_spell_id = 28380; break; // Rank 4
+ case 19311: trigger_spell_id = 28381; break; // Rank 5
+ case 19312: trigger_spell_id = 28382; break; // Rank 6
+ case 25477: trigger_spell_id = 28385; break; // Rank 7
+ default:
+ sLog.outError("Unit::HandleProcTriggerSpell: Spell %u not handled in SG", auraSpellInfo->Id);
+ return false;
+ }
+ }
+ // Blessed Recovery
+ else if (auraSpellInfo->SpellIconID == 1875)
+ {
+ switch (auraSpellInfo->Id)
+ {
+ case 27811: trigger_spell_id = 27813; break;
+ case 27815: trigger_spell_id = 27817; break;
+ case 27816: trigger_spell_id = 27818; break;
+ default:
+ sLog.outError("Unit::HandleProcTriggerSpell: Spell %u not handled in BR", auraSpellInfo->Id);
+ return false;
+ }
+ basepoints0 = damage * triggerAmount / 100 / 3;
+ target = this;
+ }
+ break;
+ }
+ //=====================================================================
+ // Druid
+ // ====================================================================
+ // Druid Forms Trinket trigger = 18350
+ // Entangling Roots trigger = 30023
+ // Leader of the Pack trigger = 18350
+ //=====================================================================
+ case SPELLFAMILY_DRUID:
+ {
+ // Druid Forms Trinket
+ if (auraSpellInfo->Id==37336)
+ {
+ switch(m_form)
+ {
+ case 0: trigger_spell_id = 37344;break;
+ case FORM_CAT: trigger_spell_id = 37341;break;
+ case FORM_BEAR:
+ case FORM_DIREBEAR: trigger_spell_id = 37340;break;
+ case FORM_TREE: trigger_spell_id = 37342;break;
+ case FORM_MOONKIN: trigger_spell_id = 37343;break;
+ default:
+ return false;
+ }
+ }
+// else if (auraSpellInfo->Id==40363)// Entangling Roots ()
+// trigger_spell_id = ????;
+ // Leader of the Pack
+ else if (auraSpellInfo->Id == 24932)
+ {
+ if (triggerAmount == 0)
+ return false;
+ basepoints0 = triggerAmount * GetMaxHealth() / 100;
+ trigger_spell_id = 34299;
+ }
+ break;
+ }
+ //=====================================================================
+ // Hunter
+ // ====================================================================
+ // ......
+ //=====================================================================
+ case SPELLFAMILY_HUNTER:
+ break;
+ //=====================================================================
+ // Paladin
+ // ====================================================================
+ // Blessed Life trigger = 31934
+ // Healing Discount trigger = 18350
+ // Illumination (Rank 1-5) trigger = 18350
+ // Judgement of Light (Rank 1-5) trigger = 5373
+ // Judgement of Wisdom (Rank 1-4) trigger = 1826
+ // Lightning Capacitor trigger = 18350
+ //=====================================================================
+ case SPELLFAMILY_PALADIN:
+ {
+ /* // Blessed Life
+ if (auraSpellInfo->SpellIconID == 2137)
+ {
+ switch (auraSpellInfo->Id)
+ {
+ case 31828: // Rank 1
+ case 31829: // Rank 2
+ case 31830: // Rank 3
+ break;
+ default:
+ sLog.outError("Unit::HandleProcTriggerSpell: Spell %u miss posibly Blessed Life", auraSpellInfo->Id);
+ return false;
+ }
+ }*/
+ // Healing Discount
+ if (auraSpellInfo->Id==37705)
+ {
+ trigger_spell_id = 37706;
+ target = this;
+ }
+ // Judgement of Light and Judgement of Wisdom
+ else if (auraSpellInfo->SpellFamilyFlags & 0x0000000000080000LL)
+ {
+ switch (auraSpellInfo->Id)
+ {
+ // Judgement of Light
+ case 20185: trigger_spell_id = 20267;break; // Rank 1
+ case 20344: trigger_spell_id = 20341;break; // Rank 2
+ case 20345: trigger_spell_id = 20342;break; // Rank 3
+ case 20346: trigger_spell_id = 20343;break; // Rank 4
+ case 27162: trigger_spell_id = 27163;break; // Rank 5
+ // Judgement of Wisdom
+ case 20186: trigger_spell_id = 20268;break; // Rank 1
+ case 20354: trigger_spell_id = 20352;break; // Rank 2
+ case 20355: trigger_spell_id = 20353;break; // Rank 3
+ case 27164: trigger_spell_id = 27165;break; // Rank 4
+ default:
+ sLog.outError("Unit::HandleProcTriggerSpell: Spell %u miss posibly Judgement of Light/Wisdom", auraSpellInfo->Id);
+ return false;
+ }
+ pVictim->CastSpell(pVictim, trigger_spell_id, true, castItem, triggeredByAura);
+ return true; // no hidden cooldown
+ }
+ // Illumination
+ else if (auraSpellInfo->SpellIconID==241)
+ {
+ if(!procSpell)
+ return false;
+ // procspell is triggered spell but we need mana cost of original casted spell
+ uint32 originalSpellId = procSpell->Id;
+ // Holy Shock
+ if(procSpell->SpellFamilyFlags & 0x00200000)
+ {
+ switch(procSpell->Id)
+ {
+ case 25914: originalSpellId = 20473; break;
+ case 25913: originalSpellId = 20929; break;
+ case 25903: originalSpellId = 20930; break;
+ case 27175: originalSpellId = 27174; break;
+ case 33074: originalSpellId = 33072; break;
+ default:
+ sLog.outError("Unit::HandleProcTriggerSpell: Spell %u not handled in HShock",procSpell->Id);
+ return false;
+ }
+ }
+ SpellEntry const *originalSpell = sSpellStore.LookupEntry(originalSpellId);
+ if(!originalSpell)
+ {
+ sLog.outError("Unit::HandleProcTriggerSpell: Spell %u unknown but selected as original in Illu",originalSpellId);
+ return false;
+ }
+ // percent stored in effect 1 (class scripts) base points
+ basepoints0 = originalSpell->manaCost*(auraSpellInfo->EffectBasePoints[1]+1)/100;
+ trigger_spell_id = 20272;
+ target = this;
+ }
+ // Lightning Capacitor
+ else if (auraSpellInfo->Id==37657)
+ {
+ if(!pVictim || !pVictim->isAlive())
+ return false;
+ // stacking
+ CastSpell(this, 37658, true, NULL, triggeredByAura);
+ // counting
+ uint32 count = 0;
+ AuraList const& dummyAura = GetAurasByType(SPELL_AURA_DUMMY);
+ for(AuraList::const_iterator itr = dummyAura.begin(); itr != dummyAura.end(); ++itr)
+ if((*itr)->GetId()==37658)
+ ++count;
+ // release at 3 aura in stack (cont contain in basepoint of trigger aura)
+ if(count < triggerAmount)
+ return false;
+
+ RemoveAurasDueToSpell(37658);
+ trigger_spell_id = 37661;
+ target = pVictim;
+ }
+ break;
+ }
+ //=====================================================================
+ // Shaman
+ //====================================================================
+ // Lightning Shield trigger = 18350
+ // Mana Surge trigger = 18350
+ // Nature's Guardian (Rank 1-5) trigger = 18350
+ //=====================================================================
+ case SPELLFAMILY_SHAMAN:
+ {
+ //Lightning Shield (overwrite non existing triggered spell call in spell.dbc
+ if(auraSpellInfo->SpellFamilyFlags==0x00000400 && auraSpellInfo->SpellVisual==37)
+ {
+ switch(auraSpellInfo->Id)
+ {
+ case 324: trigger_spell_id = 26364; break; // Rank 1
+ case 325: trigger_spell_id = 26365; break; // Rank 2
+ case 905: trigger_spell_id = 26366; break; // Rank 3
+ case 945: trigger_spell_id = 26367; break; // Rank 4
+ case 8134: trigger_spell_id = 26369; break; // Rank 5
+ case 10431: trigger_spell_id = 26370; break; // Rank 6
+ case 10432: trigger_spell_id = 26363; break; // Rank 7
+ case 25469: trigger_spell_id = 26371; break; // Rank 8
+ case 25472: trigger_spell_id = 26372; break; // Rank 9
+ default:
+ sLog.outError("Unit::HandleProcTriggerSpell: Spell %u not handled in LShield", auraSpellInfo->Id);
+ return false;
+ }
+ }
+ // Lightning Shield (The Ten Storms set)
+ else if (auraSpellInfo->Id == 23551)
+ {
+ trigger_spell_id = 23552;
+ target = pVictim;
+ }
+ // Damage from Lightning Shield (The Ten Storms set)
+ else if (auraSpellInfo->Id == 23552)
+ trigger_spell_id = 27635;
+ // Mana Surge (The Earthfury set)
+ else if (auraSpellInfo->Id == 23572)
+ {
+ if(!procSpell)
+ return false;
+ basepoints0 = procSpell->manaCost * 35 / 100;
+ trigger_spell_id = 23571;
+ target = this;
+ }
+ else if (auraSpellInfo->SpellIconID == 2013) //Nature's Guardian
+ {
+ // Check health condition - should drop to less 30% (damage deal after this!)
+ if (!(10*(int32(GetHealth() - damage)) < 3 * GetMaxHealth()))
+ return false;
+
+ if(pVictim && pVictim->isAlive())
+ pVictim->getThreatManager().modifyThreatPercent(this,-10);
+
+ basepoints0 = triggerAmount * GetMaxHealth() / 100;
+ trigger_spell_id = 31616;
+ target = this;
+ }
+ break;
+ }
+ // default
+ default:
+ break;
+ }
+
+ // All ok. Check current trigger spell
+ SpellEntry const* triggerEntry = sSpellStore.LookupEntry(trigger_spell_id);
+ if ( triggerEntry == NULL )
+ {
+ // Not cast unknown spell
+ // sLog.outError("Unit::HandleProcTriggerSpell: Spell %u have 0 in EffectTriggered[%d], not handled custom case?",auraSpellInfo->Id,triggeredByAura->GetEffIndex());
+ return false;
+ }
+
+ // not allow proc extra attack spell at extra attack
+ if( m_extraAttacks && IsSpellHaveEffect(triggerEntry, SPELL_EFFECT_ADD_EXTRA_ATTACKS) )
+ return false;
+
+ // Costum requirements (not listed in procEx) Warning! damage dealing after this
+ // Custom triggered spells
+ switch (auraSpellInfo->Id)
+ {
+ // Persistent Shield (Scarab Brooch trinket)
+ // This spell originally trigger 13567 - Dummy Trigger (vs dummy efect)
+ case 26467:
+ {
+ basepoints0 = damage * 15 / 100;
+ target = pVictim;
+ trigger_spell_id = 26470;
+ break;
+ }
+ // Cheat Death
+ case 28845:
+ {
+ // When your health drops below 20% ....
+ if (GetHealth() - damage > GetMaxHealth() / 5 || GetHealth() < GetMaxHealth() / 5)
+ return false;
+ break;
+ }
+ // Deadly Swiftness (Rank 1)
+ case 31255:
+ {
+ // whenever you deal damage to a target who is below 20% health.
+ if (pVictim->GetHealth() > pVictim->GetMaxHealth() / 5)
+ return false;
+
+ target = this;
+ trigger_spell_id = 22588;
+ }
+ // Greater Heal Refund (Avatar Raiment set)
+ case 37594:
+ {
+ // Not give if target alredy have full health
+ if (pVictim->GetHealth() == pVictim->GetMaxHealth())
+ return false;
+ // If your Greater Heal brings the target to full health, you gain $37595s1 mana.
+ if (pVictim->GetHealth() + damage < pVictim->GetMaxHealth())
+ return false;
+ break;
+ }
+ // Bonus Healing (Crystal Spire of Karabor mace)
+ case 40971:
+ {
+ // If your target is below $s1% health
+ if (pVictim->GetHealth() > pVictim->GetMaxHealth() * triggerAmount / 100)
+ return false;
+ break;
+ }
+ // Evasive Maneuvers (Commendation of Kael`thas trinket)
+ case 45057:
+ {
+ // reduce you below $s1% health
+ if (GetHealth() - damage > GetMaxHealth() * triggerAmount / 100)
+ return false;
+ break;
+ }
+ }
+
+ // Costum basepoints/target for exist spell
+ // dummy basepoints or other customs
+ switch(trigger_spell_id)
+ {
+ // Cast positive spell on enemy target
+ case 7099: // Curse of Mending
+ case 39647: // Curse of Mending
+ case 29494: // Temptation
+ case 20233: // Improved Lay on Hands (cast on target)
+ {
+ target = pVictim;
+ break;
+ }
+ // Combo points add triggers (need add combopoint only for main tatget, and after possible combopoints reset)
+ case 15250: // Rogue Setup
+ {
+ if(!pVictim || pVictim != getVictim()) // applied only for main target
+ return false;
+ break; // continue normal case
+ }
+ // Finish movies that add combo
+ case 14189: // Seal Fate (Netherblade set)
+ case 14157: // Ruthlessness
+ {
+ // Need add combopoint AFTER finish movie (or they dropped in finish phase)
+ break;
+ }
+ // Shamanistic Rage triggered spell
+ case 30824:
+ {
+ basepoints0 = int32(GetTotalAttackPowerValue(BASE_ATTACK) * triggerAmount / 100);
+ trigger_spell_id = 30824;
+ break;
+ }
+ // Enlightenment (trigger only from mana cost spells)
+ case 35095:
+ {
+ if(!procSpell || procSpell->powerType!=POWER_MANA || procSpell->manaCost==0 && procSpell->ManaCostPercentage==0 && procSpell->manaCostPerlevel==0)
+ return false;
+ break;
+ }
+ }
+
+ if( cooldown && GetTypeId()==TYPEID_PLAYER && ((Player*)this)->HasSpellCooldown(trigger_spell_id))
+ return false;
+
+ // try detect target manually if not set
+ if ( target == NULL )
+ target = !(procFlags & PROC_FLAG_SUCCESSFUL_POSITIVE_SPELL) && IsPositiveSpell(trigger_spell_id) ? this : pVictim;
+
+ // default case
+ if(!target || target!=this && !target->isAlive())
+ return false;
+
+ if(basepoints0)
+ CastCustomSpell(target,trigger_spell_id,&basepoints0,NULL,NULL,true,castItem,triggeredByAura);
+ else
+ CastSpell(target,trigger_spell_id,true,castItem,triggeredByAura);
+
+ if( cooldown && GetTypeId()==TYPEID_PLAYER )
+ ((Player*)this)->AddSpellCooldown(trigger_spell_id,0,time(NULL) + cooldown);
+
+ return true;
+}
bool Unit::HandleOverrideClassScriptAuraProc(Unit *pVictim, Aura *triggeredByAura, SpellEntry const *procSpell, uint32 cooldown)
{
@@ -8044,38 +9293,17 @@ int32 Unit::SpellBaseHealingBonusForVictim(SpellSchoolMask schoolMask, Unit *pVi
bool Unit::IsImmunedToDamage(SpellSchoolMask shoolMask, bool useCharges)
{
- // no charges dependent checks
+ //If m_immuneToSchool type contain this school type, IMMUNE damage.
SpellImmuneList const& schoolList = m_spellImmune[IMMUNITY_SCHOOL];
for (SpellImmuneList::const_iterator itr = schoolList.begin(); itr != schoolList.end(); ++itr)
if(itr->type & shoolMask)
return true;
- // charges dependent checks
+ //If m_immuneToDamage type contain magic, IMMUNE damage.
SpellImmuneList const& damageList = m_spellImmune[IMMUNITY_DAMAGE];
for (SpellImmuneList::const_iterator itr = damageList.begin(); itr != damageList.end(); ++itr)
- {
if(itr->type & shoolMask)
- {
- if(useCharges)
- {
- AuraList const& auraDamageImmunity = GetAurasByType(SPELL_AURA_DAMAGE_IMMUNITY);
- for(AuraList::const_iterator auraItr = auraDamageImmunity.begin(); auraItr != auraDamageImmunity.end(); ++auraItr)
- {
- if((*auraItr)->GetId()==itr->spellId)
- {
- if((*auraItr)->m_procCharges > 0)
- {
- --(*auraItr)->m_procCharges;
- if((*auraItr)->m_procCharges==0)
- RemoveAurasDueToSpell(itr->spellId);
- }
- break;
- }
- }
- }
return true;
- }
- }
return false;
}
@@ -8085,8 +9313,6 @@ bool Unit::IsImmunedToSpell(SpellEntry const* spellInfo, bool useCharges)
if (!spellInfo)
return false;
- // no charges first
-
//FIX ME this hack: don't get feared if stunned
if (spellInfo->Mechanic == MECHANIC_FEAR )
{
@@ -8094,7 +9320,6 @@ bool Unit::IsImmunedToSpell(SpellEntry const* spellInfo, bool useCharges)
return true;
}
- // not have spells with charges currently
SpellImmuneList const& dispelList = m_spellImmune[IMMUNITY_DISPEL];
for(SpellImmuneList::const_iterator itr = dispelList.begin(); itr != dispelList.end(); ++itr)
if(itr->type == spellInfo->Dispel)
@@ -8102,7 +9327,6 @@ bool Unit::IsImmunedToSpell(SpellEntry const* spellInfo, bool useCharges)
if( !(spellInfo->AttributesEx & SPELL_ATTR_EX_UNAFFECTED_BY_SCHOOL_IMMUNE) && (spellInfo->Id != 42292)) // unaffected by school immunity
{
- // not have spells with charges currently
SpellImmuneList const& schoolList = m_spellImmune[IMMUNITY_SCHOOL];
for(SpellImmuneList::const_iterator itr = schoolList.begin(); itr != schoolList.end(); ++itr)
if( !(IsPositiveSpell(itr->spellId) && IsPositiveSpell(spellInfo->Id)) &&
@@ -8110,30 +9334,11 @@ bool Unit::IsImmunedToSpell(SpellEntry const* spellInfo, bool useCharges)
return true;
}
- // charges dependent checks
-
SpellImmuneList const& mechanicList = m_spellImmune[IMMUNITY_MECHANIC];
for(SpellImmuneList::const_iterator itr = mechanicList.begin(); itr != mechanicList.end(); ++itr)
{
if(itr->type == spellInfo->Mechanic)
{
- if(useCharges)
- {
- AuraList const& auraMechImmunity = GetAurasByType(SPELL_AURA_MECHANIC_IMMUNITY);
- for(AuraList::const_iterator auraItr = auraMechImmunity.begin(); auraItr != auraMechImmunity.end(); ++auraItr)
- {
- if((*auraItr)->GetId()==itr->spellId)
- {
- if((*auraItr)->m_procCharges > 0)
- {
- --(*auraItr)->m_procCharges;
- if((*auraItr)->m_procCharges==0)
- RemoveAurasDueToSpell(itr->spellId);
- }
- break;
- }
- }
- }
return true;
}
}
@@ -9990,6 +11195,7 @@ bool Unit::isFrozen() const
return false;
}
+/*
struct ProcTriggeredData
{
ProcTriggeredData(Aura* _triggeredByAura, uint32 _cooldown)
@@ -10163,6 +11369,371 @@ void Unit::ProcDamageAndSpellFor( bool isVictim, Unit * pTarget, uint32 procFlag
}
}
}
+*/
+struct ProcTriggeredData
+{
+ ProcTriggeredData(SpellProcEventEntry const * _spellProcEvent, Aura* _triggeredByAura)
+ : spellProcEvent(_spellProcEvent), triggeredByAura(_triggeredByAura),
+ triggeredByAura_SpellPair(Unit::spellEffectPair(triggeredByAura->GetId(),triggeredByAura->GetEffIndex()))
+ {}
+ SpellProcEventEntry const *spellProcEvent;
+ Aura* triggeredByAura;
+ Unit::spellEffectPair triggeredByAura_SpellPair;
+};
+
+typedef std::list< ProcTriggeredData > ProcTriggeredList;
+typedef std::list< uint32> RemoveSpellList;
+
+// List of auras that CAN be trigger but may not exist in spell_proc_event
+// in most case need for drop charges
+// in some types of aura need do additional check
+// for example SPELL_AURA_MECHANIC_IMMUNITY - need check for mechanic
+static bool isTriggerAura[TOTAL_AURAS];
+static bool isNonTriggerAura[TOTAL_AURAS];
+void InitTriggerAuraData()
+{
+ for (int i=0;i<TOTAL_AURAS;i++)
+ {
+ isTriggerAura[i]=false;
+ isNonTriggerAura[i] = false;
+ }
+ isTriggerAura[SPELL_AURA_DUMMY] = true;
+ isTriggerAura[SPELL_AURA_MOD_CONFUSE] = true;
+ isTriggerAura[SPELL_AURA_MOD_THREAT] = true;
+ isTriggerAura[SPELL_AURA_MOD_STUN] = true; // Aura not have charges but need remove him on trigger
+ isTriggerAura[SPELL_AURA_MOD_DAMAGE_DONE] = true;
+ isTriggerAura[SPELL_AURA_MOD_DAMAGE_TAKEN] = true;
+ isTriggerAura[SPELL_AURA_MOD_RESISTANCE] = true;
+ isTriggerAura[SPELL_AURA_MOD_ROOT] = true;
+ isTriggerAura[SPELL_AURA_REFLECT_SPELLS] = true;
+ isTriggerAura[SPELL_AURA_DAMAGE_IMMUNITY] = true;
+ isTriggerAura[SPELL_AURA_PROC_TRIGGER_SPELL] = true;
+ isTriggerAura[SPELL_AURA_PROC_TRIGGER_DAMAGE] = true;
+ isTriggerAura[SPELL_AURA_MOD_CASTING_SPEED] = true;
+ isTriggerAura[SPELL_AURA_MOD_POWER_COST_SCHOOL_PCT] = true;
+ isTriggerAura[SPELL_AURA_MOD_POWER_COST_SCHOOL] = true;
+ isTriggerAura[SPELL_AURA_REFLECT_SPELLS_SCHOOL] = true;
+ isTriggerAura[SPELL_AURA_MECHANIC_IMMUNITY] = true;
+ isTriggerAura[SPELL_AURA_MOD_DAMAGE_PERCENT_TAKEN] = true;
+ isTriggerAura[SPELL_AURA_SPELL_MAGNET] = true;
+ isTriggerAura[SPELL_AURA_MOD_ATTACK_POWER] = true;
+ isTriggerAura[SPELL_AURA_ADD_CASTER_HIT_TRIGGER] = true;
+ isTriggerAura[SPELL_AURA_OVERRIDE_CLASS_SCRIPTS] = true;
+ isTriggerAura[SPELL_AURA_MOD_MECHANIC_RESISTANCE] = true;
+ isTriggerAura[SPELL_AURA_RANGED_ATTACK_POWER_ATTACKER_BONUS] = true;
+ isTriggerAura[SPELL_AURA_MOD_HASTE] = true;
+ isTriggerAura[SPELL_AURA_MOD_ATTACKER_MELEE_HIT_CHANCE]=true;
+ isTriggerAura[SPELL_AURA_PRAYER_OF_MENDING] = true;
+
+ isNonTriggerAura[SPELL_AURA_MOD_POWER_REGEN]=true;
+ isNonTriggerAura[SPELL_AURA_RESIST_PUSHBACK]=true;
+}
+
+uint32 createProcExtendMask(SpellNonMeleeDamage *damageInfo, SpellMissInfo missCondition)
+{
+ uint32 procEx = PROC_EX_NONE;
+ // Check victim state
+ if (missCondition!=SPELL_MISS_NONE)
+ switch (missCondition)
+ {
+ case SPELL_MISS_MISS: procEx|=PROC_EX_MISS; break;
+ case SPELL_MISS_RESIST: procEx|=PROC_EX_RESIST; break;
+ case SPELL_MISS_DODGE: procEx|=PROC_EX_DODGE; break;
+ case SPELL_MISS_PARRY: procEx|=PROC_EX_PARRY; break;
+ case SPELL_MISS_BLOCK: procEx|=PROC_EX_BLOCK; break;
+ case SPELL_MISS_EVADE: procEx|=PROC_EX_EVADE; break;
+ case SPELL_MISS_IMMUNE: procEx|=PROC_EX_IMMUNE; break;
+ case SPELL_MISS_IMMUNE2: procEx|=PROC_EX_IMMUNE; break;
+ case SPELL_MISS_DEFLECT: procEx|=PROC_EX_DEFLECT;break;
+ case SPELL_MISS_ABSORB: procEx|=PROC_EX_ABSORB; break;
+ case SPELL_MISS_REFLECT: procEx|=PROC_EX_REFLECT;break;
+ default:
+ break;
+ }
+ else
+ {
+ // On block
+ if (damageInfo->blocked)
+ procEx|=PROC_EX_BLOCK;
+ // On absorb
+ if (damageInfo->absorb)
+ procEx|=PROC_EX_ABSORB;
+ // On crit
+ if (damageInfo->HitInfo & SPELL_HIT_TYPE_CRIT)
+ procEx|=PROC_EX_CRITICAL_HIT;
+ else
+ procEx|=PROC_EX_NORMAL_HIT;
+ }
+ return procEx;
+}
+
+static int deep = 0;
+void Unit::ProcDamageAndSpellFor( bool isVictim, Unit * pTarget, uint32 procFlag, uint32 procExtra, WeaponAttackType attType, SpellEntry const * procSpell, uint32 damage )
+{
+ deep ++;
+ if (deep > 5)
+ {
+ sLog.outError("Prevent possible stack owerflow in Unit::ProcDamageAndSpellFor");
+ if (procSpell)
+ sLog.outError(" Spell %u", procSpell->Id);
+ deep--;
+ return;
+ }
+ // For melee/ranged based attack need update skills and set some Aura states
+ if (procFlag & MELEE_BASED_TRIGGER_MASK)
+ {
+ // Update skills here for players
+ if (GetTypeId() == TYPEID_PLAYER)
+ {
+ // On melee based hit/miss/resist need update skill (for victim and attacker)
+ if (procExtra&(PROC_EX_NORMAL_HIT|PROC_EX_MISS|PROC_EX_RESIST))
+ {
+ if (pTarget->GetTypeId() != TYPEID_PLAYER && pTarget->GetCreatureType() != CREATURE_TYPE_CRITTER)
+ ((Player*)this)->UpdateCombatSkills(pTarget, attType, MELEE_HIT_MISS, isVictim);
+ }
+ // Update defence if player is victim and parry/dodge/block
+ if (isVictim && procExtra&(PROC_EX_DODGE|PROC_EX_PARRY|PROC_EX_BLOCK))
+ ((Player*)this)->UpdateDefense();
+ }
+ // If exist crit/parry/dodge/block need update aura state (for victim and attacker)
+ if (procExtra & (PROC_EX_CRITICAL_HIT|PROC_EX_PARRY|PROC_EX_DODGE|PROC_EX_BLOCK))
+ {
+ // for victim
+ if (isVictim)
+ {
+ // if victim and dodge attack
+ if (procExtra&PROC_EX_DODGE)
+ {
+ //Update AURA_STATE on dodge
+ if (getClass() != CLASS_ROGUE) // skip Rogue Riposte
+ {
+ ModifyAuraState(AURA_STATE_DEFENSE, true);
+ StartReactiveTimer( REACTIVE_DEFENSE );
+ }
+ }
+ // if victim and parry attack
+ if (procExtra & PROC_EX_PARRY)
+ {
+ // For Hunters only Counterattack (skip Mongoose bite)
+ if (getClass() == CLASS_HUNTER)
+ {
+ ModifyAuraState(AURA_STATE_HUNTER_PARRY, true);
+ StartReactiveTimer( REACTIVE_HUNTER_PARRY );
+ }
+ else
+ {
+ ModifyAuraState(AURA_STATE_DEFENSE, true);
+ StartReactiveTimer( REACTIVE_DEFENSE );
+ }
+ }
+ // if and victim block attack
+ if (procExtra & PROC_EX_BLOCK)
+ {
+ ModifyAuraState(AURA_STATE_DEFENSE,true);
+ StartReactiveTimer( REACTIVE_DEFENSE );
+ }
+ }
+ else //For attacker
+ {
+ // Overpower on victim dodge
+ if (procExtra&PROC_EX_DODGE && GetTypeId() == TYPEID_PLAYER && getClass() == CLASS_WARRIOR)
+ {
+ ((Player*)this)->AddComboPoints(pTarget, 1);
+ StartReactiveTimer( REACTIVE_OVERPOWER );
+ }
+ // Enable AURA_STATE_CRIT on crit
+ if (procExtra & PROC_EX_CRITICAL_HIT)
+ {
+ ModifyAuraState(AURA_STATE_CRIT, true);
+ StartReactiveTimer( REACTIVE_CRIT );
+ if(getClass()==CLASS_HUNTER)
+ {
+ ModifyAuraState(AURA_STATE_HUNTER_CRIT_STRIKE, true);
+ StartReactiveTimer( REACTIVE_HUNTER_CRIT );
+ }
+ }
+ }
+ }
+ }
+
+ RemoveSpellList removedSpells;
+ ProcTriggeredList procTriggered;
+ // Fill procTriggered list
+ for(AuraMap::const_iterator itr = GetAuras().begin(); itr!= GetAuras().end(); ++itr)
+ {
+ SpellProcEventEntry const* spellProcEvent = NULL;
+ if(!IsTriggeredAtSpellProcEvent(itr->second, procSpell, procFlag, procExtra, attType, isVictim, (damage > 0), spellProcEvent))
+ continue;
+
+ procTriggered.push_back( ProcTriggeredData(spellProcEvent, itr->second) );
+ }
+ // Handle effects proceed this time
+ for(ProcTriggeredList::iterator i = procTriggered.begin(); i != procTriggered.end(); ++i)
+ {
+ // Some auras can be deleted in function called in this loop (except first, ofc)
+ // Until storing auars in std::multimap to hard check deleting by another way
+ if(i != procTriggered.begin())
+ {
+ bool found = false;
+ AuraMap::const_iterator lower = GetAuras().lower_bound(i->triggeredByAura_SpellPair);
+ AuraMap::const_iterator upper = GetAuras().upper_bound(i->triggeredByAura_SpellPair);
+ for(AuraMap::const_iterator itr = lower; itr!= upper; ++itr)
+ {
+ if(itr->second==i->triggeredByAura)
+ {
+ found = true;
+ break;
+ }
+ }
+ if(!found)
+ {
+// sLog.outDebug("Spell aura %u (id:%u effect:%u) has been deleted before call spell proc event handler", i->triggeredByAura->GetModifier()->m_auraname, i->triggeredByAura_SpellPair.first, i->triggeredByAura_SpellPair.second);
+// sLog.outDebug("It can be deleted one from early proccesed auras:");
+// for(ProcTriggeredList::iterator i2 = procTriggered.begin(); i != i2; ++i2)
+// sLog.outDebug(" Spell aura %u (id:%u effect:%u)", i->triggeredByAura->GetModifier()->m_auraname,i2->triggeredByAura_SpellPair.first,i2->triggeredByAura_SpellPair.second);
+// sLog.outDebug(" <end of list>");
+ continue;
+ }
+ }
+
+ SpellProcEventEntry const *spellProcEvent = i->spellProcEvent;
+ Aura *triggeredByAura = i->triggeredByAura;
+ Modifier *auraModifier = triggeredByAura->GetModifier();
+ SpellEntry const *spellInfo = triggeredByAura->GetSpellProto();
+ uint32 effIndex = triggeredByAura->GetEffIndex();
+ bool useCharges = triggeredByAura->m_procCharges > 0;
+ // For players set spell cooldown if need
+ uint32 cooldown = 0;
+ if (GetTypeId() == TYPEID_PLAYER && spellProcEvent && spellProcEvent->cooldown)
+ cooldown = spellProcEvent->cooldown;
+
+ switch(auraModifier->m_auraname)
+ {
+ case SPELL_AURA_PROC_TRIGGER_SPELL:
+ {
+ sLog.outDebug("ProcDamageAndSpell: casting spell %u (triggered by %s aura of spell %u)", spellInfo->Id,(isVictim?"a victim's":"an attacker's"), triggeredByAura->GetId());
+ // Don`t drop charge or add cooldown for not started trigger
+ if (!HandleProcTriggerSpell(pTarget, damage, triggeredByAura, procSpell, procFlag, procExtra, cooldown))
+ continue;
+ break;
+ }
+ case SPELL_AURA_PROC_TRIGGER_DAMAGE:
+ {
+ sLog.outDebug("ProcDamageAndSpell: doing %u damage from spell id %u (triggered by %s aura of spell %u)", auraModifier->m_amount, spellInfo->Id, (isVictim?"a victim's":"an attacker's"), triggeredByAura->GetId());
+ SpellNonMeleeDamage damageInfo(this, pTarget, spellInfo->Id, spellInfo->SchoolMask);
+ CalculateSpellDamage(&damageInfo, auraModifier->m_amount, spellInfo);
+ SendSpellNonMeleeDamageLog(&damageInfo);
+ DealSpellDamage(&damageInfo, true);
+ break;
+ }
+ case SPELL_AURA_MANA_SHIELD:
+ case SPELL_AURA_DUMMY:
+ {
+ sLog.outDebug("ProcDamageAndSpell: casting spell id %u (triggered by %s dummy aura of spell %u)", spellInfo->Id,(isVictim?"a victim's":"an attacker's"), triggeredByAura->GetId());
+ if (!HandleDummyAuraProc(pTarget, damage, triggeredByAura, procSpell, procFlag, procExtra, cooldown))
+ continue;
+ break;
+ }
+ case SPELL_AURA_MOD_HASTE:
+ {
+ sLog.outDebug("ProcDamageAndSpell: casting spell id %u (triggered by %s haste aura of spell %u)", spellInfo->Id,(isVictim?"a victim's":"an attacker's"), triggeredByAura->GetId());
+ if (!HandleHasteAuraProc(pTarget, damage, triggeredByAura, procSpell, procFlag, procExtra, cooldown))
+ continue;
+ break;
+ }
+ case SPELL_AURA_OVERRIDE_CLASS_SCRIPTS:
+ {
+ sLog.outDebug("ProcDamageAndSpell: casting spell id %u (triggered by %s aura of spell %u)", spellInfo->Id,(isVictim?"a victim's":"an attacker's"), triggeredByAura->GetId());
+ if (!HandleOverrideClassScriptAuraProc(pTarget, triggeredByAura, procSpell, cooldown))
+ continue;
+ break;
+ }
+ case SPELL_AURA_PRAYER_OF_MENDING:
+ {
+ sLog.outDebug("ProcDamageAndSpell: casting mending (triggered by %s dummy aura of spell %u)",
+ (isVictim?"a victim's":"an attacker's"),triggeredByAura->GetId());
+
+ HandleMeandingAuraProc(triggeredByAura);
+ break;
+ }
+ case SPELL_AURA_MOD_STUN:
+ // Remove by default, but if charge exist drop it
+ if (triggeredByAura->m_procCharges == 0)
+ removedSpells.push_back(triggeredByAura->GetId());
+ break;
+ case SPELL_AURA_MELEE_ATTACK_POWER_ATTACKER_BONUS:
+ case SPELL_AURA_RANGED_ATTACK_POWER_ATTACKER_BONUS:
+ // Hunter's Mark (1-4 Rangs)
+ if (spellInfo->SpellFamilyName == SPELLFAMILY_HUNTER && (spellInfo->SpellFamilyFlags&0x0000000000000400LL))
+ {
+ uint32 basevalue = triggeredByAura->GetBasePoints();
+ auraModifier->m_amount += basevalue/10;
+ if (auraModifier->m_amount > basevalue*4)
+ auraModifier->m_amount = basevalue*4;
+ }
+ break;
+ case SPELL_AURA_MOD_CASTING_SPEED:
+ // Skip melee hits or instant cast spells
+ if (procSpell == NULL || GetSpellCastTime(procSpell) == 0)
+ continue;
+ break;
+ case SPELL_AURA_REFLECT_SPELLS_SCHOOL:
+ // Skip Melee hits and spells ws wrong school
+ if (procSpell == NULL || (auraModifier->m_miscvalue & procSpell->SchoolMask) == 0)
+ continue;
+ break;
+ case SPELL_AURA_MOD_POWER_COST_SCHOOL_PCT:
+ case SPELL_AURA_MOD_POWER_COST_SCHOOL:
+ // Skip melee hits and spells ws wrong school or zero cost
+ if (procSpell == NULL ||
+ (procSpell->manaCost == 0 && procSpell->ManaCostPercentage == 0) || // Cost check
+ (auraModifier->m_miscvalue & procSpell->SchoolMask) == 0) // School check
+ continue;
+ break;
+ case SPELL_AURA_MECHANIC_IMMUNITY:
+ // Compare mechanic
+ if (procSpell==NULL || procSpell->Mechanic != auraModifier->m_miscvalue)
+ continue;
+ break;
+ case SPELL_AURA_MOD_MECHANIC_RESISTANCE:
+ // Compare mechanic
+ if (procSpell==NULL || procSpell->Mechanic != auraModifier->m_miscvalue)
+ continue;
+ break;
+ default:
+ // nothing do, just charges counter
+ break;
+ }
+ // Remove charge (aura can be removed by triggers)
+ if(useCharges)
+ {
+ // need found aura on drop (can be dropped by triggers)
+ AuraMap::const_iterator lower = GetAuras().lower_bound(i->triggeredByAura_SpellPair);
+ AuraMap::const_iterator upper = GetAuras().upper_bound(i->triggeredByAura_SpellPair);
+ for(AuraMap::const_iterator itr = lower; itr!= upper; ++itr)
+ {
+ if(itr->second == i->triggeredByAura)
+ {
+ triggeredByAura->m_procCharges -=1;
+ triggeredByAura->UpdateAuraCharges();
+ if (triggeredByAura->m_procCharges <= 0)
+ removedSpells.push_back(triggeredByAura->GetId());
+ break;
+ }
+ }
+ }
+ }
+ if (removedSpells.size())
+ {
+ // Sort spells and remove dublicates
+ removedSpells.sort();
+ removedSpells.unique();
+ // Remove auras from removedAuras
+ for(RemoveSpellList::const_iterator i = removedSpells.begin(); i != removedSpells.end();i++)
+ RemoveAurasDueToSpell(*i);
+ }
+ deep--;
+}
SpellSchoolMask Unit::GetMeleeDamageSchoolMask() const
{
@@ -10769,60 +12340,83 @@ Pet* Unit::CreateTamedPetFrom(Creature* creatureTarget,uint32 spell_id)
return pet;
}
-bool Unit::IsTriggeredAtSpellProcEvent(SpellEntry const* spellProto, SpellEntry const* procSpell, uint32 procFlag, WeaponAttackType attType, bool isVictim, uint32& cooldown )
+bool Unit::IsTriggeredAtSpellProcEvent(Aura* aura, SpellEntry const* procSpell, uint32 procFlag, uint32 procExtra, WeaponAttackType attType, bool isVictim, bool active, SpellProcEventEntry const*& spellProcEvent )
{
- SpellProcEventEntry const * spellProcEvent = spellmgr.GetSpellProcEvent(spellProto->Id);
+ SpellEntry const* spellProto = aura->GetSpellProto ();
- if(!spellProcEvent)
- {
- // used to prevent spam in log about same non-handled spells
- static std::set<uint32> nonHandledSpellProcSet;
+ // Get proc Event Entry
+ spellProcEvent = spellmgr.GetSpellProcEvent(spellProto->Id);
- if(spellProto->procFlags != 0 && nonHandledSpellProcSet.find(spellProto->Id)==nonHandledSpellProcSet.end())
- {
- sLog.outError("ProcDamageAndSpell: spell %u (%s aura source) not have record in `spell_proc_event`)",spellProto->Id,(isVictim?"a victim's":"an attacker's"));
- nonHandledSpellProcSet.insert(spellProto->Id);
- }
+ // Aura info stored here
+ Modifier *mod = aura->GetModifier();
+ // Skip this auras
+ if (isNonTriggerAura[mod->m_auraname])
+ return false;
+ // If not trigger by default and spellProcEvent==NULL - skip
+ if (!isTriggerAura[mod->m_auraname] && spellProcEvent==NULL)
+ return false;
- // spell.dbc use totally different flags, that only can create problems if used.
+ // Get EventProcFlag
+ uint32 EventProcFlag;
+ if (spellProcEvent && spellProcEvent->procFlags) // if exist get custom spellProcEvent->procFlags
+ EventProcFlag = spellProcEvent->procFlags;
+ else
+ EventProcFlag = spellProto->procFlags; // else get from spell proto
+ // Continue if no trigger exist
+ if (!EventProcFlag)
return false;
- }
// Check spellProcEvent data requirements
- if(!SpellMgr::IsSpellProcEventCanTriggeredBy(spellProcEvent, procSpell,procFlag))
+ if(!SpellMgr::IsSpellProcEventCanTriggeredBy(spellProcEvent, EventProcFlag, procSpell, procFlag, procExtra, active))
+ return false;
+
+ // Aura added by spell can`t trogger from self (prevent drop cahres/do triggers)
+ // But except periodic triggers (can triggered from self)
+ if(procSpell && procSpell->Id == spellProto->Id && !(spellProto->procFlags&PROC_FLAG_ON_TAKE_PERIODIC))
return false;
// Check if current equipment allows aura to proc
- if(!isVictim && GetTypeId() == TYPEID_PLAYER )
+ if(!isVictim && GetTypeId() == TYPEID_PLAYER)
{
if(spellProto->EquippedItemClass == ITEM_CLASS_WEAPON)
{
- Item *item = ((Player*)this)->GetWeaponForAttack(attType,true);
+ Item *item = NULL;
+ if(attType == BASE_ATTACK)
+ item = ((Player*)this)->GetItemByPos(INVENTORY_SLOT_BAG_0, EQUIPMENT_SLOT_MAINHAND);
+ else if (attType == OFF_ATTACK)
+ item = ((Player*)this)->GetItemByPos(INVENTORY_SLOT_BAG_0, EQUIPMENT_SLOT_OFFHAND);
+ else
+ item = ((Player*)this)->GetItemByPos(INVENTORY_SLOT_BAG_0, EQUIPMENT_SLOT_RANGED);
+
+ if (!((Player*)this)->IsUseEquipedWeapon(attType==BASE_ATTACK))
+ return false;
- if(!item || !((1<<item->GetProto()->SubClass) & spellProto->EquippedItemSubClassMask))
+ if(!item || item->IsBroken() || item->GetProto()->Class != ITEM_CLASS_WEAPON || !((1<<item->GetProto()->SubClass) & spellProto->EquippedItemSubClassMask))
return false;
}
else if(spellProto->EquippedItemClass == ITEM_CLASS_ARMOR)
{
// Check if player is wearing shield
- Item *item = ((Player*)this)->GetShield(true);
- if(!item || !((1<<item->GetProto()->SubClass) & spellProto->EquippedItemSubClassMask))
+ Item *item = ((Player*)this)->GetItemByPos(INVENTORY_SLOT_BAG_0, EQUIPMENT_SLOT_OFFHAND);
+ if(!item || item->IsBroken() || item->GetProto()->Class != ITEM_CLASS_ARMOR || !((1<<item->GetProto()->SubClass) & spellProto->EquippedItemSubClassMask))
return false;
}
}
-
+ // Get chance from spell
float chance = (float)spellProto->procChance;
-
- if(Player* modOwner = GetSpellModOwner())
- modOwner->ApplySpellMod(spellProto->Id,SPELLMOD_CHANCE_OF_SUCCESS,chance);
-
+ // If in spellProcEvent exist custom chance, chance = spellProcEvent->customChance;
+ if(spellProcEvent && spellProcEvent->customChance)
+ chance = spellProcEvent->customChance;
+ // If PPM exist calculate chance from PPM
if(!isVictim && spellProcEvent && spellProcEvent->ppmRate != 0)
{
uint32 WeaponSpeed = GetAttackTime(attType);
chance = GetPPMProcChance(WeaponSpeed, spellProcEvent->ppmRate);
}
+ // Apply chance modifer aura
+ if(Player* modOwner = GetSpellModOwner())
+ modOwner->ApplySpellMod(spellProto->Id,SPELLMOD_CHANCE_OF_SUCCESS,chance);
- cooldown = spellProcEvent ? spellProcEvent->cooldown : 0;
return roll_chance_f(chance);
}
diff --git a/src/game/Unit.h b/src/game/Unit.h
index 24122368e61..d0cdb24f57c 100644
--- a/src/game/Unit.h
+++ b/src/game/Unit.h
@@ -583,6 +583,50 @@ struct CleanDamage
MeleeHitOutcome hitOutCome;
};
+// Struct for use in Unit::CalculateMeleeDamage
+// Need create structure like in SMSG_ATTACKERSTATEUPDATE opcode
+struct CalcDamageInfo
+{
+ Unit *attacker; // Attacker
+ Unit *target; // Target for damage
+ uint32 damageSchoolMask;
+ uint32 damage;
+ uint32 absorb;
+ uint32 resist;
+ uint32 blocked_amount;
+ uint32 HitInfo;
+ uint32 TargetState;
+// Helper
+ WeaponAttackType attackType; //
+ uint32 procAttacker;
+ uint32 procVictim;
+ uint32 procEx;
+ uint32 cleanDamage; // Used only fo rage calcultion
+ MeleeHitOutcome hitOutCome; // TODO: remove this field (need use TargetState)
+};
+
+// Spell damage info structure based on structure sending in SMSG_SPELLNONMELEEDAMAGELOG opcode
+struct SpellNonMeleeDamage{
+ SpellNonMeleeDamage(Unit *_attacker, Unit *_target, uint32 _SpellID, uint32 _schoolMask) :
+ attacker(_attacker), target(_target), SpellID(_SpellID), damage(0), schoolMask(_schoolMask),
+ absorb(0), resist(0), phusicalLog(false), unused(false), blocked(0), HitInfo(0), cleanDamage(0) {}
+ Unit *target;
+ Unit *attacker;
+ uint32 SpellID;
+ uint32 damage;
+ uint32 schoolMask;
+ uint32 absorb;
+ uint32 resist;
+ bool phusicalLog;
+ bool unused;
+ uint32 blocked;
+ uint32 HitInfo;
+ // Used for help
+ uint32 cleanDamage;
+};
+
+uint32 createProcExtendMask(SpellNonMeleeDamage *damageInfo, SpellMissInfo missCondition);
+
struct UnitActionBarEntry
{
uint32 Type;
@@ -692,6 +736,8 @@ enum ReactiveType
// delay time next attack to prevent client attack animation problems
#define ATTACK_DISPLAY_DELAY 200
+struct SpellProcEventEntry; // used only privately
+
class TRINITY_DLL_SPEC Unit : public WorldObject
{
public:
@@ -849,15 +895,23 @@ class TRINITY_DLL_SPEC Unit : public WorldObject
uint16 GetMaxSkillValueForLevel(Unit const* target = NULL) const { return (target ? getLevelForTarget(target) : getLevel()) * 5; }
void RemoveSpellbyDamageTaken(uint32 damage, uint32 spell);
uint32 DealDamage(Unit *pVictim, uint32 damage, CleanDamage const* cleanDamage, DamageEffectType damagetype, SpellSchoolMask damageSchoolMask, SpellEntry const *spellProto, bool durabilityLoss);
- void DealFlatDamage(Unit *pVictim, SpellEntry const *spellInfo, uint32 *damage, CleanDamage *cleanDamage, bool *crit = false, bool isTriggeredSpell = false);
- void DoAttackDamage(Unit *pVictim, uint32 *damage, CleanDamage *cleanDamage, uint32 *blocked_amount, SpellSchoolMask damageSchoolMask, uint32 *hitInfo, VictimState *victimState, uint32 *absorbDamage, uint32 *resistDamage, WeaponAttackType attType, SpellEntry const *spellCasted = NULL, bool isTriggeredSpell = false);
- void CastMeleeProcDamageAndSpell(Unit* pVictim, uint32 damage, SpellSchoolMask damageSchoolMask, WeaponAttackType attType, MeleeHitOutcome outcome, SpellEntry const *spellCasted = NULL, bool isTriggeredSpell = false);
- void ProcDamageAndSpell(Unit *pVictim, uint32 procAttacker, uint32 procVictim, uint32 damage = 0, SpellSchoolMask damageSchoolMask = SPELL_SCHOOL_MASK_NONE, SpellEntry const *procSpell = NULL, bool isTriggeredSpell = false, WeaponAttackType attType = BASE_ATTACK);
+ void ProcDamageAndSpell(Unit *pVictim, uint32 procAttacker, uint32 procVictim, uint32 procEx, uint32 amount, WeaponAttackType attType = BASE_ATTACK, SpellEntry const *procSpell = NULL);
+ void ProcDamageAndSpellFor( bool isVictim, Unit * pTarget, uint32 procFlag, uint32 procExtra, WeaponAttackType attType, SpellEntry const * procSpell, uint32 damage );
+
void HandleEmoteCommand(uint32 anim_id);
void AttackerStateUpdate (Unit *pVictim, WeaponAttackType attType = BASE_ATTACK, bool extra = false );
float MeleeMissChanceCalc(const Unit *pVictim, WeaponAttackType attType) const;
+
+ void CalculateMeleeDamage(Unit *pVictim, uint32 damage, CalcDamageInfo *damageInfo, WeaponAttackType attackType = BASE_ATTACK);
+ void DealMeleeDamage(CalcDamageInfo *damageInfo, bool durabilityLoss);
+
+ void CalculateSpellDamage(SpellNonMeleeDamage *damageInfo, int32 damage, SpellEntry const *spellInfo, WeaponAttackType attackType = BASE_ATTACK);
+ void DealSpellDamage(SpellNonMeleeDamage *damageInfo, bool durabilityLoss);
+
+ float MeleeSpellMissChance(Unit *pVictim, WeaponAttackType attType, int32 skillDiff, SpellEntry const *spell);
+ SpellMissInfo MeleeSpellHitResult(Unit *pVictim, SpellEntry const *spell);
SpellMissInfo MagicSpellHitResult(Unit *pVictim, SpellEntry const *spell);
SpellMissInfo SpellHitResult(Unit *pVictim, SpellEntry const *spell, bool canReflect = false);
@@ -872,7 +926,7 @@ class TRINITY_DLL_SPEC Unit : public WorldObject
uint32 GetWeaponSkillValue(WeaponAttackType attType, Unit const* target = NULL) const;
float GetWeaponProcChance() const;
float GetPPMProcChance(uint32 WeaponSpeed, float PPM) const;
- MeleeHitOutcome RollPhysicalOutcomeAgainst (const Unit *pVictim, WeaponAttackType attType, SpellEntry const *spellInfo);
+
MeleeHitOutcome RollMeleeOutcomeAgainst (const Unit *pVictim, WeaponAttackType attType) const;
MeleeHitOutcome RollMeleeOutcomeAgainst (const Unit *pVictim, WeaponAttackType attType, int32 crit_chance, int32 miss_chance, int32 dodge_chance, int32 parry_chance, int32 block_chance, bool SpellCasted ) const;
@@ -948,7 +1002,9 @@ class TRINITY_DLL_SPEC Unit : public WorldObject
void DeMorph();
void SendAttackStart(Unit* pVictim);
+ void SendAttackStateUpdate(CalcDamageInfo *damageInfo);
void SendAttackStateUpdate(uint32 HitInfo, Unit *target, uint8 SwingType, SpellSchoolMask damageSchoolMask, uint32 Damage, uint32 AbsorbDamage, uint32 Resist, VictimState TargetState, uint32 BlockedAmount);
+ void SendSpellNonMeleeDamageLog(SpellNonMeleeDamage *log);
void SendSpellNonMeleeDamageLog(Unit *target,uint32 SpellID,uint32 Damage, SpellSchoolMask damageSchoolMask,uint32 AbsorbedDamage, uint32 Resist,bool PhysicalDamage, uint32 Blocked, bool CriticalHit = false);
void SendSpellMiss(Unit *target, uint32 spellID, SpellMissInfo missInfo);
@@ -1228,7 +1284,8 @@ class TRINITY_DLL_SPEC Unit : public WorldObject
int32 SpellBaseHealingBonusForVictim(SpellSchoolMask schoolMask, Unit *pVictim);
uint32 SpellDamageBonus(Unit *pVictim, SpellEntry const *spellProto, uint32 damage, DamageEffectType damagetype);
uint32 SpellHealingBonus(SpellEntry const *spellProto, uint32 healamount, DamageEffectType damagetype, Unit *pVictim);
- bool isSpellCrit(Unit *pVictim, SpellEntry const *spellProto, SpellSchoolMask schoolMask, WeaponAttackType attackType);
+ bool isSpellBlocked(Unit *pVictim, SpellEntry const *spellProto, WeaponAttackType attackType = BASE_ATTACK);
+ bool isSpellCrit(Unit *pVictim, SpellEntry const *spellProto, SpellSchoolMask schoolMask, WeaponAttackType attackType = BASE_ATTACK);
uint32 SpellCriticalBonus(SpellEntry const *spellProto, uint32 damage, Unit *pVictim);
void SetLastManaUse(uint32 spellCastTime) { m_lastManaUse = spellCastTime; }
@@ -1371,12 +1428,11 @@ class TRINITY_DLL_SPEC Unit : public WorldObject
void SendAttackStop(Unit* victim); // only from AttackStop(Unit*)
//void SendAttackStart(Unit* pVictim); // only from Unit::AttackStart(Unit*)
- void ProcDamageAndSpellFor( bool isVictim, Unit * pTarget, uint32 procFlag, AuraTypeSet const& procAuraTypes, WeaponAttackType attType, SpellEntry const * procSpell, uint32 damage, SpellSchoolMask damageSchoolMask );
- bool IsTriggeredAtSpellProcEvent( SpellEntry const* spellProto, SpellEntry const* procSpell, uint32 procFlag, WeaponAttackType attType, bool isVictim, uint32& cooldown );
- bool HandleDummyAuraProc( Unit *pVictim, uint32 damage, Aura* triggeredByAura, SpellEntry const* procSpell, uint32 procFlag, uint32 cooldown);
- bool HandleProcTriggerSpell(Unit *pVictim, uint32 damage, Aura* triggeredByAura, SpellEntry const* procSpell, uint32 procFlag, WeaponAttackType attType, uint32 cooldown);
- bool HandleHasteAuraProc( Unit *pVictim, uint32 damage, Aura* triggeredByAura, SpellEntry const* procSpell, uint32 procFlag, uint32 cooldown);
- bool HandleOverrideClassScriptAuraProc(Unit *pVictim, Aura* triggeredByAura, SpellEntry const *procSpell, uint32 cooldown);
+ bool IsTriggeredAtSpellProcEvent( Aura* aura, SpellEntry const* procSpell, uint32 procFlag, uint32 procExtra, WeaponAttackType attType, bool isVictim, bool active, SpellProcEventEntry const*& spellProcEvent );
+ bool HandleDummyAuraProc( Unit *pVictim, uint32 damage, Aura* triggredByAura, SpellEntry const *procSpell, uint32 procFlag, uint32 procEx, uint32 cooldown);
+ bool HandleHasteAuraProc( Unit *pVictim, uint32 damage, Aura* triggredByAura, SpellEntry const *procSpell, uint32 procFlag, uint32 procEx, uint32 cooldown);
+ bool HandleProcTriggerSpell(Unit *pVictim, uint32 damage, Aura* triggredByAura, SpellEntry const *procSpell, uint32 procFlag, uint32 procEx, uint32 cooldown);
+ bool HandleOverrideClassScriptAuraProc(Unit *pVictim, Aura* triggredByAura, SpellEntry const *procSpell, uint32 cooldown);
bool HandleMeandingAuraProc(Aura* triggeredByAura);
uint32 m_state; // Even derived shouldn't modify