diff --git a/common/database/database_update_manifest.cpp b/common/database/database_update_manifest.cpp index a27e39de6..cf6f80645 100644 --- a/common/database/database_update_manifest.cpp +++ b/common/database/database_update_manifest.cpp @@ -5620,7 +5620,21 @@ ALTER TABLE `npc_types` ADD COLUMN `greed` tinyint(8) UNSIGNED NOT NULL DEFAULT 0 AFTER `merchant_id`; )", .content_schema_update = true - } + }, + ManifestEntry{ + .version = 9279, + .description = "2024_05_13_content_flagging_npc_spells_entries.sql", + .check = "SHOW COLUMNS FROM `npc_spells_entries` LIKE 'content_flags'", + .condition = "empty", + .match = "", + .sql = R"( +ALTER TABLE `npc_spells_entries` ADD `min_expansion` tinyint(4) NOT NULL DEFAULT -1; +ALTER TABLE `npc_spells_entries` ADD `max_expansion` tinyint(4) NOT NULL DEFAULT -1; +ALTER TABLE `npc_spells_entries` ADD `content_flags` varchar(100) NULL; +ALTER TABLE `npc_spells_entries` ADD `content_flags_disabled` varchar(100) NULL; +)", + .content_schema_update = true + }, // -- template; copy/paste this when you need to create a new entry // ManifestEntry{ // .version = 9228, diff --git a/common/repositories/base/base_npc_spells_entries_repository.h b/common/repositories/base/base_npc_spells_entries_repository.h index d5508c645..446944d87 100644 --- a/common/repositories/base/base_npc_spells_entries_repository.h +++ b/common/repositories/base/base_npc_spells_entries_repository.h @@ -19,18 +19,22 @@ class BaseNpcSpellsEntriesRepository { public: struct NpcSpellsEntries { - uint32_t id; - int32_t npc_spells_id; - uint16_t spellid; - uint32_t type; - uint8_t minlevel; - uint8_t maxlevel; - int16_t manacost; - int32_t recast_delay; - int16_t priority; - int32_t resist_adjust; - int16_t min_hp; - int16_t max_hp; + uint32_t id; + int32_t npc_spells_id; + uint16_t spellid; + uint32_t type; + uint8_t minlevel; + uint8_t maxlevel; + int16_t manacost; + int32_t recast_delay; + int16_t priority; + int32_t resist_adjust; + int16_t min_hp; + int16_t max_hp; + int8_t min_expansion; + int8_t max_expansion; + std::string content_flags; + std::string content_flags_disabled; }; static std::string PrimaryKey() @@ -53,6 +57,10 @@ public: "resist_adjust", "min_hp", "max_hp", + "min_expansion", + "max_expansion", + "content_flags", + "content_flags_disabled", }; } @@ -71,6 +79,10 @@ public: "resist_adjust", "min_hp", "max_hp", + "min_expansion", + "max_expansion", + "content_flags", + "content_flags_disabled", }; } @@ -111,18 +123,22 @@ public: { NpcSpellsEntries e{}; - e.id = 0; - e.npc_spells_id = 0; - e.spellid = 0; - e.type = 0; - e.minlevel = 0; - e.maxlevel = 255; - e.manacost = -1; - e.recast_delay = -1; - e.priority = 0; - e.resist_adjust = 0; - e.min_hp = 0; - e.max_hp = 0; + e.id = 0; + e.npc_spells_id = 0; + e.spellid = 0; + e.type = 0; + e.minlevel = 0; + e.maxlevel = 255; + e.manacost = -1; + e.recast_delay = -1; + e.priority = 0; + e.resist_adjust = 0; + e.min_hp = 0; + e.max_hp = 0; + e.min_expansion = -1; + e.max_expansion = -1; + e.content_flags = ""; + e.content_flags_disabled = ""; return e; } @@ -159,18 +175,22 @@ public: if (results.RowCount() == 1) { NpcSpellsEntries e{}; - e.id = row[0] ? static_cast(strtoul(row[0], nullptr, 10)) : 0; - e.npc_spells_id = row[1] ? static_cast(atoi(row[1])) : 0; - e.spellid = row[2] ? static_cast(strtoul(row[2], nullptr, 10)) : 0; - e.type = row[3] ? static_cast(strtoul(row[3], nullptr, 10)) : 0; - e.minlevel = row[4] ? static_cast(strtoul(row[4], nullptr, 10)) : 0; - e.maxlevel = row[5] ? static_cast(strtoul(row[5], nullptr, 10)) : 255; - e.manacost = row[6] ? static_cast(atoi(row[6])) : -1; - e.recast_delay = row[7] ? static_cast(atoi(row[7])) : -1; - e.priority = row[8] ? static_cast(atoi(row[8])) : 0; - e.resist_adjust = row[9] ? static_cast(atoi(row[9])) : 0; - e.min_hp = row[10] ? static_cast(atoi(row[10])) : 0; - e.max_hp = row[11] ? static_cast(atoi(row[11])) : 0; + e.id = row[0] ? static_cast(strtoul(row[0], nullptr, 10)) : 0; + e.npc_spells_id = row[1] ? static_cast(atoi(row[1])) : 0; + e.spellid = row[2] ? static_cast(strtoul(row[2], nullptr, 10)) : 0; + e.type = row[3] ? static_cast(strtoul(row[3], nullptr, 10)) : 0; + e.minlevel = row[4] ? static_cast(strtoul(row[4], nullptr, 10)) : 0; + e.maxlevel = row[5] ? static_cast(strtoul(row[5], nullptr, 10)) : 255; + e.manacost = row[6] ? static_cast(atoi(row[6])) : -1; + e.recast_delay = row[7] ? static_cast(atoi(row[7])) : -1; + e.priority = row[8] ? static_cast(atoi(row[8])) : 0; + e.resist_adjust = row[9] ? static_cast(atoi(row[9])) : 0; + e.min_hp = row[10] ? static_cast(atoi(row[10])) : 0; + e.max_hp = row[11] ? static_cast(atoi(row[11])) : 0; + e.min_expansion = row[12] ? static_cast(atoi(row[12])) : -1; + e.max_expansion = row[13] ? static_cast(atoi(row[13])) : -1; + e.content_flags = row[14] ? row[14] : ""; + e.content_flags_disabled = row[15] ? row[15] : ""; return e; } @@ -215,6 +235,10 @@ public: v.push_back(columns[9] + " = " + std::to_string(e.resist_adjust)); v.push_back(columns[10] + " = " + std::to_string(e.min_hp)); v.push_back(columns[11] + " = " + std::to_string(e.max_hp)); + v.push_back(columns[12] + " = " + std::to_string(e.min_expansion)); + v.push_back(columns[13] + " = " + std::to_string(e.max_expansion)); + v.push_back(columns[14] + " = '" + Strings::Escape(e.content_flags) + "'"); + v.push_back(columns[15] + " = '" + Strings::Escape(e.content_flags_disabled) + "'"); auto results = db.QueryDatabase( fmt::format( @@ -248,6 +272,10 @@ public: v.push_back(std::to_string(e.resist_adjust)); 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.min_expansion)); + v.push_back(std::to_string(e.max_expansion)); + v.push_back("'" + Strings::Escape(e.content_flags) + "'"); + v.push_back("'" + Strings::Escape(e.content_flags_disabled) + "'"); auto results = db.QueryDatabase( fmt::format( @@ -289,6 +317,10 @@ public: v.push_back(std::to_string(e.resist_adjust)); 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.min_expansion)); + v.push_back(std::to_string(e.max_expansion)); + v.push_back("'" + Strings::Escape(e.content_flags) + "'"); + v.push_back("'" + Strings::Escape(e.content_flags_disabled) + "'"); insert_chunks.push_back("(" + Strings::Implode(",", v) + ")"); } @@ -322,18 +354,22 @@ public: for (auto row = results.begin(); row != results.end(); ++row) { NpcSpellsEntries e{}; - e.id = row[0] ? static_cast(strtoul(row[0], nullptr, 10)) : 0; - e.npc_spells_id = row[1] ? static_cast(atoi(row[1])) : 0; - e.spellid = row[2] ? static_cast(strtoul(row[2], nullptr, 10)) : 0; - e.type = row[3] ? static_cast(strtoul(row[3], nullptr, 10)) : 0; - e.minlevel = row[4] ? static_cast(strtoul(row[4], nullptr, 10)) : 0; - e.maxlevel = row[5] ? static_cast(strtoul(row[5], nullptr, 10)) : 255; - e.manacost = row[6] ? static_cast(atoi(row[6])) : -1; - e.recast_delay = row[7] ? static_cast(atoi(row[7])) : -1; - e.priority = row[8] ? static_cast(atoi(row[8])) : 0; - e.resist_adjust = row[9] ? static_cast(atoi(row[9])) : 0; - e.min_hp = row[10] ? static_cast(atoi(row[10])) : 0; - e.max_hp = row[11] ? static_cast(atoi(row[11])) : 0; + e.id = row[0] ? static_cast(strtoul(row[0], nullptr, 10)) : 0; + e.npc_spells_id = row[1] ? static_cast(atoi(row[1])) : 0; + e.spellid = row[2] ? static_cast(strtoul(row[2], nullptr, 10)) : 0; + e.type = row[3] ? static_cast(strtoul(row[3], nullptr, 10)) : 0; + e.minlevel = row[4] ? static_cast(strtoul(row[4], nullptr, 10)) : 0; + e.maxlevel = row[5] ? static_cast(strtoul(row[5], nullptr, 10)) : 255; + e.manacost = row[6] ? static_cast(atoi(row[6])) : -1; + e.recast_delay = row[7] ? static_cast(atoi(row[7])) : -1; + e.priority = row[8] ? static_cast(atoi(row[8])) : 0; + e.resist_adjust = row[9] ? static_cast(atoi(row[9])) : 0; + e.min_hp = row[10] ? static_cast(atoi(row[10])) : 0; + e.max_hp = row[11] ? static_cast(atoi(row[11])) : 0; + e.min_expansion = row[12] ? static_cast(atoi(row[12])) : -1; + e.max_expansion = row[13] ? static_cast(atoi(row[13])) : -1; + e.content_flags = row[14] ? row[14] : ""; + e.content_flags_disabled = row[15] ? row[15] : ""; all_entries.push_back(e); } @@ -358,18 +394,22 @@ public: for (auto row = results.begin(); row != results.end(); ++row) { NpcSpellsEntries e{}; - e.id = row[0] ? static_cast(strtoul(row[0], nullptr, 10)) : 0; - e.npc_spells_id = row[1] ? static_cast(atoi(row[1])) : 0; - e.spellid = row[2] ? static_cast(strtoul(row[2], nullptr, 10)) : 0; - e.type = row[3] ? static_cast(strtoul(row[3], nullptr, 10)) : 0; - e.minlevel = row[4] ? static_cast(strtoul(row[4], nullptr, 10)) : 0; - e.maxlevel = row[5] ? static_cast(strtoul(row[5], nullptr, 10)) : 255; - e.manacost = row[6] ? static_cast(atoi(row[6])) : -1; - e.recast_delay = row[7] ? static_cast(atoi(row[7])) : -1; - e.priority = row[8] ? static_cast(atoi(row[8])) : 0; - e.resist_adjust = row[9] ? static_cast(atoi(row[9])) : 0; - e.min_hp = row[10] ? static_cast(atoi(row[10])) : 0; - e.max_hp = row[11] ? static_cast(atoi(row[11])) : 0; + e.id = row[0] ? static_cast(strtoul(row[0], nullptr, 10)) : 0; + e.npc_spells_id = row[1] ? static_cast(atoi(row[1])) : 0; + e.spellid = row[2] ? static_cast(strtoul(row[2], nullptr, 10)) : 0; + e.type = row[3] ? static_cast(strtoul(row[3], nullptr, 10)) : 0; + e.minlevel = row[4] ? static_cast(strtoul(row[4], nullptr, 10)) : 0; + e.maxlevel = row[5] ? static_cast(strtoul(row[5], nullptr, 10)) : 255; + e.manacost = row[6] ? static_cast(atoi(row[6])) : -1; + e.recast_delay = row[7] ? static_cast(atoi(row[7])) : -1; + e.priority = row[8] ? static_cast(atoi(row[8])) : 0; + e.resist_adjust = row[9] ? static_cast(atoi(row[9])) : 0; + e.min_hp = row[10] ? static_cast(atoi(row[10])) : 0; + e.max_hp = row[11] ? static_cast(atoi(row[11])) : 0; + e.min_expansion = row[12] ? static_cast(atoi(row[12])) : -1; + e.max_expansion = row[13] ? static_cast(atoi(row[13])) : -1; + e.content_flags = row[14] ? row[14] : ""; + e.content_flags_disabled = row[15] ? row[15] : ""; all_entries.push_back(e); } @@ -456,6 +496,10 @@ public: v.push_back(std::to_string(e.resist_adjust)); 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.min_expansion)); + v.push_back(std::to_string(e.max_expansion)); + v.push_back("'" + Strings::Escape(e.content_flags) + "'"); + v.push_back("'" + Strings::Escape(e.content_flags_disabled) + "'"); auto results = db.QueryDatabase( fmt::format( @@ -490,6 +534,10 @@ public: v.push_back(std::to_string(e.resist_adjust)); 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.min_expansion)); + v.push_back(std::to_string(e.max_expansion)); + v.push_back("'" + Strings::Escape(e.content_flags) + "'"); + v.push_back("'" + Strings::Escape(e.content_flags_disabled) + "'"); insert_chunks.push_back("(" + Strings::Implode(",", v) + ")"); } diff --git a/common/servertalk.h b/common/servertalk.h index 98cb8c141..7830f0ecf 100644 --- a/common/servertalk.h +++ b/common/servertalk.h @@ -277,6 +277,7 @@ #define ServerOP_ReloadLoot 0x4127 #define ServerOP_ReloadBaseData 0x4128 #define ServerOP_ReloadSkillCaps 0x4129 +#define ServerOP_ReloadNPCSpells 0x4130 #define ServerOP_CZDialogueWindow 0x4500 #define ServerOP_CZLDoNUpdate 0x4501 diff --git a/common/version.h b/common/version.h index 47c648d0c..9d7d7d165 100644 --- a/common/version.h +++ b/common/version.h @@ -42,7 +42,7 @@ * Manifest: https://github.com/EQEmu/Server/blob/master/utils/sql/db_update_manifest.txt */ -#define CURRENT_BINARY_DATABASE_VERSION 9278 +#define CURRENT_BINARY_DATABASE_VERSION 9279 #define CURRENT_BINARY_BOTS_DATABASE_VERSION 9044 #endif diff --git a/world/eqemu_api_world_data_service.cpp b/world/eqemu_api_world_data_service.cpp index adbb873aa..bfa7f6a1b 100644 --- a/world/eqemu_api_world_data_service.cpp +++ b/world/eqemu_api_world_data_service.cpp @@ -152,6 +152,7 @@ std::vector reload_types = { Reload{.command = "loot", .opcode = ServerOP_ReloadLoot, .desc = "Loot"}, Reload{.command = "merchants", .opcode = ServerOP_ReloadMerchants, .desc = "Merchants"}, Reload{.command = "npc_emotes", .opcode = ServerOP_ReloadNPCEmotes, .desc = "NPC Emotes"}, + Reload{.command = "npc_spells", .opcode = ServerOP_ReloadNPCSpells, .desc = "NPC Spells"}, Reload{.command = "objects", .opcode = ServerOP_ReloadObjects, .desc = "Objects"}, Reload{.command = "opcodes", .opcode = ServerOP_ReloadOpcodes, .desc = "Opcodes"}, Reload{.command = "perl_export", .opcode = ServerOP_ReloadPerlExportSettings, .desc = "Perl Event Export Settings"}, diff --git a/world/zoneserver.cpp b/world/zoneserver.cpp index cf3fe0014..4399a9c27 100644 --- a/world/zoneserver.cpp +++ b/world/zoneserver.cpp @@ -1410,6 +1410,7 @@ void ZoneServer::HandleMessage(uint16 opcode, const EQ::Net::Packet &p) { case ServerOP_ReloadLevelEXPMods: case ServerOP_ReloadMerchants: case ServerOP_ReloadNPCEmotes: + case ServerOP_ReloadNPCSpells: case ServerOP_ReloadObjects: case ServerOP_ReloadPerlExportSettings: case ServerOP_ReloadStaticZoneData: diff --git a/zone/client.cpp b/zone/client.cpp index e7535fb2c..e7073a647 100644 --- a/zone/client.cpp +++ b/zone/client.cpp @@ -9367,6 +9367,7 @@ void Client::ShowDevToolsMenu() menu_reload_five += Saylink::Silent("#reload merchants", "Merchants"); menu_reload_five += " | " + Saylink::Silent("#reload npc_emotes", "NPC Emotes"); + menu_reload_five += " | " + Saylink::Silent("#reload npc_spells", "NPC Spells"); menu_reload_five += " | " + Saylink::Silent("#reload objects", "Objects"); menu_reload_five += " | " + Saylink::Silent("#reload opcodes", "Opcodes"); @@ -11448,6 +11449,15 @@ void Client::SendReloadCommandMessages() { ).c_str() ); + auto npc_spells_link = Saylink::Silent("#reload npc_spells"); + Message( + Chat::White, + fmt::format( + "Usage: {} - Reloads NPC Spells globally", + npc_spells_link + ).c_str() + ); + auto objects_link = Saylink::Silent("#reload objects"); Message( diff --git a/zone/gm_commands/reload.cpp b/zone/gm_commands/reload.cpp index 2566d3ba0..361b02a95 100644 --- a/zone/gm_commands/reload.cpp +++ b/zone/gm_commands/reload.cpp @@ -29,6 +29,7 @@ void command_reload(Client *c, const Seperator *sep) bool is_loot = !strcasecmp(sep->arg[1], "loot"); bool is_merchants = !strcasecmp(sep->arg[1], "merchants"); bool is_npc_emotes = !strcasecmp(sep->arg[1], "npc_emotes"); + bool is_npc_spells = !strcasecmp(sep->arg[1], "npc_spells"); bool is_objects = !strcasecmp(sep->arg[1], "objects"); bool is_opcodes = !strcasecmp(sep->arg[1], "opcodes") || is_opcodes_reload_alias; bool is_perl_export = !strcasecmp(sep->arg[1], "perl_export"); @@ -62,6 +63,7 @@ void command_reload(Client *c, const Seperator *sep) !is_loot && !is_merchants && !is_npc_emotes && + !is_npc_spells && !is_objects && !is_opcodes && !is_perl_export && @@ -137,6 +139,9 @@ void command_reload(Client *c, const Seperator *sep) } else if (is_npc_emotes) { c->Message(Chat::White, "Attempting to reload NPC Emotes globally."); pack = new ServerPacket(ServerOP_ReloadNPCEmotes, 0); + } else if (is_npc_spells) { + c->Message(Chat::White, "Attempting to reload NPC Spells globally."); + pack = new ServerPacket(ServerOP_ReloadNPCSpells, 0); } else if (is_objects) { c->Message(Chat::White, "Attempting to reload Objects globally."); pack = new ServerPacket(ServerOP_ReloadObjects, 0); diff --git a/zone/mob_ai.cpp b/zone/mob_ai.cpp index b43355303..13df61681 100644 --- a/zone/mob_ai.cpp +++ b/zone/mob_ai.cpp @@ -33,6 +33,9 @@ #include "../common/data_verification.h" #include "bot.h" +#include "../common/repositories/npc_spells_repository.h" +#include "../common/repositories/npc_spells_entries_repository.h" +#include "../common/repositories/criteria/content_filter_criteria.h" #include #include @@ -2841,102 +2844,92 @@ void NPC::AISpellsList(Client *c) return; } -DBnpcspells_Struct *ZoneDatabase::GetNPCSpells(uint32 iDBSpellsID) +DBnpcspells_Struct *ZoneDatabase::GetNPCSpells(uint32 npc_spells_id) { - if (iDBSpellsID == 0) + if (npc_spells_id == 0) { return nullptr; + } - auto it = npc_spells_cache.find(iDBSpellsID); - + auto it = npc_spells_cache.find(npc_spells_id); if (it != npc_spells_cache.end()) { // it's in the cache, easy =) return &it->second; } - if (!npc_spells_loadtried.count(iDBSpellsID)) { // no reason to ask the DB again if we have failed once already - npc_spells_loadtried.insert(iDBSpellsID); + if (!npc_spells_loadtried.count(npc_spells_id)) { // no reason to ask the DB again if we have failed once already + npc_spells_loadtried.insert(npc_spells_id); - std::string query = StringFormat("SELECT id, parent_list, attack_proc, proc_chance, " - "range_proc, rproc_chance, defensive_proc, dproc_chance, " - "fail_recast, engaged_no_sp_recast_min, engaged_no_sp_recast_max, " - "engaged_b_self_chance, engaged_b_other_chance, engaged_d_chance, " - "pursue_no_sp_recast_min, pursue_no_sp_recast_max, " - "pursue_d_chance, idle_no_sp_recast_min, idle_no_sp_recast_max, " - "idle_b_chance FROM npc_spells WHERE id=%d", - iDBSpellsID); - auto results = QueryDatabase(query); - if (!results.Success()) { + auto ns = NpcSpellsRepository::FindOne(*this, npc_spells_id); + if (!ns.id) { return nullptr; } - if (results.RowCount() != 1) - return nullptr; + DBnpcspells_Struct ss; - auto row = results.begin(); - DBnpcspells_Struct spell_set; + ss.parent_list = ns.parent_list; + ss.attack_proc = ns.attack_proc; + ss.proc_chance = ns.proc_chance; + ss.range_proc = ns.range_proc; + ss.rproc_chance = ns.rproc_chance; + ss.defensive_proc = ns.defensive_proc; + ss.dproc_chance = ns.dproc_chance; + ss.fail_recast = ns.fail_recast; + ss.engaged_no_sp_recast_min = ns.engaged_no_sp_recast_min; + ss.engaged_no_sp_recast_max = ns.engaged_no_sp_recast_max; + ss.engaged_beneficial_self_chance = ns.engaged_b_self_chance; + ss.engaged_beneficial_other_chance = ns.engaged_b_other_chance; + ss.engaged_detrimental_chance = ns.engaged_d_chance; + ss.pursue_no_sp_recast_min = ns.pursue_no_sp_recast_min; + ss.pursue_no_sp_recast_max = ns.pursue_no_sp_recast_max; + ss.pursue_detrimental_chance = ns.pursue_d_chance; + ss.idle_no_sp_recast_min = ns.idle_no_sp_recast_min; + ss.idle_no_sp_recast_max = ns.idle_no_sp_recast_max; + ss.idle_beneficial_chance = ns.idle_b_chance; - spell_set.parent_list = Strings::ToInt(row[1]); - spell_set.attack_proc = Strings::ToInt(row[2]); - spell_set.proc_chance = Strings::ToInt(row[3]); - spell_set.range_proc = Strings::ToInt(row[4]); - spell_set.rproc_chance = Strings::ToInt(row[5]); - spell_set.defensive_proc = Strings::ToInt(row[6]); - spell_set.dproc_chance = Strings::ToInt(row[7]); - spell_set.fail_recast = Strings::ToInt(row[8]); - spell_set.engaged_no_sp_recast_min = Strings::ToInt(row[9]); - spell_set.engaged_no_sp_recast_max = Strings::ToInt(row[10]); - spell_set.engaged_beneficial_self_chance = Strings::ToInt(row[11]); - spell_set.engaged_beneficial_other_chance = Strings::ToInt(row[12]); - spell_set.engaged_detrimental_chance = Strings::ToInt(row[13]); - spell_set.pursue_no_sp_recast_min = Strings::ToInt(row[14]); - spell_set.pursue_no_sp_recast_max = Strings::ToInt(row[15]); - spell_set.pursue_detrimental_chance = Strings::ToInt(row[16]); - spell_set.idle_no_sp_recast_min = Strings::ToInt(row[17]); - spell_set.idle_no_sp_recast_max = Strings::ToInt(row[18]); - spell_set.idle_beneficial_chance = Strings::ToInt(row[19]); + auto entries = NpcSpellsEntriesRepository::GetWhere( + *this, + fmt::format( + "npc_spells_id = {} {} ORDER BY minlevel", + npc_spells_id, + ContentFilterCriteria::apply() + ) + ); - // pulling fixed values from an auto-increment field is dangerous... - query = StringFormat( - "SELECT spellid, type, minlevel, maxlevel, " - "manacost, recast_delay, priority, min_hp, max_hp, resist_adjust " - "FROM npc_spells_entries " - "WHERE npc_spells_id=%d ORDER BY minlevel", - iDBSpellsID); - results = QueryDatabase(query); - - if (!results.Success()) { + if (entries.empty()) { return nullptr; } - int entryIndex = 0; - for (row = results.begin(); row != results.end(); ++row, ++entryIndex) { - DBnpcspells_entries_Struct entry; - int spell_id = Strings::ToInt(row[0]); - entry.spellid = spell_id; - entry.type = Strings::ToUnsignedInt(row[1]); - entry.minlevel = Strings::ToInt(row[2]); - entry.maxlevel = Strings::ToInt(row[3]); - entry.manacost = Strings::ToInt(row[4]); - entry.recast_delay = Strings::ToInt(row[5]); - entry.priority = Strings::ToInt(row[6]); - entry.min_hp = Strings::ToInt(row[7]); - entry.max_hp = Strings::ToInt(row[8]); + for (auto &e: entries) { + DBnpcspells_entries_Struct se{}; + + se.spellid = e.spellid; + se.type = e.type; + se.minlevel = e.minlevel; + se.maxlevel = e.maxlevel; + se.manacost = e.manacost; + se.recast_delay = e.recast_delay; + se.priority = e.priority; + se.min_hp = e.min_hp; + se.max_hp = e.max_hp; // some spell types don't make much since to be priority 0, so fix that - if (!(entry.type & SPELL_TYPES_INNATE) && entry.priority == 0) - entry.priority = 1; + if (!(se.type & SPELL_TYPES_INNATE) && se.priority == 0) { + se.priority = 1; + } - if (row[9]) - entry.resist_adjust = Strings::ToInt(row[9]); - else if (IsValidSpell(spell_id)) - entry.resist_adjust = spells[spell_id].resist_difficulty; + if (e.resist_adjust) { + se.resist_adjust = e.resist_adjust; + } + else if (IsValidSpell(e.id)) { + se.resist_adjust = spells[e.id].resist_difficulty; + } - spell_set.entries.push_back(entry); + ss.entries.push_back(se); } - npc_spells_cache.emplace(std::make_pair(iDBSpellsID, spell_set)); + npc_spells_cache.emplace(std::make_pair(npc_spells_id, ss)); - return &npc_spells_cache[iDBSpellsID]; - } + return &npc_spells_cache[npc_spells_id]; + } return nullptr; } diff --git a/zone/worldserver.cpp b/zone/worldserver.cpp index e43616137..358fc4e75 100644 --- a/zone/worldserver.cpp +++ b/zone/worldserver.cpp @@ -2086,6 +2086,17 @@ void WorldServer::HandleMessage(uint16 opcode, const EQ::Net::Packet &p) } break; } + case ServerOP_ReloadNPCSpells: + { + if (zone && zone->IsLoaded()) { + zone->SendReloadMessage("NPC Spells"); + content_db.ClearNPCSpells(); + for (auto& e : entity_list.GetNPCList()) { + e.second->ReloadSpells(); + } + } + break; + } case ServerOP_ReloadPerlExportSettings: { zone->SendReloadMessage("Perl Event Export Settings"); diff --git a/zone/zone.cpp b/zone/zone.cpp index a4717f173..7d49d5950 100644 --- a/zone/zone.cpp +++ b/zone/zone.cpp @@ -2899,6 +2899,8 @@ std::string Zone::GetZoneDescription() void Zone::SendReloadMessage(std::string reload_type) { + LogInfo("Reloaded [{}]", reload_type); + worldserver.SendEmoteMessage( 0, 0, diff --git a/zone/zonedb.h b/zone/zonedb.h index ed3bccd8b..10e8a44ed 100644 --- a/zone/zonedb.h +++ b/zone/zonedb.h @@ -569,7 +569,7 @@ public: bool GetAuraEntry(uint16 spell_id, AuraRecord &record); void LoadGlobalLoot(); - DBnpcspells_Struct* GetNPCSpells(uint32 iDBSpellsID); + DBnpcspells_Struct* GetNPCSpells(uint32 npc_spells_id); DBnpcspellseffects_Struct* GetNPCSpellsEffects(uint32 iDBSpellsEffectsID); void ClearNPCSpells() { npc_spells_cache.clear(); npc_spells_loadtried.clear(); } const NPCType* LoadNPCTypesData(uint32 id, bool bulk_load = false);