[Feature] Add Data Bucket support for scaling of Heroic Stats. (#3058)

* [Feature] Add Data Bucket support for scaling of Heroic Stats.

* update

* fixes, still reworking logic

* fixes, still reworking logic

* logic done

* logic done

* fixes

* Cleanup

* Cleanup

* Cleanup naming, verify behaviors

* formatting

* formatting

* fix issue with endurance and mana.

* update rule desc

* cleanup

* DataBucket Struct

* Cleanup data_bucket.cpp and add constants

* cleanup

* changes

* formatting

* fix from merge

* escape keyword `key`

* Add `key` to generator, run repository-generator.pl

* fix for change to key

* cleanup

* formatting

* formatting

* typo
This commit is contained in:
Aeadoin 2023-03-23 21:42:13 -04:00 committed by GitHub
parent abc27ab423
commit 59ad91a140
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
28 changed files with 433 additions and 243 deletions

View File

@ -593,4 +593,29 @@ enum class ApplySpellType {
Raid Raid
}; };
namespace HeroicBonusBucket
{
const std::string WisMaxMana = "HWIS-MaxMana";
const std::string WisManaRegen = "HWIS-ManaRegen";
const std::string WisHealAmt = "HWIS-HealAmt";
const std::string IntMaxMana = "HINT-MaxMana";
const std::string IntManaRegen = "HINT-ManaRegen";
const std::string IntSpellDmg = "HINT-SpellDmg";
const std::string StrMeleeDamage = "HSTR-MeleeDamage";
const std::string StrShieldAC = "HSTR-ShieldAC";
const std::string StrMaxEndurance = "HSTR-MaxEndurance";
const std::string StrEnduranceRegen = "HSTR-EnduranceRegen";
const std::string StaMaxHP = "HSTA-MaxHP";
const std::string StaHPRegen = "HSTA-HPRegen";
const std::string StaMaxEndurance = "HSTA-MaxEndurance";
const std::string StaEnduranceRegen = "HSTA-EnduranceRegen";
const std::string AgiAvoidance = "HAGI-Avoidance";
const std::string AgiMaxEndurance = "HAGI-MaxEndurance";
const std::string AgiEnduranceRegen = "HAGI-EnduranceRegen";
const std::string DexRangedDamage = "HDEX-RangedDamage";
const std::string DexMaxEndurance = "HDEX-MaxEndurance";
const std::string DexEnduranceRegen = "HDEX-EnduranceRegen";
}
#endif /*COMMON_EMU_CONSTANTS_H*/ #endif /*COMMON_EMU_CONSTANTS_H*/

View File

