From 992a5cc132a14c24324dc7fa6392f82433cb45c0 Mon Sep 17 00:00:00 2001 From: Fryguy Date: Sun, 26 May 2024 20:27:18 -0400 Subject: [PATCH] [Rule] Legacy Compute Defense against modern agi based defense. (#4349) * [Rule] Legacy Compute Defense against modern agi based defense. In new code, AGI becomes a large contributor to avoidance at low levels, since AGI isn't capped by Level but Defense is A scale factor is implemented for PCs to reduce the effect of AGI at low levels. This isn't applied to NPCs since they can be easily controlled via the Database. * `snake_case` --------- Co-authored-by: Kinglykrab --- common/ruletypes.h | 1 + zone/attack.cpp | 37 +++++++++++++++++++++++++++++++------ zone/tune.cpp | 44 ++++++++++++++++++++++++++++---------------- 3 files changed, 60 insertions(+), 22 deletions(-) diff --git a/common/ruletypes.h b/common/ruletypes.h index 113b582d1..838044a33 100644 --- a/common/ruletypes.h +++ b/common/ruletypes.h @@ -614,6 +614,7 @@ RULE_INT(Combat, PCAttackPowerScaling, 100, "Applies scaling to PC Attack Power RULE_INT(Combat, PCAccuracyAvoidanceMod2Scale, 100, "Scale Factor for PC Accuracy and Avoidance (Mod2, found on items). Found a value of 100 to make both too strong (75 = x0.75). DEFAULT: 100 to not adjust existing Servers") RULE_BOOL(Combat, AllowRaidTargetBlind, false, "Toggle to allow raid targets to be blinded, default is false (Live-like)") RULE_BOOL(Combat, RogueBackstabHasteCorrection, false, "Toggle to enable correction for Haste impacting Backstab DPS too much. DEFAULT: false") +RULE_BOOL(Combat, LegacyComputeDefense, false, "Trim AGI Scaling of defense mostly for lower levels to help compensate for the newer agi based defense system. Default: False") RULE_CATEGORY_END() RULE_CATEGORY(NPC) diff --git a/zone/attack.cpp b/zone/attack.cpp index c06a6a639..040ddf95e 100644 --- a/zone/attack.cpp +++ b/zone/attack.cpp @@ -256,19 +256,44 @@ int Mob::GetTotalToHit(EQ::skills::SkillType skill, int chance_mod) int Mob::compute_defense() { int defense = GetSkill(EQ::skills::SkillDefense) * 400 / 225; - defense += (8000 * (GetAGI() - 40)) / 36000; - if (IsOfClientBot()) { - defense += itembonuses.heroic_agi_avoidance; + + // In new code, AGI becomes a large contributor to avoidance at low levels, since AGI isn't capped by Level but Defense is + // A scale factor is implemented for PCs to reduce the effect of AGI at low levels. This isn't applied to NPCs since they can be + // easily controlled via the Database. + if (RuleB(Combat, LegacyComputeDefense)) { + int agi_scale_factor = 1000; + + if (IsOfClientBot()) { + agi_scale_factor = std::min(1000, static_cast(GetLevel()) * 1000 / 70); // Scales Agi Contribution for PC's Level, max Contribution at Level 70 + } + + defense += agi_scale_factor * (800 * (GetAGI() - 40)) / 3600 / 1000; + + if (IsOfClientBot()) { + defense += GetHeroicAGI() / 10; + } + + defense += itembonuses.AvoidMeleeChance * RuleI(Combat, PCAccuracyAvoidanceMod2Scale) / 100; // item mod2 + } else { + defense += (8000 * (GetAGI() - 40)) / 36000; + + if (IsOfClientBot()) { + defense += itembonuses.heroic_agi_avoidance; + } + + defense += itembonuses.AvoidMeleeChance; // item mod2 } + //516 SE_AC_Mitigation_Max_Percent auto ac_bonus = itembonuses.AC_Mitigation_Max_Percent + aabonuses.AC_Mitigation_Max_Percent + spellbonuses.AC_Mitigation_Max_Percent; - if (ac_bonus) + if (ac_bonus) { defense += round(static_cast(defense) * static_cast(ac_bonus) * 0.0001); + } - defense += itembonuses.AvoidMeleeChance; // item mod2 - if (IsNPC()) + if (IsNPC()) { defense += CastToNPC()->GetAvoidanceRating(); + } if (IsClient()) { double reduction = CastToClient()->GetIntoxication() / 2.0; diff --git a/zone/tune.cpp b/zone/tune.cpp index 853153884..5baab32db 100644 --- a/zone/tune.cpp +++ b/zone/tune.cpp @@ -1347,7 +1347,7 @@ int64 Mob::TuneGetTotalToHit(EQ::skills::SkillType skill, int chance_mod, int ac hit_bonus += spellbonuses.increase_archery + aabonuses.increase_archery + itembonuses.increase_archery; hit_bonus -= hit_bonus * RuleR(Combat, ArcheryHitPenalty); } - + accuracy = (accuracy * (100 + hit_bonus)) / 100; return accuracy; } @@ -1381,31 +1381,43 @@ int64 Mob::TuneGetTotalDefense(int avoidance_override, int add_avoidance) int64 Mob::Tunecompute_defense(int avoidance_override, int add_avoidance) { int defense = GetSkill(EQ::skills::SkillDefense) * 400 / 225; - defense += (8000 * (GetAGI() - 40)) / 36000; - if (IsOfClientBot()) { - if (avoidance_override) { - defense = avoidance_override; + + // In new code, AGI becomes a large contributor to avoidance at low levels, since AGI isn't capped by Level but Defense is + // A scale factor is implemented for PCs to reduce the effect of AGI at low levels. This isn't applied to NPCs since they can be + // easily controlled via the Database. + if (RuleB(Combat, LegacyComputeDefense)) { + int agi_scale_factor = 1000; + + if (IsOfClientBot()) { + agi_scale_factor = std::min(1000, static_cast(GetLevel()) * 1000 / 70); // Scales Agi Contribution for PC's Level, max Contribution at Level 70 } - else { + + defense += agi_scale_factor * (800 * (GetAGI() - 40)) / 3600 / 1000; + + if (IsOfClientBot()) { + defense += GetHeroicAGI() / 10; + } + + defense += itembonuses.AvoidMeleeChance * RuleI(Combat, PCAccuracyAvoidanceMod2Scale) / 100; // item mod2 + } else { + defense += (8000 * (GetAGI() - 40)) / 36000; + + if (IsOfClientBot()) { defense += itembonuses.heroic_agi_avoidance; } - defense += add_avoidance; //1 pt = 10 heroic agi + + defense += itembonuses.AvoidMeleeChance; // item mod2 } + //516 SE_AC_Mitigation_Max_Percent auto ac_bonus = itembonuses.AC_Mitigation_Max_Percent + aabonuses.AC_Mitigation_Max_Percent + spellbonuses.AC_Mitigation_Max_Percent; - if (ac_bonus) + if (ac_bonus) { defense += round(static_cast(defense) * static_cast(ac_bonus) * 0.0001); + } - defense += itembonuses.AvoidMeleeChance; // item mod2 if (IsNPC()) { - if (avoidance_override) { - defense += avoidance_override; - } - else { - defense += CastToNPC()->GetAvoidanceRating(); - } - defense += add_avoidance; + defense += CastToNPC()->GetAvoidanceRating(); } if (IsClient()) {