Merge pull request #879 from jordan-ayers/bugfix/1544

Improve Drop Command - Bugfix/1544
coverity_scan
scrawl 9 years ago
commit ce9a18fb87

@ -265,18 +265,20 @@ namespace MWGui
} }
} }
void InventoryWindow::ensureSelectedItemUnequipped() void InventoryWindow::ensureSelectedItemUnequipped(int count)
{ {
const ItemStack& item = mTradeModel->getItem(mSelectedItem); const ItemStack& item = mTradeModel->getItem(mSelectedItem);
if (item.mType == ItemStack::Type_Equipped) if (item.mType == ItemStack::Type_Equipped)
{ {
MWWorld::InventoryStore& invStore = mPtr.getClass().getInventoryStore(mPtr); 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 // The unequipped item was re-stacked. We have to update the index
// since the item pointed does not exist anymore. // since the item pointed does not exist anymore.
if (item.mBase != newStack) 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 // newIndex will store the index of the ItemStack the item was stacked on
int newIndex = -1; int newIndex = -1;
for (size_t i=0; i < mTradeModel->getItemCount(); ++i) for (size_t i=0; i < mTradeModel->getItemCount(); ++i)
@ -298,14 +300,14 @@ namespace MWGui
void InventoryWindow::dragItem(MyGUI::Widget* sender, int count) void InventoryWindow::dragItem(MyGUI::Widget* sender, int count)
{ {
ensureSelectedItemUnequipped(); ensureSelectedItemUnequipped(count);
mDragAndDrop->startDrag(mSelectedItem, mSortModel, mTradeModel, mItemView, count); mDragAndDrop->startDrag(mSelectedItem, mSortModel, mTradeModel, mItemView, count);
notifyContentChanged(); notifyContentChanged();
} }
void InventoryWindow::sellItem(MyGUI::Widget* sender, int count) void InventoryWindow::sellItem(MyGUI::Widget* sender, int count)
{ {
ensureSelectedItemUnequipped(); ensureSelectedItemUnequipped(count);
const ItemStack& item = mTradeModel->getItem(mSelectedItem); const ItemStack& item = mTradeModel->getItem(mSelectedItem);
std::string sound = item.mBase.getClass().getDownSoundId(item.mBase); std::string sound = item.mBase.getClass().getDownSoundId(item.mBase);
MWBase::Environment::get().getSoundManager()->playSound (sound, 1.0, 1.0); MWBase::Environment::get().getSoundManager()->playSound (sound, 1.0, 1.0);

@ -126,8 +126,8 @@ namespace MWGui
void adjustPanes(); void adjustPanes();
/// Unequips mSelectedItem, if it is equipped, and then updates mSelectedItem in case it was re-stacked /// Unequips count items from mSelectedItem, if it is equipped, and then updates mSelectedItem in case the items were re-stacked
void ensureSelectedItemUnequipped(); void ensureSelectedItemUnequipped(int count);
}; };
} }

@ -21,6 +21,7 @@
#include "../mwworld/class.hpp" #include "../mwworld/class.hpp"
#include "../mwworld/player.hpp" #include "../mwworld/player.hpp"
#include "../mwworld/containerstore.hpp" #include "../mwworld/containerstore.hpp"
#include "../mwworld/inventorystore.hpp"
#include "../mwworld/esmstore.hpp" #include "../mwworld/esmstore.hpp"
#include "../mwworld/cellstore.hpp" #include "../mwworld/cellstore.hpp"
@ -509,13 +510,43 @@ namespace MWScript
if (amount == 0) if (amount == 0)
return; 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; int toRemove = amount;
MWWorld::ContainerStore& store = ptr.getClass().getContainerStore (ptr);
for (MWWorld::ContainerStoreIterator iter (store.begin()); iter!=store.end(); ++iter) 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); int removed = store.remove(*iter, toRemove, ptr);
MWWorld::Ptr dropped = MWBase::Environment::get().getWorld()->dropObjectOnGround(ptr, *iter, removed); MWWorld::Ptr dropped = MWBase::Environment::get().getWorld()->dropObjectOnGround(ptr, *iter, removed);

@ -136,16 +136,18 @@ int MWWorld::ContainerStore::count(const std::string &id)
return total; 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) if (ptr.getRefData().getCount() <= count)
return; return end();
MWWorld::ContainerStoreIterator it = addNewStack(ptr, ptr.getRefData().getCount()-1); MWWorld::ContainerStoreIterator it = addNewStack(ptr, ptr.getRefData().getCount()-count);
const std::string script = it->getClass().getScript(*it); const std::string script = it->getClass().getScript(*it);
if (!script.empty()) if (!script.empty())
MWBase::Environment::get().getWorld()->getLocalScripts().add(script, *it); 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) MWWorld::ContainerStoreIterator MWWorld::ContainerStore::restack(const MWWorld::Ptr& item)

@ -130,8 +130,10 @@ namespace MWWorld
/// ///
/// @return the number of items actually removed /// @return the number of items actually removed
void unstack (const Ptr& ptr, const Ptr& container); 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 1, then a new stack will be added with (origCount-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); MWWorld::ContainerStoreIterator restack (const MWWorld::Ptr& item);
///< Attempt to re-stack an item in this container. ///< Attempt to re-stack an item in this container.

@ -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) MWWorld::ContainerStoreIterator MWWorld::InventoryStore::unequipSlot(int slot, const MWWorld::Ptr& actor)
{ {
if (slot<0 || slot>=static_cast<int> (mSlots.size()))
throw std::runtime_error ("slot number out of range");
ContainerStoreIterator it = mSlots[slot]; ContainerStoreIterator it = mSlots[slot];
if (it != end()) if (it != end())
@ -571,6 +574,33 @@ MWWorld::ContainerStoreIterator MWWorld::InventoryStore::unequipItem(const MWWor
throw std::runtime_error ("attempt to unequip an item that is not currently equipped"); 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() MWWorld::InventoryStoreListener* MWWorld::InventoryStore::getListener()
{ {
return mListener; return mListener;

@ -188,6 +188,15 @@ namespace MWWorld
/// (it can be re-stacked so its count may be different than when it /// (it can be re-stacked so its count may be different than when it
/// was equipped). /// 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); void setListener (InventoryStoreListener* listener, const Ptr& actor);
///< Set a listener for various events, see \a InventoryStoreListener ///< Set a listener for various events, see \a InventoryStoreListener

Loading…
Cancel
Save