NPC vs Client damage revamp

Shot list of things that changed:
AC Softcap is based on your defensive skill (the scaling factors
are based on at least previously stated dev quotes)
The over AC Softcap returns are now based on exact calculations
made with the Armor of Wisdom AA.
Shielding item bonus and Melee mitigation spell bonus (nerfs)
NPCs damage will now correctly follow the
"damage base + (damage interval * [1, 20])" formula.

These changes might seem a little weird but I didn't want to change
any of the other damage situations since they were fairly good, but
they still could use a revamp as well.

New rules:
RULE_BOOL ( Combat, OldACSoftcapRules, false) // use old softcaps
RULE_BOOL ( Combat, UseOldDamageIntervalRules, false) // use old damage formulas for everything
RULE_REAL ( Combat, WarACSoftcapReturn, 0.3448) // new AC returns
RULE_REAL ( Combat, ClrRngMnkBrdACSoftcapReturn, 0.3030)
RULE_REAL ( Combat, PalShdACSoftcapReturn, 0.3226)
RULE_REAL ( Combat, DruNecWizEncMagACSoftcapReturn, 0.2000)
RULE_REAL ( Combat, RogShmBstBerACSoftcapReturn, 0.2500)
RULE_REAL ( Combat, SoftcapFactor, 1.88)

If you want to use the old calculations only, set
Combat:OldACSoftcapRules and Combat:UseOldDamageIntervalRules to true
This commit is contained in:
Michael Cook (mackal) 2014-02-19 20:29:19 -05:00
parent ab4c9581ad
commit eb3a11b49a
4 changed files with 167 additions and 124 deletions

View File

@ -357,6 +357,14 @@ RULE_REAL ( Combat, LowPlateChainACSoftcapReturn, 0.23)
RULE_REAL ( Combat, LowChainLeatherACSoftcapReturn, 0.17) RULE_REAL ( Combat, LowChainLeatherACSoftcapReturn, 0.17)
RULE_REAL ( Combat, CasterACSoftcapReturn, 0.06) RULE_REAL ( Combat, CasterACSoftcapReturn, 0.06)
RULE_REAL ( Combat, MiscACSoftcapReturn, 0.3) RULE_REAL ( Combat, MiscACSoftcapReturn, 0.3)
RULE_BOOL ( Combat, OldACSoftcapRules, false) // use old softcaps
RULE_BOOL ( Combat, UseOldDamageIntervalRules, false) // use old damage formulas for everything
RULE_REAL ( Combat, WarACSoftcapReturn, 0.3448) // new AC returns
RULE_REAL ( Combat, ClrRngMnkBrdACSoftcapReturn, 0.3030)
RULE_REAL ( Combat, PalShdACSoftcapReturn, 0.3226)
RULE_REAL ( Combat, DruNecWizEncMagACSoftcapReturn, 0.2000)
RULE_REAL ( Combat, RogShmBstBerACSoftcapReturn, 0.2500)
RULE_REAL ( Combat, SoftcapFactor, 1.88)
RULE_REAL ( Combat, ACthac0Factor, 0.55) RULE_REAL ( Combat, ACthac0Factor, 0.55)
RULE_REAL ( Combat, ACthac20Factor, 0.55) RULE_REAL ( Combat, ACthac20Factor, 0.55)
RULE_INT ( Combat, HitCapPre20, 40) // live has it capped at 40 for whatever dumb reason... this is mainly for custom servers RULE_INT ( Combat, HitCapPre20, 40) // live has it capped at 40 for whatever dumb reason... this is mainly for custom servers

View File

