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

This commit is contained in:
mrcheko 2014-04-26 00:20:55 +04:00
parent fbd0ffe86f
commit f3626adc86
6 changed files with 65 additions and 33 deletions

View file

@ -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;
} }

View file

@ -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;

View file

@ -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))
{ {

View file

@ -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

View file

@ -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]);

View file

@ -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