diff --git a/apps/openmw/mwclass/armor.cpp b/apps/openmw/mwclass/armor.cpp index 320944d3ce..496e695450 100644 --- a/apps/openmw/mwclass/armor.cpp +++ b/apps/openmw/mwclass/armor.cpp @@ -16,6 +16,8 @@ #include "../mwworld/cellstore.hpp" #include "../mwworld/physicssystem.hpp" #include "../mwworld/nullaction.hpp" +#include "../mwworld/containerstore.hpp" +#include "../mwworld/player.hpp" #include "../mwrender/objects.hpp" #include "../mwrender/renderinginterface.hpp" @@ -290,6 +292,91 @@ namespace MWClass ref->mBase = record; } + bool Armor::canEquip(const MWWorld::Ptr &npc, const MWWorld::Ptr &item) const + { + MWWorld::InventoryStore& invStore = MWWorld::Class::get(npc).getInventoryStore(npc); + + // slots that this item can be equipped in + std::pair, bool> slots = MWWorld::Class::get(item).getEquipmentSlots(item); + + // retrieve ContainerStoreIterator to the item + MWWorld::ContainerStoreIterator it = invStore.begin(); + for (; it != invStore.end(); ++it) + { + if (*it == item) + { + break; + } + } + + assert(it != invStore.end()); + + std::string npcRace = npc.get()->mBase->mRace; + + for (std::vector::const_iterator slot=slots.first.begin(); + slot!=slots.first.end(); ++slot) + { + + // Beast races cannot equip shoes / boots, or full helms (head part vs hair part) + const ESM::Race* race = MWBase::Environment::get().getWorld()->getStore().get().find(npcRace); + if(race->mData.mFlags & ESM::Race::Beast) + { + if(*slot == MWWorld::InventoryStore::Slot_Helmet) + { + std::vector parts; + if(it.getType() == MWWorld::ContainerStore::Type_Clothing) + parts = it->get()->mBase->mParts.mParts; + else + parts = it->get()->mBase->mParts.mParts; + + bool allow = true; + for(std::vector::iterator itr = parts.begin(); itr != parts.end(); ++itr) + { + if((*itr).mPart == ESM::PRT_Head) + { + if(npc == MWBase::Environment::get().getWorld()->getPlayer().getPlayer() ) + MWBase::Environment::get().getWindowManager()->messageBox ("#{sNotifyMessage13}"); + + allow = false; + break; + } + } + + if(!allow) + break; + } + + if (*slot == MWWorld::InventoryStore::Slot_Boots) + { + bool allow = true; + std::vector parts; + if(it.getType() == MWWorld::ContainerStore::Type_Clothing) + parts = it->get()->mBase->mParts.mParts; + else + parts = it->get()->mBase->mParts.mParts; + for(std::vector::iterator itr = parts.begin(); itr != parts.end(); ++itr) + { + if((*itr).mPart == ESM::PRT_LFoot || (*itr).mPart == ESM::PRT_RFoot) + { + allow = false; + // Only notify the player, not npcs + if(npc == MWBase::Environment::get().getWorld()->getPlayer().getPlayer() ) + { + MWBase::Environment::get().getWindowManager()->messageBox ("#{sNotifyMessage14}"); + } + break; + } + } + + if(!allow) + return false; + } + + } + } + return true; + } + boost::shared_ptr Armor::use (const MWWorld::Ptr& ptr) const { boost::shared_ptr action(new MWWorld::ActionEquip(ptr)); diff --git a/apps/openmw/mwclass/armor.hpp b/apps/openmw/mwclass/armor.hpp index 65f49abb7d..bb07e42b03 100644 --- a/apps/openmw/mwclass/armor.hpp +++ b/apps/openmw/mwclass/armor.hpp @@ -67,6 +67,8 @@ namespace MWClass virtual void applyEnchantment(const MWWorld::Ptr &ptr, const std::string& enchId, int enchCharge, const std::string& newName) const; + virtual bool canEquip(const MWWorld::Ptr &npc, const MWWorld::Ptr &item) const; + virtual boost::shared_ptr use (const MWWorld::Ptr& ptr) const; ///< Generate action for using via inventory menu diff --git a/apps/openmw/mwclass/clothing.cpp b/apps/openmw/mwclass/clothing.cpp index abad267675..77a7557bf6 100644 --- a/apps/openmw/mwclass/clothing.cpp +++ b/apps/openmw/mwclass/clothing.cpp @@ -14,6 +14,7 @@ #include "../mwworld/cellstore.hpp" #include "../mwworld/physicssystem.hpp" #include "../mwworld/nullaction.hpp" +#include "../mwworld/player.hpp" #include "../mwgui/tooltips.hpp" @@ -237,6 +238,66 @@ namespace MWClass ref->mBase = record; } + bool Clothing::canEquip(const MWWorld::Ptr &npc, const MWWorld::Ptr &item) const + { + MWWorld::InventoryStore& invStore = MWWorld::Class::get(npc).getInventoryStore(npc); + + // slots that this item can be equipped in + std::pair, bool> slots = MWWorld::Class::get(item).getEquipmentSlots(item); + + // retrieve ContainerStoreIterator to the item + MWWorld::ContainerStoreIterator it = invStore.begin(); + for (; it != invStore.end(); ++it) + { + if (*it == item) + { + break; + } + } + + assert(it != invStore.end()); + + std::string npcRace = npc.get()->mBase->mRace; + + for (std::vector::const_iterator slot=slots.first.begin(); + slot!=slots.first.end(); ++slot) + { + + // Beast races cannot equip shoes / boots, or full helms (head part vs hair part) + const ESM::Race* race = MWBase::Environment::get().getWorld()->getStore().get().find(npcRace); + if(race->mData.mFlags & ESM::Race::Beast) + { + if (*slot == MWWorld::InventoryStore::Slot_Boots) + { + bool allow = true; + std::vector parts; + if(it.getType() == MWWorld::ContainerStore::Type_Clothing) + parts = it->get()->mBase->mParts.mParts; + else + parts = it->get()->mBase->mParts.mParts; + for(std::vector::iterator itr = parts.begin(); itr != parts.end(); ++itr) + { + if((*itr).mPart == ESM::PRT_LFoot || (*itr).mPart == ESM::PRT_RFoot) + { + allow = false; + // Only notify the player, not npcs + if(npc == MWBase::Environment::get().getWorld()->getPlayer().getPlayer() ) + { + MWBase::Environment::get().getWindowManager()->messageBox ("#{sNotifyMessage15}"); + } + break; + } + } + + if(!allow) + return false; + } + + } + } + return true; + } + boost::shared_ptr Clothing::use (const MWWorld::Ptr& ptr) const { boost::shared_ptr action(new MWWorld::ActionEquip(ptr)); diff --git a/apps/openmw/mwclass/clothing.hpp b/apps/openmw/mwclass/clothing.hpp index c3ef22f113..4978fa2288 100644 --- a/apps/openmw/mwclass/clothing.hpp +++ b/apps/openmw/mwclass/clothing.hpp @@ -61,6 +61,8 @@ namespace MWClass virtual void applyEnchantment(const MWWorld::Ptr &ptr, const std::string& enchId, int enchCharge, const std::string& newName) const; + virtual bool canEquip(const MWWorld::Ptr &npc, const MWWorld::Ptr &item) const; + virtual boost::shared_ptr use (const MWWorld::Ptr& ptr) const; ///< Generate action for using via inventory menu diff --git a/apps/openmw/mwclass/weapon.cpp b/apps/openmw/mwclass/weapon.cpp index 0a527262f8..51e8130039 100644 --- a/apps/openmw/mwclass/weapon.cpp +++ b/apps/openmw/mwclass/weapon.cpp @@ -384,6 +384,72 @@ namespace MWClass ref->mBase = record; } + bool Weapon::canEquip(const MWWorld::Ptr &npc, const MWWorld::Ptr &item) const + { + MWWorld::InventoryStore& invStore = MWWorld::Class::get(npc).getInventoryStore(npc); + + // slots that this item can be equipped in + std::pair, bool> slots = MWWorld::Class::get(item).getEquipmentSlots(item); + + // retrieve ContainerStoreIterator to the item + MWWorld::ContainerStoreIterator it = invStore.begin(); + for (; it != invStore.end(); ++it) + { + if (*it == item) + { + break; + } + } + + assert(it != invStore.end()); + + std::string npcRace = npc.get()->mBase->mRace; + + // equip the item in the first free slot + for (std::vector::const_iterator slot=slots.first.begin(); + slot!=slots.first.end(); ++slot) + { + if(*slot == MWWorld::InventoryStore::Slot_CarriedRight) + { + if (it.getType() == MWWorld::ContainerStore::Type_Weapon) + { + if(it->get()->mBase->mData.mType == ESM::Weapon::LongBladeTwoHand || + it->get()->mBase->mData.mType == ESM::Weapon::BluntTwoClose || + it->get()->mBase->mData.mType == ESM::Weapon::BluntTwoWide || + it->get()->mBase->mData.mType == ESM::Weapon::SpearTwoWide || + it->get()->mBase->mData.mType == ESM::Weapon::AxeTwoHand || + it->get()->mBase->mData.mType == ESM::Weapon::MarksmanBow || + it->get()->mBase->mData.mType == ESM::Weapon::MarksmanCrossbow) + { + //unequip lefthand item + invStore.equip(MWWorld::InventoryStore::Slot_CarriedLeft, invStore.end()); + } + } + } + if(*slot == MWWorld::InventoryStore::Slot_CarriedLeft) + { + MWWorld::ContainerStoreIterator weapon = invStore.getSlot(MWWorld::InventoryStore::Slot_CarriedRight); + + if (weapon.getType() == MWWorld::ContainerStore::Type_Weapon) + { + if(weapon->get()->mBase->mData.mType == ESM::Weapon::LongBladeTwoHand || + weapon->get()->mBase->mData.mType == ESM::Weapon::BluntTwoClose || + weapon->get()->mBase->mData.mType == ESM::Weapon::BluntTwoWide || + weapon->get()->mBase->mData.mType == ESM::Weapon::SpearTwoWide || + weapon->get()->mBase->mData.mType == ESM::Weapon::AxeTwoHand || + weapon->get()->mBase->mData.mType == ESM::Weapon::MarksmanBow || + weapon->get()->mBase->mData.mType == ESM::Weapon::MarksmanCrossbow) + { + //unequip twohanded item + invStore.equip(MWWorld::InventoryStore::Slot_CarriedRight, invStore.end()); + } + } + } + return true; + } + return false; + } + boost::shared_ptr Weapon::use (const MWWorld::Ptr& ptr) const { boost::shared_ptr action(new MWWorld::ActionEquip(ptr)); diff --git a/apps/openmw/mwclass/weapon.hpp b/apps/openmw/mwclass/weapon.hpp index 4774bb50be..922cd165f2 100644 --- a/apps/openmw/mwclass/weapon.hpp +++ b/apps/openmw/mwclass/weapon.hpp @@ -67,6 +67,8 @@ namespace MWClass virtual void applyEnchantment(const MWWorld::Ptr &ptr, const std::string& enchId, int enchCharge, const std::string& newName) const; + virtual bool canEquip(const MWWorld::Ptr &npc, const MWWorld::Ptr &item) const; + virtual boost::shared_ptr use (const MWWorld::Ptr& ptr) const; ///< Generate action for using via inventory menu diff --git a/apps/openmw/mwmechanics/actors.cpp b/apps/openmw/mwmechanics/actors.cpp index f60567c6c4..89671ee086 100644 --- a/apps/openmw/mwmechanics/actors.cpp +++ b/apps/openmw/mwmechanics/actors.cpp @@ -36,8 +36,7 @@ namespace MWMechanics void Actors::updateNpc (const MWWorld::Ptr& ptr, float duration, bool paused) { if (!paused && ptr.getRefData().getHandle()!="player") - MWWorld::Class::get (ptr).getInventoryStore (ptr).autoEquip ( - MWWorld::Class::get (ptr).getNpcStats (ptr)); + MWWorld::Class::get (ptr).getInventoryStore (ptr).autoEquip (ptr); } void Actors::adjustMagicEffects (const MWWorld::Ptr& creature) diff --git a/apps/openmw/mwworld/actionequip.cpp b/apps/openmw/mwworld/actionequip.cpp index 9a44a99791..c147113fc6 100644 --- a/apps/openmw/mwworld/actionequip.cpp +++ b/apps/openmw/mwworld/actionequip.cpp @@ -43,108 +43,8 @@ namespace MWWorld for (std::vector::const_iterator slot=slots.first.begin(); slot!=slots.first.end(); ++slot) { - - // Beast races cannot equip shoes / boots, or full helms (head part vs hair part) - const ESM::Race* race = MWBase::Environment::get().getWorld()->getStore().get().find(npcRace); - if(race->mData.mFlags & ESM::Race::Beast) - { - if(*slot == MWWorld::InventoryStore::Slot_Helmet) - { - std::vector parts; - if(it.getType() == MWWorld::ContainerStore::Type_Clothing) - parts = it->get()->mBase->mParts.mParts; - else - parts = it->get()->mBase->mParts.mParts; - - bool allow = true; - for(std::vector::iterator itr = parts.begin(); itr != parts.end(); ++itr) - { - if((*itr).mPart == ESM::PRT_Head) - { - if(actor == MWBase::Environment::get().getWorld()->getPlayer().getPlayer() ) - MWBase::Environment::get().getWindowManager()->messageBox ("#{sNotifyMessage13}"); - - allow = false; - break; - } - } - - if(!allow) - break; - } - - if (*slot == MWWorld::InventoryStore::Slot_Boots) - { - bool allow = true; - std::vector parts; - if(it.getType() == MWWorld::ContainerStore::Type_Clothing) - parts = it->get()->mBase->mParts.mParts; - else - parts = it->get()->mBase->mParts.mParts; - for(std::vector::iterator itr = parts.begin(); itr != parts.end(); ++itr) - { - if((*itr).mPart == ESM::PRT_LFoot || (*itr).mPart == ESM::PRT_RFoot) - { - allow = false; - // Only notify the player, not npcs - if(actor == MWBase::Environment::get().getWorld()->getPlayer().getPlayer() ) - { - if(it.getType() == MWWorld::ContainerStore::Type_Clothing){ // It's shoes - MWBase::Environment::get().getWindowManager()->messageBox ("#{sNotifyMessage15}"); - } - - else // It's boots - { - MWBase::Environment::get().getWindowManager()->messageBox ("#{sNotifyMessage14}"); - } - } - break; - } - } - - if(!allow) - break; - } - - } - - //Disable twohanded when shield equipped, shield when twohanded equipped - if(*slot == MWWorld::InventoryStore::Slot_CarriedRight) - { - if (it.getType() == MWWorld::ContainerStore::Type_Weapon) - { - if(it->get()->mBase->mData.mType == ESM::Weapon::LongBladeTwoHand || - it->get()->mBase->mData.mType == ESM::Weapon::BluntTwoClose || - it->get()->mBase->mData.mType == ESM::Weapon::BluntTwoWide || - it->get()->mBase->mData.mType == ESM::Weapon::SpearTwoWide || - it->get()->mBase->mData.mType == ESM::Weapon::AxeTwoHand || - it->get()->mBase->mData.mType == ESM::Weapon::MarksmanBow || - it->get()->mBase->mData.mType == ESM::Weapon::MarksmanCrossbow) - { - //unequip lefthand item - invStore.equip(MWWorld::InventoryStore::Slot_CarriedLeft, invStore.end()); - } - } - } - if(*slot == MWWorld::InventoryStore::Slot_CarriedLeft) - { - ContainerStoreIterator weapon = invStore.getSlot(MWWorld::InventoryStore::Slot_CarriedRight); - - if (weapon.getType() == MWWorld::ContainerStore::Type_Weapon) - { - if(weapon->get()->mBase->mData.mType == ESM::Weapon::LongBladeTwoHand || - weapon->get()->mBase->mData.mType == ESM::Weapon::BluntTwoClose || - weapon->get()->mBase->mData.mType == ESM::Weapon::BluntTwoWide || - weapon->get()->mBase->mData.mType == ESM::Weapon::SpearTwoWide || - weapon->get()->mBase->mData.mType == ESM::Weapon::AxeTwoHand || - weapon->get()->mBase->mData.mType == ESM::Weapon::MarksmanBow || - weapon->get()->mBase->mData.mType == ESM::Weapon::MarksmanCrossbow) - { - //unequip twohanded item - invStore.equip(MWWorld::InventoryStore::Slot_CarriedRight, invStore.end()); - } - } - } + if(!MWWorld::Class::get(getTarget()).canEquip(actor,getTarget())) + break; // if all slots are occupied, replace the last slot if (slot == --slots.first.end()) diff --git a/apps/openmw/mwworld/class.cpp b/apps/openmw/mwworld/class.cpp index 2dfa241b3b..ee2072cf09 100644 --- a/apps/openmw/mwworld/class.cpp +++ b/apps/openmw/mwworld/class.cpp @@ -259,6 +259,11 @@ namespace MWWorld throw std::runtime_error ("class can't be enchanted"); } + bool Class::canEquip(const MWWorld::Ptr &npc, const MWWorld::Ptr &item) const + { + return true; + } + void Class::adjustPosition(const MWWorld::Ptr& ptr) const { } diff --git a/apps/openmw/mwworld/class.hpp b/apps/openmw/mwworld/class.hpp index de4741e38b..4931da7a86 100644 --- a/apps/openmw/mwworld/class.hpp +++ b/apps/openmw/mwworld/class.hpp @@ -242,6 +242,9 @@ namespace MWWorld virtual void applyEnchantment(const MWWorld::Ptr &ptr, const std::string& enchId, int enchCharge, const std::string& newName) const; + virtual bool canEquip(const MWWorld::Ptr &npc, const MWWorld::Ptr &item) const; + ///< Return 0 if player cannot equip item. Unequip twohanded item if neccesary + virtual Ptr copyToCell(const Ptr &ptr, CellStore &cell) const; diff --git a/apps/openmw/mwworld/inventorystore.cpp b/apps/openmw/mwworld/inventorystore.cpp index dd518ff6a4..7a5ae38d0e 100644 --- a/apps/openmw/mwworld/inventorystore.cpp +++ b/apps/openmw/mwworld/inventorystore.cpp @@ -128,8 +128,9 @@ MWWorld::ContainerStoreIterator MWWorld::InventoryStore::getSlot (int slot) return mSlots[slot]; } -void MWWorld::InventoryStore::autoEquip (const MWMechanics::NpcStats& stats) +void MWWorld::InventoryStore::autoEquip (const MWWorld::Ptr& npc) { + const MWMechanics::NpcStats& stats = MWWorld::Class::get(npc).getNpcStats(npc); TSlots slots; initSlots (slots); @@ -183,6 +184,9 @@ void MWWorld::InventoryStore::autoEquip (const MWMechanics::NpcStats& stats) } } + if(!MWWorld::Class::get (test).canEquip (npc, test)) + continue; + if (!itemsSlots.second) // if itemsSlots.second is true, item can stay stacked when equipped { // unstack item pointed to by iterator if required diff --git a/apps/openmw/mwworld/inventorystore.hpp b/apps/openmw/mwworld/inventorystore.hpp index e55eca0ae7..cc28a0a0c0 100644 --- a/apps/openmw/mwworld/inventorystore.hpp +++ b/apps/openmw/mwworld/inventorystore.hpp @@ -78,7 +78,7 @@ namespace MWWorld ContainerStoreIterator getSlot (int slot); - void autoEquip (const MWMechanics::NpcStats& stats); + void autoEquip (const MWWorld::Ptr& npc); ///< Auto equip items according to stats and item value. const MWMechanics::MagicEffects& getMagicEffects();