@ -16,11 +16,12 @@
#include "../../strings.h" #include "../../strings.h"
#include <ctime> #include <ctime>
class BaseDataBucketsRepository { class BaseDataBucketsRepository {
public: public:
struct DataBuckets { struct DataBuckets {
uint64_t id; uint64_t id;
std::string key; std::string key_;
std::string value; std::string value;
uint32_t expires; uint32_t expires;
}; };
@ -34,7 +35,7 @@ public:
{ {
return { return {
"id", "id",
"key", "`key`",
"value", "value",
"expires", "expires",
}; };
@ -44,7 +45,7 @@ public:
{ {
return { return {
"id", "id",
"key", "`key`",
"value", "value",
"expires", "expires",
}; };
@ -88,7 +89,7 @@ public:
DataBuckets e{}; DataBuckets e{};
e.id = 0; e.id = 0;
e.key = ""; e.key_ = "";
e.value = ""; e.value = "";
e.expires = 0; e.expires = 0;
@ -116,8 +117,9 @@ public:
{ {
auto results = db.QueryDatabase( auto results = db.QueryDatabase(
fmt::format( fmt::format(
"{} WHERE id = {} LIMIT 1", "{} WHERE {} = {} LIMIT 1",
BaseSelect(), BaseSelect(),
PrimaryKey(),
data_buckets_id data_buckets_id
) )
); );
@ -127,7 +129,7 @@ public:
DataBuckets e{}; DataBuckets e{};
e.id = strtoull(row[0], nullptr, 10); e.id = strtoull(row[0], nullptr, 10);
e.key = row[1] ? row[1] : ""; e.key_ = row[1] ? row[1] : "";
e.value = row[2] ? row[2] : ""; e.value = row[2] ? row[2] : "";
e.expires = static_cast<uint32_t>(strtoul(row[3], nullptr, 10)); e.expires = static_cast<uint32_t>(strtoul(row[3], nullptr, 10));
@ -163,7 +165,7 @@ public:
auto columns = Columns(); auto columns = Columns();
v.push_back(columns[1] + " = '" + Strings::Escape(e.key) + "'"); v.push_back(columns[1] + " = '" + Strings::Escape(e.key_) + "'");
v.push_back(columns[2] + " = '" + Strings::Escape(e.value) + "'"); v.push_back(columns[2] + " = '" + Strings::Escape(e.value) + "'");
v.push_back(columns[3] + " = " + std::to_string(e.expires)); v.push_back(columns[3] + " = " + std::to_string(e.expires));
@ -188,7 +190,7 @@ public:
std::vector<std::string> v; std::vector<std::string> v;
v.push_back(std::to_string(e.id)); v.push_back(std::to_string(e.id));
v.push_back("'" + Strings::Escape(e.key) + "'"); v.push_back("'" + Strings::Escape(e.key_) + "'");
v.push_back("'" + Strings::Escape(e.value) + "'"); v.push_back("'" + Strings::Escape(e.value) + "'");
v.push_back(std::to_string(e.expires)); v.push_back(std::to_string(e.expires));
@ -221,7 +223,7 @@ public:
std::vector<std::string> v; std::vector<std::string> v;
v.push_back(std::to_string(e.id)); v.push_back(std::to_string(e.id));
v.push_back("'" + Strings::Escape(e.key) + "'"); v.push_back("'" + Strings::Escape(e.key_) + "'");
v.push_back("'" + Strings::Escape(e.value) + "'"); v.push_back("'" + Strings::Escape(e.value) + "'");
v.push_back(std::to_string(e.expires)); v.push_back(std::to_string(e.expires));
@ -258,7 +260,7 @@ public:
DataBuckets e{}; DataBuckets e{};
e.id = strtoull(row[0], nullptr, 10); e.id = strtoull(row[0], nullptr, 10);
e.key = row[1] ? row[1] : ""; e.key_ = row[1] ? row[1] : "";
e.value = row[2] ? row[2] : ""; e.value = row[2] ? row[2] : "";
e.expires = static_cast<uint32_t>(strtoul(row[3], nullptr, 10)); e.expires = static_cast<uint32_t>(strtoul(row[3], nullptr, 10));
@ -286,7 +288,7 @@ public:
DataBuckets e{}; DataBuckets e{};
e.id = strtoull(row[0], nullptr, 10); e.id = strtoull(row[0], nullptr, 10);
e.key = row[1] ? row[1] : ""; e.key_ = row[1] ? row[1] : "";
e.value = row[2] ? row[2] : ""; e.value = row[2] ? row[2] : "";
e.expires = static_cast<uint32_t>(strtoul(row[3], nullptr, 10)); e.expires = static_cast<uint32_t>(strtoul(row[3], nullptr, 10));

View File

@ -93,6 +93,9 @@ 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, 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, HasteCap, 100, "Haste cap for non-v3(over haste) haste")
RULE_INT(Character, Hastev3Cap, 25, "Haste cap for v3(over haste) haste") RULE_INT(Character, Hastev3Cap, 25, "Haste cap for v3(over haste) haste")
RULE_BOOL(Character, HeroicStatsUseDataBucketsToScale, false, "Allows scaling the benefits a character receives from Heroic Stats using Data Buckets. Stacks with other Heroic Stats Mulitplier Rules.")
RULE_REAL(Character, HeroicIntelligenceIncreaseSpellDmgMultiplier, 0.00, "Allows Heroic Intelligence to increase a Players Worn Spell Damage Stat from Equipment, for example, setting this rule to 1.00 will always grant 1 Spell Damage per 1 Heroic Intelligence")
RULE_REAL(Character, HeroicWisdomIncreaseHealAmtMultiplier, 0.00, "Allows Heroic Wisdom to increase a Players Worn Heal Amount Stat from Equipment, for example, setting this rule to 1.00 will always grant 1 Heal Amount per 1 Heroic Wisdom")
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, 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, 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, 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)")

View File

@ -585,7 +585,8 @@ sub translate_mysql_data_type_to_c {
sub get_reserved_cpp_variable_names { sub get_reserved_cpp_variable_names {
return ( return (
"class", "class",
"int" "int",
"key"
); );
} }

View File

@ -250,7 +250,7 @@ int Mob::compute_defense()
int defense = GetSkill(EQ::skills::SkillDefense) * 400 / 225; int defense = GetSkill(EQ::skills::SkillDefense) * 400 / 225;
defense += (8000 * (GetAGI() - 40)) / 36000; defense += (8000 * (GetAGI() - 40)) / 36000;
if (IsOfClientBot()) { if (IsOfClientBot()) {
defense += GetHeroicAGI() * RuleR(Character, HeroicAgilityMultiplier) / 10; defense += itembonuses.heroic_agi_avoidance;
} }
//516 SE_AC_Mitigation_Max_Percent //516 SE_AC_Mitigation_Max_Percent
@ -883,7 +883,7 @@ int Mob::ACSum(bool skip_caps)
shield_ac = CalcRecommendedLevelBonus(GetLevel(), inst->GetItemRecommendedLevel(true), inst->GetItemArmorClass(true)); shield_ac = CalcRecommendedLevelBonus(GetLevel(), inst->GetItemRecommendedLevel(true), inst->GetItemArmorClass(true));
} }
} }
shield_ac += GetHeroicSTR() * RuleR(Character, HeroicStrengthMultiplier) / 10; shield_ac += itembonuses.heroic_str_shield_ac;
} }
// EQ math // EQ math
ac = (ac * 4) / 3; ac = (ac * 4) / 3;
@ -5909,10 +5909,10 @@ void Mob::CommonOutgoingHitSuccess(Mob* defender, DamageHitInfo &hit, ExtraAttac
switch (hit.skill) { switch (hit.skill) {
case EQ::skills::SkillThrowing: case EQ::skills::SkillThrowing:
case EQ::skills::SkillArchery: case EQ::skills::SkillArchery:
extra = GetHeroicDEX() * RuleR(Character, HeroicDexterityMultiplier) / 10; extra = itembonuses.heroic_dex_ranged_damage;
break; break;
default: default:
extra = GetHeroicSTR() * RuleR(Character, HeroicStrengthMultiplier) / 10; extra = itembonuses.heroic_str_melee_damage;
break; break;
} }
hit.damage_done += extra; hit.damage_done += extra;

View File

@ -77,6 +77,7 @@ void Client::CalcBonuses()
{ {
memset(&itembonuses, 0, sizeof(StatBonuses)); memset(&itembonuses, 0, sizeof(StatBonuses));
CalcItemBonuses(&itembonuses); CalcItemBonuses(&itembonuses);
CalcHeroicBonuses(&itembonuses);
CalcEdibleBonuses(&itembonuses); CalcEdibleBonuses(&itembonuses);
CalcSpellBonuses(&spellbonuses); CalcSpellBonuses(&spellbonuses);
CalcAABonuses(&aabonuses); CalcAABonuses(&aabonuses);
@ -204,7 +205,7 @@ void Client::CalcItemBonuses(StatBonuses* newbon) {
} }
// These item stat caps depend on spells/AAs so we process them after those are processed // These item stat caps depend on spells/AAs so we process them after those are processed
void Client::ProcessItemCaps() void Mob::ProcessItemCaps()
{ {
itembonuses.HPRegen = std::min(itembonuses.HPRegen, CalcHPRegenCap()); itembonuses.HPRegen = std::min(itembonuses.HPRegen, CalcHPRegenCap());
itembonuses.ManaRegen = std::min(itembonuses.ManaRegen, CalcManaRegenCap()); itembonuses.ManaRegen = std::min(itembonuses.ManaRegen, CalcManaRegenCap());
@ -219,6 +220,15 @@ void Client::ProcessItemCaps()
} }
itembonuses.ATK = std::min(itembonuses.ATK, CalcItemATKCap()); itembonuses.ATK = std::min(itembonuses.ATK, CalcItemATKCap());
if (itembonuses.SpellDmg > RuleI(Character, ItemSpellDmgCap)) {
itembonuses.SpellDmg = RuleI(Character, ItemSpellDmgCap);
}
if (itembonuses.HealAmt > RuleI(Character, ItemHealAmtCap)) {
itembonuses.HealAmt = RuleI(Character, ItemHealAmtCap);
}
} }
void Client::AddItemBonuses(const EQ::ItemInstance *inst, StatBonuses *newbon, bool isAug, bool isTribute, int rec_override, bool ammo_slot_item) void Client::AddItemBonuses(const EQ::ItemInstance *inst, StatBonuses *newbon, bool isAug, bool isTribute, int rec_override, bool ammo_slot_item)
@ -5634,3 +5644,134 @@ void Mob::NegateSpellEffectBonuses(uint16 spell_id)
} }
} }
} }
void Mob::CalcHeroicBonuses(StatBonuses* newbon)
{
if (GetHeroicSTR()) {
SetHeroicStrBonuses(newbon);
}
if (GetHeroicSTA()) {
SetHeroicStaBonuses(newbon);
}
if (GetHeroicAGI()) {
SetHeroicAgiBonuses(newbon);
}
if (GetHeroicDEX()) {
SetHeroicDexBonuses(newbon);
}
if (GetHeroicINT()) {
SetHeroicIntBonuses(newbon);
}
if (GetHeroicWIS()) {
SetHeroicWisBonuses(newbon);
}
}
void Mob::SetHeroicWisBonuses(StatBonuses* n)
{
n->heroic_max_mana += IsWISCasterClass(GetClass()) ? GetHeroicWIS() * RuleR(Character, HeroicWisdomMultiplier) * 10 : 0;
n->heroic_mana_regen += IsWISCasterClass(GetClass()) ? GetHeroicWIS() * RuleR(Character, HeroicWisdomMultiplier) / 25 : 0;
n->HealAmt += GetHeroicWIS() * RuleR(Character, HeroicWisdomIncreaseHealAmtMultiplier);
if (RuleB(Character, HeroicStatsUseDataBucketsToScale)) {
n->heroic_max_mana += IsWISCasterClass(GetClass()) ? GetHeroicWIS() * CheckHeroicBonusesDataBuckets(HeroicBonusBucket::WisMaxMana) * 10 : 0;
n->heroic_mana_regen += IsWISCasterClass(GetClass()) ? GetHeroicWIS() * CheckHeroicBonusesDataBuckets(HeroicBonusBucket::WisManaRegen) / 25 : 0;
n->HealAmt += GetHeroicWIS() * CheckHeroicBonusesDataBuckets(HeroicBonusBucket::WisHealAmt);
}
}
void Mob::SetHeroicIntBonuses(StatBonuses* n)
{
n->heroic_max_mana += IsINTCasterClass(GetClass()) ? GetHeroicINT() * RuleR(Character, HeroicIntelligenceMultiplier) * 10 : 0;
n->heroic_mana_regen += IsINTCasterClass(GetClass()) ? GetHeroicINT() * RuleR(Character, HeroicIntelligenceMultiplier) / 25 : 0;
n->SpellDmg += GetHeroicINT() * RuleR(Character, HeroicIntelligenceIncreaseSpellDmgMultiplier);
if (RuleB(Character, HeroicStatsUseDataBucketsToScale)) {
n->heroic_max_mana += IsINTCasterClass(GetClass()) ? GetHeroicINT() * CheckHeroicBonusesDataBuckets(HeroicBonusBucket::IntMaxMana) * 10 : 0;
n->heroic_mana_regen += IsINTCasterClass(GetClass()) ? GetHeroicINT() * CheckHeroicBonusesDataBuckets(HeroicBonusBucket::IntManaRegen) / 25 : 0;
n->SpellDmg += GetHeroicINT() * CheckHeroicBonusesDataBuckets(HeroicBonusBucket::IntSpellDmg);
}
}
void Mob::SetHeroicDexBonuses(StatBonuses* n)
{
n->heroic_dex_ranged_damage += GetHeroicDEX() * RuleR(Character, HeroicDexterityMultiplier) / 10;
n->heroic_max_end += GetHeroicDEX() * RuleR(Character, HeroicDexterityMultiplier) / 4 * 10.0f;
n->heroic_end_regen += GetHeroicDEX() * RuleR(Character, HeroicDexterityMultiplier) / 4 / 50;
if (RuleB(Character, HeroicStatsUseDataBucketsToScale)) {
n->heroic_dex_ranged_damage += GetHeroicDEX() * CheckHeroicBonusesDataBuckets(HeroicBonusBucket::DexRangedDamage) / 10;
n->heroic_max_end += GetHeroicDEX() * CheckHeroicBonusesDataBuckets(HeroicBonusBucket::DexMaxEndurance) / 4 * 10.0f;
n->heroic_end_regen += GetHeroicDEX() * CheckHeroicBonusesDataBuckets(HeroicBonusBucket::DexEnduranceRegen) / 4 / 50;
}
}
void Mob::SetHeroicAgiBonuses(StatBonuses* n)
{
n->heroic_agi_avoidance += GetHeroicAGI() * RuleR(Character, HeroicAgilityMultiplier) / 10;
n->heroic_max_end += GetHeroicAGI() * RuleR(Character, HeroicAgilityMultiplier) / 4 * 10.0f;
n->heroic_end_regen += GetHeroicAGI() * RuleR(Character, HeroicAgilityMultiplier) / 4 / 50;
if (RuleB(Character, HeroicStatsUseDataBucketsToScale)) {
n->heroic_agi_avoidance += GetHeroicAGI() * CheckHeroicBonusesDataBuckets(HeroicBonusBucket::AgiAvoidance) / 10;
n->heroic_max_end += GetHeroicAGI() * CheckHeroicBonusesDataBuckets(HeroicBonusBucket::AgiMaxEndurance) / 4 * 10.0f;
n->heroic_end_regen += GetHeroicAGI() * CheckHeroicBonusesDataBuckets(HeroicBonusBucket::AgiEnduranceRegen) / 4 / 50;
}
}
void Mob::SetHeroicStaBonuses(StatBonuses* n)
{
n->heroic_max_hp += GetHeroicSTA() * RuleR(Character, HeroicStaminaMultiplier) * 10;
n->heroic_hp_regen += GetHeroicSTA() * RuleR(Character, HeroicStaminaMultiplier) / 20;
n->heroic_max_end += GetHeroicSTA() * RuleR(Character, HeroicStaminaMultiplier) / 4 * 10.0f;
n->heroic_end_regen += GetHeroicSTA() * RuleR(Character, HeroicStaminaMultiplier) / 4 / 50;
if (RuleB(Character, HeroicStatsUseDataBucketsToScale)) {
n->heroic_max_hp += GetHeroicSTA() * CheckHeroicBonusesDataBuckets(HeroicBonusBucket::StaMaxHP) * 10;
n->heroic_hp_regen += GetHeroicSTA() * CheckHeroicBonusesDataBuckets(HeroicBonusBucket::StaHPRegen) / 20;
n->heroic_max_end += GetHeroicSTA() * CheckHeroicBonusesDataBuckets(HeroicBonusBucket::StaMaxEndurance) / 4 * 10.0f;
n->heroic_end_regen += GetHeroicSTA() * CheckHeroicBonusesDataBuckets(HeroicBonusBucket::StaEnduranceRegen) / 4 / 50;
}
}
void Mob::SetHeroicStrBonuses(StatBonuses* n)
{
n->heroic_str_shield_ac += GetHeroicSTR() * RuleR(Character, HeroicStrengthMultiplier) / 10;
n->heroic_str_melee_damage += GetHeroicSTR() * RuleR(Character, HeroicStrengthMultiplier) / 10;
n->heroic_max_end += GetHeroicSTR() * RuleR(Character, HeroicStrengthMultiplier) / 4 * 10.0f;
n->heroic_end_regen += GetHeroicSTR() * RuleR(Character, HeroicStrengthMultiplier) / 4 / 50;
if (RuleB(Character, HeroicStatsUseDataBucketsToScale)) {
n->heroic_str_shield_ac += GetHeroicSTR() * CheckHeroicBonusesDataBuckets(HeroicBonusBucket::StrShieldAC) / 10;
n->heroic_str_melee_damage += GetHeroicSTR() * CheckHeroicBonusesDataBuckets(HeroicBonusBucket::StrMeleeDamage) / 10;
n->heroic_max_end += GetHeroicSTR() * CheckHeroicBonusesDataBuckets(HeroicBonusBucket::StrMaxEndurance) / 4 * 10.0f;
n->heroic_end_regen += GetHeroicSTR() * CheckHeroicBonusesDataBuckets(HeroicBonusBucket::StrEnduranceRegen) / 4 / 50;
}
}
float Mob::CheckHeroicBonusesDataBuckets(std::string bucket_name)
{
std::string bucket_value;
if (!bucket_name.empty()) {
const auto full_name = fmt::format(
"{}-{}",
GetBucketKey(),
bucket_name
);
if (IsOfClientBot()) {
bucket_value = DataBucket::CheckBucketKey(this, full_name);
}
if (bucket_value.empty() || !Strings::IsNumber(bucket_value)) {
return 0.00f;
}
}
return Strings::ToFloat(bucket_value);
}

View File

