forked from mirror/openmw-tes3mp
remake of z-moving in combat for flying/swimming enemies
This commit is contained in:
parent
fbd0ffe86f
commit
f3626adc86
6 changed files with 65 additions and 33 deletions
|
@ -38,6 +38,13 @@ namespace
|
||||||
return Ogre::Radian( Ogre::Math::ACos(dir.y / len) * sgn(Ogre::Math::ASin(dir.x / len)) ).valueDegrees();
|
return Ogre::Radian( Ogre::Math::ACos(dir.y / len) * sgn(Ogre::Math::ASin(dir.x / len)) ).valueDegrees();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
float getXAngleToDir(const Ogre::Vector3& dir, float dirLen = 0.0f)
|
||||||
|
{
|
||||||
|
float len = (dirLen >= 0.0f)? dirLen : dir.length();
|
||||||
|
return Ogre::Radian(-Ogre::Math::ASin(dir.z / len)).valueDegrees();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
const float PATHFIND_Z_REACH = 50.0f;
|
const float PATHFIND_Z_REACH = 50.0f;
|
||||||
// distance at which actor pays more attention to decide whether to shortcut or stick to pathgrid
|
// distance at which actor pays more attention to decide whether to shortcut or stick to pathgrid
|
||||||
const float PATHFIND_CAUTION_DIST = 500.0f;
|
const float PATHFIND_CAUTION_DIST = 500.0f;
|
||||||
|
@ -79,7 +86,6 @@ namespace MWMechanics
|
||||||
mStrike(false),
|
mStrike(false),
|
||||||
mCombatMove(false),
|
mCombatMove(false),
|
||||||
mMovement(),
|
mMovement(),
|
||||||
mTargetAngle(0),
|
|
||||||
mForceNoShortcut(false),
|
mForceNoShortcut(false),
|
||||||
mShortcutFailPos()
|
mShortcutFailPos()
|
||||||
{
|
{
|
||||||
|
@ -108,13 +114,18 @@ namespace MWMechanics
|
||||||
}
|
}
|
||||||
|
|
||||||
actor.getClass().getMovementSettings(actor) = mMovement;
|
actor.getClass().getMovementSettings(actor) = mMovement;
|
||||||
|
actor.getClass().getMovementSettings(actor).mRotation[0] = 0;
|
||||||
|
actor.getClass().getMovementSettings(actor).mRotation[2] = 0;
|
||||||
|
|
||||||
if(actor.getRefData().getPosition().rot[2] != mTargetAngle)
|
if(mMovement.mRotation[2] != 0)
|
||||||
{
|
{
|
||||||
zTurn(actor, Ogre::Degree(mTargetAngle));
|
if(zTurn(actor, Ogre::Degree(mMovement.mRotation[2]))) mMovement.mRotation[2] = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(mMovement.mRotation[0] != 0)
|
||||||
|
{
|
||||||
|
if(smoothTurn(actor, Ogre::Degree(mMovement.mRotation[0]), 0)) mMovement.mRotation[0] = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
mTimerAttack -= duration;
|
mTimerAttack -= duration;
|
||||||
actor.getClass().getCreatureStats(actor).setAttackingOrSpell(mStrike);
|
actor.getClass().getCreatureStats(actor).setAttackingOrSpell(mStrike);
|
||||||
|
@ -225,7 +236,7 @@ namespace MWMechanics
|
||||||
|
|
||||||
bool isStuck = false;
|
bool isStuck = false;
|
||||||
float speed = 0.0f;
|
float speed = 0.0f;
|
||||||
if(mMovement.mPosition[1] && (Ogre::Vector3(mLastPos.pos) - vActorPos).length() < (speed = cls.getSpeed(actor)) / 10.0f)
|
if(mMovement.mPosition[1] && (Ogre::Vector3(mLastPos.pos) - vActorPos).length() < (speed = cls.getSpeed(actor)) / 10.0f)
|
||||||
isStuck = true;
|
isStuck = true;
|
||||||
|
|
||||||
mLastPos = pos;
|
mLastPos = pos;
|
||||||
|
@ -235,31 +246,28 @@ namespace MWMechanics
|
||||||
if(canMoveByZ = ((actor.getClass().isNpc() || actor.getClass().canSwim(actor)) && MWBase::Environment::get().getWorld()->isSwimming(actor))
|
if(canMoveByZ = ((actor.getClass().isNpc() || actor.getClass().canSwim(actor)) && MWBase::Environment::get().getWorld()->isSwimming(actor))
|
||||||
|| (actor.getClass().canFly(actor) && MWBase::Environment::get().getWorld()->isFlying(actor)))
|
|| (actor.getClass().canFly(actor) && MWBase::Environment::get().getWorld()->isFlying(actor)))
|
||||||
{
|
{
|
||||||
float zToTarget = vTargetPos.z - pos.pos[2];
|
// determine vertical angle to target
|
||||||
|
mMovement.mRotation[0] = getXAngleToDir(vDirToTarget, distToTarget);
|
||||||
mMovement.mPosition[1] = sqrt(distToTarget*distToTarget - zToTarget*zToTarget); // XY-plane vec length
|
|
||||||
mMovement.mPosition[2] = zToTarget;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if(distToTarget < rangeMelee || (distToTarget <= rangeCloseUp && mFollowTarget && !isStuck) )
|
if(distToTarget < rangeMelee || (distToTarget <= rangeCloseUp && mFollowTarget && !isStuck) )
|
||||||
{
|
{
|
||||||
//Melee and Close-up combat
|
//Melee and Close-up combat
|
||||||
vDirToTarget.z = 0;
|
vDirToTarget.z = 0;
|
||||||
mTargetAngle = getZAngleToDir(vDirToTarget, distToTarget);
|
mMovement.mRotation[2] = getZAngleToDir(vDirToTarget, distToTarget);
|
||||||
|
|
||||||
if (mFollowTarget && distToTarget > rangeMelee)
|
if (mFollowTarget && distToTarget > rangeMelee)
|
||||||
{
|
{
|
||||||
//Close-up combat: just run up on target
|
//Close-up combat: just run up on target
|
||||||
if(!canMoveByZ) mMovement.mPosition[1] = 1;
|
mMovement.mPosition[1] = 1;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
//Melee: stop running and attack
|
//Melee: stop running and attack
|
||||||
mMovement.mPosition[1] = 0;
|
mMovement.mPosition[1] = 0;
|
||||||
if(canMoveByZ) mMovement.mPosition[2] = 0;
|
|
||||||
|
|
||||||
// When attacking with a weapon, choose between slash, thrust or chop
|
// When attacking with a weapon, choose between slash, thrust or chop
|
||||||
if (actor.getClass().hasInventoryStore(actor))
|
if (mStrike && actor.getClass().hasInventoryStore(actor))
|
||||||
chooseBestAttack(weapon, mMovement);
|
chooseBestAttack(weapon, mMovement);
|
||||||
|
|
||||||
if(mMovement.mPosition[0] || mMovement.mPosition[1])
|
if(mMovement.mPosition[0] || mMovement.mPosition[1])
|
||||||
|
@ -304,15 +312,15 @@ namespace MWMechanics
|
||||||
if(speed == 0.0f) speed = cls.getSpeed(actor);
|
if(speed == 0.0f) speed = cls.getSpeed(actor);
|
||||||
// maximum dist before pit/obstacle for actor to avoid them depending on his speed
|
// maximum dist before pit/obstacle for actor to avoid them depending on his speed
|
||||||
float maxAvoidDist = tReaction * speed + speed / MAX_VEL_ANGULAR.valueRadians() * 2;
|
float maxAvoidDist = tReaction * speed + speed / MAX_VEL_ANGULAR.valueRadians() * 2;
|
||||||
//if(actor.getRefData().getPosition().rot[2] != mTargetAngle)
|
|
||||||
preferShortcut = checkWayIsClear(vActorPos, vTargetPos, distToTarget > maxAvoidDist*1.5? maxAvoidDist : maxAvoidDist/2);
|
preferShortcut = checkWayIsClear(vActorPos, vTargetPos, distToTarget > maxAvoidDist*1.5? maxAvoidDist : maxAvoidDist/2);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// don't use pathgrid when actor can move in 3 dimensions
|
||||||
if(canMoveByZ) preferShortcut = true;
|
if(canMoveByZ) preferShortcut = true;
|
||||||
|
|
||||||
if(preferShortcut)
|
if(preferShortcut)
|
||||||
{
|
{
|
||||||
mTargetAngle = getZAngleToDir(vDirToTarget, distToTarget);
|
mMovement.mRotation[2] = getZAngleToDir(vDirToTarget, distToTarget);
|
||||||
mForceNoShortcut = false;
|
mForceNoShortcut = false;
|
||||||
mShortcutFailPos.pos[0] = mShortcutFailPos.pos[1] = mShortcutFailPos.pos[2] = 0;
|
mShortcutFailPos.pos[0] = mShortcutFailPos.pos[1] = mShortcutFailPos.pos[2] = 0;
|
||||||
mPathFinder.clearPath();
|
mPathFinder.clearPath();
|
||||||
|
@ -338,12 +346,12 @@ namespace MWMechanics
|
||||||
// get point just before target
|
// get point just before target
|
||||||
std::list<ESM::Pathgrid::Point>::iterator pntIter = --mPathFinder.getPath().end();
|
std::list<ESM::Pathgrid::Point>::iterator pntIter = --mPathFinder.getPath().end();
|
||||||
--pntIter;
|
--pntIter;
|
||||||
Ogre::Vector3 vBeforeTarget = Ogre::Vector3(pntIter->mX, pntIter->mY, pntIter->mZ);
|
Ogre::Vector3 vBeforeTarget = Ogre::Vector3(pntIter->mX, pntIter->mY, pntIter->mZ);
|
||||||
|
|
||||||
// if current actor pos is closer to target then last point of path (excluding target itself) then go straight on target
|
// if current actor pos is closer to target then last point of path (excluding target itself) then go straight on target
|
||||||
if(distToTarget <= (vTargetPos - vBeforeTarget).length())
|
if(distToTarget <= (vTargetPos - vBeforeTarget).length())
|
||||||
{
|
{
|
||||||
mTargetAngle = getZAngleToDir(vDirToTarget, distToTarget);
|
mMovement.mRotation[2] = getZAngleToDir(vDirToTarget, distToTarget);
|
||||||
preferShortcut = true;
|
preferShortcut = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -352,13 +360,13 @@ namespace MWMechanics
|
||||||
if(!preferShortcut)
|
if(!preferShortcut)
|
||||||
{
|
{
|
||||||
if(!mPathFinder.getPath().empty())
|
if(!mPathFinder.getPath().empty())
|
||||||
mTargetAngle = mPathFinder.getZAngleToNext(pos.pos[0], pos.pos[1]);
|
mMovement.mRotation[2] = mPathFinder.getZAngleToNext(pos.pos[0], pos.pos[1]);
|
||||||
else
|
else
|
||||||
mTargetAngle = getZAngleToDir(vDirToTarget, distToTarget);
|
mMovement.mRotation[2] = getZAngleToDir(vDirToTarget, distToTarget);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if(!canMoveByZ) mMovement.mPosition[1] = 1;
|
mMovement.mPosition[1] = 1;
|
||||||
mReadyToAttack = false;
|
mReadyToAttack = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -392,8 +400,6 @@ namespace MWMechanics
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
actor.getClass().getMovementSettings(actor) = mMovement;
|
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -36,10 +36,6 @@ namespace MWMechanics
|
||||||
// when mCombatMove is true
|
// when mCombatMove is true
|
||||||
float mTimerCombatMove;
|
float mTimerCombatMove;
|
||||||
|
|
||||||
// the z rotation angle (degrees) we want to reach
|
|
||||||
// used every frame when mRotate is true
|
|
||||||
float mTargetAngle;
|
|
||||||
|
|
||||||
bool mReadyToAttack, mStrike;
|
bool mReadyToAttack, mStrike;
|
||||||
bool mFollowTarget;
|
bool mFollowTarget;
|
||||||
bool mCombatMove;
|
bool mCombatMove;
|
||||||
|
|
|
@ -1014,11 +1014,11 @@ void CharacterController::update(float duration)
|
||||||
if(mHitState != CharState_None && mJumpState == JumpState_None)
|
if(mHitState != CharState_None && mJumpState == JumpState_None)
|
||||||
vec = Ogre::Vector3(0.0f);
|
vec = Ogre::Vector3(0.0f);
|
||||||
Ogre::Vector3 rot = cls.getRotationVector(mPtr);
|
Ogre::Vector3 rot = cls.getRotationVector(mPtr);
|
||||||
|
|
||||||
mMovementSpeed = cls.getSpeed(mPtr);
|
mMovementSpeed = cls.getSpeed(mPtr);
|
||||||
|
|
||||||
vec.x *= mMovementSpeed;
|
vec.x *= mMovementSpeed;
|
||||||
vec.y *= mMovementSpeed;
|
vec.y *= mMovementSpeed;
|
||||||
if(inwater || flying) vec.z *= mMovementSpeed;
|
|
||||||
|
|
||||||
CharacterState movestate = CharState_None;
|
CharacterState movestate = CharState_None;
|
||||||
CharacterState idlestate = CharState_SpecialIdle;
|
CharacterState idlestate = CharState_SpecialIdle;
|
||||||
|
@ -1085,8 +1085,7 @@ void CharacterController::update(float duration)
|
||||||
fatigue.setCurrent(fatigue.getCurrent() - fatigueLoss, fatigue.getCurrent() < 0);
|
fatigue.setCurrent(fatigue.getCurrent() - fatigueLoss, fatigue.getCurrent() < 0);
|
||||||
cls.getCreatureStats(mPtr).setFatigue(fatigue);
|
cls.getCreatureStats(mPtr).setFatigue(fatigue);
|
||||||
|
|
||||||
// kind of hack, reason - creatures can move along z when in water/flying
|
if(sneak || inwater || flying)
|
||||||
if(sneak || ((inwater || flying) && mPtr.getRefData().getHandle() == "player"))
|
|
||||||
vec.z = 0.0f;
|
vec.z = 0.0f;
|
||||||
|
|
||||||
if (inwater || flying)
|
if (inwater || flying)
|
||||||
|
@ -1121,7 +1120,7 @@ void CharacterController::update(float duration)
|
||||||
vec.y *= mult;
|
vec.y *= mult;
|
||||||
vec.z = 0.0f;
|
vec.z = 0.0f;
|
||||||
}
|
}
|
||||||
else if(!inwater && !flying && vec.z > 0.0f && mJumpState == JumpState_None)
|
else if(vec.z > 0.0f && mJumpState == JumpState_None)
|
||||||
{
|
{
|
||||||
// Started a jump.
|
// Started a jump.
|
||||||
float z = cls.getJump(mPtr);
|
float z = cls.getJump(mPtr);
|
||||||
|
@ -1183,7 +1182,7 @@ void CharacterController::update(float duration)
|
||||||
{
|
{
|
||||||
if(!(vec.z > 0.0f))
|
if(!(vec.z > 0.0f))
|
||||||
mJumpState = JumpState_None;
|
mJumpState = JumpState_None;
|
||||||
if(!inwater && !flying) vec.z = 0.0f;
|
vec.z = 0.0f;
|
||||||
|
|
||||||
if(std::abs(vec.x/2.0f) > std::abs(vec.y))
|
if(std::abs(vec.x/2.0f) > std::abs(vec.y))
|
||||||
{
|
{
|
||||||
|
|
|
@ -67,7 +67,7 @@ namespace MWMechanics
|
||||||
/** Synchronize new path with old one to avoid visiting 1 waypoint 2 times
|
/** Synchronize new path with old one to avoid visiting 1 waypoint 2 times
|
||||||
@note
|
@note
|
||||||
If the first point is chosen as the nearest one
|
If the first point is chosen as the nearest one
|
||||||
the cituation can occure when the 1st point of the new path is undesirable
|
the situation can occur when the 1st point of the new path is undesirable
|
||||||
(i.e. the 2nd point of new path == the 1st point of old path).
|
(i.e. the 2nd point of new path == the 1st point of old path).
|
||||||
@param path - old path
|
@param path - old path
|
||||||
@return true if such point was found and deleted
|
@return true if such point was found and deleted
|
||||||
|
|
|
@ -10,6 +10,35 @@
|
||||||
namespace MWMechanics
|
namespace MWMechanics
|
||||||
{
|
{
|
||||||
|
|
||||||
|
bool smoothTurn(const MWWorld::Ptr& actor, Ogre::Radian targetAngle, int axis)
|
||||||
|
{
|
||||||
|
Ogre::Radian currentAngle (actor.getRefData().getPosition().rot[axis]);
|
||||||
|
Ogre::Radian diff (targetAngle - currentAngle);
|
||||||
|
if (diff >= Ogre::Degree(180))
|
||||||
|
{
|
||||||
|
// Turning the other way would be a better idea
|
||||||
|
diff = diff-Ogre::Degree(360);
|
||||||
|
}
|
||||||
|
else if (diff <= Ogre::Degree(-180))
|
||||||
|
{
|
||||||
|
diff = Ogre::Degree(360)-diff;
|
||||||
|
}
|
||||||
|
Ogre::Radian absDiff = Ogre::Math::Abs(diff);
|
||||||
|
|
||||||
|
// The turning animation actually moves you slightly, so the angle will be wrong again.
|
||||||
|
// Use epsilon to prevent jerkiness.
|
||||||
|
const Ogre::Degree epsilon (0.5);
|
||||||
|
if (absDiff < epsilon)
|
||||||
|
return true;
|
||||||
|
|
||||||
|
Ogre::Radian limit = MAX_VEL_ANGULAR * MWBase::Environment::get().getFrameDuration();
|
||||||
|
if (absDiff > limit)
|
||||||
|
diff = Ogre::Math::Sign(diff) * limit;
|
||||||
|
|
||||||
|
actor.getClass().getMovementSettings(actor).mRotation[axis] = diff.valueRadians();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
bool zTurn(const MWWorld::Ptr& actor, Ogre::Radian targetAngle)
|
bool zTurn(const MWWorld::Ptr& actor, Ogre::Radian targetAngle)
|
||||||
{
|
{
|
||||||
Ogre::Radian currentAngle (actor.getRefData().getPosition().rot[2]);
|
Ogre::Radian currentAngle (actor.getRefData().getPosition().rot[2]);
|
||||||
|
|
|
@ -17,6 +17,8 @@ const Ogre::Radian MAX_VEL_ANGULAR(10);
|
||||||
/// @return have we reached the target angle?
|
/// @return have we reached the target angle?
|
||||||
bool zTurn(const MWWorld::Ptr& actor, Ogre::Radian targetAngle);
|
bool zTurn(const MWWorld::Ptr& actor, Ogre::Radian targetAngle);
|
||||||
|
|
||||||
|
bool smoothTurn(const MWWorld::Ptr& actor, Ogre::Radian targetAngle, int axis);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
Loading…
Reference in a new issue