From 8c3c5238d78267933877368b26ee873d82235c59 Mon Sep 17 00:00:00 2001 From: Mads Buvik Sandvei Date: Sat, 8 Jul 2023 17:10:07 +0200 Subject: [PATCH 1/7] Preserve refnum when dropping items from inventory to ground. --- apps/openmw/mwbase/world.hpp | 7 ++- apps/openmw/mwgui/companionitemmodel.cpp | 8 +++ apps/openmw/mwgui/companionitemmodel.hpp | 1 + apps/openmw/mwgui/hud.cpp | 17 ++++-- apps/openmw/mwgui/inventoryitemmodel.cpp | 20 ++++++- apps/openmw/mwgui/inventoryitemmodel.hpp | 2 + apps/openmw/mwgui/itemmodel.cpp | 28 ++++++++-- apps/openmw/mwgui/itemmodel.hpp | 7 +++ apps/openmw/mwworld/worldimp.cpp | 67 ++++++++++++++++++------ apps/openmw/mwworld/worldimp.hpp | 7 ++- 10 files changed, 136 insertions(+), 28 deletions(-) diff --git a/apps/openmw/mwbase/world.hpp b/apps/openmw/mwbase/world.hpp index ac572b35b0..1194402b91 100644 --- a/apps/openmw/mwbase/world.hpp +++ b/apps/openmw/mwbase/world.hpp @@ -337,14 +337,17 @@ namespace MWBase ///< Toggle a render mode. ///< \return Resulting mode - virtual MWWorld::Ptr placeObject(const MWWorld::ConstPtr& object, float cursorX, float cursorY, int amount) = 0; + virtual MWWorld::Ptr placeObject( + const MWWorld::Ptr& object, float cursorX, float cursorY, int amount, bool copy = true) + = 0; ///< copy and place an object into the gameworld at the specified cursor position /// @param object /// @param cursor X (relative 0-1) /// @param cursor Y (relative 0-1) /// @param number of objects to place - virtual MWWorld::Ptr dropObjectOnGround(const MWWorld::Ptr& actor, const MWWorld::ConstPtr& object, int amount) + virtual MWWorld::Ptr dropObjectOnGround( + const MWWorld::Ptr& actor, const MWWorld::Ptr& object, int amount, bool copy = true) = 0; ///< copy and place an object into the gameworld at the given actor's position /// @param actor giving the dropped object position diff --git a/apps/openmw/mwgui/companionitemmodel.cpp b/apps/openmw/mwgui/companionitemmodel.cpp index 1172de5897..6bae8aef52 100644 --- a/apps/openmw/mwgui/companionitemmodel.cpp +++ b/apps/openmw/mwgui/companionitemmodel.cpp @@ -25,6 +25,14 @@ namespace MWGui { } + MWWorld::Ptr CompanionItemModel::addItem(const ItemStack& item, size_t count, bool allowAutoEquip) + { + if (hasProfit(mActor)) + modifyProfit(mActor, item.mBase.getClass().getValue(item.mBase) * count); + + return InventoryItemModel::addItem(item, count, allowAutoEquip); + } + MWWorld::Ptr CompanionItemModel::copyItem(const ItemStack& item, size_t count, bool allowAutoEquip) { if (hasProfit(mActor)) diff --git a/apps/openmw/mwgui/companionitemmodel.hpp b/apps/openmw/mwgui/companionitemmodel.hpp index 0a9491c348..b9ee500693 100644 --- a/apps/openmw/mwgui/companionitemmodel.hpp +++ b/apps/openmw/mwgui/companionitemmodel.hpp @@ -13,6 +13,7 @@ namespace MWGui public: CompanionItemModel(const MWWorld::Ptr& actor); + MWWorld::Ptr addItem(const ItemStack& item, size_t count, bool allowAutoEquip = true) override; MWWorld::Ptr copyItem(const ItemStack& item, size_t count, bool allowAutoEquip = true) override; void removeItem(const ItemStack& item, size_t count) override; diff --git a/apps/openmw/mwgui/hud.cpp b/apps/openmw/mwgui/hud.cpp index 4df0e1dba4..022840ea5e 100644 --- a/apps/openmw/mwgui/hud.cpp +++ b/apps/openmw/mwgui/hud.cpp @@ -45,20 +45,31 @@ namespace MWGui { } virtual ~WorldItemModel() override {} - MWWorld::Ptr copyItem(const ItemStack& item, size_t count, bool /*allowAutoEquip*/) override + + MWWorld::Ptr dropItemImpl(const ItemStack& item, size_t count, bool copy) { MWBase::World* world = MWBase::Environment::get().getWorld(); MWWorld::Ptr dropped; if (world->canPlaceObject(mLeft, mTop)) - dropped = world->placeObject(item.mBase, mLeft, mTop, count); + dropped = world->placeObject(item.mBase, mLeft, mTop, count, copy); else - dropped = world->dropObjectOnGround(world->getPlayerPtr(), item.mBase, count); + dropped = world->dropObjectOnGround(world->getPlayerPtr(), item.mBase, count, copy); dropped.getCellRef().setOwner(ESM::RefId()); return dropped; } + MWWorld::Ptr addItem(const ItemStack& item, size_t count, bool /*allowAutoEquip*/) override + { + return dropItemImpl(item, count, false); + } + + MWWorld::Ptr copyItem(const ItemStack& item, size_t count, bool /*allowAutoEquip*/) override + { + return dropItemImpl(item, count, true); + } + void removeItem(const ItemStack& item, size_t count) override { throw std::runtime_error("removeItem not implemented"); diff --git a/apps/openmw/mwgui/inventoryitemmodel.cpp b/apps/openmw/mwgui/inventoryitemmodel.cpp index bcd51cd1b8..3620ceaf69 100644 --- a/apps/openmw/mwgui/inventoryitemmodel.cpp +++ b/apps/openmw/mwgui/inventoryitemmodel.cpp @@ -46,13 +46,29 @@ namespace MWGui return -1; } - MWWorld::Ptr InventoryItemModel::copyItem(const ItemStack& item, size_t count, bool allowAutoEquip) + MWWorld::Ptr InventoryItemModel::addItem(const ItemStack& item, size_t count, bool allowAutoEquip) { if (item.mBase.getContainerStore() == &mActor.getClass().getContainerStore(mActor)) - throw std::runtime_error("Item to copy needs to be from a different container!"); + throw std::runtime_error("Item to add needs to be from a different container!"); return *mActor.getClass().getContainerStore(mActor).add(item.mBase, count, allowAutoEquip); } + MWWorld::Ptr InventoryItemModel::copyItem(const ItemStack& item, size_t count, bool allowAutoEquip) + { + // TODO: This does not copy the item, but adds it directly. This will duplicate the item's + // refnum and other ref data unless the caller handles that. + return addItem(item, count, allowAutoEquip); + } + + MWWorld::Ptr InventoryItemModel::unstackItem(const ItemStack& item, size_t count) + { + MWWorld::ContainerStore& store = mActor.getClass().getContainerStore(mActor); + auto it = store.unstack(item.mBase, count); + if (it != store.end()) + return *it; + return MWWorld::Ptr(); + } + void InventoryItemModel::removeItem(const ItemStack& item, size_t count) { int removed = 0; diff --git a/apps/openmw/mwgui/inventoryitemmodel.hpp b/apps/openmw/mwgui/inventoryitemmodel.hpp index b99bfc544d..78cd0a739e 100644 --- a/apps/openmw/mwgui/inventoryitemmodel.hpp +++ b/apps/openmw/mwgui/inventoryitemmodel.hpp @@ -17,7 +17,9 @@ namespace MWGui bool onTakeItem(const MWWorld::Ptr& item, int count) override; + MWWorld::Ptr addItem(const ItemStack& item, size_t count, bool allowAutoEquip = true) override; MWWorld::Ptr copyItem(const ItemStack& item, size_t count, bool allowAutoEquip = true) override; + MWWorld::Ptr unstackItem(const ItemStack& item, size_t count) override; void removeItem(const ItemStack& item, size_t count) override; /// Move items from this model to \a otherModel. diff --git a/apps/openmw/mwgui/itemmodel.cpp b/apps/openmw/mwgui/itemmodel.cpp index 9b9815c98a..12f1b5842d 100644 --- a/apps/openmw/mwgui/itemmodel.cpp +++ b/apps/openmw/mwgui/itemmodel.cpp @@ -57,13 +57,25 @@ namespace MWGui MWWorld::Ptr ItemModel::moveItem(const ItemStack& item, size_t count, ItemModel* otherModel) { - // TODO(#6148): moving an item should preserve RefNum and Lua scripts (unless the item stack is merged with - // already existing stack). - MWWorld::Ptr ret = otherModel->copyItem(item, count); + MWWorld::Ptr ret = otherModel->addItem(item, count); + // Unstacking here ensures that new a refnum is assigned to the leftover stack if there is a leftover. + // Otherwise we end up with duplicated instances. + unstackItem(item, count); removeItem(item, count); return ret; } + MWWorld::Ptr ItemModel::addItem(const ItemStack& item, size_t count, bool allowAutoEquip) + { + return copyItem(item, count, allowAutoEquip); + } + + MWWorld::Ptr ItemModel::unstackItem(const ItemStack& item, size_t count) + { + // By default does nothing + return MWWorld::Ptr(); + } + bool ItemModel::allowedToUseItems() const { return true; @@ -143,6 +155,16 @@ namespace MWGui return mSourceModel->onTakeItem(item, count); } + MWWorld::Ptr ProxyItemModel::unstackItem(const ItemStack& item, size_t count) + { + return mSourceModel->unstackItem(item, count); + } + + MWWorld::Ptr ProxyItemModel::addItem(const ItemStack& item, size_t count, bool allowAutoEquip) + { + return mSourceModel->addItem(item, count, allowAutoEquip); + } + bool ProxyItemModel::usesContainer(const MWWorld::Ptr& container) { return mSourceModel->usesContainer(container); diff --git a/apps/openmw/mwgui/itemmodel.hpp b/apps/openmw/mwgui/itemmodel.hpp index c7143b4b91..5ea3ce78e3 100644 --- a/apps/openmw/mwgui/itemmodel.hpp +++ b/apps/openmw/mwgui/itemmodel.hpp @@ -65,6 +65,11 @@ namespace MWGui /// @note Derived implementations may return an empty Ptr if the move was unsuccessful. virtual MWWorld::Ptr moveItem(const ItemStack& item, size_t count, ItemModel* otherModel); + /// Unstacks items from this model and returns a ptr to the new remainder stack. + /// @note Returns en empty ptr if there is no remainder or the item model does not support unstacking. + virtual MWWorld::Ptr unstackItem(const ItemStack& item, size_t count); + + virtual MWWorld::Ptr addItem(const ItemStack& item, size_t count, bool allowAutoEquip = true); virtual MWWorld::Ptr copyItem(const ItemStack& item, size_t count, bool allowAutoEquip = true) = 0; virtual void removeItem(const ItemStack& item, size_t count) = 0; @@ -95,6 +100,8 @@ namespace MWGui bool onDropItem(const MWWorld::Ptr& item, int count) override; bool onTakeItem(const MWWorld::Ptr& item, int count) override; + MWWorld::Ptr unstackItem(const ItemStack& item, size_t count) override; + MWWorld::Ptr addItem(const ItemStack& item, size_t count, bool allowAutoEquip = true) override; MWWorld::Ptr copyItem(const ItemStack& item, size_t count, bool allowAutoEquip = true) override; void removeItem(const ItemStack& item, size_t count) override; ModelIndex getIndex(const ItemStack& item) override; diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index 3f246628cc..23b507f4dc 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -2001,7 +2001,7 @@ namespace MWWorld item.getRefData().getLocals().setVarByInt(script, "onpcdrop", 1); } - MWWorld::Ptr World::placeObject(const MWWorld::ConstPtr& object, float cursorX, float cursorY, int amount) + MWWorld::Ptr World::placeObject(const MWWorld::Ptr& object, float cursorX, float cursorY, int amount, bool copy) { const float maxDist = 200.f; @@ -2022,7 +2022,8 @@ namespace MWWorld pos.rot[1] = 0; // copy the object and set its count - Ptr dropped = copyObjectToCell(object, cell, pos, amount, true); + Ptr dropped = copy ? copyObjectToCell(object, cell, pos, amount, true) + : moveObjectToCell(object, cell, pos, amount, true); // only the player place items in the world, so no need to check actor PCDropped(dropped); @@ -2062,30 +2063,65 @@ namespace MWWorld MWWorld::Ptr dropped = object.getClass().copyToCell(object, *cell, pos, count); + addObjectToCell(dropped, cell, pos, adjustPos); + + return dropped; + } + + Ptr World::moveObjectToCell(const Ptr& object, CellStore* cell, ESM::Position pos, int count, bool adjustPos) + { + if (!cell) + throw std::runtime_error("moveObjectToCell(): cannot move object to null cell"); + if (cell->isExterior()) + { + const ESM::ExteriorCellLocation index + = ESM::positionToExteriorCellLocation(pos.pos[0], pos.pos[1], cell->getCell()->getWorldSpace()); + cell = &mWorldModel.getExterior(index); + } + + MWWorld::Ptr dropped = object.getClass().moveToCell(object, *cell); + dropped.getRefData().setCount(count); + dropped.getRefData().setPosition(pos); + + addObjectToCell(dropped, cell, pos, adjustPos); + + return dropped; + } + + void World::addObjectToCell(const Ptr& object, CellStore* cell, ESM::Position pos, bool adjustPos) + { + if (!cell) + throw std::runtime_error("addObjectToCell(): cannot add object to null cell"); + if (cell->isExterior()) + { + const ESM::ExteriorCellLocation index + = ESM::positionToExteriorCellLocation(pos.pos[0], pos.pos[1], cell->getCell()->getWorldSpace()); + cell = &mWorldModel.getExterior(index); + } + // Reset some position values that could be uninitialized if this item came from a container - dropped.getCellRef().setPosition(pos); - dropped.getCellRef().unsetRefNum(); + object.getCellRef().setPosition(pos); if (mWorldScene->isCellActive(*cell)) { - if (dropped.getRefData().isEnabled()) + if (object.getRefData().isEnabled()) { - mWorldScene->addObjectToScene(dropped); + mWorldScene->addObjectToScene(object); } - const auto& script = dropped.getClass().getScript(dropped); + const auto& script = object.getClass().getScript(object); if (!script.empty()) { - mLocalScripts.add(script, dropped); + mLocalScripts.add(script, object); } - addContainerScripts(dropped, cell); + addContainerScripts(object, cell); } - if (!object.getClass().isActor() && adjustPos && dropped.getRefData().getBaseNode()) + if (!object.getClass().isActor() && adjustPos && object.getRefData().getBaseNode()) { // Adjust position so the location we wanted ends up in the middle of the object bounding box osg::ComputeBoundsVisitor computeBounds; computeBounds.setTraversalMask(~MWRender::Mask_ParticleSystem); - dropped.getRefData().getBaseNode()->accept(computeBounds); + object.getRefData().getBaseNode()->accept(computeBounds); osg::BoundingBox bounds = computeBounds.getBoundingBox(); if (bounds.valid()) { @@ -2096,14 +2132,12 @@ namespace MWWorld pos.pos[0] -= adjust.x(); pos.pos[1] -= adjust.y(); pos.pos[2] -= adjust.z(); - moveObject(dropped, pos.asVec3()); + moveObject(object, pos.asVec3()); } } - - return dropped; } - MWWorld::Ptr World::dropObjectOnGround(const Ptr& actor, const ConstPtr& object, int amount) + MWWorld::Ptr World::dropObjectOnGround(const Ptr& actor, const Ptr& object, int amount, bool copy) { MWWorld::CellStore* cell = actor.getCell(); @@ -2123,7 +2157,8 @@ namespace MWWorld pos.pos[2] = result.mHitPointWorld.z(); // copy the object and set its count - Ptr dropped = copyObjectToCell(object, cell, pos, amount, true); + Ptr dropped = copy ? copyObjectToCell(object, cell, pos, amount, true) + : moveObjectToCell(object, cell, pos, amount, true); if (actor == mPlayer->getPlayer()) // Only call if dropped by player PCDropped(dropped); diff --git a/apps/openmw/mwworld/worldimp.hpp b/apps/openmw/mwworld/worldimp.hpp index e15f5808d3..b2ea5986fa 100644 --- a/apps/openmw/mwworld/worldimp.hpp +++ b/apps/openmw/mwworld/worldimp.hpp @@ -143,6 +143,8 @@ namespace MWWorld void updateWeather(float duration, bool paused = false); + void addObjectToCell(const Ptr& ptr, CellStore* cell, ESM::Position pos, bool adjustPos); + Ptr moveObjectToCell(const Ptr& ptr, CellStore* cell, ESM::Position pos, int count, bool adjustPos); Ptr copyObjectToCell(const ConstPtr& ptr, CellStore* cell, ESM::Position pos, int count, bool adjustPos); void updateSoundListener(); @@ -425,7 +427,8 @@ namespace MWWorld void updateWindowManager(); - MWWorld::Ptr placeObject(const MWWorld::ConstPtr& object, float cursorX, float cursorY, int amount) override; + MWWorld::Ptr placeObject( + const MWWorld::Ptr& object, float cursorX, float cursorY, int amount, bool copy = true) override; ///< copy and place an object into the gameworld at the specified cursor position /// @param object /// @param cursor X (relative 0-1) @@ -433,7 +436,7 @@ namespace MWWorld /// @param number of objects to place MWWorld::Ptr dropObjectOnGround( - const MWWorld::Ptr& actor, const MWWorld::ConstPtr& object, int amount) override; + const MWWorld::Ptr& actor, const MWWorld::Ptr& object, int amount, bool copy = true) override; ///< copy and place an object into the gameworld at the given actor's position /// @param actor giving the dropped object position /// @param object From 6e03d710ba64ad48d30d6248e10f86ea7b041a90 Mon Sep 17 00:00:00 2001 From: Mads Buvik Sandvei Date: Sat, 15 Jul 2023 18:19:52 +0200 Subject: [PATCH 2/7] Remove ItemModel::copyItem(), it is obsolete. --- apps/openmw/mwgui/companionitemmodel.cpp | 8 -------- apps/openmw/mwgui/companionitemmodel.hpp | 1 - apps/openmw/mwgui/containeritemmodel.cpp | 4 ++-- apps/openmw/mwgui/containeritemmodel.hpp | 2 +- apps/openmw/mwgui/hud.cpp | 16 +++------------- apps/openmw/mwgui/inventoryitemmodel.cpp | 13 ++----------- apps/openmw/mwgui/inventoryitemmodel.hpp | 3 +-- apps/openmw/mwgui/itemmodel.cpp | 14 ++------------ apps/openmw/mwgui/itemmodel.hpp | 6 ++---- apps/openmw/mwgui/tradeitemmodel.cpp | 7 ++----- 10 files changed, 15 insertions(+), 59 deletions(-) diff --git a/apps/openmw/mwgui/companionitemmodel.cpp b/apps/openmw/mwgui/companionitemmodel.cpp index 6bae8aef52..3c017a2856 100644 --- a/apps/openmw/mwgui/companionitemmodel.cpp +++ b/apps/openmw/mwgui/companionitemmodel.cpp @@ -33,14 +33,6 @@ namespace MWGui return InventoryItemModel::addItem(item, count, allowAutoEquip); } - MWWorld::Ptr CompanionItemModel::copyItem(const ItemStack& item, size_t count, bool allowAutoEquip) - { - if (hasProfit(mActor)) - modifyProfit(mActor, item.mBase.getClass().getValue(item.mBase) * count); - - return InventoryItemModel::copyItem(item, count, allowAutoEquip); - } - void CompanionItemModel::removeItem(const ItemStack& item, size_t count) { if (hasProfit(mActor)) diff --git a/apps/openmw/mwgui/companionitemmodel.hpp b/apps/openmw/mwgui/companionitemmodel.hpp index b9ee500693..49c84fe075 100644 --- a/apps/openmw/mwgui/companionitemmodel.hpp +++ b/apps/openmw/mwgui/companionitemmodel.hpp @@ -14,7 +14,6 @@ namespace MWGui CompanionItemModel(const MWWorld::Ptr& actor); MWWorld::Ptr addItem(const ItemStack& item, size_t count, bool allowAutoEquip = true) override; - MWWorld::Ptr copyItem(const ItemStack& item, size_t count, bool allowAutoEquip = true) override; void removeItem(const ItemStack& item, size_t count) override; bool hasProfit(const MWWorld::Ptr& actor); diff --git a/apps/openmw/mwgui/containeritemmodel.cpp b/apps/openmw/mwgui/containeritemmodel.cpp index b0abe5c733..e072f9974a 100644 --- a/apps/openmw/mwgui/containeritemmodel.cpp +++ b/apps/openmw/mwgui/containeritemmodel.cpp @@ -99,12 +99,12 @@ namespace MWGui return -1; } - MWWorld::Ptr ContainerItemModel::copyItem(const ItemStack& item, size_t count, bool allowAutoEquip) + MWWorld::Ptr ContainerItemModel::addItem(const ItemStack& item, size_t count, bool allowAutoEquip) { auto& source = mItemSources[0]; MWWorld::ContainerStore& store = source.first.getClass().getContainerStore(source.first); if (item.mBase.getContainerStore() == &store) - throw std::runtime_error("Item to copy needs to be from a different container!"); + throw std::runtime_error("Item needs to be from a different container!"); return *store.add(item.mBase, count, allowAutoEquip); } diff --git a/apps/openmw/mwgui/containeritemmodel.hpp b/apps/openmw/mwgui/containeritemmodel.hpp index 1bdcec90f9..52b1911411 100644 --- a/apps/openmw/mwgui/containeritemmodel.hpp +++ b/apps/openmw/mwgui/containeritemmodel.hpp @@ -32,7 +32,7 @@ namespace MWGui ModelIndex getIndex(const ItemStack& item) override; size_t getItemCount() override; - MWWorld::Ptr copyItem(const ItemStack& item, size_t count, bool allowAutoEquip = true) override; + MWWorld::Ptr addItem(const ItemStack& item, size_t count, bool allowAutoEquip = true) override; void removeItem(const ItemStack& item, size_t count) override; void update() override; diff --git a/apps/openmw/mwgui/hud.cpp b/apps/openmw/mwgui/hud.cpp index 022840ea5e..b936620895 100644 --- a/apps/openmw/mwgui/hud.cpp +++ b/apps/openmw/mwgui/hud.cpp @@ -46,30 +46,20 @@ namespace MWGui } virtual ~WorldItemModel() override {} - MWWorld::Ptr dropItemImpl(const ItemStack& item, size_t count, bool copy) + MWWorld::Ptr addItem(const ItemStack& item, size_t count, bool /*allowAutoEquip*/) override { MWBase::World* world = MWBase::Environment::get().getWorld(); MWWorld::Ptr dropped; if (world->canPlaceObject(mLeft, mTop)) - dropped = world->placeObject(item.mBase, mLeft, mTop, count, copy); + dropped = world->placeObject(item.mBase, mLeft, mTop, count, false); else - dropped = world->dropObjectOnGround(world->getPlayerPtr(), item.mBase, count, copy); + dropped = world->dropObjectOnGround(world->getPlayerPtr(), item.mBase, count, false); dropped.getCellRef().setOwner(ESM::RefId()); return dropped; } - MWWorld::Ptr addItem(const ItemStack& item, size_t count, bool /*allowAutoEquip*/) override - { - return dropItemImpl(item, count, false); - } - - MWWorld::Ptr copyItem(const ItemStack& item, size_t count, bool /*allowAutoEquip*/) override - { - return dropItemImpl(item, count, true); - } - void removeItem(const ItemStack& item, size_t count) override { throw std::runtime_error("removeItem not implemented"); diff --git a/apps/openmw/mwgui/inventoryitemmodel.cpp b/apps/openmw/mwgui/inventoryitemmodel.cpp index 3620ceaf69..ce4d1fc1d0 100644 --- a/apps/openmw/mwgui/inventoryitemmodel.cpp +++ b/apps/openmw/mwgui/inventoryitemmodel.cpp @@ -53,13 +53,6 @@ namespace MWGui return *mActor.getClass().getContainerStore(mActor).add(item.mBase, count, allowAutoEquip); } - MWWorld::Ptr InventoryItemModel::copyItem(const ItemStack& item, size_t count, bool allowAutoEquip) - { - // TODO: This does not copy the item, but adds it directly. This will duplicate the item's - // refnum and other ref data unless the caller handles that. - return addItem(item, count, allowAutoEquip); - } - MWWorld::Ptr InventoryItemModel::unstackItem(const ItemStack& item, size_t count) { MWWorld::ContainerStore& store = mActor.getClass().getContainerStore(mActor); @@ -99,16 +92,14 @@ namespace MWGui } } - MWWorld::Ptr InventoryItemModel::moveItem(const ItemStack& item, size_t count, ItemModel* otherModel) + MWWorld::Ptr InventoryItemModel::moveItem(const ItemStack& item, size_t count, ItemModel* otherModel, bool allowAutoEquip) { // Can't move conjured items: This is a general fix that also takes care of issues with taking conjured items // via the 'Take All' button. if (item.mFlags & ItemStack::Flag_Bound) return MWWorld::Ptr(); - MWWorld::Ptr ret = otherModel->copyItem(item, count); - removeItem(item, count); - return ret; + return ItemModel::moveItem(item, count, otherModel, allowAutoEquip); } void InventoryItemModel::update() diff --git a/apps/openmw/mwgui/inventoryitemmodel.hpp b/apps/openmw/mwgui/inventoryitemmodel.hpp index 78cd0a739e..6844291cb6 100644 --- a/apps/openmw/mwgui/inventoryitemmodel.hpp +++ b/apps/openmw/mwgui/inventoryitemmodel.hpp @@ -18,12 +18,11 @@ namespace MWGui bool onTakeItem(const MWWorld::Ptr& item, int count) override; MWWorld::Ptr addItem(const ItemStack& item, size_t count, bool allowAutoEquip = true) override; - MWWorld::Ptr copyItem(const ItemStack& item, size_t count, bool allowAutoEquip = true) override; MWWorld::Ptr unstackItem(const ItemStack& item, size_t count) override; void removeItem(const ItemStack& item, size_t count) override; /// Move items from this model to \a otherModel. - MWWorld::Ptr moveItem(const ItemStack& item, size_t count, ItemModel* otherModel) override; + MWWorld::Ptr moveItem(const ItemStack& item, size_t count, ItemModel* otherModel, bool allowAutoEquip = true) override; void update() override; diff --git a/apps/openmw/mwgui/itemmodel.cpp b/apps/openmw/mwgui/itemmodel.cpp index 12f1b5842d..25b66c28c1 100644 --- a/apps/openmw/mwgui/itemmodel.cpp +++ b/apps/openmw/mwgui/itemmodel.cpp @@ -55,9 +55,9 @@ namespace MWGui ItemModel::ItemModel() {} - MWWorld::Ptr ItemModel::moveItem(const ItemStack& item, size_t count, ItemModel* otherModel) + MWWorld::Ptr ItemModel::moveItem(const ItemStack& item, size_t count, ItemModel* otherModel, bool allowAutoEquip) { - MWWorld::Ptr ret = otherModel->addItem(item, count); + MWWorld::Ptr ret = otherModel->addItem(item, count, allowAutoEquip); // Unstacking here ensures that new a refnum is assigned to the leftover stack if there is a leftover. // Otherwise we end up with duplicated instances. unstackItem(item, count); @@ -65,11 +65,6 @@ namespace MWGui return ret; } - MWWorld::Ptr ItemModel::addItem(const ItemStack& item, size_t count, bool allowAutoEquip) - { - return copyItem(item, count, allowAutoEquip); - } - MWWorld::Ptr ItemModel::unstackItem(const ItemStack& item, size_t count) { // By default does nothing @@ -96,11 +91,6 @@ namespace MWGui return mSourceModel->allowedToUseItems(); } - MWWorld::Ptr ProxyItemModel::copyItem(const ItemStack& item, size_t count, bool allowAutoEquip) - { - return mSourceModel->copyItem(item, count, allowAutoEquip); - } - void ProxyItemModel::removeItem(const ItemStack& item, size_t count) { mSourceModel->removeItem(item, count); diff --git a/apps/openmw/mwgui/itemmodel.hpp b/apps/openmw/mwgui/itemmodel.hpp index 5ea3ce78e3..faf783ea70 100644 --- a/apps/openmw/mwgui/itemmodel.hpp +++ b/apps/openmw/mwgui/itemmodel.hpp @@ -63,14 +63,13 @@ namespace MWGui /// Move items from this model to \a otherModel. /// @note Derived implementations may return an empty Ptr if the move was unsuccessful. - virtual MWWorld::Ptr moveItem(const ItemStack& item, size_t count, ItemModel* otherModel); + virtual MWWorld::Ptr moveItem(const ItemStack& item, size_t count, ItemModel* otherModel, bool allowAutoEquip = true); /// Unstacks items from this model and returns a ptr to the new remainder stack. /// @note Returns en empty ptr if there is no remainder or the item model does not support unstacking. virtual MWWorld::Ptr unstackItem(const ItemStack& item, size_t count); - virtual MWWorld::Ptr addItem(const ItemStack& item, size_t count, bool allowAutoEquip = true); - virtual MWWorld::Ptr copyItem(const ItemStack& item, size_t count, bool allowAutoEquip = true) = 0; + virtual MWWorld::Ptr addItem(const ItemStack& item, size_t count, bool allowAutoEquip = true) = 0; virtual void removeItem(const ItemStack& item, size_t count) = 0; /// Is the player allowed to use items from this item model? (default true) @@ -102,7 +101,6 @@ namespace MWGui MWWorld::Ptr unstackItem(const ItemStack& item, size_t count) override; MWWorld::Ptr addItem(const ItemStack& item, size_t count, bool allowAutoEquip = true) override; - MWWorld::Ptr copyItem(const ItemStack& item, size_t count, bool allowAutoEquip = true) override; void removeItem(const ItemStack& item, size_t count) override; ModelIndex getIndex(const ItemStack& item) override; diff --git a/apps/openmw/mwgui/tradeitemmodel.cpp b/apps/openmw/mwgui/tradeitemmodel.cpp index f14d7fe72c..50a55f5061 100644 --- a/apps/openmw/mwgui/tradeitemmodel.cpp +++ b/apps/openmw/mwgui/tradeitemmodel.cpp @@ -139,11 +139,8 @@ namespace MWGui if (i == sourceModel->getItemCount()) throw std::runtime_error("The borrowed item disappeared"); - const ItemStack& item = sourceModel->getItem(i); - // copy the borrowed items to our model - copyItem(item, itemStack.mCount, !Settings::game().mPreventMerchantEquipping); - // then remove them from the source model - sourceModel->removeItem(item, itemStack.mCount); + sourceModel->moveItem( + sourceModel->getItem(i), itemStack.mCount, this, !Settings::game().mPreventMerchantEquipping); } mBorrowedToUs.clear(); mBorrowedFromUs.clear(); From 64e55b37ec9c8a6b246874a5032be6a5aea95275 Mon Sep 17 00:00:00 2001 From: Mads Buvik Sandvei Date: Sat, 15 Jul 2023 18:59:16 +0200 Subject: [PATCH 3/7] Clang'd --- apps/openmw/mwgui/inventoryitemmodel.cpp | 3 ++- apps/openmw/mwgui/inventoryitemmodel.hpp | 3 ++- apps/openmw/mwgui/itemmodel.hpp | 3 ++- 3 files changed, 6 insertions(+), 3 deletions(-) diff --git a/apps/openmw/mwgui/inventoryitemmodel.cpp b/apps/openmw/mwgui/inventoryitemmodel.cpp index ce4d1fc1d0..9ba7ad6a1f 100644 --- a/apps/openmw/mwgui/inventoryitemmodel.cpp +++ b/apps/openmw/mwgui/inventoryitemmodel.cpp @@ -92,7 +92,8 @@ namespace MWGui } } - MWWorld::Ptr InventoryItemModel::moveItem(const ItemStack& item, size_t count, ItemModel* otherModel, bool allowAutoEquip) + MWWorld::Ptr InventoryItemModel::moveItem( + const ItemStack& item, size_t count, ItemModel* otherModel, bool allowAutoEquip) { // Can't move conjured items: This is a general fix that also takes care of issues with taking conjured items // via the 'Take All' button. diff --git a/apps/openmw/mwgui/inventoryitemmodel.hpp b/apps/openmw/mwgui/inventoryitemmodel.hpp index 6844291cb6..373f1ecd3a 100644 --- a/apps/openmw/mwgui/inventoryitemmodel.hpp +++ b/apps/openmw/mwgui/inventoryitemmodel.hpp @@ -22,7 +22,8 @@ namespace MWGui void removeItem(const ItemStack& item, size_t count) override; /// Move items from this model to \a otherModel. - MWWorld::Ptr moveItem(const ItemStack& item, size_t count, ItemModel* otherModel, bool allowAutoEquip = true) override; + MWWorld::Ptr moveItem( + const ItemStack& item, size_t count, ItemModel* otherModel, bool allowAutoEquip = true) override; void update() override; diff --git a/apps/openmw/mwgui/itemmodel.hpp b/apps/openmw/mwgui/itemmodel.hpp index faf783ea70..f7e6cf01f1 100644 --- a/apps/openmw/mwgui/itemmodel.hpp +++ b/apps/openmw/mwgui/itemmodel.hpp @@ -63,7 +63,8 @@ namespace MWGui /// Move items from this model to \a otherModel. /// @note Derived implementations may return an empty Ptr if the move was unsuccessful. - virtual MWWorld::Ptr moveItem(const ItemStack& item, size_t count, ItemModel* otherModel, bool allowAutoEquip = true); + virtual MWWorld::Ptr moveItem( + const ItemStack& item, size_t count, ItemModel* otherModel, bool allowAutoEquip = true); /// Unstacks items from this model and returns a ptr to the new remainder stack. /// @note Returns en empty ptr if there is no remainder or the item model does not support unstacking. From e22eec0585092a4fd92c0df2830a09dbdcbc6815 Mon Sep 17 00:00:00 2001 From: Mads Buvik Sandvei Date: Mon, 17 Jul 2023 15:10:54 +0200 Subject: [PATCH 4/7] optimize moveItem() by unsetting refnum instead of calling unstackItem. --- apps/openmw/mwgui/itemmodel.cpp | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/apps/openmw/mwgui/itemmodel.cpp b/apps/openmw/mwgui/itemmodel.cpp index 25b66c28c1..a9edae34b7 100644 --- a/apps/openmw/mwgui/itemmodel.cpp +++ b/apps/openmw/mwgui/itemmodel.cpp @@ -2,6 +2,7 @@ #include "../mwworld/class.hpp" #include "../mwworld/containerstore.hpp" +#include "../mwworld/worldmodel.hpp" #include "../mwbase/environment.hpp" #include "../mwbase/mechanicsmanager.hpp" @@ -58,10 +59,15 @@ namespace MWGui MWWorld::Ptr ItemModel::moveItem(const ItemStack& item, size_t count, ItemModel* otherModel, bool allowAutoEquip) { MWWorld::Ptr ret = otherModel->addItem(item, count, allowAutoEquip); - // Unstacking here ensures that new a refnum is assigned to the leftover stack if there is a leftover. - // Otherwise we end up with duplicated instances. - unstackItem(item, count); removeItem(item, count); + // Although logically the same as an unstack, to avoid unneccesarily allocating a new stack + // and then immediately removing it, we assign a new refnum here instead of calling unstack() + if (!item.mBase.getRefData().isDeleted()) + { + ret.getCellRef().unsetRefNum(); + ret.getRefData().setLuaScripts(nullptr); + MWBase::Environment::get().getWorldModel()->registerPtr(ret); + } return ret; } From b8a9fcad68eee11cc6ca25f486ff4cc21708edaf Mon Sep 17 00:00:00 2001 From: Mads Buvik Sandvei Date: Mon, 17 Jul 2023 17:06:28 +0200 Subject: [PATCH 5/7] fixes based on comments by ptmikheev --- apps/openmw/mwgui/inventoryitemmodel.cpp | 9 -------- apps/openmw/mwgui/inventoryitemmodel.hpp | 1 - apps/openmw/mwgui/itemmodel.cpp | 12 +---------- apps/openmw/mwgui/itemmodel.hpp | 5 ----- apps/openmw/mwworld/class.cpp | 10 +++++++++ apps/openmw/mwworld/class.hpp | 1 + apps/openmw/mwworld/worldimp.cpp | 27 ++++++------------------ apps/openmw/mwworld/worldimp.hpp | 2 +- 8 files changed, 20 insertions(+), 47 deletions(-) diff --git a/apps/openmw/mwgui/inventoryitemmodel.cpp b/apps/openmw/mwgui/inventoryitemmodel.cpp index 9ba7ad6a1f..1bd51acc1e 100644 --- a/apps/openmw/mwgui/inventoryitemmodel.cpp +++ b/apps/openmw/mwgui/inventoryitemmodel.cpp @@ -53,15 +53,6 @@ namespace MWGui return *mActor.getClass().getContainerStore(mActor).add(item.mBase, count, allowAutoEquip); } - MWWorld::Ptr InventoryItemModel::unstackItem(const ItemStack& item, size_t count) - { - MWWorld::ContainerStore& store = mActor.getClass().getContainerStore(mActor); - auto it = store.unstack(item.mBase, count); - if (it != store.end()) - return *it; - return MWWorld::Ptr(); - } - void InventoryItemModel::removeItem(const ItemStack& item, size_t count) { int removed = 0; diff --git a/apps/openmw/mwgui/inventoryitemmodel.hpp b/apps/openmw/mwgui/inventoryitemmodel.hpp index 373f1ecd3a..5310016d74 100644 --- a/apps/openmw/mwgui/inventoryitemmodel.hpp +++ b/apps/openmw/mwgui/inventoryitemmodel.hpp @@ -18,7 +18,6 @@ namespace MWGui bool onTakeItem(const MWWorld::Ptr& item, int count) override; MWWorld::Ptr addItem(const ItemStack& item, size_t count, bool allowAutoEquip = true) override; - MWWorld::Ptr unstackItem(const ItemStack& item, size_t count) override; void removeItem(const ItemStack& item, size_t count) override; /// Move items from this model to \a otherModel. diff --git a/apps/openmw/mwgui/itemmodel.cpp b/apps/openmw/mwgui/itemmodel.cpp index a9edae34b7..23b777e91f 100644 --- a/apps/openmw/mwgui/itemmodel.cpp +++ b/apps/openmw/mwgui/itemmodel.cpp @@ -67,16 +67,11 @@ namespace MWGui ret.getCellRef().unsetRefNum(); ret.getRefData().setLuaScripts(nullptr); MWBase::Environment::get().getWorldModel()->registerPtr(ret); + MWBase::Environment::get().getWorldModel()->registerPtr(item.mBase); } return ret; } - MWWorld::Ptr ItemModel::unstackItem(const ItemStack& item, size_t count) - { - // By default does nothing - return MWWorld::Ptr(); - } - bool ItemModel::allowedToUseItems() const { return true; @@ -151,11 +146,6 @@ namespace MWGui return mSourceModel->onTakeItem(item, count); } - MWWorld::Ptr ProxyItemModel::unstackItem(const ItemStack& item, size_t count) - { - return mSourceModel->unstackItem(item, count); - } - MWWorld::Ptr ProxyItemModel::addItem(const ItemStack& item, size_t count, bool allowAutoEquip) { return mSourceModel->addItem(item, count, allowAutoEquip); diff --git a/apps/openmw/mwgui/itemmodel.hpp b/apps/openmw/mwgui/itemmodel.hpp index f7e6cf01f1..78bfd508f0 100644 --- a/apps/openmw/mwgui/itemmodel.hpp +++ b/apps/openmw/mwgui/itemmodel.hpp @@ -66,10 +66,6 @@ namespace MWGui virtual MWWorld::Ptr moveItem( const ItemStack& item, size_t count, ItemModel* otherModel, bool allowAutoEquip = true); - /// Unstacks items from this model and returns a ptr to the new remainder stack. - /// @note Returns en empty ptr if there is no remainder or the item model does not support unstacking. - virtual MWWorld::Ptr unstackItem(const ItemStack& item, size_t count); - virtual MWWorld::Ptr addItem(const ItemStack& item, size_t count, bool allowAutoEquip = true) = 0; virtual void removeItem(const ItemStack& item, size_t count) = 0; @@ -100,7 +96,6 @@ namespace MWGui bool onDropItem(const MWWorld::Ptr& item, int count) override; bool onTakeItem(const MWWorld::Ptr& item, int count) override; - MWWorld::Ptr unstackItem(const ItemStack& item, size_t count) override; MWWorld::Ptr addItem(const ItemStack& item, size_t count, bool allowAutoEquip = true) override; void removeItem(const ItemStack& item, size_t count) override; ModelIndex getIndex(const ItemStack& item) override; diff --git a/apps/openmw/mwworld/class.cpp b/apps/openmw/mwworld/class.cpp index 03e624a608..28d351902b 100644 --- a/apps/openmw/mwworld/class.cpp +++ b/apps/openmw/mwworld/class.cpp @@ -391,10 +391,20 @@ namespace MWWorld return newPtr; } + Ptr Class::moveToCell(const Ptr& ptr, CellStore& cell, const ESM::Position& pos, int count) const + { + Ptr newPtr = moveToCell(ptr, cell); + newPtr.getRefData().setPosition(pos); + newPtr.getCellRef().setPosition(pos); + newPtr.getRefData().setCount(count); + return newPtr; + } + MWWorld::Ptr Class::copyToCell(const ConstPtr& ptr, CellStore& cell, const ESM::Position& pos, int count) const { Ptr newPtr = copyToCell(ptr, cell, count); newPtr.getRefData().setPosition(pos); + newPtr.getCellRef().setPosition(pos); return newPtr; } diff --git a/apps/openmw/mwworld/class.hpp b/apps/openmw/mwworld/class.hpp index fbd2b98a74..9aa72be041 100644 --- a/apps/openmw/mwworld/class.hpp +++ b/apps/openmw/mwworld/class.hpp @@ -317,6 +317,7 @@ namespace MWWorld // The original is expected to be removed after calling this function, // but this function itself doesn't remove the original. virtual Ptr moveToCell(const Ptr& ptr, CellStore& cell) const; + Ptr moveToCell(const Ptr& ptr, CellStore& cell, const ESM::Position& pos, int count) const; Ptr copyToCell(const ConstPtr& ptr, CellStore& cell, const ESM::Position& pos, int count) const; diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index 23b507f4dc..5285f3567b 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -2063,7 +2063,7 @@ namespace MWWorld MWWorld::Ptr dropped = object.getClass().copyToCell(object, *cell, pos, count); - addObjectToCell(dropped, cell, pos, adjustPos); + initObjectInCell(dropped, *cell, adjustPos); return dropped; } @@ -2079,30 +2079,16 @@ namespace MWWorld cell = &mWorldModel.getExterior(index); } - MWWorld::Ptr dropped = object.getClass().moveToCell(object, *cell); - dropped.getRefData().setCount(count); - dropped.getRefData().setPosition(pos); + MWWorld::Ptr dropped = object.getClass().moveToCell(object, *cell, pos, count); - addObjectToCell(dropped, cell, pos, adjustPos); + initObjectInCell(dropped, *cell, adjustPos); return dropped; } - void World::addObjectToCell(const Ptr& object, CellStore* cell, ESM::Position pos, bool adjustPos) + void World::initObjectInCell(const Ptr& object, CellStore& cell, bool adjustPos) { - if (!cell) - throw std::runtime_error("addObjectToCell(): cannot add object to null cell"); - if (cell->isExterior()) - { - const ESM::ExteriorCellLocation index - = ESM::positionToExteriorCellLocation(pos.pos[0], pos.pos[1], cell->getCell()->getWorldSpace()); - cell = &mWorldModel.getExterior(index); - } - - // Reset some position values that could be uninitialized if this item came from a container - object.getCellRef().setPosition(pos); - - if (mWorldScene->isCellActive(*cell)) + if (mWorldScene->isCellActive(cell)) { if (object.getRefData().isEnabled()) { @@ -2113,7 +2099,7 @@ namespace MWWorld { mLocalScripts.add(script, object); } - addContainerScripts(object, cell); + addContainerScripts(object, &cell); } if (!object.getClass().isActor() && adjustPos && object.getRefData().getBaseNode()) @@ -2125,6 +2111,7 @@ namespace MWWorld osg::BoundingBox bounds = computeBounds.getBoundingBox(); if (bounds.valid()) { + ESM::Position pos = object.getRefData().getPosition(); bounds.set(bounds._min - pos.asVec3(), bounds._max - pos.asVec3()); osg::Vec3f adjust( diff --git a/apps/openmw/mwworld/worldimp.hpp b/apps/openmw/mwworld/worldimp.hpp index b2ea5986fa..30d3102cc2 100644 --- a/apps/openmw/mwworld/worldimp.hpp +++ b/apps/openmw/mwworld/worldimp.hpp @@ -143,7 +143,7 @@ namespace MWWorld void updateWeather(float duration, bool paused = false); - void addObjectToCell(const Ptr& ptr, CellStore* cell, ESM::Position pos, bool adjustPos); + void initObjectInCell(const Ptr& ptr, CellStore& cell, bool adjustPos); Ptr moveObjectToCell(const Ptr& ptr, CellStore* cell, ESM::Position pos, int count, bool adjustPos); Ptr copyObjectToCell(const ConstPtr& ptr, CellStore* cell, ESM::Position pos, int count, bool adjustPos); From fe0b640f853403e45ef0d61f9e78e8e38755205a Mon Sep 17 00:00:00 2001 From: Mads Buvik Sandvei Date: Mon, 17 Jul 2023 17:44:20 +0200 Subject: [PATCH 6/7] Use copyItem() when not moving full stack. --- apps/openmw/mwgui/companionitemmodel.cpp | 8 +++++++ apps/openmw/mwgui/companionitemmodel.hpp | 1 + apps/openmw/mwgui/containeritemmodel.cpp | 11 +++++++++- apps/openmw/mwgui/containeritemmodel.hpp | 1 + apps/openmw/mwgui/hud.cpp | 16 +++++++++++--- apps/openmw/mwgui/inventoryitemmodel.cpp | 9 ++++++++ apps/openmw/mwgui/inventoryitemmodel.hpp | 1 + apps/openmw/mwgui/itemmodel.cpp | 28 ++++++++++++++++-------- apps/openmw/mwgui/itemmodel.hpp | 2 ++ apps/openmw/mwworld/containerstore.cpp | 4 ++-- apps/openmw/mwworld/containerstore.hpp | 2 +- 11 files changed, 67 insertions(+), 16 deletions(-) diff --git a/apps/openmw/mwgui/companionitemmodel.cpp b/apps/openmw/mwgui/companionitemmodel.cpp index 3c017a2856..6bae8aef52 100644 --- a/apps/openmw/mwgui/companionitemmodel.cpp +++ b/apps/openmw/mwgui/companionitemmodel.cpp @@ -33,6 +33,14 @@ namespace MWGui return InventoryItemModel::addItem(item, count, allowAutoEquip); } + MWWorld::Ptr CompanionItemModel::copyItem(const ItemStack& item, size_t count, bool allowAutoEquip) + { + if (hasProfit(mActor)) + modifyProfit(mActor, item.mBase.getClass().getValue(item.mBase) * count); + + return InventoryItemModel::copyItem(item, count, allowAutoEquip); + } + void CompanionItemModel::removeItem(const ItemStack& item, size_t count) { if (hasProfit(mActor)) diff --git a/apps/openmw/mwgui/companionitemmodel.hpp b/apps/openmw/mwgui/companionitemmodel.hpp index 49c84fe075..b9ee500693 100644 --- a/apps/openmw/mwgui/companionitemmodel.hpp +++ b/apps/openmw/mwgui/companionitemmodel.hpp @@ -14,6 +14,7 @@ namespace MWGui CompanionItemModel(const MWWorld::Ptr& actor); MWWorld::Ptr addItem(const ItemStack& item, size_t count, bool allowAutoEquip = true) override; + MWWorld::Ptr copyItem(const ItemStack& item, size_t count, bool allowAutoEquip = true) override; void removeItem(const ItemStack& item, size_t count) override; bool hasProfit(const MWWorld::Ptr& actor); diff --git a/apps/openmw/mwgui/containeritemmodel.cpp b/apps/openmw/mwgui/containeritemmodel.cpp index e072f9974a..ba4f7156c9 100644 --- a/apps/openmw/mwgui/containeritemmodel.cpp +++ b/apps/openmw/mwgui/containeritemmodel.cpp @@ -104,10 +104,19 @@ namespace MWGui auto& source = mItemSources[0]; MWWorld::ContainerStore& store = source.first.getClass().getContainerStore(source.first); if (item.mBase.getContainerStore() == &store) - throw std::runtime_error("Item needs to be from a different container!"); + throw std::runtime_error("Item to add needs to be from a different container!"); return *store.add(item.mBase, count, allowAutoEquip); } + MWWorld::Ptr ContainerItemModel::copyItem(const ItemStack& item, size_t count, bool allowAutoEquip) + { + auto& source = mItemSources[0]; + MWWorld::ContainerStore& store = source.first.getClass().getContainerStore(source.first); + if (item.mBase.getContainerStore() == &store) + throw std::runtime_error("Item to copy needs to be from a different container!"); + return *store.add(item.mBase.getCellRef().getRefId(), count, allowAutoEquip); + } + void ContainerItemModel::removeItem(const ItemStack& item, size_t count) { int toRemove = count; diff --git a/apps/openmw/mwgui/containeritemmodel.hpp b/apps/openmw/mwgui/containeritemmodel.hpp index 52b1911411..8e6f5a37ef 100644 --- a/apps/openmw/mwgui/containeritemmodel.hpp +++ b/apps/openmw/mwgui/containeritemmodel.hpp @@ -33,6 +33,7 @@ namespace MWGui size_t getItemCount() override; MWWorld::Ptr addItem(const ItemStack& item, size_t count, bool allowAutoEquip = true) override; + MWWorld::Ptr copyItem(const ItemStack& item, size_t count, bool allowAutoEquip = true) override; void removeItem(const ItemStack& item, size_t count) override; void update() override; diff --git a/apps/openmw/mwgui/hud.cpp b/apps/openmw/mwgui/hud.cpp index b936620895..022840ea5e 100644 --- a/apps/openmw/mwgui/hud.cpp +++ b/apps/openmw/mwgui/hud.cpp @@ -46,20 +46,30 @@ namespace MWGui } virtual ~WorldItemModel() override {} - MWWorld::Ptr addItem(const ItemStack& item, size_t count, bool /*allowAutoEquip*/) override + MWWorld::Ptr dropItemImpl(const ItemStack& item, size_t count, bool copy) { MWBase::World* world = MWBase::Environment::get().getWorld(); MWWorld::Ptr dropped; if (world->canPlaceObject(mLeft, mTop)) - dropped = world->placeObject(item.mBase, mLeft, mTop, count, false); + dropped = world->placeObject(item.mBase, mLeft, mTop, count, copy); else - dropped = world->dropObjectOnGround(world->getPlayerPtr(), item.mBase, count, false); + dropped = world->dropObjectOnGround(world->getPlayerPtr(), item.mBase, count, copy); dropped.getCellRef().setOwner(ESM::RefId()); return dropped; } + MWWorld::Ptr addItem(const ItemStack& item, size_t count, bool /*allowAutoEquip*/) override + { + return dropItemImpl(item, count, false); + } + + MWWorld::Ptr copyItem(const ItemStack& item, size_t count, bool /*allowAutoEquip*/) override + { + return dropItemImpl(item, count, true); + } + void removeItem(const ItemStack& item, size_t count) override { throw std::runtime_error("removeItem not implemented"); diff --git a/apps/openmw/mwgui/inventoryitemmodel.cpp b/apps/openmw/mwgui/inventoryitemmodel.cpp index 1bd51acc1e..e87ae7b8e0 100644 --- a/apps/openmw/mwgui/inventoryitemmodel.cpp +++ b/apps/openmw/mwgui/inventoryitemmodel.cpp @@ -8,6 +8,7 @@ #include "../mwworld/class.hpp" #include "../mwworld/containerstore.hpp" #include "../mwworld/inventorystore.hpp" +#include "../mwworld/manualref.hpp" #include "../mwbase/environment.hpp" #include "../mwbase/mechanicsmanager.hpp" @@ -53,6 +54,14 @@ namespace MWGui return *mActor.getClass().getContainerStore(mActor).add(item.mBase, count, allowAutoEquip); } + MWWorld::Ptr InventoryItemModel::copyItem(const ItemStack& item, size_t count, bool allowAutoEquip) + { + if (item.mBase.getContainerStore() == &mActor.getClass().getContainerStore(mActor)) + throw std::runtime_error("Item to copy needs to be from a different container!"); + return *mActor.getClass().getContainerStore(mActor).add( + item.mBase.getCellRef().getRefId(), count, allowAutoEquip); + } + void InventoryItemModel::removeItem(const ItemStack& item, size_t count) { int removed = 0; diff --git a/apps/openmw/mwgui/inventoryitemmodel.hpp b/apps/openmw/mwgui/inventoryitemmodel.hpp index 5310016d74..560d1a0fbd 100644 --- a/apps/openmw/mwgui/inventoryitemmodel.hpp +++ b/apps/openmw/mwgui/inventoryitemmodel.hpp @@ -18,6 +18,7 @@ namespace MWGui bool onTakeItem(const MWWorld::Ptr& item, int count) override; MWWorld::Ptr addItem(const ItemStack& item, size_t count, bool allowAutoEquip = true) override; + MWWorld::Ptr copyItem(const ItemStack& item, size_t count, bool allowAutoEquip = true) override; void removeItem(const ItemStack& item, size_t count) override; /// Move items from this model to \a otherModel. diff --git a/apps/openmw/mwgui/itemmodel.cpp b/apps/openmw/mwgui/itemmodel.cpp index 23b777e91f..03bec62e7a 100644 --- a/apps/openmw/mwgui/itemmodel.cpp +++ b/apps/openmw/mwgui/itemmodel.cpp @@ -7,6 +7,8 @@ #include "../mwbase/environment.hpp" #include "../mwbase/mechanicsmanager.hpp" +#include + namespace MWGui { @@ -58,16 +60,19 @@ namespace MWGui MWWorld::Ptr ItemModel::moveItem(const ItemStack& item, size_t count, ItemModel* otherModel, bool allowAutoEquip) { - MWWorld::Ptr ret = otherModel->addItem(item, count, allowAutoEquip); - removeItem(item, count); - // Although logically the same as an unstack, to avoid unneccesarily allocating a new stack - // and then immediately removing it, we assign a new refnum here instead of calling unstack() - if (!item.mBase.getRefData().isDeleted()) + MWWorld::Ptr ret = MWWorld::Ptr(); + if (item.mBase.getRefData().getCount() <= count) + { + // We are moving the full stack + ret = otherModel->addItem(item, count, allowAutoEquip); + removeItem(item, count); + } + else { - ret.getCellRef().unsetRefNum(); - ret.getRefData().setLuaScripts(nullptr); - MWBase::Environment::get().getWorldModel()->registerPtr(ret); - MWBase::Environment::get().getWorldModel()->registerPtr(item.mBase); + // We are moving only part of the stack, so create a copy in the other model + // and then remove count from this model. + ret = otherModel->copyItem(item, count, allowAutoEquip); + removeItem(item, count); } return ret; } @@ -92,6 +97,11 @@ namespace MWGui return mSourceModel->allowedToUseItems(); } + MWWorld::Ptr ProxyItemModel::copyItem(const ItemStack& item, size_t count, bool allowAutoEquip) + { + return mSourceModel->copyItem(item, count, allowAutoEquip); + } + void ProxyItemModel::removeItem(const ItemStack& item, size_t count) { mSourceModel->removeItem(item, count); diff --git a/apps/openmw/mwgui/itemmodel.hpp b/apps/openmw/mwgui/itemmodel.hpp index 78bfd508f0..7af29878eb 100644 --- a/apps/openmw/mwgui/itemmodel.hpp +++ b/apps/openmw/mwgui/itemmodel.hpp @@ -67,6 +67,7 @@ namespace MWGui const ItemStack& item, size_t count, ItemModel* otherModel, bool allowAutoEquip = true); virtual MWWorld::Ptr addItem(const ItemStack& item, size_t count, bool allowAutoEquip = true) = 0; + virtual MWWorld::Ptr copyItem(const ItemStack& item, size_t count, bool allowAutoEquip = true) = 0; virtual void removeItem(const ItemStack& item, size_t count) = 0; /// Is the player allowed to use items from this item model? (default true) @@ -97,6 +98,7 @@ namespace MWGui bool onTakeItem(const MWWorld::Ptr& item, int count) override; MWWorld::Ptr addItem(const ItemStack& item, size_t count, bool allowAutoEquip = true) override; + MWWorld::Ptr copyItem(const ItemStack& item, size_t count, bool allowAutoEquip = true) override; void removeItem(const ItemStack& item, size_t count) override; ModelIndex getIndex(const ItemStack& item) override; diff --git a/apps/openmw/mwworld/containerstore.cpp b/apps/openmw/mwworld/containerstore.cpp index b429bc31ed..b3ea654404 100644 --- a/apps/openmw/mwworld/containerstore.cpp +++ b/apps/openmw/mwworld/containerstore.cpp @@ -300,10 +300,10 @@ bool MWWorld::ContainerStore::stacks(const ConstPtr& ptr1, const ConstPtr& ptr2) && cls2.getItemHealth(ptr2) == cls2.getItemMaxHealth(ptr2))); } -MWWorld::ContainerStoreIterator MWWorld::ContainerStore::add(const ESM::RefId& id, int count) +MWWorld::ContainerStoreIterator MWWorld::ContainerStore::add(const ESM::RefId& id, int count, bool allowAutoEquip) { MWWorld::ManualRef ref(*MWBase::Environment::get().getESMStore(), id, count); - return add(ref.getPtr(), count); + return add(ref.getPtr(), count, allowAutoEquip); } MWWorld::ContainerStoreIterator MWWorld::ContainerStore::add( diff --git a/apps/openmw/mwworld/containerstore.hpp b/apps/openmw/mwworld/containerstore.hpp index 9d02e0b95c..253df39567 100644 --- a/apps/openmw/mwworld/containerstore.hpp +++ b/apps/openmw/mwworld/containerstore.hpp @@ -198,7 +198,7 @@ namespace MWWorld /// @return if stacking happened, return iterator to the item that was stacked against, otherwise iterator to /// the newly inserted item. - ContainerStoreIterator add(const ESM::RefId& id, int count); + ContainerStoreIterator add(const ESM::RefId& id, int count, bool allowAutoEquip = true); ///< Utility to construct a ManualRef and call add(ptr, count, actorPtr, true) int remove(const ESM::RefId& itemId, int count, bool equipReplacement = 0, bool resolve = true); From 61f1f4a013cc189e88189e5f22a505293d546f4a Mon Sep 17 00:00:00 2001 From: Mads Buvik Sandvei Date: Mon, 17 Jul 2023 20:41:33 +0200 Subject: [PATCH 7/7] signed/unsigned comparison warning. --- apps/openmw/mwgui/itemmodel.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/openmw/mwgui/itemmodel.cpp b/apps/openmw/mwgui/itemmodel.cpp index 03bec62e7a..fce3e68e78 100644 --- a/apps/openmw/mwgui/itemmodel.cpp +++ b/apps/openmw/mwgui/itemmodel.cpp @@ -61,7 +61,7 @@ namespace MWGui MWWorld::Ptr ItemModel::moveItem(const ItemStack& item, size_t count, ItemModel* otherModel, bool allowAutoEquip) { MWWorld::Ptr ret = MWWorld::Ptr(); - if (item.mBase.getRefData().getCount() <= count) + if (static_cast(item.mBase.getRefData().getCount()) <= count) { // We are moving the full stack ret = otherModel->addItem(item, count, allowAutoEquip);