mirror of
https://github.com/TES3MP/openmw-tes3mp.git
synced 2025-01-30 02:45:32 +00:00
Smooth turning; smooth stopping; combat headtracking
This commit is contained in:
parent
acdf4cb5e3
commit
71ba94a89a
7 changed files with 82 additions and 21 deletions
|
@ -473,6 +473,10 @@ namespace MWMechanics
|
|||
|
||||
void Actors::updateMovementSpeed(const MWWorld::Ptr& actor)
|
||||
{
|
||||
static const bool smoothMovement = Settings::Manager::getBool("smooth movement", "Game");
|
||||
if (smoothMovement)
|
||||
return;
|
||||
|
||||
CreatureStats &stats = actor.getClass().getCreatureStats(actor);
|
||||
MWMechanics::AiSequence& seq = stats.getAiSequence();
|
||||
|
||||
|
@ -481,9 +485,10 @@ namespace MWMechanics
|
|||
osg::Vec3f targetPos = seq.getActivePackage().getDestination();
|
||||
osg::Vec3f actorPos = actor.getRefData().getPosition().asVec3();
|
||||
float distance = (targetPos - actorPos).length();
|
||||
|
||||
if (distance < DECELERATE_DISTANCE)
|
||||
{
|
||||
float speedCoef = std::max(0.7f, 0.1f * (distance/64.f + 2.f));
|
||||
float speedCoef = std::max(0.7f, 0.2f + 0.8f * distance / DECELERATE_DISTANCE);
|
||||
auto& movement = actor.getClass().getMovementSettings(actor);
|
||||
movement.mPosition[0] *= speedCoef;
|
||||
movement.mPosition[1] *= speedCoef;
|
||||
|
@ -1769,14 +1774,12 @@ namespace MWMechanics
|
|||
|
||||
MWMechanics::CreatureStats& stats = iter->first.getClass().getCreatureStats(iter->first);
|
||||
bool firstPersonPlayer = isPlayer && world->isFirstPerson();
|
||||
bool inCombatOrPursue = stats.getAiSequence().isInCombat() || stats.getAiSequence().hasPackage(AiPackageTypeId::Pursue);
|
||||
|
||||
// 1. Unconsious actor can not track target
|
||||
// 2. Actors in combat and pursue mode do not bother to headtrack
|
||||
// 3. Player character does not use headtracking in the 1st-person view
|
||||
if (!stats.getKnockedDown() &&
|
||||
!stats.getAiSequence().isInCombat() &&
|
||||
!stats.getAiSequence().hasPackage(AiPackageTypeId::Pursue) &&
|
||||
!firstPersonPlayer)
|
||||
if (!stats.getKnockedDown() && !firstPersonPlayer && !inCombatOrPursue)
|
||||
{
|
||||
for(PtrActorMap::iterator it(mActors.begin()); it != mActors.end(); ++it)
|
||||
{
|
||||
|
@ -1786,6 +1789,17 @@ namespace MWMechanics
|
|||
}
|
||||
}
|
||||
|
||||
if (!stats.getKnockedDown() && !isPlayer && inCombatOrPursue)
|
||||
{
|
||||
// Actors in combat and pursue mode always look at their target.
|
||||
for (const auto& package : stats.getAiSequence())
|
||||
{
|
||||
headTrackTarget = package->getTarget();
|
||||
if (!headTrackTarget.isEmpty())
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
ctrl->setHeadTrackTarget(headTrackTarget);
|
||||
}
|
||||
|
||||
|
|
|
@ -23,7 +23,7 @@ bool MWMechanics::AiBreathe::execute (const MWWorld::Ptr& actor, CharacterContro
|
|||
actorClass.getCreatureStats(actor).setMovementFlag(CreatureStats::Flag_Run, true);
|
||||
|
||||
actorClass.getMovementSettings(actor).mPosition[1] = 1;
|
||||
smoothTurn(actor, -180, 0);
|
||||
smoothTurn(actor, -osg::PI / 2, 0);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
|
|
@ -5,6 +5,7 @@
|
|||
#include <components/esm/loadmgef.hpp>
|
||||
#include <components/detournavigator/navigator.hpp>
|
||||
#include <components/misc/coordinateconverter.hpp>
|
||||
#include <components/settings/settings.hpp>
|
||||
|
||||
#include "../mwbase/world.hpp"
|
||||
#include "../mwbase/environment.hpp"
|
||||
|
@ -87,6 +88,7 @@ bool MWMechanics::AiPackage::pathTo(const MWWorld::Ptr& actor, const osg::Vec3f&
|
|||
//... But AI processing distance may increase in the future.
|
||||
if (isNearInactiveCell(position))
|
||||
{
|
||||
actor.getClass().getMovementSettings(actor).mPosition[0] = 0;
|
||||
actor.getClass().getMovementSettings(actor).mPosition[1] = 0;
|
||||
world->updateActorPath(actor, mPathFinder.getPath(), halfExtents, position, dest);
|
||||
return false;
|
||||
|
@ -169,12 +171,34 @@ bool MWMechanics::AiPackage::pathTo(const MWWorld::Ptr& actor, const osg::Vec3f&
|
|||
}
|
||||
|
||||
// turn to next path point by X,Z axes
|
||||
zTurn(actor, mPathFinder.getZAngleToNext(position.x(), position.y()));
|
||||
float zAngleToNext = mPathFinder.getZAngleToNext(position.x(), position.y());
|
||||
zTurn(actor, zAngleToNext);
|
||||
smoothTurn(actor, mPathFinder.getXAngleToNext(position.x(), position.y(), position.z()), 0);
|
||||
|
||||
const auto destination = mPathFinder.getPath().empty() ? dest : mPathFinder.getPath().front();
|
||||
mObstacleCheck.update(actor, destination, duration);
|
||||
|
||||
static const bool smoothMovement = Settings::Manager::getBool("smooth movement", "Game");
|
||||
if (smoothMovement)
|
||||
{
|
||||
const float smoothTurnReservedDist = 150;
|
||||
auto& movement = actor.getClass().getMovementSettings(actor);
|
||||
float distToNextSqr = osg::Vec2f(destination.x() - position.x(), destination.y() - position.y()).length2();
|
||||
float diffAngle = zAngleToNext - actor.getRefData().getPosition().rot[2];
|
||||
if (std::cos(diffAngle) < -0.1)
|
||||
movement.mPosition[0] = movement.mPosition[1] = 0;
|
||||
else if (distToNextSqr > smoothTurnReservedDist * smoothTurnReservedDist)
|
||||
{ // Go forward (and slowly turn towards the next path point)
|
||||
movement.mPosition[0] = 0;
|
||||
movement.mPosition[1] = 1;
|
||||
}
|
||||
else
|
||||
{ // Next path point is near, so use diagonal movement to follow the path precisely.
|
||||
movement.mPosition[0] = std::sin(diffAngle);
|
||||
movement.mPosition[1] = std::max(std::cos(diffAngle), 0.f);
|
||||
}
|
||||
}
|
||||
|
||||
// handle obstacles on the way
|
||||
evadeObstacles(actor);
|
||||
|
||||
|
|
|
@ -96,6 +96,7 @@ namespace MWMechanics
|
|||
|
||||
void stopMovement(const MWWorld::Ptr& actor)
|
||||
{
|
||||
actor.getClass().getMovementSettings(actor).mPosition[0] = 0;
|
||||
actor.getClass().getMovementSettings(actor).mPosition[1] = 0;
|
||||
}
|
||||
|
||||
|
|
|
@ -88,6 +88,24 @@ namespace
|
|||
const auto halfExtents = world->getHalfExtents(actor);
|
||||
return 2.0 * halfExtents.z();
|
||||
}
|
||||
|
||||
// Returns true if turn in `p2` is less than 10 degrees and all the 3 points are almost on one line.
|
||||
bool isAlmostStraight(const osg::Vec3f& p1, const osg::Vec3f& p2, const osg::Vec3f& p3, float pointTolerance) {
|
||||
osg::Vec3f v1 = p1 - p2;
|
||||
osg::Vec3f v3 = p3 - p2;
|
||||
v1.z() = v3.z() = 0;
|
||||
float dotProduct = v1.x() * v3.x() + v1.y() * v3.y();
|
||||
float crossProduct = v1.x() * v3.y() - v1.y() * v3.x();
|
||||
|
||||
// Check that the angle between v1 and v3 is less or equal than 10 degrees.
|
||||
static const float cos170 = std::cos(osg::PI / 180 * 170);
|
||||
bool checkAngle = dotProduct <= cos170 * v1.length() * v3.length();
|
||||
|
||||
// Check that distance from p2 to the line (p1, p3) is less or equal than `pointTolerance`.
|
||||
bool checkDist = std::abs(crossProduct) <= pointTolerance * (p3 - p1).length() * 2;
|
||||
|
||||
return checkAngle && checkDist;
|
||||
}
|
||||
}
|
||||
|
||||
namespace MWMechanics
|
||||
|
@ -286,6 +304,11 @@ namespace MWMechanics
|
|||
while (mPath.size() > 1 && sqrDistanceIgnoreZ(mPath.front(), position) < pointTolerance * pointTolerance)
|
||||
mPath.pop_front();
|
||||
|
||||
while (mPath.size() > 2 && isAlmostStraight(mPath[0], mPath[1], mPath[2], pointTolerance))
|
||||
mPath.erase(mPath.begin() + 1);
|
||||
if (mPath.size() > 1 && isAlmostStraight(position, mPath[0], mPath[1], pointTolerance))
|
||||
mPath.pop_front();
|
||||
|
||||
if (mPath.size() == 1 && sqrDistanceIgnoreZ(mPath.front(), position) < destinationTolerance * destinationTolerance)
|
||||
mPath.pop_front();
|
||||
}
|
||||
|
|
|
@ -1,5 +1,8 @@
|
|||
#include "steering.hpp"
|
||||
|
||||
#include <components/misc/mathutil.hpp>
|
||||
#include <components/settings/settings.hpp>
|
||||
|
||||
#include "../mwworld/class.hpp"
|
||||
#include "../mwworld/ptr.hpp"
|
||||
|
||||
|
@ -12,19 +15,8 @@ namespace MWMechanics
|
|||
|
||||
bool smoothTurn(const MWWorld::Ptr& actor, float targetAngleRadians, int axis, float epsilonRadians)
|
||||
{
|
||||
float currentAngle (actor.getRefData().getPosition().rot[axis]);
|
||||
float diff (targetAngleRadians - currentAngle);
|
||||
if (std::abs(diff) >= osg::DegreesToRadians(180.f))
|
||||
{
|
||||
if (diff >= 0)
|
||||
{
|
||||
diff = diff - osg::DegreesToRadians(360.f);
|
||||
}
|
||||
else
|
||||
{
|
||||
diff = osg::DegreesToRadians(360.f) + diff;
|
||||
}
|
||||
}
|
||||
MWMechanics::Movement& movement = actor.getClass().getMovementSettings(actor);
|
||||
float diff = Misc::normalizeAngle(targetAngleRadians - actor.getRefData().getPosition().rot[axis]);
|
||||
float absDiff = std::abs(diff);
|
||||
|
||||
// The turning animation actually moves you slightly, so the angle will be wrong again.
|
||||
|
@ -33,10 +25,14 @@ bool smoothTurn(const MWWorld::Ptr& actor, float targetAngleRadians, int axis, f
|
|||
return true;
|
||||
|
||||
float limit = getAngularVelocity(actor.getClass().getMaxSpeed(actor)) * MWBase::Environment::get().getFrameDuration();
|
||||
static const bool smoothMovement = Settings::Manager::getBool("smooth movement", "Game");
|
||||
if (smoothMovement)
|
||||
limit *= std::min(absDiff / osg::PI + 0.1, 0.5);
|
||||
|
||||
if (absDiff > limit)
|
||||
diff = osg::sign(diff) * limit;
|
||||
|
||||
actor.getClass().getMovementSettings(actor).mRotation[axis] = diff;
|
||||
movement.mRotation[axis] = diff;
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
|
@ -325,6 +325,9 @@ uncapped damage fatigue = false
|
|||
# Turn lower body to movement direction. 'true' makes diagonal movement more realistic.
|
||||
turn to movement direction = false
|
||||
|
||||
# Makes all movements of NPCs and player more smooth.
|
||||
smooth movement = false
|
||||
|
||||
# Makes player swim a bit upward from the line of sight.
|
||||
swim upward correction = false
|
||||
|
||||
|
|
Loading…
Reference in a new issue