aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorariel- <ariel-@users.noreply.github.com>2016-10-04 01:09:27 -0300
committerariel- <ariel-@users.noreply.github.com>2016-10-04 01:26:34 -0300
commit0c24e4ee0e1f0bbb37f740aa6f0d45c384ee955d (patch)
tree659b3fe178d8473edbdb85445308113b649603d8
parentc25f7c48b5ba3982fc83b8c88a8cff619f72b162 (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
-rw-r--r--src/server/game/Entities/Player/Player.cpp10
-rw-r--r--src/server/game/Entities/Player/Player.h4
-rw-r--r--src/server/game/Entities/Unit/StatSystem.cpp2
-rw-r--r--src/server/game/Entities/Unit/Unit.cpp411
-rw-r--r--src/server/game/Entities/Unit/Unit.h24
-rw-r--r--src/server/game/Spells/Auras/SpellAuraEffects.cpp2
-rw-r--r--src/server/scripts/Spells/spell_dk.cpp2
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);
}