From 3c0080d2c17bcfd8a02e372ecb300ab942c53139 Mon Sep 17 00:00:00 2001 From: scrawl Date: Tue, 7 Jan 2014 19:49:16 +0100 Subject: [PATCH] Implement theft detection --- apps/openmw/mwbase/mechanicsmanager.hpp | 21 ++++++ apps/openmw/mwgui/container.cpp | 21 ++++-- apps/openmw/mwgui/inventorywindow.cpp | 3 + apps/openmw/mwmechanics/actors.hpp | 12 +++- .../mwmechanics/mechanicsmanagerimp.cpp | 66 +++++++++++++++++++ .../mwmechanics/mechanicsmanagerimp.hpp | 12 ++++ apps/openmw/mwworld/actiontake.cpp | 4 +- 7 files changed, 129 insertions(+), 10 deletions(-) diff --git a/apps/openmw/mwbase/mechanicsmanager.hpp b/apps/openmw/mwbase/mechanicsmanager.hpp index 90f0caee61..a0abb8e485 100644 --- a/apps/openmw/mwbase/mechanicsmanager.hpp +++ b/apps/openmw/mwbase/mechanicsmanager.hpp @@ -91,6 +91,27 @@ namespace MWBase /// Check if \a observer is potentially aware of \a ptr. Does not do a line of sight check! virtual bool awarenessCheck (const MWWorld::Ptr& ptr, const MWWorld::Ptr& observer) = 0; + enum OffenseType + { + OT_Theft, // Taking items owned by an NPC or a faction you are not a member of + OT_Assault, // Attacking a peaceful NPC + OT_Murder, // Murdering a peaceful NPC + OT_Trespassing, // Staying in a cell you are not allowed in (where is this defined?) + OT_SleepingInOwnedBed, // Sleeping in a bed owned by an NPC or a faction you are not a member of + OT_Pickpocket // Entering pickpocket mode, leaving it, and being detected. Any items stolen are a separate crime (Theft) + }; + /** + * @brief Commit a crime. If any actors witness the crime and report it, + * reportCrime will be called automatically. + * @param arg Depends on \a type, e.g. for Theft, the value of the item that was stolen. + */ + virtual void commitCrime (const MWWorld::Ptr& ptr, const MWWorld::Ptr& victim, + OffenseType type, int arg=0) = 0; + virtual void reportCrime (const MWWorld::Ptr& ptr, const MWWorld::Ptr& victim, + OffenseType type, int arg=0) = 0; + /// Utility to check if taking this item is illegal and calling commitCrime if so + virtual void itemTaken (const MWWorld::Ptr& ptr, const MWWorld::Ptr& item, int count) = 0; + enum PersuasionType { PT_Admire, diff --git a/apps/openmw/mwgui/container.cpp b/apps/openmw/mwgui/container.cpp index b4de0aa625..aa80388f34 100644 --- a/apps/openmw/mwgui/container.cpp +++ b/apps/openmw/mwgui/container.cpp @@ -7,9 +7,11 @@ #include "../mwbase/soundmanager.hpp" #include "../mwbase/windowmanager.hpp" #include "../mwbase/dialoguemanager.hpp" +#include "../mwbase/mechanicsmanager.hpp" #include "../mwworld/class.hpp" #include "../mwworld/player.hpp" +#include "../mwworld/containerstore.hpp" #include "../mwmechanics/pickpocket.hpp" @@ -249,11 +251,12 @@ namespace MWGui && !mPickpocketDetected ) { - MWMechanics::Pickpocket pickpocket(MWBase::Environment::get().getWorld()->getPlayer().getPlayer(), - mPtr); + MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayer().getPlayer(); + MWMechanics::Pickpocket pickpocket(player, mPtr); if (pickpocket.finish()) { - // TODO: crime + MWBase::Environment::get().getMechanicsManager()->reportCrime( + player, MWWorld::Ptr(), MWBase::MechanicsManager::OT_Pickpocket); MWBase::Environment::get().getWindowManager()->removeGuiMode(MWGui::GM_Container); MWBase::Environment::get().getDialogueManager()->say(mPtr, "Thief"); mPickpocketDetected = true; @@ -322,19 +325,25 @@ namespace MWGui bool ContainerWindow::onTakeItem(const ItemStack &item, int count) { + MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayer().getPlayer(); if (dynamic_cast(mModel)) { - MWMechanics::Pickpocket pickpocket(MWBase::Environment::get().getWorld()->getPlayer().getPlayer(), - mPtr); + MWMechanics::Pickpocket pickpocket(player, mPtr); if (pickpocket.pick(item.mBase, count)) { - // TODO: crime + int value = item.mBase.getClass().getValue(item.mBase) * count; + MWBase::Environment::get().getMechanicsManager()->reportCrime( + player, MWWorld::Ptr(), MWBase::MechanicsManager::OT_Theft, value); MWBase::Environment::get().getWindowManager()->removeGuiMode(MWGui::GM_Container); MWBase::Environment::get().getDialogueManager()->say(mPtr, "Thief"); mPickpocketDetected = true; return false; } } + else + { + MWBase::Environment::get().getMechanicsManager()->itemTaken(player, item.mBase, count); + } return true; } diff --git a/apps/openmw/mwgui/inventorywindow.cpp b/apps/openmw/mwgui/inventorywindow.cpp index 21da53c6df..33814455a9 100644 --- a/apps/openmw/mwgui/inventorywindow.cpp +++ b/apps/openmw/mwgui/inventorywindow.cpp @@ -8,6 +8,7 @@ #include "../mwbase/environment.hpp" #include "../mwbase/soundmanager.hpp" #include "../mwbase/windowmanager.hpp" +#include "../mwbase/mechanicsmanager.hpp" #include "../mwworld/player.hpp" #include "../mwworld/inventorystore.hpp" @@ -533,6 +534,8 @@ namespace MWGui if (i == mTradeModel->getItemCount()) throw std::runtime_error("Added item not found"); mDragAndDrop->startDrag(i, mSortModel, mTradeModel, mItemView, count); + + MWBase::Environment::get().getMechanicsManager()->itemTaken(player, newObject, count); } MyGUI::IntCoord InventoryWindow::getAvatarScreenCoord () diff --git a/apps/openmw/mwmechanics/actors.hpp b/apps/openmw/mwmechanics/actors.hpp index 83aff63e3c..7046543e6a 100644 --- a/apps/openmw/mwmechanics/actors.hpp +++ b/apps/openmw/mwmechanics/actors.hpp @@ -25,9 +25,6 @@ namespace MWMechanics { class Actors { - typedef std::map PtrControllerMap; - PtrControllerMap mActors; - std::map mDeathCount; void updateNpc(const MWWorld::Ptr &ptr, float duration, bool paused); @@ -50,6 +47,11 @@ namespace MWMechanics Actors(); ~Actors(); + typedef std::map PtrControllerMap; + + PtrControllerMap::const_iterator begin() { return mActors.begin(); } + PtrControllerMap::const_iterator end() { return mActors.end(); } + /// Update magic effects for an actor. Usually done automatically once per frame, but if we're currently /// paused we may want to do it manually (after equipping permanent enchantment) void updateMagicEffects (const MWWorld::Ptr& ptr) { adjustMagicEffects(ptr); } @@ -88,6 +90,10 @@ namespace MWMechanics void playAnimationGroup(const MWWorld::Ptr& ptr, const std::string& groupName, int mode, int number); void skipAnimation(const MWWorld::Ptr& ptr); bool checkAnimationPlaying(const MWWorld::Ptr& ptr, const std::string& groupName); + + private: + PtrControllerMap mActors; + }; } diff --git a/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp b/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp index 884294df3d..94ea660675 100644 --- a/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp +++ b/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp @@ -728,6 +728,72 @@ namespace MWMechanics return mAI; } + void MechanicsManager::itemTaken(const MWWorld::Ptr &ptr, const MWWorld::Ptr &item, int count) + { + const std::string& owner = item.getCellRef().mOwner; + if (owner.empty()) + return; + const std::string& faction = item.getCellRef().mFaction; + if (faction.empty()) + return; + const std::map& factions = ptr.getClass().getNpcStats(ptr).getFactionRanks(); + if (factions.find(Misc::StringUtils::lowerCase(faction)) != factions.end()) + return; + + MWWorld::Ptr victim = MWBase::Environment::get().getWorld()->getPtr(owner, true); + + commitCrime(ptr, victim, OT_Theft, item.getClass().getValue(item) * count); + } + + void MechanicsManager::commitCrime(const MWWorld::Ptr &ptr, const MWWorld::Ptr &victim, OffenseType type, int arg) + { + bool reported=false; + for (Actors::PtrControllerMap::const_iterator it = mActors.begin(); it != mActors.end(); ++it) + { + if (it->first != ptr && awarenessCheck(ptr, it->first)) + { + // NPCs will always curse you when they notice you steal their items, even if they don't report the crime + if (it->first == victim && type == OT_Theft) + { + MWBase::Environment::get().getDialogueManager()->say(victim, "Thief"); + } + + // Actor has witnessed a crime. Will he report it? + // (not sure, is > 0 correct?) + if (it->first.getClass().getCreatureStats(it->first).getAiSetting(CreatureStats::AI_Alarm).getModified() > 0 + // This is a bit inconsistent, but AFAIK assaulted NPCs can not report if they are alone + && (type != OT_Assault || it->first != victim) + ) + { + reported=true; + break; + } + } + } + + if (reported) + reportCrime(ptr, victim, type, arg); + } + + void MechanicsManager::reportCrime(const MWWorld::Ptr &ptr, const MWWorld::Ptr &victim, OffenseType type, int arg) + { + // Bounty for each type of crime + if (type == OT_Trespassing || type == OT_SleepingInOwnedBed) + arg = 5; + else if (type == OT_Pickpocket) + arg = 25; + else if (type == OT_Assault) + arg = 40; + else if (type == OT_Murder) + arg = 1000; + + MWBase::Environment::get().getWindowManager()->messageBox("#{sCrimeMessage}"); + ptr.getClass().getNpcStats(ptr).setBounty(ptr.getClass().getNpcStats(ptr).getBounty() + + arg); + + // TODO: make any guards in the area try to arrest the player + } + bool MechanicsManager::awarenessCheck(const MWWorld::Ptr &ptr, const MWWorld::Ptr &observer) { const MWWorld::Store& store = MWBase::Environment::get().getWorld()->getStore().get(); diff --git a/apps/openmw/mwmechanics/mechanicsmanagerimp.hpp b/apps/openmw/mwmechanics/mechanicsmanagerimp.hpp index 63111f1ccb..198a62d841 100644 --- a/apps/openmw/mwmechanics/mechanicsmanagerimp.hpp +++ b/apps/openmw/mwmechanics/mechanicsmanagerimp.hpp @@ -101,6 +101,18 @@ namespace MWMechanics /// Check if \a observer is potentially aware of \a ptr. Does not do a line of sight check! virtual bool awarenessCheck (const MWWorld::Ptr& ptr, const MWWorld::Ptr& observer); + /** + * @brief Commit a crime. If any actors witness the crime and report it, + * reportCrime will be called automatically. + * @param arg Depends on \a type, e.g. for Theft, the value of the item that was stolen. + */ + virtual void commitCrime (const MWWorld::Ptr& ptr, const MWWorld::Ptr& victim, + OffenseType type, int arg=0); + virtual void reportCrime (const MWWorld::Ptr& ptr, const MWWorld::Ptr& victim, + OffenseType type, int arg=0); + /// Utility to check if taking this item is illegal and calling commitCrime if so + virtual void itemTaken (const MWWorld::Ptr& ptr, const MWWorld::Ptr& item, int count); + virtual void forceStateUpdate(const MWWorld::Ptr &ptr); virtual void playAnimationGroup(const MWWorld::Ptr& ptr, const std::string& groupName, int mode, int number); diff --git a/apps/openmw/mwworld/actiontake.cpp b/apps/openmw/mwworld/actiontake.cpp index 867a046cf2..548a949819 100644 --- a/apps/openmw/mwworld/actiontake.cpp +++ b/apps/openmw/mwworld/actiontake.cpp @@ -4,6 +4,7 @@ #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" #include "../mwbase/windowmanager.hpp" +#include "../mwbase/mechanicsmanager.hpp" #include "class.hpp" #include "containerstore.hpp" @@ -14,8 +15,9 @@ namespace MWWorld void ActionTake::executeImp (const Ptr& actor) { + MWBase::Environment::get().getMechanicsManager()->itemTaken( + actor, getTarget(), getTarget().getRefData().getCount()); actor.getClass().getContainerStore (actor).add (getTarget(), getTarget().getRefData().getCount(), actor); - MWBase::Environment::get().getWorld()->deleteObject (getTarget()); } }