[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
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 540 additions and 348 deletions

View File

@ -37,7 +37,7 @@
#define CURRENT_BINARY_DATABASE_VERSION 9212
#ifdef BOTS
#define CURRENT_BINARY_BOTS_DATABASE_VERSION 9031
#define CURRENT_BINARY_BOTS_DATABASE_VERSION 9032
#else
#define CURRENT_BINARY_BOTS_DATABASE_VERSION 0 // must be 0
#endif

View File

@ -30,6 +30,7 @@
9029|2022_06_21_bot_groups_auto_spawn.sql|SHOW COLUMNS from `bot_groups` LIKE 'auto_spawm'|empty|
9030|2022_10_27_bot_data_buckets.sql|SHOW COLUMNS FROM `bot_spells_entries` LIKE 'bucket_name'|empty|
9031|2022_11_13_bot_spells_entries.sql|SELECT * FROM db_version WHERE bots_version >= 9031|empty|
9032|2022_11_07_bot_expansion_bitmask.sql|SHOW COLUMNS FROM `bot_data` LIKE 'expansion_bitmask'|empty|
# Upgrade conditions:
# This won't be needed after this system is implemented, but it is used database that are not

View File

@ -0,0 +1,2 @@
ALTER TABLE `bot_data`
ADD COLUMN `expansion_bitmask` int(11) NOT NULL DEFAULT -1 AFTER `stop_melee_level`;

View File

@ -36,6 +36,10 @@ Copyright (C) 2001-2016 EQEMu Development Team (http://eqemulator.net)
#include "zonedb.h"
#include "../common/zone_store.h"
#ifdef BOTS
#include "bot.h"
#endif
extern QueryServ* QServ;
void Mob::TemporaryPets(uint16 spell_id, Mob *targ, const char *name_override, uint32 duration_override, bool followme, bool sticktarg, uint16 *eye_id) {
@ -1594,7 +1598,7 @@ bool Mob::CanUseAlternateAdvancementRank(AA::Rank *rank) {
}
#ifdef BOTS
else if (IsBot()) {
if (rank->expansion && !(RuleI(Bots, BotExpansionSettings) & (1 << (rank->expansion - 1)))) {
if (rank->expansion && !(CastToBot()->GetExpansionBitmask() & (1 << (rank->expansion - 1)))) {
return false;
}
}

View File

@ -573,253 +573,284 @@ bool Bot::IsStanding() {
return result;
}
NPCType *Bot::FillNPCTypeStruct(uint32 botSpellsID, std::string botName, std::string botLastName, uint8 botLevel, uint16 botRace, uint8 botClass, uint8 gender, float size, uint32 face, uint32 hairStyle, uint32 hairColor, uint32 eyeColor, uint32 eyeColor2, uint32 beardColor, uint32 beard, uint32 drakkinHeritage, uint32 drakkinTattoo, uint32 drakkinDetails, int32 hp, int32 mana, int32 mr, int32 cr, int32 dr, int32 fr, int32 pr, int32 corrup, int32 ac, uint32 str, uint32 sta, uint32 dex, uint32 agi, uint32 _int, uint32 wis, uint32 cha, uint32 attack) {
auto bot_npc_type = new NPCType{ 0 };
int copy_length = 0;
NPCType *Bot::FillNPCTypeStruct(
uint32 botSpellsID,
std::string botName,
std::string botLastName,
uint8 botLevel,
uint16 botRace,
uint8 botClass,
uint8 gender,
float size,
uint32 face,
uint32 hairStyle,
uint32 hairColor,
uint32 eyeColor,
uint32 eyeColor2,
uint32 beard,
uint32 beardColor,
uint32 drakkinHeritage,
uint32 drakkinTattoo,
uint32 drakkinDetails,
int32 hp,
int32 mana,
int32 mr,
int32 cr,
int32 dr,
int32 fr,
int32 pr,
int32 corrup,
int32 ac,
uint32 str,
uint32 sta,
uint32 dex,
uint32 agi,
uint32 _int,
uint32 wis,
uint32 cha,
uint32 attack
) {
auto n = new NPCType{ 0 };
copy_length = botName.copy(bot_npc_type->name, 63);
bot_npc_type->name[copy_length] = '\0';
copy_length = 0;
strn0cpy(n->name, botName.c_str(), sizeof(n->name));
strn0cpy(n->lastname, botLastName.c_str(), sizeof(n->lastname));
copy_length = botLastName.copy(bot_npc_type->lastname, 69);
bot_npc_type->lastname[copy_length] = '\0';
copy_length = 0;
n->current_hp = hp;
n->max_hp = hp;
n->size = size;
n->runspeed = 0.7f;
n->gender = gender;
n->race = botRace;
n->class_ = botClass;
n->bodytype = 1;
n->deity = EQ::deity::DeityAgnostic;
n->level = botLevel;
//n->npc_id = 0;
//n->texture = 0;
//n->helmtexture = 0;
//n->herosforgemodel = 0;
//n->loottable_id = 0;
n->npc_spells_id = botSpellsID;
//n->npc_spells_effects_id = 0;
//n->npc_faction_id = 0;
//n->merchanttype = 0;
//n->alt_currency_type = 0;
//n->adventure_template = 0;
//n->trap_template = 0;
//n->light = 0;
n->AC = ac;
n->Mana = mana;
n->ATK = attack;
n->STR = str;
n->STA = sta;
n->DEX = dex;
n->AGI = agi;
n->INT = _int;
n->WIS = wis;
n->CHA = cha;
n->MR = mr;
n->FR = fr;
n->CR = cr;
n->PR = pr;
n->DR = dr;
n->Corrup = corrup;
//n->PhR = 0;
n->haircolor = hairColor;
n->beardcolor = beardColor;
n->eyecolor1 = eyeColor;
n->eyecolor2 = eyeColor2;
n->hairstyle = hairStyle;
n->luclinface = face;
n->beard = beard;
n->drakkin_heritage = drakkinHeritage;
n->drakkin_tattoo = drakkinTattoo;
n->drakkin_details = drakkinDetails;
//n->armor_tint = { 0 };
//n->min_dmg = 0;
//n->max_dmg = 0;
//n->charm_ac = 0;
//n->charm_min_dmg = 0;
//n->charm_max_dmg = 0;
//n->charm_attack_delay = 0;
//n->charm_accuracy_rating = 0;
//n->charm_avoidance_rating = 0;
//n->charm_atk = 0;
//n->attack_count = 0;
//*n->special_abilities = { 0 };
//n->d_melee_texture1 = 0;
//n->d_melee_texture2 = 0;
//*n->ammo_idfile = { 0 };
//n->prim_melee_type = 0;
//n->sec_melee_type = 0;
//n->ranged_type = 0;
n->hp_regen = 1;
n->mana_regen = 1;
//n->aggroradius = 0;
//n->assistradius = 0;
//n->see_invis = 0;
//n->see_invis_undead = false;
//n->see_hide = false;
//n->see_improved_hide = false;
//n->qglobal = false;
//n->npc_aggro = false;
//n->spawn_limit = 0;
//n->mount_color = 0;
//n->attack_speed = 0.0f;
//n->attack_delay = 0;
//n->accuracy_rating = 0;
//n->avoidance_rating = 0;
//n->findable = false;
n->trackable = true;
//n->slow_mitigation = 0;
n->maxlevel = botLevel;
//n->scalerate = 0;
//n->private_corpse = false;
//n->unique_spawn_by_name = false;
//n->underwater = false;
//n->emoteid = 0;
//n->spellscale = 0.0f;
//n->healscale = 0.0f;
//n->no_target_hotkey = false;
//n->raid_target = false;
//n->armtexture = 0;
//n->bracertexture = 0;
//n->handtexture = 0;
//n->legtexture = 0;
//n->feettexture = 0;
//n->ignore_despawn = false;
n->show_name = true;
//n->untargetable = false;
n->skip_global_loot = true;
//n->rare_spawn = false;
n->stuck_behavior = Ground;
n->skip_auto_scale = true;
bot_npc_type->current_hp = hp;
bot_npc_type->max_hp = hp;
bot_npc_type->size = size;
bot_npc_type->runspeed = 0.7f;
bot_npc_type->gender = gender;
bot_npc_type->race = botRace;
bot_npc_type->class_ = botClass;
bot_npc_type->bodytype = 1;
bot_npc_type->deity = EQ::deity::DeityAgnostic;
bot_npc_type->level = botLevel;
//bot_npc_type->npc_id = 0;
//bot_npc_type->texture = 0;
//bot_npc_type->helmtexture = 0;
//bot_npc_type->herosforgemodel = 0;
//bot_npc_type->loottable_id = 0;
bot_npc_type->npc_spells_id = botSpellsID;
//bot_npc_type->npc_spells_effects_id = 0;
//bot_npc_type->npc_faction_id = 0;
//bot_npc_type->merchanttype = 0;
//bot_npc_type->alt_currency_type = 0;
//bot_npc_type->adventure_template = 0;
//bot_npc_type->trap_template = 0;
//bot_npc_type->light = 0;
bot_npc_type->AC = ac;
bot_npc_type->Mana = mana;
bot_npc_type->ATK = attack;
bot_npc_type->STR = str;
bot_npc_type->STA = sta;
bot_npc_type->DEX = dex;
bot_npc_type->AGI = agi;
bot_npc_type->INT = _int;
bot_npc_type->WIS = wis;
bot_npc_type->CHA = cha;
bot_npc_type->MR = mr;
bot_npc_type->FR = fr;
bot_npc_type->CR = cr;
bot_npc_type->PR = pr;
bot_npc_type->DR = dr;
bot_npc_type->Corrup = corrup;
//bot_npc_type->PhR = 0;
bot_npc_type->haircolor = hairColor;
bot_npc_type->beardcolor = beardColor;
bot_npc_type->eyecolor1 = eyeColor;
bot_npc_type->eyecolor2 = eyeColor2;
bot_npc_type->hairstyle = hairStyle;
bot_npc_type->luclinface = face;
bot_npc_type->beard = beard;
bot_npc_type->drakkin_heritage = drakkinHeritage;
bot_npc_type->drakkin_tattoo = drakkinTattoo;
bot_npc_type->drakkin_details = drakkinDetails;
//bot_npc_type->armor_tint = { 0 };
//bot_npc_type->min_dmg = 0;
//bot_npc_type->max_dmg = 0;
//bot_npc_type->charm_ac = 0;
//bot_npc_type->charm_min_dmg = 0;
//bot_npc_type->charm_max_dmg = 0;
//bot_npc_type->charm_attack_delay = 0;
//bot_npc_type->charm_accuracy_rating = 0;
//bot_npc_type->charm_avoidance_rating = 0;
//bot_npc_type->charm_atk = 0;
//bot_npc_type->attack_count = 0;
//*bot_npc_type->special_abilities = { 0 };
//bot_npc_type->d_melee_texture1 = 0;
//bot_npc_type->d_melee_texture2 = 0;
//*bot_npc_type->ammo_idfile = { 0 };
//bot_npc_type->prim_melee_type = 0;
//bot_npc_type->sec_melee_type = 0;
//bot_npc_type->ranged_type = 0;
bot_npc_type->hp_regen = 1;
bot_npc_type->mana_regen = 1;
//bot_npc_type->aggroradius = 0;
//bot_npc_type->assistradius = 0;
//bot_npc_type->see_invis = 0;
//bot_npc_type->see_invis_undead = false;
//bot_npc_type->see_hide = false;
//bot_npc_type->see_improved_hide = false;
//bot_npc_type->qglobal = false;
//bot_npc_type->npc_aggro = false;
//bot_npc_type->spawn_limit = 0;
//bot_npc_type->mount_color = 0;
//bot_npc_type->attack_speed = 0.0f;
//bot_npc_type->attack_delay = 0;
//bot_npc_type->accuracy_rating = 0;
//bot_npc_type->avoidance_rating = 0;
//bot_npc_type->findable = false;
bot_npc_type->trackable = true;
//bot_npc_type->slow_mitigation = 0;
bot_npc_type->maxlevel = botLevel;
//bot_npc_type->scalerate = 0;
//bot_npc_type->private_corpse = false;
//bot_npc_type->unique_spawn_by_name = false;
//bot_npc_type->underwater = false;
//bot_npc_type->emoteid = 0;
//bot_npc_type->spellscale = 0.0f;
//bot_npc_type->healscale = 0.0f;
//bot_npc_type->no_target_hotkey = false;
//bot_npc_type->raid_target = false;
//bot_npc_type->armtexture = 0;
//bot_npc_type->bracertexture = 0;
//bot_npc_type->handtexture = 0;
//bot_npc_type->legtexture = 0;
//bot_npc_type->feettexture = 0;
//bot_npc_type->ignore_despawn = false;
bot_npc_type->show_name = true;
//bot_npc_type->untargetable = false;
bot_npc_type->skip_global_loot = true;
//bot_npc_type->rare_spawn = false;
bot_npc_type->stuck_behavior = Ground;
bot_npc_type->skip_auto_scale = true;
return bot_npc_type;
return n;
}
NPCType *Bot::CreateDefaultNPCTypeStructForBot(std::string botName, std::string botLastName, uint8 botLevel, uint16 botRace, uint8 botClass, uint8 gender) {
auto bot_npc_type = new NPCType{ 0 };
int copy_length = 0;
NPCType *Bot::CreateDefaultNPCTypeStructForBot(
std::string botName,
std::string botLastName,
uint8 botLevel,
uint16 botRace,
uint8 botClass,
uint8 gender
) {
auto n = new NPCType{ 0 };
copy_length = botName.copy(bot_npc_type->name, 63);
bot_npc_type->name[copy_length] = '\0';
copy_length = 0;
strn0cpy(n->name, botName.c_str(), sizeof(n->name));
strn0cpy(n->lastname, botLastName.c_str(), sizeof(n->lastname));
copy_length = botLastName.copy(bot_npc_type->lastname, 69);
bot_npc_type->lastname[copy_length] = '\0';
copy_length = 0;
//n->current_hp = 0;
//n->max_hp = 0;
n->size = 6.0f;
n->runspeed = 0.7f;
n->gender = gender;
n->race = botRace;
n->class_ = botClass;
n->bodytype = 1;
n->deity = EQ::deity::DeityAgnostic;
n->level = botLevel;
//n->npc_id = 0;
//n->texture = 0;
//n->helmtexture = 0;
//n->herosforgemodel = 0;
//n->loottable_id = 0;
//n->npc_spells_id = 0;
//n->npc_spells_effects_id = 0;
//n->npc_faction_id = 0;
//n->merchanttype = 0;
//n->alt_currency_type = 0;
//n->adventure_template = 0;
//n->trap_template = 0;
//n->light = 0;
n->AC = 12;
//n->Mana = 0;
n->ATK = 75;
n->STR = 75;
n->STA = 75;
n->DEX = 75;
n->AGI = 75;
n->INT = 75;
n->WIS = 75;
n->CHA = 75;
n->MR = 25;
n->FR = 25;
n->CR = 25;
n->PR = 15;
n->DR = 15;
n->Corrup = 15;
//n->PhR = 0;
//n->haircolor = 0;
//n->beardcolor = 0;
//n->eyecolor1 = 0;
//n->eyecolor2 = 0;
//n->hairstyle = 0;
//n->luclinface = 0;
//n->beard = 0;
//n->drakkin_heritage = 0;
//n->drakkin_tattoo = 0;
//n->drakkin_details = 0;
//n->armor_tint = { 0 };
//n->min_dmg = 0;
//n->max_dmg = 0;
//n->charm_ac = 0;
//n->charm_min_dmg = 0;
//n->charm_max_dmg = 0;
//n->charm_attack_delay = 0;
//n->charm_accuracy_rating = 0;
//n->charm_avoidance_rating = 0;
//n->charm_atk = 0;
//n->attack_count = 0;
//*n->special_abilities = { 0 };
//n->d_melee_texture1 = 0;
//n->d_melee_texture2 = 0;
//*n->ammo_idfile = { 0 };
//n->prim_melee_type = 0;
//n->sec_melee_type = 0;
//n->ranged_type = 0;
n->hp_regen = 1;
n->mana_regen = 1;
//n->aggroradius = 0;
//n->assistradius = 0;
//n->see_invis = 0;
//n->see_invis_undead = false;
//n->see_hide = false;
//n->see_improved_hide = false;
//n->qglobal = false;
//n->npc_aggro = false;
//n->spawn_limit = 0;
//n->mount_color = 0;
//n->attack_speed = 0.0f;
//n->attack_delay = 0;
//n->accuracy_rating = 0;
//n->avoidance_rating = 0;
//n->findable = false;
n->trackable = true;
//n->slow_mitigation = 0;
n->maxlevel = botLevel;
//n->scalerate = 0;
//n->private_corpse = false;
//n->unique_spawn_by_name = false;
//n->underwater = false;
//n->emoteid = 0;
//n->spellscale = 0.0f;
//n->healscale = 0.0f;
//n->no_target_hotkey = false;
//n->raid_target = false;
//n->armtexture = 0;
//n->bracertexture = 0;
//n->handtexture = 0;
//n->legtexture = 0;
//n->feettexture = 0;
//n->ignore_despawn = false;
n->show_name = true;
//n->untargetable = false;
n->skip_global_loot = true;
//n->rare_spawn = false;
n->stuck_behavior = Ground;
//bot_npc_type->current_hp = 0;
//bot_npc_type->max_hp = 0;
bot_npc_type->size = 6.0f;
bot_npc_type->runspeed = 0.7f;
bot_npc_type->gender = gender;
bot_npc_type->race = botRace;
bot_npc_type->class_ = botClass;
bot_npc_type->bodytype = 1;
bot_npc_type->deity = EQ::deity::DeityAgnostic;
bot_npc_type->level = botLevel;
//bot_npc_type->npc_id = 0;
//bot_npc_type->texture = 0;
//bot_npc_type->helmtexture = 0;
//bot_npc_type->herosforgemodel = 0;
//bot_npc_type->loottable_id = 0;
//bot_npc_type->npc_spells_id = 0;
//bot_npc_type->npc_spells_effects_id = 0;
//bot_npc_type->npc_faction_id = 0;
//bot_npc_type->merchanttype = 0;
//bot_npc_type->alt_currency_type = 0;
//bot_npc_type->adventure_template = 0;
//bot_npc_type->trap_template = 0;
//bot_npc_type->light = 0;
bot_npc_type->AC = 12;
//bot_npc_type->Mana = 0;
bot_npc_type->ATK = 75;
bot_npc_type->STR = 75;
bot_npc_type->STA = 75;
bot_npc_type->DEX = 75;
bot_npc_type->AGI = 75;
bot_npc_type->INT = 75;
bot_npc_type->WIS = 75;
bot_npc_type->CHA = 75;
bot_npc_type->MR = 25;
bot_npc_type->FR = 25;
bot_npc_type->CR = 25;
bot_npc_type->PR = 15;
bot_npc_type->DR = 15;
bot_npc_type->Corrup = 15;
//bot_npc_type->PhR = 0;
//bot_npc_type->haircolor = 0;
//bot_npc_type->beardcolor = 0;
//bot_npc_type->eyecolor1 = 0;
//bot_npc_type->eyecolor2 = 0;
//bot_npc_type->hairstyle = 0;
//bot_npc_type->luclinface = 0;
//bot_npc_type->beard = 0;
//bot_npc_type->drakkin_heritage = 0;
//bot_npc_type->drakkin_tattoo = 0;
//bot_npc_type->drakkin_details = 0;
//bot_npc_type->armor_tint = { 0 };
//bot_npc_type->min_dmg = 0;
//bot_npc_type->max_dmg = 0;
//bot_npc_type->charm_ac = 0;
//bot_npc_type->charm_min_dmg = 0;
//bot_npc_type->charm_max_dmg = 0;
//bot_npc_type->charm_attack_delay = 0;
//bot_npc_type->charm_accuracy_rating = 0;
//bot_npc_type->charm_avoidance_rating = 0;
//bot_npc_type->charm_atk = 0;
//bot_npc_type->attack_count = 0;
//*bot_npc_type->special_abilities = { 0 };
//bot_npc_type->d_melee_texture1 = 0;
//bot_npc_type->d_melee_texture2 = 0;
//*bot_npc_type->ammo_idfile = { 0 };
//bot_npc_type->prim_melee_type = 0;
//bot_npc_type->sec_melee_type = 0;
//bot_npc_type->ranged_type = 0;
bot_npc_type->hp_regen = 1;
bot_npc_type->mana_regen = 1;
//bot_npc_type->aggroradius = 0;
//bot_npc_type->assistradius = 0;
//bot_npc_type->see_invis = 0;
//bot_npc_type->see_invis_undead = false;
//bot_npc_type->see_hide = false;
//bot_npc_type->see_improved_hide = false;
//bot_npc_type->qglobal = false;
//bot_npc_type->npc_aggro = false;
//bot_npc_type->spawn_limit = 0;
//bot_npc_type->mount_color = 0;
//bot_npc_type->attack_speed = 0.0f;
//bot_npc_type->attack_delay = 0;
//bot_npc_type->accuracy_rating = 0;
//bot_npc_type->avoidance_rating = 0;
//bot_npc_type->findable = false;
bot_npc_type->trackable = true;
//bot_npc_type->slow_mitigation = 0;
bot_npc_type->maxlevel = botLevel;
//bot_npc_type->scalerate = 0;
//bot_npc_type->private_corpse = false;
//bot_npc_type->unique_spawn_by_name = false;
//bot_npc_type->underwater = false;
//bot_npc_type->emoteid = 0;
//bot_npc_type->spellscale = 0.0f;
//bot_npc_type->healscale = 0.0f;
//bot_npc_type->no_target_hotkey = false;
//bot_npc_type->raid_target = false;
//bot_npc_type->armtexture = 0;
//bot_npc_type->bracertexture = 0;
//bot_npc_type->handtexture = 0;
//bot_npc_type->legtexture = 0;
//bot_npc_type->feettexture = 0;
//bot_npc_type->ignore_despawn = false;
bot_npc_type->show_name = true;
//bot_npc_type->untargetable = false;
bot_npc_type->skip_global_loot = true;
//bot_npc_type->rare_spawn = false;
bot_npc_type->stuck_behavior = Ground;
return bot_npc_type;
return n;
}
void Bot::GenerateBaseStats()
@ -10397,6 +10428,36 @@ bool Bot::CheckDataBucket(std::string bucket_name, std::string bucket_value, uin
return false;
}
int Bot::GetExpansionBitmask()
{
if (m_expansion_bitmask >= 0) {
return m_expansion_bitmask;
}
return RuleI(Bots, BotExpansionSettings);
}
void Bot::SetExpansionBitmask(int expansion_bitmask, bool save)
{
m_expansion_bitmask = expansion_bitmask;
if (save) {
if (!database.botdb.SaveExpansionBitmask(GetBotID(), expansion_bitmask)) {
if (GetBotOwner() && GetBotOwner()->IsClient()) {
GetBotOwner()->CastToClient()->Message(
Chat::White,
fmt::format(
"Failed to save expansion bitmask for {}.",
GetCleanName()
).c_str()
);
}
}
}
LoadAAs();
}
uint8 Bot::spell_casting_chances[SPELL_TYPE_COUNT][PLAYER_CLASS_COUNT][EQ::constants::STANCE_TYPE_COUNT][cntHSND] = { 0 };
#endif

View File

@ -425,7 +425,14 @@ public:
static BotSpell GetBestBotSpellForCure(Bot* botCaster, Mob* target);
static BotSpell GetBestBotSpellForResistDebuff(Bot* botCaster, Mob* target);
static NPCType *CreateDefaultNPCTypeStructForBot(std::string botName, std::string botLastName, uint8 botLevel, uint16 botRace, uint8 botClass, uint8 gender);
static NPCType *CreateDefaultNPCTypeStructForBot(
std::string botName,
std::string botLastName,
uint8 botLevel,
uint16 botRace,
uint8 botClass,
uint8 gender
);
// Static Bot Group Methods
static bool AddBotToGroup(Bot* bot, Group* group);
@ -583,6 +590,9 @@ public:
void SetDrakkinTattoo(uint32 value) { drakkin_tattoo = value; }
bool DyeArmor(int16 slot_id, uint32 rgb, bool all_flag = false, bool save_flag = true);
int GetExpansionBitmask();
void SetExpansionBitmask(int expansion_bitmask, bool save = true);
static void SpawnBotGroupByName(Client* c, std::string botgroup_name, uint32 leader_id);
std::string CreateSayLink(Client* botOwner, const char* message, const char* name);
@ -594,7 +604,43 @@ public:
virtual void BotRangedAttack(Mob* other);
// Publicized private functions
static NPCType *FillNPCTypeStruct(uint32 botSpellsID, std::string botName, std::string botLastName, uint8 botLevel, uint16 botRace, uint8 botClass, uint8 gender, float size, uint32 face, uint32 hairStyle, uint32 hairColor, uint32 eyeColor, uint32 eyeColor2, uint32 beardColor, uint32 beard, uint32 drakkinHeritage, uint32 drakkinTattoo, uint32 drakkinDetails, int32 hp, int32 mana, int32 mr, int32 cr, int32 dr, int32 fr, int32 pr, int32 corrup, int32 ac, uint32 str, uint32 sta, uint32 dex, uint32 agi, uint32 _int, uint32 wis, uint32 cha, uint32 attack);
static NPCType *FillNPCTypeStruct(
uint32 botSpellsID,
std::string botName,
std::string botLastName,
uint8 botLevel,
uint16 botRace,
uint8 botClass,
uint8 gender,
float size,
uint32 face,
uint32 hairStyle,
uint32 hairColor,
uint32 eyeColor,
uint32 eyeColor2,
uint32 beard,
uint32 beardColor,
uint32 drakkinHeritage,
uint32 drakkinTattoo,
uint32 drakkinDetails,
int32 hp,
int32 mana,
int32 mr,
int32 cr,
int32 dr,
int32 fr,
int32 pr,
int32 corrup,
int32 ac,
uint32 str,
uint32 sta,
uint32 dex,
uint32 agi,
uint32 _int,
uint32 wis,
uint32 cha,
uint32 attack
);
void BotRemoveEquipItem(uint16 slot_id);
void RemoveBotItemBySlot(uint16 slot_id, std::string* error_message);
void AddBotItem(
@ -718,6 +764,7 @@ private:
bool _showhelm;
bool _pauseAI;
uint8 _stopMeleeLevel;
int m_expansion_bitmask;
// Private "base stats" Members
int32 _baseMR;

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"; }

View File

@ -97,11 +97,13 @@ public:
bool LoadEquipmentColor(const uint32 bot_id, const uint8 material_slot_id, uint32& rgb);
bool SaveEquipmentColor(const uint32 bot_id, const int16 slot_id, const uint32 rgb);
bool SaveExpansionBitmask(const uint32 bot_id, const int expansion_bitmask);
/* Bot pet functions */
bool LoadPetIndex(const uint32 bot_id, uint32& pet_index);
bool LoadPetSpellID(const uint32 bot_id, uint32& pet_spell_id);
bool LoadPetStats(const uint32 bot_id, std::string& pet_name, uint32& pet_mana, uint32& pet_hp, uint32& pet_spell_id);
bool SavePetStats(const uint32 bot_id, const std::string& pet_name, const uint32 pet_mana, const uint32 pet_hp, const uint32 pet_spell_id);
bool DeletePetStats(const uint32 bot_id);
@ -119,7 +121,7 @@ public:
bool LoadInspectMessage(const uint32 bot_id, InspectMessage_Struct& inspect_message);
bool SaveInspectMessage(const uint32 bot_id, const InspectMessage_Struct& inspect_message);
bool DeleteInspectMessage(const uint32 bot_id);
bool SaveAllInspectMessages(const uint32 owner_id, const InspectMessage_Struct& inspect_message);
bool DeleteAllInspectMessages(const uint32 owner_id);
@ -143,7 +145,7 @@ public:
bool LoadOwnerOptions(Client *owner);
bool SaveOwnerOption(const uint32 owner_id, size_t type, const bool flag);
bool SaveOwnerOption(const uint32 owner_id, const std::pair<size_t, size_t> type, const std::pair<bool, bool> flag);
/* Bot bot-group functions */
bool QueryBotGroupExistence(const std::string& botgroup_name);
@ -156,7 +158,7 @@ public:
bool LoadBotGroupNameByBotGroupID(const uint32 botgroup_id, std::string& botgroup_name);
bool LoadBotGroupNameByLeaderID(const uint32 leader_id, std::string& botgroup_name);
bool CreateBotGroup(const std::string& botgroup_name, const uint32 leader_id);
bool DeleteBotGroup(const uint32 leader_id);
bool AddMemberToBotGroup(const uint32 leader_id, const uint32 member_id);
@ -164,7 +166,7 @@ public:
bool LoadBotGroupIDForLoadBotGroup(const uint32 owner_id, const std::string& botgroup_name, uint32& botgroup_id);
bool LoadBotGroup(const std::string& botgroup_name, std::map<uint32, std::list<uint32>>& member_list);
bool LoadBotGroupsListByOwnerID(const uint32 owner_id, std::list<std::pair<std::string, std::string>>& botgroups_list);

View File

@ -84,6 +84,21 @@ uint32 Lua_Bot::GetBotItemIDBySlot(uint16 slot_id) {
return self->GetBotItemBySlot(slot_id);
}
int Lua_Bot::GetExpansionBitmask() {
Lua_Safe_Call_Int();
return self->GetExpansionBitmask();
}
void Lua_Bot::SetExpansionBitmask(int expansion_bitmask) {
Lua_Safe_Call_Void();
self->SetExpansionBitmask(expansion_bitmask);
}
void Lua_Bot::SetExpansionBitmask(int expansion_bitmask, bool save) {
Lua_Safe_Call_Void();
self->SetExpansionBitmask(expansion_bitmask, save);
}
luabind::scope lua_register_bot() {
return luabind::class_<Lua_Bot, Lua_Mob>("Bot")
.def(luabind::constructor<>())
@ -99,9 +114,12 @@ luabind::scope lua_register_bot() {
.def("CountBotItem", (uint32(Lua_Bot::*)(uint32))&Lua_Bot::CountBotItem)
.def("GetBotItem", (Lua_ItemInst(Lua_Bot::*)(uint16))&Lua_Bot::GetBotItem)
.def("GetBotItemIDBySlot", (uint32(Lua_Bot::*)(uint16))&Lua_Bot::GetBotItemIDBySlot)
.def("GetExpansionBitmask", (int(Lua_Bot::*)(void))&Lua_Bot::GetExpansionBitmask)
.def("GetOwner", (Lua_Mob(Lua_Bot::*)(void))&Lua_Bot::GetOwner)
.def("HasBotItem", (bool(Lua_Bot::*)(uint32))&Lua_Bot::HasBotItem)
.def("RemoveBotItem", (void(Lua_Bot::*)(uint32))&Lua_Bot::RemoveBotItem);
.def("RemoveBotItem", (void(Lua_Bot::*)(uint32))&Lua_Bot::RemoveBotItem)
.def("SetExpansionBitmask", (void(Lua_Bot::*)(int))&Lua_Bot::SetExpansionBitmask)
.def("SetExpansionBitmask", (void(Lua_Bot::*)(int,bool))&Lua_Bot::SetExpansionBitmask);
}
#endif

View File

@ -39,11 +39,14 @@ public:
uint32 CountBotItem(uint32 item_id);
Lua_ItemInst GetBotItem(uint16 slot_id);
uint32 GetBotItemIDBySlot(uint16 slot_id);
int GetExpansionBitmask();
Lua_Mob GetOwner();
bool HasBotItem(uint32 item_id);
void RemoveBotItem(uint32 item_id);
void SetExpansionBitmask(int expansion_bitmask);
void SetExpansionBitmask(int expansion_bitmask, bool save);
};
#endif
#endif
#endif
#endif

View File

@ -81,6 +81,21 @@ uint32 Perl_Bot_GetBotItemIDBySlot(Bot* self, uint16 slot_id)
return self->GetBotItemBySlot(slot_id);
}
int Perl_Bot_GetExpansionBitmask(Bot* self)
{
return self->GetExpansionBitmask();
}
void Perl_Bot_SetExpansionBitmask(Bot* self, int expansion_bitmask)
{
self->SetExpansionBitmask(expansion_bitmask);
}
void Perl_Bot_SetExpansionBitmask(Bot* self, int expansion_bitmask, bool save)
{
self->SetExpansionBitmask(expansion_bitmask, save);
}
void perl_register_bot()
{
perl::interpreter state(PERL_GET_THX);
@ -99,10 +114,14 @@ void perl_register_bot()
package.add("CountBotItem", &Perl_Bot_CountBotItem);
package.add("GetBotItem", &Perl_Bot_GetBotItem);
package.add("GetBotItemIDBySlot", &Perl_Bot_GetBotItemIDBySlot);
package.add("GetExpansionBitmask", &Perl_Bot_GetExpansionBitmask);
package.add("GetOwner", &Perl_Bot_GetOwner);
package.add("HasBotItem", &Perl_Bot_HasBotItem);
package.add("RemoveBotItem", &Perl_Bot_RemoveBotItem);
package.add("SetExpansionBitmask", (void(*)(Bot*, int))&Perl_Bot_SetExpansionBitmask);
package.add("SetExpansionBitmask", (void(*)(Bot*, int, bool))&Perl_Bot_SetExpansionBitmask);
}
#endif //EMBPERL_XS_CLASSES
#endif //BOTS