Core/Unit: cleanup and minor fixes of miss and resist calculations

- Removed Unit::GetUnitMeleeSkill as it was basically a copy of GetMaxSkillValueForLevel
- Removed static from GetEffectiveResistChance, as this was passed anyways, changed name to CalculateAverageResistReduction, which better reflects what it does
- Fix melee miss chances calculated from attacker maxskill instead of victim maxskill
- Do actual checks if spell can be resisted/missed in MagicSpellHitResult (ie chances > 0)
- Fixed SPELLMOD_RESIST_MISS_CHANCE calculation in MeleeSpellMissChance
- Minor codestyle and cleanup of diminishing returns calcs

(cherry picked from commit e565b34f6d24411e210151d0d03e524f94cdff85)
This commit is contained in:
ariel- authored and funjoker committed 2022-02-16 04:49:24 +01:00
1 parent 0e8422c30a
commit d441e864c4
3 files changed
+105 -91

No files matched your search

+56 -35
View File
@@ -575,7 +575,7 @@ void Player::UpdateMastery()
}
}
const float m_diminishing_k[MAX_CLASSES] =
float const m_diminishing_k[MAX_CLASSES] =
{
0.9560f, // Warrior
0.9560f, // Paladin
@@ -590,26 +590,48 @@ const float m_diminishing_k[MAX_CLASSES] =
0.9720f // Druid
};
void Player::UpdateParryPercentage()
float CalculateDiminishingReturns(float const (&capArray)[MAX_CLASSES], uint8 playerClass, float nonDiminishValue, float diminishValue)
{
const float parry_cap[MAX_CLASSES] =
{
65.631440f, // Warrior
65.631440f, // Paladin
145.560408f, // Hunter
145.560408f, // Rogue
0.0f, // Priest
65.631440f, // DK
145.560408f, // Shaman
0.0f, // Mage
0.0f, // Warlock
0.0f, // ??
0.0f // Druid
};
// 1 1 k cx
// --- = --- + --- <=> x' = --------
// x' c x x + ck
// where:
// k is m_diminishing_k for that class
// c is capArray for that class
// x is chance before DR (diminishValue)
// x' is chance after DR (our result)
uint32 const classIdx = playerClass - 1;
float const k = m_diminishing_k[classIdx];
float const c = capArray[classIdx];
float result = c * diminishValue / (diminishValue + c * k);
result += nonDiminishValue;
return result;
}
float const parry_cap[MAX_CLASSES] =
{
65.631440f, // Warrior
65.631440f, // Paladin
145.560408f, // Hunter
145.560408f, // Rogue
0.0f, // Priest
65.631440f, // DK
145.560408f, // Shaman
0.0f, // Mage
0.0f, // Warlock
0.0f, // ??
0.0f // Druid
};
void Player::UpdateParryPercentage()
{
// No parry
float value = 0.0f;
uint32 pclass = getClass()-1;
uint32 pclass = getClass() - 1;
if (CanParry() && parry_cap[pclass] > 0.0f)
{
float nondiminishing = 5.0f;
@@ -618,7 +640,7 @@ void Player::UpdateParryPercentage()
// Parry from SPELL_AURA_MOD_PARRY_PERCENT aura
nondiminishing += GetTotalAuraModifier(SPELL_AURA_MOD_PARRY_PERCENT);
// apply diminishing formula to diminishing parry chance
value = nondiminishing + diminishing * parry_cap[pclass] / (diminishing + parry_cap[pclass] * m_diminishing_k[pclass]);
value = CalculateDiminishingReturns(parry_cap, getClass(), nondiminishing, diminishing);
if (sWorld->getBoolConfig(CONFIG_STATS_LIMITS_ENABLE))
value = value > sWorld->getFloatConfig(CONFIG_STATS_LIMITS_PARRY) ? sWorld->getFloatConfig(CONFIG_STATS_LIMITS_PARRY) : value;
@@ -628,23 +650,23 @@ void Player::UpdateParryPercentage()
SetStatFloatValue(PLAYER_PARRY_PERCENTAGE, value);
}
float const dodge_cap[MAX_CLASSES] =
{
65.631440f, // Warrior
65.631440f, // Paladin
145.560408f, // Hunter
145.560408f, // Rogue
150.375940f, // Priest
65.631440f, // DK
145.560408f, // Shaman
150.375940f, // Mage
150.375940f, // Warlock
0.0f, // ??
116.890707f // Druid
};
void Player::UpdateDodgePercentage()
{
const float dodge_cap[MAX_CLASSES] =
{
65.631440f, // Warrior
65.631440f, // Paladin
145.560408f, // Hunter
145.560408f, // Rogue
150.375940f, // Priest
65.631440f, // DK
145.560408f, // Shaman
150.375940f, // Mage
150.375940f, // Warlock
0.0f, // ??
116.890707f // Druid
};
float diminishing = 0.0f, nondiminishing = 0.0f;
GetDodgeFromAgility(diminishing, nondiminishing);
// Dodge from SPELL_AURA_MOD_DODGE_PERCENT aura
@@ -652,8 +674,7 @@ void Player::UpdateDodgePercentage()
// Dodge from rating
diminishing += GetRatingBonusValue(CR_DODGE);
// apply diminishing formula to diminishing dodge chance
uint32 pclass = getClass()-1;
float value = nondiminishing + (diminishing * dodge_cap[pclass] / (diminishing + dodge_cap[pclass] * m_diminishing_k[pclass]));
float value = CalculateDiminishingReturns(dodge_cap, getClass(), nondiminishing, diminishing);
if (sWorld->getBoolConfig(CONFIG_STATS_LIMITS_ENABLE))
value = value > sWorld->getFloatConfig(CONFIG_STATS_LIMITS_DODGE) ? sWorld->getFloatConfig(CONFIG_STATS_LIMITS_DODGE) : value;
+47 -54
View File
@@ -1468,27 +1468,22 @@ void Unit::DealMeleeDamage(CalcDamageInfo* damageInfo, bool durabilityLoss)
GetTypeId() != TYPEID_PLAYER && !ToCreature()->IsControlledByPlayer() && !victim->HasInArc(float(M_PI), this)
&& (victim->GetTypeId() == TYPEID_PLAYER || !victim->ToCreature()->isWorldBoss())&& !victim->IsVehicle())
{
// -probability is between 0% and 40%
// 20% base chance
float Probability = 20.0f;
float chance = 20.0f;
// there is a newbie protection, at level 10 just 7% base chance; assuming linear function
if (victim->getLevel() < 30)
Probability = 0.65f * victim->getLevel() + 0.5f;
chance = 0.65f * victim->getLevel() + 0.5f;
uint32 VictimDefense = victim->GetMaxSkillValueForLevel(this);
uint32 AttackerMeleeSkill = GetMaxSkillValueForLevel();
uint32 const victimDefense = victim->GetMaxSkillValueForLevel(this);
uint32 const attackerMeleeSkill = GetMaxSkillValueForLevel();
Probability *= AttackerMeleeSkill/(float)VictimDefense*0.16f;
chance *= attackerMeleeSkill / float(victimDefense) * 0.16f;
if (Probability < 0)
Probability = 0;
if (Probability > 40.0f)
Probability = 40.0f;
if (roll_chance_f(Probability))
CastSpell(victim, 1604, true);
// -probability is between 0% and 40%
RoundToInterval(chance, 0.0f, 40.0f);
if (roll_chance_f(chance))
CastSpell(victim, 1604 /*SPELL_DAZED*/, true);
}
if (GetTypeId() == TYPEID_PLAYER)
@@ -1659,29 +1654,28 @@ uint32 Unit::CalcSpellResistedDamage(Unit* victim, uint32 damage, SpellSchoolMas
return 0;
}
float averageResist = Unit::GetEffectiveResistChance(this, schoolMask, victim, spellInfo);
float discreteResistProbability[11];
for (uint32 i = 0; i < 11; ++i)
{
discreteResistProbability[i] = 0.5f - 2.5f * std::fabs(0.1f * i - averageResist);
if (discreteResistProbability[i] < 0.0f)
discreteResistProbability[i] = 0.0f;
}
float const averageResist = CalculateAverageResistReduction(schoolMask, victim, spellInfo);
float discreteResistProbability[11] = { };
if (averageResist <= 0.1f)
{
discreteResistProbability[0] = 1.0f - 7.5f * averageResist;
discreteResistProbability[1] = 5.0f * averageResist;
discreteResistProbability[2] = 2.5f * averageResist;
}
else
{
for (uint32 i = 0; i < 11; ++i)
discreteResistProbability[i] = std::max(0.5f - 2.5f * std::fabs(0.1f * i - averageResist), 0.0f);
}
float roll = float(rand_norm());
float probabilitySum = 0.0f;
uint32 resistance = 0;
float r = float(rand_norm());
float probabilitySum = discreteResistProbability[0];
while (r >= probabilitySum && resistance < 10)
probabilitySum += discreteResistProbability[++resistance];
for (; resistance < 11; ++resistance)
if (roll < (probabilitySum += discreteResistProbability[resistance]))
break;
float damageResisted = damage * resistance / 10.f;
if (damageResisted > 0.0f) // if any damage was resisted
@@ -1694,31 +1688,31 @@ uint32 Unit::CalcSpellResistedDamage(Unit* victim, uint32 damage, SpellSchoolMas
if (spellInfo && spellInfo->HasAttribute(SPELL_ATTR0_CU_SCHOOLMASK_NORMAL_WITH_MAGIC))
{
uint32 damageAfterArmor = CalcArmorReducedDamage(victim, damage, spellInfo, BASE_ATTACK);
uint32 armorReduction = damage - damageAfterArmor;
if (armorReduction < damageResisted) // pick the lower one, the weakest resistance counts
damageResisted = armorReduction;
float armorReduction = damage - damageAfterArmor;
// pick the lower one, the weakest resistance counts
damageResisted = std::min(damageResisted, armorReduction);
}
}
return damageResisted;
damageResisted = std::max(damageResisted, 0.f);
return uint32(damageResisted);
}
float Unit::GetEffectiveResistChance(Unit const* owner, SpellSchoolMask schoolMask, Unit const* victim, SpellInfo const* spellInfo)
float Unit::CalculateAverageResistReduction(SpellSchoolMask schoolMask, Unit const* victim, SpellInfo const* spellInfo) const
{
float victimResistance = float(victim->GetResistance(schoolMask));
if (owner)
// pets inherit 100% of masters penetration
// excluding traps
Player const* player = GetSpellModOwner();
if (player && GetEntry() != WORLD_TRIGGER)
{
// pets inherit 100% of masters penetration
// excluding traps
Player const* player = owner->GetSpellModOwner();
if (player && owner->GetEntry() != WORLD_TRIGGER)
{
victimResistance += float(player->GetTotalAuraModifierByMiscMask(SPELL_AURA_MOD_TARGET_RESISTANCE, schoolMask));
victimResistance -= float(player->GetSpellPenetrationItemMod());
}
else
victimResistance += float(owner->GetTotalAuraModifierByMiscMask(SPELL_AURA_MOD_TARGET_RESISTANCE, schoolMask));
victimResistance += float(player->GetTotalAuraModifierByMiscMask(SPELL_AURA_MOD_TARGET_RESISTANCE, schoolMask));
victimResistance -= float(player->GetSpellPenetrationItemMod());
}
else
victimResistance += float(GetTotalAuraModifierByMiscMask(SPELL_AURA_MOD_TARGET_RESISTANCE, schoolMask));
// holy resistance exists in pve and comes from level difference, ignore template values
if (schoolMask & SPELL_SCHOOL_MASK_HOLY)
@@ -1729,8 +1723,10 @@ float Unit::GetEffectiveResistChance(Unit const* owner, SpellSchoolMask schoolMa
victimResistance = 0.0f;
victimResistance = std::max(victimResistance, 0.0f);
if (owner)
victimResistance += std::max((float(victim->getLevelForTarget(owner)) - float(owner->getLevelForTarget(victim))) * 5.0f, 0.0f);
// level-based resistance does not apply to binary spells, and cannot be overcome by spell penetration
if (!spellInfo->HasAttribute(SPELL_ATTR0_CU_BINARY_SPELL))
victimResistance += std::max((float(victim->getLevelForTarget(this)) - float(getLevelForTarget(victim))) * 5.0f, 0.0f);
static uint32 const BOSS_LEVEL = 83;
static float const BOSS_RESISTANCE_CONSTANT = 510.0f;
@@ -2497,7 +2493,6 @@ SpellMissInfo Unit::MeleeSpellHitResult(Unit* victim, SpellInfo const* spellInfo
return SPELL_MISS_NONE;
}
/// @todo need use unit spell resistances in calculations
SpellMissInfo Unit::MagicSpellHitResult(Unit* victim, SpellInfo const* spellInfo)
{
// Can`t miss on dead target (on skinning for example)
@@ -2564,19 +2559,17 @@ SpellMissInfo Unit::MagicSpellHitResult(Unit* victim, SpellInfo const* spellInfo
// Chance resist mechanic (select max value from every mechanic spell effect)
int32 resist_chance = victim->GetMechanicResistChance(spellInfo) * 100;
tmp += resist_chance;
// Chance resist debuff
if (!spellInfo->IsPositive() && !spellInfo->HasAttribute(SPELL_ATTR4_IGNORE_RESISTANCES))
{
// resistance for binary spells
if (spellInfo->HasAttribute(SPELL_ATTR0_CU_BINARY_SPELL) && (spellInfo->GetSchoolMask() & SPELL_SCHOOL_MASK_NORMAL) == 0)
if ((spellInfo->GetSchoolMask() & SPELL_SCHOOL_MASK_HOLY) == 0 || victim->GetTypeId() == TYPEID_UNIT)
tmp += int32(Unit::GetEffectiveResistChance(this, spellInfo->GetSchoolMask(), victim, spellInfo) * 10000.0f); // 100 for spell calculations, and 100 for return value percentage
// resistance chance for binary spells, equals to average damage reduction of non-binary spell
if (spellInfo->HasAttribute(SPELL_ATTR0_CU_BINARY_SPELL) && (spellInfo->GetSchoolMask() & SPELL_SCHOOL_MASK_MAGIC))
resist_chance += int32(CalculateAverageResistReduction(spellInfo->GetSchoolMask(), victim, spellInfo) * 10000.f); // 100 for spell calculations, and 100 for return value percentage
}
// Roll chance
if (rand < tmp)
if (resist_chance > 0 && rand < (tmp += resist_chance))
return SPELL_MISS_RESIST;
// cast by caster in front of victim
@@ -14271,7 +14264,7 @@ void Unit::SendClearTarget()
uint32 Unit::GetResistance(SpellSchoolMask mask) const
{
int32 resist = -1;
for (int i = SPELL_SCHOOL_NORMAL; i < MAX_SPELL_SCHOOL; ++i)
for (int32 i = SPELL_SCHOOL_NORMAL; i < MAX_SPELL_SCHOOL; ++i)
if (mask & (1 << i) && (resist < 0 || resist > int32(GetResistance(SpellSchools(i)))))
resist = int32(GetResistance(SpellSchools(i)));
+2 -2
View File
@@ -941,7 +941,7 @@ class TC_GAME_API Unit : public WorldObject
uint32 GetResistance(SpellSchools school) const { return GetUInt32Value(UNIT_FIELD_RESISTANCES+school); }
uint32 GetResistance(SpellSchoolMask mask) const;
void SetResistance(SpellSchools school, int32 val) { SetStatInt32Value(UNIT_FIELD_RESISTANCES+school, val); }
static float GetEffectiveResistChance(Unit const* owner, SpellSchoolMask schoolMask, Unit const* victim, SpellInfo const* spellInfo = nullptr);
float CalculateAverageResistReduction(SpellSchoolMask schoolMask, Unit const* victim, SpellInfo const* spellInfo = nullptr) const;
uint32 GetHealth() const { return GetUInt32Value(UNIT_FIELD_HEALTH); }
uint32 GetMaxHealth() const { return GetUInt32Value(UNIT_FIELD_MAXHEALTH); }
@@ -1035,7 +1035,7 @@ class TC_GAME_API Unit : public WorldObject
void SendDurabilityLoss(Player* receiver, uint32 percent);
void PlayOneShotAnimKitId(uint16 animKitId);
uint16 GetMaxSkillValueForLevel(Unit const* target = nullptr) const { return (target ? getLevelForTarget(target) : getLevel()) * 5; }
uint32 GetMaxSkillValueForLevel(Unit const* target = nullptr) const { return (target ? getLevelForTarget(target) : getLevel()) * 5; }
void DealDamageMods(Unit const* victim, uint32 &damage, uint32* absorb) const;
uint32 DealDamage(Unit* victim, uint32 damage, CleanDamage const* cleanDamage = nullptr, DamageEffectType damagetype = DIRECT_DAMAGE, SpellSchoolMask damageSchoolMask = SPELL_SCHOOL_MASK_NORMAL, SpellInfo const* spellProto = nullptr, bool durabilityLoss = true);
void Kill(Unit* victim, bool durabilityLoss = true);