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.
pull/142/head
scrawl 8 years ago
parent cce42b6e9d
commit 541fbb4792

@ -18,7 +18,7 @@ namespace MWPhysics
Actor::Actor(const MWWorld::Ptr& ptr, osg::ref_ptr<const Resource::BulletShape> shape, btCollisionWorld* world) Actor::Actor(const MWWorld::Ptr& ptr, osg::ref_ptr<const Resource::BulletShape> shape, btCollisionWorld* world)
: mCanWaterWalk(false), mWalkingOnWater(false) : 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) , mInternalCollisionMode(true)
, mExternalCollisionMode(true) , mExternalCollisionMode(true)
, mCollisionWorld(world) , mCollisionWorld(world)
@ -180,6 +180,11 @@ void Actor::setOnGround(bool grounded)
mOnGround = grounded; mOnGround = grounded;
} }
void Actor::setOnSlope(bool slope)
{
mOnSlope = slope;
}
bool Actor::isWalkingOnWater() const bool Actor::isWalkingOnWater() const
{ {
return mWalkingOnWater; return mWalkingOnWater;

@ -124,6 +124,13 @@ namespace MWPhysics
return mInternalCollisionMode && mOnGround; return mInternalCollisionMode && mOnGround;
} }
void setOnSlope(bool slope);
bool getOnSlope() const
{
return mInternalCollisionMode && mOnSlope;
}
btCollisionObject* getCollisionObject() const btCollisionObject* getCollisionObject() const
{ {
return mCollisionObject.get(); return mCollisionObject.get();
@ -160,6 +167,7 @@ namespace MWPhysics
osg::Vec3f mForce; osg::Vec3f mForce;
bool mOnGround; bool mOnGround;
bool mOnSlope;
bool mInternalCollisionMode; bool mInternalCollisionMode;
bool mExternalCollisionMode; bool mExternalCollisionMode;

@ -250,6 +250,8 @@ namespace MWPhysics
} }
else else
{ {
actor->setOnGround(true);
// Check if we actually found a valid spawn point (use an infinitely thin ray this time). // 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 // 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 // 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 ( (toOsg(resultCallback1.m_hitPointWorld) - tracer.mEndPos).length2() > 35*35
|| !isWalkableSlope(tracer.mPlaneNormal))) || !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); return toOsg(resultCallback1.m_hitPointWorld) + osg::Vec3f(0.f, 0.f, 1.f);
} }
else
actor->setOnGround(isWalkableSlope(tracer.mPlaneNormal)); {
actor->setOnSlope(isWalkableSlope(tracer.mPlaneNormal));
}
return tracer.mEndPos; return tracer.mEndPos;
} }
@ -322,12 +326,10 @@ namespace MWPhysics
{ {
velocity = (osg::Quat(refpos.rot[2], osg::Vec3f(0, 0, -1))) * movement; 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; inertia = velocity;
else if(!physicActor->getOnGround()) else if(!physicActor->getOnGround() || physicActor->getOnSlope())
{
velocity = velocity + physicActor->getInertialForce(); velocity = velocity + physicActor->getInertialForce();
}
} }
// dead actors underwater will float to the surface, if the CharacterController tells us to do so // 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 isOnGround = false;
bool isOnSlope = false;
if (!(inertia.z() > 0.f) && !(newPosition.z() < swimlevel)) if (!(inertia.z() > 0.f) && !(newPosition.z() < swimlevel))
{ {
osg::Vec3f from = newPosition; osg::Vec3f from = newPosition;
osg::Vec3f to = newPosition - (physicActor->getOnGround() ? osg::Vec3f to = newPosition - (physicActor->getOnGround() ?
osg::Vec3f(0,0,sStepSizeDown+2.f) : osg::Vec3f(0,0,2.f)); osg::Vec3f(0,0,sStepSizeDown+2.f) : osg::Vec3f(0,0,2.f));
tracer.doTrace(colobj, from, to, collisionWorld); 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) && tracer.mHitObject->getBroadphaseHandle()->m_collisionFilterGroup != CollisionType_Actor)
{ {
const btCollisionObject* standingOn = tracer.mHitObject; const btCollisionObject* standingOn = tracer.mHitObject;
@ -460,6 +463,8 @@ namespace MWPhysics
newPosition.z() = tracer.mEndPos.z() + 1.0f; newPosition.z() = tracer.mEndPos.z() + 1.0f;
isOnGround = true; isOnGround = true;
isOnSlope = !isWalkableSlope(tracer.mPlaneNormal);
} }
else 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)); physicActor->setInertialForce(osg::Vec3f(0.f, 0.f, 0.f));
else else
{ {
@ -497,6 +502,7 @@ namespace MWPhysics
physicActor->setInertialForce(inertia); physicActor->setInertialForce(inertia);
} }
physicActor->setOnGround(isOnGround); physicActor->setOnGround(isOnGround);
physicActor->setOnSlope(isOnSlope);
newPosition.z() -= halfExtents.z(); // remove what was added at the beginning newPosition.z() -= halfExtents.z(); // remove what was added at the beginning
return newPosition; return newPosition;

Loading…
Cancel
Save