remake of z-moving in combat for flying/swimming enemies

actorid
mrcheko 11 years ago
parent fbd0ffe86f
commit f3626adc86

@ -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…
Cancel
Save