2012-11-14 17:42:04 +00:00
# include "aitravel.hpp"
2014-06-12 21:27:04 +00:00
# include <components/esm/aisequence.hpp>
2013-03-06 20:31:47 +00:00
# include "../mwbase/environment.hpp"
2020-05-17 01:06:39 +00:00
# include "../mwbase/mechanicsmanager.hpp"
2018-09-21 12:34:23 +00:00
# include "../mwbase/world.hpp"
2014-02-23 19:11:05 +00:00
2013-06-01 00:49:52 +00:00
# include "../mwworld/class.hpp"
2014-02-23 19:11:05 +00:00
# include "../mwworld/cellstore.hpp"
2013-03-06 20:31:47 +00:00
2014-01-29 19:29:07 +00:00
# include "movement.hpp"
2014-04-24 03:17:01 +00:00
# include "creaturestats.hpp"
2014-01-29 19:29:07 +00:00
2016-01-19 13:51:37 +00:00
namespace
{
bool isWithinMaxRange ( const osg : : Vec3f & pos1 , const osg : : Vec3f & pos2 )
{
// Maximum travel distance for vanilla compatibility.
// Was likely meant to prevent NPCs walking into non-loaded exterior cells, but for some reason is used in interior cells as well.
2019-04-14 13:51:12 +00:00
// We can make this configurable at some point, but the default *must* be the below value. Anything else will break shoddily-written content (*cough* MW *cough*) in bizarre ways.
return ( pos1 - pos2 ) . length2 ( ) < = 7168 * 7168 ;
2016-01-19 13:51:37 +00:00
}
}
2014-12-31 17:55:07 +00:00
2013-06-01 00:49:52 +00:00
namespace MWMechanics
2013-05-25 01:16:35 +00:00
{
2020-05-16 19:08:39 +00:00
AiTravel : : AiTravel ( float x , float y , float z , AiTravel * )
: mX ( x ) , mY ( y ) , mZ ( z ) , mHidden ( false )
{
}
AiTravel : : AiTravel ( float x , float y , float z , AiInternalTravel * derived )
: TypedAiPackage < AiTravel > ( derived ) , mX ( x ) , mY ( y ) , mZ ( z ) , mHidden ( true )
{
}
AiTravel : : AiTravel ( float x , float y , float z )
: AiTravel ( x , y , z , this )
2013-06-01 00:49:52 +00:00
{
}
2013-03-31 17:30:03 +00:00
2014-06-12 21:27:04 +00:00
AiTravel : : AiTravel ( const ESM : : AiSequence : : AiTravel * travel )
2020-05-16 19:08:39 +00:00
: mX ( travel - > mData . mX ) , mY ( travel - > mData . mY ) , mZ ( travel - > mData . mZ ) , mHidden ( false )
2014-06-12 21:27:04 +00:00
{
2020-05-16 19:08:39 +00:00
// Hidden ESM::AiSequence::AiTravel package should be converted into MWMechanics::AiInternalTravel type
assert ( ! travel - > mHidden ) ;
2014-06-12 21:27:04 +00:00
}
2015-06-26 15:47:04 +00:00
bool AiTravel : : execute ( const MWWorld : : Ptr & actor , CharacterController & characterController , AiState & state , float duration )
2013-06-01 00:49:52 +00:00
{
2020-05-17 01:06:39 +00:00
MWBase : : MechanicsManager * mechMgr = MWBase : : Environment : : get ( ) . getMechanicsManager ( ) ;
2019-10-09 16:57:24 +00:00
2020-05-17 01:06:39 +00:00
if ( mechMgr - > isTurningToPlayer ( actor ) | | mechMgr - > getGreetingState ( actor ) = = Greet_InProgress )
2019-10-09 16:57:24 +00:00
return false ;
2018-11-02 11:24:43 +00:00
const osg : : Vec3f actorPos ( actor . getRefData ( ) . getPosition ( ) . asVec3 ( ) ) ;
const osg : : Vec3f targetPos ( mX , mY , mZ ) ;
2013-04-01 15:44:08 +00:00
2020-05-17 01:06:39 +00:00
auto & stats = actor . getClass ( ) . getCreatureStats ( actor ) ;
2019-10-09 16:57:24 +00:00
stats . setMovementFlag ( CreatureStats : : Flag_Run , false ) ;
stats . setDrawState ( DrawState_Nothing ) ;
2014-07-28 15:28:00 +00:00
2020-02-02 07:02:19 +00:00
// Note: we should cancel internal "return after combat" package, if original location is too far away
2018-11-02 11:24:43 +00:00
if ( ! isWithinMaxRange ( targetPos , actorPos ) )
2020-02-02 07:02:19 +00:00
return mHidden ;
2014-09-17 10:39:10 +00:00
2018-07-29 15:38:51 +00:00
// Unfortunately, with vanilla assets destination is sometimes blocked by other actor.
// If we got close to target, check for actors nearby. If they are, finish AI package.
int destinationTolerance = 64 ;
2018-11-02 11:24:43 +00:00
if ( distance ( actorPos , targetPos ) < = destinationTolerance )
2018-07-29 15:38:51 +00:00
{
std : : vector < MWWorld : : Ptr > targetActors ;
std : : pair < MWWorld : : Ptr , osg : : Vec3f > result = MWBase : : Environment : : get ( ) . getWorld ( ) - > getHitContact ( actor , destinationTolerance , targetActors ) ;
if ( ! result . first . isEmpty ( ) )
{
actor . getClass ( ) . getMovementSettings ( actor ) . mPosition [ 1 ] = 0 ;
return true ;
}
}
2018-11-02 11:24:43 +00:00
if ( pathTo ( actor , targetPos , duration ) )
2013-03-10 15:07:22 +00:00
{
2015-06-11 06:28:31 +00:00
actor . getClass ( ) . getMovementSettings ( actor ) . mPosition [ 1 ] = 0 ;
return true ;
2013-03-10 15:07:22 +00:00
}
2015-06-11 06:28:31 +00:00
return false ;
}
2013-03-06 20:31:47 +00:00
2014-12-31 17:41:57 +00:00
void AiTravel : : fastForward ( const MWWorld : : Ptr & actor , AiState & state )
{
2015-06-03 17:41:19 +00:00
if ( ! isWithinMaxRange ( osg : : Vec3f ( mX , mY , mZ ) , actor . getRefData ( ) . getPosition ( ) . asVec3 ( ) ) )
2014-12-31 17:55:07 +00:00
return ;
// does not do any validation on the travel target (whether it's in air, inside collision geometry, etc),
// that is the user's responsibility
MWBase : : Environment : : get ( ) . getWorld ( ) - > moveObject ( actor , mX , mY , mZ ) ;
actor . getClass ( ) . adjustPosition ( actor , false ) ;
2019-08-01 13:10:46 +00:00
reset ( ) ;
2014-12-31 17:41:57 +00:00
}
2014-06-12 21:27:04 +00:00
void AiTravel : : writeState ( ESM : : AiSequence : : AiSequence & sequence ) const
{
2017-04-28 15:30:26 +00:00
std : : unique_ptr < ESM : : AiSequence : : AiTravel > travel ( new ESM : : AiSequence : : AiTravel ( ) ) ;
2014-06-12 21:27:04 +00:00
travel - > mData . mX = mX ;
travel - > mData . mY = mY ;
travel - > mData . mZ = mZ ;
2018-05-25 15:31:31 +00:00
travel - > mHidden = mHidden ;
2014-06-12 21:27:04 +00:00
ESM : : AiSequence : : AiPackageContainer package ;
package . mType = ESM : : AiSequence : : Ai_Travel ;
package . mPackage = travel . release ( ) ;
sequence . mPackages . push_back ( package ) ;
}
2020-05-16 19:08:39 +00:00
AiInternalTravel : : AiInternalTravel ( float x , float y , float z )
: AiTravel ( x , y , z , this )
{
}
AiInternalTravel : : AiInternalTravel ( const ESM : : AiSequence : : AiTravel * travel )
: AiTravel ( travel - > mData . mX , travel - > mData . mY , travel - > mData . mZ , this )
{
}
std : : unique_ptr < AiPackage > AiInternalTravel : : clone ( ) const
{
return std : : make_unique < AiInternalTravel > ( * this ) ;
}
2013-05-25 01:16:35 +00:00
}