From 780bf5a2cdcc0bdb642901685165f6882c4bbec8 Mon Sep 17 00:00:00 2001 From: scrawl Date: Tue, 7 Jan 2014 03:01:33 +0100 Subject: [PATCH] Implement pickpocket detection. Play a voiced dialogue entry when detected. --- apps/openmw/CMakeLists.txt | 2 +- apps/openmw/mwgui/container.cpp | 57 +++++++++++++++++- apps/openmw/mwgui/container.hpp | 6 ++ apps/openmw/mwmechanics/pickpocket.cpp | 67 ++++++++++++++++++++++ apps/openmw/mwmechanics/pickpocket.hpp | 30 ++++++++++ files/mygui/openmw_container_window.layout | 1 + 6 files changed, 160 insertions(+), 3 deletions(-) create mode 100644 apps/openmw/mwmechanics/pickpocket.cpp create mode 100644 apps/openmw/mwmechanics/pickpocket.hpp diff --git a/apps/openmw/CMakeLists.txt b/apps/openmw/CMakeLists.txt index 8c8a1b324..77d442eef 100644 --- a/apps/openmw/CMakeLists.txt +++ b/apps/openmw/CMakeLists.txt @@ -74,7 +74,7 @@ add_openmw_dir (mwmechanics mechanicsmanagerimp stat character creaturestats magiceffects movement actors objects drawstate spells activespells npcstats aipackage aisequence alchemy aiwander aitravel aifollow aiescort aiactivate aicombat repair enchanting pathfinding security spellsuccess spellcasting - disease + disease pickpocket ) add_openmw_dir (mwbase diff --git a/apps/openmw/mwgui/container.cpp b/apps/openmw/mwgui/container.cpp index b7c6e3367..31cfd8bc9 100644 --- a/apps/openmw/mwgui/container.cpp +++ b/apps/openmw/mwgui/container.cpp @@ -6,10 +6,13 @@ #include "../mwbase/world.hpp" #include "../mwbase/soundmanager.hpp" #include "../mwbase/windowmanager.hpp" +#include "../mwbase/dialoguemanager.hpp" #include "../mwworld/class.hpp" #include "../mwworld/player.hpp" +#include "../mwmechanics/pickpocket.hpp" + #include "countdialog.hpp" #include "tradewindow.hpp" #include "inventorywindow.hpp" @@ -123,6 +126,7 @@ namespace MWGui , mSelectedItem(-1) , mModel(NULL) , mSortModel(NULL) + , mPickpocketDetected(false) { getWidget(mDisposeCorpseButton, "DisposeCorpseButton"); getWidget(mTakeButton, "TakeButton"); @@ -171,6 +175,9 @@ namespace MWGui void ContainerWindow::dragItem(MyGUI::Widget* sender, int count) { + if (!onTakeItem(mModel->getItem(mSelectedItem), count)) + return; + mDragAndDrop->startDrag(mSelectedItem, mSortModel, mModel, mItemView, count); } @@ -208,6 +215,7 @@ namespace MWGui void ContainerWindow::open(const MWWorld::Ptr& container, bool loot) { + mPickpocketDetected = false; mPtr = container; if (mPtr.getTypeName() == typeid(ESM::NPC).name() && !loot) @@ -230,6 +238,28 @@ namespace MWGui setTitle(MWWorld::Class::get(container).getName(container)); } + void ContainerWindow::close() + { + WindowBase::close(); + + // 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) + && !mPickpocketDetected // If it was already detected while taking an item, no need to check now + ) + { + MWMechanics::Pickpocket pickpocket(MWBase::Environment::get().getWorld()->getPlayer().getPlayer(), + mPtr); + if (pickpocket.finish()) + { + // TODO: crime + MWBase::Environment::get().getWindowManager()->removeGuiMode(MWGui::GM_Container); + MWBase::Environment::get().getDialogueManager()->say(mPtr, "Thief"); + mPickpocketDetected = true; + return; + } + } + } + void ContainerWindow::onCloseButtonClicked(MyGUI::Widget* _sender) { if(mDragAndDrop == NULL || !mDragAndDrop->mIsOnDragAndDrop) @@ -255,8 +285,13 @@ namespace MWGui MWBase::Environment::get().getSoundManager()->playSound (sound, 1.0, 1.0); } - playerModel->copyItem(mModel->getItem(i), mModel->getItem(i).mCount); - mModel->removeItem(mModel->getItem(i), mModel->getItem(i).mCount); + const ItemStack& item = mModel->getItem(i); + + if (!onTakeItem(item, item.mCount)) + break; + + playerModel->copyItem(item, item.mCount); + mModel->removeItem(item, item.mCount); } MWBase::Environment::get().getWindowManager()->removeGuiMode(GM_Container); @@ -283,4 +318,22 @@ namespace MWGui MWBase::Environment::get().getWindowManager()->removeGuiMode(GM_Container); } + bool ContainerWindow::onTakeItem(const ItemStack &item, int count) + { + if (dynamic_cast(mModel)) + { + MWMechanics::Pickpocket pickpocket(MWBase::Environment::get().getWorld()->getPlayer().getPlayer(), + mPtr); + if (pickpocket.pick(item.mBase, count)) + { + // TODO: crime + MWBase::Environment::get().getWindowManager()->removeGuiMode(MWGui::GM_Container); + MWBase::Environment::get().getDialogueManager()->say(mPtr, "Thief"); + mPickpocketDetected = true; + return false; + } + } + return true; + } + } diff --git a/apps/openmw/mwgui/container.hpp b/apps/openmw/mwgui/container.hpp index 243f77aa5..f934d8828 100644 --- a/apps/openmw/mwgui/container.hpp +++ b/apps/openmw/mwgui/container.hpp @@ -52,10 +52,13 @@ namespace MWGui ContainerWindow(DragAndDrop* dragAndDrop); void open(const MWWorld::Ptr& container, bool loot=false); + virtual void close(); private: DragAndDrop* mDragAndDrop; + bool mPickpocketDetected; + MWGui::ItemView* mItemView; SortFilterItemModel* mSortModel; ItemModel* mModel; @@ -73,6 +76,9 @@ namespace MWGui void onTakeAllButtonClicked(MyGUI::Widget* _sender); void onDisposeCorpseButtonClicked(MyGUI::Widget* sender); + /// @return is taking the item allowed? + bool onTakeItem(const ItemStack& item, int count); + virtual void onReferenceUnavailable(); }; } diff --git a/apps/openmw/mwmechanics/pickpocket.cpp b/apps/openmw/mwmechanics/pickpocket.cpp new file mode 100644 index 000000000..8e8a70d88 --- /dev/null +++ b/apps/openmw/mwmechanics/pickpocket.cpp @@ -0,0 +1,67 @@ +#include "pickpocket.hpp" + +#include "../mwworld/class.hpp" +#include "../mwbase/world.hpp" +#include "../mwbase/environment.hpp" +#include "npcstats.hpp" + +namespace MWMechanics +{ + + Pickpocket::Pickpocket(const MWWorld::Ptr &thief, const MWWorld::Ptr &victim) + : mThief(thief) + , mVictim(victim) + { + } + + float Pickpocket::getChanceModifier(const MWWorld::Ptr &ptr, float add) + { + NpcStats& stats = ptr.getClass().getNpcStats(ptr); + float agility = stats.getAttribute(ESM::Attribute::Agility).getModified(); + float luck = stats.getAttribute(ESM::Attribute::Luck).getModified(); + float sneak = stats.getSkill(ESM::Skill::Sneak).getModified(); + return (add + 0.2 * agility + 0.1 * luck + sneak) * stats.getFatigueTerm(); + } + + bool Pickpocket::getDetected(float valueTerm) + { + float x = getChanceModifier(mThief); + float y = getChanceModifier(mVictim, valueTerm); + + float t = 2*x - y; + + NpcStats& pcStats = mThief.getClass().getNpcStats(mThief); + float pcSneak = pcStats.getSkill(ESM::Skill::Sneak).getModified(); + int iPickMinChance = MWBase::Environment::get().getWorld()->getStore().get() + .find("iPickMinChance")->getInt(); + int iPickMaxChance = MWBase::Environment::get().getWorld()->getStore().get() + .find("iPickMaxChance")->getInt(); + + int roll = std::rand()/ (static_cast (RAND_MAX) + 1) * 100; // [0, 99] + if (t < pcSneak / iPickMinChance) + { + return (roll > int(pcSneak / iPickMinChance)); + } + else + { + t = std::min(float(iPickMaxChance), t); + return (roll > int(t)); + } + } + + bool Pickpocket::pick(MWWorld::Ptr item, int count) + { + float stackValue = item.getClass().getValue(item) * count; + float fPickPocketMod = MWBase::Environment::get().getWorld()->getStore().get() + .find("fPickPocketMod")->getFloat(); + float valueTerm = 10 * fPickPocketMod * stackValue; + + return getDetected(valueTerm); + } + + bool Pickpocket::finish() + { + return getDetected(0.f); + } + +} diff --git a/apps/openmw/mwmechanics/pickpocket.hpp b/apps/openmw/mwmechanics/pickpocket.hpp new file mode 100644 index 000000000..4de1e37f8 --- /dev/null +++ b/apps/openmw/mwmechanics/pickpocket.hpp @@ -0,0 +1,30 @@ +#ifndef OPENMW_MECHANICS_PICKPOCKET_H +#define OPENMW_MECHANICS_PICKPOCKET_H + +#include "../mwworld/ptr.hpp" + +namespace MWMechanics +{ + + class Pickpocket + { + public: + Pickpocket (const MWWorld::Ptr& thief, const MWWorld::Ptr& victim); + + /// Steal some items + /// @return Was the thief detected? + bool pick (MWWorld::Ptr item, int count); + /// End the pickpocketing process + /// @return Was the thief detected? + bool finish (); + + private: + bool getDetected(float valueTerm); + float getChanceModifier(const MWWorld::Ptr& ptr, float add=0); + MWWorld::Ptr mThief; + MWWorld::Ptr mVictim; + }; + +} + +#endif diff --git a/files/mygui/openmw_container_window.layout b/files/mygui/openmw_container_window.layout index 06cc04ebe..87651b0f2 100644 --- a/files/mygui/openmw_container_window.layout +++ b/files/mygui/openmw_container_window.layout @@ -3,6 +3,7 @@ +