From bbdbb95d5d477b9934bf27cb45c53afbabb2db78 Mon Sep 17 00:00:00 2001 From: Guillaume Melquiond Date: Thu, 4 Jan 2007 17:36:52 +0000 Subject: Started serializing character possessions. Reduced their memory footprint. --- ChangeLog | 17 ++ src/Makefile.am | 2 + src/account-server/account.hpp | 12 + src/account-server/serverhandler.cpp | 25 +- src/account-server/serverhandler.hpp | 6 + src/defines.h | 6 +- src/game-server/accountconnection.cpp | 29 +-- src/game-server/gamehandler.cpp | 11 +- src/game-server/inventory.cpp | 417 +++++++++++++--------------------- src/game-server/inventory.hpp | 159 +++---------- src/game-server/player.cpp | 2 + src/game-server/player.hpp | 42 ---- src/playerdata.cpp | 89 ++++++++ src/playerdata.hpp | 67 ++++-- 14 files changed, 391 insertions(+), 493 deletions(-) create mode 100644 src/playerdata.cpp diff --git a/ChangeLog b/ChangeLog index 1cdf3b18..745ac098 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,20 @@ +2007-01-04 Guillaume Melquiond + + * src/playerdata.hpp, src/playerdata.cpp, src/defines.h, + src/Makefile.am: Factored serialization of PlayerData into a new file. + Added inventory to PlayerData. Removed counted pointers. + * src/account-server/serverhandler.hpp, src/account-server/account.hpp: + Moved counted pointers here. + * src/account-server/serverhandler.cpp, + src/game-server/accountconnection.cpp: Simplified by relying on the + serialization functionality of PlayerData. + * src/game-server/inventory.hpp, src/game-server/inventory.cpp: + Transformed Inventory into a strict helper class, as data are now stored + inside PlayerData. Reduced memory footprint of inventory by avoiding + storing empty slots. + * src/game-server/player.hpp, src/game-server/player.cpp, + src/game-server/gamehandler.cpp: Updated accordingly. + 2007-01-04 Eugenio Favalli * accountserver.cbp, gameserver.cbp, diff --git a/src/Makefile.am b/src/Makefile.am index 8f6f2c56..61901420 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -10,6 +10,7 @@ tmwserv_account_SOURCES = \ configuration.cpp \ defines.h \ playerdata.hpp \ + playerdata.cpp \ point.h \ resourcemanager.h \ resourcemanager.cpp \ @@ -69,6 +70,7 @@ tmwserv_game_SOURCES = \ controller.cpp \ defines.h \ playerdata.hpp \ + playerdata.cpp \ point.h \ resourcemanager.h \ resourcemanager.cpp \ diff --git a/src/account-server/account.hpp b/src/account-server/account.hpp index a97158a7..e717e118 100644 --- a/src/account-server/account.hpp +++ b/src/account-server/account.hpp @@ -27,6 +27,18 @@ #include "defines.h" #include "playerdata.hpp" +#include "utils/countedptr.h" + +/** + * Type definition for a smart pointer to PlayerData. + */ +typedef utils::CountedPtr< PlayerData > PlayerPtr; + +/** + * Type definition for a list of Players. + */ +typedef std::vector< PlayerPtr > Players; + /** * Notes: diff --git a/src/account-server/serverhandler.cpp b/src/account-server/serverhandler.cpp index 2a1af5ee..b7e51bce 100644 --- a/src/account-server/serverhandler.cpp +++ b/src/account-server/serverhandler.cpp @@ -75,18 +75,8 @@ void ServerHandler::registerGameClient(std::string const &token, PlayerPtr ptr) MessageOut msg(AGMSG_PLAYER_ENTER); msg.writeLong(ptr->getDatabaseID()); msg.writeString(ptr->getName()); - msg.writeByte(ptr->getGender()); - msg.writeByte(ptr->getHairStyle()); - msg.writeByte(ptr->getHairColor()); - msg.writeByte(ptr->getLevel()); - msg.writeShort(ptr->getMoney()); - for (int j = 0; j < NB_RSTAT; ++j) - msg.writeShort(ptr->getRawStat(j)); - Point pos = ptr->getPos(); - msg.writeShort(pos.x); - msg.writeShort(pos.y); - msg.writeShort(mapId); msg.writeString(token, 32); + ptr->serialize(msg); Servers::const_iterator i = servers.find(mapId); assert(i != servers.end()); i->second.server->send(msg); @@ -130,18 +120,7 @@ void ServerHandler::processMessage(NetComputer *comp, MessageIn &msg) int id = msg.readLong(); Storage &store = Storage::instance("tmw"); PlayerPtr ptr = store.getCharacter(id); - ptr->setGender(msg.readByte()); - ptr->setHairStyle(msg.readByte()); - ptr->setHairColor(msg.readByte()); - ptr->setLevel(msg.readByte()); - ptr->setMoney(msg.readShort()); - for (int j = 0; j < NB_RSTAT; ++j) - ptr->setRawStat(j, msg.readShort()); - int x = msg.readShort(); - int y = msg.readShort(); - Point pos = { x, y }; - ptr->setPos(pos); - ptr->setMap(msg.readShort()); + ptr->deserialize(msg); } break; case GAMSG_REDIRECT: diff --git a/src/account-server/serverhandler.hpp b/src/account-server/serverhandler.hpp index 1325b065..30dadd65 100644 --- a/src/account-server/serverhandler.hpp +++ b/src/account-server/serverhandler.hpp @@ -28,6 +28,12 @@ #include "playerdata.hpp" #include "net/connectionhandler.hpp" +#include "utils/countedptr.h" + +/** + * Type definition for a smart pointer to PlayerData. + */ +typedef utils::CountedPtr< PlayerData > PlayerPtr; /** * Manages communications with all the game servers. This class also keeps diff --git a/src/defines.h b/src/defines.h index 940f9322..6cceeaa8 100644 --- a/src/defines.h +++ b/src/defines.h @@ -178,10 +178,8 @@ enum { // Inter-server GAMSG_REGISTER = 0x500, // S address, W port, { W map id }* AGMSG_ACTIVE_MAP = 0x501, // W map id - AGMSG_PLAYER_ENTER = 0x510, // L id, S name, B gender, B hair style, B hair color, B level, W money, - // W*6 stats, W x, W y, W map id, B*32 token - GAMSG_PLAYER_DATA = 0x520, // L id, B gender, B hair style, B hair color, B level, W money, - // W*6 stats, W x, W y, W map id + AGMSG_PLAYER_ENTER = 0x510, // L id, S name, B*32 token, B* player data + GAMSG_PLAYER_DATA = 0x520, // L id, B* player data GAMSG_REDIRECT = 0x530, // L id AGMSG_REDIRECT_RESPONSE = 0x531, // L id, B*32 token, S game address, W game port diff --git a/src/game-server/accountconnection.cpp b/src/game-server/accountconnection.cpp index d536abfc..68a1430f 100644 --- a/src/game-server/accountconnection.cpp +++ b/src/game-server/accountconnection.cpp @@ -57,17 +57,7 @@ void AccountConnection::sendPlayerData(PlayerData *p) { MessageOut msg(GAMSG_PLAYER_DATA); msg.writeLong(p->getDatabaseID()); - msg.writeByte(p->getGender()); - msg.writeByte(p->getHairStyle()); - msg.writeByte(p->getHairColor()); - msg.writeByte(p->getLevel()); - msg.writeShort(p->getMoney()); - for (int j = 0; j < NB_RSTAT; ++j) - msg.writeShort(p->getRawStat(j)); - Point pos = p->getPos(); - msg.writeShort(pos.x); - msg.writeShort(pos.y); - msg.writeShort(p->getMap()); + p->serialize(msg); send(msg); } @@ -79,21 +69,12 @@ void AccountConnection::processMessage(MessageIn &msg) { int id = msg.readLong(); std::string name = msg.readString(); + std::string token = msg.readString(32); Player *ptr = new Player(name, id); - ptr->setGender(msg.readByte()); - ptr->setHairStyle(msg.readByte()); - ptr->setHairColor(msg.readByte()); - ptr->setLevel(msg.readByte()); - ptr->setMoney(msg.readShort()); - for (int j = 0; j < NB_RSTAT; ++j) - ptr->setRawStat(j, msg.readShort()); - int x = msg.readShort(); - int y = msg.readShort(); - Point pos = { x, y }; - ptr->setPosition(pos); - ptr->setMapId(msg.readShort()); + ptr->deserialize(msg); + ptr->setMapId(ptr->getMap()); + ptr->setPosition(ptr->getPos()); ptr->setSpeed(150); // TODO - std::string token = msg.readString(32); registerGameClient(token, ptr); } break; diff --git a/src/game-server/gamehandler.cpp b/src/game-server/gamehandler.cpp index 08e043f1..7478e8d1 100644 --- a/src/game-server/gamehandler.cpp +++ b/src/game-server/gamehandler.cpp @@ -233,6 +233,7 @@ void GameHandler::processMessage(NetComputer *comp, MessageIn &message) case PGMSG_PICKUP: { + /* // add item to inventory (this is too simplistic atm) int itemId = message.readLong(); @@ -242,10 +243,12 @@ void GameHandler::processMessage(NetComputer *comp, MessageIn &message) computer.character->insertItem(itemId, 1); result.writeShort(GPMSG_PICKUP_RESPONSE); result.writeByte(ERRMSG_OK); + */ } break; case PGMSG_USE_ITEM: { + /* int itemId = message.readLong(); result.writeShort(GPMSG_USE_RESPONSE); @@ -258,6 +261,7 @@ void GameHandler::processMessage(NetComputer *comp, MessageIn &message) } else { result.writeByte(ERRMSG_FAILURE); } + */ } break; case PGMSG_WALK: @@ -272,12 +276,9 @@ void GameHandler::processMessage(NetComputer *comp, MessageIn &message) case PGMSG_EQUIP: { - message.readLong(); // ItemId: Not useful, the inventory knows it int slot = message.readByte(); - - result.writeShort(GPMSG_EQUIP_RESPONSE); - result.writeByte(computer.character->equip(slot) ? - ERRMSG_OK : ERRMSG_FAILURE); + result.writeShort(GPMSG_EQUIP_RESPONSE); // TODO: something else + Inventory(computer.character, result).equip(slot); } break; case PGMSG_ATTACK: diff --git a/src/game-server/inventory.cpp b/src/game-server/inventory.cpp index c5913e15..4807d4ff 100644 --- a/src/game-server/inventory.cpp +++ b/src/game-server/inventory.cpp @@ -21,255 +21,217 @@ * $Id$ */ +#include #include #include "game-server/inventory.hpp" #include "game-server/itemmanager.hpp" -// --------- -// Items -// --------- -int Inventory::getInventoryFreeSlot() +int Inventory::getItem(int slot) { - for (int a = 0; a < MAX_ITEMS_IN_INVENTORY; a++) + for (std::vector< InventoryItem >::iterator i = poss.inventory.begin(), + i_end = poss.inventory.end(); i != i_end; ++i) { - if (itemList[a].amount == 0) - return a; - } - return INVENTORY_FULL; -} + if (slot == 0) + { + return i->itemId; + } -int Inventory::getSlotFromId(int itemId) -{ - for (int a = 0; a < MAX_ITEMS_IN_INVENTORY; a++) - { - if (itemList[a].itemId == itemId) - return a; + slot -= i->itemId ? 1 : i->amount; + + if (slot < 0) + { + return 0; + } } - return INVENTORY_FULL; + return 0; } -bool Inventory::hasItem(int itemId, - bool searchInInventory, - bool searchInEquipment) +int Inventory::getIndex(int slot) { - bool hasItem = false; - // Search in inventory - if (searchInInventory) + int index = 0; + for (std::vector< InventoryItem >::iterator i = poss.inventory.begin(), + i_end = poss.inventory.end(); i != i_end; ++i, ++index) { - std::vector::iterator iter = itemList.begin(); - for (iter = itemList.begin(); iter != itemList.end(); ++iter) + if (slot == 0) { - if (iter->itemId == itemId) - hasItem = true; + return index; } - } - // Search in equipment - if (searchInEquipment) - { - std::vector::iterator equipIter = equippedItemList.begin(); - for (equipIter = equippedItemList.begin(); - equipIter != equippedItemList.end(); - ++equipIter) + + slot -= i->itemId ? 1 : i->amount; + + if (slot < 0) { - if (equipIter->itemId == itemId) - hasItem = true; + return -1; } - if (equippedProjectiles.itemId == itemId) - hasItem = true; } - return hasItem; + return -1; } -int Inventory::insertItem(int itemId, int amount) +int Inventory::fillFreeSlot(int itemId, int amount, int maxPerSlot) { - if (amount <= 0) + int slot = 0; + for (int i = 0, i_end = poss.inventory.size(); i < i_end; ++i) { - return 0; - } - - // We get the max number of item we can have in the same slot - // for the given item. - int maxPerSlot = itemManager->getItem(itemId)->getMaxPerSlot(); - // We'll add items in slots with the item type and in free slots - // until it's all done or until the inventory will be all parsed. - // Searching for items with the same Id in the inventory, before - // seeking a free slot. - int amountToAdd = amount; - - // Parsing inventory - for (std::vector< StoredItem >::iterator iter = itemList.begin(), - iter_end = itemList.end(); iter != iter_end; ++iter) - { - int currentAmountInSlot = iter->amount; - // If a slot has got place for some more of such an item. - if (iter->itemId == itemId && currentAmountInSlot < maxPerSlot) + InventoryItem &it = poss.inventory[i]; + if (it.itemId == 0) { - // If there isn't enough space to put every item in the slot. - // We add the difference. - int possibleAmount = maxPerSlot - currentAmountInSlot; - assert(possibleAmount > 0); - if (possibleAmount < amountToAdd) - { - iter->amount += possibleAmount; - amountToAdd -= possibleAmount; - } - else // there is enough to add everything. + int nb = std::min(amount, maxPerSlot); + if (it.amount <= 1) { - iter->amount += amountToAdd; - amountToAdd = 0; // Ok! - break; + it.itemId = itemId; + it.amount = nb; } - } - // Or if there is an empty slot. - else if (iter->amount == 0) - { - // We add the item in the new slot (setting also the Id.) - iter->itemId = itemId; - if (maxPerSlot < amountToAdd) + else { - iter->amount = maxPerSlot; - amountToAdd -= maxPerSlot; + --it.amount; + InventoryItem iu = { itemId, nb }; + poss.inventory.insert(poss.inventory.begin() + i, iu); + ++i_end; } - else + + // TODO: fill msg + amount -= nb; + if (amount == 0) { - iter->amount += amountToAdd; - amountToAdd = 0; // Ok! - break; + return 0; } } + ++slot; } - return amount - amountToAdd; -} - -int Inventory::removeItemById(int itemId, int amount) -{ - if (amount <= 0) + while (slot < INVENTORY_SLOTS - 1 && amount > 0) { - return 0; + int nb = std::min(amount, maxPerSlot); + amount -= nb; + InventoryItem it = { itemId, nb }; + poss.inventory.push_back(it); + ++slot; + // TODO: fill msg } - // We'll remove items in slots with the item type - // until it's all done or until the inventory will be all parsed. - // Searching for items with the same Id in the inventory - int amountToRemove = amount; + return amount; +} - // Parsing inventory - for (std::vector< StoredItem >::iterator iter = itemList.begin(), - iter_end = itemList.end(); iter != iter_end; ++iter) +int Inventory::insert(int itemId, int amount) +{ + int maxPerSlot = itemManager->getItem(itemId)->getMaxPerSlot(); + + for (std::vector< InventoryItem >::iterator i = poss.inventory.begin(), + i_end = poss.inventory.end(); i != i_end; ++i) { - int currentAmountInSlot = iter->amount; - // If a slot has got such an item, remove it - if (iter->itemId == itemId && currentAmountInSlot > 0) + if (i->itemId == itemId) { - // If there isn't enough to finish, we remove the difference - // Emptying the slot, so we clean also the itemId. - if (currentAmountInSlot <= amountToRemove) + int nb = std::min(maxPerSlot - i->amount, amount); + i->amount += nb; + amount -= nb; + // TODO: fill msg + if (amount == 0) { - amountToRemove -= currentAmountInSlot; - iter->amount = 0; - iter->itemId = 0; - } - else // there is enough to remove everything. - { - iter->amount -= amountToRemove; - amountToRemove = 0; // Ok! - break; + return 0; } } } - return amount - amountToRemove; + + return amount > 0 ? fillFreeSlot(itemId, amount, maxPerSlot) : 0; } -int Inventory::removeItemBySlot(int slot, int amount) +void Inventory::freeIndex(int i) { - if (amount <= 0) + InventoryItem &it = poss.inventory[i]; + + if (i == (int)poss.inventory.size() - 1) { - return 0; + poss.inventory.pop_back(); } - - if (itemList[slot].amount < amount) + else if (poss.inventory[i + 1].itemId == 0) { - int value = itemList[slot].amount; - itemList[slot].amount = 0; - return value; + it.itemId = 0; + it.amount = poss.inventory[i + 1].amount + 1; + poss.inventory.erase(poss.inventory.begin() + i + 1); } else { - itemList[slot].amount -= amount; - return amount; + it.itemId = 0; + it.amount = 1; + } + + if (i > 0 && poss.inventory[i - 1].itemId == 0) + { + // Note: "it" is no longer a valid iterator. + poss.inventory[i - 1].amount += poss.inventory[i].amount; + poss.inventory.erase(poss.inventory.begin() + i); + } +} + +int Inventory::remove(int itemId, int amount) +{ + for (int i = poss.inventory.size() - 1; i >= 0; --i) + { + InventoryItem &it = poss.inventory[i]; + if (it.itemId == itemId) + { + int nb = std::min((int)it.amount, amount); + it.amount -= nb; + amount -= nb; + // TODO: fill msg + + // If the slot is empty, compress the inventory. + if (it.amount == 0) + { + freeIndex(i); + } + + if (amount == 0) + { + return 0; + } + } } + + return amount; } -// --------- -// Equipment -// --------- -int Inventory::equipItem(int itemId) +bool Inventory::equip(int slot) { - // First, we look for the item in the player's inventory. - if (!hasItem(itemId, true, false)) + int itemId = getItem(slot); + if (!itemId) + { return false; + } int availableSlots = 0, firstSlot = 0, secondSlot = 0; - int itemType = itemManager->getItem(itemId)->getType(); - switch (itemType) + switch (itemManager->getItem(itemId)->getType()) { case ITEM_EQUIPMENT_TWO_HANDS_WEAPON: - // Special case 1, the two one-handed weapons are to be placed back - // in the inventory, if there are any. - if (equippedItemList[EQUIP_FIGHT1_SLOT].itemId > 0) - { // Slot 1 full - // old two-handed weapon case: - if (equippedItemList[EQUIP_FIGHT1_SLOT].itemType - == ITEM_EQUIPMENT_TWO_HANDS_WEAPON) + // Special case 1, the two one-handed weapons are to be placed back + // in the inventory, if there are any. + if (int id = poss.equipment[EQUIP_FIGHT1_SLOT]) + { + // Slot 1 full + if (!insert(id, 1)) { - equippedItemList[EQUIP_FIGHT2_SLOT].itemId = 0; - equippedItemList[EQUIP_FIGHT2_SLOT].itemType = 0; + return false; } - - if (unequipItem_(equippedItemList[EQUIP_FIGHT1_SLOT].itemId, - EQUIP_FIGHT1_SLOT) == INVENTORY_FULL) - return INVENTORY_FULL; - } - if (equippedItemList[EQUIP_FIGHT2_SLOT].itemId > 0) - { // Slot 2 full - if (unequipItem_(equippedItemList[EQUIP_FIGHT2_SLOT].itemId, - EQUIP_FIGHT2_SLOT) == INVENTORY_FULL) - return INVENTORY_FULL; - } - // Only the slot 1 needs to be updated. - return equipItem_(itemId, itemType, EQUIP_FIGHT1_SLOT); - break; - - case ITEM_EQUIPMENT_PROJECTILE: - // Special case 2: the projectile is a Stored Item structure, - // but is still considered as part of the equipment. - // Case 1: Reloading - if (equippedProjectiles.itemId == itemId) - { - equippedProjectiles.amount += - removeItemById(itemId, 255 - equippedProjectiles.amount); - return EQUIP_PROJECTILES_SLOT; } - else // Case 2: Changing projectiles. + if (int id = poss.equipment[EQUIP_FIGHT2_SLOT]) { - int added = insertItem(equippedProjectiles.itemId, - equippedProjectiles.amount); - if (added == equippedProjectiles.amount) - { // Ok, we can equip - equippedProjectiles.itemId = itemId; - equippedProjectiles.amount = removeItemById(itemId, 255); - return EQUIP_PROJECTILES_SLOT; - } - else // Some were unequipped. + // Slot 2 full + if (!insert(id, 1)) { - equippedProjectiles.amount -= added; - return INVENTORY_FULL; + return false; } } - break; + + poss.equipment[EQUIP_FIGHT1_SLOT] = itemId; + poss.equipment[EQUIP_FIGHT2_SLOT] = 0; + freeIndex(getIndex(slot)); + return true; + + case ITEM_EQUIPMENT_PROJECTILE: + poss.equipment[EQUIP_PROJECTILE_SLOT] = itemId; + return true; case ITEM_EQUIPMENT_ONE_HAND_WEAPON: case ITEM_EQUIPMENT_SHIELD: @@ -310,88 +272,35 @@ int Inventory::equipItem(int itemId) case ITEM_UNUSABLE: case ITEM_USABLE: default: - return NOT_EQUIPPABLE; + return false; } + int id = poss.equipment[firstSlot]; + switch (availableSlots) { - case 1: - if (equippedItemList[firstSlot].itemId > 0) - { - if (unequipItem_(equippedItemList[firstSlot].itemId, - firstSlot) != INVENTORY_FULL) - return equipItem_(itemId, itemType, firstSlot); - else - return INVENTORY_FULL; - } - else // slot empty, we can equip. - { - return equipItem_(itemId, itemType, firstSlot); - } - break; - case 2: - if (equippedItemList[firstSlot].itemId > 0) - { - // If old weapon is two-handed one, we can unequip - // the first slot only, and clean the second. - if (equippedItemList[firstSlot].itemId == + if (id && !poss.equipment[secondSlot] && + itemManager->getItem(id)->getType() != ITEM_EQUIPMENT_TWO_HANDS_WEAPON) - { - if (unequipItem_(equippedItemList[firstSlot].itemId, - firstSlot) != INVENTORY_FULL) - return equipItem_(itemId, itemType, firstSlot); - else - return INVENTORY_FULL; - } - - if (equippedItemList[secondSlot].itemId > 0) - { // Both slots are full, - // we remove the first one to equip - if (unequipItem_(equippedItemList[firstSlot].itemId, - firstSlot) != INVENTORY_FULL) - return equipItem_(itemId, itemType, firstSlot); - else - return INVENTORY_FULL; - } - else // Second slot empty, we can equip. - { - return equipItem_(itemId, itemType, secondSlot); - } + { + // The first slot is full and the second slot is empty. + poss.equipment[secondSlot] = itemId; + freeIndex(getIndex(slot)); + return true; } - else // first slot empty, we can equip. + // no break! + + case 1: + if (id && !insert(id, 1)) { - return equipItem_(itemId, itemType, firstSlot); + return false; } - break; + poss.equipment[firstSlot] = itemId; + freeIndex(getIndex(slot)); + return true; default: - return NOT_EQUIPPABLE; - } -} - -int Inventory::equipItem_(int itemId, - int itemType, - int equipmentSlot) -{ - if (removeItemById(itemId, 1) == 1) - { - equippedItemList[equipmentSlot].itemId = itemId; - equippedItemList[equipmentSlot].itemType = itemType; - return equipmentSlot; - } - else - return NO_ITEM_TO_EQUIP; -} - -int Inventory::unequipItem_(int itemId, int equipmentSlot) -{ - if (insertItem(itemId, 1) == 1) - { - equippedItemList[equipmentSlot].itemId = 0; - equippedItemList[equipmentSlot].itemType = 0; - return equipmentSlot; - } - else - return INVENTORY_FULL; + return false; + } } diff --git a/src/game-server/inventory.hpp b/src/game-server/inventory.hpp index 1fa66373..87c1010c 100644 --- a/src/game-server/inventory.hpp +++ b/src/game-server/inventory.hpp @@ -24,12 +24,10 @@ #ifndef INVENTORY_H #define INVENTORY_H -#include "game-server/being.hpp" +#include "playerdata.hpp" enum { -// items in inventory : - MAX_ITEMS_IN_INVENTORY = 50, // Max 252. // Equipment rules: // 1 Brest equipment EQUIP_BREST_SLOT = 0, @@ -47,158 +45,69 @@ enum // 1 necklace EQUIP_NECKLACE_SLOT = 7, // Fight: -// 2 one-handed weapons +// 2 one-handed weapons +// or 1 two-handed weapon +// or 1 one-handed weapon + 1 shield. EQUIP_FIGHT1_SLOT = 8, EQUIP_FIGHT2_SLOT = 9, -// or 1 two-handed weapon -// or 1 one-handed weapon + 1 shield. -// Projectiles - EQUIP_PROJECTILES_SLOT = 10, -// = 10 total slots for equipment. - TOTAL_EQUIPMENT_SLOTS = 11, +// Projectile: +// this item does not amount to one, it only indicates the chosen projectile. + EQUIP_PROJECTILE_SLOT = 10, + // Error codes NOT_EQUIPPABLE = 253, NO_ITEM_TO_EQUIP = 254, INVENTORY_FULL = 255 }; -/** - * Stored Item only contains id reference to items - * in the order not to carry every item info for each carried items - * in the inventory. - * Also contains amount. - */ -struct StoredItem -{ - int itemId; - unsigned char amount; -}; - -/** - * Equipped items that keeps which kind of item is in equipment. - */ -struct EquippedItem -{ - int itemId; - short itemType; -}; +class MessageOut; /** - * Class used to store minimal info on player's inventories - * to keep it fast. - * See Item and ItemManager to get more info on an item. + * Class used to handle Player possessions and prepare outgoing messages. */ class Inventory { + Possessions &poss; + MessageOut &msg; public: - /** - * Convenience function to get slot from ItemId. - * If more than one occurence is found, the first is given. - */ - int getSlotFromId(int itemId); + Inventory(PlayerData *p, MessageOut &m) + : poss(p->getPossessions()), msg(m) + {} /** - * Returns item. + * Equips item from given inventory slot. */ - StoredItem const &getStoredItemAt(int slot) const - { return itemList[slot]; }; + bool equip(int slot); /** - * Looks in inventory and equipment whether an item is present or not. + * Gets the ID of projectiles. Removes one of these projectiles from + * inventory. */ - bool hasItem(int itemId, - bool searchInInventory = true, - bool searchInEquipment = true); + int fireProjectile(); /** - * Tells an item's amount + * Inserts some items into the inventory. + * @return number of items not inserted (to be dropped on floor?). */ - int getItemAmount(int slot) const - { return itemList[slot].amount; }; + int insert(int itemId, int amount); /** - * Returns item reference ID. + * Removes some items from inventory. + * @return number of items not removed. */ - int getItemId(int slot) const - { return itemList[slot].itemId; }; - - /** - * Adds a given amount of items. - * @return Number of items really added. - */ - int insertItem(int itemId, int amount = 1); - - /** - * Removes an item given by ID. - * @return Number of items really removed. - */ - int removeItemById(int itemId, int amount); - - /** - * Removes an item given by slot. - * @return Number of items really removed. - */ - int removeItemBySlot(int slot, int amount); - - /** - * Equip an item searched by its id. - * Can equip more than one item at a time. - * @return unsigned char value: Returns the slot if successful - * or the error code if not. - */ - int equipItem(int itemId); - - /** - * Unequip an item searched by its id. - * Can unequip more than one item at a time. - */ - bool unequipItem(int itemId); - - /** - * Equips an item searched by its slot index. - */ - bool equipItem(int inventorySlot, int equipmentSlot); - - /** - * Unequips an equipped item searched by its slot index. - */ - bool unequipItem(int inventorySlot, int equipmentSlot); + int remove(int itemId, int amount); + int countItem(int itemId); private: - /** - * Gives the first free slot number in itemList. + * Fills some slots with items. + * @return number of items not inserted. */ - int getInventoryFreeSlot(); - - /** - * Quick equip an equipment with a given equipSlot, - * an itemId and an itemType. - * @return the equipment slot if successful, - * the error code, if not. - */ - int equipItem_(int itemId, - int itemType, - int equipmentSlot); - - /** - * Quick unequip an equipment with a given equipSlot, - * and an itemId. - * @return the Equipment slot if successful, - * the error code, if not. - */ - int unequipItem_(int itemId, - int equipmentSlot); - - - // Stored items in inventory and equipment - std::vector itemList; /**< Items in inventory */ - std::vector equippedItemList; /**< Equipped Items */ - /** - * Used to know which type of arrow is used with a bow, - * for instance - */ - StoredItem equippedProjectiles; + int fillFreeSlot(int itemId, int amount, int MaxPerSlot); + void freeIndex(int index); + int getItem(int slot); + int getIndex(int slot); }; + #endif diff --git a/src/game-server/player.cpp b/src/game-server/player.cpp index 99884661..1f6fc935 100644 --- a/src/game-server/player.cpp +++ b/src/game-server/player.cpp @@ -56,6 +56,7 @@ void Player::update() } } +/* bool Player::insertItem(int itemId, int amount) { return inventory.insertItem(itemId, amount); @@ -80,3 +81,4 @@ bool Player::unequip(int slot) { return false; // TODO } +*/ diff --git a/src/game-server/player.hpp b/src/game-server/player.hpp index eaf4d9e9..63bf06c7 100644 --- a/src/game-server/player.hpp +++ b/src/game-server/player.hpp @@ -48,47 +48,6 @@ class Player : public Being, public PlayerData */ void update(); - /** - * Sets inventory. - */ - void setInventory(Inventory const &inven) - { inventory = inven; } - - /** - * Adds item with ID to inventory. - * - * @return Item add success/failure - */ - bool insertItem(int itemId, int amount); - - /** - * Removes item with ID from inventory. - * - * @return Item delete success/failure - */ - bool removeItem(int itemId, int amount); - - /** - * Checks if character has an item. - * - * @return true if being has item, false otherwise - */ - bool hasItem(int itemId); - - /** - * Equips item with ID in equipment slot. - * - * @return Equip success/failure - */ - bool equip(int slot); - - /** - * Un-equips item. - * - * @return Un-equip success/failure - */ - bool unequip(int slot); - /** * Set attacking state **/ @@ -112,7 +71,6 @@ class Player : public Being, public PlayerData Player &operator=(Player const &); GameClient *mClient; /**< Client computer. */ - Inventory inventory; /**< Player inventory and equipment. */ bool mIsAttacking; /**< Attacking state. */ }; diff --git a/src/playerdata.cpp b/src/playerdata.cpp new file mode 100644 index 00000000..1b9760d2 --- /dev/null +++ b/src/playerdata.cpp @@ -0,0 +1,89 @@ +/* + * The Mana World Server + * Copyright 2007 The Mana World Development Team + * + * This file is part of The Mana World. + * + * The Mana World 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. + * + * The Mana World 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 The Mana World; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * $Id$ + */ + +#include "playerdata.hpp" +#include "net/messagein.hpp" +#include "net/messageout.hpp" + +PlayerData::PlayerData(std::string const &name, int id) + : mDatabaseID(id), + mName(name) +{ + for (int j = 0; j < EQUIPMENT_SLOTS; ++j) + { + mPossessions.equipment[j] = 0; + } +} + +void PlayerData::serialize(MessageOut &msg) const +{ + msg.writeByte(mGender); + msg.writeByte(mHairStyle); + msg.writeByte(mHairColor); + msg.writeByte(mLevel); + msg.writeShort(mMoney); + for (int j = 0; j < NB_RSTAT; ++j) + { + msg.writeByte(mRawStats.stats[j]); + } + msg.writeShort(mMapId); + msg.writeShort(mPos.x); + msg.writeShort(mPos.y); + for (int j = 0; j < EQUIPMENT_SLOTS; ++j) + { + msg.writeShort(mPossessions.equipment[j]); + } + for (std::vector< InventoryItem >::const_iterator j = mPossessions.inventory.begin(), + j_end = mPossessions.inventory.end(); j != j_end; ++j) + { + msg.writeShort(j->itemId); + msg.writeByte(j->amount); + } +} + +void PlayerData::deserialize(MessageIn &msg) +{ + mGender = msg.readByte(); + mHairStyle = msg.readByte(); + mHairColor = msg.readByte(); + mLevel = msg.readByte(); + mMoney = msg.readShort(); + for (int j = 0; j < NB_RSTAT; ++j) + { + mRawStats.stats[j] = msg.readByte(); + } + mMapId = msg.readShort(); + mPos.x = msg.readShort(); + mPos.y = msg.readShort(); + for (int j = 0; j < EQUIPMENT_SLOTS; ++j) + { + mPossessions.equipment[j] = msg.readShort(); + } + mPossessions.inventory.clear(); + while (msg.getUnreadLength()) + { + InventoryItem i; + i.itemId = msg.readShort(); + i.amount = msg.readByte(); + mPossessions.inventory.push_back(i); + } +} diff --git a/src/playerdata.hpp b/src/playerdata.hpp index cc4a4b46..a7fffe99 100644 --- a/src/playerdata.hpp +++ b/src/playerdata.hpp @@ -27,7 +27,9 @@ #include #include "point.h" -#include "utils/countedptr.h" + +class MessageIn; +class MessageOut; /** * Gender of a Player. @@ -53,22 +55,48 @@ enum }; /** - * Structure types for the raw statistics of a Player. + * Structure storing the raw statistics of a Player. */ struct RawStatistics { unsigned short stats[NB_RSTAT]; }; +/** + * Numbers of inventory slots + */ + +enum +{ + EQUIPMENT_SLOTS = 11, + INVENTORY_SLOTS = 50 +}; + +/** + * Structure storing an item in the inventory. + * When the itemId, it represents "amount" consecutive empty slots. + */ + +struct InventoryItem +{ + unsigned short itemId; + unsigned char amount; +}; + +/** + * Structure storing the equipment and inventory of a Player. + */ +struct Possessions +{ + unsigned short equipment[EQUIPMENT_SLOTS]; + std::vector< InventoryItem > inventory; +}; class PlayerData { public: - PlayerData(std::string const &name, int id = -1) - : mDatabaseID(id), - mName(name) - {} + PlayerData(std::string const &name, int id = -1); /** * Gets the name. @@ -221,6 +249,22 @@ class PlayerData Point const &getPos() const { return mPos; } + /** + * Gets a reference on the possession. + */ + Possessions &getPossessions() + { return mPossessions; } + + /** + * Stores data into a packet. + */ + void serialize(MessageOut &) const; + + /** + * Restores data from a packet. + */ + void deserialize(MessageIn &); + private: PlayerData(PlayerData const &); PlayerData &operator=(PlayerData const &); @@ -235,16 +279,7 @@ class PlayerData Point mPos; /**< Position the being is at. */ unsigned int mMoney; /**< Wealth of the being. */ RawStatistics mRawStats; /**< Raw statistics of the being. */ + Possessions mPossessions; /**< Possesssions of the being. */ }; -/** - * Type definition for a smart pointer to PlayerData. - */ -typedef utils::CountedPtr< PlayerData > PlayerPtr; - -/** - * Type definition for a list of Players. - */ -typedef std::vector< PlayerPtr > Players; - #endif -- cgit v1.2.3-70-g09d2