diff --git a/apps/openmw/mwclass/armor.cpp b/apps/openmw/mwclass/armor.cpp index fb34e4c88..87a5dde27 100644 --- a/apps/openmw/mwclass/armor.cpp +++ b/apps/openmw/mwclass/armor.cpp @@ -248,4 +248,12 @@ namespace MWClass return info; } + + std::string Armor::getEnchantment (const MWWorld::Ptr& ptr) const + { + ESMS::LiveCellRef *ref = + ptr.get(); + + return ref->base->enchant; + } } diff --git a/apps/openmw/mwclass/armor.hpp b/apps/openmw/mwclass/armor.hpp index c757587e5..3ff101a5e 100644 --- a/apps/openmw/mwclass/armor.hpp +++ b/apps/openmw/mwclass/armor.hpp @@ -55,6 +55,9 @@ namespace MWClass virtual std::string getDownSoundId (const MWWorld::Ptr& ptr) const; ///< Return the put down sound Id + + virtual std::string getEnchantment (const MWWorld::Ptr& ptr) const; + ///< @return the enchantment ID if the object is enchanted, otherwise an empty string }; } diff --git a/apps/openmw/mwclass/clothing.cpp b/apps/openmw/mwclass/clothing.cpp index 620b664cc..a242874df 100644 --- a/apps/openmw/mwclass/clothing.cpp +++ b/apps/openmw/mwclass/clothing.cpp @@ -201,4 +201,12 @@ namespace MWClass return info; } + + std::string Clothing::getEnchantment (const MWWorld::Ptr& ptr) const + { + ESMS::LiveCellRef *ref = + ptr.get(); + + return ref->base->enchant; + } } diff --git a/apps/openmw/mwclass/clothing.hpp b/apps/openmw/mwclass/clothing.hpp index afb184999..9b9322969 100644 --- a/apps/openmw/mwclass/clothing.hpp +++ b/apps/openmw/mwclass/clothing.hpp @@ -49,6 +49,9 @@ namespace MWClass virtual std::string getDownSoundId (const MWWorld::Ptr& ptr) const; ///< Return the put down sound Id + + virtual std::string getEnchantment (const MWWorld::Ptr& ptr) const; + ///< @return the enchantment ID if the object is enchanted, otherwise an empty string }; } diff --git a/apps/openmw/mwclass/weapon.cpp b/apps/openmw/mwclass/weapon.cpp index fcfaebcb7..0ddf19d3c 100644 --- a/apps/openmw/mwclass/weapon.cpp +++ b/apps/openmw/mwclass/weapon.cpp @@ -339,4 +339,12 @@ namespace MWClass return info; } + + std::string Weapon::getEnchantment (const MWWorld::Ptr& ptr) const + { + ESMS::LiveCellRef *ref = + ptr.get(); + + return ref->base->enchant; + } } diff --git a/apps/openmw/mwclass/weapon.hpp b/apps/openmw/mwclass/weapon.hpp index 9c5f43bd3..c27a39620 100644 --- a/apps/openmw/mwclass/weapon.hpp +++ b/apps/openmw/mwclass/weapon.hpp @@ -55,6 +55,9 @@ namespace MWClass virtual std::string getDownSoundId (const MWWorld::Ptr& ptr) const; ///< Return the put down sound Id + + virtual std::string getEnchantment (const MWWorld::Ptr& ptr) const; + ///< @return the enchantment ID if the object is enchanted, otherwise an empty string }; } diff --git a/apps/openmw/mwworld/class.cpp b/apps/openmw/mwworld/class.cpp index 78ed5ca2e..151d913be 100644 --- a/apps/openmw/mwworld/class.cpp +++ b/apps/openmw/mwworld/class.cpp @@ -181,4 +181,9 @@ namespace MWWorld { return false; } + + std::string Class::getEnchantment (const Ptr& ptr) const + { + return ""; + } } diff --git a/apps/openmw/mwworld/class.hpp b/apps/openmw/mwworld/class.hpp index e69b8f2ac..d7a2d2d75 100644 --- a/apps/openmw/mwworld/class.hpp +++ b/apps/openmw/mwworld/class.hpp @@ -136,7 +136,7 @@ namespace MWWorld ///< Set or unset a stance. virtual bool getStance (const Ptr& ptr, Stance stance, bool ignoreForce = false) const; - ////< Check if a stance is active or not. + ///< Check if a stance is active or not. virtual float getSpeed (const Ptr& ptr) const; ///< Return movement speed. @@ -179,6 +179,10 @@ namespace MWWorld virtual std::string getDownSoundId (const Ptr& ptr) const; ///< Return the down sound ID of \a ptr or throw an exception, if class does not support ID retrieval /// (default implementation: throw an exception) + + virtual std::string getEnchantment (const MWWorld::Ptr& ptr) const; + ///< @return the enchantment ID if the object is enchanted, otherwise an empty string + /// (default implementation: return empty string) }; } diff --git a/apps/openmw/mwworld/containerstore.cpp b/apps/openmw/mwworld/containerstore.cpp index 2800b6f3c..dcddc333b 100644 --- a/apps/openmw/mwworld/containerstore.cpp +++ b/apps/openmw/mwworld/containerstore.cpp @@ -9,6 +9,7 @@ #include "manualref.hpp" #include "refdata.hpp" +#include "class.hpp" namespace { @@ -44,11 +45,43 @@ MWWorld::ContainerStoreIterator MWWorld::ContainerStore::end() return ContainerStoreIterator (this); } +bool MWWorld::ContainerStore::stacks(const Ptr& ptr1, const Ptr& ptr2) const +{ + /// \todo add current weapon/armor health, remaining lockpick/repair uses, current enchantment charge here as soon as they are implemented + if ( ptr1.mCellRef->refID == ptr2.mCellRef->refID + && MWWorld::Class::get(ptr1).getScript(ptr1) == "" // item with a script never stacks + && ptr1.mCellRef->owner == ptr2.mCellRef->owner + && ptr1.mCellRef->soul == ptr2.mCellRef->soul + && ptr1.mCellRef->charge == ptr2.mCellRef->charge) + return true; + + return false; +} + void MWWorld::ContainerStore::add (const Ptr& ptr) { - /// \todo implement item stacking + int type = getType(ptr); - switch (getType (ptr)) + // determine whether to stack or not + for (MWWorld::ContainerStoreIterator iter (begin(type)); iter!=end(); ++iter) + { + if (stacks(*iter, ptr)) + { + // stack + iter->getRefData().setCount( iter->getRefData().getCount() + ptr.getRefData().getCount() ); + + flagAsModified(); + return; + } + } + + // if we got here, this means no stacking + addImpl(ptr); +} + +void MWWorld::ContainerStore::addImpl (const Ptr& ptr) +{ + switch (getType(ptr)) { case Type_Potion: potions.list.push_back (*ptr.get()); break; case Type_Apparatus: appas.list.push_back (*ptr.get()); break; diff --git a/apps/openmw/mwworld/containerstore.hpp b/apps/openmw/mwworld/containerstore.hpp index da5424fe0..f0e9d7e4a 100644 --- a/apps/openmw/mwworld/containerstore.hpp +++ b/apps/openmw/mwworld/containerstore.hpp @@ -67,13 +67,23 @@ namespace MWWorld ContainerStoreIterator end(); void add (const Ptr& ptr); - ///< Add the item pointed to by \a ptr to this container. + ///< Add the item pointed to by \a ptr to this container. (Stacks automatically if needed) /// /// \note The item pointed to is not required to exist beyond this function call. /// /// \attention Do not add items to an existing stack by increasing the count instead of /// calling this function! + protected: + void addImpl (const Ptr& ptr); + ///< Add the item to this container (no stacking) + + virtual bool stacks (const Ptr& ptr1, const Ptr& ptr2) const; + ///< @return true if the two specified objects can stack with each other + /// @note ptr1 is the item that is already in this container + + public: + void fill (const ESM::InventoryList& items, const ESMS::ESMStore& store); ///< Insert items into *this. diff --git a/apps/openmw/mwworld/inventorystore.cpp b/apps/openmw/mwworld/inventorystore.cpp index 576f2371f..6cf35ac64 100644 --- a/apps/openmw/mwworld/inventorystore.cpp +++ b/apps/openmw/mwworld/inventorystore.cpp @@ -59,17 +59,38 @@ void MWWorld::InventoryStore::equip (int slot, const ContainerStoreIterator& ite if (iterator.getContainerStore()!=this) throw std::runtime_error ("attempt to equip an item that is not in the inventory"); + std::pair, bool> slots; if (iterator!=end()) { - std::pair, bool> slots = Class::get (*iterator).getEquipmentSlots (*iterator); + slots = Class::get (*iterator).getEquipmentSlots (*iterator); if (std::find (slots.first.begin(), slots.first.end(), slot)==slots.first.end()) throw std::runtime_error ("invalid slot"); } - /// \todo restack item previously in this slot (if required) + // restack item previously in this slot (if required) + if (mSlots[slot] != end()) + { + for (MWWorld::ContainerStoreIterator iter (begin()); iter!=end(); ++iter) + { + if (stacks(*iter, *mSlots[slot])) + { + iter->getRefData().setCount( iter->getRefData().getCount() + mSlots[slot]->getRefData().getCount() ); + mSlots[slot]->getRefData().setCount(0); + break; + } + } + } - /// \todo unstack item pointed to by iterator if required) + // unstack item pointed to by iterator if required + if (iterator!=end() && !slots.second && iterator->getRefData().getCount() > 1) // if slots.second is true, item can stay stacked when equipped + { + // add the item again with a count of count-1, then set the count of the original (that will be equipped) to 1 + int count = iterator->getRefData().getCount(); + iterator->getRefData().setCount(count-1); + addImpl(*iterator); + iterator->getRefData().setCount(1); + } mSlots[slot] = iterator; @@ -147,7 +168,18 @@ void MWWorld::InventoryStore::autoEquip (const MWMechanics::NpcStats& stats) } } - /// \todo unstack, if reqquired (itemsSlots.second) + if (!itemsSlots.second) // if itemsSlots.second is true, item can stay stacked when equipped + { + // unstack item pointed to by iterator if required + if (iter->getRefData().getCount() > 1) + { + // add the item again with a count of count-1, then set the count of the original (that will be equipped) to 1 + int count = iter->getRefData().getCount(); + iter->getRefData().setCount(count-1); + addImpl(*iter); + iter->getRefData().setCount(1); + } + } slots[*iter2] = iter; break; @@ -168,3 +200,20 @@ void MWWorld::InventoryStore::autoEquip (const MWMechanics::NpcStats& stats) flagAsModified(); } } + +bool MWWorld::InventoryStore::stacks(const Ptr& ptr1, const Ptr& ptr2) const +{ + bool canStack = MWWorld::ContainerStore::stacks(ptr1, ptr2); + if (!canStack) + return false; + + // don't stack if the item being checked against is currently equipped. + for (TSlots::const_iterator iter (mSlots.begin()); + iter!=mSlots.end(); ++iter) + { + if (ptr1 == **iter) + return false; + } + + return true; +} diff --git a/apps/openmw/mwworld/inventorystore.hpp b/apps/openmw/mwworld/inventorystore.hpp index 5eeaf570d..4162c7e2e 100644 --- a/apps/openmw/mwworld/inventorystore.hpp +++ b/apps/openmw/mwworld/inventorystore.hpp @@ -64,6 +64,13 @@ namespace MWWorld void autoEquip (const MWMechanics::NpcStats& stats); ///< Auto equip items according to stats and item value. + + protected: + + virtual bool stacks (const Ptr& ptr1, const Ptr& ptr2) const; + ///< @return true if the two specified objects can stack with each other + /// @note ptr1 is the item that is already in this container + }; }