diff --git a/common/database/database_update_manifest_bots.cpp b/common/database/database_update_manifest_bots.cpp index 76f564baf..f73afd598 100644 --- a/common/database/database_update_manifest_bots.cpp +++ b/common/database/database_update_manifest_bots.cpp @@ -270,6 +270,7 @@ UPDATE `bot_command_settings` SET `aliases`= 'minhp' WHERE `bot_command`='spellm UPDATE `bot_command_settings` SET `aliases`= 'minmana' WHERE `bot_command`='spellminmanapct'; UPDATE `bot_command_settings` SET `aliases`= 'minthresholds' WHERE `bot_command`='spellminthresholds'; UPDATE `bot_command_settings` SET `aliases`= 'pursuepriority' WHERE `bot_command`='spellpursuepriority'; +UPDATE `bot_command_settings` SET `aliases`= 'resistlimits' WHERE `bot_command`='spellresistlimits'; UPDATE `bot_command_settings` SET `aliases`= 'targetcount' WHERE `bot_command`='spelltargetcount'; UPDATE `bot_command_settings` SET `aliases`= 'disc' WHERE `bot_command`='discipline'; UPDATE `bot_command_settings` SET `aliases`= CASE WHEN LENGTH(`aliases`) > 0 THEN CONCAT(`aliases`, '|vc') ELSE 'vc' END WHERE `bot_command`='viewcombos' AND `aliases` NOT LIKE '%vc%'; diff --git a/zone/bot.cpp b/zone/bot.cpp index fe5f20fdf..1436cd363 100644 --- a/zone/bot.cpp +++ b/zone/bot.cpp @@ -10295,6 +10295,9 @@ void Bot::SetBotSetting(uint8 setting_type, uint16 bot_setting, int setting_valu case BotSettingCategories::SpellMaxThreshold: SetSpellMaxThreshold(bot_setting, setting_value); break; + case BotSettingCategories::SpellTypeResistLimit: + SetSpellTypeResistLimit(bot_setting, setting_value); + break; case BotSettingCategories::SpellTypeAggroCheck: SetSpellTypeAggroCheck(bot_setting, setting_value); break; @@ -10606,6 +10609,8 @@ int Bot::GetDefaultSetting(uint16 setting_category, uint16 setting_type, uint8 s return GetDefaultSpellMinThreshold(setting_type, stance); case BotSettingCategories::SpellMaxThreshold: return GetDefaultSpellMaxThreshold(setting_type, stance); + case BotSettingCategories::SpellTypeResistLimit: + return GetDefaultSpellTypeResistLimit(setting_type, stance); case BotSettingCategories::SpellTypeAggroCheck: return GetDefaultSpellTypeAggroCheck(setting_type, stance); case BotSettingCategories::SpellTypeMinManaPct: @@ -10641,6 +10646,8 @@ int Bot::GetSetting(uint16 setting_category, uint16 setting_type) { return GetSpellMinThreshold(setting_type); case BotSettingCategories::SpellMaxThreshold: return GetSpellMaxThreshold(setting_type); + case BotSettingCategories::SpellTypeResistLimit: + return GetSpellTypeResistLimit(setting_type); case BotSettingCategories::SpellTypeAggroCheck: return GetSpellTypeAggroCheck(setting_type); case BotSettingCategories::SpellTypeMinManaPct: diff --git a/zone/bot.h b/zone/bot.h index 1e1186e56..870eac312 100644 --- a/zone/bot.h +++ b/zone/bot.h @@ -99,16 +99,17 @@ namespace BotSettingCategories { // Update GetBotSpellCategoryName as needed constexpr uint8 SpellDelay = 2; constexpr uint8 SpellMinThreshold = 3; constexpr uint8 SpellMaxThreshold = 4; - constexpr uint8 SpellTypeAggroCheck = 5; - constexpr uint8 SpellTypeMinManaPct = 6; - constexpr uint8 SpellTypeMaxManaPct = 7; - constexpr uint8 SpellTypeMinHPPct = 8; - constexpr uint8 SpellTypeMaxHPPct = 9; - constexpr uint8 SpellTypeIdlePriority = 10; - constexpr uint8 SpellTypeEngagedPriority = 11; - constexpr uint8 SpellTypePursuePriority = 12; - constexpr uint8 SpellTypeAEOrGroupTargetCount = 13; - constexpr uint8 SpellTypeRecastDelay = 14; + constexpr uint8 SpellTypeResistLimit = 5; + constexpr uint8 SpellTypeAggroCheck = 6; + constexpr uint8 SpellTypeMinManaPct = 7; + constexpr uint8 SpellTypeMaxManaPct = 8; + constexpr uint8 SpellTypeMinHPPct = 9; + constexpr uint8 SpellTypeMaxHPPct = 10; + constexpr uint8 SpellTypeIdlePriority = 11; + constexpr uint8 SpellTypeEngagedPriority = 12; + constexpr uint8 SpellTypePursuePriority = 13; + constexpr uint8 SpellTypeAEOrGroupTargetCount = 14; + constexpr uint8 SpellTypeRecastDelay = 15; constexpr uint16 START = BotSettingCategories::BaseSetting; constexpr uint16 START_NO_BASE = BotSettingCategories::SpellHold; @@ -124,6 +125,7 @@ static std::map botSpellCategory_names = { { BotSettingCategories::SpellDelay, "SpellDelays" }, { BotSettingCategories::SpellMinThreshold, "SpellMinThresholds" }, { BotSettingCategories::SpellMaxThreshold, "SpellMaxThresholds" }, + { BotSettingCategories::SpellTypeResistLimit, "SpellResistLimit" }, { BotSettingCategories::SpellTypeAggroCheck, "SpellAggroChecks" }, { BotSettingCategories::SpellTypeMinManaPct, "SpellMinManaPct" }, { BotSettingCategories::SpellTypeMaxManaPct, "SpellMaxManaPct" }, diff --git a/zone/bot_command.cpp b/zone/bot_command.cpp index 7575e9c00..2398538cc 100644 --- a/zone/bot_command.cpp +++ b/zone/bot_command.cpp @@ -190,6 +190,7 @@ int bot_command_init(void) bot_command_add("spellminhppct", "Controls at what HP percent a bot will start casting different spell types", AccountStatus::Player, bot_command_spell_min_hp_pct) || bot_command_add("spellminmanapct", "Controls at what mana percent a bot will start casting different spell types", AccountStatus::Player, bot_command_spell_min_mana_pct) || bot_command_add("spellminthresholds", "Controls the maximum target HP threshold for a spell to be cast for a specific type", AccountStatus::Player, bot_command_spell_min_thresholds) || + bot_command_add("spellresistlimits", "Controls the resist limits for bots to cast spells on their target", AccountStatus::Player, bot_command_spell_resist_limits) || bot_command_add("spellpursuepriority", "Controls the order of casts by spell type when pursuing in combat", AccountStatus::Player, bot_command_spell_pursue_priority) || bot_command_add("spelltargetcount", "Sets the required target amount for group/AE spells by spell type", AccountStatus::Player, bot_command_spell_target_count) || bot_command_add("spellinfo", "Opens a dialogue window with spell info", AccountStatus::Player, bot_spell_info_dialogue_window) || @@ -969,6 +970,7 @@ void SendSpellTypeWindow(Client* c, const Seperator* sep) { #include "bot_commands/spell_min_mana_pct.cpp" #include "bot_commands/spell_min_thresholds.cpp" #include "bot_commands/spell_pursue_priority.cpp" +#include "bot_commands/spell_resist_limits.cpp" #include "bot_commands/spell_target_count.cpp" #include "bot_commands/spelltypes.cpp" #include "bot_commands/summon.cpp" diff --git a/zone/bot_command.h b/zone/bot_command.h index 553f098c4..6304fb194 100644 --- a/zone/bot_command.h +++ b/zone/bot_command.h @@ -1095,6 +1095,7 @@ void bot_command_spell_min_hp_pct(Client* c, const Seperator* sep); void bot_command_spell_min_mana_pct(Client* c, const Seperator* sep); void bot_command_spell_min_thresholds(Client* c, const Seperator* sep); void bot_command_spell_pursue_priority(Client* c, const Seperator* sep); +void bot_command_spell_resist_limits(Client* c, const Seperator* sep); void bot_command_spell_target_count(Client* c, const Seperator* sep); void bot_command_spell_list(Client* c, const Seperator *sep); void bot_command_spell_settings_add(Client* c, const Seperator *sep); diff --git a/zone/bot_commands/spell_resist_limits.cpp b/zone/bot_commands/spell_resist_limits.cpp new file mode 100644 index 000000000..e7c928c33 --- /dev/null +++ b/zone/bot_commands/spell_resist_limits.cpp @@ -0,0 +1,214 @@ +#include "../bot_command.h" + +void bot_command_spell_resist_limits(Client* c, const Seperator* sep) { + if (helper_command_alias_fail(c, "bot_command_spell_resist_limits", sep->arg[0], "spellresistlimits")) { + c->Message(Chat::White, "note: Sets the limit of a target's resists to where a bot won't attempt to cast due to resist chances."); + + return; + } + + if (helper_is_help_or_usage(sep->arg[1])) { + BotCommandHelpParams p; + + p.description = { "Sets the limit of a target's resists to where a bot won't attempt to cast due to resist chances." }; + p.example_format = + { + fmt::format("{} [Type Shortname] [value] [actionable]", sep->arg[0]), + fmt::format("{} [Type ID] [value] [actionable]", sep->arg[0]) + }; + p.examples_one = + { + "To set all bots' slow resist limit to 250:", + fmt::format( + "{} {} 250 spawned", + sep->arg[0], + Bot::GetSpellTypeShortNameByID(BotSpellTypes::Slow) + ), + fmt::format( + "{} {} 250 spawned", + sep->arg[0], + BotSpellTypes::Slow + ) + }; + p.examples_two = + { + "To set Magicians to limit their resist to 175 for nukes:", + fmt::format( + "{} {} 175 byclass {}", + sep->arg[0], + Bot::GetSpellTypeShortNameByID(BotSpellTypes::Nuke), + Class::Magician + ), + fmt::format( + "{} {} 175 byclass {}", + sep->arg[0], + BotSpellTypes::Nuke, + Class::Magician + + ) + }; + p.examples_three = + { + "To check the current debuff resist limit on all bots:", + fmt::format( + "{} {} current spawned", + sep->arg[0], + Bot::GetSpellTypeShortNameByID(BotSpellTypes::Debuff) + ), + fmt::format( + "{} {} current spawned", + sep->arg[0], + BotSpellTypes::Debuff + ) + }; + p.actionables = { "target, byname, ownergroup, ownerraid, targetgroup, namesgroup, healrotationtargets, mmr, byclass, byrace, spawned" }; + + std::string popup_text = c->SendBotCommandHelpWindow(p); + popup_text = DialogueWindow::Table(popup_text); + + c->SendPopupToClient(sep->arg[0], popup_text.c_str()); + c->SendSpellTypePrompts(); + + if (RuleB(Bots, SendClassRaceOnHelp)) { + c->Message( + Chat::Yellow, + fmt::format( + "Use {} for information about race/class IDs.", + Saylink::Silent("^classracelist") + ).c_str() + ); + } + + return; + } + + std::string arg1 = sep->arg[1]; + std::string arg2 = sep->arg[2]; + int ab_arg = 2; + bool current_check = false; + uint16 spell_type = 0; + uint32 type_value = 0; + + // String/Int type checks + if (sep->IsNumber(1)) { + spell_type = atoi(sep->arg[1]); + + if (!EQ::ValueWithin(spell_type, BotSpellTypes::START, BotSpellTypes::END)) { + c->Message(Chat::Yellow, "You must choose a valid spell type. Spell types range from %i to %i", BotSpellTypes::START, BotSpellTypes::END); + + return; + } + } + else { + if (Bot::GetSpellTypeIDByShortName(arg1) != UINT16_MAX) { + spell_type = Bot::GetSpellTypeIDByShortName(arg1); + } + else { + c->Message( + Chat::Yellow, + fmt::format( + "Incorrect argument, use {} for information regarding this command.", + Saylink::Silent( + fmt::format("{} help", sep->arg[0]) + ) + ).c_str() + ); + + return; + } + } + + if (sep->IsNumber(2)) { + type_value = atoi(sep->arg[2]); + ++ab_arg; + + if (type_value < 0 || type_value > 1000) { + c->Message(Chat::Yellow, "You must enter a value between 1-1000."); + + return; + } + } + else if (!arg2.compare("current")) { + ++ab_arg; + current_check = true; + } + else { + c->Message( + Chat::Yellow, + fmt::format( + "Incorrect argument, use {} for information regarding this command.", + Saylink::Silent( + fmt::format("{} help", sep->arg[0]) + ) + ).c_str() + ); + + return; + } + + const int ab_mask = ActionableBots::ABM_Type1; + std::string class_race_arg = sep->arg[ab_arg]; + bool class_race_check = false; + if (!class_race_arg.compare("byclass") || !class_race_arg.compare("byrace")) { + class_race_check = true; + } + + std::vector sbl; + if (ActionableBots::PopulateSBL(c, sep->arg[ab_arg], sbl, ab_mask, !class_race_check ? sep->arg[ab_arg + 1] : nullptr, class_race_check ? atoi(sep->arg[ab_arg + 1]) : 0) == ActionableBots::ABT_None) { + return; + } + + sbl.erase(std::remove(sbl.begin(), sbl.end(), nullptr), sbl.end()); + + Bot* first_found = nullptr; + int success_count = 0; + for (auto my_bot : sbl) { + if (my_bot->BotPassiveCheck()) { + continue; + } + + if (!first_found) { + first_found = my_bot; + } + + if (current_check) { + c->Message( + Chat::Green, + fmt::format( + "{} says, 'My [{}] resist limit is currently [{}].'", + my_bot->GetCleanName(), + Bot::GetSpellTypeNameByID(spell_type), + my_bot->GetSpellTypeResistLimit(spell_type) + ).c_str() + ); + } + else { + my_bot->SetSpellTypeResistLimit(spell_type, type_value); + ++success_count; + } + } + if (!current_check) { + if (success_count == 1 && first_found) { + c->Message( + Chat::Green, + fmt::format( + "{} says, 'My [{}] resist limit was set to [{}].'", + first_found->GetCleanName(), + Bot::GetSpellTypeNameByID(spell_type), + first_found->GetSpellTypeResistLimit(spell_type) + ).c_str() + ); + } + else { + c->Message( + Chat::Green, + fmt::format( + "{} of your bots set their [{}] resist limit to [{}].", + success_count, + Bot::GetSpellTypeNameByID(spell_type), + type_value + ).c_str() + ); + } + } +}