From 3eeae12c498d1a4dbe969462d2ba841f77ee3ccb Mon Sep 17 00:00:00 2001 From: Andrei Karas Date: Sun, 2 Jan 2011 01:48:38 +0200 Subject: Initial commit. This code based on mana client http://www.gitorious.org/mana/mana and my private repository. --- src/net/tmwa/inventoryhandler.cpp | 609 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 609 insertions(+) create mode 100644 src/net/tmwa/inventoryhandler.cpp (limited to 'src/net/tmwa/inventoryhandler.cpp') diff --git a/src/net/tmwa/inventoryhandler.cpp b/src/net/tmwa/inventoryhandler.cpp new file mode 100644 index 000000000..b4b38e623 --- /dev/null +++ b/src/net/tmwa/inventoryhandler.cpp @@ -0,0 +1,609 @@ +/* + * The Mana Client + * Copyright (C) 2004-2009 The Mana World Development Team + * Copyright (C) 2009-2010 The Mana Developers + * + * This file is part of The Mana 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/tmwa/inventoryhandler.h" + +#include "configuration.h" +#include "equipment.h" +#include "event.h" +#include "inventory.h" +#include "item.h" +#include "itemshortcut.h" +#include "localplayer.h" +#include "log.h" + +#include "gui/widgets/chattab.h" + +#include "net/messagein.h" +#include "net/messageout.h" + +#include "net/tmwa/protocol.h" + +#include "resources/iteminfo.h" + +#include "utils/gettext.h" +#include "utils/stringutils.h" + +#include + +extern Net::InventoryHandler *inventoryHandler; + +const Equipment::Slot EQUIP_POINTS[Equipment::EQUIP_VECTOREND] = +{ + Equipment::EQUIP_LEGS_SLOT, + Equipment::EQUIP_FIGHT1_SLOT, + Equipment::EQUIP_GLOVES_SLOT, + Equipment::EQUIP_RING2_SLOT, + Equipment::EQUIP_RING1_SLOT, + Equipment::EQUIP_FIGHT2_SLOT, + Equipment::EQUIP_FEET_SLOT, + Equipment::EQUIP_NECK_SLOT, + Equipment::EQUIP_HEAD_SLOT, + Equipment::EQUIP_TORSO_SLOT, + Equipment::EQUIP_EVOL_RING1_SLOT, + Equipment::EQUIP_EVOL_RING2_SLOT, + Equipment::EQUIP_PROJECTILE_SLOT, +}; + +const Equipment::Slot EQUIP_CONVERT[] = +{ + Equipment::EQUIP_PROJECTILE_SLOT, // 0 + Equipment::EQUIP_FEET_SLOT, // SPRITE_SHOE + Equipment::EQUIP_LEGS_SLOT, // SPRITE_BOTTOMCLOTHES + Equipment::EQUIP_TORSO_SLOT, // SPRITE_TOPCLOTHES + Equipment::EQUIP_PROJECTILE_SLOT, // 0 + Equipment::EQUIP_PROJECTILE_SLOT, // 0 + Equipment::EQUIP_PROJECTILE_SLOT, // 0 + Equipment::EQUIP_HEAD_SLOT, // SPRITE_HAT + Equipment::EQUIP_PROJECTILE_SLOT, // 0 + Equipment::EQUIP_GLOVES_SLOT, // SPRITE_GLOVES + Equipment::EQUIP_FIGHT1_SLOT, // SPRITE_WEAPON + Equipment::EQUIP_FIGHT2_SLOT, // SPRITE_SHIELD + Equipment::EQUIP_PROJECTILE_SLOT, // 0 + Equipment::EQUIP_PROJECTILE_SLOT, // 0 + Equipment::EQUIP_PROJECTILE_SLOT, // 0 +}; + +namespace TmwAthena +{ + +int getSlot(int eAthenaSlot); + +int getSlot(int eAthenaSlot) +{ + if (eAthenaSlot == 0) + return Equipment::EQUIP_VECTOREND; + + if (eAthenaSlot & 0x8000) + return Equipment::EQUIP_PROJECTILE_SLOT; + + int mask = 1; + int position = 0; + while (!(eAthenaSlot & mask)) + { + mask <<= 1; + position++; + } + return EQUIP_POINTS[position]; +} + +enum +{ + debugInventory = 1 +}; + +InventoryHandler::InventoryHandler() +{ + static const Uint16 _messages[] = + { + SMSG_PLAYER_INVENTORY, + SMSG_PLAYER_INVENTORY_ADD, + SMSG_PLAYER_INVENTORY_REMOVE, + SMSG_PLAYER_INVENTORY_USE, + SMSG_ITEM_USE_RESPONSE, + SMSG_PLAYER_STORAGE_ITEMS, + SMSG_PLAYER_STORAGE_EQUIP, + SMSG_PLAYER_STORAGE_STATUS, + SMSG_PLAYER_STORAGE_ADD, + SMSG_PLAYER_STORAGE_REMOVE, + SMSG_PLAYER_STORAGE_CLOSE, + SMSG_PLAYER_EQUIPMENT, + SMSG_PLAYER_EQUIP, + SMSG_PLAYER_UNEQUIP, + SMSG_PLAYER_ARROW_EQUIP, + SMSG_PLAYER_ATTACK_RANGE, + 0 + }; + handledMessages = _messages; + inventoryHandler = this; + + mStorage = 0; + mStorageWindow = 0; +} + +InventoryHandler::~InventoryHandler() +{ + if (mStorageWindow) + { + mStorageWindow->close(); + mStorageWindow = 0; + } + + delete mStorage; + mStorage = 0; +} + +void InventoryHandler::handleMessage(Net::MessageIn &msg) +{ + int number, flag; + int index, amount, itemId, equipType, arrow, refine; + int identified, cards[4], itemType; + Inventory *inventory = 0; + if (player_node) + inventory = PlayerInfo::getInventory(); + + switch (msg.getId()) + { + case SMSG_PLAYER_INVENTORY: + case SMSG_PLAYER_STORAGE_ITEMS: + if (msg.getId() == SMSG_PLAYER_INVENTORY) + { + // Clear inventory - this will be a complete refresh + mEquips.clear(); + PlayerInfo::getEquipment()->setBackend(&mEquips); + + if (inventory) + inventory->clear(); + } + else + { + mInventoryItems.clear(); + } + + msg.readInt16(); // length + number = (msg.getLength() - 4) / 18; + + for (int loop = 0; loop < number; loop++) + { + index = msg.readInt16(); + itemId = msg.readInt16(); + itemType = msg.readInt8(); + identified = msg.readInt8(); + amount = msg.readInt16(); + arrow = msg.readInt16(); + for (int i = 0; i < 4; i++) + cards[i] = msg.readInt16(); + + index -= (msg.getId() == SMSG_PLAYER_INVENTORY) ? + INVENTORY_OFFSET : STORAGE_OFFSET; + + if (debugInventory) + { + logger->log("Index: %d, ID: %d, Type: %d, Identified: %d, " + "Qty: %d, Cards: %d, %d, %d, %d", + index, itemId, itemType, identified, amount, + cards[0], cards[1], cards[2], cards[3]); + } + + if (msg.getId() == SMSG_PLAYER_INVENTORY) + { + // Trick because arrows are not considered equipment + bool isEquipment = arrow & 0x8000; + + if (inventory) + { + inventory->setItem(index, itemId, amount, + 0, isEquipment); + } + } + else + { + mInventoryItems.push_back(InventoryItem(index, itemId, + amount, 0, false)); + } + } + break; + + case SMSG_PLAYER_STORAGE_EQUIP: + msg.readInt16(); // length + number = (msg.getLength() - 4) / 20; + + for (int loop = 0; loop < number; loop++) + { + index = msg.readInt16() - STORAGE_OFFSET; + itemId = msg.readInt16(); + itemType = msg.readInt8(); + identified = msg.readInt8(); + amount = 1; + msg.readInt16(); // Equip Point? + msg.readInt16(); // Another Equip Point? + msg.readInt8(); // Attribute (broken) + refine = msg.readInt8(); + for (int i = 0; i < 4; i++) + cards[i] = msg.readInt16(); + + if (debugInventory) + { + logger->log("Index: %d, ID: %d, Type: %d, Identified: %d, " + "Qty: %d, Cards: %d, %d, %d, %d, Refine: %d", + index, itemId, itemType, identified, amount, + cards[0], cards[1], cards[2], cards[3], + refine); + } + + mInventoryItems.push_back(InventoryItem(index, itemId, amount, + refine, false)); + } + break; + + case SMSG_PLAYER_INVENTORY_ADD: + index = msg.readInt16() - INVENTORY_OFFSET; + amount = msg.readInt16(); + itemId = msg.readInt16(); + identified = msg.readInt8(); + msg.readInt8(); // attribute + refine = msg.readInt8(); + for (int i = 0; i < 4; i++) + cards[i] = msg.readInt16(); + equipType = msg.readInt16(); + itemType = msg.readInt8(); + + { + const ItemInfo &itemInfo = ItemDB::get(itemId); + + if (msg.readInt8() > 0) + { + if (player_node) + player_node->pickedUp(itemInfo, 0); + } + else + { + if (player_node) + player_node->pickedUp(itemInfo, amount); + + if (inventory) + { + Item *item = inventory->getItem(index); + + if (item && item->getId() == itemId) + amount += inventory->getItem(index)->getQuantity(); + + inventory->setItem(index, itemId, amount, refine, + equipType != 0); + } + } + } break; + + case SMSG_PLAYER_INVENTORY_REMOVE: + index = msg.readInt16() - INVENTORY_OFFSET; + amount = msg.readInt16(); + if (inventory) + { + if (Item *item = inventory->getItem(index)) + { + item->increaseQuantity(-amount); + if (item->getQuantity() == 0) + inventory->removeItemAt(index); + } + } + break; + + case SMSG_PLAYER_INVENTORY_USE: + index = msg.readInt16() - INVENTORY_OFFSET; + msg.readInt16(); // item id + msg.readInt32(); // id + amount = msg.readInt16(); + msg.readInt8(); // type + + if (inventory) + { + if (Item *item = inventory->getItem(index)) + { + if (amount) + item->setQuantity(amount); + else + inventory->removeItemAt(index); + } + } + break; + + case SMSG_ITEM_USE_RESPONSE: + index = msg.readInt16() - INVENTORY_OFFSET; + amount = msg.readInt16(); + + if (msg.readInt8() == 0) + { + SERVER_NOTICE(_("Failed to use item.")) + } + else + { + if (inventory) + { + if (Item *item = inventory->getItem(index)) + { + if (amount) + item->setQuantity(amount); + else + inventory->removeItemAt(index); + } + } + } + break; + + case SMSG_PLAYER_STORAGE_STATUS: + /* + * This is the closest we get to an "Open Storage" packet from the + * server. It always comes after the two SMSG_PLAYER_STORAGE_... + * packets that update storage contents. + */ + { + msg.readInt16(); // Used count + int size = msg.readInt16(); // Max size + + if (!mStorage) + mStorage = new Inventory(Inventory::STORAGE, size); + + InventoryItems::iterator it = mInventoryItems.begin(); + InventoryItems::iterator it_end = mInventoryItems.end(); + for (; it != it_end; it++) + mStorage->setItem((*it).slot, (*it).id, (*it).quantity, + (*it).equip); + mInventoryItems.clear(); + + if (!mStorageWindow) + mStorageWindow = new InventoryWindow(mStorage); + } + break; + + case SMSG_PLAYER_STORAGE_ADD: + // Move an item into storage + index = msg.readInt16() - STORAGE_OFFSET; + amount = msg.readInt32(); + itemId = msg.readInt16(); + identified = msg.readInt8(); + msg.readInt8(); // attribute + refine = msg.readInt8(); + for (int i = 0; i < 4; i++) + cards[i] = msg.readInt16(); + + if (Item *item = mStorage->getItem(index)) + { + item->setId(itemId); + item->increaseQuantity(amount); + } + else + { + if (mStorage) + mStorage->setItem(index, itemId, amount, false); + } + break; + + case SMSG_PLAYER_STORAGE_REMOVE: + // Move an item out of storage + index = msg.readInt16() - STORAGE_OFFSET; + amount = msg.readInt16(); + if (mStorage) + { + if (Item *item = mStorage->getItem(index)) + { + item->increaseQuantity(-amount); + if (item->getQuantity() == 0) + mStorage->removeItemAt(index); + } + } + break; + + case SMSG_PLAYER_STORAGE_CLOSE: + // Storage access has been closed + + // Storage window deletes itself + mStorageWindow = 0; + + if (mStorage) + mStorage->clear(); + + delete mStorage; + mStorage = 0; + break; + + case SMSG_PLAYER_EQUIPMENT: + msg.readInt16(); // length + number = (msg.getLength() - 4) / 20; + + for (int loop = 0; loop < number; loop++) + { + index = msg.readInt16() - INVENTORY_OFFSET; + itemId = msg.readInt16(); + msg.readInt8(); // type + msg.readInt8(); // identify flag + msg.readInt16(); // equip type + equipType = msg.readInt16(); + msg.readInt8(); // attribute + refine = msg.readInt8(); + msg.skip(8); // card + + if (inventory) + inventory->setItem(index, itemId, 1, refine, true); + + if (equipType) + mEquips.setEquipment(getSlot(equipType), index); + } + break; + + case SMSG_PLAYER_EQUIP: + index = msg.readInt16() - INVENTORY_OFFSET; + equipType = msg.readInt16(); + flag = msg.readInt8(); + + if (!flag) + SERVER_NOTICE(_("Unable to equip.")) + else + mEquips.setEquipment(getSlot(equipType), index); + break; + + case SMSG_PLAYER_UNEQUIP: + index = msg.readInt16() - INVENTORY_OFFSET; + equipType = msg.readInt16(); + flag = msg.readInt8(); + + if (flag) + mEquips.setEquipment(getSlot(equipType), -1); + break; + + case SMSG_PLAYER_ATTACK_RANGE: + { + int range = msg.readInt16(); + if (player_node) + player_node->setAttackRange(range); + PlayerInfo::setStatBase(ATTACK_RANGE, range); + PlayerInfo::setStatMod(ATTACK_RANGE, 0); + break; + } + + case SMSG_PLAYER_ARROW_EQUIP: + index = msg.readInt16(); + + if (index <= 1) + break; + + index -= INVENTORY_OFFSET; + + logger->log("Arrows equipped: %i", index); + mEquips.setEquipment(Equipment::EQUIP_PROJECTILE_SLOT, index); + break; + + default: + break; + } +} + +void InventoryHandler::equipItem(const Item *item) +{ + if (!item) + return; + + MessageOut outMsg(CMSG_PLAYER_EQUIP); + outMsg.writeInt16(static_cast( + item->getInvIndex() + INVENTORY_OFFSET)); + outMsg.writeInt16(0); +} + +void InventoryHandler::unequipItem(const Item *item) +{ + if (!item) + return; + + MessageOut outMsg(CMSG_PLAYER_UNEQUIP); + outMsg.writeInt16(static_cast( + item->getInvIndex() + INVENTORY_OFFSET)); +} + +void InventoryHandler::useItem(const Item *item) +{ + if (!item) + return; + + MessageOut outMsg(CMSG_PLAYER_INVENTORY_USE); + outMsg.writeInt16(static_cast( + item->getInvIndex() + INVENTORY_OFFSET)); + outMsg.writeInt32(item->getId()); // unused +} + +void InventoryHandler::dropItem(const Item *item, int amount) +{ + if (!item) + return; + + // TODO: Fix wrong coordinates of drops, serverside? (what's wrong here?) + MessageOut outMsg(CMSG_PLAYER_INVENTORY_DROP); + outMsg.writeInt16(static_cast( + item->getInvIndex() + INVENTORY_OFFSET)); + outMsg.writeInt16(static_cast(amount)); +} + +bool InventoryHandler::canSplit(const Item *item _UNUSED_) +{ + return false; +} + +void InventoryHandler::splitItem(const Item *item _UNUSED_, + int amount _UNUSED_) +{ + // Not implemented for eAthena (possible?) +} + +void InventoryHandler::moveItem(int oldIndex _UNUSED_, int newIndex _UNUSED_) +{ + // Not implemented for eAthena (possible?) +} + +void InventoryHandler::openStorage(int type _UNUSED_) +{ + // Doesn't apply to eAthena, since opening happens through NPCs? +} + +void InventoryHandler::closeStorage(int type _UNUSED_) +{ + MessageOut outMsg(CMSG_CLOSE_STORAGE); +} + +void InventoryHandler::moveItem(int source, int slot, int amount, + int destination) +{ + if (source == Inventory::INVENTORY && destination == Inventory::STORAGE) + { + MessageOut outMsg(CMSG_MOVE_TO_STORAGE); + outMsg.writeInt16(slot + INVENTORY_OFFSET); + outMsg.writeInt32(amount); + } + else if (source == Inventory::STORAGE + && destination == Inventory::INVENTORY) + { + MessageOut outMsg(CSMG_MOVE_FROM_STORAGE); + outMsg.writeInt16(slot + STORAGE_OFFSET); + outMsg.writeInt32(amount); + } +} + +size_t InventoryHandler::getSize(int type) const +{ + switch (type) + { + case Inventory::INVENTORY: + return 100; + case Inventory::STORAGE: + return 0; // Comes from server after items + case Inventory::TRADE: + return 12; + case GUILD_STORAGE: + return 0; // Comes from server after items + default: + return 0; + } +} +int InventoryHandler::convertFromServerSlot(int serverSlot) +{ + if (serverSlot < 0 || serverSlot > 13) + return 0; + + return EQUIP_CONVERT[serverSlot]; +} +} // namespace TmwAthena -- cgit v1.2.3-70-g09d2