1
0
Fork 1
mirror of https://github.com/TES3MP/openmw-tes3mp.git synced 2025-01-21 09:23:51 +00:00

Merge branch 'cherry-pick-6eaf0a38' into 'master'

Merge branch 'movement_refactoring' into 'master'

See merge request OpenMW/openmw!287
This commit is contained in:
psi29a 2020-08-27 11:55:07 +00:00
commit 26c38ecb46
16 changed files with 97 additions and 83 deletions

View file

@ -91,4 +91,13 @@ namespace MWClass
{ {
return true; return true;
} }
float Actor::getCurrentSpeed(const MWWorld::Ptr& ptr) const
{
const MWMechanics::Movement& movementSettings = ptr.getClass().getMovementSettings(ptr);
float moveSpeed = this->getMaxSpeed(ptr) * movementSettings.mSpeedFactor;
if (movementSettings.mIsStrafing)
moveSpeed *= 0.75f;
return moveSpeed;
}
} }

View file

@ -31,7 +31,7 @@ namespace MWClass
virtual void block(const MWWorld::Ptr &ptr) const; virtual void block(const MWWorld::Ptr &ptr) const;
virtual osg::Vec3f getRotationVector(const MWWorld::Ptr& ptr) const; virtual osg::Vec3f getRotationVector(const MWWorld::Ptr& ptr) const;
///< Return desired rotations, as euler angles. ///< Return desired rotations, as euler angles. Sets getMovementSettings(ptr).mRotation to zero.
virtual float getEncumbrance(const MWWorld::Ptr& ptr) const; virtual float getEncumbrance(const MWWorld::Ptr& ptr) const;
///< Returns total weight of objects inside this object (including modifications from magic ///< Returns total weight of objects inside this object (including modifications from magic
@ -42,6 +42,9 @@ namespace MWClass
virtual bool isActor() const; virtual bool isActor() const;
/// Return current movement speed.
virtual float getCurrentSpeed(const MWWorld::Ptr& ptr) const;
// not implemented // not implemented
Actor(const Actor&); Actor(const Actor&);
Actor& operator= (const Actor&); Actor& operator= (const Actor&);

View file

@ -500,7 +500,7 @@ namespace MWClass
registerClass (typeid (ESM::Creature).name(), instance); registerClass (typeid (ESM::Creature).name(), instance);
} }
float Creature::getSpeed(const MWWorld::Ptr &ptr) const float Creature::getMaxSpeed(const MWWorld::Ptr &ptr) const
{ {
const MWMechanics::CreatureStats& stats = getCreatureStats(ptr); const MWMechanics::CreatureStats& stats = getCreatureStats(ptr);
@ -532,11 +532,6 @@ namespace MWClass
else else
moveSpeed = getWalkSpeed(ptr); moveSpeed = getWalkSpeed(ptr);
const MWMechanics::Movement& movementSettings = ptr.getClass().getMovementSettings(ptr);
if (movementSettings.mIsStrafing)
moveSpeed *= 0.75f;
moveSpeed *= movementSettings.mSpeedFactor;
return moveSpeed; return moveSpeed;
} }

View file

@ -94,7 +94,7 @@ namespace MWClass
virtual MWMechanics::Movement& getMovementSettings (const MWWorld::Ptr& ptr) const; virtual MWMechanics::Movement& getMovementSettings (const MWWorld::Ptr& ptr) const;
///< Return desired movement. ///< Return desired movement.
float getSpeed (const MWWorld::Ptr& ptr) const; float getMaxSpeed (const MWWorld::Ptr& ptr) const;
static void registerSelf(); static void registerSelf();

View file

