diff --git a/common/ruletypes.h b/common/ruletypes.h index a261afc25..2e8529d70 100644 --- a/common/ruletypes.h +++ b/common/ruletypes.h @@ -563,9 +563,6 @@ RULE_BOOL(Bots, BotGroupXP, false, "Determines whether client gets experience fo RULE_BOOL(Bots, BotLevelsWithOwner, false, "Auto-updates spawned bots as owner levels/de-levels (false is original behavior)") RULE_INT(Bots, BotCharacterLevel, 0, "If level is greater that value player can spawn bots if BotCharacterLevelEnabled is true") RULE_INT(Bots, CasterStopMeleeLevel, 13, "Level at which caster bots stop melee attacks") -RULE_INT(Bots, AllowedClasses, 0xFFFFFFFF, "Bitmask of allowed bot classes") -RULE_INT(Bots, AllowedRaces, 0xFFFFFFFF, "Bitmask of allowed bot races") -RULE_INT(Bots, AllowedGenders, 0x3, "Bitmask of allowed bot genders") RULE_BOOL(Bots, AllowOwnerOptionAltCombat, true, "When option is enabled, bots will use an auto-/shared-aggro combat model") RULE_BOOL(Bots, AllowOwnerOptionAutoDefend, true, "When option is enabled, bots will defend their owner on enemy aggro") RULE_REAL(Bots, LeashDistance, 562500.0f, "Distance a bot is allowed to travel from leash owner before being pulled back (squared value)") diff --git a/common/version.h b/common/version.h index 70bb6c410..e04ba3418 100644 --- a/common/version.h +++ b/common/version.h @@ -37,7 +37,7 @@ #define CURRENT_BINARY_DATABASE_VERSION 9166 #ifdef BOTS - #define CURRENT_BINARY_BOTS_DATABASE_VERSION 9027 + #define CURRENT_BINARY_BOTS_DATABASE_VERSION 9028 #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 86e7c49dd..221020683 100644 --- a/utils/sql/git/bots/bots_db_update_manifest.txt +++ b/utils/sql/git/bots/bots_db_update_manifest.txt @@ -26,6 +26,7 @@ 9025|2019_08_26_bots_owner_option_spawn_message.sql|SELECT * FROM db_version WHERE bots_version >= 9025|empty| 9026|2019_09_09_bots_owner_options_rework.sql|SHOW COLUMNS FROM `bot_owner_options` LIKE 'option_type'|empty| 9027|2020_03_30_bots_view_update.sql|SELECT * FROM db_version WHERE bots_version >= 9027|empty| +9028|2021_06_04_bot_create_combinations.sql|SHOW TABLES LIKE 'bot_create_combinations'|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/2021_06_04_bot_create_combinations.sql b/utils/sql/git/bots/required/2021_06_04_bot_create_combinations.sql new file mode 100644 index 000000000..00974fcda --- /dev/null +++ b/utils/sql/git/bots/required/2021_06_04_bot_create_combinations.sql @@ -0,0 +1,34 @@ +SET NAMES utf8mb4; +SET FOREIGN_KEY_CHECKS = 0; + +-- ---------------------------- +-- Table structure for bot_create_combinations +-- ---------------------------- +DROP TABLE IF EXISTS `bot_create_combinations`; +CREATE TABLE `bot_create_combinations` ( + `race` int UNSIGNED NOT NULL DEFAULT 0, + `classes` int UNSIGNED NOT NULL DEFAULT 0, + PRIMARY KEY (`race`) USING BTREE +) ENGINE = InnoDB CHARACTER SET = latin1 COLLATE = latin1_swedish_ci ROW_FORMAT = Compact; + +-- ---------------------------- +-- Records of bot_create_combinations +-- ---------------------------- +INSERT INTO `bot_create_combinations` VALUES (1, 15871); -- Human +INSERT INTO `bot_create_combinations` VALUES (2, 49921); -- Barbarian +INSERT INTO `bot_create_combinations` VALUES (3, 15382); -- Erudite +INSERT INTO `bot_create_combinations` VALUES (4, 425); -- Wood Elf +INSERT INTO `bot_create_combinations` VALUES (5, 14342); -- High Elf +INSERT INTO `bot_create_combinations` VALUES (6, 15635); -- Dark Elf +INSERT INTO `bot_create_combinations` VALUES (7, 429); -- Half Elf +INSERT INTO `bot_create_combinations` VALUES (8, 33031); -- Dwarf +INSERT INTO `bot_create_combinations` VALUES (9, 49681); -- Troll +INSERT INTO `bot_create_combinations` VALUES (10, 49681); -- Ogre +INSERT INTO `bot_create_combinations` VALUES (11, 303); -- Halfling +INSERT INTO `bot_create_combinations` VALUES (12, 15639); -- Gnome +INSERT INTO `bot_create_combinations` VALUES (128, 18001); -- Iksar +INSERT INTO `bot_create_combinations` VALUES (130, 50049); -- Vah Shir +INSERT INTO `bot_create_combinations` VALUES (330, 3863); -- Froglok +INSERT INTO `bot_create_combinations` VALUES (522, 15871); -- Drakkin + +SET FOREIGN_KEY_CHECKS = 1; diff --git a/zone/bot.cpp b/zone/bot.cpp index 99bcd079b..e3abb5935 100644 --- a/zone/bot.cpp +++ b/zone/bot.cpp @@ -1701,206 +1701,15 @@ bool Bot::IsValidRaceClassCombo() return Bot::IsValidRaceClassCombo(GetRace(), GetClass()); } -bool Bot::IsValidRaceClassCombo(uint16 r, uint8 c) +bool Bot::IsValidRaceClassCombo(uint16 bot_race, uint8 bot_class) { - switch (r) { - case HUMAN: - switch (c) { - case WARRIOR: - case CLERIC: - case PALADIN: - case RANGER: - case SHADOWKNIGHT: - case DRUID: - case MONK: - case BARD: - case ROGUE: - case NECROMANCER: - case WIZARD: - case MAGICIAN: - case ENCHANTER: - return true; - } - break; - case BARBARIAN: - switch (c) { - case WARRIOR: - case ROGUE: - case SHAMAN: - case BEASTLORD: - case BERSERKER: - return true; - } - break; - case ERUDITE: - switch (c) { - case CLERIC: - case PALADIN: - case SHADOWKNIGHT: - case NECROMANCER: - case WIZARD: - case MAGICIAN: - case ENCHANTER: - return true; - } - break; - case WOOD_ELF: - switch (c) { - case WARRIOR: - case RANGER: - case DRUID: - case BARD: - case ROGUE: - return true; - } - break; - case HIGH_ELF: - switch (c) { - case CLERIC: - case PALADIN: - case WIZARD: - case MAGICIAN: - case ENCHANTER: - return true; - } - break; - case DARK_ELF: - switch (c) { - case WARRIOR: - case CLERIC: - case SHADOWKNIGHT: - case ROGUE: - case NECROMANCER: - case WIZARD: - case MAGICIAN: - case ENCHANTER: - return true; - } - break; - case HALF_ELF: - switch (c) { - case WARRIOR: - case PALADIN: - case RANGER: - case DRUID: - case BARD: - case ROGUE: - return true; - } - break; - case DWARF: - switch (c) { - case WARRIOR: - case CLERIC: - case PALADIN: - case ROGUE: - case BERSERKER: - return true; - } - break; - case TROLL: - switch (c) { - case WARRIOR: - case SHADOWKNIGHT: - case SHAMAN: - case BEASTLORD: - case BERSERKER: - return true; - } - break; - case OGRE: - switch (c) { - case WARRIOR: - case SHADOWKNIGHT: - case SHAMAN: - case BEASTLORD: - case BERSERKER: - return true; - } - break; - case HALFLING: - switch (c) { - case WARRIOR: - case CLERIC: - case PALADIN: - case RANGER: - case DRUID: - case ROGUE: - return true; - } - break; - case GNOME: - switch (c) { - case WARRIOR: - case CLERIC: - case PALADIN: - case SHADOWKNIGHT: - case ROGUE: - case NECROMANCER: - case WIZARD: - case MAGICIAN: - case ENCHANTER: - return true; - } - break; - case IKSAR: - switch (c) { - case WARRIOR: - case SHADOWKNIGHT: - case MONK: - case SHAMAN: - case NECROMANCER: - case BEASTLORD: - return true; - } - break; - case VAHSHIR: - switch (c) { - case WARRIOR: - case BARD: - case ROGUE: - case SHAMAN: - case BEASTLORD: - case BERSERKER: - return true; - } - break; - case FROGLOK: - switch (c) { - case WARRIOR: - case CLERIC: - case PALADIN: - case SHADOWKNIGHT: - case ROGUE: - case SHAMAN: - case NECROMANCER: - case WIZARD: - return true; - } - break; - case DRAKKIN: - switch (c) { - case WARRIOR: - case CLERIC: - case PALADIN: - case RANGER: - case SHADOWKNIGHT: - case DRUID: - case MONK: - case BARD: - case ROGUE: - case NECROMANCER: - case WIZARD: - case MAGICIAN: - case ENCHANTER: - return true; - } - break; - default: - break; + bool is_valid = false; + auto classes = database.botdb.GetRaceClassBitmask(bot_race); + auto bot_class_bitmask = GetPlayerClassBit(bot_class); + if (classes & bot_class_bitmask) { + is_valid = true; } - - return false; + return is_valid; } bool Bot::IsValidName() @@ -4264,124 +4073,6 @@ void Bot::LevelBotWithClient(Client* client, uint8 level, bool sendlvlapp) { } } -std::string Bot::ClassIdToString(uint16 classId) { - std::string Result; - - if(classId > 0 && classId < 17) { - switch(classId) { - case 1: - Result = std::string("Warrior"); - break; - case 2: - Result = std::string("Cleric"); - break; - case 3: - Result = std::string("Paladin"); - break; - case 4: - Result = std::string("Ranger"); - break; - case 5: - Result = std::string("Shadowknight"); - break; - case 6: - Result = std::string("Druid"); - break; - case 7: - Result = std::string("Monk"); - break; - case 8: - Result = std::string("Bard"); - break; - case 9: - Result = std::string("Rogue"); - break; - case 10: - Result = std::string("Shaman"); - break; - case 11: - Result = std::string("Necromancer"); - break; - case 12: - Result = std::string("Wizard"); - break; - case 13: - Result = std::string("Magician"); - break; - case 14: - Result = std::string("Enchanter"); - break; - case 15: - Result = std::string("Beastlord"); - break; - case 16: - Result = std::string("Berserker"); - break; - } - } - - return Result; -} - -std::string Bot::RaceIdToString(uint16 raceId) { - std::string Result; - - if(raceId > 0) { - switch(raceId) { - case 1: - Result = std::string("Human"); - break; - case 2: - Result = std::string("Barbarian"); - break; - case 3: - Result = std::string("Erudite"); - break; - case 4: - Result = std::string("Wood Elf"); - break; - case 5: - Result = std::string("High Elf"); - break; - case 6: - Result = std::string("Dark Elf"); - break; - case 7: - Result = std::string("Half Elf"); - break; - case 8: - Result = std::string("Dwarf"); - break; - case 9: - Result = std::string("Troll"); - break; - case 10: - Result = std::string("Ogre"); - break; - case 11: - Result = std::string("Halfling"); - break; - case 12: - Result = std::string("Gnome"); - break; - case 128: - Result = std::string("Iksar"); - break; - case 130: - Result = std::string("Vah Shir"); - break; - case 330: - Result = std::string("Froglok"); - break; - case 522: - Result = std::string("Drakkin"); - break; - } - } - - return Result; -} - void Bot::SendBotArcheryWearChange(uint8 material_slot, uint32 material, uint32 color) { EQApplicationPacket* outapp = new EQApplicationPacket(OP_WearChange, sizeof(WearChange_Struct)); WearChange_Struct* wc = (WearChange_Struct*)outapp->pBuffer; diff --git a/zone/bot.h b/zone/bot.h index caa556cf9..5ed980fe8 100644 --- a/zone/bot.h +++ b/zone/bot.h @@ -358,8 +358,6 @@ public: static uint32 SpawnedBotCount(uint32 botOwnerCharacterID); static void LevelBotWithClient(Client* client, uint8 level, bool sendlvlapp); //static bool SetBotOwnerCharacterID(uint32 botID, uint32 botOwnerCharacterID, std::string* errorMessage); - static std::string ClassIdToString(uint16 classId); - static std::string RaceIdToString(uint16 raceId); static bool IsBotAttackAllowed(Mob* attacker, Mob* target, bool& hasRuleDefined); static Bot* GetBotByBotClientOwnerAndBotName(Client* c, std::string botName); static void ProcessBotGroupInvite(Client* c, std::string botName); diff --git a/zone/bot_command.cpp b/zone/bot_command.cpp index 10b869d9c..65c2cc70a 100644 --- a/zone/bot_command.cpp +++ b/zone/bot_command.cpp @@ -1119,7 +1119,7 @@ private: for (bcst_levels::iterator levels_iter = bot_levels.begin(); levels_iter != bot_levels.end(); ++levels_iter) { if (levels_iter->second < test_iter->second) test_iter = levels_iter; - if (strcasecmp(Bot::ClassIdToString(levels_iter->first).c_str(), Bot::ClassIdToString(test_iter->first).c_str()) < 0 && levels_iter->second <= test_iter->second) + if (strcasecmp(GetClassIDName(levels_iter->first), GetClassIDName(test_iter->first)) < 0 && levels_iter->second <= test_iter->second) test_iter = levels_iter; } @@ -1131,8 +1131,8 @@ private: else bot_segment = " or %s(%u)"; - required_bots_map[type_index].append(StringFormat(bot_segment.c_str(), Bot::ClassIdToString(test_iter->first).c_str(), test_iter->second)); - required_bots_map_by_class[type_index][test_iter->first] = StringFormat("%s(%u)", Bot::ClassIdToString(test_iter->first).c_str(), test_iter->second); + required_bots_map[type_index].append(StringFormat(bot_segment.c_str(), GetClassIDName(test_iter->first), test_iter->second)); + required_bots_map_by_class[type_index][test_iter->first] = StringFormat("%s(%u)", GetClassIDName(test_iter->first), test_iter->second); bot_levels.erase(test_iter); } } @@ -1428,6 +1428,7 @@ int bot_command_init(void) bot_command_add("suspend", "Suspends a bot's AI processing until released", 0, bot_command_suspend) || bot_command_add("taunt", "Toggles taunt use by a bot", 0, bot_command_taunt) || bot_command_add("track", "Orders a capable bot to track enemies", 0, bot_command_track) || + bot_command_add("viewcombos", "Views bot race class combinations", 0, bot_command_view_combos) || bot_command_add("waterbreathing", "Orders a bot to cast a water breathing spell", 0, bot_command_water_breathing) ) { bot_command_deinit(); @@ -5107,6 +5108,68 @@ void bot_subcommand_bot_clone(Client *c, const Seperator *sep) c->Message(m_action, "Bot '%s' was successfully cloned to bot '%s'", my_bot->GetCleanName(), bot_name.c_str()); } +void bot_command_view_combos(Client *c, const Seperator *sep) +{ + const std::string class_substrs[17] = { "", + "%u (WAR)", "%u (CLR)", "%u (PAL)", "%u (RNG)", + "%u (SHD)", "%u (DRU)", "%u (MNK)", "%u (BRD)", + "%u (ROG)", "%u (SHM)", "%u (NEC)", "%u (WIZ)", + "%u (MAG)", "%u (ENC)", "%u (BST)", "%u (BER)" + }; + + const std::string race_substrs[17] = { "", + "%u (HUM)", "%u (BAR)", "%u (ERU)", "%u (ELF)", + "%u (HIE)", "%u (DEF)", "%u (HEF)", "%u (DWF)", + "%u (TRL)", "%u (OGR)", "%u (HFL)", "%u (GNM)", + "%u (IKS)", "%u (VAH)", "%u (FRG)", "%u (DRK)" + }; + + const uint16 race_values[17] = { 0, + HUMAN, BARBARIAN, ERUDITE, WOOD_ELF, + HIGH_ELF, DARK_ELF, HALF_ELF, DWARF, + TROLL, OGRE, HALFLING, GNOME, + IKSAR, VAHSHIR, FROGLOK, DRAKKIN + }; + if (helper_command_alias_fail(c, "bot_command_view_combos", sep->arg[0], "viewcombos")) + return; + if (helper_is_help_or_usage(sep->arg[1])) { + std::string window_title = "Bot Races"; + std::string window_text; + std::string message_separator = " "; + c->Message(m_usage, "Usage: %s [bot_race]", sep->arg[0]); + window_text.append("Races:"); + for (int race_id = 0; race_id <= 15; ++race_id) { + window_text.append(message_separator); + window_text.append(StringFormat(race_substrs[race_id + 1].c_str(), race_values[race_id + 1])); + message_separator = ", "; + } + c->SendPopupToClient(window_title.c_str(), window_text.c_str()); + return; + } + + if (sep->arg[1][0] == '\0' || !sep->IsNumber(1)) { + c->Message(m_fail, "Invalid Race!"); + return; + } + uint16 bot_race = atoi(sep->arg[1]); + auto classes_bitmask = database.botdb.GetRaceClassBitmask(bot_race); + auto race_name = GetRaceIDName(bot_race); + std::string window_title = "Bot Classes"; + std::string window_text; + std::string message_separator = " "; + c->Message(m_usage, "%s can be these classes.", race_name); + window_text.append("Classes:"); + for (int class_id = 0; class_id <= 15; ++class_id) { + if (classes_bitmask & GetPlayerClassBit(class_id)) { + window_text.append(message_separator); + window_text.append(StringFormat(class_substrs[class_id].c_str(), class_id)); + message_separator = ", "; + } + } + c->SendPopupToClient(window_title.c_str(), window_text.c_str()); + return; +} + void bot_subcommand_bot_create(Client *c, const Seperator *sep) { const std::string class_substrs[17] = { "", @@ -5148,10 +5211,7 @@ void bot_subcommand_bot_create(Client *c, const Seperator *sep) message_separator = " "; object_count = 1; for (int i = 0; i <= 15; ++i) { - if (((1 << i) & RuleI(Bots, AllowedClasses)) == 0) - continue; - - window_text.append(const_cast(message_separator)); + window_text.append(message_separator); if (object_count >= object_max) { window_text.append("
"); object_count = 0; @@ -5166,10 +5226,7 @@ void bot_subcommand_bot_create(Client *c, const Seperator *sep) message_separator = " "; object_count = 1; for (int i = 0; i <= 15; ++i) { - if (((1 << i) & RuleI(Bots, AllowedRaces)) == 0) - continue; - - window_text.append(const_cast(message_separator)); + window_text.append(message_separator); if (object_count >= object_max) { window_text.append("
"); object_count = 0; @@ -5183,12 +5240,8 @@ void bot_subcommand_bot_create(Client *c, const Seperator *sep) window_text.append("Genders:"); message_separator = " "; for (int i = 0; i <= 1; ++i) { - if (((1 << i) & RuleI(Bots, AllowedGenders)) == 0) - continue; - - window_text.append(const_cast(message_separator)); + window_text.append(message_separator); window_text.append(StringFormat(gender_substrs[i].c_str(), i)); - message_separator = ", "; } @@ -5802,9 +5855,9 @@ void bot_subcommand_bot_list(Client *c, const Seperator *sep) c->Message(Chat::White, "[%s] is a level %u %s %s %s who is owned by %s", ((c->CharacterID() == bots_iter.Owner_ID) && (!botCheckNotOnline) ? (EQ::SayLinkEngine::GenerateQuestSaylink(botspawn_saylink, false, bots_iter.Name).c_str()) : (bots_iter.Name)), bots_iter.Level, - Bot::RaceIdToString(bots_iter.Race).c_str(), + GetRaceIDName(bots_iter.Race), ((bots_iter.Gender == FEMALE) ? ("Female") : ((bots_iter.Gender == MALE) ? ("Male") : ("Neuter"))), - Bot::ClassIdToString(bots_iter.Class).c_str(), + GetClassIDName(bots_iter.Class), bots_iter.Owner ); if (c->CharacterID() == bots_iter.Owner_ID) { ++bots_owned; } @@ -5977,7 +6030,7 @@ void bot_subcommand_bot_report(Client *c, const Seperator *sep) if (!bot_iter) continue; - std::string report_msg = StringFormat("%s %s reports", Bot::ClassIdToString(bot_iter->GetClass()).c_str(), bot_iter->GetCleanName()); + std::string report_msg = StringFormat("%s %s reports", GetClassIDName(bot_iter->GetClass()), bot_iter->GetCleanName()); report_msg.append(StringFormat(": %3.1f%% health", bot_iter->GetHPRatio())); if (!IsNonSpellFighterClass(bot_iter->GetClass())) report_msg.append(StringFormat(": %3.1f%% mana", bot_iter->GetManaRatio())); @@ -8672,33 +8725,25 @@ uint32 helper_bot_create(Client *bot_owner, std::string bot_name, uint8 bot_clas return bot_id; } - auto class_bit = GetPlayerClassBit(bot_class); - if ((class_bit & RuleI(Bots, AllowedClasses)) == PLAYER_CLASS_UNKNOWN_BIT) { - bot_owner->Message(m_fail, "Class '%s' bots are not allowed on this server", GetPlayerClassName(bot_class)); - return bot_id; - } - - auto race_bit = GetPlayerRaceBit(bot_race); - if ((race_bit & RuleI(Bots, AllowedRaces)) == PLAYER_RACE_UNKNOWN_BIT) { - bot_owner->Message(m_fail, "Race '%s' bots are not allowed on this server", GetPlayerRaceName(bot_class)); - return bot_id; - } - if (!Bot::IsValidRaceClassCombo(bot_race, bot_class)) { - bot_owner->Message(m_fail, "'%s'(%u):'%s'(%u) is an invalid race-class combination", - Bot::RaceIdToString(bot_race).c_str(), bot_race, Bot::ClassIdToString(bot_class).c_str(), bot_class); + const char* bot_race_name = GetRaceIDName(bot_race); + const char* bot_class_name = GetClassIDName(bot_class); + std::string view_saylink = EQ::SayLinkEngine::GenerateQuestSaylink(fmt::format("^viewcombos {}", bot_race), false, "view"); + bot_owner->Message( + m_fail, + fmt::format( + "{} {} is an invalid race-class combination, would you like to {} proper combinations for {}?", + bot_race_name, + bot_class_name, + view_saylink, + bot_race_name + ).c_str() + ); return bot_id; } - if (bot_gender > FEMALE || (((1 << bot_gender) & RuleI(Bots, AllowedGenders)) == 0)) { - if (RuleI(Bots, AllowedGenders) == 3) - bot_owner->Message(m_fail, "gender: %u(M), %u(F)", MALE, FEMALE); - else if (RuleI(Bots, AllowedGenders) == 2) - bot_owner->Message(m_fail, "gender: %u(F)", FEMALE); - else if (RuleI(Bots, AllowedGenders) == 1) - bot_owner->Message(m_fail, "gender: %u(M)", MALE); - else - bot_owner->Message(m_fail, "gender: ERROR - No valid genders exist"); + if (bot_gender > FEMALE) { + bot_owner->Message(m_fail, "gender: %u (M), %u (F)", MALE, FEMALE); return bot_id; } @@ -8710,7 +8755,7 @@ uint32 helper_bot_create(Client *bot_owner, std::string bot_name, uint8 bot_clas return bot_id; } if (bot_count >= max_bot_count) { - bot_owner->Message(m_fail, "You have reached the maximum limit of %i bots", max_bot_count); + bot_owner->Message(m_fail, "You have reached the maximum limit of %i bots.", max_bot_count); return bot_id; } diff --git a/zone/bot_command.h b/zone/bot_command.h index 84a56f239..34ced3872 100644 --- a/zone/bot_command.h +++ b/zone/bot_command.h @@ -593,6 +593,7 @@ void bot_command_summon_corpse(Client *c, const Seperator *sep); void bot_command_suspend(Client *c, const Seperator *sep); void bot_command_taunt(Client *c, const Seperator *sep); void bot_command_track(Client *c, const Seperator *sep); +void bot_command_view_combos(Client *c, const Seperator *sep); void bot_command_water_breathing(Client *c, const Seperator *sep); diff --git a/zone/bot_database.cpp b/zone/bot_database.cpp index c419fd355..b9d0bd6cd 100644 --- a/zone/bot_database.cpp +++ b/zone/bot_database.cpp @@ -2952,6 +2952,20 @@ uint8 BotDatabase::GetSpellCastingChance(uint8 spell_type_index, uint8 class_ind return Bot::spell_casting_chances[spell_type_index][class_index][stance_index][conditional_index]; } +uint16 BotDatabase::GetRaceClassBitmask(uint16 bot_race) +{ + std::string query = fmt::format( + "SELECT `classes` FROM `bot_create_combinations` WHERE `race` = {}", + bot_race + ); + auto results = database.QueryDatabase(query); + uint16 classes = 0; + if (results.RowCount() == 1) { + auto row = results.begin(); + classes = atoi(row[0]); + } + return classes; +} /* fail::Bot functions */ const char* BotDatabase::fail::QueryNameAvailablity() { return "Failed to query name availability"; } diff --git a/zone/bot_database.h b/zone/bot_database.h index 57cf185ce..9ebb2b0c5 100644 --- a/zone/bot_database.h +++ b/zone/bot_database.h @@ -186,6 +186,7 @@ public: /* Bot miscellaneous functions */ uint8 GetSpellCastingChance(uint8 spell_type_index, uint8 class_index, uint8 stance_index, uint8 conditional_index); + uint16 GetRaceClassBitmask(uint16 bot_race); class fail { public: