aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorNay <dnpd.dd@gmail.com>2012-07-30 16:39:46 +0100
committerNay <dnpd.dd@gmail.com>2012-07-30 16:39:46 +0100
commitc24de2c6d8883a9c4541003c259196c830de2add (patch)
tree677ee37797e9cbbef028c210e06ad802cef95f83
parent195e0369cff80fb89afb267ce479bdd2e9a4e31b (diff)
Core: Implement Void Storage
-rw-r--r--sql/base/characters_database.sql30
-rw-r--r--sql/updates/characters/2012_07_30_00_characters_void_storage_434.sql11
-rwxr-xr-xsrc/server/game/Entities/Player/Player.cpp190
-rwxr-xr-xsrc/server/game/Entities/Player/Player.h41
-rwxr-xr-xsrc/server/game/Entities/Unit/Unit.h2
-rwxr-xr-xsrc/server/game/Globals/ObjectMgr.cpp11
-rwxr-xr-xsrc/server/game/Globals/ObjectMgr.h2
-rw-r--r--src/server/game/Handlers/CharacterHandler.cpp4
-rw-r--r--src/server/game/Handlers/VoidStorageHandler.cpp618
-rwxr-xr-xsrc/server/game/Miscellaneous/SharedDefines.h20
-rw-r--r--src/server/game/Server/Protocol/Opcodes.cpp21
-rwxr-xr-xsrc/server/game/Server/Protocol/Opcodes.h6
-rwxr-xr-xsrc/server/game/Server/WorldSession.h7
-rwxr-xr-xsrc/server/shared/Database/Implementation/CharacterDatabase.cpp4
-rwxr-xr-xsrc/server/shared/Database/Implementation/CharacterDatabase.h4
15 files changed, 956 insertions, 15 deletions
diff --git a/sql/base/characters_database.sql b/sql/base/characters_database.sql
index 8c3913bda73..f42c8f2e6ad 100644
--- a/sql/base/characters_database.sql
+++ b/sql/base/characters_database.sql
@@ -2266,6 +2266,34 @@ LOCK TABLES `reserved_name` WRITE;
UNLOCK TABLES;
--
+-- Table structure for table `void_storage`
+--
+
+DROP TABLE IF EXISTS `void_storage`;
+/*!40101 SET @saved_cs_client = @@character_set_client */;
+/*!40101 SET character_set_client = utf8 */;
+CREATE TABLE `void_storage` (
+ `itemId` bigint(20) unsigned NOT NULL,
+ `playerGuid` int(10) unsigned NOT NULL,
+ `itemEntry` mediumint(8) unsigned NOT NULL,
+ `slot` tinyint(3) unsigned NOT NULL,
+ `creatorGuid` int(10) unsigned NOT NULL DEFAULT '0',
+ PRIMARY KEY (`itemId`),
+ UNIQUE KEY `idx_player_slot` (`playerGuid`,`slot`),
+ KEY `idx_player` (`playerGuid`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8;
+/*!40101 SET character_set_client = @saved_cs_client */;
+
+--
+-- Dumping data for table `void_storage`
+--
+
+LOCK TABLES `void_storage` WRITE;
+/*!40000 ALTER TABLE `void_storage` DISABLE KEYS */;
+/*!40000 ALTER TABLE `void_storage` ENABLE KEYS */;
+UNLOCK TABLES;
+
+--
-- Table structure for table `warden_action`
--
@@ -2322,4 +2350,4 @@ UNLOCK TABLES;
/*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */;
/*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */;
--- Dump completed on 2012-05-30 12:56:18
+-- Dump completed on 2012-07-30 16:34:12
diff --git a/sql/updates/characters/2012_07_30_00_characters_void_storage_434.sql b/sql/updates/characters/2012_07_30_00_characters_void_storage_434.sql
new file mode 100644
index 00000000000..e8a47c5f91c
--- /dev/null
+++ b/sql/updates/characters/2012_07_30_00_characters_void_storage_434.sql
@@ -0,0 +1,11 @@
+DROP TABLE IF EXISTS `void_storage`;
+CREATE TABLE IF NOT EXISTS `void_storage` (
+ `itemId` bigint(20) unsigned NOT NULL,
+ `playerGuid` int(10) unsigned NOT NULL,
+ `itemEntry` mediumint(8) unsigned NOT NULL,
+ `slot` tinyint(3) unsigned NOT NULL,
+ `creatorGuid` int(10) unsigned NOT NULL DEFAULT '0',
+ PRIMARY KEY (`itemId`),
+ UNIQUE KEY `idx_player_slot` (`playerGuid`,`slot`),
+ KEY `idx_player` (`playerGuid`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8;
diff --git a/src/server/game/Entities/Player/Player.cpp b/src/server/game/Entities/Player/Player.cpp
index 56c3915bd65..6cdea51bfb3 100755
--- a/src/server/game/Entities/Player/Player.cpp
+++ b/src/server/game/Entities/Player/Player.cpp
@@ -714,7 +714,7 @@ Player::Player(WorldSession* session): Unit(true), m_achievementMgr(this), m_rep
m_DailyQuestChanged = false;
m_lastDailyQuestTime = 0;
- for (uint8 i=0; i<MAX_TIMERS; i++)
+ for (uint8 i=0; i < MAX_TIMERS; i++)
m_MirrorTimer[i] = DISABLED_MIRROR_TIMER;
m_MirrorTimerFlags = UNDERWATER_NONE;
@@ -730,7 +730,7 @@ Player::Player(WorldSession* session): Unit(true), m_achievementMgr(this), m_rep
for (uint8 j = 0; j < PLAYER_MAX_BATTLEGROUND_QUEUES; ++j)
{
- m_bgBattlegroundQueueID[j].bgQueueTypeId = BATTLEGROUND_QUEUE_NONE;
+ m_bgBattlegroundQueueID[j].bgQueueTypeId = BATTLEGROUND_QUEUE_NONE;
m_bgBattlegroundQueueID[j].invitedToInstance = 0;
}
@@ -842,6 +842,8 @@ Player::Player(WorldSession* session): Unit(true), m_achievementMgr(this), m_rep
m_SeasonalQuestChanged = false;
SetPendingBind(0, 0);
+
+ memset(_voidStorageItems, 0, VOID_STORAGE_MAX_SLOT * sizeof(VoidStorageItem*));
}
Player::~Player()
@@ -873,6 +875,9 @@ Player::~Player()
delete m_declinedname;
delete m_runes;
+ for (uint8 i = 0; i < VOID_STORAGE_MAX_SLOT; ++i)
+ delete _voidStorageItems[i];
+
sWorld->DecreasePlayerCount();
}
@@ -17195,6 +17200,9 @@ bool Player::LoadFromDB(uint32 guid, SQLQueryHolder *holder)
_LoadInventory(holder->GetPreparedResult(PLAYER_LOGIN_QUERY_LOADINVENTORY), time_diff);
+ if (IsVoidStorageUnlocked())
+ _LoadVoidStorage(holder->GetPreparedResult(PLAYER_LOGIN_QUERY_LOADVOIDSTORAGE));
+
// update items with duration and realtime
UpdateItemDuration(time_diff, true);
@@ -17654,6 +17662,50 @@ void Player::_LoadInventory(PreparedQueryResult result, uint32 timeDiff)
_ApplyAllItemMods();
}
+void Player::_LoadVoidStorage(PreparedQueryResult result)
+{
+ if (!result)
+ return;
+
+ do
+ {
+ // SELECT itemid, itemEntry, slot, creatorGuid FROM void_storage WHERE playerGuid = ?
+ Field* fields = result->Fetch();
+
+ uint64 itemId = fields[0].GetUInt64();
+ uint32 itemEntry = fields[1].GetUInt32();
+ uint8 slot = fields[2].GetUInt8();
+ uint32 creatorGuid = fields[3].GetUInt32();
+
+ if (!itemId)
+ {
+ sLog->outError("Player::_LoadVoidStorage - Player (GUID: %u, name: %s) has an item with an invalid id (item id: %u, entry: %u).", GetGUIDLow(), GetName(), itemId, itemEntry);
+ continue;
+ }
+
+ if (!sObjectMgr->GetItemTemplate(itemEntry))
+ {
+ sLog->outError("Player::_LoadVoidStorage - Player (GUID: %u, name: %s) has an item with an invalid entry (item id: %u, entry: %u).", GetGUIDLow(), GetName(), itemId, itemEntry);
+ continue;
+ }
+
+ if (slot < 0 || slot > VOID_STORAGE_MAX_SLOT)
+ {
+ sLog->outError("Player::_LoadVoidStorage - Player (GUID: %u, name: %s) has an item with an invalid slot (item id: %u, entry: %u, slot: %u).", GetGUIDLow(), GetName(), itemId, itemEntry, slot);
+ continue;
+ }
+
+ if (!sObjectMgr->GetPlayerByLowGUID(creatorGuid))
+ {
+ sLog->outError("Player::_LoadVoidStorage - Player (GUID: %u, name: %s) has an item with an invalid creator guid, set to 0 (item id: %u, entry: %u, creatorGuid: %u).", GetGUIDLow(), GetName(), itemId, itemEntry, creatorGuid);
+ creatorGuid = 0;
+ }
+
+ _voidStorageItems[slot] = new VoidStorageItem(itemId, itemEntry, creatorGuid);
+ }
+ while (result->NextRow());
+}
+
Item* Player::_LoadItem(SQLTransaction& trans, uint32 zoneId, uint32 timeDiff, Field* fields)
{
PreparedStatement* stmt = NULL;
@@ -18882,6 +18934,7 @@ void Player::SaveToDB(bool create /*=false*/)
_SaveBGData(trans);
_SaveInventory(trans);
+ _SaveVoidStorage(trans);
_SaveQuestStatus(trans);
_SaveDailyQuestStatus(trans);
_SaveWeeklyQuestStatus(trans);
@@ -19121,7 +19174,7 @@ void Player::_SaveInventory(SQLTransaction& trans)
// save all changes to the item...
if (item->GetState() != ITEM_NEW) // only for existing items, no dupes
item->SaveToDB(trans);
- // ...but do not save position in invntory
+ // ...but do not save position in inventory
continue;
}
}
@@ -19151,6 +19204,35 @@ void Player::_SaveInventory(SQLTransaction& trans)
m_itemUpdateQueue.clear();
}
+void Player::_SaveVoidStorage(SQLTransaction& trans)
+{
+ PreparedStatement* stmt = NULL;
+ uint32 lowGuid = GetGUIDLow();
+
+ for (uint8 i = 0; i < VOID_STORAGE_MAX_SLOT; ++i)
+ {
+ if (!_voidStorageItems[i]) // unused item
+ {
+ // DELETE FROM void_Storage WHERE slot = ? AND playerGuid = ?
+ stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_VOID_STORAGE_ITEM_BY_SLOT);
+ stmt->setUInt8(0, i);
+ stmt->setUInt32(1, lowGuid);
+ }
+ else
+ {
+ // REPLACE INTO character_inventory (itemId, playerGuid, itemEntry, slot, creatorGuid) VALUES (?, ?, ?, ?, ?)
+ stmt = CharacterDatabase.GetPreparedStatement(CHAR_REP_VOID_STORAGE_ITEM);
+ stmt->setUInt64(0, _voidStorageItems[i]->ItemId);
+ stmt->setUInt32(1, lowGuid);
+ stmt->setUInt32(2, _voidStorageItems[i]->ItemEntry);
+ stmt->setUInt8(3, i);
+ stmt->setUInt32(4, _voidStorageItems[i]->CreatorGuid);
+ }
+
+ trans->Append(stmt);
+ }
+}
+
void Player::_SaveMail(SQLTransaction& trans)
{
if (!m_mailsLoaded)
@@ -25602,6 +25684,108 @@ bool Player::IsInWhisperWhiteList(uint64 guid)
return false;
}
+uint8 Player::GetNextVoidStorageFreeSlot() const
+{
+ for (uint8 i = 0; i < VOID_STORAGE_MAX_SLOT; ++i)
+ if (!_voidStorageItems[i]) // unused item
+ return i;
+
+ return -1;
+}
+
+uint8 Player::GetNumOfVoidStorageFreeSlots() const
+{
+ uint8 count = 0;
+
+ for (uint8 i = 0; i < VOID_STORAGE_MAX_SLOT; ++i)
+ if (!_voidStorageItems[i])
+ count++;
+
+ return count;
+}
+
+uint8 Player::AddVoidStorageItem(const VoidStorageItem& item)
+{
+ uint8 slot = GetNextVoidStorageFreeSlot();
+
+ if (slot < 0 || slot > VOID_STORAGE_MAX_SLOT)
+ {
+ GetSession()->SendVoidStorageTransferResult(VOID_TRANSFER_ERROR_FULL);
+ return -1;
+ }
+
+ _voidStorageItems[slot] = new VoidStorageItem(item.ItemId, item.ItemEntry, item.CreatorGuid);
+ return slot;
+}
+
+void Player::AddVoidStorageItemAtSlot(uint8 slot, const VoidStorageItem& item)
+{
+ if (slot < 0 || slot > VOID_STORAGE_MAX_SLOT)
+ {
+ GetSession()->SendVoidStorageTransferResult(VOID_TRANSFER_ERROR_FULL);
+ return;
+ }
+
+ if (_voidStorageItems[slot])
+ {
+ sLog->outError("Player::AddVoidStorageItemAtSlot - Player (GUID: %u, name: %s) tried to add an item to an used slot (item id: %u, entry: %u, slot: %u).", GetGUIDLow(), GetName(), _voidStorageItems[slot]->ItemId, _voidStorageItems[slot]->ItemEntry, slot);
+ GetSession()->SendVoidStorageTransferResult(VOID_TRANSFER_ERROR_INTERNAL_ERROR_1);
+ return;
+ }
+
+ _voidStorageItems[slot] = new VoidStorageItem(item.ItemId, item.ItemId, item.CreatorGuid);
+}
+
+void Player::DeleteVoidStorageItem(uint8 slot)
+{
+ if (slot < 0 || slot > VOID_STORAGE_MAX_SLOT)
+ {
+ GetSession()->SendVoidStorageTransferResult(VOID_TRANSFER_ERROR_INTERNAL_ERROR_1);
+ return;
+ }
+
+ delete _voidStorageItems[slot];
+ _voidStorageItems[slot] = NULL;
+}
+
+bool Player::SwapVoidStorageItem(uint8 oldSlot, uint8 newSlot)
+{
+ if (oldSlot < 0 || oldSlot > VOID_STORAGE_MAX_SLOT || newSlot < 0 || newSlot > VOID_STORAGE_MAX_SLOT || oldSlot == newSlot)
+ {
+
+ return false;
+ }
+
+ // verify
+ std::swap(_voidStorageItems[newSlot], _voidStorageItems[oldSlot]);
+ return true;
+}
+
+VoidStorageItem* Player::GetVoidStorageItem(uint8 slot) const
+{
+ if (slot < 0 || slot > VOID_STORAGE_MAX_SLOT)
+ {
+ GetSession()->SendVoidStorageTransferResult(VOID_TRANSFER_ERROR_INTERNAL_ERROR_1);
+ return NULL;
+ }
+
+ return _voidStorageItems[slot];
+}
+
+VoidStorageItem* Player::GetVoidStorageItem(uint64 id, uint8& slot) const
+{
+ for (uint8 i = 0; i < VOID_STORAGE_MAX_SLOT; ++i)
+ {
+ if (_voidStorageItems[i] && _voidStorageItems[i]->ItemId == id)
+ {
+ slot = i;
+ return _voidStorageItems[i];
+ }
+ }
+
+ return NULL;
+}
+
bool Player::SetHover(bool enable)
{
if (!Unit::SetHover(enable))
diff --git a/src/server/game/Entities/Player/Player.h b/src/server/game/Entities/Player/Player.h
index aba570c1d37..6b8686a7c0e 100755
--- a/src/server/game/Entities/Player/Player.h
+++ b/src/server/game/Entities/Player/Player.h
@@ -405,7 +405,7 @@ enum PlayerFlags
PLAYER_FLAGS_UNK26 = 0x04000000,
PLAYER_FLAGS_UNK27 = 0x08000000,
PLAYER_FLAGS_UNK28 = 0x10000000,
- PLAYER_FLAGS_UNK29 = 0x20000000,
+ PLAYER_FLAGS_VOID_UNLOCKED = 0x20000000, // void storage
PLAYER_FLAGS_UNK30 = 0x40000000,
PLAYER_FLAGS_UNK31 = 0x80000000,
};
@@ -816,6 +816,7 @@ enum PlayerLoginQueryIndex
PLAYER_LOGIN_QUERY_LOADQUESTSTATUSREW = 29,
PLAYER_LOGIN_QUERY_LOADINSTANCELOCKTIMES = 30,
PLAYER_LOGIN_QUERY_LOADSEASONALQUESTSTATUS = 31,
+ PLAYER_LOGIN_QUERY_LOADVOIDSTORAGE = 32,
MAX_PLAYER_LOGIN_QUERY,
};
@@ -979,6 +980,27 @@ struct BGData
bool HasTaxiPath() const { return taxiPath[0] && taxiPath[1]; }
};
+struct VoidStorageItem
+{
+ VoidStorageItem()
+ {
+ ItemId = 0;
+ ItemEntry = 0;
+ CreatorGuid = 0;
+ }
+
+ VoidStorageItem(uint64 id, uint32 entry, uint32 creator)
+ {
+ ItemId = id;
+ ItemEntry = entry;
+ CreatorGuid = creator;
+ }
+
+ uint64 ItemId;
+ uint32 ItemEntry;
+ uint32 CreatorGuid;
+};
+
class TradeData
{
public: // constructors
@@ -2589,6 +2611,19 @@ class Player : public Unit, public GridObject<Player>
}
}
+ // Void Storage
+ bool IsVoidStorageUnlocked() const { return HasFlag(PLAYER_FLAGS, PLAYER_FLAGS_VOID_UNLOCKED); }
+ void UnlockVoidStorage() { SetFlag(PLAYER_FLAGS, PLAYER_FLAGS_VOID_UNLOCKED); }
+ void LockVoidStorage() { RemoveFlag(PLAYER_FLAGS, PLAYER_FLAGS_VOID_UNLOCKED); }
+ uint8 GetNextVoidStorageFreeSlot() const;
+ uint8 GetNumOfVoidStorageFreeSlots() const;
+ uint8 AddVoidStorageItem(const VoidStorageItem& item);
+ void AddVoidStorageItemAtSlot(uint8 slot, const VoidStorageItem& item);
+ void DeleteVoidStorageItem(uint8 slot);
+ bool SwapVoidStorageItem(uint8 oldSlot, uint8 newSlot);
+ VoidStorageItem* GetVoidStorageItem(uint8 slot) const;
+ VoidStorageItem* GetVoidStorageItem(uint64 id, uint8& slot) const;
+
protected:
// Gamemaster whisper whitelist
WhisperListContainer WhisperList;
@@ -2638,6 +2673,7 @@ class Player : public Unit, public GridObject<Player>
void _LoadGlyphAuras();
void _LoadBoundInstances(PreparedQueryResult result);
void _LoadInventory(PreparedQueryResult result, uint32 timeDiff);
+ void _LoadVoidStorage(PreparedQueryResult result);
void _LoadMailInit(PreparedQueryResult resultUnread, PreparedQueryResult resultDelivery);
void _LoadMail();
void _LoadMailedItems(Mail* mail);
@@ -2667,6 +2703,7 @@ class Player : public Unit, public GridObject<Player>
void _SaveActions(SQLTransaction& trans);
void _SaveAuras(SQLTransaction& trans);
void _SaveInventory(SQLTransaction& trans);
+ void _SaveVoidStorage(SQLTransaction& trans);
void _SaveMail(SQLTransaction& trans);
void _SaveQuestStatus(SQLTransaction& trans);
void _SaveDailyQuestStatus(SQLTransaction& trans);
@@ -2714,6 +2751,8 @@ class Player : public Unit, public GridObject<Player>
PlayerCurrenciesMap m_currencies;
uint32 _GetCurrencyWeekCap(const CurrencyTypesEntry* currency) const;
+ VoidStorageItem* _voidStorageItems[VOID_STORAGE_MAX_SLOT];
+
std::vector<Item*> m_itemUpdateQueue;
bool m_itemUpdateQueueBlocked;
diff --git a/src/server/game/Entities/Unit/Unit.h b/src/server/game/Entities/Unit/Unit.h
index 7984c91bbd3..a010a119017 100755
--- a/src/server/game/Entities/Unit/Unit.h
+++ b/src/server/game/Entities/Unit/Unit.h
@@ -661,6 +661,8 @@ enum NPCFlags
UNIT_NPC_FLAG_GUILD_BANKER = 0x00800000, // cause client to send 997 opcode
UNIT_NPC_FLAG_SPELLCLICK = 0x01000000, // cause client to send 1015 opcode (spell click)
UNIT_NPC_FLAG_PLAYER_VEHICLE = 0x02000000, // players with mounts that have vehicle data should have it set
+ UNIT_NPC_FLAG_REFORGER = 0x08000000, // reforging
+ UNIT_NPC_FLAG_VAULTKEEPER = 0x20000000, // void storage
};
enum MovementFlags
diff --git a/src/server/game/Globals/ObjectMgr.cpp b/src/server/game/Globals/ObjectMgr.cpp
index 79e29ff2761..258e5d3ec25 100755
--- a/src/server/game/Globals/ObjectMgr.cpp
+++ b/src/server/game/Globals/ObjectMgr.cpp
@@ -233,7 +233,7 @@ bool SpellClickInfo::IsFitToRequirements(Unit const* clicker, Unit const* clicke
ObjectMgr::ObjectMgr(): _auctionId(1), _equipmentSetGuid(1),
_itemTextId(1), _mailId(1), _hiPetNumber(1), _hiCharGuid(1),
_hiCreatureGuid(1), _hiPetGuid(1), _hiVehicleGuid(1), _hiItemGuid(1),
- _hiGoGuid(1), _hiDoGuid(1), _hiCorpseGuid(1), _hiMoTransGuid(1)
+ _hiGoGuid(1), _hiDoGuid(1), _hiCorpseGuid(1), _hiMoTransGuid(1), _voidItemId(1)
{}
ObjectMgr::~ObjectMgr()
@@ -6315,6 +6315,10 @@ void ObjectMgr::SetHighestGuids()
result = CharacterDatabase.Query("SELECT MAX(guid) FROM groups");
if (result)
sGroupMgr->SetGroupDbStoreSize((*result)[0].GetUInt32()+1);
+
+ result = CharacterDatabase.Query("SELECT MAX(itemId) from void_storage");
+ if (result)
+ _voidItemId = (*result)[0].GetUInt64()+1;
}
uint32 ObjectMgr::GenerateAuctionID()
@@ -6797,6 +6801,11 @@ uint32 ObjectMgr::GeneratePetNumber()
return ++_hiPetNumber;
}
+uint64 ObjectMgr::GenerateVoidStorageItemId()
+{
+ return ++_voidItemId;
+}
+
void ObjectMgr::LoadCorpses()
{
// 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
diff --git a/src/server/game/Globals/ObjectMgr.h b/src/server/game/Globals/ObjectMgr.h
index b1ee342a0e7..a13108bd7f3 100755
--- a/src/server/game/Globals/ObjectMgr.h
+++ b/src/server/game/Globals/ObjectMgr.h
@@ -941,6 +941,7 @@ class ObjectMgr
uint64 GenerateEquipmentSetGuid();
uint32 GenerateMailID();
uint32 GeneratePetNumber();
+ uint64 GenerateVoidStorageItemId();
typedef std::multimap<int32, uint32> ExclusiveQuestGroups;
ExclusiveQuestGroups mExclusiveQuestGroups;
@@ -1176,6 +1177,7 @@ class ObjectMgr
uint32 _itemTextId;
uint32 _mailId;
uint32 _hiPetNumber;
+ uint64 _voidItemId;
// first free low guid for selected guid type
uint32 _hiCharGuid;
diff --git a/src/server/game/Handlers/CharacterHandler.cpp b/src/server/game/Handlers/CharacterHandler.cpp
index 01668314c63..d75d25b00e2 100644
--- a/src/server/game/Handlers/CharacterHandler.cpp
+++ b/src/server/game/Handlers/CharacterHandler.cpp
@@ -110,6 +110,10 @@ bool LoginQueryHolder::Initialize()
stmt->setUInt32(0, lowGuid);
res &= SetPreparedQuery(PLAYER_LOGIN_QUERY_LOADINVENTORY, stmt);
+ stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_VOID_STORAGE);
+ stmt->setUInt32(0, lowGuid);
+ res &= SetPreparedQuery(PLAYER_LOGIN_QUERY_LOADVOIDSTORAGE, stmt);
+
stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_CHARACTER_ACTIONS);
stmt->setUInt32(0, lowGuid);
res &= SetPreparedQuery(PLAYER_LOGIN_QUERY_LOADACTIONS, stmt);
diff --git a/src/server/game/Handlers/VoidStorageHandler.cpp b/src/server/game/Handlers/VoidStorageHandler.cpp
new file mode 100644
index 00000000000..0a0cc32e535
--- /dev/null
+++ b/src/server/game/Handlers/VoidStorageHandler.cpp
@@ -0,0 +1,618 @@
+/*
+ * Copyright (C) 2008-2012 TrinityCore <http://www.trinitycore.org/>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 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 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 <http://www.gnu.org/licenses/>.
+ */
+
+#include "Common.h"
+#include "WorldPacket.h"
+#include "WorldSession.h"
+#include "World.h"
+#include "ObjectAccessor.h"
+#include "Log.h"
+#include "Opcodes.h"
+#include "Player.h"
+#include <list>
+#include <vector>
+#include <utility>
+
+void WorldSession::SendVoidStorageTransferResult(VoidTransferError result)
+{
+ WorldPacket data(SMSG_VOID_TRANSFER_RESULT, 4);
+ data << uint32(result);
+ SendPacket(&data);
+}
+
+void WorldSession::HandleVoidStorageUnlock(WorldPacket& recvData)
+{
+ sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: Received CMSG_VOID_STORAGE_UNLOCK");
+ Player* player = GetPlayer();
+
+ ObjectGuid npcGuid;
+ npcGuid[4] = recvData.ReadBit();
+ npcGuid[5] = recvData.ReadBit();
+ npcGuid[3] = recvData.ReadBit();
+ npcGuid[0] = recvData.ReadBit();
+ npcGuid[2] = recvData.ReadBit();
+ npcGuid[1] = recvData.ReadBit();
+ npcGuid[7] = recvData.ReadBit();
+ npcGuid[6] = recvData.ReadBit();
+
+ recvData.ReadByteSeq(npcGuid[7]);
+ recvData.ReadByteSeq(npcGuid[1]);
+ recvData.ReadByteSeq(npcGuid[2]);
+ recvData.ReadByteSeq(npcGuid[3]);
+ recvData.ReadByteSeq(npcGuid[5]);
+ recvData.ReadByteSeq(npcGuid[0]);
+ recvData.ReadByteSeq(npcGuid[6]);
+ recvData.ReadByteSeq(npcGuid[4]);
+
+ Creature* unit = player->GetNPCIfCanInteractWith(npcGuid, UNIT_NPC_FLAG_VAULTKEEPER);
+ if (!unit)
+ {
+ sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: HandleVoidStorageUnlock - Unit (GUID: %u) not found or player can't interact with it.", GUID_LOPART(npcGuid));
+ return;
+ }
+
+ if (player->IsVoidStorageUnlocked())
+ {
+ sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: HandleVoidStorageUnlock - Player (GUID: %u, name: %s) tried to unlock void storage a 2nd time.", player->GetGUIDLow(), player->GetName());
+ return;
+ }
+
+ player->ModifyMoney(-int64(VOID_STORAGE_UNLOCK));
+ player->UnlockVoidStorage();
+}
+
+void WorldSession::HandleVoidStorageQuery(WorldPacket& recvData)
+{
+ sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: Received CMSG_VOID_STORAGE_QUERY");
+ Player* player = GetPlayer();
+
+ ObjectGuid npcGuid;
+ npcGuid[4] = recvData.ReadBit();
+ npcGuid[0] = recvData.ReadBit();
+ npcGuid[5] = recvData.ReadBit();
+ npcGuid[7] = recvData.ReadBit();
+ npcGuid[6] = recvData.ReadBit();
+ npcGuid[3] = recvData.ReadBit();
+ npcGuid[1] = recvData.ReadBit();
+ npcGuid[2] = recvData.ReadBit();
+
+ recvData.ReadByteSeq(npcGuid[5]);
+ recvData.ReadByteSeq(npcGuid[6]);
+ recvData.ReadByteSeq(npcGuid[3]);
+ recvData.ReadByteSeq(npcGuid[7]);
+ recvData.ReadByteSeq(npcGuid[1]);
+ recvData.ReadByteSeq(npcGuid[0]);
+ recvData.ReadByteSeq(npcGuid[4]);
+ recvData.ReadByteSeq(npcGuid[2]);
+
+ Creature* unit = player->GetNPCIfCanInteractWith(npcGuid, UNIT_NPC_FLAG_VAULTKEEPER);
+ if (!unit)
+ {
+ sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: HandleVoidStorageQuery - Unit (GUID: %u) not found or player can't interact with it.", GUID_LOPART(npcGuid));
+ return;
+ }
+
+ if (!player->IsVoidStorageUnlocked())
+ {
+ sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: HandleVoidStorageQuery - Player (GUID: %u, name: %s) queried void storage without unlocking it.", player->GetGUIDLow(), player->GetName());
+ return;
+ }
+
+ uint8 count = 0;
+ for (uint8 i = 0; i < VOID_STORAGE_MAX_SLOT; ++i)
+ if (player->GetVoidStorageItem(i))
+ ++count;
+
+ WorldPacket data(SMSG_VOID_STORAGE_CONTENTS, 2 * count + (14 + 4 + 4 + 4 + 4) * count);
+
+ data.WriteBits(count, 8);
+
+ ByteBuffer itemData((14 + 4 + 4 + 4 + 4) * count);
+
+ for (uint8 i = 0; i < VOID_STORAGE_MAX_SLOT; ++i)
+ {
+ VoidStorageItem* item = player->GetVoidStorageItem(i);
+ if (!item)
+ continue;
+
+ ObjectGuid itemId = item->ItemId;
+ ObjectGuid creatorGuid = item->CreatorGuid;
+
+ data.WriteBit(creatorGuid[3]);
+ data.WriteBit(itemId[5]);
+ data.WriteBit(creatorGuid[6]);
+ data.WriteBit(creatorGuid[1]);
+ data.WriteBit(itemId[1]);
+ data.WriteBit(itemId[3]);
+ data.WriteBit(itemId[6]);
+ data.WriteBit(creatorGuid[5]);
+ data.WriteBit(creatorGuid[2]);
+ data.WriteBit(itemId[2]);
+ data.WriteBit(creatorGuid[4]);
+ data.WriteBit(itemId[0]);
+ data.WriteBit(itemId[4]);
+ data.WriteBit(itemId[7]);
+ data.WriteBit(creatorGuid[0]);
+ data.WriteBit(creatorGuid[7]);
+
+ itemData.WriteByteSeq(creatorGuid[3]);
+
+ itemData << int32(0); // unk, SuffixFactor? large ints, both positive and negative appear here
+
+ itemData.WriteByteSeq(creatorGuid[4]);
+
+ itemData << uint32(i);
+
+ itemData.WriteByteSeq(itemId[0]);
+ itemData.WriteByteSeq(itemId[6]);
+ itemData.WriteByteSeq(creatorGuid[0]);
+
+ itemData << int32(0); // unk, usually 0, not always
+
+ itemData.WriteByteSeq(itemId[4]);
+ itemData.WriteByteSeq(itemId[5]);
+ itemData.WriteByteSeq(itemId[2]);
+ itemData.WriteByteSeq(creatorGuid[2]);
+ itemData.WriteByteSeq(creatorGuid[6]);
+ itemData.WriteByteSeq(itemId[1]);
+ itemData.WriteByteSeq(itemId[3]);
+ itemData.WriteByteSeq(creatorGuid[5]);
+ itemData.WriteByteSeq(creatorGuid[7]);
+
+ itemData << uint32(item->ItemEntry);
+
+ itemData.WriteByteSeq(itemId[7]);
+ }
+
+ data.FlushBits();
+ data.append(itemData);
+
+ SendPacket(&data);
+}
+
+void WorldSession::HandleVoidStorageTransfer(WorldPacket& recvData)
+{
+ sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: Received CMSG_VOID_STORAGE_TRANSFER");
+ Player* player = GetPlayer();
+
+ // Read everything
+
+ ObjectGuid npcGuid;
+ npcGuid[1] = recvData.ReadBit();
+
+ uint32 countDeposit = recvData.ReadBits(26);
+
+ if (countDeposit > 9)
+ {
+ sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: HandleVoidStorageTransfer - Player (GUID: %u, name: %s) wants to deposit more than 9 items (%u).", player->GetGUIDLow(), player->GetName(), countDeposit);
+ return;
+ }
+
+ std::vector<ObjectGuid> itemGuids(countDeposit);
+ for (uint32 i = 0; i < countDeposit; ++i)
+ {
+ itemGuids[i][4] = recvData.ReadBit();
+ itemGuids[i][6] = recvData.ReadBit();
+ itemGuids[i][7] = recvData.ReadBit();
+ itemGuids[i][0] = recvData.ReadBit();
+ itemGuids[i][1] = recvData.ReadBit();
+ itemGuids[i][5] = recvData.ReadBit();
+ itemGuids[i][3] = recvData.ReadBit();
+ itemGuids[i][2] = recvData.ReadBit();
+ }
+
+ npcGuid[2] = recvData.ReadBit();
+ npcGuid[0] = recvData.ReadBit();
+ npcGuid[3] = recvData.ReadBit();
+ npcGuid[5] = recvData.ReadBit();
+ npcGuid[6] = recvData.ReadBit();
+ npcGuid[4] = recvData.ReadBit();
+
+ uint32 countWithdraw = recvData.ReadBits(26);
+
+ if (countWithdraw > 9)
+ {
+ sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: HandleVoidStorageTransfer - Player (GUID: %u, name: %s) wants to withdraw more than 9 items (%u).", player->GetGUIDLow(), player->GetName(), countWithdraw);
+ return;
+ }
+
+ std::vector<ObjectGuid> itemIds(countWithdraw);
+ for (uint32 i = 0; i < countWithdraw; ++i)
+ {
+ itemIds[i][4] = recvData.ReadBit();
+ itemIds[i][7] = recvData.ReadBit();
+ itemIds[i][1] = recvData.ReadBit();
+ itemIds[i][0] = recvData.ReadBit();
+ itemIds[i][2] = recvData.ReadBit();
+ itemIds[i][3] = recvData.ReadBit();
+ itemIds[i][5] = recvData.ReadBit();
+ itemIds[i][6] = recvData.ReadBit();
+ }
+
+ npcGuid[7] = recvData.ReadBit();
+
+ recvData.FlushBits();
+
+ for (uint32 i = 0; i < countDeposit; ++i)
+ {
+ recvData.ReadByteSeq(itemGuids[i][6]);
+ recvData.ReadByteSeq(itemGuids[i][1]);
+ recvData.ReadByteSeq(itemGuids[i][0]);
+ recvData.ReadByteSeq(itemGuids[i][2]);
+ recvData.ReadByteSeq(itemGuids[i][4]);
+ recvData.ReadByteSeq(itemGuids[i][5]);
+ recvData.ReadByteSeq(itemGuids[i][3]);
+ recvData.ReadByteSeq(itemGuids[i][7]);
+ }
+
+ recvData.ReadByteSeq(npcGuid[5]);
+ recvData.ReadByteSeq(npcGuid[6]);
+
+ for (uint32 i = 0; i < countWithdraw; ++i)
+ {
+ recvData.ReadByteSeq(itemIds[i][3]);
+ recvData.ReadByteSeq(itemIds[i][1]);
+ recvData.ReadByteSeq(itemIds[i][0]);
+ recvData.ReadByteSeq(itemIds[i][6]);
+ recvData.ReadByteSeq(itemIds[i][2]);
+ recvData.ReadByteSeq(itemIds[i][7]);
+ recvData.ReadByteSeq(itemIds[i][5]);
+ recvData.ReadByteSeq(itemIds[i][4]);
+ }
+
+ recvData.ReadByteSeq(npcGuid[1]);
+ recvData.ReadByteSeq(npcGuid[4]);
+ recvData.ReadByteSeq(npcGuid[7]);
+ recvData.ReadByteSeq(npcGuid[3]);
+ recvData.ReadByteSeq(npcGuid[2]);
+ recvData.ReadByteSeq(npcGuid[0]);
+
+ Creature* unit = player->GetNPCIfCanInteractWith(npcGuid, UNIT_NPC_FLAG_VAULTKEEPER);
+ if (!unit)
+ {
+ sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: HandleVoidStorageTransfer - Unit (GUID: %u) not found or player can't interact with it.", GUID_LOPART(npcGuid));
+ return;
+ }
+
+ if (!player->IsVoidStorageUnlocked())
+ {
+ sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: HandleVoidStorageTransfer - Player (GUID: %u, name: %s) queried void storage without unlocking it.", player->GetGUIDLow(), player->GetName());
+ return;
+ }
+
+ if (itemGuids.size() > player->GetNumOfVoidStorageFreeSlots())
+ {
+ SendVoidStorageTransferResult(VOID_TRANSFER_ERROR_FULL);
+ return;
+ }
+
+ uint32 freeBagSlots = 0;
+ if (itemIds.size() != 0)
+ {
+ // make this a Player function
+ for (uint8 i = INVENTORY_SLOT_BAG_START; i < INVENTORY_SLOT_BAG_END; i++)
+ if (Bag* bag = player->GetBagByPos(i))
+ freeBagSlots += bag->GetFreeSlots();
+ for (uint8 i = INVENTORY_SLOT_ITEM_START; i < INVENTORY_SLOT_ITEM_END; i++)
+ if (!player->GetItemByPos(INVENTORY_SLOT_BAG_0, i))
+ ++freeBagSlots;
+ }
+
+ if (itemIds.size() > freeBagSlots)
+ {
+ SendVoidStorageTransferResult(VOID_TRANSFER_ERROR_INVENTORY_FULL);
+ return;
+ }
+
+ if (!player->HasEnoughMoney(uint64(itemGuids.size() * VOID_STORAGE_STORE_ITEM)))
+ {
+ SendVoidStorageTransferResult(VOID_TRANSFER_ERROR_NOT_ENOUGH_MONEY);
+ return;
+ }
+
+ std::pair<VoidStorageItem, uint8> depositItems[VOID_STORAGE_MAX_DEPOSIT];
+ uint8 depositCount = 0;
+ for (std::vector<ObjectGuid>::iterator itr = itemGuids.begin(); itr != itemGuids.end(); ++itr)
+ {
+ Item* item = player->GetItemByGuid(*itr);
+ if (!item)
+ {
+ sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: HandleVoidStorageTransfer - Player (GUID: %u, name: %s) wants to deposit an invalid item (item guid: %u).", player->GetGUIDLow(), player->GetName(), *itr);
+ continue;
+ }
+
+ VoidStorageItem itemVS(sObjectMgr->GenerateVoidStorageItemId(), item->GetEntry(), item->GetUInt64Value(ITEM_FIELD_CREATOR));
+
+ uint8 slot = player->AddVoidStorageItem(itemVS);
+
+ depositItems[depositCount++] = std::make_pair(itemVS, slot);
+
+ player->DestroyItem(item->GetBagSlot(), item->GetSlot(), true);
+ }
+
+ int64 cost = depositCount * VOID_STORAGE_STORE_ITEM;
+
+ player->ModifyMoney(-cost);
+
+ VoidStorageItem withdrawItems[VOID_STORAGE_MAX_WITHDRAW];
+ uint8 withdrawCount = 0;
+ for (std::vector<ObjectGuid>::iterator itr = itemIds.begin(); itr != itemIds.end(); ++itr)
+ {
+ uint8 slot;
+ VoidStorageItem* itemVS = player->GetVoidStorageItem(*itr, slot);
+ if (!itemVS)
+ {
+ sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: HandleVoidStorageTransfer - Player (GUID: %u, name: %s) tried to withdraw an invalid item (id: %u)", player->GetGUIDLow(), player->GetName(), *itr);
+ continue;
+ }
+
+ ItemPosCountVec dest;
+ InventoryResult msg = player->CanStoreNewItem(NULL_BAG, NULL_SLOT, dest, itemVS->ItemEntry, 1);
+ if (msg != EQUIP_ERR_OK)
+ {
+ SendVoidStorageTransferResult(VOID_TRANSFER_ERROR_INVENTORY_FULL);
+ sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: HandleVoidStorageTransfer - Player (GUID: %u, name: %s) couldn't withdraw item id %u because inventory was full.", player->GetGUIDLow(), player->GetName(), *itr);
+ return;
+ }
+
+ Item* item = player->StoreNewItem(dest, itemVS->ItemEntry, true);
+ item->SetUInt64Value(ITEM_FIELD_CREATOR, uint64(itemVS->CreatorGuid));
+ player->SendNewItem(item, 1, true, false, false);
+
+ withdrawItems[withdrawCount++] = *itemVS;
+
+ player->DeleteVoidStorageItem(slot);
+ }
+
+ WorldPacket data(SMSG_VOID_STORAGE_TRANSFER_CHANGES, ((5 + 5 + (7 + 7) * depositCount +
+ 7 * withdrawCount) / 8) + 7 * withdrawCount + (7 + 7 + 4 * 4) * depositCount);
+
+ data.WriteBits(depositCount, 5);
+ data.WriteBits(withdrawCount, 5);
+
+ for (uint8 i = 0; i < depositCount; ++i)
+ {
+ ObjectGuid itemId = depositItems[i].first.ItemId;
+ ObjectGuid creatorGuid = depositItems[i].first.CreatorGuid;
+ data.WriteBit(creatorGuid[7]);
+ data.WriteBit(itemId[7]);
+ data.WriteBit(itemId[4]);
+ data.WriteBit(creatorGuid[6]);
+ data.WriteBit(creatorGuid[5]);
+ data.WriteBit(itemId[3]);
+ data.WriteBit(itemId[5]);
+ data.WriteBit(creatorGuid[4]);
+ data.WriteBit(creatorGuid[2]);
+ data.WriteBit(creatorGuid[0]);
+ data.WriteBit(creatorGuid[3]);
+ data.WriteBit(creatorGuid[1]);
+ data.WriteBit(itemId[2]);
+ data.WriteBit(itemId[0]);
+ data.WriteBit(itemId[1]);
+ data.WriteBit(itemId[6]);
+ }
+
+ for (uint8 i = 0; i < withdrawCount; ++i)
+ {
+ ObjectGuid itemId = withdrawItems[i].ItemId;
+ data.WriteBit(itemId[1]);
+ data.WriteBit(itemId[7]);
+ data.WriteBit(itemId[3]);
+ data.WriteBit(itemId[5]);
+ data.WriteBit(itemId[6]);
+ data.WriteBit(itemId[2]);
+ data.WriteBit(itemId[4]);
+ data.WriteBit(itemId[0]);
+ }
+
+ data.FlushBits();
+
+ for (uint8 i = 0; i < withdrawCount; ++i)
+ {
+ ObjectGuid itemId = withdrawItems[i].ItemId;
+ data.WriteByteSeq(itemId[3]);
+ data.WriteByteSeq(itemId[1]);
+ data.WriteByteSeq(itemId[0]);
+ data.WriteByteSeq(itemId[2]);
+ data.WriteByteSeq(itemId[7]);
+ data.WriteByteSeq(itemId[5]);
+ data.WriteByteSeq(itemId[6]);
+ data.WriteByteSeq(itemId[4]);
+ }
+
+ for (uint8 i = 0; i < depositCount; ++i)
+ {
+ ObjectGuid itemId = depositItems[i].first.ItemId;
+ ObjectGuid creatorGuid = depositItems[i].first.CreatorGuid;
+
+ data << int32(0); // unk
+
+ data.WriteByteSeq(itemId[6]);
+ data.WriteByteSeq(itemId[4]);
+ data.WriteByteSeq(creatorGuid[4]);
+ data.WriteByteSeq(itemId[2]);
+ data.WriteByteSeq(creatorGuid[1]);
+ data.WriteByteSeq(creatorGuid[3]);
+ data.WriteByteSeq(itemId[3]);
+ data.WriteByteSeq(creatorGuid[0]);
+ data.WriteByteSeq(itemId[0]);
+ data.WriteByteSeq(creatorGuid[6]);
+ data.WriteByteSeq(itemId[5]);
+ data.WriteByteSeq(creatorGuid[5]);
+ data.WriteByteSeq(creatorGuid[7]);
+
+ data << uint32(depositItems[i].first.ItemEntry);
+
+ data.WriteByteSeq(itemId[1]);
+
+ data << uint32(depositItems[i].second); // slot
+
+ data.WriteByteSeq(creatorGuid[2]);
+ data.WriteByteSeq(itemId[7]);
+
+ data << int32(0); // unk
+ }
+
+ SendPacket(&data);
+
+ SendVoidStorageTransferResult(VOID_TRANSFER_ERROR_NO_ERROR);
+}
+
+void WorldSession::HandleVoidSwapItem(WorldPacket& recvData)
+{
+ sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: Received CMSG_VOID_SWAP_ITEM");
+
+ Player* player = GetPlayer();
+ uint32 newSlot;
+ ObjectGuid npcGuid;
+ ObjectGuid itemId;
+
+ recvData >> uint32(newSlot);
+
+ npcGuid[2] = recvData.ReadBit();
+ npcGuid[4] = recvData.ReadBit();
+ npcGuid[0] = recvData.ReadBit();
+ itemId[2] = recvData.ReadBit();
+ itemId[6] = recvData.ReadBit();
+ itemId[5] = recvData.ReadBit();
+ npcGuid[1] = recvData.ReadBit();
+ npcGuid[7] = recvData.ReadBit();
+ itemId[3] = recvData.ReadBit();
+ itemId[7] = recvData.ReadBit();
+ itemId[0] = recvData.ReadBit();
+ npcGuid[6] = recvData.ReadBit();
+ npcGuid[5] = recvData.ReadBit();
+ npcGuid[3] = recvData.ReadBit();
+ itemId[1] = recvData.ReadBit();
+ itemId[4] = recvData.ReadBit();
+
+ recvData.ReadByteSeq(npcGuid[1]);
+ recvData.ReadByteSeq(itemId[3]);
+ recvData.ReadByteSeq(itemId[2]);
+ recvData.ReadByteSeq(itemId[4]);
+ recvData.ReadByteSeq(npcGuid[3]);
+ recvData.ReadByteSeq(npcGuid[0]);
+ recvData.ReadByteSeq(itemId[6]);
+ recvData.ReadByteSeq(itemId[1]);
+ recvData.ReadByteSeq(npcGuid[5]);
+ recvData.ReadByteSeq(itemId[5]);
+ recvData.ReadByteSeq(npcGuid[6]);
+ recvData.ReadByteSeq(itemId[0]);
+ recvData.ReadByteSeq(npcGuid[2]);
+ recvData.ReadByteSeq(npcGuid[7]);
+ recvData.ReadByteSeq(npcGuid[4]);
+ recvData.ReadByteSeq(itemId[7]);
+
+ Creature* unit = player->GetNPCIfCanInteractWith(npcGuid, UNIT_NPC_FLAG_VAULTKEEPER);
+ if (!unit)
+ {
+ sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: HandleVoidSwapItem - Unit (GUID: %u) not found or player can't interact with it.", GUID_LOPART(npcGuid));
+ return;
+ }
+
+ if (!player->IsVoidStorageUnlocked())
+ {
+ sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: HandleVoidSwapItem - Player (GUID: %u, name: %s) queried void storage without unlocking it.", player->GetGUIDLow(), player->GetName());
+ return;
+ }
+
+ uint8 oldSlot;
+ if (!player->GetVoidStorageItem(itemId, oldSlot))
+ {
+ sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: HandleVoidSwapItem - Player (GUID: %u, name: %s) requested swapping an invalid item (slot: %u, itemid: %u).", player->GetGUIDLow(), player->GetName(), newSlot, itemId);
+ return;
+ }
+
+ bool usedSrcSlot = player->GetVoidStorageItem(oldSlot) != NULL; // should be always true
+ bool usedDestSlot = player->GetVoidStorageItem(newSlot) != NULL;
+ ObjectGuid itemIdDest;
+ if (usedDestSlot)
+ itemIdDest = player->GetVoidStorageItem(newSlot)->ItemId;
+
+ if (!player->SwapVoidStorageItem(oldSlot, newSlot))
+ {
+ SendVoidStorageTransferResult(VOID_TRANSFER_ERROR_INTERNAL_ERROR_1);
+ return;
+ }
+
+ WorldPacket data(SMSG_VOID_ITEM_SWAP_RESPONSE, 1 + (usedSrcSlot + usedDestSlot) * (1 + 7 + 4));
+
+ data.WriteBit(!usedDestSlot);
+ data.WriteBit(!usedSrcSlot);
+
+ if (usedSrcSlot)
+ {
+ data.WriteBit(itemId[5]);
+ data.WriteBit(itemId[2]);
+ data.WriteBit(itemId[1]);
+ data.WriteBit(itemId[4]);
+ data.WriteBit(itemId[0]);
+ data.WriteBit(itemId[6]);
+ data.WriteBit(itemId[7]);
+ data.WriteBit(itemId[3]);
+ }
+
+ data.WriteBit(!usedDestSlot); // unk
+
+ if (usedDestSlot)
+ {
+ data.WriteBit(itemIdDest[7]);
+ data.WriteBit(itemIdDest[3]);
+ data.WriteBit(itemIdDest[4]);
+ data.WriteBit(itemIdDest[0]);
+ data.WriteBit(itemIdDest[5]);
+ data.WriteBit(itemIdDest[1]);
+ data.WriteBit(itemIdDest[2]);
+ data.WriteBit(itemIdDest[6]);
+ }
+
+ data.WriteBit(!usedSrcSlot); // unk
+
+ data.FlushBits();
+
+ if (usedDestSlot)
+ {
+ data.WriteByteSeq(itemIdDest[4]);
+ data.WriteByteSeq(itemIdDest[6]);
+ data.WriteByteSeq(itemIdDest[5]);
+ data.WriteByteSeq(itemIdDest[2]);
+ data.WriteByteSeq(itemIdDest[3]);
+ data.WriteByteSeq(itemIdDest[1]);
+ data.WriteByteSeq(itemIdDest[7]);
+ data.WriteByteSeq(itemIdDest[0]);
+ }
+
+ if (usedSrcSlot)
+ {
+ data.WriteByteSeq(itemId[6]);
+ data.WriteByteSeq(itemId[3]);
+ data.WriteByteSeq(itemId[5]);
+ data.WriteByteSeq(itemId[0]);
+ data.WriteByteSeq(itemId[1]);
+ data.WriteByteSeq(itemId[2]);
+ data.WriteByteSeq(itemId[4]);
+ data.WriteByteSeq(itemId[7]);
+ }
+
+ if (usedSrcSlot)
+ data << uint32(newSlot);
+
+ if (usedDestSlot)
+ data << uint32(oldSlot);
+
+ SendPacket(&data);
+}
diff --git a/src/server/game/Miscellaneous/SharedDefines.h b/src/server/game/Miscellaneous/SharedDefines.h
index 0b3dc884a1a..e17bbc0729f 100755
--- a/src/server/game/Miscellaneous/SharedDefines.h
+++ b/src/server/game/Miscellaneous/SharedDefines.h
@@ -3667,4 +3667,24 @@ enum CalendarError
// Calendar - end
+#define VOID_STORAGE_UNLOCK 100*GOLD
+#define VOID_STORAGE_STORE_ITEM 25*GOLD
+#define VOID_STORAGE_MAX_DEPOSIT 9
+#define VOID_STORAGE_MAX_WITHDRAW 9
+#define VOID_STORAGE_MAX_SLOT 80
+
+enum VoidTransferError
+{
+ VOID_TRANSFER_ERROR_NO_ERROR = 0,
+ VOID_TRANSFER_ERROR_INTERNAL_ERROR_1 = 1,
+ VOID_TRANSFER_ERROR_INTERNAL_ERROR_2 = 2,
+ VOID_TRANSFER_ERROR_FULL = 3,
+ VOID_TRANSFER_ERROR_INTERNAL_ERROR_3 = 4,
+ VOID_TRANSFER_ERROR_INTERNAL_ERROR_4 = 5,
+ VOID_TRANSFER_ERROR_NOT_ENOUGH_MONEY = 6,
+ VOID_TRANSFER_ERROR_INVENTORY_FULL = 7,
+ VOID_TRANSFER_ERROR_INTERNAL_ERROR_5 = 8,
+ VOID_TRANSFER_ERROR_TRANSFER_UNKNOWN = 9,
+};
+
#endif
diff --git a/src/server/game/Server/Protocol/Opcodes.cpp b/src/server/game/Server/Protocol/Opcodes.cpp
index 301ebb12221..9fe345b9c5f 100644
--- a/src/server/game/Server/Protocol/Opcodes.cpp
+++ b/src/server/game/Server/Protocol/Opcodes.cpp
@@ -82,12 +82,12 @@ void InitOpcodes()
//DEFINE_OPCODE_HANDLER(CMSG_AUTH_SRP6_RECODE, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_NULL );
//DEFINE_OPCODE_HANDLER(CMSG_AUTOBANK_ITEM, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, &WorldSession::HandleAutoBankItemOpcode );
//DEFINE_OPCODE_HANDLER(CMSG_AUTOEQUIP_GROUND_ITEM, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_NULL );
- //DEFINE_OPCODE_HANDLER(CMSG_AUTOEQUIP_ITEM, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, &WorldSession::HandleAutoEquipItemOpcode );
+ DEFINE_OPCODE_HANDLER(CMSG_AUTOEQUIP_ITEM, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, &WorldSession::HandleAutoEquipItemOpcode );
//DEFINE_OPCODE_HANDLER(CMSG_AUTOEQUIP_ITEM_SLOT, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, &WorldSession::HandleAutoEquipItemSlotOpcode );
//DEFINE_OPCODE_HANDLER(CMSG_AUTOSTORE_BAG_ITEM, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, &WorldSession::HandleAutoStoreBagItemOpcode );
- //DEFINE_OPCODE_HANDLER(CMSG_AUTOSTORE_BANK_ITEM, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, &WorldSession::HandleAutoStoreBankItemOpcode );
+ DEFINE_OPCODE_HANDLER(CMSG_AUTOSTORE_BANK_ITEM, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, &WorldSession::HandleAutoStoreBankItemOpcode );
//DEFINE_OPCODE_HANDLER(CMSG_AUTOSTORE_GROUND_ITEM, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_NULL );
- //DEFINE_OPCODE_HANDLER(CMSG_AUTOSTORE_LOOT_ITEM, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, &WorldSession::HandleAutostoreLootItemOpcode );
+ DEFINE_OPCODE_HANDLER(CMSG_AUTOSTORE_LOOT_ITEM, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, &WorldSession::HandleAutostoreLootItemOpcode );
//DEFINE_OPCODE_HANDLER(CMSG_BANKER_ACTIVATE, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, &WorldSession::HandleBankerActivateOpcode );
//DEFINE_OPCODE_HANDLER(CMSG_BATTLEFIELD_JOIN, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_NULL );
//DEFINE_OPCODE_HANDLER(CMSG_BATTLEFIELD_LIST, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, &WorldSession::HandleBattlefieldListOpcode );
@@ -631,7 +631,7 @@ void InitOpcodes()
//DEFINE_OPCODE_HANDLER(CMSG_STORE_LOOT_IN_SLOT, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_NULL );
//DEFINE_OPCODE_HANDLER(CMSG_SUMMON_RESPONSE, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, &WorldSession::HandleSummonResponseOpcode );
//DEFINE_OPCODE_HANDLER(CMSG_SUSPEND_COMMS_ACK, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_NULL );
- //DEFINE_OPCODE_HANDLER(CMSG_SWAP_INV_ITEM, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, &WorldSession::HandleSwapInvItemOpcode );
+ DEFINE_OPCODE_HANDLER(CMSG_SWAP_INV_ITEM, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, &WorldSession::HandleSwapInvItemOpcode );
//DEFINE_OPCODE_HANDLER(CMSG_SWAP_ITEM, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, &WorldSession::HandleSwapItem );
//DEFINE_OPCODE_HANDLER(CMSG_SYNC_DANCE, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_NULL );
//DEFINE_OPCODE_HANDLER(CMSG_TARGET_CAST, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_NULL );
@@ -655,7 +655,7 @@ void InitOpcodes()
//DEFINE_OPCODE_HANDLER(CMSG_TRIGGER_CINEMATIC_CHEAT, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_NULL );
//DEFINE_OPCODE_HANDLER(CMSG_TURN_IN_PETITION, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, &WorldSession::HandleTurnInPetitionOpcode );
//DEFINE_OPCODE_HANDLER(CMSG_TUTORIAL_CLEAR, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, &WorldSession::HandleTutorialClear );
- //DEFINE_OPCODE_HANDLER(CMSG_TUTORIAL_FLAG, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, &WorldSession::HandleTutorialFlag );
+ DEFINE_OPCODE_HANDLER(CMSG_TUTORIAL_FLAG, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, &WorldSession::HandleTutorialFlag );
//DEFINE_OPCODE_HANDLER(CMSG_TUTORIAL_RESET, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, &WorldSession::HandleTutorialReset );
DEFINE_OPCODE_HANDLER(CMSG_UNACCEPT_TRADE, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, &WorldSession::HandleUnacceptTradeOpcode );
//DEFINE_OPCODE_HANDLER(CMSG_UNCLAIM_LICENSE, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_NULL );
@@ -676,6 +676,10 @@ void InitOpcodes()
DEFINE_OPCODE_HANDLER(CMSG_VIOLENCE_LEVEL, STATUS_LOGGEDIN, PROCESS_INPLACE, &WorldSession::HandleViolenceLevel );
DEFINE_OPCODE_HANDLER(CMSG_VOICE_SESSION_ENABLE, STATUS_AUTHED, PROCESS_THREADUNSAFE, &WorldSession::HandleVoiceSessionEnableOpcode );
//DEFINE_OPCODE_HANDLER(CMSG_VOICE_SET_TALKER_MUTED_REQUEST, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_NULL );
+ DEFINE_OPCODE_HANDLER(CMSG_VOID_STORAGE_UNLOCK, STATUS_LOGGEDIN, PROCESS_INPLACE, &WorldSession::HandleVoidStorageUnlock );
+ DEFINE_OPCODE_HANDLER(CMSG_VOID_STORAGE_QUERY, STATUS_LOGGEDIN, PROCESS_INPLACE, &WorldSession::HandleVoidStorageQuery );
+ DEFINE_OPCODE_HANDLER(CMSG_VOID_STORAGE_TRANSFER, STATUS_LOGGEDIN, PROCESS_INPLACE, &WorldSession::HandleVoidStorageTransfer );
+ DEFINE_OPCODE_HANDLER(CMSG_VOID_SWAP_ITEM, STATUS_LOGGEDIN, PROCESS_INPLACE, &WorldSession::HandleVoidSwapItem );
//DEFINE_OPCODE_HANDLER(CMSG_WARDEN_DATA, STATUS_AUTHED, PROCESS_THREADUNSAFE, &WorldSession::HandleWardenDataOpcode );
//DEFINE_OPCODE_HANDLER(CMSG_WEATHER_SPEED_CHEAT, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_NULL );
//DEFINE_OPCODE_HANDLER(CMSG_WHO, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, &WorldSession::HandleWhoOpcode );
@@ -1042,7 +1046,7 @@ void InitOpcodes()
DEFINE_OPCODE_HANDLER(SMSG_ITEM_COOLDOWN, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide );
DEFINE_OPCODE_HANDLER(SMSG_ITEM_ENCHANT_TIME_UPDATE, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide );
//DEFINE_OPCODE_HANDLER(SMSG_ITEM_NAME_QUERY_RESPONSE, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide );
- //DEFINE_OPCODE_HANDLER(SMSG_ITEM_PUSH_RESULT, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide );
+ DEFINE_OPCODE_HANDLER(SMSG_ITEM_PUSH_RESULT, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide );
DEFINE_OPCODE_HANDLER(SMSG_ITEM_REFUND_INFO_RESPONSE, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide );
//DEFINE_OPCODE_HANDLER(SMSG_ITEM_REFUND_RESULT, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide );
//DEFINE_OPCODE_HANDLER(SMSG_ITEM_TEXT_QUERY_RESPONSE, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide );
@@ -1380,6 +1384,11 @@ void InitOpcodes()
//DEFINE_OPCODE_HANDLER(SMSG_VOICE_SESSION_LEAVE, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide );
//DEFINE_OPCODE_HANDLER(SMSG_VOICE_SESSION_ROSTER_UPDATE, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide );
//DEFINE_OPCODE_HANDLER(SMSG_VOICE_SET_TALKER_MUTED, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide );
+ DEFINE_OPCODE_HANDLER(SMSG_VOID_ITEM_SWAP_RESPONSE, STATUS_LOGGEDIN, PROCESS_INPLACE, &WorldSession::Handle_ServerSide );
+ DEFINE_OPCODE_HANDLER(SMSG_VOID_STORAGE_CONTENTS, STATUS_LOGGEDIN, PROCESS_INPLACE, &WorldSession::Handle_ServerSide );
+ DEFINE_OPCODE_HANDLER(SMSG_VOID_STORAGE_TRANSFER_CHANGES, STATUS_LOGGEDIN, PROCESS_INPLACE, &WorldSession::Handle_ServerSide );
+ DEFINE_OPCODE_HANDLER(SMSG_VOID_TRANSFER_RESULT, STATUS_LOGGEDIN, PROCESS_INPLACE, &WorldSession::Handle_ServerSide );
+ DEFINE_OPCODE_HANDLER(SMSG_VOID_STORAGE_FAILED, STATUS_LOGGEDIN, PROCESS_INPLACE, &WorldSession::Handle_ServerSide );
//DEFINE_OPCODE_HANDLER(SMSG_WARDEN_DATA, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide );
DEFINE_OPCODE_HANDLER(SMSG_WEATHER, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide );
//DEFINE_OPCODE_HANDLER(SMSG_WHO, STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide );
diff --git a/src/server/game/Server/Protocol/Opcodes.h b/src/server/game/Server/Protocol/Opcodes.h
index c1fb3f940be..d286e33fcbb 100755
--- a/src/server/game/Server/Protocol/Opcodes.h
+++ b/src/server/game/Server/Protocol/Opcodes.h
@@ -440,7 +440,6 @@ enum Opcodes
CMSG_QUERY_INSPECT_ACHIEVEMENTS = 0x4D27,
CMSG_QUERY_QUESTS_COMPLETED = 0x2317,
CMSG_QUERY_TIME = 0x0A36,
- CMSG_QUERY_VOID_STORAGE = 0x790A,
CMSG_QUESTGIVER_ACCEPT_QUEST = 0x6B37,
CMSG_QUESTGIVER_CANCEL = 0x0000,
CMSG_QUESTGIVER_CHOOSE_REWARD = 0x2125,
@@ -552,7 +551,6 @@ enum Opcodes
CMSG_SUSPEND_TOKEN = 0x046D,
CMSG_SWAP_INV_ITEM = 0x2614,
CMSG_SWAP_ITEM = 0x6326,
- CMSG_SWAP_VOID_ITEM = 0x3204,
CMSG_SYNC_DANCE = 0x0036,
CMSG_TAXICLEARALLNODES = 0x0000,
CMSG_TAXIENABLEALLNODES = 0x0000,
@@ -577,7 +575,6 @@ enum Opcodes
CMSG_UNACCEPT_TRADE = 0x391A,
CMSG_UNLEARN_SKILL = 0x6106,
CMSG_UNLEARN_SPECIALIZATION = 0x3210,
- CMSG_UNLOCK_VOID_STORAGE = 0x7B14,
CMSG_UNREGISTER_ALL_ADDON_PREFIXES = 0x3D54,
CMSG_UPDATE_ACCOUNT_DATA = 0x4736,
CMSG_UPDATE_MISSILE_TRAJECTORY = 0x781E,
@@ -586,7 +583,10 @@ enum Opcodes
CMSG_USE_ITEM = 0x2C06,
CMSG_VIOLENCE_LEVEL = 0x7816,
CMSG_VOICE_SESSION_ENABLE = 0x2314,
+ CMSG_VOID_STORAGE_QUERY = 0x790A,
CMSG_VOID_STORAGE_TRANSFER = 0x380E,
+ CMSG_VOID_STORAGE_UNLOCK = 0x7B14,
+ CMSG_VOID_SWAP_ITEM = 0x3204,
CMSG_WARDEN_DATA = 0x25A2,
CMSG_WARGAME_ACCEPT = 0x2410,
CMSG_WARGAME_START = 0x05A0,
diff --git a/src/server/game/Server/WorldSession.h b/src/server/game/Server/WorldSession.h
index 80c532e7430..c215063fefa 100755
--- a/src/server/game/Server/WorldSession.h
+++ b/src/server/game/Server/WorldSession.h
@@ -911,6 +911,13 @@ class WorldSession
void SendCalendarRaidLockoutUpdated(InstanceSave const* save);
void SendCalendarCommandResult(CalendarError err, char const* param = NULL);
+ // Void Storage
+ void HandleVoidStorageUnlock(WorldPacket& recvData);
+ void HandleVoidStorageQuery(WorldPacket& recvData);
+ void HandleVoidStorageTransfer(WorldPacket& recvData);
+ void HandleVoidSwapItem(WorldPacket& recvData);
+ void SendVoidStorageTransferResult(VoidTransferError result);
+
void HandleSpellClick(WorldPacket& recv_data);
void HandleMirrorImageDataRequest(WorldPacket& recv_data);
void HandleAlterAppearance(WorldPacket& recv_data);
diff --git a/src/server/shared/Database/Implementation/CharacterDatabase.cpp b/src/server/shared/Database/Implementation/CharacterDatabase.cpp
index 75fbdbf78f8..d1544d43232 100755
--- a/src/server/shared/Database/Implementation/CharacterDatabase.cpp
+++ b/src/server/shared/Database/Implementation/CharacterDatabase.cpp
@@ -555,4 +555,8 @@ void CharacterDatabaseConnection::DoPrepareStatements()
PREPARE_STATEMENT(CHAR_INS_CHAR_TALENT, "INSERT INTO character_talent (guid, spell, spec) VALUES (?, ?, ?)", CONNECTION_ASYNC);
PREPARE_STATEMENT(CHAR_DEL_CHAR_ACTION_EXCEPT_SPEC, "DELETE FROM character_action WHERE spec<>? AND guid = ?", CONNECTION_ASYNC);
PREPARE_STATEMENT(CHAR_SEL_CHAR_PET_BY_ENTRY_AND_SLOT, "SELECT id, entry, owner, modelid, level, exp, Reactstate, slot, name, renamed, curhealth, curmana, abdata, savetime, CreatedBySpell, PetType FROM character_pet WHERE owner = ? AND slot = ?", CONNECTION_SYNCH);
+
+ PREPARE_STATEMENT(CHAR_SEL_VOID_STORAGE, "SELECT itemid, itemEntry, slot, creatorGuid FROM void_storage WHERE playerGuid = ?", CONNECTION_ASYNC);
+ PREPARE_STATEMENT(CHAR_REP_VOID_STORAGE_ITEM, "REPLACE INTO void_Storage (itemId, playerGuid, itemEntry, slot, creatorGuid) VALUES (?, ?, ?, ?, ?)", CONNECTION_ASYNC)
+ PREPARE_STATEMENT(CHAR_DEL_VOID_STORAGE_ITEM_BY_SLOT, "DELETE FROM void_Storage WHERE slot = ? AND playerGuid = ?", CONNECTION_ASYNC);
}
diff --git a/src/server/shared/Database/Implementation/CharacterDatabase.h b/src/server/shared/Database/Implementation/CharacterDatabase.h
index c0ab078dfe3..5dac60d6ba8 100755
--- a/src/server/shared/Database/Implementation/CharacterDatabase.h
+++ b/src/server/shared/Database/Implementation/CharacterDatabase.h
@@ -519,6 +519,10 @@ enum CharacterDatabaseStatements
CHAR_DEL_CHAR_ACTION_EXCEPT_SPEC,
CHAR_SEL_CHAR_PET_BY_ENTRY_AND_SLOT,
+ CHAR_SEL_VOID_STORAGE,
+ CHAR_REP_VOID_STORAGE_ITEM,
+ CHAR_DEL_VOID_STORAGE_ITEM_BY_SLOT,
+
MAX_CHARACTERDATABASE_STATEMENTS,
};