From 61ab7f3c1e5bd82eaaa3bb05e6dfcad3a6a6a605 Mon Sep 17 00:00:00 2001 From: Andrei Kortunov Date: Thu, 31 Oct 2019 09:44:40 +0400 Subject: [PATCH] Implement actors movement deceleration (feature #4544) --- CHANGELOG.md | 1 + apps/openmw/mwclass/creature.cpp | 2 ++ apps/openmw/mwclass/npc.cpp | 2 ++ apps/openmw/mwmechanics/actors.cpp | 22 ++++++++++++++++++++++ apps/openmw/mwmechanics/actors.hpp | 1 + apps/openmw/mwmechanics/aiescort.hpp | 4 ++++ apps/openmw/mwmechanics/aifollow.hpp | 13 +++++++++++++ apps/openmw/mwmechanics/aipackage.hpp | 5 +++++ apps/openmw/mwmechanics/aisequence.cpp | 5 +++++ apps/openmw/mwmechanics/aisequence.hpp | 2 ++ apps/openmw/mwmechanics/aitravel.hpp | 4 ++++ apps/openmw/mwmechanics/movement.hpp | 2 ++ 12 files changed, 63 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index a2e13e3f0..9180d7c73 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -184,6 +184,7 @@ Feature #4255: Handle broken RepairedOnMe script function Feature #4316: Implement RaiseRank/LowerRank functions properly Feature #4360: Improve default controller bindings + Feature #4544: Actors movement deceleration Feature #4673: Weapon sheathing Feature #4675: Support for NiRollController Feature #4730: Native animated containers support diff --git a/apps/openmw/mwclass/creature.cpp b/apps/openmw/mwclass/creature.cpp index b53b7d040..9d5bd14e4 100644 --- a/apps/openmw/mwclass/creature.cpp +++ b/apps/openmw/mwclass/creature.cpp @@ -556,6 +556,8 @@ namespace MWClass if(getMovementSettings(ptr).mPosition[0] != 0 && getMovementSettings(ptr).mPosition[1] == 0) moveSpeed *= 0.75f; + moveSpeed *= ptr.getClass().getMovementSettings(ptr).mSpeedFactor; + return moveSpeed; } diff --git a/apps/openmw/mwclass/npc.cpp b/apps/openmw/mwclass/npc.cpp index 5c8557dfd..4f1a996e7 100644 --- a/apps/openmw/mwclass/npc.cpp +++ b/apps/openmw/mwclass/npc.cpp @@ -990,6 +990,8 @@ namespace MWClass if(npcdata->mNpcStats.isWerewolf() && running && npcdata->mNpcStats.getDrawState() == MWMechanics::DrawState_Nothing) moveSpeed *= gmst.fWereWolfRunMult->mValue.getFloat(); + moveSpeed *= ptr.getClass().getMovementSettings(ptr).mSpeedFactor; + return moveSpeed; } diff --git a/apps/openmw/mwmechanics/actors.cpp b/apps/openmw/mwmechanics/actors.cpp index 21a24f457..486c8b31b 100644 --- a/apps/openmw/mwmechanics/actors.cpp +++ b/apps/openmw/mwmechanics/actors.cpp @@ -144,6 +144,7 @@ namespace MWMechanics { static const int GREETING_SHOULD_START = 4; //how many updates should pass before NPC can greet player static const int GREETING_SHOULD_END = 10; + static const float DECELERATE_DISTANCE = 512.f; class GetStuntedMagickaDuration : public MWMechanics::EffectSourceVisitor { @@ -422,6 +423,26 @@ namespace MWMechanics MWBase::Environment::get().getDialogueManager()->say(actor, "idle"); } + void Actors::updateMovementSpeed(const MWWorld::Ptr& actor) + { + float previousSpeedFactor = actor.getClass().getMovementSettings(actor).mSpeedFactor; + float newSpeedFactor = 1.f; + + CreatureStats &stats = actor.getClass().getCreatureStats(actor); + MWMechanics::AiSequence& seq = stats.getAiSequence(); + + if (!seq.isEmpty() && seq.getActivePackage()->useVariableSpeed()) + { + osg::Vec3f targetPos = seq.getActivePackage()->getDestination(); + osg::Vec3f actorPos = actor.getRefData().getPosition().asVec3(); + float distance = (targetPos - actorPos).length(); + if (distance < DECELERATE_DISTANCE) + newSpeedFactor = std::max(0.7f, 0.1f * previousSpeedFactor * (distance/64.f + 2.f)); + } + + actor.getClass().getMovementSettings(actor).mSpeedFactor = newSpeedFactor; + } + void Actors::updateGreetingState(const MWWorld::Ptr& actor, bool turnOnly) { if (!actor.getClass().isActor() || actor == getPlayer()) @@ -1668,6 +1689,7 @@ namespace MWMechanics stats.getAiSequence().execute(iter->first, *ctrl, duration); updateGreetingState(iter->first, timerUpdateHello > 0); playIdleDialogue(iter->first); + updateMovementSpeed(iter->first); } } } diff --git a/apps/openmw/mwmechanics/actors.hpp b/apps/openmw/mwmechanics/actors.hpp index 04a7be8af..eb705fd68 100644 --- a/apps/openmw/mwmechanics/actors.hpp +++ b/apps/openmw/mwmechanics/actors.hpp @@ -121,6 +121,7 @@ namespace MWMechanics void engageCombat(const MWWorld::Ptr& actor1, const MWWorld::Ptr& actor2, std::map >& cachedAllies, bool againstPlayer); void playIdleDialogue(const MWWorld::Ptr& actor); + void updateMovementSpeed(const MWWorld::Ptr& actor); void updateGreetingState(const MWWorld::Ptr& actor, bool turnOnly); void turnActorToFacePlayer(const MWWorld::Ptr& actor, const osg::Vec3f& dir); diff --git a/apps/openmw/mwmechanics/aiescort.hpp b/apps/openmw/mwmechanics/aiescort.hpp index 56c9b1c85..f7761e58f 100644 --- a/apps/openmw/mwmechanics/aiescort.hpp +++ b/apps/openmw/mwmechanics/aiescort.hpp @@ -36,12 +36,16 @@ namespace MWMechanics virtual int getTypeId() const; + virtual bool useVariableSpeed() const { return true;} + virtual bool sideWithTarget() const { return true; } void writeState(ESM::AiSequence::AiSequence &sequence) const; void fastForward(const MWWorld::Ptr& actor, AiState& state); + virtual osg::Vec3f getDestination() { return osg::Vec3f(mX, mY, mZ); } + private: std::string mCellId; float mX; diff --git a/apps/openmw/mwmechanics/aifollow.hpp b/apps/openmw/mwmechanics/aifollow.hpp index 96249e28c..7b5a9e63e 100644 --- a/apps/openmw/mwmechanics/aifollow.hpp +++ b/apps/openmw/mwmechanics/aifollow.hpp @@ -7,6 +7,8 @@ #include +#include "../mwworld/ptr.hpp" + #include "pathfinding.hpp" namespace ESM @@ -61,6 +63,8 @@ namespace MWMechanics virtual int getTypeId() const; + virtual bool useVariableSpeed() const { return true;} + /// Returns the actor being followed std::string getFollowedActor(); @@ -72,6 +76,15 @@ namespace MWMechanics void fastForward(const MWWorld::Ptr& actor, AiState& state); + virtual osg::Vec3f getDestination() + { + MWWorld::Ptr target = getTarget(); + if (target.isEmpty()) + return osg::Vec3f(0, 0, 0); + + return target.getRefData().getPosition().asVec3(); + } + private: /// This will make the actor always follow. /** Thus ignoring mDuration and mX,mY,mZ (used for summoned creatures). **/ diff --git a/apps/openmw/mwmechanics/aipackage.hpp b/apps/openmw/mwmechanics/aipackage.hpp index 20b4c390e..0e628388d 100644 --- a/apps/openmw/mwmechanics/aipackage.hpp +++ b/apps/openmw/mwmechanics/aipackage.hpp @@ -73,6 +73,9 @@ namespace MWMechanics /// Higher number is higher priority (0 being the lowest) virtual unsigned int getPriority() const {return 0;} + /// Check if package use movement with variable speed + virtual bool useVariableSpeed() const { return false;} + virtual void writeState (ESM::AiSequence::AiSequence& sequence) const {} /// Simulates the passing of time @@ -99,6 +102,8 @@ namespace MWMechanics /// Return true if this package should repeat. Currently only used for Wander packages. virtual bool getRepeat() const; + virtual osg::Vec3f getDestination() { return osg::Vec3f(0, 0, 0); } + /// Reset pathfinding state void reset(); diff --git a/apps/openmw/mwmechanics/aisequence.cpp b/apps/openmw/mwmechanics/aisequence.cpp index 13f34058c..e02240352 100644 --- a/apps/openmw/mwmechanics/aisequence.cpp +++ b/apps/openmw/mwmechanics/aisequence.cpp @@ -388,6 +388,11 @@ void AiSequence::stack (const AiPackage& package, const MWWorld::Ptr& actor, boo } } +bool MWMechanics::AiSequence::isEmpty() const +{ + return mPackages.empty(); +} + AiPackage* MWMechanics::AiSequence::getActivePackage() { if(mPackages.empty()) diff --git a/apps/openmw/mwmechanics/aisequence.hpp b/apps/openmw/mwmechanics/aisequence.hpp index 4d0482a98..5df440407 100644 --- a/apps/openmw/mwmechanics/aisequence.hpp +++ b/apps/openmw/mwmechanics/aisequence.hpp @@ -132,6 +132,8 @@ namespace MWMechanics \see ESM::AIPackageList **/ void fill (const ESM::AIPackageList& list); + bool isEmpty() const; + void writeState (ESM::AiSequence::AiSequence& sequence) const; void readState (const ESM::AiSequence::AiSequence& sequence); }; diff --git a/apps/openmw/mwmechanics/aitravel.hpp b/apps/openmw/mwmechanics/aitravel.hpp index f9f9f43fc..ea69b5d74 100644 --- a/apps/openmw/mwmechanics/aitravel.hpp +++ b/apps/openmw/mwmechanics/aitravel.hpp @@ -32,6 +32,10 @@ namespace MWMechanics virtual int getTypeId() const; + virtual bool useVariableSpeed() const { return true;} + + virtual osg::Vec3f getDestination() { return osg::Vec3f(mX, mY, mZ); } + private: float mX; float mY; diff --git a/apps/openmw/mwmechanics/movement.hpp b/apps/openmw/mwmechanics/movement.hpp index c12b61538..cb9087359 100644 --- a/apps/openmw/mwmechanics/movement.hpp +++ b/apps/openmw/mwmechanics/movement.hpp @@ -10,11 +10,13 @@ namespace MWMechanics { float mPosition[3]; float mRotation[3]; + float mSpeedFactor; Movement() { mPosition[0] = mPosition[1] = mPosition[2] = 0.0f; mRotation[0] = mRotation[1] = mRotation[2] = 0.0f; + mSpeedFactor = 1.f; } osg::Vec3f asVec3()