diff --git a/common/database/database_update_manifest.cpp b/common/database/database_update_manifest.cpp index cbbba58d0..17c81adff 100644 --- a/common/database/database_update_manifest.cpp +++ b/common/database/database_update_manifest.cpp @@ -6912,6 +6912,18 @@ CREATE TABLE `zone_state_spawns` ( )", .content_schema_update = false }, + ManifestEntry{ + .version = 9308, + .description = "2025_add_multivalue_support_to_evolving_subtype.sql", + .check = "SHOW COLUMNS FROM `items_evolving_details` LIKE 'sub_type'", + .condition = "missing", + .match = "varchar(200)", + .sql = R"( +ALTER TABLE `items_evolving_details` + CHANGE COLUMN `sub_type` `sub_type` VARCHAR(200) NULL DEFAULT '0' AFTER `type`; +)", + .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_items_evolving_details_repository.h b/common/repositories/base/base_items_evolving_details_repository.h index 0f932bfb8..5930b3cd8 100644 --- a/common/repositories/base/base_items_evolving_details_repository.h +++ b/common/repositories/base/base_items_evolving_details_repository.h @@ -19,13 +19,13 @@ class BaseItemsEvolvingDetailsRepository { public: struct ItemsEvolvingDetails { - uint32_t id; - uint32_t item_evo_id; - uint32_t item_evolve_level; - uint32_t item_id; - uint32_t type; - uint32_t sub_type; - int64_t required_amount; + uint32_t id; + uint32_t item_evo_id; + uint32_t item_evolve_level; + uint32_t item_id; + uint32_t type; + std::string sub_type; + int64_t required_amount; }; static std::string PrimaryKey() @@ -101,7 +101,7 @@ public: e.item_evolve_level = 0; e.item_id = 0; e.type = 0; - e.sub_type = 0; + e.sub_type = "0"; e.required_amount = 0; return e; @@ -144,7 +144,7 @@ public: e.item_evolve_level = row[2] ? static_cast(strtoul(row[2], nullptr, 10)) : 0; e.item_id = row[3] ? static_cast(strtoul(row[3], nullptr, 10)) : 0; e.type = row[4] ? static_cast(strtoul(row[4], nullptr, 10)) : 0; - e.sub_type = row[5] ? static_cast(strtoul(row[5], nullptr, 10)) : 0; + e.sub_type = row[5] ? row[5] : "0"; e.required_amount = row[6] ? strtoll(row[6], nullptr, 10) : 0; return e; @@ -183,7 +183,7 @@ public: v.push_back(columns[2] + " = " + std::to_string(e.item_evolve_level)); v.push_back(columns[3] + " = " + std::to_string(e.item_id)); v.push_back(columns[4] + " = " + std::to_string(e.type)); - v.push_back(columns[5] + " = " + std::to_string(e.sub_type)); + v.push_back(columns[5] + " = '" + Strings::Escape(e.sub_type) + "'"); v.push_back(columns[6] + " = " + std::to_string(e.required_amount)); auto results = db.QueryDatabase( @@ -211,7 +211,7 @@ public: v.push_back(std::to_string(e.item_evolve_level)); v.push_back(std::to_string(e.item_id)); v.push_back(std::to_string(e.type)); - v.push_back(std::to_string(e.sub_type)); + v.push_back("'" + Strings::Escape(e.sub_type) + "'"); v.push_back(std::to_string(e.required_amount)); auto results = db.QueryDatabase( @@ -247,7 +247,7 @@ public: v.push_back(std::to_string(e.item_evolve_level)); v.push_back(std::to_string(e.item_id)); v.push_back(std::to_string(e.type)); - v.push_back(std::to_string(e.sub_type)); + v.push_back("'" + Strings::Escape(e.sub_type) + "'"); v.push_back(std::to_string(e.required_amount)); insert_chunks.push_back("(" + Strings::Implode(",", v) + ")"); @@ -287,7 +287,7 @@ public: e.item_evolve_level = row[2] ? static_cast(strtoul(row[2], nullptr, 10)) : 0; e.item_id = row[3] ? static_cast(strtoul(row[3], nullptr, 10)) : 0; e.type = row[4] ? static_cast(strtoul(row[4], nullptr, 10)) : 0; - e.sub_type = row[5] ? static_cast(strtoul(row[5], nullptr, 10)) : 0; + e.sub_type = row[5] ? row[5] : "0"; e.required_amount = row[6] ? strtoll(row[6], nullptr, 10) : 0; all_entries.push_back(e); @@ -318,7 +318,7 @@ public: e.item_evolve_level = row[2] ? static_cast(strtoul(row[2], nullptr, 10)) : 0; e.item_id = row[3] ? static_cast(strtoul(row[3], nullptr, 10)) : 0; e.type = row[4] ? static_cast(strtoul(row[4], nullptr, 10)) : 0; - e.sub_type = row[5] ? static_cast(strtoul(row[5], nullptr, 10)) : 0; + e.sub_type = row[5] ? row[5] : "0"; e.required_amount = row[6] ? strtoll(row[6], nullptr, 10) : 0; all_entries.push_back(e); @@ -399,7 +399,7 @@ public: v.push_back(std::to_string(e.item_evolve_level)); v.push_back(std::to_string(e.item_id)); v.push_back(std::to_string(e.type)); - v.push_back(std::to_string(e.sub_type)); + v.push_back("'" + Strings::Escape(e.sub_type) + "'"); v.push_back(std::to_string(e.required_amount)); auto results = db.QueryDatabase( @@ -428,7 +428,7 @@ public: v.push_back(std::to_string(e.item_evolve_level)); v.push_back(std::to_string(e.item_id)); v.push_back(std::to_string(e.type)); - v.push_back(std::to_string(e.sub_type)); + v.push_back("'" + Strings::Escape(e.sub_type) + "'"); v.push_back(std::to_string(e.required_amount)); insert_chunks.push_back("(" + Strings::Implode(",", v) + ")"); diff --git a/common/version.h b/common/version.h index 4c9344b79..9a77205f2 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 9307 +#define CURRENT_BINARY_DATABASE_VERSION 9308 #define CURRENT_BINARY_BOTS_DATABASE_VERSION 9054 #endif diff --git a/zone/client_evolving_items.cpp b/zone/client_evolving_items.cpp index a22623534..72488405a 100644 --- a/zone/client_evolving_items.cpp +++ b/zone/client_evolving_items.cpp @@ -8,7 +8,8 @@ #include "worldserver.h" extern WorldServer worldserver; -extern QueryServ* QServ; +extern QueryServ* QServ; +const std::string SUB_TYPE_DELIMITER = "."; void Client::DoEvolveItemToggle(const EQApplicationPacket *app) { @@ -77,11 +78,12 @@ void Client::ProcessEvolvingItem(const uint64 exp, const Mob *mob) "CharacterID [{}] found equipped item ID [{}]", CharacterID(), inst->GetID()); if (!inst->IsEvolving() || !inst->GetEvolveActivated()) { LogEvolveItemDetail( - "CharacterID [{}], item ID [{}] not an evolving item.", CharacterID(), inst->GetID()); + "CharacterID [{}], item ID [{}] not an evolving item.", CharacterID(), inst->GetID() + ); continue; } - if (inst->GetTimers().contains("evolve") && !inst->GetTimers().at("evolve").Check()) { + if (inst->GetTimers().contains("evolve") && !inst->GetTimers().at("evolve").Check(false)) { LogEvolveItemDetail( "CharacterID [{}], item ID [{}] timer not yet expired. [{}] secs remaining.", CharacterID(), @@ -98,32 +100,39 @@ void Client::ProcessEvolvingItem(const uint64 exp, const Mob *mob) CharacterID(), inst->GetID(), type, - sub_type); + sub_type + ); + + auto sub_types = Strings::Split(sub_type, SUB_TYPE_DELIMITER); + auto has_sub_type = [&](uint32_t type) { + return Strings::Contains(sub_types, std::to_string(type)); + }; switch (type) { case EvolvingItems::Types::AMOUNT_OF_EXP: { LogEvolveItemDetail("Type [{}] Processing sub_type", type); - if (sub_type == EvolvingItems::SubTypes::ALL_EXP || - (sub_type == EvolvingItems::SubTypes::GROUP_EXP && IsGrouped())) { - LogEvolveItemDetail("Sub_Type [{}] Processing Item", sub_type); - inst->SetEvolveAddToCurrentAmount(exp * RuleR(EvolvingItems, PercentOfGroupExperience) / 100); + + // Determine the evolve amount based on sub_type conditions + int evolve_amount = 0; + + if (has_sub_type(EvolvingItems::SubTypes::ALL_EXP) || + (has_sub_type(EvolvingItems::SubTypes::GROUP_EXP) && IsGrouped())) { + evolve_amount = exp * RuleR(EvolvingItems, PercentOfGroupExperience) / 100; } - else if ( - sub_type == EvolvingItems::SubTypes::ALL_EXP || - (sub_type == EvolvingItems::SubTypes::RAID_EXP && IsRaidGrouped())) { - LogEvolveItemDetail("Sub_Type [{}] Processing Item", sub_type); - inst->SetEvolveAddToCurrentAmount(exp * RuleR(EvolvingItems, PercentOfRaidExperience) / 100); + else if (has_sub_type(EvolvingItems::SubTypes::ALL_EXP) || + (has_sub_type(EvolvingItems::SubTypes::RAID_EXP) && IsRaidGrouped())) { + evolve_amount = exp * RuleR(EvolvingItems, PercentOfRaidExperience) / 100; } - else if ( - sub_type == EvolvingItems::SubTypes::ALL_EXP || sub_type == EvolvingItems::SubTypes::SOLO_EXP) { - LogEvolveItemDetail("Sub_Type [{}] Processing Item", sub_type); - inst->SetEvolveAddToCurrentAmount(exp * RuleR(EvolvingItems, PercentOfSoloExperience) / 100); + else if (has_sub_type(EvolvingItems::SubTypes::ALL_EXP) || + has_sub_type(EvolvingItems::SubTypes::SOLO_EXP)) { + evolve_amount = exp * RuleR(EvolvingItems, PercentOfSoloExperience) / 100; } inst->CalculateEvolveProgression(); auto e = CharacterEvolvingItemsRepository::SetCurrentAmountAndProgression( - database, inst->GetEvolveUniqueID(), inst->GetEvolveCurrentAmount(), inst->GetEvolveProgression()); + database, inst->GetEvolveUniqueID(), inst->GetEvolveCurrentAmount(), inst->GetEvolveProgression() + ); if (!e.id) { break; } @@ -146,7 +155,7 @@ void Client::ProcessEvolvingItem(const uint64 exp, const Mob *mob) } case EvolvingItems::Types::SPECIFIC_MOB_RACE: { LogEvolveItemDetail("Type [{}] Processing sub type", type); - if (mob && mob->GetRace() == sub_type) { + if (mob && has_sub_type(mob->GetRace())) { LogEvolveItemDetail("Sub_Type [{}] Processing Item", sub_type); inst->SetEvolveAddToCurrentAmount(1); inst->CalculateEvolveProgression(); @@ -155,7 +164,8 @@ void Client::ProcessEvolvingItem(const uint64 exp, const Mob *mob) database, inst->GetEvolveUniqueID(), inst->GetEvolveCurrentAmount(), - inst->GetEvolveProgression()); + inst->GetEvolveProgression() + ); if (!e.id) { break; } @@ -163,7 +173,8 @@ void Client::ProcessEvolvingItem(const uint64 exp, const Mob *mob) SendEvolvingPacket(EvolvingItems::Actions::UPDATE_ITEMS, e); LogEvolveItem( - "Processing Complete for item id [{1}] Type 3 Specific Mob Race - SubType [{0}] " + "Processing Complete for item id [{1}] Type 3 Specific Mob Race - SubType " + "[{0}] " "- Increased count by 1 for [{1}]", sub_type, inst->GetID() @@ -178,7 +189,7 @@ void Client::ProcessEvolvingItem(const uint64 exp, const Mob *mob) } case EvolvingItems::Types::SPECIFIC_ZONE_ID: { LogEvolveItemDetail("Type [{}] Processing sub type", type); - if (mob && mob->GetZoneID() == sub_type) { + if (mob && has_sub_type(mob->GetZoneID())) { LogEvolveItemDetail("Sub_Type [{}] Processing Item", sub_type); inst->SetEvolveAddToCurrentAmount(1); inst->CalculateEvolveProgression(); @@ -187,7 +198,8 @@ void Client::ProcessEvolvingItem(const uint64 exp, const Mob *mob) database, inst->GetEvolveUniqueID(), inst->GetEvolveCurrentAmount(), - inst->GetEvolveProgression()); + inst->GetEvolveProgression() + ); if (!e.id) { break; } @@ -195,7 +207,8 @@ void Client::ProcessEvolvingItem(const uint64 exp, const Mob *mob) SendEvolvingPacket(EvolvingItems::Actions::UPDATE_ITEMS, e); LogEvolveItem( - "Processing Complete for item id [{1}] Type 4 Specific Zone ID - SubType [{0}] " + "Processing Complete for item id [{1}] Type 4 Specific Zone ID - SubType " + "[{0}] " "- Increased count by 1 for [{1}]", sub_type, inst->GetID() @@ -208,6 +221,44 @@ void Client::ProcessEvolvingItem(const uint64 exp, const Mob *mob) break; } + case EvolvingItems::Types::NUMBER_OF_KILLS: { + LogEvolveItemDetail("Type [{}] Processing sub type", type); + if (mob) { + if (mob->GetLevel() >= Strings::ToUnsignedInt(sub_types.front()) || + Strings::ToUnsignedInt(sub_types.front()) == 0 + ) { + LogEvolveItemDetail("Sub_Type [{}] Processing Item", sub_type); + inst->SetEvolveAddToCurrentAmount(1); + inst->CalculateEvolveProgression(); + + auto e = CharacterEvolvingItemsRepository::SetCurrentAmountAndProgression( + database, + inst->GetEvolveUniqueID(), + inst->GetEvolveCurrentAmount(), + inst->GetEvolveProgression() + ); + if (!e.id) { + break; + } + + SendEvolvingPacket(EvolvingItems::Actions::UPDATE_ITEMS, e); + + LogEvolveItem( + "Processing Complete for item id [{1}] Type 4 Specific Zone ID - SubType " + "[{0}] " + "- Increased count by 1 for [{1}]", + sub_type, + inst->GetID() + ); + } + } + + if (inst->GetEvolveProgression() >= 100) { + queue.push_back(inst); + } + + break; + } default: { } }