@ -31,10 +31,52 @@ namespace
//chooses an attack depending on probability to avoid uniformity
void chooseBestAttack ( const ESM : : Weapon * weapon , MWMechanics : : Movement & movement ) ;
float getZAngleToDir ( const Ogre : : Vector3 & dir , float dirLen = 0.0f )
{
float len = ( dirLen > 0.0f ) ? dirLen : dir . length ( ) ;
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 ;
// distance after which actor (failed previously to shortcut) will try again
const float PATHFIND_SHORTCUT_RETRY_DIST = 300.0f ;
// cast up-down ray with some offset from actor position to check for pits/obstacles on the way to target;
// magnitude of pits/obstacles is defined by PATHFIND_Z_REACH
bool checkWayIsClear ( const Ogre : : Vector3 & from , const Ogre : : Vector3 & to , float offset )
{
if ( ( to - from ) . length ( ) > = PATHFIND_CAUTION_DIST | | abs ( from . z - to . z ) < = PATHFIND_Z_REACH )
{
Ogre : : Vector3 dir = to - from ;
dir . z = 0 ;
dir . normalise ( ) ;
float verticalOffset = 200 ; // instead of '200' here we want the height of the actor
Ogre : : Vector3 _from = from + dir * offset + Ogre : : Vector3 : : UNIT_Z * verticalOffset ;
// cast up-down ray and find height in world space of hit
float h = _from . z - MWBase : : Environment : : get ( ) . getWorld ( ) - > getDistToNearestRayHit ( _from , - Ogre : : Vector3 : : UNIT_Z , verticalOffset + PATHFIND_Z_REACH + 1 ) ;
if ( abs ( from . z - h ) < = PATHFIND_Z_REACH )
return true ;
}
return false ;
}
}
namespace MWMechanics
{
static const float MAX_ATTACK_DURATION = 0.35f ;
static const float DOOR_CHECK_INTERVAL = 1.5f ; // same as AiWander
// NOTE: MIN_DIST_TO_DOOR_SQUARED is defined in obstacle.hpp
@ -45,16 +87,16 @@ namespace MWMechanics
mTimerCombatMove ( 0 ) ,
mFollowTarget ( false ) ,
mReadyToAttack ( false ) ,
m Strike ( false ) ,
m Attack ( false ) ,
mCombatMove ( false ) ,
mBackOffDoor ( false ) ,
mRotate ( false ) ,
mMovement ( ) ,
mForceNoShortcut ( false ) ,
mShortcutFailPos ( ) ,
mBackOffDoor ( false ) ,
mCell ( NULL ) ,
mDoorIter ( actor . getCell ( ) - > get < ESM : : Door > ( ) . mList . end ( ) ) ,
mDoors ( actor . getCell ( ) - > get < ESM : : Door > ( ) ) ,
mDoorCheckDuration ( 0 ) ,
mTargetAngle ( 0 )
mDoorCheckDuration ( 0 )
{
}
@ -125,17 +167,23 @@ namespace MWMechanics
mCombatMove = false ;
}
}
actor . getClass ( ) . getMovementSettings ( actor ) = mMovement ;
actor . getClass ( ) . getMovementSettings ( actor ) . mRotation [ 0 ] = 0 ;
actor . getClass ( ) . getMovementSettings ( actor ) . mRotation [ 2 ] = 0 ;
if ( mRotate )
if ( mMovement . mRotation [ 2 ] ! = 0 )
{
if ( zTurn ( actor , Ogre : : Degree ( mMovement . mRotation [ 2 ] ) ) ) mMovement . mRotation [ 2 ] = 0 ;
}
if ( mMovement . mRotation [ 0 ] ! = 0 )
{
if ( zTurn ( actor , Ogre : : Degree ( mTargetAngle ) ) )
mRotate = false ;
if ( smoothTurn ( actor , Ogre : : Degree ( mMovement . mRotation [ 0 ] ) , 0 ) ) mMovement . mRotation [ 0 ] = 0 ;
}
mTimerAttack - = duration ;
actor . getClass ( ) . getCreatureStats ( actor ) . setAttackingOrSpell ( m Strike ) ;
actor . getClass ( ) . getCreatureStats ( actor ) . setAttackingOrSpell ( m Attack ) ;
float tReaction = 0.25f ;
if ( mTimerReact < tReaction )
@ -145,7 +193,7 @@ namespace MWMechanics
}
//Update with period = tReaction
mTimerReact = 0 ;
bool cellChange = mCell & & ( actor . getCell ( ) ! = mCell ) ;
@ -156,16 +204,16 @@ namespace MWMechanics
//actual attacking logic
//TODO: Some skills affect period of strikes.For berserk-like style period ~ 0.25f
float attack Period = 1.0f ;
float attack s Period = 1.0f ;
if ( mReadyToAttack )
{
if ( mTimerAttack < = - attack Period)
if ( mTimerAttack < = - attack s Period)
{
//TODO: should depend on time between 'start' to 'min attack'
//for better controlling of NPCs' attack strength.
//Also it seems that this time is different for slash/thrust/chop
mTimerAttack = 0.35f * static_cast < float > ( rand ( ) ) / RAND_MAX ;
m Strike = true ;
mTimerAttack = MAX_ATTACK_DURATION * static_cast < float > ( rand ( ) ) / RAND_MAX ;
m Attack = true ;
//say a provoking combat phrase
if ( actor . getClass ( ) . isNpc ( ) )
@ -180,30 +228,31 @@ namespace MWMechanics
}
}
else if ( mTimerAttack < = 0 )
m Strike = false ;
m Attack = false ;
}
else
{
mTimerAttack = - attack Period;
m Strike = false ;
mTimerAttack = - attack s Period;
m Attack = false ;
}
const MWWorld : : Class & cls = actor . getClass ( ) ;
const MWWorld : : Class & a ctorC ls = actor . getClass ( ) ;
const ESM : : Weapon * weapon = NULL ;
MWMechanics : : WeaponType weaptype ;
float weapRange , weapSpeed = 1.0f ;
actor . get Cla ss( ) . getCreatureStats ( actor ) . setMovementFlag ( CreatureStats : : Flag_Run , true ) ;
actor Cls. getCreatureStats ( actor ) . setMovementFlag ( CreatureStats : : Flag_Run , true ) ;
if ( actor . getClass ( ) . hasInventoryStore ( actor ) )
// Get weapon characteristics
if ( actorCls . hasInventoryStore ( actor ) )
{
MWMechanics : : DrawState_ state = actor . get Cla ss( ) . getCreatureStats ( actor ) . getDrawState ( ) ;
MWMechanics : : DrawState_ state = actor Cls. getCreatureStats ( actor ) . getDrawState ( ) ;
if ( state = = MWMechanics : : DrawState_Spell | | state = = MWMechanics : : DrawState_Nothing )
actor . get Cla ss( ) . getCreatureStats ( actor ) . setDrawState ( MWMechanics : : DrawState_Weapon ) ;
actor Cls. getCreatureStats ( actor ) . setDrawState ( MWMechanics : : DrawState_Weapon ) ;
//Get weapon speed and range
MWWorld : : ContainerStoreIterator weaponSlot =
MWMechanics : : getActiveWeapon ( cls. getCreatureStats ( actor ) , cls. getInventoryStore ( actor ) , & weaptype ) ;
MWMechanics : : getActiveWeapon ( a ctorC ls. getCreatureStats ( actor ) , a ctorC ls. getInventoryStore ( actor ) , & weaptype ) ;
if ( weaptype = = WeapType_HandToHand )
{
@ -225,36 +274,27 @@ namespace MWMechanics
weapRange = 150 ; //TODO: use true attack range (the same problem in Creature::hit)
}
ESM : : Position pos = actor . getRefData ( ) . getPosition ( ) ;
/*
* Some notes on meanings of variables :
*
* range Melee :
* range Attack :
*
* - Distance where attack using the actor ' s weapon is possible
* - longer for ranged weapons ( obviously ? ) vs . melee weapons
* - Distance where attack using the actor ' s weapon is possible :
* longer for ranged weapons ( obviously ? ) vs . melee weapons
* - Determined by weapon ' s reach parameter ; hardcoded value
* for ranged weapon and for creatures
* - Once within this distance mFollowTarget is triggered
* ( TODO : check whether the follow logic still works for ranged
* weapons , since rangeCloseup is set to zero )
* - TODO : The variable name is confusing . It was ok when AiCombat only
* had melee weapons but now that ranged weapons are supported that is
* no longer the case . It should really be renamed to something
* like rangeStrike - alternatively , keep this name for melee
* weapons and use a different variable for tracking ranged weapon
* distance ( rangeRanged maybe ? )
*
* range Closeup :
* rangeFollow :
*
* - Applies to melee weapons or hand to hand only ( or creatures without
* weapons )
* - Distance a little further away from the actor ' s weapon strike
* i . e . rangeCloseup > rangeMelee for melee weapons
* ( the variable names make this simple concept counter - intuitive ,
* something like rangeMelee > rangeStrike may be better )
* - Distance a little further away than the actor ' s weapon reach
* i . e . rangeFollow > rangeAttack for melee weapons
* - Hardcoded value ( 0 for ranged weapons )
* - Once the target gets beyond this distance mFollowTarget is cleared
* and a path to the target needs to be found
* - TODO : Possibly rename this variable to rangeMelee or even rangeFollow
*
* mFollowTarget :
*
@ -263,58 +303,72 @@ namespace MWMechanics
* available , since the default path without pathgrids is direct to
* target even if LOS is not achieved )
*/
float rangeMelee ;
float rangeCloseUp ;
float rangeAttack ;
float rangeFollow ;
bool distantCombat = false ;
if ( weaptype = = WeapType_BowAndArrow | | weaptype = = WeapType_Crossbow | | weaptype = = WeapType_Thrown )
if ( weaptype = = WeapType_BowAndArrow | | weaptype = = WeapType_Crossbow | | weaptype = = WeapType_Thrown )
{
range Melee = 1000 ; // TODO: should depend on archer skill
range CloseUp = 0 ; //doesn't needed when attacking from distance
range Attack = 1000 ; // TODO: should depend on archer skill
range Follow = 0 ; // not needed in ranged combat
distantCombat = true ;
}
else
{
range Melee = weapRange ;
range CloseUp = 300 ;
range Attack = weapRange ;
range Follow = 300 ;
}
ESM : : Position pos = actor . getRefData ( ) . getPosition ( ) ;
Ogre : : Vector3 vActorPos ( pos . pos ) ;
Ogre : : Vector3 vTargetPos ( mTarget . getRefData ( ) . getPosition ( ) . pos ) ;
Ogre : : Vector3 vDirToTarget = vTargetPos - vActorPos ;
bool isStuck = false ;
float speed = 0.0f ;
if ( mMovement . mPosition [ 1 ] & & ( Ogre : : Vector3 ( mLastPos . pos ) - vActorPos ) . length ( ) < ( speed = actorCls . getSpeed ( actor ) ) / 10.0f )
isStuck = true ;
mLastPos = pos ;
// check if actor can move along z-axis
bool canMoveByZ = ( actorCls . canSwim ( actor ) & & MWBase : : Environment : : get ( ) . getWorld ( ) - > isSwimming ( actor ) )
| | MWBase : : Environment : : get ( ) . getWorld ( ) - > isFlying ( actor ) ;
Ogre : : Vector3 vStart ( pos . pos [ 0 ] , pos . pos [ 1 ] , pos . pos [ 2 ] ) ;
ESM : : Position targetPos = mTarget . getRefData ( ) . getPosition ( ) ;
Ogre : : Vector3 vDest ( targetPos . pos [ 0 ] , targetPos . pos [ 1 ] , targetPos . pos [ 2 ] ) ;
Ogre : : Vector3 vDir = vDest - vStart ;
float distBetween = vDir . length ( ) ;
// determine vertical angle to target
// if actor can move along z-axis it will control movement dir
// if can't - it will control correct aiming
mMovement . mRotation [ 0 ] = getXAngleToDir ( vDirToTarget ) ;
vDirToTarget . z = 0 ;
float distToTarget = vDirToTarget . length ( ) ;
// (within strike dist) || (not quite strike dist while following)
if ( distBetween < rangeMelee | | ( distBetween < = rangeCloseUp & & mFollowTarget ) )
if ( dist ToTarget < rangeAttack | | ( distToTarget < = rangeFollow & & mFollowTarget & & ! isStuck ) )
{
//Melee and Close-up combat
vDir . z = 0 ;
float dirLen = vDir . length ( ) ;
mTargetAngle = Ogre : : Radian ( Ogre : : Math : : ACos ( vDir . y / dirLen ) * sgn ( Ogre : : Math : : ASin ( vDir . x / dirLen ) ) ) . valueDegrees ( ) ;
mRotate = true ;
mMovement . mRotation [ 2 ] = getZAngleToDir ( vDirToTarget , distToTarget ) ;
//bool LOS = MWBase::Environment::get().getWorld()->getLOS(actor, mTarget);
// (not quite strike dist while following)
if ( mFollowTarget & & dist Between > rangeMelee )
if ( mFollowTarget & & distToTarget > rangeAttack )
{
//Close-up combat: just run up on target
mMovement . mPosition [ 1 ] = 1 ;
}
else // (within strike dist)
{
//Melee: stop running and attack
mMovement . mPosition [ 1 ] = 0 ;
// When attacking with a weapon, choose between slash, thrust or chop
if ( actor . getClass ( ) . hasInventoryStore ( actor ) )
chooseBestAttack ( weapon , mMovement ) ;
// set slash/thrust/chop attack
if ( mAttack & & ! distantCombat ) chooseBestAttack ( weapon , mMovement ) ;
if ( mMovement . mPosition [ 0 ] | | mMovement . mPosition [ 1 ] )
{
mTimerCombatMove = 0.1f + 0.1f * static_cast < float > ( rand ( ) ) / RAND_MAX ;
mCombatMove = true ;
}
else if ( actor . getClass ( ) . isNpc ( ) & & ( ! distantCombat | | ( distantCombat & & rangeMelee / 5 ) ) )
// only NPCs are smart enough to use dodge movements
else if ( actorCls . isNpc ( ) & & ( ! distantCombat | | ( distantCombat & & distToTarget < rangeAttack / 2 ) ) )
{
//apply sideway movement (kind of dodging) with some probability
if ( static_cast < float > ( rand ( ) ) / RAND_MAX < 0.25 )
@ -325,7 +379,7 @@ namespace MWMechanics
}
}
if ( distantCombat & & dist Between < rangeMelee / 4 )
if ( distantCombat & & dist ToTarget < rangeAttack / 4 )
{
mMovement . mPosition [ 1 ] = - 1 ;
}
@ -335,56 +389,105 @@ namespace MWMechanics
mFollowTarget = true ;
}
}
else
else // remote pathfinding
{
//target is at far distance: build path to target
mFollowTarget = false ;
buildNewPath ( actor ) ; //may fail to build a path, check before use
bool preferShortcut = false ;
bool inLOS = MWBase : : Environment : : get ( ) . getWorld ( ) - > getLOS ( actor , mTarget ) ;
if ( mReadyToAttack ) isStuck = false ;
// check if shortcut is available
if ( ! isStuck
& & ( ! mForceNoShortcut
| | ( Ogre : : Vector3 ( mShortcutFailPos . pos ) - vActorPos ) . length ( ) > = PATHFIND_SHORTCUT_RETRY_DIST )
& & inLOS )
{
if ( speed = = 0.0f ) speed = actorCls . 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 ; // *2 - for reliability
preferShortcut = checkWayIsClear ( vActorPos , vTargetPos , distToTarget > maxAvoidDist * 1.5 ? maxAvoidDist : maxAvoidDist / 2 ) ;
}
//delete visited path node
mPathFinder . checkPathCompleted ( pos . pos [ 0 ] , pos . pos [ 1 ] , pos . pos [ 2 ] ) ;
// don't use pathgrid when actor can move in 3 dimensions
if ( canMoveByZ ) preferShortcut = true ;
//if no new path leave mTargetAngle unchanged
if ( ! mPathFinder . getPath ( ) . empty ( ) )
if ( preferShortcut )
{
//try shortcut
if ( vDir . length ( ) < mPathFinder . getDistToNext ( pos . pos [ 0 ] , pos . pos [ 1 ] , pos . pos [ 2 ] ) & & MWBase : : Environment : : get ( ) . getWorld ( ) - > getLOS ( actor , mTarget ) )
mTargetAngle = Ogre : : Radian ( Ogre : : Math : : ACos ( vDir . y / vDir . length ( ) ) * sgn ( Ogre : : Math : : ASin ( vDir . x / vDir . length ( ) ) ) ) . valueDegrees ( ) ;
else
mTargetAngle = mPathFinder . getZAngleToNext ( pos . pos [ 0 ] , pos . pos [ 1 ] ) ;
mRotate = true ;
mMovement . mRotation [ 2 ] = getZAngleToDir ( vDirToTarget , distToTarget ) ;
mForceNoShortcut = false ;
mShortcutFailPos . pos [ 0 ] = mShortcutFailPos . pos [ 1 ] = mShortcutFailPos . pos [ 2 ] = 0 ;
mPathFinder . clearPath ( ) ;
}
else // if shortcut failed stick to path grid
{
if ( ! isStuck & & mShortcutFailPos . pos [ 0 ] = = 0.0f & & mShortcutFailPos . pos [ 1 ] = = 0.0f & & mShortcutFailPos . pos [ 2 ] = = 0.0f )
{
mForceNoShortcut = true ;
mShortcutFailPos = pos ;
}
mFollowTarget = false ;
buildNewPath ( actor ) ; //may fail to build a path, check before use
//delete visited path node
mPathFinder . checkWaypoint ( pos . pos [ 0 ] , pos . pos [ 1 ] , pos . pos [ 2 ] ) ;
// This works on the borders between the path grid and areas with no waypoints.
if ( inLOS & & mPathFinder . getPath ( ) . size ( ) > 1 )
{
// get point just before target
std : : list < ESM : : Pathgrid : : Point > : : const_iterator pntIter = - - mPathFinder . getPath ( ) . end ( ) ;
- - pntIter ;
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 ( ) )
{
mMovement . mRotation [ 2 ] = getZAngleToDir ( vDirToTarget , distToTarget ) ;
preferShortcut = true ;
}
}
// if there is no new path, then go straight on target
if ( ! preferShortcut )
{
if ( ! mPathFinder . getPath ( ) . empty ( ) )
mMovement . mRotation [ 2 ] = mPathFinder . getZAngleToNext ( pos . pos [ 0 ] , pos . pos [ 1 ] ) ;
else
mMovement . mRotation [ 2 ] = getZAngleToDir ( vDirToTarget , distToTarget ) ;
}
}
mMovement . mPosition [ 1 ] = 1 ;
mReadyToAttack = false ;
}
if ( distBetween > rangeMelee )
if ( dist ToTarget > rangeAttack & & ! distantCombat )
{
//special run attack; it shouldn't affect melee combat tactics
if ( actor . getClass ( ) . getMovementSettings ( actor ) . mPosition [ 1 ] = = 1 )
if ( actor Cls. getMovementSettings ( actor ) . mPosition [ 1 ] = = 1 )
{
//check if actor can overcome the distance = distToTarget - attackerWeapRange
//less than in time of playing weapon anim from 'start' to 'hit' tags (t_swing)
//then start attacking
float speed1 = cls . getSpeed ( actor ) ;
float speed1 = a ctorC ls. getSpeed ( actor ) ;
float speed2 = mTarget . getClass ( ) . getSpeed ( mTarget ) ;
if ( mTarget . getClass ( ) . getMovementSettings ( mTarget ) . mPosition [ 0 ] = = 0
& & mTarget . getClass ( ) . getMovementSettings ( mTarget ) . mPosition [ 1 ] = = 0 )
speed2 = 0 ;
float s1 = distBetween - weapRange ;
float s1 = dist ToTarget - weapRange ;
float t = s1 / speed1 ;
float s2 = speed2 * t ;
float t_swing = 0.17f / weapSpeed ; //instead of 0.17 should be the time of playing weapon anim from 'start' to 'hit' tags
float t_swing = ( MAX_ATTACK_DURATION / 2 ) / weapSpeed ; //instead of 0.17 should be the time of playing weapon anim from 'start' to 'hit' tags
if ( t + s2 / speed1 < = t_swing )
{
mReadyToAttack = true ;
if ( mTimerAttack < = - attackPeriod )
if ( mTimerAttack < = - attack s Period)
{
mTimerAttack = 0.3f * static_cast < float > ( rand ( ) ) / RAND_MAX ;
mStrike = true ;
mTimerAttack = MAX_ATTACK_DURATION * static_cast < float > ( rand ( ) ) / RAND_MAX ;
m Attack = true ;
}
}
}
@ -394,7 +497,7 @@ namespace MWMechanics
// coded at 250ms or 1/4 second
//
// TODO: Add a parameter to vary DURATION_SAME_SPOT?
if ( ( dist Between > rangeMelee | | mFollowTarget ) & &
if ( ( dist ToTarget > rangeAttack | | mFollowTarget ) & &
mObstacleCheck . check ( actor , tReaction ) ) // check if evasive action needed
{
// first check if we're walking into a door
@ -406,12 +509,11 @@ namespace MWMechanics
// Check all the doors in this cell
mDoors = cell - > get < ESM : : Door > ( ) ; // update
mDoorIter = mDoors . mList . begin ( ) ;
Ogre : : Vector3 actorPos ( actor . getRefData ( ) . getPosition ( ) . pos ) ;
for ( ; mDoorIter ! = mDoors . mList . end ( ) ; + + mDoorIter )
{
MWWorld : : LiveCellRef < ESM : : Door > & ref = * mDoorIter ;
float minSqr = 1.3 * 1.3 * MIN_DIST_TO_DOOR_SQUARED ; // for legibility
if ( a ctorPos. squaredDistance ( Ogre : : Vector3 ( ref . mRef . mPos . pos ) ) < minSqr & &
if ( vA ctorPos. squaredDistance ( Ogre : : Vector3 ( ref . mRef . mPos . pos ) ) < minSqr & &
ref . mData . getLocalRotation ( ) . rot [ 2 ] < 0.4f ) // even small opening
{
//std::cout<<"closed door id \""<<ref.mRef.mRefID<<"\""<<std::endl;
@ -441,11 +543,10 @@ namespace MWMechanics
}
MWWorld : : LiveCellRef < ESM : : Door > & ref = * mDoorIter ;
Ogre : : Vector3 actorPos ( actor . getRefData ( ) . getPosition ( ) . pos ) ;
float minSqr = 1.6 * 1.6 * MIN_DIST_TO_DOOR_SQUARED ; // for legibility
// TODO: add reaction to checking open doors
if ( mBackOffDoor & &
a ctorPos. squaredDistance ( Ogre : : Vector3 ( ref . mRef . mPos . pos ) ) < minSqr )
vA ctorPos. squaredDistance ( Ogre : : Vector3 ( ref . mRef . mPos . pos ) ) < minSqr )
{
mMovement . mPosition [ 1 ] = - 0.2 ; // back off, but slowly
}
@ -454,47 +555,38 @@ namespace MWMechanics
ref . mData . getLocalRotation ( ) . rot [ 2 ] > = 1 )
{
mDoorIter = mDoors . mList . end ( ) ;
mBackOffDoor = false ;
mBackOffDoor = false ;
//std::cout<<"open door id \""<<ref.mRef.mRefID<<"\""<<std::endl;
mMovement . mPosition [ 1 ] = 1 ;
}
else
{
mMovement . mPosition [ 1 ] = 1 ; // FIXME: oscillation?
}
actor . getClass ( ) . getMovementSettings ( actor ) = mMovement ;
// these lines break ranged combat distance keeping
//else
//{
// mMovement.mPosition[1] = 1; // FIXME: oscillation?
//}
return false ;
}
void AiCombat : : buildNewPath ( const MWWorld : : Ptr & actor )
{
//Construct path to target
ESM : : Pathgrid : : Point dest ;
dest . mX = mTarget . getRefData ( ) . getPosition ( ) . pos [ 0 ] ;
dest . mY = mTarget . getRefData ( ) . getPosition ( ) . pos [ 1 ] ;
dest . mZ = mTarget . getRefData ( ) . getPosition ( ) . pos [ 2 ] ;
Ogre : : Vector3 newPathTarget = Ogre : : Vector3 ( dest . mX , dest . mY , dest . mZ ) ;
float dist = - 1 ; //hack to indicate first time, to construct a new path
Ogre : : Vector3 newPathTarget = Ogre : : Vector3 ( mTarget . getRefData ( ) . getPosition ( ) . pos ) ;
float dist ;
if ( ! mPathFinder . getPath ( ) . empty ( ) )
{
ESM : : Pathgrid : : Point lastPt = mPathFinder . getPath ( ) . back ( ) ;
Ogre : : Vector3 currPathTarget ( lastPt . mX , lastPt . mY , lastPt . mZ ) ;
dist = Ogre : : Math : : Abs ( ( newPathTarget - currPathTarget ) . length ( ) ) ;
dist = ( newPathTarget - currPathTarget ) . length ( ) ;
}
else dist = 1e+38 F ; // necessarily construct a new path
float targetPosThreshold ;
bool isOutside = actor . getCell ( ) - > getCell ( ) - > isExterior ( ) ;
if ( isOutside )
targetPosThreshold = 300 ;
else
targetPosThreshold = 100 ;
float targetPosThreshold = ( actor . getCell ( ) - > getCell ( ) - > isExterior ( ) ) ? 300 : 100 ;
if ( ( dist < 0 ) | | ( dist > targetPosThreshold ) )
//construct new path only if target has moved away more than on [targetPosThreshold]
if ( dist > targetPosThreshold )
{
//construct new path only if target has moved away more than on <targetPosThreshold>
ESM : : Position pos = actor . getRefData ( ) . getPosition ( ) ;
ESM : : Pathgrid : : Point start ;
@ -502,17 +594,18 @@ namespace MWMechanics
start . mY = pos . pos [ 1 ] ;
start . mZ = pos . pos [ 2 ] ;
ESM : : Pathgrid : : Point dest ;
dest . mX = newPathTarget . x ;
dest . mY = newPathTarget . y ;
dest . mZ = newPathTarget . z ;
if ( ! mPathFinder . isPathConstructed ( ) )
mPathFinder . buildPath ( start , dest , actor . getCell ( ) , isOutside ) ;
mPathFinder . buildPath ( start , dest , actor . getCell ( ) , fals e) ;
else
{
PathFinder newPathFinder ;
newPathFinder . buildPath ( start , dest , actor . getCell ( ) , isOutsid e) ;
newPathFinder . buildPath ( start , dest , actor . getCell ( ) , fals e) ;
//TO EXPLORE:
//maybe here is a mistake (?): PathFinder::getPathSize() returns number of grid points in the path,
//not the actual path length. Here we should know if the new path is actually more effective.
//if(pathFinder2.getPathSize() < mPathFinder.getPathSize())
if ( ! mPathFinder . getPath ( ) . empty ( ) )
{
newPathFinder . syncStart ( mPathFinder . getPath ( ) ) ;