diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/account-server/character.cpp | 53 | ||||
-rw-r--r-- | src/account-server/storage.cpp | 91 | ||||
-rw-r--r-- | src/common/inventorydata.h | 38 | ||||
-rw-r--r-- | src/common/manaserv_protocol.h | 8 | ||||
-rw-r--r-- | src/game-server/character.cpp | 48 | ||||
-rw-r--r-- | src/game-server/gamehandler.cpp | 51 | ||||
-rw-r--r-- | src/game-server/gamehandler.h | 1 | ||||
-rw-r--r-- | src/game-server/inventory.cpp | 487 | ||||
-rw-r--r-- | src/game-server/inventory.h | 36 | ||||
-rw-r--r-- | src/game-server/state.cpp | 65 | ||||
-rw-r--r-- | src/scripting/lua.cpp | 46 | ||||
-rw-r--r-- | src/sql/mysql/createTables.sql | 18 | ||||
-rw-r--r-- | src/sql/mysql/updates/update_24_to_25.sql | 18 | ||||
-rw-r--r-- | src/sql/sqlite/createTables.sql | 16 | ||||
-rw-r--r-- | src/sql/sqlite/updates/update_24_to_25.sql | 47 |
15 files changed, 329 insertions, 694 deletions
diff --git a/src/account-server/character.cpp b/src/account-server/character.cpp index 47bca5da..cafc256b 100644 --- a/src/account-server/character.cpp +++ b/src/account-server/character.cpp @@ -94,25 +94,21 @@ void CharacterData::serialize(MessageOut &msg) // inventory - must be last because size isn't transmitted const Possessions &poss = getPossessions(); const EquipData &equipData = poss.getEquipment(); - msg.writeInt16(equipData.size()); // number of equipment - for (EquipData::const_iterator k = equipData.begin(), - k_end = equipData.end(); k != k_end; ++k) - { - msg.writeInt16(k->first); // Equip slot id - msg.writeInt16(k->second.itemId); // ItemId - msg.writeInt16(k->second.itemInstance); // Item Instance id - } const InventoryData &inventoryData = poss.getInventory(); - for (InventoryData::const_iterator j = inventoryData.begin(), - j_end = inventoryData.end(); j != j_end; ++j) + for (InventoryData::const_iterator itemIt = inventoryData.begin(), + itemIt_end = inventoryData.end(); itemIt != itemIt_end; ++itemIt) { - msg.writeInt16(j->first); // slot id - msg.writeInt16(j->second.itemId); // item id - msg.writeInt16(j->second.amount); // amount + int slot = itemIt->first; + msg.writeInt16(slot); + msg.writeInt16(itemIt->second.itemId); + msg.writeInt16(itemIt->second.amount); + if (equipData.find(itemIt->first) != equipData.end()) + msg.writeInt8(1); // equipped + else + msg.writeInt8(0); // not equipped } } - void CharacterData::deserialize(MessageIn &msg) { // general character properties @@ -170,33 +166,24 @@ void CharacterData::deserialize(MessageIn &msg) giveAbility(id); } - + // inventory - must be last because size isn't transmitted Possessions &poss = getPossessions(); - EquipData equipData; - int equipSlotsSize = msg.readInt16(); - unsigned equipSlot; - EquipmentItem equipItem; - for (int j = 0; j < equipSlotsSize; ++j) - { - equipSlot = msg.readInt16(); - equipItem.itemId = msg.readInt16(); - equipItem.itemInstance = msg.readInt16(); - equipData.insert(equipData.end(), - std::make_pair(equipSlot, equipItem)); - } - poss.setEquipment(equipData); - // Loads inventory - must be last because size isn't transmitted InventoryData inventoryData; + EquipData equipmentData; while (msg.getUnreadLength()) { InventoryItem i; - int slotId = msg.readInt16(); - i.itemId = msg.readInt16(); - i.amount = msg.readInt16(); - inventoryData.insert(inventoryData.end(), std::make_pair(slotId, i)); + i.slot = msg.readInt16(); + i.itemId = msg.readInt16(); + i.amount = msg.readInt16(); + i.equipmentSlot = msg.readInt8(); + inventoryData.insert(std::make_pair(i.slot, i)); + if (i.equipmentSlot != 0) + equipmentData.insert(i.slot); } poss.setInventory(inventoryData); + poss.setEquipment(equipmentData); } void CharacterData::setAccount(Account *acc) diff --git a/src/account-server/storage.cpp b/src/account-server/storage.cpp index 2ba1e911..bcfa39f8 100644 --- a/src/account-server/storage.cpp +++ b/src/account-server/storage.cpp @@ -78,7 +78,6 @@ static const char *CHAR_ATTR_TBL_NAME = "mana_char_attr"; static const char *CHAR_STATUS_EFFECTS_TBL_NAME = "mana_char_status_effects"; static const char *CHAR_KILL_COUNT_TBL_NAME = "mana_char_kill_stats"; static const char *CHAR_ABILITIES_TBL_NAME = "mana_char_abilities"; -static const char *CHAR_EQUIPS_TBL_NAME = "mana_char_equips"; static const char *INVENTORIES_TBL_NAME = "mana_inventories"; static const char *ITEMS_TBL_NAME = "mana_items"; static const char *GUILDS_TBL_NAME = "mana_guilds"; @@ -480,41 +479,12 @@ CharacterData *Storage::getCharacterBySQL(Account *owner) try { std::ostringstream sql; - sql << " select slot_type, item_id, item_instance from " - << CHAR_EQUIPS_TBL_NAME - << " where owner_id = '" - << character->getDatabaseID() << "' order by slot_type desc;"; - - EquipData equipData; - const dal::RecordSet &equipInfo = mDb->execSql(sql.str()); - if (!equipInfo.isEmpty()) - { - EquipmentItem equipItem; - for (int k = 0, size = equipInfo.rows(); k < size; ++k) - { - equipItem.itemId = toUint(equipInfo(k, 1)); - equipItem.itemInstance = toUint(equipInfo(k, 2)); - equipData.insert(std::pair<unsigned, EquipmentItem>( - toUint(equipInfo(k, 0)), - equipItem)); - } - } - poss.setEquipment(equipData); - } - catch (const dal::DbSqlQueryExecFailure &e) - { - utils::throwError("DALStorage::getCharacter #2) SQL query failure: ", - e); - } - - try - { - std::ostringstream sql; - sql << " select * from " << INVENTORIES_TBL_NAME - << " where owner_id = '" + sql << " select id, owner_id, slot, class_id, amount, equipped from " + << INVENTORIES_TBL_NAME << " where owner_id = '" << character->getDatabaseID() << "' order by slot asc;"; InventoryData inventoryData; + EquipData equipmentData; const dal::RecordSet &itemInfo = mDb->execSql(sql.str()); if (!itemInfo.isEmpty()) { @@ -525,9 +495,22 @@ CharacterData *Storage::getCharacterBySQL(Account *owner) item.itemId = toUint(itemInfo(k, 3)); item.amount = toUint(itemInfo(k, 4)); inventoryData[slot] = item; + + if (toUint(itemInfo(k, 5)) != 0) + { + // The game server will set the right slot anyway, + // but this speeds up checking if the item is equipped + item.equipmentSlot = 1; + equipmentData.insert(slot); + } + else + { + item.equipmentSlot = 0; + } } } poss.setInventory(inventoryData); + poss.setEquipment(equipmentData); } catch (const dal::DbSqlQueryExecFailure &e) { @@ -781,12 +764,6 @@ bool Storage::updateCharacter(CharacterData *character) // Delete the old inventory and equipment table first try { - std::ostringstream sqlDeleteCharacterEquipment; - sqlDeleteCharacterEquipment - << "delete from " << CHAR_EQUIPS_TBL_NAME - << " where owner_id = '" << character->getDatabaseID() << "';"; - mDb->execSql(sqlDeleteCharacterEquipment.str()); - std::ostringstream sqlDeleteCharacterInventory; sqlDeleteCharacterInventory << "delete from " << INVENTORIES_TBL_NAME @@ -804,39 +781,25 @@ bool Storage::updateCharacter(CharacterData *character) { std::ostringstream sql; - sql << "insert into " << CHAR_EQUIPS_TBL_NAME - << " (owner_id, slot_type, item_id, item_instance) values (" + sql << "insert into " << INVENTORIES_TBL_NAME + << " (owner_id, slot, class_id, amount, equipped) values (" << character->getDatabaseID() << ", "; std::string base = sql.str(); const Possessions &poss = character->getPossessions(); - const EquipData &equipData = poss.getEquipment(); - for (EquipData::const_iterator it = equipData.begin(), - it_end = equipData.end(); it != it_end; ++it) - { - sql.str(""); - sql << base << it->first << ", " << it->second.itemId - << ", " << it->second.itemInstance << ");"; - mDb->execSql(sql.str()); - } - - sql.str(""); - - sql << "insert into " << INVENTORIES_TBL_NAME - << " (owner_id, slot, class_id, amount) values (" - << character->getDatabaseID() << ", "; - base = sql.str(); - const InventoryData &inventoryData = poss.getInventory(); - for (InventoryData::const_iterator j = inventoryData.begin(), - j_end = inventoryData.end(); j != j_end; ++j) + const EquipData &equipData = poss.getEquipment(); + for (InventoryData::const_iterator itemIt = inventoryData.begin(), + j_end = inventoryData.end(); itemIt != j_end; ++itemIt) { sql.str(""); - unsigned short slot = j->first; - unsigned itemId = j->second.itemId; - unsigned amount = j->second.amount; + unsigned short slot = itemIt->first; + unsigned itemId = itemIt->second.itemId; + unsigned amount = itemIt->second.amount; + bool equipped = itemIt->second.equipmentSlot != 0; assert(itemId); - sql << base << slot << ", " << itemId << ", " << amount << ");"; + sql << base << slot << ", " << itemId << ", " << amount << ", " + << (equipped ? 1 : 0) << ");"; mDb->execSql(sql.str()); } diff --git a/src/common/inventorydata.h b/src/common/inventorydata.h index b410b3fb..5d1d9388 100644 --- a/src/common/inventorydata.h +++ b/src/common/inventorydata.h @@ -23,6 +23,7 @@ #include <vector> #include <map> +#include <set> /** * Numbers of inventory slots @@ -36,38 +37,23 @@ struct InventoryItem { InventoryItem(): - itemId(0), amount(0) + slot(0), + itemId(0), + amount(0), + equipmentSlot(0) {} + unsigned slot; unsigned itemId; unsigned amount; -}; - -struct EquipmentItem -{ - EquipmentItem(): - itemId(0), itemInstance(0) - {} - - EquipmentItem(unsigned itemId, unsigned itemInstance) - { - this->itemId = itemId; - this->itemInstance = itemInstance; - } - - // The item id taken from the item db. - unsigned itemId; - // A unique instance number used to separate items when equipping the same - // item id multiple times on possible multiple slots. - unsigned itemInstance; + unsigned equipmentSlot; /** 0 if not equipped */ }; // inventory slot id -> { item } typedef std::map< unsigned, InventoryItem > InventoryData; -// equip slot id -> { item id, item instance } -// Equipment taking up multiple equip slot ids will be referenced multiple times -typedef std::multimap< unsigned, EquipmentItem > EquipData; +// the slots which are equipped +typedef std::set<int> EquipData; /** * Structure storing the equipment and inventory of a Player. @@ -77,7 +63,7 @@ struct Possessions friend class Inventory; public: const EquipData &getEquipment() const - { return equipSlots; } + { return equipment; } const InventoryData &getInventory() const { return inventory; } @@ -86,13 +72,13 @@ public: * Should be done only at character serialization and storage load time. */ void setEquipment(EquipData &equipData) - { equipSlots.swap(equipData); } + { equipment.swap(equipData); } void setInventory(InventoryData &inventoryData) { inventory.swap(inventoryData); } private: InventoryData inventory; - EquipData equipSlots; + EquipData equipment; }; #endif diff --git a/src/common/manaserv_protocol.h b/src/common/manaserv_protocol.h index 50bfdd84..1d5c7e56 100644 --- a/src/common/manaserv_protocol.h +++ b/src/common/manaserv_protocol.h @@ -113,10 +113,12 @@ enum { PGMSG_DROP = 0x0111, // W slot, W amount PGMSG_EQUIP = 0x0112, // W inventory slot PGMSG_UNEQUIP = 0x0113, // W item Instance id - PGMSG_MOVE_ITEM = 0x0114, // W slot1, W slot2, W amount GPMSG_INVENTORY = 0x0120, // { W slot, W item id [, W amount] (if item id is nonzero) }* - GPMSG_INVENTORY_FULL = 0x0121, // W inventory slot count { W slot, W itemId, W amount }, { W equip slot, W item Id, W item Instance}* - GPMSG_EQUIP = 0x0122, // W item Id, W equip slot type count //{ W equip slot, W capacity used}*//<- When equipping, //{ W item instance, W 0}*//<- When unequipping + GPMSG_INVENTORY_FULL = 0x0121, // W inventory slot count { W slot, W itemId, W amount, W equipmentSlot } + GPMSG_EQUIP = 0x0122, // W equipped inventory slot, W slot equipmentSlot + GPMSG_EQUIP_RESPONSE = 0x0123, // B error, W slot + GPMSG_UNEQUIP = 0x0124, // W equipped inventory slot + GPMSG_UNEQUIP_RESPONE = 0x0125, // B error, W slot GPMSG_PLAYER_ATTRIBUTE_CHANGE = 0x0130, // { W attribute, D base value (in 1/256ths), D modified value (in 1/256ths)}* GPMSG_ATTRIBUTE_POINTS_STATUS = 0x0140, // W character points, W correction points PGMSG_RAISE_ATTRIBUTE = 0x0160, // W attribute diff --git a/src/game-server/character.cpp b/src/game-server/character.cpp index 0944f757..ea9228a7 100644 --- a/src/game-server/character.cpp +++ b/src/game-server/character.cpp @@ -182,31 +182,23 @@ void CharacterComponent::deserialize(Entity &entity, MessageIn &msg) Possessions &poss = getPossessions(); - EquipData equipData; - int equipSlotsSize = msg.readInt16(); - unsigned eqSlot; - EquipmentItem equipItem; - for (int j = 0; j < equipSlotsSize; ++j) - { - eqSlot = msg.readInt16(); - equipItem.itemId = msg.readInt16(); - equipItem.itemInstance = msg.readInt16(); - equipData.insert(equipData.end(), - std::make_pair(eqSlot, equipItem)); - } - poss.setEquipment(equipData); // Loads inventory - must be last because size isn't transmitted InventoryData inventoryData; + EquipData equipmentData; while (msg.getUnreadLength()) { InventoryItem i; - int slotId = msg.readInt16(); - i.itemId = msg.readInt16(); - i.amount = msg.readInt16(); - inventoryData.insert(inventoryData.end(), std::make_pair(slotId, i)); + i.slot = msg.readInt16(); + i.itemId = msg.readInt16(); + i.amount = msg.readInt16(); + bool isEquipped = msg.readInt8() != 0; + inventoryData.insert(std::make_pair(i.slot, i)); + if (isEquipped) + equipmentData.insert(i.slot); } poss.setInventory(inventoryData); + poss.setEquipment(equipmentData); } void CharacterComponent::serialize(Entity &entity, MessageOut &msg) @@ -271,22 +263,18 @@ void CharacterComponent::serialize(Entity &entity, MessageOut &msg) // inventory - must be last because size isn't transmitted const Possessions &poss = getPossessions(); const EquipData &equipData = poss.getEquipment(); - msg.writeInt16(equipData.size()); // number of equipment - for (EquipData::const_iterator k = equipData.begin(), - k_end = equipData.end(); k != k_end; ++k) - { - msg.writeInt16(k->first); // Equip slot id - msg.writeInt16(k->second.itemId); // ItemId - msg.writeInt16(k->second.itemInstance); // Item Instance id - } const InventoryData &inventoryData = poss.getInventory(); - for (InventoryData::const_iterator j = inventoryData.begin(), - j_end = inventoryData.end(); j != j_end; ++j) + for (InventoryData::const_iterator itemIt = inventoryData.begin(), + itemIt_end = inventoryData.end(); itemIt != itemIt_end; ++itemIt) { - msg.writeInt16(j->first); // slot id - msg.writeInt16(j->second.itemId); // item id - msg.writeInt16(j->second.amount); // amount + msg.writeInt16(itemIt->first); // slot id + msg.writeInt16(itemIt->second.itemId); // item id + msg.writeInt16(itemIt->second.amount); // amount + if (equipData.find(itemIt->first) != equipData.end()) + msg.writeInt8(1); // equipped + else + msg.writeInt8(0); // not equipped } } diff --git a/src/game-server/gamehandler.cpp b/src/game-server/gamehandler.cpp index 1a0ca158..fe274ace 100644 --- a/src/game-server/gamehandler.cpp +++ b/src/game-server/gamehandler.cpp @@ -249,10 +249,6 @@ void GameHandler::processMessage(NetComputer *computer, MessageIn &message) handleUnequip(client, message); break; - case PGMSG_MOVE_ITEM: - handleMoveItem(client, message); - break; - case PGMSG_USE_ABILITY_ON_BEING: handleUseAbilityOnBeing(client, message); break; @@ -628,42 +624,25 @@ void GameHandler::handleWalk(GameClient &client, MessageIn &message) void GameHandler::handleEquip(GameClient &client, MessageIn &message) { const int slot = message.readInt16(); - if (!Inventory(client.character).equip(slot)) - { - MessageOut msg(GPMSG_SAY); - msg.writeInt16(0); // From the server - msg.writeString("Unable to equip."); - client.send(msg); - } + MessageOut msg(GPMSG_EQUIP_RESPONSE); + if (Inventory(client.character).equip(slot)) + msg.writeInt8(ERRMSG_OK); + else + msg.writeInt8(ERRMSG_FAILURE); + msg.writeInt16(slot); + client.send(msg); } void GameHandler::handleUnequip(GameClient &client, MessageIn &message) { - const int itemInstance = message.readInt16(); - if (!Inventory(client.character).unequip(itemInstance)) - { - MessageOut msg(GPMSG_SAY); - msg.writeInt16(0); // From the server - msg.writeString("Unable to unequip."); - client.send(msg); - } -} - -void GameHandler::handleMoveItem(GameClient &client, MessageIn &message) -{ - const int slot1 = message.readInt16(); - const int slot2 = message.readInt16(); - const int amount = message.readInt16(); - - Inventory(client.character).move(slot1, slot2, amount); - // log transaction - std::stringstream str; - str << "User moved item " - << " from slot " << slot1 << " to slot " << slot2; - auto *characterComponent = - client.character->getComponent<CharacterComponent>(); - accountHandler->sendTransaction(characterComponent->getDatabaseID(), - TRANS_ITEM_MOVE, str.str()); + const int slot = message.readInt16(); + MessageOut msg(GPMSG_UNEQUIP_RESPONE); + if (Inventory(client.character).unequip(slot)) + msg.writeInt8(ERRMSG_OK); + else + msg.writeInt8(ERRMSG_FAILURE); + msg.writeInt16(slot); + client.send(msg); } void GameHandler::handleUseAbilityOnBeing(GameClient &client, MessageIn &message) diff --git a/src/game-server/gamehandler.h b/src/game-server/gamehandler.h index 2f44a3c9..f4995497 100644 --- a/src/game-server/gamehandler.h +++ b/src/game-server/gamehandler.h @@ -132,7 +132,6 @@ class GameHandler: public ConnectionHandler void handleEquip(GameClient &client, MessageIn &message); void handleUnequip(GameClient &client, MessageIn &message); - void handleMoveItem(GameClient &client, MessageIn &message); void handleUseAbilityOnBeing(GameClient &client, MessageIn &message); void handleUseAbilityOnPoint(GameClient &client, MessageIn &message); diff --git a/src/game-server/inventory.cpp b/src/game-server/inventory.cpp index bec5961b..2f17af10 100644 --- a/src/game-server/inventory.cpp +++ b/src/game-server/inventory.cpp @@ -48,23 +48,14 @@ void Inventory::sendFull() const MessageOut m(GPMSG_INVENTORY_FULL); m.writeInt16(mPoss->inventory.size()); - for (InventoryData::const_iterator l = mPoss->inventory.begin(), - l_end = mPoss->inventory.end(); l != l_end; ++l) + for (InventoryData::const_iterator it = mPoss->inventory.begin(), + l_end = mPoss->inventory.end(); it != l_end; ++it) { - assert(l->second.itemId); - m.writeInt16(l->first); // Slot id - m.writeInt16(l->second.itemId); - m.writeInt16(l->second.amount); - } - - for (EquipData::const_iterator k = mPoss->equipSlots.begin(), - k_end = mPoss->equipSlots.end(); - k != k_end; - ++k) - { - m.writeInt16(k->first); // Equip slot id - m.writeInt16(k->second.itemId); // Item id - m.writeInt16(k->second.itemInstance); // Item instance + assert(it->second.itemId); + m.writeInt16(it->first); // slot + m.writeInt16(it->second.itemId); + m.writeInt16(it->second.amount); + m.writeInt16(it->second.equipmentSlot); } gameHandler->sendTo(mCharacter, m); @@ -73,73 +64,54 @@ void Inventory::sendFull() const void Inventory::initialize() { /* + * Set the equipment slots + */ + for (EquipData::iterator it = mPoss->equipment.begin(), + it_end = mPoss->equipment.end(); it != it_end; ++it) + { + InventoryData::iterator itemIt = mPoss->inventory.find(*it); + const ItemEquipRequirement &equipReq = itemManager->getItem( + itemIt->second.itemId)->getItemEquipRequirement(); + itemIt->second.equipmentSlot = equipReq.equipSlotId; + } + + /* * Construct a set of item Ids to keep track of duplicate item Ids. */ std::set<unsigned> itemIds; + std::set<unsigned> equipmentIds; /* * Construct a set of itemIds to keep track of duplicate itemIds. */ - InventoryData::iterator it1; - for (it1 = mPoss->inventory.begin(); it1 != mPoss->inventory.end();) + + for (InventoryData::iterator it = mPoss->inventory.begin(), + it_end = mPoss->inventory.end(); it != it_end;) { - ItemClass *item = itemManager->getItem(it1->second.itemId); + ItemClass *item = itemManager->getItem(it->second.itemId); if (item) { // If the insertion succeeded, it's the first time we're // adding the item in the inventory. Hence, we can trigger // item presence in inventory effect. - if (itemIds.insert(it1->second.itemId).second) + if (itemIds.insert(it->second.itemId).second) item->useTrigger(mCharacter, ITT_IN_INVY); - ++it1; - } - else - { - LOG_WARN("Inventory: deleting unknown item type " - << it1->second.itemId << " from the inventory of '" - << mCharacter->getComponent<BeingComponent>()->getName() - << "'!"); - mPoss->inventory.erase(it1++); - } - } - - itemIds.clear(); - /* - * Equipment effects can be cumulative if more than one item instance - * is equipped, but we check to trigger the item presence in equipment - * effect only based on the first item instance insertion. - */ - EquipData::iterator it2; - for (it2 = mPoss->equipSlots.begin(); it2 != mPoss->equipSlots.end();) - { - ItemClass *item = itemManager->getItem(it2->second.itemId); - if (item) - { - // TODO: Check equip conditions. - // If not all needed slots are there, put the item back - // in the inventory. + if (it->second.equipmentSlot != 0 && + equipmentIds.insert(it->second.itemId).second) + { + item->useTrigger(mCharacter, ITT_EQUIP); + } + ++it; } else { LOG_WARN("Equipment: deleting unknown item id " - << it2->second.itemId << " from the equipment of '" + << it->second.itemId << " from the equipment of '" << mCharacter->getComponent<BeingComponent>()->getName() << "'!"); - mPoss->equipSlots.erase(it2++); - continue; - } - - /* - * Apply all equip triggers at first item instance insertion - */ - if (itemIds.insert(it2->second.itemInstance).second) - { - itemManager->getItem(it2->second.itemId) - ->useTrigger(mCharacter, ITT_EQUIP); + mPoss->inventory.erase(it++); } - - ++it2; } } @@ -233,32 +205,14 @@ unsigned Inventory::insert(unsigned itemId, unsigned amount) return amount; } -unsigned Inventory::count(unsigned itemId, - bool inInventory, bool inEquipment) const +unsigned Inventory::count(unsigned itemId) const { unsigned nb = 0; - if (inInventory) - { - for (InventoryData::iterator it = mPoss->inventory.begin(), - it_end = mPoss->inventory.end(); it != it_end; ++it) - if (it->second.itemId == itemId) - nb += it->second.amount; - } - - if (inEquipment) + for (InventoryData::iterator it = mPoss->inventory.begin(), + it_end = mPoss->inventory.end(); it != it_end; ++it) { - std::set<unsigned> itemInstances; - for (EquipData::iterator it = mPoss->equipSlots.begin(), - it_end = mPoss->equipSlots.end(); it != it_end; ++it) - { - if (it->second.itemId != itemId || !it->second.itemInstance) - continue; - - // If the insertion was successful, then it was the first time, - // and can be counted. - if ((itemInstances.insert(it->second.itemInstance)).second) - ++nb; - } + if (it->second.itemId == itemId) + nb += it->second.amount; } return nb; @@ -336,131 +290,6 @@ unsigned Inventory::remove(unsigned itemId, unsigned amount) return amount; } -unsigned Inventory::move(unsigned slot1, unsigned slot2, - unsigned amount) -{ - LOG_DEBUG(amount << " item(s) requested to move from: " << slot1 << " to " - << slot2 << " for character: '" - << mCharacter->getComponent<BeingComponent>()->getName() << "'."); - - if (!amount || slot1 == slot2 || slot2 >= INVENTORY_SLOTS) - return amount; - - InventoryData::iterator it1 = mPoss->inventory.find(slot1), - it2 = mPoss->inventory.find(slot2), - inv_end = mPoss->inventory.end(); - - if (it1 == inv_end) - return amount; - - MessageOut invMsg(GPMSG_INVENTORY); - - unsigned nb = std::min(amount, it1->second.amount); - if (it2 == inv_end) - { - // Slot2 does not yet exist. - mPoss->inventory[slot2].itemId = it1->second.itemId; - nb = std::min(itemManager->getItem(it1->second.itemId)->getMaxPerSlot(), - nb); - - mPoss->inventory[slot2].amount = nb; - it1->second.amount -= nb; - amount -= nb; - - //Save the itemId in case of deletion of the iterator - unsigned itemId = it1->second.itemId; - invMsg.writeInt16(slot1); // Slot - if (it1->second.amount) - { - invMsg.writeInt16(it1->second.itemId); // Item Id - invMsg.writeInt16(it1->second.amount); // Amount - LOG_DEBUG("Left " << amount << " item(s) id:" - << it1->second.itemId << " into slot: " << slot1); - } - else - { - invMsg.writeInt16(0); - mPoss->inventory.erase(it1); - LOG_DEBUG("Slot: " << slot1 << " is now empty."); - } - invMsg.writeInt16(slot2); // Slot - invMsg.writeInt16(itemId); // Item Id (same as slot 1) - invMsg.writeInt16(nb); // Amount - LOG_DEBUG("Slot: " << slot2 << " has now " << nb << " of item id: " - << itemId); - } - else - { - // Slot2 exists. - if (it2->second.itemId != it1->second.itemId) - { - // Swap items when they are of a different type - // and when all the amount of slot 1 is moving onto slot 2. - if (amount >= it1->second.amount) - { - unsigned itemId = it1->second.itemId; - unsigned amount = it1->second.amount; - it1->second.itemId = it2->second.itemId; - it1->second.amount = it2->second.amount; - it2->second.itemId = itemId; - it2->second.amount = amount; - - // Sending swapped slots. - invMsg.writeInt16(slot1); - invMsg.writeInt16(it1->second.itemId); - invMsg.writeInt16(it1->second.amount); - invMsg.writeInt16(slot2); - invMsg.writeInt16(it2->second.itemId); - invMsg.writeInt16(it2->second.amount); - LOG_DEBUG("Swapping items in slots " << slot1 - << " and " << slot2); - } - else - { - // Cannot partially stack items of a different type. - LOG_DEBUG("Cannot move " << amount << " item(s) from slot " - << slot1 << " to " << slot2); - return amount; - } - } - else // Same item type on slot 2. - { - // Number of items moving - nb = std::min(itemManager->getItem( - it1->second.itemId)->getMaxPerSlot() - - it2->second.amount, nb); - - // If nothing can move, we can abort - if (!nb) - return amount; - - it1->second.amount -= nb; - it2->second.amount += nb; - amount -= nb; - - invMsg.writeInt16(slot1); // Slot - if (it1->second.amount) - { - invMsg.writeInt16(it1->second.itemId); // Item Id - invMsg.writeInt16(it1->second.amount); // Amount - } - else - { - invMsg.writeInt16(0); - mPoss->inventory.erase(it1); - } - invMsg.writeInt16(slot2); // Slot - invMsg.writeInt16(it2->second.itemId); // Item Id - invMsg.writeInt16(it2->second.amount); // Amount - } - } - - if (invMsg.getLength() > 2) - gameHandler->sendTo(mCharacter, invMsg); - - return amount; -} - unsigned Inventory::removeFromSlot(unsigned slot, unsigned amount) { InventoryData::iterator it = mPoss->inventory.find(slot); @@ -544,22 +373,6 @@ void Inventory::updateEquipmentTrigger(ItemClass *oldI, ItemClass *newI) newI->useTrigger(mCharacter, ITT_EQUIP); } -unsigned Inventory::getNewEquipItemInstance() -{ - std::set<int> alreadyUsed; - for (EquipData::const_iterator it = mPoss->equipSlots.begin(); - it != mPoss->equipSlots.end(); ++it) - { - alreadyUsed.insert(it->second.itemInstance); - } - - unsigned itemInstance = 1; - while (alreadyUsed.count(itemInstance)) - itemInstance++; - - return itemInstance; -} - bool Inventory::checkEquipmentCapacity(unsigned equipmentSlot, unsigned capacityRequested) { @@ -570,15 +383,15 @@ bool Inventory::checkEquipmentCapacity(unsigned equipmentSlot, return false; // Test whether the slot capacity requested is reached. - for (EquipData::const_iterator it = mPoss->equipSlots.begin(), - it_end = mPoss->equipSlots.end(); it != it_end; ++it) + for (EquipData::const_iterator it = mPoss->equipment.begin(), + it_end = mPoss->equipment.end(); it != it_end; ++it) { - if (it->first == equipmentSlot) + InventoryData::iterator itemIt = mPoss->inventory.find(*it); + if (itemIt->second.equipmentSlot == equipmentSlot) { - if (it->second.itemInstance != 0) - { - capacity--; - } + const int itemId = itemIt->second.itemId; + const ItemClass *item = itemManager->getItem(itemId); + capacity -= item->getItemEquipRequirement().capacityRequired; } } @@ -593,29 +406,35 @@ bool Inventory::checkEquipmentCapacity(unsigned equipmentSlot, bool Inventory::equip(int inventorySlot) { // Test inventory slot existence - InventoryData::iterator it; - if ((it = mPoss->inventory.find(inventorySlot)) == mPoss->inventory.end()) + InventoryData::iterator itemIt; + if ((itemIt = mPoss->inventory.find(inventorySlot)) == mPoss->inventory.end()) { LOG_DEBUG("No existing item in inventory at slot: " << inventorySlot); return false; } + InventoryItem &item = itemIt->second; + + // Already equipped? + if (item.equipmentSlot != 0) + return false; + // Test the equipment scripted requirements - if (!testEquipScriptRequirements(it->second.itemId)) + if (!testEquipScriptRequirements(item.itemId)) return false; // Test the equip requirements. If none, it's not an equipable item. const ItemEquipRequirement &equipReq = - itemManager->getItem(it->second.itemId)->getItemEquipRequirement(); + itemManager->getItem(item.itemId)->getItemEquipRequirement(); if (!equipReq.equipSlotId) { - LOG_DEBUG("No equip requirements for item id: " << it->second.itemId + LOG_DEBUG("No equip requirements for item id: " << item.itemId << " at slot: " << inventorySlot); return false; } // List of potential unique itemInstances to unequip first. - std::set<unsigned> equipInstancesToUnequipFirst; + std::set<unsigned> slotsToUnequipFirst; // We first check the equipment slots for: // - 1. whether enough total equip slot space is available. @@ -642,14 +461,15 @@ bool Inventory::equip(int inventorySlot) && hasInventoryEnoughSpace(equipReq.equipSlotId)) { // Then, we unequip each iteminstance of the equip slot - for (EquipData::iterator iter = - mPoss->equipSlots.begin(); - iter != mPoss->equipSlots.end(); ++iter) + for (EquipData::iterator it = mPoss->equipment.begin(), + it_end = mPoss->equipment.end(); it != it_end; ++it) { - if (iter->first == equipReq.equipSlotId - && iter->second.itemInstance) - equipInstancesToUnequipFirst.insert( - iter->second.itemInstance); + const unsigned slot = *it; + InventoryData::iterator itemIt = mPoss->inventory.find(slot); + assert(itemIt != mPoss->inventory.end()); + if (itemIt->second.equipmentSlot == equipReq.equipSlotId) { + slotsToUnequipFirst.insert(itemIt->first); + } } } else @@ -662,90 +482,33 @@ bool Inventory::equip(int inventorySlot) } // Potential Pre-unequipment process - for (std::set<unsigned>::const_iterator it3 = - equipInstancesToUnequipFirst.begin(); - it3 != equipInstancesToUnequipFirst.end(); ++it3) + for (std::set<unsigned>::const_iterator itemsToUnequip = + slotsToUnequipFirst.begin(), + itemsToUnequip_end = slotsToUnequipFirst.end(); + itemsToUnequip != itemsToUnequip_end; ++itemsToUnequip) { - if (!unequip(*it3)) + if (!unequip(*itemsToUnequip)) { // Something went wrong even when we tested the unequipment process. LOG_WARN("Unable to unequip even when unequip was tested. " "Character : " - << mCharacter->getComponent<BeingComponent>()->getName() - << ", unequip slot: " << *it3); + << mCharacter->getComponent<BeingComponent>()->getName() + << ", unequip slot: " << *itemsToUnequip); return false; } } // Actually equip the item now that the requirements has met. - //W equip slot type count, W item id, { W equip slot, W capacity used}* - MessageOut equipMsg(GPMSG_EQUIP); - equipMsg.writeInt16(it->second.itemId); // Item Id - equipMsg.writeInt16(1); // Number of equip slot changed. - - // Compute an unique equip item Instance id (unicity is per character only.) - int itemInstance = getNewEquipItemInstance(); - - unsigned capacityLeft = equipReq.capacityRequired; - unsigned capacityUsed = 0; - // Apply equipment changes - for (EquipData::iterator it4 = mPoss->equipSlots.begin(), - it4_end = mPoss->equipSlots.end(); it4 != it4_end; ++it4) - { - if (!capacityLeft) - break; - - // We've found an existing equip slot - if (it4->first == equipReq.equipSlotId) - { - // We've found an empty slot - if (it4->second.itemInstance == 0) - { - it4->second.itemId = it->second.itemId; - it4->second.itemInstance = itemInstance; - --capacityLeft; - } - else // The slot is already in use. - { - ++capacityUsed; - } - } - } - - // When there is still something to apply even when out of that loop, - // It means that the equip multimapis missing empty slots. - // Hence, we add them back - if(capacityLeft) - { - unsigned maxCapacity = - itemManager->getEquipSlotCapacity(equipReq.equipSlotId); + item.equipmentSlot = equipReq.equipSlotId; + mPoss->equipment.insert(inventorySlot); - // A should never happen case - assert(maxCapacity >= capacityUsed + capacityLeft); - - while (capacityLeft) - { - EquipmentItem equipItem(it->second.itemId, itemInstance); - mPoss->equipSlots.insert( - std::make_pair(equipReq.equipSlotId, equipItem)); - --capacityLeft; - } - } - - // Equip slot - equipMsg.writeInt16(equipReq.equipSlotId); - // Capacity used - equipMsg.writeInt16(equipReq.capacityRequired); - // Item instance - equipMsg.writeInt16(itemInstance); + MessageOut equipMsg(GPMSG_EQUIP); + equipMsg.writeInt16(inventorySlot); + equipMsg.writeInt16(item.equipmentSlot); + gameHandler->sendTo(mCharacter, equipMsg); // New item trigger - updateEquipmentTrigger(0, it->second.itemId); - - // Remove item from inventory - removeFromSlot(inventorySlot, 1); - - gameHandler->sendTo(mCharacter, equipMsg); + updateEquipmentTrigger(0, item.itemId); // Update look when necessary checkLookchanges(equipReq.equipSlotId); @@ -753,87 +516,51 @@ bool Inventory::equip(int inventorySlot) return true; } -unsigned Inventory::getSlotItemInstance(unsigned slot) +bool Inventory::unequipAll(unsigned itemId) { - EquipData::iterator it = mPoss->equipSlots.find(slot); - if (it != mPoss->equipSlots.end()) - return it->second.itemInstance; - return 0; -} - -bool Inventory::unequipItem(unsigned itemId) -{ - std::set<unsigned> itemInstances; - for (EquipData::iterator it = mPoss->equipSlots.begin(), - it_end = mPoss->equipSlots.end(); it != it_end; ++it) + while (true) { - if (it->second.itemId == itemId) - itemInstances.insert(it->second.itemInstance); - } + const int slot = getFirstSlot(itemId); + // No item left + if (slot == -1) + return true; - // Nothing to do but it's a success - if (itemInstances.empty()) - return true; - - for (std::set<unsigned>::const_iterator it = itemInstances.begin(), - it_end = itemInstances.end(); it != it_end; ++it) - { - if (!unequip(*it)) + if (!unequip(slot)) return false; } - return true; + + // silence compiler warnings + assert(false); + return false; } -bool Inventory::unequip(unsigned itemInstance) +bool Inventory::unequip(unsigned itemSlot) { - if (!itemInstance) - return false; - - // The itemId to unequip - unsigned itemId = 0; - unsigned slotTypeId = 0; - - bool addedToInventory = false; - - // Empties all equip entries that point to the given equipment slot - // The equipment slots should NEVER be erased after initialization! - for (EquipData::iterator it = mPoss->equipSlots.begin(), - it_end = mPoss->equipSlots.end(); it != it_end; ++it) + InventoryData::iterator it = mPoss->inventory.find(itemSlot); + if (it == mPoss->inventory.end()) { - if (it->second.itemInstance == itemInstance && it->second.itemId) - { - // Add the item to the inventory list if not already present there - itemId = it->second.itemId; + LOG_DEBUG("Tried to unequip invalid item at slot " << itemSlot); + return false; + } - // Move the item back to inventory and return false when it failed. - if (!addedToInventory && insert(itemId, 1) > 0) - return false; - else - addedToInventory = true; + InventoryItem &item = it->second; - it->second.itemId = 0; - it->second.itemInstance = 0; + // Item was not equipped + if (item.equipmentSlot == 0) + return false; - // We keep track of the slot type to be able to raise a potential - // change in the character sprite - slotTypeId = it->first; - } - } + const unsigned slotTypeId = item.equipmentSlot; - // When there were no corresponding item id, it means no item was to - // be unequipped. - if (!itemId) - return false; + // unequip + item.equipmentSlot = 0; + mPoss->equipment.erase(mPoss->equipment.find(itemSlot)); - MessageOut equipMsg(GPMSG_EQUIP); - equipMsg.writeInt16(0); // Item Id, useless in case of unequip. - equipMsg.writeInt16(1); // Number of slot types touched. - equipMsg.writeInt16(itemInstance); - equipMsg.writeInt16(0); // Capacity used, set to 0 to unequip. + MessageOut equipMsg(GPMSG_UNEQUIP); + equipMsg.writeInt16(itemSlot); gameHandler->sendTo(mCharacter, equipMsg); // Apply unequip trigger - updateEquipmentTrigger(itemId, 0); + updateEquipmentTrigger(item.itemId, 0); checkLookchanges(slotTypeId); diff --git a/src/game-server/inventory.h b/src/game-server/inventory.h index 1740a8d9..949471b6 100644 --- a/src/game-server/inventory.h +++ b/src/game-server/inventory.h @@ -65,26 +65,19 @@ class Inventory bool equip(int inventorySlot); /** - * Unequips all the items with the given item if - * from given equipment slot. + * Unequips all the items with the given item id * @param itemId The item Id to unequip. * @returns whether all item id could be unequipped. * @note returns true when no item with given ids were equipped. */ - bool unequipItem(unsigned itemId); + bool unequipAll(unsigned itemId); /** * Unequips item from given equipment slot. - * @param itemInstance The item instance id used to know what to unequip + * @param itemSlot The item slot used to know what to unequip * @returns Whether it was unequipped. */ - bool unequip(unsigned itemInstance); - - /** - * Gets the item instance from the given equipment slot. - * Return 0 if none. - */ - unsigned getSlotItemInstance(unsigned slot); + bool unequip(unsigned itemSlot); /** * Inserts some items into the inventory. @@ -99,13 +92,6 @@ class Inventory unsigned remove(unsigned itemId, unsigned amount); /** - * Moves some items from the first slot to the second one. - * @returns number of items not moved. - */ - unsigned move(unsigned slot1, unsigned slot2, - unsigned amount); - - /** * Removes some items from inventory. * @return number of items not removed. */ @@ -113,11 +99,9 @@ class Inventory /** * Counts number of items with given Id. - * @param inInventory Search in player's inventory. - * @param inEquipment Search in player's equipment. + * @param itemId The id to look for. */ - unsigned count(unsigned itemId, bool inInventory = true, - bool inEquipment = true) const; + unsigned count(unsigned itemId) const; /** * Gets the ID of the items in a given slot. @@ -163,14 +147,6 @@ class Inventory { return true; } /** - * Return an equip item instance id unique to the item used, - * per character. - * This is used to differenciate some items that can be equipped - * multiple times, like one-handed weapons for instance. - */ - unsigned getNewEquipItemInstance(); - - /** * Check the inventory is within the slot limit and capacity. * Forcibly delete items from the end if it is not. * @todo Drop items instead? diff --git a/src/game-server/state.cpp b/src/game-server/state.cpp index a7cac98c..b6528955 100644 --- a/src/game-server/state.cpp +++ b/src/game-server/state.cpp @@ -85,10 +85,8 @@ static void serializeLooks(Entity *ch, MessageOut &msg) msg.writeInt8(characterComponent->getHairColor()); const EquipData &equipData = characterComponent->getPossessions().getEquipment(); - - // We'll use a set to check whether we already sent the update for the given - // item instance. - std::set<unsigned> itemInstances; + const InventoryData &inventoryData = + characterComponent->getPossessions().getInventory(); // The map storing the info about the look changes to send //{ slot type id, item id } @@ -99,17 +97,14 @@ static void serializeLooks(Entity *ch, MessageOut &msg) for (EquipData::const_iterator it = equipData.begin(), it_end = equipData.end(); it != it_end; ++it) { - if (!itemManager->isEquipSlotVisible(it->first)) + InventoryData::const_iterator itemIt = inventoryData.find(*it); + + if (!itemManager->isEquipSlotVisible(itemIt->second.equipmentSlot)) continue; - if (!it->second.itemInstance - || itemInstances.insert(it->second.itemInstance).second) - { - // When the insertion succeeds, its the first time - // we encounter the item, so we can send the look change. - // We also send empty slots for unequipment handling. - lookChanges.insert(std::make_pair(it->first, it->second.itemId)); - } + lookChanges.insert(std::make_pair( + itemIt->second.equipmentSlot, + itemIt->second.itemId)); } if (!lookChanges.empty()) @@ -117,12 +112,12 @@ static void serializeLooks(Entity *ch, MessageOut &msg) // Number of look changes to send msg.writeInt8(lookChanges.size()); - for (std::map<unsigned, unsigned>::const_iterator it2 = - lookChanges.begin(), it2_end = lookChanges.end(); - it2 != it2_end; ++it2) + for (std::map<unsigned, unsigned>::const_iterator it = + lookChanges.begin(), it_end = lookChanges.end(); + it != it_end; ++it) { - msg.writeInt8(it2->first); - msg.writeInt16(it2->second); + msg.writeInt8(it->first); + msg.writeInt16(it->second); } } } @@ -170,20 +165,20 @@ static void informPlayer(MapComposite *map, Entity *p) // Send action change messages. if ((oflags & UPDATEFLAG_ACTIONCHANGE)) { - MessageOut ActionMsg(GPMSG_BEING_ACTION_CHANGE); - ActionMsg.writeInt16(oid); - ActionMsg.writeInt8( + MessageOut actionMsg(GPMSG_BEING_ACTION_CHANGE); + actionMsg.writeInt16(oid); + actionMsg.writeInt8( o->getComponent<BeingComponent>()->getAction()); - gameHandler->sendTo(p, ActionMsg); + gameHandler->sendTo(p, actionMsg); } // Send looks change messages. if (oflags & UPDATEFLAG_LOOKSCHANGE) { - MessageOut LooksMsg(GPMSG_BEING_LOOKS_CHANGE); - LooksMsg.writeInt16(oid); - serializeLooks(o, LooksMsg); - gameHandler->sendTo(p, LooksMsg); + MessageOut looksMsg(GPMSG_BEING_LOOKS_CHANGE); + looksMsg.writeInt16(oid); + serializeLooks(o, looksMsg); + gameHandler->sendTo(p, looksMsg); } // Send emote messages. @@ -193,21 +188,21 @@ static void informPlayer(MapComposite *map, Entity *p) o->getComponent<BeingComponent>()->getLastEmote(); if (emoteId > -1) { - MessageOut EmoteMsg(GPMSG_BEING_EMOTE); - EmoteMsg.writeInt16(oid); - EmoteMsg.writeInt16(emoteId); - gameHandler->sendTo(p, EmoteMsg); + MessageOut emoteMsg(GPMSG_BEING_EMOTE); + emoteMsg.writeInt16(oid); + emoteMsg.writeInt16(emoteId); + gameHandler->sendTo(p, emoteMsg); } } // Send direction change messages. if (oflags & UPDATEFLAG_DIRCHANGE) { - MessageOut DirMsg(GPMSG_BEING_DIR_CHANGE); - DirMsg.writeInt16(oid); - DirMsg.writeInt8( + MessageOut dirMsg(GPMSG_BEING_DIR_CHANGE); + dirMsg.writeInt16(oid); + dirMsg.writeInt8( o->getComponent<BeingComponent>()->getDirection()); - gameHandler->sendTo(p, DirMsg); + gameHandler->sendTo(p, dirMsg); } // Send ability uses @@ -279,8 +274,6 @@ static void informPlayer(MapComposite *map, Entity *p) { case OBJECT_CHARACTER: { - auto *characterComponent = - o->getComponent<CharacterComponent>(); enterMsg.writeString( o->getComponent<BeingComponent>()->getName()); serializeLooks(o, enterMsg); diff --git a/src/scripting/lua.cpp b/src/scripting/lua.cpp index 0193d888..50bf3f35 100644 --- a/src/scripting/lua.cpp +++ b/src/scripting/lua.cpp @@ -901,10 +901,8 @@ static int trade(lua_State *s) } /** LUA entity:inv_count (inventory) - * entity:inv_count(bool inInventory, bool inEquipment, - * int id1, ..., int idN) - * entity:inv_count(bool inInventory, bool inEquipment, - * string name1, ..., string nameN) + * entity:inv_count(int id1, ..., int idN) + * entity:inv_count(string name1, ..., string nameN) ** * Valid only for character entities. * @@ -923,17 +921,14 @@ static int entity_inv_count(lua_State *s) return 0; } - bool inInventory = lua_toboolean(s, 2); - bool inEquipment = lua_toboolean(s, 3); - - int nb_items = lua_gettop(s) - 3; + int nb_items = lua_gettop(s) - 1; Inventory inv(q); int nb = 0; for (int i = 4; i < nb_items + 4; ++i) { ItemClass *it = checkItemClass(s, i); - nb = inv.count(it->getDatabaseID(), inInventory, inEquipment); + nb = inv.count(it->getDatabaseID()); lua_pushinteger(s, nb); } return nb_items; @@ -1004,7 +999,7 @@ static int entity_inv_change(lua_State *s) } /** LUA entity:inventory (inventory) - * entity:inventory(): table[]{slot, item id, name, amount} + * entity:inventory(): table[]{slot, item id, name, amount, equipped} ** * Valid only for character entities. * @@ -1064,6 +1059,10 @@ static int entity_get_inventory(lua_State *s) lua_pushinteger(s, it->second.amount); lua_settable(s, subTableStackPosition); + lua_pushliteral(s, "equipped"); + lua_pushboolean(s, it->second.equipmentSlot != 0); + lua_settable(s, subTableStackPosition); + // Add the sub-table as value of the main one. lua_rawseti(s, firstTableStackPosition, tableIndex); ++tableIndex; @@ -1098,39 +1097,36 @@ static int entity_get_equipment(lua_State *s) Entity *q = checkCharacter(s, 1); // Create a lua table with the inventory ids. - const EquipData equipData = q->getComponent<CharacterComponent>() - ->getPossessions().getEquipment(); + auto *characterComponent = q->getComponent<CharacterComponent>(); + const InventoryData inventoryData = + characterComponent->getPossessions().getInventory(); + const EquipData equipData = + characterComponent->getPossessions().getEquipment(); lua_newtable(s); int firstTableStackPosition = lua_gettop(s); int tableIndex = 1; - std::set<unsigned> itemInstances; - for (EquipData::const_iterator it = equipData.begin(), it_end = equipData.end(); it != it_end; ++it) { - if (!it->second.itemId || !it->second.itemInstance) - continue; - - // Only count multi-slot items once. - if (!itemInstances.insert(it->second.itemInstance).second) - continue; + InventoryData::const_iterator itemIt = inventoryData.find(*it); + const InventoryItem &item = itemIt->second; // Create the sub-table (value of the main one) lua_createtable(s, 0, 3); int subTableStackPosition = lua_gettop(s); // Stores the item info in it. lua_pushliteral(s, "slot"); - lua_pushinteger(s, it->first); // The slot id + lua_pushinteger(s, item.slot); // The slot id lua_settable(s, subTableStackPosition); lua_pushliteral(s, "id"); - lua_pushinteger(s, it->second.itemId); + lua_pushinteger(s, item.itemId); lua_settable(s, subTableStackPosition); lua_pushliteral(s, "name"); - push(s, itemManager->getItem(it->second.itemId)->getName()); + push(s, itemManager->getItem(item.itemId)->getName()); lua_settable(s, subTableStackPosition); // Add the sub-table as value of the main one. @@ -1202,7 +1198,7 @@ static int entity_unequip_slot(lua_State *s) Inventory inv(ch); - lua_pushboolean(s, inv.unequip(inv.getSlotItemInstance(equipmentSlot))); + lua_pushboolean(s, inv.unequip(equipmentSlot)); return 1; } @@ -1223,7 +1219,7 @@ static int entity_unequip_item(lua_State *s) ItemClass *it = checkItemClass(s, 2); Inventory inv(ch); - lua_pushboolean(s, inv.unequipItem(it->getDatabaseID())); + lua_pushboolean(s, inv.unequipAll(it->getDatabaseID())); return 1; } diff --git a/src/sql/mysql/createTables.sql b/src/sql/mysql/createTables.sql index fc873712..c15c005b 100644 --- a/src/sql/mysql/createTables.sql +++ b/src/sql/mysql/createTables.sql @@ -179,21 +179,6 @@ DEFAULT CHARSET=utf8 AUTO_INCREMENT=1 ; -- --- table: `mana_char_equips` --- -CREATE TABLE IF NOT EXISTS `mana_char_equips` ( - `id` int(10) unsigned NOT NULL auto_increment, - `owner_id` int(10) unsigned NOT NULL, - `slot_type` int(10) unsigned NOT NULL, - `item_id` int(10) unsigned NOT NULL, - `item_instance` int(10) unsigned NOT NULL, - -- - PRIMARY KEY (`id`), - FOREIGN KEY (`owner_id`) REFERENCES `mana_characters` (`id`) -) ENGINE=InnoDB -DEFAULT CHARSET=utf8; - --- -- table: `mana_inventories` -- todo: remove class_id and amount and reference on mana_item_instances -- @@ -203,6 +188,7 @@ CREATE TABLE IF NOT EXISTS `mana_inventories` ( `slot` tinyint(3) unsigned NOT NULL, `class_id` int(10) unsigned NOT NULL, `amount` tinyint(3) unsigned NOT NULL, + `equipped` tinyint(3) unsigned NOT NULL, -- PRIMARY KEY (`id`), UNIQUE KEY `owner_id` (`owner_id`, `slot`), @@ -420,7 +406,7 @@ AUTO_INCREMENT=0 ; INSERT INTO mana_world_states VALUES('accountserver_startup',-1,'0', NOW()); INSERT INTO mana_world_states VALUES('accountserver_version',-1,'0', NOW()); -INSERT INTO mana_world_states VALUES('database_version', -1,'24', NOW()); +INSERT INTO mana_world_states VALUES('database_version', -1,'25', NOW()); -- all known transaction codes diff --git a/src/sql/mysql/updates/update_24_to_25.sql b/src/sql/mysql/updates/update_24_to_25.sql new file mode 100644 index 00000000..8f5ea24e --- /dev/null +++ b/src/sql/mysql/updates/update_24_to_25.sql @@ -0,0 +1,18 @@ +START TRANSACTION; + +ALTER TABLE mana_inventories ADD COLUMN equipped tinyint(3) unsigned NOT NULL; + +INSERT INTO mana_inventories (owner_id, slot, class_id, amount, equipped) +SELECT owner_id, (SELECT IF COUNT(slot) = 0 THEN 1 ELSE MAX(slot) + 1 END IF FROM mana_inventories + WHERE owner_id=owner_id), + item_id, 1, 1 FROM mana_char_equips; + +DROP TABLE mana_char_equips; + +-- Update database version. +UPDATE mana_world_states + SET value = '25', + moddate = UNIX_TIMESTAMP() + WHERE state_name = 'database_version'; + +COMMIT; diff --git a/src/sql/sqlite/createTables.sql b/src/sql/sqlite/createTables.sql index 9d7d1c2c..95dc5a23 100644 --- a/src/sql/sqlite/createTables.sql +++ b/src/sql/sqlite/createTables.sql @@ -173,19 +173,6 @@ CREATE TABLE mana_floor_items ----------------------------------------------------------------------------- -CREATE TABLE mana_char_equips -( - id INTEGER PRIMARY KEY, - owner_id INTEGER NOT NULL, - slot_type INTEGER NOT NULL, - item_id INTEGER NOT NULL, - item_instance INTEGER NOT NULL, - -- - FOREIGN KEY (owner_id) REFERENCES mana_characters(id) -); - ------------------------------------------------------------------------------ - -- todo: remove class_id and amount and reference on mana_item_instances CREATE TABLE mana_inventories ( @@ -194,6 +181,7 @@ CREATE TABLE mana_inventories slot INTEGER NOT NULL, class_id INTEGER NOT NULL, amount INTEGER NOT NULL, + equipped INTEGER NOT NULL, -- FOREIGN KEY (owner_id) REFERENCES mana_characters(id) ); @@ -408,7 +396,7 @@ AS INSERT INTO mana_world_states VALUES('accountserver_startup',-1,'0', strftime('%s','now')); INSERT INTO mana_world_states VALUES('accountserver_version',-1,'0', strftime('%s','now')); -INSERT INTO mana_world_states VALUES('database_version', -1,'24', strftime('%s','now')); +INSERT INTO mana_world_states VALUES('database_version', -1,'25', strftime('%s','now')); -- all known transaction codes diff --git a/src/sql/sqlite/updates/update_24_to_25.sql b/src/sql/sqlite/updates/update_24_to_25.sql new file mode 100644 index 00000000..955d9c59 --- /dev/null +++ b/src/sql/sqlite/updates/update_24_to_25.sql @@ -0,0 +1,47 @@ +BEGIN; + +CREATE TABLE mana_inventories_backup +( + id INTEGER PRIMARY KEY, + owner_id INTEGER NOT NULL, + slot INTEGER NOT NULL, + class_id INTEGER NOT NULL, + amount INTEGER NOT NULL, + equipped INTEGER NOT NULL, + -- + FOREIGN KEY (owner_id) REFERENCES mana_characters(id) +); + +INSERT INTO mana_inventories_backup SELECT + id, owner_id, slot, class_id, amount, 0 FROM mana_inventories; + +DROP TABLE mana_inventories; + +CREATE TABLE mana_inventories +( + id INTEGER PRIMARY KEY AUTOINCREMENT, + owner_id INTEGER NOT NULL, + slot INTEGER NOT NULL, + class_id INTEGER NOT NULL, + amount INTEGER NOT NULL, + equipped INTEGER NOT NULL, + -- + FOREIGN KEY (owner_id) REFERENCES mana_characters(id) +); + +INSERT INTO mana_inventories SELECT * FROM mana_inventories_backup; +DROP TABLE mana_inventories_backup; + + +INSERT INTO mana_inventories (owner_id, slot, class_id, amount, equipped) +SELECT owner_id, (SELECT CASE WHEN COUNT(slot) = 0 THEN 1 ELSE MAX(slot) + 1 END as slot FROM mana_inventories + WHERE owner_id=owner_id), + item_id, 1, 1 FROM mana_char_equips; + +-- Update the database version, and set date of update +UPDATE mana_world_states + SET value = '25', + moddate = strftime('%s','now') + WHERE state_name = 'database_version'; + +END; |