#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]; } return false; } void Client::SetBotOption(BotOwnerOption boo, bool flag) { if (boo < _booCount) { bot_owner_options[boo] = flag; } } uint32 Client::GetBotCreationLimit(uint8 class_id) { uint32 bot_creation_limit = RuleI(Bots, CreationLimit); 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))) : "" ); auto bucket_value = GetBucket(bucket_name); if (!bucket_value.empty() && Strings::IsNumber(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; } 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))) : "" ); 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 bot_spawn_limit = RuleI(Bots, SpawnLimit); 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))) : "" ); auto bucket_value = GetBucket(bucket_name); 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", bucket_name, CharacterID() ); auto results = database.QueryDatabase(query); // use 'database' for non-bot table calls if (results.Success() && results.RowCount()) { auto row = results.begin(); bot_spawn_limit = Strings::ToInt(row[0]); } } 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 (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 (new_limit < bot_spawn_limit) { bot_spawn_limit = new_limit; } } catch (const std::exception &e) { LogInfo("Invalid entry in Rule Bots:ZoneSpawnLimits: [{}]", e.what()); } } } } 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 (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 (new_limit != bot_spawn_limit) { bot_spawn_limit = new_limit; } } catch (const std::exception &e) { LogInfo("Invalid entry in Rule Bots:ZoneForcedSpawnLimits: [{}]", e.what()); } } } } } return bot_spawn_limit; } void Client::SetBotCreationLimit(uint32 new_creation_limit, uint8 class_id) { const auto bucket_name = fmt::format( "bot_creation_limit{}", ( class_id && IsPlayerClass(class_id) ? fmt::format( "_{}", Strings::ToLower(GetClassIDName(class_id)) ) : "" ) ); SetBucket(bucket_name, std::to_string(new_creation_limit)); } void Client::SetBotRequiredLevel(int new_required_level, uint8 class_id) { const auto bucket_name = fmt::format( "bot_required_level{}", ( class_id && IsPlayerClass(class_id) ? fmt::format( "_{}", Strings::ToLower(GetClassIDName(class_id)) ) : "" ) ); SetBucket(bucket_name, std::to_string(new_required_level)); } void Client::SetBotSpawnLimit(int new_spawn_limit, uint8 class_id) { const auto bucket_name = fmt::format( "bot_spawn_limit{}", ( class_id && IsPlayerClass(class_id) ? fmt::format( "_{}", Strings::ToLower(GetClassIDName(class_id)) ) : "" ) ); SetBucket(bucket_name, std::to_string(new_spawn_limit)); } void Client::CampAllBots(uint8 class_id) { Bot::BotOrderCampAll(this, class_id); } void Client::LoadDefaultBotSettings() { m_bot_spell_settings.clear(); /* No longer used, left as example. SetBotSetting(BotSettingCategories::BaseSetting, BotBaseSettings::IllusionBlock, GetDefaultBotSettings(BotSettingCategories::BaseSetting, BotBaseSettings::IllusionBlock)); LogBotSettingsDetail("{} says, 'Setting default {} [{}] to [{}]'", GetCleanName(), CastToBot()->GetBotSettingCategoryName(BotBaseSettings::IllusionBlock), BotBaseSettings::IllusionBlock, GetDefaultBotSettings(BotSettingCategories::BaseSetting, BotBaseSettings::IllusionBlock)); */ for (uint16 i = BotSpellTypes::START; i <= BotSpellTypes::END; ++i) { BotSpellSettings t; t.spell_type = i; t.short_name = Bot::GetSpellTypeShortNameByID(i); t.name = Bot::GetSpellTypeNameByID(i); t.delay = GetDefaultSpellTypeDelay(i); t.min_threshold = GetDefaultSpellTypeMinThreshold(i); t.max_threshold = GetDefaultSpellTypeMaxThreshold(i); t.recast_timer.Start(); m_bot_spell_settings.push_back(t); LogBotSettingsDetail("{} says, 'Setting defaults for {} ({}) [#{}]'", GetCleanName(), t.name, t.short_name, t.spell_type); LogBotSettingsDetail("{} says, 'Delay = [{}ms] | MinThreshold = [{}\%] | MaxThreshold = [{}\%]'", GetCleanName(), GetDefaultSpellTypeDelay(i), GetDefaultSpellTypeMinThreshold(i), GetDefaultSpellTypeMaxThreshold(i)); } } int Client::GetDefaultBotSettings(uint8 setting_type, uint16 bot_setting) { switch (setting_type) { case BotSettingCategories::BaseSetting: return false; // only setting supported currently is illusion block case BotSettingCategories::SpellDelay: return GetDefaultSpellTypeDelay(bot_setting); case BotSettingCategories::SpellMinThreshold: return GetDefaultSpellTypeMinThreshold(bot_setting); case BotSettingCategories::SpellMaxThreshold: return GetDefaultSpellTypeMaxThreshold(bot_setting); default: return 0; // default return for any unsupported setting type } } int Client::GetBotSetting(uint8 setting_type, uint16 bot_setting) { switch (setting_type) { case BotSettingCategories::BaseSetting: return 0; // unused currently case BotSettingCategories::SpellDelay: return GetSpellTypeDelay(bot_setting); case BotSettingCategories::SpellMinThreshold: return GetSpellTypeMinThreshold(bot_setting); case BotSettingCategories::SpellMaxThreshold: return GetSpellTypeMaxThreshold(bot_setting); default: return 0; // default return for any unsupported setting type } } void Client::SetBotSetting(uint8 setting_type, uint16 bot_setting, uint32 setting_value) { switch (setting_type) { case BotSettingCategories::BaseSetting: break; // unused currently case BotSettingCategories::SpellDelay: SetSpellTypeDelay(bot_setting, setting_value); break; case BotSettingCategories::SpellMinThreshold: SetSpellTypeMinThreshold(bot_setting, setting_value); break; case BotSettingCategories::SpellMaxThreshold: SetSpellTypeMaxThreshold(bot_setting, setting_value); break; } } void Client::SendSpellTypePrompts(bool commanded_types, bool client_only_types) { if (client_only_types) { Message( Chat::Yellow, fmt::format( "You can view client spell types by {} or {}.", Saylink::Silent( fmt::format("^spelltypeids client"), "ID" ), Saylink::Silent( fmt::format("^spelltypenames client"), "Shortname" ) ).c_str() ); } else { Message( Chat::Yellow, fmt::format( "You can view spell types by {}, {}, {} or by {}, {}, {}.", Saylink::Silent( fmt::format("^spelltypeids 0-19"), "ID 0-19" ), Saylink::Silent( fmt::format("^spelltypeids 20-39"), "20-39" ), Saylink::Silent( fmt::format("^spelltypeids 40+"), "40+" ), Saylink::Silent( fmt::format("^spelltypenames 0-19"), "Shortname 0-19" ), Saylink::Silent( fmt::format("^spelltypenames 20-39"), "20-39" ), Saylink::Silent( fmt::format("^spelltypenames 40+"), "40+" ) ).c_str() ); } if (commanded_types) { Message( Chat::Yellow, fmt::format( "You can view commanded spell types by {} or {}.", Saylink::Silent( fmt::format("^spelltypeids commanded"), "ID" ), Saylink::Silent( fmt::format("^spelltypenames commanded"), "Shortname" ) ).c_str() ); } return; } uint16 Client::GetDefaultSpellTypeDelay(uint16 spell_type) { switch (spell_type) { case BotSpellTypes::VeryFastHeals: case BotSpellTypes::PetVeryFastHeals: return 1500; case BotSpellTypes::FastHeals: case BotSpellTypes::PetFastHeals: return 2500; case BotSpellTypes::GroupHeals: case BotSpellTypes::RegularHeal: case BotSpellTypes::PetRegularHeals: return 4000; case BotSpellTypes::CompleteHeal: case BotSpellTypes::GroupCompleteHeals: case BotSpellTypes::PetCompleteHeals: return 8000; case BotSpellTypes::GroupHoTHeals: case BotSpellTypes::HoTHeals: case BotSpellTypes::PetHoTHeals: return 22000; case BotSpellTypes::Cure: return 2000; case BotSpellTypes::GroupCures: return 3000; case BotSpellTypes::PetCures: return 5000; default: return 100; } } uint8 Client::GetDefaultSpellTypeMinThreshold(uint16 spell_type) { switch (spell_type) { default: return 0; } } uint8 Client::GetDefaultSpellTypeMaxThreshold(uint16 spell_type) { uint8 client_class = GetClass(); switch (spell_type) { case BotSpellTypes::VeryFastHeals: case BotSpellTypes::PetVeryFastHeals: return 25; case BotSpellTypes::FastHeals: case BotSpellTypes::PetFastHeals: return 40; case BotSpellTypes::GroupHeals: case BotSpellTypes::RegularHeal: case BotSpellTypes::PetRegularHeals: return 60; case BotSpellTypes::CompleteHeal: case BotSpellTypes::GroupCompleteHeals: case BotSpellTypes::PetCompleteHeals: if (client_class == Class::Necromancer || client_class == Class::Shaman) { return 55; } else { return 80; } case BotSpellTypes::GroupHoTHeals: case BotSpellTypes::HoTHeals: case BotSpellTypes::PetHoTHeals: if (client_class == Class::Necromancer || client_class == Class::Shaman) { return 70; } else { return 90; } case BotSpellTypes::Buff: case BotSpellTypes::Cure: case BotSpellTypes::GroupCures: case BotSpellTypes::PetCures: case BotSpellTypes::PetBuffs: case BotSpellTypes::PetDamageShields: case BotSpellTypes::PetResistBuffs: case BotSpellTypes::ResistBuffs: default: return 100; } }