2014-01-24 18:16:50 +00:00
# include "aifollow.hpp"
2014-06-12 21:27:04 +00:00
# include <components/esm/aisequence.hpp>
2015-07-25 02:14:22 +00:00
# include <components/esm/loadcell.hpp>
2014-06-12 21:27:04 +00:00
2014-01-11 11:06:36 +00:00
# include "../mwbase/world.hpp"
# include "../mwbase/environment.hpp"
2014-12-09 15:02:07 +00:00
# include "../mwbase/mechanicsmanager.hpp"
2014-01-11 11:06:36 +00:00
# include "../mwworld/class.hpp"
2014-03-29 17:36:32 +00:00
# include "../mwworld/cellstore.hpp"
2014-04-23 06:57:48 +00:00
# include "creaturestats.hpp"
2014-01-11 11:06:36 +00:00
# include "movement.hpp"
2014-01-24 18:13:23 +00:00
2014-01-29 19:29:07 +00:00
# include "steering.hpp"
2014-12-09 21:25:28 +00:00
namespace MWMechanics
{
struct AiFollowStorage : AiTemporaryBase
{
float mTimer ;
2015-09-25 23:55:12 +00:00
bool mMoving ;
2014-12-09 21:25:28 +00:00
2015-09-25 23:55:12 +00:00
AiFollowStorage ( ) : mTimer ( 0.f ) , mMoving ( false ) { }
2014-12-09 21:25:28 +00:00
} ;
2014-12-09 15:02:07 +00:00
2014-12-09 21:25:28 +00:00
int AiFollow : : mFollowIndexCounter = 0 ;
AiFollow : : AiFollow ( const std : : string & actorId , float duration , float x , float y , float z )
2014-08-13 14:00:32 +00:00
: mAlwaysFollow ( false ) , mCommanded ( false ) , mRemainingDuration ( duration ) , mX ( x ) , mY ( y ) , mZ ( z )
2015-05-01 00:24:27 +00:00
, mActorRefId ( actorId ) , mActorId ( - 1 ) , mCellId ( " " ) , mActive ( false ) , mFollowIndex ( mFollowIndexCounter + + )
2014-01-29 19:29:07 +00:00
{
}
2014-12-09 21:25:28 +00:00
AiFollow : : AiFollow ( const std : : string & actorId , const std : : string & cellId , float duration , float x , float y , float z )
2014-08-13 14:00:32 +00:00
: mAlwaysFollow ( false ) , mCommanded ( false ) , mRemainingDuration ( duration ) , mX ( x ) , mY ( y ) , mZ ( z )
2015-05-01 00:24:27 +00:00
, mActorRefId ( actorId ) , mActorId ( - 1 ) , mCellId ( cellId ) , mActive ( false ) , mFollowIndex ( mFollowIndexCounter + + )
2014-03-05 10:24:39 +00:00
{
}
2014-12-09 21:25:28 +00:00
AiFollow : : AiFollow ( const std : : string & actorId , bool commanded )
2014-08-13 14:00:32 +00:00
: mAlwaysFollow ( true ) , mCommanded ( commanded ) , mRemainingDuration ( 0 ) , mX ( 0 ) , mY ( 0 ) , mZ ( 0 )
2015-05-01 00:24:27 +00:00
, mActorRefId ( actorId ) , mActorId ( - 1 ) , mCellId ( " " ) , mActive ( false ) , mFollowIndex ( mFollowIndexCounter + + )
2014-01-29 19:29:07 +00:00
{
2014-12-09 15:02:07 +00:00
2014-01-29 19:29:07 +00:00
}
2012-11-15 21:32:15 +00:00
2014-12-09 21:25:28 +00:00
AiFollow : : AiFollow ( const ESM : : AiSequence : : AiFollow * follow )
2015-05-01 00:24:27 +00:00
: mAlwaysFollow ( follow - > mAlwaysFollow ) , mCommanded ( follow - > mCommanded ) , mRemainingDuration ( follow - > mRemainingDuration )
2014-08-17 15:01:04 +00:00
, mX ( follow - > mData . mX ) , mY ( follow - > mData . mY ) , mZ ( follow - > mData . mZ )
2015-05-01 00:24:27 +00:00
, mActorRefId ( follow - > mTargetId ) , mActorId ( - 1 )
, mCellId ( follow - > mCellId ) , mActive ( follow - > mActive ) , mFollowIndex ( mFollowIndexCounter + + )
2014-08-17 15:01:04 +00:00
{
}
2015-06-26 15:47:04 +00:00
bool AiFollow : : execute ( const MWWorld : : Ptr & actor , CharacterController & characterController , AiState & state , float duration )
2012-11-15 21:32:15 +00:00
{
2014-08-13 14:00:32 +00:00
MWWorld : : Ptr target = getTarget ( ) ;
2014-01-11 11:06:36 +00:00
2014-09-14 08:49:33 +00:00
if ( target . isEmpty ( ) | | ! target . getRefData ( ) . getCount ( ) | | ! target . getRefData ( ) . isEnabled ( ) // Really we should be checking whether the target is currently registered
// with the MechanicsManager
)
2014-12-09 15:02:07 +00:00
return false ; // Target is not here right now, wait for it to return
2014-01-11 11:06:36 +00:00
2014-07-28 15:28:00 +00:00
actor . getClass ( ) . getCreatureStats ( actor ) . setDrawState ( DrawState_Nothing ) ;
2015-09-25 23:55:12 +00:00
AiFollowStorage & storage = state . get < AiFollowStorage > ( ) ;
2014-12-09 21:25:28 +00:00
// AiFollow requires the target to be in range and within sight for the initial activation
if ( ! mActive )
{
storage . mTimer - = duration ;
if ( storage . mTimer < 0 )
{
2015-06-03 17:41:19 +00:00
if ( ( actor . getRefData ( ) . getPosition ( ) . asVec3 ( ) - target . getRefData ( ) . getPosition ( ) . asVec3 ( ) ) . length2 ( )
2014-12-09 21:25:28 +00:00
< 500 * 500
& & MWBase : : Environment : : get ( ) . getWorld ( ) - > getLOS ( actor , target ) )
mActive = true ;
storage . mTimer = 0.5f ;
}
}
if ( ! mActive )
return false ;
2014-04-23 06:57:48 +00:00
ESM : : Position pos = actor . getRefData ( ) . getPosition ( ) ; //position of the actor
2014-12-09 15:02:07 +00:00
float followDistance = 180 ;
// When there are multiple actors following the same target, they form a group with each group member at 180*(i+1) distance to the target
int i = 0 ;
std : : list < int > followers = MWBase : : Environment : : get ( ) . getMechanicsManager ( ) - > getActorsFollowingIndices ( target ) ;
followers . sort ( ) ;
for ( std : : list < int > : : iterator it = followers . begin ( ) ; it ! = followers . end ( ) ; + + it )
{
if ( * it = = mFollowIndex )
followDistance * = ( i + 1 ) ;
+ + i ;
}
2014-04-23 06:57:48 +00:00
if ( ! mAlwaysFollow ) //Update if you only follow for a bit
2014-01-12 10:38:58 +00:00
{
2014-06-12 21:27:04 +00:00
//Check if we've run out of time
if ( mRemainingDuration ! = 0 )
{
mRemainingDuration - = duration ;
if ( duration < = 0 )
return true ;
}
2014-03-05 10:24:39 +00:00
if ( ( pos . pos [ 0 ] - mX ) * ( pos . pos [ 0 ] - mX ) +
( pos . pos [ 1 ] - mY ) * ( pos . pos [ 1 ] - mY ) +
2014-12-09 15:02:07 +00:00
( pos . pos [ 2 ] - mZ ) * ( pos . pos [ 2 ] - mZ ) < followDistance * followDistance ) //Close-ish to final position
2014-01-12 10:38:58 +00:00
{
2014-04-23 06:57:48 +00:00
if ( actor . getCell ( ) - > isExterior ( ) ) //Outside?
2014-03-05 10:24:39 +00:00
{
2014-04-23 06:57:48 +00:00
if ( mCellId = = " " ) //No cell to travel to
2014-03-05 10:24:39 +00:00
return true ;
}
else
{
2014-04-23 06:57:48 +00:00
if ( mCellId = = actor . getCell ( ) - > getCell ( ) - > mName ) //Cell to travel to
2014-03-05 10:24:39 +00:00
return true ;
}
2014-01-12 10:38:58 +00:00
}
}
2014-01-11 11:06:36 +00:00
2014-06-12 21:27:04 +00:00
//Set the target destination from the actor
2014-05-13 01:05:32 +00:00
ESM : : Pathgrid : : Point dest = target . getRefData ( ) . getPosition ( ) . pos ;
2014-01-11 11:06:36 +00:00
2015-09-25 23:55:12 +00:00
float dist = distance ( dest , pos . pos [ 0 ] , pos . pos [ 1 ] , pos . pos [ 2 ] ) ;
if ( storage . mMoving ) //Stop when you get close
storage . mMoving = ( dist > followDistance ) ;
else
2016-01-03 17:20:34 +00:00
{
const float threshold = 10 ;
2015-09-25 23:55:12 +00:00
storage . mMoving = ( dist > followDistance + threshold ) ;
2016-01-03 17:20:34 +00:00
}
2015-09-25 23:55:12 +00:00
if ( ! storage . mMoving )
2014-12-09 21:25:28 +00:00
{
2014-01-29 19:29:07 +00:00
actor . getClass ( ) . getMovementSettings ( actor ) . mPosition [ 1 ] = 0 ;
2014-12-09 21:25:28 +00:00
// turn towards target anyway
float directionX = target . getRefData ( ) . getPosition ( ) . pos [ 0 ] - actor . getRefData ( ) . getPosition ( ) . pos [ 0 ] ;
float directionY = target . getRefData ( ) . getPosition ( ) . pos [ 1 ] - actor . getRefData ( ) . getPosition ( ) . pos [ 1 ] ;
2015-06-03 17:41:19 +00:00
zTurn ( actor , std : : atan2 ( directionX , directionY ) , osg : : DegreesToRadians ( 5.f ) ) ;
2014-12-09 21:25:28 +00:00
}
else
{
2014-05-14 18:11:34 +00:00
pathTo ( actor , dest , duration ) ; //Go to the destination
}
2014-01-11 11:06:36 +00:00
2014-04-23 06:57:48 +00:00
//Check if you're far away
2015-09-25 23:55:12 +00:00
if ( dist > 450 )
2014-04-23 06:57:48 +00:00
actor . getClass ( ) . getCreatureStats ( actor ) . setMovementFlag ( MWMechanics : : CreatureStats : : Flag_Run , true ) ; //Make NPC run
2015-09-25 23:55:12 +00:00
else if ( dist < 325 ) //Have a bit of a dead zone, otherwise npc will constantly flip between running and not when right on the edge of the running threshhold
2014-04-23 06:57:48 +00:00
actor . getClass ( ) . getCreatureStats ( actor ) . setMovementFlag ( MWMechanics : : CreatureStats : : Flag_Run , false ) ; //make NPC walk
2014-01-11 11:06:36 +00:00
return false ;
2012-11-15 21:32:15 +00:00
}
2014-12-09 21:25:28 +00:00
std : : string AiFollow : : getFollowedActor ( )
2014-01-12 13:02:40 +00:00
{
2014-08-13 14:00:32 +00:00
return mActorRefId ;
2014-01-12 13:02:40 +00:00
}
2014-01-24 18:16:50 +00:00
2014-12-09 21:25:28 +00:00
AiFollow * MWMechanics : : AiFollow : : clone ( ) const
2014-01-29 19:29:07 +00:00
{
return new AiFollow ( * this ) ;
}
2014-12-09 21:25:28 +00:00
int AiFollow : : getTypeId ( ) const
2014-01-29 19:29:07 +00:00
{
return TypeIdFollow ;
2014-01-24 18:16:50 +00:00
}
2014-06-12 21:27:04 +00:00
2014-12-09 21:25:28 +00:00
bool AiFollow : : isCommanded ( ) const
2014-08-06 19:16:14 +00:00
{
return mCommanded ;
}
2014-12-09 21:25:28 +00:00
void AiFollow : : writeState ( ESM : : AiSequence : : AiSequence & sequence ) const
2014-06-12 21:27:04 +00:00
{
std : : auto_ptr < ESM : : AiSequence : : AiFollow > follow ( new ESM : : AiSequence : : AiFollow ( ) ) ;
follow - > mData . mX = mX ;
follow - > mData . mY = mY ;
follow - > mData . mZ = mZ ;
2014-08-13 14:00:32 +00:00
follow - > mTargetId = mActorRefId ;
2014-06-12 21:27:04 +00:00
follow - > mRemainingDuration = mRemainingDuration ;
follow - > mCellId = mCellId ;
follow - > mAlwaysFollow = mAlwaysFollow ;
2014-08-06 19:16:14 +00:00
follow - > mCommanded = mCommanded ;
2014-12-09 21:25:28 +00:00
follow - > mActive = mActive ;
2014-06-12 21:27:04 +00:00
ESM : : AiSequence : : AiPackageContainer package ;
package . mType = ESM : : AiSequence : : Ai_Follow ;
package . mPackage = follow . release ( ) ;
sequence . mPackages . push_back ( package ) ;
}
2014-12-09 21:25:28 +00:00
MWWorld : : Ptr AiFollow : : getTarget ( )
2014-07-27 18:30:52 +00:00
{
2014-08-13 23:08:09 +00:00
if ( mActorId = = - 2 )
return MWWorld : : Ptr ( ) ;
2014-08-13 14:00:32 +00:00
if ( mActorId = = - 1 )
{
MWWorld : : Ptr target = MWBase : : Environment : : get ( ) . getWorld ( ) - > searchPtr ( mActorRefId , false ) ;
2014-08-13 23:08:09 +00:00
if ( target . isEmpty ( ) )
{
mActorId = - 2 ;
return target ;
}
else
mActorId = target . getClass ( ) . getCreatureStats ( target ) . getActorId ( ) ;
2014-08-13 14:00:32 +00:00
}
if ( mActorId ! = - 1 )
return MWBase : : Environment : : get ( ) . getWorld ( ) - > searchPtrViaActorId ( mActorId ) ;
else
return MWWorld : : Ptr ( ) ;
2014-07-27 18:30:52 +00:00
}
2014-12-09 15:02:07 +00:00
2014-12-09 21:25:28 +00:00
int AiFollow : : getFollowIndex ( ) const
2014-12-09 15:02:07 +00:00
{
return mFollowIndex ;
}
2014-12-09 21:25:28 +00:00
}