@ -3284,8 +3284,7 @@ bool Bot::Spawn(Client* botCharacterOwner) {
m_targetable = true; m_targetable = true;
entity_list.AddBot(this, true, true); entity_list.AddBot(this, true, true);
GetBotOwnerDataBuckets(); DataBucket::GetDataBuckets(this);
GetBotDataBuckets();
LoadBotSpellSettings(); LoadBotSpellSettings();
if (!AI_AddBotSpells(GetBotSpellID())) { if (!AI_AddBotSpells(GetBotSpellID())) {
GetBotOwner()->CastToClient()->Message( GetBotOwner()->CastToClient()->Message(
@ -5461,10 +5460,10 @@ int64 Bot::CalcMaxMana() {
switch(GetCasterClass()) { switch(GetCasterClass()) {
case 'I': case 'I':
max_mana = (GenerateBaseManaPoints() + itembonuses.Mana + spellbonuses.Mana + GroupLeadershipAAManaEnhancement()); max_mana = (GenerateBaseManaPoints() + itembonuses.Mana + spellbonuses.Mana + GroupLeadershipAAManaEnhancement());
max_mana += (GetHeroicINT() * 10); max_mana += itembonuses.heroic_max_mana;
case 'W': { case 'W': {
max_mana = (GenerateBaseManaPoints() + itembonuses.Mana + spellbonuses.Mana + GroupLeadershipAAManaEnhancement()); max_mana = (GenerateBaseManaPoints() + itembonuses.Mana + spellbonuses.Mana + GroupLeadershipAAManaEnhancement());
max_mana += (GetHeroicWIS() * 10); max_mana += itembonuses.heroic_max_mana;
break; break;
} }
case 'N': { case 'N': {
@ -6001,11 +6000,13 @@ void Bot::CalcBonuses() {
memset(&itembonuses, 0, sizeof(StatBonuses)); memset(&itembonuses, 0, sizeof(StatBonuses));
GenerateBaseStats(); GenerateBaseStats();
CalcItemBonuses(&itembonuses); CalcItemBonuses(&itembonuses);
CalcHeroicBonuses(&itembonuses);
CalcSpellBonuses(&spellbonuses); CalcSpellBonuses(&spellbonuses);
CalcAABonuses(&aabonuses); CalcAABonuses(&aabonuses);
SetAttackTimer(); SetAttackTimer();
CalcSeeInvisibleLevel(); CalcSeeInvisibleLevel();
CalcInvisibleLevel(); CalcInvisibleLevel();
ProcessItemCaps();
CalcATK(); CalcATK();
CalcSTR(); CalcSTR();
CalcSTA(); CalcSTA();
@ -6037,15 +6038,7 @@ int64 Bot::CalcHPRegenCap() {
} }
int64 Bot::CalcManaRegenCap() { int64 Bot::CalcManaRegenCap() {
int64 cap = RuleI(Character, ItemManaRegenCap) + aabonuses.ItemManaRegenCap; int64 cap = RuleI(Character, ItemManaRegenCap) + aabonuses.ItemManaRegenCap + itembonuses.heroic_mana_regen;
switch(GetCasterClass()) {
case 'I':
cap += itembonuses.HeroicINT * RuleR(Character, HeroicIntelligenceMultiplier) / 25;
break;
case 'W':
cap += itembonuses.HeroicWIS * RuleR(Character, HeroicWisdomMultiplier) / 25;
break;
}
return (cap * RuleI(Character, ManaRegenMultiplier) / 100); return (cap * RuleI(Character, ManaRegenMultiplier) / 100);
} }
@ -6435,7 +6428,7 @@ int32 Bot::LevelRegen() {
int64 Bot::CalcHPRegen() { int64 Bot::CalcHPRegen() {
int32 regen = (LevelRegen() + itembonuses.HPRegen + spellbonuses.HPRegen); int32 regen = (LevelRegen() + itembonuses.HPRegen + spellbonuses.HPRegen);
regen += GetHeroicSTA() * RuleR(Character, HeroicStaminaMultiplier) / 20; regen += itembonuses.heroic_hp_regen;
regen += (aabonuses.HPRegen + GroupLeadershipAAHealthRegeneration()); regen += (aabonuses.HPRegen + GroupLeadershipAAHealthRegeneration());
regen = ((regen * RuleI(Character, HPRegenMultiplier)) / 100); regen = ((regen * RuleI(Character, HPRegenMultiplier)) / 100);
@ -6456,15 +6449,8 @@ int64 Bot::CalcManaRegen() {
} else } else
regen = (2 + spellbonuses.ManaRegen + itembonuses.ManaRegen); regen = (2 + spellbonuses.ManaRegen + itembonuses.ManaRegen);
if(GetCasterClass() == 'I') regen += aabonuses.ManaRegen + itembonuses.heroic_mana_regen;
regen += itembonuses.HeroicINT * RuleR(Character, HeroicIntelligenceMultiplier) / 25;
else if(GetCasterClass() == 'W')
regen += itembonuses.HeroicWIS * RuleR(Character, HeroicWisdomMultiplier) / 25;
else
regen = 0;
regen += aabonuses.ManaRegen;
regen = ((regen * RuleI(Character, ManaRegenMultiplier)) / 100); regen = ((regen * RuleI(Character, ManaRegenMultiplier)) / 100);
float mana_regen_rate = RuleR(Bots, ManaRegen); float mana_regen_rate = RuleR(Bots, ManaRegen);
if (mana_regen_rate < 0.0f) if (mana_regen_rate < 0.0f)
@ -6509,7 +6495,7 @@ int64 Bot::CalcMaxHP() {
int32 bot_hp = 0; int32 bot_hp = 0;
uint32 nd = 10000; uint32 nd = 10000;
bot_hp += (GenerateBaseHitPoints() + itembonuses.HP); bot_hp += (GenerateBaseHitPoints() + itembonuses.HP);
bot_hp += GetHeroicSTA() * RuleR(Character, HeroicStaminaMultiplier) * 10; bot_hp += itembonuses.heroic_max_hp;
nd += aabonuses.MaxHP; nd += aabonuses.MaxHP;
bot_hp = ((float)bot_hp * (float)nd / (float)10000); bot_hp = ((float)bot_hp * (float)nd / (float)10000);
bot_hp += (spellbonuses.HP + aabonuses.HP); bot_hp += (spellbonuses.HP + aabonuses.HP);
@ -6553,13 +6539,7 @@ int64 Bot::CalcBaseEndurance() {
int32 sta_end = 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)) { if (GetOwner() && GetOwner()->CastToClient() && GetOwner()->CastToClient()->ClientVersion() >= EQ::versions::ClientVersion::SoD && RuleB(Character, SoDClientUseSoDHPManaEnd)) {
double heroic_stats = 0;
stats = ((GetSTR() + GetSTA() + GetDEX() + GetAGI()) / 4); 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) { if (stats > 100) {
converted_stats = (((stats - 100) * 5 / 2) + 100); converted_stats = (((stats - 100) * 5 / 2) + 100);
@ -6580,7 +6560,7 @@ int64 Bot::CalcBaseEndurance() {
sta_end = (9 * converted_stats); sta_end = (9 * converted_stats);
base_endurance = (1800 + ((GetLevel() - 80) * 18)); base_endurance = (1800 + ((GetLevel() - 80) * 18));
} }
base_end = (base_endurance + sta_end + (heroic_stats * 10)); base_end = base_endurance + sta_end + itembonuses.heroic_max_end;
} else { } else {
stats = (GetSTR() + GetSTA() + GetDEX() + GetAGI()); stats = (GetSTR() + GetSTA() + GetDEX() + GetAGI());
@ -6593,7 +6573,8 @@ int64 Bot::CalcBaseEndurance() {
int HalfBonus400to800 = 0; int HalfBonus400to800 = 0;
int Bonus800plus = 0; int Bonus800plus = 0;
int HalfBonus800plus = 0; int HalfBonus800plus = 0;
int BonusUpto800 = int(at_most_800 / 4) ;
auto BonusUpto800 = int(at_most_800 / 4) ;
if(stats > 400) { if(stats > 400) {
Bonus400to800 = int((at_most_800 - 400) / 4); Bonus400to800 = int((at_most_800 - 400) / 4);
@ -8768,62 +8749,7 @@ void Bot::OwnerMessage(const std::string& message)
); );
} }
bool Bot::GetBotOwnerDataBuckets() bool Bot::CheckDataBucket(std::string bucket_name, const std::string& bucket_value, uint8 bucket_comparison)
{
auto bot_owner = GetBotOwner();
if (!bot_owner) {
return false;
}
const auto query = fmt::format(
"SELECT `key`, `value` FROM data_buckets WHERE `key` LIKE '{}-%'",
Strings::Escape(bot_owner->GetBucketKey())
);
auto results = database.QueryDatabase(query);
if (!results.Success()) {
return false;
}
bot_owner_data_buckets.clear();
if (!results.RowCount()) {
return true;
}
for (auto row : results) {
bot_owner_data_buckets.insert(std::pair<std::string,std::string>(row[0], row[1]));
}
return true;
}
bool Bot::GetBotDataBuckets()
{
const auto query = fmt::format(
"SELECT `key`, `value` FROM data_buckets WHERE `key` LIKE '{}-%'",
Strings::Escape(GetBucketKey())
);
auto results = database.QueryDatabase(query);
if (!results.Success()) {
return false;
}
bot_data_buckets.clear();
if (!results.RowCount()) {
return true;
}
for (auto row : results) {
bot_data_buckets.insert(std::pair<std::string,std::string>(row[0], row[1]));
}
return true;
}
bool Bot::CheckDataBucket(const std::string& bucket_name, const std::string& bucket_value, uint8 bucket_comparison)
{ {
if (!bucket_name.empty() && !bucket_value.empty()) { if (!bucket_name.empty() && !bucket_value.empty()) {
auto full_name = fmt::format( auto full_name = fmt::format(
@ -8832,7 +8758,7 @@ bool Bot::CheckDataBucket(const std::string& bucket_name, const std::string& buc
bucket_name bucket_name
); );
auto player_value = bot_data_buckets[full_name]; auto player_value = DataBucket::CheckBucketKey(this, full_name);
if (player_value.empty() && GetBotOwner()) { if (player_value.empty() && GetBotOwner()) {
full_name = fmt::format( full_name = fmt::format(
"{}-{}", "{}-{}",
@ -8840,13 +8766,13 @@ bool Bot::CheckDataBucket(const std::string& bucket_name, const std::string& buc
bucket_name bucket_name
); );
player_value = bot_owner_data_buckets[full_name]; player_value = DataBucket::CheckBucketKey(GetBotOwner(), full_name);
if (player_value.empty()) { if (player_value.empty()) {
return false; return false;
} }
} }
if (zone->CheckDataBucket(bucket_comparison, bucket_value, player_value)) { if (zone->CompareDataBucket(bucket_comparison, bucket_value, player_value)) {
return true; return true;
} }
} }
@ -9351,6 +9277,12 @@ float Bot::GetBotCasterMaxRange(float melee_distance_max) {// Calculate caster d
return caster_distance_max; return caster_distance_max;
} }
int32 Bot::CalcItemATKCap()
{
return RuleI(Character, ItemATKCap) + itembonuses.ItemATKCap + spellbonuses.ItemATKCap + aabonuses.ItemATKCap;
}
bool Bot::CheckSpawnConditions(Client* c) { bool Bot::CheckSpawnConditions(Client* c) {
if (c->GetFeigned()) { if (c->GetFeigned()) {
@ -9386,5 +9318,4 @@ bool Bot::CheckSpawnConditions(Client* c) {
return true; return true;
} }
uint8 Bot::spell_casting_chances[SPELL_TYPE_COUNT][PLAYER_CLASS_COUNT][EQ::constants::STANCE_TYPE_COUNT][cntHSND] = { 0 }; uint8 Bot::spell_casting_chances[SPELL_TYPE_COUNT][PLAYER_CLASS_COUNT][EQ::constants::STANCE_TYPE_COUNT][cntHSND] = { 0 };

View File

@ -269,8 +269,8 @@ public:
int32 CalcPR(); int32 CalcPR();
int32 CalcCR(); int32 CalcCR();
int32 CalcCorrup(); int32 CalcCorrup();
int64 CalcHPRegenCap(); int64 CalcHPRegenCap() final;
int64 CalcManaRegenCap(); int64 CalcManaRegenCap() final;
int32 LevelRegen(); int32 LevelRegen();
int64 CalcHPRegen(); int64 CalcHPRegen();
int64 CalcManaRegen(); int64 CalcManaRegen();
@ -280,6 +280,7 @@ public:
int GroupLeadershipAAHealthRegeneration(); int GroupLeadershipAAHealthRegeneration();
int GroupLeadershipAAOffenseEnhancement(); int GroupLeadershipAAOffenseEnhancement();
void CalcRestState(); void CalcRestState();
int64 CalcMaxEndurance(); int64 CalcMaxEndurance();
int64 CalcBaseEndurance(); int64 CalcBaseEndurance();
int64 CalcEnduranceRegen(); int64 CalcEnduranceRegen();
@ -382,9 +383,7 @@ public:
[[nodiscard]] int GetMaxDiscSlots() const final { return EQ::spells::DISC_BUFFS; } [[nodiscard]] int GetMaxDiscSlots() const final { return EQ::spells::DISC_BUFFS; }
[[nodiscard]] int GetMaxTotalSlots() const final { return EQ::spells::TOTAL_BUFFS; } [[nodiscard]] int GetMaxTotalSlots() const final { return EQ::spells::TOTAL_BUFFS; }
bool GetBotOwnerDataBuckets(); bool CheckDataBucket(std::string bucket_name, const std::string& bucket_value, uint8 bucket_comparison);
bool GetBotDataBuckets();
bool CheckDataBucket(const std::string& bucket_name, const std::string& bucket_value, uint8 bucket_comparison);
// Bot Equipment & Inventory Class Methods // Bot Equipment & Inventory Class Methods
void BotTradeAddItem(const EQ::ItemInstance* inst, uint16 slot_id, std::string* error_message, bool save_to_database = true); void BotTradeAddItem(const EQ::ItemInstance* inst, uint16 slot_id, std::string* error_message, bool save_to_database = true);
@ -571,8 +570,8 @@ public:
inline virtual int32 GetCombatEffects() const { return itembonuses.ProcChance; } inline virtual int32 GetCombatEffects() const { return itembonuses.ProcChance; }
inline virtual int32 GetDS() const { return itembonuses.DamageShield; } inline virtual int32 GetDS() const { return itembonuses.DamageShield; }
// Mod3 // Mod3
inline virtual int32 GetHealAmt() const { return itembonuses.HealAmt; } inline int32 GetHealAmt() const override { return itembonuses.HealAmt; }
inline virtual int32 GetSpellDmg() const { return itembonuses.SpellDmg; } inline int32 GetSpellDmg() const override { return itembonuses.SpellDmg; }
inline virtual int32 GetClair() const { return itembonuses.Clairvoyance; } inline virtual int32 GetClair() const { return itembonuses.Clairvoyance; }
inline virtual int32 GetDSMit() const { return itembonuses.DSMitigation; } inline virtual int32 GetDSMit() const { return itembonuses.DSMitigation; }
@ -883,8 +882,6 @@ private:
eStandingPetOrder m_previous_pet_order; eStandingPetOrder m_previous_pet_order;
uint32 m_bot_caster_range; uint32 m_bot_caster_range;
BotCastingRoles m_CastingRoles; BotCastingRoles m_CastingRoles;
std::map<std::string,std::string> bot_data_buckets;
std::map<std::string,std::string> bot_owner_data_buckets;
std::map<uint16, BotSpellSetting> bot_spell_settings; std::map<uint16, BotSpellSetting> bot_spell_settings;
@ -937,6 +934,10 @@ private:
bool LoadPet(); // Load and spawn bot pet if there is one bool LoadPet(); // Load and spawn bot pet if there is one
bool SavePet(); // Save and depop bot pet if there is one bool SavePet(); // Save and depop bot pet if there is one
bool DeletePet(); bool DeletePet();
public:
int32 CalcItemATKCap() final;
}; };
bool IsSpellInBotList(DBbotspells_Struct* spell_list, uint16 iSpellID); bool IsSpellInBotList(DBbotspells_Struct* spell_list, uint16 iSpellID);

View File

@ -6437,8 +6437,7 @@ void Client::SendStatsWindow(Client* client, bool use_window)
regen_row_color = color_red; regen_row_color = color_red;
base_regen_field = itoa(LevelRegen()); base_regen_field = itoa(LevelRegen());
item_regen_field = itoa( item_regen_field = itoa(itembonuses.HPRegen + itembonuses.heroic_hp_regen);
itembonuses.HPRegen +(GetHeroicSTA() * RuleR(Character, HeroicStaminaMultiplier) / 20));
cap_regen_field = itoa(CalcHPRegenCap()); cap_regen_field = itoa(CalcHPRegenCap());
spell_regen_field = itoa(spellbonuses.HPRegen); spell_regen_field = itoa(spellbonuses.HPRegen);
aa_regen_field = itoa(aabonuses.HPRegen); aa_regen_field = itoa(aabonuses.HPRegen);
@ -6451,9 +6450,7 @@ void Client::SendStatsWindow(Client* client, bool use_window)
regen_row_color = color_blue; regen_row_color = color_blue;
base_regen_field = itoa(CalcBaseManaRegen()); base_regen_field = itoa(CalcBaseManaRegen());
int32 heroic_mana_regen = (GetCasterClass() == 'W') ? int32 heroic_mana_regen = itembonuses.heroic_mana_regen;
GetHeroicWIS() * RuleR(Character, HeroicWisdomMultiplier) / 25 :
GetHeroicINT() * RuleR(Character, HeroicIntelligenceMultiplier) / 25;
item_regen_field = itoa(itembonuses.ManaRegen + heroic_mana_regen); item_regen_field = itoa(itembonuses.ManaRegen + heroic_mana_regen);
cap_regen_field = itoa(CalcManaRegenCap()); cap_regen_field = itoa(CalcManaRegenCap());
spell_regen_field = itoa(spellbonuses.ManaRegen); spell_regen_field = itoa(spellbonuses.ManaRegen);
@ -6468,12 +6465,7 @@ void Client::SendStatsWindow(Client* client, bool use_window)
regen_row_color = color_green; regen_row_color = color_green;
base_regen_field = itoa(((GetLevel() * 4 / 10) + 2)); base_regen_field = itoa(((GetLevel() * 4 / 10) + 2));
double heroic_str = GetHeroicSTR() * RuleR(Character, HeroicStrengthMultiplier); item_regen_field = itoa(itembonuses.EnduranceRegen + itembonuses.heroic_end_regen);
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()); cap_regen_field = itoa(CalcEnduranceRegenCap());
spell_regen_field = itoa(spellbonuses.EnduranceRegen); spell_regen_field = itoa(spellbonuses.EnduranceRegen);
aa_regen_field = itoa(aabonuses.EnduranceRegen); aa_regen_field = itoa(aabonuses.EnduranceRegen);
@ -11584,27 +11576,6 @@ void Client::SendReloadCommandMessages() {
SendChatLineBreak(); SendChatLineBreak();
} }
std::map<std::string,std::string> Client::GetMerchantDataBuckets()
{
std::map<std::string,std::string> merchant_data_buckets;
auto query = fmt::format(
"SELECT `key`, `value` FROM data_buckets WHERE `key` LIKE '{}-%'",
Strings::Escape(GetBucketKey())
);
auto results = database.QueryDatabase(query);
if (!results.Success() || !results.RowCount()) {
return merchant_data_buckets;
}
for (auto row : results) {
merchant_data_buckets.insert(std::pair<std::string,std::string>(row[0], row[1]));
}
return merchant_data_buckets;
}
void Client::Undye() void Client::Undye()
{ {
for (uint8 slot = EQ::textures::textureBegin; slot <= EQ::textures::LastTexture; slot++) { for (uint8 slot = EQ::textures::textureBegin; slot <= EQ::textures::LastTexture; slot++) {
@ -12215,4 +12186,4 @@ void Client::PlayerTradeEventLog(Trade *t, Trade *t2)
RecordPlayerEventLogWithClient(trader, PlayerEvent::TRADE, e); RecordPlayerEventLogWithClient(trader, PlayerEvent::TRADE, e);
RecordPlayerEventLogWithClient(trader2, PlayerEvent::TRADE, e); RecordPlayerEventLogWithClient(trader2, PlayerEvent::TRADE, e);
} }

View File

@ -430,7 +430,7 @@ public:
int64 CalcMaxMana(); int64 CalcMaxMana();
int64 CalcBaseMana(); int64 CalcBaseMana();
const int64& SetMana(int64 amount); const int64& SetMana(int64 amount);
int64 CalcManaRegenCap(); int64 CalcManaRegenCap() final;
// guild pool regen shit. Sends a SpawnAppearance with a value that regens to value * 0.001 // guild pool regen shit. Sends a SpawnAppearance with a value that regens to value * 0.001
void EnableAreaHPRegen(int value); void EnableAreaHPRegen(int value);
@ -535,8 +535,8 @@ public:
inline virtual int32 GetCombatEffects() const { return itembonuses.ProcChance; } inline virtual int32 GetCombatEffects() const { return itembonuses.ProcChance; }
inline virtual int32 GetDS() const { return itembonuses.DamageShield; } inline virtual int32 GetDS() const { return itembonuses.DamageShield; }
// Mod3 // Mod3
inline virtual int32 GetHealAmt() const { return itembonuses.HealAmt; } inline int32 GetHealAmt() const override { return itembonuses.HealAmt; }
inline virtual int32 GetSpellDmg() const { return itembonuses.SpellDmg; } inline int32 GetSpellDmg() const final { return itembonuses.SpellDmg; }
inline virtual int32 GetClair() const { return itembonuses.Clairvoyance; } inline virtual int32 GetClair() const { return itembonuses.Clairvoyance; }
inline virtual int32 GetDSMit() const { return itembonuses.DSMitigation; } inline virtual int32 GetDSMit() const { return itembonuses.DSMitigation; }
@ -578,8 +578,8 @@ public:
int64 CalcEnduranceRegen(bool bCombat = false); //Calculates endurance regen used in DoEnduranceRegen() int64 CalcEnduranceRegen(bool bCombat = false); //Calculates endurance regen used in DoEnduranceRegen()
int64 GetEndurance() const {return current_endurance;} //This gets our current endurance int64 GetEndurance() const {return current_endurance;} //This gets our current endurance
int64 GetMaxEndurance() const {return max_end;} //This gets our endurance from the last CalcMaxEndurance() call int64 GetMaxEndurance() const {return max_end;} //This gets our endurance from the last CalcMaxEndurance() call
int64 CalcEnduranceRegenCap(); int64 CalcEnduranceRegenCap() final;
int64 CalcHPRegenCap(); int64 CalcHPRegenCap() final;
inline uint8 GetEndurancePercent() { return (uint8)((float)current_endurance / (float)max_end * 100.0f); } inline uint8 GetEndurancePercent() { return (uint8)((float)current_endurance / (float)max_end * 100.0f); }
void SetEndurance(int32 newEnd); //This sets the current endurance to the new value void SetEndurance(int32 newEnd); //This sets the current endurance to the new value
void DoEnduranceRegen(); //This Regenerates endurance void DoEnduranceRegen(); //This Regenerates endurance
@ -1638,19 +1638,17 @@ public:
// rate limit // rate limit
Timer m_list_task_timers_rate_limit = {}; Timer m_list_task_timers_rate_limit = {};
std::map<std::string,std::string> GetMerchantDataBuckets();
std::string GetGuildPublicNote(); std::string GetGuildPublicNote();
PlayerEvent::PlayerEvent GetPlayerEvent(); PlayerEvent::PlayerEvent GetPlayerEvent();
void RecordKilledNPCEvent(NPC *n); void RecordKilledNPCEvent(NPC *n);
protected: protected:
friend class Mob; friend class Mob;
void CalcItemBonuses(StatBonuses* newbon); void CalcItemBonuses(StatBonuses* newbon);
void AddItemBonuses(const EQ::ItemInstance *inst, StatBonuses* newbon, bool isAug = false, bool isTribute = false, int rec_override = 0, bool ammo_slot_item = false); void AddItemBonuses(const EQ::ItemInstance *inst, StatBonuses* newbon, bool isAug = false, bool isTribute = false, int rec_override = 0, bool ammo_slot_item = false);
void AdditiveWornBonuses(const EQ::ItemInstance *inst, StatBonuses* newbon, bool isAug = false); void AdditiveWornBonuses(const EQ::ItemInstance *inst, StatBonuses* newbon, bool isAug = false);
void CalcEdibleBonuses(StatBonuses* newbon); void CalcEdibleBonuses(StatBonuses* newbon);
void ProcessItemCaps();
void MakeBuffFadePacket(uint16 spell_id, int slot_id, bool send_message = true); void MakeBuffFadePacket(uint16 spell_id, int slot_id, bool send_message = true);
bool client_data_loaded; bool client_data_loaded;
@ -1701,7 +1699,7 @@ private:
void HandleTraderPriceUpdate(const EQApplicationPacket *app); void HandleTraderPriceUpdate(const EQApplicationPacket *app);
int32 CalcItemATKCap(); int32 CalcItemATKCap() final;
int32 CalcHaste(); int32 CalcHaste();
int32 CalcAlcoholPhysicalEffect(); int32 CalcAlcoholPhysicalEffect();

View File

@ -234,7 +234,7 @@ int32 Client::LevelRegen()
int64 Client::CalcHPRegen(bool bCombat) int64 Client::CalcHPRegen(bool bCombat)
{ {
int64 item_regen = itembonuses.HPRegen; // worn spells and +regen, already capped int64 item_regen = itembonuses.HPRegen; // worn spells and +regen, already capped
item_regen += GetHeroicSTA() * RuleR(Character, HeroicStaminaMultiplier) / 20; item_regen += itembonuses.heroic_hp_regen;
item_regen += aabonuses.HPRegen; item_regen += aabonuses.HPRegen;
@ -491,7 +491,7 @@ int64 Client::CalcBaseHP()
auto base_data = database.GetBaseData(GetLevel(), GetClass()); auto base_data = database.GetBaseData(GetLevel(), GetClass());
if (base_data) { if (base_data) {
base_hp += base_data->base_hp + (base_data->hp_factor * stats); base_hp += base_data->base_hp + (base_data->hp_factor * stats);
base_hp += GetHeroicSTA() * RuleR(Character, HeroicStaminaMultiplier) * 10; base_hp += itembonuses.heroic_max_hp;
} }
} }
else { else {
@ -624,8 +624,7 @@ int64 Client::CalcBaseMana()
auto base_data = database.GetBaseData(GetLevel(), GetClass()); auto base_data = database.GetBaseData(GetLevel(), GetClass());
if (base_data) { if (base_data) {
max_m = base_data->base_mana + max_m = base_data->base_mana +
(ConvertedWisInt * base_data->mana_factor) + (ConvertedWisInt * base_data->mana_factor) + itembonuses.heroic_max_mana;
(GetHeroicINT() * RuleR(Character, HeroicIntelligenceMultiplier) * 10);
} }
} }
else { else {
@ -658,8 +657,7 @@ int64 Client::CalcBaseMana()
auto base_data = database.GetBaseData(GetLevel(), GetClass()); auto base_data = database.GetBaseData(GetLevel(), GetClass());
if (base_data) { if (base_data) {
max_m = base_data->base_mana + max_m = base_data->base_mana +
(ConvertedWisInt * base_data->mana_factor) + (ConvertedWisInt * base_data->mana_factor) + itembonuses.heroic_max_mana;
((GetHeroicWIS() * RuleR(Character, HeroicWisdomMultiplier)) * 10);
} }
} }
else { else {
@ -752,18 +750,7 @@ int64 Client::CalcManaRegen(bool bCombat)
// add in + 1 bonus for SE_CompleteHeal, but we don't do anything for it yet? // add in + 1 bonus for SE_CompleteHeal, but we don't do anything for it yet?
int item_bonus = itembonuses.ManaRegen; // this is capped already int item_bonus = itembonuses.ManaRegen; // this is capped already
int heroic_bonus = 0; item_bonus += itembonuses.heroic_mana_regen;
switch (GetCasterClass()) {
case 'W':
heroic_bonus = GetHeroicWIS() * RuleR(Character, HeroicWisdomMultiplier);
break;
default:
heroic_bonus = GetHeroicINT() * RuleR(Character, HeroicIntelligenceMultiplier);
break;
}
item_bonus += heroic_bonus / 25;
regen += item_bonus; regen += item_bonus;
if (level <= 70 && regen > 65) if (level <= 70 && regen > 65)
@ -1690,11 +1677,6 @@ int64 Client::CalcBaseEndurance()
{ {
int64 base_end = 0; int64 base_end = 0;
if (ClientVersion() >= EQ::versions::ClientVersion::SoF && RuleB(Character, SoDClientUseSoDHPManaEnd)) { if (ClientVersion() >= EQ::versions::ClientVersion::SoF && RuleB(Character, SoDClientUseSoDHPManaEnd)) {
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; double stats = (GetSTR() + GetSTA() + GetDEX() + GetAGI()) / 4.0f;
if (stats > 201.0f) { if (stats > 201.0f) {
@ -1705,7 +1687,7 @@ int64 Client::CalcBaseEndurance()
} }
auto base_data = database.GetBaseData(GetLevel(), GetClass()); auto base_data = database.GetBaseData(GetLevel(), GetClass());
if (base_data) { if (base_data) {
base_end = base_data->base_end + (heroic_stats * 10.0f) + (base_data->endurance_factor * static_cast<int>(stats)); base_end = base_data->base_end + itembonuses.heroic_max_end + (base_data->endurance_factor * static_cast<int>(stats));
} }
} }
else { else {
@ -1794,13 +1776,7 @@ int64 Client::CalcEnduranceRegen(bool bCombat)
if (encumbered) if (encumbered)
base += level / -15; base += level / -15;
double heroic_str = GetHeroicSTR() * RuleR(Character, HeroicStrengthMultiplier); auto item_bonus = itembonuses.EnduranceRegen + itembonuses.heroic_end_regen; // this is capped already
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; base += item_bonus;
base = base * AreaEndRegen + 0.5f; base = base * AreaEndRegen + 0.5f;

View File

@ -1380,6 +1380,9 @@ void Client::Handle_Connect_OP_ZoneEntry(const EQApplicationPacket *app)
drakkin_tattoo = m_pp.drakkin_tattoo; drakkin_tattoo = m_pp.drakkin_tattoo;
drakkin_details = m_pp.drakkin_details; drakkin_details = m_pp.drakkin_details;
// Load Data Buckets
DataBucket::GetDataBuckets(this);
// Max Level for Character:PerCharacterQglobalMaxLevel and Character:PerCharacterBucketMaxLevel // Max Level for Character:PerCharacterQglobalMaxLevel and Character:PerCharacterBucketMaxLevel
uint8 client_max_level = 0; uint8 client_max_level = 0;
if (RuleB(Character, PerCharacterQglobalMaxLevel)) { if (RuleB(Character, PerCharacterQglobalMaxLevel)) {

View File

@ -845,18 +845,16 @@ void Client::BulkSendMerchantInventory(int merchant_id, int npcid) {
} }
} }
auto client_data_buckets = GetMerchantDataBuckets();
auto temporary_merchant_list = zone->tmpmerchanttable[npcid]; auto temporary_merchant_list = zone->tmpmerchanttable[npcid];
uint32 slot_id = 1; uint32 slot_id = 1;
uint8 handy_chance = 0; uint8 handy_chance = 0;
for (auto ml : merchant_list) { for (const auto& ml : merchant_list) {
if (slot_id > merchant_slots) { if (slot_id > merchant_slots) {
break; break;
} }
auto bucket_name = ml.bucket_name; auto bucket_name = ml.bucket_name;
auto bucket_value = ml.bucket_value; auto const& bucket_value = ml.bucket_value;
if (!bucket_name.empty() && !bucket_value.empty()) { if (!bucket_name.empty() && !bucket_value.empty()) {
auto full_name = fmt::format( auto full_name = fmt::format(
"{}-{}", "{}-{}",
@ -864,12 +862,12 @@ void Client::BulkSendMerchantInventory(int merchant_id, int npcid) {
bucket_name bucket_name
); );
auto player_value = client_data_buckets[full_name]; auto const& player_value = DataBucket::CheckBucketKey(this, full_name);
if (player_value.empty()) { if (player_value.empty()) {
continue; continue;
} }
if (!zone->CheckDataBucket(ml.bucket_comparison, bucket_value, player_value)) { if (!zone->CompareDataBucket(ml.bucket_comparison, bucket_value, player_value)) {
continue; continue;
} }
} }

View File

@ -639,6 +639,16 @@ struct StatBonuses {
int aura_slots; int aura_slots;
int trap_slots; int trap_slots;
bool hunger; // Song of Sustenance -- min caps to 3500 bool hunger; // Song of Sustenance -- min caps to 3500
int64 heroic_max_hp;
int64 heroic_max_mana;
int64 heroic_max_end;
int64 heroic_hp_regen;
int64 heroic_mana_regen;
int64 heroic_end_regen;
int32 heroic_str_shield_ac;
int32 heroic_str_melee_damage;
int32 heroic_agi_avoidance;
int32 heroic_dex_ranged_damage;
}; };
// StatBonus Indexes // StatBonus Indexes
@ -915,5 +925,13 @@ struct ExpeditionInvite
std::string swap_remove_name; std::string swap_remove_name;
}; };
struct DataBucketCache
{
uint64_t bucket_id;
std::string bucket_key;
std::string bucket_value;
uint32_t bucket_expires;
};
#endif #endif

View File

@ -13,7 +13,7 @@
* @param bucket_value * @param bucket_value
* @param expires_time * @param expires_time
*/ */
void DataBucket::SetData(std::string bucket_key, std::string bucket_value, std::string expires_time) { void DataBucket::SetData(const std::string& bucket_key, const std::string& bucket_value, std::string expires_time) {
uint64 bucket_id = DataBucket::DoesBucketExist(bucket_key); uint64 bucket_id = DataBucket::DoesBucketExist(bucket_key);
std::string query; std::string query;
@ -57,7 +57,7 @@ void DataBucket::SetData(std::string bucket_key, std::string bucket_value, std::
* @param bucket_key * @param bucket_key
* @return * @return
*/ */
std::string DataBucket::GetData(std::string bucket_key) { std::string DataBucket::GetData(const std::string& bucket_key) {
std::string query = StringFormat( std::string query = StringFormat(
"SELECT `value` from `data_buckets` WHERE `key` = '%s' AND (`expires` > %lld OR `expires` = 0) LIMIT 1", "SELECT `value` from `data_buckets` WHERE `key` = '%s' AND (`expires` > %lld OR `expires` = 0) LIMIT 1",
bucket_key.c_str(), bucket_key.c_str(),
@ -82,7 +82,7 @@ std::string DataBucket::GetData(std::string bucket_key) {
* @param bucket_key * @param bucket_key
* @return * @return
*/ */
std::string DataBucket::GetDataExpires(std::string bucket_key) { std::string DataBucket::GetDataExpires(const std::string& bucket_key) {
std::string query = StringFormat( std::string query = StringFormat(
"SELECT `expires` from `data_buckets` WHERE `key` = '%s' AND (`expires` > %lld OR `expires` = 0) LIMIT 1", "SELECT `expires` from `data_buckets` WHERE `key` = '%s' AND (`expires` > %lld OR `expires` = 0) LIMIT 1",
bucket_key.c_str(), bucket_key.c_str(),
@ -102,7 +102,7 @@ std::string DataBucket::GetDataExpires(std::string bucket_key) {
return std::string(row[0]); return std::string(row[0]);
} }
std::string DataBucket::GetDataRemaining(std::string bucket_key) { std::string DataBucket::GetDataRemaining(const std::string& bucket_key) {
if (DataBucket::GetDataExpires(bucket_key).empty()) { if (DataBucket::GetDataExpires(bucket_key).empty()) {
return "0"; return "0";
} }
@ -130,7 +130,7 @@ std::string DataBucket::GetDataRemaining(std::string bucket_key) {
* @param bucket_key * @param bucket_key
* @return * @return
*/ */
uint64 DataBucket::DoesBucketExist(std::string bucket_key) { uint64 DataBucket::DoesBucketExist(const std::string& bucket_key) {
std::string query = StringFormat( std::string query = StringFormat(
"SELECT `id` from `data_buckets` WHERE `key` = '%s' AND (`expires` > %lld OR `expires` = 0) LIMIT 1", "SELECT `id` from `data_buckets` WHERE `key` = '%s' AND (`expires` > %lld OR `expires` = 0) LIMIT 1",
Strings::Escape(bucket_key).c_str(), Strings::Escape(bucket_key).c_str(),
@ -154,7 +154,7 @@ uint64 DataBucket::DoesBucketExist(std::string bucket_key) {
* @param bucket_key * @param bucket_key
* @return * @return
*/ */
bool DataBucket::DeleteData(std::string bucket_key) { bool DataBucket::DeleteData(const std::string& bucket_key) {
std::string query = StringFormat( std::string query = StringFormat(
"DELETE FROM `data_buckets` WHERE `key` = '%s'", "DELETE FROM `data_buckets` WHERE `key` = '%s'",
Strings::Escape(bucket_key).c_str() Strings::Escape(bucket_key).c_str()
@ -164,3 +164,46 @@ bool DataBucket::DeleteData(std::string bucket_key) {
return results.Success(); return results.Success();
} }
bool DataBucket::GetDataBuckets(Mob* mob)
{
auto l = BaseDataBucketsRepository::GetWhere(
database,
fmt::format(
"`key` LIKE '{}-%'",
Strings::Escape(mob->GetBucketKey())
)
);
if (l.empty()) {
return false;
}
mob->m_data_bucket_cache.clear();
DataBucketCache d;
for (const auto& e : l) {
d.bucket_id = e.id;
d.bucket_key = e.key_;
d.bucket_value = e.value;
d.bucket_expires = e.expires;
mob->m_data_bucket_cache.emplace_back(d);
}
return true;
}
std::string DataBucket::CheckBucketKey(const Mob* mob, std::string_view full_name)
{
std::string bucket_value;
for (const auto &d : mob->m_data_bucket_cache) {
if (d.bucket_key == full_name) {
bucket_value = d.bucket_value;
break;
}
}
return bucket_value;
}

View File

@ -5,20 +5,23 @@
#ifndef EQEMU_DATABUCKET_H #ifndef EQEMU_DATABUCKET_H
#define EQEMU_DATABUCKET_H #define EQEMU_DATABUCKET_H
#include <string> #include <string>
#include "../common/types.h" #include "../common/types.h"
#include "../common/repositories/data_buckets_repository.h"
#include "mob.h"
class DataBucket { class DataBucket {
public: public:
static void SetData(std::string bucket_key, std::string bucket_value, std::string expires_time = ""); static void SetData(const std::string& bucket_key, const std::string& bucket_value, std::string expires_time = "");
static bool DeleteData(std::string bucket_key); static bool DeleteData(const std::string& bucket_key);
static std::string GetData(std::string bucket_key); static std::string GetData(const std::string& bucket_key);
static std::string GetDataExpires(std::string bucket_key); static std::string GetDataExpires(const std::string& bucket_key);
static std::string GetDataRemaining(std::string bucket_key); static std::string GetDataRemaining(const std::string& bucket_key);
static bool GetDataBuckets(Mob* mob);
static std::string CheckBucketKey(const Mob* mob, std::string_view full_name);
private: private:
static uint64 DoesBucketExist(std::string bucket_key); static uint64 DoesBucketExist(const std::string& bucket_key);
}; };
#endif //EQEMU_DATABUCKET_H #endif //EQEMU_DATABUCKET_H

View File

@ -132,6 +132,7 @@ int64 Mob::GetActSpellDamage(uint16 spell_id, int64 value, Mob* target) {
if (RuleB(Spells, IgnoreSpellDmgLvlRestriction) && !spells[spell_id].no_heal_damage_item_mod && itembonuses.SpellDmg) { if (RuleB(Spells, IgnoreSpellDmgLvlRestriction) && !spells[spell_id].no_heal_damage_item_mod && itembonuses.SpellDmg) {
value -= GetExtraSpellAmt(spell_id, itembonuses.SpellDmg, base_value) * ratio / 100; value -= GetExtraSpellAmt(spell_id, itembonuses.SpellDmg, base_value) * ratio / 100;
} }
else if (!spells[spell_id].no_heal_damage_item_mod && itembonuses.SpellDmg && spells[spell_id].classes[(GetClass() % 17) - 1] >= GetLevel() - 5) { else if (!spells[spell_id].no_heal_damage_item_mod && itembonuses.SpellDmg && spells[spell_id].classes[(GetClass() % 17) - 1] >= GetLevel() - 5) {
@ -140,7 +141,7 @@ int64 Mob::GetActSpellDamage(uint16 spell_id, int64 value, Mob* target) {
entity_list.FilteredMessageCloseString( entity_list.FilteredMessageCloseString(
this, true, 100, Chat::SpellCrit, FilterSpellCrits, this, true, 100, Chat::SpellCrit, FilterSpellCrits,
OTHER_CRIT_BLAST, 0, GetName(), itoa(-value)); OTHER_CRIT_BLAST, nullptr, GetName(), itoa(-value));
if (IsClient()) if (IsClient())
MessageString(Chat::SpellCrit, YOU_CRIT_BLAST, itoa(-value)); MessageString(Chat::SpellCrit, YOU_CRIT_BLAST, itoa(-value));
@ -175,8 +176,13 @@ int64 Mob::GetActSpellDamage(uint16 spell_id, int64 value, Mob* target) {
if (RuleB(Spells, IgnoreSpellDmgLvlRestriction) && !spells[spell_id].no_heal_damage_item_mod && itembonuses.SpellDmg) if (RuleB(Spells, IgnoreSpellDmgLvlRestriction) && !spells[spell_id].no_heal_damage_item_mod && itembonuses.SpellDmg)
value -= GetExtraSpellAmt(spell_id, itembonuses.SpellDmg, base_value); value -= GetExtraSpellAmt(spell_id, itembonuses.SpellDmg, base_value);
else if (!spells[spell_id].no_heal_damage_item_mod && itembonuses.SpellDmg && spells[spell_id].classes[(GetClass() % 17) - 1] >= GetLevel() - 5) else if (
value -= GetExtraSpellAmt(spell_id, itembonuses.SpellDmg, base_value); !spells[spell_id].no_heal_damage_item_mod &&
GetSpellDmg() &&
spells[spell_id].classes[(GetClass() % 17) - 1] >= GetLevel() - 5
) {
value -= GetExtraSpellAmt(spell_id, GetSpellDmg(), base_value);
}
return value; return value;
} }
@ -260,11 +266,19 @@ int64 Mob::GetActDoTDamage(uint16 spell_id, int64 value, Mob* target, bool from_
GetFocusEffect(focusFcAmplifyAmt, spell_id, nullptr, from_buff_tic); GetFocusEffect(focusFcAmplifyAmt, spell_id, nullptr, from_buff_tic);
if (RuleB(Spells, DOTsScaleWithSpellDmg)) { if (RuleB(Spells, DOTsScaleWithSpellDmg)) {
if (RuleB(Spells, IgnoreSpellDmgLvlRestriction) && !spells[spell_id].no_heal_damage_item_mod && itembonuses.SpellDmg) { if (
extra_dmg += GetExtraSpellAmt(spell_id, itembonuses.SpellDmg, base_value)*ratio/100; RuleB(Spells, IgnoreSpellDmgLvlRestriction) &&
!spells[spell_id].no_heal_damage_item_mod &&
GetSpellDmg()
) {
extra_dmg += GetExtraSpellAmt(spell_id, GetSpellDmg(), base_value)*ratio/100;
} }
else if(!spells[spell_id].no_heal_damage_item_mod && itembonuses.SpellDmg && spells[spell_id].classes[(GetClass() % 17) - 1] >= GetLevel() - 5) { else if (
extra_dmg += GetExtraSpellAmt(spell_id, itembonuses.SpellDmg, base_value)*ratio/100; !spells[spell_id].no_heal_damage_item_mod &&
GetSpellDmg() &&
spells[spell_id].classes[(GetClass() % 17) - 1] >= GetLevel() - 5
) {
extra_dmg += GetExtraSpellAmt(spell_id, GetSpellDmg(), base_value)*ratio/100;
} }
} }
@ -295,11 +309,19 @@ int64 Mob::GetActDoTDamage(uint16 spell_id, int64 value, Mob* target, bool from_
GetFocusEffect(focusFcAmplifyAmt, spell_id, nullptr, from_buff_tic); GetFocusEffect(focusFcAmplifyAmt, spell_id, nullptr, from_buff_tic);
if (RuleB(Spells, DOTsScaleWithSpellDmg)) { if (RuleB(Spells, DOTsScaleWithSpellDmg)) {
if (RuleB(Spells, IgnoreSpellDmgLvlRestriction) && !spells[spell_id].no_heal_damage_item_mod && itembonuses.SpellDmg) { if (
extra_dmg += GetExtraSpellAmt(spell_id, itembonuses.SpellDmg, base_value); RuleB(Spells, IgnoreSpellDmgLvlRestriction) &&
!spells[spell_id].no_heal_damage_item_mod &&
GetSpellDmg()
) {
extra_dmg += GetExtraSpellAmt(spell_id, GetSpellDmg(), base_value);
} }
else if(!spells[spell_id].no_heal_damage_item_mod && itembonuses.SpellDmg && spells[spell_id].classes[(GetClass() % 17) - 1] >= GetLevel() - 5) { else if (
extra_dmg += GetExtraSpellAmt(spell_id, itembonuses.SpellDmg, base_value); !spells[spell_id].no_heal_damage_item_mod &&
GetSpellDmg() &&
spells[spell_id].classes[(GetClass() % 17) - 1] >= GetLevel() - 5
) {
extra_dmg += GetExtraSpellAmt(spell_id, GetSpellDmg(), base_value);
} }
} }
@ -321,6 +343,7 @@ int64 Mob::GetActDoTDamage(uint16 spell_id, int64 value, Mob* target, bool from_
int64 Mob::GetExtraSpellAmt(uint16 spell_id, int64 extra_spell_amt, int64 base_spell_dmg) int64 Mob::GetExtraSpellAmt(uint16 spell_id, int64 extra_spell_amt, int64 base_spell_dmg)
{ {
if (RuleB(Spells, FlatItemExtraSpellAmt)) { if (RuleB(Spells, FlatItemExtraSpellAmt)) {
if (RuleB(Spells, ItemExtraSpellAmtCalcAsPercent)) { if (RuleB(Spells, ItemExtraSpellAmtCalcAsPercent)) {
return std::abs(base_spell_dmg) * extra_spell_amt / 100; return std::abs(base_spell_dmg) * extra_spell_amt / 100;
@ -425,11 +448,19 @@ int64 Mob::GetActSpellHealing(uint16 spell_id, int64 value, Mob* target, bool fr
value += GetFocusEffect(focusFcHealAmtCrit, spell_id); //SPA 396 Add before critical value += GetFocusEffect(focusFcHealAmtCrit, spell_id); //SPA 396 Add before critical
//Using IgnoreSpellDmgLvlRestriction to also allow healing to scale //Using IgnoreSpellDmgLvlRestriction to also allow healing to scale
if (RuleB(Spells, IgnoreSpellDmgLvlRestriction) && !spells[spell_id].no_heal_damage_item_mod && itembonuses.HealAmt) { if (
value += GetExtraSpellAmt(spell_id, itembonuses.HealAmt, base_value);//Item Heal Amt Add before critical RuleB(Spells, IgnoreSpellDmgLvlRestriction) &&
!spells[spell_id].no_heal_damage_item_mod &&
GetHealAmt()
) {
value += GetExtraSpellAmt(spell_id, GetHealAmt(), base_value); //Item Heal Amt Add before critical
} }
else if (!spells[spell_id].no_heal_damage_item_mod && itembonuses.HealAmt && spells[spell_id].classes[(GetClass() % 17) - 1] >= GetLevel() - 5) { else if (
value += GetExtraSpellAmt(spell_id, itembonuses.HealAmt, base_value);//Item Heal Amt Add before critical !spells[spell_id].no_heal_damage_item_mod &&
GetHealAmt() &&
spells[spell_id].classes[(GetClass() % 17) - 1] >= GetLevel() - 5
) {
value += GetExtraSpellAmt(spell_id, GetHealAmt(), base_value); //Item Heal Amt Add before critical
} }
if (target) { if (target) {
@ -471,11 +502,19 @@ int64 Mob::GetActSpellHealing(uint16 spell_id, int64 value, Mob* target, bool fr
} }
if (RuleB(Spells, HOTsScaleWithHealAmt)) { if (RuleB(Spells, HOTsScaleWithHealAmt)) {
if (RuleB(Spells, IgnoreSpellDmgLvlRestriction) && !spells[spell_id].no_heal_damage_item_mod && itembonuses.HealAmt) { if (
extra_heal += GetExtraSpellAmt(spell_id, itembonuses.HealAmt, base_value); RuleB(Spells, IgnoreSpellDmgLvlRestriction) &&
!spells[spell_id].no_heal_damage_item_mod &&
GetHealAmt()
) {
extra_heal += GetExtraSpellAmt(spell_id, GetHealAmt(), base_value);
} }
else if(!spells[spell_id].no_heal_damage_item_mod && itembonuses.HealAmt && spells[spell_id].classes[(GetClass() % 17) - 1] >= GetLevel() - 5) { else if (
extra_heal += GetExtraSpellAmt(spell_id, itembonuses.HealAmt, base_value); !spells[spell_id].no_heal_damage_item_mod &&
GetHealAmt() &&
spells[spell_id].classes[(GetClass() % 17) - 1] >= GetLevel() - 5
) {
extra_heal += GetExtraSpellAmt(spell_id, GetHealAmt(), base_value);
} }
} }

View File

@ -112,12 +112,12 @@ void Lua_Bot::SetExpansionBitmask(int expansion_bitmask, bool save) {
bool Lua_Bot::ReloadBotDataBuckets() { bool Lua_Bot::ReloadBotDataBuckets() {
Lua_Safe_Call_Bool(); Lua_Safe_Call_Bool();
return self->GetBotDataBuckets(); return DataBucket::GetDataBuckets(self);
} }
bool Lua_Bot::ReloadBotOwnerDataBuckets() { bool Lua_Bot::ReloadBotOwnerDataBuckets() {
Lua_Safe_Call_Bool(); Lua_Safe_Call_Bool();
return self->GetBotOwnerDataBuckets(); return self->HasOwner() && DataBucket::GetDataBuckets(self->GetBotOwner());
} }
bool Lua_Bot::ReloadBotSpells() { bool Lua_Bot::ReloadBotSpells() {

View File

@ -3046,6 +3046,11 @@ bool Lua_Client::IsAutoFireEnabled()
return self->AutoFireEnabled(); return self->AutoFireEnabled();
} }
bool Lua_Client::ReloadDataBuckets() {
Lua_Safe_Call_Bool();
return DataBucket::GetDataBuckets(self);
}
luabind::scope lua_register_client() { luabind::scope lua_register_client() {
return luabind::class_<Lua_Client, Lua_Mob>("Client") return luabind::class_<Lua_Client, Lua_Mob>("Client")
.def(luabind::constructor<>()) .def(luabind::constructor<>())
@ -3394,6 +3399,7 @@ luabind::scope lua_register_client() {
.def("QueuePacket", (void(Lua_Client::*)(Lua_Packet,bool,int,int))&Lua_Client::QueuePacket) .def("QueuePacket", (void(Lua_Client::*)(Lua_Packet,bool,int,int))&Lua_Client::QueuePacket)
.def("ReadBookByName", (void(Lua_Client::*)(std::string,uint8))&Lua_Client::ReadBookByName) .def("ReadBookByName", (void(Lua_Client::*)(std::string,uint8))&Lua_Client::ReadBookByName)
.def("RefundAA", (void(Lua_Client::*)(void))&Lua_Client::RefundAA) .def("RefundAA", (void(Lua_Client::*)(void))&Lua_Client::RefundAA)
.def("ReloadDataBuckets", (bool(Lua_Client::*)(void))&Lua_Client::ReloadDataBuckets)
.def("RemoveAllExpeditionLockouts", (void(Lua_Client::*)(std::string))&Lua_Client::RemoveAllExpeditionLockouts) .def("RemoveAllExpeditionLockouts", (void(Lua_Client::*)(std::string))&Lua_Client::RemoveAllExpeditionLockouts)
.def("RemoveAllExpeditionLockouts", (void(Lua_Client::*)(void))&Lua_Client::RemoveAllExpeditionLockouts) .def("RemoveAllExpeditionLockouts", (void(Lua_Client::*)(void))&Lua_Client::RemoveAllExpeditionLockouts)
.def("RemoveExpeditionLockout", (void(Lua_Client::*)(std::string, std::string))&Lua_Client::RemoveExpeditionLockout) .def("RemoveExpeditionLockout", (void(Lua_Client::*)(std::string, std::string))&Lua_Client::RemoveExpeditionLockout)

View File

@ -534,6 +534,8 @@ public:
void DialogueWindow(std::string markdown); void DialogueWindow(std::string markdown);
bool ReloadDataBuckets();
Lua_Expedition CreateExpedition(luabind::object expedition_info); Lua_Expedition CreateExpedition(luabind::object expedition_info);
Lua_Expedition CreateExpedition(std::string zone_name, uint32 version, uint32 duration, std::string expedition_name, uint32 min_players, uint32 max_players); Lua_Expedition CreateExpedition(std::string zone_name, uint32 version, uint32 duration, std::string expedition_name, uint32 min_players, uint32 max_players);
Lua_Expedition CreateExpedition(std::string zone_name, uint32 version, uint32 duration, std::string expedition_name, uint32 min_players, uint32 max_players, bool disable_messages); Lua_Expedition CreateExpedition(std::string zone_name, uint32 version, uint32 duration, std::string expedition_name, uint32 min_players, uint32 max_players, bool disable_messages);

View File

@ -226,8 +226,8 @@ public:
inline virtual int32 GetCombatEffects() const { return itembonuses.ProcChance; } inline virtual int32 GetCombatEffects() const { return itembonuses.ProcChance; }
inline virtual int32 GetDS() const { return itembonuses.DamageShield; } inline virtual int32 GetDS() const { return itembonuses.DamageShield; }
// Mod3 // Mod3
inline virtual int32 GetHealAmt() const { return itembonuses.HealAmt; } inline int32 GetHealAmt() const override { return itembonuses.HealAmt; }
inline virtual int32 GetSpellDmg() const { return itembonuses.SpellDmg; } inline int32 GetSpellDmg() const override { return itembonuses.SpellDmg; }
inline virtual int32 GetClair() const { return itembonuses.Clairvoyance; } inline virtual int32 GetClair() const { return itembonuses.Clairvoyance; }
inline virtual int32 GetDSMit() const { return itembonuses.DSMitigation; } inline virtual int32 GetDSMit() const { return itembonuses.DSMitigation; }

View File

@ -7115,4 +7115,4 @@ int Mob::DispatchZoneControllerEvent(
} }
return ret; return ret;
} }

View File

@ -20,6 +20,7 @@
#define MOB_H #define MOB_H
#include "common.h" #include "common.h"
#include "data_bucket.h"
#include "entity.h" #include "entity.h"
#include "hate_list.h" #include "hate_list.h"
#include "pathfinder_interface.h" #include "pathfinder_interface.h"
@ -618,10 +619,13 @@ public:
inline int64 GetHP() const { return current_hp; } inline int64 GetHP() const { return current_hp; }
inline int64 GetMaxHP() const { return max_hp; } inline int64 GetMaxHP() const { return max_hp; }
virtual int64 CalcMaxHP(); virtual int64 CalcMaxHP();
virtual int64 CalcHPRegenCap() { return 0; }
inline int64 GetMaxMana() const { return max_mana; } inline int64 GetMaxMana() const { return max_mana; }
virtual int64 CalcManaRegenCap() { return 0; }
inline int64 GetMana() const { return current_mana; } inline int64 GetMana() const { return current_mana; }
virtual int64 GetEndurance() const { return 0; } virtual int64 GetEndurance() const { return 0; }
virtual int64 GetMaxEndurance() const { return 0; } virtual int64 GetMaxEndurance() const { return 0; }
virtual int64 CalcEnduranceRegenCap() { return 0; }
virtual void SetEndurance(int32 newEnd) { return; } virtual void SetEndurance(int32 newEnd) { return; }
int64 GetItemHPBonuses(); int64 GetItemHPBonuses();
int64 GetSpellHPBonuses(); int64 GetSpellHPBonuses();
@ -654,7 +658,10 @@ public:
inline int32 GetHeroicStrikethrough() const { return heroic_strikethrough; } inline int32 GetHeroicStrikethrough() const { return heroic_strikethrough; }
inline const bool GetKeepsSoldItems() const { return keeps_sold_items; } inline const bool GetKeepsSoldItems() const { return keeps_sold_items; }
inline void SetKeepsSoldItems(bool in_keeps_sold_items) { keeps_sold_items = in_keeps_sold_items; } inline void SetKeepsSoldItems(bool in_keeps_sold_items) { keeps_sold_items = in_keeps_sold_items; }
virtual int32 GetHealAmt() const { return 0; }
virtual int32 GetSpellDmg() const { return 0; }
void ProcessItemCaps();
virtual int32 CalcItemATKCap() { return 0; }
virtual bool IsSitting() const { return false; } virtual bool IsSitting() const { return false; }
int CalcRecommendedLevelBonus(uint8 level, uint8 reclevel, int basestat); int CalcRecommendedLevelBonus(uint8 level, uint8 reclevel, int basestat);
@ -1390,6 +1397,9 @@ public:
/// this cures timing issues cuz dead animation isn't done but server side feigning is? /// this cures timing issues cuz dead animation isn't done but server side feigning is?
inline bool GetFeigned() const { return(feigned); } inline bool GetFeigned() const { return(feigned); }
std::vector<DataBucketCache> m_data_bucket_cache;
// Data Bucket Methods
void DeleteBucket(std::string bucket_name); void DeleteBucket(std::string bucket_name);
std::string GetBucket(std::string bucket_name); std::string GetBucket(std::string bucket_name);
std::string GetBucketExpires(std::string bucket_name); std::string GetBucketExpires(std::string bucket_name);
@ -1397,6 +1407,9 @@ public:
std::string GetBucketRemaining(std::string bucket_name); std::string GetBucketRemaining(std::string bucket_name);
void SetBucket(std::string bucket_name, std::string bucket_value, std::string expiration = ""); void SetBucket(std::string bucket_name, std::string bucket_value, std::string expiration = "");
// Heroic Stat Benefits
float CheckHeroicBonusesDataBuckets(std::string bucket_name);
int DispatchZoneControllerEvent(QuestEventID evt, Mob* init, const std::string& data, uint32 extra, std::vector<std::any>* pointers); int DispatchZoneControllerEvent(QuestEventID evt, Mob* init, const std::string& data, uint32 extra, std::vector<std::any>* pointers);
// Bots HealRotation methods // Bots HealRotation methods
@ -1418,6 +1431,8 @@ public:
void DrawDebugCoordinateNode(std::string node_name, const glm::vec4 vec); void DrawDebugCoordinateNode(std::string node_name, const glm::vec4 vec);
void CalcHeroicBonuses(StatBonuses* newbon);
protected: protected:
void CommonDamage(Mob* other, int64 &damage, const uint16 spell_id, const EQ::skills::SkillType attack_skill, bool &avoidable, const int8 buffslot, const bool iBuffTic, eSpecialAttacks specal = eSpecialAttacks::None); void CommonDamage(Mob* other, int64 &damage, const uint16 spell_id, const EQ::skills::SkillType attack_skill, bool &avoidable, const int8 buffslot, const bool iBuffTic, eSpecialAttacks specal = eSpecialAttacks::None);
static uint16 GetProcID(uint16 spell_id, uint8 effect_index); static uint16 GetProcID(uint16 spell_id, uint8 effect_index);
@ -1701,6 +1716,7 @@ protected:
bool is_boat; bool is_boat;
CombatRecord m_combat_record{}; CombatRecord m_combat_record{};
public: public:
const CombatRecord &GetCombatRecord() const; const CombatRecord &GetCombatRecord() const;
@ -1833,6 +1849,13 @@ private:
std::shared_ptr<HealRotation> m_target_of_heal_rotation; std::shared_ptr<HealRotation> m_target_of_heal_rotation;
bool m_manual_follow; bool m_manual_follow;
void SetHeroicStrBonuses(StatBonuses* n);
void SetHeroicStaBonuses(StatBonuses* n);
void SetHeroicAgiBonuses(StatBonuses* n);
void SetHeroicDexBonuses(StatBonuses* n);
void SetHeroicIntBonuses(StatBonuses* n);
void SetHeroicWisBonuses(StatBonuses* n);
void DoSpellInterrupt(uint16 spell_id, int32 mana_cost, int my_curmana); void DoSpellInterrupt(uint16 spell_id, int32 mana_cost, int my_curmana);
}; };

View File

@ -337,12 +337,12 @@ void Perl_Bot_SetSpellDurationGroup(Bot* self, int spell_id, int duration, bool
bool Perl_Bot_ReloadBotDataBuckets(Bot* self) bool Perl_Bot_ReloadBotDataBuckets(Bot* self)
{ {
return self->GetBotDataBuckets(); return DataBucket::GetDataBuckets(self);
} }
bool Perl_Bot_ReloadBotOwnerDataBuckets(Bot* self) bool Perl_Bot_ReloadBotOwnerDataBuckets(Bot* self)
{ {
return self->GetBotOwnerDataBuckets(); return self->HasOwner() && DataBucket::GetDataBuckets(self->GetBotOwner());
} }
bool Perl_Bot_ReloadBotSpells(Bot* self) bool Perl_Bot_ReloadBotSpells(Bot* self)

View File

@ -2902,6 +2902,11 @@ bool Perl_Client_IsAutoFireEnabled(Client* self)
return self->AutoFireEnabled(); return self->AutoFireEnabled();
} }
bool Perl_Client_ReloadDataBuckets(Client* self)
{
return DataBucket::GetDataBuckets(self);
}
void perl_register_client() void perl_register_client()
{ {
perl::interpreter perl(PERL_GET_THX); perl::interpreter perl(PERL_GET_THX);
@ -3248,6 +3253,7 @@ void perl_register_client()
package.add("ReadBook", &Perl_Client_ReadBook); package.add("ReadBook", &Perl_Client_ReadBook);
package.add("ReadBookByName", &Perl_Client_ReadBookByName); package.add("ReadBookByName", &Perl_Client_ReadBookByName);
package.add("RefundAA", &Perl_Client_RefundAA); package.add("RefundAA", &Perl_Client_RefundAA);
package.add("ReloadDataBuckets", &Perl_Client_ReloadDataBuckets);
package.add("RemoveAllExpeditionLockouts", (void(*)(Client*))&Perl_Client_RemoveAllExpeditionLockouts); package.add("RemoveAllExpeditionLockouts", (void(*)(Client*))&Perl_Client_RemoveAllExpeditionLockouts);
package.add("RemoveAllExpeditionLockouts", (void(*)(Client*, std::string))&Perl_Client_RemoveAllExpeditionLockouts); package.add("RemoveAllExpeditionLockouts", (void(*)(Client*, std::string))&Perl_Client_RemoveAllExpeditionLockouts);
package.add("RemoveExpeditionLockout", &Perl_Client_RemoveExpeditionLockout); package.add("RemoveExpeditionLockout", &Perl_Client_RemoveExpeditionLockout);

View File

@ -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 = CalcRecommendedLevelBonus(GetLevel(), inst->GetItemRecommendedLevel(true),inst->GetItemArmorClass(true));
} }
} }
shield_ac += GetHeroicSTR() * RuleR(Character, HeroicStrengthMultiplier) / 10; shield_ac += itembonuses.heroic_str_shield_ac;
} }
// EQ math // EQ math
ac = (ac * 4) / 3; ac = (ac * 4) / 3;
@ -1353,7 +1353,7 @@ int64 Mob::Tunecompute_defense(int avoidance_override, int add_avoidance)
defense = avoidance_override; defense = avoidance_override;
} }
else { else {
defense += GetHeroicAGI() * RuleR(Character, HeroicAgilityMultiplier) / 10; defense += itembonuses.heroic_agi_avoidance;
} }
defense += add_avoidance; //1 pt = 10 heroic agi defense += add_avoidance; //1 pt = 10 heroic agi
} }
@ -1495,10 +1495,10 @@ void Mob::TuneCommonOutgoingHitSuccess(Mob* defender, DamageHitInfo &hit, ExtraA
switch (hit.skill) { switch (hit.skill) {
case EQ::skills::SkillThrowing: case EQ::skills::SkillThrowing:
case EQ::skills::SkillArchery: case EQ::skills::SkillArchery:
extra = GetHeroicDEX() * RuleR(Character, HeroicDexterityMultiplier) / 10; extra = itembonuses.heroic_dex_ranged_damage;
break; break;
default: default:
extra = GetHeroicSTR() * RuleR(Character, HeroicStrengthMultiplier) / 10; extra = itembonuses.heroic_str_melee_damage;
break; break;
} }
hit.damage_done += extra; hit.damage_done += extra;

View File

@ -2986,7 +2986,7 @@ std::string Zone::GetAAName(int aa_id)
return std::string(); return std::string();
} }
bool Zone::CheckDataBucket(uint8 bucket_comparison, std::string bucket_value, std::string player_value) bool Zone::CompareDataBucket(uint8 bucket_comparison, const std::string& bucket_value, const std::string& player_value)
{ {
std::vector<std::string> bucket_checks; std::vector<std::string> bucket_checks;
bool found = false; bool found = false;

View File

@ -311,7 +311,7 @@ public:
bool IsQuestHotReloadQueued() const; bool IsQuestHotReloadQueued() const;
void SetQuestHotReloadQueued(bool in_quest_hot_reload_queued); void SetQuestHotReloadQueued(bool in_quest_hot_reload_queued);
bool CheckDataBucket(uint8 bucket_comparison, std::string bucket_value, std::string player_value); bool CompareDataBucket(uint8 bucket_comparison, const std::string& bucket_value, const std::string& player_value);
WaterMap *watermap; WaterMap *watermap;
ZonePoint *GetClosestZonePoint(const glm::vec3 &location, uint32 to, Client *client, float max_distance = 40000.0f); ZonePoint *GetClosestZonePoint(const glm::vec3 &location, uint32 to, Client *client, float max_distance = 40000.0f);