diff --git a/apps/openmw/mwclass/actor.cpp b/apps/openmw/mwclass/actor.cpp index 4eb9728a1..61d6e7347 100644 --- a/apps/openmw/mwclass/actor.cpp +++ b/apps/openmw/mwclass/actor.cpp @@ -91,4 +91,13 @@ namespace MWClass { 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; + } } diff --git a/apps/openmw/mwclass/actor.hpp b/apps/openmw/mwclass/actor.hpp index 7cdee8061..6ccd552f9 100644 --- a/apps/openmw/mwclass/actor.hpp +++ b/apps/openmw/mwclass/actor.hpp @@ -31,7 +31,7 @@ namespace MWClass virtual void block(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; ///< Returns total weight of objects inside this object (including modifications from magic @@ -42,6 +42,9 @@ namespace MWClass virtual bool isActor() const; + /// Return current movement speed. + virtual float getCurrentSpeed(const MWWorld::Ptr& ptr) const; + // not implemented Actor(const Actor&); Actor& operator= (const Actor&); diff --git a/apps/openmw/mwclass/creature.cpp b/apps/openmw/mwclass/creature.cpp index 15e0fa6ab..5c5524a12 100644 --- a/apps/openmw/mwclass/creature.cpp +++ b/apps/openmw/mwclass/creature.cpp @@ -500,7 +500,7 @@ namespace MWClass 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); @@ -532,11 +532,6 @@ namespace MWClass else moveSpeed = getWalkSpeed(ptr); - const MWMechanics::Movement& movementSettings = ptr.getClass().getMovementSettings(ptr); - if (movementSettings.mIsStrafing) - moveSpeed *= 0.75f; - moveSpeed *= movementSettings.mSpeedFactor; - return moveSpeed; } diff --git a/apps/openmw/mwclass/creature.hpp b/apps/openmw/mwclass/creature.hpp index 2d7aa5a19..ca991052b 100644 --- a/apps/openmw/mwclass/creature.hpp +++ b/apps/openmw/mwclass/creature.hpp @@ -94,7 +94,7 @@ namespace MWClass virtual MWMechanics::Movement& getMovementSettings (const MWWorld::Ptr& ptr) const; ///< Return desired movement. - float getSpeed (const MWWorld::Ptr& ptr) const; + float getMaxSpeed (const MWWorld::Ptr& ptr) const; static void registerSelf(); diff --git a/apps/openmw/mwclass/npc.cpp b/apps/openmw/mwclass/npc.cpp index 0c00d3bd1..b89d79fc9 100644 --- a/apps/openmw/mwclass/npc.cpp +++ b/apps/openmw/mwclass/npc.cpp @@ -936,7 +936,7 @@ namespace MWClass 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); if (stats.isParalyzed() || stats.getKnockedDown() || stats.isDead()) @@ -979,11 +979,6 @@ namespace MWClass if(npcdata->mNpcStats.isWerewolf() && running && npcdata->mNpcStats.getDrawState() == MWMechanics::DrawState_Nothing) moveSpeed *= gmst.fWereWolfRunMult->mValue.getFloat(); - const MWMechanics::Movement& movementSettings = ptr.getClass().getMovementSettings(ptr); - if (movementSettings.mIsStrafing) - moveSpeed *= 0.75f; - moveSpeed *= movementSettings.mSpeedFactor; - return moveSpeed; } diff --git a/apps/openmw/mwclass/npc.hpp b/apps/openmw/mwclass/npc.hpp index d52afcd82..1ed4e8cae 100644 --- a/apps/openmw/mwclass/npc.hpp +++ b/apps/openmw/mwclass/npc.hpp @@ -84,8 +84,8 @@ namespace MWClass virtual std::string getScript (const MWWorld::ConstPtr& ptr) const; ///< Return name of the script attached to ptr - virtual float getSpeed (const MWWorld::Ptr& ptr) const; - ///< Return movement speed. + virtual float getMaxSpeed (const MWWorld::Ptr& ptr) const; + ///< Return maximal movement speed. virtual float getJump(const MWWorld::Ptr &ptr) const; ///< Return jump velocity (not accounting for movement) diff --git a/apps/openmw/mwmechanics/actors.cpp b/apps/openmw/mwmechanics/actors.cpp index b91b01e4a..14a2e17c9 100644 --- a/apps/openmw/mwmechanics/actors.cpp +++ b/apps/openmw/mwmechanics/actors.cpp @@ -472,9 +472,6 @@ namespace MWMechanics 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(); @@ -484,10 +481,13 @@ namespace MWMechanics 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)); + { + 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) diff --git a/apps/openmw/mwmechanics/aipackage.cpp b/apps/openmw/mwmechanics/aipackage.cpp index f7c07bde0..53e366579 100644 --- a/apps/openmw/mwmechanics/aipackage.cpp +++ b/apps/openmw/mwmechanics/aipackage.cpp @@ -144,7 +144,7 @@ bool MWMechanics::AiPackage::pathTo(const MWWorld::Ptr& actor, const osg::Vec3f& 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()); const float pointTolerance = std::max(MIN_TOLERANCE, actorTolerance); @@ -300,7 +300,7 @@ bool MWMechanics::AiPackage::checkWayIsClearForActor(const osg::Vec3f& startPoin if (canActorMoveByZAxis(actor)) 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 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) { // 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 float radius = speed / getAngularVelocity(speed); diff --git a/apps/openmw/mwmechanics/character.cpp b/apps/openmw/mwmechanics/character.cpp index 2ff8241f4..5d102f69e 100644 --- a/apps/openmw/mwmechanics/character.cpp +++ b/apps/openmw/mwmechanics/character.cpp @@ -21,6 +21,7 @@ #include +#include #include #include @@ -51,15 +52,6 @@ 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) { 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 vec(movementSettings.asVec3()); - vec.normalize(); - float analogueMult = 1.0f; if (isPlayer) { // TODO: Move this code to mwinput. // Joystick analogue movement. - float xAxis = std::abs(movementSettings.mPosition[0]); - float yAxis = std::abs(movementSettings.mPosition[1]); - analogueMult = std::max(xAxis, yAxis); + movementSettings.mSpeedFactor = std::max(std::abs(vec.x()), std::abs(vec.y())); // 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) - analogueMult *= 2.f; - - movementSettings.mSpeedFactor = analogueMult; - } + if(!isrunning && !sneak && !flying && movementSettings.mSpeedFactor <= 0.5f) + movementSettings.mSpeedFactor *= 2.f; + } else + movementSettings.mSpeedFactor = std::min(vec.length(), 1.f); + vec.normalize(); float effectiveRotation = rot.z(); static const bool turnToMovementDirection = Settings::Manager::getBool("turn to movement direction", "Game"); @@ -2007,7 +1995,7 @@ void CharacterController::update(float duration, bool animationOnly) else mAnimation->setUpperBodyYawRadians(stats.getSideMovementAngle() / 4); - speed = cls.getSpeed(mPtr); + speed = cls.getCurrentSpeed(mPtr); vec.x() *= speed; vec.y() *= speed; @@ -2077,7 +2065,7 @@ void CharacterController::update(float duration, bool animationOnly) } } fatigueLoss *= duration; - fatigueLoss *= analogueMult; + fatigueLoss *= movementSettings.mSpeedFactor; DynamicStat fatigue = cls.getCreatureStats(mPtr).getFatigue(); if (!godmode) @@ -2908,13 +2896,10 @@ void CharacterController::updateHeadTracking(float duration) zAngleRadians = std::atan2(direction.x(), direction.y()) - std::atan2(actorDirection.x(), actorDirection.y()); xAngleRadians = -std::asin(direction.z()); - wrap(zAngleRadians); - wrap(xAngleRadians); - - xAngleRadians = std::min(xAngleRadians, osg::DegreesToRadians(40.f)); - xAngleRadians = std::max(xAngleRadians, osg::DegreesToRadians(-40.f)); - zAngleRadians = std::min(zAngleRadians, osg::DegreesToRadians(30.f)); - zAngleRadians = std::max(zAngleRadians, osg::DegreesToRadians(-30.f)); + const double xLimit = osg::DegreesToRadians(40.0); + const double zLimit = osg::DegreesToRadians(30.0); + zAngleRadians = osg::clampBetween(Misc::normalizeAngle(zAngleRadians), -xLimit, xLimit); + xAngleRadians = osg::clampBetween(Misc::normalizeAngle(xAngleRadians), -zLimit, zLimit); } float factor = duration*5; diff --git a/apps/openmw/mwmechanics/movement.hpp b/apps/openmw/mwmechanics/movement.hpp index 86b970e60..57e106cde 100644 --- a/apps/openmw/mwmechanics/movement.hpp +++ b/apps/openmw/mwmechanics/movement.hpp @@ -8,8 +8,14 @@ namespace MWMechanics /// Desired movement for an actor 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]; + // Desired rotation delta (euler angles). 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; bool mIsStrafing; diff --git a/apps/openmw/mwmechanics/obstacle.cpp b/apps/openmw/mwmechanics/obstacle.cpp index 715dfecd2..88325ee7c 100644 --- a/apps/openmw/mwmechanics/obstacle.cpp +++ b/apps/openmw/mwmechanics/obstacle.cpp @@ -122,7 +122,7 @@ namespace MWMechanics 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 currentDistance = (destination - position).length(); const float movedDistance = prevDistance - currentDistance; diff --git a/apps/openmw/mwmechanics/steering.cpp b/apps/openmw/mwmechanics/steering.cpp index b08a90220..d442085ea 100644 --- a/apps/openmw/mwmechanics/steering.cpp +++ b/apps/openmw/mwmechanics/steering.cpp @@ -32,7 +32,7 @@ bool smoothTurn(const MWWorld::Ptr& actor, float targetAngleRadians, int axis, f if (absDiff < epsilonRadians) 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) diff = osg::sign(diff) * limit; diff --git a/apps/openmw/mwrender/camera.cpp b/apps/openmw/mwrender/camera.cpp index 40cc9895d..0d6ed262b 100644 --- a/apps/openmw/mwrender/camera.cpp +++ b/apps/openmw/mwrender/camera.cpp @@ -2,6 +2,7 @@ #include +#include #include #include @@ -200,7 +201,7 @@ namespace MWRender updateFocalPointOffset(duration); updatePosition(); - float speed = mTrackingPtr.getClass().getSpeed(mTrackingPtr); + float speed = mTrackingPtr.getClass().getCurrentSpeed(mTrackingPtr); speed /= (1.f + speed / 500.f); float maxDelta = 300.f * duration; mSmoothedSpeed += osg::clampBetween(speed - mSmoothedSpeed, -maxDelta, maxDelta); @@ -249,7 +250,7 @@ namespace MWRender { if (!mStandingPreviewAllowed) return; - float speed = mTrackingPtr.getClass().getSpeed(mTrackingPtr); + float speed = mTrackingPtr.getClass().getCurrentSpeed(mTrackingPtr); bool combat = mTrackingPtr.getClass().isActor() && mTrackingPtr.getClass().getCreatureStats(mTrackingPtr).getDrawState() != MWMechanics::DrawState_Nothing; bool standingStill = speed == 0 && !combat && !mFirstPersonView; @@ -396,12 +397,7 @@ namespace MWRender void Camera::setYaw(float angle) { - if (angle > osg::PI) { - angle -= osg::PI*2; - } else if (angle < -osg::PI) { - angle += osg::PI*2; - } - mYaw = angle; + mYaw = Misc::normalizeAngle(angle); } void Camera::setPitch(float angle) @@ -538,16 +534,8 @@ namespace MWRender return; } - mDeferredRotation.x() = -ptr.getRefData().getPosition().rot[0] - mPitch; - mDeferredRotation.z() = -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; + mDeferredRotation.x() = Misc::normalizeAngle(-ptr.getRefData().getPosition().rot[0] - mPitch); + mDeferredRotation.z() = Misc::normalizeAngle(-ptr.getRefData().getPosition().rot[2] - mYaw); } } diff --git a/apps/openmw/mwworld/class.cpp b/apps/openmw/mwworld/class.cpp index ad8766d06..950c8a6d4 100644 --- a/apps/openmw/mwworld/class.cpp +++ b/apps/openmw/mwworld/class.cpp @@ -159,7 +159,12 @@ namespace MWWorld 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; } diff --git a/apps/openmw/mwworld/class.hpp b/apps/openmw/mwworld/class.hpp index e82712220..445f2e986 100644 --- a/apps/openmw/mwworld/class.hpp +++ b/apps/openmw/mwworld/class.hpp @@ -172,8 +172,15 @@ namespace MWWorld ///< Return name of the script attached to ptr (default implementation: return an empty /// string). - virtual float getSpeed (const Ptr& ptr) const; - ///< Return movement speed. + virtual float getWalkSpeed(const Ptr& ptr) const; + 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; ///< Return jump velocity (not accounting for movement) @@ -182,7 +189,7 @@ namespace MWWorld ///< Return desired movement. 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, bool> getEquipmentSlots (const ConstPtr& ptr) const; ///< \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 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; }; } diff --git a/components/misc/mathutil.hpp b/components/misc/mathutil.hpp new file mode 100644 index 000000000..2f7f446b5 --- /dev/null +++ b/components/misc/mathutil.hpp @@ -0,0 +1,27 @@ +#ifndef MISC_MATHUTIL_H +#define MISC_MATHUTIL_H + +#include +#include + +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