mirror of
				https://github.com/OpenMW/openmw.git
				synced 2025-10-28 17:56:37 +00:00 
			
		
		
		
	Implement theft detection
This commit is contained in:
		
							parent
							
								
									d2d76f4f47
								
							
						
					
					
						commit
						3c0080d2c1
					
				
					 7 changed files with 129 additions and 10 deletions
				
			
		|  | @ -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, | ||||
|  |  | |||
|  | @ -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<PickpocketItemModel*>(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; | ||||
|     } | ||||
| 
 | ||||
|  |  | |||
|  | @ -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 () | ||||
|  |  | |||
|  | @ -25,9 +25,6 @@ namespace MWMechanics | |||
| { | ||||
|     class Actors | ||||
|     { | ||||
|             typedef std::map<MWWorld::Ptr,CharacterController*> PtrControllerMap; | ||||
|             PtrControllerMap mActors; | ||||
| 
 | ||||
|             std::map<std::string, int> mDeathCount; | ||||
| 
 | ||||
|             void updateNpc(const MWWorld::Ptr &ptr, float duration, bool paused); | ||||
|  | @ -50,6 +47,11 @@ namespace MWMechanics | |||
|             Actors(); | ||||
|             ~Actors(); | ||||
| 
 | ||||
|             typedef std::map<MWWorld::Ptr,CharacterController*> 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; | ||||
| 
 | ||||
|     }; | ||||
| } | ||||
| 
 | ||||
|  |  | |||
|  | @ -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<std::string, int>& 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<ESM::GameSetting>& store = MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>(); | ||||
|  |  | |||
|  | @ -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); | ||||
|  |  | |||
|  | @ -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()); | ||||
|     } | ||||
| } | ||||
|  |  | |||
		Loading…
	
		Reference in a new issue