diff --git a/apps/openmw/mwmechanics/character.cpp b/apps/openmw/mwmechanics/character.cpp index cf09fa6f7..aed638895 100644 --- a/apps/openmw/mwmechanics/character.cpp +++ b/apps/openmw/mwmechanics/character.cpp @@ -1914,6 +1914,7 @@ void CharacterController::update(float duration, bool animationOnly) mTimeUntilWake -= duration; bool isPlayer = mPtr == MWMechanics::getPlayer(); + bool isFirstPersonPlayer = isPlayer && MWBase::Environment::get().getWorld()->isFirstPerson(); bool godmode = isPlayer && MWBase::Environment::get().getWorld()->getGodModeState(); float scale = mPtr.getCellRef().getScale(); @@ -1977,7 +1978,7 @@ void CharacterController::update(float duration, bool animationOnly) float effectiveRotation = rot.z(); static const bool turnToMovementDirection = Settings::Manager::getBool("turn to movement direction", "Game"); - if (turnToMovementDirection && !(isPlayer && MWBase::Environment::get().getWorld()->isFirstPerson())) + if (turnToMovementDirection && !isFirstPersonPlayer) { 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) @@ -2206,8 +2207,7 @@ void CharacterController::update(float duration, bool animationOnly) // It seems only bipedal actors use turning animations. // Also do not use turning animations in the first-person view and when sneaking. - bool isFirstPlayer = isPlayer && MWBase::Environment::get().getWorld()->isFirstPerson(); - if (!sneak && jumpstate == JumpState_None && !isFirstPlayer && mPtr.getClass().isBipedal(mPtr)) + if (!sneak && jumpstate == JumpState_None && !isFirstPersonPlayer && mPtr.getClass().isBipedal(mPtr)) { if(effectiveRotation > rotationThreshold) movestate = inwater ? CharState_SwimTurnRight : CharState_TurnRight; @@ -2231,6 +2231,26 @@ void CharacterController::update(float duration, bool animationOnly) sndMgr->playSound3D(mPtr, sound, 1.f, 1.f, MWSound::Type::Foot, MWSound::PlayMode::NoPlayerLocal); } + if (turnToMovementDirection) + { + float targetSwimmingPitch; + if (inwater && vec.y() != 0 && !isFirstPersonPlayer && !movementSettings.mIsStrafing) + targetSwimmingPitch = -mPtr.getRefData().getPosition().rot[0]; + else + targetSwimmingPitch = 0; + float maxSwimPitchDelta = 3.0f * duration; + float swimmingPitch = mAnimation->getBodyPitchRadians(); + swimmingPitch += osg::clampBetween(targetSwimmingPitch - swimmingPitch, -maxSwimPitchDelta, maxSwimPitchDelta); + mAnimation->setBodyPitchRadians(swimmingPitch); + } + if (inwater && isPlayer && !isFirstPersonPlayer) + { + static const float swimUpwardCoef = Settings::Manager::getFloat("swim upward coef", "Game"); + static const float swimForwardCoef = sqrtf(1.0f - swimUpwardCoef * swimUpwardCoef); + vec.z() = std::abs(vec.y()) * swimUpwardCoef; + vec.y() *= swimForwardCoef; + } + // Player can not use smooth turning as NPCs, so we play turning animation a bit to avoid jittering if (isPlayer) { diff --git a/apps/openmw/mwrender/animation.cpp b/apps/openmw/mwrender/animation.cpp index 688937ce9..3835e26de 100644 --- a/apps/openmw/mwrender/animation.cpp +++ b/apps/openmw/mwrender/animation.cpp @@ -623,6 +623,7 @@ namespace MWRender , mHeadPitchRadians(0.f) , mUpperBodyYawRadians(0.f) , mLegsYawRadians(0.f) + , mBodyPitchRadians(0.f) , mHasMagicEffects(false) , mAlpha(1.f) { @@ -1340,11 +1341,11 @@ namespace MWRender float yawOffset = 0; if (mRootController) { - bool enable = std::abs(mLegsYawRadians) > epsilon; + bool enable = std::abs(mLegsYawRadians) > epsilon || std::abs(mBodyPitchRadians) > epsilon; mRootController->setEnabled(enable); if (enable) { - mRootController->setRotate(osg::Quat(mLegsYawRadians, osg::Vec3f(0,0,1))); + mRootController->setRotate(osg::Quat(mLegsYawRadians, osg::Vec3f(0,0,1)) * osg::Quat(mBodyPitchRadians, osg::Vec3f(1,0,0))); yawOffset = mLegsYawRadians; } } diff --git a/apps/openmw/mwrender/animation.hpp b/apps/openmw/mwrender/animation.hpp index 564952a90..a04a3f999 100644 --- a/apps/openmw/mwrender/animation.hpp +++ b/apps/openmw/mwrender/animation.hpp @@ -273,6 +273,7 @@ protected: float mHeadPitchRadians; float mUpperBodyYawRadians; float mLegsYawRadians; + float mBodyPitchRadians; RotateController* addRotateController(std::string bone); @@ -489,6 +490,8 @@ public: virtual void setLegsYawRadians(float v) { mLegsYawRadians = v; } virtual float getUpperBodyYawRadians() const { return mUpperBodyYawRadians; } virtual float getLegsYawRadians() const { return mLegsYawRadians; } + virtual void setBodyPitchRadians(float v) { mBodyPitchRadians = v; } + virtual float getBodyPitchRadians() const { return mBodyPitchRadians; } virtual void setAccurateAiming(bool enabled) {} virtual bool canBeHarvested() const { return false; } diff --git a/apps/openmw/mwrender/creatureanimation.cpp b/apps/openmw/mwrender/creatureanimation.cpp index 8e7f30687..489a7a987 100644 --- a/apps/openmw/mwrender/creatureanimation.cpp +++ b/apps/openmw/mwrender/creatureanimation.cpp @@ -273,7 +273,7 @@ osg::Vec3f CreatureWeaponAnimation::runAnimation(float duration) { osg::Vec3f ret = Animation::runAnimation(duration); - WeaponAnimation::configureControllers(mPtr.getRefData().getPosition().rot[0]); + WeaponAnimation::configureControllers(mPtr.getRefData().getPosition().rot[0] + getBodyPitchRadians()); return ret; } diff --git a/apps/openmw/mwrender/npcanimation.cpp b/apps/openmw/mwrender/npcanimation.cpp index c6a0b15bd..468938d22 100644 --- a/apps/openmw/mwrender/npcanimation.cpp +++ b/apps/openmw/mwrender/npcanimation.cpp @@ -747,7 +747,7 @@ osg::Vec3f NpcAnimation::runAnimation(float timepassed) mFirstPersonNeckController->setOffset(mFirstPersonOffset); } - WeaponAnimation::configureControllers(mPtr.getRefData().getPosition().rot[0]); + WeaponAnimation::configureControllers(mPtr.getRefData().getPosition().rot[0] + getBodyPitchRadians()); return ret; } diff --git a/docs/source/reference/modding/settings/game.rst b/docs/source/reference/modding/settings/game.rst index 9cac845a5..8d0b0dfc1 100644 --- a/docs/source/reference/modding/settings/game.rst +++ b/docs/source/reference/modding/settings/game.rst @@ -331,8 +331,19 @@ If enabled then the character turns lower body to the direction of movement. Upp This setting can only be configured by editing the settings configuration file. +swim upward coef +---------------- + +:Type: floating point +:Range: -1.0 to 1.0 +:Default: 0.0 + +Makes player swim a bit upward (or downward in case of negative value) from the line of sight. Intended to make simpler swimming without diving. Recommened range of values is from 0.0 to 0.2. + +This setting can only be configured by editing the settings configuration file. + trainers training skills based on base skill ------------------------ +-------------------------------------------- :Type: boolean :Range: True/False diff --git a/files/settings-default.cfg b/files/settings-default.cfg index 494b06092..b044bdd5c 100644 --- a/files/settings-default.cfg +++ b/files/settings-default.cfg @@ -310,6 +310,9 @@ uncapped damage fatigue = false # Turn lower body to movement direction. 'true' makes diagonal movement more realistic. turn to movement direction = false +# Makes player swim a bit upward (or downward in case of negative value) from the line of sight. +swim upward coef = 0.0 + # Make the training skills proposed by a trainer based on its base attribute instead of its modified ones trainers training skills based on base skill = false