diff --git a/apps/openmw/mwbase/mechanicsmanager.hpp b/apps/openmw/mwbase/mechanicsmanager.hpp index 1de6fdaaf..01b1edff0 100644 --- a/apps/openmw/mwbase/mechanicsmanager.hpp +++ b/apps/openmw/mwbase/mechanicsmanager.hpp @@ -150,7 +150,7 @@ namespace MWBase /// Utility to check if taking this item is illegal and calling commitCrime if so /// @param container The container the item is in; may be empty for an item in the world virtual void itemTaken (const MWWorld::Ptr& ptr, const MWWorld::Ptr& item, const MWWorld::Ptr& container, - int count) = 0; + int count, bool alarm = true) = 0; /// Utility to check if opening (i.e. unlocking) this object is illegal and calling commitCrime if so virtual void objectOpened (const MWWorld::Ptr& ptr, const MWWorld::Ptr& item) = 0; /// Attempt sleeping in a bed. If this is illegal, call commitCrime. @@ -248,6 +248,7 @@ namespace MWBase /// Has the player stolen this item from the given owner? virtual bool isItemStolenFrom(const std::string& itemid, const std::string& ownerid) = 0; + virtual bool isBoundItem(const MWWorld::Ptr& item) = 0; virtual bool isAllowedToUse (const MWWorld::Ptr& ptr, const MWWorld::Ptr& target, MWWorld::Ptr& victim) = 0; /// Turn actor into werewolf or normal form. @@ -260,7 +261,6 @@ namespace MWBase virtual void cleanupSummonedCreature(const MWWorld::Ptr& caster, int creatureActorId) = 0; virtual void confiscateStolenItemToOwner(const MWWorld::Ptr &player, const MWWorld::Ptr &item, const MWWorld::Ptr& victim, int count) = 0; - virtual bool isAttackPrepairing(const MWWorld::Ptr& ptr) = 0; virtual bool isRunning(const MWWorld::Ptr& ptr) = 0; virtual bool isSneaking(const MWWorld::Ptr& ptr) = 0; diff --git a/apps/openmw/mwgui/container.cpp b/apps/openmw/mwgui/container.cpp index 085404fcc..8817c0088 100644 --- a/apps/openmw/mwgui/container.cpp +++ b/apps/openmw/mwgui/container.cpp @@ -22,12 +22,10 @@ #include "../mwbase/windowmanager.hpp" #include "../mwbase/dialoguemanager.hpp" #include "../mwbase/mechanicsmanager.hpp" -#include "../mwmechanics/actorutil.hpp" #include "../mwworld/class.hpp" #include "../mwworld/inventorystore.hpp" -#include "../mwmechanics/pickpocket.hpp" #include "../mwmechanics/creaturestats.hpp" #include "countdialog.hpp" @@ -47,7 +45,6 @@ namespace MWGui ContainerWindow::ContainerWindow(DragAndDrop* dragAndDrop) : WindowBase("openmw_container_window.layout") , mDragAndDrop(dragAndDrop) - , mPickpocketDetected(false) , mSortModel(NULL) , mModel(NULL) , mSelectedItem(-1) @@ -69,10 +66,9 @@ namespace MWGui void ContainerWindow::onItemSelected(int index) { - if (mDragAndDrop->mIsOnDragAndDrop) + if (mDragAndDrop->mIsOnDragAndDrop && mModel) { - if (mModel && mModel->allowedToInsertItems()) - dropItem(); + dropItem(); return; } @@ -151,71 +147,56 @@ namespace MWGui void ContainerWindow::dropItem() { - if (mPtr.getTypeName() == typeid(ESM::Container).name()) - { - // check container organic flag - MWWorld::LiveCellRef* ref = mPtr.get(); - if (ref->mBase->mFlags & ESM::Container::Organic) - { - MWBase::Environment::get().getWindowManager()-> - messageBox("#{sContentsMessage2}"); - return; - } - - // check that we don't exceed container capacity - MWWorld::Ptr item = mDragAndDrop->mItem.mBase; - float weight = item.getClass().getWeight(item) * mDragAndDrop->mDraggedCount; - if (mPtr.getClass().getCapacity(mPtr) < mPtr.getClass().getEncumbrance(mPtr) + weight) - { - MWBase::Environment::get().getWindowManager()->messageBox("#{sContentsMessage3}"); - return; - } - } + bool success = mModel->onDropItem(mDragAndDrop->mItem.mBase, mDragAndDrop->mDraggedCount); /* Start of tes3mp addition Send an ID_CONTAINER packet every time an item is dropped in a container */ - mwmp::WorldEvent *worldEvent = mwmp::Main::get().getNetworking()->getWorldEvent(); - worldEvent->reset(); - worldEvent->cell = *mPtr.getCell()->getCell(); - worldEvent->action = mwmp::BaseEvent::ADD; + if (success) + { + mwmp::WorldEvent *worldEvent = mwmp::Main::get().getNetworking()->getWorldEvent(); + worldEvent->reset(); + worldEvent->cell = *mPtr.getCell()->getCell(); + worldEvent->action = mwmp::BaseEvent::ADD; - mwmp::WorldObject worldObject; - worldObject.refId = mPtr.getCellRef().getRefId(); - worldObject.refNumIndex = mPtr.getCellRef().getRefNum().mIndex; - worldObject.mpNum = mPtr.getCellRef().getMpNum(); + mwmp::WorldObject worldObject; + worldObject.refId = mPtr.getCellRef().getRefId(); + worldObject.refNumIndex = mPtr.getCellRef().getRefNum().mIndex; + worldObject.mpNum = mPtr.getCellRef().getMpNum(); - MWWorld::Ptr itemPtr = mDragAndDrop->mItem.mBase; + MWWorld::Ptr itemPtr = mDragAndDrop->mItem.mBase; - mwmp::ContainerItem containerItem; - containerItem.refId = itemPtr.getCellRef().getRefId(); - - // Make sure we get the drag and drop count, not the count of the original item - containerItem.count = mDragAndDrop->mDraggedCount; + mwmp::ContainerItem containerItem; + containerItem.refId = itemPtr.getCellRef().getRefId(); + + // Make sure we get the drag and drop count, not the count of the original item + containerItem.count = mDragAndDrop->mDraggedCount; - containerItem.charge = itemPtr.getCellRef().getCharge(); + containerItem.charge = itemPtr.getCellRef().getCharge(); - worldObject.containerItems.push_back(containerItem); - worldEvent->addObject(worldObject); + worldObject.containerItems.push_back(containerItem); + worldEvent->addObject(worldObject); - mwmp::Main::get().getNetworking()->getWorldPacket(ID_CONTAINER)->setEvent(worldEvent); - mwmp::Main::get().getNetworking()->getWorldPacket(ID_CONTAINER)->Send(); + mwmp::Main::get().getNetworking()->getWorldPacket(ID_CONTAINER)->setEvent(worldEvent); + mwmp::Main::get().getNetworking()->getWorldPacket(ID_CONTAINER)->Send(); - LOG_MESSAGE_SIMPLE(Log::LOG_INFO, "Sending ID_CONTAINER about\n- Ptr cellRef: %s, %i\n- cell: %s\n- item: %s, %i", - worldObject.refId.c_str(), worldObject.refNumIndex, worldEvent->cell.getDescription().c_str(), - containerItem.refId.c_str(), containerItem.count); + LOG_MESSAGE_SIMPLE(Log::LOG_INFO, "Sending ID_CONTAINER about\n- Ptr cellRef: %s, %i\n- cell: %s\n- item: %s, %i", + worldObject.refId.c_str(), worldObject.refNumIndex, worldEvent->cell.getDescription().c_str(), + containerItem.refId.c_str(), containerItem.count); + } /* End of tes3mp addition */ - mDragAndDrop->drop(mModel, mItemView); + if (success) + mDragAndDrop->drop(mModel, mItemView); } void ContainerWindow::onBackgroundSelected() { - if (mDragAndDrop->mIsOnDragAndDrop && mModel && mModel->allowedToInsertItems()) + if (mDragAndDrop->mIsOnDragAndDrop && mModel) dropItem(); } @@ -231,7 +212,6 @@ namespace MWGui End of tes3mp addition */ - mPickpocketDetected = false; mPtr = container; bool loot = mPtr.getClass().isActor() && mPtr.getClass().getCreatureStats(mPtr).isDead(); @@ -241,8 +221,7 @@ namespace MWGui if (mPtr.getClass().isNpc() && !loot) { // we are stealing stuff - MWWorld::Ptr player = MWMechanics::getPlayer(); - mModel = new PickpocketItemModel(player, new InventoryItemModel(container), + mModel = new PickpocketItemModel(mPtr, new InventoryItemModel(container), !mPtr.getClass().getCreatureStats(mPtr).getKnockedDown()); } else @@ -287,24 +266,7 @@ namespace MWGui WindowBase::onClose(); - if (dynamic_cast(mModel) - // Make sure we were actually closed, rather than just temporarily hidden (e.g. console or main menu opened) - && !MWBase::Environment::get().getWindowManager()->containsMode(GM_Container) - // If it was already detected while taking an item, no need to check now - && !mPickpocketDetected - ) - { - MWWorld::Ptr player = MWMechanics::getPlayer(); - MWMechanics::Pickpocket pickpocket(player, mPtr); - if (pickpocket.finish()) - { - MWBase::Environment::get().getMechanicsManager()->commitCrime( - player, mPtr, MWBase::MechanicsManager::OT_Pickpocket, 0, true); - MWBase::Environment::get().getWindowManager()->removeGuiMode(MWGui::GM_Container); - mPickpocketDetected = true; - return; - } - } + mModel->onClose(); } void ContainerWindow::onCloseButtonClicked(MyGUI::Widget* _sender) @@ -422,32 +384,7 @@ namespace MWGui bool ContainerWindow::onTakeItem(const ItemStack &item, int count) { - MWWorld::Ptr player = MWMechanics::getPlayer(); - // TODO: move to ItemModels - if (dynamic_cast(mModel) - && !mPtr.getClass().getCreatureStats(mPtr).getKnockedDown()) - { - MWMechanics::Pickpocket pickpocket(player, mPtr); - if (pickpocket.pick(item.mBase, count)) - { - MWBase::Environment::get().getMechanicsManager()->commitCrime( - player, mPtr, MWBase::MechanicsManager::OT_Pickpocket, 0, true); - MWBase::Environment::get().getWindowManager()->removeGuiMode(MWGui::GM_Container); - mPickpocketDetected = true; - return false; - } - else - player.getClass().skillUsageSucceeded(player, ESM::Skill::Sneak, 1); - } - else - { - // Looting a dead corpse is considered OK - if (mPtr.getClass().isActor() && mPtr.getClass().getCreatureStats(mPtr).isDead()) - return true; - else - MWBase::Environment::get().getMechanicsManager()->itemTaken(player, item.mBase, mPtr, count); - } - return true; + return mModel->onTakeItem(item.mBase, count); } } diff --git a/apps/openmw/mwgui/container.hpp b/apps/openmw/mwgui/container.hpp index 50c69da3b..cf02d165d 100644 --- a/apps/openmw/mwgui/container.hpp +++ b/apps/openmw/mwgui/container.hpp @@ -44,8 +44,6 @@ namespace MWGui private: DragAndDrop* mDragAndDrop; - bool mPickpocketDetected; - MWGui::ItemView* mItemView; SortFilterItemModel* mSortModel; ItemModel* mModel; diff --git a/apps/openmw/mwgui/containeritemmodel.cpp b/apps/openmw/mwgui/containeritemmodel.cpp index 9f7a9965c..24e7f8d00 100644 --- a/apps/openmw/mwgui/containeritemmodel.cpp +++ b/apps/openmw/mwgui/containeritemmodel.cpp @@ -2,12 +2,16 @@ #include +#include "../mwmechanics/creaturestats.hpp" +#include "../mwmechanics/actorutil.hpp" + #include "../mwworld/containerstore.hpp" #include "../mwworld/class.hpp" -#include "../mwbase/world.hpp" -#include "../mwbase/mechanicsmanager.hpp" #include "../mwbase/environment.hpp" +#include "../mwbase/mechanicsmanager.hpp" +#include "../mwbase/windowmanager.hpp" +#include "../mwbase/world.hpp" #include "../mwmechanics/actorutil.hpp" @@ -185,5 +189,51 @@ void ContainerItemModel::update() } } } +bool ContainerItemModel::onDropItem(const MWWorld::Ptr &item, int count) +{ + if (mItemSources.empty()) + return false; + + MWWorld::Ptr target = mItemSources[0]; + + if (target.getTypeName() != typeid(ESM::Container).name()) + return true; + + // check container organic flag + MWWorld::LiveCellRef* ref = target.get(); + if (ref->mBase->mFlags & ESM::Container::Organic) + { + MWBase::Environment::get().getWindowManager()-> + messageBox("#{sContentsMessage2}"); + return false; + } + + // check that we don't exceed container capacity + float weight = item.getClass().getWeight(item) * count; + if (target.getClass().getCapacity(target) < target.getClass().getEncumbrance(target) + weight) + { + MWBase::Environment::get().getWindowManager()->messageBox("#{sContentsMessage3}"); + return false; + } + + return true; +} + +bool ContainerItemModel::onTakeItem(const MWWorld::Ptr &item, int count) +{ + if (mItemSources.empty()) + return false; + + MWWorld::Ptr target = mItemSources[0]; + + // Looting a dead corpse is considered OK + if (target.getClass().isActor() && target.getClass().getCreatureStats(target).isDead()) + return true; + + MWWorld::Ptr player = MWMechanics::getPlayer(); + MWBase::Environment::get().getMechanicsManager()->itemTaken(player, item, target, count); + + return true; +} } diff --git a/apps/openmw/mwgui/containeritemmodel.hpp b/apps/openmw/mwgui/containeritemmodel.hpp index c6ecafd46..e8d1c5328 100644 --- a/apps/openmw/mwgui/containeritemmodel.hpp +++ b/apps/openmw/mwgui/containeritemmodel.hpp @@ -18,6 +18,10 @@ namespace MWGui ContainerItemModel (const MWWorld::Ptr& source); virtual bool allowedToUseItems() const; + + virtual bool onDropItem(const MWWorld::Ptr &item, int count); + virtual bool onTakeItem(const MWWorld::Ptr &item, int count); + virtual ItemStack getItem (ModelIndex index); virtual ModelIndex getIndex (ItemStack item); virtual size_t getItemCount(); diff --git a/apps/openmw/mwgui/inventoryitemmodel.cpp b/apps/openmw/mwgui/inventoryitemmodel.cpp index 222243ec1..c9f55d352 100644 --- a/apps/openmw/mwgui/inventoryitemmodel.cpp +++ b/apps/openmw/mwgui/inventoryitemmodel.cpp @@ -9,6 +9,9 @@ #include "../mwworld/class.hpp" #include "../mwworld/inventorystore.hpp" +#include "../mwbase/environment.hpp" +#include "../mwbase/mechanicsmanager.hpp" + namespace MWGui { @@ -116,4 +119,16 @@ void InventoryItemModel::update() } } +bool InventoryItemModel::onTakeItem(const MWWorld::Ptr &item, int count) +{ + // Looting a dead corpse is considered OK + if (mActor.getClass().isActor() && mActor.getClass().getCreatureStats(mActor).isDead()) + return true; + + MWWorld::Ptr player = MWMechanics::getPlayer(); + MWBase::Environment::get().getMechanicsManager()->itemTaken(player, item, mActor, count); + + return true; +} + } diff --git a/apps/openmw/mwgui/inventoryitemmodel.hpp b/apps/openmw/mwgui/inventoryitemmodel.hpp index f58ee2939..38730bd4d 100644 --- a/apps/openmw/mwgui/inventoryitemmodel.hpp +++ b/apps/openmw/mwgui/inventoryitemmodel.hpp @@ -15,6 +15,8 @@ namespace MWGui virtual ModelIndex getIndex (ItemStack item); virtual size_t getItemCount(); + virtual bool onTakeItem(const MWWorld::Ptr &item, int count); + virtual MWWorld::Ptr copyItem (const ItemStack& item, size_t count, bool setNewOwner=false); virtual void removeItem (const ItemStack& item, size_t count); diff --git a/apps/openmw/mwgui/itemmodel.cpp b/apps/openmw/mwgui/itemmodel.cpp index ffcf9075e..e3f38a54c 100644 --- a/apps/openmw/mwgui/itemmodel.cpp +++ b/apps/openmw/mwgui/itemmodel.cpp @@ -9,6 +9,7 @@ #include "../mwbase/world.hpp" #include "../mwbase/environment.hpp" +#include "../mwbase/mechanicsmanager.hpp" namespace MWGui { @@ -23,38 +24,7 @@ namespace MWGui if (base.getClass().getEnchantment(base) != "") mFlags |= Flag_Enchanted; - static std::set boundItemIDCache; - - // If this is empty then we haven't executed the GMST cache logic yet; or there isn't any sMagicBound* GMST's for some reason - if (boundItemIDCache.empty()) - { - // Build a list of known bound item ID's - const MWWorld::Store &gameSettings = MWBase::Environment::get().getWorld()->getStore().get(); - - for (MWWorld::Store::iterator currentIteration = gameSettings.begin(); currentIteration != gameSettings.end(); ++currentIteration) - { - const ESM::GameSetting ¤tSetting = *currentIteration; - std::string currentGMSTID = currentSetting.mId; - Misc::StringUtils::lowerCaseInPlace(currentGMSTID); - - // Don't bother checking this GMST if it's not a sMagicBound* one. - const std::string& toFind = "smagicbound"; - if (currentGMSTID.compare(0, toFind.length(), toFind) != 0) - continue; - - // All sMagicBound* GMST's should be of type string - std::string currentGMSTValue = currentSetting.getString(); - Misc::StringUtils::lowerCaseInPlace(currentGMSTValue); - - boundItemIDCache.insert(currentGMSTValue); - } - } - - // Perform bound item check and assign the Flag_Bound bit if it passes - std::string tempItemID = base.getCellRef().getRefId(); - Misc::StringUtils::lowerCaseInPlace(tempItemID); - - if (boundItemIDCache.count(tempItemID) != 0) + if (MWBase::Environment::get().getMechanicsManager()->isBoundItem(base)) mFlags |= Flag_Bound; } @@ -124,7 +94,12 @@ namespace MWGui return true; } - bool ItemModel::allowedToInsertItems() const + bool ItemModel::onDropItem(const MWWorld::Ptr &item, int count) + { + return true; + } + + bool ItemModel::onTakeItem(const MWWorld::Ptr &item, int count) { return true; } @@ -198,4 +173,18 @@ namespace MWGui mSourceModel = sourceModel; } + void ProxyItemModel::onClose() + { + mSourceModel->onClose(); + } + + bool ProxyItemModel::onDropItem(const MWWorld::Ptr &item, int count) + { + return mSourceModel->onDropItem(item, count); + } + + bool ProxyItemModel::onTakeItem(const MWWorld::Ptr &item, int count) + { + return mSourceModel->onTakeItem(item, count); + } } diff --git a/apps/openmw/mwgui/itemmodel.hpp b/apps/openmw/mwgui/itemmodel.hpp index bc6be8023..e8e348a8a 100644 --- a/apps/openmw/mwgui/itemmodel.hpp +++ b/apps/openmw/mwgui/itemmodel.hpp @@ -72,9 +72,11 @@ namespace MWGui /// Is the player allowed to use items from this item model? (default true) virtual bool allowedToUseItems() const; - - /// Is the player allowed to insert items into this model? (default true) - virtual bool allowedToInsertItems() const; + virtual void onClose() + { + } + virtual bool onDropItem(const MWWorld::Ptr &item, int count); + virtual bool onTakeItem(const MWWorld::Ptr &item, int count); private: ItemModel(const ItemModel&); @@ -91,6 +93,10 @@ namespace MWGui bool allowedToUseItems() const; + virtual void onClose(); + virtual bool onDropItem(const MWWorld::Ptr &item, int count); + virtual bool onTakeItem(const MWWorld::Ptr &item, int count); + virtual MWWorld::Ptr copyItem (const ItemStack& item, size_t count, bool setNewOwner=false); virtual void removeItem (const ItemStack& item, size_t count); virtual ModelIndex getIndex (ItemStack item); diff --git a/apps/openmw/mwgui/pickpocketitemmodel.cpp b/apps/openmw/mwgui/pickpocketitemmodel.cpp index ec4b378a0..e85715e87 100644 --- a/apps/openmw/mwgui/pickpocketitemmodel.cpp +++ b/apps/openmw/mwgui/pickpocketitemmodel.cpp @@ -3,15 +3,26 @@ #include #include +#include "../mwmechanics/actorutil.hpp" +#include "../mwmechanics/creaturestats.hpp" +#include "../mwmechanics/pickpocket.hpp" + +#include "../mwworld/containerstore.hpp" #include "../mwworld/class.hpp" +#include "../mwbase/environment.hpp" +#include "../mwbase/mechanicsmanager.hpp" +#include "../mwbase/windowmanager.hpp" + namespace MWGui { - PickpocketItemModel::PickpocketItemModel(const MWWorld::Ptr& thief, ItemModel *sourceModel, bool hideItems) + PickpocketItemModel::PickpocketItemModel(const MWWorld::Ptr& actor, ItemModel *sourceModel, bool hideItems) + : mActor(actor), mPickpocketDetected(false) { + MWWorld::Ptr player = MWMechanics::getPlayer(); mSourceModel = sourceModel; - int chance = thief.getClass().getSkill(thief, ESM::Skill::Sneak); + int chance = player.getClass().getSkill(player, ESM::Skill::Sneak); mSourceModel->update(); @@ -66,13 +77,63 @@ namespace MWGui void PickpocketItemModel::removeItem (const ItemStack &item, size_t count) { ProxyItemModel::removeItem(item, count); - /// \todo check if player is detected } - bool PickpocketItemModel::allowedToInsertItems() const + bool PickpocketItemModel::onDropItem(const MWWorld::Ptr &item, int count) { - // don't allow "reverse pickpocket" (yet) + // don't allow "reverse pickpocket" (it will be handled by scripts after 1.0) return false; } + void PickpocketItemModel::onClose() + { + // Make sure we were actually closed, rather than just temporarily hidden (e.g. console or main menu opened) + if (MWBase::Environment::get().getWindowManager()->containsMode(GM_Container) + // If it was already detected while taking an item, no need to check now + || mPickpocketDetected) + return; + + MWWorld::Ptr player = MWMechanics::getPlayer(); + MWMechanics::Pickpocket pickpocket(player, mActor); + if (pickpocket.finish()) + { + MWBase::Environment::get().getMechanicsManager()->commitCrime( + player, mActor, MWBase::MechanicsManager::OT_Pickpocket, 0, true); + MWBase::Environment::get().getWindowManager()->removeGuiMode(MWGui::GM_Container); + mPickpocketDetected = true; + } + } + + bool PickpocketItemModel::onTakeItem(const MWWorld::Ptr &item, int count) + { + if (mActor.getClass().getCreatureStats(mActor).getKnockedDown()) + return mSourceModel->onTakeItem(item, count); + + bool success = stealItem(item, count); + if (success) + { + MWWorld::Ptr player = MWMechanics::getPlayer(); + MWBase::Environment::get().getMechanicsManager()->itemTaken(player, item, mActor, count, false); + } + + return success; + } + + bool PickpocketItemModel::stealItem(const MWWorld::Ptr &item, int count) + { + MWWorld::Ptr player = MWMechanics::getPlayer(); + MWMechanics::Pickpocket pickpocket(player, mActor); + if (pickpocket.pick(item, count)) + { + MWBase::Environment::get().getMechanicsManager()->commitCrime( + player, mActor, MWBase::MechanicsManager::OT_Pickpocket, 0, true); + MWBase::Environment::get().getWindowManager()->removeGuiMode(MWGui::GM_Container); + mPickpocketDetected = true; + return false; + } + else + player.getClass().skillUsageSucceeded(player, ESM::Skill::Sneak, 1); + + return true; + } } diff --git a/apps/openmw/mwgui/pickpocketitemmodel.hpp b/apps/openmw/mwgui/pickpocketitemmodel.hpp index 1b08ec485..83caf2ffd 100644 --- a/apps/openmw/mwgui/pickpocketitemmodel.hpp +++ b/apps/openmw/mwgui/pickpocketitemmodel.hpp @@ -17,7 +17,14 @@ namespace MWGui virtual size_t getItemCount(); virtual void update(); virtual void removeItem (const ItemStack& item, size_t count); - virtual bool allowedToInsertItems() const; + virtual void onClose(); + virtual bool onDropItem(const MWWorld::Ptr &item, int count); + virtual bool onTakeItem(const MWWorld::Ptr &item, int count); + + protected: + MWWorld::Ptr mActor; + bool mPickpocketDetected; + bool stealItem(const MWWorld::Ptr &item, int count); private: std::vector mHiddenItems; diff --git a/apps/openmw/mwgui/sortfilteritemmodel.cpp b/apps/openmw/mwgui/sortfilteritemmodel.cpp index e294ebe07..1ee3cf631 100644 --- a/apps/openmw/mwgui/sortfilteritemmodel.cpp +++ b/apps/openmw/mwgui/sortfilteritemmodel.cpp @@ -311,4 +311,18 @@ namespace MWGui std::sort(mItems.begin(), mItems.end(), cmp); } + void SortFilterItemModel::onClose() + { + mSourceModel->onClose(); + } + + bool SortFilterItemModel::onDropItem(const MWWorld::Ptr &item, int count) + { + return mSourceModel->onDropItem(item, count); + } + + bool SortFilterItemModel::onTakeItem(const MWWorld::Ptr &item, int count) + { + return mSourceModel->onTakeItem(item, count); + } } diff --git a/apps/openmw/mwgui/sortfilteritemmodel.hpp b/apps/openmw/mwgui/sortfilteritemmodel.hpp index 6ddb019b0..98da8d8c9 100644 --- a/apps/openmw/mwgui/sortfilteritemmodel.hpp +++ b/apps/openmw/mwgui/sortfilteritemmodel.hpp @@ -29,6 +29,10 @@ namespace MWGui /// Use ItemStack::Type for sorting? void setSortByType(bool sort) { mSortByType = sort; } + void onClose(); + bool onDropItem(const MWWorld::Ptr &item, int count); + bool onTakeItem(const MWWorld::Ptr &item, int count); + static const int Category_Weapon = (1<<1); static const int Category_Apparel = (1<<2); static const int Category_Misc = (1<<3); diff --git a/apps/openmw/mwmechanics/actors.cpp b/apps/openmw/mwmechanics/actors.cpp index 4247a052f..7bce03bfe 100644 --- a/apps/openmw/mwmechanics/actors.cpp +++ b/apps/openmw/mwmechanics/actors.cpp @@ -1381,8 +1381,13 @@ namespace MWMechanics float sqrHeadTrackDistance = std::numeric_limits::max(); MWWorld::Ptr headTrackTarget; + MWMechanics::CreatureStats& stats = iter->first.getClass().getCreatureStats(iter->first); + // Unconsious actor can not track target - if (!iter->first.getClass().getCreatureStats(iter->first).getKnockedDown()) + // Also actors in combat and pursue mode do not bother to headtrack + if (!stats.getKnockedDown() && + !stats.getAiSequence().isInCombat() && + !stats.getAiSequence().hasPackage(AiPackage::TypeIdPursue)) { for(PtrActorMap::iterator it(mActors.begin()); it != mActors.end(); ++it) { @@ -1390,8 +1395,9 @@ namespace MWMechanics continue; updateHeadTracking(iter->first, it->first, headTrackTarget, sqrHeadTrackDistance); } - iter->second->getCharacterController()->setHeadTrackTarget(headTrackTarget); } + + iter->second->getCharacterController()->setHeadTrackTarget(headTrackTarget); } if (iter->first.getClass().isNpc() && iter->first != player && (isLocalActor || isAIActive)) diff --git a/apps/openmw/mwmechanics/aicombat.cpp b/apps/openmw/mwmechanics/aicombat.cpp index 1d761ae6b..352ce6a9b 100644 --- a/apps/openmw/mwmechanics/aicombat.cpp +++ b/apps/openmw/mwmechanics/aicombat.cpp @@ -558,6 +558,22 @@ namespace MWMechanics void AiCombatStorage::startCombatMove(bool isDistantCombat, float distToTarget, float rangeAttack, const MWWorld::Ptr& actor, const MWWorld::Ptr& target) { + // get the range of the target's weapon + MWWorld::Ptr targetWeapon = MWWorld::Ptr(); + const MWWorld::Class& targetClass = target.getClass(); + + if (targetClass.hasInventoryStore(target)) + { + MWMechanics::WeaponType weapType = WeapType_None; + MWWorld::ContainerStoreIterator weaponSlot = + MWMechanics::getActiveWeapon(targetClass.getCreatureStats(target), targetClass.getInventoryStore(target), &weapType); + if (weapType != WeapType_PickProbe && weapType != WeapType_Spell && weapType != WeapType_None && weapType != WeapType_HandToHand) + targetWeapon = *weaponSlot; + } + + bool targetUsesRanged = false; + float rangeAttackOfTarget = ActionWeapon(targetWeapon).getCombatRange(targetUsesRanged); + if (mMovement.mPosition[0] || mMovement.mPosition[1]) { mTimerCombatMove = 0.1f + 0.1f * Misc::Rng::rollClosedProbability(); @@ -566,28 +582,6 @@ namespace MWMechanics // dodge movements (for NPCs and bipedal creatures) else if (actor.getClass().isBipedal(actor)) { - // get the range of the target's weapon - float rangeAttackOfTarget = 0.f; - MWWorld::Ptr targetWeapon = MWWorld::Ptr(); - const MWWorld::Class& targetClass = target.getClass(); - - if (targetClass.hasInventoryStore(target)) - { - MWMechanics::WeaponType weapType = WeapType_None; - MWWorld::ContainerStoreIterator weaponSlot = - MWMechanics::getActiveWeapon(targetClass.getCreatureStats(target), targetClass.getInventoryStore(target), &weapType); - if (weapType != WeapType_PickProbe && weapType != WeapType_Spell && weapType != WeapType_None && weapType != WeapType_HandToHand) - targetWeapon = *weaponSlot; - } - - std::shared_ptr targetWeaponAction (new ActionWeapon(targetWeapon)); - - if (targetWeaponAction.get()) - { - bool isRangedCombat = false; - rangeAttackOfTarget = targetWeaponAction->getCombatRange(isRangedCombat); - } - // apply sideway movement (kind of dodging) with some probability // if actor is within range of target's weapon if (distToTarget <= rangeAttackOfTarget && Misc::Rng::rollClosedProbability() < 0.25) @@ -598,16 +592,18 @@ namespace MWMechanics } } - // Below behavior for backing up during ranged combat differs from vanilla. - // Vanilla is observed as backing up only as far as fCombatDistance or - // opponent's weapon range, or not backing up if opponent is also using a ranged weapon - if (isDistantCombat && distToTarget < rangeAttack / 4) + // Backing up behaviour + // Actor backs up slightly further away than opponent's weapon range + // (in vanilla - only as far as oponent's weapon range), + // or not at all if opponent is using a ranged weapon + if (isDistantCombat) { // actor should not back up into water if (MWBase::Environment::get().getWorld()->isUnderwater(MWWorld::ConstPtr(actor), 0.5f)) return; - mMovement.mPosition[1] = -1; + if (!targetUsesRanged && distToTarget <= rangeAttackOfTarget*1.5) // Don't back up if the target is wielding ranged weapon + mMovement.mPosition[1] = -1; } } diff --git a/apps/openmw/mwmechanics/aicombataction.cpp b/apps/openmw/mwmechanics/aicombataction.cpp index d44498966..df636dd56 100644 --- a/apps/openmw/mwmechanics/aicombataction.cpp +++ b/apps/openmw/mwmechanics/aicombataction.cpp @@ -115,6 +115,7 @@ namespace MWMechanics isRanged = false; static const float fCombatDistance = MWBase::Environment::get().getWorld()->getStore().get().find("fCombatDistance")->getFloat(); + static const float fProjectileMaxSpeed = MWBase::Environment::get().getWorld()->getStore().get().find("fProjectileMaxSpeed")->getFloat(); if (mWeapon.isEmpty()) { @@ -128,7 +129,7 @@ namespace MWMechanics if (weapon->mData.mType >= ESM::Weapon::MarksmanBow) { isRanged = true; - return 1000.f; + return fProjectileMaxSpeed; } else return weapon->mData.mReach * fCombatDistance; diff --git a/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp b/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp index a361f5c93..021444915 100644 --- a/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp +++ b/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp @@ -866,6 +866,45 @@ namespace MWMechanics mAI = true; } + bool MechanicsManager::isBoundItem(const MWWorld::Ptr& item) + { + static std::set boundItemIDCache; + + // If this is empty then we haven't executed the GMST cache logic yet; or there isn't any sMagicBound* GMST's for some reason + if (boundItemIDCache.empty()) + { + // Build a list of known bound item ID's + const MWWorld::Store &gameSettings = MWBase::Environment::get().getWorld()->getStore().get(); + + for (MWWorld::Store::iterator currentIteration = gameSettings.begin(); currentIteration != gameSettings.end(); ++currentIteration) + { + const ESM::GameSetting ¤tSetting = *currentIteration; + std::string currentGMSTID = currentSetting.mId; + Misc::StringUtils::lowerCaseInPlace(currentGMSTID); + + // Don't bother checking this GMST if it's not a sMagicBound* one. + const std::string& toFind = "smagicbound"; + if (currentGMSTID.compare(0, toFind.length(), toFind) != 0) + continue; + + // All sMagicBound* GMST's should be of type string + std::string currentGMSTValue = currentSetting.getString(); + Misc::StringUtils::lowerCaseInPlace(currentGMSTValue); + + boundItemIDCache.insert(currentGMSTValue); + } + } + + // Perform bound item check and assign the Flag_Bound bit if it passes + std::string tempItemID = item.getCellRef().getRefId(); + Misc::StringUtils::lowerCaseInPlace(tempItemID); + + if (boundItemIDCache.count(tempItemID) != 0) + return true; + + return false; + } + bool MechanicsManager::isAllowedToUse (const MWWorld::Ptr& ptr, const MWWorld::Ptr& target, MWWorld::Ptr& victim) { if (target.isEmpty()) @@ -1050,7 +1089,7 @@ namespace MWMechanics } void MechanicsManager::itemTaken(const MWWorld::Ptr &ptr, const MWWorld::Ptr &item, const MWWorld::Ptr& container, - int count) + int count, bool alarm) { if (ptr != getPlayer()) return; @@ -1079,19 +1118,29 @@ namespace MWMechanics return; Owner owner; - owner.first = ownerCellRef->getOwner(); owner.second = false; - if (owner.first.empty()) + if (!container.isEmpty() && container.getClass().isActor()) { - owner.first = ownerCellRef->getFaction(); - owner.second = true; + // "container" is an actor inventory, so just take actor's ID + owner.first = ownerCellRef->getRefId(); } + else + { + owner.first = ownerCellRef->getOwner(); + if (owner.first.empty()) + { + owner.first = ownerCellRef->getFaction(); + owner.second = true; + } + } + Misc::StringUtils::lowerCaseInPlace(owner.first); if (!Misc::StringUtils::ciEqual(item.getCellRef().getRefId(), MWWorld::ContainerStore::sGoldId)) mStolenItems[Misc::StringUtils::lowerCase(item.getCellRef().getRefId())][owner] += count; - commitCrime(ptr, victim, OT_Theft, item.getClass().getValue(item) * count); + if (alarm) + commitCrime(ptr, victim, OT_Theft, item.getClass().getValue(item) * count); } diff --git a/apps/openmw/mwmechanics/mechanicsmanagerimp.hpp b/apps/openmw/mwmechanics/mechanicsmanagerimp.hpp index 25c2dae3d..fafcdcd28 100644 --- a/apps/openmw/mwmechanics/mechanicsmanagerimp.hpp +++ b/apps/openmw/mwmechanics/mechanicsmanagerimp.hpp @@ -145,7 +145,7 @@ namespace MWMechanics /// Utility to check if taking this item is illegal and calling commitCrime if so /// @param container The container the item is in; may be empty for an item in the world virtual void itemTaken (const MWWorld::Ptr& ptr, const MWWorld::Ptr& item, const MWWorld::Ptr& container, - int count); + int count, bool alarm = true); /// Utility to check if opening (i.e. unlocking) this object is illegal and calling commitCrime if so virtual void objectOpened (const MWWorld::Ptr& ptr, const MWWorld::Ptr& item); /// Attempt sleeping in a bed. If this is illegal, call commitCrime. @@ -214,6 +214,8 @@ namespace MWMechanics /// Has the player stolen this item from the given owner? virtual bool isItemStolenFrom(const std::string& itemid, const std::string& ownerid); + virtual bool isBoundItem(const MWWorld::Ptr& item); + /// @return is \a ptr allowed to take/use \a target or is it a crime? virtual bool isAllowedToUse (const MWWorld::Ptr& ptr, const MWWorld::Ptr& target, MWWorld::Ptr& victim);