From 61b91d92c3aefd8de3f40a9f352ffa7a76c17e27 Mon Sep 17 00:00:00 2001 From: Aeadoin <109764533+Aeadoin@users.noreply.github.com> Date: Sat, 3 Dec 2022 12:15:57 -0500 Subject: [PATCH] [Bots] Expanded Bot Spell Settings List. (#2606) * [Bots] Expanded Bot Spell List Settings * [Bots] Expanded Bot Spell List Settings * Fixes/formatting * typo * Formatting & update SpellInfo Command * Spelling * Typo --- .../base/base_bot_data_repository.h | 394 +++++++++--------- .../base/base_bot_spell_settings_repository.h | 45 +- .../bot_spell_settings_repository.h | 8 +- .../2022_12_02_bot_spell_settings.sql | 5 + zone/bot.cpp | 121 +++--- zone/bot.h | 9 +- zone/bot_command.cpp | 193 +++++++-- zone/bot_command.h | 2 + zone/bot_database.cpp | 26 +- zone/bot_database.h | 1 + zone/botspellsai.cpp | 84 ++-- zone/npc.h | 2 + 12 files changed, 504 insertions(+), 386 deletions(-) create mode 100644 utils/sql/git/bots/required/2022_12_02_bot_spell_settings.sql diff --git a/common/repositories/base/base_bot_data_repository.h b/common/repositories/base/base_bot_data_repository.h index f51c467c5..657f156ea 100644 --- a/common/repositories/base/base_bot_data_repository.h +++ b/common/repositories/base/base_bot_data_repository.h @@ -67,6 +67,7 @@ public: uint32_t follow_distance; uint8_t stop_melee_level; int32_t expansion_bitmask; + uint8_t enforce_spell_settings; }; static std::string PrimaryKey() @@ -125,6 +126,7 @@ public: "follow_distance", "stop_melee_level", "expansion_bitmask", + "enforce_spell_settings", }; } @@ -179,6 +181,7 @@ public: "follow_distance", "stop_melee_level", "expansion_bitmask", + "enforce_spell_settings", }; } @@ -219,54 +222,55 @@ public: { BotData e{}; - e.bot_id = 0; - e.owner_id = 0; - e.spells_id = 0; - e.name = ""; - e.last_name = ""; - e.title = ""; - e.suffix = ""; - e.zone_id = 0; - e.gender = 0; - e.race = 0; - e.class_ = 0; - e.level = 0; - e.deity = 0; - e.creation_day = 0; - e.last_spawn = 0; - e.time_spawned = 0; - e.size = 0; - e.face = 1; - e.hair_color = 1; - e.hair_style = 1; - e.beard = 0; - e.beard_color = 1; - e.eye_color_1 = 1; - e.eye_color_2 = 1; - e.drakkin_heritage = 0; - e.drakkin_tattoo = 0; - e.drakkin_details = 0; - e.ac = 0; - e.atk = 0; - e.hp = 0; - e.mana = 0; - e.str = 75; - e.sta = 75; - e.cha = 75; - e.dex = 75; - e.int_ = 75; - e.agi = 75; - e.wis = 75; - e.fire = 0; - e.cold = 0; - e.magic = 0; - e.poison = 0; - e.disease = 0; - e.corruption = 0; - e.show_helm = 0; - e.follow_distance = 200; - e.stop_melee_level = 255; - e.expansion_bitmask = -1; + e.bot_id = 0; + e.owner_id = 0; + e.spells_id = 0; + e.name = ""; + e.last_name = ""; + e.title = ""; + e.suffix = ""; + e.zone_id = 0; + e.gender = 0; + e.race = 0; + e.class_ = 0; + e.level = 0; + e.deity = 0; + e.creation_day = 0; + e.last_spawn = 0; + e.time_spawned = 0; + e.size = 0; + e.face = 1; + e.hair_color = 1; + e.hair_style = 1; + e.beard = 0; + e.beard_color = 1; + e.eye_color_1 = 1; + e.eye_color_2 = 1; + e.drakkin_heritage = 0; + e.drakkin_tattoo = 0; + e.drakkin_details = 0; + e.ac = 0; + e.atk = 0; + e.hp = 0; + e.mana = 0; + e.str = 75; + e.sta = 75; + e.cha = 75; + e.dex = 75; + e.int_ = 75; + e.agi = 75; + e.wis = 75; + e.fire = 0; + e.cold = 0; + e.magic = 0; + e.poison = 0; + e.disease = 0; + e.corruption = 0; + e.show_helm = 0; + e.follow_distance = 200; + e.stop_melee_level = 255; + e.expansion_bitmask = -1; + e.enforce_spell_settings = 0; return e; } @@ -302,54 +306,55 @@ public: if (results.RowCount() == 1) { BotData e{}; - e.bot_id = static_cast(strtoul(row[0], nullptr, 10)); - e.owner_id = static_cast(strtoul(row[1], nullptr, 10)); - e.spells_id = static_cast(strtoul(row[2], nullptr, 10)); - e.name = row[3] ? row[3] : ""; - e.last_name = row[4] ? row[4] : ""; - e.title = row[5] ? row[5] : ""; - e.suffix = row[6] ? row[6] : ""; - e.zone_id = static_cast(atoi(row[7])); - e.gender = static_cast(atoi(row[8])); - e.race = static_cast(atoi(row[9])); - e.class_ = static_cast(atoi(row[10])); - e.level = static_cast(strtoul(row[11], nullptr, 10)); - e.deity = static_cast(strtoul(row[12], nullptr, 10)); - e.creation_day = static_cast(strtoul(row[13], nullptr, 10)); - e.last_spawn = static_cast(strtoul(row[14], nullptr, 10)); - e.time_spawned = static_cast(strtoul(row[15], nullptr, 10)); - e.size = strtof(row[16], nullptr); - e.face = static_cast(atoi(row[17])); - e.hair_color = static_cast(atoi(row[18])); - e.hair_style = static_cast(atoi(row[19])); - e.beard = static_cast(atoi(row[20])); - e.beard_color = static_cast(atoi(row[21])); - e.eye_color_1 = static_cast(atoi(row[22])); - e.eye_color_2 = static_cast(atoi(row[23])); - e.drakkin_heritage = static_cast(atoi(row[24])); - e.drakkin_tattoo = static_cast(atoi(row[25])); - e.drakkin_details = static_cast(atoi(row[26])); - e.ac = static_cast(atoi(row[27])); - e.atk = static_cast(atoi(row[28])); - e.hp = static_cast(atoi(row[29])); - e.mana = static_cast(atoi(row[30])); - e.str = static_cast(atoi(row[31])); - e.sta = static_cast(atoi(row[32])); - e.cha = static_cast(atoi(row[33])); - e.dex = static_cast(atoi(row[34])); - e.int_ = static_cast(atoi(row[35])); - e.agi = static_cast(atoi(row[36])); - e.wis = static_cast(atoi(row[37])); - e.fire = static_cast(atoi(row[38])); - e.cold = static_cast(atoi(row[39])); - e.magic = static_cast(atoi(row[40])); - e.poison = static_cast(atoi(row[41])); - e.disease = static_cast(atoi(row[42])); - e.corruption = static_cast(atoi(row[43])); - e.show_helm = static_cast(strtoul(row[44], nullptr, 10)); - e.follow_distance = static_cast(strtoul(row[45], nullptr, 10)); - e.stop_melee_level = static_cast(strtoul(row[46], nullptr, 10)); - e.expansion_bitmask = static_cast(atoi(row[47])); + e.bot_id = static_cast(strtoul(row[0], nullptr, 10)); + e.owner_id = static_cast(strtoul(row[1], nullptr, 10)); + e.spells_id = static_cast(strtoul(row[2], nullptr, 10)); + e.name = row[3] ? row[3] : ""; + e.last_name = row[4] ? row[4] : ""; + e.title = row[5] ? row[5] : ""; + e.suffix = row[6] ? row[6] : ""; + e.zone_id = static_cast(atoi(row[7])); + e.gender = static_cast(atoi(row[8])); + e.race = static_cast(atoi(row[9])); + e.class_ = static_cast(atoi(row[10])); + e.level = static_cast(strtoul(row[11], nullptr, 10)); + e.deity = static_cast(strtoul(row[12], nullptr, 10)); + e.creation_day = static_cast(strtoul(row[13], nullptr, 10)); + e.last_spawn = static_cast(strtoul(row[14], nullptr, 10)); + e.time_spawned = static_cast(strtoul(row[15], nullptr, 10)); + e.size = strtof(row[16], nullptr); + e.face = static_cast(atoi(row[17])); + e.hair_color = static_cast(atoi(row[18])); + e.hair_style = static_cast(atoi(row[19])); + e.beard = static_cast(atoi(row[20])); + e.beard_color = static_cast(atoi(row[21])); + e.eye_color_1 = static_cast(atoi(row[22])); + e.eye_color_2 = static_cast(atoi(row[23])); + e.drakkin_heritage = static_cast(atoi(row[24])); + e.drakkin_tattoo = static_cast(atoi(row[25])); + e.drakkin_details = static_cast(atoi(row[26])); + e.ac = static_cast(atoi(row[27])); + e.atk = static_cast(atoi(row[28])); + e.hp = static_cast(atoi(row[29])); + e.mana = static_cast(atoi(row[30])); + e.str = static_cast(atoi(row[31])); + e.sta = static_cast(atoi(row[32])); + e.cha = static_cast(atoi(row[33])); + e.dex = static_cast(atoi(row[34])); + e.int_ = static_cast(atoi(row[35])); + e.agi = static_cast(atoi(row[36])); + e.wis = static_cast(atoi(row[37])); + e.fire = static_cast(atoi(row[38])); + e.cold = static_cast(atoi(row[39])); + e.magic = static_cast(atoi(row[40])); + e.poison = static_cast(atoi(row[41])); + e.disease = static_cast(atoi(row[42])); + e.corruption = static_cast(atoi(row[43])); + e.show_helm = static_cast(strtoul(row[44], nullptr, 10)); + e.follow_distance = static_cast(strtoul(row[45], nullptr, 10)); + e.stop_melee_level = static_cast(strtoul(row[46], nullptr, 10)); + e.expansion_bitmask = static_cast(atoi(row[47])); + e.enforce_spell_settings = static_cast(strtoul(row[48], nullptr, 10)); return e; } @@ -430,6 +435,7 @@ public: v.push_back(columns[45] + " = " + std::to_string(e.follow_distance)); v.push_back(columns[46] + " = " + std::to_string(e.stop_melee_level)); v.push_back(columns[47] + " = " + std::to_string(e.expansion_bitmask)); + v.push_back(columns[48] + " = " + std::to_string(e.enforce_spell_settings)); auto results = db.QueryDatabase( fmt::format( @@ -499,6 +505,7 @@ public: v.push_back(std::to_string(e.follow_distance)); v.push_back(std::to_string(e.stop_melee_level)); v.push_back(std::to_string(e.expansion_bitmask)); + v.push_back(std::to_string(e.enforce_spell_settings)); auto results = db.QueryDatabase( fmt::format( @@ -576,6 +583,7 @@ public: v.push_back(std::to_string(e.follow_distance)); v.push_back(std::to_string(e.stop_melee_level)); v.push_back(std::to_string(e.expansion_bitmask)); + v.push_back(std::to_string(e.enforce_spell_settings)); insert_chunks.push_back("(" + Strings::Implode(",", v) + ")"); } @@ -609,54 +617,55 @@ public: for (auto row = results.begin(); row != results.end(); ++row) { BotData e{}; - e.bot_id = static_cast(strtoul(row[0], nullptr, 10)); - e.owner_id = static_cast(strtoul(row[1], nullptr, 10)); - e.spells_id = static_cast(strtoul(row[2], nullptr, 10)); - e.name = row[3] ? row[3] : ""; - e.last_name = row[4] ? row[4] : ""; - e.title = row[5] ? row[5] : ""; - e.suffix = row[6] ? row[6] : ""; - e.zone_id = static_cast(atoi(row[7])); - e.gender = static_cast(atoi(row[8])); - e.race = static_cast(atoi(row[9])); - e.class_ = static_cast(atoi(row[10])); - e.level = static_cast(strtoul(row[11], nullptr, 10)); - e.deity = static_cast(strtoul(row[12], nullptr, 10)); - e.creation_day = static_cast(strtoul(row[13], nullptr, 10)); - e.last_spawn = static_cast(strtoul(row[14], nullptr, 10)); - e.time_spawned = static_cast(strtoul(row[15], nullptr, 10)); - e.size = strtof(row[16], nullptr); - e.face = static_cast(atoi(row[17])); - e.hair_color = static_cast(atoi(row[18])); - e.hair_style = static_cast(atoi(row[19])); - e.beard = static_cast(atoi(row[20])); - e.beard_color = static_cast(atoi(row[21])); - e.eye_color_1 = static_cast(atoi(row[22])); - e.eye_color_2 = static_cast(atoi(row[23])); - e.drakkin_heritage = static_cast(atoi(row[24])); - e.drakkin_tattoo = static_cast(atoi(row[25])); - e.drakkin_details = static_cast(atoi(row[26])); - e.ac = static_cast(atoi(row[27])); - e.atk = static_cast(atoi(row[28])); - e.hp = static_cast(atoi(row[29])); - e.mana = static_cast(atoi(row[30])); - e.str = static_cast(atoi(row[31])); - e.sta = static_cast(atoi(row[32])); - e.cha = static_cast(atoi(row[33])); - e.dex = static_cast(atoi(row[34])); - e.int_ = static_cast(atoi(row[35])); - e.agi = static_cast(atoi(row[36])); - e.wis = static_cast(atoi(row[37])); - e.fire = static_cast(atoi(row[38])); - e.cold = static_cast(atoi(row[39])); - e.magic = static_cast(atoi(row[40])); - e.poison = static_cast(atoi(row[41])); - e.disease = static_cast(atoi(row[42])); - e.corruption = static_cast(atoi(row[43])); - e.show_helm = static_cast(strtoul(row[44], nullptr, 10)); - e.follow_distance = static_cast(strtoul(row[45], nullptr, 10)); - e.stop_melee_level = static_cast(strtoul(row[46], nullptr, 10)); - e.expansion_bitmask = static_cast(atoi(row[47])); + e.bot_id = static_cast(strtoul(row[0], nullptr, 10)); + e.owner_id = static_cast(strtoul(row[1], nullptr, 10)); + e.spells_id = static_cast(strtoul(row[2], nullptr, 10)); + e.name = row[3] ? row[3] : ""; + e.last_name = row[4] ? row[4] : ""; + e.title = row[5] ? row[5] : ""; + e.suffix = row[6] ? row[6] : ""; + e.zone_id = static_cast(atoi(row[7])); + e.gender = static_cast(atoi(row[8])); + e.race = static_cast(atoi(row[9])); + e.class_ = static_cast(atoi(row[10])); + e.level = static_cast(strtoul(row[11], nullptr, 10)); + e.deity = static_cast(strtoul(row[12], nullptr, 10)); + e.creation_day = static_cast(strtoul(row[13], nullptr, 10)); + e.last_spawn = static_cast(strtoul(row[14], nullptr, 10)); + e.time_spawned = static_cast(strtoul(row[15], nullptr, 10)); + e.size = strtof(row[16], nullptr); + e.face = static_cast(atoi(row[17])); + e.hair_color = static_cast(atoi(row[18])); + e.hair_style = static_cast(atoi(row[19])); + e.beard = static_cast(atoi(row[20])); + e.beard_color = static_cast(atoi(row[21])); + e.eye_color_1 = static_cast(atoi(row[22])); + e.eye_color_2 = static_cast(atoi(row[23])); + e.drakkin_heritage = static_cast(atoi(row[24])); + e.drakkin_tattoo = static_cast(atoi(row[25])); + e.drakkin_details = static_cast(atoi(row[26])); + e.ac = static_cast(atoi(row[27])); + e.atk = static_cast(atoi(row[28])); + e.hp = static_cast(atoi(row[29])); + e.mana = static_cast(atoi(row[30])); + e.str = static_cast(atoi(row[31])); + e.sta = static_cast(atoi(row[32])); + e.cha = static_cast(atoi(row[33])); + e.dex = static_cast(atoi(row[34])); + e.int_ = static_cast(atoi(row[35])); + e.agi = static_cast(atoi(row[36])); + e.wis = static_cast(atoi(row[37])); + e.fire = static_cast(atoi(row[38])); + e.cold = static_cast(atoi(row[39])); + e.magic = static_cast(atoi(row[40])); + e.poison = static_cast(atoi(row[41])); + e.disease = static_cast(atoi(row[42])); + e.corruption = static_cast(atoi(row[43])); + e.show_helm = static_cast(strtoul(row[44], nullptr, 10)); + e.follow_distance = static_cast(strtoul(row[45], nullptr, 10)); + e.stop_melee_level = static_cast(strtoul(row[46], nullptr, 10)); + e.expansion_bitmask = static_cast(atoi(row[47])); + e.enforce_spell_settings = static_cast(strtoul(row[48], nullptr, 10)); all_entries.push_back(e); } @@ -681,54 +690,55 @@ public: for (auto row = results.begin(); row != results.end(); ++row) { BotData e{}; - e.bot_id = static_cast(strtoul(row[0], nullptr, 10)); - e.owner_id = static_cast(strtoul(row[1], nullptr, 10)); - e.spells_id = static_cast(strtoul(row[2], nullptr, 10)); - e.name = row[3] ? row[3] : ""; - e.last_name = row[4] ? row[4] : ""; - e.title = row[5] ? row[5] : ""; - e.suffix = row[6] ? row[6] : ""; - e.zone_id = static_cast(atoi(row[7])); - e.gender = static_cast(atoi(row[8])); - e.race = static_cast(atoi(row[9])); - e.class_ = static_cast(atoi(row[10])); - e.level = static_cast(strtoul(row[11], nullptr, 10)); - e.deity = static_cast(strtoul(row[12], nullptr, 10)); - e.creation_day = static_cast(strtoul(row[13], nullptr, 10)); - e.last_spawn = static_cast(strtoul(row[14], nullptr, 10)); - e.time_spawned = static_cast(strtoul(row[15], nullptr, 10)); - e.size = strtof(row[16], nullptr); - e.face = static_cast(atoi(row[17])); - e.hair_color = static_cast(atoi(row[18])); - e.hair_style = static_cast(atoi(row[19])); - e.beard = static_cast(atoi(row[20])); - e.beard_color = static_cast(atoi(row[21])); - e.eye_color_1 = static_cast(atoi(row[22])); - e.eye_color_2 = static_cast(atoi(row[23])); - e.drakkin_heritage = static_cast(atoi(row[24])); - e.drakkin_tattoo = static_cast(atoi(row[25])); - e.drakkin_details = static_cast(atoi(row[26])); - e.ac = static_cast(atoi(row[27])); - e.atk = static_cast(atoi(row[28])); - e.hp = static_cast(atoi(row[29])); - e.mana = static_cast(atoi(row[30])); - e.str = static_cast(atoi(row[31])); - e.sta = static_cast(atoi(row[32])); - e.cha = static_cast(atoi(row[33])); - e.dex = static_cast(atoi(row[34])); - e.int_ = static_cast(atoi(row[35])); - e.agi = static_cast(atoi(row[36])); - e.wis = static_cast(atoi(row[37])); - e.fire = static_cast(atoi(row[38])); - e.cold = static_cast(atoi(row[39])); - e.magic = static_cast(atoi(row[40])); - e.poison = static_cast(atoi(row[41])); - e.disease = static_cast(atoi(row[42])); - e.corruption = static_cast(atoi(row[43])); - e.show_helm = static_cast(strtoul(row[44], nullptr, 10)); - e.follow_distance = static_cast(strtoul(row[45], nullptr, 10)); - e.stop_melee_level = static_cast(strtoul(row[46], nullptr, 10)); - e.expansion_bitmask = static_cast(atoi(row[47])); + e.bot_id = static_cast(strtoul(row[0], nullptr, 10)); + e.owner_id = static_cast(strtoul(row[1], nullptr, 10)); + e.spells_id = static_cast(strtoul(row[2], nullptr, 10)); + e.name = row[3] ? row[3] : ""; + e.last_name = row[4] ? row[4] : ""; + e.title = row[5] ? row[5] : ""; + e.suffix = row[6] ? row[6] : ""; + e.zone_id = static_cast(atoi(row[7])); + e.gender = static_cast(atoi(row[8])); + e.race = static_cast(atoi(row[9])); + e.class_ = static_cast(atoi(row[10])); + e.level = static_cast(strtoul(row[11], nullptr, 10)); + e.deity = static_cast(strtoul(row[12], nullptr, 10)); + e.creation_day = static_cast(strtoul(row[13], nullptr, 10)); + e.last_spawn = static_cast(strtoul(row[14], nullptr, 10)); + e.time_spawned = static_cast(strtoul(row[15], nullptr, 10)); + e.size = strtof(row[16], nullptr); + e.face = static_cast(atoi(row[17])); + e.hair_color = static_cast(atoi(row[18])); + e.hair_style = static_cast(atoi(row[19])); + e.beard = static_cast(atoi(row[20])); + e.beard_color = static_cast(atoi(row[21])); + e.eye_color_1 = static_cast(atoi(row[22])); + e.eye_color_2 = static_cast(atoi(row[23])); + e.drakkin_heritage = static_cast(atoi(row[24])); + e.drakkin_tattoo = static_cast(atoi(row[25])); + e.drakkin_details = static_cast(atoi(row[26])); + e.ac = static_cast(atoi(row[27])); + e.atk = static_cast(atoi(row[28])); + e.hp = static_cast(atoi(row[29])); + e.mana = static_cast(atoi(row[30])); + e.str = static_cast(atoi(row[31])); + e.sta = static_cast(atoi(row[32])); + e.cha = static_cast(atoi(row[33])); + e.dex = static_cast(atoi(row[34])); + e.int_ = static_cast(atoi(row[35])); + e.agi = static_cast(atoi(row[36])); + e.wis = static_cast(atoi(row[37])); + e.fire = static_cast(atoi(row[38])); + e.cold = static_cast(atoi(row[39])); + e.magic = static_cast(atoi(row[40])); + e.poison = static_cast(atoi(row[41])); + e.disease = static_cast(atoi(row[42])); + e.corruption = static_cast(atoi(row[43])); + e.show_helm = static_cast(strtoul(row[44], nullptr, 10)); + e.follow_distance = static_cast(strtoul(row[45], nullptr, 10)); + e.stop_melee_level = static_cast(strtoul(row[46], nullptr, 10)); + e.expansion_bitmask = static_cast(atoi(row[47])); + e.enforce_spell_settings = static_cast(strtoul(row[48], nullptr, 10)); all_entries.push_back(e); } diff --git a/common/repositories/base/base_bot_spell_settings_repository.h b/common/repositories/base/base_bot_spell_settings_repository.h index 3fe5600fc..91fbeabd1 100644 --- a/common/repositories/base/base_bot_spell_settings_repository.h +++ b/common/repositories/base/base_bot_spell_settings_repository.h @@ -23,8 +23,6 @@ public: int32_t bot_id; int16_t spell_id; int16_t priority; - uint16_t min_level; - uint16_t max_level; int16_t min_hp; int16_t max_hp; uint8_t is_enabled; @@ -42,8 +40,6 @@ public: "bot_id", "spell_id", "priority", - "min_level", - "max_level", "min_hp", "max_hp", "is_enabled", @@ -57,8 +53,6 @@ public: "bot_id", "spell_id", "priority", - "min_level", - "max_level", "min_hp", "max_hp", "is_enabled", @@ -106,8 +100,6 @@ public: e.bot_id = 0; e.spell_id = 0; e.priority = 0; - e.min_level = 0; - e.max_level = 255; e.min_hp = 0; e.max_hp = 0; e.is_enabled = 1; @@ -150,11 +142,9 @@ public: e.bot_id = static_cast(atoi(row[1])); e.spell_id = static_cast(atoi(row[2])); e.priority = static_cast(atoi(row[3])); - e.min_level = static_cast(strtoul(row[4], nullptr, 10)); - e.max_level = static_cast(strtoul(row[5], nullptr, 10)); - e.min_hp = static_cast(atoi(row[6])); - e.max_hp = static_cast(atoi(row[7])); - e.is_enabled = static_cast(strtoul(row[8], nullptr, 10)); + e.min_hp = static_cast(atoi(row[4])); + e.max_hp = static_cast(atoi(row[5])); + e.is_enabled = static_cast(strtoul(row[6], nullptr, 10)); return e; } @@ -188,15 +178,12 @@ public: auto columns = Columns(); - v.push_back(columns[0] + " = " + std::to_string(e.id)); v.push_back(columns[1] + " = " + std::to_string(e.bot_id)); v.push_back(columns[2] + " = " + std::to_string(e.spell_id)); v.push_back(columns[3] + " = " + std::to_string(e.priority)); - v.push_back(columns[4] + " = " + std::to_string(e.min_level)); - v.push_back(columns[5] + " = " + std::to_string(e.max_level)); - v.push_back(columns[6] + " = " + std::to_string(e.min_hp)); - v.push_back(columns[7] + " = " + std::to_string(e.max_hp)); - v.push_back(columns[8] + " = " + std::to_string(e.is_enabled)); + v.push_back(columns[4] + " = " + std::to_string(e.min_hp)); + v.push_back(columns[5] + " = " + std::to_string(e.max_hp)); + v.push_back(columns[6] + " = " + std::to_string(e.is_enabled)); auto results = db.QueryDatabase( fmt::format( @@ -222,8 +209,6 @@ public: v.push_back(std::to_string(e.bot_id)); v.push_back(std::to_string(e.spell_id)); v.push_back(std::to_string(e.priority)); - v.push_back(std::to_string(e.min_level)); - v.push_back(std::to_string(e.max_level)); v.push_back(std::to_string(e.min_hp)); v.push_back(std::to_string(e.max_hp)); v.push_back(std::to_string(e.is_enabled)); @@ -260,8 +245,6 @@ public: v.push_back(std::to_string(e.bot_id)); v.push_back(std::to_string(e.spell_id)); v.push_back(std::to_string(e.priority)); - v.push_back(std::to_string(e.min_level)); - v.push_back(std::to_string(e.max_level)); v.push_back(std::to_string(e.min_hp)); v.push_back(std::to_string(e.max_hp)); v.push_back(std::to_string(e.is_enabled)); @@ -302,11 +285,9 @@ public: e.bot_id = static_cast(atoi(row[1])); e.spell_id = static_cast(atoi(row[2])); e.priority = static_cast(atoi(row[3])); - e.min_level = static_cast(strtoul(row[4], nullptr, 10)); - e.max_level = static_cast(strtoul(row[5], nullptr, 10)); - e.min_hp = static_cast(atoi(row[6])); - e.max_hp = static_cast(atoi(row[7])); - e.is_enabled = static_cast(strtoul(row[8], nullptr, 10)); + e.min_hp = static_cast(atoi(row[4])); + e.max_hp = static_cast(atoi(row[5])); + e.is_enabled = static_cast(strtoul(row[6], nullptr, 10)); all_entries.push_back(e); } @@ -335,11 +316,9 @@ public: e.bot_id = static_cast(atoi(row[1])); e.spell_id = static_cast(atoi(row[2])); e.priority = static_cast(atoi(row[3])); - e.min_level = static_cast(strtoul(row[4], nullptr, 10)); - e.max_level = static_cast(strtoul(row[5], nullptr, 10)); - e.min_hp = static_cast(atoi(row[6])); - e.max_hp = static_cast(atoi(row[7])); - e.is_enabled = static_cast(strtoul(row[8], nullptr, 10)); + e.min_hp = static_cast(atoi(row[4])); + e.max_hp = static_cast(atoi(row[5])); + e.is_enabled = static_cast(strtoul(row[6], nullptr, 10)); all_entries.push_back(e); } diff --git a/common/repositories/bot_spell_settings_repository.h b/common/repositories/bot_spell_settings_repository.h index d606b0c0e..595e08525 100644 --- a/common/repositories/bot_spell_settings_repository.h +++ b/common/repositories/bot_spell_settings_repository.h @@ -54,11 +54,9 @@ public: auto columns = Columns(); v.push_back(columns[3] + " = " + std::to_string(e.priority)); - v.push_back(columns[4] + " = " + std::to_string(e.min_level)); - v.push_back(columns[5] + " = " + std::to_string(e.max_level)); - v.push_back(columns[6] + " = " + std::to_string(e.min_hp)); - v.push_back(columns[7] + " = " + std::to_string(e.max_hp)); - v.push_back(columns[8] + " = " + std::to_string(e.is_enabled)); + v.push_back(columns[4] + " = " + std::to_string(e.min_hp)); + v.push_back(columns[5] + " = " + std::to_string(e.max_hp)); + v.push_back(columns[6] + " = " + std::to_string(e.is_enabled)); auto results = db.QueryDatabase( fmt::format( diff --git a/utils/sql/git/bots/required/2022_12_02_bot_spell_settings.sql b/utils/sql/git/bots/required/2022_12_02_bot_spell_settings.sql new file mode 100644 index 000000000..28e825da6 --- /dev/null +++ b/utils/sql/git/bots/required/2022_12_02_bot_spell_settings.sql @@ -0,0 +1,5 @@ +ALTER TABLE `bot_data` +ADD COLUMN `enforce_spell_settings` TINYINT(2) UNSIGNED NOT NULL DEFAULT '0' AFTER `expansion_bitmask`; + +ALTER TABLE `bot_spell_settings` DROP `min_level`; +ALTER TABLE `bot_spell_settings` DROP `max_level`; diff --git a/zone/bot.cpp b/zone/bot.cpp index e9173ae30..07de18015 100644 --- a/zone/bot.cpp +++ b/zone/bot.cpp @@ -406,11 +406,6 @@ Bot::Bot(uint32 botID, uint32 botOwnerCharacterID, uint32 botSpellsID, double to bot_owner->Message(Chat::White, "&s for '%s'", BotDatabase::fail::LoadBuffs(), GetCleanName()); } - GetBotOwnerDataBuckets(); - GetBotDataBuckets(); - LoadBotSpellSettings(); - AI_AddBotSpells(GetBotSpellID()); - CalcBotStats(false); hp_regen = CalcHPRegen(); mana_regen = CalcManaRegen(); @@ -10606,6 +10601,27 @@ void Bot::SetExpansionBitmask(int expansion_bitmask, bool save) LoadAAs(); } +void Bot::SetBotEnforceSpellSetting(bool enforce_spell_settings, bool save) +{ + m_enforce_spell_settings = enforce_spell_settings; + + if (save) { + if (!database.botdb.SaveEnforceSpellSetting(GetBotID(), enforce_spell_settings)) { + if (GetBotOwner() && GetBotOwner()->IsClient()) { + GetBotOwner()->CastToClient()->Message( + Chat::White, + fmt::format( + "Failed to save enforce spell settings for {}.", + GetCleanName() + ).c_str() + ); + } + } + } + LoadBotSpellSettings(); + AI_AddBotSpells(GetBotSpellID()); +} + bool Bot::AddBotSpellSetting(uint16 spell_id, BotSpellSetting* bs) { if (!IsValidSpell(spell_id) || !bs) { @@ -10623,8 +10639,6 @@ bool Bot::AddBotSpellSetting(uint16 spell_id, BotSpellSetting* bs) s.bot_id = GetBotID(); s.priority = bs->priority; - s.min_level = bs->min_level; - s.max_level = bs->max_level; s.min_hp = bs->min_hp; s.max_hp = bs->max_hp; s.is_enabled = bs->is_enabled; @@ -10675,7 +10689,7 @@ BotSpellSetting* Bot::GetBotSpellSetting(uint16 spell_id) return nullptr; } -void Bot::ListBotSpells() +void Bot::ListBotSpells(uint8 min_level) { auto bot_owner = GetBotOwner(); if (!bot_owner) { @@ -10697,28 +10711,25 @@ void Bot::ListBotSpells() auto spell_number = 1; for (const auto& s : AIBot_spells) { - bot_owner->Message( - Chat::White, - fmt::format( - "Spell {} | Spell: {} ({})", - spell_number, - spells[s.spellid].name, - s.spellid - ).c_str() - ); + auto b = bot_spell_settings.find(s.spellid); + if (b == bot_spell_settings.end() && s.minlevel >= min_level) { + bot_owner->Message( + Chat::White, + fmt::format( + "Spell {} | Spell: {} | Add Spell: {}", + spell_number, + Saylink::Silent( + fmt::format("^spellinfo {}", s.spellid), + spells[s.spellid].name + ), + Saylink::Silent( + fmt::format("^spellsettingsadd {} {} {} {}", s.spellid, s.priority, s.min_hp, s.max_hp), "Add") + ).c_str() + ); - bot_owner->Message( - Chat::White, - fmt::format( - "Spell {} | Priority: {} Health: {}", - spell_number, - s.priority, - GetHPString(s.min_hp, s.max_hp) - ).c_str() - ); - - spell_count++; - spell_number++; + spell_count++; + spell_number++; + } } bot_owner->Message( @@ -10757,22 +10768,15 @@ void Bot::ListBotSpellSettings() bot_owner->Message( Chat::White, fmt::format( - "Setting {} | Spell: {} ({}) Enabled: {}", + "Setting {} | Spell: {} | State: {} | {}", setting_number, - spells[bs.first].name, - bs.first, - bs.second.is_enabled ? "Yes" : "No" - ).c_str() - ); - - bot_owner->Message( - Chat::White, - fmt::format( - "Setting {} | Priority: {} Levels: {} Health: {}", - setting_number, - bs.second.priority, - GetLevelString(bs.second.min_level, bs.second.max_level), - GetHPString(bs.second.min_hp, bs.second.max_hp) + Saylink::Silent(fmt::format("^spellinfo {}", bs.first), spells[bs.first].name), + Saylink::Silent( + fmt::format("^spellsettingstoggle {} {}", + bs.first, bs.second.is_enabled ? "False" : "True"), + bs.second.is_enabled ? "Enabled" : "Disabled" + ), + Saylink::Silent(fmt::format("^spellsettingsdelete {}", bs.first), "Remove") ).c_str() ); @@ -10804,12 +10808,9 @@ void Bot::LoadBotSpellSettings() BotSpellSetting b; b.priority = e.priority; - b.min_level = e.min_level; - b.max_level = e.max_level; b.min_hp = e.min_hp; b.max_hp = e.max_hp; b.is_enabled = e.is_enabled; - bot_spell_settings[e.spell_id] = b; } } @@ -10825,8 +10826,6 @@ bool Bot::UpdateBotSpellSetting(uint16 spell_id, BotSpellSetting* bs) s.spell_id = spell_id; s.bot_id = GetBotID(); s.priority = bs->priority; - s.min_level = bs->min_level; - s.max_level = bs->max_level; s.min_hp = bs->min_hp; s.max_hp = bs->max_hp; s.is_enabled = bs->is_enabled; @@ -10844,30 +10843,6 @@ bool Bot::UpdateBotSpellSetting(uint16 spell_id, BotSpellSetting* bs) return true; } -std::string Bot::GetLevelString(uint8 min_level, uint8 max_level) -{ - std::string level_string = "Any"; - if (min_level && max_level) { - level_string = fmt::format( - "{} to {}", - min_level, - max_level - ); - } else if (min_level && !max_level) { - level_string = fmt::format( - "{}+", - min_level - ); - } else if (!min_level && max_level) { - level_string = fmt::format( - "1 to {}", - max_level - ); - } - - return level_string; -} - std::string Bot::GetHPString(int8 min_hp, int8 max_hp) { std::string hp_string = "Any"; diff --git a/zone/bot.h b/zone/bot.h index 06798f5d8..98cdd072d 100644 --- a/zone/bot.h +++ b/zone/bot.h @@ -315,6 +315,8 @@ public: int16 iManaCost, int32 iRecastDelay, int16 iResistAdjust, + uint8 min_level, + uint8 max_level, int8 min_hp, int8 max_hp, std::string bucket_name, @@ -456,6 +458,7 @@ public: BotRoleType GetBotRole() { return _botRole; } EQ::constants::StanceType GetBotStance() { return _botStance; } uint8 GetChanceToCastBySpellType(uint32 spellType); + bool GetBotEnforceSpellSetting() { return m_enforce_spell_settings; } bool IsGroupHealer() { return m_CastingRoles.GroupHealer; } bool IsGroupSlower() { return m_CastingRoles.GroupSlower; } @@ -476,6 +479,7 @@ public: bool IsBotSpellFighter() { return IsSpellFighterClass(GetClass()); } bool IsBotFighter() { return IsFighterClass(GetClass()); } bool IsBotNonSpellFighter() { return IsNonSpellFighterClass(GetClass()); } + uint8 GetBotClass() { return GetClass(); } bool CanHeal(); int GetRawACNoShield(int &shield_ac); @@ -597,9 +601,8 @@ public: int GetExpansionBitmask(); void SetExpansionBitmask(int expansion_bitmask, bool save = true); - void ListBotSpells(); + void ListBotSpells(uint8 min_level); - std::string GetLevelString(uint8 min_level, uint8 max_level); std::string GetHPString(int8 min_hp, int8 max_hp); bool AddBotSpellSetting(uint16 spell_id, BotSpellSetting* bs); @@ -608,6 +611,7 @@ public: void ListBotSpellSettings(); void LoadBotSpellSettings(); bool UpdateBotSpellSetting(uint16 spell_id, BotSpellSetting* bs); + void SetBotEnforceSpellSetting(bool enforcespellsettings, bool save = false); static void SpawnBotGroupByName(Client* c, std::string botgroup_name, uint32 leader_id); @@ -789,6 +793,7 @@ private: bool _pauseAI; uint8 _stopMeleeLevel; int m_expansion_bitmask; + bool m_enforce_spell_settings; // Private "base stats" Members int32 _baseMR; diff --git a/zone/bot_command.cpp b/zone/bot_command.cpp index a6303ccb3..6dfc0e22c 100644 --- a/zone/bot_command.cpp +++ b/zone/bot_command.cpp @@ -73,6 +73,7 @@ #include "titles.h" #include "water_map.h" #include "worldserver.h" +#include "dialogue_window.h" #include @@ -1362,6 +1363,7 @@ int bot_command_init(void) bot_command_add("cure", "Orders a bot to remove any ailments", AccountStatus::Player, bot_command_cure) || bot_command_add("defensive", "Orders a bot to use a defensive discipline", AccountStatus::Player, bot_command_defensive) || bot_command_add("depart", "Orders a bot to open a magical doorway to a specified destination", AccountStatus::Player, bot_command_depart) || + bot_command_add("enforcespelllist", "Toggles your Bot to cast only spells in their spell settings list.", AccountStatus::Player, bot_command_enforce_spell_list) || bot_command_add("escape", "Orders a bot to send a target group to a safe location within the zone", AccountStatus::Player, bot_command_escape) || bot_command_add("findaliases", "Find available aliases for a bot command", AccountStatus::Player, bot_command_find_aliases) || bot_command_add("follow", "Orders bots to follow a designated target (option 'chain' auto-links eligible spawned bots)", AccountStatus::Player, bot_command_follow) || @@ -1416,12 +1418,13 @@ int bot_command_init(void) bot_command_add("rune", "Orders a bot to cast a rune of protection", AccountStatus::Player, bot_command_rune) || bot_command_add("sendhome", "Orders a bot to open a magical doorway home", AccountStatus::Player, bot_command_send_home) || bot_command_add("size", "Orders a bot to change a player's size", AccountStatus::Player, bot_command_size) || - bot_command_add("spelllist", "Lists a Caster of Hybrid bot's spells", AccountStatus::Player, bot_command_spell_list) || - bot_command_add("spellsettingsadd", "Add a bot spell setting for a Caster or Hybrid bot", AccountStatus::Player, bot_command_spell_settings_add) || - bot_command_add("spellsettingsdelete", "Delete a bot spell setting from a Caster or Hybrid bot", AccountStatus::Player, bot_command_spell_settings_delete) || - bot_command_add("spellsettingslist", "Lists a Caster or Hybrid bot's spell settings", AccountStatus::Player, bot_command_spell_settings_list) || - bot_command_add("spellsettingstoggle", "Toggle a bot spell for a Caster or Hybrid bot", AccountStatus::Player, bot_command_spell_settings_toggle) || - bot_command_add("spellsettingsupdate", "Update a bot spell setting for a Caster or Hybrid bot", AccountStatus::Player, bot_command_spell_settings_update) || + bot_command_add("spellinfo", "Opens a dialogue window with spell info", AccountStatus::Player, bot_spell_info_dialogue_window) || + bot_command_add("spelllist", "Lists all Spells learned by the Bot.", AccountStatus::Player, bot_command_spell_list) || + bot_command_add("spellsettingsadd", "Add a bot spell setting entry", AccountStatus::Player, bot_command_spell_settings_add) || + bot_command_add("spellsettingsdelete", "Delete a bot spell setting entry", AccountStatus::Player, bot_command_spell_settings_delete) || + bot_command_add("spellsettingslist", "Lists a bot's spell setting entries", AccountStatus::Player, bot_command_spell_settings_list) || + bot_command_add("spellsettingstoggle", "Toggle a bot spells use", AccountStatus::Player, bot_command_spell_settings_toggle) || + bot_command_add("spellsettingsupdate", "Update a bot spell setting entry", AccountStatus::Player, bot_command_spell_settings_update) || bot_command_add("summoncorpse", "Orders a bot to summon a corpse to its feet", AccountStatus::Player, bot_command_summon_corpse) || bot_command_add("suspend", "Suspends a bot's AI processing until released", AccountStatus::Player, bot_command_suspend) || bot_command_add("taunt", "Toggles taunt use by a bot", AccountStatus::Player, bot_command_taunt) || @@ -6683,6 +6686,20 @@ void bot_subcommand_bot_spawn(Client *c, const Seperator *sep) return; } + my_bot->GetBotOwnerDataBuckets(); + my_bot->GetBotDataBuckets(); + my_bot->LoadBotSpellSettings(); + if (!my_bot->AI_AddBotSpells(my_bot->GetBotSpellID())) { + c->Message( + Chat::White, + fmt::format( + "Failed to load spells for '{}' (ID {}).", + bot_name, + bot_id + ).c_str() + ); + } + static std::string bot_spawn_message[17] = { "I am ready to fight!", // DEFAULT "A solid weapon is my ally!", // WARRIOR @@ -10242,7 +10259,7 @@ void bot_command_spell_list(Client* c, const Seperator *sep) c->Message( Chat::White, fmt::format( - "Usage: {} [Spell ID] [Priority] [Min Level] [Max Level] [Min HP] [Max HP]", + "Usage: {} [Min Level] (Level is optional)", sep->arg[0] ).c_str() ); @@ -10255,7 +10272,13 @@ void bot_command_spell_list(Client* c, const Seperator *sep) return; } - my_bot->ListBotSpells(); + uint8 min_level = 0; + + if (sep->IsNumber(1)) { + min_level = static_cast(std::stoul(sep->arg[1])); + } + + my_bot->ListBotSpells(min_level); } void bot_command_spell_settings_add(Client *c, const Seperator *sep) @@ -10268,7 +10291,7 @@ void bot_command_spell_settings_add(Client *c, const Seperator *sep) c->Message( Chat::White, fmt::format( - "Usage: {} [Spell ID] [Priority] [Min Level] [Max Level] [Min HP] [Max HP]", + "Usage: {} [Spell ID] [Priority] [Min HP] [Max HP]", sep->arg[0] ).c_str() ); @@ -10283,18 +10306,16 @@ void bot_command_spell_settings_add(Client *c, const Seperator *sep) auto arguments = sep->argnum; if ( - arguments < 6 || + arguments < 4 || !sep->IsNumber(1) || !sep->IsNumber(2) || !sep->IsNumber(3) || - !sep->IsNumber(4) || - !sep->IsNumber(5) || - !sep->IsNumber(6) + !sep->IsNumber(4) ) { c->Message( Chat::White, fmt::format( - "Usage: {} [Spell ID] [Priority] [Min Level] [Max Level] [Min HP] [Max HP]", + "Usage: {} [Spell ID] [Priority] [Min HP] [Max HP]", sep->arg[0] ).c_str() ); @@ -10329,16 +10350,12 @@ void bot_command_spell_settings_add(Client *c, const Seperator *sep) } auto priority = static_cast(std::stoi(sep->arg[2])); - auto min_level = static_cast(std::stoul(sep->arg[3])); - auto max_level = static_cast(std::stoul(sep->arg[4])); - auto min_hp = static_cast(EQ::Clamp(std::stoi(sep->arg[5]), -1, 99)); - auto max_hp = static_cast(EQ::Clamp(std::stoi(sep->arg[6]), -1, 100)); + auto min_hp = static_cast(EQ::Clamp(std::stoi(sep->arg[3]), -1, 99)); + auto max_hp = static_cast(EQ::Clamp(std::stoi(sep->arg[4]), -1, 100)); BotSpellSetting bs; bs.priority = priority; - bs.min_level = min_level; - bs.max_level = max_level; bs.min_hp = min_hp; bs.max_hp = max_hp; @@ -10375,9 +10392,8 @@ void bot_command_spell_settings_add(Client *c, const Seperator *sep) c->Message( Chat::White, fmt::format( - "Spell Setting Added | Priority: {} Levels: {} Health: {}", + "Spell Setting Added | Priority: {} Health: {}", priority, - my_bot->GetLevelString(min_level, max_level), my_bot->GetHPString(min_hp, max_hp) ).c_str() ); @@ -10555,8 +10571,6 @@ void bot_command_spell_settings_toggle(Client *c, const Seperator *sep) BotSpellSetting bs; bs.priority = obs->priority; - bs.min_level = obs->min_level; - bs.max_level = obs->max_level; bs.min_hp = obs->min_hp; bs.max_hp = obs->max_hp; bs.is_enabled = toggle; @@ -10605,7 +10619,7 @@ void bot_command_spell_settings_update(Client *c, const Seperator *sep) c->Message( Chat::White, fmt::format( - "Usage: {} [Spell ID] [Priority] [Min Level] [Max Level] [Min HP] [Max HP]", + "Usage: {} [Spell ID] [Priority] [Min HP] [Max HP]", sep->arg[0] ).c_str() ); @@ -10620,18 +10634,16 @@ void bot_command_spell_settings_update(Client *c, const Seperator *sep) auto arguments = sep->argnum; if ( - arguments < 6 || + arguments < 4 || !sep->IsNumber(1) || !sep->IsNumber(2) || !sep->IsNumber(3) || - !sep->IsNumber(4) || - !sep->IsNumber(5) || - !sep->IsNumber(6) + !sep->IsNumber(4) ) { c->Message( Chat::White, fmt::format( - "Usage: {} [Spell ID] [Priority] [Min Level] [Max Level] [Min HP] [Max HP]", + "Usage: {} [Spell ID] [Priority] [Min HP] [Max HP]", sep->arg[0] ).c_str() ); @@ -10652,16 +10664,12 @@ void bot_command_spell_settings_update(Client *c, const Seperator *sep) } auto priority = static_cast(std::stoi(sep->arg[2])); - auto min_level = static_cast(std::stoul(sep->arg[3])); - auto max_level = static_cast(std::stoul(sep->arg[4])); - auto min_hp = static_cast(EQ::Clamp(std::stoi(sep->arg[5]), -1, 99)); - auto max_hp = static_cast(EQ::Clamp(std::stoi(sep->arg[6]), -1, 100)); + auto min_hp = static_cast(EQ::Clamp(std::stoi(sep->arg[3]), -1, 99)); + auto max_hp = static_cast(EQ::Clamp(std::stoi(sep->arg[4]), -1, 100)); BotSpellSetting bs; bs.priority = priority; - bs.min_level = min_level; - bs.max_level = max_level; bs.min_hp = min_hp; bs.max_hp = max_hp; @@ -10698,12 +10706,121 @@ void bot_command_spell_settings_update(Client *c, const Seperator *sep) c->Message( Chat::White, fmt::format( - "Spell Setting Updated | Priority: {} Levels: {} Health: {}", + "Spell Setting Updated | Priority: {} Health: {}", priority, - my_bot->GetLevelString(min_level, max_level), my_bot->GetHPString(min_hp, max_hp) ).c_str() ); } +void bot_spell_info_dialogue_window(Client* c, const Seperator *sep) +{ + if (helper_command_alias_fail(c, "bot_spell_info_dialogue_window", sep->arg[0], "spellinfo")) { + return; + } + + auto arguments = sep->argnum; + if ( + arguments < 1 || + !sep->IsNumber(1) + ) { + c->Message( + Chat::White, + fmt::format( + "Usage: {} [Spell ID]", + sep->arg[0] + ).c_str() + ); + return; + } + + auto my_bot = ActionableBots::AsTarget_ByBot(c); + if (!my_bot) { + c->Message(Chat::White, "You must target a bot that you own to use this command."); + return; + } + + auto spell_id = static_cast(std::stoul(sep->arg[1])); + auto min_level = spells[spell_id].classes; + auto class_level = min_level[my_bot->GetBotClass() - 1]; + + if (class_level > my_bot->GetLevel()) { + c->Message(Chat::White, "This is not a usable spell by your bot."); + return; + } + + auto results = database.QueryDatabase( + fmt::format( + "SELECT value FROM db_str WHERE id = {} and type = 6 LIMIT 1", + spells[spell_id].effect_description_id + ) + ); + + if (!results.Success() || !results.RowCount()) { + c->Message(Chat::White, "No Spell Information Available for this."); + return; + } + + auto row = results.begin(); + std::string spell_desc = row[0]; + + auto m = DialogueWindow::TableRow( + DialogueWindow::TableCell("Spell Effect: ") + + DialogueWindow::TableCell(spell_desc) + ); + + m += DialogueWindow::TableRow( + DialogueWindow::TableCell("Spell Level: ") + + DialogueWindow::TableCell(fmt::format("{}", class_level)) + ); + + c->SendPopupToClient( + fmt::format( + "Spell: {}", spells[spell_id].name + ).c_str(), + DialogueWindow::Table(m).c_str() + ); +} + +void bot_command_enforce_spell_list(Client* c, const Seperator *sep) +{ + if (helper_command_alias_fail(c, "bot_command_enforce_spell_list", sep->arg[0], "enforcespelllist")) { + return; + } + + if (helper_is_help_or_usage(sep->arg[1])) { + c->Message( + Chat::White, + fmt::format( + "Usage: {} [True/False]", + sep->arg[0] + ).c_str() + ); + return; + } + + auto my_bot = ActionableBots::AsTarget_ByBot(c); + if (!my_bot) { + c->Message(Chat::White, "You must target a bot that you own to use this command."); + return; + } + + bool toggle = ( + sep->IsNumber(1) ? + (std::stoi(sep->arg[1]) ? true : false) : + atobool(sep->arg[1]) + ); + + my_bot->SetBotEnforceSpellSetting(toggle, true); + + c->Message( + Chat::White, + fmt::format( + "{}'s Spell Settings List entries are now {}.", + my_bot->GetCleanName(), + toggle ? "enforced" : "optional" + ).c_str() + ); +} + #endif // BOTS diff --git a/zone/bot_command.h b/zone/bot_command.h index ad341c123..535d2c314 100644 --- a/zone/bot_command.h +++ b/zone/bot_command.h @@ -592,6 +592,8 @@ void bot_command_spell_settings_delete(Client* c, const Seperator *sep); void bot_command_spell_settings_list(Client* c, const Seperator *sep); void bot_command_spell_settings_toggle(Client* c, const Seperator *sep); void bot_command_spell_settings_update(Client* c, const Seperator *sep); +void bot_spell_info_dialogue_window(Client* c, const Seperator *sep); +void bot_command_enforce_spell_list(Client* c, const Seperator* sep); void bot_command_summon_corpse(Client *c, const Seperator *sep); void bot_command_suspend(Client *c, const Seperator *sep); void bot_command_taunt(Client *c, const Seperator *sep); diff --git a/zone/bot_database.cpp b/zone/bot_database.cpp index 572e26628..b2ab960b9 100644 --- a/zone/bot_database.cpp +++ b/zone/bot_database.cpp @@ -409,7 +409,8 @@ bool BotDatabase::LoadBot(const uint32 bot_id, Bot*& loaded_bot) " `show_helm`," // 25 " `follow_distance`," // 26 " `stop_melee_level`," // 27 - " `expansion_bitmask`" // 28 + " `expansion_bitmask`," // 28 + " `enforce_spell_settings`" // 29 " FROM `bot_data`" " WHERE `bot_id` = {}" " LIMIT 1", @@ -508,6 +509,8 @@ bool BotDatabase::LoadBot(const uint32 bot_id, Bot*& loaded_bot) auto eb = std::stoi(row[28]); loaded_bot->SetExpansionBitmask(eb, false); + + loaded_bot->SetBotEnforceSpellSetting((std::stoi(row[29]) > 0 ? true : false)); } return true; @@ -3278,6 +3281,27 @@ bool BotDatabase::SaveExpansionBitmask(const uint32 bot_id, const int expansion_ return true; } +bool BotDatabase::SaveEnforceSpellSetting(const uint32 bot_id, const bool enforce_spell_setting) +{ + if (!bot_id) { + return false; + } + + query = fmt::format( + "UPDATE `bot_data`" + "SET `enforce_spell_settings` = {} " + "WHERE `bot_id` = {}", + (enforce_spell_setting ? 1 : 0), + bot_id + ); + auto results = database.QueryDatabase(query); + if (!results.Success()) { + return false; + } + + return true; +} + /* fail::Bot functions */ const char* BotDatabase::fail::LoadBotsList() { return "Failed to bots list"; } const char* BotDatabase::fail::LoadOwnerID() { return "Failed to load owner ID"; } diff --git a/zone/bot_database.h b/zone/bot_database.h index 7700caf67..2f4828a46 100644 --- a/zone/bot_database.h +++ b/zone/bot_database.h @@ -99,6 +99,7 @@ public: bool SaveEquipmentColor(const uint32 bot_id, const int16 slot_id, const uint32 rgb); bool SaveExpansionBitmask(const uint32 bot_id, const int expansion_bitmask); + bool SaveEnforceSpellSetting(const uint32 bot_id, const bool enforce_spell_setting); /* Bot pet functions */ diff --git a/zone/botspellsai.cpp b/zone/botspellsai.cpp index 65af3aab4..e56bded24 100644 --- a/zone/botspellsai.cpp +++ b/zone/botspellsai.cpp @@ -3003,14 +3003,6 @@ bool Bot::AI_AddBotSpells(uint32 bot_spell_id) { continue; } - if (bs->min_level > 0 && GetLevel() < bs->min_level) { - continue; - } - - if (bs->max_level > 0 && GetLevel() > bs->max_level) { - continue; - } - AddSpellToBotList( bs->priority, e.spellid, @@ -3018,6 +3010,8 @@ bool Bot::AI_AddBotSpells(uint32 bot_spell_id) { e.manacost, e.recast_delay, e.resist_adjust, + e.minlevel, + e.maxlevel, bs->min_hp, bs->max_hp, e.bucket_name, @@ -3027,19 +3021,23 @@ bool Bot::AI_AddBotSpells(uint32 bot_spell_id) { continue; } - AddSpellToBotList( - e.priority, - e.spellid, - e.type, - e.manacost, - e.recast_delay, - e.resist_adjust, - e.min_hp, - e.max_hp, - e.bucket_name, - e.bucket_value, - e.bucket_comparison - ); + if (!GetBotEnforceSpellSetting()) { + AddSpellToBotList( + e.priority, + e.spellid, + e.type, + e.manacost, + e.recast_delay, + e.resist_adjust, + e.minlevel, + e.maxlevel, + e.min_hp, + e.max_hp, + e.bucket_name, + e.bucket_value, + e.bucket_comparison + ); + } } } } @@ -3102,14 +3100,6 @@ bool Bot::AI_AddBotSpells(uint32 bot_spell_id) { continue; } - if (bs->min_level > 0 && GetLevel() < bs->min_level) { - continue; - } - - if (bs->max_level > 0 && GetLevel() > bs->max_level) { - continue; - } - AddSpellToBotList( bs->priority, e.spellid, @@ -3117,6 +3107,8 @@ bool Bot::AI_AddBotSpells(uint32 bot_spell_id) { e.manacost, e.recast_delay, e.resist_adjust, + e.minlevel, + e.maxlevel, bs->min_hp, bs->max_hp, e.bucket_name, @@ -3126,19 +3118,23 @@ bool Bot::AI_AddBotSpells(uint32 bot_spell_id) { continue; } - AddSpellToBotList( - e.priority, - e.spellid, - e.type, - e.manacost, - e.recast_delay, - e.resist_adjust, - e.min_hp, - e.max_hp, - e.bucket_name, - e.bucket_value, - e.bucket_comparison - ); + if (!GetBotEnforceSpellSetting()) { + AddSpellToBotList( + e.priority, + e.spellid, + e.type, + e.manacost, + e.recast_delay, + e.resist_adjust, + e.minlevel, + e.maxlevel, + e.min_hp, + e.max_hp, + e.bucket_name, + e.bucket_value, + e.bucket_comparison + ); + } } } @@ -3295,6 +3291,8 @@ void Bot::AddSpellToBotList( int16 iManaCost, int32 iRecastDelay, int16 iResistAdjust, + uint8 min_level, + uint8 max_level, int8 min_hp, int8 max_hp, std::string bucket_name, @@ -3315,6 +3313,8 @@ void Bot::AddSpellToBotList( t.recast_delay = iRecastDelay; t.time_cancast = 0; t.resist_adjust = iResistAdjust; + t.minlevel = min_level; + t.maxlevel = maxlevel; t.min_hp = min_hp; t.max_hp = max_hp; t.bucket_name = bucket_name; diff --git a/zone/npc.h b/zone/npc.h index 8020b5cb1..209703f3f 100644 --- a/zone/npc.h +++ b/zone/npc.h @@ -67,6 +67,8 @@ struct BotSpells_Struct { int32 recast_delay; int16 priority; int16 resist_adjust; + uint8 minlevel; + uint8 maxlevel; int16 min_hp; // >0 won't cast if HP is below int16 max_hp; // >0 won't cast if HP is above std::string bucket_name;