forked from teamnwah/openmw-tes3coop
Implement pickpocket detection. Play a voiced dialogue entry when detected.
This commit is contained in:
parent
ea3ee4407f
commit
780bf5a2cd
6 changed files with 160 additions and 3 deletions
|
@ -74,7 +74,7 @@ add_openmw_dir (mwmechanics
|
||||||
mechanicsmanagerimp stat character creaturestats magiceffects movement actors objects
|
mechanicsmanagerimp stat character creaturestats magiceffects movement actors objects
|
||||||
drawstate spells activespells npcstats aipackage aisequence alchemy aiwander aitravel aifollow
|
drawstate spells activespells npcstats aipackage aisequence alchemy aiwander aitravel aifollow
|
||||||
aiescort aiactivate aicombat repair enchanting pathfinding security spellsuccess spellcasting
|
aiescort aiactivate aicombat repair enchanting pathfinding security spellsuccess spellcasting
|
||||||
disease
|
disease pickpocket
|
||||||
)
|
)
|
||||||
|
|
||||||
add_openmw_dir (mwbase
|
add_openmw_dir (mwbase
|
||||||
|
|
|
@ -6,10 +6,13 @@
|
||||||
#include "../mwbase/world.hpp"
|
#include "../mwbase/world.hpp"
|
||||||
#include "../mwbase/soundmanager.hpp"
|
#include "../mwbase/soundmanager.hpp"
|
||||||
#include "../mwbase/windowmanager.hpp"
|
#include "../mwbase/windowmanager.hpp"
|
||||||
|
#include "../mwbase/dialoguemanager.hpp"
|
||||||
|
|
||||||
#include "../mwworld/class.hpp"
|
#include "../mwworld/class.hpp"
|
||||||
#include "../mwworld/player.hpp"
|
#include "../mwworld/player.hpp"
|
||||||
|
|
||||||
|
#include "../mwmechanics/pickpocket.hpp"
|
||||||
|
|
||||||
#include "countdialog.hpp"
|
#include "countdialog.hpp"
|
||||||
#include "tradewindow.hpp"
|
#include "tradewindow.hpp"
|
||||||
#include "inventorywindow.hpp"
|
#include "inventorywindow.hpp"
|
||||||
|
@ -123,6 +126,7 @@ namespace MWGui
|
||||||
, mSelectedItem(-1)
|
, mSelectedItem(-1)
|
||||||
, mModel(NULL)
|
, mModel(NULL)
|
||||||
, mSortModel(NULL)
|
, mSortModel(NULL)
|
||||||
|
, mPickpocketDetected(false)
|
||||||
{
|
{
|
||||||
getWidget(mDisposeCorpseButton, "DisposeCorpseButton");
|
getWidget(mDisposeCorpseButton, "DisposeCorpseButton");
|
||||||
getWidget(mTakeButton, "TakeButton");
|
getWidget(mTakeButton, "TakeButton");
|
||||||
|
@ -171,6 +175,9 @@ namespace MWGui
|
||||||
|
|
||||||
void ContainerWindow::dragItem(MyGUI::Widget* sender, int count)
|
void ContainerWindow::dragItem(MyGUI::Widget* sender, int count)
|
||||||
{
|
{
|
||||||
|
if (!onTakeItem(mModel->getItem(mSelectedItem), count))
|
||||||
|
return;
|
||||||
|
|
||||||
mDragAndDrop->startDrag(mSelectedItem, mSortModel, mModel, mItemView, count);
|
mDragAndDrop->startDrag(mSelectedItem, mSortModel, mModel, mItemView, count);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -208,6 +215,7 @@ namespace MWGui
|
||||||
|
|
||||||
void ContainerWindow::open(const MWWorld::Ptr& container, bool loot)
|
void ContainerWindow::open(const MWWorld::Ptr& container, bool loot)
|
||||||
{
|
{
|
||||||
|
mPickpocketDetected = false;
|
||||||
mPtr = container;
|
mPtr = container;
|
||||||
|
|
||||||
if (mPtr.getTypeName() == typeid(ESM::NPC).name() && !loot)
|
if (mPtr.getTypeName() == typeid(ESM::NPC).name() && !loot)
|
||||||
|
@ -230,6 +238,28 @@ namespace MWGui
|
||||||
setTitle(MWWorld::Class::get(container).getName(container));
|
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)
|
void ContainerWindow::onCloseButtonClicked(MyGUI::Widget* _sender)
|
||||||
{
|
{
|
||||||
if(mDragAndDrop == NULL || !mDragAndDrop->mIsOnDragAndDrop)
|
if(mDragAndDrop == NULL || !mDragAndDrop->mIsOnDragAndDrop)
|
||||||
|
@ -255,8 +285,13 @@ namespace MWGui
|
||||||
MWBase::Environment::get().getSoundManager()->playSound (sound, 1.0, 1.0);
|
MWBase::Environment::get().getSoundManager()->playSound (sound, 1.0, 1.0);
|
||||||
}
|
}
|
||||||
|
|
||||||
playerModel->copyItem(mModel->getItem(i), mModel->getItem(i).mCount);
|
const ItemStack& item = mModel->getItem(i);
|
||||||
mModel->removeItem(mModel->getItem(i), mModel->getItem(i).mCount);
|
|
||||||
|
if (!onTakeItem(item, item.mCount))
|
||||||
|
break;
|
||||||
|
|
||||||
|
playerModel->copyItem(item, item.mCount);
|
||||||
|
mModel->removeItem(item, item.mCount);
|
||||||
}
|
}
|
||||||
|
|
||||||
MWBase::Environment::get().getWindowManager()->removeGuiMode(GM_Container);
|
MWBase::Environment::get().getWindowManager()->removeGuiMode(GM_Container);
|
||||||
|
@ -283,4 +318,22 @@ namespace MWGui
|
||||||
MWBase::Environment::get().getWindowManager()->removeGuiMode(GM_Container);
|
MWBase::Environment::get().getWindowManager()->removeGuiMode(GM_Container);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool ContainerWindow::onTakeItem(const ItemStack &item, int count)
|
||||||
|
{
|
||||||
|
if (dynamic_cast<PickpocketItemModel*>(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;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -52,10 +52,13 @@ namespace MWGui
|
||||||
ContainerWindow(DragAndDrop* dragAndDrop);
|
ContainerWindow(DragAndDrop* dragAndDrop);
|
||||||
|
|
||||||
void open(const MWWorld::Ptr& container, bool loot=false);
|
void open(const MWWorld::Ptr& container, bool loot=false);
|
||||||
|
virtual void close();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
DragAndDrop* mDragAndDrop;
|
DragAndDrop* mDragAndDrop;
|
||||||
|
|
||||||
|
bool mPickpocketDetected;
|
||||||
|
|
||||||
MWGui::ItemView* mItemView;
|
MWGui::ItemView* mItemView;
|
||||||
SortFilterItemModel* mSortModel;
|
SortFilterItemModel* mSortModel;
|
||||||
ItemModel* mModel;
|
ItemModel* mModel;
|
||||||
|
@ -73,6 +76,9 @@ namespace MWGui
|
||||||
void onTakeAllButtonClicked(MyGUI::Widget* _sender);
|
void onTakeAllButtonClicked(MyGUI::Widget* _sender);
|
||||||
void onDisposeCorpseButtonClicked(MyGUI::Widget* sender);
|
void onDisposeCorpseButtonClicked(MyGUI::Widget* sender);
|
||||||
|
|
||||||
|
/// @return is taking the item allowed?
|
||||||
|
bool onTakeItem(const ItemStack& item, int count);
|
||||||
|
|
||||||
virtual void onReferenceUnavailable();
|
virtual void onReferenceUnavailable();
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
67
apps/openmw/mwmechanics/pickpocket.cpp
Normal file
67
apps/openmw/mwmechanics/pickpocket.cpp
Normal file
|
@ -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<ESM::GameSetting>()
|
||||||
|
.find("iPickMinChance")->getInt();
|
||||||
|
int iPickMaxChance = MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>()
|
||||||
|
.find("iPickMaxChance")->getInt();
|
||||||
|
|
||||||
|
int roll = std::rand()/ (static_cast<double> (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<ESM::GameSetting>()
|
||||||
|
.find("fPickPocketMod")->getFloat();
|
||||||
|
float valueTerm = 10 * fPickPocketMod * stackValue;
|
||||||
|
|
||||||
|
return getDetected(valueTerm);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Pickpocket::finish()
|
||||||
|
{
|
||||||
|
return getDetected(0.f);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
30
apps/openmw/mwmechanics/pickpocket.hpp
Normal file
30
apps/openmw/mwmechanics/pickpocket.hpp
Normal file
|
@ -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
|
|
@ -3,6 +3,7 @@
|
||||||
<MyGUI type="Layout">
|
<MyGUI type="Layout">
|
||||||
<Widget type="Window" skin="MW_Window" layer="Windows" position="0 0 600 300" name="_Main">
|
<Widget type="Window" skin="MW_Window" layer="Windows" position="0 0 600 300" name="_Main">
|
||||||
<Property key="MinSize" value="245 145"/>
|
<Property key="MinSize" value="245 145"/>
|
||||||
|
<Property key="Visible" value="false"/>
|
||||||
|
|
||||||
<!-- Items -->
|
<!-- Items -->
|
||||||
<Widget type="ItemView" skin="MW_ItemView" position="5 5 575 225" name="ItemView" align="Left Top Stretch">
|
<Widget type="ItemView" skin="MW_ItemView" position="5 5 575 225" name="ItemView" align="Left Top Stretch">
|
||||||
|
|
Loading…
Reference in a new issue