@ -540,13 +540,12 @@ void Mob::MeleeMitigation(Mob *attacker, int32 &damage, int32 minhit, ExtraAttac
return; return;
Mob* defender = this; Mob* defender = this;
float aa_mit = 0; float aa_mit = (aabonuses.CombatStability + itembonuses.CombatStability +
spellbonuses.CombatStability) / 100.0f;
aa_mit = (aabonuses.CombatStability + itembonuses.CombatStability + spellbonuses.CombatStability)/100.0f; if (RuleB(Combat, UseIntervalAC)) {
float softcap = (GetSkill(SkillDefense) + GetLevel()) *
if(RuleB(Combat, UseIntervalAC)) RuleR(Combat, SoftcapFactor) * (1.0 + aa_mit);
{
float softcap = 0.0;
float mitigation_rating = 0.0; float mitigation_rating = 0.0;
float attack_rating = 0.0; float attack_rating = 0.0;
int shield_ac = 0; int shield_ac = 0;
@ -556,19 +555,14 @@ void Mob::MeleeMitigation(Mob *attacker, int32 &damage, int32 minhit, ExtraAttac
float monkweight = RuleI(Combat, MonkACBonusWeight); float monkweight = RuleI(Combat, MonkACBonusWeight);
monkweight = mod_monk_weight(monkweight, attacker); monkweight = mod_monk_weight(monkweight, attacker);
if(IsClient()) if (IsClient()) {
{
armor = CastToClient()->GetRawACNoShield(shield_ac); armor = CastToClient()->GetRawACNoShield(shield_ac);
weight = (CastToClient()->CalcCurrentWeight() / 10.0); weight = (CastToClient()->CalcCurrentWeight() / 10.0);
} } else if (IsNPC()) {
else if(IsNPC())
{
armor = CastToNPC()->GetRawAC(); armor = CastToNPC()->GetRawAC();
if (!IsPet()) if (!IsPet())
{
armor = (armor / RuleR(Combat, NPCACFactor)); armor = (armor / RuleR(Combat, NPCACFactor));
}
armor += spellbonuses.AC + itembonuses.AC + 1; armor += spellbonuses.AC + itembonuses.AC + 1;
} }
@ -578,128 +572,84 @@ void Mob::MeleeMitigation(Mob *attacker, int32 &damage, int32 minhit, ExtraAttac
armor -= opts->armor_pen_flat; armor -= opts->armor_pen_flat;
} }
if(GetClass() == WIZARD || GetClass() == MAGICIAN || GetClass() == NECROMANCER || GetClass() == ENCHANTER) if (RuleB(Combat, OldACSoftcapRules)) {
{ if (GetClass() == WIZARD || GetClass() == MAGICIAN ||
GetClass() == NECROMANCER || GetClass() == ENCHANTER)
softcap = RuleI(Combat, ClothACSoftcap); softcap = RuleI(Combat, ClothACSoftcap);
}
else if (GetClass() == MONK && weight <= monkweight) else if (GetClass() == MONK && weight <= monkweight)
{
softcap = RuleI(Combat, MonkACSoftcap); softcap = RuleI(Combat, MonkACSoftcap);
}
else if(GetClass() == DRUID || GetClass() == BEASTLORD || GetClass() == MONK) else if(GetClass() == DRUID || GetClass() == BEASTLORD || GetClass() == MONK)
{
softcap = RuleI(Combat, LeatherACSoftcap); softcap = RuleI(Combat, LeatherACSoftcap);
} else if(GetClass() == SHAMAN || GetClass() == ROGUE ||
else if(GetClass() == SHAMAN || GetClass() == ROGUE || GetClass() == BERSERKER || GetClass() == RANGER) GetClass() == BERSERKER || GetClass() == RANGER)
{
softcap = RuleI(Combat, ChainACSoftcap); softcap = RuleI(Combat, ChainACSoftcap);
}
else else
{
softcap = RuleI(Combat, PlateACSoftcap); softcap = RuleI(Combat, PlateACSoftcap);
} }
softcap += shield_ac; softcap += shield_ac;
armor += shield_ac; armor += shield_ac;
if (RuleB(Combat, OldACSoftcapRules))
softcap += (softcap * (aa_mit * RuleR(Combat, AAMitigationACFactor))); softcap += (softcap * (aa_mit * RuleR(Combat, AAMitigationACFactor)));
if(armor > softcap) if (armor > softcap) {
{
int softcap_armor = armor - softcap; int softcap_armor = armor - softcap;
if (RuleB(Combat, OldACSoftcapRules)) {
if (GetClass() == WARRIOR) if (GetClass() == WARRIOR)
{
softcap_armor = softcap_armor * RuleR(Combat, WarriorACSoftcapReturn); softcap_armor = softcap_armor * RuleR(Combat, WarriorACSoftcapReturn);
} else if (GetClass() == SHADOWKNIGHT || GetClass() == PALADIN ||
else if(GetClass() == SHADOWKNIGHT || GetClass() == PALADIN || (GetClass() == MONK && weight <= monkweight)) (GetClass() == MONK && weight <= monkweight))
{
softcap_armor = softcap_armor * RuleR(Combat, KnightACSoftcapReturn); softcap_armor = softcap_armor * RuleR(Combat, KnightACSoftcapReturn);
} else if (GetClass() == CLERIC || GetClass() == BARD ||
else if(GetClass() == CLERIC || GetClass() == BARD || GetClass() == BERSERKER || GetClass() == ROGUE || GetClass() == SHAMAN || GetClass() == MONK) GetClass() == BERSERKER || GetClass() == ROGUE ||
{ GetClass() == SHAMAN || GetClass() == MONK)
softcap_armor = softcap_armor * RuleR(Combat, LowPlateChainACSoftcapReturn); softcap_armor = softcap_armor * RuleR(Combat, LowPlateChainACSoftcapReturn);
}
else if (GetClass() == RANGER || GetClass() == BEASTLORD) else if (GetClass() == RANGER || GetClass() == BEASTLORD)
{
softcap_armor = softcap_armor * RuleR(Combat, LowChainLeatherACSoftcapReturn); softcap_armor = softcap_armor * RuleR(Combat, LowChainLeatherACSoftcapReturn);
} else if (GetClass() == WIZARD || GetClass() == MAGICIAN ||
else if(GetClass() == WIZARD || GetClass() == MAGICIAN || GetClass() == NECROMANCER || GetClass() == ENCHANTER || GetClass() == DRUID) GetClass() == NECROMANCER || GetClass() == ENCHANTER ||
{ GetClass() == DRUID)
softcap_armor = softcap_armor * RuleR(Combat, CasterACSoftcapReturn); softcap_armor = softcap_armor * RuleR(Combat, CasterACSoftcapReturn);
}
else else
{
softcap_armor = softcap_armor * RuleR(Combat, MiscACSoftcapReturn); softcap_armor = softcap_armor * RuleR(Combat, MiscACSoftcapReturn);
} else {
if (GetClass() == WARRIOR)
softcap_armor *= RuleR(Combat, WarACSoftcapReturn);
else if (GetClass() == PALADIN || GetClass() == SHADOWKNIGHT)
softcap_armor *= RuleR(Combat, PalShdACSoftcapReturn);
else if (GetClass() == CLERIC || GetClass() == RANGER ||
GetClass() == MONK || GetClass() == BARD)
softcap_armor *= RuleR(Combat, ClrRngMnkBrdACSoftcapReturn);
else if (GetClass() == DRUID || GetClass() == NECROMANCER ||
GetClass() == WIZARD || GetClass() == ENCHANTER ||
GetClass() == MAGICIAN)
softcap_armor *= RuleR(Combat, DruNecWizEncMagACSoftcapReturn);
else if (GetClass() == ROGUE || GetClass() == SHAMAN ||
GetClass() == BEASTLORD || GetClass() == BERSERKER)
softcap_armor *= RuleR(Combat, RogShmBstBerACSoftcapReturn);
else
softcap_armor *= RuleR(Combat, MiscACSoftcapReturn);
} }
armor = softcap + softcap_armor; armor = softcap + softcap_armor;
} }
mitigation_rating = 0.0; if (GetClass() == WIZARD || GetClass() == MAGICIAN ||
if(GetClass() == WIZARD || GetClass() == MAGICIAN || GetClass() == NECROMANCER || GetClass() == ENCHANTER) GetClass() == NECROMANCER || GetClass() == ENCHANTER)
{
mitigation_rating = ((GetSkill(SkillDefense) + itembonuses.HeroicAGI/10) / 4.0) + armor + 1; mitigation_rating = ((GetSkill(SkillDefense) + itembonuses.HeroicAGI/10) / 4.0) + armor + 1;
}
else else
{
mitigation_rating = ((GetSkill(SkillDefense) + itembonuses.HeroicAGI/10) / 3.0) + (armor * 1.333333) + 1; mitigation_rating = ((GetSkill(SkillDefense) + itembonuses.HeroicAGI/10) / 3.0) + (armor * 1.333333) + 1;
}
mitigation_rating *= 0.847; mitigation_rating *= 0.847;
mitigation_rating = mod_mitigation_rating(mitigation_rating, attacker); mitigation_rating = mod_mitigation_rating(mitigation_rating, attacker);
if (attacker->IsClient()) if (attacker->IsClient())
{
attack_rating = (attacker->CastToClient()->CalcATK() + ((attacker->GetSTR()-66) * 0.9) + (attacker->GetSkill(SkillOffense)*1.345)); attack_rating = (attacker->CastToClient()->CalcATK() + ((attacker->GetSTR()-66) * 0.9) + (attacker->GetSkill(SkillOffense)*1.345));
}
else else
{
attack_rating = (attacker->GetATK() + (attacker->GetSkill(SkillOffense)*1.345) + ((attacker->GetSTR()-66) * 0.9)); attack_rating = (attacker->GetATK() + (attacker->GetSkill(SkillOffense)*1.345) + ((attacker->GetSTR()-66) * 0.9));
}
attack_rating = attacker->mod_attack_rating(attack_rating, this); attack_rating = attacker->mod_attack_rating(attack_rating, this);
float d = 10.0; damage = GetMeleeMitDmg(attacker, damage, minhit, mitigation_rating, attack_rating);
float mit_roll = MakeRandomFloat(0, mitigation_rating); } else {
float atk_roll = MakeRandomFloat(0, attack_rating);
if(atk_roll > mit_roll)
{
float a_diff = (atk_roll - mit_roll);
float thac0 = attack_rating * RuleR(Combat, ACthac0Factor);
float thac0cap = ((attacker->GetLevel() * 9) + 20);
if(thac0 > thac0cap)
{
thac0 = thac0cap;
}
d -= 10.0 * (a_diff / thac0);
}
else if(mit_roll > atk_roll)
{
float m_diff = (mit_roll - atk_roll);
float thac20 = mitigation_rating * RuleR(Combat, ACthac20Factor);
float thac20cap = ((defender->GetLevel() * 9) + 20);
if(thac20 > thac20cap)
{
thac20 = thac20cap;
}
d += 10 * (m_diff / thac20);
}
if(d < 0.0)
{
d = 0.0;
}
if(d > 20)
{
d = 20.0;
}
float interval = (damage - minhit) / 20.0;
damage = damage - ((int)d * interval);
}
else{
//////////////////////////////////////////////////////// ////////////////////////////////////////////////////////
// Scorpious2k: Include AC in the calculation // Scorpious2k: Include AC in the calculation
// use serverop variables to set values // use serverop variables to set values
@ -750,17 +700,100 @@ void Mob::MeleeMitigation(Mob *attacker, int32 &damage, int32 minhit, ExtraAttac
if(damage != 0 && damage < minhit) if(damage != 0 && damage < minhit)
damage = minhit; damage = minhit;
}
//reduce the damage from shielding item and aa based on the min dmg //reduce the damage from shielding item and aa based on the min dmg
//spells offer pure mitigation //spells offer pure mitigation
damage -= (minhit * defender->itembonuses.MeleeMitigation / 100); damage -= (minhit * defender->itembonuses.MeleeMitigation / 100);
damage -= (damage * defender->spellbonuses.MeleeMitigation / 100); damage -= (damage * defender->spellbonuses.MeleeMitigation / 100);
}
if (damage < 0) if (damage < 0)
damage = 0; damage = 0;
} }
// This is called when the Mob is the one being hit
int32 Mob::GetMeleeMitDmg(Mob *attacker, int32 damage, int32 minhit,
float mit_rating, float atk_rating)
{
float d = 10.0;
float mit_roll = MakeRandomFloat(0, mit_rating);
float atk_roll = MakeRandomFloat(0, atk_rating);
if (atk_roll > mit_roll) {
float a_diff = atk_roll - mit_roll;
float thac0 = atk_rating * RuleR(Combat, ACthac0Factor);
float thac0cap = attacker->GetLevel() * 9 + 20;
if (thac0 > thac0cap)
thac0 = thac0cap;
d -= 10.0 * (a_diff / thac0);
} else if (mit_roll > atk_roll) {
float m_diff = mit_roll - atk_roll;
float thac20 = mit_rating * RuleR(Combat, ACthac20Factor);
float thac20cap = GetLevel() * 9 + 20;
if (thac20 > thac20cap)
thac20 = thac20cap;
d += 10.0 * (m_diff / thac20);
}
if (d < 0.0)
d = 0.0;
else if (d > 20.0)
d = 20.0;
float interval = (damage - minhit) / 20.0;
damage -= ((int)d * interval);
damage -= (minhit * itembonuses.MeleeMitigation / 100);
damage -= (damage * spellbonuses.MeleeMitigation / 100);
return damage;
}
// This is called when the Client is the one being hit
int32 Client::GetMeleeMitDmg(Mob *attacker, int32 damage, int32 minhit,
float mit_rating, float atk_rating)
{
if (!attacker->IsNPC() || RuleB(Combat, UseOldDamageIntervalRules))
return Mob::GetMeleeMitDmg(attacker, damage, minhit, mit_rating, atk_rating);
int d = 10;
// floats for the rounding issues
float dmg_interval = (damage - minhit) / 19.0;
float dmg_bonus = minhit - dmg_interval;
float spellMeleeMit = spellbonuses.MeleeMitigation / 100.0;
if (GetClass() == WARRIOR)
spellMeleeMit += 0.05;
dmg_bonus -= dmg_bonus * (itembonuses.MeleeMitigation / 100.0);
dmg_interval -= dmg_interval * spellMeleeMit;
float mit_roll = MakeRandomFloat(0, mit_rating);
float atk_roll = MakeRandomFloat(0, atk_rating);
if (atk_roll > mit_roll) {
float a_diff = atk_roll - mit_roll;
float thac0 = atk_rating * RuleR(Combat, ACthac0Factor);
float thac0cap = attacker->GetLevel() * 9 + 20;
if (thac0 > thac0cap)
thac0 = thac0cap;
d += 10 * (a_diff / thac0);
} else if (mit_roll > atk_roll) {
float m_diff = mit_roll - atk_roll;
float thac20 = mit_rating * RuleR(Combat, ACthac20Factor);
float thac20cap = GetLevel() * 9 + 20;
if (thac20 > thac20cap)
thac20 = thac20cap;
d -= 10 * (m_diff / thac20);
}
if (d < 1)
d = 1;
else if (d > 20)
d = 20;
return static_cast<int32>((dmg_bonus + dmg_interval * d));
}
//Returns the weapon damage against the input mob //Returns the weapon damage against the input mob
//if we cannot hit the mob with the current weapon we will get a value less than or equal to zero //if we cannot hit the mob with the current weapon we will get a value less than or equal to zero
//Else we know we can hit. //Else we know we can hit.

