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"
2016-06-17 14:07:16 +00:00
2014-01-11 11:06:36 +00:00
# include "../mwworld/class.hpp"
2014-03-29 17:36:32 +00:00
# include "../mwworld/cellstore.hpp"
2016-06-17 14:07:16 +00:00
2014-04-23 06:57:48 +00:00
# include "creaturestats.hpp"
2014-01-11 11:06:36 +00:00
# include "movement.hpp"
2014-01-29 19:29:07 +00:00
# include "steering.hpp"
2014-12-09 21:25:28 +00:00
namespace MWMechanics
{
int AiFollow : : mFollowIndexCounter = 0 ;
2017-11-21 16:00:51 +00:00
AiFollow : : AiFollow ( const std : : string & actorId , float duration , float x , float y , float z )
2020-05-16 19:08:39 +00:00
: mAlwaysFollow ( false ) , mDuration ( duration ) , mRemainingDuration ( duration ) , mX ( x ) , mY ( y ) , mZ ( z )
2017-11-21 16:00:51 +00:00
, mCellId ( " " ) , mActive ( false ) , mFollowIndex ( mFollowIndexCounter + + )
2014-01-29 19:29:07 +00:00
{
2017-11-21 16:00:51 +00:00
mTargetActorRefId = actorId ;
2014-01-29 19:29:07 +00:00
}
2016-06-11 13:34:49 +00:00
2017-11-21 16:00:51 +00:00
AiFollow : : AiFollow ( const std : : string & actorId , const std : : string & cellId , float duration , float x , float y , float z )
2020-05-16 19:08:39 +00:00
: mAlwaysFollow ( false ) , mDuration ( duration ) , mRemainingDuration ( duration ) , mX ( x ) , mY ( y ) , mZ ( z )
2017-11-21 16:00:51 +00:00
, mCellId ( cellId ) , mActive ( false ) , mFollowIndex ( mFollowIndexCounter + + )
2014-03-05 10:24:39 +00:00
{
2017-11-21 16:00:51 +00:00
mTargetActorRefId = actorId ;
2014-03-05 10:24:39 +00:00
}
2017-11-21 16:00:51 +00:00
AiFollow : : AiFollow ( const MWWorld : : Ptr & actor , float duration , float x , float y , float z )
2020-05-16 19:08:39 +00:00
: mAlwaysFollow ( false ) , mDuration ( duration ) , mRemainingDuration ( duration ) , mX ( x ) , mY ( y ) , mZ ( z )
2017-11-21 16:00:51 +00:00
, mCellId ( " " ) , mActive ( false ) , mFollowIndex ( mFollowIndexCounter + + )
{
mTargetActorRefId = actor . getCellRef ( ) . getRefId ( ) ;
mTargetActorId = actor . getClass ( ) . getCreatureStats ( actor ) . getActorId ( ) ;
}
AiFollow : : AiFollow ( const MWWorld : : Ptr & actor , const std : : string & cellId , float duration , float x , float y , float z )
2020-05-16 19:08:39 +00:00
: mAlwaysFollow ( false ) , mDuration ( duration ) , mRemainingDuration ( duration ) , mX ( x ) , mY ( y ) , mZ ( z )
2017-11-21 16:00:51 +00:00
, mCellId ( cellId ) , mActive ( false ) , mFollowIndex ( mFollowIndexCounter + + )
{
mTargetActorRefId = actor . getCellRef ( ) . getRefId ( ) ;
mTargetActorId = actor . getClass ( ) . getCreatureStats ( actor ) . getActorId ( ) ;
}
AiFollow : : AiFollow ( const MWWorld : : Ptr & actor , bool commanded )
2020-05-16 19:08:39 +00:00
: TypedAiPackage < AiFollow > ( makeDefaultOptions ( ) . withShouldCancelPreviousAi ( ! commanded ) )
, mAlwaysFollow ( true ) , mDuration ( 0 ) , mRemainingDuration ( 0 ) , mX ( 0 ) , mY ( 0 ) , mZ ( 0 )
2017-11-21 16:00:51 +00:00
, mCellId ( " " ) , mActive ( false ) , mFollowIndex ( mFollowIndexCounter + + )
2014-01-29 19:29:07 +00:00
{
2017-11-21 16:00:51 +00:00
mTargetActorRefId = actor . getCellRef ( ) . getRefId ( ) ;
mTargetActorId = actor . getClass ( ) . getCreatureStats ( actor ) . getActorId ( ) ;
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 )
2020-05-16 19:08:39 +00:00
: TypedAiPackage < AiFollow > ( makeDefaultOptions ( ) . withShouldCancelPreviousAi ( ! follow - > mCommanded ) )
, mAlwaysFollow ( follow - > mAlwaysFollow )
2020-06-02 19:30:46 +00:00
// mDuration isn't saved in the save file, so just giving it "1" for now if the package had a duration.
// The exact value of mDuration only matters for repeating packages.
// Previously mRemainingDuration could be negative even when mDuration was 0. Checking for > 0 should fix old saves.
, mDuration ( follow - > mRemainingDuration )
, 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
, mCellId ( follow - > mCellId ) , mActive ( follow - > mActive ) , mFollowIndex ( mFollowIndexCounter + + )
2014-08-17 15:01:04 +00:00
{
2017-11-21 16:00:51 +00:00
mTargetActorRefId = follow - > mTargetId ;
mTargetActorId = follow - > mTargetActorId ;
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
{
2018-11-02 11:24:43 +00:00
const MWWorld : : Ptr target = getTarget ( ) ;
2014-01-11 11:06:36 +00:00
2018-11-02 11:24:43 +00:00
// Target is not here right now, wait for it to return
// Really we should be checking whether the target is currently registered with the MechanicsManager
if ( target = = MWWorld : : Ptr ( ) | | ! target . getRefData ( ) . getCount ( ) | | ! target . getRefData ( ) . isEnabled ( ) )
return false ;
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 > ( ) ;
2017-08-13 14:05:35 +00:00
bool & rotate = storage . mTurnActorToTarget ;
if ( rotate )
{
if ( zTurn ( actor , storage . mTargetAngleRadians ) )
rotate = false ;
return false ;
}
2018-11-02 11:24:43 +00:00
const osg : : Vec3f actorPos ( actor . getRefData ( ) . getPosition ( ) . asVec3 ( ) ) ;
const osg : : Vec3f targetPos ( target . getRefData ( ) . getPosition ( ) . asVec3 ( ) ) ;
const osg : : Vec3f targetDir = targetPos - actorPos ;
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 )
{
2018-11-02 11:24:43 +00:00
if ( targetDir . length2 ( ) < 500 * 500 & & MWBase : : Environment : : get ( ) . getWorld ( ) - > getLOS ( actor , target ) )
2014-12-09 21:25:28 +00:00
mActive = true ;
storage . mTimer = 0.5f ;
}
}
if ( ! mActive )
return false ;
2017-03-26 07:51:32 +00:00
// The distances below are approximations based on observations of the original engine.
// If only one actor is following the target, it uses 186.
// If there are multiple actors following the same target, they form a group with each group member at 313 + (130 * i) distance to the target.
short followDistance = 186 ;
2014-12-09 15:02:07 +00:00
std : : list < int > followers = MWBase : : Environment : : get ( ) . getMechanicsManager ( ) - > getActorsFollowingIndices ( target ) ;
2017-03-26 07:51:32 +00:00
if ( followers . size ( ) > = 2 )
2014-12-09 15:02:07 +00:00
{
2017-03-26 07:51:32 +00:00
followDistance = 313 ;
short i = 0 ;
followers . sort ( ) ;
for ( std : : list < int > : : iterator it = followers . begin ( ) ; it ! = followers . end ( ) ; + + it )
{
if ( * it = = mFollowIndex )
followDistance + = 130 * i ;
+ + i ;
}
2014-12-09 15:02:07 +00:00
}
2017-02-17 16:38:21 +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
2017-02-17 16:38:21 +00:00
if ( mDuration > 0 )
2014-06-12 21:27:04 +00:00
{
2016-06-11 13:34:49 +00:00
mRemainingDuration - = ( ( duration * MWBase : : Environment : : get ( ) . getWorld ( ) - > getTimeScaleFactor ( ) ) / 3600 ) ;
if ( mRemainingDuration < = 0 )
{
mRemainingDuration = mDuration ;
2014-06-12 21:27:04 +00:00
return true ;
2016-06-11 13:34:49 +00:00
}
2014-06-12 21:27:04 +00:00
}
2014-03-05 10:24:39 +00:00
2018-11-02 11:24:43 +00:00
osg : : Vec3f finalPos ( mX , mY , mZ ) ;
if ( ( actorPos - finalPos ) . length2 ( ) < followDistance * followDistance ) //Close-ish to final position
2014-01-12 10:38:58 +00:00
{
2017-02-17 16:38:21 +00:00
if ( actor . getCell ( ) - > isExterior ( ) ) //Outside?
2014-03-05 10:24:39 +00:00
{
2017-02-17 16:38:21 +00:00
if ( mCellId = = " " ) //No cell to travel to
2014-03-05 10:24:39 +00:00
return true ;
}
else
{
2017-02-17 16:38:21 +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
2017-08-13 14:05:35 +00:00
short baseFollowDistance = followDistance ;
short threshold = 30 ; // to avoid constant switching between moving/stopping
if ( storage . mMoving )
followDistance - = threshold ;
else
2016-08-19 19:15:26 +00:00
followDistance + = threshold ;
2017-08-13 14:05:35 +00:00
2018-11-02 11:24:43 +00:00
if ( targetDir . length2 ( ) < = followDistance * followDistance )
2017-08-13 14:05:35 +00:00
{
2018-11-02 11:24:43 +00:00
float faceAngleRadians = std : : atan2 ( targetDir . x ( ) , targetDir . y ( ) ) ;
2017-08-13 14:05:35 +00:00
if ( ! zTurn ( actor , faceAngleRadians , osg : : DegreesToRadians ( 45.f ) ) )
{
storage . mTargetAngleRadians = faceAngleRadians ;
storage . mTurnActorToTarget = true ;
}
return false ;
2016-01-03 17:20:34 +00:00
}
2015-09-25 23:55:12 +00:00
2018-11-02 11:24:43 +00:00
storage . mMoving = ! pathTo ( actor , targetPos , duration , baseFollowDistance ) ; // Go to the destination
2016-01-01 13:41:45 +00:00
2016-08-19 19:15:26 +00:00
if ( storage . mMoving )
2016-08-14 16:02:13 +00:00
{
2016-08-19 19:15:26 +00:00
//Check if you're far away
2018-11-02 11:24:43 +00:00
if ( targetDir . length2 ( ) > 450 * 450 )
2016-08-19 19:15:26 +00:00
actor . getClass ( ) . getCreatureStats ( actor ) . setMovementFlag ( MWMechanics : : CreatureStats : : Flag_Run , true ) ; //Make NPC run
2018-11-02 11:24:43 +00:00
else if ( targetDir . length2 ( ) < 325 * 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 threshold
2016-08-19 19:15:26 +00:00
actor . getClass ( ) . getCreatureStats ( actor ) . setMovementFlag ( MWMechanics : : CreatureStats : : Flag_Run , false ) ; //make NPC walk
}
2014-04-23 06:57:48 +00:00
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
{
2017-11-21 16:00:51 +00:00
return mTargetActorRefId ;
2014-01-12 13:02:40 +00:00
}
2014-01-24 18:16:50 +00:00
2014-12-09 21:25:28 +00:00
bool AiFollow : : isCommanded ( ) const
2014-08-06 19:16:14 +00:00
{
2020-05-16 19:08:39 +00:00
return ! mOptions . mShouldCancelPreviousAi ;
2014-08-06 19:16:14 +00:00
}
2014-12-09 21:25:28 +00:00
void AiFollow : : writeState ( ESM : : AiSequence : : AiSequence & sequence ) const
2014-06-12 21:27:04 +00:00
{
2017-04-28 15:30:26 +00:00
std : : unique_ptr < ESM : : AiSequence : : AiFollow > follow ( new ESM : : AiSequence : : AiFollow ( ) ) ;
2014-06-12 21:27:04 +00:00
follow - > mData . mX = mX ;
follow - > mData . mY = mY ;
follow - > mData . mZ = mZ ;
2017-11-21 16:00:51 +00:00
follow - > mTargetId = mTargetActorRefId ;
follow - > mTargetActorId = mTargetActorId ;
2014-06-12 21:27:04 +00:00
follow - > mRemainingDuration = mRemainingDuration ;
follow - > mCellId = mCellId ;
follow - > mAlwaysFollow = mAlwaysFollow ;
2020-05-16 19:08:39 +00:00
follow - > mCommanded = isCommanded ( ) ;
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
int AiFollow : : getFollowIndex ( ) const
2014-12-09 15:02:07 +00:00
{
return mFollowIndex ;
}
2014-12-09 21:25:28 +00:00
2016-06-11 13:34:49 +00:00
void AiFollow : : fastForward ( const MWWorld : : Ptr & actor , AiState & state )
{
2017-02-17 16:38:21 +00:00
// Update duration counter if this package has a duration
if ( mDuration > 0 )
mRemainingDuration - - ;
2016-06-11 13:34:49 +00:00
}
2014-12-09 21:25:28 +00:00
}