diff --git a/apps/openmw/CMakeLists.txt b/apps/openmw/CMakeLists.txt index a06678488..1aa311fd2 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 431ad09b3..44a457cb6 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 000000000..4e0076824 --- /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 000000000..263ab8c2b --- /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 5e23e085e..acbd87908 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 2f33d6e4e..d9652ef54 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)