diff --git a/common/database/database_update_manifest.cpp b/common/database/database_update_manifest.cpp index 2236b4ab9..d5abb2a0e 100644 --- a/common/database/database_update_manifest.cpp +++ b/common/database/database_update_manifest.cpp @@ -7188,9 +7188,24 @@ CHANGE COLUMN `field221` `caster_requirement_id` int(11) NULL DEFAULT 0 AFTER `n CHANGE COLUMN `field222` `spell_class` int(11) NULL DEFAULT 0 AFTER `caster_requirement_id`, CHANGE COLUMN `field223` `spell_subclass` int(11) NULL DEFAULT 0 AFTER `spell_class`, CHANGE COLUMN `field232` `no_remove` int(11) NOT NULL DEFAULT 0 AFTER `min_range`; -)" - } +)", + .content_schema_update = true + }, + ManifestEntry{ + .version = 9329, + .description = "2025_08_22_character_parcel_updates.sql", + .check = "SHOW COLUMNS FROM `character_parcels` LIKE 'evolve_amount'", + .condition = "empty", + .match = "", + .sql = R"( +ALTER TABLE `character_parcels` + ADD COLUMN `evolve_amount` INT UNSIGNED NOT NULL DEFAULT '0' AFTER `quantity`; +ALTER TABLE `character_parcels_containers` + ADD COLUMN `evolve_amount` INT UNSIGNED NOT NULL DEFAULT '0' AFTER `quantity`; +)", + .content_schema_update = false + }, // -- template; copy/paste this when you need to create a new entry // ManifestEntry{ // .version = 9228, diff --git a/common/evolving_items.cpp b/common/evolving_items.cpp index f7f99b7a6..f14a733f3 100644 --- a/common/evolving_items.cpp +++ b/common/evolving_items.cpp @@ -74,6 +74,10 @@ void EvolvingItemsManager::DoLootChecks(const uint32 char_id, const uint16 slot_ e.item_id = inst.GetID(); e.equipped = inst.GetEvolveEquipped(); e.final_item_id = EvolvingItemsManager::Instance()->GetFinalItemID(inst); + if (inst.GetEvolveCurrentAmount() > 0) { + e.current_amount = inst.GetEvolveCurrentAmount(); + inst.CalculateEvolveProgression(); + } auto r = CharacterEvolvingItemsRepository::InsertOne(*m_db, e); e.id = r.id; diff --git a/common/patches/rof2.cpp b/common/patches/rof2.cpp index 0d4043d30..b250a5de6 100644 --- a/common/patches/rof2.cpp +++ b/common/patches/rof2.cpp @@ -6481,7 +6481,7 @@ namespace RoF2 hdr.scaled_value = (inst->IsScaling() ? (inst->GetExp() / 100) : 0); hdr.instance_id = (inst->GetMerchantSlot() ? inst->GetMerchantSlot() : inst->GetSerialNumber()); hdr.parcel_item_id = packet_type == ItemPacketParcel ? inst->GetID() : 0; - if (item->EvolvingItem) { + if (item->EvolvingItem && packet_type != ItemPacketParcel && packet_type != ItemPacketMerchant) { hdr.instance_id = inst->GetEvolveUniqueID() & 0xFFFFFFFF; //lower dword hdr.parcel_item_id = inst->GetEvolveUniqueID() >> 32; //upper dword } @@ -6500,6 +6500,7 @@ namespace RoF2 if (item->EvolvingItem > 0) { RoF2::structs::EvolvingItem_Struct evotop; + inst->CalculateEvolveProgression(); evotop.final_item_id = inst->GetEvolveFinalItemID(); evotop.evolve_level = item->EvolvingLevel; diff --git a/common/repositories/base/base_character_parcels_containers_repository.h b/common/repositories/base/base_character_parcels_containers_repository.h index 9c98cc068..12ca611b7 100644 --- a/common/repositories/base/base_character_parcels_containers_repository.h +++ b/common/repositories/base/base_character_parcels_containers_repository.h @@ -30,6 +30,7 @@ public: uint32_t aug_slot_5; uint32_t aug_slot_6; uint32_t quantity; + uint32_t evolve_amount; }; static std::string PrimaryKey() @@ -51,6 +52,7 @@ public: "aug_slot_5", "aug_slot_6", "quantity", + "evolve_amount", }; } @@ -68,6 +70,7 @@ public: "aug_slot_5", "aug_slot_6", "quantity", + "evolve_amount", }; } @@ -108,17 +111,18 @@ public: { CharacterParcelsContainers e{}; - e.id = 0; - e.parcels_id = 0; - e.slot_id = 0; - e.item_id = 0; - e.aug_slot_1 = 0; - e.aug_slot_2 = 0; - e.aug_slot_3 = 0; - e.aug_slot_4 = 0; - e.aug_slot_5 = 0; - e.aug_slot_6 = 0; - e.quantity = 0; + e.id = 0; + e.parcels_id = 0; + e.slot_id = 0; + e.item_id = 0; + e.aug_slot_1 = 0; + e.aug_slot_2 = 0; + e.aug_slot_3 = 0; + e.aug_slot_4 = 0; + e.aug_slot_5 = 0; + e.aug_slot_6 = 0; + e.quantity = 0; + e.evolve_amount = 0; return e; } @@ -155,17 +159,18 @@ public: if (results.RowCount() == 1) { CharacterParcelsContainers e{}; - e.id = row[0] ? static_cast(strtoul(row[0], nullptr, 10)) : 0; - e.parcels_id = row[1] ? static_cast(strtoul(row[1], nullptr, 10)) : 0; - e.slot_id = row[2] ? static_cast(strtoul(row[2], nullptr, 10)) : 0; - e.item_id = row[3] ? static_cast(strtoul(row[3], nullptr, 10)) : 0; - e.aug_slot_1 = row[4] ? static_cast(strtoul(row[4], nullptr, 10)) : 0; - e.aug_slot_2 = row[5] ? static_cast(strtoul(row[5], nullptr, 10)) : 0; - e.aug_slot_3 = row[6] ? static_cast(strtoul(row[6], nullptr, 10)) : 0; - e.aug_slot_4 = row[7] ? static_cast(strtoul(row[7], nullptr, 10)) : 0; - e.aug_slot_5 = row[8] ? static_cast(strtoul(row[8], nullptr, 10)) : 0; - e.aug_slot_6 = row[9] ? static_cast(strtoul(row[9], nullptr, 10)) : 0; - e.quantity = row[10] ? static_cast(strtoul(row[10], nullptr, 10)) : 0; + e.id = row[0] ? static_cast(strtoul(row[0], nullptr, 10)) : 0; + e.parcels_id = row[1] ? static_cast(strtoul(row[1], nullptr, 10)) : 0; + e.slot_id = row[2] ? static_cast(strtoul(row[2], nullptr, 10)) : 0; + e.item_id = row[3] ? static_cast(strtoul(row[3], nullptr, 10)) : 0; + e.aug_slot_1 = row[4] ? static_cast(strtoul(row[4], nullptr, 10)) : 0; + e.aug_slot_2 = row[5] ? static_cast(strtoul(row[5], nullptr, 10)) : 0; + e.aug_slot_3 = row[6] ? static_cast(strtoul(row[6], nullptr, 10)) : 0; + e.aug_slot_4 = row[7] ? static_cast(strtoul(row[7], nullptr, 10)) : 0; + e.aug_slot_5 = row[8] ? static_cast(strtoul(row[8], nullptr, 10)) : 0; + e.aug_slot_6 = row[9] ? static_cast(strtoul(row[9], nullptr, 10)) : 0; + e.quantity = row[10] ? static_cast(strtoul(row[10], nullptr, 10)) : 0; + e.evolve_amount = row[11] ? static_cast(strtoul(row[11], nullptr, 10)) : 0; return e; } @@ -209,6 +214,7 @@ public: v.push_back(columns[8] + " = " + std::to_string(e.aug_slot_5)); v.push_back(columns[9] + " = " + std::to_string(e.aug_slot_6)); v.push_back(columns[10] + " = " + std::to_string(e.quantity)); + v.push_back(columns[11] + " = " + std::to_string(e.evolve_amount)); auto results = db.QueryDatabase( fmt::format( @@ -241,6 +247,7 @@ public: v.push_back(std::to_string(e.aug_slot_5)); v.push_back(std::to_string(e.aug_slot_6)); v.push_back(std::to_string(e.quantity)); + v.push_back(std::to_string(e.evolve_amount)); auto results = db.QueryDatabase( fmt::format( @@ -281,6 +288,7 @@ public: v.push_back(std::to_string(e.aug_slot_5)); v.push_back(std::to_string(e.aug_slot_6)); v.push_back(std::to_string(e.quantity)); + v.push_back(std::to_string(e.evolve_amount)); insert_chunks.push_back("(" + Strings::Implode(",", v) + ")"); } @@ -314,17 +322,18 @@ public: for (auto row = results.begin(); row != results.end(); ++row) { CharacterParcelsContainers e{}; - e.id = row[0] ? static_cast(strtoul(row[0], nullptr, 10)) : 0; - e.parcels_id = row[1] ? static_cast(strtoul(row[1], nullptr, 10)) : 0; - e.slot_id = row[2] ? static_cast(strtoul(row[2], nullptr, 10)) : 0; - e.item_id = row[3] ? static_cast(strtoul(row[3], nullptr, 10)) : 0; - e.aug_slot_1 = row[4] ? static_cast(strtoul(row[4], nullptr, 10)) : 0; - e.aug_slot_2 = row[5] ? static_cast(strtoul(row[5], nullptr, 10)) : 0; - e.aug_slot_3 = row[6] ? static_cast(strtoul(row[6], nullptr, 10)) : 0; - e.aug_slot_4 = row[7] ? static_cast(strtoul(row[7], nullptr, 10)) : 0; - e.aug_slot_5 = row[8] ? static_cast(strtoul(row[8], nullptr, 10)) : 0; - e.aug_slot_6 = row[9] ? static_cast(strtoul(row[9], nullptr, 10)) : 0; - e.quantity = row[10] ? static_cast(strtoul(row[10], nullptr, 10)) : 0; + e.id = row[0] ? static_cast(strtoul(row[0], nullptr, 10)) : 0; + e.parcels_id = row[1] ? static_cast(strtoul(row[1], nullptr, 10)) : 0; + e.slot_id = row[2] ? static_cast(strtoul(row[2], nullptr, 10)) : 0; + e.item_id = row[3] ? static_cast(strtoul(row[3], nullptr, 10)) : 0; + e.aug_slot_1 = row[4] ? static_cast(strtoul(row[4], nullptr, 10)) : 0; + e.aug_slot_2 = row[5] ? static_cast(strtoul(row[5], nullptr, 10)) : 0; + e.aug_slot_3 = row[6] ? static_cast(strtoul(row[6], nullptr, 10)) : 0; + e.aug_slot_4 = row[7] ? static_cast(strtoul(row[7], nullptr, 10)) : 0; + e.aug_slot_5 = row[8] ? static_cast(strtoul(row[8], nullptr, 10)) : 0; + e.aug_slot_6 = row[9] ? static_cast(strtoul(row[9], nullptr, 10)) : 0; + e.quantity = row[10] ? static_cast(strtoul(row[10], nullptr, 10)) : 0; + e.evolve_amount = row[11] ? static_cast(strtoul(row[11], nullptr, 10)) : 0; all_entries.push_back(e); } @@ -349,17 +358,18 @@ public: for (auto row = results.begin(); row != results.end(); ++row) { CharacterParcelsContainers e{}; - e.id = row[0] ? static_cast(strtoul(row[0], nullptr, 10)) : 0; - e.parcels_id = row[1] ? static_cast(strtoul(row[1], nullptr, 10)) : 0; - e.slot_id = row[2] ? static_cast(strtoul(row[2], nullptr, 10)) : 0; - e.item_id = row[3] ? static_cast(strtoul(row[3], nullptr, 10)) : 0; - e.aug_slot_1 = row[4] ? static_cast(strtoul(row[4], nullptr, 10)) : 0; - e.aug_slot_2 = row[5] ? static_cast(strtoul(row[5], nullptr, 10)) : 0; - e.aug_slot_3 = row[6] ? static_cast(strtoul(row[6], nullptr, 10)) : 0; - e.aug_slot_4 = row[7] ? static_cast(strtoul(row[7], nullptr, 10)) : 0; - e.aug_slot_5 = row[8] ? static_cast(strtoul(row[8], nullptr, 10)) : 0; - e.aug_slot_6 = row[9] ? static_cast(strtoul(row[9], nullptr, 10)) : 0; - e.quantity = row[10] ? static_cast(strtoul(row[10], nullptr, 10)) : 0; + e.id = row[0] ? static_cast(strtoul(row[0], nullptr, 10)) : 0; + e.parcels_id = row[1] ? static_cast(strtoul(row[1], nullptr, 10)) : 0; + e.slot_id = row[2] ? static_cast(strtoul(row[2], nullptr, 10)) : 0; + e.item_id = row[3] ? static_cast(strtoul(row[3], nullptr, 10)) : 0; + e.aug_slot_1 = row[4] ? static_cast(strtoul(row[4], nullptr, 10)) : 0; + e.aug_slot_2 = row[5] ? static_cast(strtoul(row[5], nullptr, 10)) : 0; + e.aug_slot_3 = row[6] ? static_cast(strtoul(row[6], nullptr, 10)) : 0; + e.aug_slot_4 = row[7] ? static_cast(strtoul(row[7], nullptr, 10)) : 0; + e.aug_slot_5 = row[8] ? static_cast(strtoul(row[8], nullptr, 10)) : 0; + e.aug_slot_6 = row[9] ? static_cast(strtoul(row[9], nullptr, 10)) : 0; + e.quantity = row[10] ? static_cast(strtoul(row[10], nullptr, 10)) : 0; + e.evolve_amount = row[11] ? static_cast(strtoul(row[11], nullptr, 10)) : 0; all_entries.push_back(e); } @@ -445,6 +455,7 @@ public: v.push_back(std::to_string(e.aug_slot_5)); v.push_back(std::to_string(e.aug_slot_6)); v.push_back(std::to_string(e.quantity)); + v.push_back(std::to_string(e.evolve_amount)); auto results = db.QueryDatabase( fmt::format( @@ -478,6 +489,7 @@ public: v.push_back(std::to_string(e.aug_slot_5)); v.push_back(std::to_string(e.aug_slot_6)); v.push_back(std::to_string(e.quantity)); + v.push_back(std::to_string(e.evolve_amount)); insert_chunks.push_back("(" + Strings::Implode(",", v) + ")"); } diff --git a/common/repositories/base/base_character_parcels_repository.h b/common/repositories/base/base_character_parcels_repository.h index 9263f307b..df7242aed 100644 --- a/common/repositories/base/base_character_parcels_repository.h +++ b/common/repositories/base/base_character_parcels_repository.h @@ -30,6 +30,7 @@ public: uint32_t aug_slot_6; uint32_t slot_id; uint32_t quantity; + uint32_t evolve_amount; std::string from_name; std::string note; time_t sent_date; @@ -54,6 +55,7 @@ public: "aug_slot_6", "slot_id", "quantity", + "evolve_amount", "from_name", "note", "sent_date", @@ -74,6 +76,7 @@ public: "aug_slot_6", "slot_id", "quantity", + "evolve_amount", "from_name", "note", "UNIX_TIMESTAMP(sent_date)", @@ -117,20 +120,21 @@ public: { CharacterParcels e{}; - e.id = 0; - e.char_id = 0; - e.item_id = 0; - e.aug_slot_1 = 0; - e.aug_slot_2 = 0; - e.aug_slot_3 = 0; - e.aug_slot_4 = 0; - e.aug_slot_5 = 0; - e.aug_slot_6 = 0; - e.slot_id = 0; - e.quantity = 0; - e.from_name = ""; - e.note = ""; - e.sent_date = 0; + e.id = 0; + e.char_id = 0; + e.item_id = 0; + e.aug_slot_1 = 0; + e.aug_slot_2 = 0; + e.aug_slot_3 = 0; + e.aug_slot_4 = 0; + e.aug_slot_5 = 0; + e.aug_slot_6 = 0; + e.slot_id = 0; + e.quantity = 0; + e.evolve_amount = 0; + e.from_name = ""; + e.note = ""; + e.sent_date = 0; return e; } @@ -167,20 +171,21 @@ public: if (results.RowCount() == 1) { CharacterParcels e{}; - e.id = row[0] ? static_cast(strtoul(row[0], nullptr, 10)) : 0; - e.char_id = row[1] ? static_cast(strtoul(row[1], nullptr, 10)) : 0; - e.item_id = row[2] ? static_cast(strtoul(row[2], nullptr, 10)) : 0; - e.aug_slot_1 = row[3] ? static_cast(strtoul(row[3], nullptr, 10)) : 0; - e.aug_slot_2 = row[4] ? static_cast(strtoul(row[4], nullptr, 10)) : 0; - e.aug_slot_3 = row[5] ? static_cast(strtoul(row[5], nullptr, 10)) : 0; - e.aug_slot_4 = row[6] ? static_cast(strtoul(row[6], nullptr, 10)) : 0; - e.aug_slot_5 = row[7] ? static_cast(strtoul(row[7], nullptr, 10)) : 0; - e.aug_slot_6 = row[8] ? static_cast(strtoul(row[8], nullptr, 10)) : 0; - e.slot_id = row[9] ? static_cast(strtoul(row[9], nullptr, 10)) : 0; - e.quantity = row[10] ? static_cast(strtoul(row[10], nullptr, 10)) : 0; - e.from_name = row[11] ? row[11] : ""; - e.note = row[12] ? row[12] : ""; - e.sent_date = strtoll(row[13] ? row[13] : "-1", nullptr, 10); + e.id = row[0] ? static_cast(strtoul(row[0], nullptr, 10)) : 0; + e.char_id = row[1] ? static_cast(strtoul(row[1], nullptr, 10)) : 0; + e.item_id = row[2] ? static_cast(strtoul(row[2], nullptr, 10)) : 0; + e.aug_slot_1 = row[3] ? static_cast(strtoul(row[3], nullptr, 10)) : 0; + e.aug_slot_2 = row[4] ? static_cast(strtoul(row[4], nullptr, 10)) : 0; + e.aug_slot_3 = row[5] ? static_cast(strtoul(row[5], nullptr, 10)) : 0; + e.aug_slot_4 = row[6] ? static_cast(strtoul(row[6], nullptr, 10)) : 0; + e.aug_slot_5 = row[7] ? static_cast(strtoul(row[7], nullptr, 10)) : 0; + e.aug_slot_6 = row[8] ? static_cast(strtoul(row[8], nullptr, 10)) : 0; + e.slot_id = row[9] ? static_cast(strtoul(row[9], nullptr, 10)) : 0; + e.quantity = row[10] ? static_cast(strtoul(row[10], nullptr, 10)) : 0; + e.evolve_amount = row[11] ? static_cast(strtoul(row[11], nullptr, 10)) : 0; + e.from_name = row[12] ? row[12] : ""; + e.note = row[13] ? row[13] : ""; + e.sent_date = strtoll(row[14] ? row[14] : "-1", nullptr, 10); return e; } @@ -224,9 +229,10 @@ public: v.push_back(columns[8] + " = " + std::to_string(e.aug_slot_6)); v.push_back(columns[9] + " = " + std::to_string(e.slot_id)); v.push_back(columns[10] + " = " + std::to_string(e.quantity)); - v.push_back(columns[11] + " = '" + Strings::Escape(e.from_name) + "'"); - v.push_back(columns[12] + " = '" + Strings::Escape(e.note) + "'"); - v.push_back(columns[13] + " = FROM_UNIXTIME(" + (e.sent_date > 0 ? std::to_string(e.sent_date) : "null") + ")"); + v.push_back(columns[11] + " = " + std::to_string(e.evolve_amount)); + v.push_back(columns[12] + " = '" + Strings::Escape(e.from_name) + "'"); + v.push_back(columns[13] + " = '" + Strings::Escape(e.note) + "'"); + v.push_back(columns[14] + " = FROM_UNIXTIME(" + (e.sent_date > 0 ? std::to_string(e.sent_date) : "null") + ")"); auto results = db.QueryDatabase( fmt::format( @@ -259,6 +265,7 @@ public: v.push_back(std::to_string(e.aug_slot_6)); v.push_back(std::to_string(e.slot_id)); v.push_back(std::to_string(e.quantity)); + v.push_back(std::to_string(e.evolve_amount)); v.push_back("'" + Strings::Escape(e.from_name) + "'"); v.push_back("'" + Strings::Escape(e.note) + "'"); v.push_back("FROM_UNIXTIME(" + (e.sent_date > 0 ? std::to_string(e.sent_date) : "null") + ")"); @@ -302,6 +309,7 @@ public: v.push_back(std::to_string(e.aug_slot_6)); v.push_back(std::to_string(e.slot_id)); v.push_back(std::to_string(e.quantity)); + v.push_back(std::to_string(e.evolve_amount)); v.push_back("'" + Strings::Escape(e.from_name) + "'"); v.push_back("'" + Strings::Escape(e.note) + "'"); v.push_back("FROM_UNIXTIME(" + (e.sent_date > 0 ? std::to_string(e.sent_date) : "null") + ")"); @@ -338,20 +346,21 @@ public: for (auto row = results.begin(); row != results.end(); ++row) { CharacterParcels e{}; - e.id = row[0] ? static_cast(strtoul(row[0], nullptr, 10)) : 0; - e.char_id = row[1] ? static_cast(strtoul(row[1], nullptr, 10)) : 0; - e.item_id = row[2] ? static_cast(strtoul(row[2], nullptr, 10)) : 0; - e.aug_slot_1 = row[3] ? static_cast(strtoul(row[3], nullptr, 10)) : 0; - e.aug_slot_2 = row[4] ? static_cast(strtoul(row[4], nullptr, 10)) : 0; - e.aug_slot_3 = row[5] ? static_cast(strtoul(row[5], nullptr, 10)) : 0; - e.aug_slot_4 = row[6] ? static_cast(strtoul(row[6], nullptr, 10)) : 0; - e.aug_slot_5 = row[7] ? static_cast(strtoul(row[7], nullptr, 10)) : 0; - e.aug_slot_6 = row[8] ? static_cast(strtoul(row[8], nullptr, 10)) : 0; - e.slot_id = row[9] ? static_cast(strtoul(row[9], nullptr, 10)) : 0; - e.quantity = row[10] ? static_cast(strtoul(row[10], nullptr, 10)) : 0; - e.from_name = row[11] ? row[11] : ""; - e.note = row[12] ? row[12] : ""; - e.sent_date = strtoll(row[13] ? row[13] : "-1", nullptr, 10); + e.id = row[0] ? static_cast(strtoul(row[0], nullptr, 10)) : 0; + e.char_id = row[1] ? static_cast(strtoul(row[1], nullptr, 10)) : 0; + e.item_id = row[2] ? static_cast(strtoul(row[2], nullptr, 10)) : 0; + e.aug_slot_1 = row[3] ? static_cast(strtoul(row[3], nullptr, 10)) : 0; + e.aug_slot_2 = row[4] ? static_cast(strtoul(row[4], nullptr, 10)) : 0; + e.aug_slot_3 = row[5] ? static_cast(strtoul(row[5], nullptr, 10)) : 0; + e.aug_slot_4 = row[6] ? static_cast(strtoul(row[6], nullptr, 10)) : 0; + e.aug_slot_5 = row[7] ? static_cast(strtoul(row[7], nullptr, 10)) : 0; + e.aug_slot_6 = row[8] ? static_cast(strtoul(row[8], nullptr, 10)) : 0; + e.slot_id = row[9] ? static_cast(strtoul(row[9], nullptr, 10)) : 0; + e.quantity = row[10] ? static_cast(strtoul(row[10], nullptr, 10)) : 0; + e.evolve_amount = row[11] ? static_cast(strtoul(row[11], nullptr, 10)) : 0; + e.from_name = row[12] ? row[12] : ""; + e.note = row[13] ? row[13] : ""; + e.sent_date = strtoll(row[14] ? row[14] : "-1", nullptr, 10); all_entries.push_back(e); } @@ -376,20 +385,21 @@ public: for (auto row = results.begin(); row != results.end(); ++row) { CharacterParcels e{}; - e.id = row[0] ? static_cast(strtoul(row[0], nullptr, 10)) : 0; - e.char_id = row[1] ? static_cast(strtoul(row[1], nullptr, 10)) : 0; - e.item_id = row[2] ? static_cast(strtoul(row[2], nullptr, 10)) : 0; - e.aug_slot_1 = row[3] ? static_cast(strtoul(row[3], nullptr, 10)) : 0; - e.aug_slot_2 = row[4] ? static_cast(strtoul(row[4], nullptr, 10)) : 0; - e.aug_slot_3 = row[5] ? static_cast(strtoul(row[5], nullptr, 10)) : 0; - e.aug_slot_4 = row[6] ? static_cast(strtoul(row[6], nullptr, 10)) : 0; - e.aug_slot_5 = row[7] ? static_cast(strtoul(row[7], nullptr, 10)) : 0; - e.aug_slot_6 = row[8] ? static_cast(strtoul(row[8], nullptr, 10)) : 0; - e.slot_id = row[9] ? static_cast(strtoul(row[9], nullptr, 10)) : 0; - e.quantity = row[10] ? static_cast(strtoul(row[10], nullptr, 10)) : 0; - e.from_name = row[11] ? row[11] : ""; - e.note = row[12] ? row[12] : ""; - e.sent_date = strtoll(row[13] ? row[13] : "-1", nullptr, 10); + e.id = row[0] ? static_cast(strtoul(row[0], nullptr, 10)) : 0; + e.char_id = row[1] ? static_cast(strtoul(row[1], nullptr, 10)) : 0; + e.item_id = row[2] ? static_cast(strtoul(row[2], nullptr, 10)) : 0; + e.aug_slot_1 = row[3] ? static_cast(strtoul(row[3], nullptr, 10)) : 0; + e.aug_slot_2 = row[4] ? static_cast(strtoul(row[4], nullptr, 10)) : 0; + e.aug_slot_3 = row[5] ? static_cast(strtoul(row[5], nullptr, 10)) : 0; + e.aug_slot_4 = row[6] ? static_cast(strtoul(row[6], nullptr, 10)) : 0; + e.aug_slot_5 = row[7] ? static_cast(strtoul(row[7], nullptr, 10)) : 0; + e.aug_slot_6 = row[8] ? static_cast(strtoul(row[8], nullptr, 10)) : 0; + e.slot_id = row[9] ? static_cast(strtoul(row[9], nullptr, 10)) : 0; + e.quantity = row[10] ? static_cast(strtoul(row[10], nullptr, 10)) : 0; + e.evolve_amount = row[11] ? static_cast(strtoul(row[11], nullptr, 10)) : 0; + e.from_name = row[12] ? row[12] : ""; + e.note = row[13] ? row[13] : ""; + e.sent_date = strtoll(row[14] ? row[14] : "-1", nullptr, 10); all_entries.push_back(e); } @@ -475,6 +485,7 @@ public: v.push_back(std::to_string(e.aug_slot_6)); v.push_back(std::to_string(e.slot_id)); v.push_back(std::to_string(e.quantity)); + v.push_back(std::to_string(e.evolve_amount)); v.push_back("'" + Strings::Escape(e.from_name) + "'"); v.push_back("'" + Strings::Escape(e.note) + "'"); v.push_back("FROM_UNIXTIME(" + (e.sent_date > 0 ? std::to_string(e.sent_date) : "null") + ")"); @@ -511,6 +522,7 @@ public: v.push_back(std::to_string(e.aug_slot_6)); v.push_back(std::to_string(e.slot_id)); v.push_back(std::to_string(e.quantity)); + v.push_back(std::to_string(e.evolve_amount)); v.push_back("'" + Strings::Escape(e.from_name) + "'"); v.push_back("'" + Strings::Escape(e.note) + "'"); v.push_back("FROM_UNIXTIME(" + (e.sent_date > 0 ? std::to_string(e.sent_date) : "null") + ")"); diff --git a/common/repositories/character_data_repository.h b/common/repositories/character_data_repository.h index d95ac2fda..68390fc35 100644 --- a/common/repositories/character_data_repository.h +++ b/common/repositories/character_data_repository.h @@ -191,6 +191,22 @@ public: return character_ids; } + + static uint32_t GetTotalTimePlayed(Database& db, uint32_t account_id) + { + auto query = fmt::format( + "SELECT SUM(time_played) FROM `character_data` WHERE `account_id` = {}", + account_id + ); + + auto results = db.QueryDatabase(query); + if (!results.Success()) { + return 0; + } + + auto row = results.begin(); + return Strings::ToUnsignedInt(row[0]); + } }; #endif //EQEMU_CHARACTER_DATA_REPOSITORY_H diff --git a/common/shareddb.cpp b/common/shareddb.cpp index a36cebb8e..ba88ce5bb 100644 --- a/common/shareddb.cpp +++ b/common/shareddb.cpp @@ -429,27 +429,20 @@ bool SharedDatabase::DeleteSharedBankSlot(uint32 char_id, int16 slot_id) ); } - int32 SharedDatabase::GetSharedPlatinum(uint32 account_id) { - const auto query = fmt::format("SELECT sharedplat FROM account WHERE id = {}", account_id); - auto results = QueryDatabase(query); - if (!results.Success() || !results.RowCount()) { - return 0; - } - - auto row = results.begin(); - return Strings::ToInt(row[0]); + const auto& e = AccountRepository::FindOne(*this, account_id); + + return e.sharedplat; } -bool SharedDatabase::SetSharedPlatinum(uint32 account_id, int32 amount_to_add) { - const std::string query = StringFormat("UPDATE account SET sharedplat = sharedplat + %i WHERE id = %i", amount_to_add, account_id); - const auto results = QueryDatabase(query); - if (!results.Success()) { - return false; - } +bool SharedDatabase::AddSharedPlatinum(uint32 account_id, int amount) +{ + auto e = AccountRepository::FindOne(*this, account_id); - return true; + e.sharedplat += amount; + + return AccountRepository::UpdateOne(*this, e); } bool SharedDatabase::SetStartingItems( diff --git a/common/shareddb.h b/common/shareddb.h index 57a2fe249..463aaac96 100644 --- a/common/shareddb.h +++ b/common/shareddb.h @@ -82,7 +82,6 @@ public: bool UpdateInjectedCommandSettings(const std::vector> &injected); bool UpdateOrphanedCommandSettings(const std::vector &orphaned); bool GetCommandSubSettings(std::vector &command_subsettings); - uint32 GetTotalTimeEntitledOnAccount(uint32 AccountID); bool SetGMInvul(uint32 account_id, bool gminvul); bool SetGMFlymode(uint32 account_id, uint8 flymode); void SetMailKey(uint32 character_id, int ip_address, int mail_key); @@ -104,7 +103,7 @@ public: bool VerifyInventory(uint32 account_id, int16 slot_id, const EQ::ItemInstance *inst); bool GetSharedBank(uint32 id, EQ::InventoryProfile *inv, bool is_charid); int32 GetSharedPlatinum(uint32 account_id); - bool SetSharedPlatinum(uint32 account_id, int32 amount_to_add); + bool AddSharedPlatinum(uint32 account_id, int amount); bool GetInventory(Client* c); bool GetInventory(uint32 account_id, char *name, EQ::InventoryProfile *inv); // deprecated std::map GetItemRecastTimestamps(uint32 char_id); diff --git a/common/version.h b/common/version.h index af74568c6..9b9ddb4a6 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 9328 +#define CURRENT_BINARY_DATABASE_VERSION 9329 #define CURRENT_BINARY_BOTS_DATABASE_VERSION 9054 #define CUSTOM_BINARY_DATABASE_VERSION 0 diff --git a/zone/client_packet.cpp b/zone/client_packet.cpp index 42f0b8bbe..441b4a538 100644 --- a/zone/client_packet.cpp +++ b/zone/client_packet.cpp @@ -1713,7 +1713,7 @@ void Client::Handle_Connect_OP_ZoneEntry(const EQApplicationPacket *app) if (zone->IsPVPZone()) m_pp.pvp = 1; /* Time entitled on Account: Move to account */ - m_pp.timeentitledonaccount = database.GetTotalTimeEntitledOnAccount(AccountID()) / 1440; + m_pp.timeentitledonaccount = CharacterDataRepository::GetTotalTimePlayed(database, AccountID()) / 1440; /* Reset rest timer if the durations have been lowered in the database */ if ((m_pp.RestTimer > RuleI(Character, RestRegenTimeToActivate)) && (m_pp.RestTimer > RuleI(Character, RestRegenRaidTimeToActivate))) m_pp.RestTimer = 0; @@ -7988,7 +7988,7 @@ void Client::Handle_OP_GuildCreate(const EQApplicationPacket *app) if ((Admin() < RuleI(Guild, PlayerCreationRequiredStatus)) || (GetLevel() < RuleI(Guild, PlayerCreationRequiredLevel)) || - (database.GetTotalTimeEntitledOnAccount(AccountID()) < (unsigned int)RuleI(Guild, PlayerCreationRequiredTime))) + (CharacterDataRepository::GetTotalTimePlayed(database, AccountID()) < (unsigned int)RuleI(Guild, PlayerCreationRequiredTime))) { Message(Chat::Red, "Your status, level or time playing on this account are insufficient to use this feature."); return; diff --git a/zone/client_process.cpp b/zone/client_process.cpp index 71144db89..e14985763 100644 --- a/zone/client_process.cpp +++ b/zone/client_process.cpp @@ -1571,7 +1571,7 @@ void Client::OPMoveCoin(const EQApplicationPacket* app) if (from_bucket == &m_pp.platinum_shared) amount_to_add = 0 - amount_to_take; - database.SetSharedPlatinum(AccountID(),amount_to_add); + database.AddSharedPlatinum(AccountID(),amount_to_add); } } else{ diff --git a/zone/embparser.cpp b/zone/embparser.cpp index 6d220b8d8..ad9708d52 100644 --- a/zone/embparser.cpp +++ b/zone/embparser.cpp @@ -208,6 +208,8 @@ const char* QuestEventSubroutines[_LargestEventID] = { "EVENT_SPELL_BLOCKED", "EVENT_READ_ITEM", "EVENT_PET_COMMAND", + "EVENT_CHARM_START", + "EVENT_CHARM_END", // Add new events before these or Lua crashes "EVENT_SPELL_EFFECT_BOT", diff --git a/zone/event_codes.h b/zone/event_codes.h index 0d14f565d..ff345abff 100644 --- a/zone/event_codes.h +++ b/zone/event_codes.h @@ -146,6 +146,8 @@ typedef enum { EVENT_SPELL_BLOCKED, EVENT_READ_ITEM, EVENT_PET_COMMAND, + EVENT_CHARM_START, + EVENT_CHARM_END, // Add new events before these or Lua crashes EVENT_SPELL_EFFECT_BOT, diff --git a/zone/lua_general.cpp b/zone/lua_general.cpp index ae8c4b2af..1873aec0b 100644 --- a/zone/lua_general.cpp +++ b/zone/lua_general.cpp @@ -6992,7 +6992,9 @@ luabind::scope lua_register_events() { luabind::value("entity_variable_update", static_cast(EVENT_ENTITY_VARIABLE_UPDATE)), luabind::value("aa_loss", static_cast(EVENT_AA_LOSS)), luabind::value("read", static_cast(EVENT_READ_ITEM)), - luabind::value("pet_command", static_cast(EVENT_PET_COMMAND)) + luabind::value("pet_command", static_cast(EVENT_PET_COMMAND)), + luabind::value("charm_start", static_cast(EVENT_CHARM_START)), + luabind::value("charm_end", static_cast(EVENT_CHARM_END)) )]; } diff --git a/zone/lua_parser.cpp b/zone/lua_parser.cpp index c3f37aec5..3e3a5257f 100644 --- a/zone/lua_parser.cpp +++ b/zone/lua_parser.cpp @@ -189,7 +189,9 @@ const char *LuaEvents[_LargestEventID] = { "event_aa_loss", "event_spell_blocked", "event_read_item", - "event_pet_command" + "event_pet_command", + "event_charm_start", + "event_charm_end" }; extern Zone *zone; @@ -266,6 +268,8 @@ LuaParser::LuaParser() { NPCArgumentDispatch[EVENT_ENTITY_VARIABLE_UPDATE] = handle_npc_entity_variable; NPCArgumentDispatch[EVENT_SPELL_BLOCKED] = handle_npc_spell_blocked; NPCArgumentDispatch[EVENT_PET_COMMAND] = handle_npc_pet_command; + NPCArgumentDispatch[EVENT_CHARM_START] = handle_npc_single_mob; + NPCArgumentDispatch[EVENT_CHARM_END] = handle_npc_single_mob; PlayerArgumentDispatch[EVENT_SAY] = handle_player_say; PlayerArgumentDispatch[EVENT_ENVIRONMENTAL_DAMAGE] = handle_player_environmental_damage; diff --git a/zone/npc.cpp b/zone/npc.cpp index 8f12645b7..0a91d8e52 100644 --- a/zone/npc.cpp +++ b/zone/npc.cpp @@ -2274,9 +2274,9 @@ void NPC::SetLevel(uint8 in_level, bool command) void NPC::ModifyNPCStat(const std::string& stat, const std::string& value) { - auto stat_lower = Strings::ToLower(stat); + const std::string& stat_lower = Strings::ToLower(stat); - auto variable_key = fmt::format( + const std::string& variable_key = fmt::format( "modify_stat_{}", stat_lower ); @@ -2288,40 +2288,24 @@ void NPC::ModifyNPCStat(const std::string& stat, const std::string& value) if (stat_lower == "ac") { AC = Strings::ToInt(value); CalcAC(); - return; - } - else if (stat_lower == "str") { + } else if (stat_lower == "str") { STR = Strings::ToInt(value); - return; - } - else if (stat_lower == "sta") { + } else if (stat_lower == "sta") { STA = Strings::ToInt(value); - return; - } - else if (stat_lower == "agi") { + } else if (stat_lower == "agi") { AGI = Strings::ToInt(value); CalcAC(); - return; - } - else if (stat_lower == "dex") { + } else if (stat_lower == "dex") { DEX = Strings::ToInt(value); - return; - } - else if (stat_lower == "wis") { + } else if (stat_lower == "wis") { WIS = Strings::ToInt(value); CalcMaxMana(); - return; - } - else if (stat_lower == "int" || stat_lower == "_int") { + } else if (stat_lower == "int" || stat_lower == "_int") { INT = Strings::ToInt(value); CalcMaxMana(); - return; - } - else if (stat_lower == "cha") { + } else if (stat_lower == "cha") { CHA = Strings::ToInt(value); - return; - } - else if (stat_lower == "max_hp") { + } else if (stat_lower == "max_hp") { base_hp = Strings::ToBigInt(value); CalcMaxHP(); @@ -2329,45 +2313,27 @@ void NPC::ModifyNPCStat(const std::string& stat, const std::string& value) current_hp = max_hp; } - return; - } - else if (stat_lower == "max_mana") { + } else if (stat_lower == "max_mana") { npc_mana = Strings::ToUnsignedBigInt(value); CalcMaxMana(); if (current_mana > max_mana) { current_mana = max_mana; } - return; - } - else if (stat_lower == "mr") { + } else if (stat_lower == "mr") { MR = Strings::ToInt(value); - return; - } - else if (stat_lower == "fr") { + } else if (stat_lower == "fr") { FR = Strings::ToInt(value); - return; - } - else if (stat_lower == "cr") { + } else if (stat_lower == "cr") { CR = Strings::ToInt(value); - return; - } - else if (stat_lower == "cor") { + } else if (stat_lower == "cor") { Corrup = Strings::ToInt(value); - return; - } - else if (stat_lower == "pr") { + } else if (stat_lower == "pr") { PR = Strings::ToInt(value); - return; - } - else if (stat_lower == "dr") { + } else if (stat_lower == "dr") { DR = Strings::ToInt(value); - return; - } - else if (stat_lower == "phr") { + } else if (stat_lower == "phr") { PhR = Strings::ToInt(value); - return; - } - else if (stat_lower == "runspeed") { + } else if (stat_lower == "runspeed") { runspeed = Strings::ToFloat(value); base_runspeed = (int) (runspeed * 40.0f); base_walkspeed = base_runspeed * 100 / 265; @@ -2375,297 +2341,229 @@ void NPC::ModifyNPCStat(const std::string& stat, const std::string& value) base_fearspeed = base_runspeed * 100 / 127; fearspeed = ((float) base_fearspeed) * 0.025f; CalcBonuses(); - return; - } - else if (stat_lower == "special_attacks") { + } else if (stat_lower == "special_attacks") { NPCSpecialAttacks(value.c_str(), 0, true); - return; - } - else if (stat_lower == "special_abilities") { + } else if (stat_lower == "special_abilities") { ProcessSpecialAbilities(value); - return; - } - else if (stat_lower == "attack_speed") { + } else if (stat_lower == "attack_speed") { attack_speed = Strings::ToFloat(value); CalcBonuses(); - return; - } - else if (stat_lower == "attack_delay") { + } else if (stat_lower == "attack_delay") { /* TODO: fix DB */ attack_delay = Strings::ToInt(value) * 100; CalcBonuses(); - return; - } - else if (stat_lower == "atk") { + } else if (stat_lower == "atk") { ATK = Strings::ToInt(value); - return; - } - else if (stat_lower == "accuracy") { + } else if (stat_lower == "accuracy") { accuracy_rating = Strings::ToInt(value); - return; - } - else if (stat_lower == "avoidance") { + } else if (stat_lower == "avoidance") { avoidance_rating = Strings::ToInt(value); - return; - } - else if (stat_lower == "trackable") { + } else if (stat_lower == "trackable") { trackable = Strings::ToInt(value); - return; - } - else if (stat_lower == "min_hit") { - min_dmg = Strings::ToInt(value); + } else if (stat_lower == "min_hit") { + min_dmg = Strings::ToInt(value); // Clamp max_dmg to be >= min_dmg - max_dmg = std::max(min_dmg, max_dmg); + max_dmg = std::max(min_dmg, max_dmg); base_damage = round((max_dmg - min_dmg) / 1.9); min_damage = min_dmg - round(base_damage / 10.0); - return; - } - else if (stat_lower == "max_hit") { - max_dmg = Strings::ToInt(value); + } else if (stat_lower == "max_hit") { + max_dmg = Strings::ToInt(value); // Clamp min_dmg to be <= max_dmg - min_dmg = std::min(min_dmg, max_dmg); + min_dmg = std::min(min_dmg, max_dmg); base_damage = round((max_dmg - min_dmg) / 1.9); min_damage = min_dmg - round(base_damage / 10.0); - return; - } - else if (stat_lower == "attack_count") { + } else if (stat_lower == "attack_count") { attack_count = Strings::ToInt(value); - return; - } - else if (stat_lower == "see_invis") { + } else if (stat_lower == "see_invis") { see_invis = Strings::ToInt(value); - return; - } - else if (stat_lower == "see_invis_undead") { + } else if (stat_lower == "see_invis_undead") { see_invis_undead = Strings::ToInt(value); - return; - } - else if (stat_lower == "see_hide") { + } else if (stat_lower == "see_hide") { see_hide = Strings::ToInt(value); - return; - } - else if (stat_lower == "see_improved_hide") { + } else if (stat_lower == "see_improved_hide") { see_improved_hide = Strings::ToInt(value); - return; - } - else if (stat_lower == "hp_regen") { + } else if (stat_lower == "hp_regen") { hp_regen = Strings::ToBigInt(value); - return; - } - else if (stat_lower == "hp_regen_per_second") { + } else if (stat_lower == "hp_regen_per_second") { hp_regen_per_second = Strings::ToBigInt(value); - return; - } - else if (stat_lower == "mana_regen") { + } else if (stat_lower == "mana_regen") { mana_regen = Strings::ToBigInt(value); - return; - } - else if (stat_lower == "level") { + } else if (stat_lower == "level") { SetLevel(Strings::ToInt(value)); - return; - } - else if (stat_lower == "aggro") { + } else if (stat_lower == "aggro") { pAggroRange = Strings::ToFloat(value); - return; - } - else if (stat_lower == "assist") { + } else if (stat_lower == "assist") { pAssistRange = Strings::ToFloat(value); - return; - } - else if (stat_lower == "slow_mitigation") { + } else if (stat_lower == "slow_mitigation") { slow_mitigation = Strings::ToInt(value); - return; - } - else if (stat_lower == "loottable_id") { + } else if (stat_lower == "loottable_id") { m_loottable_id = Strings::ToFloat(value); - return; - } - else if (stat_lower == "healscale") { + } else if (stat_lower == "healscale") { healscale = Strings::ToFloat(value); - return; - } - else if (stat_lower == "spellscale") { + } else if (stat_lower == "spellscale") { spellscale = Strings::ToFloat(value); - return; - } - else if (stat_lower == "npc_spells_id") { + } else if (stat_lower == "npc_spells_id") { AI_AddNPCSpells(Strings::ToInt(value)); - return; - } - else if (stat_lower == "npc_spells_effects_id") { + } else if (stat_lower == "npc_spells_effects_id") { AI_AddNPCSpellsEffects(Strings::ToInt(value)); CalcBonuses(); - return; - } - else if (stat_lower == "heroic_strikethrough") { + } else if (stat_lower == "heroic_strikethrough") { heroic_strikethrough = Strings::ToInt(value); - return; - } - else if (stat_lower == "keeps_sold_items") { + } else if (stat_lower == "keeps_sold_items") { SetKeepsSoldItems(Strings::ToBool(value)); - return; + } else if (stat_lower == "charm_ac") { + charm_ac = Strings::ToInt(value); + } else if (stat_lower == "charm_min_dmg") { + charm_min_dmg = Strings::ToInt(value); + } else if (stat_lower == "charm_max_dmg") { + charm_max_dmg = Strings::ToInt(value); + } else if (stat_lower == "charm_attack_delay") { + charm_attack_delay = Strings::ToInt(value); + } else if (stat_lower == "charm_accuracy_rating") { + charm_accuracy_rating = Strings::ToInt(value); + } else if (stat_lower == "charm_avoidance_rating") { + charm_avoidance_rating = Strings::ToInt(value); + } else if (stat_lower == "charm_atk") { + charm_atk = Strings::ToInt(value); + } else if (stat_lower == "default_ac") { + default_ac = Strings::ToInt(value); + } else if (stat_lower == "default_min_dmg") { + default_min_dmg = Strings::ToInt(value); + } else if (stat_lower == "default_max_dmg") { + default_max_dmg = Strings::ToInt(value); + } else if (stat_lower == "default_attack_delay") { + default_attack_delay = Strings::ToInt(value); + } else if (stat_lower == "default_accuracy_rating") { + default_accuracy_rating = Strings::ToInt(value); + } else if (stat_lower == "default_avoidance_rating") { + default_avoidance_rating = Strings::ToInt(value); + } else if (stat_lower == "default_atk") { + default_atk = Strings::ToInt(value); } } float NPC::GetNPCStat(const std::string& stat) { + const std::string& stat_lower = Strings::ToLower(stat); - if (auto stat_lower = Strings::ToLower(stat); stat_lower == "ac") { + if (stat_lower == "ac") { return AC; - } - else if (stat_lower == "str") { + } else if (stat_lower == "str") { return STR; - } - else if (stat_lower == "sta") { + } else if (stat_lower == "sta") { return STA; - } - else if (stat_lower == "agi") { + } else if (stat_lower == "agi") { return AGI; - } - else if (stat_lower == "dex") { + } else if (stat_lower == "dex") { return DEX; - } - else if (stat_lower == "wis") { + } else if (stat_lower == "wis") { return WIS; - } - else if (stat_lower == "int" || stat_lower == "_int") { + } else if (stat_lower == "int" || stat_lower == "_int") { return INT; - } - else if (stat_lower == "cha") { + } else if (stat_lower == "cha") { return CHA; - } - else if (stat_lower == "max_hp") { + } else if (stat_lower == "max_hp") { return base_hp; - } - else if (stat_lower == "max_mana") { + } else if (stat_lower == "max_mana") { return npc_mana; - } - else if (stat_lower == "mr") { + } else if (stat_lower == "mr") { return MR; - } - else if (stat_lower == "fr") { + } else if (stat_lower == "fr") { return FR; - } - else if (stat_lower == "cr") { + } else if (stat_lower == "cr") { return CR; - } - else if (stat_lower == "cor") { + } else if (stat_lower == "cor") { return Corrup; - } - else if (stat_lower == "phr") { + } else if (stat_lower == "phr") { return PhR; - } - else if (stat_lower == "pr") { + } else if (stat_lower == "pr") { return PR; - } - else if (stat_lower == "dr") { + } else if (stat_lower == "dr") { return DR; - } - else if (stat_lower == "runspeed") { + } else if (stat_lower == "runspeed") { return runspeed; - } - else if (stat_lower == "attack_speed") { + } else if (stat_lower == "attack_speed") { return attack_speed; - } - else if (stat_lower == "attack_delay") { + } else if (stat_lower == "attack_delay") { return attack_delay; - } - else if (stat_lower == "atk") { + } else if (stat_lower == "atk") { return ATK; - } - else if (stat_lower == "accuracy") { + } else if (stat_lower == "accuracy") { return accuracy_rating; - } - else if (stat_lower == "avoidance") { + } else if (stat_lower == "avoidance") { return avoidance_rating; - } - else if (stat_lower == "trackable") { + } else if (stat_lower == "trackable") { return trackable; - } - else if (stat_lower == "min_hit") { + } else if (stat_lower == "min_hit") { return min_dmg; - } - else if (stat_lower == "max_hit") { + } else if (stat_lower == "max_hit") { return max_dmg; - } - else if (stat_lower == "attack_count") { + } else if (stat_lower == "attack_count") { return attack_count; - } - else if (stat_lower == "see_invis") { + } else if (stat_lower == "see_invis") { return see_invis; - } - else if (stat_lower == "see_invis_undead") { + } else if (stat_lower == "see_invis_undead") { return see_invis_undead; - } - else if (stat_lower == "see_hide") { + } else if (stat_lower == "see_hide") { return see_hide; - } - else if (stat_lower == "see_improved_hide") { + } else if (stat_lower == "see_improved_hide") { return see_improved_hide; - } - else if (stat_lower == "hp_regen") { + } else if (stat_lower == "hp_regen") { return hp_regen; - } - else if (stat_lower == "hp_regen_per_second") { + } else if (stat_lower == "hp_regen_per_second") { return hp_regen_per_second; - } - else if (stat_lower == "mana_regen") { + } else if (stat_lower == "mana_regen") { return mana_regen; - } - else if (stat_lower == "level") { + } else if (stat_lower == "level") { return GetOrigLevel(); - } - else if (stat_lower == "aggro") { + } else if (stat_lower == "aggro") { return pAggroRange; - } - else if (stat_lower == "assist") { + } else if (stat_lower == "assist") { return pAssistRange; - } - else if (stat_lower == "slow_mitigation") { + } else if (stat_lower == "slow_mitigation") { return slow_mitigation; - } - else if (stat_lower == "loottable_id") { + } else if (stat_lower == "loottable_id") { return m_loottable_id; - } - else if (stat_lower == "healscale") { + } else if (stat_lower == "healscale") { return healscale; - } - else if (stat_lower == "spellscale") { + } else if (stat_lower == "spellscale") { return spellscale; - } - else if (stat_lower == "npc_spells_id") { + } else if (stat_lower == "npc_spells_id") { return npc_spells_id; - } - else if (stat_lower == "npc_spells_effects_id") { + } else if (stat_lower == "npc_spells_effects_id") { return npc_spells_effects_id; - } - else if (stat_lower == "heroic_strikethrough") { + } else if (stat_lower == "heroic_strikethrough") { return heroic_strikethrough; - } - else if (stat_lower == "keeps_sold_items") { + } else if (stat_lower == "keeps_sold_items") { return keeps_sold_items; - } - //default values - else if (stat_lower == "default_ac") { + } else if (stat_lower == "default_ac") { return default_ac; - } - else if (stat_lower == "default_min_hit") { + } else if (stat_lower == "default_min_hit") { return default_min_dmg; - } - else if (stat_lower == "default_max_hit") { + } else if (stat_lower == "default_max_hit") { return default_max_dmg; - } - else if (stat_lower == "default_attack_delay") { + } else if (stat_lower == "default_attack_delay") { return default_attack_delay; - } - else if (stat_lower == "default_accuracy") { + } else if (stat_lower == "default_accuracy") { return default_accuracy_rating; - } - else if (stat_lower == "default_avoidance") { + } else if (stat_lower == "default_avoidance") { return default_avoidance_rating; - } - else if (stat_lower == "default_atk") { + } else if (stat_lower == "default_atk") { return default_atk; + } else if (stat_lower == "charm_ac") { + return charm_ac; + } else if (stat_lower == "charm_min_hit") { + return charm_min_dmg; + } else if (stat_lower == "charm_max_hit") { + return charm_max_dmg; + } else if (stat_lower == "charm_attack_delay") { + return charm_attack_delay; + } else if (stat_lower == "charm_accuracy") { + return charm_accuracy_rating; + } else if (stat_lower == "charm_avoidance") { + return charm_avoidance_rating; + } else if (stat_lower == "charm_atk") { + return charm_atk; } return 0.0f; @@ -3387,62 +3285,55 @@ void NPC::DepopSwarmPets() } } -void NPC::ModifyStatsOnCharm(bool is_charm_removed) +void NPC::ModifyStatsOnCharm(bool remove_charm, Mob* charmer) { - if (is_charm_removed) { - if (charm_ac) { - AC = default_ac; - } - if (charm_attack_delay) { - attack_delay = default_attack_delay; - } - if (charm_accuracy_rating) { - accuracy_rating = default_accuracy_rating; - } - if (charm_avoidance_rating) { - avoidance_rating = default_avoidance_rating; - } - if (charm_atk) { - ATK = default_atk; - } - if (charm_min_dmg || charm_max_dmg) { - base_damage = round((default_max_dmg - default_min_dmg) / 1.9); - min_damage = default_min_dmg - round(base_damage / 10.0); - } - if (RuleB(Spells, CharmDisablesSpecialAbilities)) { - ProcessSpecialAbilities(default_special_abilities); - } - - SetAttackTimer(); - CalcAC(); - - return; + if (!remove_charm && parse->HasQuestSub(GetNPCTypeID(), EVENT_CHARM_START)) { + parse->EventNPC(EVENT_CHARM_START, this, charmer, "", 0); + } else if (remove_charm && parse->HasQuestSub(GetNPCTypeID(), EVENT_CHARM_END)) { + parse->EventNPC(EVENT_CHARM_END, this, charmer, "", 0); } - if (charm_ac) { - AC = charm_ac; + const int new_ac = remove_charm ? default_ac : charm_ac; + const int new_attack_delay = remove_charm ? default_attack_delay : charm_attack_delay; + const int new_accuracy_rating = remove_charm ? default_accuracy_rating : charm_accuracy_rating; + const int new_avoidance_rating = remove_charm ? default_avoidance_rating : charm_avoidance_rating; + const int new_atk = remove_charm ? default_atk : charm_atk; + const int new_min_dmg = remove_charm ? default_min_dmg : charm_min_dmg; + const int new_max_dmg = remove_charm ? default_max_dmg : charm_max_dmg; + + if (new_ac) { + AC = new_ac; } - if (charm_attack_delay) { - attack_delay = charm_attack_delay; + + if (new_attack_delay) { + attack_delay = new_attack_delay; } - if (charm_accuracy_rating) { - accuracy_rating = charm_accuracy_rating; + + if (new_accuracy_rating) { + accuracy_rating = new_accuracy_rating; } - if (charm_avoidance_rating) { - avoidance_rating = charm_avoidance_rating; + + if (new_avoidance_rating) { + avoidance_rating = new_avoidance_rating; } - if (charm_atk) { - ATK = charm_atk; + + if (new_atk) { + ATK = new_atk; } - if (charm_min_dmg || charm_max_dmg) { - base_damage = round((charm_max_dmg - charm_min_dmg) / 1.9); - min_damage = charm_min_dmg - round(base_damage / 10.0); + + if (new_min_dmg || new_max_dmg) { + base_damage = std::round((new_max_dmg - new_min_dmg) / 1.9); + min_damage = new_min_dmg - std::round(base_damage / 10.0); } + if (RuleB(Spells, CharmDisablesSpecialAbilities)) { - ClearSpecialAbilities(); + if (remove_charm) { + ProcessSpecialAbilities(default_special_abilities); + } else { + ClearSpecialAbilities(); + } } - // the rest of the stats aren't cached, so lets just do these two instead of full CalcBonuses() SetAttackTimer(); CalcAC(); } diff --git a/zone/npc.h b/zone/npc.h index 4c4d3cb1b..90569d12b 100644 --- a/zone/npc.h +++ b/zone/npc.h @@ -346,7 +346,7 @@ public: int64 GetNPCHPRegen() const { return hp_regen + itembonuses.HPRegen + spellbonuses.HPRegen; } inline const char* GetAmmoIDfile() const { return ammo_idfile; } - void ModifyStatsOnCharm(bool is_charm_removed); + void ModifyStatsOnCharm(bool remove_charm, Mob* charmer); //waypoint crap int GetMaxWp() const { return max_wp; } diff --git a/zone/parcels.cpp b/zone/parcels.cpp index 4ef3b1a2b..f1dc874c1 100644 --- a/zone/parcels.cpp +++ b/zone/parcels.cpp @@ -61,6 +61,7 @@ void Client::SendBulkParcels() inst->SetCharges(p.second.quantity); inst->SetMerchantCount(1); inst->SetMerchantSlot(p.second.slot_id); + inst->SetEvolveCurrentAmount(p.second.evolve_amount); if (inst->IsStackable()) { inst->SetCharges(p.second.quantity); } @@ -164,6 +165,7 @@ void Client::SendParcel(Parcel_Struct &parcel_in) inst->SetCharges(p.quantity); inst->SetMerchantCount(1); inst->SetMerchantSlot(p.slot_id); + inst->SetEvolveCurrentAmount(p.evolve_amount); if (inst->IsStackable()) { inst->SetCharges(p.quantity); } @@ -381,23 +383,23 @@ void Client::DoParcelSend(const Parcel_Struct *parcel_in) return; } - uint32 quantity{}; + uint32 quantity = 1; if (inst->IsStackable()) { quantity = parcel_in->quantity; - } - else { - quantity = inst->GetCharges() >= 0 ? inst->GetCharges() : parcel_in->quantity; + } else if (inst->GetItem()->MaxCharges > 0) { + quantity = inst->GetCharges(); } CharacterParcelsRepository::CharacterParcels parcel_out{}; - parcel_out.from_name = GetName(); - parcel_out.note = parcel_in->note; - parcel_out.sent_date = time(nullptr); - parcel_out.quantity = quantity; - parcel_out.item_id = inst->GetID(); - parcel_out.char_id = send_to_client.at(0).char_id; - parcel_out.slot_id = next_slot; - parcel_out.id = 0; + parcel_out.from_name = GetName(); + parcel_out.note = parcel_in->note; + parcel_out.sent_date = time(nullptr); + parcel_out.quantity = quantity; + parcel_out.item_id = inst->GetID(); + parcel_out.char_id = send_to_client.at(0).char_id; + parcel_out.slot_id = next_slot; + parcel_out.evolve_amount = inst->GetEvolveCurrentAmount(); + parcel_out.id = 0; if (inst->IsAugmented()) { auto augs = inst->GetAugmentIDs(); @@ -445,7 +447,9 @@ void Client::DoParcelSend(const Parcel_Struct *parcel_in) cpc.aug_slot_5 = augs.at(4); cpc.aug_slot_6 = augs.at(5); } - cpc.quantity = kv.second->GetCharges() >= 0 ? kv.second->GetCharges() : 1; + + cpc.quantity = kv.second->GetCharges() >= 0 ? kv.second->GetCharges() : 1; + cpc.evolve_amount = kv.second->GetEvolveCurrentAmount(); all_entries.push_back(cpc); } CharacterParcelsContainersRepository::InsertMany(database, all_entries); @@ -679,6 +683,8 @@ void Client::DoParcelRetrieve(const ParcelRetrieve_Struct &parcel_in) return; } + inst->SetEvolveCurrentAmount(p->second.evolve_amount); + if (inst->IsStackable()) { inst->SetCharges(item_quantity > 0 ? item_quantity : 1); } @@ -715,6 +721,8 @@ void Client::DoParcelRetrieve(const ParcelRetrieve_Struct &parcel_in) return; } + item->SetEvolveCurrentAmount(i.evolve_amount); + if (CheckLoreConflict(item->GetItem())) { if (RuleB(Parcel, DeleteOnDuplicate)) { MessageString(Chat::Yellow, PARCEL_DUPLICATE_DELETE, inst->GetItem()->Name); diff --git a/zone/spell_effects.cpp b/zone/spell_effects.cpp index 145c4f67a..42078e322 100644 --- a/zone/spell_effects.cpp +++ b/zone/spell_effects.cpp @@ -835,12 +835,11 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial, int level_ove SendAppearancePacket(AppearanceType::Pet, caster->GetID(), true, true); } - if (IsClient()) - { + if (IsClient()) { CastToClient()->AI_Start(); - } else if(IsNPC()) { - CastToNPC()->SetPetSpellID(0); //not a pet spell. - CastToNPC()->ModifyStatsOnCharm(false); + } else if (IsNPC()) { + CastToNPC()->SetPetSpellID(0); //not a pet spell. + CastToNPC()->ModifyStatsOnCharm(false, caster); } bool bBreak = false; @@ -4418,10 +4417,9 @@ void Mob::BuffFadeBySlot(int slot, bool iRecalcBonuses) case SpellEffect::Charm: { - if(IsNPC()) - { + if (IsNPC()) { CastToNPC()->RestoreGuardSpotCharm(); - CastToNPC()->ModifyStatsOnCharm(true); + CastToNPC()->ModifyStatsOnCharm(true, GetOwner()); } SendAppearancePacket(AppearanceType::Pet, 0, true, true);