diff --git a/CHANGELOG.md b/CHANGELOG.md index b6d95cd0d..8af60aa9c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -82,6 +82,7 @@ Bug #4984: "Friendly hits" feature should be used only for player's followers Bug #4989: Object dimension-dependent VFX scaling behavior is inconsistent Bug #4990: Dead bodies prevent you from hitting + Bug #4999: Drop instruction behaves differently from vanilla Bug #5004: Werewolves shield their eyes during storm Feature #1774: Handle AvoidNode Feature #2229: Improve pathfinding AI diff --git a/apps/openmw/mwscript/miscextensions.cpp b/apps/openmw/mwscript/miscextensions.cpp index d73fbbe35..b64609c95 100644 --- a/apps/openmw/mwscript/miscextensions.cpp +++ b/apps/openmw/mwscript/miscextensions.cpp @@ -15,6 +15,7 @@ #include "../mwbase/environment.hpp" #include "../mwbase/windowmanager.hpp" #include "../mwbase/scriptmanager.hpp" +#include "../mwbase/soundmanager.hpp" #include "../mwbase/world.hpp" #include "../mwworld/class.hpp" @@ -23,6 +24,7 @@ #include "../mwworld/inventorystore.hpp" #include "../mwworld/esmstore.hpp" #include "../mwworld/cellstore.hpp" +#include "../mwworld/manualref.hpp" #include "../mwmechanics/aicast.hpp" #include "../mwmechanics/npcstats.hpp" @@ -469,6 +471,9 @@ namespace MWScript std::string gem = runtime.getStringLiteral (runtime[0].mInteger); runtime.pop(); + if (!ptr.getClass().hasInventoryStore(ptr)) + return; + const MWWorld::ESMStore& store = MWBase::Environment::get().getWorld()->getStore(); store.get().find(creature); // This line throws an exception if it can't find the creature @@ -499,7 +504,10 @@ namespace MWScript for (unsigned int i=0; igetCellRef().getSoul(), soul)) @@ -534,16 +542,18 @@ namespace MWScript if (amount == 0) return; - // 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); + if (!ptr.getClass().isActor()) + return; - int numNotEquipped = invStorePtr->count(item); + if (ptr.getClass().hasInventoryStore(ptr)) + { + // Prefer dropping unequipped items first; re-stack if possible by unequipping items before dropping them. + MWWorld::InventoryStore& store = ptr.getClass().getInventoryStore(ptr); + int numNotEquipped = store.count(item); for (int slot = 0; slot < MWWorld::InventoryStore::Slots; ++slot) { - MWWorld::ConstContainerStoreIterator it = invStorePtr->getSlot (slot); - if (it != invStorePtr->end() && ::Misc::StringUtils::ciEqual(it->getCellRef().getRefId(), item)) + MWWorld::ConstContainerStoreIterator it = store.getSlot (slot); + if (it != store.end() && ::Misc::StringUtils::ciEqual(it->getCellRef().getRefId(), item)) { numNotEquipped -= it->getRefData().getCount(); } @@ -551,37 +561,48 @@ namespace MWScript 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)) + MWWorld::ContainerStoreIterator it = store.getSlot (slot); + if (it != store.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); + int numToRemove = std::min(amount - numNotEquipped, it->getRefData().getCount()); + store.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) - && (!invStorePtr || !invStorePtr->isEquipped(*iter))) + for (MWWorld::ContainerStoreIterator iter (store.begin()); iter!=store.end(); ++iter) { - int removed = store.remove(*iter, toRemove, ptr); - MWWorld::Ptr dropped = MWBase::Environment::get().getWorld()->dropObjectOnGround(ptr, *iter, removed); - dropped.getCellRef().setOwner(""); + if (::Misc::StringUtils::ciEqual(iter->getCellRef().getRefId(), item) && !store.isEquipped(*iter)) + { + int removed = store.remove(*iter, amount, ptr); + MWWorld::Ptr dropped = MWBase::Environment::get().getWorld()->dropObjectOnGround(ptr, *iter, removed); + dropped.getCellRef().setOwner(""); - toRemove -= removed; + amount -= removed; - if (toRemove <= 0) - break; + if (amount <= 0) + break; + } + } + } + + MWWorld::ManualRef ref(MWBase::Environment::get().getWorld()->getStore(), item, 1); + MWWorld::Ptr itemPtr(ref.getPtr()); + if (amount > 0) + { + if (itemPtr.getClass().getScript(itemPtr).empty()) + { + MWBase::Environment::get().getWorld()->dropObjectOnGround(ptr, itemPtr, amount); + } + else + { + // Dropping one item per time to prevent making stacks of scripted items + for (int i = 0; i < amount; i++) + MWBase::Environment::get().getWorld()->dropObjectOnGround(ptr, itemPtr, 1); } } + + MWBase::Environment::get().getSoundManager()->playSound3D(ptr, itemPtr.getClass().getDownSoundId(itemPtr), 1.f, 1.f); } }; @@ -598,8 +619,10 @@ namespace MWScript std::string soul = runtime.getStringLiteral (runtime[0].mInteger); runtime.pop(); - MWWorld::ContainerStore& store = ptr.getClass().getContainerStore (ptr); + if (!ptr.getClass().hasInventoryStore(ptr)) + return; + MWWorld::InventoryStore& store = ptr.getClass().getInventoryStore(ptr); for (MWWorld::ContainerStoreIterator iter (store.begin()); iter!=store.end(); ++iter) {