mirror of
https://github.com/EQEmu/Server.git
synced 2026-02-23 19:52:25 +00:00
[Feature] Add Bot/Client Specific Stat Caps
This commit is contained in:
parent
1eb89edbbd
commit
aba58172f4
@ -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 `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`;
|
||||
)"
|
||||
},
|
||||
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
|
||||
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'
|
||||
FROM bot_data bd
|
||||
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
|
||||
ELSE 255
|
||||
END) != bd.stop_melee_level
|
||||
@ -532,7 +532,7 @@ UPDATE bot_spells_entries SET `type` = 17 WHERE `spell_id` = 3229;
|
||||
.condition = "empty",
|
||||
.match = "",
|
||||
.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
|
||||
(3006, 9957, 100, 20, 254),
|
||||
(3006, 9956, 100, 20, 254),
|
||||
@ -1106,7 +1106,7 @@ FROM bot_spells_entries
|
||||
WHERE `npc_spells_id` = 3005
|
||||
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
|
||||
(3003, 10173, 24, 72, 76, 3),
|
||||
(3003, 10174, 24, 72, 76, 2),
|
||||
@ -2122,7 +2122,21 @@ WHERE NOT EXISTS
|
||||
FROM spells_new
|
||||
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
|
||||
// ManifestEntry{
|
||||
// .version = 9228,
|
||||
|
||||
@ -69,6 +69,7 @@ namespace DatabaseSchema {
|
||||
{"character_potionbelt", "id"},
|
||||
{"character_skills", "id"},
|
||||
{"character_spells", "id"},
|
||||
{"character_stat_caps", "character_id"},
|
||||
{"character_stats_record", "character_id"},
|
||||
{"character_task_timers", "character_id"},
|
||||
{"character_tasks", "charid"},
|
||||
@ -144,6 +145,7 @@ namespace DatabaseSchema {
|
||||
"character_potionbelt",
|
||||
"character_skills",
|
||||
"character_spells",
|
||||
"character_stat_caps",
|
||||
"character_stats_record",
|
||||
"character_task_timers",
|
||||
"character_tasks",
|
||||
|
||||
@ -919,4 +919,29 @@ namespace PetType {
|
||||
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;
|
||||
}
|
||||
|
||||
#endif /*COMMON_EMU_CONSTANTS_H*/
|
||||
|
||||
404
common/repositories/base/base_bot_stat_caps_repository.h
Normal file
404
common/repositories/base/base_bot_stat_caps_repository.h
Normal file
@ -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
|
||||
404
common/repositories/base/base_character_stat_caps_repository.h
Normal file
404
common/repositories/base/base_character_stat_caps_repository.h
Normal file
@ -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
|
||||
50
common/repositories/bot_stat_caps_repository.h
Normal file
50
common/repositories/bot_stat_caps_repository.h
Normal file
@ -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
|
||||
50
common/repositories/character_stat_caps_repository.h
Normal file
50
common/repositories/character_stat_caps_repository.h
Normal file
@ -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
|
||||
@ -42,8 +42,8 @@
|
||||
* Manifest: https://github.com/EQEmu/Server/blob/master/utils/sql/db_update_manifest.txt
|
||||
*/
|
||||
|
||||
#define CURRENT_BINARY_DATABASE_VERSION 9328
|
||||
#define CURRENT_BINARY_BOTS_DATABASE_VERSION 9054
|
||||
#define CURRENT_BINARY_DATABASE_VERSION 9329
|
||||
#define CURRENT_BINARY_BOTS_DATABASE_VERSION 9055
|
||||
#define CUSTOM_BINARY_DATABASE_VERSION 0
|
||||
|
||||
#endif
|
||||
|
||||
@ -6701,7 +6701,7 @@ void Client::SetAttackTimer()
|
||||
if (ItemToUse && ItemToUse->ItemType == EQ::item::ItemTypeBow) {
|
||||
// Live actually had a bug here where they would return the non-modified attack speed
|
||||
// rather than the cap ...
|
||||
speed = std::max(speed - GetQuiverHaste(speed), RuleI(Combat, QuiverHasteCap));
|
||||
speed = std::max(speed - GetQuiverHaste(speed), GetStatCap(StatCap::QuiverHaste));
|
||||
}
|
||||
else {
|
||||
if (RuleB(Spells, Jun182014HundredHandsRevamp))
|
||||
|
||||
156
zone/bonuses.cpp
156
zone/bonuses.cpp
@ -246,12 +246,22 @@ void Mob::ProcessItemCaps()
|
||||
|
||||
itembonuses.ATK = std::min(itembonuses.ATK, CalcItemATKCap());
|
||||
|
||||
if (IsOfClientBotMerc() && itembonuses.SpellDmg > RuleI(Character, ItemSpellDmgCap)) {
|
||||
itembonuses.SpellDmg = RuleI(Character, ItemSpellDmgCap);
|
||||
int spell_damage_cap = (
|
||||
IsOfClientBot() ?
|
||||
GetStatCap(StatCap::SpellDamage) :
|
||||
RuleI(Character, ItemSpellDmgCap)
|
||||
);
|
||||
if (IsOfClientBotMerc() && itembonuses.SpellDmg > spell_damage_cap) {
|
||||
itembonuses.SpellDmg = spell_damage_cap;
|
||||
}
|
||||
|
||||
if (IsOfClientBotMerc() && itembonuses.HealAmt > RuleI(Character, ItemHealAmtCap)) {
|
||||
itembonuses.HealAmt = RuleI(Character, ItemHealAmtCap);
|
||||
int heal_amount_cap = (
|
||||
IsOfClientBot() ?
|
||||
GetStatCap(StatCap::HealAmount) :
|
||||
RuleI(Character, ItemHealAmtCap)
|
||||
);
|
||||
if (IsOfClientBotMerc() && itembonuses.HealAmt > heal_amount_cap) {
|
||||
itembonuses.HealAmt = heal_amount_cap;
|
||||
}
|
||||
}
|
||||
|
||||
@ -351,33 +361,139 @@ void Mob::AddItemBonuses(const EQ::ItemInstance* inst, StatBonuses* b, bool is_a
|
||||
b->ManaRegen += CalcItemBonus(item->ManaRegen);
|
||||
b->EnduranceRegen += CalcItemBonus(item->EnduranceRegen);
|
||||
|
||||
int accuracy_cap = (
|
||||
IsOfClientBot() ?
|
||||
GetStatCap(StatCap::Accuracy) :
|
||||
RuleI(Character, ItemAccuracyCap)
|
||||
);
|
||||
|
||||
int attack_cap = (
|
||||
IsOfClientBot() ?
|
||||
GetStatCap(StatCap::Attack) :
|
||||
RuleI(Character, ItemATKCap)
|
||||
);
|
||||
|
||||
int avoidance_cap = (
|
||||
IsOfClientBot() ?
|
||||
GetStatCap(StatCap::Avoidance) :
|
||||
RuleI(Character, ItemAvoidanceCap)
|
||||
);
|
||||
|
||||
int clairvoyance_cap = (
|
||||
IsOfClientBot() ?
|
||||
GetStatCap(StatCap::Clairvoyance) :
|
||||
RuleI(Character, ItemClairvoyanceCap)
|
||||
);
|
||||
|
||||
int combat_effects_cap = (
|
||||
IsOfClientBot() ?
|
||||
GetStatCap(StatCap::CombatEffects) :
|
||||
RuleI(Character, ItemCombatEffectsCap)
|
||||
);
|
||||
|
||||
int damage_shield_cap = (
|
||||
IsOfClientBot() ?
|
||||
GetStatCap(StatCap::DamageShield) :
|
||||
RuleI(Character, ItemDamageShieldCap)
|
||||
);
|
||||
|
||||
int dot_shielding_cap = (
|
||||
IsOfClientBot() ?
|
||||
GetStatCap(StatCap::DOTShielding) :
|
||||
RuleI(Character, ItemDoTShieldingCap)
|
||||
);
|
||||
|
||||
int dsmitigation_cap = (
|
||||
IsOfClientBot() ?
|
||||
GetStatCap(StatCap::DSMitigation) :
|
||||
RuleI(Character, ItemDSMitigationCap)
|
||||
);
|
||||
|
||||
int heal_amount_cap = (
|
||||
IsOfClientBot() ?
|
||||
GetStatCap(StatCap::HealAmount) :
|
||||
RuleI(Character, ItemHealAmtCap)
|
||||
);
|
||||
|
||||
int shielding_cap = (
|
||||
IsOfClientBot() ?
|
||||
GetStatCap(StatCap::Shielding) :
|
||||
RuleI(Character, ItemShieldingCap)
|
||||
);
|
||||
|
||||
int spell_damage_cap = (
|
||||
IsOfClientBot() ?
|
||||
GetStatCap(StatCap::SpellDamage) :
|
||||
RuleI(Character, ItemSpellDmgCap)
|
||||
);
|
||||
|
||||
int spell_shielding_cap = (
|
||||
IsOfClientBot() ?
|
||||
GetStatCap(StatCap::SpellShielding) :
|
||||
RuleI(Character, ItemSpellShieldingCap)
|
||||
);
|
||||
|
||||
int strikethrough_cap = (
|
||||
IsOfClientBot() ?
|
||||
GetStatCap(StatCap::Strikethrough) :
|
||||
RuleI(Character, ItemStrikethroughCap)
|
||||
);
|
||||
|
||||
int stun_resist_cap = (
|
||||
IsOfClientBot() ?
|
||||
GetStatCap(StatCap::StunResist) :
|
||||
RuleI(Character, ItemStunResistCap)
|
||||
);
|
||||
|
||||
// These have rule-configured caps.
|
||||
b->ATK = CalcCappedItemBonus(b->ATK, item->Attack, RuleI(Character, ItemATKCap) + itembonuses.ItemATKCap + spellbonuses.ItemATKCap + aabonuses.ItemATKCap);
|
||||
b->DamageShield = CalcCappedItemBonus(b->DamageShield, item->DamageShield, RuleI(Character, ItemDamageShieldCap));
|
||||
b->SpellShield = CalcCappedItemBonus(b->SpellShield, item->SpellShield, RuleI(Character, ItemSpellShieldingCap));
|
||||
b->MeleeMitigation = CalcCappedItemBonus(b->MeleeMitigation, item->Shielding, RuleI(Character, ItemShieldingCap));
|
||||
b->StunResist = CalcCappedItemBonus(b->StunResist, item->StunResist, RuleI(Character, ItemStunResistCap));
|
||||
b->StrikeThrough = CalcCappedItemBonus(b->StrikeThrough, item->StrikeThrough, RuleI(Character, ItemStrikethroughCap));
|
||||
b->AvoidMeleeChance = CalcCappedItemBonus(b->AvoidMeleeChance, item->Avoidance, RuleI(Character, ItemAvoidanceCap));
|
||||
b->HitChance = CalcCappedItemBonus(b->HitChance, item->Accuracy, RuleI(Character, ItemAccuracyCap));
|
||||
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->SpellDmg = CalcCappedItemBonus(b->SpellDmg, item->SpellDmg, RuleI(Character, ItemSpellDmgCap));
|
||||
b->Clairvoyance = CalcCappedItemBonus(b->Clairvoyance, item->Clairvoyance, RuleI(Character, ItemClairvoyanceCap));
|
||||
b->DSMitigation = CalcCappedItemBonus(b->DSMitigation, item->DSMitigation, RuleI(Character, ItemDSMitigationCap));
|
||||
b->ATK = CalcCappedItemBonus(
|
||||
b->ATK,
|
||||
item->Attack,
|
||||
(
|
||||
attack_cap +
|
||||
itembonuses.ItemATKCap +
|
||||
spellbonuses.ItemATKCap +
|
||||
aabonuses.ItemATKCap
|
||||
)
|
||||
);
|
||||
b->DamageShield = CalcCappedItemBonus(b->DamageShield, item->DamageShield, damage_shield_cap);
|
||||
b->SpellShield = CalcCappedItemBonus(b->SpellShield, item->SpellShield, spell_shielding_cap);
|
||||
b->MeleeMitigation = CalcCappedItemBonus(b->MeleeMitigation, item->Shielding, shielding_cap);
|
||||
b->StunResist = CalcCappedItemBonus(b->StunResist, item->StunResist, stun_resist_cap);
|
||||
b->StrikeThrough = CalcCappedItemBonus(b->StrikeThrough, item->StrikeThrough, strikethrough_cap);
|
||||
b->AvoidMeleeChance = CalcCappedItemBonus(b->AvoidMeleeChance, item->Avoidance, avoidance_cap);
|
||||
b->HitChance = CalcCappedItemBonus(b->HitChance, item->Accuracy, accuracy_cap);
|
||||
b->ProcChance = CalcCappedItemBonus(b->ProcChance, item->CombatEffects, combat_effects_cap);
|
||||
b->DoTShielding = CalcCappedItemBonus(b->DoTShielding, item->DotShielding, dot_shielding_cap);
|
||||
b->HealAmt = CalcCappedItemBonus(b->HealAmt, item->HealAmt, heal_amount_cap);
|
||||
b->SpellDmg = CalcCappedItemBonus(b->SpellDmg, item->SpellDmg, spell_damage_cap);
|
||||
b->Clairvoyance = CalcCappedItemBonus(b->Clairvoyance, item->Clairvoyance, clairvoyance_cap);
|
||||
b->DSMitigation = CalcCappedItemBonus(b->DSMitigation, item->DSMitigation, dsmitigation_cap);
|
||||
|
||||
if (b->haste < item->Haste) {
|
||||
b->haste = item->Haste;
|
||||
}
|
||||
|
||||
if (item->ExtraDmgAmt != 0 && item->ExtraDmgSkill <= EQ::skills::HIGHEST_SKILL) {
|
||||
int extra_damage_cap = (
|
||||
IsOfClientBot() ?
|
||||
GetStatCap(StatCap::ExtraDamage) :
|
||||
RuleI(Character, ItemExtraDmgCap)
|
||||
);
|
||||
if (item->ExtraDmgSkill == ALL_SKILLS) {
|
||||
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,
|
||||
extra_damage_cap
|
||||
);
|
||||
}
|
||||
} 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,
|
||||
extra_damage_cap
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -268,6 +268,8 @@ Bot::Bot(
|
||||
LoadDefaultBotSettings();
|
||||
database.botdb.LoadBotSettings(this);
|
||||
|
||||
database.LoadStatCaps(this);
|
||||
|
||||
if (RuleB(Bots, AllowBotBlockedBuffs)) {
|
||||
bot_blocked_buffs.clear();
|
||||
database.botdb.LoadBotBlockedBuffs(this);
|
||||
@ -1420,6 +1422,7 @@ bool Bot::Save()
|
||||
database.botdb.SaveTimers(this);
|
||||
database.botdb.SaveStance(this);
|
||||
database.botdb.SaveBotSettings(this);
|
||||
database.SaveStatCaps(this);
|
||||
|
||||
if (RuleB(Bots, AllowBotBlockedBuffs)) {
|
||||
database.botdb.SaveBotBlockedBuffs(this);
|
||||
|
||||
13
zone/bot.h
13
zone/bot.h
@ -101,7 +101,7 @@ namespace BotSettingCategories {
|
||||
constexpr uint8 SpellTypeMinManaPct = 7;
|
||||
constexpr uint8 SpellTypeMaxManaPct = 8;
|
||||
constexpr uint8 SpellTypeMinHPPct = 9;
|
||||
constexpr uint8 SpellTypeMaxHPPct = 10;
|
||||
constexpr uint8 SpellTypeMaxHPPct = 10;
|
||||
constexpr uint8 SpellTypeIdlePriority = 11;
|
||||
constexpr uint8 SpellTypeEngagedPriority = 12;
|
||||
constexpr uint8 SpellTypePursuePriority = 13;
|
||||
@ -166,7 +166,7 @@ namespace BotBaseSettings {
|
||||
constexpr uint16 ExpansionBitmask = 0;
|
||||
constexpr uint16 ShowHelm = 1;
|
||||
constexpr uint16 FollowDistance = 2;
|
||||
constexpr uint16 StopMeleeLevel = 3;
|
||||
constexpr uint16 StopMeleeLevel = 3;
|
||||
constexpr uint16 EnforceSpellSettings = 4;
|
||||
constexpr uint16 RangedSetting = 5;
|
||||
constexpr uint16 PetSetTypeSetting = 6;
|
||||
@ -487,7 +487,7 @@ public:
|
||||
void SetCommandedSpell(bool value) { _commandedSpell = value; }
|
||||
bool IsPullingSpell() const { return _pullingSpell; }
|
||||
void SetPullingSpell(bool value) { _pullingSpell = value; }
|
||||
|
||||
|
||||
void SetGuardMode();
|
||||
void SetHoldMode();
|
||||
|
||||
@ -550,7 +550,7 @@ public:
|
||||
bool IsValidMezTarget(Mob* owner, Mob* npc, uint16 spell_id);
|
||||
|
||||
// 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 IsImmuneToBotSpell(uint16 spell_id, Mob* caster);
|
||||
bool CanCastSpellType(uint16 spell_type, uint16 spell_id, Mob* tar);
|
||||
@ -601,7 +601,7 @@ public:
|
||||
void ResetBotSpellSettings();
|
||||
|
||||
void CopyBotBlockedBuffs(Bot* to);
|
||||
void CopyBotBlockedPetBuffs(Bot* to);
|
||||
void CopyBotBlockedPetBuffs(Bot* to);
|
||||
void CleanBotBlockedBuffs();
|
||||
void ClearBotBlockedBuffs() { bot_blocked_buffs.clear(); }
|
||||
bool IsBlockedBuff(int32 spell_id) override;
|
||||
@ -658,7 +658,7 @@ public:
|
||||
bool GetBehindMob() const { return _behindMobStatus; }
|
||||
void SetBehindMob(bool value) { _behindMobStatus = value; }
|
||||
bool GetMaxMeleeRange() const { return _maxMeleeRangeStatus; }
|
||||
void SetMaxMeleeRange(bool value) { _maxMeleeRangeStatus = value; }
|
||||
void SetMaxMeleeRange(bool value) { _maxMeleeRangeStatus = value; }
|
||||
uint8 GetStopMeleeLevel() const { return _stopMeleeLevel; }
|
||||
void SetStopMeleeLevel(uint8 level) { _stopMeleeLevel = level; }
|
||||
uint32 GetBotDistanceRanged() const { return _distanceRanged; }
|
||||
@ -1210,7 +1210,6 @@ private:
|
||||
bool _hasLoS;
|
||||
bool _commandedSpell;
|
||||
bool _pullingSpell;
|
||||
|
||||
bool _illusionBlock;
|
||||
std::vector<BotSpellSettings> m_bot_spell_settings;
|
||||
std::vector<Mob*> _spell_target_list;
|
||||
|
||||
@ -1103,6 +1103,8 @@ bool Client::Save(uint8 iCommitNow) {
|
||||
|
||||
database.SaveCharacterEXPModifier(this);
|
||||
|
||||
database.SaveStatCaps(this);
|
||||
|
||||
if (RuleB(Bots, Enabled)) {
|
||||
database.botdb.SaveBotSettings(this);
|
||||
}
|
||||
|
||||
@ -32,23 +32,24 @@
|
||||
|
||||
int32 Client::GetMaxStat() const
|
||||
{
|
||||
if ((RuleI(Character, StatCap)) > 0) {
|
||||
return (RuleI(Character, StatCap));
|
||||
int character_cap = GetStatCap(StatCap::Stat);
|
||||
if (character_cap > 0) {
|
||||
return character_cap;
|
||||
}
|
||||
int level = GetLevel();
|
||||
int32 base = 0;
|
||||
|
||||
uint8 level = GetLevel();
|
||||
int base = 0;
|
||||
|
||||
if (level < 61) {
|
||||
base = 255;
|
||||
}
|
||||
else if (ClientVersion() >= EQ::versions::ClientVersion::SoF) {
|
||||
} else if (ClientVersion() >= EQ::versions::ClientVersion::SoF) {
|
||||
base = 255 + 5 * (level - 60);
|
||||
}
|
||||
else if (level < 71) {
|
||||
} else if (level < 71) {
|
||||
base = 255 + 5 * (level - 60);
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
base = 330;
|
||||
}
|
||||
|
||||
return base;
|
||||
}
|
||||
|
||||
@ -300,7 +301,7 @@ int64 Client::CalcHPRegen(bool bCombat)
|
||||
|
||||
int64 Client::CalcHPRegenCap()
|
||||
{
|
||||
int64 cap = RuleI(Character, ItemHealthRegenCap);
|
||||
int64 cap = GetStatCap(StatCap::HealthRegen);
|
||||
if (GetLevel() > 60) {
|
||||
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 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);
|
||||
}
|
||||
|
||||
@ -976,7 +977,7 @@ int Client::CalcHaste()
|
||||
}
|
||||
// 60+ 100, 51-59 85, 1-50 level+25
|
||||
if (level > 59 || RuleB(Character, IgnoreLevelBasedHasteCaps)) { // 60+
|
||||
cap = RuleI(Character, HasteCap);
|
||||
cap = GetStatCap(StatCap::Haste);
|
||||
}
|
||||
else if (level > 50) { // 51-59
|
||||
cap = 85;
|
||||
@ -989,7 +990,7 @@ int Client::CalcHaste()
|
||||
}
|
||||
// 51+ 25 (despite there being higher spells...), 1-50 10
|
||||
if (level > 50 || RuleB(Character, IgnoreLevelBasedHasteCaps)) { // 51+
|
||||
cap = RuleI(Character, Hastev3Cap);
|
||||
cap = GetStatCap(StatCap::HasteV3);
|
||||
if (spellbonuses.hastetype3 > cap) {
|
||||
h += cap;
|
||||
} else {
|
||||
@ -1745,13 +1746,13 @@ int64 Client::CalcEnduranceRegen(bool bCombat)
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
|
||||
@ -1386,6 +1386,7 @@ void Client::Handle_Connect_OP_ZoneEntry(const EQApplicationPacket *app)
|
||||
database.LoadCharacterTribute(this); /* Load CharacterTribute */
|
||||
database.LoadCharacterEXPModifier(this); /* Load Character EXP Modifier */
|
||||
database.LoadCharacterTitleSets(this); /* Load Character Title Sets */
|
||||
database.LoadStatCaps(this); /* Load Character Stat Caps */
|
||||
|
||||
// this pattern is strange
|
||||
// this is remnants of the old way of doing things
|
||||
|
||||
@ -664,6 +664,18 @@ void Lua_Bot::RaidGroupSay(const char* 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() {
|
||||
return luabind::class_<Lua_Bot, Lua_Mob>("Bot")
|
||||
.def(luabind::constructor<>())
|
||||
@ -747,6 +759,7 @@ luabind::scope lua_register_bot() {
|
||||
.def("GetSpellDamage", (int(Lua_Bot::*)(void))&Lua_Bot::GetSpellDamage)
|
||||
.def("GetSpellRecastTimer", (uint32(Lua_Bot::*)())&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("HasBotItem", (int16(Lua_Bot::*)(uint32))&Lua_Bot::HasBotItem)
|
||||
.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("SetSpellRecastTimer", (void(Lua_Bot::*)(uint16))&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,std::string))&Lua_Bot::SendPayload)
|
||||
.def("Signal", (void(Lua_Bot::*)(int))&Lua_Bot::Signal)
|
||||
|
||||
@ -126,6 +126,8 @@ public:
|
||||
void SetItemReuseTimer(uint32 item_id, uint32 reuse_timer);
|
||||
void SetSpellRecastTimer(uint16 spell_id);
|
||||
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 CountItemEquippedByID(uint32 item_id);
|
||||
|
||||
@ -3624,6 +3624,18 @@ luabind::object Lua_Client::GetKeyRing(lua_State* L)
|
||||
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() {
|
||||
return luabind::class_<Lua_Client, Lua_Mob>("Client")
|
||||
.def(luabind::constructor<>())
|
||||
@ -3898,6 +3910,7 @@ luabind::scope lua_register_client() {
|
||||
.def("GetSpellDamage", (int(Lua_Client::*)(void))&Lua_Client::GetSpellDamage)
|
||||
.def("GetSpellIDByBookSlot", (uint32(Lua_Client::*)(int))&Lua_Client::GetSpellIDByBookSlot)
|
||||
.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("GetTargetRingX", (float(Lua_Client::*)(void))&Lua_Client::GetTargetRingX)
|
||||
.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,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("SetThirst", (void(Lua_Client::*)(int))&Lua_Client::SetThirst)
|
||||
.def("SetTint", (void(Lua_Client::*)(int,uint32))&Lua_Client::SetTint)
|
||||
|
||||
@ -607,6 +607,8 @@ public:
|
||||
bool RemoveAAPoints(uint32 points);
|
||||
bool RemoveAlternateCurrencyValue(uint32 currency_id, uint32 amount);
|
||||
bool AreTasksCompleted(luabind::object task_ids);
|
||||
int GetStatCap(uint8 stat_id);
|
||||
void SetStatCap(uint8 stat_id, int stat_cap);
|
||||
|
||||
void DialogueWindow(std::string markdown);
|
||||
|
||||
|
||||
197
zone/mob.cpp
197
zone/mob.cpp
@ -22,7 +22,9 @@
|
||||
#include "../common/misc_functions.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_stat_caps_repository.h"
|
||||
|
||||
#include "../common/data_bucket.h"
|
||||
#include "quest_parser_collection.h"
|
||||
@ -2059,10 +2061,20 @@ void Mob::SendStatsWindow(Client* c, bool use_window)
|
||||
for (auto mod2_row_counter = 0; mod2_row_counter < mod2_rows; mod2_row_counter++) {
|
||||
switch (mod2_row_counter) {
|
||||
case 0: {
|
||||
int avoidance_cap = (
|
||||
IsOfClientBot() ?
|
||||
GetStatCap(StatCap::Avoidance) :
|
||||
RuleI(Character, ItemAvoidanceCap)
|
||||
);
|
||||
int combat_effects_cap = (
|
||||
IsOfClientBot() ?
|
||||
GetStatCap(StatCap::CombatEffects) :
|
||||
RuleI(Character, ItemCombatEffectsCap)
|
||||
);
|
||||
mod2a_name = "Avoidance";
|
||||
mod2b_name = "Combat Effects";
|
||||
mod2a_cap = Strings::Commify(RuleI(Character, ItemAvoidanceCap));
|
||||
mod2b_cap = Strings::Commify(RuleI(Character, ItemCombatEffectsCap));
|
||||
mod2a_cap = Strings::Commify(avoidance_cap);
|
||||
mod2b_cap = Strings::Commify(combat_effects_cap);
|
||||
|
||||
if (IsBot()) {
|
||||
mod2a = Strings::Commify(CastToBot()->GetAvoidance());
|
||||
@ -2079,10 +2091,20 @@ void Mob::SendStatsWindow(Client* c, bool use_window)
|
||||
break;
|
||||
}
|
||||
case 1: {
|
||||
int accuracy_cap = (
|
||||
IsOfClientBot() ?
|
||||
GetStatCap(StatCap::Accuracy) :
|
||||
RuleI(Character, ItemAccuracyCap)
|
||||
);
|
||||
int strikethrough_cap = (
|
||||
IsOfClientBot() ?
|
||||
GetStatCap(StatCap::Strikethrough) :
|
||||
RuleI(Character, ItemStrikethroughCap)
|
||||
);
|
||||
mod2a_name = "Accuracy";
|
||||
mod2b_name = "Strikethrough";
|
||||
mod2a_cap = Strings::Commify(RuleI(Character, ItemAccuracyCap));
|
||||
mod2b_cap = Strings::Commify(RuleI(Character, ItemStrikethroughCap));
|
||||
mod2a_cap = Strings::Commify(accuracy_cap);
|
||||
mod2b_cap = Strings::Commify(strikethrough_cap);
|
||||
|
||||
if (IsBot()) {
|
||||
mod2a = Strings::Commify(CastToBot()->GetAccuracy());
|
||||
@ -2099,10 +2121,20 @@ void Mob::SendStatsWindow(Client* c, bool use_window)
|
||||
break;
|
||||
}
|
||||
case 2: {
|
||||
int shielding_cap = (
|
||||
IsOfClientBot() ?
|
||||
GetStatCap(StatCap::Shielding) :
|
||||
RuleI(Character, ItemShieldingCap)
|
||||
);
|
||||
int spell_shielding_cap = (
|
||||
IsOfClientBot() ?
|
||||
GetStatCap(StatCap::SpellShielding) :
|
||||
RuleI(Character, ItemSpellShieldingCap)
|
||||
);
|
||||
mod2a_name = "Shielding";
|
||||
mod2b_name = "Spell Shielding";
|
||||
mod2a_cap = Strings::Commify(RuleI(Character, ItemShieldingCap));
|
||||
mod2b_cap = Strings::Commify(RuleI(Character, ItemSpellShieldingCap));
|
||||
mod2a_cap = Strings::Commify(shielding_cap);
|
||||
mod2b_cap = Strings::Commify(spell_shielding_cap);
|
||||
|
||||
if (IsBot()) {
|
||||
mod2a = Strings::Commify(CastToBot()->GetShielding());
|
||||
@ -2120,10 +2152,20 @@ void Mob::SendStatsWindow(Client* c, bool use_window)
|
||||
break;
|
||||
}
|
||||
case 3: {
|
||||
int dot_shielding_cap = (
|
||||
IsOfClientBot() ?
|
||||
GetStatCap(StatCap::DOTShielding) :
|
||||
RuleI(Character, ItemDoTShieldingCap)
|
||||
);
|
||||
int stun_resist_cap = (
|
||||
IsOfClientBot() ?
|
||||
GetStatCap(StatCap::StunResist) :
|
||||
RuleI(Character, ItemStunResistCap)
|
||||
);
|
||||
mod2a_name = "Stun Resist";
|
||||
mod2b_name = "DOT Shielding";
|
||||
mod2a_cap = Strings::Commify(RuleI(Character, ItemStunResistCap));
|
||||
mod2b_cap = Strings::Commify(RuleI(Character, ItemDoTShieldingCap));
|
||||
mod2a_cap = Strings::Commify(stun_resist_cap);
|
||||
mod2b_cap = Strings::Commify(dot_shielding_cap);
|
||||
|
||||
if (IsBot()) {
|
||||
mod2a = Strings::Commify(CastToBot()->GetStunResist());
|
||||
@ -2359,6 +2401,11 @@ void Mob::SendStatsWindow(Client* c, bool use_window)
|
||||
|
||||
final_string += DialogueWindow::Break(1);
|
||||
|
||||
int attack_cap = (
|
||||
IsOfClientBot() ?
|
||||
GetStatCap(StatCap::Attack) :
|
||||
RuleI(Character, ItemATKCap)
|
||||
);
|
||||
// Attack 2
|
||||
final_string += fmt::format(
|
||||
"Offense: {}{}{}{}",
|
||||
@ -2368,7 +2415,7 @@ void Mob::SendStatsWindow(Client* c, bool use_window)
|
||||
fmt::format(
|
||||
" | Item: {} / {} | Used: {}",
|
||||
Strings::Commify(itembonuses.ATK),
|
||||
Strings::Commify(RuleI(Character, ItemATKCap)),
|
||||
Strings::Commify(attack_cap),
|
||||
Strings::Commify(static_cast<int>(itembonuses.ATK * 1.342))
|
||||
) :
|
||||
""
|
||||
@ -2415,6 +2462,11 @@ void Mob::SendStatsWindow(Client* c, bool use_window)
|
||||
|
||||
final_string += DialogueWindow::CenterMessage("Haste");
|
||||
|
||||
int haste_cap = (
|
||||
IsOfClientBot() ?
|
||||
GetStatCap(StatCap::Haste) :
|
||||
RuleI(Character, HasteCap)
|
||||
);
|
||||
// Haste Table
|
||||
const auto& haste_table = DialogueWindow::Table(
|
||||
fmt::format(
|
||||
@ -2433,7 +2485,7 @@ void Mob::SendStatsWindow(Client* c, bool use_window)
|
||||
fmt::format(
|
||||
"{} ({})",
|
||||
IsClient() ? Strings::Commify(CastToClient()->GetHaste()) : Strings::Commify(GetHaste()),
|
||||
Strings::Commify(RuleI(Character, HasteCap))
|
||||
Strings::Commify(haste_cap)
|
||||
)
|
||||
)
|
||||
)
|
||||
@ -2468,26 +2520,41 @@ void Mob::SendStatsWindow(Client* c, bool use_window)
|
||||
|
||||
// Heal Amount
|
||||
if (GetHealAmt()) {
|
||||
int cap = (
|
||||
IsOfClientBot() ?
|
||||
GetStatCap(StatCap::HealAmount) :
|
||||
RuleI(Character, ItemHealAmtCap)
|
||||
);
|
||||
final_string += fmt::format(
|
||||
"Heal Amount: {} / {}{}",
|
||||
Strings::Commify(GetHealAmt()),
|
||||
Strings::Commify(RuleI(Character, ItemHealAmtCap)),
|
||||
Strings::Commify(cap),
|
||||
DialogueWindow::Break(1)
|
||||
);
|
||||
}
|
||||
|
||||
// Heal Amount
|
||||
// Spell Damage
|
||||
if (GetSpellDmg()) {
|
||||
int cap = (
|
||||
IsOfClientBot() ?
|
||||
GetStatCap(StatCap::SpellDamage) :
|
||||
RuleI(Character, ItemSpellDmgCap)
|
||||
);
|
||||
final_string += fmt::format(
|
||||
"Spell Damage: {} / {}{}",
|
||||
Strings::Commify(GetSpellDmg()),
|
||||
Strings::Commify(RuleI(Character, ItemSpellDmgCap)),
|
||||
Strings::Commify(cap),
|
||||
DialogueWindow::Break(1)
|
||||
);
|
||||
}
|
||||
|
||||
// Damage Shield
|
||||
if (itembonuses.DamageShield || spellbonuses.DamageShield) {
|
||||
int cap = (
|
||||
IsOfClientBot() ?
|
||||
GetStatCap(StatCap::DamageShield) :
|
||||
RuleI(Character, ItemDamageShieldCap)
|
||||
);
|
||||
final_string += fmt::format(
|
||||
"Damage Shield: {}{}{}{}",
|
||||
Strings::Commify(itembonuses.DamageShield + spellbonuses.DamageShield),
|
||||
@ -2501,7 +2568,7 @@ void Mob::SendStatsWindow(Client* c, bool use_window)
|
||||
fmt::format(
|
||||
" | Item: {} / {}",
|
||||
Strings::Commify(itembonuses.DamageShield),
|
||||
Strings::Commify(RuleI(Character, ItemDamageShieldCap))
|
||||
Strings::Commify(cap)
|
||||
) :
|
||||
""
|
||||
),
|
||||
@ -2510,12 +2577,17 @@ void Mob::SendStatsWindow(Client* c, bool use_window)
|
||||
}
|
||||
|
||||
// Clairvoyance
|
||||
const auto clairvoyance = IsBot() ? CastToBot()->GetClair() : CastToClient()->GetClair();
|
||||
const auto clairvoyance = IsBot() ? CastToBot()->GetClair() : CastToClient()->GetClair();
|
||||
if (clairvoyance) {
|
||||
int cap = (
|
||||
IsOfClientBot() ?
|
||||
GetStatCap(StatCap::Clairvoyance) :
|
||||
RuleI(Character, ItemClairvoyanceCap)
|
||||
);
|
||||
final_string += fmt::format(
|
||||
"Clairvoyance: {} / {}{}",
|
||||
Strings::Commify(clairvoyance),
|
||||
Strings::Commify(RuleI(Character, ItemClairvoyanceCap)),
|
||||
Strings::Commify(cap),
|
||||
DialogueWindow::Break(1)
|
||||
);
|
||||
}
|
||||
@ -2523,10 +2595,15 @@ void Mob::SendStatsWindow(Client* c, bool use_window)
|
||||
// Damage Shield Mitigation
|
||||
const auto ds_mitigation = IsBot() ? CastToBot()->GetDSMit() : CastToClient()->GetDSMit();
|
||||
if (ds_mitigation) {
|
||||
int cap = (
|
||||
IsOfClientBot() ?
|
||||
GetStatCap(StatCap::DSMitigation) :
|
||||
RuleI(Character, ItemDSMitigationCap)
|
||||
);
|
||||
final_string += fmt::format(
|
||||
"DS Mitigation: {} / {}{}",
|
||||
Strings::Commify(ds_mitigation),
|
||||
Strings::Commify(RuleI(Character, ItemDSMitigationCap)),
|
||||
Strings::Commify(cap),
|
||||
DialogueWindow::Break(1)
|
||||
);
|
||||
}
|
||||
@ -2555,6 +2632,12 @@ void Mob::SendStatsWindow(Client* c, bool use_window)
|
||||
final_string += faction_item_string;
|
||||
}
|
||||
|
||||
int damage_shield_cap = (
|
||||
IsOfClientBot() ?
|
||||
GetStatCap(StatCap::DamageShield) :
|
||||
RuleI(Character, ItemDamageShieldCap)
|
||||
);
|
||||
|
||||
if (use_window) {
|
||||
if (final_string.size() < 4096) {
|
||||
const uint32 popup_buttons = (c->ClientVersion() < EQ::versions::ClientVersion::SoD) ? 0 : 1;
|
||||
@ -2596,7 +2679,7 @@ void Mob::SendStatsWindow(Client* c, bool use_window)
|
||||
GetRaceIDName(GetRace()),
|
||||
GetRace(),
|
||||
IsBot() ? Strings::Commify(CastToBot()->GetDS()) : Strings::Commify(CastToClient()->GetDS()),
|
||||
Strings::Commify(RuleI(Character, ItemDamageShieldCap)),
|
||||
Strings::Commify(damage_shield_cap),
|
||||
GetSize(),
|
||||
GetRunspeed(),
|
||||
IsBot() ? static_cast<float>(CastToBot()->CalcCurrentWeight()) / 10.0f : static_cast<float>(CastToClient()->CalcCurrentWeight()) / 10.0f,
|
||||
@ -2694,18 +2777,23 @@ void Mob::SendStatsWindow(Client* c, bool use_window)
|
||||
"Attack: {} Item and Spell Attack: {}/{} Server Side Attack: {}",
|
||||
IsBot() ? Strings::Commify(CastToBot()->GetTotalATK()) : Strings::Commify(CastToClient()->GetTotalATK()),
|
||||
Strings::Commify(GetATKBonus()),
|
||||
Strings::Commify(RuleI(Character, ItemATKCap)),
|
||||
Strings::Commify(attack_cap),
|
||||
Strings::Commify(GetATK())
|
||||
).c_str()
|
||||
);
|
||||
|
||||
if ((IsClient() && CastToClient()->GetHaste()) || (!IsClient() && GetHaste())) {
|
||||
int haste_cap = (
|
||||
IsOfClientBot() ?
|
||||
GetStatCap(StatCap::Haste) :
|
||||
RuleI(Character, HasteCap)
|
||||
);
|
||||
c->Message(
|
||||
Chat::White,
|
||||
fmt::format(
|
||||
"Haste: {}/{} (Item: {} + Spell: {} + Over: {})",
|
||||
IsClient() ? Strings::Commify(CastToClient()->GetHaste()) : Strings::Commify(GetHaste()),
|
||||
Strings::Commify(RuleI(Character, HasteCap)),
|
||||
Strings::Commify(haste_cap),
|
||||
Strings::Commify(itembonuses.haste),
|
||||
Strings::Commify(spellbonuses.haste + spellbonuses.hastetype2),
|
||||
Strings::Commify(spellbonuses.hastetype3 + extra_haste)
|
||||
@ -8793,3 +8881,72 @@ bool Mob::LoadDataBucketsCache()
|
||||
|
||||
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
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1519,6 +1519,11 @@ public:
|
||||
bool IsGuildmaster() const;
|
||||
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:
|
||||
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);
|
||||
@ -1802,6 +1807,8 @@ protected:
|
||||
|
||||
CombatRecord m_combat_record{};
|
||||
|
||||
std::map<uint8, int> m_stat_caps;
|
||||
|
||||
public:
|
||||
const CombatRecord &GetCombatRecord() const;
|
||||
|
||||
|
||||
@ -620,6 +620,16 @@ void Perl_Bot_RaidGroupSay(Bot* self, const char* message) // @categories Script
|
||||
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()
|
||||
{
|
||||
perl::interpreter state(PERL_GET_THX);
|
||||
@ -683,27 +693,28 @@ void perl_register_bot()
|
||||
package.add("GetBotItem", &Perl_Bot_GetBotItem);
|
||||
package.add("GetBotItemIDBySlot", &Perl_Bot_GetBotItemIDBySlot);
|
||||
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("GetGroup", &Perl_Bot_GetGroup);
|
||||
package.add("GetHealAmount", &Perl_Bot_GetHealAmount);
|
||||
package.add("GetInstrumentMod", &Perl_Bot_GetInstrumentMod);
|
||||
package.add("GetItemAt", &Perl_Bot_GetItemAt);
|
||||
package.add("GetItemEquippedByID", &Perl_Bot_HasItemEquippedByID);
|
||||
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("GetRaceAbbreviation", &Perl_Bot_GetRaceAbbreviation);
|
||||
package.add("GetRawItemAC", &Perl_Bot_GetRawItemAC);
|
||||
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("HasBotItem", &Perl_Bot_HasBotItem);
|
||||
package.add("HasBotSpellEntry", &Perl_Bot_HasBotSpellEntry);
|
||||
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("IsSitting", &Perl_Bot_IsSitting);
|
||||
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("SetSpellRecastTimer", (void(*)(Bot*, uint16))&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("Sit", &Perl_Bot_Sit);
|
||||
package.add("Stand", &Perl_Bot_Stand);
|
||||
|
||||
@ -3363,6 +3363,16 @@ perl::array Perl_Client_GetKeyRing(Client* self)
|
||||
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()
|
||||
{
|
||||
perl::interpreter perl(PERL_GET_THX);
|
||||
@ -3635,6 +3645,7 @@ void perl_register_client()
|
||||
package.add("GetSpellBookSlotBySpellID", &Perl_Client_GetSpellBookSlotBySpellID);
|
||||
package.add("GetSpellIDByBookSlot", &Perl_Client_GetSpellIDByBookSlot);
|
||||
package.add("GetSpentAA", &Perl_Client_GetSpentAA);
|
||||
package.add("GetStatCap", &Perl_Client_GetStatCap);
|
||||
package.add("GetStartZone", &Perl_Client_GetStartZone);
|
||||
package.add("GetTargetRingX", &Perl_Client_GetTargetRingX);
|
||||
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, 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("SetThirst", &Perl_Client_SetThirst);
|
||||
package.add("SetTint", &Perl_Client_SetTint);
|
||||
|
||||
@ -50,6 +50,8 @@
|
||||
#include "../common/repositories/character_corpses_repository.h"
|
||||
#include "../common/repositories/character_corpse_items_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/character_evolving_items_repository.h"
|
||||
@ -4297,3 +4299,83 @@ void ZoneDatabase::LoadCharacterTitleSets(Client* c)
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -465,6 +465,10 @@ public:
|
||||
void LoadCharacterEXPModifier(Client* c);
|
||||
void SaveCharacterEXPModifier(Client *c);
|
||||
|
||||
/* Stat Caps */
|
||||
void LoadStatCaps(Mob* m);
|
||||
void SaveStatCaps(Mob* m);
|
||||
|
||||
/* Player Title Sets */
|
||||
void LoadCharacterTitleSets(Client* c);
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user