diff --git a/common/database/database_update_manifest.cpp b/common/database/database_update_manifest.cpp index 6ca189093..9e77b67d4 100644 --- a/common/database/database_update_manifest.cpp +++ b/common/database/database_update_manifest.cpp @@ -5480,6 +5480,20 @@ ADD INDEX `level_skill_cap`(`skill_id`, `class_id`, `level`, `cap`); ALTER TABLE `account` ADD COLUMN `auto_login_charname` varchar(64) CHARACTER SET latin1 COLLATE latin1_swedish_ci NOT NULL DEFAULT '' AFTER `charname`; )" + }, + ManifestEntry{ + .version = 9270, + .description = "2024_04_31_content_flagging_lootdrop_entries.sql", + .check = "SHOW COLUMNS FROM `lootdrop_entries` LIKE 'content_flags'", + .condition = "empty", + .match = "", + .sql = R"( +ALTER TABLE `lootdrop_entries` ADD `min_expansion` tinyint(4) NOT NULL DEFAULT -1; +ALTER TABLE `lootdrop_entries` ADD `max_expansion` tinyint(4) NOT NULL DEFAULT -1; +ALTER TABLE `lootdrop_entries` ADD `content_flags` varchar(100) NULL; +ALTER TABLE `lootdrop_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{ diff --git a/common/repositories/base/base_lootdrop_entries_repository.h b/common/repositories/base/base_lootdrop_entries_repository.h index 567f0dbe0..e8ecd4ddb 100644 --- a/common/repositories/base/base_lootdrop_entries_repository.h +++ b/common/repositories/base/base_lootdrop_entries_repository.h @@ -19,17 +19,21 @@ class BaseLootdropEntriesRepository { public: struct LootdropEntries { - uint32_t lootdrop_id; - int32_t item_id; - uint16_t item_charges; - uint8_t equip_item; - float chance; - float disabled_chance; - uint16_t trivial_min_level; - uint16_t trivial_max_level; - uint8_t multiplier; - uint16_t npc_min_level; - uint16_t npc_max_level; + uint32_t lootdrop_id; + int32_t item_id; + uint16_t item_charges; + uint8_t equip_item; + float chance; + float disabled_chance; + uint16_t trivial_min_level; + uint16_t trivial_max_level; + uint8_t multiplier; + uint16_t npc_min_level; + uint16_t npc_max_level; + int8_t min_expansion; + int8_t max_expansion; + std::string content_flags; + std::string content_flags_disabled; }; static std::string PrimaryKey() @@ -51,6 +55,10 @@ public: "multiplier", "npc_min_level", "npc_max_level", + "min_expansion", + "max_expansion", + "content_flags", + "content_flags_disabled", }; } @@ -68,6 +76,10 @@ public: "multiplier", "npc_min_level", "npc_max_level", + "min_expansion", + "max_expansion", + "content_flags", + "content_flags_disabled", }; } @@ -108,17 +120,21 @@ public: { LootdropEntries e{}; - e.lootdrop_id = 0; - e.item_id = 0; - e.item_charges = 1; - e.equip_item = 0; - e.chance = 1; - e.disabled_chance = 0; - e.trivial_min_level = 0; - e.trivial_max_level = 0; - e.multiplier = 1; - e.npc_min_level = 0; - e.npc_max_level = 0; + e.lootdrop_id = 0; + e.item_id = 0; + e.item_charges = 1; + e.equip_item = 0; + e.chance = 1; + e.disabled_chance = 0; + e.trivial_min_level = 0; + e.trivial_max_level = 0; + e.multiplier = 1; + e.npc_min_level = 0; + e.npc_max_level = 0; + e.min_expansion = -1; + e.max_expansion = -1; + e.content_flags = ""; + e.content_flags_disabled = ""; return e; } @@ -155,17 +171,21 @@ public: if (results.RowCount() == 1) { LootdropEntries e{}; - e.lootdrop_id = row[0] ? static_cast(strtoul(row[0], nullptr, 10)) : 0; - e.item_id = row[1] ? static_cast(atoi(row[1])) : 0; - e.item_charges = row[2] ? static_cast(strtoul(row[2], nullptr, 10)) : 1; - e.equip_item = row[3] ? static_cast(strtoul(row[3], nullptr, 10)) : 0; - e.chance = row[4] ? strtof(row[4], nullptr) : 1; - e.disabled_chance = row[5] ? strtof(row[5], nullptr) : 0; - e.trivial_min_level = row[6] ? static_cast(strtoul(row[6], nullptr, 10)) : 0; - e.trivial_max_level = row[7] ? static_cast(strtoul(row[7], nullptr, 10)) : 0; - e.multiplier = row[8] ? static_cast(strtoul(row[8], nullptr, 10)) : 1; - e.npc_min_level = row[9] ? static_cast(strtoul(row[9], nullptr, 10)) : 0; - e.npc_max_level = row[10] ? static_cast(strtoul(row[10], nullptr, 10)) : 0; + e.lootdrop_id = row[0] ? static_cast(strtoul(row[0], nullptr, 10)) : 0; + e.item_id = row[1] ? static_cast(atoi(row[1])) : 0; + e.item_charges = row[2] ? static_cast(strtoul(row[2], nullptr, 10)) : 1; + e.equip_item = row[3] ? static_cast(strtoul(row[3], nullptr, 10)) : 0; + e.chance = row[4] ? strtof(row[4], nullptr) : 1; + e.disabled_chance = row[5] ? strtof(row[5], nullptr) : 0; + e.trivial_min_level = row[6] ? static_cast(strtoul(row[6], nullptr, 10)) : 0; + e.trivial_max_level = row[7] ? static_cast(strtoul(row[7], nullptr, 10)) : 0; + e.multiplier = row[8] ? static_cast(strtoul(row[8], nullptr, 10)) : 1; + e.npc_min_level = row[9] ? static_cast(strtoul(row[9], nullptr, 10)) : 0; + e.npc_max_level = row[10] ? static_cast(strtoul(row[10], nullptr, 10)) : 0; + e.min_expansion = row[11] ? static_cast(atoi(row[11])) : -1; + e.max_expansion = row[12] ? static_cast(atoi(row[12])) : -1; + e.content_flags = row[13] ? row[13] : ""; + e.content_flags_disabled = row[14] ? row[14] : ""; return e; } @@ -210,6 +230,10 @@ public: v.push_back(columns[8] + " = " + std::to_string(e.multiplier)); v.push_back(columns[9] + " = " + std::to_string(e.npc_min_level)); v.push_back(columns[10] + " = " + std::to_string(e.npc_max_level)); + v.push_back(columns[11] + " = " + std::to_string(e.min_expansion)); + v.push_back(columns[12] + " = " + std::to_string(e.max_expansion)); + v.push_back(columns[13] + " = '" + Strings::Escape(e.content_flags) + "'"); + v.push_back(columns[14] + " = '" + Strings::Escape(e.content_flags_disabled) + "'"); auto results = db.QueryDatabase( fmt::format( @@ -242,6 +266,10 @@ public: v.push_back(std::to_string(e.multiplier)); v.push_back(std::to_string(e.npc_min_level)); v.push_back(std::to_string(e.npc_max_level)); + 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( @@ -282,6 +310,10 @@ public: v.push_back(std::to_string(e.multiplier)); v.push_back(std::to_string(e.npc_min_level)); v.push_back(std::to_string(e.npc_max_level)); + 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) + ")"); } @@ -315,17 +347,21 @@ public: for (auto row = results.begin(); row != results.end(); ++row) { LootdropEntries e{}; - e.lootdrop_id = row[0] ? static_cast(strtoul(row[0], nullptr, 10)) : 0; - e.item_id = row[1] ? static_cast(atoi(row[1])) : 0; - e.item_charges = row[2] ? static_cast(strtoul(row[2], nullptr, 10)) : 1; - e.equip_item = row[3] ? static_cast(strtoul(row[3], nullptr, 10)) : 0; - e.chance = row[4] ? strtof(row[4], nullptr) : 1; - e.disabled_chance = row[5] ? strtof(row[5], nullptr) : 0; - e.trivial_min_level = row[6] ? static_cast(strtoul(row[6], nullptr, 10)) : 0; - e.trivial_max_level = row[7] ? static_cast(strtoul(row[7], nullptr, 10)) : 0; - e.multiplier = row[8] ? static_cast(strtoul(row[8], nullptr, 10)) : 1; - e.npc_min_level = row[9] ? static_cast(strtoul(row[9], nullptr, 10)) : 0; - e.npc_max_level = row[10] ? static_cast(strtoul(row[10], nullptr, 10)) : 0; + e.lootdrop_id = row[0] ? static_cast(strtoul(row[0], nullptr, 10)) : 0; + e.item_id = row[1] ? static_cast(atoi(row[1])) : 0; + e.item_charges = row[2] ? static_cast(strtoul(row[2], nullptr, 10)) : 1; + e.equip_item = row[3] ? static_cast(strtoul(row[3], nullptr, 10)) : 0; + e.chance = row[4] ? strtof(row[4], nullptr) : 1; + e.disabled_chance = row[5] ? strtof(row[5], nullptr) : 0; + e.trivial_min_level = row[6] ? static_cast(strtoul(row[6], nullptr, 10)) : 0; + e.trivial_max_level = row[7] ? static_cast(strtoul(row[7], nullptr, 10)) : 0; + e.multiplier = row[8] ? static_cast(strtoul(row[8], nullptr, 10)) : 1; + e.npc_min_level = row[9] ? static_cast(strtoul(row[9], nullptr, 10)) : 0; + e.npc_max_level = row[10] ? static_cast(strtoul(row[10], nullptr, 10)) : 0; + e.min_expansion = row[11] ? static_cast(atoi(row[11])) : -1; + e.max_expansion = row[12] ? static_cast(atoi(row[12])) : -1; + e.content_flags = row[13] ? row[13] : ""; + e.content_flags_disabled = row[14] ? row[14] : ""; all_entries.push_back(e); } @@ -350,17 +386,21 @@ public: for (auto row = results.begin(); row != results.end(); ++row) { LootdropEntries e{}; - e.lootdrop_id = row[0] ? static_cast(strtoul(row[0], nullptr, 10)) : 0; - e.item_id = row[1] ? static_cast(atoi(row[1])) : 0; - e.item_charges = row[2] ? static_cast(strtoul(row[2], nullptr, 10)) : 1; - e.equip_item = row[3] ? static_cast(strtoul(row[3], nullptr, 10)) : 0; - e.chance = row[4] ? strtof(row[4], nullptr) : 1; - e.disabled_chance = row[5] ? strtof(row[5], nullptr) : 0; - e.trivial_min_level = row[6] ? static_cast(strtoul(row[6], nullptr, 10)) : 0; - e.trivial_max_level = row[7] ? static_cast(strtoul(row[7], nullptr, 10)) : 0; - e.multiplier = row[8] ? static_cast(strtoul(row[8], nullptr, 10)) : 1; - e.npc_min_level = row[9] ? static_cast(strtoul(row[9], nullptr, 10)) : 0; - e.npc_max_level = row[10] ? static_cast(strtoul(row[10], nullptr, 10)) : 0; + e.lootdrop_id = row[0] ? static_cast(strtoul(row[0], nullptr, 10)) : 0; + e.item_id = row[1] ? static_cast(atoi(row[1])) : 0; + e.item_charges = row[2] ? static_cast(strtoul(row[2], nullptr, 10)) : 1; + e.equip_item = row[3] ? static_cast(strtoul(row[3], nullptr, 10)) : 0; + e.chance = row[4] ? strtof(row[4], nullptr) : 1; + e.disabled_chance = row[5] ? strtof(row[5], nullptr) : 0; + e.trivial_min_level = row[6] ? static_cast(strtoul(row[6], nullptr, 10)) : 0; + e.trivial_max_level = row[7] ? static_cast(strtoul(row[7], nullptr, 10)) : 0; + e.multiplier = row[8] ? static_cast(strtoul(row[8], nullptr, 10)) : 1; + e.npc_min_level = row[9] ? static_cast(strtoul(row[9], nullptr, 10)) : 0; + e.npc_max_level = row[10] ? static_cast(strtoul(row[10], nullptr, 10)) : 0; + e.min_expansion = row[11] ? static_cast(atoi(row[11])) : -1; + e.max_expansion = row[12] ? static_cast(atoi(row[12])) : -1; + e.content_flags = row[13] ? row[13] : ""; + e.content_flags_disabled = row[14] ? row[14] : ""; all_entries.push_back(e); } @@ -446,6 +486,10 @@ public: v.push_back(std::to_string(e.multiplier)); v.push_back(std::to_string(e.npc_min_level)); v.push_back(std::to_string(e.npc_max_level)); + 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( @@ -479,6 +523,10 @@ public: v.push_back(std::to_string(e.multiplier)); v.push_back(std::to_string(e.npc_min_level)); v.push_back(std::to_string(e.npc_max_level)); + 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/version.h b/common/version.h index 3b68eacdd..ed24f2a88 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 9269 +#define CURRENT_BINARY_DATABASE_VERSION 9270 #define CURRENT_BINARY_BOTS_DATABASE_VERSION 9043 #endif diff --git a/zone/loot.cpp b/zone/loot.cpp index a81284c2f..198d79312 100644 --- a/zone/loot.cpp +++ b/zone/loot.cpp @@ -22,7 +22,7 @@ void NPC::AddLootTable(uint32 loottable_id, bool is_global) if (!npctype_id) { return; } - + if (!is_global) { m_loot_copper = 0; m_loot_silver = 0; @@ -145,11 +145,30 @@ void NPC::AddLootDropTable(uint32 lootdrop_id, uint8 drop_limit, uint8 min_drop) "-- NPC [{}] Lootdrop [{}] Item [{}] ({}_ Chance [{}] Multiplier [{}]", GetCleanName(), lootdrop_id, - database.GetItem(e.item_id)->Name, + database.GetItem(e.item_id) ? database.GetItem(e.item_id)->Name : "Unknown", e.item_id, e.chance, e.multiplier ); + + if (!content_service.DoesPassContentFiltering( + ContentFlags{ + .min_expansion = e.min_expansion, + .max_expansion = e.max_expansion, + .content_flags = e.content_flags, + .content_flags_disabled = e.content_flags_disabled + } + )) { + LogLoot( + "-- NPC [{}] Lootdrop [{}] Item [{}] ({}) does not pass content filtering", + GetCleanName(), + lootdrop_id, + e.item_id, + database.GetItem(e.item_id) ? database.GetItem(e.item_id)->Name : "Unknown" + ); + continue; + } + for (int j = 0; j < e.multiplier; ++j) { if (zone->random.Real(0.0, 100.0) <= e.chance && MeetsLootDropLevelRequirements(e, true)) { const EQ::ItemData *database_item = database.GetItem(e.item_id); @@ -215,6 +234,24 @@ void NPC::AddLootDropTable(uint32 lootdrop_id, uint8 drop_limit, uint8 min_drop) if (drops < min_drop || roll_table_chance_bypass || (float) zone->random.Real(0.0, 1.0) >= no_loot_prob) { float roll = (float) zone->random.Real(0.0, roll_t); for (const auto &e: le) { + if (!content_service.DoesPassContentFiltering( + ContentFlags{ + .min_expansion = e.min_expansion, + .max_expansion = e.max_expansion, + .content_flags = e.content_flags, + .content_flags_disabled = e.content_flags_disabled + } + )) { + LogLoot( + "-- NPC [{}] Lootdrop [{}] Item [{}] ({}) does not pass content filtering", + GetCleanName(), + lootdrop_id, + e.item_id, + database.GetItem(e.item_id) ? database.GetItem(e.item_id)->Name : "Unknown" + ); + continue; + } + const auto *db_item = database.GetItem(e.item_id); if (db_item) { // if it doesn't meet the requirements do nothing