From 866fdfe8bdcde3cf07d22872f55a2a617201c0f0 Mon Sep 17 00:00:00 2001 From: scrawl Date: Fri, 19 Dec 2014 21:45:26 +0100 Subject: [PATCH] Crime system improvements - If someone saw the crime, they will notify everyone else in range, even if the Alarm rating of the witness is 0. - Pickpocket and selling stolen items now works properly, i.e. honors the victim's Alarm rating instead of always being reported. --- apps/openmw/mwbase/mechanicsmanager.hpp | 10 +-- apps/openmw/mwgui/container.cpp | 10 +-- apps/openmw/mwgui/enchantingdialog.cpp | 5 +- apps/openmw/mwgui/tradewindow.cpp | 5 +- .../mwmechanics/mechanicsmanagerimp.cpp | 82 +++++++++---------- .../mwmechanics/mechanicsmanagerimp.hpp | 14 ++-- 6 files changed, 57 insertions(+), 69 deletions(-) diff --git a/apps/openmw/mwbase/mechanicsmanager.hpp b/apps/openmw/mwbase/mechanicsmanager.hpp index c92459183d..fe4b5fb63a 100644 --- a/apps/openmw/mwbase/mechanicsmanager.hpp +++ b/apps/openmw/mwbase/mechanicsmanager.hpp @@ -118,16 +118,14 @@ namespace MWBase 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. * @note victim may be empty * @param arg Depends on \a type, e.g. for Theft, the value of the item that was stolen. - * @return was the crime reported? + * @param victimAware Is the victim already aware of the crime? + * If this parameter is false, it will be determined by a line-of-sight and awareness check. + * @return was the crime seen? */ virtual bool 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; + OffenseType type, int arg=0, bool victimAware=false) = 0; /// @return false if the attack was considered a "friendly hit" and forgiven virtual bool actorAttacked (const MWWorld::Ptr& victim, const MWWorld::Ptr& attacker) = 0; /// Utility to check if taking this item is illegal and calling commitCrime if so diff --git a/apps/openmw/mwgui/container.cpp b/apps/openmw/mwgui/container.cpp index 6df8a3f448..ee1d285925 100644 --- a/apps/openmw/mwgui/container.cpp +++ b/apps/openmw/mwgui/container.cpp @@ -301,10 +301,9 @@ namespace MWGui MWMechanics::Pickpocket pickpocket(player, mPtr); if (pickpocket.finish()) { - MWBase::Environment::get().getMechanicsManager()->reportCrime( - player, mPtr, MWBase::MechanicsManager::OT_Pickpocket); + MWBase::Environment::get().getMechanicsManager()->commitCrime( + player, mPtr, MWBase::MechanicsManager::OT_Pickpocket, 0, true); MWBase::Environment::get().getWindowManager()->removeGuiMode(MWGui::GM_Container); - MWBase::Environment::get().getDialogueManager()->say(mPtr, "Thief"); mPickpocketDetected = true; return; } @@ -384,10 +383,9 @@ namespace MWGui if (pickpocket.pick(item.mBase, count)) { 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().getMechanicsManager()->commitCrime( + player, MWWorld::Ptr(), MWBase::MechanicsManager::OT_Theft, value, true); MWBase::Environment::get().getWindowManager()->removeGuiMode(MWGui::GM_Container); - MWBase::Environment::get().getDialogueManager()->say(mPtr, "Thief"); mPickpocketDetected = true; return false; } diff --git a/apps/openmw/mwgui/enchantingdialog.cpp b/apps/openmw/mwgui/enchantingdialog.cpp index 56caa6513b..d4c10d568c 100644 --- a/apps/openmw/mwgui/enchantingdialog.cpp +++ b/apps/openmw/mwgui/enchantingdialog.cpp @@ -338,9 +338,8 @@ namespace MWGui if (msg.find("%s") != std::string::npos) msg.replace(msg.find("%s"), 2, item.getClass().getName(item)); MWBase::Environment::get().getWindowManager()->messageBox(msg); - MWBase::Environment::get().getDialogueManager()->say(mPtr, "Thief"); - MWBase::Environment::get().getMechanicsManager()->reportCrime(player, mPtr, MWBase::MechanicsManager::OT_Theft, - item.getClass().getValue(item)); + MWBase::Environment::get().getMechanicsManager()->commitCrime(player, mPtr, MWBase::MechanicsManager::OT_Theft, + item.getClass().getValue(item), true); MWBase::Environment::get().getWindowManager()->removeGuiMode (GM_Enchanting); MWBase::Environment::get().getDialogueManager()->goodbyeSelected(); return; diff --git a/apps/openmw/mwgui/tradewindow.cpp b/apps/openmw/mwgui/tradewindow.cpp index 0f98598fd8..6499a66d5f 100644 --- a/apps/openmw/mwgui/tradewindow.cpp +++ b/apps/openmw/mwgui/tradewindow.cpp @@ -290,10 +290,9 @@ namespace MWGui if (msg.find("%s") != std::string::npos) msg.replace(msg.find("%s"), 2, it->mBase.getClass().getName(it->mBase)); MWBase::Environment::get().getWindowManager()->messageBox(msg); - MWBase::Environment::get().getDialogueManager()->say(mPtr, "Thief"); - MWBase::Environment::get().getMechanicsManager()->reportCrime(player, mPtr, MWBase::MechanicsManager::OT_Theft, + MWBase::Environment::get().getMechanicsManager()->commitCrime(player, mPtr, MWBase::MechanicsManager::OT_Theft, it->mBase.getClass().getValue(it->mBase) - * it->mCount); + * it->mCount, true); onCancelButtonClicked(mCancelButton); MWBase::Environment::get().getDialogueManager()->goodbyeSelected(); return; diff --git a/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp b/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp index 720f0c4f5c..88a0b2640e 100644 --- a/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp +++ b/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp @@ -917,24 +917,19 @@ namespace MWMechanics commitCrime(ptr, victim, OT_Theft, item.getClass().getValue(item) * count); } - bool MechanicsManager::commitCrime(const MWWorld::Ptr &player, const MWWorld::Ptr &victim, OffenseType type, int arg) + bool MechanicsManager::commitCrime(const MWWorld::Ptr &player, const MWWorld::Ptr &victim, OffenseType type, int arg, bool victimAware) { - // NOTE: int arg can be from itemTaken() so DON'T modify it, since it is - // passed to reportCrime later on in this function. - // NOTE: victim may be empty // Only player can commit crime if (player.getRefData().getHandle() != "player") return false; - const MWWorld::ESMStore& esmStore = MWBase::Environment::get().getWorld()->getStore(); - - // Find all the actors within the alarm radius std::vector neighbors; Ogre::Vector3 from = Ogre::Vector3(player.getRefData().getPosition().pos); + const MWWorld::ESMStore& esmStore = MWBase::Environment::get().getWorld()->getStore(); float radius = esmStore.get().find("fAlarmRadius")->getFloat(); mActors.getObjectsInRange(from, radius, neighbors); @@ -943,11 +938,8 @@ namespace MWMechanics if (!victim.isEmpty() && from.squaredDistance(Ogre::Vector3(victim.getRefData().getPosition().pos)) > radius*radius) neighbors.push_back(victim); - bool victimAware = false; - - // Find actors who directly witnessed the crime + // Did anyone see it? bool crimeSeen = false; - bool reported = false; for (std::vector::iterator it = neighbors.begin(); it != neighbors.end(); ++it) { if (*it == player) @@ -955,17 +947,13 @@ namespace MWMechanics if (it->getClass().getCreatureStats(*it).isDead()) continue; - // Was the crime seen? - if ((MWBase::Environment::get().getWorld()->getLOS(player, *it) && awarenessCheck(player, *it) ) + if ((*it == victim && victimAware) + || (MWBase::Environment::get().getWorld()->getLOS(player, *it) && awarenessCheck(player, *it) ) // Murder crime can be reported even if no one saw it (hearing is enough, I guess). // TODO: Add mod support for stealth executions! || (type == OT_Murder && *it != victim)) { - if (*it == victim) - victimAware = true; - - // TODO: are there other messages? - if (type == OT_Theft) + if (type == OT_Theft || type == OT_Pickpocket) MWBase::Environment::get().getDialogueManager()->say(*it, "thief"); // Crime reporting only applies to NPCs @@ -977,20 +965,13 @@ namespace MWMechanics crimeSeen = true; } - - // Will the witness report the crime? - if (it->getClass().getCreatureStats(*it).getAiSetting(CreatureStats::AI_Alarm).getBase() >= 100) - { - reported = true; - } } - if (crimeSeen && reported) + if (crimeSeen) reportCrime(player, victim, type, arg); - else if (victimAware && !victim.isEmpty() && type == OT_Assault) - startCombat(victim, player); - - return reported; + else if (type == OT_Assault && !victim.isEmpty()) + startCombat(victim, player); // TODO: combat should be started with an "unaware" flag, which makes the victim flee? + return crimeSeen; } void MechanicsManager::reportCrime(const MWWorld::Ptr &player, const MWWorld::Ptr &victim, OffenseType type, int arg) @@ -1030,22 +1011,6 @@ namespace MWMechanics arg = std::max(1, arg); // Minimum bounty of 1, in case items with zero value are stolen } - MWBase::Environment::get().getWindowManager()->messageBox("#{sCrimeMessage}"); - player.getClass().getNpcStats(player).setBounty(player.getClass().getNpcStats(player).getBounty() - + arg); - - // If committing a crime against a faction member, expell from the faction - if (!victim.isEmpty() && victim.getClass().isNpc()) - { - std::string factionID; - if(!victim.getClass().getNpcStats(victim).getFactionRanks().empty()) - factionID = victim.getClass().getNpcStats(victim).getFactionRanks().begin()->first; - if (player.getClass().getNpcStats(player).isSameFaction(victim.getClass().getNpcStats(victim))) - { - player.getClass().getNpcStats(player).expell(factionID); - } - } - // Make surrounding actors within alarm distance respond to the crime std::vector neighbors; @@ -1082,6 +1047,8 @@ namespace MWMechanics else if (type == OT_Theft) fight = fightVictim = esmStore.get().find("fFightStealing")->getFloat(); + bool reported = false; + // Tell everyone (including the original reporter) in alarm range for (std::vector::iterator it = neighbors.begin(); it != neighbors.end(); ++it) { @@ -1093,6 +1060,12 @@ namespace MWMechanics if (it->getClass().getCreatureStats(*it).getAiSequence().isInCombat(victim)) continue; + // Will the witness report the crime? + if (it->getClass().getCreatureStats(*it).getAiSetting(CreatureStats::AI_Alarm).getBase() >= 100) + { + reported = true; + } + if (it->getClass().isClass(*it, "guard")) { // Mark as Alarmed for dialogue @@ -1132,6 +1105,25 @@ namespace MWMechanics observerStats.setBaseDisposition(originalDisposition); } } + + if (reported) + { + MWBase::Environment::get().getWindowManager()->messageBox("#{sCrimeMessage}"); + player.getClass().getNpcStats(player).setBounty(player.getClass().getNpcStats(player).getBounty() + + arg); + + // If committing a crime against a faction member, expell from the faction + if (!victim.isEmpty() && victim.getClass().isNpc()) + { + std::string factionID; + if(!victim.getClass().getNpcStats(victim).getFactionRanks().empty()) + factionID = victim.getClass().getNpcStats(victim).getFactionRanks().begin()->first; + if (player.getClass().getNpcStats(player).isSameFaction(victim.getClass().getNpcStats(victim))) + { + player.getClass().getNpcStats(player).expell(factionID); + } + } + } } bool MechanicsManager::actorAttacked(const MWWorld::Ptr &ptr, const MWWorld::Ptr &attacker) diff --git a/apps/openmw/mwmechanics/mechanicsmanagerimp.hpp b/apps/openmw/mwmechanics/mechanicsmanagerimp.hpp index 489da75417..74b4247faf 100644 --- a/apps/openmw/mwmechanics/mechanicsmanagerimp.hpp +++ b/apps/openmw/mwmechanics/mechanicsmanagerimp.hpp @@ -110,16 +110,14 @@ namespace MWMechanics virtual void startCombat (const MWWorld::Ptr& ptr, const MWWorld::Ptr& target); /** - * @brief Commit a crime. If any actors witness the crime and report it, - * reportCrime will be called automatically. * @note victim may be empty * @param arg Depends on \a type, e.g. for Theft, the value of the item that was stolen. - * @return was the crime reported? + * @param victimAware Is the victim already aware of the crime? + * If this parameter is false, it will be determined by a line-of-sight and awareness check. + * @return was the crime seen? */ virtual bool 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); + OffenseType type, int arg=0, bool victimAware=false); /// @return false if the attack was considered a "friendly hit" and forgiven virtual bool actorAttacked (const MWWorld::Ptr& victim, const MWWorld::Ptr& attacker); /// Utility to check if taking this item is illegal and calling commitCrime if so @@ -171,6 +169,10 @@ namespace MWMechanics virtual void keepPlayerAlive(); virtual bool isReadyToBlock (const MWWorld::Ptr& ptr) const; + + private: + void reportCrime (const MWWorld::Ptr& ptr, const MWWorld::Ptr& victim, + OffenseType type, int arg=0); }; }