diff options
| author | ariel- <ariel-@users.noreply.github.com> | 2018-01-13 06:45:21 -0300 |
|---|---|---|
| committer | ariel- <ariel-@users.noreply.github.com> | 2018-01-13 06:45:21 -0300 |
| commit | cb9e72e521d3cc415dd15bf6912c87f89e41b92a (patch) | |
| tree | be9e692f0ddf9f080f923c7396de19e4ca869c6f /src/server/game/Entities | |
| parent | 1c60af632888433b29a27bee76e82e96632d4096 (diff) | |
Core/Auras: removed caster dependency from core
- Decoupled Unit logic: split of spell critical chance into done (caster bonuses) and taken (target bonuses), this allows to precalculate caster bonuses on aura apply and then check victim's auras on damage/healing calc
- Made static a bunch of methods (they no longer have this pointer because they are now called from periodic handlers which may or may not have an active caster in world)
- Simplified all AuraEffect bonuses into AuraEffect::_amount, no more duplicated code
- Critical chance and whether or not caster is player owned unit (for resilience calcs) is now saved one level upper, on Aura itself (it's impossible as of 3.3.5 to have different effects with different critical chances)
- Minor cleanup of SPELL_DAMAGE_CLASS_NONE and Arcane Potency (#18813) crit handling
Closes #19876
Diffstat (limited to 'src/server/game/Entities')
| -rw-r--r-- | src/server/game/Entities/Pet/Pet.cpp | 6 | ||||
| -rw-r--r-- | src/server/game/Entities/Player/Player.cpp | 18 | ||||
| -rw-r--r-- | src/server/game/Entities/Player/Player.h | 2 | ||||
| -rw-r--r-- | src/server/game/Entities/Unit/Unit.cpp | 955 | ||||
| -rw-r--r-- | src/server/game/Entities/Unit/Unit.h | 43 |
5 files changed, 547 insertions, 477 deletions
diff --git a/src/server/game/Entities/Pet/Pet.cpp b/src/server/game/Entities/Pet/Pet.cpp index abd82bf7f4a..29c275a0922 100644 --- a/src/server/game/Entities/Pet/Pet.cpp +++ b/src/server/game/Entities/Pet/Pet.cpp @@ -1177,6 +1177,8 @@ void Pet::_LoadAuras(uint32 timediff) int32 maxduration = fields[11].GetInt32(); int32 remaintime = fields[12].GetInt32(); uint8 remaincharges = fields[13].GetUInt8(); + float critChance = fields[14].GetFloat(); + bool applyResilience = fields[15].GetBool(); SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(spellid); if (!spellInfo) @@ -1210,7 +1212,7 @@ void Pet::_LoadAuras(uint32 timediff) aura->Remove(); continue; } - aura->SetLoadedState(maxduration, remaintime, remaincharges, stackcount, recalculatemask, &damage[0]); + aura->SetLoadedState(maxduration, remaintime, remaincharges, stackcount, recalculatemask, critChance, applyResilience, &damage[0]); aura->ApplyForTargets(); TC_LOG_DEBUG("entities.pet", "Added aura spellid %u, effectmask %u", spellInfo->Id, effmask); } @@ -1275,6 +1277,8 @@ void Pet::_SaveAuras(SQLTransaction& trans) stmt->setInt32(index++, itr->second->GetMaxDuration()); stmt->setInt32(index++, itr->second->GetDuration()); stmt->setUInt8(index++, itr->second->GetCharges()); + stmt->setFloat(index++, itr->second->GetCritChance()); + stmt->setBool (index++, itr->second->CanApplyResilience()); trans->Append(stmt); } diff --git a/src/server/game/Entities/Player/Player.cpp b/src/server/game/Entities/Player/Player.cpp index 862f36b8dfc..b98daee4fd3 100644 --- a/src/server/game/Entities/Player/Player.cpp +++ b/src/server/game/Entities/Player/Player.cpp @@ -771,7 +771,7 @@ uint32 Player::EnvironmentalDamage(EnviromentalDamage type, uint32 damage) case DAMAGE_SLIME: { DamageInfo dmgInfo(this, this, damage, nullptr, type == DAMAGE_LAVA ? SPELL_SCHOOL_MASK_FIRE : SPELL_SCHOOL_MASK_NATURE, DIRECT_DAMAGE, BASE_ATTACK); - CalcAbsorbResist(dmgInfo); + Unit::CalcAbsorbResist(dmgInfo); absorb = dmgInfo.GetAbsorb(); resist = dmgInfo.GetResist(); damage = dmgInfo.GetDamage(); @@ -781,7 +781,7 @@ uint32 Player::EnvironmentalDamage(EnviromentalDamage type, uint32 damage) break; } - DealDamageMods(this, damage, &absorb); + Unit::DealDamageMods(this, damage, &absorb); WorldPacket data(SMSG_ENVIRONMENTALDAMAGELOG, (21)); data << uint64(GetGUID()); @@ -791,7 +791,7 @@ uint32 Player::EnvironmentalDamage(EnviromentalDamage type, uint32 damage) data << uint32(resist); SendMessageToSet(&data, true); - uint32 final_damage = DealDamage(this, damage, nullptr, SELF_DAMAGE, SPELL_SCHOOL_MASK_NORMAL, nullptr, false); + uint32 final_damage = Unit::DealDamage(this, this, damage, nullptr, SELF_DAMAGE, SPELL_SCHOOL_MASK_NORMAL, nullptr, false); if (!IsAlive()) { @@ -17769,8 +17769,8 @@ void Player::_LoadAuras(PreparedQueryResult result, uint32 timediff) /* 0 1 2 3 4 5 6 7 8 9 10 QueryResult* result = CharacterDatabase.PQuery("SELECT casterGuid, spell, effectMask, recalculateMask, stackCount, amount0, amount1, amount2, base_amount0, base_amount1, base_amount2, - 11 12 13 - maxDuration, remainTime, remainCharges FROM character_aura WHERE guid = '%u'", GetGUID().GetCounter()); + 11 12 13 14 15 + maxDuration, remainTime, remainCharges, critChance, applyResilience FROM character_aura WHERE guid = '%u'", GetGUID().GetCounter()); */ if (result) @@ -17794,6 +17794,8 @@ void Player::_LoadAuras(PreparedQueryResult result, uint32 timediff) int32 maxduration = fields[11].GetInt32(); int32 remaintime = fields[12].GetInt32(); uint8 remaincharges = fields[13].GetUInt8(); + float critChance = fields[14].GetFloat(); + bool applyResilience = fields[15].GetBool(); SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(spellid); if (!spellInfo) @@ -17831,7 +17833,7 @@ void Player::_LoadAuras(PreparedQueryResult result, uint32 timediff) continue; } - aura->SetLoadedState(maxduration, remaintime, remaincharges, stackcount, recalculatemask, &damage[0]); + aura->SetLoadedState(maxduration, remaintime, remaincharges, stackcount, recalculatemask, critChance, applyResilience, &damage[0]); aura->ApplyForTargets(); TC_LOG_DEBUG("entities.player", "Player::_LoadAuras: Added aura (SpellID: %u, EffectMask: %u) to player '%s (%s)", spellInfo->Id, effmask, GetName().c_str(), GetGUID().ToString().c_str()); @@ -19517,7 +19519,9 @@ void Player::_SaveAuras(SQLTransaction& trans) stmt->setInt32(index++, baseDamage[2]); stmt->setInt32(index++, itr->second->GetMaxDuration()); stmt->setInt32(index++, itr->second->GetDuration()); - stmt->setUInt8(index, itr->second->GetCharges()); + stmt->setUInt8(index++, itr->second->GetCharges()); + stmt->setFloat(index++, itr->second->GetCritChance()); + stmt->setBool (index++, itr->second->CanApplyResilience()); trans->Append(stmt); } } diff --git a/src/server/game/Entities/Player/Player.h b/src/server/game/Entities/Player/Player.h index d74b36c1e84..271cd465709 100644 --- a/src/server/game/Entities/Player/Player.h +++ b/src/server/game/Entities/Player/Player.h @@ -1594,6 +1594,8 @@ class TC_GAME_API Player : public Unit, public GridObject<Player> uint32 GetBaseSpellPowerBonus() const { return m_baseSpellPower; } int32 GetSpellPenetrationItemMod() const { return m_spellPenetrationItemMod; } + bool CanApplyResilience() const override { return true; } + float GetExpertiseDodgeOrParryReduction(WeaponAttackType attType) const; void UpdateBlockPercentage(); void UpdateCritPercentage(WeaponAttackType attType); diff --git a/src/server/game/Entities/Unit/Unit.cpp b/src/server/game/Entities/Unit/Unit.cpp index 811be1e2076..5020baa928f 100644 --- a/src/server/game/Entities/Unit/Unit.cpp +++ b/src/server/game/Entities/Unit/Unit.cpp @@ -625,7 +625,7 @@ bool Unit::HasBreakableByDamageCrowdControlAura(Unit* excludeCasterChannel) cons || HasBreakableByDamageAuraType(SPELL_AURA_TRANSFORM, excludeAura)); } -void Unit::DealDamageMods(Unit const* victim, uint32 &damage, uint32* absorb) const +/*static*/ void Unit::DealDamageMods(Unit const* victim, uint32& damage, uint32* absorb) { if (!victim || !victim->IsAlive() || victim->HasUnitState(UNIT_STATE_IN_FLIGHT) || (victim->GetTypeId() == TYPEID_UNIT && victim->ToCreature()->IsEvadingAttacks())) { @@ -635,20 +635,20 @@ void Unit::DealDamageMods(Unit const* victim, uint32 &damage, uint32* absorb) co } } -uint32 Unit::DealDamage(Unit* victim, uint32 damage, CleanDamage const* cleanDamage, DamageEffectType damagetype, SpellSchoolMask damageSchoolMask, SpellInfo const* spellProto, bool durabilityLoss) +/*static*/ uint32 Unit::DealDamage(Unit* attacker, Unit* victim, uint32 damage, CleanDamage const* cleanDamage, DamageEffectType damagetype, SpellSchoolMask damageSchoolMask, SpellInfo const* spellProto, bool durabilityLoss) { uint32 rage_damage = damage + (cleanDamage ? cleanDamage->absorbed_damage : 0); if (victim->IsAIEnabled) - victim->GetAI()->DamageTaken(this, damage); + victim->GetAI()->DamageTaken(attacker, damage); - if (IsAIEnabled) - GetAI()->DamageDealt(victim, damage, damagetype); + if (attacker && attacker->IsAIEnabled) + attacker->GetAI()->DamageDealt(victim, damage, damagetype); // Hook for OnDamage Event - sScriptMgr->OnDamage(this, victim, damage); + sScriptMgr->OnDamage(attacker, victim, damage); - if (victim->GetTypeId() == TYPEID_PLAYER && this != victim) + if (victim->GetTypeId() == TYPEID_PLAYER && attacker != victim) { // Signal to pets that their owner was attacked - except when DOT. if (damagetype != DOT) @@ -656,7 +656,7 @@ uint32 Unit::DealDamage(Unit* victim, uint32 damage, CleanDamage const* cleanDam for (Unit* controlled : victim->m_Controlled) if (Creature* cControlled = controlled->ToCreature()) if (cControlled->IsAIEnabled) - cControlled->AI()->OwnerAttackedBy(this); + cControlled->AI()->OwnerAttackedBy(attacker); } if (victim->ToPlayer()->GetCommandStatus(CHEAT_GOD)) @@ -674,16 +674,20 @@ uint32 Unit::DealDamage(Unit* victim, uint32 damage, CleanDamage const* cleanDam else victim->RemoveAurasWithInterruptFlags(AURA_INTERRUPT_FLAG_TAKE_DAMAGE, 0); - // interrupt spells with SPELL_INTERRUPT_FLAG_ABORT_ON_DMG on absorbed damage (no dots) - if (!damage && damagetype != DOT && cleanDamage && cleanDamage->absorbed_damage) - if (victim != this && victim->GetTypeId() == TYPEID_PLAYER) - if (Spell* spell = victim->m_currentSpells[CURRENT_GENERIC_SPELL]) - if (spell->getState() == SPELL_STATE_PREPARING) - { - uint32 interruptFlags = spell->m_spellInfo->InterruptFlags; - if ((interruptFlags & SPELL_INTERRUPT_FLAG_ABORT_ON_DMG) != 0) - victim->InterruptNonMeleeSpells(false); - } + // interrupt spells with SPELL_INTERRUPT_FLAG_ABORT_ON_DMG on absorbed damage (no dots) + if (!damage && damagetype != DOT && cleanDamage && cleanDamage->absorbed_damage) + { + if (victim != attacker && victim->GetTypeId() == TYPEID_PLAYER) + { + if (Spell* spell = victim->m_currentSpells[CURRENT_GENERIC_SPELL]) + if (spell->getState() == SPELL_STATE_PREPARING) + { + uint32 interruptFlags = spell->m_spellInfo->InterruptFlags; + if ((interruptFlags & SPELL_INTERRUPT_FLAG_ABORT_ON_DMG) != 0) + victim->InterruptNonMeleeSpells(false); + } + } + } // We're going to call functions which can modify content of the list during iteration over it's elements // Let's copy the list so we can prevent iterator invalidation @@ -706,13 +710,13 @@ uint32 Unit::DealDamage(Unit* victim, uint32 damage, CleanDamage const* cleanDam uint32 share = CalculatePct(damage, (*i)->GetAmount()); /// @todo check packets if damage is done by victim, or by attacker of victim - DealDamageMods(shareDamageTarget, share, nullptr); - DealDamage(shareDamageTarget, share, nullptr, NODAMAGE, spell->GetSchoolMask(), spell, false); + Unit::DealDamageMods(shareDamageTarget, share, nullptr); + Unit::DealDamage(attacker, shareDamageTarget, share, nullptr, NODAMAGE, spell->GetSchoolMask(), spell, false); } } // Rage from Damage made (only from direct weapon damage) - if (cleanDamage && damagetype == DIRECT_DAMAGE && this != victim && getPowerType() == POWER_RAGE) + if (attacker && cleanDamage && damagetype == DIRECT_DAMAGE && attacker != victim && attacker->getPowerType() == POWER_RAGE) { uint32 weaponSpeedHitFactor; @@ -721,11 +725,11 @@ uint32 Unit::DealDamage(Unit* victim, uint32 damage, CleanDamage const* cleanDam case BASE_ATTACK: case OFF_ATTACK: { - weaponSpeedHitFactor = uint32(GetAttackTime(cleanDamage->attackType) / 1000.0f * (cleanDamage->attackType == BASE_ATTACK ? 3.5f : 1.75f)); + weaponSpeedHitFactor = uint32(attacker->GetAttackTime(cleanDamage->attackType) / 1000.0f * (cleanDamage->attackType == BASE_ATTACK ? 3.5f : 1.75f)); if (cleanDamage->hitOutCome == MELEE_HIT_CRIT) weaponSpeedHitFactor *= 2; - RewardRage(rage_damage, weaponSpeedHitFactor, true); + attacker->RewardRage(rage_damage, weaponSpeedHitFactor, true); break; } case RANGED_ATTACK: @@ -744,18 +748,18 @@ uint32 Unit::DealDamage(Unit* victim, uint32 damage, CleanDamage const* cleanDam return 0; } - TC_LOG_DEBUG("entities.unit", "DealDamageStart"); - uint32 health = victim->GetHealth(); - TC_LOG_DEBUG("entities.unit", "%s dealt %u damage to %s", GetGUID().ToString().c_str(), damage, victim->GetGUID().ToString().c_str()); // duel ends when player has 1 or less hp bool duel_hasEnded = false; bool duel_wasMounted = false; if (victim->GetTypeId() == TYPEID_PLAYER && victim->ToPlayer()->duel && damage >= (health-1)) { + if (!attacker) + return 0; + // prevent kill only if killed in duel and killed by opponent or opponent controlled creature - if (victim->ToPlayer()->duel->opponent == this || victim->ToPlayer()->duel->opponent->GetGUID() == GetOwnerGUID()) + if (victim->ToPlayer()->duel->opponent == attacker || victim->ToPlayer()->duel->opponent->GetGUID() == attacker->GetOwnerGUID()) damage = health - 1; duel_hasEnded = true; @@ -766,8 +770,11 @@ uint32 Unit::DealDamage(Unit* victim, uint32 damage, CleanDamage const* cleanDam if (victimRider && victimRider->duel && victimRider->duel->isMounted) { + if (!attacker) + return 0; + // prevent kill only if killed in duel and killed by opponent or opponent controlled creature - if (victimRider->duel->opponent == this || victimRider->duel->opponent->GetGUID() == GetCharmerGUID()) + if (victimRider->duel->opponent == attacker || victimRider->duel->opponent->GetGUID() == attacker->GetCharmerGUID()) damage = health - 1; duel_wasMounted = true; @@ -775,17 +782,18 @@ uint32 Unit::DealDamage(Unit* victim, uint32 damage, CleanDamage const* cleanDam } } - if (GetTypeId() == TYPEID_PLAYER && this != victim) + if (attacker && attacker != victim) { - Player* killer = ToPlayer(); - - // in bg, count dmg if victim is also a player - if (victim->GetTypeId() == TYPEID_PLAYER) - if (Battleground* bg = killer->GetBattleground()) - bg->UpdatePlayerScore(killer, SCORE_DAMAGE_DONE, damage); + if (Player* killer = attacker->ToPlayer()) + { + // in bg, count dmg if victim is also a player + if (victim->GetTypeId() == TYPEID_PLAYER) + if (Battleground* bg = killer->GetBattleground()) + bg->UpdatePlayerScore(killer, SCORE_DAMAGE_DONE, damage); - killer->UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_DAMAGE_DONE, health > damage ? damage : health, 0, victim); - killer->UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_HIGHEST_HIT_DEALT, damage); + killer->UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_DAMAGE_DONE, health > damage ? damage : health, 0, victim); + killer->UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_HIGHEST_HIT_DEALT, damage); + } } if (victim->GetTypeId() == TYPEID_PLAYER) @@ -793,25 +801,21 @@ uint32 Unit::DealDamage(Unit* victim, uint32 damage, CleanDamage const* cleanDam else if (!victim->IsControlledByPlayer() || victim->IsVehicle()) { if (!victim->ToCreature()->hasLootRecipient()) - victim->ToCreature()->SetLootRecipient(this); + victim->ToCreature()->SetLootRecipient(attacker); - if (IsControlledByPlayer() || (ToTempSummon() && ToTempSummon()->GetSummoner() && ToTempSummon()->GetSummoner()->GetTypeId() == TYPEID_PLAYER)) + if (!attacker || attacker->IsControlledByPlayer() || (attacker->ToTempSummon() && attacker->ToTempSummon()->GetSummoner() && attacker->ToTempSummon()->GetSummoner()->GetTypeId() == TYPEID_PLAYER)) victim->ToCreature()->LowerPlayerDamageReq(health < damage ? health : damage); } if (health <= damage) { - TC_LOG_DEBUG("entities.unit", "DealDamage: victim just died"); - - if (victim->GetTypeId() == TYPEID_PLAYER && victim != this) + if (victim->GetTypeId() == TYPEID_PLAYER && victim != attacker) victim->ToPlayer()->UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_TOTAL_DAMAGE_RECEIVED, health); - Kill(victim, durabilityLoss); + Unit::Kill(attacker, victim, durabilityLoss); } else { - TC_LOG_DEBUG("entities.unit", "DealDamageAlive"); - if (victim->GetTypeId() == TYPEID_PLAYER) victim->ToPlayer()->UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_TOTAL_DAMAGE_RECEIVED, damage); @@ -826,7 +830,8 @@ uint32 Unit::DealDamage(Unit* victim, uint32 damage, CleanDamage const* cleanDam if (damagetype != DOT && damage > 0 && !victim->GetOwnerGUID().IsPlayer() && (!spellProto || !spellProto->HasAura(SPELL_AURA_DAMAGE_SHIELD))) victim->ToCreature()->SetLastDamagedTime(GameTime::GetGameTime() + MAX_AGGRO_RESET_TIME); - victim->GetThreatManager().AddThreat(this, float(damage), spellProto); + if (attacker) + victim->GetThreatManager().AddThreat(attacker, float(damage), spellProto); } else // victim is a player { @@ -839,29 +844,31 @@ uint32 Unit::DealDamage(Unit* victim, uint32 damage, CleanDamage const* cleanDam } // Rage from damage received - if (this != victim && victim->getPowerType() == POWER_RAGE) + if (attacker != victim && victim->getPowerType() == POWER_RAGE) { rage_damage = damage + (cleanDamage ? cleanDamage->absorbed_damage : 0); victim->RewardRage(rage_damage, 0, false); } - if (GetTypeId() == TYPEID_PLAYER) + if (attacker && attacker->GetTypeId() == TYPEID_PLAYER) { // random durability for items (HIT DONE) if (roll_chance_f(sWorld->getRate(RATE_DURABILITY_LOSS_DAMAGE))) { EquipmentSlots slot = EquipmentSlots(urand(0, EQUIPMENT_SLOT_END-1)); - ToPlayer()->DurabilityPointLossForEquipSlot(slot); + attacker->ToPlayer()->DurabilityPointLossForEquipSlot(slot); } } if (damagetype != NODAMAGE && damage) { - if (victim != this && victim->GetTypeId() == TYPEID_PLAYER && // does not support creature push_back + if (victim != attacker && victim->GetTypeId() == TYPEID_PLAYER && // does not support creature push_back (!spellProto || !(spellProto->HasAttribute(SPELL_ATTR7_NO_PUSHBACK_ON_DAMAGE) || spellProto->HasAttribute(SPELL_ATTR3_TREAT_AS_PERIODIC)))) { if (damagetype != DOT) + { if (Spell* spell = victim->m_currentSpells[CURRENT_GENERIC_SPELL]) + { if (spell->getState() == SPELL_STATE_PREPARING) { uint32 interruptFlags = spell->m_spellInfo->InterruptFlags; @@ -870,14 +877,18 @@ uint32 Unit::DealDamage(Unit* victim, uint32 damage, CleanDamage const* cleanDam else if (interruptFlags & SPELL_INTERRUPT_FLAG_PUSH_BACK) spell->Delayed(); } + } + } if (Spell* spell = victim->m_currentSpells[CURRENT_CHANNELED_SPELL]) + { if (spell->getState() == SPELL_STATE_CASTING) { uint32 channelInterruptFlags = spell->m_spellInfo->ChannelInterruptFlags; if (((channelInterruptFlags & CHANNEL_FLAG_DELAY) != 0) && (damagetype != DOT)) spell->DelayedChannel(); } + } } } @@ -901,8 +912,6 @@ uint32 Unit::DealDamage(Unit* victim, uint32 damage, CleanDamage const* cleanDam } } - TC_LOG_DEBUG("entities.unit", "DealDamageEnd returned %d damage", damage); - return damage; } @@ -970,8 +979,8 @@ void Unit::CalculateSpellDamageTaken(SpellNonMeleeDamage* damageInfo, int32 dama // Spells with SPELL_ATTR4_FIXED_DAMAGE ignore resilience because their damage is based off another spell's damage. if (!spellInfo->HasAttribute(SPELL_ATTR4_FIXED_DAMAGE)) { - if (IsDamageReducedByArmor(damageSchoolMask, spellInfo)) - damage = CalcArmorReducedDamage(victim, damage, spellInfo, attackType); + if (Unit::IsDamageReducedByArmor(damageSchoolMask, spellInfo)) + damage = Unit::CalcArmorReducedDamage(this, victim, damage, spellInfo, attackType); bool blocked = false; // Per-school calc @@ -1035,10 +1044,8 @@ void Unit::CalculateSpellDamageTaken(SpellNonMeleeDamage* damageInfo, int32 dama damage -= damageInfo->blocked; } - if (attackType != RANGED_ATTACK) - ApplyResilience(victim, nullptr, &damage, crit, CR_CRIT_TAKEN_MELEE); - else - ApplyResilience(victim, nullptr, &damage, crit, CR_CRIT_TAKEN_RANGED); + if (CanApplyResilience()) + Unit::ApplyResilience(victim, nullptr, &damage, crit, (attackType == RANGED_ATTACK ? CR_CRIT_TAKEN_RANGED : CR_CRIT_TAKEN_MELEE)); break; } // Magical Attacks @@ -1049,10 +1056,11 @@ void Unit::CalculateSpellDamageTaken(SpellNonMeleeDamage* damageInfo, int32 dama if (crit) { damageInfo->HitInfo |= SPELL_HIT_TYPE_CRIT; - damage = SpellCriticalDamageBonus(spellInfo, damage, victim); + damage = Unit::SpellCriticalDamageBonus(this, spellInfo, damage, victim); } - ApplyResilience(victim, nullptr, &damage, crit, CR_CRIT_TAKEN_SPELL); + if (CanApplyResilience()) + Unit::ApplyResilience(victim, nullptr, &damage, crit, CR_CRIT_TAKEN_SPELL); break; } default: @@ -1069,7 +1077,7 @@ void Unit::CalculateSpellDamageTaken(SpellNonMeleeDamage* damageInfo, int32 dama damageInfo->damage = damage; DamageInfo dmgInfo(*damageInfo, SPELL_DIRECT_DAMAGE, BASE_ATTACK, PROC_HIT_NONE); - CalcAbsorbResist(dmgInfo); + Unit::CalcAbsorbResist(dmgInfo); damageInfo->absorb = dmgInfo.GetAbsorb(); damageInfo->resist = dmgInfo.GetResist(); @@ -1103,7 +1111,7 @@ void Unit::DealSpellDamage(SpellNonMeleeDamage* damageInfo, bool durabilityLoss) // Call default DealDamage CleanDamage cleanDamage(damageInfo->cleanDamage, damageInfo->absorb, BASE_ATTACK, MELEE_HIT_NORMAL); - DealDamage(victim, damageInfo->damage, &cleanDamage, SPELL_DIRECT_DAMAGE, SpellSchoolMask(damageInfo->schoolMask), spellProto, durabilityLoss); + Unit::DealDamage(this, victim, damageInfo->damage, &cleanDamage, SPELL_DIRECT_DAMAGE, SpellSchoolMask(damageInfo->schoolMask), spellProto, durabilityLoss); } /// @todo for melee need create structure as in @@ -1167,9 +1175,9 @@ void Unit::CalculateMeleeDamage(Unit* victim, uint32 damage, CalcDamageInfo* dam sScriptMgr->ModifyMeleeDamage(damageInfo->target, damageInfo->attacker, damage); // Calculate armor reduction - if (IsDamageReducedByArmor((SpellSchoolMask)(damageInfo->damageSchoolMask))) + if (Unit::IsDamageReducedByArmor((SpellSchoolMask)(damageInfo->damageSchoolMask))) { - damageInfo->damage = CalcArmorReducedDamage(damageInfo->target, damage, nullptr, damageInfo->attackType); + damageInfo->damage = Unit::CalcArmorReducedDamage(damageInfo->attacker, damageInfo->target, damage, nullptr, damageInfo->attackType); damageInfo->cleanDamage += damage - damageInfo->damage; } else @@ -1278,7 +1286,8 @@ void Unit::CalculateMeleeDamage(Unit* victim, uint32 damage, CalcDamageInfo* dam int32 resilienceReduction = damageInfo->damage; // attackType is checked already for BASE_ATTACK or OFF_ATTACK so it can't be RANGED_ATTACK here - ApplyResilience(victim, nullptr, &resilienceReduction, (damageInfo->hitOutCome == MELEE_HIT_CRIT), CR_CRIT_TAKEN_MELEE); + if (CanApplyResilience()) + Unit::ApplyResilience(victim, nullptr, &resilienceReduction, (damageInfo->hitOutCome == MELEE_HIT_CRIT), CR_CRIT_TAKEN_MELEE); resilienceReduction = damageInfo->damage - resilienceReduction; damageInfo->damage -= resilienceReduction; damageInfo->cleanDamage += resilienceReduction; @@ -1289,7 +1298,7 @@ void Unit::CalculateMeleeDamage(Unit* victim, uint32 damage, CalcDamageInfo* dam damageInfo->procVictim |= PROC_FLAG_TAKEN_DAMAGE; // Calculate absorb & resists DamageInfo dmgInfo(*damageInfo); - CalcAbsorbResist(dmgInfo); + Unit::CalcAbsorbResist(dmgInfo); damageInfo->absorb = dmgInfo.GetAbsorb(); damageInfo->resist = dmgInfo.GetResist(); @@ -1353,7 +1362,7 @@ void Unit::DealMeleeDamage(CalcDamageInfo* damageInfo, bool durabilityLoss) // Call default DealDamage CleanDamage cleanDamage(damageInfo->cleanDamage, damageInfo->absorb, damageInfo->attackType, damageInfo->hitOutCome); - DealDamage(victim, damageInfo->damage, &cleanDamage, DIRECT_DAMAGE, SpellSchoolMask(damageInfo->damageSchoolMask), nullptr, durabilityLoss); + Unit::DealDamage(this, victim, damageInfo->damage, &cleanDamage, DIRECT_DAMAGE, SpellSchoolMask(damageInfo->damageSchoolMask), nullptr, 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) && @@ -1412,12 +1421,12 @@ void Unit::DealMeleeDamage(CalcDamageInfo* damageInfo, bool durabilityLoss) uint32 damage = aurEff->GetAmount(); if (Unit* caster = aurEff->GetCaster()) { - damage = caster->SpellDamageBonusDone(this, spellInfo, damage, SPELL_DIRECT_DAMAGE); + damage = caster->SpellDamageBonusDone(this, spellInfo, damage, SPELL_DIRECT_DAMAGE, { }); damage = SpellDamageBonusTaken(caster, spellInfo, damage, SPELL_DIRECT_DAMAGE); } // No Unit::CalcAbsorbResist here - opcode doesn't send that data - this damage is probably not affected by that - victim->DealDamageMods(this, damage, nullptr); + Unit::DealDamageMods(this, damage, nullptr); /// @todo Move this to a packet handler WorldPacket data(SMSG_SPELLDAMAGESHIELD, 8 + 8 + 4 + 4 + 4 + 4 + 4); @@ -1430,7 +1439,7 @@ void Unit::DealMeleeDamage(CalcDamageInfo* damageInfo, bool durabilityLoss) data << uint32(spellInfo->SchoolMask); victim->SendMessageToSet(&data, true); - victim->DealDamage(this, damage, nullptr, SPELL_DIRECT_DAMAGE, spellInfo->GetSchoolMask(), spellInfo, true); + Unit::DealDamage(victim, this, damage, nullptr, SPELL_DIRECT_DAMAGE, spellInfo->GetSchoolMask(), spellInfo, true); } } } @@ -1443,7 +1452,7 @@ void Unit::HandleEmoteCommand(uint32 anim_id) SendMessageToSet(&data, true); } -bool Unit::IsDamageReducedByArmor(SpellSchoolMask schoolMask, SpellInfo const* spellInfo /*= nullptr*/, int8 effIndex /*= -1*/) +/*static*/ bool Unit::IsDamageReducedByArmor(SpellSchoolMask schoolMask, SpellInfo const* spellInfo /*= nullptr*/, int8 effIndex /*= -1*/) { // only physical spells damage gets reduced by armor if ((schoolMask & SPELL_SCHOOL_MASK_NORMAL) == 0) @@ -1466,72 +1475,75 @@ bool Unit::IsDamageReducedByArmor(SpellSchoolMask schoolMask, SpellInfo const* s return true; } -uint32 Unit::CalcArmorReducedDamage(Unit* victim, const uint32 damage, SpellInfo const* spellInfo, WeaponAttackType attackType) const +/*static*/ uint32 Unit::CalcArmorReducedDamage(Unit const* attacker, Unit* victim, uint32 damage, SpellInfo const* spellInfo, uint8 attackerLevel /*= 0*/, WeaponAttackType attackType /*= MAX_ATTACK*/) { float armor = float(victim->GetArmor()); // Ignore enemy armor by SPELL_AURA_MOD_TARGET_RESISTANCE aura - armor += GetTotalAuraModifierByMiscMask(SPELL_AURA_MOD_TARGET_RESISTANCE, SPELL_SCHOOL_MASK_NORMAL); - - if (spellInfo) - if (Player* modOwner = GetSpellModOwner()) - modOwner->ApplySpellMod(spellInfo->Id, SPELLMOD_IGNORE_ARMOR, armor); - - AuraEffectList const& resIgnoreAurasAb = GetAuraEffectsByType(SPELL_AURA_MOD_ABILITY_IGNORE_TARGET_RESIST); - for (AuraEffectList::const_iterator j = resIgnoreAurasAb.begin(); j != resIgnoreAurasAb.end(); ++j) + if (attacker) { - if ((*j)->GetMiscValue() & SPELL_SCHOOL_MASK_NORMAL && (*j)->IsAffectedOnSpell(spellInfo)) - armor = std::floor(AddPct(armor, -(*j)->GetAmount())); - } + armor += attacker->GetTotalAuraModifierByMiscMask(SPELL_AURA_MOD_TARGET_RESISTANCE, SPELL_SCHOOL_MASK_NORMAL); - AuraEffectList const& resIgnoreAuras = GetAuraEffectsByType(SPELL_AURA_MOD_IGNORE_TARGET_RESIST); - for (AuraEffectList::const_iterator j = resIgnoreAuras.begin(); j != resIgnoreAuras.end(); ++j) - { - if ((*j)->GetMiscValue() & SPELL_SCHOOL_MASK_NORMAL) - armor = std::floor(AddPct(armor, -(*j)->GetAmount())); - } + if (spellInfo) + if (Player* modOwner = attacker->GetSpellModOwner()) + modOwner->ApplySpellMod(spellInfo->Id, SPELLMOD_IGNORE_ARMOR, armor); - // Apply Player CR_ARMOR_PENETRATION rating and buffs from stances\specializations etc. - if (GetTypeId() == TYPEID_PLAYER) - { - float arpPct = ToPlayer()->GetRatingBonusValue(CR_ARMOR_PENETRATION); + AuraEffectList const& resIgnoreAurasAb = attacker->GetAuraEffectsByType(SPELL_AURA_MOD_ABILITY_IGNORE_TARGET_RESIST); + for (AuraEffect const* aurEff : resIgnoreAurasAb) + { + if (aurEff->GetMiscValue() & SPELL_SCHOOL_MASK_NORMAL && aurEff->IsAffectedOnSpell(spellInfo)) + armor = std::floor(AddPct(armor, -aurEff->GetAmount())); + } - Item const* weapon = ToPlayer()->GetWeaponForAttack(attackType, true); - arpPct += GetTotalAuraModifier(SPELL_AURA_MOD_ARMOR_PENETRATION_PCT, [weapon](AuraEffect const* aurEff) -> bool + AuraEffectList const& resIgnoreAuras = attacker->GetAuraEffectsByType(SPELL_AURA_MOD_IGNORE_TARGET_RESIST); + for (AuraEffect const* aurEff : resIgnoreAuras) { - return aurEff->GetSpellInfo()->IsItemFitToSpellRequirements(weapon); - }); + if (aurEff->GetMiscValue() & SPELL_SCHOOL_MASK_NORMAL) + armor = std::floor(AddPct(armor, -aurEff->GetAmount())); + } - // no more than 100% - RoundToInterval(arpPct, 0.f, 100.f); + // Apply Player CR_ARMOR_PENETRATION rating and buffs from stances\specializations etc. + if (attacker->GetTypeId() == TYPEID_PLAYER) + { + float arpPct = attacker->ToPlayer()->GetRatingBonusValue(CR_ARMOR_PENETRATION); - float maxArmorPen = 0.f; - if (victim->getLevel() < 60) - maxArmorPen = float(400 + 85 * victim->getLevel()); - else - maxArmorPen = 400 + 85 * victim->getLevel() + 4.5f * 85 * (victim->getLevel() - 59); + Item const* weapon = attacker->ToPlayer()->GetWeaponForAttack(attackType, true); + arpPct += attacker->GetTotalAuraModifier(SPELL_AURA_MOD_ARMOR_PENETRATION_PCT, [weapon](AuraEffect const* aurEff) -> bool + { + return aurEff->GetSpellInfo()->IsItemFitToSpellRequirements(weapon); + }); - // Cap armor penetration to this number - maxArmorPen = std::min((armor + maxArmorPen) / 3.f, armor); - // Figure out how much armor do we ignore - armor -= CalculatePct(maxArmorPen, arpPct); + // no more than 100% + RoundToInterval(arpPct, 0.f, 100.f); + + float maxArmorPen = 0.f; + if (victim->getLevel() < 60) + maxArmorPen = float(400 + 85 * victim->getLevel()); + else + maxArmorPen = 400 + 85 * victim->getLevel() + 4.5f * 85 * (victim->getLevel() - 59); + + // Cap armor penetration to this number + maxArmorPen = std::min((armor + maxArmorPen) / 3.f, armor); + // Figure out how much armor do we ignore + armor -= CalculatePct(maxArmorPen, arpPct); + } } if (armor < 0.0f) armor = 0.0f; - float levelModifier = getLevel(); - if (levelModifier > 59) - levelModifier = levelModifier + 4.5f * (levelModifier - 59); + float levelModifier = attacker ? attacker->getLevel() : attackerLevel; + if (levelModifier > 59.f) + levelModifier = levelModifier + 4.5f * (levelModifier - 59.f); - float damageReduction = 0.1f * armor / (8.5f * levelModifier + 40); + float damageReduction = 0.1f * armor / (8.5f * levelModifier + 40.f); damageReduction /= (1.0f + damageReduction); RoundToInterval(damageReduction, 0.f, 0.75f); return std::max<uint32>(damage * (1.0f - damageReduction), 1); } -uint32 Unit::CalcSpellResistedDamage(Unit* victim, uint32 damage, SpellSchoolMask schoolMask, SpellInfo const* spellInfo) const +/*static*/ uint32 Unit::CalcSpellResistedDamage(Unit const* attacker, Unit* victim, uint32 damage, SpellSchoolMask schoolMask, SpellInfo const* spellInfo) { // Magic damage, check for resists if (!(schoolMask & SPELL_SCHOOL_MASK_MAGIC)) @@ -1552,8 +1564,7 @@ uint32 Unit::CalcSpellResistedDamage(Unit* victim, uint32 damage, SpellSchoolMas return 0; } - float const averageResist = CalculateAverageResistReduction(schoolMask, victim, spellInfo); - + float const averageResist = Unit::CalculateAverageResistReduction(attacker, schoolMask, victim, spellInfo); float discreteResistProbability[11] = { }; if (averageResist <= 0.1f) { @@ -1579,14 +1590,17 @@ uint32 Unit::CalcSpellResistedDamage(Unit* victim, uint32 damage, SpellSchoolMas if (damageResisted > 0.0f) // if any damage was resisted { int32 ignoredResistance = 0; - ignoredResistance += GetTotalAuraModifier(SPELL_AURA_MOD_ABILITY_IGNORE_TARGET_RESIST, [schoolMask, spellInfo](AuraEffect const* aurEff) -> bool + if (attacker) { - if ((aurEff->GetMiscValue() & schoolMask) && aurEff->IsAffectedOnSpell(spellInfo)) - return true; - return false; - }); + ignoredResistance += attacker->GetTotalAuraModifier(SPELL_AURA_MOD_ABILITY_IGNORE_TARGET_RESIST, [schoolMask, spellInfo](AuraEffect const* aurEff) -> bool + { + if ((aurEff->GetMiscValue() & schoolMask) && aurEff->IsAffectedOnSpell(spellInfo)) + return true; + return false; + }); - ignoredResistance += GetTotalAuraModifierByMiscMask(SPELL_AURA_MOD_IGNORE_TARGET_RESIST, schoolMask); + ignoredResistance += attacker->GetTotalAuraModifierByMiscMask(SPELL_AURA_MOD_IGNORE_TARGET_RESIST, schoolMask); + } ignoredResistance = std::min<int32>(ignoredResistance, 100); ApplyPct(damageResisted, 100 - ignoredResistance); @@ -1594,7 +1608,7 @@ uint32 Unit::CalcSpellResistedDamage(Unit* victim, uint32 damage, SpellSchoolMas // Spells with melee and magic school mask, decide whether resistance or armor absorb is higher if (spellInfo && spellInfo->HasAttribute(SPELL_ATTR0_CU_SCHOOLMASK_NORMAL_WITH_MAGIC)) { - uint32 damageAfterArmor = CalcArmorReducedDamage(victim, damage, spellInfo, BASE_ATTACK); + uint32 damageAfterArmor = Unit::CalcArmorReducedDamage(attacker, victim, damage, spellInfo, BASE_ATTACK); float armorReduction = damage - damageAfterArmor; // pick the lower one, the weakest resistance counts @@ -1606,20 +1620,22 @@ uint32 Unit::CalcSpellResistedDamage(Unit* victim, uint32 damage, SpellSchoolMas return uint32(damageResisted); } -float Unit::CalculateAverageResistReduction(SpellSchoolMask schoolMask, Unit const* victim, SpellInfo const* spellInfo) const +/*static*/ float Unit::CalculateAverageResistReduction(Unit const* attacker, SpellSchoolMask schoolMask, Unit const* victim, SpellInfo const* spellInfo) { float victimResistance = float(victim->GetResistance(schoolMask)); - - // pets inherit 100% of masters penetration - // excluding traps - Player const* player = GetSpellModOwner(); - if (player && GetEntry() != WORLD_TRIGGER) + if (attacker) { - victimResistance += float(player->GetTotalAuraModifierByMiscMask(SPELL_AURA_MOD_TARGET_RESISTANCE, schoolMask)); - victimResistance -= float(player->GetSpellPenetrationItemMod()); + // pets inherit 100% of masters penetration + // excluding traps + Player const* player = attacker->GetSpellModOwner(); + if (player && attacker->GetEntry() != WORLD_TRIGGER) + { + victimResistance += float(player->GetTotalAuraModifierByMiscMask(SPELL_AURA_MOD_TARGET_RESISTANCE, schoolMask)); + victimResistance -= float(player->GetSpellPenetrationItemMod()); + } + else + victimResistance += float(attacker->GetTotalAuraModifierByMiscMask(SPELL_AURA_MOD_TARGET_RESISTANCE, schoolMask)); } - else - victimResistance += float(GetTotalAuraModifierByMiscMask(SPELL_AURA_MOD_TARGET_RESISTANCE, schoolMask)); // holy resistance exists in pve and comes from level difference, ignore template values if (schoolMask & SPELL_SCHOOL_MASK_HOLY) @@ -1632,8 +1648,8 @@ float Unit::CalculateAverageResistReduction(SpellSchoolMask schoolMask, Unit con victimResistance = std::max(victimResistance, 0.0f); // level-based resistance does not apply to binary spells, and cannot be overcome by spell penetration - if (!spellInfo || !spellInfo->HasAttribute(SPELL_ATTR0_CU_BINARY_SPELL)) - victimResistance += std::max((float(victim->getLevelForTarget(this)) - float(getLevelForTarget(victim))) * 5.0f, 0.0f); + if (attacker && (!spellInfo || !spellInfo->HasAttribute(SPELL_ATTR0_CU_BINARY_SPELL))) + victimResistance += std::max((float(victim->getLevelForTarget(attacker)) - float(attacker->getLevelForTarget(victim))) * 5.0f, 0.0f); static uint32 const BOSS_LEVEL = 83; static float const BOSS_RESISTANCE_CONSTANT = 510.0f; @@ -1648,26 +1664,30 @@ float Unit::CalculateAverageResistReduction(SpellSchoolMask schoolMask, Unit con return victimResistance / (victimResistance + resistanceConstant); } -void Unit::CalcAbsorbResist(DamageInfo& damageInfo) +/*static*/ void Unit::CalcAbsorbResist(DamageInfo& damageInfo) { if (!damageInfo.GetVictim() || !damageInfo.GetVictim()->IsAlive() || !damageInfo.GetDamage()) return; - uint32 resistedDamage = CalcSpellResistedDamage(damageInfo.GetVictim(), damageInfo.GetDamage(), damageInfo.GetSchoolMask(), damageInfo.GetSpellInfo()); + uint32 resistedDamage = Unit::CalcSpellResistedDamage(damageInfo.GetAttacker(), damageInfo.GetVictim(), damageInfo.GetDamage(), damageInfo.GetSchoolMask(), damageInfo.GetSpellInfo()); damageInfo.ResistDamage(resistedDamage); // Ignore Absorption Auras - float auraAbsorbMod(GetMaxPositiveAuraModifierByMiscMask(SPELL_AURA_MOD_TARGET_ABSORB_SCHOOL, damageInfo.GetSchoolMask())); - auraAbsorbMod = std::max(auraAbsorbMod, static_cast<float>(GetMaxPositiveAuraModifier(SPELL_AURA_MOD_TARGET_ABILITY_ABSORB_SCHOOL, [&damageInfo](AuraEffect const* aurEff) -> bool + float auraAbsorbMod = 0.f; + if (Unit* attacker = damageInfo.GetAttacker()) { - if (!(aurEff->GetMiscValue() & damageInfo.GetSchoolMask())) - return false; + auraAbsorbMod = attacker->GetMaxPositiveAuraModifierByMiscMask(SPELL_AURA_MOD_TARGET_ABSORB_SCHOOL, damageInfo.GetSchoolMask()); + auraAbsorbMod = std::max(auraAbsorbMod, static_cast<float>(attacker->GetMaxPositiveAuraModifier(SPELL_AURA_MOD_TARGET_ABILITY_ABSORB_SCHOOL, [&damageInfo](AuraEffect const* aurEff) -> bool + { + if (!(aurEff->GetMiscValue() & damageInfo.GetSchoolMask())) + return false; - if (!aurEff->IsAffectedOnSpell(damageInfo.GetSpellInfo())) - return false; + if (!aurEff->IsAffectedOnSpell(damageInfo.GetSpellInfo())) + return false; - return true; - }))); + return true; + }))); + } RoundToInterval(auraAbsorbMod, 0.0f, 100.0f); @@ -1785,7 +1805,7 @@ void Unit::CalcAbsorbResist(DamageInfo& damageInfo) damageInfo.ModifyDamage(absorbIgnoringDamage); // split damage auras - only when not damaging self - if (damageInfo.GetVictim() != this) + if (damageInfo.GetVictim() != damageInfo.GetAttacker()) { // We're going to call functions which can modify content of the list during iteration over it's elements // Let's copy the list so we can prevent iterator invalidation @@ -1820,12 +1840,13 @@ void Unit::CalcAbsorbResist(DamageInfo& damageInfo) uint32 splitted = splitDamage; uint32 splitted_absorb = 0; - DealDamageMods(caster, splitted, &splitted_absorb); + Unit::DealDamageMods(caster, splitted, &splitted_absorb); - SendSpellNonMeleeDamageLog(caster, (*itr)->GetSpellInfo()->Id, splitted, damageInfo.GetSchoolMask(), splitted_absorb, 0, false, 0, false); + if (Unit* attacker = damageInfo.GetAttacker()) + attacker->SendSpellNonMeleeDamageLog(caster, (*itr)->GetSpellInfo()->Id, splitted, damageInfo.GetSchoolMask(), splitted_absorb, 0, false, 0, false); CleanDamage cleanDamage = CleanDamage(splitted, 0, BASE_ATTACK, MELEE_HIT_NORMAL); - DealDamage(caster, splitted, &cleanDamage, DIRECT_DAMAGE, damageInfo.GetSchoolMask(), (*itr)->GetSpellInfo(), false); + Unit::DealDamage(damageInfo.GetAttacker(), caster, splitted, &cleanDamage, DIRECT_DAMAGE, damageInfo.GetSchoolMask(), (*itr)->GetSpellInfo(), false); } // We're going to call functions which can modify content of the list during iteration over it's elements @@ -1864,20 +1885,21 @@ void Unit::CalcAbsorbResist(DamageInfo& damageInfo) } uint32 split_absorb = 0; - DealDamageMods(caster, splitDamage, &split_absorb); + Unit::DealDamageMods(caster, splitDamage, &split_absorb); - SendSpellNonMeleeDamageLog(caster, (*itr)->GetSpellInfo()->Id, splitDamage, damageInfo.GetSchoolMask(), split_absorb, 0, false, 0, false); + if (Unit* attacker = damageInfo.GetAttacker()) + attacker->SendSpellNonMeleeDamageLog(caster, (*itr)->GetSpellInfo()->Id, splitDamage, damageInfo.GetSchoolMask(), split_absorb, 0, false, 0, false); CleanDamage cleanDamage = CleanDamage(splitDamage, 0, BASE_ATTACK, MELEE_HIT_NORMAL); - DealDamage(caster, splitDamage, &cleanDamage, DIRECT_DAMAGE, damageInfo.GetSchoolMask(), (*itr)->GetSpellInfo(), false); + Unit::DealDamage(damageInfo.GetAttacker(), caster, splitDamage, &cleanDamage, DIRECT_DAMAGE, damageInfo.GetSchoolMask(), (*itr)->GetSpellInfo(), false); // break 'Fear' and similar auras - ProcSkillsAndAuras(caster, PROC_FLAG_NONE, PROC_FLAG_TAKEN_SPELL_MAGIC_DMG_CLASS_NEG, PROC_SPELL_TYPE_DAMAGE, PROC_SPELL_PHASE_HIT, PROC_HIT_NONE, nullptr, &damageInfo, nullptr); + Unit::ProcSkillsAndAuras(damageInfo.GetAttacker(), caster, PROC_FLAG_NONE, PROC_FLAG_TAKEN_SPELL_MAGIC_DMG_CLASS_NEG, PROC_SPELL_TYPE_DAMAGE, PROC_SPELL_PHASE_HIT, PROC_HIT_NONE, nullptr, &damageInfo, nullptr); } } } -void Unit::CalcHealAbsorb(HealInfo& healInfo) const +/*static*/ void Unit::CalcHealAbsorb(HealInfo& healInfo) { if (!healInfo.GetHeal()) return; @@ -1971,13 +1993,13 @@ void Unit::AttackerStateUpdate(Unit* victim, WeaponAttackType attType, bool extr CalcDamageInfo damageInfo; CalculateMeleeDamage(victim, 0, &damageInfo, attType); // Send log damage message to client - DealDamageMods(victim, damageInfo.damage, &damageInfo.absorb); + Unit::DealDamageMods(victim, damageInfo.damage, &damageInfo.absorb); SendAttackStateUpdate(&damageInfo); DealMeleeDamage(&damageInfo, true); DamageInfo dmgInfo(damageInfo); - ProcSkillsAndAuras(damageInfo.target, damageInfo.procAttacker, damageInfo.procVictim, PROC_SPELL_TYPE_NONE, PROC_SPELL_PHASE_NONE, dmgInfo.GetHitMask(), nullptr, &dmgInfo, nullptr); + Unit::ProcSkillsAndAuras(damageInfo.attacker, damageInfo.target, damageInfo.procAttacker, damageInfo.procVictim, PROC_SPELL_TYPE_NONE, PROC_SPELL_PHASE_NONE, dmgInfo.GetHitMask(), nullptr, &dmgInfo, nullptr); if (GetTypeId() == TYPEID_PLAYER) TC_LOG_DEBUG("entities.unit", "AttackerStateUpdate: (Player) %u attacked %u (TypeId: %u) for %u dmg, absorbed %u, blocked %u, resisted %u.", @@ -2055,7 +2077,7 @@ MeleeHitOutcome Unit::RollMeleeOutcomeAgainst(Unit const* victim, WeaponAttackTy int32 miss_chance = int32(MeleeSpellMissChance(victim, attType, attackerWeaponSkill - victimMaxSkillValueForLevel, 0) * 100.0f); // Critical hit chance - int32 crit_chance = int32(GetUnitCriticalChance(attType, victim) * 100.0f); + int32 crit_chance = int32(GetUnitCriticalChanceAgainst(attType, victim) * 100.0f); int32 dodge_chance = int32(GetUnitDodgeChance(attType, victim) * 100.0f); int32 block_chance = int32(GetUnitBlockChance(attType, victim) * 100.0f); @@ -2534,7 +2556,7 @@ SpellMissInfo Unit::MagicSpellHitResult(Unit* victim, SpellInfo const* spellInfo // resistance chance for binary spells, equals to average damage reduction of non-binary spell if (spellInfo->HasAttribute(SPELL_ATTR0_CU_BINARY_SPELL) && (spellInfo->GetSchoolMask() & SPELL_SCHOOL_MASK_MAGIC)) - resist_chance += int32(CalculateAverageResistReduction(spellInfo->GetSchoolMask(), victim, spellInfo) * 10000.f); // 100 for spell calculations, and 100 for return value percentage + resist_chance += int32(Unit::CalculateAverageResistReduction(this, spellInfo->GetSchoolMask(), victim, spellInfo) * 10000.f); // 100 for spell calculations, and 100 for return value percentage } // Roll chance @@ -2781,14 +2803,9 @@ float Unit::GetUnitBlockChance(WeaponAttackType attType, Unit const* victim) con return std::max(chance, 0.0f); } -float Unit::GetUnitCriticalChance(WeaponAttackType attackType, Unit const* victim) const +float Unit::GetUnitCriticalChanceDone(WeaponAttackType attackType) const { - int32 const attackerWeaponSkill = GetWeaponSkillValue(attackType, victim); - int32 const victimDefenseSkill = victim->GetDefenseSkillValue(this); - int32 const skillDiff = victimDefenseSkill - attackerWeaponSkill; - - float chance = 0.0f; - float skillBonus = 0.0f; + float chance = 0.f; if (GetTypeId() == TYPEID_PLAYER) { switch (attackType) @@ -2818,29 +2835,40 @@ float Unit::GetUnitCriticalChance(WeaponAttackType attackType, Unit const* victi } } + return chance; +} + +float Unit::GetUnitCriticalChanceTaken(Unit const* attacker, WeaponAttackType attackType, float critDone) const +{ + int32 const attackerWeaponSkill = attacker->GetWeaponSkillValue(attackType, this); + int32 const victimDefenseSkill = GetDefenseSkillValue(attacker); + int32 const skillDiff = victimDefenseSkill - attackerWeaponSkill; + + float skillBonus = 0.0f; + float chance = critDone; + // flat aura mods if (attackType == RANGED_ATTACK) - chance += victim->GetTotalAuraModifier(SPELL_AURA_MOD_ATTACKER_RANGED_CRIT_CHANCE); + chance += GetTotalAuraModifier(SPELL_AURA_MOD_ATTACKER_RANGED_CRIT_CHANCE); else - chance += victim->GetTotalAuraModifier(SPELL_AURA_MOD_ATTACKER_MELEE_CRIT_CHANCE); + chance += GetTotalAuraModifier(SPELL_AURA_MOD_ATTACKER_MELEE_CRIT_CHANCE); - chance += victim->GetTotalAuraModifier(SPELL_AURA_MOD_CRIT_CHANCE_FOR_CASTER, [this](AuraEffect const* aurEff) -> bool + chance += GetTotalAuraModifier(SPELL_AURA_MOD_CRIT_CHANCE_FOR_CASTER, [attacker](AuraEffect const* aurEff) -> bool { - if (aurEff->GetCasterGUID() == GetGUID()) + if (aurEff->GetCasterGUID() == attacker->GetGUID()) return true; return false; }); - chance += victim->GetTotalAuraModifier(SPELL_AURA_MOD_ATTACKER_SPELL_AND_WEAPON_CRIT_CHANCE); - // reduce crit chance from Rating for players - if (attackType != RANGED_ATTACK) - ApplyResilience(victim, &chance, nullptr, false, CR_CRIT_TAKEN_MELEE); - else - ApplyResilience(victim, &chance, nullptr, false, CR_CRIT_TAKEN_RANGED); + if (attacker->CanApplyResilience()) + Unit::ApplyResilience(this, &chance, nullptr, false, (attackType == RANGED_ATTACK ? CR_CRIT_TAKEN_RANGED : CR_CRIT_TAKEN_MELEE)); + + // applied after resilience + chance += GetTotalAuraModifier(SPELL_AURA_MOD_ATTACKER_SPELL_AND_WEAPON_CRIT_CHANCE); // Apply crit chance from defense skill - if (victim->GetTypeId() == TYPEID_PLAYER) + if (GetTypeId() == TYPEID_PLAYER) skillBonus = -skillDiff * 0.04f; else { @@ -2850,7 +2878,13 @@ float Unit::GetUnitCriticalChance(WeaponAttackType attackType, Unit const* victi } chance += skillBonus; - return std::max(chance, 0.0f); + return std::max(chance, 0.f); +} + +float Unit::GetUnitCriticalChanceAgainst(WeaponAttackType attackType, Unit const* victim) const +{ + float chance = GetUnitCriticalChanceDone(attackType); + return victim->GetUnitCriticalChanceTaken(this, attackType, chance); } uint32 Unit::GetWeaponSkillValue(WeaponAttackType attType, Unit const* target) const @@ -3442,7 +3476,6 @@ void Unit::_ApplyAura(AuraApplication * aurApp, uint8 effMask) if (aurApp->GetRemoveMode()) return; - aura->HandleAuraSpecificPeriodics(aurApp, caster); aura->HandleAuraSpecificMods(aurApp, caster, true, false); // apply effects of the aura @@ -3953,7 +3986,7 @@ void Unit::RemoveAurasDueToSpellBySteal(uint32 spellId, ObjectGuid casterGUID, U caster->GetSingleCastAuras().push_back(aura); } // FIXME: using aura->GetMaxDuration() maybe not blizzlike but it fixes stealing of spells like Innervate - newAura->SetLoadedState(aura->GetMaxDuration(), int32(dur), stealCharge ? 1 : aura->GetCharges(), 1, recalculateMask, &damage[0]); + newAura->SetLoadedState(aura->GetMaxDuration(), int32(dur), stealCharge ? 1 : aura->GetCharges(), 1, recalculateMask, aura->GetCritChance(), aura->CanApplyResilience(), &damage[0]); newAura->ApplyForTargets(); } } @@ -5215,16 +5248,17 @@ void Unit::SendSpellNonMeleeDamageLog(Unit* target, uint32 SpellID, uint32 Damag SendSpellNonMeleeDamageLog(&log); } -void Unit::ProcSkillsAndAuras(Unit* actionTarget, uint32 typeMaskActor, uint32 typeMaskActionTarget, uint32 spellTypeMask, uint32 spellPhaseMask, uint32 hitMask, Spell* spell, DamageInfo* damageInfo, HealInfo* healInfo) +/*static*/ void Unit::ProcSkillsAndAuras(Unit* actor, Unit* actionTarget, uint32 typeMaskActor, uint32 typeMaskActionTarget, uint32 spellTypeMask, uint32 spellPhaseMask, uint32 hitMask, Spell* spell, DamageInfo* damageInfo, HealInfo* healInfo) { WeaponAttackType attType = damageInfo ? damageInfo->GetAttackType() : BASE_ATTACK; - if (typeMaskActor) - ProcSkillsAndReactives(false, actionTarget, typeMaskActor, hitMask, attType); + if (typeMaskActor && actor) + actor->ProcSkillsAndReactives(false, actionTarget, typeMaskActor, hitMask, attType); if (typeMaskActionTarget && actionTarget) - actionTarget->ProcSkillsAndReactives(true, this, typeMaskActionTarget, hitMask, attType); + actionTarget->ProcSkillsAndReactives(true, actor, typeMaskActionTarget, hitMask, attType); - TriggerAurasProcOnEvent(actionTarget, typeMaskActor, typeMaskActionTarget, spellTypeMask, spellPhaseMask, hitMask, spell, damageInfo, healInfo); + if (actor) + actor->TriggerAurasProcOnEvent(actionTarget, typeMaskActor, typeMaskActionTarget, spellTypeMask, spellPhaseMask, hitMask, spell, damageInfo, healInfo); } void Unit::SendPeriodicAuraLog(SpellPeriodicAuraLogInfo* pInfo) @@ -6340,39 +6374,45 @@ void Unit::SetCharm(Unit* charm, bool apply) UpdatePetCombatState(); } -void Unit::DealHeal(HealInfo& healInfo) +/*static*/ void Unit::DealHeal(HealInfo& healInfo) { int32 gain = 0; + Unit* healer = healInfo.GetHealer(); Unit* victim = healInfo.GetTarget(); uint32 addhealth = healInfo.GetHeal(); - if (victim->IsAIEnabled) - victim->GetAI()->HealReceived(this, addhealth); + if (healer) + { + if (victim->IsAIEnabled) + victim->GetAI()->HealReceived(healer, addhealth); - if (IsAIEnabled) - GetAI()->HealDone(victim, addhealth); + if (healer->IsAIEnabled) + healer->GetAI()->HealDone(victim, addhealth); + } if (addhealth) gain = victim->ModifyHealth(int32(addhealth)); // Hook for OnHeal Event - sScriptMgr->OnHeal(this, victim, (uint32&)gain); + sScriptMgr->OnHeal(healer, victim, (uint32&)gain); - Unit* unit = this; + Unit* unit = healer; + if (healer && healer->GetTypeId() == TYPEID_UNIT && healer->IsTotem()) + unit = healer->GetOwner(); - if (GetTypeId() == TYPEID_UNIT && IsTotem()) - unit = GetOwner(); - - if (Player* player = unit->ToPlayer()) + if (unit) { - if (Battleground* bg = player->GetBattleground()) - bg->UpdatePlayerScore(player, SCORE_HEALING_DONE, gain); + if (Player* player = unit->ToPlayer()) + { + if (Battleground* bg = player->GetBattleground()) + bg->UpdatePlayerScore(player, SCORE_HEALING_DONE, gain); - // use the actual gain, as the overheal shall not be counted, skip gain 0 (it ignored anyway in to criteria) - if (gain) - player->UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_HEALING_DONE, gain, 0, victim); + // use the actual gain, as the overheal shall not be counted, skip gain 0 (it ignored anyway in to criteria) + if (gain) + player->UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_HEALING_DONE, gain, 0, victim); - player->UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_HIGHEST_HEAL_CAST, addhealth); + player->UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_HIGHEST_HEAL_CAST, addhealth); + } } if (Player* player = victim->ToPlayer()) @@ -6615,9 +6655,8 @@ void Unit::SendHealSpellLog(HealInfo& healInfo, bool critical /*= false*/) int32 Unit::HealBySpell(HealInfo& healInfo, bool critical /*= false*/) { // calculate heal absorb and reduce healing - CalcHealAbsorb(healInfo); - - DealHeal(healInfo); + Unit::CalcHealAbsorb(healInfo); + Unit::DealHeal(healInfo); SendHealSpellLog(healInfo, critical); return healInfo.GetEffectiveHeal(); } @@ -6647,7 +6686,7 @@ void Unit::EnergizeBySpell(Unit* victim, SpellInfo const* spellInfo, int32 damag victim->GetThreatManager().ForwardThreatForAssistingMe(this, float(damage)/2, spellInfo, true); } -uint32 Unit::SpellDamageBonusDone(Unit* victim, SpellInfo const* spellProto, uint32 pdamage, DamageEffectType damagetype, uint32 stack) const +uint32 Unit::SpellDamageBonusDone(Unit* victim, SpellInfo const* spellProto, uint32 pdamage, DamageEffectType damagetype, Optional<float> const& donePctTotal, uint32 stack /*= 1*/) const { if (!spellProto || !victim || damagetype == DIRECT_DAMAGE) return pdamage; @@ -6659,20 +6698,20 @@ uint32 Unit::SpellDamageBonusDone(Unit* victim, SpellInfo const* spellProto, uin // For totems get damage bonus from owner if (GetTypeId() == TYPEID_UNIT && IsTotem()) if (Unit* owner = GetOwner()) - return owner->SpellDamageBonusDone(victim, spellProto, pdamage, damagetype); + return owner->SpellDamageBonusDone(victim, spellProto, pdamage, damagetype, donePctTotal, stack); float ApCoeffMod = 1.0f; int32 DoneTotal = 0; + float DoneTotalMod = donePctTotal ? *donePctTotal : SpellDamagePctDone(victim, spellProto, damagetype); // done scripted mod (take it from owner) Unit const* owner = GetOwner() ? GetOwner() : this; - AuraEffectList const& mOverrideClassScript = owner->GetAuraEffectsByType(SPELL_AURA_OVERRIDE_CLASS_SCRIPTS); - for (AuraEffectList::const_iterator i = mOverrideClassScript.begin(); i != mOverrideClassScript.end(); ++i) + DoneTotal += owner->GetTotalAuraModifier(SPELL_AURA_OVERRIDE_CLASS_SCRIPTS, [spellProto](AuraEffect const* aurEff) -> bool { - if (!(*i)->IsAffectedOnSpell(spellProto)) - continue; + if (!aurEff->IsAffectedOnSpell(spellProto)) + return false; - switch ((*i)->GetMiscValue()) + switch (aurEff->GetMiscValue()) { case 4418: // Increased Shock Damage case 4554: // Increased Lightning Damage @@ -6682,12 +6721,17 @@ uint32 Unit::SpellDamageBonusDone(Unit* victim, SpellInfo const* spellProto, uin case 5148: // Idol of the Shooting Star case 6008: // Increased Lightning Damage case 8627: // Totem of Hex - { - DoneTotal += (*i)->GetAmount(); + return true; + default: break; - } } - } + + return false; + }); + + // Some spells don't benefit from pct done mods + if (!spellProto->HasAttribute(SPELL_ATTR6_LIMIT_PCT_DAMAGE_MODS)) + DoneTotal += GetTotalAuraModifierByMiscMask(SPELL_AURA_MOD_FLAT_SPELL_DAMAGE_VERSUS, victim->GetCreatureTypeMask()); // Custom scripted damage switch (spellProto->SpellFamilyName) @@ -6711,7 +6755,7 @@ uint32 Unit::SpellDamageBonusDone(Unit* victim, SpellInfo const* spellProto, uin case 49638: if (SpellInfo const* proto = sSpellMgr->GetSpellInfo(itr->first)) AddPct(ApCoeffMod, proto->Effects[EFFECT_0].CalcValue()); - break; + break; } } } @@ -6765,18 +6809,11 @@ uint32 Unit::SpellDamageBonusDone(Unit* victim, SpellInfo const* spellProto, uin DoneTotal += int32(DoneAdvertisedBenefit * coeff * factorMod); } - float tmpDamage = (int32(pdamage) + DoneTotal); + float tmpDamage = float(int32(pdamage) + DoneTotal) * DoneTotalMod; - // DOTs calculated in AuraEffect::PeriodicDamageAurasTick - // Done Percentage for DOT is already calculated, no need to do it again. The percentage mod is applied in Aura::HandleAuraSpecificMods. - if (damagetype != DOT) - { - tmpDamage *= SpellDamagePctDone(victim, spellProto, damagetype); - - // apply spellmod to Done damage (flat and pct) - if (Player* modOwner = GetSpellModOwner()) - modOwner->ApplySpellMod(spellProto->Id, SPELLMOD_DAMAGE, tmpDamage); - } + // apply spellmod to Done damage (flat and pct) + if (Player* modOwner = GetSpellModOwner()) + modOwner->ApplySpellMod(spellProto->Id, damagetype == DOT ? SPELLMOD_DOT : SPELLMOD_DAMAGE, tmpDamage); return uint32(std::max(tmpDamage, 0.0f)); } @@ -6786,13 +6823,18 @@ float Unit::SpellDamagePctDone(Unit* victim, SpellInfo const* spellProto, Damage if (!spellProto || !victim || damagetype == DIRECT_DAMAGE) return 1.0f; + // Some spells don't benefit from done mods + if (spellProto->HasAttribute(SPELL_ATTR3_NO_DONE_BONUS)) + return 1.0f; + // Some spells don't benefit from pct done mods if (spellProto->HasAttribute(SPELL_ATTR6_LIMIT_PCT_DAMAGE_MODS)) return 1.0f; - // For totems pct done mods are calculated when its calculation is run on the player in SpellDamageBonusDone. + // For totems get damage bonus from owner if (GetTypeId() == TYPEID_UNIT && IsTotem()) - return 1.0f; + if (Unit* owner = GetOwner()) + return owner->SpellDamagePctDone(victim, spellProto, damagetype); // Done total percent damage auras float DoneTotalMod = 1.0f; @@ -7221,94 +7263,84 @@ int32 Unit::SpellBaseDamageBonusTaken(SpellSchoolMask schoolMask) const return GetTotalAuraModifierByMiscMask(SPELL_AURA_MOD_DAMAGE_TAKEN, schoolMask); } -bool Unit::IsSpellCrit(Unit* victim, SpellInfo const* spellProto, SpellSchoolMask schoolMask, WeaponAttackType attackType /*= BASE_ATTACK*/) const +float Unit::SpellCritChanceDone(SpellInfo const* spellInfo, SpellSchoolMask schoolMask, WeaponAttackType attackType /*= BASE_ATTACK*/) const { - return roll_chance_f(GetUnitSpellCriticalChance(victim, spellProto, schoolMask, attackType)); -} - -float Unit::GetUnitSpellCriticalChance(Unit* victim, SpellInfo const* spellProto, SpellSchoolMask schoolMask, WeaponAttackType attackType /*= BASE_ATTACK*/) const -{ - //! Mobs can't crit with spells. Player Totems can - //! Fire Elemental (from totem) can too - but this part is a hack and needs more research - if (GetGUID().IsCreatureOrVehicle() && !(IsTotem() && GetOwnerGUID().IsPlayer()) && GetEntry() != 15438) + //! Mobs can't crit with spells. (Except player controlled) + if (GetTypeId() == TYPEID_UNIT && !GetSpellModOwner()) return 0.0f; // not critting spell - if (spellProto->HasAttribute(SPELL_ATTR2_CANT_CRIT)) + if (spellInfo->HasAttribute(SPELL_ATTR2_CANT_CRIT)) return 0.0f; float crit_chance = 0.0f; - switch (spellProto->DmgClass) + switch (spellInfo->DmgClass) { - case SPELL_DAMAGE_CLASS_NONE: - // We need more spells to find a general way (if there is any) - switch (spellProto->Id) - { - case 379: // Earth Shield - case 33778: // Lifebloom Final Bloom - case 64844: // Divine Hymn - case 71607: // Item - Bauble of True Blood 10m - case 71646: // Item - Bauble of True Blood 25m - break; - default: - return 0.0f; - } - // Do not add a break here, case fallthrough is intentional! Adding a break will make above spells unable to crit. case SPELL_DAMAGE_CLASS_MAGIC: { if (schoolMask & SPELL_SCHOOL_MASK_NORMAL) crit_chance = 0.0f; // For other schools else if (GetTypeId() == TYPEID_PLAYER) - { crit_chance = GetFloatValue(PLAYER_SPELL_CRIT_PERCENTAGE1 + GetFirstSchoolInMask(schoolMask)); - - // register aura mod, this is needed for Arcane Potency - if (Spell* spell = ToPlayer()->m_spellModTakingSpell) - { - std::vector<Aura*> affectingAuras; - (void)GetTotalAuraModifier(SPELL_AURA_MOD_SPELL_CRIT_CHANCE, [&affectingAuras](AuraEffect const* aurEff) -> bool - { - affectingAuras.push_back(aurEff->GetBase()); - return true; - }); - - (void)GetTotalAuraModifier(SPELL_AURA_MOD_SPELL_CRIT_CHANCE_SCHOOL, [&affectingAuras, schoolMask](AuraEffect const* aurEff) -> bool - { - if ((aurEff->GetMiscValue() & schoolMask) != 0) - { - affectingAuras.push_back(aurEff->GetBase()); - return true; - } - - return false; - }); - - for (Aura* aura : affectingAuras) - spell->m_appliedMods.insert(aura); - } - } else { crit_chance = (float)m_baseSpellCritChance; crit_chance += GetTotalAuraModifierByMiscMask(SPELL_AURA_MOD_SPELL_CRIT_CHANCE_SCHOOL, schoolMask); } + break; + } + case SPELL_DAMAGE_CLASS_MELEE: + case SPELL_DAMAGE_CLASS_RANGED: + { + crit_chance += GetUnitCriticalChanceDone(attackType); + crit_chance += GetTotalAuraModifierByMiscMask(SPELL_AURA_MOD_SPELL_CRIT_CHANCE_SCHOOL, schoolMask); + break; + } + case SPELL_DAMAGE_CLASS_NONE: + default: + return 0.0f; + } + // percent done + // only players use intelligence for critical chance computations + if (Player* modOwner = GetSpellModOwner()) + modOwner->ApplySpellMod(spellInfo->Id, SPELLMOD_CRITICAL_CHANCE, crit_chance); + + return std::max(crit_chance, 0.0f); +} + +float Unit::SpellCritChanceTaken(Unit const* caster, SpellInfo const* spellInfo, SpellSchoolMask schoolMask, float doneChance, WeaponAttackType attackType /*= BASE_ATTACK*/) const +{ + // not critting spell + if (spellInfo->HasAttribute(SPELL_ATTR2_CANT_CRIT)) + return 0.0f; + + float crit_chance = doneChance; + switch (spellInfo->DmgClass) + { + case SPELL_DAMAGE_CLASS_MAGIC: + { // taken - if (victim) + if (!spellInfo->IsPositive()) { - if (!spellProto->IsPositive()) - { - // Modify critical chance by victim SPELL_AURA_MOD_ATTACKER_SPELL_CRIT_CHANCE - crit_chance += victim->GetTotalAuraModifierByMiscMask(SPELL_AURA_MOD_ATTACKER_SPELL_CRIT_CHANCE, schoolMask); - // Modify critical chance by victim SPELL_AURA_MOD_ATTACKER_SPELL_AND_WEAPON_CRIT_CHANCE - crit_chance += victim->GetTotalAuraModifier(SPELL_AURA_MOD_ATTACKER_SPELL_AND_WEAPON_CRIT_CHANCE); - ApplyResilience(victim, &crit_chance, nullptr, false, CR_CRIT_TAKEN_SPELL); - } - // scripted (increase crit chance ... against ... target by x% - AuraEffectList const& mOverrideClassScript = GetAuraEffectsByType(SPELL_AURA_OVERRIDE_CLASS_SCRIPTS); + // Modify critical chance by victim SPELL_AURA_MOD_ATTACKER_SPELL_CRIT_CHANCE + crit_chance += GetTotalAuraModifierByMiscMask(SPELL_AURA_MOD_ATTACKER_SPELL_CRIT_CHANCE, schoolMask); + + if (caster && caster->CanApplyResilience()) + Unit::ApplyResilience(this, &crit_chance, nullptr, false, CR_CRIT_TAKEN_SPELL); + + // Modify critical chance by victim SPELL_AURA_MOD_ATTACKER_SPELL_AND_WEAPON_CRIT_CHANCE + // applied after resilience + crit_chance += GetTotalAuraModifier(SPELL_AURA_MOD_ATTACKER_SPELL_AND_WEAPON_CRIT_CHANCE); + } + + // scripted (increase crit chance ... against ... target by x% + if (caster) + { + AuraEffectList const& mOverrideClassScript = caster->GetAuraEffectsByType(SPELL_AURA_OVERRIDE_CLASS_SCRIPTS); for (AuraEffect const* aurEff : mOverrideClassScript) { - if (!aurEff->IsAffectedOnSpell(spellProto)) + if (!aurEff->IsAffectedOnSpell(spellInfo)) continue; float modChance = 0.f; @@ -7320,18 +7352,18 @@ float Unit::GetUnitSpellCriticalChance(Unit* victim, SpellInfo const* spellProto modChance += 17.f; case 849: // Shatter (Rank 1) modChance += 17.f; - if (!victim->HasAuraState(AURA_STATE_FROZEN, spellProto, this)) + if (!HasAuraState(AURA_STATE_FROZEN, spellInfo, caster)) break; crit_chance += modChance; break; case 7917: // Glyph of Shadowburn - if (victim->HasAuraState(AURA_STATE_HEALTHLESS_35_PERCENT, spellProto, this)) + if (HasAuraState(AURA_STATE_HEALTHLESS_35_PERCENT, spellInfo, caster)) crit_chance += aurEff->GetAmount(); break; case 7997: // Renewed Hope case 7998: - if (victim->HasAura(6788)) + if (HasAura(6788)) crit_chance += aurEff->GetAmount(); break; default: @@ -7339,61 +7371,61 @@ float Unit::GetUnitSpellCriticalChance(Unit* victim, SpellInfo const* spellProto } } // Custom crit by class - switch (spellProto->SpellFamilyName) + switch (spellInfo->SpellFamilyName) { case SPELLFAMILY_MAGE: // Glyph of Fire Blast - if (spellProto->SpellFamilyFlags[0] == 0x2 && spellProto->SpellIconID == 12) - if (victim->HasAuraWithMechanic((1 << MECHANIC_STUN) | (1 << MECHANIC_KNOCKOUT))) - if (AuraEffect const* aurEff = GetAuraEffect(56369, EFFECT_0)) + if (spellInfo->SpellFamilyFlags[0] == 0x2 && spellInfo->SpellIconID == 12) + if (HasAuraWithMechanic((1 << MECHANIC_STUN) | (1 << MECHANIC_KNOCKOUT))) + if (AuraEffect const* aurEff = caster->GetAuraEffect(56369, EFFECT_0)) crit_chance += aurEff->GetAmount(); break; case SPELLFAMILY_DRUID: // Improved Faerie Fire - if (victim->HasAuraState(AURA_STATE_FAERIE_FIRE)) - if (AuraEffect const* aurEff = GetDummyAuraEffect(SPELLFAMILY_DRUID, 109, 0)) + if (HasAuraState(AURA_STATE_FAERIE_FIRE)) + if (AuraEffect const* aurEff = caster->GetDummyAuraEffect(SPELLFAMILY_DRUID, 109, 0)) crit_chance += aurEff->GetAmount(); // cumulative effect - don't break // Starfire - if (spellProto->SpellFamilyFlags[0] & 0x4 && spellProto->SpellIconID == 1485) + if (spellInfo->SpellFamilyFlags[0] & 0x4 && spellInfo->SpellIconID == 1485) { // Improved Insect Swarm - if (AuraEffect const* aurEff = GetDummyAuraEffect(SPELLFAMILY_DRUID, 1771, 0)) - if (victim->GetAuraEffect(SPELL_AURA_PERIODIC_DAMAGE, SPELLFAMILY_DRUID, 0x00000002, 0, 0)) + if (AuraEffect const* aurEff = caster->GetDummyAuraEffect(SPELLFAMILY_DRUID, 1771, 0)) + if (GetAuraEffect(SPELL_AURA_PERIODIC_DAMAGE, SPELLFAMILY_DRUID, 0x00000002, 0, 0)) crit_chance += aurEff->GetAmount(); - break; + break; } break; case SPELLFAMILY_ROGUE: // Shiv-applied poisons can't crit - if (FindCurrentSpellBySpellId(5938)) + if (caster->FindCurrentSpellBySpellId(5938)) crit_chance = 0.0f; break; case SPELLFAMILY_PALADIN: // Flash of light - if (spellProto->SpellFamilyFlags[0] & 0x40000000) + if (spellInfo->SpellFamilyFlags[0] & 0x40000000) { // Sacred Shield - if (AuraEffect const* aura = victim->GetAuraEffect(58597, 1, GetGUID())) + if (AuraEffect const* aura = GetAuraEffect(58597, 1, GetGUID())) crit_chance += aura->GetAmount(); break; } // Exorcism - else if (spellProto->GetCategory() == 19) + else if (spellInfo->GetCategory() == 19) { - if (victim->GetCreatureTypeMask() & CREATURE_TYPEMASK_DEMON_OR_UNDEAD) + if (GetCreatureTypeMask() & CREATURE_TYPEMASK_DEMON_OR_UNDEAD) return 100.0f; break; } break; case SPELLFAMILY_SHAMAN: // Lava Burst - if (spellProto->SpellFamilyFlags[1] & 0x00001000) + if (spellInfo->SpellFamilyFlags[1] & 0x00001000) { - if (victim->GetAuraEffect(SPELL_AURA_PERIODIC_DAMAGE, SPELLFAMILY_SHAMAN, 0x10000000, 0, 0, GetGUID())) - if (victim->GetTotalAuraModifier(SPELL_AURA_MOD_ATTACKER_SPELL_AND_WEAPON_CRIT_CHANCE) > -100) + if (GetAuraEffect(SPELL_AURA_PERIODIC_DAMAGE, SPELLFAMILY_SHAMAN, 0x10000000, 0, 0, GetGUID())) + if (GetTotalAuraModifier(SPELL_AURA_MOD_ATTACKER_SPELL_AND_WEAPON_CRIT_CHANCE) > -100) return 100.0f; break; } @@ -7401,67 +7433,61 @@ float Unit::GetUnitSpellCriticalChance(Unit* victim, SpellInfo const* spellProto } // Spell crit suppression - if (victim->GetTypeId() == TYPEID_UNIT) + if (GetTypeId() == TYPEID_UNIT) { - int32 const levelDiff = static_cast<int32>(victim->getLevelForTarget(this)) - getLevel(); + int32 const levelDiff = static_cast<int32>(getLevelForTarget(caster)) - caster->getLevel(); crit_chance -= levelDiff * 0.7f; } } break; } case SPELL_DAMAGE_CLASS_MELEE: - if (victim) + { + // Custom crit by class + if (caster) { - // Custom crit by class - switch (spellProto->SpellFamilyName) + switch (spellInfo->SpellFamilyName) { case SPELLFAMILY_DRUID: // Rend and Tear - bonus crit chance for Ferocious Bite on bleeding targets - if (spellProto->SpellFamilyFlags[0] & 0x00800000 - && spellProto->SpellIconID == 1680 - && victim->HasAuraState(AURA_STATE_BLEEDING)) + if (spellInfo->SpellFamilyFlags[0] & 0x00800000 + && spellInfo->SpellIconID == 1680 + && HasAuraState(AURA_STATE_BLEEDING)) { - if (AuraEffect const* rendAndTear = GetDummyAuraEffect(SPELLFAMILY_DRUID, 2859, 1)) + if (AuraEffect const* rendAndTear = caster->GetDummyAuraEffect(SPELLFAMILY_DRUID, 2859, 1)) crit_chance += rendAndTear->GetAmount(); break; } break; case SPELLFAMILY_WARRIOR: // Victory Rush - if (spellProto->SpellFamilyFlags[1] & 0x100) + if (spellInfo->SpellFamilyFlags[1] & 0x100) { // Glyph of Victory Rush - if (AuraEffect const* aurEff = GetAuraEffect(58382, 0)) + if (AuraEffect const* aurEff = caster->GetAuraEffect(58382, 0)) crit_chance += aurEff->GetAmount(); break; } break; } } + } /// Intentional fallback. Calculate critical strike chance for both Ranged and Melee spells case SPELL_DAMAGE_CLASS_RANGED: - { - if (victim) - { - crit_chance += GetUnitCriticalChance(attackType, victim); - crit_chance += GetTotalAuraModifierByMiscMask(SPELL_AURA_MOD_SPELL_CRIT_CHANCE_SCHOOL, schoolMask); - } + if (caster) + crit_chance = GetUnitCriticalChanceTaken(caster, attackType, crit_chance); break; - } + case SPELL_DAMAGE_CLASS_NONE: default: - return 0.0f; + return 0.f; } - // percent done - // only players use intelligence for critical chance computations - if (Player* modOwner = GetSpellModOwner()) - modOwner->ApplySpellMod(spellProto->Id, SPELLMOD_CRITICAL_CHANCE, crit_chance); // for this types the bonus was already added in GetUnitCriticalChance, do not add twice - if (spellProto->DmgClass != SPELL_DAMAGE_CLASS_MELEE && spellProto->DmgClass != SPELL_DAMAGE_CLASS_RANGED) + if (caster && spellInfo->DmgClass != SPELL_DAMAGE_CLASS_MELEE && spellInfo->DmgClass != SPELL_DAMAGE_CLASS_RANGED) { - crit_chance += victim->GetTotalAuraModifier(SPELL_AURA_MOD_CRIT_CHANCE_FOR_CASTER, [this, spellProto](AuraEffect const* aurEff) -> bool + crit_chance += GetTotalAuraModifier(SPELL_AURA_MOD_CRIT_CHANCE_FOR_CASTER, [caster, spellInfo](AuraEffect const* aurEff) -> bool { - if (aurEff->GetCasterGUID() == GetGUID() && aurEff->IsAffectedOnSpell(spellProto)) + if (aurEff->GetCasterGUID() == caster->GetGUID() && aurEff->IsAffectedOnSpell(spellInfo)) return true; return false; }); @@ -7470,7 +7496,7 @@ float Unit::GetUnitSpellCriticalChance(Unit* victim, SpellInfo const* spellProto return std::max(crit_chance, 0.0f); } -uint32 Unit::SpellCriticalDamageBonus(SpellInfo const* spellProto, uint32 damage, Unit* victim) +/*static*/ uint32 Unit::SpellCriticalDamageBonus(Unit const* caster, SpellInfo const* spellProto, uint32 damage, Unit* victim) { // Calculate critical bonus int32 crit_bonus = damage; @@ -7488,29 +7514,32 @@ uint32 Unit::SpellCriticalDamageBonus(SpellInfo const* spellProto, uint32 damage break; } - crit_mod += (GetTotalAuraMultiplierByMiscMask(SPELL_AURA_MOD_CRIT_DAMAGE_BONUS, spellProto->GetSchoolMask()) - 1.0f) * 100; + if (caster) + { + crit_mod += (caster->GetTotalAuraMultiplierByMiscMask(SPELL_AURA_MOD_CRIT_DAMAGE_BONUS, spellProto->GetSchoolMask()) - 1.0f) * 100; - if (victim) - crit_mod += GetTotalAuraModifierByMiscMask(SPELL_AURA_MOD_CRIT_PERCENT_VERSUS, victim->GetCreatureTypeMask()); + if (victim) + crit_mod += caster->GetTotalAuraModifierByMiscMask(SPELL_AURA_MOD_CRIT_PERCENT_VERSUS, victim->GetCreatureTypeMask()); - if (crit_bonus != 0) - AddPct(crit_bonus, crit_mod); + if (crit_bonus != 0) + AddPct(crit_bonus, crit_mod); - crit_bonus -= damage; + crit_bonus -= damage; - if (damage > uint32(crit_bonus)) - { - // adds additional damage to critBonus (from talents) - if (Player* modOwner = GetSpellModOwner()) - modOwner->ApplySpellMod(spellProto->Id, SPELLMOD_CRIT_DAMAGE_BONUS, crit_bonus); - } + if (damage > uint32(crit_bonus)) + { + // adds additional damage to critBonus (from talents) + if (Player* modOwner = caster->GetSpellModOwner()) + modOwner->ApplySpellMod(spellProto->Id, SPELLMOD_CRIT_DAMAGE_BONUS, crit_bonus); + } - crit_bonus += damage; + crit_bonus += damage; + } return crit_bonus; } -uint32 Unit::SpellCriticalHealingBonus(SpellInfo const* spellProto, uint32 damage, Unit* victim) +/*static*/ uint32 Unit::SpellCriticalHealingBonus(Unit const* caster, SpellInfo const* spellProto, uint32 damage, Unit* victim) { // Calculate critical bonus int32 crit_bonus; @@ -7526,26 +7555,30 @@ uint32 Unit::SpellCriticalHealingBonus(SpellInfo const* spellProto, uint32 damag break; } - if (victim) + if (caster) { - uint32 creatureTypeMask = victim->GetCreatureTypeMask(); - crit_bonus = int32(crit_bonus * GetTotalAuraMultiplierByMiscMask(SPELL_AURA_MOD_CRIT_PERCENT_VERSUS, creatureTypeMask)); + if (victim) + { + uint32 creatureTypeMask = victim->GetCreatureTypeMask(); + crit_bonus = int32(crit_bonus * caster->GetTotalAuraMultiplierByMiscMask(SPELL_AURA_MOD_CRIT_PERCENT_VERSUS, creatureTypeMask)); + } } if (crit_bonus > 0) damage += crit_bonus; - damage = int32(float(damage) * GetTotalAuraMultiplier(SPELL_AURA_MOD_CRITICAL_HEALING_AMOUNT)); + if (caster) + damage = int32(float(damage) * caster->GetTotalAuraMultiplier(SPELL_AURA_MOD_CRITICAL_HEALING_AMOUNT)); return damage; } -uint32 Unit::SpellHealingBonusDone(Unit* victim, SpellInfo const* spellProto, uint32 healamount, DamageEffectType damagetype, uint32 stack) const +uint32 Unit::SpellHealingBonusDone(Unit* victim, SpellInfo const* spellProto, uint32 healamount, DamageEffectType damagetype, Optional<float> const& donePctTotal, uint32 stack /*= 1*/) const { // For totems get healing bonus from owner (statue isn't totem in fact) if (GetTypeId() == TYPEID_UNIT && IsTotem()) if (Unit* owner = GetOwner()) - return owner->SpellHealingBonusDone(victim, spellProto, healamount, damagetype, stack); + return owner->SpellHealingBonusDone(victim, spellProto, healamount, damagetype, donePctTotal, stack); // No bonus healing for potion spells if (spellProto->SpellFamilyName == SPELLFAMILY_POTION) @@ -7553,20 +7586,22 @@ uint32 Unit::SpellHealingBonusDone(Unit* victim, SpellInfo const* spellProto, ui float ApCoeffMod = 1.0f; int32 DoneTotal = 0; + float DoneTotalMod = donePctTotal ? *donePctTotal : SpellHealingPctDone(victim, spellProto); // done scripted mod (take it from owner) Unit const* owner = GetOwner() ? GetOwner() : this; AuraEffectList const& mOverrideClassScript= owner->GetAuraEffectsByType(SPELL_AURA_OVERRIDE_CLASS_SCRIPTS); - for (AuraEffectList::const_iterator i = mOverrideClassScript.begin(); i != mOverrideClassScript.end(); ++i) + for (AuraEffect const* aurEff : mOverrideClassScript) { - if (!(*i)->IsAffectedOnSpell(spellProto)) + if (!aurEff->IsAffectedOnSpell(spellProto)) continue; - switch ((*i)->GetMiscValue()) + + switch (aurEff->GetMiscValue()) { case 4415: // Increased Rejuvenation Healing case 4953: case 3736: // Hateful Totem of the Third Wind / Increased Lesser Healing Wave / LK Arena (4/5/6) Totem of the Third Wind / Savage Totem of the Third Wind - DoneTotal += (*i)->GetAmount(); + DoneTotal += aurEff->GetAmount(); break; default: break; @@ -7669,26 +7704,28 @@ uint32 Unit::SpellHealingBonusDone(Unit* victim, SpellInfo const* spellProto, ui DoneTotal = 0; } - float heal = float(int32(healamount) + DoneTotal); + float heal = float(int32(healamount) + DoneTotal) * DoneTotalMod; - // DOTs calculated in AuraEffect::HandlePeriodicHealAurasTick - // Done Percentage for DOT is already calculated, no need to do it again. The percentage mod is applied in Aura::HandleAuraSpecificMods. - if (damagetype != DOT) - { - heal *= SpellHealingPctDone(victim, spellProto); - - // apply spellmod to Done amount - if (Player* modOwner = GetSpellModOwner()) - modOwner->ApplySpellMod(spellProto->Id, SPELLMOD_DAMAGE, heal); - } + // apply spellmod to Done amount + if (Player* modOwner = GetSpellModOwner()) + modOwner->ApplySpellMod(spellProto->Id, damagetype == DOT ? SPELLMOD_DOT : SPELLMOD_DAMAGE, heal); return uint32(std::max(heal, 0.0f)); } float Unit::SpellHealingPctDone(Unit* victim, SpellInfo const* spellProto) const { - // For totems pct done mods are calculated when its calculation is run on the player in SpellHealingBonusDone. + // For totems get healing bonus from owner if (GetTypeId() == TYPEID_UNIT && IsTotem()) + if (Unit* owner = GetOwner()) + return owner->SpellHealingPctDone(victim, spellProto); + + // Some spells don't benefit from done mods + if (spellProto->HasAttribute(SPELL_ATTR3_NO_DONE_BONUS)) + return 1.0f; + + // Some spells don't benefit from done mods + if (spellProto->HasAttribute(SPELL_ATTR6_LIMIT_PCT_HEALING_MODS)) return 1.0f; // No bonus healing for potion spells @@ -7703,27 +7740,28 @@ float Unit::SpellHealingPctDone(Unit* victim, SpellInfo const* spellProto) const // done scripted mod (take it from owner) Unit const* owner = GetOwner() ? GetOwner() : this; AuraEffectList const& mOverrideClassScript= owner->GetAuraEffectsByType(SPELL_AURA_OVERRIDE_CLASS_SCRIPTS); - for (AuraEffectList::const_iterator i = mOverrideClassScript.begin(); i != mOverrideClassScript.end(); ++i) + for (AuraEffect const* aurEff : mOverrideClassScript) { - if (!(*i)->IsAffectedOnSpell(spellProto)) + if (!aurEff->IsAffectedOnSpell(spellProto)) continue; - switch ((*i)->GetMiscValue()) + + switch (aurEff->GetMiscValue()) { case 21: // Test of Faith case 6935: case 6918: if (victim->HealthBelowPct(50)) - AddPct(DoneTotalMod, (*i)->GetAmount()); + AddPct(DoneTotalMod, aurEff->GetAmount()); break; case 7798: // Glyph of Regrowth { if (victim->GetAuraEffect(SPELL_AURA_PERIODIC_HEAL, SPELLFAMILY_DRUID, 0x40, 0, 0)) - AddPct(DoneTotalMod, (*i)->GetAmount()); + AddPct(DoneTotalMod, aurEff->GetAmount()); break; } case 8477: // Nourish Heal Boost { - int32 stepPercent = (*i)->GetAmount(); + int32 stepPercent = aurEff->GetAmount(); int32 modPercent = 0; AuraApplicationMap const& victimAuras = victim->GetAppliedAuras(); for (AuraApplicationMap::const_iterator itr = victimAuras.begin(); itr != victimAuras.end(); ++itr) @@ -7731,6 +7769,7 @@ float Unit::SpellHealingPctDone(Unit* victim, SpellInfo const* spellProto) const Aura const* aura = itr->second->GetBase(); if (aura->GetCasterGUID() != GetGUID()) continue; + SpellInfo const* m_spell = aura->GetSpellInfo(); if (m_spell->SpellFamilyName != SPELLFAMILY_DRUID || !(m_spell->SpellFamilyFlags[1] & 0x00000010 || m_spell->SpellFamilyFlags[0] & 0x50)) @@ -7742,8 +7781,8 @@ float Unit::SpellHealingPctDone(Unit* victim, SpellInfo const* spellProto) const } case 7871: // Glyph of Lesser Healing Wave { - if (victim->GetAuraEffect(SPELL_AURA_DUMMY, SPELLFAMILY_SHAMAN, 0, 0x00000400, 0, GetGUID())) - AddPct(DoneTotalMod, (*i)->GetAmount()); + if (victim->GetAuraEffect(SPELL_AURA_DUMMY, SPELLFAMILY_SHAMAN, 0, 0x00000400, 0)) + AddPct(DoneTotalMod, aurEff->GetAmount()); break; } default: @@ -11591,14 +11630,20 @@ bool Unit::InitTamedPet(Pet* pet, uint8 level, uint32 spell_id) return true; } -void Unit::Kill(Unit* victim, bool durabilityLoss) +/*static*/ void Unit::Kill(Unit* attacker, Unit* victim, bool durabilityLoss /*= true*/) { // Prevent killing unit twice (and giving reward from kill twice) if (!victim->GetHealth()) return; + if (attacker && !attacker->IsInMap(victim)) + attacker = nullptr; + // find player: owner of controlled `this` or `this` itself maybe - Player* player = GetCharmerOrOwnerPlayerOrPlayerItself(); + Player* player = nullptr; + if (attacker) + player = attacker->GetCharmerOrOwnerPlayerOrPlayerItself(); + Creature* creature = victim->ToCreature(); bool isRewardAllowed = true; @@ -11689,48 +11734,44 @@ void Unit::Kill(Unit* victim, bool durabilityLoss) } // Do KILL and KILLED procs. KILL proc is called only for the unit who landed the killing blow (and its owner - for pets and totems) regardless of who tapped the victim - if (IsPet() || IsTotem()) + if (attacker && (attacker->IsPet() || attacker->IsTotem())) { // proc only once for victim - if (Unit* owner = GetOwner()) - owner->ProcSkillsAndAuras(victim, PROC_FLAG_KILL, PROC_FLAG_NONE, PROC_SPELL_TYPE_MASK_ALL, PROC_SPELL_PHASE_NONE, PROC_HIT_NONE, nullptr, nullptr, nullptr); + if (Unit* owner = attacker->GetOwner()) + Unit::ProcSkillsAndAuras(owner, victim, PROC_FLAG_KILL, PROC_FLAG_NONE, PROC_SPELL_TYPE_MASK_ALL, PROC_SPELL_PHASE_NONE, PROC_HIT_NONE, nullptr, nullptr, nullptr); } if (!victim->IsCritter()) - ProcSkillsAndAuras(victim, PROC_FLAG_KILL, PROC_FLAG_KILLED, PROC_SPELL_TYPE_MASK_ALL, PROC_SPELL_PHASE_NONE, PROC_HIT_NONE, nullptr, nullptr, nullptr); + Unit::ProcSkillsAndAuras(attacker, victim, PROC_FLAG_KILL, PROC_FLAG_KILLED, PROC_SPELL_TYPE_MASK_ALL, PROC_SPELL_PHASE_NONE, PROC_HIT_NONE, nullptr, nullptr, nullptr); // Proc auras on death - must be before aura/combat remove - victim->ProcSkillsAndAuras(victim, PROC_FLAG_NONE, PROC_FLAG_DEATH, PROC_SPELL_TYPE_MASK_ALL, PROC_SPELL_PHASE_NONE, PROC_HIT_NONE, nullptr, nullptr, nullptr); + Unit::ProcSkillsAndAuras(victim, victim, PROC_FLAG_NONE, PROC_FLAG_DEATH, PROC_SPELL_TYPE_MASK_ALL, PROC_SPELL_PHASE_NONE, PROC_HIT_NONE, nullptr, nullptr, nullptr); // update get killing blow achievements, must be done before setDeathState to be able to require auras on target // and before Spirit of Redemption as it also removes auras - if (Player* killerPlayer = GetCharmerOrOwnerPlayerOrPlayerItself()) - killerPlayer->UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_GET_KILLING_BLOWS, 1, 0, victim); + if (attacker) + if (Player* killerPlayer = attacker->GetCharmerOrOwnerPlayerOrPlayerItself()) + killerPlayer->UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_GET_KILLING_BLOWS, 1, 0, victim); + // Spirit of Redemption // if talent known but not triggered (check priest class for speedup check) bool spiritOfRedemption = false; if (victim->GetTypeId() == TYPEID_PLAYER && victim->getClass() == CLASS_PRIEST) { - AuraEffectList const& dummyAuras = victim->GetAuraEffectsByType(SPELL_AURA_DUMMY); - for (AuraEffectList::const_iterator itr = dummyAuras.begin(); itr != dummyAuras.end(); ++itr) + if (AuraEffect const* aurEff = victim->GetDummyAuraEffect(SPELLFAMILY_PRIEST, 1654, EFFECT_0)) { - if ((*itr)->GetSpellInfo()->SpellIconID == 1654) - { - AuraEffect const* aurEff = *itr; - // save value before aura remove - uint32 ressSpellId = victim->GetUInt32Value(PLAYER_SELF_RES_SPELL); - if (!ressSpellId) - ressSpellId = victim->ToPlayer()->GetResurrectionSpellId(); - // Remove all expected to remove at death auras (most important negative case like DoT or periodic triggers) - victim->RemoveAllAurasOnDeath(); - // restore for use at real death - victim->SetUInt32Value(PLAYER_SELF_RES_SPELL, ressSpellId); + // save value before aura remove + uint32 ressSpellId = victim->GetUInt32Value(PLAYER_SELF_RES_SPELL); + if (!ressSpellId) + ressSpellId = victim->ToPlayer()->GetResurrectionSpellId(); + // Remove all expected to remove at death auras (most important negative case like DoT or periodic triggers) + victim->RemoveAllAurasOnDeath(); + // restore for use at real death + victim->SetUInt32Value(PLAYER_SELF_RES_SPELL, ressSpellId); - // FORM_SPIRITOFREDEMPTION and related auras - victim->CastSpell(victim, 27827, aurEff); - spiritOfRedemption = true; - break; - } + // FORM_SPIRITOFREDEMPTION and related auras + victim->CastSpell(victim, 27827, aurEff); + spiritOfRedemption = true; } } @@ -11767,8 +11808,8 @@ void Unit::Kill(Unit* victim, bool durabilityLoss) plrVictim->SendDirectMessage(&data); } // Call KilledUnit for creatures - if (GetTypeId() == TYPEID_UNIT && IsAIEnabled) - ToCreature()->AI()->KilledUnit(victim); + if (attacker && attacker->GetTypeId() == TYPEID_UNIT && attacker->IsAIEnabled) + attacker->ToCreature()->AI()->KilledUnit(victim); // last damage from non duel opponent or opponent controlled creature if (plrVictim->duel) @@ -11794,31 +11835,30 @@ void Unit::Kill(Unit* victim, bool durabilityLoss) } // Call KilledUnit for creatures, this needs to be called after the lootable flag is set - if (GetTypeId() == TYPEID_UNIT && IsAIEnabled) - ToCreature()->AI()->KilledUnit(victim); + if (attacker && attacker->GetTypeId() == TYPEID_UNIT && attacker->IsAIEnabled) + attacker->ToCreature()->AI()->KilledUnit(victim); // Call creature just died function if (creature->IsAIEnabled) - creature->AI()->JustDied(this); + creature->AI()->JustDied(attacker); if (TempSummon* summon = creature->ToTempSummon()) if (Unit* summoner = summon->GetSummoner()) if (summoner->ToCreature() && summoner->IsAIEnabled) - summoner->ToCreature()->AI()->SummonedCreatureDies(creature, this); + summoner->ToCreature()->AI()->SummonedCreatureDies(creature, attacker); // Dungeon specific stuff, only applies to players killing creatures if (creature->GetInstanceId()) { Map* instanceMap = creature->GetMap(); - Player* creditedPlayer = GetCharmerOrOwnerPlayerOrPlayerItself(); - /// @todo do instance binding anyway if the charmer/owner is offline - if (instanceMap->IsDungeon() && (creditedPlayer || this == victim)) + /// @todo do instance binding anyway if the charmer/owner is offline + if (instanceMap->IsDungeon() && ((attacker && attacker->GetCharmerOrOwnerPlayerOrPlayerItself()) || attacker == victim)) { if (instanceMap->IsRaidOrHeroicDungeon()) { if (creature->GetCreatureTemplate()->flags_extra & CREATURE_FLAG_EXTRA_INSTANCE_BIND) - ((InstanceMap*)instanceMap)->PermBindAllPlayers(); + instanceMap->ToInstanceMap()->PermBindAllPlayers(); } else { @@ -11835,7 +11875,7 @@ void Unit::Kill(Unit* victim, bool durabilityLoss) // outdoor pvp things, do these after setting the death state, else the player activity notify won't work... doh... // handle player kill only if not suicide (spirit of redemption for example) - if (player && this != victim) + if (player && attacker != victim) { if (OutdoorPvP* pvp = player->GetOutdoorPvP()) pvp->HandleKill(player, victim); @@ -11861,26 +11901,29 @@ void Unit::Kill(Unit* victim, bool durabilityLoss) } // achievement stuff - if (victim->GetTypeId() == TYPEID_PLAYER) + if (attacker && victim->GetTypeId() == TYPEID_PLAYER) { - if (GetTypeId() == TYPEID_UNIT) - victim->ToPlayer()->UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_KILLED_BY_CREATURE, GetEntry()); - else if (GetTypeId() == TYPEID_PLAYER && victim != this) - victim->ToPlayer()->UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_KILLED_BY_PLAYER, 1, ToPlayer()->GetTeam()); + if (attacker->GetTypeId() == TYPEID_UNIT) + victim->ToPlayer()->UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_KILLED_BY_CREATURE, attacker->GetEntry()); + else if (attacker->GetTypeId() == TYPEID_PLAYER && victim != attacker) + victim->ToPlayer()->UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_KILLED_BY_PLAYER, 1, attacker->ToPlayer()->GetTeam()); } // Hook for OnPVPKill Event - if (Player* killerPlr = ToPlayer()) - { - if (Player* killedPlr = victim->ToPlayer()) - sScriptMgr->OnPVPKill(killerPlr, killedPlr); - else if (Creature* killedCre = victim->ToCreature()) - sScriptMgr->OnCreatureKill(killerPlr, killedCre); - } - else if (Creature* killerCre = ToCreature()) + if (attacker) { - if (Player* killed = victim->ToPlayer()) - sScriptMgr->OnPlayerKilledByCreature(killerCre, killed); + if (Player* killerPlr = attacker->ToPlayer()) + { + if (Player* killedPlr = victim->ToPlayer()) + sScriptMgr->OnPVPKill(killerPlr, killedPlr); + else if (Creature* killedCre = victim->ToCreature()) + sScriptMgr->OnCreatureKill(killerPlr, killedCre); + } + else if (Creature* killerCre = attacker->ToCreature()) + { + if (Player* killed = victim->ToPlayer()) + sScriptMgr->OnPlayerKilledByCreature(killerCre, killed); + } } } @@ -12649,23 +12692,26 @@ void Unit::SendPlaySpellImpact(ObjectGuid guid, uint32 id) SendMessageToSet(&data, false); } -void Unit::ApplyResilience(Unit const* victim, float* crit, int32* damage, bool isCrit, CombatRating type) const +bool Unit::CanApplyResilience() const +{ + return !IsVehicle() && GetOwnerGUID().IsPlayer(); +} + +/*static*/ void Unit::ApplyResilience(Unit const* victim, float* crit, int32* damage, bool isCrit, CombatRating type) { // player mounted on multi-passenger mount is also classified as vehicle - if (IsVehicle() || (victim->IsVehicle() && victim->GetTypeId() != TYPEID_PLAYER)) + if (victim->IsVehicle() && victim->GetTypeId() != TYPEID_PLAYER) return; - Unit const* source = nullptr; - if (GetTypeId() == TYPEID_PLAYER) - source = this; - else if (GetTypeId() == TYPEID_UNIT && GetOwner() && GetOwner()->GetTypeId() == TYPEID_PLAYER) - source = GetOwner(); - Unit const* target = nullptr; if (victim->GetTypeId() == TYPEID_PLAYER) target = victim; - else if (victim->GetTypeId() == TYPEID_UNIT && victim->GetOwner() && victim->GetOwner()->GetTypeId() == TYPEID_PLAYER) - target = victim->GetOwner(); + else // victim->GetTypeId() == TYPEID_UNIT + { + if (Unit* owner = victim->GetOwner()) + if (owner->GetTypeId() == TYPEID_PLAYER) + target = owner; + } if (!target) return; @@ -12676,7 +12722,7 @@ void Unit::ApplyResilience(Unit const* victim, float* crit, int32* damage, bool // Crit chance reduction works against nonpets if (crit) *crit -= target->GetMeleeCritChanceReduction(); - if (source && damage) + if (damage) { if (isCrit) *damage -= target->GetMeleeCritDamageReduction(*damage); @@ -12687,7 +12733,7 @@ void Unit::ApplyResilience(Unit const* victim, float* crit, int32* damage, bool // Crit chance reduction works against nonpets if (crit) *crit -= target->GetRangedCritChanceReduction(); - if (source && damage) + if (damage) { if (isCrit) *damage -= target->GetRangedCritDamageReduction(*damage); @@ -12698,7 +12744,7 @@ void Unit::ApplyResilience(Unit const* victim, float* crit, int32* damage, bool // Crit chance reduction works against nonpets if (crit) *crit -= target->GetSpellCritChanceReduction(); - if (source && damage) + if (damage) { if (isCrit) *damage -= target->GetSpellCritDamageReduction(*damage); @@ -12710,6 +12756,15 @@ void Unit::ApplyResilience(Unit const* victim, float* crit, int32* damage, bool } } +int32 Unit::CalculateAOEAvoidance(int32 damage, uint32 schoolMask, Unit* caster) const +{ + damage = int32(float(damage) * GetTotalAuraMultiplierByMiscMask(SPELL_AURA_MOD_AOE_DAMAGE_AVOIDANCE, schoolMask)); + if (caster->GetTypeId() == TYPEID_UNIT) + damage = int32(float(damage) * GetTotalAuraMultiplierByMiscMask(SPELL_AURA_MOD_CREATURE_AOE_DAMAGE_AVOIDANCE, schoolMask)); + + return damage; +} + // 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 const* victim, WeaponAttackType attType, int32 skillDiff, uint32 spellId) const diff --git a/src/server/game/Entities/Unit/Unit.h b/src/server/game/Entities/Unit/Unit.h index 6adf01b367c..407168b4dab 100644 --- a/src/server/game/Entities/Unit/Unit.h +++ b/src/server/game/Entities/Unit/Unit.h @@ -846,7 +846,7 @@ class TC_GAME_API Unit : public WorldObject uint32 GetResistance(SpellSchools school) const { return GetUInt32Value(UNIT_FIELD_RESISTANCES+school); } uint32 GetResistance(SpellSchoolMask mask) const; void SetResistance(SpellSchools school, int32 val) { SetStatInt32Value(UNIT_FIELD_RESISTANCES+school, val); } - float CalculateAverageResistReduction(SpellSchoolMask schoolMask, Unit const* victim, SpellInfo const* spellInfo = nullptr) const; + static float CalculateAverageResistReduction(Unit const* attacker, SpellSchoolMask schoolMask, Unit const* victim, SpellInfo const* spellInfo = nullptr); uint32 GetHealth() const { return GetUInt32Value(UNIT_FIELD_HEALTH); } uint32 GetMaxHealth() const { return GetUInt32Value(UNIT_FIELD_MAXHEALTH); } @@ -921,13 +921,13 @@ class TC_GAME_API Unit : public WorldObject void Dismount(); uint32 GetMaxSkillValueForLevel(Unit const* target = nullptr) const { return (target ? getLevelForTarget(target) : getLevel()) * 5; } - void DealDamageMods(Unit const* victim, uint32 &damage, uint32* absorb) const; - uint32 DealDamage(Unit* victim, uint32 damage, CleanDamage const* cleanDamage = nullptr, DamageEffectType damagetype = DIRECT_DAMAGE, SpellSchoolMask damageSchoolMask = SPELL_SCHOOL_MASK_NORMAL, SpellInfo const* spellProto = nullptr, bool durabilityLoss = true); - void Kill(Unit* victim, bool durabilityLoss = true); - void KillSelf(bool durabilityLoss = true) { Kill(this, durabilityLoss); } - void DealHeal(HealInfo& healInfo); + static void DealDamageMods(Unit const* victim, uint32& damage, uint32* absorb); + static uint32 DealDamage(Unit* attacker, Unit* victim, uint32 damage, CleanDamage const* cleanDamage = nullptr, DamageEffectType damagetype = DIRECT_DAMAGE, SpellSchoolMask damageSchoolMask = SPELL_SCHOOL_MASK_NORMAL, SpellInfo const* spellProto = nullptr, bool durabilityLoss = true); + static void Kill(Unit* attacker, Unit* victim, bool durabilityLoss = true); + void KillSelf(bool durabilityLoss = true) { Unit::Kill(this, this, durabilityLoss); } + static void DealHeal(HealInfo& healInfo); - void ProcSkillsAndAuras(Unit* actionTarget, uint32 typeMaskActor, uint32 typeMaskActionTarget, + static void ProcSkillsAndAuras(Unit* actor, Unit* actionTarget, uint32 typeMaskActor, uint32 typeMaskActionTarget, uint32 spellTypeMask, uint32 spellPhaseMask, uint32 hitMask, Spell* spell, DamageInfo* damageInfo, HealInfo* healInfo); @@ -964,7 +964,10 @@ class TC_GAME_API Unit : public WorldObject uint32 GetRangedDamageReduction(uint32 damage) const { return GetCombatRatingDamageReduction(CR_CRIT_TAKEN_RANGED, 2.0f, 100.0f, damage); } uint32 GetSpellDamageReduction(uint32 damage) const { return GetCombatRatingDamageReduction(CR_CRIT_TAKEN_SPELL, 2.0f, 100.0f, damage); } - void ApplyResilience(Unit const* victim, float* crit, int32* damage, bool isCrit, CombatRating type) const; + virtual bool CanApplyResilience() const; + static void ApplyResilience(Unit const* victim, float* crit, int32* damage, bool isCrit, CombatRating type); + + int32 CalculateAOEAvoidance(int32 damage, uint32 schoolMask, Unit* caster) const; float MeleeSpellMissChance(Unit const* victim, WeaponAttackType attType, int32 skillDiff, uint32 spellId) const; SpellMissInfo MeleeSpellHitResult(Unit* victim, SpellInfo const* spellInfo) const; @@ -975,7 +978,9 @@ class TC_GAME_API Unit : public WorldObject float GetUnitParryChance(WeaponAttackType attType, Unit const* victim) const; float GetUnitBlockChance(WeaponAttackType attType, Unit const* victim) const; float GetUnitMissChance(WeaponAttackType attType) const; - float GetUnitCriticalChance(WeaponAttackType attackType, Unit const* victim) const; + float GetUnitCriticalChanceDone(WeaponAttackType attackType) const; + float GetUnitCriticalChanceTaken(Unit const* attacker, WeaponAttackType attackType, float critDone) const; + float GetUnitCriticalChanceAgainst(WeaponAttackType attackType, Unit const* victim) const; int32 GetMechanicResistChance(SpellInfo const* spellInfo) const; bool CanUseAttackType(uint8 attacktype) const; @@ -1500,12 +1505,12 @@ class TC_GAME_API Unit : public WorldObject int32 SpellBaseDamageBonusDone(SpellSchoolMask schoolMask) const; int32 SpellBaseDamageBonusTaken(SpellSchoolMask schoolMask) const; - uint32 SpellDamageBonusDone(Unit* victim, SpellInfo const* spellProto, uint32 pdamage, DamageEffectType damagetype, uint32 stack = 1) const; + uint32 SpellDamageBonusDone(Unit* victim, SpellInfo const* spellProto, uint32 pdamage, DamageEffectType damagetype, Optional<float> const& donePctTotal, uint32 stack = 1) const; float SpellDamagePctDone(Unit* victim, SpellInfo const* spellProto, DamageEffectType damagetype) const; uint32 SpellDamageBonusTaken(Unit* caster, SpellInfo const* spellProto, uint32 pdamage, DamageEffectType damagetype, uint32 stack = 1) const; int32 SpellBaseHealingBonusDone(SpellSchoolMask schoolMask) const; int32 SpellBaseHealingBonusTaken(SpellSchoolMask schoolMask) const; - uint32 SpellHealingBonusDone(Unit* victim, SpellInfo const* spellProto, uint32 healamount, DamageEffectType damagetype, uint32 stack = 1) const; + uint32 SpellHealingBonusDone(Unit* victim, SpellInfo const* spellProto, uint32 healamount, DamageEffectType damagetype, Optional<float> const& donePctTotal, uint32 stack = 1) const; float SpellHealingPctDone(Unit* victim, SpellInfo const* spellProto) const; uint32 SpellHealingBonusTaken(Unit* caster, SpellInfo const* spellProto, uint32 healamount, DamageEffectType damagetype, uint32 stack = 1) const; @@ -1514,10 +1519,10 @@ class TC_GAME_API Unit : public WorldObject bool isSpellBlocked(Unit* victim, SpellInfo const* spellProto, WeaponAttackType attackType = BASE_ATTACK); bool isBlockCritical(); - bool IsSpellCrit(Unit* victim, SpellInfo const* spellProto, SpellSchoolMask schoolMask, WeaponAttackType attackType = BASE_ATTACK) const; - float GetUnitSpellCriticalChance(Unit* victim, SpellInfo const* spellProto, SpellSchoolMask schoolMask, WeaponAttackType attackType = BASE_ATTACK) const; - uint32 SpellCriticalDamageBonus(SpellInfo const* spellProto, uint32 damage, Unit* victim); - uint32 SpellCriticalHealingBonus(SpellInfo const* spellProto, uint32 damage, Unit* victim); + float SpellCritChanceDone(SpellInfo const* spellInfo, SpellSchoolMask schoolMask, WeaponAttackType attackType = BASE_ATTACK) const; + float SpellCritChanceTaken(Unit const* caster, SpellInfo const* spellInfo, SpellSchoolMask schoolMask, float doneChance, WeaponAttackType attackType = BASE_ATTACK) const; + static uint32 SpellCriticalDamageBonus(Unit const* caster, SpellInfo const* spellProto, uint32 damage, Unit* victim); + static uint32 SpellCriticalHealingBonus(Unit const* caster, SpellInfo const* spellProto, uint32 damage, Unit* victim); void SetLastManaUse(uint32 spellCastTime) { m_lastManaUse = spellCastTime; } bool IsUnderLastManaUseEffect() const; @@ -1538,10 +1543,10 @@ class TC_GAME_API Unit : public WorldObject virtual bool IsImmunedToSpellEffect(SpellInfo const* spellInfo, uint32 index, Unit* caster) const; // redefined in Creature static bool IsDamageReducedByArmor(SpellSchoolMask damageSchoolMask, SpellInfo const* spellInfo = nullptr, int8 effIndex = -1); - uint32 CalcArmorReducedDamage(Unit* victim, const uint32 damage, SpellInfo const* spellInfo, WeaponAttackType attackType = MAX_ATTACK) const; - uint32 CalcSpellResistedDamage(Unit* victim, uint32 damage, SpellSchoolMask schoolMask, SpellInfo const* spellInfo) const; - void CalcAbsorbResist(DamageInfo& damageInfo); - void CalcHealAbsorb(HealInfo& healInfo) const; + static uint32 CalcArmorReducedDamage(Unit const* attacker, Unit* victim, uint32 damage, SpellInfo const* spellInfo, uint8 attackerLevel = 0, WeaponAttackType attackType = MAX_ATTACK); + static uint32 CalcSpellResistedDamage(Unit const* attacker, Unit* victim, uint32 damage, SpellSchoolMask schoolMask, SpellInfo const* spellInfo); + static void CalcAbsorbResist(DamageInfo& damageInfo); + static void CalcHealAbsorb(HealInfo& healInfo); void UpdateSpeed(UnitMoveType mtype); float GetSpeed(UnitMoveType mtype) const; |