View File

@ -221,6 +221,7 @@ public:
virtual Raid* GetRaid() { return entity_list.GetRaidByClient(this); } virtual Raid* GetRaid() { return entity_list.GetRaidByClient(this); }
virtual Group* GetGroup() { return entity_list.GetGroupByClient(this); } virtual Group* GetGroup() { return entity_list.GetGroupByClient(this); }
virtual inline bool IsBerserk() { return berserk; } virtual inline bool IsBerserk() { return berserk; }
virtual int32 GetMeleeMitDmg(Mob *attacker, int32 damage, int32 minhit, float mit_rating, float atk_rating);
void AI_Init(); void AI_Init();
void AI_Start(uint32 iMoveDelay = 0); void AI_Start(uint32 iMoveDelay = 0);

View File

@ -143,6 +143,7 @@ public:
virtual void DoRiposte(Mob* defender); virtual void DoRiposte(Mob* defender);
void ApplyMeleeDamageBonus(uint16 skill, int32 &damage); void ApplyMeleeDamageBonus(uint16 skill, int32 &damage);
virtual void MeleeMitigation(Mob *attacker, int32 &damage, int32 minhit, ExtraAttackOptions *opts = nullptr); virtual void MeleeMitigation(Mob *attacker, int32 &damage, int32 minhit, ExtraAttackOptions *opts = nullptr);
virtual int32 GetMeleeMitDmg(Mob *attacker, int32 damage, int32 minhit, float mit_rating, float atk_rating);
bool CombatRange(Mob* other); bool CombatRange(Mob* other);
virtual inline bool IsBerserk() { return false; } // only clients virtual inline bool IsBerserk() { return false; } // only clients