@ -936,7 +936,7 @@ namespace MWClass
return ref->mBase->mScript; return ref->mBase->mScript;
} }
float Npc::getSpeed(const MWWorld::Ptr& ptr) const float Npc::getMaxSpeed(const MWWorld::Ptr& ptr) const
{ {
const MWMechanics::CreatureStats& stats = getCreatureStats(ptr); const MWMechanics::CreatureStats& stats = getCreatureStats(ptr);
if (stats.isParalyzed() || stats.getKnockedDown() || stats.isDead()) if (stats.isParalyzed() || stats.getKnockedDown() || stats.isDead())
@ -979,11 +979,6 @@ namespace MWClass
if(npcdata->mNpcStats.isWerewolf() && running && npcdata->mNpcStats.getDrawState() == MWMechanics::DrawState_Nothing) if(npcdata->mNpcStats.isWerewolf() && running && npcdata->mNpcStats.getDrawState() == MWMechanics::DrawState_Nothing)
moveSpeed *= gmst.fWereWolfRunMult->mValue.getFloat(); moveSpeed *= gmst.fWereWolfRunMult->mValue.getFloat();
const MWMechanics::Movement& movementSettings = ptr.getClass().getMovementSettings(ptr);
if (movementSettings.mIsStrafing)
moveSpeed *= 0.75f;
moveSpeed *= movementSettings.mSpeedFactor;
return moveSpeed; return moveSpeed;
} }

View file

@ -84,8 +84,8 @@ namespace MWClass
virtual std::string getScript (const MWWorld::ConstPtr& ptr) const; virtual std::string getScript (const MWWorld::ConstPtr& ptr) const;
///< Return name of the script attached to ptr ///< Return name of the script attached to ptr
virtual float getSpeed (const MWWorld::Ptr& ptr) const; virtual float getMaxSpeed (const MWWorld::Ptr& ptr) const;
///< Return movement speed. ///< Return maximal movement speed.
virtual float getJump(const MWWorld::Ptr &ptr) const; virtual float getJump(const MWWorld::Ptr &ptr) const;
///< Return jump velocity (not accounting for movement) ///< Return jump velocity (not accounting for movement)

View file

@ -472,9 +472,6 @@ namespace MWMechanics
void Actors::updateMovementSpeed(const MWWorld::Ptr& actor) void Actors::updateMovementSpeed(const MWWorld::Ptr& actor)
{ {
float previousSpeedFactor = actor.getClass().getMovementSettings(actor).mSpeedFactor;
float newSpeedFactor = 1.f;
CreatureStats &stats = actor.getClass().getCreatureStats(actor); CreatureStats &stats = actor.getClass().getCreatureStats(actor);
MWMechanics::AiSequence& seq = stats.getAiSequence(); MWMechanics::AiSequence& seq = stats.getAiSequence();
@ -484,10 +481,13 @@ namespace MWMechanics
osg::Vec3f actorPos = actor.getRefData().getPosition().asVec3(); osg::Vec3f actorPos = actor.getRefData().getPosition().asVec3();
float distance = (targetPos - actorPos).length(); float distance = (targetPos - actorPos).length();
if (distance < DECELERATE_DISTANCE) if (distance < DECELERATE_DISTANCE)
newSpeedFactor = std::max(0.7f, 0.1f * previousSpeedFactor * (distance/64.f + 2.f)); {
float speedCoef = std::max(0.7f, 0.1f * (distance/64.f + 2.f));
auto& movement = actor.getClass().getMovementSettings(actor);
movement.mPosition[0] *= speedCoef;
movement.mPosition[1] *= speedCoef;
}
} }
actor.getClass().getMovementSettings(actor).mSpeedFactor = newSpeedFactor;
} }
void Actors::updateGreetingState(const MWWorld::Ptr& actor, Actor& actorState, bool turnOnly) void Actors::updateGreetingState(const MWWorld::Ptr& actor, Actor& actorState, bool turnOnly)

View file

