From 2ce2e8a1e7883fced661bd37bbbdba68ce389edd Mon Sep 17 00:00:00 2001 From: Jordan Ayers Date: Mon, 11 Jan 2016 07:24:52 -0600 Subject: [PATCH 1/4] Add missing OOB slot check to InventoryStore. --- apps/openmw/mwworld/inventorystore.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/apps/openmw/mwworld/inventorystore.cpp b/apps/openmw/mwworld/inventorystore.cpp index b82b798d11..dbd2c67940 100644 --- a/apps/openmw/mwworld/inventorystore.cpp +++ b/apps/openmw/mwworld/inventorystore.cpp @@ -523,6 +523,9 @@ int MWWorld::InventoryStore::remove(const Ptr& item, int count, const Ptr& actor MWWorld::ContainerStoreIterator MWWorld::InventoryStore::unequipSlot(int slot, const MWWorld::Ptr& actor) { + if (slot<0 || slot>=static_cast (mSlots.size())) + throw std::runtime_error ("slot number out of range"); + ContainerStoreIterator it = mSlots[slot]; if (it != end()) From c82d9a1e873feae70a845feae26b6cbb5572de79 Mon Sep 17 00:00:00 2001 From: Jordan Ayers Date: Mon, 18 Jan 2016 19:56:35 -0600 Subject: [PATCH 2/4] Adjust ContainerStore / InventoryStore to allow partial unequip of items. --- apps/openmw/mwworld/containerstore.cpp | 12 +++++++----- apps/openmw/mwworld/containerstore.hpp | 6 ++++-- apps/openmw/mwworld/inventorystore.cpp | 27 ++++++++++++++++++++++++++ apps/openmw/mwworld/inventorystore.hpp | 9 +++++++++ 4 files changed, 47 insertions(+), 7 deletions(-) diff --git a/apps/openmw/mwworld/containerstore.cpp b/apps/openmw/mwworld/containerstore.cpp index 45390405e0..e07ba433ac 100644 --- a/apps/openmw/mwworld/containerstore.cpp +++ b/apps/openmw/mwworld/containerstore.cpp @@ -136,16 +136,18 @@ int MWWorld::ContainerStore::count(const std::string &id) return total; } -void MWWorld::ContainerStore::unstack(const Ptr &ptr, const Ptr& container) +MWWorld::ContainerStoreIterator MWWorld::ContainerStore::unstack(const Ptr &ptr, const Ptr& container, int count) { - if (ptr.getRefData().getCount() <= 1) - return; - MWWorld::ContainerStoreIterator it = addNewStack(ptr, ptr.getRefData().getCount()-1); + if (ptr.getRefData().getCount() <= count) + return end(); + MWWorld::ContainerStoreIterator it = addNewStack(ptr, ptr.getRefData().getCount()-count); const std::string script = it->getClass().getScript(*it); if (!script.empty()) MWBase::Environment::get().getWorld()->getLocalScripts().add(script, *it); - remove(ptr, ptr.getRefData().getCount()-1, container); + remove(ptr, ptr.getRefData().getCount()-count, container); + + return it; } MWWorld::ContainerStoreIterator MWWorld::ContainerStore::restack(const MWWorld::Ptr& item) diff --git a/apps/openmw/mwworld/containerstore.hpp b/apps/openmw/mwworld/containerstore.hpp index b7ec4bb74e..6a8fc47616 100644 --- a/apps/openmw/mwworld/containerstore.hpp +++ b/apps/openmw/mwworld/containerstore.hpp @@ -130,8 +130,10 @@ namespace MWWorld /// /// @return the number of items actually removed - void unstack (const Ptr& ptr, const Ptr& container); - ///< Unstack an item in this container. The item's count will be set to 1, then a new stack will be added with (origCount-1). + ContainerStoreIterator unstack (const Ptr& ptr, const Ptr& container, int count = 1); + ///< Unstack an item in this container. The item's count will be set to count, then a new stack will be added with (origCount-count). + /// + /// @return an iterator to the new stack, or end() if no new stack was created. MWWorld::ContainerStoreIterator restack (const MWWorld::Ptr& item); ///< Attempt to re-stack an item in this container. diff --git a/apps/openmw/mwworld/inventorystore.cpp b/apps/openmw/mwworld/inventorystore.cpp index dbd2c67940..2c994f1467 100644 --- a/apps/openmw/mwworld/inventorystore.cpp +++ b/apps/openmw/mwworld/inventorystore.cpp @@ -574,6 +574,33 @@ MWWorld::ContainerStoreIterator MWWorld::InventoryStore::unequipItem(const MWWor throw std::runtime_error ("attempt to unequip an item that is not currently equipped"); } +MWWorld::ContainerStoreIterator MWWorld::InventoryStore::unequipItemQuantity(const Ptr& item, const Ptr& actor, int count) +{ + if (!isEquipped(item)) + throw std::runtime_error ("attempt to unequip an item that is not currently equipped"); + if (count <= 0) + throw std::runtime_error ("attempt to unequip nothing (count <= 0)"); + if (count > item.getRefData().getCount()) + throw std::runtime_error ("attempt to unequip more items than equipped"); + + if (count == item.getRefData().getCount()) + return unequipItem(item, actor); + + // Move items to an existing stack if possible, otherwise split count items out into a new stack. + // Moving counts manually here, since ContainerStore's restack can't target unequipped stacks. + for (MWWorld::ContainerStoreIterator iter (begin()); iter != end(); ++iter) + { + if (stacks(*iter, item) && !isEquipped(*iter)) + { + iter->getRefData().setCount(iter->getRefData().getCount() + count); + item.getRefData().setCount(item.getRefData().getCount() - count); + return iter; + } + } + + return unstack(item, actor, item.getRefData().getCount() - count); +} + MWWorld::InventoryStoreListener* MWWorld::InventoryStore::getListener() { return mListener; diff --git a/apps/openmw/mwworld/inventorystore.hpp b/apps/openmw/mwworld/inventorystore.hpp index 28c44bcec1..2d7c9f6e9c 100644 --- a/apps/openmw/mwworld/inventorystore.hpp +++ b/apps/openmw/mwworld/inventorystore.hpp @@ -188,6 +188,15 @@ namespace MWWorld /// (it can be re-stacked so its count may be different than when it /// was equipped). + ContainerStoreIterator unequipItemQuantity(const Ptr& item, const Ptr& actor, int count); + ///< Unequip a specific quantity of an item identified by its Ptr. + /// An exception is thrown if the item is not currently equipped, + /// if count <= 0, or if count > the item stack size. + /// + /// @return an iterator to the unequipped items that were previously + /// in the slot (they can be re-stacked so its count may be different + /// than the requested count). + void setListener (InventoryStoreListener* listener, const Ptr& actor); ///< Set a listener for various events, see \a InventoryStoreListener From 1ff49cc637134e75e1101bbd14bcbffe2f4d3b24 Mon Sep 17 00:00:00 2001 From: Jordan Ayers Date: Mon, 18 Jan 2016 19:58:19 -0600 Subject: [PATCH 3/4] Improve Drop command behavior. (Fixes #1544) --- apps/openmw/mwscript/miscextensions.cpp | 35 +++++++++++++++++++++++-- 1 file changed, 33 insertions(+), 2 deletions(-) diff --git a/apps/openmw/mwscript/miscextensions.cpp b/apps/openmw/mwscript/miscextensions.cpp index 593fdcca52..51f0c6c55b 100644 --- a/apps/openmw/mwscript/miscextensions.cpp +++ b/apps/openmw/mwscript/miscextensions.cpp @@ -21,6 +21,7 @@ #include "../mwworld/class.hpp" #include "../mwworld/player.hpp" #include "../mwworld/containerstore.hpp" +#include "../mwworld/inventorystore.hpp" #include "../mwworld/esmstore.hpp" #include "../mwworld/cellstore.hpp" @@ -509,13 +510,43 @@ namespace MWScript if (amount == 0) return; - MWWorld::ContainerStore& store = ptr.getClass().getContainerStore (ptr); + // Prefer dropping unequipped items first; re-stack if possible by unequipping items before dropping them. + MWWorld::InventoryStore *invStorePtr = 0; + if (ptr.getClass().hasInventoryStore(ptr)) { + invStorePtr = &ptr.getClass().getInventoryStore(ptr); + int numNotEquipped = invStorePtr->count(item); + for (int slot = 0; slot < MWWorld::InventoryStore::Slots; ++slot) + { + MWWorld::ContainerStoreIterator it = invStorePtr->getSlot (slot); + if (it != invStorePtr->end() && ::Misc::StringUtils::ciEqual(it->getCellRef().getRefId(), item)) + { + numNotEquipped -= it->getRefData().getCount(); + } + } + + for (int slot = 0; slot < MWWorld::InventoryStore::Slots && amount > numNotEquipped; ++slot) + { + MWWorld::ContainerStoreIterator it = invStorePtr->getSlot (slot); + if (it != invStorePtr->end() && ::Misc::StringUtils::ciEqual(it->getCellRef().getRefId(), item)) + { + int numToRemove = it->getRefData().getCount(); + if (numToRemove > amount - numNotEquipped) + { + numToRemove = amount - numNotEquipped; + } + invStorePtr->unequipItemQuantity(*it, ptr, numToRemove); + numNotEquipped += numToRemove; + } + } + } int toRemove = amount; + MWWorld::ContainerStore& store = ptr.getClass().getContainerStore (ptr); for (MWWorld::ContainerStoreIterator iter (store.begin()); iter!=store.end(); ++iter) { - if (::Misc::StringUtils::ciEqual(iter->getCellRef().getRefId(), item)) + if (::Misc::StringUtils::ciEqual(iter->getCellRef().getRefId(), item) + && (!invStorePtr || !invStorePtr->isEquipped(*iter))) { int removed = store.remove(*iter, toRemove, ptr); MWWorld::Ptr dropped = MWBase::Environment::get().getWorld()->dropObjectOnGround(ptr, *iter, removed); From 5699cf7f0988d904fe4170124c9aec4faa729df7 Mon Sep 17 00:00:00 2001 From: Jordan Ayers Date: Mon, 18 Jan 2016 20:00:18 -0600 Subject: [PATCH 4/4] Barter: Leave unsold projectiles equipped. --- apps/openmw/mwgui/inventorywindow.cpp | 10 ++++++---- apps/openmw/mwgui/inventorywindow.hpp | 4 ++-- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/apps/openmw/mwgui/inventorywindow.cpp b/apps/openmw/mwgui/inventorywindow.cpp index 7678cb006c..facb17d66c 100644 --- a/apps/openmw/mwgui/inventorywindow.cpp +++ b/apps/openmw/mwgui/inventorywindow.cpp @@ -265,18 +265,20 @@ namespace MWGui } } - void InventoryWindow::ensureSelectedItemUnequipped() + void InventoryWindow::ensureSelectedItemUnequipped(int count) { const ItemStack& item = mTradeModel->getItem(mSelectedItem); if (item.mType == ItemStack::Type_Equipped) { MWWorld::InventoryStore& invStore = mPtr.getClass().getInventoryStore(mPtr); - MWWorld::Ptr newStack = *invStore.unequipItem(item.mBase, mPtr); + MWWorld::Ptr newStack = *invStore.unequipItemQuantity(item.mBase, mPtr, count); // The unequipped item was re-stacked. We have to update the index // since the item pointed does not exist anymore. if (item.mBase != newStack) { + updateItemView(); // Unequipping can produce a new stack, not yet in the window... + // newIndex will store the index of the ItemStack the item was stacked on int newIndex = -1; for (size_t i=0; i < mTradeModel->getItemCount(); ++i) @@ -298,14 +300,14 @@ namespace MWGui void InventoryWindow::dragItem(MyGUI::Widget* sender, int count) { - ensureSelectedItemUnequipped(); + ensureSelectedItemUnequipped(count); mDragAndDrop->startDrag(mSelectedItem, mSortModel, mTradeModel, mItemView, count); notifyContentChanged(); } void InventoryWindow::sellItem(MyGUI::Widget* sender, int count) { - ensureSelectedItemUnequipped(); + ensureSelectedItemUnequipped(count); const ItemStack& item = mTradeModel->getItem(mSelectedItem); std::string sound = item.mBase.getClass().getDownSoundId(item.mBase); MWBase::Environment::get().getSoundManager()->playSound (sound, 1.0, 1.0); diff --git a/apps/openmw/mwgui/inventorywindow.hpp b/apps/openmw/mwgui/inventorywindow.hpp index a8a1b15a13..b7ae067ace 100644 --- a/apps/openmw/mwgui/inventorywindow.hpp +++ b/apps/openmw/mwgui/inventorywindow.hpp @@ -126,8 +126,8 @@ namespace MWGui void adjustPanes(); - /// Unequips mSelectedItem, if it is equipped, and then updates mSelectedItem in case it was re-stacked - void ensureSelectedItemUnequipped(); + /// Unequips count items from mSelectedItem, if it is equipped, and then updates mSelectedItem in case the items were re-stacked + void ensureSelectedItemUnequipped(int count); }; }