diff --git a/apps/openmw/CMakeLists.txt b/apps/openmw/CMakeLists.txt index 89e40c9b1..7a5283125 100644 --- a/apps/openmw/CMakeLists.txt +++ b/apps/openmw/CMakeLists.txt @@ -48,7 +48,7 @@ add_openmw_dir (mwscript ) add_openmw_dir (mwsound - soundmanagerimp openal_output ffmpeg_decoder + soundmanagerimp openal_output ffmpeg_decoder sound ) add_openmw_dir (mwworld diff --git a/apps/openmw/mwbase/mechanicsmanager.hpp b/apps/openmw/mwbase/mechanicsmanager.hpp index d5f9e30cd..f31241bdb 100644 --- a/apps/openmw/mwbase/mechanicsmanager.hpp +++ b/apps/openmw/mwbase/mechanicsmanager.hpp @@ -96,6 +96,9 @@ 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; + /// Makes \a ptr fight \a target. Also shouts a combat taunt. + virtual void startCombat (const MWWorld::Ptr& ptr, const MWWorld::Ptr& target) = 0; + enum OffenseType { OT_Theft, // Taking items owned by an NPC or a faction you are not a member of diff --git a/apps/openmw/mwclass/creature.cpp b/apps/openmw/mwclass/creature.cpp index 94238e6d5..4c6ab1c75 100644 --- a/apps/openmw/mwclass/creature.cpp +++ b/apps/openmw/mwclass/creature.cpp @@ -337,6 +337,12 @@ namespace MWClass { // NOTE: 'object' and/or 'attacker' may be empty. + getCreatureStats(ptr).setAttacked(true); + + // Self defense + if (!attacker.isEmpty() && ptr.getClass().getCreatureStats(ptr).getAiSetting(MWMechanics::CreatureStats::AI_Fight).getModified() < 80) + MWBase::Environment::get().getMechanicsManager()->startCombat(ptr, attacker); + if(!successful) { // TODO: Handle HitAttemptOnMe script function diff --git a/apps/openmw/mwclass/npc.cpp b/apps/openmw/mwclass/npc.cpp index 5222eac44..6a96c955f 100644 --- a/apps/openmw/mwclass/npc.cpp +++ b/apps/openmw/mwclass/npc.cpp @@ -621,7 +621,7 @@ namespace MWClass // NOTE: 'object' and/or 'attacker' may be empty. // Attacking peaceful NPCs is a crime - if (!attacker.isEmpty() && ptr.getClass().isNpc() && ptr.getClass().getCreatureStats(ptr).getAiSetting(MWMechanics::CreatureStats::AI_Fight).getModified() <= 30) + if (!attacker.isEmpty() && ptr.getClass().getCreatureStats(ptr).getAiSetting(MWMechanics::CreatureStats::AI_Fight).getModified() <= 30) MWBase::Environment::get().getMechanicsManager()->commitCrime(attacker, ptr, MWBase::MechanicsManager::OT_Assault); getCreatureStats(ptr).setAttacked(true); diff --git a/apps/openmw/mwgui/windowmanagerimp.cpp b/apps/openmw/mwgui/windowmanagerimp.cpp index d2a31e8d1..6a15c8e33 100644 --- a/apps/openmw/mwgui/windowmanagerimp.cpp +++ b/apps/openmw/mwgui/windowmanagerimp.cpp @@ -1447,7 +1447,7 @@ namespace MWGui { return !MyGUI::InputManager::getInstance().isModalAny() // TODO: remove this, once we have properly serialized the state of open windows - && (!isGuiMode() || (mGuiModes.size() == 1 && getMode() == GM_MainMenu)); + && (!isGuiMode() || (mGuiModes.size() == 1 && (getMode() == GM_MainMenu || getMode() == GM_Rest || getMode() == GM_RestBed))); } void WindowManager::playVideo(const std::string &name, bool allowSkipping) diff --git a/apps/openmw/mwmechanics/actors.cpp b/apps/openmw/mwmechanics/actors.cpp index 4f291c36e..6375bdd1b 100644 --- a/apps/openmw/mwmechanics/actors.cpp +++ b/apps/openmw/mwmechanics/actors.cpp @@ -206,8 +206,7 @@ namespace MWMechanics if (LOS) { - creatureStats.getAiSequence().stack(AiCombat(MWBase::Environment::get().getWorld()->getPlayerPtr()), ptr); - creatureStats.setHostile(true); + MWBase::Environment::get().getMechanicsManager()->startCombat(ptr, player); } } } @@ -762,16 +761,11 @@ namespace MWMechanics if (ptr.getClass().isClass(ptr, "Guard")) creatureStats.getAiSequence().stack(AiPursue(player.getClass().getId(player)), ptr); else - creatureStats.getAiSequence().stack(AiCombat(player), ptr); - creatureStats.setHostile(true); + { + MWBase::Environment::get().getMechanicsManager()->startCombat(ptr, player); + } } } - // if I didn't report a crime was I attacked? - else if (creatureStats.getAttacked() && !creatureStats.isHostile()) - { - creatureStats.getAiSequence().stack(AiCombat(player), ptr); - creatureStats.setHostile(true); - } } } @@ -1081,6 +1075,8 @@ namespace MWMechanics if(!stats.isDead() && stats.getAiSequence().getTypeId() == AiPackage::TypeIdCombat) { MWMechanics::AiCombat* package = static_cast(stats.getAiSequence().getActivePackage()); + // TODO: This is wrong! It's comparing Ref IDs with Ogre handles. The only case where this (coincidentally) works is the player. + // possibly applies to other code using getTargetId. if(package->getTargetId() == actor.getCellRef().mRefID) list.push_front(*iter); } diff --git a/apps/openmw/mwmechanics/aisequence.hpp b/apps/openmw/mwmechanics/aisequence.hpp index 614eb4154..37e0c7f17 100644 --- a/apps/openmw/mwmechanics/aisequence.hpp +++ b/apps/openmw/mwmechanics/aisequence.hpp @@ -64,6 +64,7 @@ namespace MWMechanics void stack (const AiPackage& package, const MWWorld::Ptr& actor); ///< Add \a package to the front of the sequence (suspends current package) + /// @param actor The actor that owns this AiSequence void queue (const AiPackage& package); ///< Add \a package to the end of the sequence (executed after all other packages have been diff --git a/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp b/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp index 396e710be..5381af8da 100644 --- a/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp +++ b/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp @@ -13,6 +13,8 @@ #include "../mwworld/class.hpp" #include "../mwworld/player.hpp" +#include "../mwmechanics/aicombat.hpp" + #include #include "spellcasting.hpp" @@ -852,27 +854,45 @@ namespace MWMechanics // Innocent until proven guilty bool reported = false; - // Find all the NPCs within the alarm radius + // Find all the actors within the alarm radius std::vector neighbors; mActors.getObjectsInRange(Ogre::Vector3(ptr.getRefData().getPosition().pos), esmStore.get().find("fAlarmRadius")->getInt(), neighbors); - // Find an actor who witnessed the crime + int id = MWBase::Environment::get().getWorld()->getPlayer().getNewCrimeId(); + + // Find actors who witnessed the crime for (std::vector::iterator it = neighbors.begin(); it != neighbors.end(); ++it) { - if ( *it == ptr - || !it->getClass().isNpc()) continue; // not the player and is an NPC + if (*it == ptr) continue; // not the player // Was the crime seen? - if ( ( MWBase::Environment::get().getWorld()->getLOS(ptr, *it) && awarenessCheck(ptr, *it) ) || - type == OT_Assault ) + if (MWBase::Environment::get().getWorld()->getLOS(ptr, *it) && awarenessCheck(ptr, *it) ) { + // TODO: Add more messages + if (type == OT_Theft) + MWBase::Environment::get().getDialogueManager()->say(*it, "thief"); + + if (*it == victim) + { + // Self-defense + // The victim is aware of the criminal/assailant. If being assaulted, fight back now + // (regardless of whether the assault is reported or not) + // This applies to both NPCs and creatures + + // ... except if this is a guard: then the player is given a chance to pay a fine / go to jail instead + if (type == OT_Assault && !ptr.getClass().isClass(ptr, "guard")) + MWBase::Environment::get().getMechanicsManager()->startCombat(victim, ptr); + } + + // Crime reporting only applies to NPCs + if (!it->getClass().isNpc()) + continue; // Will the witness report the crime? if (it->getClass().getCreatureStats(*it).getAiSetting(CreatureStats::AI_Alarm).getBase() >= alarm) { reported = true; - int id = MWBase::Environment::get().getWorld()->getPlayer().getNewCrimeId(); // Tell everyone, including yourself for (std::vector::iterator it1 = neighbors.begin(); it1 != neighbors.end(); ++it1) @@ -880,12 +900,6 @@ namespace MWMechanics if ( *it1 == ptr || !it1->getClass().isNpc()) continue; // not the player and is an NPC - // TODO: Add more messages - if (type == OT_Theft) - MWBase::Environment::get().getDialogueManager()->say(*it1, "thief"); - else if (type == OT_Assault) - MWBase::Environment::get().getDialogueManager()->say(*it1, "attack"); - // Will other witnesses paticipate in crime if ( it1->getClass().getCreatureStats(*it1).getAiSetting(CreatureStats::AI_Alarm).getBase() >= alarm || type == OT_Assault ) @@ -896,7 +910,6 @@ namespace MWMechanics // Mark as Alarmed for dialogue it1->getClass().getCreatureStats(*it1).setAlarmed(true); } - break; // Someone saw the crime and everyone has been told } } } @@ -1007,6 +1020,14 @@ namespace MWMechanics return (roll >= target); } + void MechanicsManager::startCombat(const MWWorld::Ptr &ptr, const MWWorld::Ptr &target) + { + MWBase::Environment::get().getDialogueManager()->say(ptr, "attack"); + ptr.getClass().getCreatureStats(ptr).getAiSequence().stack(MWMechanics::AiCombat(target), ptr); + if (target == MWBase::Environment::get().getWorld()->getPlayerPtr()) + ptr.getClass().getCreatureStats(ptr).setHostile(true); + } + void MechanicsManager::getObjectsInRange(const Ogre::Vector3 &position, float radius, std::vector &objects) { mActors.getObjectsInRange(position, radius, objects); diff --git a/apps/openmw/mwmechanics/mechanicsmanagerimp.hpp b/apps/openmw/mwmechanics/mechanicsmanagerimp.hpp index 1c9bab25e..dcd12ee14 100644 --- a/apps/openmw/mwmechanics/mechanicsmanagerimp.hpp +++ b/apps/openmw/mwmechanics/mechanicsmanagerimp.hpp @@ -105,6 +105,9 @@ 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); + /// Makes \a ptr fight \a target. Also shouts a combat taunt. + 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. diff --git a/apps/openmw/mwscript/aiextensions.cpp b/apps/openmw/mwscript/aiextensions.cpp index 43da111bf..e53b53e58 100644 --- a/apps/openmw/mwscript/aiextensions.cpp +++ b/apps/openmw/mwscript/aiextensions.cpp @@ -16,7 +16,6 @@ #include "../mwmechanics/aifollow.hpp" #include "../mwmechanics/aitravel.hpp" #include "../mwmechanics/aiwander.hpp" -#include "../mwmechanics/aicombat.hpp" #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" @@ -435,12 +434,8 @@ namespace MWScript std::string targetID = runtime.getStringLiteral (runtime[0].mInteger); runtime.pop(); - MWMechanics::CreatureStats& creatureStats = actor.getClass().getCreatureStats(actor); - - - creatureStats.setHostile(true); - creatureStats.getAiSequence().stack( - MWMechanics::AiCombat(MWBase::Environment::get().getWorld()->getPtr(targetID, true) ), actor); + MWWorld::Ptr target = MWBase::Environment::get().getWorld()->getPtr(targetID, true); + MWBase::Environment::get().getMechanicsManager()->startCombat(actor, target); } };