From 541fbb47924499eefba2b760997ac74698681bf9 Mon Sep 17 00:00:00 2001 From: scrawl Date: Mon, 6 Feb 2017 04:46:44 +0100 Subject: [PATCH] Movement solver: add usage of 'on slope' flag to improve handling of steep slopes Previously we were handling 'on slope' synonymously with 'in air' which caused some odd effects. Practical changes: - Sliding down a slope no longer applies fall damage. - Fixed a climbing exploit that would allow climbing steep slopes with repeated use of the Jump function. --- apps/openmw/mwphysics/actor.cpp | 7 ++++++- apps/openmw/mwphysics/actor.hpp | 8 ++++++++ apps/openmw/mwphysics/physicssystem.cpp | 24 +++++++++++++++--------- 3 files changed, 29 insertions(+), 10 deletions(-) diff --git a/apps/openmw/mwphysics/actor.cpp b/apps/openmw/mwphysics/actor.cpp index 9be34495e..d18dfbcfc 100644 --- a/apps/openmw/mwphysics/actor.cpp +++ b/apps/openmw/mwphysics/actor.cpp @@ -18,7 +18,7 @@ namespace MWPhysics Actor::Actor(const MWWorld::Ptr& ptr, osg::ref_ptr shape, btCollisionWorld* world) : mCanWaterWalk(false), mWalkingOnWater(false) - , mCollisionObject(0), mForce(0.f, 0.f, 0.f), mOnGround(false) + , mCollisionObject(0), mForce(0.f, 0.f, 0.f), mOnGround(false), mOnSlope(false) , mInternalCollisionMode(true) , mExternalCollisionMode(true) , mCollisionWorld(world) @@ -180,6 +180,11 @@ void Actor::setOnGround(bool grounded) mOnGround = grounded; } +void Actor::setOnSlope(bool slope) +{ + mOnSlope = slope; +} + bool Actor::isWalkingOnWater() const { return mWalkingOnWater; diff --git a/apps/openmw/mwphysics/actor.hpp b/apps/openmw/mwphysics/actor.hpp index a0bf5bfc0..9c0144b9d 100644 --- a/apps/openmw/mwphysics/actor.hpp +++ b/apps/openmw/mwphysics/actor.hpp @@ -124,6 +124,13 @@ namespace MWPhysics return mInternalCollisionMode && mOnGround; } + void setOnSlope(bool slope); + + bool getOnSlope() const + { + return mInternalCollisionMode && mOnSlope; + } + btCollisionObject* getCollisionObject() const { return mCollisionObject.get(); @@ -160,6 +167,7 @@ namespace MWPhysics osg::Vec3f mForce; bool mOnGround; + bool mOnSlope; bool mInternalCollisionMode; bool mExternalCollisionMode; diff --git a/apps/openmw/mwphysics/physicssystem.cpp b/apps/openmw/mwphysics/physicssystem.cpp index 2d0964f25..374a68b48 100644 --- a/apps/openmw/mwphysics/physicssystem.cpp +++ b/apps/openmw/mwphysics/physicssystem.cpp @@ -250,6 +250,8 @@ namespace MWPhysics } else { + actor->setOnGround(true); + // Check if we actually found a valid spawn point (use an infinitely thin ray this time). // Required for some broken door destinations in Morrowind.esm, where the spawn point // intersects with other geometry if the actor's base is taken into account @@ -266,11 +268,13 @@ namespace MWPhysics ( (toOsg(resultCallback1.m_hitPointWorld) - tracer.mEndPos).length2() > 35*35 || !isWalkableSlope(tracer.mPlaneNormal))) { - actor->setOnGround(isWalkableSlope(resultCallback1.m_hitNormalWorld)); + actor->setOnSlope(isWalkableSlope(resultCallback1.m_hitNormalWorld)); return toOsg(resultCallback1.m_hitPointWorld) + osg::Vec3f(0.f, 0.f, 1.f); } - - actor->setOnGround(isWalkableSlope(tracer.mPlaneNormal)); + else + { + actor->setOnSlope(isWalkableSlope(tracer.mPlaneNormal)); + } return tracer.mEndPos; } @@ -322,12 +326,10 @@ namespace MWPhysics { velocity = (osg::Quat(refpos.rot[2], osg::Vec3f(0, 0, -1))) * movement; - if (velocity.z() > 0.f && physicActor->getOnGround()) + if (velocity.z() > 0.f && physicActor->getOnGround() && !physicActor->getOnSlope()) inertia = velocity; - else if(!physicActor->getOnGround()) - { + else if(!physicActor->getOnGround() || physicActor->getOnSlope()) velocity = velocity + physicActor->getInertialForce(); - } } // dead actors underwater will float to the surface, if the CharacterController tells us to do so @@ -440,13 +442,14 @@ namespace MWPhysics } bool isOnGround = false; + bool isOnSlope = false; if (!(inertia.z() > 0.f) && !(newPosition.z() < swimlevel)) { osg::Vec3f from = newPosition; osg::Vec3f to = newPosition - (physicActor->getOnGround() ? osg::Vec3f(0,0,sStepSizeDown+2.f) : osg::Vec3f(0,0,2.f)); tracer.doTrace(colobj, from, to, collisionWorld); - if(tracer.mFraction < 1.0f && isWalkableSlope(tracer.mPlaneNormal) + if(tracer.mFraction < 1.0f && tracer.mHitObject->getBroadphaseHandle()->m_collisionFilterGroup != CollisionType_Actor) { const btCollisionObject* standingOn = tracer.mHitObject; @@ -460,6 +463,8 @@ namespace MWPhysics newPosition.z() = tracer.mEndPos.z() + 1.0f; isOnGround = true; + + isOnSlope = !isWalkableSlope(tracer.mPlaneNormal); } else { @@ -483,7 +488,7 @@ namespace MWPhysics } } - if(isOnGround || newPosition.z() < swimlevel || isFlying) + if((isOnGround && !isOnSlope) || newPosition.z() < swimlevel || isFlying) physicActor->setInertialForce(osg::Vec3f(0.f, 0.f, 0.f)); else { @@ -497,6 +502,7 @@ namespace MWPhysics physicActor->setInertialForce(inertia); } physicActor->setOnGround(isOnGround); + physicActor->setOnSlope(isOnSlope); newPosition.z() -= halfExtents.z(); // remove what was added at the beginning return newPosition;