diff --git a/AUTHORS.md b/AUTHORS.md index 7252e39304..282b944817 100644 --- a/AUTHORS.md +++ b/AUTHORS.md @@ -173,6 +173,7 @@ Programmers vocollapse Yohaulticetl zelurker + James Carty (MrTopCat) Documentation ------------- diff --git a/apps/openmw/mwmechanics/aisequence.cpp b/apps/openmw/mwmechanics/aisequence.cpp index 753dc240ed..b64b3568f8 100644 --- a/apps/openmw/mwmechanics/aisequence.cpp +++ b/apps/openmw/mwmechanics/aisequence.cpp @@ -19,6 +19,7 @@ #include "aicombataction.hpp" #include "aipursue.hpp" #include "actorutil.hpp" +#include "../mwworld/class.hpp" namespace MWMechanics { @@ -122,6 +123,20 @@ bool AiSequence::isInCombat() const return false; } +bool AiSequence::isEngagedWithActor() const +{ + for (std::list::const_iterator it = mPackages.begin(); it != mPackages.end(); ++it) + { + if ((*it)->getTypeId() == AiPackage::TypeIdCombat) + { + MWWorld::Ptr target2 = (*it)->getTarget(); + if (!target2.isEmpty() && target2.getClass().isNpc()) + return true; + } + } + return false; +} + bool AiSequence::hasPackage(int typeId) const { for (std::list::const_iterator it = mPackages.begin(); it != mPackages.end(); ++it) diff --git a/apps/openmw/mwmechanics/aisequence.hpp b/apps/openmw/mwmechanics/aisequence.hpp index 5c72bcc4ca..4d0482a985 100644 --- a/apps/openmw/mwmechanics/aisequence.hpp +++ b/apps/openmw/mwmechanics/aisequence.hpp @@ -88,6 +88,9 @@ namespace MWMechanics /// Is there any combat package? bool isInCombat () const; + /// Are we in combat with any other actor, who's also engaging us? + bool isEngagedWithActor () const; + /// Does this AI sequence have the given package type? bool hasPackage(int typeId) const; diff --git a/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp b/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp index e9915397a1..83bdb2258d 100644 --- a/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp +++ b/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp @@ -1462,24 +1462,11 @@ namespace MWMechanics } } - // Attacking an NPC that is already in combat with any other NPC is not a crime - AiSequence& seq = statsTarget.getAiSequence(); - bool isFightingNpc = false; - for (std::list::const_iterator it = seq.begin(); it != seq.end(); ++it) - { - if ((*it)->getTypeId() == AiPackage::TypeIdCombat) - { - MWWorld::Ptr target2 = (*it)->getTarget(); - if (!target2.isEmpty() && target2.getClass().isNpc()) - isFightingNpc = true; - } - } - - if (target.getClass().isNpc() && !attacker.isEmpty() && !seq.isInCombat(attacker) - && !isAggressive(target, attacker) && !isFightingNpc - && !target.getClass().getCreatureStats(target).getAiSequence().hasPackage(AiPackage::TypeIdPursue)) + if (canCommitCrimeAgainst(target, attacker)) commitCrime(attacker, target, MWBase::MechanicsManager::OT_Assault); + AiSequence& seq = statsTarget.getAiSequence(); + if (!attacker.isEmpty() && (attacker.getClass().getCreatureStats(attacker).getAiSequence().isInCombat(target) || attacker == getPlayer()) && !seq.isInCombat(attacker)) @@ -1506,6 +1493,14 @@ namespace MWMechanics return true; } + bool MechanicsManager::canCommitCrimeAgainst(const MWWorld::Ptr &target, const MWWorld::Ptr &attacker) + { + MWMechanics::AiSequence seq = target.getClass().getCreatureStats(target).getAiSequence(); + return target.getClass().isNpc() && !attacker.isEmpty() && !seq.isInCombat(attacker) + && !isAggressive(target, attacker) && !seq.isEngagedWithActor() + && !target.getClass().getCreatureStats(target).getAiSequence().hasPackage(AiPackage::TypeIdPursue); + } + void MechanicsManager::actorKilled(const MWWorld::Ptr &victim, const MWWorld::Ptr &attacker) { if (attacker.isEmpty() || victim.isEmpty()) @@ -1518,11 +1513,10 @@ namespace MWMechanics return; // TODO: implement animal rights const MWMechanics::NpcStats& victimStats = victim.getClass().getNpcStats(victim); - if (victimStats.getCrimeId() == -1) - return; + const MWWorld::Ptr &player = getPlayer(); + bool canCommit = attacker == player && canCommitCrimeAgainst(victim, attacker); // For now we report only about crimes of player and player's followers - const MWWorld::Ptr &player = getPlayer(); if (attacker != player) { std::set playerFollowers; @@ -1531,6 +1525,9 @@ namespace MWMechanics return; } + if (!canCommit && victimStats.getCrimeId() == -1) + return; + // Simple check for who attacked first: if the player attacked first, a crimeId should be set // Doesn't handle possible edge case where no one reported the assault, but in such a case, // for bystanders it is not possible to tell who attacked first, anyway. diff --git a/apps/openmw/mwmechanics/mechanicsmanagerimp.hpp b/apps/openmw/mwmechanics/mechanicsmanagerimp.hpp index 676a75cafb..8c8a9e68f9 100644 --- a/apps/openmw/mwmechanics/mechanicsmanagerimp.hpp +++ b/apps/openmw/mwmechanics/mechanicsmanagerimp.hpp @@ -132,6 +132,12 @@ namespace MWMechanics /// @note No-op for non-player attackers virtual void actorKilled (const MWWorld::Ptr& victim, const MWWorld::Ptr& attacker); + /// Checks if commiting a crime is currently valid + /// @param victim The actor being attacked + /// @param attacker The actor commiting the crime + /// @return true if the victim is a valid target for crime + virtual bool canCommitCrimeAgainst(const MWWorld::Ptr& victim, const MWWorld::Ptr& attacker); + /// Utility to check if taking this item is illegal and calling commitCrime if so /// @param container The container the item is in; may be empty for an item in the world virtual void itemTaken (const MWWorld::Ptr& ptr, const MWWorld::Ptr& item, const MWWorld::Ptr& container,