diff --git a/common/ruletypes.h b/common/ruletypes.h index b5b51e17b..e01dbfa71 100644 --- a/common/ruletypes.h +++ b/common/ruletypes.h @@ -878,6 +878,7 @@ RULE_INT(Bots, MinStatusToBypassSpawnLimit, 100, "Minimum status to bypass spawn RULE_INT(Bots, MinStatusBypassSpawnLimit, 120, "Spawn limit with status bypass. Default 120.") RULE_INT(Bots, MinStatusToBypassCreateLimit, 100, "Minimum status to bypass create limit. Default 100.") RULE_INT(Bots, MinStatusBypassCreateLimit, 120, "Create limit with status bypass. Default 120.") +RULE_INT(Bots, MinStatusToBypassBotLevelRequirement, 100, "Minimum status to bypass level requirement for bots. Default 100.") RULE_BOOL(Bots, EnableBotTGB, true, "If enabled bots will cast group buffs as TGB.") RULE_BOOL(Bots, DoResponseAnimations, true, "If enabled bots will do animations to certain responses or commands.") RULE_INT(Bots, DefaultFollowDistance, 20, "Default 20. Distance a bot will follow behind.") diff --git a/zone/bot.cpp b/zone/bot.cpp index 294e49963..01adfb886 100644 --- a/zone/bot.cpp +++ b/zone/bot.cpp @@ -8715,6 +8715,86 @@ bool Bot::CheckCampSpawnConditions(Client* c) { return true; } +bool Bot::CheckHighEnoughLevelForBots(Client* c, uint8 bot_class) { + auto bot_character_level = c->GetBotRequiredLevel(bot_class); + bool not_high_enough_level = bot_character_level >= 0 && c->GetLevel() < bot_character_level; + + if (not_high_enough_level) { + c->Message( + Chat::White, + fmt::format( + "You must be level {} to spawn {}bots.", + bot_character_level, + bot_class ? GetClassIDName(bot_class) : "" + ).c_str() + ); + + return false; + } + + return true; +} + +bool Bot::CheckCreateLimit(Client* c, uint32 bot_count, uint8 bot_class) { + auto bot_creation_limit = c->GetBotCreationLimit(bot_class); + bool is_beyond_spawn_limit = bot_creation_limit >= 0 && bot_count >= bot_creation_limit; + + if (is_beyond_spawn_limit) { + std::string message; + + if (bot_creation_limit) { + message = fmt::format( + "You cannot create anymore than {} {}bot{}.", + bot_creation_limit, + bot_class ? GetClassIDName(bot_class) : "", + bot_creation_limit != 1 ? "s" : "" + ); + } else { + message = fmt::format( + "You cannot create any {}bots.", + bot_class ? GetClassIDName(bot_class) : "" + ); + } + + c->Message(Chat::Yellow, message.c_str()); + + return false; + } + + return true; +} + +bool Bot::CheckSpawnLimit(Client* c, uint8 bot_class) { + auto bot_spawn_limit = c->GetBotSpawnLimit(bot_class); + auto spawned_bot_count = Bot::SpawnedBotCount(c->CharacterID(), bot_class); + bool is_beyond_spawn_limit = bot_spawn_limit >= 0 && spawned_bot_count >= bot_spawn_limit; + + if (is_beyond_spawn_limit) { + std::string message; + + if (bot_spawn_limit) { + message = fmt::format( + "You cannot have more than {} spawned {}bot{}.", + bot_spawn_limit, + bot_class ? GetClassIDName(bot_class) : "", + bot_spawn_limit != 1 ? "s" : "" + ); + } + else { + message = fmt::format( + "You are not currently allowed to spawn any {}bots.", + bot_class ? GetClassIDName(bot_class) : "" + ); + } + + c->Message(Chat::White, message.c_str()); + + return false; + } + + return true; +} + void Bot::AddBotStartingItems(uint16 race_id, uint8 class_id) { if (!IsPlayerRace(race_id) || !IsPlayerClass(class_id)) { diff --git a/zone/bot.h b/zone/bot.h index 620e3a9a1..95ace3958 100644 --- a/zone/bot.h +++ b/zone/bot.h @@ -1105,6 +1105,9 @@ public: // Public "Refactor" Methods static bool CheckCampSpawnConditions(Client* c); + static bool CheckHighEnoughLevelForBots(Client* c, uint8 bot_class = Class::None); + static bool CheckCreateLimit(Client* c, uint32 bot_count, uint8 bot_class = Class::None); + static bool CheckSpawnLimit(Client* c, uint8 bot_class = Class::None); protected: void BotMeditate(bool is_sitting); diff --git a/zone/bot_command.cpp b/zone/bot_command.cpp index 42121b8ca..854054bfa 100644 --- a/zone/bot_command.cpp +++ b/zone/bot_command.cpp @@ -517,88 +517,31 @@ uint32 helper_bot_create(Client *bot_owner, std::string bot_name, uint8 bot_clas return bot_id; } - auto bot_creation_limit = bot_owner->GetBotCreationLimit(); - auto bot_creation_limit_class = bot_owner->GetBotCreationLimit(bot_class); + if (!Bot::CheckHighEnoughLevelForBots(bot_owner)) { + return bot_id; + } + + if (!Bot::CheckHighEnoughLevelForBots(bot_owner, bot_class)) { + return bot_id; + } uint32 bot_count = 0; uint32 bot_class_count = 0; + if (!database.botdb.QueryBotCount(bot_owner->CharacterID(), bot_class, bot_count, bot_class_count)) { bot_owner->Message(Chat::Yellow, "Failed to query bot count."); + return bot_id; } - if (bot_creation_limit >= 0 && bot_count >= bot_creation_limit) { - std::string message; - - if (bot_creation_limit) { - message = fmt::format( - "You cannot create anymore than {} bot{}.", - bot_creation_limit, - bot_creation_limit != 1 ? "s" : "" - ); - } else { - message = "You cannot create any bots."; - } - - bot_owner->Message(Chat::Yellow, message.c_str()); + if (!Bot::CheckCreateLimit(bot_owner, bot_count)) { return bot_id; } - if (bot_creation_limit_class >= 0 && bot_class_count >= bot_creation_limit_class) { - std::string message; - - if (bot_creation_limit_class) { - message = fmt::format( - "You cannot create anymore than {} {} bot{}.", - bot_creation_limit_class, - GetClassIDName(bot_class), - bot_creation_limit_class != 1 ? "s" : "" - ); - } else { - message = fmt::format( - "You cannot create any {} bots.", - GetClassIDName(bot_class) - ); - } - - bot_owner->Message(Chat::Yellow, message.c_str()); + if (!Bot::CheckCreateLimit(bot_owner, bot_class_count, bot_class)) { return bot_id; } - auto bot_character_level = bot_owner->GetBotRequiredLevel(); - - if ( - bot_character_level >= 0 && - bot_owner->GetLevel() < bot_character_level - ) { - bot_owner->Message( - Chat::Yellow, - fmt::format( - "You must be level {} to use bots.", - bot_character_level - ).c_str() - ); - return bot_id; - } - - auto bot_character_level_class = bot_owner->GetBotRequiredLevel(bot_class); - - if ( - bot_character_level_class >= 0 && - bot_owner->GetLevel() < bot_character_level_class - ) { - bot_owner->Message( - Chat::Yellow, - fmt::format( - "You must be level {} to use {} bots.", - bot_character_level_class, - GetClassIDName(bot_class) - ).c_str() - ); - return bot_id; - } - - auto my_bot = new Bot(Bot::CreateDefaultNPCTypeStructForBot(bot_name, "", bot_owner->GetLevel(), bot_race, bot_class, bot_gender), bot_owner); if (!my_bot->Save()) { diff --git a/zone/bot_commands/bot.cpp b/zone/bot_commands/bot.cpp index 8a8c4a277..a620e9e66 100644 --- a/zone/bot_commands/bot.cpp +++ b/zone/bot_commands/bot.cpp @@ -144,55 +144,25 @@ void bot_command_clone(Client *c, const Seperator *sep) return; } - auto bot_creation_limit = c->GetBotCreationLimit(); - auto bot_creation_limit_class = c->GetBotCreationLimit(my_bot->GetClass()); - uint32 bot_count = 0; uint32 bot_class_count = 0; + if (!database.botdb.QueryBotCount(c->CharacterID(), my_bot->GetClass(), bot_count, bot_class_count)) { - c->Message(Chat::White, "Failed to query bot count."); + c->Message(Chat::Yellow, "Failed to query bot count."); + return; } - if (bot_creation_limit >= 0 && bot_count >= bot_creation_limit) { - std::string message; - - if (bot_creation_limit) { - message = fmt::format( - "You have reached the maximum limit of {} bot{}.", - bot_creation_limit, - bot_creation_limit != 1 ? "s" : "" - ); - } else { - message = "You cannot create any bots."; - } - - c->Message(Chat::White, message.c_str()); + if (!Bot::CheckCreateLimit(c, bot_count)) { return; } - if (bot_creation_limit_class >= 0 && bot_class_count >= bot_creation_limit_class) { - std::string message; - - if (bot_creation_limit_class) { - message = fmt::format( - "You cannot create anymore than {} {} bot{}.", - bot_creation_limit_class, - GetClassIDName(my_bot->GetClass()), - bot_creation_limit_class != 1 ? "s" : "" - ); - } else { - message = fmt::format( - "You cannot create any {} bots.", - GetClassIDName(my_bot->GetClass()) - ); - } - - c->Message(Chat::White, message.c_str()); + if (!Bot::CheckCreateLimit(c, bot_class_count, my_bot->GetClass())) { return; } uint32 clone_id = 0; + if (!database.botdb.CreateCloneBot(my_bot->GetBotID(), bot_name, clone_id) || !clone_id) { c->Message( Chat::White, @@ -205,6 +175,7 @@ void bot_command_clone(Client *c, const Seperator *sep) } int clone_stance = Stance::Passive; + if (!database.botdb.LoadStance(my_bot->GetBotID(), clone_stance)) { c->Message( Chat::White, @@ -729,6 +700,7 @@ void bot_command_list_bots(Client *c, const Seperator *sep) return; } + int NO_BOT_LIMIT = -1; bool Account = false; int seps = 1; uint32 filter_value[FilterCount]; @@ -867,7 +839,7 @@ void bot_command_list_bots(Client *c, const Seperator *sep) for (uint8 class_id = Class::Warrior; class_id <= Class::Berserker; class_id++) { auto class_creation_limit = c->GetBotCreationLimit(class_id); - if (class_creation_limit != overall_bot_creation_limit) { + if (class_creation_limit != NO_BOT_LIMIT && class_creation_limit != overall_bot_creation_limit) { c->Message( Chat::White, fmt::format( @@ -938,20 +910,7 @@ void bot_command_spawn(Client *c, const Seperator *sep) return; } - auto bot_character_level = c->GetBotRequiredLevel(); - - if ( - bot_character_level >= 0 && - c->GetLevel() < bot_character_level && - !c->GetGM() - ) { - c->Message( - Chat::White, - fmt::format( - "You must be level {} to spawn bots.", - bot_character_level - ).c_str() - ); + if (!Bot::CheckHighEnoughLevelForBots(c)) { return; } @@ -959,27 +918,7 @@ void bot_command_spawn(Client *c, const Seperator *sep) return; } - auto bot_spawn_limit = c->GetBotSpawnLimit(); - auto spawned_bot_count = Bot::SpawnedBotCount(c->CharacterID()); - - if ( - bot_spawn_limit >= 0 && - spawned_bot_count >= bot_spawn_limit && - !c->GetGM() - ) { - std::string message; - - if (bot_spawn_limit) { - message = fmt::format( - "You cannot have more than {} spawned bot{}.", - bot_spawn_limit, - bot_spawn_limit != 1 ? "s" : "" - ); - } else { - message = "You are not currently allowed to spawn any bots."; - } - - c->Message(Chat::White, message.c_str()); + if (!Bot::CheckSpawnLimit(c)) { return; } @@ -1004,52 +943,6 @@ void bot_command_spawn(Client *c, const Seperator *sep) return; } - auto bot_spawn_limit_class = c->GetBotSpawnLimit(bot_class); - auto spawned_bot_count_class = Bot::SpawnedBotCount(c->CharacterID(), bot_class); - - if ( - bot_spawn_limit_class >= 0 && - spawned_bot_count_class >= bot_spawn_limit_class && - !c->GetGM() - ) { - std::string message; - - if (bot_spawn_limit_class) { - message = fmt::format( - "You cannot have more than {} spawned {} bot{}.", - bot_spawn_limit_class, - GetClassIDName(bot_class), - bot_spawn_limit_class != 1 ? "s" : "" - ); - } else { - message = fmt::format( - "You are not currently allowed to spawn any {} bots.", - GetClassIDName(bot_class) - ); - } - - c->Message(Chat::White, message.c_str()); - return; - } - - auto bot_character_level_class = c->GetBotRequiredLevel(bot_class); - - if ( - bot_character_level_class >= 0 && - c->GetLevel() < bot_character_level_class && - !c->GetGM() - ) { - c->Message( - Chat::White, - fmt::format( - "You must be level {} to spawn {} bots.", - bot_character_level_class, - GetClassIDName(bot_class) - ).c_str() - ); - return; - } - if (!bot_id) { c->Message( Chat::White, @@ -1061,6 +954,14 @@ void bot_command_spawn(Client *c, const Seperator *sep) return; } + if (!Bot::CheckHighEnoughLevelForBots(c, bot_class)) { + return; + } + + if (!Bot::CheckSpawnLimit(c, bot_class)) { + return; + } + if (entity_list.GetMobByBotID(bot_id)) { c->Message( Chat::White, @@ -1069,6 +970,7 @@ void bot_command_spawn(Client *c, const Seperator *sep) bot_name ).c_str() ); + return; } @@ -1083,6 +985,7 @@ void bot_command_spawn(Client *c, const Seperator *sep) bot_id ).c_str() ); + return; } @@ -1097,6 +1000,7 @@ void bot_command_spawn(Client *c, const Seperator *sep) ); safe_delete(my_bot); + return; } @@ -1121,6 +1025,7 @@ void bot_command_spawn(Client *c, const Seperator *sep) }; uint8 message_index = 0; + if (c->GetBotOption(Client::booSpawnMessageClassSpecific)) { message_index = VALIDATECLASSID(my_bot->GetClass()); } diff --git a/zone/bot_database.cpp b/zone/bot_database.cpp index 70e535711..4c78284e6 100644 --- a/zone/bot_database.cpp +++ b/zone/bot_database.cpp @@ -207,7 +207,7 @@ bool BotDatabase::QueryBotCount(const uint32 owner_id, int class_id, uint32& bot bot_count = BotDataRepository::Count( database, fmt::format( - "`owner_id` = {}", + "`owner_id` = {} AND `name` NOT LIKE '%-deleted-%'", owner_id ) ); @@ -216,7 +216,7 @@ bool BotDatabase::QueryBotCount(const uint32 owner_id, int class_id, uint32& bot bot_class_count = BotDataRepository::Count( database, fmt::format( - "`owner_id` = {} AND `class` = {}", + "`owner_id` = {} AND `class` = {} AND `name` NOT LIKE '%-deleted-%'", owner_id, class_id ) diff --git a/zone/client_bot.cpp b/zone/client_bot.cpp index 7b5b229d8..b5621840f 100644 --- a/zone/client_bot.cpp +++ b/zone/client_bot.cpp @@ -1,6 +1,8 @@ #include "bot.h" #include "client.h" +#define NO_BOT_LIMIT -1; + bool Client::GetBotOption(BotOwnerOption boo) const { if (boo < _booCount) { return bot_owner_options[boo]; @@ -18,25 +20,25 @@ void Client::SetBotOption(BotOwnerOption boo, bool flag) { uint32 Client::GetBotCreationLimit(uint8 class_id) { uint32 bot_creation_limit = RuleI(Bots, CreationLimit); - if (Admin() >= RuleI(Bots, MinStatusToBypassCreateLimit)) { + if (GetGM() && Admin() >= RuleI(Bots, MinStatusToBypassCreateLimit)) { return RuleI(Bots, MinStatusBypassCreateLimit); } const auto bucket_name = fmt::format( "bot_creation_limit{}", - ( - class_id && IsPlayerClass(class_id) ? - fmt::format( - "_{}", - Strings::ToLower(GetClassIDName(class_id)) - ) : + class_id && IsPlayerClass(class_id) ? + fmt::format("_{}", Strings::ToLower(GetClassIDName(class_id))) : "" - ) ); auto bucket_value = GetBucket(bucket_name); + if (!bucket_value.empty() && Strings::IsNumber(bucket_value)) { - bot_creation_limit = Strings::ToUnsignedInt(bucket_value); + bot_creation_limit = Strings::ToInt(bucket_value); + } + + if (class_id && bucket_value.empty()) { + bot_creation_limit = NO_BOT_LIMIT; } return bot_creation_limit; @@ -45,63 +47,55 @@ uint32 Client::GetBotCreationLimit(uint8 class_id) { int Client::GetBotRequiredLevel(uint8 class_id) { int bot_character_level = RuleI(Bots, BotCharacterLevel); + if (GetGM() && Admin() >= RuleI(Bots, MinStatusToBypassBotLevelRequirement)) { + return 0; + } + const auto bucket_name = fmt::format( "bot_required_level{}", - ( - class_id && IsPlayerClass(class_id) ? - fmt::format( - "_{}", - Strings::ToLower(GetClassIDName(class_id)) - ) : + class_id && IsPlayerClass(class_id) ? + fmt::format("_{}", Strings::ToLower(GetClassIDName(class_id))) : "" - ) ); auto bucket_value = GetBucket(bucket_name); + if (!bucket_value.empty() && Strings::IsNumber(bucket_value)) { bot_character_level = Strings::ToInt(bucket_value); } + if (class_id && bucket_value.empty()) { + bot_character_level = NO_BOT_LIMIT; + } + return bot_character_level; } -int Client::GetBotSpawnLimit(uint8 class_id) { +int Client::GetBotSpawnLimit(uint8 class_id) +{ int bot_spawn_limit = RuleI(Bots, SpawnLimit); - if (Admin() >= RuleI(Bots, MinStatusToBypassSpawnLimit)) { + if (GetGM() && Admin() >= RuleI(Bots, MinStatusToBypassSpawnLimit)) { return RuleI(Bots, MinStatusBypassSpawnLimit); } const auto bucket_name = fmt::format( "bot_spawn_limit{}", - ( - class_id && IsPlayerClass(class_id) ? - fmt::format( - "_{}", - Strings::ToLower(GetClassIDName(class_id)) - ) : + class_id && IsPlayerClass(class_id) ? + fmt::format("_{}", Strings::ToLower(GetClassIDName(class_id))) : "" - ) ); auto bucket_value = GetBucket(bucket_name); - if (class_id && !bot_spawn_limit && bucket_value.empty()) { - const auto new_bucket_name = "bot_spawn_limit"; - - bucket_value = GetBucket(new_bucket_name); - - if (!bucket_value.empty() && Strings::IsNumber(bucket_value)) { - bot_spawn_limit = Strings::ToInt(bucket_value); - - return bot_spawn_limit; - } - } - if (!bucket_value.empty() && Strings::IsNumber(bucket_value)) { bot_spawn_limit = Strings::ToInt(bucket_value); } + if (class_id && bucket_value.empty()) { + return NO_BOT_LIMIT; + } + if (RuleB(Bots, QuestableSpawnLimit)) { const auto query = fmt::format( "SELECT `value` FROM `quest_globals` WHERE `name` = '{}' AND `charid` = {} LIMIT 1", @@ -111,53 +105,53 @@ int Client::GetBotSpawnLimit(uint8 class_id) { auto results = database.QueryDatabase(query); // use 'database' for non-bot table calls - if (!results.Success() || !results.RowCount()) { - return bot_spawn_limit; + if (results.Success() && results.RowCount()) { + auto row = results.begin(); + bot_spawn_limit = Strings::ToInt(row[0]); } - - auto row = results.begin(); - bot_spawn_limit = Strings::ToInt(row[0]); } - const auto& zones_list = Strings::Split(RuleS(Bots, ZonesWithSpawnLimits), ","); + if (!class_id) { + const auto &zones_list = Strings::Split(RuleS(Bots, ZonesWithSpawnLimits), ","); - if (!zones_list.empty()) { - auto it = std::find(zones_list.begin(), zones_list.end(), std::to_string(zone->GetZoneID())); + if (!zones_list.empty()) { + auto it = std::find(zones_list.begin(), zones_list.end(), std::to_string(zone->GetZoneID())); - if (it != zones_list.end()) { - const auto& zones_limits_list = Strings::Split(RuleS(Bots, ZoneSpawnLimits), ","); + if (it != zones_list.end()) { + const auto &zones_limits_list = Strings::Split(RuleS(Bots, ZoneSpawnLimits), ","); - if (zones_list.size() == zones_limits_list.size()) { - try { - auto new_limit = std::stoul(zones_limits_list[std::distance(zones_list.begin(), it)]); + if (zones_list.size() == zones_limits_list.size()) { + try { + auto new_limit = std::stoul(zones_limits_list[std::distance(zones_list.begin(), it)]); - if (new_limit < bot_spawn_limit) { - bot_spawn_limit = new_limit; + if (new_limit < bot_spawn_limit) { + bot_spawn_limit = new_limit; + } + } catch (const std::exception &e) { + LogInfo("Invalid entry in Rule Bots:ZoneSpawnLimits: [{}]", e.what()); } - } catch (const std::exception& e) { - LogInfo("Invalid entry in Rule Bots:ZoneSpawnLimits: [{}]", e.what()); } } } - } - const auto& zones_forced_list = Strings::Split(RuleS(Bots, ZonesWithForcedSpawnLimits), ","); + const auto &zones_forced_list = Strings::Split(RuleS(Bots, ZonesWithForcedSpawnLimits), ","); - if (!zones_forced_list.empty()) { - auto it = std::find(zones_forced_list.begin(), zones_forced_list.end(), std::to_string(zone->GetZoneID())); + if (!zones_forced_list.empty()) { + auto it = std::find(zones_forced_list.begin(), zones_forced_list.end(), std::to_string(zone->GetZoneID())); - if (it != zones_forced_list.end()) { - const auto& zones_forced_limits_list = Strings::Split(RuleS(Bots, ZoneForcedSpawnLimits), ","); + if (it != zones_forced_list.end()) { + const auto &zones_forced_limits_list = Strings::Split(RuleS(Bots, ZoneForcedSpawnLimits), ","); - if (zones_forced_list.size() == zones_forced_limits_list.size()) { - try { - auto new_limit = std::stoul(zones_forced_limits_list[std::distance(zones_forced_list.begin(), it)]); + if (zones_forced_list.size() == zones_forced_limits_list.size()) { + try { + auto new_limit = std::stoul(zones_forced_limits_list[std::distance(zones_forced_list.begin(), it)]); - if (new_limit != bot_spawn_limit) { - bot_spawn_limit = new_limit; + if (new_limit != bot_spawn_limit) { + bot_spawn_limit = new_limit; + } + } catch (const std::exception &e) { + LogInfo("Invalid entry in Rule Bots:ZoneForcedSpawnLimits: [{}]", e.what()); } - } catch (const std::exception& e) { - LogInfo("Invalid entry in Rule Bots:ZoneForcedSpawnLimits: [{}]", e.what()); } } }