+static float debugChance = 0.0f; +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) + { + SpellEntry const *spellProto = itr->second->GetSpellProto(); + if(spellProto==NULL || spellProto->procFlags==0) + continue; + // Aura info stored here + Modifier *mod = itr->second->GetModifier(); + SpellProcEventEntry const *spellProcEvent = spellmgr.GetSpellProcEvent(spellProto->Id); + + // If not trigger by default and spellProcEvent==NULL - skip + if (!isTriggerAura[mod->m_auraname] && spellProcEvent==NULL) + continue; + + // Check spellProcEvent data requirements + if(!SpellMgr::IsSpellProcEventCanTriggeredBy(spellProcEvent, spellProto->procFlags, procSpell, procFlag, procExtra, damage > 0)) + continue; + // 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)) + continue; + // Check if current equipment allows aura to proc + if(!isVictim && GetTypeId() == TYPEID_PLAYER) + { + if(spellProto->EquippedItemClass == ITEM_CLASS_WEAPON) + { + 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)) + continue; + + if(!item || item->IsBroken() || item->GetProto()->Class != ITEM_CLASS_WEAPON || !((1<GetProto()->SubClass) & spellProto->EquippedItemSubClassMask)) + continue; + } + else if(spellProto->EquippedItemClass == ITEM_CLASS_ARMOR) + { + // Check if player is wearing shield + Item *item = ((Player*)this)->GetItemByPos(INVENTORY_SLOT_BAG_0, EQUIPMENT_SLOT_OFFHAND); + if(!item || item->IsBroken() || item->GetProto()->Class != ITEM_CLASS_ARMOR || !((1<GetProto()->SubClass) & spellProto->EquippedItemSubClassMask)) + continue; + } + } + // Get chance from spell + float chance = (float)spellProto->procChance; + // 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); + + if(roll_chance_f(chance+debugChance)) + 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(" "); + continue; + } + } + + SpellProcEventEntry const *spellProcEvent = i->spellProcEvent; + Aura *triggeredByAura = i->triggeredByAura; + Modifier *auraModifier = triggeredByAura->GetModifier(); + SpellEntry const *spellInfo = triggeredByAura->GetSpellProto(); + uint32 effIndex = triggeredByAura->GetEffIndex(); + // For players set spell cooldown if need + uint32 cooldown = 0; + if (GetTypeId() == TYPEID_PLAYER && spellProcEvent && spellProcEvent->Cooldown) + cooldown = spellProcEvent->Cooldown; + + // For players check spell cooldown + if (cooldown && ((Player *)this)->HasSpellCooldown(spellInfo->Id)) + continue; + + 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)) + continue; + // For players add colldown if need + if (cooldown) + ((Player *)this)->AddSpellCooldown(spellInfo->Id, 0, time(NULL) + cooldown); + 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->School); + CalculateSpellDamage(&damageInfo, auraModifier->m_amount, spellInfo); + SendSpellNonMeleeDamageLog(&damageInfo); + DealSpellDamage(&damageInfo, true); + break; + } + case SPELL_AURA_MANA_SHIELD: + case SPELL_AURA_MOD_HASTE: + case SPELL_AURA_DUMMY: + case SPELL_AURA_DUMMY_3: + { + 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()); + HandleDummyAuraProc(pTarget, spellInfo, effIndex, damage, triggeredByAura, procSpell, procFlag, procExtra); + // For players add colldown if need + if (cooldown) + ((Player *)this)->AddSpellCooldown(spellInfo->Id, 0, time(NULL) + cooldown); + 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_RANGED_ATTACK_POWER_ATTACKER_BONUS: + // Hunter's Mark (1-4 Rangs) + if (spellInfo->SpellFamilyName == SPELLFAMILY_HUNTER && (spellInfo->SpellFamilyFlags&0x0000000000000400LL)) + { + uint32 basevalue = triggeredByAura->GetBasePoints()+1; + 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(sCastTimesStore.LookupEntry(procSpell->CastingTimeIndex)) == 0) + continue; + break; + case SPELL_AURA_REFLECT_SPELLS_SCHOOL: + // Skip Melee hits and spells ws wrong school + if (procSpell == NULL || (auraModifier->m_miscvalue & (1<School)) == 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 & (1<School)) == 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) + // Not validate aura on check (for speed) + if(triggeredByAura->m_procCharges > 0) + { + // 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; + if (triggeredByAura->m_procCharges == 0) + removedSpells.push_back(triggeredByAura->GetId()); + break; + } + } + } + } + // Remove auras from removedAuras + for(RemoveSpellList::const_iterator i = removedSpells.begin(); i != removedSpells.end();i++) + RemoveAurasDueToSpell(*i); + deep--; +}