@ -144,7 +144,7 @@ bool MWMechanics::AiPackage::pathTo(const MWWorld::Ptr& actor, const osg::Vec3f&
mTimer = 0; mTimer = 0;
} }
const float actorTolerance = 2 * actor.getClass().getSpeed(actor) * duration const float actorTolerance = 2 * actor.getClass().getMaxSpeed(actor) * duration
+ 1.2 * std::max(halfExtents.x(), halfExtents.y()); + 1.2 * std::max(halfExtents.x(), halfExtents.y());
const float pointTolerance = std::max(MIN_TOLERANCE, actorTolerance); const float pointTolerance = std::max(MIN_TOLERANCE, actorTolerance);
@ -300,7 +300,7 @@ bool MWMechanics::AiPackage::checkWayIsClearForActor(const osg::Vec3f& startPoin
if (canActorMoveByZAxis(actor)) if (canActorMoveByZAxis(actor))
return true; return true;
const float actorSpeed = actor.getClass().getSpeed(actor); const float actorSpeed = actor.getClass().getMaxSpeed(actor);
const float maxAvoidDist = AI_REACTION_TIME * actorSpeed + actorSpeed / getAngularVelocity(actorSpeed) * 2; // *2 - for reliability const float maxAvoidDist = AI_REACTION_TIME * actorSpeed + actorSpeed / getAngularVelocity(actorSpeed) * 2; // *2 - for reliability
const float distToTarget = osg::Vec2f(endPoint.x(), endPoint.y()).length(); const float distToTarget = osg::Vec2f(endPoint.x(), endPoint.y()).length();
@ -360,7 +360,7 @@ bool MWMechanics::AiPackage::isNearInactiveCell(osg::Vec3f position)
bool MWMechanics::AiPackage::isReachableRotatingOnTheRun(const MWWorld::Ptr& actor, const osg::Vec3f& dest) bool MWMechanics::AiPackage::isReachableRotatingOnTheRun(const MWWorld::Ptr& actor, const osg::Vec3f& dest)
{ {
// get actor's shortest radius for moving in circle // get actor's shortest radius for moving in circle
float speed = actor.getClass().getSpeed(actor); float speed = actor.getClass().getMaxSpeed(actor);
speed += speed * 0.1f; // 10% real speed inaccuracy speed += speed * 0.1f; // 10% real speed inaccuracy
float radius = speed / getAngularVelocity(speed); float radius = speed / getAngularVelocity(speed);

View file

@ -21,6 +21,7 @@
#include <iostream> #include <iostream>
#include <components/misc/mathutil.hpp>
#include <components/misc/rng.hpp> #include <components/misc/rng.hpp>
#include <components/settings/settings.hpp> #include <components/settings/settings.hpp>
@ -51,15 +52,6 @@
namespace namespace
{ {
// Wraps a value to (-PI, PI]
void wrap(float& rad)
{
if (rad>0)
rad = std::fmod(rad+osg::PI, 2.0f*osg::PI)-osg::PI;
else
rad = std::fmod(rad-osg::PI, 2.0f*osg::PI)+osg::PI;
}
std::string getBestAttack (const ESM::Weapon* weapon) std::string getBestAttack (const ESM::Weapon* weapon)
{ {
int slash = (weapon->mData.mSlash[0] + weapon->mData.mSlash[1])/2; int slash = (weapon->mData.mSlash[0] + weapon->mData.mSlash[1])/2;
@ -1958,23 +1950,19 @@ void CharacterController::update(float duration, bool animationOnly)
osg::Vec3f rot = cls.getRotationVector(mPtr); osg::Vec3f rot = cls.getRotationVector(mPtr);
osg::Vec3f vec(movementSettings.asVec3()); osg::Vec3f vec(movementSettings.asVec3());
vec.normalize();
float analogueMult = 1.0f;
if (isPlayer) if (isPlayer)
{ {
// TODO: Move this code to mwinput. // TODO: Move this code to mwinput.
// Joystick analogue movement. // Joystick analogue movement.
float xAxis = std::abs(movementSettings.mPosition[0]); movementSettings.mSpeedFactor = std::max(std::abs(vec.x()), std::abs(vec.y()));
float yAxis = std::abs(movementSettings.mPosition[1]);
analogueMult = std::max(xAxis, yAxis);
// Due to the half way split between walking/running, we multiply speed by 2 while walking, unless a keyboard was used. // Due to the half way split between walking/running, we multiply speed by 2 while walking, unless a keyboard was used.
if(!isrunning && !sneak && !flying && analogueMult <= 0.5f) if(!isrunning && !sneak && !flying && movementSettings.mSpeedFactor <= 0.5f)
analogueMult *= 2.f; movementSettings.mSpeedFactor *= 2.f;
} else
movementSettings.mSpeedFactor = analogueMult; movementSettings.mSpeedFactor = std::min(vec.length(), 1.f);
} vec.normalize();
float effectiveRotation = rot.z(); float effectiveRotation = rot.z();
static const bool turnToMovementDirection = Settings::Manager::getBool("turn to movement direction", "Game"); static const bool turnToMovementDirection = Settings::Manager::getBool("turn to movement direction", "Game");
@ -2007,7 +1995,7 @@ void CharacterController::update(float duration, bool animationOnly)
else else
mAnimation->setUpperBodyYawRadians(stats.getSideMovementAngle() / 4); mAnimation->setUpperBodyYawRadians(stats.getSideMovementAngle() / 4);
speed = cls.getSpeed(mPtr); speed = cls.getCurrentSpeed(mPtr);
vec.x() *= speed; vec.x() *= speed;
vec.y() *= speed; vec.y() *= speed;
@ -2077,7 +2065,7 @@ void CharacterController::update(float duration, bool animationOnly)
} }
} }
fatigueLoss *= duration; fatigueLoss *= duration;
fatigueLoss *= analogueMult; fatigueLoss *= movementSettings.mSpeedFactor;
DynamicStat<float> fatigue = cls.getCreatureStats(mPtr).getFatigue(); DynamicStat<float> fatigue = cls.getCreatureStats(mPtr).getFatigue();
if (!godmode) if (!godmode)
@ -2908,13 +2896,10 @@ void CharacterController::updateHeadTracking(float duration)
zAngleRadians = std::atan2(direction.x(), direction.y()) - std::atan2(actorDirection.x(), actorDirection.y()); zAngleRadians = std::atan2(direction.x(), direction.y()) - std::atan2(actorDirection.x(), actorDirection.y());
xAngleRadians = -std::asin(direction.z()); xAngleRadians = -std::asin(direction.z());
wrap(zAngleRadians); const double xLimit = osg::DegreesToRadians(40.0);
wrap(xAngleRadians); const double zLimit = osg::DegreesToRadians(30.0);
zAngleRadians = osg::clampBetween(Misc::normalizeAngle(zAngleRadians), -xLimit, xLimit);
xAngleRadians = std::min(xAngleRadians, osg::DegreesToRadians(40.f)); xAngleRadians = osg::clampBetween(Misc::normalizeAngle(xAngleRadians), -zLimit, zLimit);
xAngleRadians = std::max(xAngleRadians, osg::DegreesToRadians(-40.f));
zAngleRadians = std::min(zAngleRadians, osg::DegreesToRadians(30.f));
zAngleRadians = std::max(zAngleRadians, osg::DegreesToRadians(-30.f));
} }
float factor = duration*5; float factor = duration*5;

View file

@ -8,8 +8,14 @@ namespace MWMechanics
/// Desired movement for an actor /// Desired movement for an actor
struct Movement struct Movement
{ {
// Desired movement. Direction is relative to the current orientation.
// Length of the vector controls desired speed. 0 - stay, 0.5 - half-speed, 1.0 - max speed.
float mPosition[3]; float mPosition[3];
// Desired rotation delta (euler angles).
float mRotation[3]; float mRotation[3];
// Controlled by CharacterController, should not be changed from other places.
// These fields can not be private fields in CharacterController, because Actor::getCurrentSpeed uses it.
float mSpeedFactor; float mSpeedFactor;
bool mIsStrafing; bool mIsStrafing;

View file

@ -122,7 +122,7 @@ namespace MWMechanics
if (mWalkState != WalkState::Evade) if (mWalkState != WalkState::Evade)
{ {
const float distSameSpot = DIST_SAME_SPOT * actor.getClass().getSpeed(actor) * duration; const float distSameSpot = DIST_SAME_SPOT * actor.getClass().getCurrentSpeed(actor) * duration;
const float prevDistance = (destination - mPrev).length(); const float prevDistance = (destination - mPrev).length();
const float currentDistance = (destination - position).length(); const float currentDistance = (destination - position).length();
const float movedDistance = prevDistance - currentDistance; const float movedDistance = prevDistance - currentDistance;

View file

@ -32,7 +32,7 @@ bool smoothTurn(const MWWorld::Ptr& actor, float targetAngleRadians, int axis, f
if (absDiff < epsilonRadians) if (absDiff < epsilonRadians)
return true; return true;
float limit = getAngularVelocity(actor.getClass().getSpeed(actor)) * MWBase::Environment::get().getFrameDuration(); float limit = getAngularVelocity(actor.getClass().getMaxSpeed(actor)) * MWBase::Environment::get().getFrameDuration();
if (absDiff > limit) if (absDiff > limit)
diff = osg::sign(diff) * limit; diff = osg::sign(diff) * limit;

View file

@ -2,6 +2,7 @@
#include <osg/Camera> #include <osg/Camera>
#include <components/misc/mathutil.hpp>
#include <components/sceneutil/positionattitudetransform.hpp> #include <components/sceneutil/positionattitudetransform.hpp>
#include <components/settings/settings.hpp> #include <components/settings/settings.hpp>
@ -200,7 +201,7 @@ namespace MWRender
updateFocalPointOffset(duration); updateFocalPointOffset(duration);
updatePosition(); updatePosition();
float speed = mTrackingPtr.getClass().getSpeed(mTrackingPtr); float speed = mTrackingPtr.getClass().getCurrentSpeed(mTrackingPtr);
speed /= (1.f + speed / 500.f); speed /= (1.f + speed / 500.f);
float maxDelta = 300.f * duration; float maxDelta = 300.f * duration;
mSmoothedSpeed += osg::clampBetween(speed - mSmoothedSpeed, -maxDelta, maxDelta); mSmoothedSpeed += osg::clampBetween(speed - mSmoothedSpeed, -maxDelta, maxDelta);
@ -249,7 +250,7 @@ namespace MWRender
{ {
if (!mStandingPreviewAllowed) if (!mStandingPreviewAllowed)
return; return;
float speed = mTrackingPtr.getClass().getSpeed(mTrackingPtr); float speed = mTrackingPtr.getClass().getCurrentSpeed(mTrackingPtr);
bool combat = mTrackingPtr.getClass().isActor() && bool combat = mTrackingPtr.getClass().isActor() &&
mTrackingPtr.getClass().getCreatureStats(mTrackingPtr).getDrawState() != MWMechanics::DrawState_Nothing; mTrackingPtr.getClass().getCreatureStats(mTrackingPtr).getDrawState() != MWMechanics::DrawState_Nothing;
bool standingStill = speed == 0 && !combat && !mFirstPersonView; bool standingStill = speed == 0 && !combat && !mFirstPersonView;
@ -396,12 +397,7 @@ namespace MWRender
void Camera::setYaw(float angle) void Camera::setYaw(float angle)
{ {
if (angle > osg::PI) { mYaw = Misc::normalizeAngle(angle);
angle -= osg::PI*2;
} else if (angle < -osg::PI) {
angle += osg::PI*2;
}
mYaw = angle;
} }
void Camera::setPitch(float angle) void Camera::setPitch(float angle)
@ -538,16 +534,8 @@ namespace MWRender
return; return;
} }
mDeferredRotation.x() = -ptr.getRefData().getPosition().rot[0] - mPitch; mDeferredRotation.x() = Misc::normalizeAngle(-ptr.getRefData().getPosition().rot[0] - mPitch);
mDeferredRotation.z() = -ptr.getRefData().getPosition().rot[2] - mYaw; mDeferredRotation.z() = Misc::normalizeAngle(-ptr.getRefData().getPosition().rot[2] - mYaw);
if (mDeferredRotation.x() > osg::PI)
mDeferredRotation.x() -= 2 * osg::PI;
if (mDeferredRotation.x() < -osg::PI)
mDeferredRotation.x() += 2 * osg::PI;
if (mDeferredRotation.z() > osg::PI)
mDeferredRotation.z() -= 2 * osg::PI;
if (mDeferredRotation.z() < -osg::PI)
mDeferredRotation.z() += 2 * osg::PI;
} }
} }

View file

@ -159,7 +159,12 @@ namespace MWWorld
return ""; return "";
} }
float Class::getSpeed (const Ptr& ptr) const float Class::getMaxSpeed (const Ptr& ptr) const
{
return 0;
}
float Class::getCurrentSpeed (const Ptr& ptr) const
{ {
return 0; return 0;
} }

