diff --git a/apps/openmw/CMakeLists.txt b/apps/openmw/CMakeLists.txt index a0667848880..1aa311fd2b9 100644 --- a/apps/openmw/CMakeLists.txt +++ b/apps/openmw/CMakeLists.txt @@ -81,7 +81,7 @@ add_openmw_dir (mwclass add_openmw_dir (mwmechanics mechanicsmanagerimp stat creaturestats magiceffects movement actorutil - drawstate spells activespells npcstats aipackage aisequence aipursue alchemy aiwander aitravel aifollow aiavoiddoor + drawstate spells activespells npcstats aipackage aisequence aipursue alchemy aiwander aitravel aifollow aiavoiddoor aibreathe aiescort aiactivate aicombat repair enchanting pathfinding pathgrid security spellsuccess spellcasting disease pickpocket levelledlist combat steering obstacle autocalcspell difficultyscaling aicombataction actor summoning character actors objects aistate coordinateconverter trading aiface diff --git a/apps/openmw/mwmechanics/actors.cpp b/apps/openmw/mwmechanics/actors.cpp index 431ad09b3a5..44a457cb602 100644 --- a/apps/openmw/mwmechanics/actors.cpp +++ b/apps/openmw/mwmechanics/actors.cpp @@ -25,6 +25,8 @@ #include "../mwbase/mechanicsmanager.hpp" #include "../mwbase/statemanager.hpp" +#include "../mwmechanics/aibreathe.hpp" + #include "spellcasting.hpp" #include "npcstats.hpp" #include "creaturestats.hpp" @@ -814,6 +816,15 @@ namespace MWMechanics if (stats.getTimeToStartDrowning() == -1.f) stats.setTimeToStartDrowning(fHoldBreathTime); + if (ptr.getClass().isNpc() && stats.getTimeToStartDrowning() < fHoldBreathTime / 2) + { + if(ptr != MWMechanics::getPlayer() ) { + MWMechanics::AiSequence& seq = ptr.getClass().getCreatureStats(ptr).getAiSequence(); + if(seq.getTypeId() != MWMechanics::AiPackage::TypeIdBreathe) //Only add it once + seq.stack(MWMechanics::AiBreathe(), ptr); + } + } + MWBase::World *world = MWBase::Environment::get().getWorld(); bool knockedOutUnderwater = (ctrl->isKnockedOut() && world->isUnderwater(ptr.getCell(), osg::Vec3f(ptr.getRefData().getPosition().asVec3()))); if((world->isSubmerged(ptr) || knockedOutUnderwater) diff --git a/apps/openmw/mwmechanics/aibreathe.cpp b/apps/openmw/mwmechanics/aibreathe.cpp new file mode 100644 index 00000000000..4e0076824b3 --- /dev/null +++ b/apps/openmw/mwmechanics/aibreathe.cpp @@ -0,0 +1,54 @@ +#include "aibreathe.hpp" + +#include "../mwbase/world.hpp" +#include "../mwbase/environment.hpp" + +#include "../mwworld/class.hpp" +#include "../mwworld/esmstore.hpp" + +#include "npcstats.hpp" + +#include "movement.hpp" +#include "steering.hpp" + +MWMechanics::AiBreathe::AiBreathe() +: AiPackage() +{ + +} + +bool MWMechanics::AiBreathe::execute (const MWWorld::Ptr& actor, CharacterController& characterController, AiState& state, float duration) +{ + static const float fHoldBreathTime = MWBase::Environment::get().getWorld()->getStore().get().find("fHoldBreathTime")->getFloat(); + + const MWWorld::Class& actorClass = actor.getClass(); + if (actorClass.isNpc()) + { + if (actorClass.getNpcStats(actor).getTimeToStartDrowning() < fHoldBreathTime / 2) + { + actor.getClass().getCreatureStats(actor).setMovementFlag(CreatureStats::Flag_Run, true); + + actor.getClass().getMovementSettings(actor).mPosition[1] = 1; + smoothTurn(actor, -180, 0); + + return false; + } + } + + return true; +} + +MWMechanics::AiBreathe *MWMechanics::AiBreathe::clone() const +{ + return new AiBreathe(*this); +} + +int MWMechanics::AiBreathe::getTypeId() const +{ + return TypeIdBreathe; +} + +unsigned int MWMechanics::AiBreathe::getPriority() const +{ + return 2; +} diff --git a/apps/openmw/mwmechanics/aibreathe.hpp b/apps/openmw/mwmechanics/aibreathe.hpp new file mode 100644 index 00000000000..263ab8c2b46 --- /dev/null +++ b/apps/openmw/mwmechanics/aibreathe.hpp @@ -0,0 +1,28 @@ +#ifndef GAME_MWMECHANICS_AIBREATHE_H +#define GAME_MWMECHANICS_AIBREATHE_H + +#include "aipackage.hpp" + +namespace MWMechanics +{ + /// \brief AiPackage to have an actor resurface to breathe + // The AI will go up if lesser than half breath left + class AiBreathe : public AiPackage + { + public: + AiBreathe(); + + virtual AiBreathe *clone() const; + + virtual bool execute (const MWWorld::Ptr& actor, CharacterController& characterController, AiState& state, float duration); + + virtual int getTypeId() const; + + virtual unsigned int getPriority() const; + + virtual bool canCancel() const { return false; } + virtual bool shouldCancelPreviousAi() const { return false; } + }; +} +#endif + diff --git a/apps/openmw/mwmechanics/aipackage.hpp b/apps/openmw/mwmechanics/aipackage.hpp index 5e23e085e5c..acbd8790877 100644 --- a/apps/openmw/mwmechanics/aipackage.hpp +++ b/apps/openmw/mwmechanics/aipackage.hpp @@ -41,12 +41,13 @@ namespace MWMechanics TypeIdFollow = 3, TypeIdActivate = 4, - // These 4 are not really handled as Ai Packages in the MW engine + // These 5 are not really handled as Ai Packages in the MW engine // For compatibility do *not* return these in the getCurrentAiPackage script function.. TypeIdCombat = 5, TypeIdPursue = 6, TypeIdAvoidDoor = 7, - TypeIdFace = 8 + TypeIdFace = 8, + TypeIdBreathe = 9 }; ///Default constructor diff --git a/apps/openmw/mwmechanics/aisequence.cpp b/apps/openmw/mwmechanics/aisequence.cpp index 2f33d6e4ec1..d9652ef5456 100644 --- a/apps/openmw/mwmechanics/aisequence.cpp +++ b/apps/openmw/mwmechanics/aisequence.cpp @@ -182,7 +182,8 @@ bool isActualAiPackage(int packageTypeId) return (packageTypeId != AiPackage::TypeIdCombat && packageTypeId != AiPackage::TypeIdPursue && packageTypeId != AiPackage::TypeIdAvoidDoor - && packageTypeId != AiPackage::TypeIdFace); + && packageTypeId != AiPackage::TypeIdFace + && packageTypeId != AiPackage::TypeIdBreathe); } void AiSequence::execute (const MWWorld::Ptr& actor, CharacterController& characterController, AiState& state, float duration)