diff --git a/data/sql/updates/pending_db_world/rev_1638818214.sql b/data/sql/updates/pending_db_world/rev_1638818214.sql new file mode 100644 index 000000000..91d0b254e --- /dev/null +++ b/data/sql/updates/pending_db_world/rev_1638818214.sql @@ -0,0 +1,30 @@ +INSERT INTO `version_db_world` (`sql_rev`) VALUES ('1638818214'); + +-- Command: Move ".itemmove" to ".item move" +UPDATE `command` SET `name`='item move' WHERE `name`='itemmove'; + +-- Add new command: ".item restore" +DELETE FROM `command` WHERE `name` = 'item restore'; +INSERT INTO `command` (`name`, `security`, `help`) +VALUES ('item restore', '2', 'Syntax: .item restore [#recoveryItemId] [#playername]\r\n\r\nRestore an disposed item for the specified player. Get recoveryId from ".item restore list" command.'); + +-- Add new command: ".item restore list" +DELETE FROM `command` WHERE `name` = 'item restore list'; +INSERT INTO `command` (`name`, `security`, `help`) +VALUES ('item restore list', '2', 'Syntax: .item restore list [#playername]\r\n\r\nSee restorable items for the specified player.'); + +-- Add new string: "LANG_COMMAND_DISABLED" +DELETE FROM `acore_string` WHERE `entry` = 5070; +INSERT INTO `acore_string` (`entry`, `content_default`) VALUES (5070, 'The command is disabled by config'); + +-- Add new string: "LANG_ITEM_RESTORE_LIST" +DELETE FROM `acore_string` WHERE `entry` = 197; +INSERT INTO `acore_string` (`entry`, `content_default`) VALUES (197, 'Recover id: %u | Item: %s (%u) | Count: %u'); + +-- Add new string: "LANG_ITEM_RESTORE_LIST_EMPTY" +DELETE FROM `acore_string` WHERE `entry` = 198; +INSERT INTO `acore_string` (`entry`, `content_default`) VALUES (198, 'Player has no recoverable items'); + +-- Add new string: "LANG_ITEM_RESTORE_MISSING" +DELETE FROM `acore_string` WHERE `entry` = 199; +INSERT INTO `acore_string` (`entry`, `content_default`) VALUES (199, 'Player has no recoverable item with id %u'); diff --git a/src/server/database/Database/Implementation/CharacterDatabase.cpp b/src/server/database/Database/Implementation/CharacterDatabase.cpp index 30875a93c..32b2ff048 100644 --- a/src/server/database/Database/Implementation/CharacterDatabase.cpp +++ b/src/server/database/Database/Implementation/CharacterDatabase.cpp @@ -593,7 +593,10 @@ void CharacterDatabaseConnection::DoPrepareStatements() // Recovery Item PrepareStatement(CHAR_INS_RECOVERY_ITEM, "INSERT INTO recovery_item (Guid, ItemEntry, Count) VALUES (?, ?, ?)", CONNECTION_SYNCH); + PrepareStatement(CHAR_SEL_RECOVERY_ITEM, "SELECT id, itemEntry, Count FROM recovery_item WHERE id = ?", CONNECTION_SYNCH); + PrepareStatement(CHAR_SEL_RECOVERY_ITEM_LIST, "SELECT id, itemEntry, Count FROM recovery_item WHERE Guid = ? ORDER BY id DESC", CONNECTION_SYNCH); 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); // 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 5efb51b86..2d675c667 100644 --- a/src/server/database/Database/Implementation/CharacterDatabase.h +++ b/src/server/database/Database/Implementation/CharacterDatabase.h @@ -509,7 +509,10 @@ enum CharacterDatabaseStatements : uint32 CHAR_UPD_QUEST_TRACK_ABANDON_TIME, CHAR_INS_RECOVERY_ITEM, + CHAR_SEL_RECOVERY_ITEM, + CHAR_SEL_RECOVERY_ITEM_LIST, CHAR_DEL_RECOVERY_ITEM, + CHAR_DEL_RECOVERY_ITEM_BY_RECOVERY_ID, CHAR_INS_RESERVED_PLAYER_NAME, diff --git a/src/server/game/Miscellaneous/Language.h b/src/server/game/Miscellaneous/Language.h index f82f1f513..29612431c 100644 --- a/src/server/game/Miscellaneous/Language.h +++ b/src/server/game/Miscellaneous/Language.h @@ -228,7 +228,10 @@ enum AcoreStrings LANG_CMD_AMBIGUOUS = 194, LANG_CMD_HELP_GENERIC = 195, LANG_CMD_NO_HELP_AVAILABLE = 196, - // Room for more level 1 197-199 not used + + LANG_ITEM_RESTORE_LIST = 197, + LANG_ITEM_RESTORE_LIST_EMPTY = 198, + LANG_ITEM_RESTORE_MISSING = 199, // level 2 chat LANG_NO_SELECTION = 200, @@ -1231,6 +1234,7 @@ enum AcoreStrings LANG_COMMAND_QUEST_NOT_COMPLETE = 5069, // Room for more strings 5070-9999 + LANG_COMMAND_DISABLED = 5070, // Level requirement notifications LANG_SAY_REQ = 6604, diff --git a/src/server/scripts/Commands/cs_item.cpp b/src/server/scripts/Commands/cs_item.cpp new file mode 100644 index 000000000..09e4603e1 --- /dev/null +++ b/src/server/scripts/Commands/cs_item.cpp @@ -0,0 +1,183 @@ +/* + * This file is part of the AzerothCore Project. See AUTHORS file for Copyright information + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU Affero General Public License as published by the + * Free Software Foundation; either version 3 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see . + */ + +/* ScriptData +Name: item_commandscript +%Complete: 0 +Comment: All item related commands +Category: commandscripts +EndScriptData */ + +#include "Chat.h" +#include "DatabaseEnv.h" +#include "DBCStores.h" +#include "ObjectMgr.h" +#include "Player.h" +#include "ScriptMgr.h" +#include "Language.h" + +using namespace Acore::ChatCommands; + +class item_commandscript : public CommandScript +{ +public: + item_commandscript() : CommandScript("item_commandscript") { } + + ChatCommandTable GetCommands() const override + { + static ChatCommandTable HandleItemRestoreCommandTable = + { + { "list", HandleItemRestoreListCommand, SEC_GAMEMASTER, Console::Yes }, + { "", HandleItemRestoreCommand, SEC_GAMEMASTER, Console::Yes }, + }; + static ChatCommandTable itemCommandTable = + { + { "restore", HandleItemRestoreCommandTable }, + { "move", HandleItemMoveCommand, SEC_GAMEMASTER, Console::Yes }, + }; + static ChatCommandTable commandTable = + { + { "item", itemCommandTable } + }; + return commandTable; + } + + static bool HandleItemRestoreCommand(ChatHandler* handler, uint32 restoreId, PlayerIdentifier player) + { + if (!restoreId) + { + return false; + } + + if (!HasItemDeletionConfig()) + { + handler->SendSysMessage(LANG_COMMAND_DISABLED); + handler->SetSentErrorMessage(true); + return false; + } + + // Check existence of item in recovery table + CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_RECOVERY_ITEM); + stmt->setUInt32(0, restoreId); + PreparedQueryResult fields = CharacterDatabase.Query(stmt); + + if (!fields || !(*fields)[1].GetUInt32()) + { + handler->SendSysMessage(LANG_ITEM_RESTORE_MISSING); + handler->SetSentErrorMessage(true); + return false; + } + + // Mail item to player + uint32 itemEntry = (*fields)[1].GetUInt32(); + uint32 itemCount = (*fields)[2].GetUInt32(); + + if (Player* onlinePlayer = player.GetConnectedPlayer()) + { + onlinePlayer->SendItemRetrievalMail({ { itemEntry, itemCount } }); + } + + else + { + MailSender sender(MAIL_CREATURE, 34337 /* The Postmaster */); + MailDraft draft("Recovered Item", "We recovered a lost item in the twisting nether and noted that it was yours.$B$BPlease find said object enclosed."); + + CharacterDatabaseTransaction trans = CharacterDatabase.BeginTransaction(); + + // Save to prevent loss at next mail load. Item deletes on fail + if (Item* item = Item::CreateItem(itemEntry, itemCount, 0)) + { + item->SaveToDB(trans); + draft.AddItem(item); + } + + draft.SendMailTo(trans, MailReceiver(player.GetGUID().GetCounter()), sender); + CharacterDatabase.CommitTransaction(trans); + } + + // Remove from recovery table + CharacterDatabasePreparedStatement* delStmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_RECOVERY_ITEM_BY_RECOVERY_ID); + delStmt->setUInt32(0, (*fields)[0].GetUInt32()); + CharacterDatabase.Execute(delStmt); + + std::string nameLink = handler->playerLink(player.GetName()); + handler->PSendSysMessage(LANG_MAIL_SENT, nameLink.c_str()); + return true; + } + + static bool HandleItemRestoreListCommand(ChatHandler* handler, PlayerIdentifier player) + { + CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_RECOVERY_ITEM_LIST); + stmt->setUInt32(0, player.GetGUID().GetCounter()); + PreparedQueryResult disposedItems = CharacterDatabase.Query(stmt); + + if (!disposedItems) + { + handler->SendSysMessage(LANG_ITEM_RESTORE_LIST_EMPTY); + handler->SetSentErrorMessage(true); + return false; + } + + do { + Field* fields = disposedItems->Fetch(); + uint32 id = fields[0].GetUInt32(); + uint32 itemId = fields[1].GetUInt32(); + uint32 count = fields[2].GetUInt32(); + + std::string itemName = ""; + if (ItemTemplate const* item = sObjectMgr->GetItemTemplate(itemId)) + { + itemName = item->Name1; + } + + handler->PSendSysMessage(LANG_ITEM_RESTORE_LIST, id, itemName, itemId, count); + } while (disposedItems->NextRow()); + + return true; + } + + // TODO - move item to other slot + static bool HandleItemMoveCommand(ChatHandler* handler, uint8 srcSlot, uint8 dstSlot) + { + + if (srcSlot == dstSlot) + return true; + + if (!handler->GetSession()->GetPlayer()->IsValidPos(INVENTORY_SLOT_BAG_0, srcSlot, true)) + return false; + + if (!handler->GetSession()->GetPlayer()->IsValidPos(INVENTORY_SLOT_BAG_0, dstSlot, false)) + return false; + + uint16 src = ((INVENTORY_SLOT_BAG_0 << 8) | srcSlot); + uint16 dst = ((INVENTORY_SLOT_BAG_0 << 8) | dstSlot); + + handler->GetSession()->GetPlayer()->SwapItem(src, dst); + + return true; + } + + static bool HasItemDeletionConfig() + { + return sWorld->getBoolConfig(CONFIG_ITEMDELETE_METHOD) || sWorld->getBoolConfig(CONFIG_ITEMDELETE_VENDOR); + } +}; + +void AddSC_item_commandscript() +{ + new item_commandscript(); +} diff --git a/src/server/scripts/Commands/cs_misc.cpp b/src/server/scripts/Commands/cs_misc.cpp index 83a4e5844..beecd919a 100644 --- a/src/server/scripts/Commands/cs_misc.cpp +++ b/src/server/scripts/Commands/cs_misc.cpp @@ -156,7 +156,6 @@ public: { "dismount", SEC_PLAYER, false, &HandleDismountCommand, "" }, { "guid", SEC_GAMEMASTER, false, &HandleGUIDCommand, "" }, { "help", SEC_PLAYER, true, &HandleHelpCommand, "" }, - { "itemmove", SEC_GAMEMASTER, false, &HandleItemMoveCommand, "" }, { "cooldown", SEC_GAMEMASTER, false, &HandleCooldownCommand, "" }, { "distance", SEC_ADMINISTRATOR, false, &HandleGetDistanceCommand, "" }, { "recall", SEC_GAMEMASTER, false, &HandleRecallCommand, "" }, @@ -1072,39 +1071,6 @@ public: return true; } - // move item to other slot - static bool HandleItemMoveCommand(ChatHandler* handler, char const* args) - { - if (!*args) - return false; - - char const* param1 = strtok((char*)args, " "); - if (!param1) - return false; - - char const* param2 = strtok(nullptr, " "); - if (!param2) - return false; - - uint8 srcSlot = uint8(atoi(param1)); - uint8 dstSlot = uint8(atoi(param2)); - - if (srcSlot == dstSlot) - return true; - - if (!handler->GetSession()->GetPlayer()->IsValidPos(INVENTORY_SLOT_BAG_0, srcSlot, true)) - return false; - - if (!handler->GetSession()->GetPlayer()->IsValidPos(INVENTORY_SLOT_BAG_0, dstSlot, false)) - return false; - - uint16 src = ((INVENTORY_SLOT_BAG_0 << 8) | srcSlot); - uint16 dst = ((INVENTORY_SLOT_BAG_0 << 8) | dstSlot); - - handler->GetSession()->GetPlayer()->SwapItem(src, dst); - - return true; - } static bool HandleCooldownCommand(ChatHandler* handler, char const* args) { diff --git a/src/server/scripts/Commands/cs_script_loader.cpp b/src/server/scripts/Commands/cs_script_loader.cpp index 1098498bb..04cf78ec9 100644 --- a/src/server/scripts/Commands/cs_script_loader.cpp +++ b/src/server/scripts/Commands/cs_script_loader.cpp @@ -54,6 +54,7 @@ void AddSC_titles_commandscript(); void AddSC_wp_commandscript(); void AddSC_player_commandscript(); void AddSC_cache_commandscript(); +void AddSC_item_commandscript(); // The name of this function should match: // void Add${NameOfDirectory}Scripts() @@ -97,4 +98,5 @@ void AddCommandsScripts() AddSC_wp_commandscript(); AddSC_player_commandscript(); AddSC_cache_commandscript(); + AddSC_item_commandscript(); }