Compare commits

...

5 Commits

Author SHA1 Message Date
Kinglykrab 35aaf05173 Update mob.cpp 2025-08-30 16:59:01 -04:00
Kinglykrab 8746c1845d Consolidate 2025-08-30 16:56:38 -04:00
Kinglykrab 0ae1a2883d Update database_update_manifest_bots.cpp 2025-08-30 16:41:16 -04:00
Kinglykrab 36f8c3a89f Push 2025-08-30 16:41:16 -04:00
Kinglykrab aba58172f4 [Feature] Add Bot/Client Specific Stat Caps 2025-08-30 16:41:16 -04:00
29 changed files with 1346 additions and 79 deletions
@@ -7188,6 +7188,21 @@ CHANGE COLUMN `field221` `caster_requirement_id` int(11) NULL DEFAULT 0 AFTER `n
CHANGE COLUMN `field222` `spell_class` int(11) NULL DEFAULT 0 AFTER `caster_requirement_id`, CHANGE COLUMN `field222` `spell_class` int(11) NULL DEFAULT 0 AFTER `caster_requirement_id`,
CHANGE COLUMN `field223` `spell_subclass` int(11) NULL DEFAULT 0 AFTER `spell_class`, CHANGE COLUMN `field223` `spell_subclass` int(11) NULL DEFAULT 0 AFTER `spell_class`,
CHANGE COLUMN `field232` `no_remove` int(11) NOT NULL DEFAULT 0 AFTER `min_range`; CHANGE COLUMN `field232` `no_remove` int(11) NOT NULL DEFAULT 0 AFTER `min_range`;
)"
},
ManifestEntry{
.version = 9329,
.description = "2025_08_20_character_stat_caps.sql",
.check = "SHOW TABLES LIKE 'character_stat_caps'",
.condition = "empty",
.match = "",
.sql = R"(
CREATE TABLE `character_stat_caps` (
`character_id` int(11) UNSIGNED NOT NULL,
`stat_id` tinyint(3) UNSIGNED NULL,
`stat_cap` int(11) NOT NULL DEFAULT -1,
PRIMARY KEY (`character_id`, `stat_id`)
)
)" )"
} }
@@ -204,11 +204,11 @@ INNER JOIN bot_stances bs ON bd.bot_id = bs.bot_id
WHERE bd.follow_distance != 184 WHERE bd.follow_distance != 184
GROUP BY bd.bot_id; GROUP BY bd.bot_id;
INSERT INTO bot_settings INSERT INTO bot_settings
SELECT 0, bd.bot_id, bs.stance_id, 3, 0, bd.stop_melee_level, 'BaseSetting', 'StopMeleeLevel' SELECT 0, bd.bot_id, bs.stance_id, 3, 0, bd.stop_melee_level, 'BaseSetting', 'StopMeleeLevel'
FROM bot_data bd FROM bot_data bd
INNER JOIN bot_stances bs ON bd.bot_id = bs.bot_id INNER JOIN bot_stances bs ON bd.bot_id = bs.bot_id
WHERE (CASE WHERE (CASE
WHEN bd.class IN (2, 6, 10, 11, 12, 13, 14) THEN 13 WHEN bd.class IN (2, 6, 10, 11, 12, 13, 14) THEN 13
ELSE 255 ELSE 255
END) != bd.stop_melee_level END) != bd.stop_melee_level
@@ -532,7 +532,7 @@ UPDATE bot_spells_entries SET `type` = 17 WHERE `spell_id` = 3229;
.condition = "empty", .condition = "empty",
.match = "", .match = "",
.sql = R"( .sql = R"(
INSERT INTO `bot_spells_entries` (`npc_spells_id`, `spell_id`, `type`, `minlevel`, `maxlevel`) INSERT INTO `bot_spells_entries` (`npc_spells_id`, `spell_id`, `type`, `minlevel`, `maxlevel`)
VALUES VALUES
(3006, 9957, 100, 20, 254), (3006, 9957, 100, 20, 254),
(3006, 9956, 100, 20, 254), (3006, 9956, 100, 20, 254),
@@ -1106,7 +1106,7 @@ FROM bot_spells_entries
WHERE `npc_spells_id` = 3005 WHERE `npc_spells_id` = 3005
AND `type` = 10; AND `type` = 10;
INSERT INTO `bot_spells_entries` (`npc_spells_id`, `spell_id`, `type`, `minlevel`, `maxlevel`, `priority`) INSERT INTO `bot_spells_entries` (`npc_spells_id`, `spell_id`, `type`, `minlevel`, `maxlevel`, `priority`)
VALUES VALUES
(3003, 10173, 24, 72, 76, 3), (3003, 10173, 24, 72, 76, 3),
(3003, 10174, 24, 72, 76, 2), (3003, 10174, 24, 72, 76, 2),
@@ -2122,7 +2122,22 @@ WHERE NOT EXISTS
FROM spells_new FROM spells_new
WHERE bot_spells_entries.spell_id = spells_new.id); WHERE bot_spells_entries.spell_id = spells_new.id);
)", )",
} },
ManifestEntry{
.version = 9055,
.description = "08_20_2025_bot_stat_caps.sql",
.check = "SHOW TABLES LIKE 'bot_stat_caps'",
.condition = "empty",
.match = "",
.sql = R"(
CREATE TABLE `bot_stat_caps` (
`bot_id` int(11) UNSIGNED NOT NULL,
`stat_id` tinyint(3) UNSIGNED NULL,
`stat_cap` int(11) NOT NULL DEFAULT -1,
PRIMARY KEY (`bot_id`, `stat_id`)
)
)"
},
// -- template; copy/paste this when you need to create a new entry // -- template; copy/paste this when you need to create a new entry
// ManifestEntry{ // ManifestEntry{
// .version = 9228, // .version = 9228,
+3
View File
@@ -69,6 +69,7 @@ namespace DatabaseSchema {
{"character_potionbelt", "id"}, {"character_potionbelt", "id"},
{"character_skills", "id"}, {"character_skills", "id"},
{"character_spells", "id"}, {"character_spells", "id"},
{"character_stat_caps", "character_id"},
{"character_stats_record", "character_id"}, {"character_stats_record", "character_id"},
{"character_task_timers", "character_id"}, {"character_task_timers", "character_id"},
{"character_tasks", "charid"}, {"character_tasks", "charid"},
@@ -144,6 +145,7 @@ namespace DatabaseSchema {
"character_potionbelt", "character_potionbelt",
"character_skills", "character_skills",
"character_spells", "character_spells",
"character_stat_caps",
"character_stats_record", "character_stats_record",
"character_task_timers", "character_task_timers",
"character_tasks", "character_tasks",
@@ -412,6 +414,7 @@ namespace DatabaseSchema {
"bot_spell_settings", "bot_spell_settings",
"bot_spells_entries", "bot_spells_entries",
"bot_stances", "bot_stances",
"bot_stat_caps",
"bot_timers" "bot_timers"
}; };
} }
+10
View File
@@ -471,3 +471,13 @@ bool PetType::IsValid(uint8 pet_type)
{ {
return pet_types.find(pet_type) != pet_types.end(); return pet_types.find(pet_type) != pet_types.end();
} }
std::string StatCap::GetName(uint8 stat_id)
{
return IsValid(stat_id) ? stat_caps[stat_id] : "UNKNOWN STAT CAP";
}
bool StatCap::IsValid(uint8 stat_id)
{
return stat_caps.find(stat_id) != stat_caps.end();
}
+53
View File
@@ -919,4 +919,57 @@ namespace PetType {
bool IsValid(uint8 pet_type); bool IsValid(uint8 pet_type);
} }
namespace StatCap {
constexpr uint8 Accuracy = 0;
constexpr uint8 Attack = 1;
constexpr uint8 Avoidance = 2;
constexpr uint8 Clairvoyance = 3;
constexpr uint8 CombatEffects = 4;
constexpr uint8 DamageShield = 5;
constexpr uint8 DOTShielding = 6;
constexpr uint8 DSMitigation = 7;
constexpr uint8 EnduranceRegen = 8;
constexpr uint8 ExtraDamage = 9;
constexpr uint8 Haste = 10;
constexpr uint8 HasteV3 = 11;
constexpr uint8 HealAmount = 12;
constexpr uint8 HealthRegen = 13;
constexpr uint8 ManaRegen = 14;
constexpr uint8 QuiverHaste = 15;
constexpr uint8 Shielding = 16;
constexpr uint8 SpellDamage = 17;
constexpr uint8 SpellShielding = 18;
constexpr uint8 Stat = 19;
constexpr uint8 Strikethrough = 20;
constexpr uint8 StunResist = 21;
static std::map<uint8, std::string> stat_caps = {
{ Accuracy, "Accuracy" },
{ Attack, "Attack" },
{ Avoidance, "Avoidance" },
{ Clairvoyance, "Clairvoyance" },
{ CombatEffects, "Combat Effects" },
{ DamageShield, "Damage Shield" },
{ DOTShielding, "Damage Over Time Shielding" },
{ DSMitigation, "Damage Shield Mitigation" },
{ EnduranceRegen, "Endurance Regen" },
{ ExtraDamage, "Extra Damage" },
{ Haste, "Haste" },
{ HasteV3, "Haste V3" },
{ HealAmount, "Heal Amount" },
{ HealthRegen, "Health Regen" },
{ ManaRegen, "Mana Regen" },
{ QuiverHaste, "Quiver Haste" },
{ Shielding, "Shielding" },
{ SpellDamage, "Spell Damage" },
{ SpellShielding, "Spell Shielding" },
{ Stat, "Stat" },
{ Strikethrough, "Strikethrough" },
{ StunResist, "Stun Resist" }
};
std::string GetName(uint8 stat_id);
bool IsValid(uint8 stat_id);
}
#endif /*COMMON_EMU_CONSTANTS_H*/ #endif /*COMMON_EMU_CONSTANTS_H*/
@@ -0,0 +1,404 @@
/**
* DO NOT MODIFY THIS FILE
*
* This repository was automatically generated and is NOT to be modified directly.
* Any repository modifications are meant to be made to the repository extending the base.
* Any modifications to base repositories are to be made by the generator only
*
* @generator ./utils/scripts/generators/repository-generator.pl
* @docs https://docs.eqemu.io/developer/repositories
*/
#ifndef EQEMU_BASE_BOT_STAT_CAPS_REPOSITORY_H
#define EQEMU_BASE_BOT_STAT_CAPS_REPOSITORY_H
#include "../../database.h"
#include "../../strings.h"
#include <ctime>
class BaseBotStatCapsRepository {
public:
struct BotStatCaps {
uint32_t bot_id;
uint8_t stat_id;
int32_t stat_cap;
};
static std::string PrimaryKey()
{
return std::string("bot_id");
}
static std::vector<std::string> Columns()
{
return {
"bot_id",
"stat_id",
"stat_cap",
};
}
static std::vector<std::string> SelectColumns()
{
return {
"bot_id",
"stat_id",
"stat_cap",
};
}
static std::string ColumnsRaw()
{
return std::string(Strings::Implode(", ", Columns()));
}
static std::string SelectColumnsRaw()
{
return std::string(Strings::Implode(", ", SelectColumns()));
}
static std::string TableName()
{
return std::string("bot_stat_caps");
}
static std::string BaseSelect()
{
return fmt::format(
"SELECT {} FROM {}",
SelectColumnsRaw(),
TableName()
);
}
static std::string BaseInsert()
{
return fmt::format(
"INSERT INTO {} ({}) ",
TableName(),
ColumnsRaw()
);
}
static BotStatCaps NewEntity()
{
BotStatCaps e{};
e.bot_id = 0;
e.stat_id = 0;
e.stat_cap = -1;
return e;
}
static BotStatCaps GetBotStatCaps(
const std::vector<BotStatCaps> &bot_stat_capss,
int bot_stat_caps_id
)
{
for (auto &bot_stat_caps : bot_stat_capss) {
if (bot_stat_caps.bot_id == bot_stat_caps_id) {
return bot_stat_caps;
}
}
return NewEntity();
}
static BotStatCaps FindOne(
Database& db,
int bot_stat_caps_id
)
{
auto results = db.QueryDatabase(
fmt::format(
"{} WHERE {} = {} LIMIT 1",
BaseSelect(),
PrimaryKey(),
bot_stat_caps_id
)
);
auto row = results.begin();
if (results.RowCount() == 1) {
BotStatCaps e{};
e.bot_id = row[0] ? static_cast<uint32_t>(strtoul(row[0], nullptr, 10)) : 0;
e.stat_id = row[1] ? static_cast<uint8_t>(strtoul(row[1], nullptr, 10)) : 0;
e.stat_cap = row[2] ? static_cast<int32_t>(atoi(row[2])) : -1;
return e;
}
return NewEntity();
}
static int DeleteOne(
Database& db,
int bot_stat_caps_id
)
{
auto results = db.QueryDatabase(
fmt::format(
"DELETE FROM {} WHERE {} = {}",
TableName(),
PrimaryKey(),
bot_stat_caps_id
)
);
return (results.Success() ? results.RowsAffected() : 0);
}
static int UpdateOne(
Database& db,
const BotStatCaps &e
)
{
std::vector<std::string> v;
auto columns = Columns();
v.push_back(columns[0] + " = " + std::to_string(e.bot_id));
v.push_back(columns[1] + " = " + std::to_string(e.stat_id));
v.push_back(columns[2] + " = " + std::to_string(e.stat_cap));
auto results = db.QueryDatabase(
fmt::format(
"UPDATE {} SET {} WHERE {} = {}",
TableName(),
Strings::Implode(", ", v),
PrimaryKey(),
e.bot_id
)
);
return (results.Success() ? results.RowsAffected() : 0);
}
static BotStatCaps InsertOne(
Database& db,
BotStatCaps e
)
{
std::vector<std::string> v;
v.push_back(std::to_string(e.bot_id));
v.push_back(std::to_string(e.stat_id));
v.push_back(std::to_string(e.stat_cap));
auto results = db.QueryDatabase(
fmt::format(
"{} VALUES ({})",
BaseInsert(),
Strings::Implode(",", v)
)
);
if (results.Success()) {
e.bot_id = results.LastInsertedID();
return e;
}
e = NewEntity();
return e;
}
static int InsertMany(
Database& db,
const std::vector<BotStatCaps> &entries
)
{
std::vector<std::string> insert_chunks;
for (auto &e: entries) {
std::vector<std::string> v;
v.push_back(std::to_string(e.bot_id));
v.push_back(std::to_string(e.stat_id));
v.push_back(std::to_string(e.stat_cap));
insert_chunks.push_back("(" + Strings::Implode(",", v) + ")");
}
std::vector<std::string> v;
auto results = db.QueryDatabase(
fmt::format(
"{} VALUES {}",
BaseInsert(),
Strings::Implode(",", insert_chunks)
)
);
return (results.Success() ? results.RowsAffected() : 0);
}
static std::vector<BotStatCaps> All(Database& db)
{
std::vector<BotStatCaps> all_entries;
auto results = db.QueryDatabase(
fmt::format(
"{}",
BaseSelect()
)
);
all_entries.reserve(results.RowCount());
for (auto row = results.begin(); row != results.end(); ++row) {
BotStatCaps e{};
e.bot_id = row[0] ? static_cast<uint32_t>(strtoul(row[0], nullptr, 10)) : 0;
e.stat_id = row[1] ? static_cast<uint8_t>(strtoul(row[1], nullptr, 10)) : 0;
e.stat_cap = row[2] ? static_cast<int32_t>(atoi(row[2])) : -1;
all_entries.push_back(e);
}
return all_entries;
}
static std::vector<BotStatCaps> GetWhere(Database& db, const std::string &where_filter)
{
std::vector<BotStatCaps> all_entries;
auto results = db.QueryDatabase(
fmt::format(
"{} WHERE {}",
BaseSelect(),
where_filter
)
);
all_entries.reserve(results.RowCount());
for (auto row = results.begin(); row != results.end(); ++row) {
BotStatCaps e{};
e.bot_id = row[0] ? static_cast<uint32_t>(strtoul(row[0], nullptr, 10)) : 0;
e.stat_id = row[1] ? static_cast<uint8_t>(strtoul(row[1], nullptr, 10)) : 0;
e.stat_cap = row[2] ? static_cast<int32_t>(atoi(row[2])) : -1;
all_entries.push_back(e);
}
return all_entries;
}
static int DeleteWhere(Database& db, const std::string &where_filter)
{
auto results = db.QueryDatabase(
fmt::format(
"DELETE FROM {} WHERE {}",
TableName(),
where_filter
)
);
return (results.Success() ? results.RowsAffected() : 0);
}
static int Truncate(Database& db)
{
auto results = db.QueryDatabase(
fmt::format(
"TRUNCATE TABLE {}",
TableName()
)
);
return (results.Success() ? results.RowsAffected() : 0);
}
static int64 GetMaxId(Database& db)
{
auto results = db.QueryDatabase(
fmt::format(
"SELECT COALESCE(MAX({}), 0) FROM {}",
PrimaryKey(),
TableName()
)
);
return (results.Success() && results.begin()[0] ? strtoll(results.begin()[0], nullptr, 10) : 0);
}
static int64 Count(Database& db, const std::string &where_filter = "")
{
auto results = db.QueryDatabase(
fmt::format(
"SELECT COUNT(*) FROM {} {}",
TableName(),
(where_filter.empty() ? "" : "WHERE " + where_filter)
)
);
return (results.Success() && results.begin()[0] ? strtoll(results.begin()[0], nullptr, 10) : 0);
}
static std::string BaseReplace()
{
return fmt::format(
"REPLACE INTO {} ({}) ",
TableName(),
ColumnsRaw()
);
}
static int ReplaceOne(
Database& db,
const BotStatCaps &e
)
{
std::vector<std::string> v;
v.push_back(std::to_string(e.bot_id));
v.push_back(std::to_string(e.stat_id));
v.push_back(std::to_string(e.stat_cap));
auto results = db.QueryDatabase(
fmt::format(
"{} VALUES ({})",
BaseReplace(),
Strings::Implode(",", v)
)
);
return (results.Success() ? results.RowsAffected() : 0);
}
static int ReplaceMany(
Database& db,
const std::vector<BotStatCaps> &entries
)
{
std::vector<std::string> insert_chunks;
for (auto &e: entries) {
std::vector<std::string> v;
v.push_back(std::to_string(e.bot_id));
v.push_back(std::to_string(e.stat_id));
v.push_back(std::to_string(e.stat_cap));
insert_chunks.push_back("(" + Strings::Implode(",", v) + ")");
}
std::vector<std::string> v;
auto results = db.QueryDatabase(
fmt::format(
"{} VALUES {}",
BaseReplace(),
Strings::Implode(",", insert_chunks)
)
);
return (results.Success() ? results.RowsAffected() : 0);
}
};
#endif //EQEMU_BASE_BOT_STAT_CAPS_REPOSITORY_H
@@ -0,0 +1,404 @@
/**
* DO NOT MODIFY THIS FILE
*
* This repository was automatically generated and is NOT to be modified directly.
* Any repository modifications are meant to be made to the repository extending the base.
* Any modifications to base repositories are to be made by the generator only
*
* @generator ./utils/scripts/generators/repository-generator.pl
* @docs https://docs.eqemu.io/developer/repositories
*/
#ifndef EQEMU_BASE_CHARACTER_STAT_CAPS_REPOSITORY_H
#define EQEMU_BASE_CHARACTER_STAT_CAPS_REPOSITORY_H
#include "../../database.h"
#include "../../strings.h"
#include <ctime>
class BaseCharacterStatCapsRepository {
public:
struct CharacterStatCaps {
uint32_t character_id;
uint8_t stat_id;
int32_t stat_cap;
};
static std::string PrimaryKey()
{
return std::string("character_id");
}
static std::vector<std::string> Columns()
{
return {
"character_id",
"stat_id",
"stat_cap",
};
}
static std::vector<std::string> SelectColumns()
{
return {
"character_id",
"stat_id",
"stat_cap",
};
}
static std::string ColumnsRaw()
{
return std::string(Strings::Implode(", ", Columns()));
}
static std::string SelectColumnsRaw()
{
return std::string(Strings::Implode(", ", SelectColumns()));
}
static std::string TableName()
{
return std::string("character_stat_caps");
}
static std::string BaseSelect()
{
return fmt::format(
"SELECT {} FROM {}",
SelectColumnsRaw(),
TableName()
);
}
static std::string BaseInsert()
{
return fmt::format(
"INSERT INTO {} ({}) ",
TableName(),
ColumnsRaw()
);
}
static CharacterStatCaps NewEntity()
{
CharacterStatCaps e{};
e.character_id = 0;
e.stat_id = 0;
e.stat_cap = -1;
return e;
}
static CharacterStatCaps GetCharacterStatCaps(
const std::vector<CharacterStatCaps> &character_stat_capss,
int character_stat_caps_id
)
{
for (auto &character_stat_caps : character_stat_capss) {
if (character_stat_caps.character_id == character_stat_caps_id) {
return character_stat_caps;
}
}
return NewEntity();
}
static CharacterStatCaps FindOne(
Database& db,
int character_stat_caps_id
)
{
auto results = db.QueryDatabase(
fmt::format(
"{} WHERE {} = {} LIMIT 1",
BaseSelect(),
PrimaryKey(),
character_stat_caps_id
)
);
auto row = results.begin();
if (results.RowCount() == 1) {
CharacterStatCaps e{};
e.character_id = row[0] ? static_cast<uint32_t>(strtoul(row[0], nullptr, 10)) : 0;
e.stat_id = row[1] ? static_cast<uint8_t>(strtoul(row[1], nullptr, 10)) : 0;
e.stat_cap = row[2] ? static_cast<int32_t>(atoi(row[2])) : -1;
return e;
}
return NewEntity();
}
static int DeleteOne(
Database& db,
int character_stat_caps_id
)
{
auto results = db.QueryDatabase(
fmt::format(
"DELETE FROM {} WHERE {} = {}",
TableName(),
PrimaryKey(),
character_stat_caps_id
)
);
return (results.Success() ? results.RowsAffected() : 0);
}
static int UpdateOne(
Database& db,
const CharacterStatCaps &e
)
{
std::vector<std::string> v;
auto columns = Columns();
v.push_back(columns[0] + " = " + std::to_string(e.character_id));
v.push_back(columns[1] + " = " + std::to_string(e.stat_id));
v.push_back(columns[2] + " = " + std::to_string(e.stat_cap));
auto results = db.QueryDatabase(
fmt::format(
"UPDATE {} SET {} WHERE {} = {}",
TableName(),
Strings::Implode(", ", v),
PrimaryKey(),
e.character_id
)
);
return (results.Success() ? results.RowsAffected() : 0);
}
static CharacterStatCaps InsertOne(
Database& db,
CharacterStatCaps e
)
{
std::vector<std::string> v;
v.push_back(std::to_string(e.character_id));
v.push_back(std::to_string(e.stat_id));
v.push_back(std::to_string(e.stat_cap));
auto results = db.QueryDatabase(
fmt::format(
"{} VALUES ({})",
BaseInsert(),
Strings::Implode(",", v)
)
);
if (results.Success()) {
e.character_id = results.LastInsertedID();
return e;
}
e = NewEntity();
return e;
}
static int InsertMany(
Database& db,
const std::vector<CharacterStatCaps> &entries
)
{
std::vector<std::string> insert_chunks;
for (auto &e: entries) {
std::vector<std::string> v;
v.push_back(std::to_string(e.character_id));
v.push_back(std::to_string(e.stat_id));
v.push_back(std::to_string(e.stat_cap));
insert_chunks.push_back("(" + Strings::Implode(",", v) + ")");
}
std::vector<std::string> v;
auto results = db.QueryDatabase(
fmt::format(
"{} VALUES {}",
BaseInsert(),
Strings::Implode(",", insert_chunks)
)
);
return (results.Success() ? results.RowsAffected() : 0);
}
static std::vector<CharacterStatCaps> All(Database& db)
{
std::vector<CharacterStatCaps> all_entries;
auto results = db.QueryDatabase(
fmt::format(
"{}",
BaseSelect()
)
);
all_entries.reserve(results.RowCount());
for (auto row = results.begin(); row != results.end(); ++row) {
CharacterStatCaps e{};
e.character_id = row[0] ? static_cast<uint32_t>(strtoul(row[0], nullptr, 10)) : 0;
e.stat_id = row[1] ? static_cast<uint8_t>(strtoul(row[1], nullptr, 10)) : 0;
e.stat_cap = row[2] ? static_cast<int32_t>(atoi(row[2])) : -1;
all_entries.push_back(e);
}
return all_entries;
}
static std::vector<CharacterStatCaps> GetWhere(Database& db, const std::string &where_filter)
{
std::vector<CharacterStatCaps> all_entries;
auto results = db.QueryDatabase(
fmt::format(
"{} WHERE {}",
BaseSelect(),
where_filter
)
);
all_entries.reserve(results.RowCount());
for (auto row = results.begin(); row != results.end(); ++row) {
CharacterStatCaps e{};
e.character_id = row[0] ? static_cast<uint32_t>(strtoul(row[0], nullptr, 10)) : 0;
e.stat_id = row[1] ? static_cast<uint8_t>(strtoul(row[1], nullptr, 10)) : 0;
e.stat_cap = row[2] ? static_cast<int32_t>(atoi(row[2])) : -1;
all_entries.push_back(e);
}
return all_entries;
}
static int DeleteWhere(Database& db, const std::string &where_filter)
{
auto results = db.QueryDatabase(
fmt::format(
"DELETE FROM {} WHERE {}",
TableName(),
where_filter
)
);
return (results.Success() ? results.RowsAffected() : 0);
}
static int Truncate(Database& db)
{
auto results = db.QueryDatabase(
fmt::format(
"TRUNCATE TABLE {}",
TableName()
)
);
return (results.Success() ? results.RowsAffected() : 0);
}
static int64 GetMaxId(Database& db)
{
auto results = db.QueryDatabase(
fmt::format(
"SELECT COALESCE(MAX({}), 0) FROM {}",
PrimaryKey(),
TableName()
)
);
return (results.Success() && results.begin()[0] ? strtoll(results.begin()[0], nullptr, 10) : 0);
}
static int64 Count(Database& db, const std::string &where_filter = "")
{
auto results = db.QueryDatabase(
fmt::format(
"SELECT COUNT(*) FROM {} {}",
TableName(),
(where_filter.empty() ? "" : "WHERE " + where_filter)
)
);
return (results.Success() && results.begin()[0] ? strtoll(results.begin()[0], nullptr, 10) : 0);
}
static std::string BaseReplace()
{
return fmt::format(
"REPLACE INTO {} ({}) ",
TableName(),
ColumnsRaw()
);
}
static int ReplaceOne(
Database& db,
const CharacterStatCaps &e
)
{
std::vector<std::string> v;
v.push_back(std::to_string(e.character_id));
v.push_back(std::to_string(e.stat_id));
v.push_back(std::to_string(e.stat_cap));
auto results = db.QueryDatabase(
fmt::format(
"{} VALUES ({})",
BaseReplace(),
Strings::Implode(",", v)
)
);
return (results.Success() ? results.RowsAffected() : 0);
}
static int ReplaceMany(
Database& db,
const std::vector<CharacterStatCaps> &entries
)
{
std::vector<std::string> insert_chunks;
for (auto &e: entries) {
std::vector<std::string> v;
v.push_back(std::to_string(e.character_id));
v.push_back(std::to_string(e.stat_id));
v.push_back(std::to_string(e.stat_cap));
insert_chunks.push_back("(" + Strings::Implode(",", v) + ")");
}
std::vector<std::string> v;
auto results = db.QueryDatabase(
fmt::format(
"{} VALUES {}",
BaseReplace(),
Strings::Implode(",", insert_chunks)
)
);
return (results.Success() ? results.RowsAffected() : 0);
}
};
#endif //EQEMU_BASE_CHARACTER_STAT_CAPS_REPOSITORY_H
@@ -0,0 +1,50 @@
#ifndef EQEMU_BOT_STAT_CAPS_REPOSITORY_H
#define EQEMU_BOT_STAT_CAPS_REPOSITORY_H
#include "../database.h"
#include "../strings.h"
#include "base/base_bot_stat_caps_repository.h"
class BotStatCapsRepository: public BaseBotStatCapsRepository {
public:
/**
* This file was auto generated and can be modified and extended upon
*
* Base repository methods are automatically
* generated in the "base" version of this repository. The base repository
* is immutable and to be left untouched, while methods in this class
* are used as extension methods for more specific persistence-layer
* accessors or mutators.
*
* Base Methods (Subject to be expanded upon in time)
*
* Note: Not all tables are designed appropriately to fit functionality with all base methods
*
* InsertOne
* UpdateOne
* DeleteOne
* FindOne
* GetWhere(std::string where_filter)
* DeleteWhere(std::string where_filter)
* InsertMany
* All
*
* Example custom methods in a repository
*
* BotStatCapsRepository::GetByZoneAndVersion(int zone_id, int zone_version)
* BotStatCapsRepository::GetWhereNeverExpires()
* BotStatCapsRepository::GetWhereXAndY()
* BotStatCapsRepository::DeleteWhereXAndY()
*
* Most of the above could be covered by base methods, but if you as a developer
* find yourself re-using logic for other parts of the code, its best to just make a
* method that can be re-used easily elsewhere especially if it can use a base repository
* method and encapsulate filters there
*/
// Custom extended repository methods here
};
#endif //EQEMU_BOT_STAT_CAPS_REPOSITORY_H
@@ -0,0 +1,50 @@
#ifndef EQEMU_CHARACTER_STAT_CAPS_REPOSITORY_H
#define EQEMU_CHARACTER_STAT_CAPS_REPOSITORY_H
#include "../database.h"
#include "../strings.h"
#include "base/base_character_stat_caps_repository.h"
class CharacterStatCapsRepository: public BaseCharacterStatCapsRepository {
public:
/**
* This file was auto generated and can be modified and extended upon
*
* Base repository methods are automatically
* generated in the "base" version of this repository. The base repository
* is immutable and to be left untouched, while methods in this class
* are used as extension methods for more specific persistence-layer
* accessors or mutators.
*
* Base Methods (Subject to be expanded upon in time)
*
* Note: Not all tables are designed appropriately to fit functionality with all base methods
*
* InsertOne
* UpdateOne
* DeleteOne
* FindOne
* GetWhere(std::string where_filter)
* DeleteWhere(std::string where_filter)
* InsertMany
* All
*
* Example custom methods in a repository
*
* CharacterStatCapsRepository::GetByZoneAndVersion(int zone_id, int zone_version)
* CharacterStatCapsRepository::GetWhereNeverExpires()
* CharacterStatCapsRepository::GetWhereXAndY()
* CharacterStatCapsRepository::DeleteWhereXAndY()
*
* Most of the above could be covered by base methods, but if you as a developer
* find yourself re-using logic for other parts of the code, its best to just make a
* method that can be re-used easily elsewhere especially if it can use a base repository
* method and encapsulate filters there
*/
// Custom extended repository methods here
};
#endif //EQEMU_CHARACTER_STAT_CAPS_REPOSITORY_H
+2 -2
View File
@@ -42,8 +42,8 @@
* Manifest: https://github.com/EQEmu/Server/blob/master/utils/sql/db_update_manifest.txt * Manifest: https://github.com/EQEmu/Server/blob/master/utils/sql/db_update_manifest.txt
*/ */
#define CURRENT_BINARY_DATABASE_VERSION 9328 #define CURRENT_BINARY_DATABASE_VERSION 9329
#define CURRENT_BINARY_BOTS_DATABASE_VERSION 9054 #define CURRENT_BINARY_BOTS_DATABASE_VERSION 9055
#define CUSTOM_BINARY_DATABASE_VERSION 0 #define CUSTOM_BINARY_DATABASE_VERSION 0
#endif #endif
+1 -1
View File
@@ -6701,7 +6701,7 @@ void Client::SetAttackTimer()
if (ItemToUse && ItemToUse->ItemType == EQ::item::ItemTypeBow) { if (ItemToUse && ItemToUse->ItemType == EQ::item::ItemTypeBow) {
// Live actually had a bug here where they would return the non-modified attack speed // Live actually had a bug here where they would return the non-modified attack speed
// rather than the cap ... // rather than the cap ...
speed = std::max(speed - GetQuiverHaste(speed), RuleI(Combat, QuiverHasteCap)); speed = std::max(speed - GetQuiverHaste(speed), GetStatCap(StatCap::QuiverHaste));
} }
else { else {
if (RuleB(Spells, Jun182014HundredHandsRevamp)) if (RuleB(Spells, Jun182014HundredHandsRevamp))
+46 -21
View File
@@ -246,16 +246,24 @@ void Mob::ProcessItemCaps()
itembonuses.ATK = std::min(itembonuses.ATK, CalcItemATKCap()); itembonuses.ATK = std::min(itembonuses.ATK, CalcItemATKCap());
if (IsOfClientBotMerc() && itembonuses.SpellDmg > RuleI(Character, ItemSpellDmgCap)) { if (IsOfClientBotMerc() && itembonuses.SpellDmg > GetStatCap(StatCap::SpellDamage)) {
itembonuses.SpellDmg = RuleI(Character, ItemSpellDmgCap); itembonuses.SpellDmg = GetStatCap(StatCap::SpellDamage);
} }
if (IsOfClientBotMerc() && itembonuses.HealAmt > RuleI(Character, ItemHealAmtCap)) { if (IsOfClientBotMerc() && itembonuses.HealAmt > GetStatCap(StatCap::HealAmount)) {
itembonuses.HealAmt = RuleI(Character, ItemHealAmtCap); itembonuses.HealAmt = GetStatCap(StatCap::HealAmount);
} }
} }
void Mob::AddItemBonuses(const EQ::ItemInstance* inst, StatBonuses* b, bool is_augment, bool is_tribute, int recommended_level_override, bool is_ammo_item) { void Mob::AddItemBonuses(
const EQ::ItemInstance* inst,
StatBonuses* b,
bool is_augment,
bool is_tribute,
int recommended_level_override,
bool is_ammo_item
)
{
if (!inst || !inst->IsClassCommon()) { if (!inst || !inst->IsClassCommon()) {
return; return;
} }
@@ -352,20 +360,29 @@ void Mob::AddItemBonuses(const EQ::ItemInstance* inst, StatBonuses* b, bool is_a
b->EnduranceRegen += CalcItemBonus(item->EnduranceRegen); b->EnduranceRegen += CalcItemBonus(item->EnduranceRegen);
// These have rule-configured caps. // These have rule-configured caps.
b->ATK = CalcCappedItemBonus(b->ATK, item->Attack, RuleI(Character, ItemATKCap) + itembonuses.ItemATKCap + spellbonuses.ItemATKCap + aabonuses.ItemATKCap); b->ATK = CalcCappedItemBonus(
b->DamageShield = CalcCappedItemBonus(b->DamageShield, item->DamageShield, RuleI(Character, ItemDamageShieldCap)); b->ATK,
b->SpellShield = CalcCappedItemBonus(b->SpellShield, item->SpellShield, RuleI(Character, ItemSpellShieldingCap)); item->Attack,
b->MeleeMitigation = CalcCappedItemBonus(b->MeleeMitigation, item->Shielding, RuleI(Character, ItemShieldingCap)); (
b->StunResist = CalcCappedItemBonus(b->StunResist, item->StunResist, RuleI(Character, ItemStunResistCap)); GetStatCap(StatCap::Attack) +
b->StrikeThrough = CalcCappedItemBonus(b->StrikeThrough, item->StrikeThrough, RuleI(Character, ItemStrikethroughCap)); itembonuses.ItemATKCap +
b->AvoidMeleeChance = CalcCappedItemBonus(b->AvoidMeleeChance, item->Avoidance, RuleI(Character, ItemAvoidanceCap)); spellbonuses.ItemATKCap +
b->HitChance = CalcCappedItemBonus(b->HitChance, item->Accuracy, RuleI(Character, ItemAccuracyCap)); aabonuses.ItemATKCap
b->ProcChance = CalcCappedItemBonus(b->ProcChance, item->CombatEffects, RuleI(Character, ItemCombatEffectsCap)); )
b->DoTShielding = CalcCappedItemBonus(b->DoTShielding, item->DotShielding, RuleI(Character, ItemDoTShieldingCap)); );
b->HealAmt = CalcCappedItemBonus(b->HealAmt, item->HealAmt, RuleI(Character, ItemHealAmtCap)); b->AvoidMeleeChance = CalcCappedItemBonus(b->AvoidMeleeChance, item->Avoidance, GetStatCap(StatCap::Avoidance));
b->SpellDmg = CalcCappedItemBonus(b->SpellDmg, item->SpellDmg, RuleI(Character, ItemSpellDmgCap)); b->Clairvoyance = CalcCappedItemBonus(b->Clairvoyance, item->Clairvoyance, GetStatCap(StatCap::Clairvoyance));
b->Clairvoyance = CalcCappedItemBonus(b->Clairvoyance, item->Clairvoyance, RuleI(Character, ItemClairvoyanceCap)); b->DamageShield = CalcCappedItemBonus(b->DamageShield, item->DamageShield, GetStatCap(StatCap::DamageShield));
b->DSMitigation = CalcCappedItemBonus(b->DSMitigation, item->DSMitigation, RuleI(Character, ItemDSMitigationCap)); b->DoTShielding = CalcCappedItemBonus(b->DoTShielding, item->DotShielding, GetStatCap(StatCap::DOTShielding));
b->DSMitigation = CalcCappedItemBonus(b->DSMitigation, item->DSMitigation, GetStatCap(StatCap::DSMitigation));
b->HealAmt = CalcCappedItemBonus(b->HealAmt, item->HealAmt, GetStatCap(StatCap::HealAmount));
b->HitChance = CalcCappedItemBonus(b->HitChance, item->Accuracy, GetStatCap(StatCap::Accuracy));
b->MeleeMitigation = CalcCappedItemBonus(b->MeleeMitigation, item->Shielding, GetStatCap(StatCap::Shielding));
b->ProcChance = CalcCappedItemBonus(b->ProcChance, item->CombatEffects, GetStatCap(StatCap::CombatEffects));
b->SpellDmg = CalcCappedItemBonus(b->SpellDmg, item->SpellDmg, GetStatCap(StatCap::SpellDamage));
b->SpellShield = CalcCappedItemBonus(b->SpellShield, item->SpellShield, GetStatCap(StatCap::SpellShielding));
b->StrikeThrough = CalcCappedItemBonus(b->StrikeThrough, item->StrikeThrough, GetStatCap(StatCap::Strikethrough));
b->StunResist = CalcCappedItemBonus(b->StunResist, item->StunResist, GetStatCap(StatCap::StunResist));
if (b->haste < item->Haste) { if (b->haste < item->Haste) {
b->haste = item->Haste; b->haste = item->Haste;
@@ -374,10 +391,18 @@ void Mob::AddItemBonuses(const EQ::ItemInstance* inst, StatBonuses* b, bool is_a
if (item->ExtraDmgAmt != 0 && item->ExtraDmgSkill <= EQ::skills::HIGHEST_SKILL) { if (item->ExtraDmgAmt != 0 && item->ExtraDmgSkill <= EQ::skills::HIGHEST_SKILL) {
if (item->ExtraDmgSkill == ALL_SKILLS) { if (item->ExtraDmgSkill == ALL_SKILLS) {
for (const auto &skill_id: EQ::skills::GetExtraDamageSkills()) { for (const auto &skill_id: EQ::skills::GetExtraDamageSkills()) {
b->SkillDamageAmount[skill_id] = CalcCappedItemBonus(b->SkillDamageAmount[skill_id], item->ExtraDmgAmt, RuleI(Character, ItemExtraDmgCap)); b->SkillDamageAmount[skill_id] = CalcCappedItemBonus(
b->SkillDamageAmount[skill_id],
item->ExtraDmgAmt,
GetStatCap(StatCap::ExtraDamage)
);
} }
} else { } else {
b->SkillDamageAmount[item->ExtraDmgSkill] = CalcCappedItemBonus(b->SkillDamageAmount[item->ExtraDmgSkill], item->ExtraDmgAmt, RuleI(Character, ItemExtraDmgCap)); b->SkillDamageAmount[item->ExtraDmgSkill] = CalcCappedItemBonus(
b->SkillDamageAmount[item->ExtraDmgSkill],
item->ExtraDmgAmt,
GetStatCap(StatCap::ExtraDamage)
);
} }
} }
+3
View File
@@ -268,6 +268,8 @@ Bot::Bot(
LoadDefaultBotSettings(); LoadDefaultBotSettings();
database.botdb.LoadBotSettings(this); database.botdb.LoadBotSettings(this);
database.LoadStatCaps(this);
if (RuleB(Bots, AllowBotBlockedBuffs)) { if (RuleB(Bots, AllowBotBlockedBuffs)) {
bot_blocked_buffs.clear(); bot_blocked_buffs.clear();
database.botdb.LoadBotBlockedBuffs(this); database.botdb.LoadBotBlockedBuffs(this);
@@ -1420,6 +1422,7 @@ bool Bot::Save()
database.botdb.SaveTimers(this); database.botdb.SaveTimers(this);
database.botdb.SaveStance(this); database.botdb.SaveStance(this);
database.botdb.SaveBotSettings(this); database.botdb.SaveBotSettings(this);
database.SaveStatCaps(this);
if (RuleB(Bots, AllowBotBlockedBuffs)) { if (RuleB(Bots, AllowBotBlockedBuffs)) {
database.botdb.SaveBotBlockedBuffs(this); database.botdb.SaveBotBlockedBuffs(this);
+6 -7
View File
@@ -101,7 +101,7 @@ namespace BotSettingCategories {
constexpr uint8 SpellTypeMinManaPct = 7; constexpr uint8 SpellTypeMinManaPct = 7;
constexpr uint8 SpellTypeMaxManaPct = 8; constexpr uint8 SpellTypeMaxManaPct = 8;
constexpr uint8 SpellTypeMinHPPct = 9; constexpr uint8 SpellTypeMinHPPct = 9;
constexpr uint8 SpellTypeMaxHPPct = 10; constexpr uint8 SpellTypeMaxHPPct = 10;
constexpr uint8 SpellTypeIdlePriority = 11; constexpr uint8 SpellTypeIdlePriority = 11;
constexpr uint8 SpellTypeEngagedPriority = 12; constexpr uint8 SpellTypeEngagedPriority = 12;
constexpr uint8 SpellTypePursuePriority = 13; constexpr uint8 SpellTypePursuePriority = 13;
@@ -166,7 +166,7 @@ namespace BotBaseSettings {
constexpr uint16 ExpansionBitmask = 0; constexpr uint16 ExpansionBitmask = 0;
constexpr uint16 ShowHelm = 1; constexpr uint16 ShowHelm = 1;
constexpr uint16 FollowDistance = 2; constexpr uint16 FollowDistance = 2;
constexpr uint16 StopMeleeLevel = 3; constexpr uint16 StopMeleeLevel = 3;
constexpr uint16 EnforceSpellSettings = 4; constexpr uint16 EnforceSpellSettings = 4;
constexpr uint16 RangedSetting = 5; constexpr uint16 RangedSetting = 5;
constexpr uint16 PetSetTypeSetting = 6; constexpr uint16 PetSetTypeSetting = 6;
@@ -487,7 +487,7 @@ public:
void SetCommandedSpell(bool value) { _commandedSpell = value; } void SetCommandedSpell(bool value) { _commandedSpell = value; }
bool IsPullingSpell() const { return _pullingSpell; } bool IsPullingSpell() const { return _pullingSpell; }
void SetPullingSpell(bool value) { _pullingSpell = value; } void SetPullingSpell(bool value) { _pullingSpell = value; }
void SetGuardMode(); void SetGuardMode();
void SetHoldMode(); void SetHoldMode();
@@ -550,7 +550,7 @@ public:
bool IsValidMezTarget(Mob* owner, Mob* npc, uint16 spell_id); bool IsValidMezTarget(Mob* owner, Mob* npc, uint16 spell_id);
// Cast checks // Cast checks
bool PrecastChecks(Mob* tar, uint16 spell_type); bool PrecastChecks(Mob* tar, uint16 spell_type);
bool CastChecks(uint16 spell_id, Mob* tar, uint16 spell_type, bool prechecks = false, bool ae_check = false); bool CastChecks(uint16 spell_id, Mob* tar, uint16 spell_type, bool prechecks = false, bool ae_check = false);
bool IsImmuneToBotSpell(uint16 spell_id, Mob* caster); bool IsImmuneToBotSpell(uint16 spell_id, Mob* caster);
bool CanCastSpellType(uint16 spell_type, uint16 spell_id, Mob* tar); bool CanCastSpellType(uint16 spell_type, uint16 spell_id, Mob* tar);
@@ -601,7 +601,7 @@ public:
void ResetBotSpellSettings(); void ResetBotSpellSettings();
void CopyBotBlockedBuffs(Bot* to); void CopyBotBlockedBuffs(Bot* to);
void CopyBotBlockedPetBuffs(Bot* to); void CopyBotBlockedPetBuffs(Bot* to);
void CleanBotBlockedBuffs(); void CleanBotBlockedBuffs();
void ClearBotBlockedBuffs() { bot_blocked_buffs.clear(); } void ClearBotBlockedBuffs() { bot_blocked_buffs.clear(); }
bool IsBlockedBuff(int32 spell_id) override; bool IsBlockedBuff(int32 spell_id) override;
@@ -658,7 +658,7 @@ public:
bool GetBehindMob() const { return _behindMobStatus; } bool GetBehindMob() const { return _behindMobStatus; }
void SetBehindMob(bool value) { _behindMobStatus = value; } void SetBehindMob(bool value) { _behindMobStatus = value; }
bool GetMaxMeleeRange() const { return _maxMeleeRangeStatus; } bool GetMaxMeleeRange() const { return _maxMeleeRangeStatus; }
void SetMaxMeleeRange(bool value) { _maxMeleeRangeStatus = value; } void SetMaxMeleeRange(bool value) { _maxMeleeRangeStatus = value; }
uint8 GetStopMeleeLevel() const { return _stopMeleeLevel; } uint8 GetStopMeleeLevel() const { return _stopMeleeLevel; }
void SetStopMeleeLevel(uint8 level) { _stopMeleeLevel = level; } void SetStopMeleeLevel(uint8 level) { _stopMeleeLevel = level; }
uint32 GetBotDistanceRanged() const { return _distanceRanged; } uint32 GetBotDistanceRanged() const { return _distanceRanged; }
@@ -1210,7 +1210,6 @@ private:
bool _hasLoS; bool _hasLoS;
bool _commandedSpell; bool _commandedSpell;
bool _pullingSpell; bool _pullingSpell;
bool _illusionBlock; bool _illusionBlock;
std::vector<BotSpellSettings> m_bot_spell_settings; std::vector<BotSpellSettings> m_bot_spell_settings;
std::vector<Mob*> _spell_target_list; std::vector<Mob*> _spell_target_list;
+2
View File
@@ -1103,6 +1103,8 @@ bool Client::Save(uint8 iCommitNow) {
database.SaveCharacterEXPModifier(this); database.SaveCharacterEXPModifier(this);
database.SaveStatCaps(this);
if (RuleB(Bots, Enabled)) { if (RuleB(Bots, Enabled)) {
database.botdb.SaveBotSettings(this); database.botdb.SaveBotSettings(this);
} }
+17 -16
View File
@@ -32,23 +32,24 @@
int32 Client::GetMaxStat() const int32 Client::GetMaxStat() const
{ {
if ((RuleI(Character, StatCap)) > 0) { int character_cap = GetStatCap(StatCap::Stat);
return (RuleI(Character, StatCap)); if (character_cap > 0) {
return character_cap;
} }
int level = GetLevel();
int32 base = 0; uint8 level = GetLevel();
int base = 0;
if (level < 61) { if (level < 61) {
base = 255; base = 255;
} } else if (ClientVersion() >= EQ::versions::ClientVersion::SoF) {
else if (ClientVersion() >= EQ::versions::ClientVersion::SoF) {
base = 255 + 5 * (level - 60); base = 255 + 5 * (level - 60);
} } else if (level < 71) {
else if (level < 71) {
base = 255 + 5 * (level - 60); base = 255 + 5 * (level - 60);
} } else {
else {
base = 330; base = 330;
} }
return base; return base;
} }
@@ -300,7 +301,7 @@ int64 Client::CalcHPRegen(bool bCombat)
int64 Client::CalcHPRegenCap() int64 Client::CalcHPRegenCap()
{ {
int64 cap = RuleI(Character, ItemHealthRegenCap); int64 cap = GetStatCap(StatCap::HealthRegen);
if (GetLevel() > 60) { if (GetLevel() > 60) {
cap = std::max(cap, static_cast<int64>(GetLevel() - 30)); // if the rule is set greater than normal I guess cap = std::max(cap, static_cast<int64>(GetLevel() - 30)); // if the rule is set greater than normal I guess
} }
@@ -717,7 +718,7 @@ int64 Client::CalcManaRegen(bool bCombat)
int64 Client::CalcManaRegenCap() int64 Client::CalcManaRegenCap()
{ {
int64 cap = RuleI(Character, ItemManaRegenCap) + aabonuses.ItemManaRegenCap + itembonuses.ItemManaRegenCap + spellbonuses.ItemManaRegenCap; int64 cap = GetStatCap(StatCap::ManaRegen) + aabonuses.ItemManaRegenCap + itembonuses.ItemManaRegenCap + spellbonuses.ItemManaRegenCap;
return (cap * RuleI(Character, ManaRegenMultiplier) / 100); return (cap * RuleI(Character, ManaRegenMultiplier) / 100);
} }
@@ -976,7 +977,7 @@ int Client::CalcHaste()
} }
// 60+ 100, 51-59 85, 1-50 level+25 // 60+ 100, 51-59 85, 1-50 level+25
if (level > 59 || RuleB(Character, IgnoreLevelBasedHasteCaps)) { // 60+ if (level > 59 || RuleB(Character, IgnoreLevelBasedHasteCaps)) { // 60+
cap = RuleI(Character, HasteCap); cap = GetStatCap(StatCap::Haste);
} }
else if (level > 50) { // 51-59 else if (level > 50) { // 51-59
cap = 85; cap = 85;
@@ -989,7 +990,7 @@ int Client::CalcHaste()
} }
// 51+ 25 (despite there being higher spells...), 1-50 10 // 51+ 25 (despite there being higher spells...), 1-50 10
if (level > 50 || RuleB(Character, IgnoreLevelBasedHasteCaps)) { // 51+ if (level > 50 || RuleB(Character, IgnoreLevelBasedHasteCaps)) { // 51+
cap = RuleI(Character, Hastev3Cap); cap = GetStatCap(StatCap::HasteV3);
if (spellbonuses.hastetype3 > cap) { if (spellbonuses.hastetype3 > cap) {
h += cap; h += cap;
} else { } else {
@@ -1745,13 +1746,13 @@ int64 Client::CalcEnduranceRegen(bool bCombat)
int64 Client::CalcEnduranceRegenCap() int64 Client::CalcEnduranceRegenCap()
{ {
int64 cap = RuleI(Character, ItemEnduranceRegenCap) + aabonuses.ItemEnduranceRegenCap + itembonuses.ItemEnduranceRegenCap + spellbonuses.ItemEnduranceRegenCap; int64 cap = GetStatCap(StatCap::EnduranceRegen) + aabonuses.ItemEnduranceRegenCap + itembonuses.ItemEnduranceRegenCap + spellbonuses.ItemEnduranceRegenCap;
return (cap * RuleI(Character, EnduranceRegenMultiplier) / 100); return (cap * RuleI(Character, EnduranceRegenMultiplier) / 100);
} }
int32 Client::CalcItemATKCap() int32 Client::CalcItemATKCap()
{ {
int cap = RuleI(Character, ItemATKCap) + itembonuses.ItemATKCap + spellbonuses.ItemATKCap + aabonuses.ItemATKCap; int cap = GetStatCap(StatCap::Attack) + itembonuses.ItemATKCap + spellbonuses.ItemATKCap + aabonuses.ItemATKCap;
return cap; return cap;
} }
+1
View File
@@ -1386,6 +1386,7 @@ void Client::Handle_Connect_OP_ZoneEntry(const EQApplicationPacket *app)
database.LoadCharacterTribute(this); /* Load CharacterTribute */ database.LoadCharacterTribute(this); /* Load CharacterTribute */
database.LoadCharacterEXPModifier(this); /* Load Character EXP Modifier */ database.LoadCharacterEXPModifier(this); /* Load Character EXP Modifier */
database.LoadCharacterTitleSets(this); /* Load Character Title Sets */ database.LoadCharacterTitleSets(this); /* Load Character Title Sets */
database.LoadStatCaps(this); /* Load Character Stat Caps */
// this pattern is strange // this pattern is strange
// this is remnants of the old way of doing things // this is remnants of the old way of doing things
+6
View File
@@ -6049,6 +6049,11 @@ std::string Perl__get_pet_type_name(uint8 pet_type)
return PetType::GetName(pet_type); return PetType::GetName(pet_type);
} }
std::string Perl__get_stat_cap_name(uint8 stat_id)
{
return StatCap::GetName(stat_id);
}
void perl_register_quest() void perl_register_quest()
{ {
perl::interpreter perl(PERL_GET_THX); perl::interpreter perl(PERL_GET_THX);
@@ -6760,6 +6765,7 @@ void perl_register_quest()
package.add("getspellstat", (int(*)(uint32, std::string))&Perl__getspellstat); package.add("getspellstat", (int(*)(uint32, std::string))&Perl__getspellstat);
package.add("getspellstat", (int(*)(uint32, std::string, uint8))&Perl__getspellstat); package.add("getspellstat", (int(*)(uint32, std::string, uint8))&Perl__getspellstat);
package.add("getskillname", &Perl__getskillname); package.add("getskillname", &Perl__getskillname);
package.add("get_stat_cap_name", &Perl__get_stat_cap_name);
package.add("get_timers", &Perl__get_timers); package.add("get_timers", &Perl__get_timers);
package.add("getlevel", &Perl__getlevel); package.add("getlevel", &Perl__getlevel);
package.add("getplayerburiedcorpsecount", &Perl__getplayerburiedcorpsecount); package.add("getplayerburiedcorpsecount", &Perl__getplayerburiedcorpsecount);
+14
View File
@@ -664,6 +664,18 @@ void Lua_Bot::RaidGroupSay(const char* message) {
self->RaidGroupSay(message); self->RaidGroupSay(message);
} }
int Lua_Bot::GetStatCap(uint8 stat_id)
{
Lua_Safe_Call_Int();
return self->GetStatCap(stat_id);
}
void Lua_Bot::SetStatCap(uint8 stat_id, int stat_cap)
{
Lua_Safe_Call_Void();
self->SetStatCap(stat_id, stat_cap, true);
}
luabind::scope lua_register_bot() { luabind::scope lua_register_bot() {
return luabind::class_<Lua_Bot, Lua_Mob>("Bot") return luabind::class_<Lua_Bot, Lua_Mob>("Bot")
.def(luabind::constructor<>()) .def(luabind::constructor<>())
@@ -747,6 +759,7 @@ luabind::scope lua_register_bot() {
.def("GetSpellDamage", (int(Lua_Bot::*)(void))&Lua_Bot::GetSpellDamage) .def("GetSpellDamage", (int(Lua_Bot::*)(void))&Lua_Bot::GetSpellDamage)
.def("GetSpellRecastTimer", (uint32(Lua_Bot::*)())&Lua_Bot::GetSpellRecastTimer) .def("GetSpellRecastTimer", (uint32(Lua_Bot::*)())&Lua_Bot::GetSpellRecastTimer)
.def("GetSpellRecastTimer", (uint32(Lua_Bot::*)(uint16))&Lua_Bot::GetSpellRecastTimer) .def("GetSpellRecastTimer", (uint32(Lua_Bot::*)(uint16))&Lua_Bot::GetSpellRecastTimer)
.def("GetStatCap", (int(Lua_Bot::*)(uint8))&Lua_Bot::GetStatCap)
.def("HasAugmentEquippedByID", (bool(Lua_Bot::*)(uint32))&Lua_Bot::HasAugmentEquippedByID) .def("HasAugmentEquippedByID", (bool(Lua_Bot::*)(uint32))&Lua_Bot::HasAugmentEquippedByID)
.def("HasBotItem", (int16(Lua_Bot::*)(uint32))&Lua_Bot::HasBotItem) .def("HasBotItem", (int16(Lua_Bot::*)(uint32))&Lua_Bot::HasBotItem)
.def("HasBotSpellEntry", (bool(Lua_Bot::*)(uint16))&Lua_Bot::HasBotSpellEntry) .def("HasBotSpellEntry", (bool(Lua_Bot::*)(uint16))&Lua_Bot::HasBotSpellEntry)
@@ -783,6 +796,7 @@ luabind::scope lua_register_bot() {
.def("SetSpellDurationRaid", (void(Lua_Bot::*)(int,int,int,bool,bool))&Lua_Bot::SetSpellDurationRaid) .def("SetSpellDurationRaid", (void(Lua_Bot::*)(int,int,int,bool,bool))&Lua_Bot::SetSpellDurationRaid)
.def("SetSpellRecastTimer", (void(Lua_Bot::*)(uint16))&Lua_Bot::SetSpellRecastTimer) .def("SetSpellRecastTimer", (void(Lua_Bot::*)(uint16))&Lua_Bot::SetSpellRecastTimer)
.def("SetSpellRecastTimer", (void(Lua_Bot::*)(uint16, uint32))&Lua_Bot::SetSpellRecastTimer) .def("SetSpellRecastTimer", (void(Lua_Bot::*)(uint16, uint32))&Lua_Bot::SetSpellRecastTimer)
.def("SetStatCap", (void(Lua_Bot::*)(uint8,int))&Lua_Bot::SetStatCap)
.def("SendPayload", (void(Lua_Bot::*)(int))&Lua_Bot::SendPayload) .def("SendPayload", (void(Lua_Bot::*)(int))&Lua_Bot::SendPayload)
.def("SendPayload", (void(Lua_Bot::*)(int,std::string))&Lua_Bot::SendPayload) .def("SendPayload", (void(Lua_Bot::*)(int,std::string))&Lua_Bot::SendPayload)
.def("Signal", (void(Lua_Bot::*)(int))&Lua_Bot::Signal) .def("Signal", (void(Lua_Bot::*)(int))&Lua_Bot::Signal)
+2
View File
@@ -126,6 +126,8 @@ public:
void SetItemReuseTimer(uint32 item_id, uint32 reuse_timer); void SetItemReuseTimer(uint32 item_id, uint32 reuse_timer);
void SetSpellRecastTimer(uint16 spell_id); void SetSpellRecastTimer(uint16 spell_id);
void SetSpellRecastTimer(uint16 spell_id, uint32 reuse_timer); void SetSpellRecastTimer(uint16 spell_id, uint32 reuse_timer);
int GetStatCap(uint8 stat_id);
void SetStatCap(uint8 stat_id, int stat_cap);
uint32 CountAugmentEquippedByID(uint32 item_id); uint32 CountAugmentEquippedByID(uint32 item_id);
uint32 CountItemEquippedByID(uint32 item_id); uint32 CountItemEquippedByID(uint32 item_id);
+14
View File
@@ -3624,6 +3624,18 @@ luabind::object Lua_Client::GetKeyRing(lua_State* L)
return lua_table; return lua_table;
} }
int Lua_Client::GetStatCap(uint8 stat_id)
{
Lua_Safe_Call_Int();
return self->GetStatCap(stat_id);
}
void Lua_Client::SetStatCap(uint8 stat_id, int stat_cap)
{
Lua_Safe_Call_Void();
self->SetStatCap(stat_id, stat_cap, true);
}
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<>())
@@ -3898,6 +3910,7 @@ luabind::scope lua_register_client() {
.def("GetSpellDamage", (int(Lua_Client::*)(void))&Lua_Client::GetSpellDamage) .def("GetSpellDamage", (int(Lua_Client::*)(void))&Lua_Client::GetSpellDamage)
.def("GetSpellIDByBookSlot", (uint32(Lua_Client::*)(int))&Lua_Client::GetSpellIDByBookSlot) .def("GetSpellIDByBookSlot", (uint32(Lua_Client::*)(int))&Lua_Client::GetSpellIDByBookSlot)
.def("GetSpentAA", (int(Lua_Client::*)(void))&Lua_Client::GetSpentAA) .def("GetSpentAA", (int(Lua_Client::*)(void))&Lua_Client::GetSpentAA)
.def("GetStatCap", (int(Lua_Client::*)(uint8))&Lua_Client::GetStatCap)
.def("GetStartZone", (int(Lua_Client::*)(void))&Lua_Client::GetStartZone) .def("GetStartZone", (int(Lua_Client::*)(void))&Lua_Client::GetStartZone)
.def("GetTargetRingX", (float(Lua_Client::*)(void))&Lua_Client::GetTargetRingX) .def("GetTargetRingX", (float(Lua_Client::*)(void))&Lua_Client::GetTargetRingX)
.def("GetTargetRingY", (float(Lua_Client::*)(void))&Lua_Client::GetTargetRingY) .def("GetTargetRingY", (float(Lua_Client::*)(void))&Lua_Client::GetTargetRingY)
@@ -4163,6 +4176,7 @@ luabind::scope lua_register_client() {
.def("SetStartZone", (void(Lua_Client::*)(int,float))&Lua_Client::SetStartZone) .def("SetStartZone", (void(Lua_Client::*)(int,float))&Lua_Client::SetStartZone)
.def("SetStartZone", (void(Lua_Client::*)(int,float,float))&Lua_Client::SetStartZone) .def("SetStartZone", (void(Lua_Client::*)(int,float,float))&Lua_Client::SetStartZone)
.def("SetStartZone", (void(Lua_Client::*)(int,float,float,float))&Lua_Client::SetStartZone) .def("SetStartZone", (void(Lua_Client::*)(int,float,float,float))&Lua_Client::SetStartZone)
.def("SetStatCap", (void(Lua_Client::*)(uint8,int))&Lua_Client::SetStatCap)
.def("SetStats", (void(Lua_Client::*)(int,int))&Lua_Client::SetStats) .def("SetStats", (void(Lua_Client::*)(int,int))&Lua_Client::SetStats)
.def("SetThirst", (void(Lua_Client::*)(int))&Lua_Client::SetThirst) .def("SetThirst", (void(Lua_Client::*)(int))&Lua_Client::SetThirst)
.def("SetTint", (void(Lua_Client::*)(int,uint32))&Lua_Client::SetTint) .def("SetTint", (void(Lua_Client::*)(int,uint32))&Lua_Client::SetTint)
+2
View File
@@ -607,6 +607,8 @@ public:
bool RemoveAAPoints(uint32 points); bool RemoveAAPoints(uint32 points);
bool RemoveAlternateCurrencyValue(uint32 currency_id, uint32 amount); bool RemoveAlternateCurrencyValue(uint32 currency_id, uint32 amount);
bool AreTasksCompleted(luabind::object task_ids); bool AreTasksCompleted(luabind::object task_ids);
int GetStatCap(uint8 stat_id);
void SetStatCap(uint8 stat_id, int stat_cap);
void DialogueWindow(std::string markdown); void DialogueWindow(std::string markdown);
+6
View File
@@ -5711,6 +5711,11 @@ std::string lua_get_pet_type_name(uint8 pet_type)
return PetType::GetName(pet_type); return PetType::GetName(pet_type);
} }
std::string lua_get_stat_cap_name(uint8 stat_id)
{
return StatCap::GetName(stat_id);
}
#define LuaCreateNPCParse(name, c_type, default_value) do { \ #define LuaCreateNPCParse(name, c_type, default_value) do { \
cur = table[#name]; \ cur = table[#name]; \
if(luabind::type(cur) != LUA_TNIL) { \ if(luabind::type(cur) != LUA_TNIL) { \
@@ -6526,6 +6531,7 @@ luabind::scope lua_register_general() {
luabind::def("get_timers", &lua_get_timers), luabind::def("get_timers", &lua_get_timers),
luabind::def("get_pet_command_name", &lua_get_pet_command_name), luabind::def("get_pet_command_name", &lua_get_pet_command_name),
luabind::def("get_pet_type_name", &lua_get_pet_type_name), luabind::def("get_pet_type_name", &lua_get_pet_type_name),
luabind::def("get_stat_cap_name", &lua_get_stat_cap_name),
/* /*
Cross Zone Cross Zone
*/ */
+91 -20
View File
@@ -22,7 +22,9 @@
#include "../common/misc_functions.h" #include "../common/misc_functions.h"
#include "../common/repositories/bot_data_repository.h" #include "../common/repositories/bot_data_repository.h"
#include "../common/repositories/bot_stat_caps_repository.h"
#include "../common/repositories/character_data_repository.h" #include "../common/repositories/character_data_repository.h"
#include "../common/repositories/character_stat_caps_repository.h"
#include "../common/data_bucket.h" #include "../common/data_bucket.h"
#include "quest_parser_collection.h" #include "quest_parser_collection.h"
@@ -2061,8 +2063,8 @@ void Mob::SendStatsWindow(Client* c, bool use_window)
case 0: { case 0: {
mod2a_name = "Avoidance"; mod2a_name = "Avoidance";
mod2b_name = "Combat Effects"; mod2b_name = "Combat Effects";
mod2a_cap = Strings::Commify(RuleI(Character, ItemAvoidanceCap)); mod2a_cap = Strings::Commify(GetStatCap(StatCap::Avoidance));
mod2b_cap = Strings::Commify(RuleI(Character, ItemCombatEffectsCap)); mod2b_cap = Strings::Commify(GetStatCap(StatCap::CombatEffects));
if (IsBot()) { if (IsBot()) {
mod2a = Strings::Commify(CastToBot()->GetAvoidance()); mod2a = Strings::Commify(CastToBot()->GetAvoidance());
@@ -2081,8 +2083,8 @@ void Mob::SendStatsWindow(Client* c, bool use_window)
case 1: { case 1: {
mod2a_name = "Accuracy"; mod2a_name = "Accuracy";
mod2b_name = "Strikethrough"; mod2b_name = "Strikethrough";
mod2a_cap = Strings::Commify(RuleI(Character, ItemAccuracyCap)); mod2a_cap = Strings::Commify(GetStatCap(StatCap::Accuracy));
mod2b_cap = Strings::Commify(RuleI(Character, ItemStrikethroughCap)); mod2b_cap = Strings::Commify(GetStatCap(StatCap::Strikethrough));
if (IsBot()) { if (IsBot()) {
mod2a = Strings::Commify(CastToBot()->GetAccuracy()); mod2a = Strings::Commify(CastToBot()->GetAccuracy());
@@ -2101,8 +2103,8 @@ void Mob::SendStatsWindow(Client* c, bool use_window)
case 2: { case 2: {
mod2a_name = "Shielding"; mod2a_name = "Shielding";
mod2b_name = "Spell Shielding"; mod2b_name = "Spell Shielding";
mod2a_cap = Strings::Commify(RuleI(Character, ItemShieldingCap)); mod2a_cap = Strings::Commify(GetStatCap(StatCap::Shielding));
mod2b_cap = Strings::Commify(RuleI(Character, ItemSpellShieldingCap)); mod2b_cap = Strings::Commify(GetStatCap(StatCap::SpellShielding));
if (IsBot()) { if (IsBot()) {
mod2a = Strings::Commify(CastToBot()->GetShielding()); mod2a = Strings::Commify(CastToBot()->GetShielding());
@@ -2122,8 +2124,8 @@ void Mob::SendStatsWindow(Client* c, bool use_window)
case 3: { case 3: {
mod2a_name = "Stun Resist"; mod2a_name = "Stun Resist";
mod2b_name = "DOT Shielding"; mod2b_name = "DOT Shielding";
mod2a_cap = Strings::Commify(RuleI(Character, ItemStunResistCap)); mod2a_cap = Strings::Commify(GetStatCap(StatCap::DOTShielding));
mod2b_cap = Strings::Commify(RuleI(Character, ItemDoTShieldingCap)); mod2b_cap = Strings::Commify(GetStatCap(StatCap::StunResist));
if (IsBot()) { if (IsBot()) {
mod2a = Strings::Commify(CastToBot()->GetStunResist()); mod2a = Strings::Commify(CastToBot()->GetStunResist());
@@ -2368,7 +2370,7 @@ void Mob::SendStatsWindow(Client* c, bool use_window)
fmt::format( fmt::format(
" | Item: {} / {} | Used: {}", " | Item: {} / {} | Used: {}",
Strings::Commify(itembonuses.ATK), Strings::Commify(itembonuses.ATK),
Strings::Commify(RuleI(Character, ItemATKCap)), Strings::Commify(GetStatCap(StatCap::Attack)),
Strings::Commify(static_cast<int>(itembonuses.ATK * 1.342)) Strings::Commify(static_cast<int>(itembonuses.ATK * 1.342))
) : ) :
"" ""
@@ -2433,7 +2435,7 @@ void Mob::SendStatsWindow(Client* c, bool use_window)
fmt::format( fmt::format(
"{} ({})", "{} ({})",
IsClient() ? Strings::Commify(CastToClient()->GetHaste()) : Strings::Commify(GetHaste()), IsClient() ? Strings::Commify(CastToClient()->GetHaste()) : Strings::Commify(GetHaste()),
Strings::Commify(RuleI(Character, HasteCap)) Strings::Commify(GetStatCap(StatCap::Haste))
) )
) )
) )
@@ -2471,17 +2473,17 @@ void Mob::SendStatsWindow(Client* c, bool use_window)
final_string += fmt::format( final_string += fmt::format(
"Heal Amount: {} / {}{}", "Heal Amount: {} / {}{}",
Strings::Commify(GetHealAmt()), Strings::Commify(GetHealAmt()),
Strings::Commify(RuleI(Character, ItemHealAmtCap)), Strings::Commify(GetStatCap(StatCap::HealAmount)),
DialogueWindow::Break(1) DialogueWindow::Break(1)
); );
} }
// Heal Amount // Spell Damage
if (GetSpellDmg()) { if (GetSpellDmg()) {
final_string += fmt::format( final_string += fmt::format(
"Spell Damage: {} / {}{}", "Spell Damage: {} / {}{}",
Strings::Commify(GetSpellDmg()), Strings::Commify(GetSpellDmg()),
Strings::Commify(RuleI(Character, ItemSpellDmgCap)), Strings::Commify(GetStatCap(StatCap::SpellDamage)),
DialogueWindow::Break(1) DialogueWindow::Break(1)
); );
} }
@@ -2501,7 +2503,7 @@ void Mob::SendStatsWindow(Client* c, bool use_window)
fmt::format( fmt::format(
" | Item: {} / {}", " | Item: {} / {}",
Strings::Commify(itembonuses.DamageShield), Strings::Commify(itembonuses.DamageShield),
Strings::Commify(RuleI(Character, ItemDamageShieldCap)) Strings::Commify(GetStatCap(StatCap::DamageShield))
) : ) :
"" ""
), ),
@@ -2510,12 +2512,12 @@ void Mob::SendStatsWindow(Client* c, bool use_window)
} }
// Clairvoyance // Clairvoyance
const auto clairvoyance = IsBot() ? CastToBot()->GetClair() : CastToClient()->GetClair(); const auto clairvoyance = IsBot() ? CastToBot()->GetClair() : CastToClient()->GetClair();
if (clairvoyance) { if (clairvoyance) {
final_string += fmt::format( final_string += fmt::format(
"Clairvoyance: {} / {}{}", "Clairvoyance: {} / {}{}",
Strings::Commify(clairvoyance), Strings::Commify(clairvoyance),
Strings::Commify(RuleI(Character, ItemClairvoyanceCap)), Strings::Commify(GetStatCap(StatCap::Clairvoyance)),
DialogueWindow::Break(1) DialogueWindow::Break(1)
); );
} }
@@ -2526,7 +2528,7 @@ void Mob::SendStatsWindow(Client* c, bool use_window)
final_string += fmt::format( final_string += fmt::format(
"DS Mitigation: {} / {}{}", "DS Mitigation: {} / {}{}",
Strings::Commify(ds_mitigation), Strings::Commify(ds_mitigation),
Strings::Commify(RuleI(Character, ItemDSMitigationCap)), Strings::Commify(GetStatCap(StatCap::DSMitigation)),
DialogueWindow::Break(1) DialogueWindow::Break(1)
); );
} }
@@ -2596,7 +2598,7 @@ void Mob::SendStatsWindow(Client* c, bool use_window)
GetRaceIDName(GetRace()), GetRaceIDName(GetRace()),
GetRace(), GetRace(),
IsBot() ? Strings::Commify(CastToBot()->GetDS()) : Strings::Commify(CastToClient()->GetDS()), IsBot() ? Strings::Commify(CastToBot()->GetDS()) : Strings::Commify(CastToClient()->GetDS()),
Strings::Commify(RuleI(Character, ItemDamageShieldCap)), Strings::Commify(GetStatCap(StatCap::DamageShield)),
GetSize(), GetSize(),
GetRunspeed(), GetRunspeed(),
IsBot() ? static_cast<float>(CastToBot()->CalcCurrentWeight()) / 10.0f : static_cast<float>(CastToClient()->CalcCurrentWeight()) / 10.0f, IsBot() ? static_cast<float>(CastToBot()->CalcCurrentWeight()) / 10.0f : static_cast<float>(CastToClient()->CalcCurrentWeight()) / 10.0f,
@@ -2694,7 +2696,7 @@ void Mob::SendStatsWindow(Client* c, bool use_window)
"Attack: {} Item and Spell Attack: {}/{} Server Side Attack: {}", "Attack: {} Item and Spell Attack: {}/{} Server Side Attack: {}",
IsBot() ? Strings::Commify(CastToBot()->GetTotalATK()) : Strings::Commify(CastToClient()->GetTotalATK()), IsBot() ? Strings::Commify(CastToBot()->GetTotalATK()) : Strings::Commify(CastToClient()->GetTotalATK()),
Strings::Commify(GetATKBonus()), Strings::Commify(GetATKBonus()),
Strings::Commify(RuleI(Character, ItemATKCap)), Strings::Commify(GetStatCap(StatCap::Attack)),
Strings::Commify(GetATK()) Strings::Commify(GetATK())
).c_str() ).c_str()
); );
@@ -2705,7 +2707,7 @@ void Mob::SendStatsWindow(Client* c, bool use_window)
fmt::format( fmt::format(
"Haste: {}/{} (Item: {} + Spell: {} + Over: {})", "Haste: {}/{} (Item: {} + Spell: {} + Over: {})",
IsClient() ? Strings::Commify(CastToClient()->GetHaste()) : Strings::Commify(GetHaste()), IsClient() ? Strings::Commify(CastToClient()->GetHaste()) : Strings::Commify(GetHaste()),
Strings::Commify(RuleI(Character, HasteCap)), Strings::Commify(GetStatCap(StatCap::Haste)),
Strings::Commify(itembonuses.haste), Strings::Commify(itembonuses.haste),
Strings::Commify(spellbonuses.haste + spellbonuses.hastetype2), Strings::Commify(spellbonuses.haste + spellbonuses.hastetype2),
Strings::Commify(spellbonuses.hastetype3 + extra_haste) Strings::Commify(spellbonuses.hastetype3 + extra_haste)
@@ -8793,3 +8795,72 @@ bool Mob::LoadDataBucketsCache()
return true; return true;
} }
int Mob::GetStatCap(uint8 stat_id) const
{
if (HasStatCap(stat_id)) {
return m_stat_caps.at(stat_id);
}
static const std::map<uint8, int> default_caps = {
{ StatCap::Accuracy, RuleI(Character, ItemAccuracyCap) },
{ StatCap::Attack, RuleI(Character, ItemATKCap) },
{ StatCap::Avoidance, RuleI(Character, ItemAvoidanceCap) },
{ StatCap::Clairvoyance, RuleI(Character, ItemClairvoyanceCap) },
{ StatCap::CombatEffects, RuleI(Character, ItemCombatEffectsCap) },
{ StatCap::DamageShield, RuleI(Character, ItemDamageShieldCap) },
{ StatCap::DOTShielding, RuleI(Character, ItemDoTShieldingCap) },
{ StatCap::DSMitigation, RuleI(Character, ItemDSMitigationCap) },
{ StatCap::EnduranceRegen, RuleI(Character, ItemEnduranceRegenCap) },
{ StatCap::ExtraDamage, RuleI(Character, ItemExtraDmgCap) },
{ StatCap::Haste, RuleI(Character, HasteCap) },
{ StatCap::HasteV3, RuleI(Character, Hastev3Cap) },
{ StatCap::HealAmount, RuleI(Character, ItemHealAmtCap) },
{ StatCap::HealthRegen, RuleI(Character, ItemHealthRegenCap) },
{ StatCap::ManaRegen, RuleI(Character, ItemManaRegenCap) },
{ StatCap::QuiverHaste, RuleI(Combat, QuiverHasteCap) },
{ StatCap::Shielding, RuleI(Character, ItemShieldingCap) },
{ StatCap::SpellDamage, RuleI(Character, ItemSpellDmgCap) },
{ StatCap::SpellShielding, RuleI(Character, ItemSpellShieldingCap) },
{ StatCap::Stat, RuleI(Character, StatCap) },
{ StatCap::Strikethrough, RuleI(Character, ItemStrikethroughCap) },
{ StatCap::StunResist, RuleI(Character, ItemStunResistCap) },
};
auto it = default_caps.find(stat_id);
if (it != default_caps.end()) {
return it->second;
}
return -1;
}
void Mob::SetStatCap(uint8 stat_id, int stat_cap, bool save)
{
m_stat_caps[stat_id] = stat_cap;
if (!save) {
return;
}
if (IsBot()) {
BotStatCapsRepository::ReplaceOne(
database,
BotStatCapsRepository::BotStatCaps{
.bot_id = CastToBot()->GetBotID(),
.stat_id = stat_id,
.stat_cap = stat_cap
}
);
}
else if (IsClient()) {
CharacterStatCapsRepository::ReplaceOne(
database,
CharacterStatCapsRepository::CharacterStatCaps{
.character_id = CastToClient()->CharacterID(),
.stat_id = stat_id,
.stat_cap = stat_cap
}
);
}
}
+7
View File
@@ -1519,6 +1519,11 @@ public:
bool IsGuildmaster() const; bool IsGuildmaster() const;
bool IsDestroying() const { return m_destroying; } bool IsDestroying() const { return m_destroying; }
int GetStatCap(uint8 stat_id) const;
std::map<uint8, int> GetStatCaps() { return m_stat_caps; }
bool HasStatCap(uint8 stat_id) const { return m_stat_caps.count(stat_id); }
void SetStatCap(uint8 stat_id, int stat_cap, bool save = false);
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);
@@ -1802,6 +1807,8 @@ protected:
CombatRecord m_combat_record{}; CombatRecord m_combat_record{};
std::map<uint8, int> m_stat_caps;
public: public:
const CombatRecord &GetCombatRecord() const; const CombatRecord &GetCombatRecord() const;
+19 -7
View File
@@ -620,6 +620,16 @@ void Perl_Bot_RaidGroupSay(Bot* self, const char* message) // @categories Script
self->RaidGroupSay(message); self->RaidGroupSay(message);
} }
int Perl_Bot_GetStatCap(Bot* self, uint8 stat_id)
{
return self->GetStatCap(stat_id);
}
void Perl_Bot_SetStatCap(Bot* self, uint8 stat_id, int stat_cap)
{
self->SetStatCap(stat_id, stat_cap, true);
}
void perl_register_bot() void perl_register_bot()
{ {
perl::interpreter state(PERL_GET_THX); perl::interpreter state(PERL_GET_THX);
@@ -683,27 +693,28 @@ void perl_register_bot()
package.add("GetBotItem", &Perl_Bot_GetBotItem); package.add("GetBotItem", &Perl_Bot_GetBotItem);
package.add("GetBotItemIDBySlot", &Perl_Bot_GetBotItemIDBySlot); package.add("GetBotItemIDBySlot", &Perl_Bot_GetBotItemIDBySlot);
package.add("GetClassAbbreviation", &Perl_Bot_GetClassAbbreviation); package.add("GetClassAbbreviation", &Perl_Bot_GetClassAbbreviation);
package.add("GetDisciplineReuseTimer", (uint32(*)(Bot*))&Perl_Bot_GetDisciplineReuseTimer);
package.add("GetDisciplineReuseTimer", (uint32(*)(Bot*, uint16))&Perl_Bot_GetDisciplineReuseTimer);
package.add("GetExpansionBitmask", &Perl_Bot_GetExpansionBitmask); package.add("GetExpansionBitmask", &Perl_Bot_GetExpansionBitmask);
package.add("GetGroup", &Perl_Bot_GetGroup); package.add("GetGroup", &Perl_Bot_GetGroup);
package.add("GetHealAmount", &Perl_Bot_GetHealAmount); package.add("GetHealAmount", &Perl_Bot_GetHealAmount);
package.add("GetInstrumentMod", &Perl_Bot_GetInstrumentMod); package.add("GetInstrumentMod", &Perl_Bot_GetInstrumentMod);
package.add("GetItemAt", &Perl_Bot_GetItemAt); package.add("GetItemAt", &Perl_Bot_GetItemAt);
package.add("GetItemEquippedByID", &Perl_Bot_HasItemEquippedByID);
package.add("GetItemIDAt", &Perl_Bot_GetItemIDAt); package.add("GetItemIDAt", &Perl_Bot_GetItemIDAt);
package.add("GetItemReuseTimer", (uint32(*)(Bot*))&Perl_Bot_GetItemReuseTimer);
package.add("GetItemReuseTimer", (uint32(*)(Bot*, uint32))&Perl_Bot_GetItemReuseTimer);
package.add("GetOwner", &Perl_Bot_GetOwner); package.add("GetOwner", &Perl_Bot_GetOwner);
package.add("GetRaceAbbreviation", &Perl_Bot_GetRaceAbbreviation); package.add("GetRaceAbbreviation", &Perl_Bot_GetRaceAbbreviation);
package.add("GetRawItemAC", &Perl_Bot_GetRawItemAC); package.add("GetRawItemAC", &Perl_Bot_GetRawItemAC);
package.add("GetSpellDamage", &Perl_Bot_GetSpellDamage); package.add("GetSpellDamage", &Perl_Bot_GetSpellDamage);
package.add("GetSpellRecastTimer", (uint32(*)(Bot*))&Perl_Bot_GetSpellRecastTimer);
package.add("GetSpellRecastTimer", (uint32(*)(Bot*, uint16))&Perl_Bot_GetSpellRecastTimer);
package.add("GetStatCap", &Perl_Bot_GetStatCap);
package.add("HasAugmentEquippedByID", &Perl_Bot_HasAugmentEquippedByID); package.add("HasAugmentEquippedByID", &Perl_Bot_HasAugmentEquippedByID);
package.add("HasBotItem", &Perl_Bot_HasBotItem); package.add("HasBotItem", &Perl_Bot_HasBotItem);
package.add("HasBotSpellEntry", &Perl_Bot_HasBotSpellEntry); package.add("HasBotSpellEntry", &Perl_Bot_HasBotSpellEntry);
package.add("HasItemEquippedByID", &Perl_Bot_HasItemEquippedByID); package.add("HasItemEquippedByID", &Perl_Bot_HasItemEquippedByID);
package.add("GetDisciplineReuseTimer", (uint32(*)(Bot*))&Perl_Bot_GetDisciplineReuseTimer);
package.add("GetDisciplineReuseTimer", (uint32(*)(Bot*, uint16))&Perl_Bot_GetDisciplineReuseTimer);
package.add("GetItemEquippedByID", &Perl_Bot_HasItemEquippedByID);
package.add("GetItemReuseTimer", (uint32(*)(Bot*))&Perl_Bot_GetItemReuseTimer);
package.add("GetItemReuseTimer", (uint32(*)(Bot*, uint32))&Perl_Bot_GetItemReuseTimer);
package.add("GetSpellRecastTimer", (uint32(*)(Bot*))&Perl_Bot_GetSpellRecastTimer);
package.add("GetSpellRecastTimer", (uint32(*)(Bot*, uint16))&Perl_Bot_GetSpellRecastTimer);
package.add("IsGrouped", &Perl_Bot_IsGrouped); package.add("IsGrouped", &Perl_Bot_IsGrouped);
package.add("IsSitting", &Perl_Bot_IsSitting); package.add("IsSitting", &Perl_Bot_IsSitting);
package.add("IsStanding", &Perl_Bot_IsStanding); package.add("IsStanding", &Perl_Bot_IsStanding);
@@ -736,6 +747,7 @@ void perl_register_bot()
package.add("SetSpellDurationRaid", (void(*)(Bot*, int, int, int, bool, bool))&Perl_Bot_SetSpellDurationRaid); package.add("SetSpellDurationRaid", (void(*)(Bot*, int, int, int, bool, bool))&Perl_Bot_SetSpellDurationRaid);
package.add("SetSpellRecastTimer", (void(*)(Bot*, uint16))&Perl_Bot_SetSpellRecastTimer); package.add("SetSpellRecastTimer", (void(*)(Bot*, uint16))&Perl_Bot_SetSpellRecastTimer);
package.add("SetSpellRecastTimer", (void(*)(Bot*, uint16, uint32))&Perl_Bot_SetSpellRecastTimer); package.add("SetSpellRecastTimer", (void(*)(Bot*, uint16, uint32))&Perl_Bot_SetSpellRecastTimer);
package.add("SetStatCap", &Perl_Bot_SetStatCap);
package.add("Signal", &Perl_Bot_Signal); package.add("Signal", &Perl_Bot_Signal);
package.add("Sit", &Perl_Bot_Sit); package.add("Sit", &Perl_Bot_Sit);
package.add("Stand", &Perl_Bot_Stand); package.add("Stand", &Perl_Bot_Stand);
+12
View File
@@ -3363,6 +3363,16 @@ perl::array Perl_Client_GetKeyRing(Client* self)
return result; return result;
} }
int Perl_Client_GetStatCap(Client* self, uint8 stat_id)
{
return self->GetStatCap(stat_id);
}
void Perl_Client_SetStatCap(Client* self, uint8 stat_id, int stat_cap)
{
self->SetStatCap(stat_id, stat_cap, true);
}
void perl_register_client() void perl_register_client()
{ {
perl::interpreter perl(PERL_GET_THX); perl::interpreter perl(PERL_GET_THX);
@@ -3635,6 +3645,7 @@ void perl_register_client()
package.add("GetSpellBookSlotBySpellID", &Perl_Client_GetSpellBookSlotBySpellID); package.add("GetSpellBookSlotBySpellID", &Perl_Client_GetSpellBookSlotBySpellID);
package.add("GetSpellIDByBookSlot", &Perl_Client_GetSpellIDByBookSlot); package.add("GetSpellIDByBookSlot", &Perl_Client_GetSpellIDByBookSlot);
package.add("GetSpentAA", &Perl_Client_GetSpentAA); package.add("GetSpentAA", &Perl_Client_GetSpentAA);
package.add("GetStatCap", &Perl_Client_GetStatCap);
package.add("GetStartZone", &Perl_Client_GetStartZone); package.add("GetStartZone", &Perl_Client_GetStartZone);
package.add("GetTargetRingX", &Perl_Client_GetTargetRingX); package.add("GetTargetRingX", &Perl_Client_GetTargetRingX);
package.add("GetTargetRingY", &Perl_Client_GetTargetRingY); package.add("GetTargetRingY", &Perl_Client_GetTargetRingY);
@@ -3899,6 +3910,7 @@ void perl_register_client()
package.add("SetStartZone", (void(*)(Client*, uint32))&Perl_Client_SetStartZone); package.add("SetStartZone", (void(*)(Client*, uint32))&Perl_Client_SetStartZone);
package.add("SetStartZone", (void(*)(Client*, uint32, float, float, float))&Perl_Client_SetStartZone); package.add("SetStartZone", (void(*)(Client*, uint32, float, float, float))&Perl_Client_SetStartZone);
package.add("SetStartZone", (void(*)(Client*, uint32, float, float, float, float))&Perl_Client_SetStartZone); package.add("SetStartZone", (void(*)(Client*, uint32, float, float, float, float))&Perl_Client_SetStartZone);
package.add("SetStatCap", &Perl_Client_SetStatCap);
package.add("SetStats", &Perl_Client_SetStats); package.add("SetStats", &Perl_Client_SetStats);
package.add("SetThirst", &Perl_Client_SetThirst); package.add("SetThirst", &Perl_Client_SetThirst);
package.add("SetTint", &Perl_Client_SetTint); package.add("SetTint", &Perl_Client_SetTint);
+82
View File
@@ -50,6 +50,8 @@
#include "../common/repositories/character_corpses_repository.h" #include "../common/repositories/character_corpses_repository.h"
#include "../common/repositories/character_corpse_items_repository.h" #include "../common/repositories/character_corpse_items_repository.h"
#include "../common/repositories/zone_repository.h" #include "../common/repositories/zone_repository.h"
#include "../common/repositories/bot_stat_caps_repository.h"
#include "../common/repositories/character_stat_caps_repository.h"
#include "../common/repositories/trader_repository.h" #include "../common/repositories/trader_repository.h"
#include "../common/repositories/character_evolving_items_repository.h" #include "../common/repositories/character_evolving_items_repository.h"
@@ -4297,3 +4299,83 @@ void ZoneDatabase::LoadCharacterTitleSets(Client* c)
c->EnableTitle(e.title_set, false); c->EnableTitle(e.title_set, false);
} }
} }
void ZoneDatabase::LoadStatCaps(Mob* m)
{
if (!zone || !m) {
return;
}
if (m->IsBot()) {
const auto& l = BotStatCapsRepository::GetWhere(
*this,
fmt::format(
"`bot_id` = {}",
m->CastToBot()->GetBotID()
)
);
if (l.empty()) {
return;
}
for (const auto& e : l) {
m->SetStatCap(e.stat_id, e.stat_cap);
}
} else if (m->IsClient()) {
const auto& l = CharacterStatCapsRepository::GetWhere(
*this,
fmt::format(
"`character_id` = {}",
m->CastToClient()->CharacterID()
)
);
if (l.empty()) {
return;
}
for (const auto& e : l) {
m->SetStatCap(e.stat_id, e.stat_cap);
}
}
}
void ZoneDatabase::SaveStatCaps(Mob* m)
{
if (m->IsBot()) {
std::vector<BotStatCapsRepository::BotStatCaps> v;
BotStatCapsRepository::BotStatCaps stat_cap;
stat_cap.bot_id = m->CastToBot()->GetBotID();
for (const auto& e: m->GetStatCaps()) {
stat_cap.stat_id = e.first;
stat_cap.stat_cap = e.second;
v.emplace_back(stat_cap);
}
if (!v.empty()) {
BotStatCapsRepository::ReplaceMany(*this, v);
}
} else if (m->IsClient()) {
std::vector<CharacterStatCapsRepository::CharacterStatCaps> v;
CharacterStatCapsRepository::CharacterStatCaps stat_cap;
stat_cap.character_id = m->CastToClient()->CharacterID();
for (const auto& e: m->GetStatCaps()) {
stat_cap.stat_id = e.first;
stat_cap.stat_cap = e.second;
v.emplace_back(stat_cap);
}
if (!v.empty()) {
CharacterStatCapsRepository::ReplaceMany(*this, v);
}
}
}
+4
View File
@@ -465,6 +465,10 @@ public:
void LoadCharacterEXPModifier(Client* c); void LoadCharacterEXPModifier(Client* c);
void SaveCharacterEXPModifier(Client *c); void SaveCharacterEXPModifier(Client *c);
/* Stat Caps */
void LoadStatCaps(Mob* m);
void SaveStatCaps(Mob* m);
/* Player Title Sets */ /* Player Title Sets */
void LoadCharacterTitleSets(Client* c); void LoadCharacterTitleSets(Client* c);