Commit b4c69934 authored by Petr Mikheev's avatar Petr Mikheev

Improved strafe movement

parent d3bd67d7
......@@ -536,10 +536,11 @@ namespace MWClass
moveSpeed = getSwimSpeed(ptr);
else
moveSpeed = getWalkSpeed(ptr);
if(getMovementSettings(ptr).mPosition[0] != 0 && getMovementSettings(ptr).mPosition[1] == 0)
moveSpeed *= 0.75f;
moveSpeed *= ptr.getClass().getMovementSettings(ptr).mSpeedFactor;
const MWMechanics::Movement& movementSettings = ptr.getClass().getMovementSettings(ptr);
if (movementSettings.mIsStrafing)
moveSpeed *= 0.75f;
moveSpeed *= movementSettings.mSpeedFactor;
return moveSpeed;
}
......
......@@ -966,13 +966,14 @@ namespace MWClass
moveSpeed = getRunSpeed(ptr);
else
moveSpeed = getWalkSpeed(ptr);
if(getMovementSettings(ptr).mPosition[0] != 0 && getMovementSettings(ptr).mPosition[1] == 0)
moveSpeed *= 0.75f;
if(npcdata->mNpcStats.isWerewolf() && running && npcdata->mNpcStats.getDrawState() == MWMechanics::DrawState_Nothing)
moveSpeed *= gmst.fWereWolfRunMult->mValue.getFloat();
moveSpeed *= ptr.getClass().getMovementSettings(ptr).mSpeedFactor;
const MWMechanics::Movement& movementSettings = ptr.getClass().getMovementSettings(ptr);
if (movementSettings.mIsStrafing)
moveSpeed *= 0.75f;
moveSpeed *= movementSettings.mSpeedFactor;
return moveSpeed;
}
......
......@@ -1939,64 +1939,76 @@ void CharacterController::update(float duration, bool animationOnly)
bool sneak = cls.getCreatureStats(mPtr).getStance(MWMechanics::CreatureStats::Stance_Sneak) && !flying;
bool isrunning = cls.getCreatureStats(mPtr).getStance(MWMechanics::CreatureStats::Stance_Run) && !flying;
CreatureStats &stats = cls.getCreatureStats(mPtr);
Movement& movementSettings = cls.getMovementSettings(mPtr);
//Force Jump Logic
bool isMoving = (std::abs(cls.getMovementSettings(mPtr).mPosition[0]) > .5 || std::abs(cls.getMovementSettings(mPtr).mPosition[1]) > .5);
bool isMoving = (std::abs(movementSettings.mPosition[0]) > .5 || std::abs(movementSettings.mPosition[1]) > .5);
if(!inwater && !flying && solid)
{
//Force Jump
if(stats.getMovementFlag(MWMechanics::CreatureStats::Flag_ForceJump))
{
if(onground)
{
cls.getMovementSettings(mPtr).mPosition[2] = 1;
}
else
cls.getMovementSettings(mPtr).mPosition[2] = 0;
}
movementSettings.mPosition[2] = onground ? 1 : 0;
//Force Move Jump, only jump if they're otherwise moving
if(stats.getMovementFlag(MWMechanics::CreatureStats::Flag_ForceMoveJump) && isMoving)
{
if(onground)
{
cls.getMovementSettings(mPtr).mPosition[2] = 1;
}
else
cls.getMovementSettings(mPtr).mPosition[2] = 0;
}
movementSettings.mPosition[2] = onground ? 1 : 0;
}
osg::Vec3f vec(cls.getMovementSettings(mPtr).asVec3());
vec.normalize();
if(mHitState != CharState_None && mJumpState == JumpState_None)
vec = osg::Vec3f(0.f, 0.f, 0.f);
osg::Vec3f rot = cls.getRotationVector(mPtr);
osg::Vec3f vec(movementSettings.asVec3());
vec.normalize();
speed = cls.getSpeed(mPtr);
float analogueMult = 1.f;
if(isPlayer)
float analogueMult = 1.0f;
if (isPlayer)
{
// TODO: Move this code to mwinput.
// Joystick analogue movement.
float xAxis = std::abs(cls.getMovementSettings(mPtr).mPosition[0]);
float yAxis = std::abs(cls.getMovementSettings(mPtr).mPosition[1]);
analogueMult = ((xAxis > yAxis) ? xAxis : yAxis);
// If Strafing, our max speed is slower so multiply by X axis instead.
if(std::abs(vec.x()/2.0f) > std::abs(vec.y()))
analogueMult = xAxis;
float xAxis = std::abs(movementSettings.mPosition[0]);
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.
if(!isrunning && !sneak && !flying && analogueMult <= 0.5f)
analogueMult *= 2.f;
movementSettings.mSpeedFactor = analogueMult;
}
speed *= analogueMult;
float effectiveRotation = rot.z();
static const bool turnToMovementDirection = Settings::Manager::getBool("turn to movement direction", "Game");
static const float turnToMovementDirectionSpeedCoef = Settings::Manager::getFloat("turn to movement direction speed coef", "Game");
if (turnToMovementDirection && !(isPlayer && MWBase::Environment::get().getWorld()->isFirstPerson()))
{
float targetMovementAngle = vec.y() >= 0 ? std::atan2(-vec.x(), vec.y()) : std::atan2(vec.x(), -vec.y());
movementSettings.mIsStrafing = (stats.getDrawState() != MWMechanics::DrawState_Nothing || inwater)
&& std::abs(targetMovementAngle) > osg::DegreesToRadians(60.0f);
if (movementSettings.mIsStrafing)
targetMovementAngle = 0;
float delta = targetMovementAngle - stats.getSideMovementAngle();
float cosDelta = cosf(delta);
movementSettings.mSpeedFactor *= std::min(std::max(cosDelta, 0.f) + 0.3f, 1.f); // slow down when turn
float maxDelta = turnToMovementDirectionSpeedCoef * osg::PI * duration * (2.5f - cosDelta);
delta = std::min(delta, maxDelta);
delta = std::max(delta, -maxDelta);
stats.setSideMovementAngle(stats.getSideMovementAngle() + delta);
effectiveRotation += delta;
}
else
movementSettings.mIsStrafing = std::abs(vec.x()) > std::abs(vec.y()) * 2;
mAnimation->setLegsYawRadians(stats.getSideMovementAngle());
if (stats.getDrawState() == MWMechanics::DrawState_Nothing || inwater)
mAnimation->setUpperBodyYawRadians(stats.getSideMovementAngle() / 2);
else
mAnimation->setUpperBodyYawRadians(stats.getSideMovementAngle() / 4);
speed = cls.getSpeed(mPtr);
vec.x() *= speed;
vec.y() *= speed;
if(mHitState != CharState_None && mJumpState == JumpState_None)
vec = osg::Vec3f();
CharacterState movestate = CharState_None;
CharacterState idlestate = CharState_SpecialIdle;
JumpingState jumpstate = JumpState_None;
......@@ -2158,7 +2170,7 @@ void CharacterController::update(float duration, bool animationOnly)
inJump = false;
if(std::abs(vec.x()/2.0f) > std::abs(vec.y()))
if (movementSettings.mIsStrafing)
{
if(vec.x() > 0.0f)
movestate = (inwater ? (isrunning ? CharState_SwimRunRight : CharState_SwimWalkRight)
......@@ -2169,18 +2181,18 @@ void CharacterController::update(float duration, bool animationOnly)
: (sneak ? CharState_SneakLeft
: (isrunning ? CharState_RunLeft : CharState_WalkLeft)));
}
else if(vec.y() != 0.0f)
else if (vec.length2() > 0.0f)
{
if(vec.y() > 0.0f)
if (vec.y() >= 0.0f)
movestate = (inwater ? (isrunning ? CharState_SwimRunForward : CharState_SwimWalkForward)
: (sneak ? CharState_SneakForward
: (isrunning ? CharState_RunForward : CharState_WalkForward)));
else if(vec.y() < 0.0f)
else
movestate = (inwater ? (isrunning ? CharState_SwimRunBack : CharState_SwimWalkBack)
: (sneak ? CharState_SneakBack
: (isrunning ? CharState_RunBack : CharState_WalkBack)));
}
else if(rot.z() != 0.0f)
else if (effectiveRotation != 0.0f)
{
// Do not play turning animation for player if rotation speed is very slow.
// Actual threshold should take framerate in account.
......@@ -2193,9 +2205,9 @@ void CharacterController::update(float duration, bool animationOnly)
bool isFirstPlayer = isPlayer && MWBase::Environment::get().getWorld()->isFirstPerson();
if (!sneak && jumpstate == JumpState_None && !isFirstPlayer && mPtr.getClass().isBipedal(mPtr))
{
if(rot.z() > rotationThreshold)
if(effectiveRotation > rotationThreshold)
movestate = inwater ? CharState_SwimTurnRight : CharState_TurnRight;
else if(rot.z() < -rotationThreshold)
else if(effectiveRotation < -rotationThreshold)
movestate = inwater ? CharState_SwimTurnLeft : CharState_TurnLeft;
}
}
......@@ -2317,9 +2329,9 @@ void CharacterController::update(float duration, bool animationOnly)
world->queueMovement(mPtr, osg::Vec3f(0.f, 0.f, 0.f));
movement = vec;
cls.getMovementSettings(mPtr).mPosition[0] = cls.getMovementSettings(mPtr).mPosition[1] = 0;
movementSettings.mPosition[0] = movementSettings.mPosition[1] = 0;
if (movement.z() == 0.f)
cls.getMovementSettings(mPtr).mPosition[2] = 0;
movementSettings.mPosition[2] = 0;
// Can't reset jump state (mPosition[2]) here in full; we don't know for sure whether the PhysicSystem will actually handle it in this frame
// due to the fixed minimum timestep used for the physics update. It will be reset in PhysicSystem::move once the jump is handled.
......@@ -2355,15 +2367,11 @@ void CharacterController::update(float duration, bool animationOnly)
if(speed > 0.f)
{
float l = moved.length();
if((movement.x() < 0.0f && movement.x() < moved.x()*2.0f) ||
(movement.x() > 0.0f && movement.x() > moved.x()*2.0f))
if (std::abs(movement.x() - moved.x()) > std::abs(moved.x()) / 2)
moved.x() = movement.x();
if((movement.y() < 0.0f && movement.y() < moved.y()*2.0f) ||
(movement.y() > 0.0f && movement.y() > moved.y()*2.0f))
if (std::abs(movement.y() - moved.y()) > std::abs(moved.y()) / 2)
moved.y() = movement.y();
if((movement.z() < 0.0f && movement.z() < moved.z()*2.0f) ||
(movement.z() > 0.0f && movement.z() > moved.z()*2.0f))
if (std::abs(movement.z() - moved.z()) > std::abs(moved.z()) / 2)
moved.z() = movement.z();
// but keep the original speed
float newLength = moved.length();
......
......@@ -23,7 +23,7 @@ namespace MWMechanics
mKnockdown(false), mKnockdownOneFrame(false), mKnockdownOverOneFrame(false),
mHitRecovery(false), mBlock(false), mMovementFlags(0),
mFallHeight(0), mRecalcMagicka(false), mLastRestock(0,0), mGoldPool(0), mActorId(-1), mHitAttemptActorId(-1),
mDeathAnimation(-1), mTimeOfDeath(), mLevel (0)
mDeathAnimation(-1), mTimeOfDeath(), mSideMovementAngle(0), mLevel (0)
{
for (int i=0; i<4; ++i)
mAiSettings[i] = 0;
......
......@@ -80,6 +80,9 @@ namespace MWMechanics
MWWorld::TimeStamp mTimeOfDeath;
// The difference between view direction and lower body direction.
float mSideMovementAngle;
public:
typedef std::pair<int, std::string> SummonKey; // <ESM::MagicEffect index, spell ID>
private:
......@@ -298,6 +301,9 @@ namespace MWMechanics
void addCorprusSpell(const std::string& sourceId, CorprusStats& stats);
void removeCorprusSpell(const std::string& sourceId);
float getSideMovementAngle() const { return mSideMovementAngle; }
void setSideMovementAngle(float angle) { mSideMovementAngle = angle; }
};
}
......
......@@ -11,12 +11,14 @@ namespace MWMechanics
float mPosition[3];
float mRotation[3];
float mSpeedFactor;
bool mIsStrafing;
Movement()
{
mPosition[0] = mPosition[1] = mPosition[2] = 0.0f;
mRotation[0] = mRotation[1] = mRotation[2] = 0.0f;
mSpeedFactor = 1.f;
mIsStrafing = false;
}
osg::Vec3f asVec3()
......
......@@ -621,6 +621,8 @@ namespace MWRender
, mTextKeyListener(nullptr)
, mHeadYawRadians(0.f)
, mHeadPitchRadians(0.f)
, mUpperBodyYawRadians(0.f)
, mLegsYawRadians(0.f)
, mHasMagicEffects(false)
, mAlpha(1.f)
{
......@@ -1334,13 +1336,36 @@ namespace MWRender
updateEffects();
const float epsilon = 0.001f;
float yawOffset = 0;
if (mRootController)
{
bool enable = std::abs(mLegsYawRadians) > epsilon;
mRootController->setEnabled(enable);
if (enable)
{
mRootController->setRotate(osg::Quat(mLegsYawRadians, osg::Vec3f(0,0,1)));
yawOffset = mLegsYawRadians;
}
}
if (mSpineController)
{
float yaw = mUpperBodyYawRadians - yawOffset;
bool enable = std::abs(yaw) > epsilon;
mSpineController->setEnabled(enable);
if (enable)
{
mSpineController->setRotate(osg::Quat(yaw, osg::Vec3f(0,0,1)));
yawOffset = mUpperBodyYawRadians;
}
}
if (mHeadController)
{
const float epsilon = 0.001f;
bool enable = (std::abs(mHeadPitchRadians) > epsilon || std::abs(mHeadYawRadians) > epsilon);
float yaw = mHeadYawRadians - yawOffset;
bool enable = (std::abs(mHeadPitchRadians) > epsilon || std::abs(yaw) > epsilon);
mHeadController->setEnabled(enable);
if (enable)
mHeadController->setRotate(osg::Quat(mHeadPitchRadians, osg::Vec3f(1,0,0)) * osg::Quat(mHeadYawRadians, osg::Vec3f(0,0,1)));
mHeadController->setRotate(osg::Quat(mHeadPitchRadians, osg::Vec3f(1,0,0)) * osg::Quat(yaw, osg::Vec3f(0,0,1)));
}
// Scripted animations should not cause movement
......@@ -1801,13 +1826,17 @@ namespace MWRender
void Animation::addControllers()
{
mHeadController = nullptr;
NodeMap::const_iterator found = getNodeMap().find("bip01 head");
if (found == getNodeMap().end())
return;
mHeadController = addRotateController("bip01 head");
mSpineController = addRotateController("bip01 spine1");
mRootController = addRotateController("bip01");
}
osg::MatrixTransform* node = found->second;
RotateController* Animation::addRotateController(std::string bone)
{
auto iter = getNodeMap().find(bone);
if (iter == getNodeMap().end())
return nullptr;
osg::MatrixTransform* node = iter->second;
bool foundKeyframeCtrl = false;
osg::Callback* cb = node->getUpdateCallback();
......@@ -1820,13 +1849,15 @@ namespace MWRender
}
cb = cb->getNestedCallback();
}
// Without KeyframeController the orientation will not be reseted each frame, so
// RotateController shouldn't be used for such nodes.
if (!foundKeyframeCtrl)
return;
return nullptr;
mHeadController = new RotateController(mObjectRoot.get());
node->addUpdateCallback(mHeadController);
mActiveControllers.emplace_back(node, mHeadController);
RotateController* controller = new RotateController(mObjectRoot.get());
node->addUpdateCallback(controller);
mActiveControllers.emplace_back(node, controller);
return controller;
}
void Animation::setHeadPitch(float pitchRadians)
......
......@@ -267,8 +267,15 @@ protected:
TextKeyListener* mTextKeyListener;
osg::ref_ptr<RotateController> mHeadController;
osg::ref_ptr<RotateController> mSpineController;
osg::ref_ptr<RotateController> mRootController;
float mHeadYawRadians;
float mHeadPitchRadians;
float mUpperBodyYawRadians;
float mLegsYawRadians;
RotateController* addRotateController(std::string bone);
bool mHasMagicEffects;
osg::ref_ptr<SceneUtil::LightSource> mGlowLight;
......@@ -477,6 +484,12 @@ public:
virtual void setHeadYaw(float yawRadians);
virtual float getHeadPitch() const;
virtual float getHeadYaw() const;
virtual void setUpperBodyYawRadians(float v) { mUpperBodyYawRadians = v; }
virtual void setLegsYawRadians(float v) { mLegsYawRadians = v; }
virtual float getUpperBodyYawRadians() const { return mUpperBodyYawRadians; }
virtual float getLegsYawRadians() const { return mLegsYawRadians; }
virtual void setAccurateAiming(bool enabled) {}
virtual bool canBeHarvested() const { return false; }
......
......@@ -244,6 +244,9 @@ namespace MWRender
else
mViewModeToggleQueued = false;
if (mTrackingPtr.getClass().isActor())
mTrackingPtr.getClass().getCreatureStats(mTrackingPtr).setSideMovementAngle(0);
mFirstPersonView = !mFirstPersonView;
processViewChange();
}
......
......@@ -302,6 +302,12 @@ projectiles enchant multiplier = 0
# This means that unlike Morrowind you will be able to knock down actors using this effect.
uncapped damage fatigue = false
# Turn lower body to movement direction. 'true' makes diagonal movement more realistic.
turn to movement direction = false
# Turning speed multiplier. Makes difference only if 'turn to movement direction' is enabled.
turn to movement direction speed coef = 1.0
[General]
# Anisotropy reduces distortion in textures at low angles (e.g. 0 to 16).
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment