From ea1ffd68940c9354e00a1bd8c748a1a09b4d360c Mon Sep 17 00:00:00 2001 From: Skjalf <47818697+Nyeriah@users.noreply.github.com> Date: Mon, 20 Dec 2021 16:11:45 -0300 Subject: [PATCH] feat(Scripts/Commands): Implement item refund command (#9811) --- .../rev_1640021759359148700.sql | 16 ++ .../Implementation/CharacterDatabase.cpp | 3 + .../Implementation/CharacterDatabase.h | 3 + src/server/game/Miscellaneous/Language.h | 12 +- src/server/scripts/Commands/cs_item.cpp | 197 +++++++++++++++++- 5 files changed, 229 insertions(+), 2 deletions(-) create mode 100644 data/sql/updates/pending_db_world/rev_1640021759359148700.sql diff --git a/data/sql/updates/pending_db_world/rev_1640021759359148700.sql b/data/sql/updates/pending_db_world/rev_1640021759359148700.sql new file mode 100644 index 000000000..9b548ef27 --- /dev/null +++ b/data/sql/updates/pending_db_world/rev_1640021759359148700.sql @@ -0,0 +1,16 @@ +INSERT INTO `version_db_world` (`sql_rev`) VALUES ('1640021759359148700'); + +DELETE FROM `command` WHERE `name` IN ('item refund'); +INSERT INTO `command` (`name`, `security`, `help`) VALUES +('item refund', 3, 'Syntax: .item refund \nRemoves the item and restores honor/arena/items according to extended cost.'); + +DELETE FROM `acore_string` WHERE `entry` IN (5071, 5072, 5073, 5074, 5075, 5076, 5077, 5078); +INSERT INTO `acore_string` (`entry`, `content_default`) VALUES +(5071, 'The extendedcost entry provided does not exist.'), +(5072, 'Refunding %s (%u) would send the target over the honor points limit (limit: %u, current honor: %u, honor to be refunded: %u).'), +(5073, 'An attempt of refunding your item %s has failed because it would put you over the honor points limit.'), +(5074, 'Item %s (%u) was refunded, restoring %u honor points.'), +(5075, 'Refunding %s (%u) would send the target over the arena points limit (limit: %u, current arena points: %u, arena points to be refunded: %u).'), +(5076, 'An attempt of refunding your item %s has failed because it would put you over the arena points limit.'), +(5077, 'Item %s (%u) was refunded, restoring %u arena points.'), +(5078, 'Item not found in the character\'s inventory (bank included)'); diff --git a/src/server/database/Database/Implementation/CharacterDatabase.cpp b/src/server/database/Database/Implementation/CharacterDatabase.cpp index c213443f0..f81862717 100644 --- a/src/server/database/Database/Implementation/CharacterDatabase.cpp +++ b/src/server/database/Database/Implementation/CharacterDatabase.cpp @@ -598,6 +598,9 @@ void CharacterDatabaseConnection::DoPrepareStatements() PrepareStatement(CHAR_DEL_RECOVERY_ITEM, "DELETE FROM recovery_item WHERE Guid = ? AND ItemEntry = ? AND Count = ? ORDER BY Id DESC LIMIT 1", CONNECTION_ASYNC); PrepareStatement(CHAR_DEL_RECOVERY_ITEM_BY_RECOVERY_ID, "DELETE FROM recovery_item WHERE id = ?", CONNECTION_ASYNC); + PrepareStatement(CHAR_SEL_HONORPOINTS, "SELECT totalHonorPoints FROM characters WHERE guid = ?", CONNECTION_SYNCH); + PrepareStatement(CHAR_SEL_ARENAPOINTS, "SELECT arenaPoints FROM characters WHERE guid = ?", CONNECTION_SYNCH); + // Character names PrepareStatement(CHAR_INS_RESERVED_PLAYER_NAME, "INSERT IGNORE INTO reserved_name (name) VALUES (?)", CONNECTION_ASYNC); } diff --git a/src/server/database/Database/Implementation/CharacterDatabase.h b/src/server/database/Database/Implementation/CharacterDatabase.h index 2d675c667..5149373fe 100644 --- a/src/server/database/Database/Implementation/CharacterDatabase.h +++ b/src/server/database/Database/Implementation/CharacterDatabase.h @@ -514,6 +514,9 @@ enum CharacterDatabaseStatements : uint32 CHAR_DEL_RECOVERY_ITEM, CHAR_DEL_RECOVERY_ITEM_BY_RECOVERY_ID, + CHAR_SEL_HONORPOINTS, + CHAR_SEL_ARENAPOINTS, + CHAR_INS_RESERVED_PLAYER_NAME, MAX_CHARACTERDATABASE_STATEMENTS diff --git a/src/server/game/Miscellaneous/Language.h b/src/server/game/Miscellaneous/Language.h index 29612431c..ea5c74cb8 100644 --- a/src/server/game/Miscellaneous/Language.h +++ b/src/server/game/Miscellaneous/Language.h @@ -1233,9 +1233,19 @@ enum AcoreStrings LANG_COMMAND_QUEST_NOT_FOUND_IN_LOG = 5068, LANG_COMMAND_QUEST_NOT_COMPLETE = 5069, - // Room for more strings 5070-9999 LANG_COMMAND_DISABLED = 5070, + LANG_CMD_ITEM_REFUND_BAD_EXTENDED_COST = 5071, + LANG_CMD_ITEM_REFUND_MAX_HONOR = 5072, + LANG_CMD_ITEM_REFUND_HONOR_FAILED = 5073, + LANG_CMD_ITEM_REFUNDED_HONOR = 5074, + LANG_CMD_ITEM_REFUND_MAX_AP = 5075, + LANG_CMD_ITEM_REFUND_AP_FAILED = 5076, + LANG_CMD_ITEM_REFUNDED_AP = 5077, + LANG_CMD_ITEM_REFUND_NOT_FOUND = 5078, + + // Room for more strings 5078-9999 + // Level requirement notifications LANG_SAY_REQ = 6604, LANG_WHISPER_REQ = 6605, diff --git a/src/server/scripts/Commands/cs_item.cpp b/src/server/scripts/Commands/cs_item.cpp index 8a57e6782..c22b2e3f9 100644 --- a/src/server/scripts/Commands/cs_item.cpp +++ b/src/server/scripts/Commands/cs_item.cpp @@ -48,6 +48,7 @@ public: { { "restore", HandleItemRestoreCommandTable }, { "move", HandleItemMoveCommand, SEC_GAMEMASTER, Console::Yes }, + { "refund", HandleItemRefundCommand, SEC_ADMINISTRATOR, Console::Yes }, }; static ChatCommandTable commandTable = { @@ -180,7 +181,201 @@ public: static bool HasItemDeletionConfig() { - return sWorld->getBoolConfig(CONFIG_ITEMDELETE_METHOD) || sWorld->getBoolConfig(CONFIG_ITEMDELETE_VENDOR); + return sWorld->getBoolConfig(CONFIG_ITEMDELETE_METHOD) || sWorld->getBoolConfig(CONFIG_ITEMDELETE_VENDOR); + } + + static bool HandleItemRefundCommand(ChatHandler* handler, PlayerIdentifier player, uint32 itemId, uint32 extendedCost) + { + ItemExtendedCostEntry const* iece = sItemExtendedCostStore.LookupEntry(extendedCost); + if (!iece) + { + handler->PSendSysMessage(LANG_CMD_ITEM_REFUND_BAD_EXTENDED_COST); + handler->SetSentErrorMessage(true); + return false; + } + + ItemTemplate const* item = sObjectMgr->GetItemTemplate(itemId); + + if (!item) + { + handler->PSendSysMessage(LANG_COMMAND_ITEMIDINVALID, itemId); + handler->SetSentErrorMessage(true); + return false; + } + + if (Player* target = player.GetConnectedPlayer()) + { + if (!target->HasItemCount(itemId, 1, true)) + { + handler->PSendSysMessage(LANG_CMD_ITEM_REFUND_NOT_FOUND, itemId); + handler->SetSentErrorMessage(true); + return false; + } + + if (iece->reqhonorpoints) + { + uint32 honor = target->GetHonorPoints() + iece->reqhonorpoints; + if (honor > sWorld->getIntConfig(CONFIG_MAX_HONOR_POINTS)) + { + handler->PSendSysMessage(LANG_CMD_ITEM_REFUND_MAX_HONOR, item->Name1, item->ItemId, sWorld->getIntConfig(CONFIG_MAX_HONOR_POINTS), target->GetHonorPoints(), iece->reqhonorpoints); + ChatHandler(target->GetSession()).PSendSysMessage(LANG_CMD_ITEM_REFUND_HONOR_FAILED, item->Name1); + handler->SetSentErrorMessage(true); + return false; + } + + target->SetHonorPoints(honor); + ChatHandler(target->GetSession()).PSendSysMessage(LANG_CMD_ITEM_REFUNDED_HONOR, item->Name1, item->ItemId, iece->reqhonorpoints); + handler->PSendSysMessage(LANG_CMD_ITEM_REFUNDED_HONOR, item->Name1, item->ItemId, iece->reqhonorpoints); + } + + if (iece->reqarenapoints) + { + uint32 arenapoints = target->GetArenaPoints() + iece->reqarenapoints; + if (arenapoints > sWorld->getIntConfig(CONFIG_MAX_ARENA_POINTS)) + { + handler->PSendSysMessage(LANG_CMD_ITEM_REFUND_MAX_AP, item->Name1, item->ItemId, sWorld->getIntConfig(CONFIG_MAX_ARENA_POINTS), target->GetArenaPoints(), iece->reqarenapoints); + ChatHandler(target->GetSession()).PSendSysMessage(LANG_CMD_ITEM_REFUND_AP_FAILED, item->Name1); + handler->SetSentErrorMessage(true); + return false; + } + + target->SetArenaPoints(arenapoints); + ChatHandler(target->GetSession()).PSendSysMessage(LANG_CMD_ITEM_REFUNDED_AP, item->Name1, item->ItemId, iece->reqarenapoints); + handler->PSendSysMessage(LANG_CMD_ITEM_REFUNDED_AP, item->Name1, item->ItemId, iece->reqarenapoints); + } + + uint8 count = 0; + for (uint32 const& reqItem : iece->reqitem) + { + if (reqItem) + { + target->AddItem(reqItem, iece->reqitemcount[count]); + } + + ++count; + } + + target->DestroyItemCount(itemId, 1, true); + } + else + { + CharacterDatabaseTransaction trans = CharacterDatabase.BeginTransaction(); + CharacterDatabasePreparedStatement* stmt; + + ObjectGuid::LowType guid = player.GetGUID().GetCounter(); + + stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_CHAR_INVENTORY_ITEM_BY_ENTRY_AND_OWNER); + stmt->setUInt32(0, itemId); + stmt->setUInt32(1, guid); + + PreparedQueryResult result = CharacterDatabase.Query(stmt); + + if (result) + { + if (iece->reqhonorpoints) + { + stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_HONORPOINTS); + stmt->setUInt32(0, guid); + + PreparedQueryResult queryResult = CharacterDatabase.Query(stmt); + + if (queryResult) + { + Field* fields = queryResult->Fetch(); + if ((fields[0].GetUInt32() + iece->reqhonorpoints) > sWorld->getIntConfig(CONFIG_MAX_HONOR_POINTS)) + { + handler->PSendSysMessage(LANG_CMD_ITEM_REFUND_MAX_HONOR, item->Name1, item->ItemId, sWorld->getIntConfig(CONFIG_MAX_HONOR_POINTS), fields[0].GetUInt32(), iece->reqhonorpoints); + handler->SetSentErrorMessage(true); + return false; + } + } + + stmt = CharacterDatabase.GetPreparedStatement(CHAR_UDP_CHAR_HONOR_POINTS_ACCUMULATIVE); + stmt->setUInt32(0, iece->reqhonorpoints); + stmt->setUInt32(1, guid); + trans->Append(stmt); + handler->PSendSysMessage(LANG_CMD_ITEM_REFUNDED_HONOR, item->Name1, item->ItemId, iece->reqhonorpoints); + } + + if (iece->reqarenapoints) + { + stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_ARENAPOINTS); + stmt->setUInt32(0, guid); + + PreparedQueryResult queryResult = CharacterDatabase.Query(stmt); + + if (queryResult) + { + Field* fields = queryResult->Fetch(); + if ((fields[0].GetUInt32() + iece->reqhonorpoints) > sWorld->getIntConfig(CONFIG_MAX_ARENA_POINTS)) + { + handler->PSendSysMessage(LANG_CMD_ITEM_REFUND_MAX_AP, item->Name1, item->ItemId, sWorld->getIntConfig(CONFIG_MAX_ARENA_POINTS), fields[0].GetUInt32(), iece->reqarenapoints); + handler->SetSentErrorMessage(true); + return false; + } + } + + stmt = CharacterDatabase.GetPreparedStatement(CHAR_UDP_CHAR_ARENA_POINTS_ACCUMULATIVE); + stmt->setUInt32(0, iece->reqarenapoints); + stmt->setUInt32(1, guid); + trans->Append(stmt); + handler->PSendSysMessage(LANG_CMD_ITEM_REFUNDED_AP, item->Name1, item->ItemId, iece->reqarenapoints); + } + + MailSender sender(MAIL_NORMAL, guid, MAIL_STATIONERY_GM); + // fill mail + std::string msg = "Your item " + item->Name1 + " has been removed and the used currency restored. This mail contains any items used as currency."; + MailDraft draft("Item Refund", msg); + + uint8 count = 0; + bool foundItems = false; + for (uint32 const& reqItem : iece->reqitem) + { + if (reqItem) + { + // Skip invalid items. + if (!sObjectMgr->GetItemTemplate(reqItem)) + { + continue; + } + + if (Item* item = Item::CreateItem(reqItem, iece->reqitemcount[count])) + { + item->SaveToDB(trans); + draft.AddItem(item); + foundItems = true; + } + } + + ++count; + } + + if (foundItems) + { + draft.SendMailTo(trans, MailReceiver(nullptr, guid), sender); + } + + Field* fields = result->Fetch(); + + stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_CHAR_INVENTORY_BY_ITEM); + stmt->setUInt32(0, fields[0].GetUInt32()); + trans->Append(stmt); + + stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_ITEM_INSTANCE); + stmt->setUInt32(0, fields[0].GetUInt32()); + trans->Append(stmt); + + CharacterDatabase.CommitTransaction(trans); + } + else + { + handler->PSendSysMessage(LANG_CMD_ITEM_REFUND_NOT_FOUND, itemId); + handler->SetSentErrorMessage(true); + return false; + } + } + + return true; } };