View file

@ -172,8 +172,15 @@ namespace MWWorld
///< Return name of the script attached to ptr (default implementation: return an empty ///< Return name of the script attached to ptr (default implementation: return an empty
/// string). /// string).
virtual float getSpeed (const Ptr& ptr) const; virtual float getWalkSpeed(const Ptr& ptr) const;
///< Return movement speed. virtual float getRunSpeed(const Ptr& ptr) const;
virtual float getSwimSpeed(const Ptr& ptr) const;
/// Return maximal movement speed for the current state.
virtual float getMaxSpeed(const Ptr& ptr) const;
/// Return current movement speed.
virtual float getCurrentSpeed(const Ptr& ptr) const;
virtual float getJump(const MWWorld::Ptr &ptr) const; virtual float getJump(const MWWorld::Ptr &ptr) const;
///< Return jump velocity (not accounting for movement) ///< Return jump velocity (not accounting for movement)
@ -182,7 +189,7 @@ namespace MWWorld
///< Return desired movement. ///< Return desired movement.
virtual osg::Vec3f getRotationVector (const Ptr& ptr) const; virtual osg::Vec3f getRotationVector (const Ptr& ptr) const;
///< Return desired rotations, as euler angles. ///< Return desired rotations, as euler angles. Sets getMovementSettings(ptr).mRotation to zero.
virtual std::pair<std::vector<int>, bool> getEquipmentSlots (const ConstPtr& ptr) const; virtual std::pair<std::vector<int>, bool> getEquipmentSlots (const ConstPtr& ptr) const;
///< \return first: Return IDs of the slot this object can be equipped in; second: can object ///< \return first: Return IDs of the slot this object can be equipped in; second: can object
@ -364,12 +371,6 @@ namespace MWWorld
virtual void setBaseAISetting(const std::string& id, MWMechanics::CreatureStats::AiSetting setting, int value) const; virtual void setBaseAISetting(const std::string& id, MWMechanics::CreatureStats::AiSetting setting, int value) const;
virtual void modifyBaseInventory(const std::string& actorId, const std::string& itemId, int amount) const; virtual void modifyBaseInventory(const std::string& actorId, const std::string& itemId, int amount) const;
virtual float getWalkSpeed(const Ptr& ptr) const;
virtual float getRunSpeed(const Ptr& ptr) const;
virtual float getSwimSpeed(const Ptr& ptr) const;
}; };
} }

View file

@ -0,0 +1,27 @@
#ifndef MISC_MATHUTIL_H
#define MISC_MATHUTIL_H
#include <osg/Math>
#include <osg/Vec2f>
namespace Misc
{
/// Normalizes given angle to the range [-PI, PI]. E.g. PI*3/2 -> -PI/2.
inline double normalizeAngle(double angle)
{
double fullTurns = angle / (2 * osg::PI) + 0.5;
return (fullTurns - floor(fullTurns) - 0.5) * (2 * osg::PI);
}
/// Rotates given 2d vector counterclockwise. Angle is in radians.
inline osg::Vec2f rotateVec2f(osg::Vec2f vec, float angle)
{
float s = std::sin(angle);
float c = std::cos(angle);
return osg::Vec2f(vec.x() * c + vec.y() * -s, vec.x() * s + vec.y() * c);
}
}
#endif