From 1d4ba082adb8a98d1cfb4cb988bf084902d3741a Mon Sep 17 00:00:00 2001 From: nytmyr <53322305+nytmyr@users.noreply.github.com> Date: Thu, 6 Mar 2025 16:05:36 -0600 Subject: [PATCH] [Bots] Move commanded spell map to zone (#4755) - Moves the mapping of commanded spell min levels to zone rather than to each individual bot. - Adds support for sub spell types. --- common/spdat.h | 11 +++---- zone/bot.cpp | 2 -- zone/bot.h | 6 +--- zone/bot_command.cpp | 7 +++-- zone/bot_database.cpp | 70 +++++++++++++++++++++++++++++++++++++++++++ zone/bot_database.h | 9 +++++- zone/botspellsai.cpp | 49 ------------------------------ 7 files changed, 90 insertions(+), 64 deletions(-) diff --git a/common/spdat.h b/common/spdat.h index 661712ff5..ceb316a6a 100644 --- a/common/spdat.h +++ b/common/spdat.h @@ -736,11 +736,12 @@ namespace BotSpellTypes constexpr uint16 DiscUtility = 203; constexpr uint16 START = BotSpellTypes::Nuke; // Do not remove or change this - constexpr uint16 END = BotSpellTypes::PetResistBuffs; // Do not remove this, increment as needed - constexpr uint16 COMMANDED_START = BotSpellTypes::Lull; // Do not remove or change this - constexpr uint16 COMMANDED_END = BotSpellTypes::AELull; // Do not remove this, increment as needed - constexpr uint16 DISCIPLINE_START = BotSpellTypes::Discipline; // Do not remove or change this - constexpr uint16 DISCIPLINE_END = BotSpellTypes::DiscUtility; // Do not remove this, increment as needed + constexpr uint16 END = BotSpellTypes::PetResistBuffs; // Do not remove this, increment as needed + constexpr uint16 COMMANDED_START = BotSpellTypes::Lull; // Do not remove or change this + constexpr uint16 COMMANDED_END = BotSpellTypes::AELull; // Do not remove this, increment as needed + constexpr uint16 DISCIPLINE_START = BotSpellTypes::Discipline; // Do not remove or change this + constexpr uint16 DISCIPLINE_END = BotSpellTypes::DiscUtility; // Do not remove this, increment as needed + constexpr uint16 PARENT_TYPE_END = BotSpellTypes::PreCombatBuffSong; // This is the last ID of the original bot spell types, the rest are considered sub types. } static std::map spell_type_names = { diff --git a/zone/bot.cpp b/zone/bot.cpp index 85f9172b1..52a2d942f 100644 --- a/zone/bot.cpp +++ b/zone/bot.cpp @@ -3741,8 +3741,6 @@ bool Bot::Spawn(Client* botCharacterOwner) { } } - MapSpellTypeLevels(); - if (RuleB(Bots, RunSpellTypeChecksOnSpawn)) { OwnerMessage("Running SpellType checks. There may be some spells that are mislabeled as incorrect. Use this as a loose guideline."); CheckBotSpells(); //This runs through a series of checks and outputs any spells that are set to the wrong spell type in the database diff --git a/zone/bot.h b/zone/bot.h index ad9bbb18d..2d259edfa 100644 --- a/zone/bot.h +++ b/zone/bot.h @@ -685,11 +685,9 @@ public: // Spell lists void CheckBotSpells(); - void MapSpellTypeLevels(); - const std::map>& GetCommandedSpellTypesMinLevels() { return commanded_spells_min_level; } std::list GetSpellTypesPrioritized(uint8 priority_type); static uint16 GetParentSpellType(uint16 spell_type); - bool IsValidSpellTypeBySpellID(uint16 spell_type, uint16 spell_id); + static bool IsValidSpellTypeBySpellID(uint16 spell_type, uint16 spell_id); inline uint16 GetCastedSpellType() const { return _castedSpellType; } void SetCastedSpellType(uint16 spell_type); bool IsValidSpellTypeSubType(uint16 spell_type, uint16 sub_type, uint16 spell_id); @@ -1153,8 +1151,6 @@ protected: std::vector AIBot_spells_enforced; std::unordered_map> AIBot_spells_by_type; - std::map> commanded_spells_min_level; - std::vector bot_timers; std::vector bot_blocked_buffs; diff --git a/zone/bot_command.cpp b/zone/bot_command.cpp index 007ea1127..c8a216f8b 100644 --- a/zone/bot_command.cpp +++ b/zone/bot_command.cpp @@ -57,6 +57,7 @@ #include "water_map.h" #include "worldserver.h" #include "mob.h" +#include "bot_database.h" #include @@ -219,6 +220,8 @@ int bot_command_init(void) std::vector> injected_bot_command_settings; std::vector orphaned_bot_command_settings; + database.botdb.MapCommandedSpellTypeMinLevels(); + for (auto bcs_iter : bot_command_settings) { auto bcl_iter = bot_command_list.find(bcs_iter.first); @@ -796,10 +799,10 @@ void helper_send_usage_required_bots(Client *bot_owner, uint16 spell_type) } } - auto& spell_map = bot->GetCommandedSpellTypesMinLevels(); + auto spell_map = database.botdb.GetCommandedSpellTypesMinLevels(); if (spell_map.empty()) { - bot_owner->Message(Chat::Yellow, "No bots are capable of casting this spell type"); + bot_owner->Message(Chat::Yellow, "No bots are capable of casting this spell type."); return; } diff --git a/zone/bot_database.cpp b/zone/bot_database.cpp index 6ebc5d316..96309be9a 100644 --- a/zone/bot_database.cpp +++ b/zone/bot_database.cpp @@ -36,6 +36,7 @@ #include "../common/repositories/bot_pet_buffs_repository.h" #include "../common/repositories/bot_pet_inventories_repository.h" #include "../common/repositories/bot_spell_casting_chances_repository.h" +#include "../common/repositories/bot_spells_entries_repository.h" #include "../common/repositories/bot_settings_repository.h" #include "../common/repositories/bot_stances_repository.h" #include "../common/repositories/bot_timers_repository.h" @@ -2521,3 +2522,72 @@ bool BotDatabase::DeleteBotBlockedBuffs(const uint32 bot_id) return true; } + +void BotDatabase::MapCommandedSpellTypeMinLevels() { + commanded_spell_type_min_levels.clear(); + + auto start = std::min({ BotSpellTypes::START, BotSpellTypes::COMMANDED_START, BotSpellTypes::DISCIPLINE_START }); + auto end = std::max({ BotSpellTypes::END, BotSpellTypes::COMMANDED_END, BotSpellTypes::DISCIPLINE_END }); + + for (int i = start; i <= end; ++i) { + if (!Bot::IsValidBotSpellType(i)) { + continue; + } + + for (int x = Class::Warrior; x <= Class::Berserker; ++x) { + commanded_spell_type_min_levels[i][x] = {UINT8_MAX, "" }; + } + } + + auto spell_list = BotSpellsEntriesRepository::All(content_db); + + for (const auto& s : spell_list) { + if (!IsValidSpell(s.spell_id)) { + LogBotSpellTypeChecks("{} is an invalid spell", s.spell_id); + continue; + } + + auto spell = spells[s.spell_id]; + + if (spell.target_type == ST_Self) { + continue; + } + + int32_t bot_class = s.npc_spells_id - BOT_CLASS_BASE_ID_PREFIX; + + if ( + !EQ::ValueWithin(bot_class, Class::Warrior, Class::Berserker) || + !Bot::IsValidBotSpellType(s.type) + ) { + continue; + } + + for (int i = start; i <= end; ++i) { + if (s.minlevel > commanded_spell_type_min_levels[i][bot_class].min_level) { + continue; + } + + if ( + i > BotSpellTypes::PARENT_TYPE_END && + i != s.type && + Bot::GetParentSpellType(i) != s.type + ) { + continue; + } + + if (!Bot::IsValidSpellTypeBySpellID(i, s.spell_id)) { + continue; + } + + if (s.minlevel < commanded_spell_type_min_levels[i][bot_class].min_level) { + commanded_spell_type_min_levels[i][bot_class].min_level = s.minlevel; + commanded_spell_type_min_levels[i][bot_class].description = StringFormat( + "%s [#%u] - Level %u", + GetClassIDName(bot_class), + bot_class, + s.minlevel + ); + } + } + } +} diff --git a/zone/bot_database.h b/zone/bot_database.h index 61fdc6301..12e429540 100644 --- a/zone/bot_database.h +++ b/zone/bot_database.h @@ -24,7 +24,7 @@ #include #include #include - +#include "bot_structs.h" class Bot; class Client; @@ -130,6 +130,9 @@ public: bool SaveBotSettings(Mob* m); bool DeleteBotSettings(const uint32 bot_id); + void MapCommandedSpellTypeMinLevels(); + std::map> GetCommandedSpellTypesMinLevels() { return commanded_spell_type_min_levels; } + /* Bot group functions */ bool LoadGroupedBotsByGroupID(const uint32 owner_id, const uint32 group_id, std::list& group_list); @@ -211,6 +214,10 @@ public: private: std::string query; + + protected: + std::map> commanded_spell_type_min_levels; + }; #endif diff --git a/zone/botspellsai.cpp b/zone/botspellsai.cpp index 5508b1d37..b25ddc6a5 100644 --- a/zone/botspellsai.cpp +++ b/zone/botspellsai.cpp @@ -2994,52 +2994,3 @@ void Bot::CheckBotSpells() { } } } - -void Bot::MapSpellTypeLevels() { - commanded_spells_min_level.clear(); - - auto start = std::min({ BotSpellTypes::START, BotSpellTypes::COMMANDED_START, BotSpellTypes::DISCIPLINE_START }); - auto end = std::max({ BotSpellTypes::END, BotSpellTypes::COMMANDED_END, BotSpellTypes::DISCIPLINE_END }); - - for (int i = start; i <= end; ++i) { - if (!Bot::IsValidBotSpellType(i)) { - continue; - } - - for (int x = Class::Warrior; x <= Class::Berserker; ++x) { - commanded_spells_min_level[i][x] = { UINT8_MAX, "" }; - } - } - - auto spell_list = BotSpellsEntriesRepository::All(content_db); - - for (const auto& s : spell_list) { - if (!IsValidSpell(s.spell_id)) { - LogBotSpellTypeChecks("{} is an invalid spell", s.spell_id); - continue; - } - - uint16_t spell_type = s.type; - int32_t bot_class = s.npc_spells_id - BOT_CLASS_BASE_ID_PREFIX; - uint8_t min_level = s.minlevel; - - if ( - !EQ::ValueWithin(bot_class, Class::Warrior, Class::Berserker) || - !Bot::IsValidBotSpellType(spell_type) - ) { - continue; - } - - auto& spell_info = commanded_spells_min_level[spell_type][bot_class]; - - if (min_level < spell_info.min_level) { - spell_info.min_level = min_level; - spell_info.description = StringFormat( - "%s [#%u]: Level %u", - GetClassIDName(bot_class), - bot_class, - min_level - ); - } - } -}