diff options
| author | megamage <none@none> | 2008-11-26 15:14:03 -0600 |
|---|---|---|
| committer | megamage <none@none> | 2008-11-26 15:14:03 -0600 |
| commit | da6c516fdf16a59ddd5dea862a50e8191966cc16 (patch) | |
| tree | 3394a46bf9be7e13127ba8b1b841c8050bb325d3 /src/game/Unit.cpp | |
| parent | 0521e2d017bebf8f8ae24313387f33c866453411 (diff) | |
*Procflag patch by DiSlord.
--HG--
branch : trunk
Diffstat (limited to 'src/game/Unit.cpp')
| -rw-r--r-- | src/game/Unit.cpp | 2026 |
1 files changed, 1810 insertions, 216 deletions
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); } |
