Skip to content

Commit

Permalink
[Rules] Add Multiplier for Heroic Stats. (EQEmu#3014)
Browse files Browse the repository at this point in the history
* initial work

* [Rules] Add Multiplier for Heroic Stats.

* Add bots

* update SendStatsWindow

* fix SendStatsWindow
  • Loading branch information
Aeadoin authored and nytmyr committed Mar 24, 2023
1 parent d106e4f commit 2c90717
Show file tree
Hide file tree
Showing 6 changed files with 91 additions and 55 deletions.
6 changes: 6 additions & 0 deletions common/ruletypes.h
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,12 @@ RULE_INT(Character, ItemEnduranceRegenCap, 15, "Limit on endurance regeneration
RULE_INT(Character, ItemExtraDmgCap, 150, "Cap for bonuses to melee skills like Bash, Frenzy, etc.")
RULE_INT(Character, HasteCap, 100, "Haste cap for non-v3(over haste) haste")
RULE_INT(Character, Hastev3Cap, 25, "Haste cap for v3(over haste) haste")
RULE_REAL(Character, HeroicStrengthMultiplier, 1.00, "Multplier scales benefits from Heroic Strength. Grants 25 Base Endurance, 0.05 Endurance Regen, 1 Melee Damage each Hit, and 1 Shield AC per 10 Heroic Strength.")
RULE_REAL(Character, HeroicStaminaMultiplier, 1.00, "Multplier scales benefits from Heroic Stamina. Grants 25 Base Endurance, 0.05 Endurance Regen, 100 Base HP, and 0.5 HP Regen per 10 Heroic Stamina.")
RULE_REAL(Character, HeroicAgilityMultiplier, 1.00, "Multplier scales benefits from Heroic Agility. Grants 25 Base Endurance, 0.05 Endurance Regen, and 1 Avoidance AC per 10 Heroic Agility. (Rule does not change Dodge Chance)")
RULE_REAL(Character, HeroicDexterityMultiplier, 1.00, "Multplier scales benefits from Heroic Dexterity. Grants 25 Base Endurance, 0.05 Endurance Regen, and 1 Archery/Throwing Damage each hit per 10 Heroic Dexterity. (Rule does not change Assassinate/Headshot/Block/Parry/Riposte Chances)")
RULE_REAL(Character, HeroicWisdomMultiplier, 1.00, "Multplier scales benefits from Heroic Wisdom. Grants 250 Base Mana, 1 Mana Regen per 25 Heroic Wisdom.")
RULE_REAL(Character, HeroicIntelligenceMultiplier, 1.00, "Multplier scales benefits from Heroic Intelligence. Grants 250 Base Mana, 1 Mana Regen per 25 Heroic Intelligence.")
RULE_INT(Character, SkillUpModifier, 100, "The probability for a skill-up is multiplied by value/100")
RULE_BOOL(Character, SharedBankPlat, false, "Shared bank platinum. Off by default to prevent duplication")
RULE_BOOL(Character, BindAnywhere, false, "Allows players to bind their soul anywhere in the world")
Expand Down
8 changes: 4 additions & 4 deletions zone/attack.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -251,7 +251,7 @@ int Mob::compute_defense()
int defense = GetSkill(EQ::skills::SkillDefense) * 400 / 225;
defense += (8000 * (GetAGI() - 40)) / 36000;
if (IsOfClientBot()) {
defense += GetHeroicAGI() / 10;
defense += GetHeroicAGI() * RuleR(Character, HeroicAgilityMultiplier) / 10;
}

//516 SE_AC_Mitigation_Max_Percent
Expand Down Expand Up @@ -884,7 +884,7 @@ int Mob::ACSum(bool skip_caps)
shield_ac = CalcRecommendedLevelBonus(GetLevel(), inst->GetItemRecommendedLevel(true), inst->GetItemArmorClass(true));
}
}
shield_ac += GetHeroicSTR() / 10;
shield_ac += GetHeroicSTR() * RuleR(Character, HeroicStrengthMultiplier) / 10;
}
// EQ math
ac = (ac * 4) / 3;
Expand Down Expand Up @@ -5964,10 +5964,10 @@ void Mob::CommonOutgoingHitSuccess(Mob* defender, DamageHitInfo &hit, ExtraAttac
switch (hit.skill) {
case EQ::skills::SkillThrowing:
case EQ::skills::SkillArchery:
extra = GetHeroicDEX() / 10;
extra = GetHeroicDEX() * RuleR(Character, HeroicDexterityMultiplier) / 10;
break;
default:
extra = GetHeroicSTR() / 10;
extra = GetHeroicSTR() * RuleR(Character, HeroicStrengthMultiplier) / 10;
break;
}
hit.damage_done += extra;
Expand Down
66 changes: 37 additions & 29 deletions zone/bot.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -7228,10 +7228,10 @@ int64 Bot::CalcManaRegenCap() {
int64 cap = RuleI(Character, ItemManaRegenCap) + aabonuses.ItemManaRegenCap;
switch(GetCasterClass()) {
case 'I':
cap += (itembonuses.HeroicINT / 25);
cap += itembonuses.HeroicINT * RuleR(Character, HeroicIntelligenceMultiplier) / 25;
break;
case 'W':
cap += (itembonuses.HeroicWIS / 25);
cap += itembonuses.HeroicWIS * RuleR(Character, HeroicWisdomMultiplier) / 25;
break;
}
return (cap * RuleI(Character, ManaRegenMultiplier) / 100);
Expand Down Expand Up @@ -7623,8 +7623,9 @@ int32 Bot::LevelRegen() {

int64 Bot::CalcHPRegen() {
int32 regen = (LevelRegen() + itembonuses.HPRegen + spellbonuses.HPRegen);
regen += GetHeroicSTA() / 20;
regen += GetHeroicSTA() * RuleR(Character, HeroicStaminaMultiplier) / 20;
regen += (aabonuses.HPRegen + GroupLeadershipAAHealthRegeneration());

regen = ((regen * RuleI(Character, HPRegenMultiplier)) / 100);
return regen;
}
Expand All @@ -7644,9 +7645,9 @@ int64 Bot::CalcManaRegen() {
regen = (2 + spellbonuses.ManaRegen + itembonuses.ManaRegen);

if(GetCasterClass() == 'I')
regen += (itembonuses.HeroicINT / 25);
regen += itembonuses.HeroicINT * RuleR(Character, HeroicIntelligenceMultiplier) / 25;
else if(GetCasterClass() == 'W')
regen += (itembonuses.HeroicWIS / 25);
regen += itembonuses.HeroicWIS * RuleR(Character, HeroicWisdomMultiplier) / 25;
else
regen = 0;

Expand Down Expand Up @@ -7695,7 +7696,7 @@ int64 Bot::CalcMaxHP() {
int32 bot_hp = 0;
uint32 nd = 10000;
bot_hp += (GenerateBaseHitPoints() + itembonuses.HP);
bot_hp += (GetHeroicSTA() * 10);
bot_hp += GetHeroicSTA() * RuleR(Character, HeroicStaminaMultiplier) * 10;
nd += aabonuses.MaxHP;
bot_hp = ((float)bot_hp * (float)nd / (float)10000);
bot_hp += (spellbonuses.HP + aabonuses.HP);
Expand Down Expand Up @@ -7735,35 +7736,42 @@ int64 Bot::CalcMaxEndurance() {
int64 Bot::CalcBaseEndurance() {
int32 base_end = 0;
int32 base_endurance = 0;
int32 ConvertedStats = 0;
int32 converted_stats = 0;
int32 sta_end = 0;
int Stats = 0;
int stats = 0;
if (GetOwner() && GetOwner()->CastToClient() && GetOwner()->CastToClient()->ClientVersion() >= EQ::versions::ClientVersion::SoD && RuleB(Character, SoDClientUseSoDHPManaEnd)) {
int HeroicStats = 0;
Stats = ((GetSTR() + GetSTA() + GetDEX() + GetAGI()) / 4);
HeroicStats = ((GetHeroicSTR() + GetHeroicSTA() + GetHeroicDEX() + GetHeroicAGI()) / 4);
if (Stats > 100) {
ConvertedStats = (((Stats - 100) * 5 / 2) + 100);
if (Stats > 201)
ConvertedStats -= ((Stats - 201) * 5 / 4);
} else
ConvertedStats = Stats;
double heroic_stats = 0;
stats = ((GetSTR() + GetSTA() + GetDEX() + GetAGI()) / 4);
double heroic_str = GetHeroicSTR() * RuleR(Character, HeroicStrengthMultiplier);
double heroic_sta = GetHeroicSTA() * RuleR(Character, HeroicStaminaMultiplier);
double heroic_dex = GetHeroicDEX() * RuleR(Character, HeroicDexterityMultiplier);
double heroic_agi = GetHeroicAGI() * RuleR(Character, HeroicAgilityMultiplier);
heroic_stats = (heroic_str + heroic_sta + heroic_dex + heroic_agi) / 4;

if (stats > 100) {
converted_stats = (((stats - 100) * 5 / 2) + 100);
if (stats > 201) {
converted_stats -= ((stats - 201) * 5 / 4);
}
} else {
converted_stats = stats;
}

if (GetLevel() < 41) {
sta_end = (GetLevel() * 75 * ConvertedStats / 1000);
sta_end = (GetLevel() * 75 * converted_stats / 1000);
base_endurance = (GetLevel() * 15);
} else if (GetLevel() < 81) {
sta_end = ((3 * ConvertedStats) + ((GetLevel() - 40) * 15 * ConvertedStats / 100));
sta_end = ((3 * converted_stats) + ((GetLevel() - 40) * 15 * converted_stats / 100));
base_endurance = (600 + ((GetLevel() - 40) * 30));
} else {
sta_end = (9 * ConvertedStats);
sta_end = (9 * converted_stats);
base_endurance = (1800 + ((GetLevel() - 80) * 18));
}
base_end = (base_endurance + sta_end + (HeroicStats * 10));
base_end = (base_endurance + sta_end + (heroic_stats * 10));
} else {
Stats = (GetSTR()+GetSTA()+GetDEX()+GetAGI());
int LevelBase = (GetLevel() * 15);
int at_most_800 = Stats;
stats = (GetSTR() + GetSTA() + GetDEX() + GetAGI());
int level_base = (GetLevel() * 15);
int at_most_800 = stats;
if(at_most_800 > 800)
at_most_800 = 800;

Expand All @@ -7772,16 +7780,16 @@ int64 Bot::CalcBaseEndurance() {
int Bonus800plus = 0;
int HalfBonus800plus = 0;
int BonusUpto800 = int(at_most_800 / 4) ;
if(Stats > 400) {
if(stats > 400) {
Bonus400to800 = int((at_most_800 - 400) / 4);
HalfBonus400to800 = int(std::max((at_most_800 - 400), 0) / 8);
if(Stats > 800) {
Bonus800plus = (int((Stats - 800) / 8) * 2);
HalfBonus800plus = int((Stats - 800) / 16);
if(stats > 800) {
Bonus800plus = (int((stats - 800) / 8) * 2);
HalfBonus800plus = int((stats - 800) / 16);
}
}
int bonus_sum = (BonusUpto800 + Bonus400to800 + HalfBonus400to800 + Bonus800plus + HalfBonus800plus);
base_end = LevelBase;
base_end = level_base;
base_end += ((bonus_sum * 3 * GetLevel()) / 40);
}
return base_end;
Expand Down
19 changes: 14 additions & 5 deletions zone/client.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6397,7 +6397,7 @@ void Client::SendStatsWindow(Client* client, bool use_window)
if(CalcMaxMana() > 0) {
cur_name = " M: ";
cur_field = itoa(GetMana());
total_field = itoa(CalcMaxMana());
total_field = itoa(GetMaxMana());
}
else { continue; }

Expand Down Expand Up @@ -6452,20 +6452,24 @@ void Client::SendStatsWindow(Client* client, bool use_window)
regen_row_color = color_red;

base_regen_field = itoa(LevelRegen());
item_regen_field = itoa(itembonuses.HPRegen);
item_regen_field = itoa(
itembonuses.HPRegen +(GetHeroicSTA() * RuleR(Character, HeroicStaminaMultiplier) / 20));
cap_regen_field = itoa(CalcHPRegenCap());
spell_regen_field = itoa(spellbonuses.HPRegen);
aa_regen_field = itoa(aabonuses.HPRegen);
total_regen_field = itoa(CalcHPRegen(true));
break;
}
case 1: {
if(CalcMaxMana() > 0) {
if(GetMaxMana() > 0) {
regen_row_header = "M: ";
regen_row_color = color_blue;

base_regen_field = itoa(CalcBaseManaRegen());
item_regen_field = itoa(itembonuses.ManaRegen);
int32 heroic_mana_regen = (GetCasterClass() == 'W') ?
GetHeroicWIS() * RuleR(Character, HeroicWisdomMultiplier) / 25 :
GetHeroicINT() * RuleR(Character, HeroicIntelligenceMultiplier) / 25;
item_regen_field = itoa(itembonuses.ManaRegen + heroic_mana_regen);
cap_regen_field = itoa(CalcManaRegenCap());
spell_regen_field = itoa(spellbonuses.ManaRegen);
aa_regen_field = itoa(aabonuses.ManaRegen);
Expand All @@ -6479,7 +6483,12 @@ void Client::SendStatsWindow(Client* client, bool use_window)
regen_row_color = color_green;

base_regen_field = itoa(((GetLevel() * 4 / 10) + 2));
item_regen_field = itoa(itembonuses.EnduranceRegen);
double heroic_str = GetHeroicSTR() * RuleR(Character, HeroicStrengthMultiplier);
double heroic_sta = GetHeroicSTA() * RuleR(Character, HeroicStaminaMultiplier);
double heroic_dex = GetHeroicDEX() * RuleR(Character, HeroicDexterityMultiplier);
double heroic_agi = GetHeroicAGI() * RuleR(Character, HeroicAgilityMultiplier);
double heroic_stats = (heroic_str + heroic_sta + heroic_dex + heroic_agi) / 4;
item_regen_field = itoa(itembonuses.EnduranceRegen + heroic_stats);
cap_regen_field = itoa(CalcEnduranceRegenCap());
spell_regen_field = itoa(spellbonuses.EnduranceRegen);
aa_regen_field = itoa(aabonuses.EnduranceRegen);
Expand Down
29 changes: 21 additions & 8 deletions zone/client_mods.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -234,7 +234,7 @@ int32 Client::LevelRegen()
int64 Client::CalcHPRegen(bool bCombat)
{
int64 item_regen = itembonuses.HPRegen; // worn spells and +regen, already capped
item_regen += GetHeroicSTA() / 20;
item_regen += GetHeroicSTA() * RuleR(Character, HeroicStaminaMultiplier) / 20;

item_regen += aabonuses.HPRegen;

Expand Down Expand Up @@ -491,7 +491,7 @@ int64 Client::CalcBaseHP()
auto base_data = database.GetBaseData(GetLevel(), GetClass());
if (base_data) {
base_hp += base_data->base_hp + (base_data->hp_factor * stats);
base_hp += (GetHeroicSTA() * 10);
base_hp += GetHeroicSTA() * RuleR(Character, HeroicStaminaMultiplier) * 10;
}
}
else {
Expand Down Expand Up @@ -623,7 +623,9 @@ int64 Client::CalcBaseMana()
}
auto base_data = database.GetBaseData(GetLevel(), GetClass());
if (base_data) {
max_m = base_data->base_mana + (ConvertedWisInt * base_data->mana_factor) + (GetHeroicINT() * 10);
max_m = base_data->base_mana +
(ConvertedWisInt * base_data->mana_factor) +
(GetHeroicINT() * RuleR(Character, HeroicIntelligenceMultiplier) * 10);
}
}
else {
Expand Down Expand Up @@ -655,7 +657,9 @@ int64 Client::CalcBaseMana()
}
auto base_data = database.GetBaseData(GetLevel(), GetClass());
if (base_data) {
max_m = base_data->base_mana + (ConvertedWisInt * base_data->mana_factor) + (GetHeroicWIS() * 10);
max_m = base_data->base_mana +
(ConvertedWisInt * base_data->mana_factor) +
((GetHeroicWIS() * RuleR(Character, HeroicWisdomMultiplier)) * 10);
}
}
else {
Expand Down Expand Up @@ -752,10 +756,10 @@ int64 Client::CalcManaRegen(bool bCombat)

switch (GetCasterClass()) {
case 'W':
heroic_bonus = GetHeroicWIS();
heroic_bonus = GetHeroicWIS() * RuleR(Character, HeroicWisdomMultiplier);
break;
default:
heroic_bonus = GetHeroicINT();
heroic_bonus = GetHeroicINT() * RuleR(Character, HeroicIntelligenceMultiplier);
break;
}

Expand Down Expand Up @@ -1686,8 +1690,13 @@ int64 Client::CalcBaseEndurance()
{
int64 base_end = 0;
if (ClientVersion() >= EQ::versions::ClientVersion::SoF && RuleB(Character, SoDClientUseSoDHPManaEnd)) {
double heroic_stats = (GetHeroicSTR() + GetHeroicSTA() + GetHeroicDEX() + GetHeroicAGI()) / 4.0f;
double heroic_str = GetHeroicSTR() * RuleR(Character, HeroicStrengthMultiplier);
double heroic_sta = GetHeroicSTA() * RuleR(Character, HeroicStaminaMultiplier);
double heroic_dex = GetHeroicDEX() * RuleR(Character, HeroicDexterityMultiplier);
double heroic_agi = GetHeroicAGI() * RuleR(Character, HeroicAgilityMultiplier);
double heroic_stats = (heroic_str + heroic_sta + heroic_dex + heroic_agi) / 4;
double stats = (GetSTR() + GetSTA() + GetDEX() + GetAGI()) / 4.0f;

if (stats > 201.0f) {
stats = 1.25f * (stats - 201.0f) + 352.5f;
}
Expand Down Expand Up @@ -1785,7 +1794,11 @@ int64 Client::CalcEnduranceRegen(bool bCombat)
if (encumbered)
base += level / -15;

auto item_bonus = GetHeroicAGI() + GetHeroicDEX() + GetHeroicSTA() + GetHeroicSTR();
double heroic_str = GetHeroicSTR() * RuleR(Character, HeroicStrengthMultiplier);
double heroic_sta = GetHeroicSTA() * RuleR(Character, HeroicStaminaMultiplier);
double heroic_dex = GetHeroicDEX() * RuleR(Character, HeroicDexterityMultiplier);
double heroic_agi = GetHeroicAGI() * RuleR(Character, HeroicAgilityMultiplier);
int32 item_bonus = heroic_str + heroic_sta + heroic_dex + heroic_agi;
item_bonus = item_bonus / 4 / 50;
item_bonus += itembonuses.EnduranceRegen; // this is capped already
base += item_bonus;
Expand Down
18 changes: 9 additions & 9 deletions zone/tune.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1049,7 +1049,7 @@ int64 Mob::TuneACSum(bool skip_caps, int ac_override, int add_ac)
shield_ac = CalcRecommendedLevelBonus(GetLevel(), inst->GetItemRecommendedLevel(true),inst->GetItemArmorClass(true));
}
}
shield_ac += GetHeroicSTR() / 10;
shield_ac += GetHeroicSTR() * RuleR(Character, HeroicStrengthMultiplier) / 10;
}
// EQ math
ac = (ac * 4) / 3;
Expand Down Expand Up @@ -1353,7 +1353,7 @@ int64 Mob::Tunecompute_defense(int avoidance_override, int add_avoidance)
defense = avoidance_override;
}
else {
defense += GetHeroicAGI() / 10;
defense += GetHeroicAGI() * RuleR(Character, HeroicAgilityMultiplier) / 10;
}
defense += add_avoidance; //1 pt = 10 heroic agi
}
Expand Down Expand Up @@ -1493,13 +1493,13 @@ void Mob::TuneCommonOutgoingHitSuccess(Mob* defender, DamageHitInfo &hit, ExtraA
if (IsOfClientBot()) {
int extra = 0;
switch (hit.skill) {
case EQ::skills::SkillThrowing:
case EQ::skills::SkillArchery:
extra = GetHeroicDEX() / 10;
break;
default:
extra = GetHeroicSTR() / 10;
break;
case EQ::skills::SkillThrowing:
case EQ::skills::SkillArchery:
extra = GetHeroicDEX() * RuleR(Character, HeroicDexterityMultiplier) / 10;
break;
default:
extra = GetHeroicSTR() * RuleR(Character, HeroicStrengthMultiplier) / 10;
break;
}
hit.damage_done += extra;
}
Expand Down

0 comments on commit 2c90717

Please sign in to comment.