mirror of
https://github.com/TES3MP/openmw-tes3mp.git
synced 2025-02-28 07:09:42 +00:00
Improved strafe movement
This commit is contained in:
parent
d3bd67d747
commit
b4c699348f
10 changed files with 142 additions and 71 deletions
|
@ -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());
|
||||
osg::Vec3f rot = cls.getRotationVector(mPtr);
|
||||
osg::Vec3f vec(movementSettings.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);
|
||||
|
||||
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;
|
||||
mHeadController = addRotateController("bip01 head");
|
||||
mSpineController = addRotateController("bip01 spine1");
|
||||
mRootController = addRotateController("bip01");
|
||||
}
|
||||
|
||||
NodeMap::const_iterator found = getNodeMap().find("bip01 head");
|
||||
if (found == getNodeMap().end())
|
||||
return;
|
||||
|
||||
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).
|
||||
|
|
Loading…
Reference in a new issue