[Bots] Add Expansion Bitmask Quest APIs. (#2523)

* [Bots] Add Expansion Bitmask Quest APIs.

- Add `$bot->GetExpansionBitmask()` to Perl.
- Add `$bot->SetExpansionBitmask(expansion_bitmask)` to Perl.

- Add `bot:GetExpansionBitmask()` to Lua.
- Add `bot:SetExpansionBitmask(expansion_bitmask)` to Lua.

- Adds `expansion_bitmask` column to `bot_data` table.
- Allows server operators to limit expansion settings on a bot-by-bot basis.
- Allows limiting or allowing of AAs in `Bot::LoadAAs()` based on expansion bitmask.
- Default value is `-1` which just defaults to the `Bots:BotExpansionSettings` rule value.
- Setting bitmask saves to database and reloads AAs so bots automatically recalculate bonuses.

* Add save parameter.

* Typo.
This commit is contained in:
Kinglykrab
2022-11-16 08:29:50 -05:00
committed by GitHub
parent bb58a9cd20
commit 730cd3f28a
11 changed files with 540 additions and 348 deletions
+133 -98
View File
@@ -18,6 +18,7 @@
#ifdef BOTS
#include "../common/data_verification.h"
#include "../common/global_define.h"
#include "../common/rulesys.h"
#include "../common/strings.h"
@@ -330,127 +331,139 @@ bool BotDatabase::LoadBotID(const uint32 owner_id, const std::string& bot_name,
bool BotDatabase::LoadBot(const uint32 bot_id, Bot*& loaded_bot)
{
if (!bot_id || loaded_bot)
if (!bot_id || loaded_bot) {
return false;
}
query = StringFormat(
query = fmt::format(
"SELECT"
" `owner_id`,"
" `spells_id`,"
" `name`,"
" `last_name`,"
" `title`,"
" `suffix`,"
" `zone_id`,"
" `gender`,"
" `race`,"
" `class`,"
" `level`,"
" `deity`," /* planned use[11] */
" `creation_day`," /* not in-use[12] */
" `last_spawn`," /* not in-use[13] */
" `time_spawned`,"
" `size`,"
" `face`,"
" `hair_color`,"
" `hair_style`,"
" `beard`,"
" `beard_color`,"
" `eye_color_1`,"
" `eye_color_2`,"
" `drakkin_heritage`,"
" `drakkin_tattoo`,"
" `drakkin_details`,"
" `ac`," /* not in-use[26] */
" `atk`," /* not in-use[27] */
" `hp`,"
" `mana`,"
" `str`," /* not in-use[30] */
" `sta`," /* not in-use[31] */
" `cha`," /* not in-use[32] */
" `dex`," /* not in-use[33] */
" `int`," /* not in-use[34] */
" `agi`," /* not in-use[35] */
" `wis`," /* not in-use[36] */
" `fire`," /* not in-use[37] */
" `cold`," /* not in-use[38] */
" `magic`," /* not in-use[39] */
" `poison`," /* not in-use[40] */
" `disease`," /* not in-use[41] */
" `corruption`," /* not in-use[42] */
" `show_helm`," // 43
" `follow_distance`," // 44
" `stop_melee_level`" // 45
" `owner_id`," // 0
" `spells_id`," // 1
" `name`," // 2
" `last_name`," // 3
" `title`," // 4
" `suffix`," // 5
" `level`," // 6
" `race`," // 7
" `class`," // 8
" `gender`," // 9
" `size`," // 10
" `face`," // 11
" `hair_style`," // 12
" `hair_color`," // 13
" `eye_color_1`," // 14
" `eye_color_2`," // 15
" `beard`," // 16
" `beard_color`," // 17
" `drakkin_heritage`," // 18
" `drakkin_tattoo`," // 19
" `drakkin_details`," // 20
" `hp`," // 21
" `mana`," // 22
" `time_spawned`," // 23
" `zone_id`," // 24
" `show_helm`," // 25
" `follow_distance`," // 26
" `stop_melee_level`," // 27
" `expansion_bitmask`" // 28
" FROM `bot_data`"
" WHERE `bot_id` = '%u'"
" WHERE `bot_id` = {}"
" LIMIT 1",
bot_id
);
auto results = database.QueryDatabase(query);
if (!results.Success())
if (!results.Success()) {
return false;
if (!results.RowCount())
return true;
}
if (!results.RowCount()) {
return true;
}
// TODO: Consider removing resists and basic attributes from the load query above since we're using defaultNPCType values instead
auto row = results.begin();
auto defaultNPCTypeStruct = Bot::CreateDefaultNPCTypeStructForBot(std::string(row[2]), std::string(row[3]), atoi(row[10]), atoi(row[8]), atoi(row[9]), atoi(row[7]));
auto tempNPCStruct = Bot::FillNPCTypeStruct(
atoi(row[1]),
std::string(row[2]),
std::string(row[3]),
atoi(row[10]),
atoi(row[8]),
atoi(row[9]),
atoi(row[7]),
atof(row[15]),
atoi(row[16]),
atoi(row[18]),
atoi(row[17]),
atoi(row[21]),
atoi(row[22]),
atoi(row[20]),
atoi(row[19]),
atoi(row[23]),
atoi(row[24]),
atoi(row[25]),
atoi(row[28]),
atoi(row[29]),
defaultNPCTypeStruct->MR,
defaultNPCTypeStruct->CR,
defaultNPCTypeStruct->DR,
defaultNPCTypeStruct->FR,
defaultNPCTypeStruct->PR,
defaultNPCTypeStruct->Corrup,
defaultNPCTypeStruct->AC,
defaultNPCTypeStruct->STR,
defaultNPCTypeStruct->STA,
defaultNPCTypeStruct->DEX,
defaultNPCTypeStruct->AGI,
defaultNPCTypeStruct->INT,
defaultNPCTypeStruct->WIS,
defaultNPCTypeStruct->CHA,
defaultNPCTypeStruct->ATK
auto d = Bot::CreateDefaultNPCTypeStructForBot(
std::string(row[2]), // Name
std::string(row[3]), // Last Name
static_cast<uint8>(EQ::Clamp(std::stoi(row[6]), 1, 255)), // Level
static_cast<uint16>(std::stoul(row[8])), // Race
static_cast<uint8>(EQ::Clamp(std::stoi(row[9]), WARRIOR, BERSERKER)), // Class
static_cast<uint8>(EQ::Clamp(std::stoi(row[7]), MALE, FEMALE)) // Gender
);
safe_delete(defaultNPCTypeStruct);
auto t = Bot::FillNPCTypeStruct(
std::stoul(row[1]), // Spells ID
std::string(row[2]), // Name
std::string(row[3]), // Last Name
static_cast<uint8>(EQ::Clamp(std::stoi(row[6]), 1, 255)), // Level
static_cast<uint16>(std::stoul(row[7])), // Race
static_cast<uint8>(EQ::Clamp(std::stoi(row[8]), WARRIOR, BERSERKER)), // Class
static_cast<uint8>(EQ::Clamp(std::stoi(row[9]), MALE, FEMALE)), // Gender
std::stof(row[10]), // Size
std::stoul(row[11]), // Face
std::stoul(row[12]), // Hair Style
std::stoul(row[13]), // Hair Color
std::stoul(row[14]), // Eye Color 1
std::stoul(row[15]), // Eye Color 2
std::stoul(row[16]), // Beard
std::stoul(row[17]), // Beard Color
std::stoul(row[18]), // Drakkin Heritage
std::stoul(row[19]), // Drakkin Tattoo
std::stoul(row[19]), // Drakkin Details
std::stoi(row[20]), // Health
std::stoi(row[21]), // Mana
d->MR,
d->CR,
d->DR,
d->FR,
d->PR,
d->Corrup,
d->AC,
d->STR,
d->STA,
d->DEX,
d->AGI,
d->INT,
d->WIS,
d->CHA,
d->ATK
);
safe_delete(d);
loaded_bot = new Bot(
bot_id,
std::stoul(row[0]), // Owner ID
std::stoul(row[1]), // Spells ID
std::stof(row[23]), // Total Play Time
std::stoul(row[24]), // Last Zone ID
t
);
loaded_bot = new Bot(bot_id, atoi(row[0]), atoi(row[1]), atof(row[14]), atoi(row[6]), tempNPCStruct);
if (loaded_bot) {
loaded_bot->SetShowHelm((atoi(row[43]) > 0 ? true : false));
loaded_bot->SetSurname(row[3]);//maintaining outside mob::lastname to cater to spaces
loaded_bot->SetSurname(row[3]);
loaded_bot->SetTitle(row[4]);
loaded_bot->SetSuffix(row[5]);
uint32 bfd = atoi(row[44]);
if (bfd < 1)
loaded_bot->SetShowHelm((std::stoi(row[25]) > 0 ? true : false));
auto bfd = std::stoul(row[26]);
if (bfd < 1) {
bfd = 1;
if (bfd > BOT_FOLLOW_DISTANCE_DEFAULT_MAX)
}
if (bfd > BOT_FOLLOW_DISTANCE_DEFAULT_MAX) {
bfd = BOT_FOLLOW_DISTANCE_DEFAULT_MAX;
}
loaded_bot->SetFollowDistance(bfd);
uint8 sml = atoi(row[45]);
auto sml = static_cast<uint8>(EQ::Clamp(std::stoi(row[27]), 1, 255));
loaded_bot->SetStopMeleeLevel(sml);
auto eb = std::stoi(row[28]);
loaded_bot->SetExpansionBitmask(eb, false);
}
return true;
@@ -3175,6 +3188,28 @@ uint16 BotDatabase::GetRaceClassBitmask(uint16 bot_race)
return classes;
}
bool BotDatabase::SaveExpansionBitmask(const uint32 bot_id, const int expansion_bitmask)
{
if (!bot_id) {
return false;
}
query = fmt::format(
"UPDATE `bot_data` "
"SET `expansion_bitmask` = {} "
"WHERE `bot_id` = {}",
expansion_bitmask,
bot_id
);
auto results = database.QueryDatabase(query);
if (!results.Success()) {
return false;
}
return true;
}
/* fail::Bot functions */
const char* BotDatabase::fail::QueryNameAvailablity() { return "Failed to query name availability"; }
const char* BotDatabase::fail::QueryBotCount() { return "Failed to query bot count"; }