From bd97453852ccb6d2671c15edbd1d54d5d5093fbb Mon Sep 17 00:00:00 2001 From: nytmyr <53322305+nytmyr@users.noreply.github.com> Date: Fri, 31 Jan 2025 14:31:48 -0600 Subject: [PATCH] Implement ^spellannouncecasts to toggle announcing casts of spell types --- .../database_update_manifest_bots.cpp | 1 + zone/bot.cpp | 63 +++++- zone/bot.h | 10 +- zone/bot_command.cpp | 2 + zone/bot_command.h | 1 + zone/bot_commands/bot_settings.cpp | 4 +- zone/bot_commands/copy_settings.cpp | 17 +- zone/bot_commands/default_settings.cpp | 35 ++- zone/bot_commands/depart.cpp | 20 +- zone/bot_commands/spell_announce_cast.cpp | 214 ++++++++++++++++++ zone/bot_structs.h | 1 + zone/botspellsai.cpp | 209 +++++++++-------- 12 files changed, 461 insertions(+), 116 deletions(-) create mode 100644 zone/bot_commands/spell_announce_cast.cpp diff --git a/common/database/database_update_manifest_bots.cpp b/common/database/database_update_manifest_bots.cpp index f73afd598..db1e269cf 100644 --- a/common/database/database_update_manifest_bots.cpp +++ b/common/database/database_update_manifest_bots.cpp @@ -274,6 +274,7 @@ UPDATE `bot_command_settings` SET `aliases`= 'resistlimits' WHERE `bot_command`= 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%'; +UPDATE `bot_command_settings` SET `aliases`= 'announcecasts' WHERE `bot_command`='spellannouncecasts'; )" }, ManifestEntry{ diff --git a/zone/bot.cpp b/zone/bot.cpp index c690b26aa..def50981e 100644 --- a/zone/bot.cpp +++ b/zone/bot.cpp @@ -7915,6 +7915,7 @@ void Bot::RaidGroupSay(Mob* speaker, const char* msg, ...) { if (speaker->IsRaidGrouped()) { Raid* r = speaker->CastToBot()->GetStoredRaid(); + if (r) { for (const auto& m : r->members) { if (m.member && !m.is_bot) { @@ -7932,6 +7933,7 @@ void Bot::RaidGroupSay(Mob* speaker, const char* msg, ...) { } else if (speaker->HasGroup()) { Group* g = speaker->GetGroup(); + if (g) { for (auto& m : g->members) { if (m && !m->IsBot()) { @@ -7948,7 +7950,6 @@ void Bot::RaidGroupSay(Mob* speaker, const char* msg, ...) { } } else { - //speaker->Say("%s", buf); speaker->GetOwner()->FilteredMessageString( speaker, Chat::PetResponse, @@ -10324,6 +10325,9 @@ void Bot::SetBotSetting(uint8 setting_type, uint16 bot_setting, int setting_valu case BotSettingCategories::SpellTypeAEOrGroupTargetCount: SetSpellTypeAEOrGroupTargetCount(bot_setting, setting_value); break; + case BotSettingCategories::SpellTypeAnnounceCast: + SetSpellTypeAnnounceCast(bot_setting, setting_value); + break; } } @@ -10515,6 +10519,7 @@ void Bot::LoadDefaultBotSettings() { bot_stance ); t.ae_or_group_target_count = GetDefaultSpellTypeAEOrGroupTargetCount(i, bot_stance); + t.announce_cast = GetDefaultSpellTypeAnnounceCast(i, bot_stance); t.recast_timer.Start(); m_bot_spell_settings.push_back(t); @@ -10522,7 +10527,8 @@ void Bot::LoadDefaultBotSettings() { LogBotSettingsDetail("{} says, 'Setting defaults for {} ({}) [#{}] - [{} [#{}] stance]'", GetCleanName(), t.name, t.short_name, t.spell_type, Stance::GetName(bot_stance), bot_stance); LogBotSettingsDetail("{} says, 'Hold = [{}] | Delay = [{}ms] | MinThreshold = [{}\%] | MaxThreshold = [{}\%]'", GetCleanName(), GetDefaultSpellHold(i, bot_stance), GetDefaultSpellDelay(i, bot_stance), GetDefaultSpellMinThreshold(i, bot_stance), GetDefaultSpellMaxThreshold(i, bot_stance)); LogBotSettingsDetail("{} says, 'AggroCheck = [{}] | MinManaPCT = [{}\%] | MaxManaPCT = [{}\%] | MinHPPCT = [{}\% | MaxHPPCT = [{}\%]'", GetCleanName(), GetDefaultSpellTypeAggroCheck(i, bot_stance), GetDefaultSpellTypeMinManaLimit(i, bot_stance), GetDefaultSpellTypeMaxManaLimit(i, bot_stance), GetDefaultSpellTypeMinHPLimit(i, bot_stance), GetDefaultSpellTypeMaxHPLimit(i, bot_stance)); - LogBotSettingsDetail("{} says, 'IdlePriority = [{}] | EngagedPriority = [{}] | PursuePriority = [{}] | ae_or_group_target_count = [{}]'", GetCleanName(), GetDefaultSpellTypeIdlePriority(i, GetClass(), bot_stance), GetDefaultSpellTypeEngagedPriority(i, GetClass(), bot_stance), GetDefaultSpellTypePursuePriority(i, GetClass(), bot_stance), GetDefaultSpellTypeAEOrGroupTargetCount(i, bot_stance)); + LogBotSettingsDetail("{} says, 'IdlePriority = [{}] | EngagedPriority = [{}] | PursuePriority = [{}]'", GetCleanName(), GetDefaultSpellTypeIdlePriority(i, GetClass(), bot_stance), GetDefaultSpellTypeEngagedPriority(i, GetClass(), bot_stance), GetDefaultSpellTypePursuePriority(i, GetClass(), bot_stance)); + LogBotSettingsDetail("{} says, 'TargetCount = [{}] | AnnounceCast = [{}]'", GetCleanName(), GetDefaultSpellTypeAEOrGroupTargetCount(i, bot_stance), GetDefaultSpellTypeAnnounceCast(i, bot_stance)); } } @@ -10638,6 +10644,8 @@ int Bot::GetDefaultSetting(uint16 setting_category, uint16 setting_type, uint8 s return GetDefaultSpellTypePriority(setting_type, BotPriorityCategories::Pursue, GetClass(), stance); case BotSettingCategories::SpellTypeAEOrGroupTargetCount: return GetDefaultSpellTypeAEOrGroupTargetCount(setting_type, stance); + case BotSettingCategories::SpellTypeAnnounceCast: + return GetDefaultSpellTypeAnnounceCast(setting_type, stance); default: break; } @@ -10675,6 +10683,8 @@ int Bot::GetSetting(uint16 setting_category, uint16 setting_type) { return GetSpellTypePriority(setting_type, BotPriorityCategories::Pursue); case BotSettingCategories::SpellTypeAEOrGroupTargetCount: return GetSpellTypeAEOrGroupTargetCount(setting_type); + case BotSettingCategories::SpellTypeAnnounceCast: + return GetSpellTypeAnnounceCast(setting_type); default: break; } @@ -11123,6 +11133,29 @@ uint16 Bot::GetDefaultSpellTypeAEOrGroupTargetCount(uint16 spell_type, uint8 sta return 1; } +uint16 Bot::GetDefaultSpellTypeAnnounceCast(uint16 spell_type, uint8 stance) { + switch (GetClass()) { + case Class::Bard: + switch (spell_type) { + case BotSpellTypes::Mez: + case BotSpellTypes::AEMez: + case BotSpellTypes::Cure: + case BotSpellTypes::GroupCures: + case BotSpellTypes::PetCures: + case BotSpellTypes::Charm: + return 1; + default: + return 0; + } + + return 1; + default: + return 1; + } + + return 1; +} + bool Bot::GetUltimateSpellHold(uint16 spell_type, Mob* tar) { if (!tar) { return GetSpellHold(spell_type); @@ -12112,6 +12145,17 @@ void Bot::CopySettings(Bot* to, uint8 setting_type, uint16 spell_type) { } } + break; + case BotSettingCategories::SpellTypeResistLimit: + if (spell_type != UINT16_MAX) { + to->SetSpellTypeResistLimit(spell_type, GetSpellTypeResistLimit(spell_type)); + } + else { + for (uint16 i = BotSpellTypes::START; i <= BotSpellTypes::END; ++i) { + to->SetSpellTypeResistLimit(i, GetSpellTypeResistLimit(i)); + } + } + break; case BotSettingCategories::SpellTypeMinManaPct: if (spell_type != UINT16_MAX) { @@ -12200,6 +12244,17 @@ void Bot::CopySettings(Bot* to, uint8 setting_type, uint16 spell_type) { } } + break; + case BotSettingCategories::SpellTypeAnnounceCast: + if (spell_type != UINT16_MAX) { + to->SetSpellTypeAnnounceCast(spell_type, GetSpellTypeAnnounceCast(spell_type)); + } + else { + for (uint16 i = BotSpellTypes::START; i <= BotSpellTypes::END; ++i) { + to->SetSpellTypeAnnounceCast(i, GetSpellTypeAnnounceCast(i)); + } + } + break; } } @@ -12723,11 +12778,11 @@ uint16 Bot::GetSpellTypeIDByShortName(std::string spell_type_string) { } bool Bot::IsValidBotSpellCategory(uint8 setting_type) { - return EQ::ValueWithin(setting_type, BotSettingCategories::START, BotSettingCategories::END_FULL); + return EQ::ValueWithin(setting_type, BotSettingCategories::START, BotSettingCategories::END); } std::string Bot::GetBotSpellCategoryName(uint8 setting_type) { - return Bot::IsValidBotBaseSetting(setting_type) ? botSpellCategory_names[setting_type] : "UNKNOWN CATEGORY"; + return Bot::IsValidBotSpellCategory(setting_type) ? botSpellCategory_names[setting_type] : "UNKNOWN CATEGORY"; } uint16 Bot::GetBotSpellCategoryIDByShortName(std::string setting_string) { diff --git a/zone/bot.h b/zone/bot.h index e4ef97216..4d779534b 100644 --- a/zone/bot.h +++ b/zone/bot.h @@ -109,7 +109,7 @@ namespace BotSettingCategories { // Update GetBotSpellCategoryName as needed constexpr uint8 SpellTypeEngagedPriority = 12; constexpr uint8 SpellTypePursuePriority = 13; constexpr uint8 SpellTypeAEOrGroupTargetCount = 14; - constexpr uint8 SpellTypeRecastDelay = 15; + constexpr uint8 SpellTypeAnnounceCast = 15; constexpr uint16 START = BotSettingCategories::BaseSetting; constexpr uint16 START_NO_BASE = BotSettingCategories::SpellHold; @@ -117,6 +117,7 @@ namespace BotSettingCategories { // Update GetBotSpellCategoryName as needed constexpr uint16 END_CLIENT = BotSettingCategories::SpellMaxThreshold; constexpr uint16 END = BotSettingCategories::SpellTypeAEOrGroupTargetCount; constexpr uint16 END_FULL = BotSettingCategories::SpellTypeRecastDelay; + constexpr uint16 END = BotSettingCategories::SpellTypeAnnounceCast; }; static std::map botSpellCategory_names = { @@ -135,7 +136,7 @@ static std::map botSpellCategory_names = { { BotSettingCategories::SpellTypeEngagedPriority, "SpellEngagedPriority" }, { BotSettingCategories::SpellTypePursuePriority, "SpellPursuePriority" }, { BotSettingCategories::SpellTypeAEOrGroupTargetCount, "SpellTargetCounts" }, - { BotSettingCategories::SpellTypeRecastDelay, "SpellRecastDelay" } + { BotSettingCategories::SpellTypeAnnounceCast, "SpellAnnounceCasts" } }; namespace BotPriorityCategories { // Update GetBotSpellCategoryName as needed @@ -582,6 +583,7 @@ public: uint8 GetDefaultSpellTypeMaxManaLimit(uint16 spell_type, uint8 stance = Stance::Balanced); uint8 GetDefaultSpellTypeMinHPLimit(uint16 spell_type, uint8 stance = Stance::Balanced); uint8 GetDefaultSpellTypeMaxHPLimit(uint16 spell_type, uint8 stance = Stance::Balanced); + uint16 GetDefaultSpellTypeAnnounceCast(uint16 spell_type, uint8 stance = Stance::Balanced); uint16 GetDefaultSpellTypeAEOrGroupTargetCount(uint16 spell_type, uint8 stance = Stance::Balanced); static bool IsValidBotBaseSetting(uint16 setting_type); @@ -627,6 +629,8 @@ public: inline void SetSpellTypeMaxHPLimit(uint16 spell_type, uint8 value) { m_bot_spell_settings[spell_type].max_hp_pct = value; } inline uint16 GetSpellTypeAEOrGroupTargetCount(uint16 spell_type) const { return m_bot_spell_settings[spell_type].ae_or_group_target_count; } inline void SetSpellTypeAEOrGroupTargetCount(uint16 spell_type, uint16 value) { m_bot_spell_settings[spell_type].ae_or_group_target_count = value; } + inline uint16 GetSpellTypeAnnounceCast(uint16 spell_type) const { return m_bot_spell_settings[spell_type].announce_cast; } + inline void SetSpellTypeAnnounceCast(uint16 spell_type, uint16 value) { m_bot_spell_settings[spell_type].announce_cast = value; } inline uint16 GetSpellDelay(uint16 spell_type) const { return m_bot_spell_settings[spell_type].delay; } inline void SetSpellDelay(uint16 spell_type, uint16 delay_value) { m_bot_spell_settings[spell_type].delay = delay_value; } inline uint8 GetSpellMinThreshold(uint16 spell_type) const { return m_bot_spell_settings[spell_type].min_threshold; } @@ -671,7 +675,7 @@ public: inline uint16 GetCastedSpellType() const { return _castedSpellType; } void SetCastedSpellType(uint16 spell_type); bool IsValidSpellTypeSubType(uint16 spell_type, uint16 sub_type, uint16 spell_id); - bool IsValidBotSpellCategory(uint8 setting_type); + static bool IsValidBotSpellCategory(uint8 setting_type); static std::string GetBotSpellCategoryName(uint8 setting_type); static uint16 GetBotSpellCategoryIDByShortName(std::string setting_string); void AssignBotSpellsToTypes(std::vector& AIBot_spells, std::unordered_map>& AIBot_spells_by_type); diff --git a/zone/bot_command.cpp b/zone/bot_command.cpp index bc5c8c45d..cd82dba6f 100644 --- a/zone/bot_command.cpp +++ b/zone/bot_command.cpp @@ -180,6 +180,7 @@ int bot_command_init(void) bot_command_add("sitincombat", "Toggles whether or a not a bot will attempt to med or sit to heal in combat", AccountStatus::Player, bot_command_sit_in_combat) || bot_command_add("sitmanapercent", "Mana threshold for a bot to start sitting in combat if allowed", AccountStatus::Player, bot_command_sit_mana_percent) || bot_command_add("spellaggrochecks", "Toggles whether or not bots will cast a spell type if they think it will get them aggro", AccountStatus::Player, bot_command_spell_aggro_checks) || + bot_command_add("spellannouncecasts", "Turn on or off cast announcements by spell type", AccountStatus::Player, bot_command_spell_announce_cast) || bot_command_add("spellengagedpriority", "Controls the order of casts by spell type when engaged in combat", AccountStatus::Player, bot_command_spell_engaged_priority) || bot_command_add("spelldelays", "Controls the delay between casts for a specific spell type", AccountStatus::Player, bot_command_spell_delays) || bot_command_add("spellholds", "Controls whether a bot holds the specified spell type or not", AccountStatus::Player, bot_command_spell_holds) || @@ -959,6 +960,7 @@ void SendSpellTypeWindow(Client* c, const Seperator* sep) { #include "bot_commands/sit_mana_percent.cpp" #include "bot_commands/spell.cpp" #include "bot_commands/spell_aggro_checks.cpp" +#include "bot_commands/spell_announce_cast.cpp" #include "bot_commands/spell_delays.cpp" #include "bot_commands/spell_engaged_priority.cpp" #include "bot_commands/spell_holds.cpp" diff --git a/zone/bot_command.h b/zone/bot_command.h index 6304fb194..77d449c44 100644 --- a/zone/bot_command.h +++ b/zone/bot_command.h @@ -1084,6 +1084,7 @@ void bot_command_sit_hp_percent(Client* c, const Seperator* sep); void bot_command_sit_in_combat(Client* c, const Seperator* sep); void bot_command_sit_mana_percent(Client* c, const Seperator* sep); void bot_command_spell_aggro_checks(Client* c, const Seperator* sep); +void bot_command_spell_announce_cast(Client* c, const Seperator* sep); void bot_command_spell_delays(Client* c, const Seperator* sep); void bot_command_spell_engaged_priority(Client* c, const Seperator* sep); void bot_command_spell_holds(Client* c, const Seperator* sep); diff --git a/zone/bot_commands/bot_settings.cpp b/zone/bot_commands/bot_settings.cpp index 8552afc36..98c4dccd7 100644 --- a/zone/bot_commands/bot_settings.cpp +++ b/zone/bot_commands/bot_settings.cpp @@ -19,7 +19,8 @@ void bot_command_bot_settings(Client* c, const Seperator* sep) "sithppercent", "sitincombat", "sitmanapercent", - "spellaggrocheck", + "spellaggrochecks", + "spellannouncecasts", "spelldelays", "spellengagedpriority", "spellholds", @@ -31,6 +32,7 @@ void bot_command_bot_settings(Client* c, const Seperator* sep) "spellminmanapct", "spellminthresholds", "spellpursuepriority", + "spellresistlimits", "spelltargetcount", "spelllist", "stance", diff --git a/zone/bot_commands/copy_settings.cpp b/zone/bot_commands/copy_settings.cpp index 66564926a..49b2c6d2e 100644 --- a/zone/bot_commands/copy_settings.cpp +++ b/zone/bot_commands/copy_settings.cpp @@ -52,7 +52,7 @@ void bot_command_copy_settings(Client* c, const Seperator* sep) ), }; p.actionables = { "target, byname, ownergroup, ownerraid, targetgroup, namesgroup, healrotationtargets, mmr, byclass, byrace, spawned" }; - p.options = { "all, misc, spellsettings, spelltypesettings, spellholds, spelldelays, spellminthresholds, spellmaxthresholds, spellminmanapct, spellmaxmanapct, spellminhppct, spellmaxhppct, spellidlepriority, spellengagedpriority, spellpursuepriority, spellaggrochecks, spelltargetcounts, spellresistlimits, blockedbuffs, blockedpetbuffs" }; + p.options = { "all, misc, spellsettings, spelltypesettings, spellholds, spelldelays, spellminthresholds, spellmaxthresholds, spellminmanapct, spellmaxmanapct, spellminhppct, spellmaxhppct, spellidlepriority, spellengagedpriority, spellpursuepriority, spellaggrochecks, spelltargetcounts, spellresistlimits, spellannouncecasts, blockedbuffs, blockedpetbuffs" }; p.options_one = { "[spellsettings] will copy ^spellsettings options", @@ -111,6 +111,7 @@ void bot_command_copy_settings(Client* c, const Seperator* sep) "spellaggrochecks", "spelltargetcounts", "spellresistlimits", + "spellannouncecasts", "blockedbuffs", "blockedpetbuffs" }; @@ -255,14 +256,16 @@ void bot_command_copy_settings(Client* c, const Seperator* sep) from->CopySettings(to, BotSettingCategories::SpellMinThreshold, spell_type); from->CopySettings(to, BotSettingCategories::SpellMaxThreshold, spell_type); from->CopySettings(to, BotSettingCategories::SpellTypeAggroCheck, spell_type); + from->CopySettings(to, BotSettingCategories::SpellTypeResistLimit, spell_type); from->CopySettings(to, BotSettingCategories::SpellTypeMinManaPct, spell_type); from->CopySettings(to, BotSettingCategories::SpellTypeMaxManaPct, spell_type); from->CopySettings(to, BotSettingCategories::SpellTypeMinHPPct, spell_type); from->CopySettings(to, BotSettingCategories::SpellTypeMaxHPPct, spell_type); from->CopySettings(to, BotSettingCategories::SpellTypeIdlePriority, spell_type); from->CopySettings(to, BotSettingCategories::SpellTypeEngagedPriority, spell_type); - from->CopySettings(to, BotSettingCategories::SpellTypePursuePriority, spell_type); + from->CopySettings(to, BotSettingCategories::SpellTypePursuePriority, spell_type); from->CopySettings(to, BotSettingCategories::SpellTypeAEOrGroupTargetCount, spell_type); + from->CopySettings(to, BotSettingCategories::SpellTypeAnnounceCast, spell_type); } else { for (uint16 i = BotSpellTypes::START; i <= BotSpellTypes::END; ++i) { @@ -271,14 +274,16 @@ void bot_command_copy_settings(Client* c, const Seperator* sep) from->CopySettings(to, BotSettingCategories::SpellMinThreshold, i); from->CopySettings(to, BotSettingCategories::SpellMaxThreshold, i); from->CopySettings(to, BotSettingCategories::SpellTypeAggroCheck, i); + from->CopySettings(to, BotSettingCategories::SpellTypeResistLimit, i); from->CopySettings(to, BotSettingCategories::SpellTypeMinManaPct, i); from->CopySettings(to, BotSettingCategories::SpellTypeMaxManaPct, i); from->CopySettings(to, BotSettingCategories::SpellTypeMinHPPct, i); from->CopySettings(to, BotSettingCategories::SpellTypeMaxHPPct, i); from->CopySettings(to, BotSettingCategories::SpellTypeIdlePriority, i); from->CopySettings(to, BotSettingCategories::SpellTypeEngagedPriority, i); - from->CopySettings(to, BotSettingCategories::SpellTypePursuePriority, i); + from->CopySettings(to, BotSettingCategories::SpellTypePursuePriority, i); from->CopySettings(to, BotSettingCategories::SpellTypeAEOrGroupTargetCount, i); + from->CopySettings(to, BotSettingCategories::SpellTypeAnnounceCast, i); } } @@ -299,14 +304,16 @@ void bot_command_copy_settings(Client* c, const Seperator* sep) from->CopySettings(to, BotSettingCategories::SpellMinThreshold, spell_type); from->CopySettings(to, BotSettingCategories::SpellMaxThreshold, spell_type); from->CopySettings(to, BotSettingCategories::SpellTypeAggroCheck, spell_type); + from->CopySettings(to, BotSettingCategories::SpellTypeResistLimit, spell_type); from->CopySettings(to, BotSettingCategories::SpellTypeMinManaPct, spell_type); from->CopySettings(to, BotSettingCategories::SpellTypeMaxManaPct, spell_type); from->CopySettings(to, BotSettingCategories::SpellTypeMinHPPct, spell_type); from->CopySettings(to, BotSettingCategories::SpellTypeMaxHPPct, spell_type); from->CopySettings(to, BotSettingCategories::SpellTypeIdlePriority, spell_type); from->CopySettings(to, BotSettingCategories::SpellTypeEngagedPriority, spell_type); - from->CopySettings(to, BotSettingCategories::SpellTypePursuePriority, spell_type); + from->CopySettings(to, BotSettingCategories::SpellTypePursuePriority, spell_type); from->CopySettings(to, BotSettingCategories::SpellTypeAEOrGroupTargetCount, spell_type); + from->CopySettings(to, BotSettingCategories::SpellTypeAnnounceCast, spell_type); } else { for (uint16 i = BotSpellTypes::START; i <= BotSpellTypes::END; ++i) { @@ -315,6 +322,7 @@ void bot_command_copy_settings(Client* c, const Seperator* sep) from->CopySettings(to, BotSettingCategories::SpellMinThreshold, i); from->CopySettings(to, BotSettingCategories::SpellMaxThreshold, i); from->CopySettings(to, BotSettingCategories::SpellTypeAggroCheck, i); + from->CopySettings(to, BotSettingCategories::SpellTypeResistLimit, i); from->CopySettings(to, BotSettingCategories::SpellTypeMinManaPct, i); from->CopySettings(to, BotSettingCategories::SpellTypeMaxManaPct, i); from->CopySettings(to, BotSettingCategories::SpellTypeMinHPPct, i); @@ -323,6 +331,7 @@ void bot_command_copy_settings(Client* c, const Seperator* sep) from->CopySettings(to, BotSettingCategories::SpellTypeEngagedPriority, i); from->CopySettings(to, BotSettingCategories::SpellTypePursuePriority, i); from->CopySettings(to, BotSettingCategories::SpellTypeAEOrGroupTargetCount, i); + from->CopySettings(to, BotSettingCategories::SpellTypeAnnounceCast, i); } } diff --git a/zone/bot_commands/default_settings.cpp b/zone/bot_commands/default_settings.cpp index 36fc7a060..7c22312c0 100644 --- a/zone/bot_commands/default_settings.cpp +++ b/zone/bot_commands/default_settings.cpp @@ -38,7 +38,7 @@ void bot_command_default_settings(Client* c, const Seperator* sep) ) }; p.actionables = { "target, byname, ownergroup, ownerraid, targetgroup, namesgroup, healrotationtargets, mmr, byclass, byrace, spawned" }; - p.options = { "all, misc, spellsettings, spelltypesettings, spellholds, spelldelays, spellminthresholds, spellmaxthresholds, spellminmanapct, spellmaxmanapct, spellminhppct, spellmaxhppct, spellidlepriority, spellengagedpriority, spellpursuepriority, spellaggrocheck, spelltargetcounts, spellresistlimits" }; + p.options = { "all, misc, spellsettings, spelltypesettings, spellholds, spelldelays, spellminthresholds, spellmaxthresholds, spellminmanapct, spellmaxmanapct, spellminhppct, spellmaxhppct, spellidlepriority, spellengagedpriority, spellpursuepriority, spellaggrocheck, spelltargetcounts, spellresistlimits, spellannouncecasts" }; p.options_one = { "[spellsettings] will restore ^spellsettings options", @@ -100,7 +100,8 @@ void bot_command_default_settings(Client* c, const Seperator* sep) "spellpursuepriority", "spellaggrochecks", "spelltargetcounts", - "spellresistlimits" + "spellresistlimits", + "spellannouncecasts" }; if (sep->IsNumber(spell_type_arg_int)) { @@ -223,6 +224,7 @@ void bot_command_default_settings(Client* c, const Seperator* sep) my_bot->SetSpellMinThreshold(spell_type, my_bot->GetDefaultSpellMinThreshold(spell_type, bot_stance)); my_bot->SetSpellMaxThreshold(spell_type, my_bot->GetDefaultSpellMaxThreshold(spell_type, bot_stance)); my_bot->SetSpellTypeAggroCheck(spell_type, my_bot->GetDefaultSpellTypeAggroCheck(spell_type, bot_stance)); + my_bot->SetSpellTypeResistLimit(spell_type, my_bot->GetDefaultSpellTypeResistLimit(spell_type, bot_stance)); my_bot->SetSpellTypeMinManaLimit(spell_type, my_bot->GetDefaultSpellTypeMinManaLimit(spell_type, bot_stance)); my_bot->SetSpellTypeMaxManaLimit(spell_type, my_bot->GetDefaultSpellTypeMaxManaLimit(spell_type, bot_stance)); my_bot->SetSpellTypeMinHPLimit(spell_type, my_bot->GetDefaultSpellTypeMinHPLimit(spell_type, bot_stance)); @@ -231,6 +233,7 @@ void bot_command_default_settings(Client* c, const Seperator* sep) my_bot->SetSpellTypePriority(spell_type, BotPriorityCategories::Engaged, my_bot->GetDefaultSpellTypePriority(spell_type, BotPriorityCategories::Engaged, my_bot->GetClass(), bot_stance)); my_bot->SetSpellTypePriority(spell_type, BotPriorityCategories::Pursue, my_bot->GetDefaultSpellTypePriority(spell_type, BotPriorityCategories::Pursue, my_bot->GetClass(), bot_stance)); my_bot->SetSpellTypeAEOrGroupTargetCount(spell_type, my_bot->GetDefaultSpellTypeAEOrGroupTargetCount(spell_type, bot_stance)); + my_bot->SetSpellTypeAnnounceCast(spell_type, my_bot->GetDefaultSpellTypeAnnounceCast(spell_type, bot_stance)); } else { for (uint16 i = BotSpellTypes::START; i <= BotSpellTypes::END; ++i) { @@ -239,6 +242,7 @@ void bot_command_default_settings(Client* c, const Seperator* sep) my_bot->SetSpellMinThreshold(i, my_bot->GetDefaultSpellMinThreshold(i, bot_stance)); my_bot->SetSpellMaxThreshold(i, my_bot->GetDefaultSpellMaxThreshold(i, bot_stance)); my_bot->SetSpellTypeAggroCheck(i, my_bot->GetDefaultSpellTypeAggroCheck(i, bot_stance)); + my_bot->SetSpellTypeResistLimit(i, my_bot->GetDefaultSpellTypeResistLimit(i, bot_stance)); my_bot->SetSpellTypeMinManaLimit(i, my_bot->GetDefaultSpellTypeMinManaLimit(i, bot_stance)); my_bot->SetSpellTypeMaxManaLimit(i, my_bot->GetDefaultSpellTypeMaxManaLimit(i, bot_stance)); my_bot->SetSpellTypeMinHPLimit(i, my_bot->GetDefaultSpellTypeMinHPLimit(i, bot_stance)); @@ -247,6 +251,7 @@ void bot_command_default_settings(Client* c, const Seperator* sep) my_bot->SetSpellTypePriority(i, BotPriorityCategories::Engaged, my_bot->GetDefaultSpellTypePriority(i, BotPriorityCategories::Engaged, my_bot->GetClass(), bot_stance)); my_bot->SetSpellTypePriority(i, BotPriorityCategories::Pursue, my_bot->GetDefaultSpellTypePriority(i, BotPriorityCategories::Pursue, my_bot->GetClass(), bot_stance)); my_bot->SetSpellTypeAEOrGroupTargetCount(i, my_bot->GetDefaultSpellTypeAEOrGroupTargetCount(i, bot_stance)); + my_bot->SetSpellTypeAnnounceCast(i, my_bot->GetDefaultSpellTypeAnnounceCast(i, bot_stance)); } } @@ -263,6 +268,7 @@ void bot_command_default_settings(Client* c, const Seperator* sep) my_bot->SetSpellMinThreshold(i, my_bot->GetDefaultSpellMinThreshold(i, bot_stance)); my_bot->SetSpellMaxThreshold(i, my_bot->GetDefaultSpellMaxThreshold(i, bot_stance)); my_bot->SetSpellTypeAggroCheck(i, my_bot->GetDefaultSpellTypeAggroCheck(i, bot_stance)); + my_bot->SetSpellTypeResistLimit(i, my_bot->GetDefaultSpellTypeResistLimit(i, bot_stance)); my_bot->SetSpellTypeMinManaLimit(i, my_bot->GetDefaultSpellTypeMinManaLimit(i, bot_stance)); my_bot->SetSpellTypeMaxManaLimit(i, my_bot->GetDefaultSpellTypeMaxManaLimit(i, bot_stance)); my_bot->SetSpellTypeMinHPLimit(i, my_bot->GetDefaultSpellTypeMinHPLimit(i, bot_stance)); @@ -271,6 +277,7 @@ void bot_command_default_settings(Client* c, const Seperator* sep) my_bot->SetSpellTypePriority(i, BotPriorityCategories::Engaged, my_bot->GetDefaultSpellTypePriority(i, BotPriorityCategories::Engaged, my_bot->GetClass(), bot_stance)); my_bot->SetSpellTypePriority(i, BotPriorityCategories::Pursue, my_bot->GetDefaultSpellTypePriority(i, BotPriorityCategories::Pursue, my_bot->GetClass(), bot_stance)); my_bot->SetSpellTypeAEOrGroupTargetCount(i, my_bot->GetDefaultSpellTypeAEOrGroupTargetCount(i, bot_stance)); + my_bot->SetSpellTypeAnnounceCast(i, my_bot->GetDefaultSpellTypeAnnounceCast(i, bot_stance)); }; my_bot->ResetBotSpellSettings(); @@ -339,6 +346,18 @@ void bot_command_default_settings(Client* c, const Seperator* sep) output = "aggro check settings"; } + else if (!strcasecmp(sep->arg[1], "resist limit")) { + if (spell_type != UINT16_MAX) { + my_bot->SetSpellTypeAEOrGroupTargetCount(spell_type, my_bot->GetDefaultSpellTypeAEOrGroupTargetCount(spell_type, bot_stance)); + } + else { + for (uint16 i = BotSpellTypes::START; i <= BotSpellTypes::END; ++i) { + my_bot->SetSpellTypeAEOrGroupTargetCount(i, my_bot->GetDefaultSpellTypeAEOrGroupTargetCount(i, bot_stance)); + } + } + + output = "resist limit settings"; + } else if (!strcasecmp(sep->arg[1], "minmanapct")) { if (spell_type != UINT16_MAX) { my_bot->SetSpellTypeMinManaLimit(spell_type, my_bot->GetDefaultSpellTypeMinManaLimit(spell_type, bot_stance)); @@ -435,6 +454,18 @@ void bot_command_default_settings(Client* c, const Seperator* sep) output = "target count settings"; } + else if (!strcasecmp(sep->arg[1], "announcecast")) { + if (spell_type != UINT16_MAX) { + my_bot->SetSpellTypeAEOrGroupTargetCount(spell_type, my_bot->GetDefaultSpellTypeAEOrGroupTargetCount(spell_type, bot_stance)); + } + else { + for (uint16 i = BotSpellTypes::START; i <= BotSpellTypes::END; ++i) { + my_bot->SetSpellTypeAEOrGroupTargetCount(i, my_bot->GetDefaultSpellTypeAEOrGroupTargetCount(i, bot_stance)); + } + } + + output = "announce cast settings"; + } my_bot->Save(); ++success_count; diff --git a/zone/bot_commands/depart.cpp b/zone/bot_commands/depart.cpp index cf5e101c5..82c7fee50 100644 --- a/zone/bot_commands/depart.cpp +++ b/zone/bot_commands/depart.cpp @@ -233,17 +233,15 @@ void bot_command_depart(Client* c, const Seperator* sep) bot_iter->SetCastedSpellType(BotSpellTypes::Teleport); } - if (bot_iter->GetClass() != Class::Bard || RuleB(Bots, BardsAnnounceCasts)) { - Bot::RaidGroupSay( - bot_iter, - fmt::format( - "Casting {} [{}] on {}.", - GetSpellName(itr->SpellId), - bot_iter->GetSpellTypeNameByID(BotSpellTypes::Teleport), - (tar == bot_iter ? "myself" : tar->GetCleanName()) - ).c_str() - ); - } + Bot::RaidGroupSay( + bot_iter, + fmt::format( + "Casting {} [{}] on {}.", + GetSpellName(itr->SpellId), + bot_iter->GetSpellTypeNameByID(BotSpellTypes::Teleport), + (tar == bot_iter ? "myself" : tar->GetCleanName()) + ).c_str() + ); is_success = true; } diff --git a/zone/bot_commands/spell_announce_cast.cpp b/zone/bot_commands/spell_announce_cast.cpp new file mode 100644 index 000000000..482599724 --- /dev/null +++ b/zone/bot_commands/spell_announce_cast.cpp @@ -0,0 +1,214 @@ +#include "../bot_command.h" + +void bot_command_spell_announce_cast(Client* c, const Seperator* sep) { + if (helper_command_alias_fail(c, "bot_command_spell_announce_cast", sep->arg[0], "spellannouncecasts")) { + c->Message(Chat::White, "note: Allows you to enable or disable cast announcements for bots by spell type."); + + return; + } + + if (helper_is_help_or_usage(sep->arg[1])) { + BotCommandHelpParams p; + + p.description = { "Allows you to enable or disable cast announcements for bots by spell type." }; + 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 to stop announcing dispels:", + fmt::format( + "{} {} 0 spawned", + sep->arg[0], + Bot::GetSpellTypeShortNameByID(BotSpellTypes::Dispel) + ), + fmt::format( + "{} {} 0 spawned", + sep->arg[0], + BotSpellTypes::Dispel + ) + }; + p.examples_two = + { + "To set Wizards to not announce nukes:", + fmt::format( + "{} {} 0 byclass {}", + sep->arg[0], + Bot::GetSpellTypeShortNameByID(BotSpellTypes::Nuke), + Class::Wizard + ), + fmt::format( + "{} {} 0 byclass {}", + sep->arg[0], + BotSpellTypes::Nuke, + Class::Wizard + + ) + }; + p.examples_three = + { + "To check the current announcement setting for debuffs:", + 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 != 1) { + c->Message(Chat::Yellow, "You must enter either 0 for [Disabled] or 1 for [Enabled]."); + + 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, 'I currently {} announce [{}] casts.'", + my_bot->GetCleanName(), + (my_bot->GetSpellTypeAnnounceCast(spell_type) ? "do" : "do not"), + Bot::GetSpellTypeNameByID(spell_type) + ).c_str() + ); + } + else { + my_bot->SetSpellTypeAnnounceCast(spell_type, type_value); + ++success_count; + } + } + if (!current_check) { + if (success_count == 1 && first_found) { + c->Message( + Chat::Green, + fmt::format( + "{} says, 'I will {} announce [{}] casts.'", + first_found->GetCleanName(), + (first_found->GetSpellTypeAnnounceCast(spell_type) ? "now" : "no longer"), + Bot::GetSpellTypeNameByID(spell_type) + ).c_str() + ); + } + else { + c->Message( + Chat::Green, + fmt::format( + "{} of your bots will {} announce [{}] casts.", + success_count, + (type_value ? "now" : "no longer"), + Bot::GetSpellTypeNameByID(spell_type) + ).c_str() + ); + } + } +} diff --git a/zone/bot_structs.h b/zone/bot_structs.h index 99662e83b..47ac30455 100644 --- a/zone/bot_structs.h +++ b/zone/bot_structs.h @@ -129,6 +129,7 @@ struct BotSpellSettings { uint16 engaged_priority; // engaged priority of the spell type uint16 pursue_priority; // pursue priority of the spell type uint16 ae_or_group_target_count; // require target count to cast an AE or Group spell type + uint16 announce_cast; // announce when casting a certain spell type Timer recast_timer; // recast timer based off delay }; diff --git a/zone/botspellsai.cpp b/zone/botspellsai.cpp index 211e9b83e..33709ef2b 100644 --- a/zone/botspellsai.cpp +++ b/zone/botspellsai.cpp @@ -257,18 +257,20 @@ bool Bot::AICastSpell(Mob* tar, uint8 chance, uint16 spell_type, uint16 sub_targ SetCastedSpellType(spell_type); } - if (bot_class != Class::Bard || RuleB(Bots, BardsAnnounceCasts)) { - RaidGroupSay( - this, - fmt::format( - "Casting {} [{}] on {}.", - GetSpellName(s.SpellId), - GetSpellTypeNameByID(spell_type), - (tar == this ? "myself" : tar->GetCleanName()) - ).c_str() - ); + if (!GetSpellTypeAnnounceCast(spell_type)) { + return true; } + RaidGroupSay( + this, + fmt::format( + "Casting {} [{}] on {}.", + GetSpellName(s.SpellId), + GetSpellTypeNameByID(spell_type), + (tar == this ? "myself" : tar->GetCleanName()) + ).c_str() + ); + return true; } } @@ -306,6 +308,10 @@ bool Bot::BotCastMez(Mob* tar, uint8 bot_class, BotSpell& bot_spell, uint16 spel SetCastedSpellType(spell_type); } + if (!GetSpellTypeAnnounceCast(spell_type)) { + return true; + } + RaidGroupSay( this, fmt::format( @@ -340,41 +346,47 @@ bool Bot::BotCastCure(Mob* tar, uint8 bot_class, BotSpell& bot_spell, uint16 spe } if (AIDoSpellCast(bot_spell.SpellIndex, tar, bot_spell.ManaCost)) { - if (bot_class != Class::Bard || RuleB(Bots, BardsAnnounceCasts)) { - if (IsGroupSpell(bot_spell.SpellId)) { - RaidGroupSay( - this, - fmt::format( - "Curing the group with {}.", - GetSpellName(bot_spell.SpellId) - ).c_str() - ); - + if (IsGroupSpell(bot_spell.SpellId)) { + if (!IsCommandedSpell()) { const std::vector v = GatherSpellTargets(false, tar); - if (!IsCommandedSpell()) { - for (Mob* m : v) { - SetBotSpellRecastTimer(spell_type, m, true); - } + for (Mob* m : v) { + SetBotSpellRecastTimer(spell_type, m, true); } } - else { - RaidGroupSay( - this, - fmt::format( - "Curing {} with {}.", - (tar == this ? "myself" : tar->GetCleanName()), - GetSpellName(bot_spell.SpellId) - ).c_str() - ); - - if (!IsCommandedSpell()) { - SetBotSpellRecastTimer(spell_type, tar, true); - } + if (!GetSpellTypeAnnounceCast(spell_type)) { return true; } + + RaidGroupSay( + this, + fmt::format( + "Curing the group with {}.", + GetSpellName(bot_spell.SpellId) + ).c_str() + ); } + else { + if (!IsCommandedSpell()) { + SetBotSpellRecastTimer(spell_type, tar, true); + } + + if (!GetSpellTypeAnnounceCast(spell_type)) { + return true; + } + + RaidGroupSay( + this, + fmt::format( + "Curing {} with {}.", + (tar == this ? "myself" : tar->GetCleanName()), + GetSpellName(bot_spell.SpellId) + ).c_str() + ); + } + + return true; } return false; @@ -417,6 +429,11 @@ bool Bot::BotCastPet(Mob* tar, uint8 bot_class, BotSpell& bot_spell, uint16 spel if (AIDoSpellCast(bot_spell.SpellIndex, tar, bot_spell.ManaCost)) { SetCastedSpellType(spell_type); + + if (!GetSpellTypeAnnounceCast(spell_type)) { + return true; + } + RaidGroupSay( this, fmt::format( @@ -473,18 +490,20 @@ bool Bot::BotCastNuke(Mob* tar, uint8 bot_class, BotSpell& bot_spell, uint16 spe if (AIDoSpellCast(s.SpellIndex, tar, s.ManaCost)) { SetCastedSpellType(spell_type); - if (bot_class != Class::Bard || RuleB(Bots, BardsAnnounceCasts)) { - RaidGroupSay( - this, - fmt::format( - "Casting {} [{}] on {}.", - GetSpellName(s.SpellId), - GetSpellTypeNameByID(spell_type), - tar->GetCleanName() - ).c_str() - ); + if (!GetSpellTypeAnnounceCast(spell_type)) { + return true; } + RaidGroupSay( + this, + fmt::format( + "Casting {} [{}] on {}.", + GetSpellName(s.SpellId), + GetSpellTypeNameByID(spell_type), + tar->GetCleanName() + ).c_str() + ); + return true; } } @@ -493,18 +512,20 @@ bool Bot::BotCastNuke(Mob* tar, uint8 bot_class, BotSpell& bot_spell, uint16 spe if (AIDoSpellCast(bot_spell.SpellIndex, tar, bot_spell.ManaCost)) { SetCastedSpellType(spell_type); - if (bot_class != Class::Bard || RuleB(Bots, BardsAnnounceCasts)) { - RaidGroupSay( - this, - fmt::format( - "Casting {} [{}] on {}.", - GetSpellName(bot_spell.SpellId), - GetSpellTypeNameByID(spell_type), - tar->GetCleanName() - ).c_str() - ); + if (!GetSpellTypeAnnounceCast(spell_type)) { + return true; } + RaidGroupSay( + this, + fmt::format( + "Casting {} [{}] on {}.", + GetSpellName(bot_spell.SpellId), + GetSpellTypeNameByID(spell_type), + tar->GetCleanName() + ).c_str() + ); + return true; } } @@ -520,45 +541,51 @@ bool Bot::BotCastHeal(Mob* tar, uint8 bot_class, BotSpell& bot_spell, uint16 spe } if (AIDoSpellCast(bot_spell.SpellIndex, tar, bot_spell.ManaCost)) { - if (bot_class != Class::Bard || RuleB(Bots, BardsAnnounceCasts)) { - if (IsGroupSpell(bot_spell.SpellId)) { - RaidGroupSay( - this, - fmt::format( - "Healing the group with {} [{}].", - GetSpellName(bot_spell.SpellId), - GetSpellTypeNameByID(spell_type) - ).c_str() - ); - - if (bot_class != Class::Bard) { + if (IsGroupSpell(bot_spell.SpellId)) { + if (bot_class != Class::Bard) { + if (!IsCommandedSpell()) { const std::vector v = GatherSpellTargets(false, tar); - if (!IsCommandedSpell()) { - for (Mob* m : v) { - SetBotSpellRecastTimer(spell_type, m, true); - } - } - } - - } - else { - RaidGroupSay( - this, - fmt::format( - "Healing {} with {} [{}].", - (tar == this ? "myself" : tar->GetCleanName()), - GetSpellName(bot_spell.SpellId), - GetSpellTypeNameByID(spell_type) - ).c_str() - ); - - if (bot_class != Class::Bard) { - if (!IsCommandedSpell()) { - SetBotSpellRecastTimer(spell_type, tar, true); + for (Mob* m : v) { + SetBotSpellRecastTimer(spell_type, m, true); } } } + + if (!GetSpellTypeAnnounceCast(spell_type)) { + return true; + } + + RaidGroupSay( + this, + fmt::format( + "Healing the group with {} [{}].", + GetSpellName(bot_spell.SpellId), + GetSpellTypeNameByID(spell_type) + ).c_str() + ); + + } + else { + if (bot_class != Class::Bard) { + if (!IsCommandedSpell()) { + SetBotSpellRecastTimer(spell_type, tar, true); + } + } + + if (!GetSpellTypeAnnounceCast(spell_type)) { + return true; + } + + RaidGroupSay( + this, + fmt::format( + "Healing {} with {} [{}].", + (tar == this ? "myself" : tar->GetCleanName()), + GetSpellName(bot_spell.SpellId), + GetSpellTypeNameByID(spell_type) + ).c_str() + ); } return true;