diff options
-rw-r--r-- | src/game-server/inventory.cpp | 316 | ||||
-rw-r--r-- | src/game-server/inventory.h | 43 | ||||
-rw-r--r-- | src/game-server/trade.cpp | 4 | ||||
-rw-r--r-- | src/scripting/lua.cpp | 9 |
4 files changed, 90 insertions, 282 deletions
diff --git a/src/game-server/inventory.cpp b/src/game-server/inventory.cpp index d7baedf2..e218a916 100644 --- a/src/game-server/inventory.cpp +++ b/src/game-server/inventory.cpp @@ -28,190 +28,11 @@ #include "net/messageout.h" #include "utils/logger.h" -Inventory::Inventory(Character *p, bool d): - mPoss(&p->getPossessions()), mInvMsg(GPMSG_INVENTORY), - mEqmMsg(GPMSG_EQUIP), mCharacter(p), mDelayed(d) +Inventory::Inventory(Character *p): + mPoss(&p->getPossessions()), mCharacter(p) { } -Inventory::~Inventory() -{ - commit(false); -} - -void Inventory::restart() -{ - mInvMsg.clear(); - mInvMsg.writeInt16(GPMSG_INVENTORY); -} - -void Inventory::cancel() -{ - assert(mDelayed); - Possessions &poss = mCharacter->getPossessions(); - if (mPoss != &poss) - { - delete mPoss; - mPoss = &poss; - } - restart(); -} - -void Inventory::commit(bool doRestart) -{ - Possessions &poss = mCharacter->getPossessions(); - /* Sends changes, whether delayed or not. */ - if (mInvMsg.getLength() > 2) - { - /* Send the message to the client directly. Perhaps this should be - done through an update flag, too? */ - gameHandler->sendTo(mCharacter, mInvMsg); - } - if (mPoss != &poss) - { - if (mDelayed) - { - /* - * Search for any and all changes to equipment. - * Search through equipment for changes between - * old and new equipment. - * Send changes directly when there is a change. - * Even when equipment references to invy slots are the same, - * it still needs to be searched for changes - * to the internal equiment slot usage. - * This is probably the worst part of doing this in delayed mode. - */ - IdSlotMap oldEquip, newEquip; - { - EquipData::const_iterator it1, it2, it1_end, it2_end; - for (it1 = mPoss->equipSlots.begin(), - it1_end = mPoss->equipSlots.end(); - it1 != it1_end; - ++it1) - { -#ifdef INV_CONST_BOUND_DEBUG - IdSlotMap::const_iterator temp2, temp = -#endif - newEquip.insert( - newEquip.upper_bound(it1->second), - std::make_pair(it1->second, it1->first)); -#ifdef INV_CONST_BOUND_DEBUG - if (temp != - --(temp2 = newEquip.upper_bound(it1->second))) - throw; -#endif - } - for (it2 = poss.equipSlots.begin(), - it2_end = poss.equipSlots.end(); - it2 != it2_end; - ++it2) - oldEquip.insert( - oldEquip.upper_bound(it2->second), - std::make_pair(it2->second, it2->first)); - } - { - IdSlotMap::const_iterator it1 = newEquip.begin(), - it2 = oldEquip.begin(), - it1_end = newEquip.end(), - it2_end = oldEquip.end(), - temp1, temp2; - while (it1 != it1_end || it2 != it2_end) - { - if (it1 == it1_end) - { - if (it2 == it2_end) - break; - equip_sub(0, it1); - } - else if (it2 == it2_end) - equip_sub(newEquip.count(it2->first), it2); - else if (it1->first == it2->first) - { - double invSlot = it1->first; - while ((it1 != it1_end && it1->first == invSlot) || - (it2 != it2_end && it2->first == invSlot)) - { - /* - * Item is still equipped, but need to check - * that the slots didn't change. - */ - if (it1->second == it2->second) - { - // No change. - ++it1; - ++it2; - continue; - } - unsigned int itemId = - mPoss->inventory.at(it1->first).itemId; - changeEquipment(itemId, itemId); - break; - } - } - else if (it1->first > it2->first) - equip_sub(newEquip.count(it2->first), it2); - else // it1->first < it2->first - equip_sub(0, it1); - } - } - } - poss = *mPoss; - delete mPoss; - mPoss = &poss; - } - - /* Update server sided states if in delayed mode. If we are not in - delayed mode, the server sided states already reflect the changes - that have just been sent to the client. */ - - if (mEqmMsg.getLength() > 2) - gameHandler->sendTo(mCharacter, mEqmMsg); - - if (doRestart) - restart(); -} - -void Inventory::equip_sub(unsigned int newCount, IdSlotMap::const_iterator &it) -{ - const unsigned int invSlot = it->first; - unsigned int count = 0, eqSlot = it->second; - mEqmMsg.writeInt16(invSlot); - mEqmMsg.writeInt8(newCount); - do { - if (newCount) - { - if (it->second != eqSlot) - { - mEqmMsg.writeInt8(eqSlot); - mEqmMsg.writeInt8(count); - count = 1; - eqSlot = it->second; - } - ++count; - } - if (itemManager->isEquipSlotVisible(it->second)) - mCharacter->raiseUpdateFlags(UPDATEFLAG_LOOKSCHANGE); - } while ((++it)->first == invSlot); - if (count) - { - mEqmMsg.writeInt8(eqSlot); - mEqmMsg.writeInt8(count); - } - mEqmMsg.writeInt16(invSlot); - changeEquipment(newCount ? 0 : mPoss->inventory.at(invSlot).itemId, - newCount ? mPoss->inventory.at(invSlot).itemId : 0); -} - -void Inventory::prepare() -{ - if (!mDelayed) - return; - - Possessions *poss = &mCharacter->getPossessions(); - if (mPoss == poss) - mPoss = new Possessions(*poss); -} - void Inventory::sendFull() const { /* Sends all the information needed to construct inventory @@ -242,8 +63,6 @@ void Inventory::sendFull() const void Inventory::initialize() { - assert(!mDelayed); - InventoryData::iterator it1; EquipData::const_iterator it2, it2_end = mPoss->equipSlots.end(); /* @@ -343,10 +162,11 @@ unsigned int Inventory::getItem(unsigned int slot) const unsigned int Inventory::insert(unsigned int itemId, unsigned int amount) { - unsigned int maxPerSlot = itemManager->getItem(itemId)->getMaxPerSlot(); if (!itemId || !amount) return 0; - prepare(); + + MessageOut invMsg(GPMSG_INVENTORY); + unsigned int maxPerSlot = itemManager->getItem(itemId)->getMaxPerSlot(); InventoryData::iterator it, it_end = mPoss->inventory.end(); // Add to slots with existing items of this type first. for (it = mPoss->inventory.begin(); it != it_end; ++it) @@ -369,11 +189,11 @@ unsigned int Inventory::insert(unsigned int itemId, unsigned int amount) amount -= spaceleft; } - mInvMsg.writeInt16(it->first); - mInvMsg.writeInt16(itemId); - mInvMsg.writeInt16(it->second.amount); + invMsg.writeInt16(it->first); + invMsg.writeInt16(itemId); + invMsg.writeInt16(it->second.amount); if (!amount) - return 0; + break; } int slot = 0; @@ -381,7 +201,7 @@ unsigned int Inventory::insert(unsigned int itemId, unsigned int amount) for (it = mPoss->inventory.begin();; ++it) { if (!amount) - return 0; + break; int lim = (it == it_end) ? INVENTORY_SLOTS : it->first; while (amount && slot < lim) { @@ -389,14 +209,18 @@ unsigned int Inventory::insert(unsigned int itemId, unsigned int amount) mPoss->inventory[slot].itemId = itemId; mPoss->inventory[slot].amount = additions; amount -= additions; - mInvMsg.writeInt16(slot++); // Last read, so also increment - mInvMsg.writeInt16(itemId); - mInvMsg.writeInt16(additions); + invMsg.writeInt16(slot++); // Last read, so also increment + invMsg.writeInt16(itemId); + invMsg.writeInt16(additions); } ++slot; // Skip the slot that the iterator points to if (it == it_end) break; } + // Send that first, before checking potential removals + if (invMsg.getLength() > 2) + gameHandler->sendTo(mCharacter, invMsg); + checkInventorySize(); return amount; @@ -415,9 +239,11 @@ unsigned int Inventory::count(unsigned int itemId) const unsigned int Inventory::remove(unsigned int itemId, unsigned int amount, bool force) { - prepare(); bool inv = false, eq = !itemManager->getItem(itemId)->getItemEquipData().empty(); + + MessageOut invMsg(GPMSG_INVENTORY); + bool triggerLeaveInventory = true; for (InventoryData::iterator it = mPoss->inventory.begin(), it_end = mPoss->inventory.end(); it != it_end; ++it) @@ -448,29 +274,34 @@ unsigned int Inventory::remove(unsigned int itemId, unsigned int amount, bool fo unsigned int sub = std::min(amount, it->second.amount); amount -= sub; it->second.amount -= sub; - mInvMsg.writeInt16(it->first); + invMsg.writeInt16(it->first); if (it->second.amount) { - mInvMsg.writeInt16(it->second.itemId); - mInvMsg.writeInt16(it->second.amount); + invMsg.writeInt16(it->second.itemId); + invMsg.writeInt16(it->second.amount); // Some still exist, and we have none left to remove, so // no need to run leave invy triggers. if (!amount) - return 0; + triggerLeaveInventory = false; } else { - mInvMsg.writeInt16(0); + invMsg.writeInt16(0); mPoss->inventory.erase(it); } } else // We found an instance of them existing and have none left to // remove, so no need to run leave invy triggers. - return 0; + triggerLeaveInventory = false; } - if (force) + + if (triggerLeaveInventory) itemManager->getItem(itemId)->useTrigger(mCharacter, ITT_LEAVE_INVY); + + if (invMsg.getLength() > 2) + gameHandler->sendTo(mCharacter, invMsg); + // Rather inefficient, but still usable for now assuming small invy size. // FIXME return inv && !force ? remove(itemId, amount, true) : amount; @@ -481,7 +312,7 @@ unsigned int Inventory::move(unsigned int slot1, unsigned int slot2, { if (!amount || slot1 == slot2 || slot2 >= INVENTORY_SLOTS) return amount; - prepare(); + InventoryData::iterator it1 = mPoss->inventory.find(slot1), it2 = mPoss->inventory.find(slot2), inv_end = mPoss->inventory.end(); @@ -498,6 +329,8 @@ unsigned int Inventory::move(unsigned int slot1, unsigned int slot2, // items in the same slot anyway. it->second = slot2; + MessageOut invMsg(GPMSG_INVENTORY); + unsigned int nb = std::min(amount, it1->second.amount); if (it2 == inv_end) { @@ -510,20 +343,20 @@ unsigned int Inventory::move(unsigned int slot1, unsigned int slot2, it1->second.amount -= nb; amount -= nb; - mInvMsg.writeInt16(slot1); // Slot + invMsg.writeInt16(slot1); // Slot if (it1->second.amount) { - mInvMsg.writeInt16(it1->second.itemId); // Item Id - mInvMsg.writeInt16(it1->second.amount); // Amount + invMsg.writeInt16(it1->second.itemId); // Item Id + invMsg.writeInt16(it1->second.amount); // Amount } else { - mInvMsg.writeInt16(0); + invMsg.writeInt16(0); mPoss->inventory.erase(it1); } - mInvMsg.writeInt16(slot2); // Slot - mInvMsg.writeInt16(it1->second.itemId); // Item Id (same as slot 1) - mInvMsg.writeInt16(nb); // Amount + invMsg.writeInt16(slot2); // Slot + invMsg.writeInt16(it1->second.itemId); // Item Id (same as slot 1) + invMsg.writeInt16(nb); // Amount } else { @@ -538,34 +371,37 @@ unsigned int Inventory::move(unsigned int slot1, unsigned int slot2, it2->second.amount += nb; amount -= nb; - mInvMsg.writeInt16(slot1); // Slot + invMsg.writeInt16(slot1); // Slot if (it1->second.amount) { - mInvMsg.writeInt16(it1->second.itemId); // Item Id - mInvMsg.writeInt16(it1->second.amount); // Amount + invMsg.writeInt16(it1->second.itemId); // Item Id + invMsg.writeInt16(it1->second.amount); // Amount } else { - mInvMsg.writeInt16(0); + invMsg.writeInt16(0); mPoss->inventory.erase(it1); } - mInvMsg.writeInt16(slot2); // Slot - mInvMsg.writeInt16(it2->second.itemId); // Item Id - mInvMsg.writeInt16(it2->second.amount); // Amount + 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 int Inventory::removeFromSlot(unsigned int slot, unsigned int amount) { - prepare(); - InventoryData::iterator it = mPoss->inventory.find(slot); // When the given slot doesn't exist, we can't remove anything if (it == mPoss->inventory.end()) return amount; + MessageOut invMsg(GPMSG_INVENTORY); // Check if an item of the same class exists elsewhere in the inventory bool exists = false; for (InventoryData::const_iterator it2 = mPoss->inventory.begin(), @@ -587,17 +423,21 @@ unsigned int Inventory::removeFromSlot(unsigned int slot, unsigned int amount) unsigned int sub = std::min(amount, it->second.amount); amount -= sub; it->second.amount -= sub; - mInvMsg.writeInt16(it->first); + invMsg.writeInt16(it->first); if (it->second.amount) { - mInvMsg.writeInt16(it->second.itemId); - mInvMsg.writeInt16(it->second.amount); + invMsg.writeInt16(it->second.itemId); + invMsg.writeInt16(it->second.amount); } else { - mInvMsg.writeInt16(0); + invMsg.writeInt16(0); mPoss->inventory.erase(it); } + + if (invMsg.getLength() > 2) + gameHandler->sendTo(mCharacter, invMsg); + return amount; } @@ -636,6 +476,8 @@ bool Inventory::equip(int slot, bool override) if (eq.empty()) return false; ItemEquipInfo const *ovd = 0; + + MessageOut equipMsg(GPMSG_EQUIP); // Iterate through all possible combinations of slots for (ItemEquipsInfo::const_iterator it2 = eq.begin(), it2_end = eq.end(); it2 != it2_end; ++it2) @@ -679,19 +521,15 @@ bool Inventory::equip(int slot, bool override) /* * Clean fit. Equip and apply immediately. */ - if (!mDelayed) { - mEqmMsg.writeInt16(slot); // Inventory slot - mEqmMsg.writeInt8(it2->size()); // Equip slot type count - } + equipMsg.writeInt16(slot); // Inventory slot + equipMsg.writeInt8(it2->size()); // Equip slot type count for (it3 = it2->begin(), it3_end = it2->end(); it3 != it3_end; ++it3) { - if (!mDelayed) { - mEqmMsg.writeInt8(it3->first); // Equip slot - mEqmMsg.writeInt8(it3->second); // How many are used - } + equipMsg.writeInt8(it3->first); // Equip slot + equipMsg.writeInt8(it3->second); // How many are used /* * This bit can be somewhat inefficient, but is far better for * average case assuming most equip use one slot max for each @@ -704,8 +542,8 @@ bool Inventory::equip(int slot, bool override) mPoss->equipSlots.insert( std::make_pair(it3->first, slot)); } - if (!mDelayed) - changeEquipment(0, it->second.itemId); + + changeEquipment(0, it->second.itemId); return true; case 1: /* @@ -731,6 +569,9 @@ bool Inventory::equip(int slot, bool override) break; } } + + if (equipMsg.getLength() > 2) + gameHandler->sendTo(mCharacter, equipMsg); // We didn't find a clean equip. if (ovd) { @@ -762,11 +603,11 @@ bool Inventory::unequip(EquipData::iterator it) bool Inventory::unequip(unsigned int slot, EquipData::iterator *itp) { - prepare(); EquipData::iterator it = itp ? *itp : mPoss->equipSlots.begin(), it_end = mPoss->equipSlots.end(); bool changed = false; + MessageOut equipMsg(GPMSG_EQUIP); // Erase all equip entries that point to the given inventory slot while (it != it_end) { @@ -781,12 +622,15 @@ bool Inventory::unequip(unsigned int slot, EquipData::iterator *itp) } } - if (changed && !mDelayed) + if (changed) { changeEquipment(mPoss->inventory.at(slot).itemId, 0); - mEqmMsg.writeInt16(slot); - mEqmMsg.writeInt8(0); + equipMsg.writeInt16(slot); + equipMsg.writeInt8(0); } + if (equipMsg.getLength() > 2) + gameHandler->sendTo(mCharacter, equipMsg); + return changed; } diff --git a/src/game-server/inventory.h b/src/game-server/inventory.h index 4a77ad93..3f8403ae 100644 --- a/src/game-server/inventory.h +++ b/src/game-server/inventory.h @@ -35,30 +35,15 @@ class Inventory /** * Creates a view on the possessions of a character. - * @param delayed If the changes need to be cancelable. */ - Inventory(Character *, bool delayed = false); + Inventory(Character *); /** * Commits delayed changes if applicable. * Sends the update message to the client. */ - ~Inventory(); - - /** - * Commits changes. - * Exclusive to delayed mode. - * @param doRestart Whether to prepare the inventory for more changes - after this. If you are unsure, it is safe (though not - terribly efficient) to leave this as true. - */ - void commit(bool doRestart = true); - - /** - * Cancels changes. - * Exclusive to delayed mode. - */ - void cancel(); + ~Inventory() + {} /** * Sends complete inventory status to the client. @@ -129,19 +114,6 @@ class Inventory unsigned int getItem(unsigned int slot) const; private: - - /** - * Make sure that changes are being done on a copy, not directly. - * No effect when not in delayed mode. - */ - void prepare(); - - /** - * Starts a new notification message. - */ - void restart(); - - /** * Check the inventory is within the slot limit and capacity. * Forcibly delete items from the end if it is not. @@ -164,15 +136,8 @@ class Inventory void changeEquipment(ItemClass *oldI, ItemClass *newI); Possessions *mPoss; /**< Pointer to the modified possessions. */ - /** - * Update message containing inventory changes. - * Note that in sendFull(), this is reused to send all full changes - * (for both inventory and equipment) - */ - MessageOut mInvMsg; - MessageOut mEqmMsg; /**< Update message containing equipment changes */ + Character *mCharacter; /**< Character to notify. */ - bool mDelayed; /**< Delayed changes. */ }; #endif diff --git a/src/game-server/trade.cpp b/src/game-server/trade.cpp index 51509307..e1779d2f 100644 --- a/src/game-server/trade.cpp +++ b/src/game-server/trade.cpp @@ -130,7 +130,7 @@ void Trade::agree(Character *c) // Check if both player has the objects in their inventories // and enouth money, then swap them. - Inventory v1(mChar1, true), v2(mChar2, true); + Inventory v1(mChar1), v2(mChar2); if (mChar1->getAttribute(mCurrencyId) >= mMoney1 - mMoney2 && mChar2->getAttribute(mCurrencyId) >= mMoney2 - mMoney1 && perform(mItems1, v1, v2) && @@ -143,8 +143,6 @@ void Trade::agree(Character *c) } else { - v1.cancel(); - v2.cancel(); cancel(); return; } diff --git a/src/scripting/lua.cpp b/src/scripting/lua.cpp index d461bf44..b422fc3e 100644 --- a/src/scripting/lua.cpp +++ b/src/scripting/lua.cpp @@ -359,7 +359,7 @@ static int chr_inv_change(lua_State *s) return 0; } int nb_items = (lua_gettop(s) - 1) / 2; - Inventory inv(q, true); + Inventory inv(q); for (int i = 0; i < nb_items; ++i) { if (!lua_isnumber(s, i * 2 + 2) || !lua_isnumber(s, i * 2 + 3)) @@ -377,12 +377,13 @@ static int chr_inv_change(lua_State *s) } else if (nb < 0) { + // Removing too much item is a success as for the scripter's + // point of view. We log it anyway. nb = inv.remove(id, -nb); if (nb) { - inv.cancel(); - lua_pushboolean(s, 0); - return 1; + LOG_WARN("mana.chr_inv_change() removed more items than owned: " + << "character: " << q->getName() << " item id: " << id); } } else |