2012-11-14 17:42:04 +00:00
# include "aitravel.hpp"
2014-09-17 10:39:10 +00:00
# include <OgreVector3.h>
2014-06-12 21:27:04 +00:00
# include <components/esm/aisequence.hpp>
2013-03-06 20:31:47 +00:00
# include "../mwbase/world.hpp"
# include "../mwbase/environment.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 "steering.hpp"
# include "movement.hpp"
2014-04-24 03:17:01 +00:00
# include "creaturestats.hpp"
2014-01-29 19:29:07 +00:00
2014-12-31 17:55:07 +00:00
namespace
{
bool isWithinMaxRange ( const Ogre : : Vector3 & pos1 , const Ogre : : Vector3 & 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.
// 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 . squaredDistance ( pos2 ) < = 7168 * 7168 ) ;
}
}
2013-06-01 00:49:52 +00:00
namespace MWMechanics
2013-05-25 01:16:35 +00:00
{
2013-06-01 00:49:52 +00:00
AiTravel : : AiTravel ( float x , float y , float z )
2014-10-08 21:09:50 +00:00
: mX ( x ) , mY ( y ) , mZ ( z )
2014-02-05 15:12:50 +00:00
, mCellX ( std : : numeric_limits < int > : : max ( ) )
, mCellY ( std : : numeric_limits < int > : : max ( ) )
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 )
: mX ( travel - > mData . mX ) , mY ( travel - > mData . mY ) , mZ ( travel - > mData . mZ )
, mCellX ( std : : numeric_limits < int > : : max ( ) )
, mCellY ( std : : numeric_limits < int > : : max ( ) )
{
}
2013-06-01 00:49:52 +00:00
AiTravel * MWMechanics : : AiTravel : : clone ( ) const
{
return new AiTravel ( * this ) ;
}
2013-04-01 15:44:08 +00:00
2014-10-08 08:58:52 +00:00
bool AiTravel : : execute ( const MWWorld : : Ptr & actor , AiState & state , float duration )
2013-06-01 00:49:52 +00:00
{
2013-08-30 00:07:02 +00:00
MWBase : : World * world = MWBase : : Environment : : get ( ) . getWorld ( ) ;
2013-06-01 00:49:52 +00:00
ESM : : Position pos = actor . getRefData ( ) . getPosition ( ) ;
2013-08-30 00:07:02 +00:00
Movement & movement = actor . getClass ( ) . getMovementSettings ( actor ) ;
2014-02-21 10:35:46 +00:00
const ESM : : Cell * cell = actor . getCell ( ) - > getCell ( ) ;
2013-04-01 15:44:08 +00:00
2014-04-24 03:17:01 +00:00
actor . getClass ( ) . getCreatureStats ( actor ) . setMovementFlag ( CreatureStats : : Flag_Run , false ) ;
2014-07-28 15:28:00 +00:00
actor . getClass ( ) . getCreatureStats ( actor ) . setDrawState ( DrawState_Nothing ) ;
2014-01-08 17:39:44 +00:00
MWWorld : : Ptr player = world - > getPlayerPtr ( ) ;
2014-02-21 10:35:46 +00:00
if ( cell - > mData . mX ! = player . getCell ( ) - > getCell ( ) - > mData . mX )
2013-03-14 17:16:37 +00:00
{
2014-03-16 22:38:51 +00:00
int sideX = PathFinder : : sgn ( cell - > mData . mX - player . getCell ( ) - > getCell ( ) - > mData . mX ) ;
2013-08-30 00:07:02 +00:00
//check if actor is near the border of an inactive cell. If so, stop walking.
if ( sideX * ( pos . pos [ 0 ] - cell - > mData . mX * ESM : : Land : : REAL_SIZE ) >
sideX * ( ESM : : Land : : REAL_SIZE / 2.0f - 200.0f ) )
2013-06-01 00:49:52 +00:00
{
2013-08-30 00:07:02 +00:00
movement . mPosition [ 1 ] = 0 ;
return false ;
2013-06-01 00:49:52 +00:00
}
2013-03-14 17:16:37 +00:00
}
2014-02-21 10:35:46 +00:00
if ( cell - > mData . mY ! = player . getCell ( ) - > getCell ( ) - > mData . mY )
2013-03-14 17:16:37 +00:00
{
2014-03-16 22:38:51 +00:00
int sideY = PathFinder : : sgn ( cell - > mData . mY - player . getCell ( ) - > getCell ( ) - > mData . mY ) ;
2013-08-30 00:07:02 +00:00
//check if actor is near the border of an inactive cell. If so, stop walking.
if ( sideY * ( pos . pos [ 1 ] - cell - > mData . mY * ESM : : Land : : REAL_SIZE ) >
sideY * ( ESM : : Land : : REAL_SIZE / 2.0f - 200.0f ) )
2013-06-01 00:49:52 +00:00
{
2013-08-30 00:07:02 +00:00
movement . mPosition [ 1 ] = 0 ;
return false ;
2013-06-01 00:49:52 +00:00
}
2013-03-14 17:16:37 +00:00
}
2013-03-14 18:05:00 +00:00
2014-12-31 17:55:07 +00:00
if ( ! isWithinMaxRange ( Ogre : : Vector3 ( mX , mY , mZ ) , Ogre : : Vector3 ( pos . pos ) ) )
2014-09-17 10:39:10 +00:00
return false ;
2014-02-05 15:12:50 +00:00
bool cellChange = cell - > mData . mX ! = mCellX | | cell - > mData . mY ! = mCellY ;
2013-06-01 00:49:52 +00:00
if ( ! mPathFinder . isPathConstructed ( ) | | cellChange )
2013-03-10 15:07:22 +00:00
{
2014-02-05 15:12:50 +00:00
mCellX = cell - > mData . mX ;
mCellY = cell - > mData . mY ;
2013-06-01 00:49:52 +00:00
2015-03-08 04:42:07 +00:00
ESM : : Pathgrid : : Point dest ( static_cast < int > ( mX ) , static_cast < int > ( mY ) , static_cast < int > ( mZ ) ) ;
ESM : : Pathgrid : : Point start ( PathFinder : : MakePathgridPoint ( pos ) ) ;
2013-06-01 00:49:52 +00:00
2014-01-12 17:42:31 +00:00
mPathFinder . buildPath ( start , dest , actor . getCell ( ) , true ) ;
2013-03-10 15:07:22 +00:00
}
2013-03-06 20:31:47 +00:00
2015-03-23 07:09:46 +00:00
if ( mPathFinder . checkPathCompleted ( pos . pos [ 0 ] , pos . pos [ 1 ] ) )
2013-06-01 00:49:52 +00:00
{
2013-08-30 00:07:02 +00:00
movement . mPosition [ 1 ] = 0 ;
2013-06-01 00:49:52 +00:00
return true ;
}
2013-03-31 17:30:03 +00:00
2014-01-29 19:29:07 +00:00
zTurn ( actor , Ogre : : Degree ( mPathFinder . getZAngleToNext ( pos . pos [ 0 ] , pos . pos [ 1 ] ) ) ) ;
2013-08-30 00:07:02 +00:00
movement . mPosition [ 1 ] = 1 ;
2013-05-25 01:16:35 +00:00
2013-06-01 00:49:52 +00:00
return false ;
2013-03-06 20:31:47 +00:00
}
2013-04-01 15:44:08 +00:00
2013-06-01 00:49:52 +00:00
int AiTravel : : getTypeId ( ) const
2013-03-06 20:31:47 +00:00
{
2014-01-07 00:12:37 +00:00
return TypeIdTravel ;
2013-03-06 20:31:47 +00:00
}
2014-06-12 21:27:04 +00:00
2014-12-31 17:41:57 +00:00
void AiTravel : : fastForward ( const MWWorld : : Ptr & actor , AiState & state )
{
2014-12-31 17:55:07 +00:00
if ( ! isWithinMaxRange ( Ogre : : Vector3 ( mX , mY , mZ ) , Ogre : : Vector3 ( actor . getRefData ( ) . getPosition ( ) . pos ) ) )
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 ) ;
2014-12-31 17:41:57 +00:00
}
2014-06-12 21:27:04 +00:00
void AiTravel : : writeState ( ESM : : AiSequence : : AiSequence & sequence ) const
{
std : : auto_ptr < ESM : : AiSequence : : AiTravel > travel ( new ESM : : AiSequence : : AiTravel ( ) ) ;
travel - > mData . mX = mX ;
travel - > mData . mY = mY ;
travel - > mData . mZ = mZ ;
ESM : : AiSequence : : AiPackageContainer package ;
package . mType = ESM : : AiSequence : : Ai_Travel ;
package . mPackage = travel . release ( ) ;
sequence . mPackages . push_back ( package ) ;
}
2013-05-25 01:16:35 +00:00
}