diff --git a/common/CMakeLists.txt b/common/CMakeLists.txt index 6de4825ab..eee1c7562 100644 --- a/common/CMakeLists.txt +++ b/common/CMakeLists.txt @@ -178,6 +178,7 @@ SET(repositories repositories/base/base_character_material_repository.h repositories/base/base_character_memmed_spells_repository.h repositories/base/base_character_parcels_repository.h + repositories/base/base_character_parcels_containers_repository.h repositories/base/base_character_peqzone_flags_repository.h repositories/base/base_character_pet_buffs_repository.h repositories/base/base_character_pet_info_repository.h @@ -359,6 +360,7 @@ SET(repositories repositories/character_material_repository.h repositories/character_memmed_spells_repository.h repositories/character_parcels_repository.h + repositories/character_parcels_containers_repository.h repositories/character_peqzone_flags_repository.h repositories/character_pet_buffs_repository.h repositories/character_pet_info_repository.h diff --git a/common/database/database_update_manifest.cpp b/common/database/database_update_manifest.cpp index 120347803..aff1d7173 100644 --- a/common/database/database_update_manifest.cpp +++ b/common/database/database_update_manifest.cpp @@ -5578,6 +5578,35 @@ ADD COLUMN `extra_haste` int(11) NOT NULL DEFAULT 0 AFTER `wis`; .sql = R"( ALTER TABLE `guild_bank` CHANGE COLUMN `qty` `qty` INT(10) NOT NULL DEFAULT '0' AFTER `itemid`; +)" + }, + ManifestEntry{ + .version = 9277, + .description = "2024_05_09_parcel_enable_containers.sql", + .check = "SHOW TABLES LIKE 'character_parcels_containers'", + .condition = "empty", + .match = "", + .sql = R"( +CREATE TABLE `character_parcels_containers` ( + `id` INT(10) UNSIGNED NOT NULL AUTO_INCREMENT, + `parcels_id` INT(10) UNSIGNED NOT NULL DEFAULT '0', + `slot_id` INT(10) UNSIGNED NOT NULL DEFAULT '0', + `item_id` INT(10) UNSIGNED NOT NULL DEFAULT '0', + `aug_slot_1` INT(10) UNSIGNED NOT NULL DEFAULT '0', + `aug_slot_2` INT(10) UNSIGNED NOT NULL DEFAULT '0', + `aug_slot_3` INT(10) UNSIGNED NOT NULL DEFAULT '0', + `aug_slot_4` INT(10) UNSIGNED NOT NULL DEFAULT '0', + `aug_slot_5` INT(10) UNSIGNED NOT NULL DEFAULT '0', + `aug_slot_6` INT(10) UNSIGNED NOT NULL DEFAULT '0', + `quantity` INT(10) UNSIGNED NOT NULL DEFAULT '0', + PRIMARY KEY (`id`) USING BTREE, + INDEX `fk_character_parcels_id` (`parcels_id`) USING BTREE, + CONSTRAINT `fk_character_parcels_id` FOREIGN KEY (`parcels_id`) REFERENCES `character_parcels` (`id`) ON UPDATE NO ACTION ON DELETE CASCADE +) +COLLATE='latin1_swedish_ci' +ENGINE=InnoDB +AUTO_INCREMENT=1 +; )" } // -- template; copy/paste this when you need to create a new entry diff --git a/common/database_schema.h b/common/database_schema.h index e7c554179..61bdb5bea 100644 --- a/common/database_schema.h +++ b/common/database_schema.h @@ -60,6 +60,7 @@ namespace DatabaseSchema { {"character_material", "id"}, {"character_memmed_spells", "id"}, {"character_parcels", "char_id"}, + {"character_parcels_containers", "id"}, {"character_pet_buffs", "char_id"}, {"character_pet_info", "char_id"}, {"character_pet_inventory", "char_id"}, @@ -130,6 +131,7 @@ namespace DatabaseSchema { "character_material", "character_memmed_spells", "character_parcels", + "character_parcels_containers", "character_pet_buffs", "character_pet_info", "character_pet_inventory", diff --git a/common/repositories/base/base_character_parcels_containers_repository.h b/common/repositories/base/base_character_parcels_containers_repository.h new file mode 100644 index 000000000..9c98cc068 --- /dev/null +++ b/common/repositories/base/base_character_parcels_containers_repository.h @@ -0,0 +1,499 @@ +/** + * DO NOT MODIFY THIS FILE + * + * This repository was automatically generated and is NOT to be modified directly. + * Any repository modifications are meant to be made to the repository extending the base. + * Any modifications to base repositories are to be made by the generator only + * + * @generator ./utils/scripts/generators/repository-generator.pl + * @docs https://docs.eqemu.io/developer/repositories + */ + +#ifndef EQEMU_BASE_CHARACTER_PARCELS_CONTAINERS_REPOSITORY_H +#define EQEMU_BASE_CHARACTER_PARCELS_CONTAINERS_REPOSITORY_H + +#include "../../database.h" +#include "../../strings.h" +#include + +class BaseCharacterParcelsContainersRepository { +public: + struct CharacterParcelsContainers { + uint32_t id; + uint32_t parcels_id; + uint32_t slot_id; + uint32_t item_id; + uint32_t aug_slot_1; + uint32_t aug_slot_2; + uint32_t aug_slot_3; + uint32_t aug_slot_4; + uint32_t aug_slot_5; + uint32_t aug_slot_6; + uint32_t quantity; + }; + + static std::string PrimaryKey() + { + return std::string("id"); + } + + static std::vector Columns() + { + return { + "id", + "parcels_id", + "slot_id", + "item_id", + "aug_slot_1", + "aug_slot_2", + "aug_slot_3", + "aug_slot_4", + "aug_slot_5", + "aug_slot_6", + "quantity", + }; + } + + static std::vector SelectColumns() + { + return { + "id", + "parcels_id", + "slot_id", + "item_id", + "aug_slot_1", + "aug_slot_2", + "aug_slot_3", + "aug_slot_4", + "aug_slot_5", + "aug_slot_6", + "quantity", + }; + } + + static std::string ColumnsRaw() + { + return std::string(Strings::Implode(", ", Columns())); + } + + static std::string SelectColumnsRaw() + { + return std::string(Strings::Implode(", ", SelectColumns())); + } + + static std::string TableName() + { + return std::string("character_parcels_containers"); + } + + static std::string BaseSelect() + { + return fmt::format( + "SELECT {} FROM {}", + SelectColumnsRaw(), + TableName() + ); + } + + static std::string BaseInsert() + { + return fmt::format( + "INSERT INTO {} ({}) ", + TableName(), + ColumnsRaw() + ); + } + + static CharacterParcelsContainers NewEntity() + { + 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; + + return e; + } + + static CharacterParcelsContainers GetCharacterParcelsContainers( + const std::vector &character_parcels_containerss, + int character_parcels_containers_id + ) + { + for (auto &character_parcels_containers : character_parcels_containerss) { + if (character_parcels_containers.id == character_parcels_containers_id) { + return character_parcels_containers; + } + } + + return NewEntity(); + } + + static CharacterParcelsContainers FindOne( + Database& db, + int character_parcels_containers_id + ) + { + auto results = db.QueryDatabase( + fmt::format( + "{} WHERE {} = {} LIMIT 1", + BaseSelect(), + PrimaryKey(), + character_parcels_containers_id + ) + ); + + auto row = results.begin(); + 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; + + return e; + } + + return NewEntity(); + } + + static int DeleteOne( + Database& db, + int character_parcels_containers_id + ) + { + auto results = db.QueryDatabase( + fmt::format( + "DELETE FROM {} WHERE {} = {}", + TableName(), + PrimaryKey(), + character_parcels_containers_id + ) + ); + + return (results.Success() ? results.RowsAffected() : 0); + } + + static int UpdateOne( + Database& db, + const CharacterParcelsContainers &e + ) + { + std::vector v; + + auto columns = Columns(); + + v.push_back(columns[1] + " = " + std::to_string(e.parcels_id)); + v.push_back(columns[2] + " = " + std::to_string(e.slot_id)); + v.push_back(columns[3] + " = " + std::to_string(e.item_id)); + v.push_back(columns[4] + " = " + std::to_string(e.aug_slot_1)); + v.push_back(columns[5] + " = " + std::to_string(e.aug_slot_2)); + v.push_back(columns[6] + " = " + std::to_string(e.aug_slot_3)); + v.push_back(columns[7] + " = " + std::to_string(e.aug_slot_4)); + 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)); + + auto results = db.QueryDatabase( + fmt::format( + "UPDATE {} SET {} WHERE {} = {}", + TableName(), + Strings::Implode(", ", v), + PrimaryKey(), + e.id + ) + ); + + return (results.Success() ? results.RowsAffected() : 0); + } + + static CharacterParcelsContainers InsertOne( + Database& db, + CharacterParcelsContainers e + ) + { + std::vector v; + + v.push_back(std::to_string(e.id)); + v.push_back(std::to_string(e.parcels_id)); + v.push_back(std::to_string(e.slot_id)); + v.push_back(std::to_string(e.item_id)); + v.push_back(std::to_string(e.aug_slot_1)); + v.push_back(std::to_string(e.aug_slot_2)); + v.push_back(std::to_string(e.aug_slot_3)); + v.push_back(std::to_string(e.aug_slot_4)); + 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)); + + auto results = db.QueryDatabase( + fmt::format( + "{} VALUES ({})", + BaseInsert(), + Strings::Implode(",", v) + ) + ); + + if (results.Success()) { + e.id = results.LastInsertedID(); + return e; + } + + e = NewEntity(); + + return e; + } + + static int InsertMany( + Database& db, + const std::vector &entries + ) + { + std::vector insert_chunks; + + for (auto &e: entries) { + std::vector v; + + v.push_back(std::to_string(e.id)); + v.push_back(std::to_string(e.parcels_id)); + v.push_back(std::to_string(e.slot_id)); + v.push_back(std::to_string(e.item_id)); + v.push_back(std::to_string(e.aug_slot_1)); + v.push_back(std::to_string(e.aug_slot_2)); + v.push_back(std::to_string(e.aug_slot_3)); + v.push_back(std::to_string(e.aug_slot_4)); + 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)); + + insert_chunks.push_back("(" + Strings::Implode(",", v) + ")"); + } + + std::vector v; + + auto results = db.QueryDatabase( + fmt::format( + "{} VALUES {}", + BaseInsert(), + Strings::Implode(",", insert_chunks) + ) + ); + + return (results.Success() ? results.RowsAffected() : 0); + } + + static std::vector All(Database& db) + { + std::vector all_entries; + + auto results = db.QueryDatabase( + fmt::format( + "{}", + BaseSelect() + ) + ); + + all_entries.reserve(results.RowCount()); + + 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; + + all_entries.push_back(e); + } + + return all_entries; + } + + static std::vector GetWhere(Database& db, const std::string &where_filter) + { + std::vector all_entries; + + auto results = db.QueryDatabase( + fmt::format( + "{} WHERE {}", + BaseSelect(), + where_filter + ) + ); + + all_entries.reserve(results.RowCount()); + + 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; + + all_entries.push_back(e); + } + + return all_entries; + } + + static int DeleteWhere(Database& db, const std::string &where_filter) + { + auto results = db.QueryDatabase( + fmt::format( + "DELETE FROM {} WHERE {}", + TableName(), + where_filter + ) + ); + + return (results.Success() ? results.RowsAffected() : 0); + } + + static int Truncate(Database& db) + { + auto results = db.QueryDatabase( + fmt::format( + "TRUNCATE TABLE {}", + TableName() + ) + ); + + return (results.Success() ? results.RowsAffected() : 0); + } + + static int64 GetMaxId(Database& db) + { + auto results = db.QueryDatabase( + fmt::format( + "SELECT COALESCE(MAX({}), 0) FROM {}", + PrimaryKey(), + TableName() + ) + ); + + return (results.Success() && results.begin()[0] ? strtoll(results.begin()[0], nullptr, 10) : 0); + } + + static int64 Count(Database& db, const std::string &where_filter = "") + { + auto results = db.QueryDatabase( + fmt::format( + "SELECT COUNT(*) FROM {} {}", + TableName(), + (where_filter.empty() ? "" : "WHERE " + where_filter) + ) + ); + + return (results.Success() && results.begin()[0] ? strtoll(results.begin()[0], nullptr, 10) : 0); + } + + static std::string BaseReplace() + { + return fmt::format( + "REPLACE INTO {} ({}) ", + TableName(), + ColumnsRaw() + ); + } + + static int ReplaceOne( + Database& db, + const CharacterParcelsContainers &e + ) + { + std::vector v; + + v.push_back(std::to_string(e.id)); + v.push_back(std::to_string(e.parcels_id)); + v.push_back(std::to_string(e.slot_id)); + v.push_back(std::to_string(e.item_id)); + v.push_back(std::to_string(e.aug_slot_1)); + v.push_back(std::to_string(e.aug_slot_2)); + v.push_back(std::to_string(e.aug_slot_3)); + v.push_back(std::to_string(e.aug_slot_4)); + 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)); + + auto results = db.QueryDatabase( + fmt::format( + "{} VALUES ({})", + BaseReplace(), + Strings::Implode(",", v) + ) + ); + + return (results.Success() ? results.RowsAffected() : 0); + } + + static int ReplaceMany( + Database& db, + const std::vector &entries + ) + { + std::vector insert_chunks; + + for (auto &e: entries) { + std::vector v; + + v.push_back(std::to_string(e.id)); + v.push_back(std::to_string(e.parcels_id)); + v.push_back(std::to_string(e.slot_id)); + v.push_back(std::to_string(e.item_id)); + v.push_back(std::to_string(e.aug_slot_1)); + v.push_back(std::to_string(e.aug_slot_2)); + v.push_back(std::to_string(e.aug_slot_3)); + v.push_back(std::to_string(e.aug_slot_4)); + 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)); + + insert_chunks.push_back("(" + Strings::Implode(",", v) + ")"); + } + + std::vector v; + + auto results = db.QueryDatabase( + fmt::format( + "{} VALUES {}", + BaseReplace(), + Strings::Implode(",", insert_chunks) + ) + ); + + return (results.Success() ? results.RowsAffected() : 0); + } +}; + +#endif //EQEMU_BASE_CHARACTER_PARCELS_CONTAINERS_REPOSITORY_H diff --git a/common/repositories/character_parcels_containers_repository.h b/common/repositories/character_parcels_containers_repository.h new file mode 100644 index 000000000..80f23ad35 --- /dev/null +++ b/common/repositories/character_parcels_containers_repository.h @@ -0,0 +1,50 @@ +#ifndef EQEMU_CHARACTER_PARCELS_CONTAINERS_REPOSITORY_H +#define EQEMU_CHARACTER_PARCELS_CONTAINERS_REPOSITORY_H + +#include "../database.h" +#include "../strings.h" +#include "base/base_character_parcels_containers_repository.h" + +class CharacterParcelsContainersRepository: public BaseCharacterParcelsContainersRepository { +public: + + /** + * This file was auto generated and can be modified and extended upon + * + * Base repository methods are automatically + * generated in the "base" version of this repository. The base repository + * is immutable and to be left untouched, while methods in this class + * are used as extension methods for more specific persistence-layer + * accessors or mutators. + * + * Base Methods (Subject to be expanded upon in time) + * + * Note: Not all tables are designed appropriately to fit functionality with all base methods + * + * InsertOne + * UpdateOne + * DeleteOne + * FindOne + * GetWhere(std::string where_filter) + * DeleteWhere(std::string where_filter) + * InsertMany + * All + * + * Example custom methods in a repository + * + * CharacterParcelsContainersRepository::GetByZoneAndVersion(int zone_id, int zone_version) + * CharacterParcelsContainersRepository::GetWhereNeverExpires() + * CharacterParcelsContainersRepository::GetWhereXAndY() + * CharacterParcelsContainersRepository::DeleteWhereXAndY() + * + * Most of the above could be covered by base methods, but if you as a developer + * find yourself re-using logic for other parts of the code, its best to just make a + * method that can be re-used easily elsewhere especially if it can use a base repository + * method and encapsulate filters there + */ + + // Custom extended repository methods here + +}; + +#endif //EQEMU_CHARACTER_PARCELS_CONTAINERS_REPOSITORY_H diff --git a/common/version.h b/common/version.h index 2257221e2..a21f51dac 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 9276 +#define CURRENT_BINARY_DATABASE_VERSION 9277 #define CURRENT_BINARY_BOTS_DATABASE_VERSION 9044 #endif diff --git a/zone/client.cpp b/zone/client.cpp index c95f995e1..d586c97fe 100644 --- a/zone/client.cpp +++ b/zone/client.cpp @@ -12435,3 +12435,43 @@ uint16 Client::GetSkill(EQ::skills::SkillType skill_id) const } return 0; } + +void Client::RemoveItemBySerialNumber(uint32 serial_number, uint32 quantity) +{ + EQ::ItemInstance *item = nullptr; + static const int16 slots[][2] = { + { EQ::invslot::POSSESSIONS_BEGIN, EQ::invslot::POSSESSIONS_END }, + { EQ::invbag::GENERAL_BAGS_BEGIN, EQ::invbag::GENERAL_BAGS_END }, + { EQ::invbag::CURSOR_BAG_BEGIN, EQ::invbag::CURSOR_BAG_END}, + { EQ::invslot::BANK_BEGIN, EQ::invslot::BANK_END }, + { EQ::invslot::GUILD_TRIBUTE_BEGIN, EQ::invslot::GUILD_TRIBUTE_END }, + { EQ::invbag::BANK_BAGS_BEGIN, EQ::invbag::BANK_BAGS_END }, + { EQ::invslot::SHARED_BANK_BEGIN, EQ::invslot::SHARED_BANK_END }, + { EQ::invbag::SHARED_BANK_BAGS_BEGIN, EQ::invbag::SHARED_BANK_BAGS_END }, + }; + int16 removed_count = 0; + const size_t slot_index_count = sizeof(slots) / sizeof(slots[0]); + for (int slot_index = 0; slot_index < slot_index_count; ++slot_index) { + for (int slot_id = slots[slot_index][0]; slot_id <= slots[slot_index][1]; ++slot_id) { + if (removed_count == quantity) { + break; + } + + item = GetInv().GetItem(slot_id); + if (item && item->GetSerialNumber() == serial_number) { + int16 charges = item->IsStackable() ? item->GetCharges() : 0; + int16 stack_size = std::max(charges, static_cast(1)); + if ((removed_count + stack_size) <= quantity) { + removed_count += stack_size; + DeleteItemInInventory(slot_id, charges, true); + } else { + int16 amount_left = (quantity - removed_count); + if (amount_left > 0 && stack_size >= amount_left) { + removed_count += amount_left; + DeleteItemInInventory(slot_id, amount_left, true); + } + } + } + } + } +} \ No newline at end of file diff --git a/zone/client.h b/zone/client.h index 24ce3a002..7ac740f3d 100644 --- a/zone/client.h +++ b/zone/client.h @@ -1042,6 +1042,7 @@ public: void SetItemCooldown(uint32 item_id, bool use_saved_timer = false, uint32 in_seconds = 1); uint32 GetItemCooldown(uint32 item_id); void RemoveItem(uint32 item_id, uint32 quantity = 1); + void RemoveItemBySerialNumber(uint32 serial_number, uint32 quantity = 1); bool SwapItem(MoveItem_Struct* move_in); void SwapItemResync(MoveItem_Struct* move_slots); void QSSwapItemAuditor(MoveItem_Struct* move_in, bool postaction_call = false); diff --git a/zone/parcels.cpp b/zone/parcels.cpp index 6304a1951..5d69e0e59 100644 --- a/zone/parcels.cpp +++ b/zone/parcels.cpp @@ -20,6 +20,7 @@ #include "../common/events/player_event_logs.h" #include "../common/repositories/trader_repository.h" #include "../common/repositories/character_parcels_repository.h" +#include "../common/repositories/character_parcels_containers_repository.h" #include "worldserver.h" #include "string_ids.h" #include "client.h" @@ -403,7 +404,30 @@ void Client::DoParcelSend(const Parcel_Struct *parcel_in) return; } - RemoveItem(parcel_out.item_id, parcel_out.quantity); + std::vector all_entries{}; + if (inst->IsNoneEmptyContainer()) { + CharacterParcelsContainersRepository::CharacterParcelsContainers cpc{}; + + for (auto const &kv: *inst->GetContents()) { + cpc.parcels_id = result.id; + cpc.slot_id = kv.first; + cpc.item_id = kv.second->GetID(); + if (kv.second->IsAugmented()) { + auto augs = kv.second->GetAugmentIDs(); + cpc.aug_slot_1 = augs.at(0); + cpc.aug_slot_2 = augs.at(1); + cpc.aug_slot_3 = augs.at(2); + cpc.aug_slot_4 = augs.at(3); + cpc.aug_slot_5 = augs.at(4); + cpc.aug_slot_6 = augs.at(5); + } + cpc.quantity = kv.second->GetCharges() > 0 ? kv.second->GetCharges() : 1; + all_entries.push_back(cpc); + } + CharacterParcelsContainersRepository::InsertMany(database, all_entries); + } + + RemoveItemBySerialNumber(inst->GetSerialNumber(), parcel_out.quantity); std::unique_ptr outapp(new EQApplicationPacket(OP_ShopSendParcel)); QueuePacket(outapp.get()); @@ -435,6 +459,23 @@ void Client::DoParcelSend(const Parcel_Struct *parcel_in) e.sent_date = parcel_out.sent_date; RecordPlayerEventLog(PlayerEvent::PARCEL_SEND, e); + + if (!all_entries.empty()) { + for (auto const &i: all_entries) { + e.from_player_name = parcel_out.from_name; + e.to_player_name = send_to_client.at(0).character_name; + e.item_id = i.item_id; + e.aug_slot_1 = i.aug_slot_1; + e.aug_slot_2 = i.aug_slot_2; + e.aug_slot_3 = i.aug_slot_3; + e.aug_slot_4 = i.aug_slot_4; + e.aug_slot_5 = i.aug_slot_5; + e.aug_slot_6 = i.aug_slot_6; + e.quantity = i.quantity; + e.sent_date = parcel_out.sent_date; + RecordPlayerEventLog(PlayerEvent::PARCEL_SEND, e); + } + } } Parcel_Struct ps{}; @@ -663,8 +704,54 @@ void Client::DoParcelRetrieve(const ParcelRetrieve_Struct &parcel_in) } } else if (free_id != INVALID_INDEX) { + std::vector results{}; + + if (inst->IsClassBag() && inst->GetItem()->BagSlots > 0) { + results = CharacterParcelsContainersRepository::GetWhere(database, fmt::format("`parcels_id` = {}", p->second.id)); + for (auto const &i : results) { + std::unique_ptr item( + database.CreateItem( + i.item_id, + i.quantity, + i.aug_slot_1, + i.aug_slot_2, + i.aug_slot_3, + i.aug_slot_4, + i.aug_slot_5, + i.aug_slot_6 + ) + ); + if (CheckLoreConflict(item->GetItem())) { + Message( + Chat::Yellow, + fmt::format("Lore Item Found in Inventory: {}", item->GetItem()->Name).c_str()); + MessageString(Chat::Yellow, DUP_LORE); + Message(Chat::Red, "Unable to retrieve parcel."); + SendParcelRetrieveAck(); + return; + } + } + } inst->SetCharges(item_quantity > 0 ? item_quantity : 1); if (PutItemInInventory(free_id, *inst.get(), true)) { + if (inst->IsClassBag() && inst->GetItem()->BagSlots > 0) { + for (auto const &i: results) { + std::unique_ptr item( + database.CreateItem( + i.item_id, + i.quantity, + i.aug_slot_1, + i.aug_slot_2, + i.aug_slot_3, + i.aug_slot_4, + i.aug_slot_5, + i.aug_slot_6 + ) + ); + auto bag_slot = EQ::InventoryProfile::CalcSlotId(free_id, i.slot_id); + PutItemInInventory(bag_slot, *item.get(), true); + } + } MessageString( Chat::Yellow, PARCEL_DELIVERED, @@ -672,6 +759,34 @@ void Client::DoParcelRetrieve(const ParcelRetrieve_Struct &parcel_in) inst->GetItem()->Name, p->second.from_name.c_str() ); + if (player_event_logs.IsEventEnabled(PlayerEvent::PARCEL_RETRIEVE)) { + PlayerEvent::ParcelRetrieve e{}; + e.from_player_name = p->second.from_name; + e.item_id = p->second.item_id; + e.aug_slot_1 = p->second.aug_slot_1; + e.aug_slot_2 = p->second.aug_slot_2; + e.aug_slot_3 = p->second.aug_slot_3; + e.aug_slot_4 = p->second.aug_slot_4; + e.aug_slot_5 = p->second.aug_slot_5; + e.aug_slot_6 = p->second.aug_slot_6; + e.quantity = p->second.quantity; + e.sent_date = p->second.sent_date; + RecordPlayerEventLog(PlayerEvent::PARCEL_RETRIEVE, e); + + for (auto const &i:results) { + e.from_player_name = p->second.from_name; + e.item_id = i.item_id; + e.aug_slot_1 = i.aug_slot_1; + e.aug_slot_2 = i.aug_slot_2; + e.aug_slot_3 = i.aug_slot_3; + e.aug_slot_4 = i.aug_slot_4; + e.aug_slot_5 = i.aug_slot_5; + e.aug_slot_6 = i.aug_slot_6; + e.quantity = i.quantity; + e.sent_date = p->second.sent_date; + RecordPlayerEventLog(PlayerEvent::PARCEL_RETRIEVE, e); + } + } } else { MessageString(Chat::Yellow, PARCEL_INV_FULL, merchant->GetCleanName()); @@ -687,22 +802,6 @@ void Client::DoParcelRetrieve(const ParcelRetrieve_Struct &parcel_in) } } - if (player_event_logs.IsEventEnabled(PlayerEvent::PARCEL_RETRIEVE)) { - PlayerEvent::ParcelRetrieve e{}; - e.from_player_name = p->second.from_name; - e.item_id = p->second.item_id; - e.aug_slot_1 = p->second.aug_slot_1; - e.aug_slot_2 = p->second.aug_slot_2; - e.aug_slot_3 = p->second.aug_slot_3; - e.aug_slot_4 = p->second.aug_slot_4; - e.aug_slot_5 = p->second.aug_slot_5; - e.aug_slot_6 = p->second.aug_slot_6; - e.quantity = p->second.quantity; - e.sent_date = p->second.sent_date; - - RecordPlayerEventLog(PlayerEvent::PARCEL_RETRIEVE, e); - } - DeleteParcel(p->second.id); SendParcelDelete(parcel_in); m_parcels.erase(p);