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();
|
||||
}
|
||||
|
||||
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;
|
||||
// distance at which actor pays more attention to decide whether to shortcut or stick to pathgrid
|
||||
const float PATHFIND_CAUTION_DIST = 500.0f;
|
||||
|
@ -79,7 +86,6 @@ namespace MWMechanics
|
|||
mStrike(false),
|
||||
mCombatMove(false),
|
||||
mMovement(),
|
||||
mTargetAngle(0),
|
||||
mForceNoShortcut(false),
|
||||
mShortcutFailPos()
|
||||
{
|
||||
|
@ -108,13 +114,18 @@ namespace MWMechanics
|
|||
}
|
||||
|
||||
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;
|
||||
actor.getClass().getCreatureStats(actor).setAttackingOrSpell(mStrike);
|
||||
|
@ -225,7 +236,7 @@ namespace MWMechanics
|
|||
|
||||
bool isStuck = false;
|
||||
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;
|
||||
|
||||
mLastPos = pos;
|
||||
|
@ -235,31 +246,28 @@ namespace MWMechanics
|
|||
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)))
|
||||
{
|
||||
float zToTarget = vTargetPos.z - pos.pos[2];
|
||||
|
||||
mMovement.mPosition[1] = sqrt(distToTarget*distToTarget - zToTarget*zToTarget); // XY-plane vec length
|
||||
mMovement.mPosition[2] = zToTarget;
|
||||
// determine vertical angle to target
|
||||
mMovement.mRotation[0] = getXAngleToDir(vDirToTarget, distToTarget);
|
||||
}
|
||||
|
||||
if(distToTarget < rangeMelee || (distToTarget <= rangeCloseUp && mFollowTarget && !isStuck) )
|
||||
{
|
||||
//Melee and Close-up combat
|
||||
vDirToTarget.z = 0;
|
||||
mTargetAngle = getZAngleToDir(vDirToTarget, distToTarget);
|
||||
mMovement.mRotation[2] = getZAngleToDir(vDirToTarget, distToTarget);
|
||||
|
||||
if (mFollowTarget && distToTarget > rangeMelee)
|
||||
{
|
||||
//Close-up combat: just run up on target
|
||||
if(!canMoveByZ) mMovement.mPosition[1] = 1;
|
||||
mMovement.mPosition[1] = 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
//Melee: stop running and attack
|
||||
mMovement.mPosition[1] = 0;
|
||||
if(canMoveByZ) mMovement.mPosition[2] = 0;
|
||||
|
||||
// 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);
|
||||
|
||||
if(mMovement.mPosition[0] || mMovement.mPosition[1])
|
||||
|
@ -304,15 +312,15 @@ namespace MWMechanics
|
|||
if(speed == 0.0f) speed = cls.getSpeed(actor);
|
||||
// maximum dist before pit/obstacle for actor to avoid them depending on his speed
|
||||
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);
|
||||
}
|
||||
|
||||
// don't use pathgrid when actor can move in 3 dimensions
|
||||
if(canMoveByZ) preferShortcut = true;
|
||||
|
||||
if(preferShortcut)
|
||||
{
|
||||
mTargetAngle = getZAngleToDir(vDirToTarget, distToTarget);
|
||||
mMovement.mRotation[2] = getZAngleToDir(vDirToTarget, distToTarget);
|
||||
mForceNoShortcut = false;
|
||||
mShortcutFailPos.pos[0] = mShortcutFailPos.pos[1] = mShortcutFailPos.pos[2] = 0;
|
||||
mPathFinder.clearPath();
|
||||
|
@ -338,12 +346,12 @@ namespace MWMechanics
|
|||
// get point just before target
|
||||
std::list<ESM::Pathgrid::Point>::iterator pntIter = --mPathFinder.getPath().end();
|
||||
--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(distToTarget <= (vTargetPos - vBeforeTarget).length())
|
||||
{
|
||||
mTargetAngle = getZAngleToDir(vDirToTarget, distToTarget);
|
||||
mMovement.mRotation[2] = getZAngleToDir(vDirToTarget, distToTarget);
|
||||
preferShortcut = true;
|
||||
}
|
||||
}
|
||||
|
@ -352,13 +360,13 @@ namespace MWMechanics
|
|||
if(!preferShortcut)
|
||||
{
|
||||
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
|
||||
mTargetAngle = getZAngleToDir(vDirToTarget, distToTarget);
|
||||
mMovement.mRotation[2] = getZAngleToDir(vDirToTarget, distToTarget);
|
||||
}
|
||||
}
|
||||
|
||||
if(!canMoveByZ) mMovement.mPosition[1] = 1;
|
||||
mMovement.mPosition[1] = 1;
|
||||
mReadyToAttack = false;
|
||||
}
|
||||
|
||||
|
@ -392,8 +400,6 @@ namespace MWMechanics
|
|||
}
|
||||
}
|
||||
|
||||
actor.getClass().getMovementSettings(actor) = mMovement;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
|
@ -36,10 +36,6 @@ namespace MWMechanics
|
|||
// when mCombatMove is true
|
||||
float mTimerCombatMove;
|
||||
|
||||
// the z rotation angle (degrees) we want to reach
|
||||
// used every frame when mRotate is true
|
||||
float mTargetAngle;
|
||||
|
||||
bool mReadyToAttack, mStrike;
|
||||
bool mFollowTarget;
|
||||
bool mCombatMove;
|
||||
|
|
|
@ -1014,11 +1014,11 @@ void CharacterController::update(float duration)
|
|||
if(mHitState != CharState_None && mJumpState == JumpState_None)
|
||||
vec = Ogre::Vector3(0.0f);
|
||||
Ogre::Vector3 rot = cls.getRotationVector(mPtr);
|
||||
|
||||
mMovementSpeed = cls.getSpeed(mPtr);
|
||||
|
||||
vec.x *= mMovementSpeed;
|
||||
vec.y *= mMovementSpeed;
|
||||
if(inwater || flying) vec.z *= mMovementSpeed;
|
||||
|
||||
CharacterState movestate = CharState_None;
|
||||
CharacterState idlestate = CharState_SpecialIdle;
|
||||
|
@ -1085,8 +1085,7 @@ void CharacterController::update(float duration)
|
|||
fatigue.setCurrent(fatigue.getCurrent() - fatigueLoss, fatigue.getCurrent() < 0);
|
||||
cls.getCreatureStats(mPtr).setFatigue(fatigue);
|
||||
|
||||
// kind of hack, reason - creatures can move along z when in water/flying
|
||||
if(sneak || ((inwater || flying) && mPtr.getRefData().getHandle() == "player"))
|
||||
if(sneak || inwater || flying)
|
||||
vec.z = 0.0f;
|
||||
|
||||
if (inwater || flying)
|
||||
|
@ -1121,7 +1120,7 @@ void CharacterController::update(float duration)
|
|||
vec.y *= mult;
|
||||
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.
|
||||
float z = cls.getJump(mPtr);
|
||||
|
@ -1183,7 +1182,7 @@ void CharacterController::update(float duration)
|
|||
{
|
||||
if(!(vec.z > 0.0f))
|
||||
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))
|
||||
{
|
||||
|
|
|
@ -67,7 +67,7 @@ namespace MWMechanics
|
|||
/** Synchronize new path with old one to avoid visiting 1 waypoint 2 times
|
||||
@note
|
||||
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).
|
||||
@param path - old path
|
||||
@return true if such point was found and deleted
|
||||
|
|
|
@ -10,6 +10,35 @@
|
|||
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)
|
||||
{
|
||||
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?
|
||||
bool zTurn(const MWWorld::Ptr& actor, Ogre::Radian targetAngle);
|
||||
|
||||
bool smoothTurn(const MWWorld::Ptr& actor, Ogre::Radian targetAngle, int axis);
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
Loading…
Reference in a new issue