/* * The ManaPlus Client * Copyright (C) 2004-2009 The Mana World Development Team * Copyright (C) 2009-2010 The Mana Developers * Copyright (C) 2011-2020 The ManaPlus Developers * Copyright (C) 2020-2023 The ManaVerse Developers * * This file is part of The ManaPlus Client. * * 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 * 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 . */ #include "net/eathena/inventoryhandler.h" #include "const/net/inventory.h" #include "enums/equipslot.h" #include "net/ea/inventoryrecv.h" #include "net/eathena/inventoryrecv.h" #include "net/eathena/menu.h" #include "net/eathena/messageout.h" #include "net/eathena/protocolout.h" #include "utils/foreach.h" #include "resources/item/item.h" #include "debug.h" extern int packetVersion; extern int packetVersionMain; extern int packetVersionRe; extern int packetVersionZero; extern int serverVersion; // this conversion from bit corrupted LOOK_* to EquipSlot // for how it corrupted, see BeingRecv::processBeingChangeLookContinue const EquipSlot::Type EQUIP_CONVERT[] = { EquipSlot::PROJECTILE_SLOT, // 0 SPRITE_BASE EquipSlot::FEET_SLOT, // 1 SPRITE_HAIR EquipSlot::LEGS_SLOT, // 2 SPRITE_WEAPON EquipSlot::TORSO_SLOT, // 3 SPRITE_HEAD_BOTTOM EquipSlot::GLOVES_SLOT, // 4 SPRITE_HEAD_TOP EquipSlot::EVOL_RING1_SLOT, // 5 SPRITE_HEAD_MID EquipSlot::PROJECTILE_SLOT, // 6 SPRITE_HAIR_COLOR EquipSlot::HEAD_SLOT, // 7 SPRITE_CLOTHES_COLOR EquipSlot::RING2_SLOT, // 8 SPRITE_SHIELD EquipSlot::PROJECTILE_SLOT, // 9 SPRITE_SHOES EquipSlot::FIGHT1_SLOT, // 10 SPRITE_BODY EquipSlot::FIGHT2_SLOT, // 11 SPRITE_FLOOR EquipSlot::EVOL_RING2_SLOT, // 12 SPRITE_ROBE EquipSlot::PROJECTILE_SLOT, // 13 SPRITE_EVOL2 EquipSlot::COSTUME_ROBE_SLOT, // 14 SPRITE_EVOL3 EquipSlot::RING1_SLOT, // 15 SPRITE_EVOL4 EquipSlot::NECK_SLOT, // 16 SPRITE_EVOL5 EquipSlot::RING2_SLOT, // 17 SPRITE_EVOL6 }; namespace EAthena { InventoryHandler::InventoryHandler() : Ea::InventoryHandler(), mItemIndex(0) { inventoryHandler = this; InventoryRecv::mInventoryItems.clear(); InventoryRecv::mCartItems.clear(); } InventoryHandler::~InventoryHandler() { inventoryHandler = nullptr; } void InventoryHandler::clear() const { Ea::InventoryHandler::clear(); Ea::InventoryRecv::mStorageItems.clear(); InventoryRecv::mInventoryItems.clear(); InventoryRecv::mCartItems.clear(); } void InventoryHandler::equipItem(const Item *const item) const { if (item == nullptr) return; createOutPacket(CMSG_PLAYER_EQUIP); outMsg.writeInt16(CAST_S16( item->getInvIndex() + INVENTORY_OFFSET), "index"); // here we set flag for any slots, // probably better set to slot from item properties if (packetVersion >= 20130320) outMsg.writeInt32(0xFFFFFFFFU, "wear location"); else outMsg.writeInt16(0x7FFFU, "wear location"); } void InventoryHandler::unequipItem(const Item *const item) const { if (item == nullptr) return; createOutPacket(CMSG_PLAYER_UNEQUIP); outMsg.writeInt16(CAST_S16( item->getInvIndex() + INVENTORY_OFFSET), "index"); } void InventoryHandler::useItem(const Item *const item) const { if (item == nullptr) return; createOutPacket(CMSG_PLAYER_INVENTORY_USE); outMsg.writeInt16(CAST_S16( item->getInvIndex() + INVENTORY_OFFSET), "index"); outMsg.writeInt32(item->getId(), "unused"); } void InventoryHandler::useItem(const Item *const item, const int16_t useType) const { if (item == nullptr) return; if (serverVersion > 0) { createOutPacket(CMSG_PLAYER_INVENTORY_USE2); outMsg.writeInt16(CAST_S16( item->getInvIndex() + INVENTORY_OFFSET), "index"); outMsg.writeInt16(useType, "use type"); } else { createOutPacket(CMSG_PLAYER_INVENTORY_USE); outMsg.writeInt16(CAST_S16( item->getInvIndex() + INVENTORY_OFFSET), "index"); outMsg.writeInt32(item->getId(), "unused"); } } void InventoryHandler::dropItem(const Item *const item, const int amount) const { if (item == nullptr) return; createOutPacket(CMSG_PLAYER_INVENTORY_DROP); const int16_t index = CAST_S16(item->getInvIndex() + INVENTORY_OFFSET); if (packetVersion >= 20101124) { outMsg.writeInt16(index, "index"); outMsg.writeInt16(CAST_S16(amount), "amount"); } else if (packetVersion >= 20080827) { outMsg.writeInt16(0, "unknown"); outMsg.writeInt16(index, "index"); outMsg.writeInt32(0, "unknown"); outMsg.writeInt16(0, "unknown"); outMsg.writeInt8(0, "unknown"); outMsg.writeInt16(CAST_S16(amount), "amount"); } else if (packetVersion >= 20070212) { outMsg.writeInt16(0, "unknown"); outMsg.writeInt8(0, "unknown"); outMsg.writeInt16(index, "index"); outMsg.writeInt8(0, "unknown"); outMsg.writeInt16(CAST_S16(amount), "amount"); } else if (packetVersion >= 20070108) { outMsg.writeInt32(0, "unknown"); outMsg.writeInt32(0, "unknown"); outMsg.writeInt16(0, "unknown"); outMsg.writeInt16(index, "index"); outMsg.writeInt32(0, "unknown"); outMsg.writeInt16(CAST_S16(amount), "amount"); } else if (packetVersion >= 20060327) { outMsg.writeInt32(0, "unknown"); outMsg.writeInt16(0, "unknown"); outMsg.writeInt16(index, "index"); outMsg.writeInt32(0, "unknown"); outMsg.writeInt8(0, "unknown"); outMsg.writeInt16(CAST_S16(amount), "amount"); } else if (packetVersion >= 20050719) { outMsg.writeInt8(0, "unknown"); outMsg.writeInt16(index, "index"); outMsg.writeInt8(0, "unknown"); outMsg.writeInt32(0, "unknown"); outMsg.writeInt16(CAST_S16(amount), "amount"); } else if (packetVersion >= 20050718) { outMsg.writeInt32(0, "unknown"); outMsg.writeInt16(index, "index"); outMsg.writeInt16(0, "unknown"); outMsg.writeInt16(CAST_S16(amount), "amount"); } else if (packetVersion >= 20050628) { outMsg.writeInt8(0, "unknown"); outMsg.writeInt16(index, "index"); outMsg.writeInt32(0, "unknown"); outMsg.writeInt8(0, "unknown"); outMsg.writeInt16(CAST_S16(amount), "amount"); } else if (packetVersion >= 20050509) { outMsg.writeInt16(0, "unknown"); outMsg.writeInt8(0, "unknown"); outMsg.writeInt16(index, "index"); outMsg.writeInt8(0, "unknown"); outMsg.writeInt16(CAST_S16(amount), "amount"); } else if (packetVersion >= 20050110) { outMsg.writeInt32(0, "unknown"); outMsg.writeInt32(0, "unknown"); outMsg.writeInt32(0, "unknown"); outMsg.writeInt8(0, "unknown"); outMsg.writeInt16(index, "index"); outMsg.writeInt8(0, "unknown"); outMsg.writeInt16(CAST_S16(amount), "amount"); } else if (packetVersion >= 20041129) { outMsg.writeInt16(0, "unknown"); outMsg.writeInt16(index, "index"); outMsg.writeInt32(0, "unknown"); outMsg.writeInt16(CAST_S16(amount), "amount"); } else if (packetVersion >= 20041025) { outMsg.writeInt32(0, "unknown"); outMsg.writeInt16(index, "index"); outMsg.writeInt16(0, "unknown"); outMsg.writeInt16(CAST_S16(amount), "amount"); } else if (packetVersion >= 20041005) { outMsg.writeInt16(0, "unknown"); outMsg.writeInt8(0, "unknown"); outMsg.writeInt16(index, "index"); outMsg.writeInt32(0, "unknown"); outMsg.writeInt8(0, "unknown"); outMsg.writeInt16(CAST_S16(amount), "amount"); } else if (packetVersion >= 20040920) { outMsg.writeInt32(0, "unknown"); outMsg.writeInt32(0, "unknown"); outMsg.writeInt16(0, "unknown"); outMsg.writeInt16(index, "index"); outMsg.writeInt16(0, "unknown"); outMsg.writeInt8(0, "unknown"); outMsg.writeInt16(CAST_S16(amount), "amount"); } else if (packetVersion >= 20040906) { outMsg.writeInt32(0, "unknown"); outMsg.writeInt16(index, "index"); outMsg.writeInt32(0, "unknown"); outMsg.writeInt16(0, "unknown"); outMsg.writeInt8(0, "unknown"); outMsg.writeInt16(CAST_S16(amount), "amount"); } else if (packetVersion >= 20040809) { outMsg.writeInt32(0, "unknown"); outMsg.writeInt16(0, "unknown"); outMsg.writeInt16(index, "index"); outMsg.writeInt32(0, "unknown"); outMsg.writeInt8(0, "unknown"); outMsg.writeInt16(CAST_S16(amount), "amount"); } else if (packetVersion >= 20040726) { outMsg.writeInt16(0, "unknown"); outMsg.writeInt8(0, "unknown"); outMsg.writeInt16(index, "index"); outMsg.writeInt32(0, "unknown"); outMsg.writeInt8(0, "unknown"); outMsg.writeInt16(CAST_S16(amount), "amount"); } else { outMsg.writeInt16(index, "index"); outMsg.writeInt16(CAST_S16(amount), "amount"); } } void InventoryHandler::closeStorage() const { createOutPacket(CMSG_CLOSE_STORAGE); } void InventoryHandler::moveItemInventoryToStorage(const int slot, const int amount) const { createOutPacket(CMSG_MOVE_TO_STORAGE); if (packetVersion >= 20101124) { outMsg.writeInt16(CAST_S16(slot + INVENTORY_OFFSET), "index"); outMsg.writeInt32(amount, "amount"); } else if (packetVersion >= 20080827) { outMsg.writeInt8(0, "unknown"); outMsg.writeInt16(CAST_S16(slot + INVENTORY_OFFSET), "index"); outMsg.writeInt32(0, "unknown"); outMsg.writeInt32(0, "unknown"); outMsg.writeInt32(0, "unknown"); outMsg.writeInt16(0, "unknown"); outMsg.writeInt8(0, "unknown"); outMsg.writeInt32(amount, "amount"); } else if (packetVersion >= 20070212) { outMsg.writeInt32(0, "unknown"); outMsg.writeInt8(0, "unknown"); outMsg.writeInt16(CAST_S16(slot + INVENTORY_OFFSET), "index"); outMsg.writeInt8(0, "unknown"); outMsg.writeInt32(amount, "amount"); } else if (packetVersion >= 20070108) { outMsg.writeInt16(0, "unknown"); outMsg.writeInt16(CAST_S16(slot + INVENTORY_OFFSET), "index"); outMsg.writeInt32(0, "unknown"); outMsg.writeInt16(0, "unknown"); outMsg.writeInt8(0, "unknown"); outMsg.writeInt32(amount, "amount"); } else if (packetVersion >= 20060327) { outMsg.writeInt16(0, "unknown"); outMsg.writeInt8(0, "unknown"); outMsg.writeInt16(CAST_S16(slot + INVENTORY_OFFSET), "index"); outMsg.writeInt32(0, "unknown"); outMsg.writeInt32(0, "unknown"); outMsg.writeInt32(0, "unknown"); outMsg.writeInt32(amount, "amount"); } else if (packetVersion >= 20050719) { outMsg.writeInt32(0, "unknown"); outMsg.writeInt32(0, "unknown"); outMsg.writeInt32(0, "unknown"); outMsg.writeInt16(0, "unknown"); outMsg.writeInt16(CAST_S16(slot + INVENTORY_OFFSET), "index"); outMsg.writeInt32(0, "unknown"); outMsg.writeInt32(0, "unknown"); outMsg.writeInt8(0, "unknown"); outMsg.writeInt32(amount, "amount"); } else if (packetVersion >= 20050718) { outMsg.writeInt32(0, "unknown"); outMsg.writeInt32(0, "unknown"); outMsg.writeInt16(0, "unknown"); outMsg.writeInt16(CAST_S16(slot + INVENTORY_OFFSET), "index"); outMsg.writeInt16(0, "unknown"); outMsg.writeInt8(0, "unknown"); outMsg.writeInt32(amount, "amount"); } else if (packetVersion >= 20050628) { outMsg.writeInt32(0, "unknown"); outMsg.writeInt32(0, "unknown"); outMsg.writeInt32(0, "unknown"); outMsg.writeInt16(0, "unknown"); outMsg.writeInt16(CAST_S16(slot + INVENTORY_OFFSET), "index"); outMsg.writeInt32(0, "unknown"); outMsg.writeInt32(0, "unknown"); outMsg.writeInt16(0, "unknown"); outMsg.writeInt32(amount, "amount"); } else if (packetVersion >= 20050509) { outMsg.writeInt32(0, "unknown"); outMsg.writeInt8(0, "unknown"); outMsg.writeInt16(CAST_S16(slot + INVENTORY_OFFSET), "index"); outMsg.writeInt8(0, "unknown"); outMsg.writeInt32(amount, "amount"); } else if (packetVersion >= 20050110) { outMsg.writeInt32(0, "unknown"); outMsg.writeInt32(0, "unknown"); outMsg.writeInt16(CAST_S16(slot + INVENTORY_OFFSET), "index"); outMsg.writeInt32(0, "unknown"); outMsg.writeInt16(0, "unknown"); outMsg.writeInt32(amount, "amount"); } else if (packetVersion >= 20041129) { outMsg.writeInt16(0, "unknown"); outMsg.writeInt16(CAST_S16(slot + INVENTORY_OFFSET), "index"); outMsg.writeInt32(0, "unknown"); outMsg.writeInt32(amount, "amount"); } else if (packetVersion >= 20041025) { outMsg.writeInt32(0, "unknown"); outMsg.writeInt16(CAST_S16(slot + INVENTORY_OFFSET), "index"); outMsg.writeInt8(0, "unknown"); outMsg.writeInt32(amount, "amount"); } else if (packetVersion >= 20041005) { outMsg.writeInt16(0, "unknown"); outMsg.writeInt8(0, "unknown"); outMsg.writeInt16(CAST_S16(slot + INVENTORY_OFFSET), "index"); outMsg.writeInt32(0, "unknown"); outMsg.writeInt8(0, "unknown"); outMsg.writeInt32(amount, "amount"); } else if (packetVersion >= 20040920) { outMsg.writeInt32(0, "unknown"); outMsg.writeInt16(CAST_S16(slot + INVENTORY_OFFSET), "index"); outMsg.writeInt32(0, "unknown"); outMsg.writeInt32(0, "unknown"); outMsg.writeInt32(0, "unknown"); outMsg.writeInt8(0, "unknown"); outMsg.writeInt32(amount, "amount"); } else if (packetVersion >= 20040906) { outMsg.writeInt8(0, "unknown"); outMsg.writeInt16(CAST_S16(slot + INVENTORY_OFFSET), "index"); outMsg.writeInt32(0, "unknown"); outMsg.writeInt32(0, "unknown"); outMsg.writeInt16(0, "unknown"); outMsg.writeInt32(amount, "amount"); } else if (packetVersion >= 20040809) { outMsg.writeInt16(0, "unknown"); outMsg.writeInt8(0, "unknown"); outMsg.writeInt16(CAST_S16(slot + INVENTORY_OFFSET), "index"); outMsg.writeInt32(0, "unknown"); outMsg.writeInt32(0, "unknown"); outMsg.writeInt32(0, "unknown"); outMsg.writeInt32(amount, "amount"); } else if (packetVersion >= 20040726) { outMsg.writeInt16(0, "unknown"); outMsg.writeInt8(0, "unknown"); outMsg.writeInt16(CAST_S16(slot + INVENTORY_OFFSET), "index"); outMsg.writeInt32(0, "unknown"); outMsg.writeInt8(0, "unknown"); outMsg.writeInt32(amount, "amount"); } else { outMsg.writeInt16(CAST_S16(slot + INVENTORY_OFFSET), "index"); outMsg.writeInt32(amount, "amount"); } } void InventoryHandler::moveItem2(const InventoryTypeT source, const int slot, const int amount, const InventoryTypeT destination) const { int packet = 0; int offset = INVENTORY_OFFSET; if (source == InventoryType::Inventory) { if (destination == InventoryType::Storage) { moveItemInventoryToStorage(slot, amount); return; } else if (destination == InventoryType::Cart) { packet = CMSG_MOVE_TO_CART; } } else if (source == InventoryType::Storage) { offset = STORAGE_OFFSET; if (destination == InventoryType::Inventory) packet = CMSG_MOVE_FROM_STORAGE; else if (destination == InventoryType::Cart) packet = CMSG_MOVE_FROM_STORAGE_TO_CART; } else if (source == InventoryType::Cart) { if (destination == InventoryType::Inventory) packet = CMSG_MOVE_FROM_CART; else if (destination == InventoryType::Storage) packet = CMSG_MOVE_FROM_CART_TO_STORAGE; } // for packetVersion < 20101124 need use other packet offset if (packet != 0) { createOutPacket(packet); outMsg.writeInt16(CAST_S16(slot + offset), "index"); outMsg.writeInt32(amount, "amount"); } } void InventoryHandler::useCard(const Item *const item) { if (item == nullptr) return; mItemIndex = item->getInvIndex(); createOutPacket(CMSG_PLAYER_USE_CARD); outMsg.writeInt16(CAST_S16( mItemIndex + INVENTORY_OFFSET), "index"); } void InventoryHandler::insertCard(const int cardIndex, const int itemIndex) const { createOutPacket(CMSG_PLAYER_INSERT_CARD); outMsg.writeInt16(CAST_S16(cardIndex + INVENTORY_OFFSET), "card index"); outMsg.writeInt16(CAST_S16(itemIndex + INVENTORY_OFFSET), "item index"); } void InventoryHandler::favoriteItem(const Item *const item, const bool favorite) const { if (item == nullptr) return; if (packetVersion < 20120410) return; createOutPacket(CMSG_PLAYER_FAVORITE_ITEM); outMsg.writeInt16(CAST_S16(item->getInvIndex() + INVENTORY_OFFSET), "item index"); outMsg.writeInt8(static_cast(favorite), "favorite flag"); } void InventoryHandler::selectEgg(const Item *const item) const { if (item == nullptr) return; createOutPacket(CMSG_PET_SELECT_EGG); outMsg.writeInt16(CAST_S16( item->getInvIndex() + INVENTORY_OFFSET), "index"); menu = MenuType::Unknown; } int InventoryHandler::convertFromServerSlot(const int serverSlot) const { if (serverSlot < 0 || serverSlot >= 17) return 0; return CAST_S32(EQUIP_CONVERT[serverSlot]); } void InventoryHandler::selectCart(const BeingId accountId, const int type) const { if (packetVersion < 20150805) return; createOutPacket(CMSG_SELECT_CART); outMsg.writeBeingId(accountId, "account id"); outMsg.writeInt8(CAST_S8(type), "type"); } void InventoryHandler::identifyItem(const Item *const item) const { if (packetVersion < 20150513) return; createOutPacket(CMSG_QUICK_IDENTIFY_ITEM); outMsg.writeInt16(CAST_S16(item->getInvIndex()), "item index"); } void InventoryHandler::mergeItemsAck(const STD_VECTOR &items) const { createOutPacket(CMSG_MERGE_ITEM_ACK); const size_t sz = items.size(); outMsg.writeInt16(CAST_S16(sz * 2 + 4), "len"); FOR_EACH (STD_VECTOR::const_iterator, it, items) { outMsg.writeInt16(CAST_S16((*it)->getInvIndex() + 2), "item index"); } } void InventoryHandler::mergetItemsCancel() const { createOutPacket(CMSG_MERGE_ITEM_CANCEL); } void InventoryHandler::expandInventory() const { if (packetVersionMain < 20181031 && packetVersionRe < 20181031 && packetVersionZero < 20181114) { return; } createOutPacket(CMSG_INVENTORY_EXPAND); } void InventoryHandler::expandInventoryConfirm() const { if (packetVersionMain < 20181031 && packetVersionRe < 20181031 && packetVersionZero < 20181114) { return; } createOutPacket(CMSG_INVENTORY_EXPAND_CONFIRM); } void InventoryHandler::expandInventoryReject() const { if (packetVersionMain < 20181031 && packetVersionRe < 20181031 && packetVersionZero < 20181114) { return; } createOutPacket(CMSG_INVENTORY_EXPAND_REJECT); } } // namespace EAthena