Core/Units: backported hit, dodge, parry and crit chance updates from 335 and fixed logics in 4.x specific code

This commit is contained in:
Ovahlord committed 2020-08-23 17:14:34 +02:00
1 parent ec1b73e6eb
commit a657a21c4a
4 files changed
+245 -305

No files matched your search

+5 -14
View File
@@ -840,8 +840,7 @@ void Creature::Update(uint32 diff)
if (!IsAlive())
break;
if (HasFlag(UNIT_FIELD_FLAGS_2, UNIT_FLAG2_REGENERATE_POWER))
Regenerate(GetPowerType(), diff);
Regenerate(GetPowerType(), diff);
// Update serverside orientation of channeled spells that are suposed to track the channel target
if (Spell const* spell = m_currentSpells[CURRENT_CHANNELED_SPELL])
@@ -881,24 +880,16 @@ void Creature::RegenerateHealth()
uint32 addvalue = 0;
// Not only pet, but any controlled creature
if (GetCharmerOrOwnerGUID())
if (!GetCharmerOrOwnerGUID().IsEmpty())
{
float HealthIncreaseRate = sWorld->getRate(RATE_HEALTH);
float Spirit = GetStat(STAT_SPIRIT);
if (GetPower(POWER_MANA) > 0)
addvalue = uint32(Spirit * 0.25 * HealthIncreaseRate);
else
addvalue = uint32(Spirit * 0.80 * HealthIncreaseRate);
float healthIncreaseRate = sWorld->getRate(RATE_HEALTH);
addvalue = 0.015f * ((float)GetMaxHealth()) * healthIncreaseRate;
}
else
addvalue = maxValue / 3;
// Apply modifiers (if any).
AuraEffectList const& ModPowerRegenPCTAuras = GetAuraEffectsByType(SPELL_AURA_MOD_HEALTH_REGEN_PERCENT);
for (AuraEffectList::const_iterator i = ModPowerRegenPCTAuras.begin(); i != ModPowerRegenPCTAuras.end(); ++i)
AddPct(addvalue, (*i)->GetAmount());
addvalue *= GetTotalAuraMultiplier(SPELL_AURA_MOD_HEALTH_REGEN_PERCENT);
addvalue += GetTotalAuraModifier(SPELL_AURA_MOD_REGEN) * UNIT_HEALTH_REGENERATION_INTERVAL / (5 * IN_MILLISECONDS);
ModifyHealth(addvalue);
+1 -1
View File
@@ -922,7 +922,7 @@ void Creature::UpdatePowerRegeneration(Powers powerType)
{
case POWER_MANA:
// Regeneration for controlled units
if (GetCharmerGUID() || GetOwnerGUID())
if (!GetCharmerGUID().IsEmpty() || !GetOwnerGUID().IsEmpty())
{
float spiritRegen = (GetStat(STAT_SPIRIT) / 5.f + 17.f);
SetFloatValue(UNIT_FIELD_POWER_REGEN_FLAT_MODIFIER, spiritRegen + powerRegenMod);
+232 -284
View File
@@ -2222,178 +2222,124 @@ void Unit::HandleProcExtraAttackFor(Unit* victim)
MeleeHitOutcome Unit::RollMeleeOutcomeAgainst(const Unit* victim, WeaponAttackType attType) const
{
// This is only wrapper
// Miss chance based on melee
//float miss_chance = MeleeMissChanceCalc(victim, attType);
float miss_chance = MeleeSpellMissChance(victim, attType, 0);
// 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(this);
float block_chance = victim->GetUnitBlockChance();
float parry_chance = victim->GetUnitParryChance(this);
// 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);
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
{
if (victim->GetTypeId() == TYPEID_UNIT && victim->ToCreature()->IsEvadingAttacks())
if (victim->IsCreature() && victim->ToCreature()->IsInEvadeMode())
return MELEE_HIT_EVADE;
int32 attackerMaxSkillValueForLevel = GetMaxSkillValueForLevel(victim);
int32 victimMaxSkillValueForLevel = victim->GetMaxSkillValueForLevel(this);
int32 const attackerMaxSkillValueForLevel = GetMaxSkillValueForLevel(victim);
int32 const victimMaxSkillValueForLevel = victim->GetMaxSkillValueForLevel(this);
// Miss chance based on melee
int32 miss_chance = int32(MeleeSpellMissChance(victim, attType, 0) * 100.f);
// Critical hit chance
int32 crit_chance = int32(GetUnitCriticalChanceAgainst(attType, victim) * 100.0f);
// stunned target cannot dodge and this is check in GetUnitDodgeChance() (returned 0 in this case)
int32 dodge_chance = int32(GetUnitDodgeChance(attType, victim) * 100.0f);
int32 block_chance = int32(GetUnitBlockChance(attType, victim) * 100.0f);
int32 parry_chance = int32(GetUnitParryChance(attType, victim) * 100.0f);
// melee attack table implementation
// outcome priority:
// 1. > 2. > 3. > 4. > 5. > 6. > 7. > 8.
// MISS > DODGE > PARRY > GLANCING > BLOCK > CRIT > CRUSHING > HIT
// bonus from skills is 0.04%
int32 skillBonus = 4 * (attackerMaxSkillValueForLevel - victimMaxSkillValueForLevel);
int32 sum = 0, tmp = 0;
int32 roll = urand (0, 10000);
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);
tmp = miss_chance;
if (tmp > 0 && roll < (sum += tmp))
{
TC_LOG_DEBUG("entities.unit", "RollMeleeOutcomeAgainst: MISS");
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
{
// 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)
&& roll < (sum += tmp))
{
TC_LOG_DEBUG("entities.unit", "RollMeleeOutcomeAgainst: DODGE <%d, %d)", sum-tmp, sum);
return MELEE_HIT_DODGE;
}
}
// parry & block chances
int32 roll = urand(0, 9999);
// 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
bool canParryOrBlock = victim->HasInArc(float(M_PI), this) || victim->HasAuraType(SPELL_AURA_IGNORE_HIT_DIRECTION);
// only creatures can dodge if attacker is behind
bool canDodge = victim->GetTypeId() != TYPEID_PLAYER || canParryOrBlock;
// if victim is casting or cc'd it can't avoid attacks
if (victim->IsNonMeleeSpellCast(false) || victim->HasUnitState(UNIT_STATE_CONTROLLED))
{
// 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;
}
}
canDodge = false;
canParryOrBlock = false;
}
// Critical chance
tmp = crit_chance;
// 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->IsPlayer() && crit_chance > 0 && !victim->IsStandState())
return MELEE_HIT_CRIT;
// 2. DODGE
if (canDodge)
{
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 = dodge_chance;
if (tmp > 0 // check if unit _can_ dodge
&& roll < (sum += tmp))
return MELEE_HIT_DODGE;
}
// 3. PARRY
if (canParryOrBlock)
{
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))
{
// cap possible value (with bonuses > max skill)
int32 skill = attackerMaxSkillValueForLevel;
tmp = (10 + (victimMaxSkillValueForLevel - 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 + (victimMaxSkillValueForLevel - 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))
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
!IsControlledByPlayer() &&
!(GetTypeId() == TYPEID_UNIT && ToCreature()->GetCreatureTemplate()->flags_extra & CREATURE_FLAG_EXTRA_NO_CRUSH))
!(IsCreature() && ToCreature()->GetCreatureTemplate()->flags_extra & CREATURE_FLAG_EXTRA_NO_CRUSH))
{
// when their weapon skill is 15 or more above victim's defense skill
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, min. is 15%
tmp = tmp * 200 - 1500;
if (roll < (sum += tmp))
return MELEE_HIT_CRUSHING;
}
TC_LOG_DEBUG("entities.unit", "RollMeleeOutcomeAgainst: NORMAL");
// 8. HIT
return MELEE_HIT_NORMAL;
}
@@ -2465,22 +2411,23 @@ void Unit::SendMeleeAttackStop(Unit* victim)
TC_LOG_DEBUG("entities.unit", "%s %u stopped attacking", (GetTypeId() == TYPEID_PLAYER ? "Player" : "Creature"), GetGUID().GetCounter());
}
bool Unit::isSpellBlocked(Unit* victim, SpellInfo const* spellProto, WeaponAttackType /*attackType*/)
bool Unit::isSpellBlocked(Unit* victim, SpellInfo const* spellProto, WeaponAttackType attackType)
{
// These spells can't be blocked
if (spellProto && (spellProto->HasAttribute(SPELL_ATTR0_IMPOSSIBLE_DODGE_PARRY_BLOCK) || spellProto->HasAttribute(SPELL_ATTR3_IGNORE_HIT_RESULT)))
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
if (victim->GetTypeId() == TYPEID_UNIT &&
victim->ToCreature()->GetCreatureTemplate()->flags_extra & CREATURE_FLAG_EXTRA_NO_BLOCK)
return false;
if (roll_chance_f(victim->GetUnitBlockChance()))
float blockChance = GetUnitBlockChance(attackType, victim);
if (blockChance && roll_chance_f(blockChance))
return true;
}
return false;
}
@@ -2561,6 +2508,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)
{
@@ -2568,7 +2523,7 @@ SpellMissInfo Unit::MeleeSpellHitResult(Unit* victim, SpellInfo const* spellInfo
canDodge = false;
// only if in front
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;
@@ -2596,23 +2551,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)->IsAffectingSpell(spellInfo))
if (!aurEff->IsAffectingSpell(spellInfo))
continue;
switch ((*i)->GetMiscValue())
switch (aurEff->GetMiscValue())
{
case MELEE_HIT_DODGE:
canDodge = false;
@@ -2624,7 +2571,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;
}
}
@@ -2632,15 +2579,7 @@ SpellMissInfo Unit::MeleeSpellHitResult(Unit* victim, SpellInfo const* spellInfo
if (canDodge)
{
// Roll dodge
int32 dodgeChance = int32(victim->GetUnitDodgeChance(this) * 100.0f);
// 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;
@@ -2651,12 +2590,7 @@ SpellMissInfo Unit::MeleeSpellHitResult(Unit* victim, SpellInfo const* spellInfo
if (canParry)
{
// Roll parry
int32 parryChance = int32(victim->GetUnitParryChance(this) * 100.0f);
// 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;
@@ -2667,7 +2601,7 @@ SpellMissInfo Unit::MeleeSpellHitResult(Unit* victim, SpellInfo const* spellInfo
if (canBlock)
{
int32 blockChance = int32(victim->GetUnitBlockChance() * 100.0f);
int32 blockChance = int32(GetUnitBlockChance(attType, victim) * 100.0f);
if (blockChance < 0)
blockChance = 0;
tmp += blockChance;
@@ -2683,12 +2617,12 @@ SpellMissInfo Unit::MeleeSpellHitResult(Unit* victim, SpellInfo const* spellInfo
SpellMissInfo Unit::MagicSpellHitResult(Unit* victim, SpellInfo const* spellInfo)
{
// Can`t miss on dead target (on skinning for example)
if (!victim->IsAlive() && victim->GetTypeId() != TYPEID_PLAYER)
if (!victim->IsAlive() && !victim->IsPlayer())
return SPELL_MISS_NONE;
SpellSchoolMask schoolMask = spellInfo->GetSchoolMask();
int32 thisLevel = getLevel();
if (GetTypeId() == TYPEID_UNIT && ToCreature()->IsTrigger())
if (IsCreature() && ToCreature()->IsTrigger())
thisLevel = std::max<int32>(thisLevel, spellInfo->SpellLevel);
// Base hit chance from attacker and victim levels
@@ -2808,168 +2742,183 @@ uint32 Unit::GetUnitMeleeSkill(Unit const* target) const
return (target ? getLevelForTarget(target) : getLevel()) * 5;
}
float Unit::GetUnitDodgeChance(Unit const* attacker) const
float Unit::GetUnitDodgeChance(WeaponAttackType attType, Unit const* victim) const
{
if (IsNonMeleeSpellCast(false) || HasUnitState(UNIT_STATE_CONTROLLED))
return 0.0f;
if (GetTypeId() == TYPEID_PLAYER)
return GetFloatValue(PLAYER_DODGE_PERCENTAGE);
else
{
if (IsTotem())
return 0.0f;
else
{
float dodge = 0.0f;
int8 levelDifference = getLevel() - attacker->getLevel();
if (levelDifference >= 0 && levelDifference <= 3)
dodge += EnemyDodgeChance[levelDifference];
else if (levelDifference > 3)
dodge += EnemyDodgeChance[3];
dodge += GetTotalAuraModifier(SPELL_AURA_MOD_DODGE_PERCENT);
return dodge;
}
}
}
float Unit::GetUnitParryChance(Unit const* attacker) const
{
if (IsNonMeleeSpellCast(false) || HasUnitState(UNIT_STATE_CONTROLLED))
return 0.0f;
float chance = 0.0f;
if (Player const* player = ToPlayer())
if (victim->IsPlayer())
chance += victim->GetFloatValue(PLAYER_DODGE_PERCENTAGE);
else
{
if (player->CanParry())
if (!victim->IsTotem())
{
Item* tmpitem = player->GetWeaponForAttack(BASE_ATTACK, true);
int8 levelDifference = victim->getLevel() - getLevel();
if (levelDifference < 0)
levelDifference = 0;
else if (levelDifference > 3)
levelDifference = 3;
chance += EnemyDodgeChance[levelDifference];
chance += victim->GetTotalAuraModifier(SPELL_AURA_MOD_DODGE_PERCENT);
}
}
// 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);
if (IsPlayer())
chance -= ToPlayer()->GetExpertiseDodgeOrParryReduction(attType);
else
chance -= GetTotalAuraModifier(SPELL_AURA_MOD_EXPERTISE) / 4.0f;
return std::max(chance, 0.0f);
}
float Unit::GetUnitParryChance(WeaponAttackType attType, Unit const* victim) const
{
float chance = 0.0f;
if (Player const* playerVictim = victim->ToPlayer())
{
if (playerVictim->CanParry())
{
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);
}
}
else if (GetTypeId() == TYPEID_UNIT)
else
{
if (GetCreatureType() == CREATURE_TYPE_HUMANOID)
// Allow parries for creatures only if it's not a totem, does not have a virtual item equipped and does not have CREATURE_FLAG_EXTRA_NO_PARRY
if (!victim->IsTotem() && (GetUInt32Value(UNIT_VIRTUAL_ITEM_SLOT_ID + 0) || GetUInt32Value(UNIT_VIRTUAL_ITEM_SLOT_ID + 1)) &&
!(victim->ToCreature()->GetCreatureTemplate()->flags_extra & CREATURE_FLAG_EXTRA_NO_PARRY))
{
float chance = 0.0f;
int8 levelDifference = getLevel() - attacker->getLevel();
if (levelDifference >= 0 && levelDifference <= 3)
chance += EnemyParryChance[levelDifference];
int8 levelDifference = victim->getLevel() - getLevel();
if (levelDifference < 0)
levelDifference = 0;
else if (levelDifference > 3)
chance += EnemyParryChance[3];
levelDifference = 3;
chance += GetTotalAuraModifier(SPELL_AURA_MOD_PARRY_PERCENT);
chance += EnemyParryChance[levelDifference];
chance += victim->GetTotalAuraModifier(SPELL_AURA_MOD_PARRY_PERCENT);
}
}
return chance > 0.0f ? chance : 0.0f;
if (IsPlayer())
chance -= ToPlayer()->GetExpertiseDodgeOrParryReduction(attType);
else
chance -= GetTotalAuraModifier(SPELL_AURA_MOD_EXPERTISE) / 4.0f;
return std::max(chance, 0.0f);
}
float Unit::GetUnitMissChance(WeaponAttackType attType, Unit const* attacker) const
float Unit::GetUnitMissChance(Unit const* victim) const
{
float miss_chance = 0.0f;
int8 levelDifference = getLevel() - attacker->getLevel();
if (levelDifference >= 0 && levelDifference <= 3)
miss_chance += MissChancePhysical[levelDifference];
float chance = 0.0f;
int8 levelDifference = victim->getLevel() - getLevel();
if (levelDifference < 0)
chance += MissChancePhysical[0];
else if (levelDifference > 3)
miss_chance += MissChancePhysical[3] + 2.0f * (levelDifference - 3);
// Normalize chance
miss_chance = std::min(100.0f, miss_chance);
if (attType == RANGED_ATTACK)
miss_chance -= GetTotalAuraModifier(SPELL_AURA_MOD_ATTACKER_RANGED_HIT_CHANCE);
chance += MissChancePhysical[3] + 2.0f * (levelDifference - 3);
else
miss_chance -= GetTotalAuraModifier(SPELL_AURA_MOD_ATTACKER_MELEE_HIT_CHANCE);
chance += MissChancePhysical[levelDifference];
return miss_chance;
return chance;
}
float Unit::GetUnitBlockChance() const
float Unit::GetUnitBlockChance(WeaponAttackType attType, Unit const* victim) const
{
if (IsNonMeleeSpellCast(false) || HasUnitState(UNIT_STATE_CONTROLLED))
return 0.0f;
float chance = 0.f;
if (Player const* player = ToPlayer())
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())
return GetFloatValue(PLAYER_BLOCK_PERCENTAGE);
chance = playerVictim->GetFloatValue(PLAYER_BLOCK_PERCENTAGE);
}
// 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);
}
}
return std::max(chance, 0.0f);
}
float Unit::GetUnitCriticalChance(WeaponAttackType attackType, const Unit* victim) const
float Unit::GetUnitCriticalChanceDone(WeaponAttackType attackType) const
{
float crit;
float chance = 0.f;
if (GetTypeId() == TYPEID_PLAYER)
{
switch (attackType)
{
case BASE_ATTACK:
crit = GetFloatValue(PLAYER_CRIT_PERCENTAGE);
chance = GetFloatValue(PLAYER_CRIT_PERCENTAGE);
break;
case OFF_ATTACK:
crit = GetFloatValue(PLAYER_OFFHAND_CRIT_PERCENTAGE);
chance = GetFloatValue(PLAYER_OFFHAND_CRIT_PERCENTAGE);
break;
case RANGED_ATTACK:
crit = GetFloatValue(PLAYER_RANGED_CRIT_PERCENTAGE);
chance = GetFloatValue(PLAYER_RANGED_CRIT_PERCENTAGE);
break;
// Just for good manner
default:
crit = 0.0f;
chance = 0.0f;
break;
}
}
else
{
crit = 5.0f;
crit += GetTotalAuraModifier(SPELL_AURA_MOD_WEAPON_CRIT_PERCENT);
crit += GetTotalAuraModifier(SPELL_AURA_MOD_CRIT_PCT);
if (!(ToCreature()->GetCreatureTemplate()->flags_extra & CREATURE_FLAG_EXTRA_NO_CRIT))
{
chance = 5.0f;
chance += GetTotalAuraModifier(SPELL_AURA_MOD_WEAPON_CRIT_PERCENT);
chance += GetTotalAuraModifier(SPELL_AURA_MOD_CRIT_PCT);
}
}
return chance;
}
float Unit::GetUnitCriticalChanceTaken(Unit const* attacker, WeaponAttackType attackType, float critDone) const
{
float chance = critDone;
// flat aura mods
if (attackType == RANGED_ATTACK)
crit += victim->GetTotalAuraModifier(SPELL_AURA_MOD_ATTACKER_RANGED_CRIT_CHANCE);
chance += GetTotalAuraModifier(SPELL_AURA_MOD_ATTACKER_RANGED_CRIT_CHANCE);
else
crit += victim->GetTotalAuraModifier(SPELL_AURA_MOD_ATTACKER_MELEE_CRIT_CHANCE);
chance += GetTotalAuraModifier(SPELL_AURA_MOD_ATTACKER_MELEE_CRIT_CHANCE);
AuraEffectList const& critChanceForCaster = victim->GetAuraEffectsByType(SPELL_AURA_MOD_CRIT_CHANCE_FOR_CASTER);
for (AuraEffect const* aurEff : critChanceForCaster)
chance += GetTotalAuraModifier(SPELL_AURA_MOD_CRIT_CHANCE_FOR_CASTER, [attacker](AuraEffect const* aurEff) -> bool
{
if (aurEff->GetCasterGUID() != GetGUID())
continue;
if (aurEff->GetCasterGUID() == attacker->GetGUID())
return true;
return false;
});
crit += aurEff->GetAmount();
}
// applied after resilience
chance += GetTotalAuraModifier(SPELL_AURA_MOD_ATTACKER_SPELL_AND_WEAPON_CRIT_CHANCE);
crit += victim->GetTotalAuraModifier(SPELL_AURA_MOD_ATTACKER_SPELL_AND_WEAPON_CRIT_CHANCE);
return std::max(chance, 0.f);
}
if (crit < 0.0f)
crit = 0.0f;
return crit;
float Unit::GetUnitCriticalChanceAgainst(WeaponAttackType attackType, Unit const* victim) const
{
float chance = GetUnitCriticalChanceDone(attackType);
return victim->GetUnitCriticalChanceTaken(this, attackType, chance);
}
void Unit::_DeleteRemovedAuras()
@@ -7287,7 +7236,7 @@ float Unit::GetUnitSpellCriticalChance(Unit* victim, SpellInfo const* spellProto
{
if (victim)
{
crit_chance += GetUnitCriticalChance(attackType, victim);
crit_chance += GetUnitCriticalChanceDone(attackType);
crit_chance += GetTotalAuraModifierByMiscMask(SPELL_AURA_MOD_SPELL_CRIT_CHANCE_SCHOOL, schoolMask);
switch (spellProto->SpellFamilyName)
@@ -10068,6 +10017,9 @@ void Unit::Regenerate(Powers powerType, uint32 diff)
if (!maxValue)
return;
if (!HasFlag(UNIT_FIELD_FLAGS_2, UNIT_FLAG2_REGENERATE_POWER))
return;
uint32 curValue = GetPower(powerType);
// Block power regeneration
@@ -10091,7 +10043,7 @@ void Unit::Regenerate(Powers powerType, uint32 diff)
addValue *= sWorld->getRate(RATE_POWER_RAGE_LOSS);
break;
case POWER_RUNIC_POWER:
addValue*= sWorld->getRate(RATE_POWER_RUNICPOWER_LOSS);
addValue *= sWorld->getRate(RATE_POWER_RUNICPOWER_LOSS);
break;
case POWER_FOCUS:
addValue *= sWorld->getRate(RATE_POWER_FOCUS);
@@ -12566,23 +12518,20 @@ void Unit::ApplyResilience(Unit const* victim, int32* damage) const
// Crit or block - determined on damage calculation phase! (and can be both in some time)
float Unit::MeleeSpellMissChance(Unit const* victim, WeaponAttackType attType, uint32 spellId) const
{
//calculate miss chance
float missChance = victim->GetUnitMissChance(attType, this);
// Calculate miss chance
float missChance = GetUnitMissChance(victim);
if (!spellId && haveOffhandWeapon())
missChance += 19;
// Calculate hit chance
float hitChance = 100.0f;
// Spellmod from SPELLMOD_RESIST_MISS_CHANCE
float resistMissChance = 100.0f;
if (spellId)
{
if (Player* modOwner = GetSpellModOwner())
modOwner->ApplySpellMod(spellId, SPELLMOD_RESIST_MISS_CHANCE, hitChance);
modOwner->ApplySpellMod(spellId, SPELLMOD_RESIST_MISS_CHANCE, resistMissChance);
}
missChance += hitChance - 100.0f;
missChance -= resistMissChance - 100.0f;
if (attType == RANGED_ATTACK)
missChance -= m_modRangedHitChance;
@@ -12591,7 +12540,6 @@ float Unit::MeleeSpellMissChance(Unit const* victim, WeaponAttackType attType, u
// miss chance from auras after calculating skill based miss
missChance -= GetTotalAuraModifier(SPELL_AURA_MOD_HIT_CHANCE);
if (attType == RANGED_ATTACK)
missChance -= victim->GetTotalAuraModifier(SPELL_AURA_MOD_ATTACKER_RANGED_HIT_CHANCE);
else
+7 -6
View File
@@ -1192,11 +1192,13 @@ class TC_GAME_API Unit : public WorldObject
SpellMissInfo MagicSpellHitResult(Unit* victim, SpellInfo const* spellInfo);
SpellMissInfo SpellHitResult(Unit* victim, SpellInfo const* spellInfo, bool canReflect = false, Optional<uint8> effectMask = nullptr);
float GetUnitDodgeChance(Unit const* attacker) const;
float GetUnitParryChance(Unit const* attacker) const;
float GetUnitBlockChance() const;
float GetUnitMissChance(WeaponAttackType attType, Unit const* victim) const;
float GetUnitCriticalChance(WeaponAttackType attackType, const Unit* victim) 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(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;
@@ -1208,7 +1210,6 @@ class TC_GAME_API Unit : public WorldObject
float GetPPMProcChance(uint32 WeaponSpeed, float PPM, SpellInfo const* 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;
bool IsVendor() const { return HasFlag(UNIT_NPC_FLAGS, UNIT_NPC_FLAG_VENDOR); }
bool IsTrainer() const { return HasFlag(UNIT_NPC_FLAGS, UNIT_NPC_FLAG_TRAINER); }