diff options
author | ariel- <ariel-@users.noreply.github.com> | 2016-10-04 01:09:27 -0300 |
---|---|---|
committer | ariel- <ariel-@users.noreply.github.com> | 2016-10-04 01:26:34 -0300 |
commit | 0c24e4ee0e1f0bbb37f740aa6f0d45c384ee955d (patch) | |
tree | 659b3fe178d8473edbdb85445308113b649603d8 /src | |
parent | c25f7c48b5ba3982fc83b8c88a8cff619f72b162 (diff) |
Core/Unit: rewrite of the attack table system
- Removed a bunch of duplicated code
- Fix off-by-one errors in Unit::RollMeleeOutcomeAgainst and Unit::MeleeSpellHitResult (TC's combat table was actually of 100.01%)
- Implemented boss-level hit table (6.5% dodge/14% parry), bosses only had 5.6% of each until now
- Updated formula for chance and damage of Glancing hits
Sources:
- http://wow.gamepedia.com/index.php?title=Attack_table&oldid=2071465
- http://web.archive.org/web/20100903145646/http://www.mmo-champion.com/threads/650071-Expertise-Hit-for-Paladins-%28updated-for-3.3%29?daysprune=60
Diffstat (limited to 'src')
-rw-r--r-- | src/server/game/Entities/Player/Player.cpp | 10 | ||||
-rw-r--r-- | src/server/game/Entities/Player/Player.h | 4 | ||||
-rw-r--r-- | src/server/game/Entities/Unit/StatSystem.cpp | 2 | ||||
-rw-r--r-- | src/server/game/Entities/Unit/Unit.cpp | 411 | ||||
-rw-r--r-- | src/server/game/Entities/Unit/Unit.h | 24 | ||||
-rw-r--r-- | src/server/game/Spells/Auras/SpellAuraEffects.cpp | 2 | ||||
-rw-r--r-- | src/server/scripts/Spells/spell_dk.cpp | 2 |
7 files changed, 231 insertions, 224 deletions
diff --git a/src/server/game/Entities/Player/Player.cpp b/src/server/game/Entities/Player/Player.cpp index 01bc075a35b..cdd9da88d7b 100644 --- a/src/server/game/Entities/Player/Player.cpp +++ b/src/server/game/Entities/Player/Player.cpp @@ -5897,7 +5897,7 @@ void Player::UpdateWeaponSkill(WeaponAttackType attType) UpdateAllCritPercentages(); } -void Player::UpdateCombatSkills(Unit* victim, WeaponAttackType attType, bool defence) +void Player::UpdateCombatSkills(Unit* victim, WeaponAttackType attType, bool defense) { uint8 plevel = getLevel(); // if defense than victim == attacker uint8 greylevel = Trinity::XP::GetGrayLevel(plevel); @@ -5910,12 +5910,12 @@ void Player::UpdateCombatSkills(Unit* victim, WeaponAttackType attType, bool def if (lvldif < 3) lvldif = 3; - uint32 skilldif = 5 * plevel - (defence ? GetBaseDefenseSkillValue() : GetBaseWeaponSkillValue(attType)); + uint32 skilldif = 5 * plevel - (defense ? GetBaseDefenseSkillValue() : GetBaseWeaponSkillValue(attType)); if (skilldif <= 0) return; float chance = float(3 * lvldif * skilldif) / plevel; - if (!defence) + if (!defense) if (getClass() == CLASS_WARRIOR || getClass() == CLASS_ROGUE) chance += chance * 0.02f * GetStat(STAT_INTELLECT); @@ -5923,7 +5923,7 @@ void Player::UpdateCombatSkills(Unit* victim, WeaponAttackType attType, bool def if (roll_chance_f(chance)) { - if (defence) + if (defense) UpdateDefense(); else UpdateWeaponSkill(attType); @@ -13716,7 +13716,7 @@ void Player::ApplyEnchantment(Item* item, EnchantmentSlot slot, bool apply, bool break; case ITEM_MOD_DEFENSE_SKILL_RATING: ApplyRatingMod(CR_DEFENSE_SKILL, enchant_amount, apply); - TC_LOG_DEBUG("entities.player.items", "+ %u DEFENCE", enchant_amount); + TC_LOG_DEBUG("entities.player.items", "+ %u DEFENSE", enchant_amount); break; case ITEM_MOD_DODGE_RATING: ApplyRatingMod(CR_DODGE, enchant_amount, apply); diff --git a/src/server/game/Entities/Player/Player.h b/src/server/game/Entities/Player/Player.h index 56e0ed70496..269cbb4a9ef 100644 --- a/src/server/game/Entities/Player/Player.h +++ b/src/server/game/Entities/Player/Player.h @@ -1741,7 +1741,7 @@ class TC_GAME_API Player : public Unit, public GridObject<Player> void RecalculateRating(CombatRating cr) { ApplyRatingMod(cr, 0, true);} float GetMeleeCritFromAgility() const; void GetDodgeFromAgility(float &diminishing, float &nondiminishing) const; - float GetMissPercentageFromDefence() const; + float GetMissPercentageFromDefense() const; float GetSpellCritFromIntellect() const; float OCTRegenHPPerSpirit() const; float OCTRegenMPPerSpirit() const; @@ -1849,7 +1849,7 @@ class TC_GAME_API Player : public Unit, public GridObject<Player> void UpdateDefense(); void UpdateWeaponSkill (WeaponAttackType attType); - void UpdateCombatSkills(Unit* victim, WeaponAttackType attType, bool defence); + void UpdateCombatSkills(Unit* victim, WeaponAttackType attType, bool defense); void SetSkill(uint16 id, uint16 step, uint16 newVal, uint16 maxVal); uint16 GetMaxSkillValue(uint32 skill) const; // max + perm. bonus + temp bonus diff --git a/src/server/game/Entities/Unit/StatSystem.cpp b/src/server/game/Entities/Unit/StatSystem.cpp index d1984da9648..699709fabb1 100644 --- a/src/server/game/Entities/Unit/StatSystem.cpp +++ b/src/server/game/Entities/Unit/StatSystem.cpp @@ -666,7 +666,7 @@ const float m_diminishing_k[MAX_CLASSES] = 0.9720f // Druid }; -float Player::GetMissPercentageFromDefence() const +float Player::GetMissPercentageFromDefense() const { float const miss_cap[MAX_CLASSES] = { diff --git a/src/server/game/Entities/Unit/Unit.cpp b/src/server/game/Entities/Unit/Unit.cpp index e0d73809d5a..6467aa5969d 100644 --- a/src/server/game/Entities/Unit/Unit.cpp +++ b/src/server/game/Entities/Unit/Unit.cpp @@ -1302,7 +1302,12 @@ void Unit::CalculateMeleeDamage(Unit* victim, uint32 damage, CalcDamageInfo* dam int32 leveldif = int32(victim->getLevel()) - int32(getLevel()); if (leveldif > 3) leveldif = 3; - float reducePercent = 1 - leveldif * 0.1f; + + // against boss-level targets - 24% chance of 25% average damage reduction (damage reduction range : 20-30%) + // against level 82 elites - 18% chance of 15% average damage reduction (damage reduction range : 10-20%) + int32 const reductionMax = leveldif * 10; + int32 const reductionMin = reductionMax - 10; + float reducePercent = 1.f - irand(reductionMin, reductionMax) / 100.0f; damageInfo->cleanDamage += damageInfo->damage - uint32(reducePercent * damageInfo->damage); damageInfo->damage = uint32(reducePercent * damageInfo->damage); break; @@ -2030,7 +2035,7 @@ void Unit::HandleProcExtraAttackFor(Unit* victim) } } -MeleeHitOutcome Unit::RollMeleeOutcomeAgainst(const Unit* victim, WeaponAttackType attType) const +MeleeHitOutcome Unit::RollMeleeOutcomeAgainst(Unit const* victim, WeaponAttackType attType) const { // This is only wrapper @@ -2041,10 +2046,9 @@ MeleeHitOutcome Unit::RollMeleeOutcomeAgainst(const Unit* victim, WeaponAttackTy // Critical hit chance float crit_chance = GetUnitCriticalChance(attType, victim); - // stunned target cannot dodge and this is check in GetUnitDodgeChance() (returned 0 in this case) - float dodge_chance = victim->GetUnitDodgeChance(); - float block_chance = victim->GetUnitBlockChance(); - float parry_chance = victim->GetUnitParryChance(); + float dodge_chance = GetUnitDodgeChance(attType, victim); + float block_chance = GetUnitBlockChance(attType, victim); + float parry_chance = GetUnitParryChance(attType, victim); // Useful if want to specify crit & miss chances for melee, else it could be removed TC_LOG_DEBUG("entities.unit", "MELEE OUTCOME: miss %f crit %f dodge %f parry %f block %f", miss_chance, crit_chance, dodge_chance, parry_chance, block_chance); @@ -2052,123 +2056,68 @@ MeleeHitOutcome Unit::RollMeleeOutcomeAgainst(const Unit* victim, WeaponAttackTy return RollMeleeOutcomeAgainst(victim, attType, int32(crit_chance*100), int32(miss_chance*100), int32(dodge_chance*100), int32(parry_chance*100), int32(block_chance*100)); } -MeleeHitOutcome Unit::RollMeleeOutcomeAgainst (const Unit* victim, WeaponAttackType attType, int32 crit_chance, int32 miss_chance, int32 dodge_chance, int32 parry_chance, int32 block_chance) const +MeleeHitOutcome Unit::RollMeleeOutcomeAgainst(Unit const* victim, WeaponAttackType attType, int32 crit_chance, int32 miss_chance, int32 dodge_chance, int32 parry_chance, int32 block_chance) const { if (victim->GetTypeId() == TYPEID_UNIT && victim->ToCreature()->IsEvadingAttacks()) return MELEE_HIT_EVADE; + // melee attack table implementation + // outcome priority: + // 1. > 2. > 3. > 4. > 5. > 6. > 7. > 8. + // MISS > DODGE > PARRY > GLANCING > BLOCK > CRIT > CRUSHING > HIT + int32 attackerMaxSkillValueForLevel = GetMaxSkillValueForLevel(victim); int32 victimMaxSkillValueForLevel = victim->GetMaxSkillValueForLevel(this); int32 attackerWeaponSkill = GetWeaponSkillValue(attType, victim); int32 victimDefenseSkill = victim->GetDefenseSkillValue(this); - // bonus from skills is 0.04% - int32 skillBonus = 4 * (attackerWeaponSkill - victimMaxSkillValueForLevel); int32 sum = 0, tmp = 0; - int32 roll = urand (0, 10000); + int32 roll = urand(0, 9999); - TC_LOG_DEBUG("entities.unit", "RollMeleeOutcomeAgainst: skill bonus of %d for attacker", skillBonus); - TC_LOG_DEBUG("entities.unit", "RollMeleeOutcomeAgainst: rolled %d, miss %d, dodge %d, parry %d, block %d, crit %d", - roll, miss_chance, dodge_chance, parry_chance, block_chance, crit_chance); + // check if attack comes from behind, nobody can parry or block if attacker is behind + bool canParryOrBlock = victim->HasInArc(float(M_PI), this) || victim->HasAuraType(SPELL_AURA_IGNORE_HIT_DIRECTION); - tmp = miss_chance; + // only creatures can dodge if attacker is behind + bool canDodge = victim->GetTypeId() != TYPEID_PLAYER || canParryOrBlock; - if (tmp > 0 && roll < (sum += tmp)) + // if victim is casting or cc'd it can't avoid attacks + if (victim->IsNonMeleeSpellCast(false) || victim->HasUnitState(UNIT_STATE_CONTROLLED)) { - TC_LOG_DEBUG("entities.unit", "RollMeleeOutcomeAgainst: MISS"); - return MELEE_HIT_MISS; + canDodge = false; + canParryOrBlock = false; } + // 1. MISS + tmp = miss_chance; + if (tmp > 0 && roll < (sum += tmp)) + return MELEE_HIT_MISS; + // always crit against a sitting target (except 0 crit chance) if (victim->GetTypeId() == TYPEID_PLAYER && crit_chance > 0 && !victim->IsStandState()) - { - TC_LOG_DEBUG("entities.unit", "RollMeleeOutcomeAgainst: CRIT (sitting victim)"); return MELEE_HIT_CRIT; - } - - // Dodge chance - // only players can't dodge if attacker is behind - if (victim->GetTypeId() == TYPEID_PLAYER && !victim->HasInArc(float(M_PI), this) && !victim->HasAuraType(SPELL_AURA_IGNORE_HIT_DIRECTION)) - { - TC_LOG_DEBUG("entities.unit", "RollMeleeOutcomeAgainst: attack came from behind and victim was a player."); - } - else + // 2. DODGE + if (canDodge) { - // Reduce dodge chance by attacker expertise rating - if (GetTypeId() == TYPEID_PLAYER) - dodge_chance -= int32(ToPlayer()->GetExpertiseDodgeOrParryReduction(attType) * 100); - else - dodge_chance -= GetTotalAuraModifier(SPELL_AURA_MOD_EXPERTISE) * 25; - - // Modify dodge chance by attacker SPELL_AURA_MOD_COMBAT_RESULT_CHANCE - dodge_chance+= GetTotalAuraModifierByMiscValue(SPELL_AURA_MOD_COMBAT_RESULT_CHANCE, VICTIMSTATE_DODGE) * 100; - dodge_chance = int32 (float (dodge_chance) * GetTotalAuraMultiplier(SPELL_AURA_MOD_ENEMY_DODGE)); - tmp = dodge_chance; - if ((tmp > 0) // check if unit _can_ dodge - && ((tmp -= skillBonus) > 0) + if (tmp > 0 // check if unit _can_ dodge && roll < (sum += tmp)) - { - TC_LOG_DEBUG("entities.unit", "RollMeleeOutcomeAgainst: DODGE <%d, %d)", sum-tmp, sum); return MELEE_HIT_DODGE; - } - } - - // parry & block chances - - // check if attack comes from behind, nobody can parry or block if attacker is behind - if (!victim->HasInArc(float(M_PI), this) && !victim->HasAuraType(SPELL_AURA_IGNORE_HIT_DIRECTION)) - TC_LOG_DEBUG("entities.unit", "RollMeleeOutcomeAgainst: attack came from behind."); - else - { - // Reduce parry chance by attacker expertise rating - if (GetTypeId() == TYPEID_PLAYER) - parry_chance -= int32(ToPlayer()->GetExpertiseDodgeOrParryReduction(attType) * 100); - else - parry_chance -= GetTotalAuraModifier(SPELL_AURA_MOD_EXPERTISE) * 25; - - if (victim->GetTypeId() == TYPEID_PLAYER || !(victim->ToCreature()->GetCreatureTemplate()->flags_extra & CREATURE_FLAG_EXTRA_NO_PARRY)) - { - int32 tmp2 = int32(parry_chance); - if (tmp2 > 0 // check if unit _can_ parry - && (tmp2 -= skillBonus) > 0 - && roll < (sum += tmp2)) - { - TC_LOG_DEBUG("entities.unit", "RollMeleeOutcomeAgainst: PARRY <%d, %d)", sum-tmp2, sum); - return MELEE_HIT_PARRY; - } - } - - if (victim->GetTypeId() == TYPEID_PLAYER || !(victim->ToCreature()->GetCreatureTemplate()->flags_extra & CREATURE_FLAG_EXTRA_NO_BLOCK)) - { - tmp = block_chance; - if (tmp > 0 // check if unit _can_ block - && (tmp -= skillBonus) > 0 - && roll < (sum += tmp)) - { - TC_LOG_DEBUG("entities.unit", "RollMeleeOutcomeAgainst: BLOCK <%d, %d)", sum-tmp, sum); - return MELEE_HIT_BLOCK; - } - } } - // Critical chance - tmp = crit_chance; - - if (tmp > 0 && roll < (sum += tmp)) + // 3. PARRY + if (canParryOrBlock) { - TC_LOG_DEBUG("entities.unit", "RollMeleeOutcomeAgainst: CRIT <%d, %d)", sum-tmp, sum); - if (GetTypeId() == TYPEID_UNIT && (ToCreature()->GetCreatureTemplate()->flags_extra & CREATURE_FLAG_EXTRA_NO_CRIT)) - TC_LOG_DEBUG("entities.unit", "RollMeleeOutcomeAgainst: CRIT DISABLED)"); - else - return MELEE_HIT_CRIT; + tmp = parry_chance; + if (tmp > 0 // check if unit _can_ parry + && roll < (sum += tmp)) + return MELEE_HIT_PARRY; } + // 4. GLANCING // Max 40% chance to score a glancing blow against mobs that are higher level (can do only players and pets and not with ranged weapon) - if (attType != RANGED_ATTACK && - (GetTypeId() == TYPEID_PLAYER || IsPet()) && + if ((GetTypeId() == TYPEID_PLAYER || IsPet()) && victim->GetTypeId() != TYPEID_PLAYER && !victim->IsPet() && getLevel() < victim->getLevelForTarget(this)) { @@ -2177,15 +2126,32 @@ MeleeHitOutcome Unit::RollMeleeOutcomeAgainst (const Unit* victim, WeaponAttackT int32 maxskill = attackerMaxSkillValueForLevel; skill = (skill > maxskill) ? maxskill : skill; - tmp = (10 + (victimDefenseSkill - skill)) * 100; - tmp = tmp > 4000 ? 4000 : tmp; - if (roll < (sum += tmp)) - { - TC_LOG_DEBUG("entities.unit", "RollMeleeOutcomeAgainst: GLANCING <%d, %d)", sum-4000, sum); + // against boss-level targets - 24% chance of 25% average damage reduction (damage reduction range : 20-30%) + // against level 82 elites - 18% chance of 15% average damage reduction (damage reduction range : 10-20%) + tmp = 600 + (victimDefenseSkill - skill) * 120; + tmp = std::min(tmp, 4000); + if (tmp > 0 && roll < (sum += tmp)) return MELEE_HIT_GLANCING; - } } + // 5. BLOCK + if (canParryOrBlock) + { + tmp = block_chance; + if (tmp > 0 // check if unit _can_ block + && roll < (sum += tmp)) + return MELEE_HIT_BLOCK; + } + + // 6.CRIT + tmp = crit_chance; + if (tmp > 0 && roll < (sum += tmp)) + { + if (GetTypeId() != TYPEID_UNIT || !(ToCreature()->GetCreatureTemplate()->flags_extra & CREATURE_FLAG_EXTRA_NO_CRIT)) + return MELEE_HIT_CRIT; + } + + // 7. CRUSHING // mobs can score crushing blows if they're 4 or more levels above victim if (getLevelForTarget(victim) >= victim->getLevelForTarget(this) + 4 && // can be from by creature (if can) or from controlled player that considered as creature @@ -2194,24 +2160,20 @@ MeleeHitOutcome Unit::RollMeleeOutcomeAgainst (const Unit* victim, WeaponAttackT { // when their weapon skill is 15 or more above victim's defense skill tmp = victimDefenseSkill; - int32 tmpmax = victimMaxSkillValueForLevel; // having defense above your maximum (from items, talents etc.) has no effect - tmp = tmp > tmpmax ? tmpmax : tmp; + tmp = std::min(tmp, victimMaxSkillValueForLevel); // tmp = mob's level * 5 - player's current defense skill tmp = attackerMaxSkillValueForLevel - tmp; - if (tmp >= 15) - { - // add 2% chance per lacking skill point, min. is 15% - tmp = tmp * 200 - 1500; - if (roll < (sum += tmp)) - { - TC_LOG_DEBUG("entities.unit", "RollMeleeOutcomeAgainst: CRUSHING <%d, %d)", sum-tmp, sum); - return MELEE_HIT_CRUSHING; - } - } + // minimum of 20 points diff (4 levels difference) + tmp = std::max(tmp, 20); + + // add 2% chance per lacking skill point + tmp = tmp * 200 - 1500; + if (tmp > 0 && roll < (sum += tmp)) + return MELEE_HIT_CRUSHING; } - TC_LOG_DEBUG("entities.unit", "RollMeleeOutcomeAgainst: NORMAL"); + // 8. HIT return MELEE_HIT_NORMAL; } @@ -2305,6 +2267,10 @@ bool Unit::isSpellBlocked(Unit* victim, SpellInfo const* spellProto, WeaponAttac if (spellProto && spellProto->HasAttribute(SPELL_ATTR0_IMPOSSIBLE_DODGE_PARRY_BLOCK)) return false; + // Can't block when casting/controlled + if (victim->IsNonMeleeSpellCast(false) || victim->HasUnitState(UNIT_STATE_CONTROLLED)) + return false; + if (victim->HasAuraType(SPELL_AURA_IGNORE_HIT_DIRECTION) || victim->HasInArc(float(M_PI), this)) { // Check creatures flags_extra for disable block @@ -2312,11 +2278,11 @@ bool Unit::isSpellBlocked(Unit* victim, SpellInfo const* spellProto, WeaponAttac victim->ToCreature()->GetCreatureTemplate()->flags_extra & CREATURE_FLAG_EXTRA_NO_BLOCK) return false; - float blockChance = victim->GetUnitBlockChance(); - blockChance += (int32(GetWeaponSkillValue(attackType)) - int32(victim->GetMaxSkillValueForLevel())) * 0.04f; + float blockChance = GetUnitBlockChance(attackType, victim); if (roll_chance_f(blockChance)) return true; } + return false; } @@ -2346,7 +2312,8 @@ int32 Unit::GetMechanicResistChance(SpellInfo const* spellInfo) const resistMech = temp; } } - return resistMech; + + return std::max(resistMech, 0); } bool Unit::CanUseAttackType(uint8 attacktype) const @@ -2365,7 +2332,7 @@ bool Unit::CanUseAttackType(uint8 attacktype) const } // Melee based spells hit result calculations -SpellMissInfo Unit::MeleeSpellHitResult(Unit* victim, SpellInfo const* spellInfo) +SpellMissInfo Unit::MeleeSpellHitResult(Unit* victim, SpellInfo const* spellInfo) const { // Spells with SPELL_ATTR3_IGNORE_HIT_RESULT will additionally fully ignore // resist and deflect chances @@ -2380,16 +2347,15 @@ SpellMissInfo Unit::MeleeSpellHitResult(Unit* victim, SpellInfo const* spellInfo attType = RANGED_ATTACK; int32 attackerWeaponSkill; - // skill value for these spells (for example judgements) is 5* level + // skill value for these spells (for example judgements) is 5 * level if (spellInfo->DmgClass == SPELL_DAMAGE_CLASS_RANGED && !spellInfo->IsRangedWeaponSpell()) - attackerWeaponSkill = getLevel() * 5; - // bonus from skills is 0.04% per skill Diff + attackerWeaponSkill = GetMaxSkillValueForLevel(); else attackerWeaponSkill = int32(GetWeaponSkillValue(attType, victim)); int32 skillDiff = attackerWeaponSkill - int32(victim->GetMaxSkillValueForLevel(this)); - uint32 roll = urand (0, 10000); + uint32 roll = urand(0, 9999); uint32 missChance = uint32(MeleeSpellMissChance(victim, attType, skillDiff, spellInfo->Id) * 100.0f); // Roll miss @@ -2411,6 +2377,14 @@ SpellMissInfo Unit::MeleeSpellHitResult(Unit* victim, SpellInfo const* spellInfo bool canParry = true; bool canBlock = spellInfo->HasAttribute(SPELL_ATTR3_BLOCKABLE_SPELL); + // if victim is casting or cc'd it can't avoid attacks + if (victim->IsNonMeleeSpellCast(false) || victim->HasUnitState(UNIT_STATE_CONTROLLED)) + { + canDodge = false; + canParry = false; + canBlock = false; + } + // Ranged attacks can only miss, resist and deflect if (attType == RANGED_ATTACK) { @@ -2433,10 +2407,10 @@ SpellMissInfo Unit::MeleeSpellHitResult(Unit* victim, SpellInfo const* spellInfo { if (!victim->HasAuraType(SPELL_AURA_IGNORE_HIT_DIRECTION)) { - // Can`t dodge from behind in PvP (but its possible in PvE) + // Can't dodge from behind in PvP (but its possible in PvE) if (victim->GetTypeId() == TYPEID_PLAYER) canDodge = false; - // Can`t parry or block + // Can't parry or block canParry = false; canBlock = false; } @@ -2446,23 +2420,15 @@ SpellMissInfo Unit::MeleeSpellHitResult(Unit* victim, SpellInfo const* spellInfo canParry = false; } } - // Check creatures flags_extra for disable parry - if (victim->GetTypeId() == TYPEID_UNIT) - { - uint32 flagEx = victim->ToCreature()->GetCreatureTemplate()->flags_extra; - if (flagEx & CREATURE_FLAG_EXTRA_NO_PARRY) - canParry = false; - // Check creatures flags_extra for disable block - if (flagEx & CREATURE_FLAG_EXTRA_NO_BLOCK) - canBlock = false; - } + // Ignore combat result aura AuraEffectList const& ignore = GetAuraEffectsByType(SPELL_AURA_IGNORE_COMBAT_RESULT); - for (AuraEffectList::const_iterator i = ignore.begin(); i != ignore.end(); ++i) + for (AuraEffect const* aurEff : ignore) { - if (!(*i)->IsAffectedOnSpell(spellInfo)) + if (!aurEff->IsAffectedOnSpell(spellInfo)) continue; - switch ((*i)->GetMiscValue()) + + switch (aurEff->GetMiscValue()) { case MELEE_HIT_DODGE: canDodge = false; @@ -2474,7 +2440,7 @@ SpellMissInfo Unit::MeleeSpellHitResult(Unit* victim, SpellInfo const* spellInfo canParry = false; break; default: - TC_LOG_DEBUG("entities.unit", "Spell %u SPELL_AURA_IGNORE_COMBAT_RESULT has unhandled state %d", (*i)->GetId(), (*i)->GetMiscValue()); + TC_LOG_DEBUG("entities.unit", "Spell %u SPELL_AURA_IGNORE_COMBAT_RESULT has unhandled state %d", aurEff->GetId(), aurEff->GetMiscValue()); break; } } @@ -2482,15 +2448,7 @@ SpellMissInfo Unit::MeleeSpellHitResult(Unit* victim, SpellInfo const* spellInfo if (canDodge) { // Roll dodge - int32 dodgeChance = int32(victim->GetUnitDodgeChance() * 100.0f) - skillDiff * 4; - // Reduce enemy dodge chance by SPELL_AURA_MOD_COMBAT_RESULT_CHANCE - dodgeChance += GetTotalAuraModifierByMiscValue(SPELL_AURA_MOD_COMBAT_RESULT_CHANCE, VICTIMSTATE_DODGE) * 100; - dodgeChance = int32(float(dodgeChance) * GetTotalAuraMultiplier(SPELL_AURA_MOD_ENEMY_DODGE)); - // Reduce dodge chance by attacker expertise rating - if (GetTypeId() == TYPEID_PLAYER) - dodgeChance -= int32(ToPlayer()->GetExpertiseDodgeOrParryReduction(attType) * 100.0f); - else - dodgeChance -= GetTotalAuraModifier(SPELL_AURA_MOD_EXPERTISE) * 25; + int32 dodgeChance = int32(GetUnitDodgeChance(attType, victim) * 100.0f); if (dodgeChance < 0) dodgeChance = 0; @@ -2501,12 +2459,7 @@ SpellMissInfo Unit::MeleeSpellHitResult(Unit* victim, SpellInfo const* spellInfo if (canParry) { // Roll parry - int32 parryChance = int32(victim->GetUnitParryChance() * 100.0f) - skillDiff * 4; - // Reduce parry chance by attacker expertise rating - if (GetTypeId() == TYPEID_PLAYER) - parryChance -= int32(ToPlayer()->GetExpertiseDodgeOrParryReduction(attType) * 100.0f); - else - parryChance -= GetTotalAuraModifier(SPELL_AURA_MOD_EXPERTISE) * 25; + int32 parryChance = int32(GetUnitParryChance(attType, victim) * 100.0f); if (parryChance < 0) parryChance = 0; @@ -2517,7 +2470,7 @@ SpellMissInfo Unit::MeleeSpellHitResult(Unit* victim, SpellInfo const* spellInfo if (canBlock) { - int32 blockChance = int32(victim->GetUnitBlockChance() * 100.0f) - skillDiff * 4; + int32 blockChance = int32(GetUnitBlockChance(attType, victim) * 100.0f); if (blockChance < 0) blockChance = 0; tmp += blockChance; @@ -2530,7 +2483,7 @@ SpellMissInfo Unit::MeleeSpellHitResult(Unit* victim, SpellInfo const* spellInfo } /// @todo need use unit spell resistances in calculations -SpellMissInfo Unit::MagicSpellHitResult(Unit* victim, SpellInfo const* spellInfo) +SpellMissInfo Unit::MagicSpellHitResult(Unit* victim, SpellInfo const* spellInfo) const { // Can`t miss on dead target (on skinning for example) if ((!victim->IsAlive() && victim->GetTypeId() != TYPEID_PLAYER) || spellInfo->HasAttribute(SPELL_ATTR3_IGNORE_HIT_RESULT)) @@ -2610,7 +2563,7 @@ SpellMissInfo Unit::MagicSpellHitResult(Unit* victim, SpellInfo const* spellInfo return SPELL_MISS_RESIST; // cast by caster in front of victim - if (victim->HasInArc(float(M_PI), this) || victim->HasAuraType(SPELL_AURA_IGNORE_HIT_DIRECTION)) + if (!victim->HasUnitState(UNIT_STATE_CONTROLLED) && (victim->HasInArc(float(M_PI), this) || victim->HasAuraType(SPELL_AURA_IGNORE_HIT_DIRECTION))) { int32 deflect_chance = victim->GetTotalAuraModifier(SPELL_AURA_DEFLECT_SPELLS) * 100; tmp += deflect_chance; @@ -2715,55 +2668,93 @@ uint32 Unit::GetDefenseSkillValue(Unit const* target) const return GetUnitMeleeSkill(target); } -float Unit::GetUnitDodgeChance() const +float Unit::GetUnitDodgeChance(WeaponAttackType attType, Unit const* victim) const { - if (IsNonMeleeSpellCast(false) || HasUnitState(UNIT_STATE_CONTROLLED)) - return 0.0f; + int32 const attackerWeaponSkill = GetWeaponSkillValue(attType, victim); + int32 const victimMaxSkillValueForLevel = victim->GetMaxSkillValueForLevel(this); + int32 const skillDiff = victimMaxSkillValueForLevel - attackerWeaponSkill; - if (GetTypeId() == TYPEID_PLAYER) - return GetFloatValue(PLAYER_DODGE_PERCENTAGE); + float chance = 0.0f; + float skillBonus = 0.0f; + if (victim->GetTypeId() == TYPEID_PLAYER) + { + chance = victim->GetFloatValue(PLAYER_DODGE_PERCENTAGE); + skillBonus = 0.04f * skillDiff; + } else { - if (IsTotem()) - return 0.0f; - else + if (!victim->IsTotem()) { - float dodge = 5.0f; - dodge += GetTotalAuraModifier(SPELL_AURA_MOD_DODGE_PERCENT); - return dodge > 0.0f ? dodge : 0.0f; + chance = 5.0f; + chance += victim->GetTotalAuraModifier(SPELL_AURA_MOD_DODGE_PERCENT); + + if (skillDiff <= 10) + skillBonus = skillDiff * 0.1f; + else + skillBonus = 1.0f + (skillDiff - 10) * 0.1f; } } + + chance += skillBonus; + + // Reduce enemy dodge chance by SPELL_AURA_MOD_COMBAT_RESULT_CHANCE + chance += GetTotalAuraModifierByMiscValue(SPELL_AURA_MOD_COMBAT_RESULT_CHANCE, VICTIMSTATE_DODGE); + + // reduce dodge by SPELL_AURA_MOD_ENEMY_DODGE + chance += GetTotalAuraModifier(SPELL_AURA_MOD_ENEMY_DODGE) * 100; + + // Reduce dodge chance by attacker expertise rating + if (GetTypeId() == TYPEID_PLAYER) + chance -= ToPlayer()->GetExpertiseDodgeOrParryReduction(attType); + else + chance -= GetTotalAuraModifier(SPELL_AURA_MOD_EXPERTISE) / 4.0f; + return std::max(chance, 0.0f); } -float Unit::GetUnitParryChance() const +float Unit::GetUnitParryChance(WeaponAttackType attType, Unit const* victim) const { - if (IsNonMeleeSpellCast(false) || HasUnitState(UNIT_STATE_CONTROLLED)) - return 0.0f; + int32 const attackerWeaponSkill = GetWeaponSkillValue(attType, victim); + int32 const victimMaxSkillValueForLevel = victim->GetMaxSkillValueForLevel(this); + int32 const skillDiff = victimMaxSkillValueForLevel - attackerWeaponSkill; float chance = 0.0f; - - if (Player const* player = ToPlayer()) + float skillBonus = 0.0f; + if (Player const* playerVictim = victim->ToPlayer()) { - if (player->CanParry()) + if (playerVictim->CanParry()) { - Item* tmpitem = player->GetWeaponForAttack(BASE_ATTACK, true); + Item* tmpitem = playerVictim->GetWeaponForAttack(BASE_ATTACK, true); if (!tmpitem) - tmpitem = player->GetWeaponForAttack(OFF_ATTACK, true); + tmpitem = playerVictim->GetWeaponForAttack(OFF_ATTACK, true); if (tmpitem) - chance = GetFloatValue(PLAYER_PARRY_PERCENTAGE); + chance = playerVictim->GetFloatValue(PLAYER_PARRY_PERCENTAGE); + + skillBonus = 0.04f * skillDiff; } } - else if (GetTypeId() == TYPEID_UNIT) + else if (victim->GetTypeId() == TYPEID_UNIT && !(victim->ToCreature()->GetCreatureTemplate()->flags_extra & CREATURE_FLAG_EXTRA_NO_PARRY)) { - if (GetCreatureType() == CREATURE_TYPE_HUMANOID) + if (victim->GetCreatureType() == CREATURE_TYPE_HUMANOID) { chance = 5.0f; - chance += GetTotalAuraModifier(SPELL_AURA_MOD_PARRY_PERCENT); + chance += victim->GetTotalAuraModifier(SPELL_AURA_MOD_PARRY_PERCENT); + + if (skillDiff <= 10) + skillBonus = skillDiff * 0.1f; + else + skillBonus = 1.0f + (skillDiff - 10) * 1.6f; } } - return chance > 0.0f ? chance : 0.0f; + chance += skillBonus; + + // Reduce parry chance by attacker expertise rating + if (GetTypeId() == TYPEID_PLAYER) + chance -= ToPlayer()->GetExpertiseDodgeOrParryReduction(attType); + else + chance -= GetTotalAuraModifier(SPELL_AURA_MOD_EXPERTISE) / 4.0f; + return std::max(chance, 0.0f); } float Unit::GetUnitMissChance(WeaponAttackType attType) const @@ -2771,7 +2762,7 @@ float Unit::GetUnitMissChance(WeaponAttackType attType) const float miss_chance = 5.00f; if (Player const* player = ToPlayer()) - miss_chance += player->GetMissPercentageFromDefence(); + miss_chance += player->GetMissPercentageFromDefense(); if (attType == RANGED_ATTACK) miss_chance -= GetTotalAuraModifier(SPELL_AURA_MOD_ATTACKER_RANGED_HIT_CHANCE); @@ -2781,36 +2772,45 @@ float Unit::GetUnitMissChance(WeaponAttackType attType) const return miss_chance; } -float Unit::GetUnitBlockChance() const +float Unit::GetUnitBlockChance(WeaponAttackType attType, Unit const* victim) const { - if (IsNonMeleeSpellCast(false) || HasUnitState(UNIT_STATE_CONTROLLED)) - return 0.0f; + int32 const attackerWeaponSkill = GetWeaponSkillValue(attType, victim); + int32 const victimMaxSkillValueForLevel = victim->GetMaxSkillValueForLevel(this); + int32 const skillDiff = victimMaxSkillValueForLevel - attackerWeaponSkill; - if (Player const* player = ToPlayer()) + float chance = 0.0f; + float skillBonus = 0.0f; + if (Player const* playerVictim = victim->ToPlayer()) { - if (player->CanBlock()) + if (playerVictim->CanBlock()) { - Item* tmpitem = player->GetUseableItemByPos(INVENTORY_SLOT_BAG_0, EQUIPMENT_SLOT_OFFHAND); + Item* tmpitem = playerVictim->GetUseableItemByPos(INVENTORY_SLOT_BAG_0, EQUIPMENT_SLOT_OFFHAND); if (tmpitem && !tmpitem->IsBroken() && tmpitem->GetTemplate()->Block) - return GetFloatValue(PLAYER_BLOCK_PERCENTAGE); + { + chance = playerVictim->GetFloatValue(PLAYER_BLOCK_PERCENTAGE); + skillBonus = 0.04f * skillDiff; + } } - // is player but has no block ability or no not broken shield equipped - return 0.0f; } else { - if (IsTotem()) - return 0.0f; - else + if (!victim->IsTotem() && !(victim->ToCreature()->GetCreatureTemplate()->flags_extra & CREATURE_FLAG_EXTRA_NO_BLOCK)) { - float block = 5.0f; - block += GetTotalAuraModifier(SPELL_AURA_MOD_BLOCK_PERCENT); - return block > 0.0f ? block : 0.0f; + chance = 5.0f; + chance += victim->GetTotalAuraModifier(SPELL_AURA_MOD_BLOCK_PERCENT); + + if (skillDiff <= 10) + skillBonus = skillDiff * 0.1f; + else + skillBonus = 1.0f + (skillDiff - 10) * 0.1f; } } + + chance += skillBonus; + return std::max(chance, 0.0f); } -float Unit::GetUnitCriticalChance(WeaponAttackType attackType, const Unit* victim) const +float Unit::GetUnitCriticalChance(WeaponAttackType attackType, Unit const* victim) const { float crit; @@ -2868,7 +2868,7 @@ float Unit::GetUnitCriticalChance(WeaponAttackType attackType, const Unit* victi else ApplyResilience(victim, &crit, NULL, false, CR_CRIT_TAKEN_RANGED); - // Apply crit chance from defence skill + // Apply crit chance from defense skill crit += (int32(GetMaxSkillValueForLevel(victim)) - int32(victim->GetDefenseSkillValue(this))) * 0.04f; if (crit < 0.0f) @@ -2876,7 +2876,7 @@ float Unit::GetUnitCriticalChance(WeaponAttackType attackType, const Unit* victi return crit; } -uint32 Unit::GetWeaponSkillValue (WeaponAttackType attType, Unit const* target) const +uint32 Unit::GetWeaponSkillValue(WeaponAttackType attType, Unit const* target) const { uint32 value = 0; if (Player const* player = ToPlayer()) @@ -2903,15 +2903,22 @@ uint32 Unit::GetWeaponSkillValue (WeaponAttackType attType, Unit const* target) value += uint32(player->GetRatingBonusValue(CR_WEAPON_SKILL)); switch (attType) { - case BASE_ATTACK: value += uint32(player->GetRatingBonusValue(CR_WEAPON_SKILL_MAINHAND)); break; - case OFF_ATTACK: value += uint32(player->GetRatingBonusValue(CR_WEAPON_SKILL_OFFHAND)); break; - case RANGED_ATTACK: value += uint32(player->GetRatingBonusValue(CR_WEAPON_SKILL_RANGED)); break; - default: break; + case BASE_ATTACK: + value += uint32(player->GetRatingBonusValue(CR_WEAPON_SKILL_MAINHAND)); + break; + case OFF_ATTACK: + value += uint32(player->GetRatingBonusValue(CR_WEAPON_SKILL_OFFHAND)); + break; + case RANGED_ATTACK: + value += uint32(player->GetRatingBonusValue(CR_WEAPON_SKILL_RANGED)); + break; + default: + break; } } else value = GetUnitMeleeSkill(target); - return value; + return value; } void Unit::_DeleteRemovedAuras() @@ -13764,7 +13771,7 @@ void Unit::ProcDamageAndSpellFor(bool isVictim, Unit* target, uint32 procFlag, u if (target->GetTypeId() != TYPEID_PLAYER && !target->IsCritter()) ToPlayer()->UpdateCombatSkills(target, attType, isVictim); } - // Update defence if player is victim and parry/dodge/block + // Update defense if player is victim and parry/dodge/block else if (isVictim && procExtra & (PROC_EX_DODGE|PROC_EX_PARRY|PROC_EX_BLOCK)) ToPlayer()->UpdateCombatSkills(target, attType, true); } diff --git a/src/server/game/Entities/Unit/Unit.h b/src/server/game/Entities/Unit/Unit.h index aa232577d3d..f036968f9bd 100644 --- a/src/server/game/Entities/Unit/Unit.h +++ b/src/server/game/Entities/Unit/Unit.h @@ -1470,28 +1470,28 @@ class TC_GAME_API Unit : public WorldObject void ApplyResilience(Unit const* victim, float* crit, int32* damage, bool isCrit, CombatRating type) const; float MeleeSpellMissChance(Unit const* victim, WeaponAttackType attType, int32 skillDiff, uint32 spellId) const; - SpellMissInfo MeleeSpellHitResult(Unit* victim, SpellInfo const* spellInfo); - SpellMissInfo MagicSpellHitResult(Unit* victim, SpellInfo const* spellInfo); + SpellMissInfo MeleeSpellHitResult(Unit* victim, SpellInfo const* spellInfo) const; + SpellMissInfo MagicSpellHitResult(Unit* victim, SpellInfo const* spellInfo) const; SpellMissInfo SpellHitResult(Unit* victim, SpellInfo const* spellInfo, bool canReflect = false); - float GetUnitDodgeChance() const; - float GetUnitParryChance() const; - float GetUnitBlockChance() const; + float GetUnitDodgeChance(WeaponAttackType attType, Unit const* victim) const; + 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, const Unit* victim) const; + float GetUnitCriticalChance(WeaponAttackType attackType, Unit const* victim) const; int32 GetMechanicResistChance(SpellInfo const* spellInfo) const; bool CanUseAttackType(uint8 attacktype) const; - virtual uint32 GetShieldBlockValue() const =0; + virtual uint32 GetShieldBlockValue() const = 0; uint32 GetShieldBlockValue(uint32 soft_cap, uint32 hard_cap) const; - uint32 GetUnitMeleeSkill(Unit const* target = NULL) const; - uint32 GetDefenseSkillValue(Unit const* target = NULL) const; - uint32 GetWeaponSkillValue(WeaponAttackType attType, Unit const* target = NULL) const; + uint32 GetUnitMeleeSkill(Unit const* target = nullptr) const; + uint32 GetDefenseSkillValue(Unit const* target = nullptr) const; + uint32 GetWeaponSkillValue(WeaponAttackType attType, Unit const* target = nullptr) const; float GetWeaponProcChance() const; float GetPPMProcChance(uint32 WeaponSpeed, float PPM, const SpellInfo* spellProto) const; - MeleeHitOutcome RollMeleeOutcomeAgainst (const Unit* victim, WeaponAttackType attType) const; - MeleeHitOutcome RollMeleeOutcomeAgainst (const Unit* victim, WeaponAttackType attType, int32 crit_chance, int32 miss_chance, int32 dodge_chance, int32 parry_chance, int32 block_chance) const; + MeleeHitOutcome RollMeleeOutcomeAgainst(Unit const* victim, WeaponAttackType attType) const; + MeleeHitOutcome RollMeleeOutcomeAgainst(Unit const* victim, WeaponAttackType attType, int32 crit_chance, int32 miss_chance, int32 dodge_chance, int32 parry_chance, int32 block_chance) const; bool IsVendor() const { return HasFlag(UNIT_NPC_FLAGS, UNIT_NPC_FLAG_VENDOR); } bool IsTrainer() const { return HasFlag(UNIT_NPC_FLAGS, UNIT_NPC_FLAG_TRAINER); } diff --git a/src/server/game/Spells/Auras/SpellAuraEffects.cpp b/src/server/game/Spells/Auras/SpellAuraEffects.cpp index 5acb97056e8..ed49ed197cc 100644 --- a/src/server/game/Spells/Auras/SpellAuraEffects.cpp +++ b/src/server/game/Spells/Auras/SpellAuraEffects.cpp @@ -308,7 +308,7 @@ pAuraEffectHandler AuraEffectHandler[TOTAL_AURAS]= &AuraEffect::HandleNoImmediateEffect, //248 SPELL_AURA_MOD_COMBAT_RESULT_CHANCE implemented in Unit::RollMeleeOutcomeAgainst &AuraEffect::HandleAuraConvertRune, //249 SPELL_AURA_CONVERT_RUNE &AuraEffect::HandleAuraModIncreaseHealth, //250 SPELL_AURA_MOD_INCREASE_HEALTH_2 - &AuraEffect::HandleNoImmediateEffect, //251 SPELL_AURA_MOD_ENEMY_DODGE + &AuraEffect::HandleNoImmediateEffect, //251 SPELL_AURA_MOD_ENEMY_DODGE implemented in Unit::GetUnitDodgeChance &AuraEffect::HandleModCombatSpeedPct, //252 SPELL_AURA_252 Is there any difference between this and SPELL_AURA_MELEE_SLOW ? maybe not stacking mod? &AuraEffect::HandleNoImmediateEffect, //253 SPELL_AURA_MOD_BLOCK_CRIT_CHANCE implemented in Unit::isBlockCritical &AuraEffect::HandleAuraModDisarm, //254 SPELL_AURA_MOD_DISARM_OFFHAND diff --git a/src/server/scripts/Spells/spell_dk.cpp b/src/server/scripts/Spells/spell_dk.cpp index 980c0db19cc..4dfd9ff09ff 100644 --- a/src/server/scripts/Spells/spell_dk.cpp +++ b/src/server/scripts/Spells/spell_dk.cpp @@ -1727,7 +1727,7 @@ class spell_dk_spell_deflection : public SpellScriptLoader void Absorb(AuraEffect* /*aurEff*/, DamageInfo & dmgInfo, uint32 & absorbAmount) { // You have a chance equal to your Parry chance - if ((dmgInfo.GetDamageType() == SPELL_DIRECT_DAMAGE) && roll_chance_f(GetTarget()->GetUnitParryChance())) + if ((dmgInfo.GetDamageType() == SPELL_DIRECT_DAMAGE) && roll_chance_f(GetTarget()->GetFloatValue(PLAYER_PARRY_PERCENTAGE))) absorbAmount = CalculatePct(dmgInfo.GetDamage(), absorbPct); } |