From 730cd3f28a51ab7977bd45d1c0cebdcabe3a5956 Mon Sep 17 00:00:00 2001 From: Kinglykrab <89047260+Kinglykrab@users.noreply.github.com> Date: Wed, 16 Nov 2022 08:29:50 -0500 Subject: [PATCH] [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. --- common/version.h | 2 +- .../sql/git/bots/bots_db_update_manifest.txt | 1 + .../2022_11_07_bot_expansion_bitmask.sql | 2 + zone/aa.cpp | 6 +- zone/bot.cpp | 539 ++++++++++-------- zone/bot.h | 51 +- zone/bot_database.cpp | 231 ++++---- zone/bot_database.h | 12 +- zone/lua_bot.cpp | 20 +- zone/lua_bot.h | 5 +- zone/perl_bot.cpp | 19 + 11 files changed, 540 insertions(+), 348 deletions(-) create mode 100644 utils/sql/git/bots/required/2022_11_07_bot_expansion_bitmask.sql diff --git a/common/version.h b/common/version.h index cdc25f33b..b0df19344 100644 --- a/common/version.h +++ b/common/version.h @@ -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 diff --git a/utils/sql/git/bots/bots_db_update_manifest.txt b/utils/sql/git/bots/bots_db_update_manifest.txt index 5c13e914c..040ff3ec4 100644 --- a/utils/sql/git/bots/bots_db_update_manifest.txt +++ b/utils/sql/git/bots/bots_db_update_manifest.txt @@ -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 diff --git a/utils/sql/git/bots/required/2022_11_07_bot_expansion_bitmask.sql b/utils/sql/git/bots/required/2022_11_07_bot_expansion_bitmask.sql new file mode 100644 index 000000000..c46686bb4 --- /dev/null +++ b/utils/sql/git/bots/required/2022_11_07_bot_expansion_bitmask.sql @@ -0,0 +1,2 @@ +ALTER TABLE `bot_data` +ADD COLUMN `expansion_bitmask` int(11) NOT NULL DEFAULT -1 AFTER `stop_melee_level`; diff --git a/zone/aa.cpp b/zone/aa.cpp index a9b573612..afad257eb 100644 --- a/zone/aa.cpp +++ b/zone/aa.cpp @@ -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; } } diff --git a/zone/bot.cpp b/zone/bot.cpp index 35a04e781..0ca9db626 100644 --- a/zone/bot.cpp +++ b/zone/bot.cpp @@ -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 diff --git a/zone/bot.h b/zone/bot.h index 50461ea68..d89899bb0 100644 --- a/zone/bot.h +++ b/zone/bot.h @@ -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; diff --git a/zone/bot_database.cpp b/zone/bot_database.cpp index 53f2632ee..605e5d286 100644 --- a/zone/bot_database.cpp +++ b/zone/bot_database.cpp @@ -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(EQ::Clamp(std::stoi(row[6]), 1, 255)), // Level + static_cast(std::stoul(row[8])), // Race + static_cast(EQ::Clamp(std::stoi(row[9]), WARRIOR, BERSERKER)), // Class + static_cast(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(EQ::Clamp(std::stoi(row[6]), 1, 255)), // Level + static_cast(std::stoul(row[7])), // Race + static_cast(EQ::Clamp(std::stoi(row[8]), WARRIOR, BERSERKER)), // Class + static_cast(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(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"; } diff --git a/zone/bot_database.h b/zone/bot_database.h index 600bff7e9..1174d0b45 100644 --- a/zone/bot_database.h +++ b/zone/bot_database.h @@ -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 type, const std::pair 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>& member_list); - + bool LoadBotGroupsListByOwnerID(const uint32 owner_id, std::list>& botgroups_list); diff --git a/zone/lua_bot.cpp b/zone/lua_bot.cpp index 3c1a3666d..5f2542fec 100644 --- a/zone/lua_bot.cpp +++ b/zone/lua_bot.cpp @@ -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_("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 diff --git a/zone/lua_bot.h b/zone/lua_bot.h index a80062526..52cbc1dd2 100644 --- a/zone/lua_bot.h +++ b/zone/lua_bot.h @@ -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 \ No newline at end of file +#endif diff --git a/zone/perl_bot.cpp b/zone/perl_bot.cpp index 1c67fddf8..2baabf6a8 100644 --- a/zone/perl_bot.cpp +++ b/zone/perl_bot.cpp @@ -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 +