2012-04-09 14:51:03 +02:00
/*
2020-01-02 06:44:10 +01:00
* This file is part of the TrinityCore Project. See AUTHORS file for Copyright information
2012-04-09 14:51:03 +02:00
*
2020-09-04 13:38:24 +02:00
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation; either version 2 of the License, or (at your
* option) any later version.
2012-04-09 14:51:03 +02:00
*
2020-09-04 13:38:24 +02:00
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
2012-04-09 14:51:03 +02:00
*
2020-09-04 13:38:24 +02:00
* You should have received a copy of the GNU General Public License along
* with this program. If not, see <http://www.gnu.org/licenses/>.
2012-04-09 14:51:03 +02:00
*/
2012-08-25 21:55:03 -05:00
#include "PathGenerator.h"
2012-04-09 14:51:03 +02:00
#include "Creature.h"
#include "DetourCommon.h"
#include "DetourNavMeshQuery.h"
2018-03-03 15:39:19 +01:00
#include "DisableMgr.h"
#include "Log.h"
#include "MMapFactory.h"
#include "MMapManager.h"
#include "Map.h"
2016-06-04 11:26:57 +01:00
#include "Metric.h"
2018-03-03 15:39:19 +01:00
#include "PhasingHandler.h"
2012-04-09 14:51:03 +02:00
2012-08-25 21:55:03 -05:00
////////////////// PathGenerator //////////////////
2020-09-04 13:38:24 +02:00
PathGenerator :: PathGenerator ( Unit const * owner ) :
2014-01-18 01:36:12 +01:00
_polyLength ( 0 ), _type ( PATHFIND_BLANK ), _useStraightPath ( false ),
2014-02-24 21:01:50 +01:00
_forceDestination ( false ), _pointPathLimit ( MAX_POINT_PATH_LENGTH ), _straightLine ( false ),
2020-08-14 17:06:03 +02:00
_endPosition ( G3D :: Vector3 :: zero ()), _sourceUnit ( owner ), _navMesh ( nullptr ),
_navMeshQuery ( nullptr )
2012-04-09 14:51:03 +02:00
{
2014-01-18 01:36:12 +01:00
memset ( _pathPolyRefs , 0 , sizeof ( _pathPolyRefs ));
2019-03-03 16:54:19 +01:00
TC_LOG_DEBUG ( "maps.mmaps" , "++ PathGenerator::PathGenerator for %s" , _sourceUnit -> GetGUID (). ToString (). c_str ());
2012-04-09 14:51:03 +02:00
2018-03-03 15:39:19 +01:00
uint32 mapId = PhasingHandler :: GetTerrainMapId ( _sourceUnit -> GetPhaseShift (), _sourceUnit -> GetMap (), _sourceUnit -> GetPositionX (), _sourceUnit -> GetPositionY ());
2018-03-03 14:29:53 +01:00
if ( DisableMgr :: IsPathfindingEnabled ( _sourceUnit -> GetMapId ()))
2012-04-09 14:51:03 +02:00
{
2014-01-18 01:36:12 +01:00
MMAP :: MMapManager * mmap = MMAP :: MMapFactory :: createOrGetMMapManager ();
2018-02-20 22:33:38 +01:00
_navMesh = mmap -> GetNavMesh ( mapId );
_navMeshQuery = mmap -> GetNavMeshQuery ( mapId , _sourceUnit -> GetInstanceId ());
2012-04-09 14:51:03 +02:00
}
2012-09-05 23:47:02 +02:00
CreateFilter ();
2012-04-09 14:51:03 +02:00
}
2012-08-25 21:55:03 -05:00
PathGenerator ::~ PathGenerator ()
2012-04-09 14:51:03 +02:00
{
2019-03-03 16:54:19 +01:00
TC_LOG_DEBUG ( "maps.mmaps" , "++ PathGenerator::~PathGenerator() for %s" , _sourceUnit -> GetGUID (). ToString (). c_str ());
2012-04-09 14:51:03 +02:00
}
2014-02-24 21:01:50 +01:00
bool PathGenerator :: CalculatePath ( float destX , float destY , float destZ , bool forceDest , bool straightLine )
2012-04-09 14:51:03 +02:00
{
2012-09-04 23:33:11 +02:00
float x , y , z ;
2012-09-05 23:47:02 +02:00
_sourceUnit -> GetPosition ( x , y , z );
2012-09-04 23:33:11 +02:00
if ( ! Trinity :: IsValidMapCoord ( destX , destY , destZ ) || ! Trinity :: IsValidMapCoord ( x , y , z ))
2012-04-09 14:51:03 +02:00
return false ;
2016-06-04 11:26:57 +01:00
TC_METRIC_EVENT ( "mmap_events" , "CalculatePath" , "" );
2013-05-07 12:02:22 +02:00
G3D :: Vector3 dest ( destX , destY , destZ );
2012-09-05 23:47:02 +02:00
SetEndPosition ( dest );
2012-04-09 14:51:03 +02:00
2013-05-07 12:02:22 +02:00
G3D :: Vector3 start ( x , y , z );
2012-09-05 23:47:02 +02:00
SetStartPosition ( start );
2012-04-09 14:51:03 +02:00
2014-01-18 01:36:12 +01:00
_forceDestination = forceDest ;
2014-02-24 21:01:50 +01:00
_straightLine = straightLine ;
2014-01-18 01:36:12 +01:00
2019-03-03 16:54:19 +01:00
TC_LOG_DEBUG ( "maps.mmaps" , "++ PathGenerator::CalculatePath() for %s" , _sourceUnit -> GetGUID (). ToString (). c_str ());
2012-04-09 14:51:03 +02:00
// make sure navMesh works - we can run on map w/o mmap
// check if the start and end point have a .mmtile loaded (can we pass via not loaded tile on the way?)
2014-01-18 01:36:12 +01:00
if ( ! _navMesh || ! _navMeshQuery || _sourceUnit -> HasUnitState ( UNIT_STATE_IGNORE_PATHFINDING ) ||
! HaveTile ( start ) || ! HaveTile ( dest ))
2012-04-09 14:51:03 +02:00
{
2014-01-18 01:36:12 +01:00
BuildShortcut ();
2012-09-05 23:47:02 +02:00
_type = PathType ( PATHFIND_NORMAL | PATHFIND_NOT_USING_PATH );
2012-04-09 14:51:03 +02:00
return true ;
}
2012-09-05 23:47:02 +02:00
UpdateFilter ();
2012-04-09 14:51:03 +02:00
2014-01-18 01:36:12 +01:00
BuildPolyPath ( start , dest );
return true ;
}
2012-04-09 14:51:03 +02:00
2014-01-18 01:36:12 +01:00
dtPolyRef PathGenerator :: GetPathPolyByPosition ( dtPolyRef const * polyPath , uint32 polyPathSize , float const * point , float * distance ) const
{
if ( ! polyPath || ! polyPathSize )
return INVALID_POLYREF ;
2012-04-09 14:51:03 +02:00
2014-01-18 01:36:12 +01:00
dtPolyRef nearestPoly = INVALID_POLYREF ;
float minDist2d = FLT_MAX ;
float minDist3d = 0.0f ;
2012-04-09 14:51:03 +02:00
2014-01-18 01:36:12 +01:00
for ( uint32 i = 0 ; i < polyPathSize ; ++ i )
{
float closestPoint [ VERTEX_SIZE ];
2020-08-14 17:06:03 +02:00
if ( dtStatusFailed ( _navMeshQuery -> closestPointOnPoly ( polyPath [ i ], point , closestPoint , nullptr )))
2014-01-18 01:36:12 +01:00
continue ;
2012-04-09 14:51:03 +02:00
2014-01-18 01:36:12 +01:00
float d = dtVdist2DSqr ( point , closestPoint );
if ( d < minDist2d )
{
minDist2d = d ;
nearestPoly = polyPath [ i ];
minDist3d = dtVdistSqr ( point , closestPoint );
}
2012-04-09 14:51:03 +02:00
2014-01-18 01:36:12 +01:00
if ( minDist2d < 1.0f ) // shortcut out - close enough for us
break ;
}
if ( distance )
2015-06-28 19:51:59 +02:00
* distance = dtMathSqrtf ( minDist3d );
2014-01-18 01:36:12 +01:00
return ( minDist2d < 3.0f ) ? nearestPoly : INVALID_POLYREF ;
}
2012-04-09 14:51:03 +02:00
2014-01-18 01:36:12 +01:00
dtPolyRef PathGenerator :: GetPolyByLocation ( float const * point , float * distance ) const
{
// first we check the current path
// if the current path doesn't contain the current poly,
// we need to use the expensive navMesh.findNearestPoly
dtPolyRef polyRef = GetPathPolyByPosition ( _pathPolyRefs , _polyLength , point , distance );
if ( polyRef != INVALID_POLYREF )
return polyRef ;
// we don't have it in our old path
// try to get it by findNearestPoly()
// first try with low search box
float extents [ VERTEX_SIZE ] = { 3.0f , 5.0f , 3.0f }; // bounds of poly search area
float closestPoint [ VERTEX_SIZE ] = { 0.0f , 0.0f , 0.0f };
if ( dtStatusSucceed ( _navMeshQuery -> findNearestPoly ( point , extents , & _filter , & polyRef , closestPoint )) && polyRef != INVALID_POLYREF )
2012-04-09 14:51:03 +02:00
{
2014-01-18 01:36:12 +01:00
* distance = dtVdist ( closestPoint , point );
return polyRef ;
2012-04-09 14:51:03 +02:00
}
2014-01-18 01:36:12 +01:00
// still nothing ..
// try with bigger search box
// Note that the extent should not overlap more than 128 polygons in the navmesh (see dtNavMeshQuery::findNearestPoly)
extents [ 1 ] = 50.0f ;
2012-04-09 14:51:03 +02:00
2014-01-18 01:36:12 +01:00
if ( dtStatusSucceed ( _navMeshQuery -> findNearestPoly ( point , extents , & _filter , & polyRef , closestPoint )) && polyRef != INVALID_POLYREF )
2012-04-09 14:51:03 +02:00
{
2014-01-18 01:36:12 +01:00
* distance = dtVdist ( closestPoint , point );
return polyRef ;
2012-04-09 14:51:03 +02:00
}
2014-01-18 01:36:12 +01:00
return INVALID_POLYREF ;
}
void PathGenerator :: BuildPolyPath ( G3D :: Vector3 const & startPos , G3D :: Vector3 const & endPos )
{
// *** getting start/end poly logic ***
float distToStartPoly , distToEndPoly ;
float startPoint [ VERTEX_SIZE ] = { startPos . y , startPos . z , startPos . x };
float endPoint [ VERTEX_SIZE ] = { endPos . y , endPos . z , endPos . x };
dtPolyRef startPoly = GetPolyByLocation ( startPoint , & distToStartPoly );
dtPolyRef endPoly = GetPolyByLocation ( endPoint , & distToEndPoly );
2012-04-09 14:51:03 +02:00
2014-01-18 01:36:12 +01:00
// we have a hole in our mesh
// make shortcut path and mark it as NOPATH ( with flying and swimming exception )
// its up to caller how he will use this info
if ( startPoly == INVALID_POLYREF || endPoly == INVALID_POLYREF )
2012-04-09 14:51:03 +02:00
{
2019-03-03 16:54:19 +01:00
TC_LOG_DEBUG ( "maps.mmaps" , "++ BuildPolyPath :: (startPoly == 0 || endPoly == 0)" );
2014-01-18 01:36:12 +01:00
BuildShortcut ();
bool path = _sourceUnit -> GetTypeId () == TYPEID_UNIT && _sourceUnit -> ToCreature () -> CanFly ();
bool waterPath = _sourceUnit -> GetTypeId () == TYPEID_UNIT && _sourceUnit -> ToCreature () -> CanSwim ();
if ( waterPath )
{
// Check both start and end points, if they're both in water, then we can *safely* let the creature move
for ( uint32 i = 0 ; i < _pathPoints . size (); ++ i )
{
2021-05-29 21:21:37 +02:00
ZLiquidStatus status = _sourceUnit -> GetMap () -> GetLiquidStatus ( _sourceUnit -> GetPhaseShift (), _pathPoints [ i ]. x , _pathPoints [ i ]. y , _pathPoints [ i ]. z , map_liquidHeaderTypeFlags :: AllLiquids , nullptr , _sourceUnit -> GetCollisionHeight ());
2014-01-18 01:36:12 +01:00
// One of the points is not in the water, cancel movement.
if ( status == LIQUID_MAP_NO_WATER )
{
waterPath = false ;
break ;
}
}
}
_type = ( path || waterPath ) ? PathType ( PATHFIND_NORMAL | PATHFIND_NOT_USING_PATH ) : PATHFIND_NOPATH ;
return ;
2012-09-03 20:30:30 +02:00
}
2012-04-09 14:51:03 +02:00
2014-01-18 01:36:12 +01:00
// we may need a better number here
bool farFromPoly = ( distToStartPoly > 7.0f || distToEndPoly > 7.0f );
if ( farFromPoly )
{
2019-03-03 16:54:19 +01:00
TC_LOG_DEBUG ( "maps.mmaps" , "++ BuildPolyPath :: farFromPoly distToStartPoly=%.3f distToEndPoly=%.3f" , distToStartPoly , distToEndPoly );
2013-10-05 18:11:39 -05:00
2014-01-18 01:36:12 +01:00
bool buildShotrcut = false ;
if ( _sourceUnit -> GetTypeId () == TYPEID_UNIT )
{
Creature * owner = ( Creature * ) _sourceUnit ;
G3D :: Vector3 const & p = ( distToStartPoly > 7.0f ) ? startPos : endPos ;
2018-03-28 22:01:22 +02:00
if ( _sourceUnit -> GetMap () -> IsUnderWater ( _sourceUnit -> GetPhaseShift (), p . x , p . y , p . z ))
2014-01-18 01:36:12 +01:00
{
2019-03-03 16:54:19 +01:00
TC_LOG_DEBUG ( "maps.mmaps" , "++ BuildPolyPath :: underWater case" );
2014-01-18 01:36:12 +01:00
if ( owner -> CanSwim ())
buildShotrcut = true ;
}
else
{
2019-03-03 16:54:19 +01:00
TC_LOG_DEBUG ( "maps.mmaps" , "++ BuildPolyPath :: flying case" );
2014-01-18 01:36:12 +01:00
if ( owner -> CanFly ())
buildShotrcut = true ;
}
}
if ( buildShotrcut )
{
BuildShortcut ();
_type = PathType ( PATHFIND_NORMAL | PATHFIND_NOT_USING_PATH );
return ;
}
else
{
float closestPoint [ VERTEX_SIZE ];
// we may want to use closestPointOnPolyBoundary instead
2020-08-14 17:06:03 +02:00
if ( dtStatusSucceed ( _navMeshQuery -> closestPointOnPoly ( endPoly , endPoint , closestPoint , nullptr )))
2014-01-18 01:36:12 +01:00
{
dtVcopy ( endPoint , closestPoint );
SetActualEndPosition ( G3D :: Vector3 ( endPoint [ 2 ], endPoint [ 0 ], endPoint [ 1 ]));
}
_type = PATHFIND_INCOMPLETE ;
}
}
// *** poly path generating logic ***
// start and end are on same polygon
// just need to move in straight line
if ( startPoly == endPoly )
2013-12-30 15:19:50 -05:00
{
2019-03-03 16:54:19 +01:00
TC_LOG_DEBUG ( "maps.mmaps" , "++ BuildPolyPath :: (startPoly == endPoly)" );
2014-01-18 01:36:12 +01:00
BuildShortcut ();
_pathPolyRefs [ 0 ] = startPoly ;
_polyLength = 1 ;
_type = farFromPoly ? PATHFIND_INCOMPLETE : PATHFIND_NORMAL ;
2019-03-03 16:54:19 +01:00
TC_LOG_DEBUG ( "maps.mmaps" , "++ BuildPolyPath :: path type %d" , _type );
2014-01-18 01:36:12 +01:00
return ;
2013-12-30 15:19:50 -05:00
}
2012-04-09 14:51:03 +02:00
2014-01-18 01:36:12 +01:00
// look for startPoly/endPoly in current path
/// @todo we can merge it with getPathPolyByPosition() loop
bool startPolyFound = false ;
bool endPolyFound = false ;
uint32 pathStartIndex = 0 ;
uint32 pathEndIndex = 0 ;
if ( _polyLength )
{
for (; pathStartIndex < _polyLength ; ++ pathStartIndex )
{
2014-02-27 21:03:57 +01:00
// here to catch few bugs
if ( _pathPolyRefs [ pathStartIndex ] == INVALID_POLYREF )
{
2019-03-03 16:54:19 +01:00
TC_LOG_ERROR ( "maps.mmaps" , "Invalid poly ref in BuildPolyPath. _polyLength: %u, pathStartIndex: %u,"
2014-02-27 21:03:57 +01:00
" startPos: %s, endPos: %s, mapid: %u" ,
_polyLength , pathStartIndex , startPos . toString (). c_str (), endPos . toString (). c_str (),
_sourceUnit -> GetMapId ());
break ;
}
2014-01-18 01:36:12 +01:00
if ( _pathPolyRefs [ pathStartIndex ] == startPoly )
{
startPolyFound = true ;
break ;
}
}
for ( pathEndIndex = _polyLength - 1 ; pathEndIndex > pathStartIndex ; -- pathEndIndex )
if ( _pathPolyRefs [ pathEndIndex ] == endPoly )
{
endPolyFound = true ;
break ;
}
}
if ( startPolyFound && endPolyFound )
{
2019-03-03 16:54:19 +01:00
TC_LOG_DEBUG ( "maps.mmaps" , "++ BuildPolyPath :: (startPolyFound && endPolyFound)" );
2014-01-18 01:36:12 +01:00
// we moved along the path and the target did not move out of our old poly-path
// our path is a simple subpath case, we have all the data we need
// just "cut" it out
_polyLength = pathEndIndex - pathStartIndex + 1 ;
memmove ( _pathPolyRefs , _pathPolyRefs + pathStartIndex , _polyLength * sizeof ( dtPolyRef ));
}
else if ( startPolyFound && ! endPolyFound )
{
2019-03-03 16:54:19 +01:00
TC_LOG_DEBUG ( "maps.mmaps" , "++ BuildPolyPath :: (startPolyFound && !endPolyFound)" );
2014-01-18 01:36:12 +01:00
// we are moving on the old path but target moved out
// so we have atleast part of poly-path ready
_polyLength -= pathStartIndex ;
// try to adjust the suffix of the path instead of recalculating entire length
// at given interval the target cannot get too far from its last location
// thus we have less poly to cover
// sub-path of optimal path is optimal
// take ~80% of the original length
/// @todo play with the values here
uint32 prefixPolyLength = uint32 ( _polyLength * 0.8f + 0.5f );
memmove ( _pathPolyRefs , _pathPolyRefs + pathStartIndex , prefixPolyLength * sizeof ( dtPolyRef ));
dtPolyRef suffixStartPoly = _pathPolyRefs [ prefixPolyLength - 1 ];
// we need any point on our suffix start poly to generate poly-path, so we need last poly in prefix data
float suffixEndPoint [ VERTEX_SIZE ];
2020-08-14 17:06:03 +02:00
if ( dtStatusFailed ( _navMeshQuery -> closestPointOnPoly ( suffixStartPoly , endPoint , suffixEndPoint , nullptr )))
2014-01-18 01:36:12 +01:00
{
// we can hit offmesh connection as last poly - closestPointOnPoly() don't like that
// try to recover by using prev polyref
-- prefixPolyLength ;
suffixStartPoly = _pathPolyRefs [ prefixPolyLength - 1 ];
2020-08-14 17:06:03 +02:00
if ( dtStatusFailed ( _navMeshQuery -> closestPointOnPoly ( suffixStartPoly , endPoint , suffixEndPoint , nullptr )))
2014-01-18 01:36:12 +01:00
{
// suffixStartPoly is still invalid, error state
BuildShortcut ();
_type = PATHFIND_NOPATH ;
return ;
}
}
// generate suffix
uint32 suffixPolyLength = 0 ;
2014-02-24 21:01:50 +01:00
dtStatus dtResult ;
if ( _straightLine )
{
float hit = 0 ;
float hitNormal [ 3 ];
memset ( hitNormal , 0 , sizeof ( hitNormal ));
dtResult = _navMeshQuery -> raycast (
suffixStartPoly ,
suffixEndPoint ,
endPoint ,
& _filter ,
& hit ,
hitNormal ,
_pathPolyRefs + prefixPolyLength - 1 ,
( int * ) & suffixPolyLength ,
MAX_PATH_LENGTH - prefixPolyLength );
// raycast() sets hit to FLT_MAX if there is a ray between start and end
if ( hit != FLT_MAX )
{
// the ray hit something, return no path instead of the incomplete one
_type = PATHFIND_NOPATH ;
return ;
}
}
else
{
dtResult = _navMeshQuery -> findPath (
suffixStartPoly , // start polygon
endPoly , // end polygon
suffixEndPoint , // start position
endPoint , // end position
& _filter , // polygon search filter
_pathPolyRefs + prefixPolyLength - 1 , // [out] path
( int * ) & suffixPolyLength ,
MAX_PATH_LENGTH - prefixPolyLength ); // max number of polygons in output path
}
2014-01-18 01:36:12 +01:00
if ( ! suffixPolyLength || dtStatusFailed ( dtResult ))
{
// this is probably an error state, but we'll leave it
// and hopefully recover on the next Update
// we still need to copy our preffix
2019-03-03 16:54:19 +01:00
TC_LOG_ERROR ( "maps.mmaps" , "Path Build failed \n %s" , _sourceUnit -> GetDebugInfo (). c_str ());
2014-01-18 01:36:12 +01:00
}
2019-03-03 16:54:19 +01:00
TC_LOG_DEBUG ( "maps.mmaps" , "++ m_polyLength=%u prefixPolyLength=%u suffixPolyLength=%u" , _polyLength , prefixPolyLength , suffixPolyLength );
2014-01-18 01:36:12 +01:00
// new path = prefix + suffix - overlap
_polyLength = prefixPolyLength + suffixPolyLength - 1 ;
}
else
{
2019-03-03 16:54:19 +01:00
TC_LOG_DEBUG ( "maps.mmaps" , "++ BuildPolyPath :: (!startPolyFound && !endPolyFound)" );
2014-01-18 01:36:12 +01:00
// either we have no path at all -> first run
// or something went really wrong -> we aren't moving along the path to the target
// just generate new path
// free and invalidate old path data
Clear ();
2014-02-24 21:01:50 +01:00
dtStatus dtResult ;
if ( _straightLine )
{
float hit = 0 ;
float hitNormal [ 3 ];
memset ( hitNormal , 0 , sizeof ( hitNormal ));
dtResult = _navMeshQuery -> raycast (
startPoly ,
startPoint ,
endPoint ,
& _filter ,
& hit ,
hitNormal ,
_pathPolyRefs ,
( int * ) & _polyLength ,
MAX_PATH_LENGTH );
// raycast() sets hit to FLT_MAX if there is a ray between start and end
if ( hit != FLT_MAX )
{
// the ray hit something, return no path instead of the incomplete one
_type = PATHFIND_NOPATH ;
return ;
}
}
else
{
dtResult = _navMeshQuery -> findPath (
startPoly , // start polygon
endPoly , // end polygon
startPoint , // start position
endPoint , // end position
& _filter , // polygon search filter
_pathPolyRefs , // [out] path
( int * ) & _polyLength ,
MAX_PATH_LENGTH ); // max number of polygons in output path
}
2014-01-18 01:36:12 +01:00
if ( ! _polyLength || dtStatusFailed ( dtResult ))
{
// only happens if we passed bad data to findPath(), or navmesh is messed up
2019-03-03 16:54:19 +01:00
TC_LOG_ERROR ( "maps.mmaps" , "%s's Path Build failed: 0 length path" , _sourceUnit -> GetGUID (). ToString (). c_str ());
2014-01-18 01:36:12 +01:00
BuildShortcut ();
_type = PATHFIND_NOPATH ;
return ;
}
}
// by now we know what type of path we can get
if ( _pathPolyRefs [ _polyLength - 1 ] == endPoly && ! ( _type & PATHFIND_INCOMPLETE ))
_type = PATHFIND_NORMAL ;
else
_type = PATHFIND_INCOMPLETE ;
// generate the point-path out of our up-to-date poly-path
BuildPointPath ( startPoint , endPoint );
}
void PathGenerator :: BuildPointPath ( const float * startPoint , const float * endPoint )
{
float pathPoints [ MAX_POINT_PATH_LENGTH * VERTEX_SIZE ];
uint32 pointCount = 0 ;
dtStatus dtResult = DT_FAILURE ;
2014-02-24 21:01:50 +01:00
if ( _straightLine )
{
dtResult = DT_SUCCESS ;
2014-07-12 13:13:38 +01:00
pointCount = 1 ;
memcpy ( & pathPoints [ VERTEX_SIZE * 0 ], startPoint , sizeof ( float ) * 3 ); // first point
// path has to be split into polygons with dist SMOOTH_PATH_STEP_SIZE between them
G3D :: Vector3 startVec = G3D :: Vector3 ( startPoint [ 0 ], startPoint [ 1 ], startPoint [ 2 ]);
G3D :: Vector3 endVec = G3D :: Vector3 ( endPoint [ 0 ], endPoint [ 1 ], endPoint [ 2 ]);
G3D :: Vector3 diffVec = ( endVec - startVec );
G3D :: Vector3 prevVec = startVec ;
float len = diffVec . length ();
diffVec *= SMOOTH_PATH_STEP_SIZE / len ;
while ( len > SMOOTH_PATH_STEP_SIZE )
{
len -= SMOOTH_PATH_STEP_SIZE ;
prevVec += diffVec ;
pathPoints [ VERTEX_SIZE * pointCount + 0 ] = prevVec . x ;
pathPoints [ VERTEX_SIZE * pointCount + 1 ] = prevVec . y ;
pathPoints [ VERTEX_SIZE * pointCount + 2 ] = prevVec . z ;
++ pointCount ;
}
memcpy ( & pathPoints [ VERTEX_SIZE * pointCount ], endPoint , sizeof ( float ) * 3 ); // last point
++ pointCount ;
2014-02-24 21:01:50 +01:00
}
else if ( _useStraightPath )
2014-01-18 01:36:12 +01:00
{
dtResult = _navMeshQuery -> findStraightPath (
startPoint , // start position
endPoint , // end position
_pathPolyRefs , // current path
_polyLength , // lenth of current path
pathPoints , // [out] path corner points
2020-08-14 17:06:03 +02:00
nullptr , // [out] flags
nullptr , // [out] shortened path
2014-01-18 01:36:12 +01:00
( int * ) & pointCount ,
_pointPathLimit ); // maximum number of points/polygons to use
}
else
{
dtResult = FindSmoothPath (
startPoint , // start position
endPoint , // end position
_pathPolyRefs , // current path
_polyLength , // length of current path
pathPoints , // [out] path corner points
( int * ) & pointCount ,
_pointPathLimit ); // maximum number of points
}
if ( pointCount < 2 || dtStatusFailed ( dtResult ))
{
// only happens if pass bad data to findStraightPath or navmesh is broken
// single point paths can be generated here
/// @todo check the exact cases
2019-03-03 16:54:19 +01:00
TC_LOG_DEBUG ( "maps.mmaps" , "++ PathGenerator::BuildPointPath FAILED! path sized %d returned \n " , pointCount );
2014-01-18 01:36:12 +01:00
BuildShortcut ();
2019-04-07 20:15:40 +02:00
_type = PathType ( _type | PATHFIND_NOPATH );
2014-01-18 01:36:12 +01:00
return ;
}
else if ( pointCount == _pointPathLimit )
{
2019-03-03 16:54:19 +01:00
TC_LOG_DEBUG ( "maps.mmaps" , "++ PathGenerator::BuildPointPath FAILED! path sized %d returned, lower than limit set to %d" , pointCount , _pointPathLimit );
2014-01-18 01:36:12 +01:00
BuildShortcut ();
2019-04-07 20:15:40 +02:00
_type = PathType ( _type | PATHFIND_SHORT );
2014-01-18 01:36:12 +01:00
return ;
}
_pathPoints . resize ( pointCount );
for ( uint32 i = 0 ; i < pointCount ; ++ i )
_pathPoints [ i ] = G3D :: Vector3 ( pathPoints [ i * VERTEX_SIZE + 2 ], pathPoints [ i * VERTEX_SIZE ], pathPoints [ i * VERTEX_SIZE + 1 ]);
NormalizePath ();
// first point is always our current location - we need the next one
SetActualEndPosition ( _pathPoints [ pointCount - 1 ]);
// force the given destination, if needed
if ( _forceDestination &&
( ! ( _type & PATHFIND_NORMAL ) || ! InRange ( GetEndPosition (), GetActualEndPosition (), 1.0f , 1.0f )))
{
// we may want to keep partial subpath
if ( Dist3DSqr ( GetActualEndPosition (), GetEndPosition ()) < 0.3f * Dist3DSqr ( GetStartPosition (), GetEndPosition ()))
{
SetActualEndPosition ( GetEndPosition ());
_pathPoints [ _pathPoints . size () - 1 ] = GetEndPosition ();
}
else
{
SetActualEndPosition ( GetEndPosition ());
BuildShortcut ();
}
_type = PathType ( PATHFIND_NORMAL | PATHFIND_NOT_USING_PATH );
}
2019-03-03 16:54:19 +01:00
TC_LOG_DEBUG ( "maps.mmaps" , "++ PathGenerator::BuildPointPath path type %d size %d poly-size %d" , _type , pointCount , _polyLength );
2014-01-18 01:36:12 +01:00
}
void PathGenerator :: NormalizePath ()
{
for ( uint32 i = 0 ; i < _pathPoints . size (); ++ i )
_sourceUnit -> UpdateAllowedPositionZ ( _pathPoints [ i ]. x , _pathPoints [ i ]. y , _pathPoints [ i ]. z );
}
void PathGenerator :: BuildShortcut ()
{
2019-03-03 16:54:19 +01:00
TC_LOG_DEBUG ( "maps.mmaps" , "++ BuildShortcut :: making shortcut" );
2014-01-18 01:36:12 +01:00
Clear ();
// make two point path, our curr pos is the start, and dest is the end
_pathPoints . resize ( 2 );
// set start and a default next position
_pathPoints [ 0 ] = GetStartPosition ();
_pathPoints [ 1 ] = GetActualEndPosition ();
NormalizePath ();
_type = PATHFIND_SHORTCUT ;
2012-04-09 14:51:03 +02:00
}
2012-09-05 23:47:02 +02:00
void PathGenerator :: CreateFilter ()
2012-04-09 14:51:03 +02:00
{
2014-01-18 01:36:12 +01:00
uint16 includeFlags = 0 ;
2012-04-09 14:51:03 +02:00
uint16 excludeFlags = 0 ;
2014-01-18 01:36:12 +01:00
if ( _sourceUnit -> GetTypeId () == TYPEID_UNIT )
2012-04-09 14:51:03 +02:00
{
2014-01-18 01:36:12 +01:00
Creature * creature = ( Creature * ) _sourceUnit ;
if ( creature -> CanWalk ())
includeFlags |= NAV_GROUND ; // walk
// creatures don't take environmental damage
if ( creature -> CanSwim ())
2018-04-22 01:01:48 +02:00
includeFlags |= ( NAV_WATER | NAV_MAGMA_SLIME ); // swim
2014-01-18 01:36:12 +01:00
}
else // assume Player
{
// perfect support not possible, just stay 'safe'
2018-04-22 01:01:48 +02:00
includeFlags |= ( NAV_GROUND | NAV_WATER | NAV_MAGMA_SLIME );
2012-04-09 14:51:03 +02:00
}
2012-09-05 23:47:02 +02:00
_filter . setIncludeFlags ( includeFlags );
_filter . setExcludeFlags ( excludeFlags );
2012-04-09 14:51:03 +02:00
2012-09-05 23:47:02 +02:00
UpdateFilter ();
2012-04-09 14:51:03 +02:00
}
2012-09-05 23:47:02 +02:00
void PathGenerator :: UpdateFilter ()
2012-04-09 14:51:03 +02:00
{
2014-01-18 01:36:12 +01:00
// allow creatures to cheat and use different movement types if they are moved
// forcefully into terrain they can't normally move in
if ( _sourceUnit -> IsInWater () || _sourceUnit -> IsUnderWater ())
{
uint16 includedFlags = _filter . getIncludeFlags ();
includedFlags |= GetNavTerrain ( _sourceUnit -> GetPositionX (),
_sourceUnit -> GetPositionY (),
_sourceUnit -> GetPositionZ ());
2012-04-09 14:51:03 +02:00
2014-01-18 01:36:12 +01:00
_filter . setIncludeFlags ( includedFlags );
}
2013-10-05 18:11:39 -05:00
}
2018-04-22 01:01:48 +02:00
NavTerrainFlag PathGenerator :: GetNavTerrain ( float x , float y , float z )
2013-10-05 18:11:39 -05:00
{
2014-01-18 01:36:12 +01:00
LiquidData data ;
2021-05-29 21:21:37 +02:00
ZLiquidStatus liquidStatus = _sourceUnit -> GetMap () -> GetLiquidStatus ( _sourceUnit -> GetPhaseShift (), x , y , z , map_liquidHeaderTypeFlags :: AllLiquids , & data , _sourceUnit -> GetCollisionHeight ());
2014-01-18 01:36:12 +01:00
if ( liquidStatus == LIQUID_MAP_NO_WATER )
return NAV_GROUND ;
2013-10-05 18:11:39 -05:00
2021-05-29 21:21:37 +02:00
data . type_flags &= map_liquidHeaderTypeFlags :: DarkWater ;
2014-01-18 01:36:12 +01:00
switch ( data . type_flags )
2013-10-05 18:11:39 -05:00
{
2021-05-29 21:21:37 +02:00
case map_liquidHeaderTypeFlags :: Water :
case map_liquidHeaderTypeFlags :: Ocean :
2014-01-18 01:36:12 +01:00
return NAV_WATER ;
2021-05-29 21:21:37 +02:00
case map_liquidHeaderTypeFlags :: Magma :
case map_liquidHeaderTypeFlags :: Slime :
2018-04-22 01:01:48 +02:00
return NAV_MAGMA_SLIME ;
2014-01-18 01:36:12 +01:00
default :
return NAV_GROUND ;
2013-10-05 18:11:39 -05:00
}
}
2014-01-18 01:36:12 +01:00
bool PathGenerator :: HaveTile ( const G3D :: Vector3 & p ) const
2013-10-05 18:11:39 -05:00
{
2014-01-18 01:36:12 +01:00
int tx = - 1 , ty = - 1 ;
float point [ VERTEX_SIZE ] = { p . y , p . z , p . x };
2012-04-09 14:51:03 +02:00
2014-01-18 01:36:12 +01:00
_navMesh -> calcTileLoc ( point , & tx , & ty );
2012-04-09 14:51:03 +02:00
2014-01-18 01:36:12 +01:00
/// Workaround
/// For some reason, often the tx and ty variables wont get a valid value
/// Use this check to prevent getting negative tile coords and crashing on getTileAt
if ( tx < 0 || ty < 0 )
return false ;
2013-10-05 18:11:39 -05:00
2020-08-14 17:06:03 +02:00
return ( _navMesh -> getTileAt ( tx , ty , 0 ) != nullptr );
2014-01-18 01:36:12 +01:00
}
2013-10-05 18:11:39 -05:00
2014-01-18 01:36:12 +01:00
uint32 PathGenerator :: FixupCorridor ( dtPolyRef * path , uint32 npath , uint32 maxPath , dtPolyRef const * visited , uint32 nvisited )
{
int32 furthestPath = - 1 ;
int32 furthestVisited = - 1 ;
// Find furthest common polygon.
for ( int32 i = npath - 1 ; i >= 0 ; -- i )
2013-10-05 18:11:39 -05:00
{
2014-01-18 01:36:12 +01:00
bool found = false ;
for ( int32 j = nvisited - 1 ; j >= 0 ; -- j )
{
if ( path [ i ] == visited [ j ])
{
furthestPath = i ;
furthestVisited = j ;
found = true ;
}
}
if ( found )
break ;
2012-04-09 14:51:03 +02:00
}
2013-10-05 18:11:39 -05:00
2014-01-18 01:36:12 +01:00
// If no intersection found just return current path.
if ( furthestPath == - 1 || furthestVisited == - 1 )
return npath ;
// Concatenate paths.
// Adjust beginning of the buffer to include the visited.
uint32 req = nvisited - furthestVisited ;
uint32 orig = uint32 ( furthestPath + 1 ) < npath ? furthestPath + 1 : npath ;
uint32 size = npath > orig ? npath - orig : 0 ;
if ( req + size > maxPath )
size = maxPath - req ;
2013-10-05 18:11:39 -05:00
2014-01-18 01:36:12 +01:00
if ( size )
memmove ( path + req , path + orig , size * sizeof ( dtPolyRef ));
2013-10-05 18:11:39 -05:00
2014-01-18 01:36:12 +01:00
// Store visited
for ( uint32 i = 0 ; i < req ; ++ i )
path [ i ] = visited [( nvisited - 1 ) - i ];
return req + size ;
2013-10-05 18:11:39 -05:00
}
2014-01-18 01:36:12 +01:00
bool PathGenerator :: GetSteerTarget ( float const * startPos , float const * endPos ,
float minTargetDist , dtPolyRef const * path , uint32 pathSize ,
float * steerPos , unsigned char & steerPosFlag , dtPolyRef & steerPosRef )
2013-10-05 18:11:39 -05:00
{
2014-01-18 01:36:12 +01:00
// Find steer target.
static const uint32 MAX_STEER_POINTS = 3 ;
float steerPath [ MAX_STEER_POINTS * VERTEX_SIZE ];
unsigned char steerPathFlags [ MAX_STEER_POINTS ];
dtPolyRef steerPathPolys [ MAX_STEER_POINTS ];
uint32 nsteerPath = 0 ;
dtStatus dtResult = _navMeshQuery -> findStraightPath ( startPos , endPos , path , pathSize ,
steerPath , steerPathFlags , steerPathPolys , ( int * ) & nsteerPath , MAX_STEER_POINTS );
if ( ! nsteerPath || dtStatusFailed ( dtResult ))
return false ;
// Find vertex far enough to steer to.
uint32 ns = 0 ;
while ( ns < nsteerPath )
2013-10-05 18:11:39 -05:00
{
2014-01-18 01:36:12 +01:00
// Stop at Off-Mesh link or when point is further than slop away.
if (( steerPathFlags [ ns ] & DT_STRAIGHTPATH_OFFMESH_CONNECTION ) ||
! InRangeYZX ( & steerPath [ ns * VERTEX_SIZE ], startPos , minTargetDist , 1000.0f ))
break ;
ns ++ ;
}
// Failed to find good point to steer to.
if ( ns >= nsteerPath )
return false ;
dtVcopy ( steerPos , & steerPath [ ns * VERTEX_SIZE ]);
steerPos [ 1 ] = startPos [ 1 ]; // keep Z value
steerPosFlag = steerPathFlags [ ns ];
steerPosRef = steerPathPolys [ ns ];
2013-10-05 18:11:39 -05:00
2014-01-18 01:36:12 +01:00
return true ;
}
dtStatus PathGenerator :: FindSmoothPath ( float const * startPos , float const * endPos ,
dtPolyRef const * polyPath , uint32 polyPathSize ,
float * smoothPath , int * smoothPathSize , uint32 maxSmoothPathSize )
{
* smoothPathSize = 0 ;
uint32 nsmoothPath = 0 ;
dtPolyRef polys [ MAX_PATH_LENGTH ];
memcpy ( polys , polyPath , sizeof ( dtPolyRef ) * polyPathSize );
uint32 npolys = polyPathSize ;
float iterPos [ VERTEX_SIZE ], targetPos [ VERTEX_SIZE ];
if ( dtStatusFailed ( _navMeshQuery -> closestPointOnPolyBoundary ( polys [ 0 ], startPos , iterPos )))
return DT_FAILURE ;
if ( dtStatusFailed ( _navMeshQuery -> closestPointOnPolyBoundary ( polys [ npolys - 1 ], endPos , targetPos )))
return DT_FAILURE ;
dtVcopy ( & smoothPath [ nsmoothPath * VERTEX_SIZE ], iterPos );
nsmoothPath ++ ;
// Move towards target a small advancement at a time until target reached or
// when ran out of memory to store the path.
while ( npolys && nsmoothPath < maxSmoothPathSize )
{
// Find location to steer towards.
float steerPos [ VERTEX_SIZE ];
unsigned char steerPosFlag ;
dtPolyRef steerPosRef = INVALID_POLYREF ;
if ( ! GetSteerTarget ( iterPos , targetPos , SMOOTH_PATH_SLOP , polys , npolys , steerPos , steerPosFlag , steerPosRef ))
break ;
2014-07-17 15:42:57 +02:00
bool endOfPath = ( steerPosFlag & DT_STRAIGHTPATH_END ) != 0 ;
bool offMeshConnection = ( steerPosFlag & DT_STRAIGHTPATH_OFFMESH_CONNECTION ) != 0 ;
2014-01-18 01:36:12 +01:00
// Find movement delta.
float delta [ VERTEX_SIZE ];
dtVsub ( delta , steerPos , iterPos );
2015-06-28 19:51:59 +02:00
float len = dtMathSqrtf ( dtVdot ( delta , delta ));
2014-01-18 01:36:12 +01:00
// If the steer target is end of path or off-mesh link, do not move past the location.
if (( endOfPath || offMeshConnection ) && len < SMOOTH_PATH_STEP_SIZE )
len = 1.0f ;
else
len = SMOOTH_PATH_STEP_SIZE / len ;
float moveTgt [ VERTEX_SIZE ];
dtVmad ( moveTgt , iterPos , delta , len );
// Move
float result [ VERTEX_SIZE ];
const static uint32 MAX_VISIT_POLY = 16 ;
dtPolyRef visited [ MAX_VISIT_POLY ];
uint32 nvisited = 0 ;
2019-03-02 19:53:46 +01:00
if ( dtStatusFailed ( _navMeshQuery -> moveAlongSurface ( polys [ 0 ], iterPos , moveTgt , & _filter , result , visited , ( int * ) & nvisited , MAX_VISIT_POLY )))
return DT_FAILURE ;
2014-01-18 01:36:12 +01:00
npolys = FixupCorridor ( polys , npolys , MAX_PATH_LENGTH , visited , nvisited );
2019-03-02 19:53:46 +01:00
if ( dtStatusFailed ( _navMeshQuery -> getPolyHeight ( polys [ 0 ], result , & result [ 1 ])))
2019-08-18 11:15:54 +02:00
TC_LOG_DEBUG ( "maps.mmaps" , "Cannot find height at position X: %f Y: %f Z: %f for %s" , result [ 2 ], result [ 0 ], result [ 1 ], _sourceUnit -> GetDebugInfo (). c_str ());
2014-01-18 01:36:12 +01:00
result [ 1 ] += 0.5f ;
dtVcopy ( iterPos , result );
// Handle end of path and off-mesh links when close enough.
if ( endOfPath && InRangeYZX ( iterPos , steerPos , SMOOTH_PATH_SLOP , 1.0f ))
2013-10-05 18:11:39 -05:00
{
2014-01-18 01:36:12 +01:00
// Reached end of path.
dtVcopy ( iterPos , targetPos );
if ( nsmoothPath < maxSmoothPathSize )
2013-10-05 18:11:39 -05:00
{
2014-01-18 01:36:12 +01:00
dtVcopy ( & smoothPath [ nsmoothPath * VERTEX_SIZE ], iterPos );
nsmoothPath ++ ;
2013-10-05 18:11:39 -05:00
}
2014-01-18 01:36:12 +01:00
break ;
}
else if ( offMeshConnection && InRangeYZX ( iterPos , steerPos , SMOOTH_PATH_SLOP , 1.0f ))
{
// Advance the path up to and over the off-mesh connection.
dtPolyRef prevRef = INVALID_POLYREF ;
dtPolyRef polyRef = polys [ 0 ];
uint32 npos = 0 ;
while ( npos < npolys && polyRef != steerPosRef )
2013-10-05 18:11:39 -05:00
{
2014-01-18 01:36:12 +01:00
prevRef = polyRef ;
polyRef = polys [ npos ];
npos ++ ;
}
for ( uint32 i = npos ; i < npolys ; ++ i )
polys [ i - npos ] = polys [ i ];
2013-10-05 18:11:39 -05:00
2014-01-18 01:36:12 +01:00
npolys -= npos ;
// Handle the connection.
2015-07-21 00:33:36 +02:00
float connectionStartPos [ VERTEX_SIZE ], connectionEndPos [ VERTEX_SIZE ];
if ( dtStatusSucceed ( _navMesh -> getOffMeshConnectionPolyEndPoints ( prevRef , polyRef , connectionStartPos , connectionEndPos )))
2014-01-18 01:36:12 +01:00
{
if ( nsmoothPath < maxSmoothPathSize )
{
2015-07-21 00:33:36 +02:00
dtVcopy ( & smoothPath [ nsmoothPath * VERTEX_SIZE ], connectionStartPos );
2014-01-18 01:36:12 +01:00
nsmoothPath ++ ;
}
// Move position at the other side of the off-mesh link.
2015-07-21 00:33:36 +02:00
dtVcopy ( iterPos , connectionEndPos );
2019-03-02 19:53:46 +01:00
if ( dtStatusFailed ( _navMeshQuery -> getPolyHeight ( polys [ 0 ], iterPos , & iterPos [ 1 ])))
return DT_FAILURE ;
2014-01-18 01:36:12 +01:00
iterPos [ 1 ] += 0.5f ;
2013-10-05 18:11:39 -05:00
}
}
2014-01-18 01:36:12 +01:00
// Store results.
if ( nsmoothPath < maxSmoothPathSize )
{
dtVcopy ( & smoothPath [ nsmoothPath * VERTEX_SIZE ], iterPos );
nsmoothPath ++ ;
}
2013-10-05 18:11:39 -05:00
}
2014-01-18 01:36:12 +01:00
* smoothPathSize = nsmoothPath ;
// this is most likely a loop
return nsmoothPath < MAX_POINT_PATH_LENGTH ? DT_SUCCESS : DT_FAILURE ;
}
2020-09-04 13:38:24 +02:00
bool PathGenerator :: InRangeYZX ( float const * v1 , float const * v2 , float r , float h ) const
2014-01-18 01:36:12 +01:00
{
const float dx = v2 [ 0 ] - v1 [ 0 ];
const float dy = v2 [ 1 ] - v1 [ 1 ]; // elevation
const float dz = v2 [ 2 ] - v1 [ 2 ];
return ( dx * dx + dz * dz ) < r * r && fabsf ( dy ) < h ;
}
bool PathGenerator :: InRange ( G3D :: Vector3 const & p1 , G3D :: Vector3 const & p2 , float r , float h ) const
{
G3D :: Vector3 d = p1 - p2 ;
return ( d . x * d . x + d . y * d . y ) < r * r && fabsf ( d . z ) < h ;
}
float PathGenerator :: Dist3DSqr ( G3D :: Vector3 const & p1 , G3D :: Vector3 const & p2 ) const
{
return ( p1 - p2 ). squaredLength ();
2012-04-09 14:51:03 +02:00
}
2014-07-04 22:22:28 +01:00
2018-04-06 18:09:55 +02:00
void PathGenerator :: ShortenPathUntilDist ( G3D :: Vector3 const & target , float dist )
2014-07-04 22:22:28 +01:00
{
2018-04-06 18:09:55 +02:00
if ( GetPathType () == PATHFIND_BLANK || _pathPoints . size () < 2 )
2014-07-04 22:22:28 +01:00
{
2019-03-03 16:54:19 +01:00
TC_LOG_ERROR ( "maps.mmaps" , "PathGenerator::ReducePathLengthByDist called before path was successfully built" );
2014-07-04 22:22:28 +01:00
return ;
}
2018-04-06 18:09:55 +02:00
float const distSq = dist * dist ;
// the first point of the path must be outside the specified range
// (this should have really been checked by the caller...)
if (( _pathPoints [ 0 ] - target ). squaredLength () < distSq )
return ;
// check if we even need to do anything
if (( * _pathPoints . rbegin () - target ). squaredLength () >= distSq )
2014-07-04 22:22:28 +01:00
return ;
2018-04-06 18:09:55 +02:00
size_t i = _pathPoints . size () - 1 ;
2019-09-13 21:06:28 +02:00
float x , y , z , collisionHeight = _sourceUnit -> GetCollisionHeight ();
2018-04-06 18:09:55 +02:00
// find the first i s.t.:
// - _pathPoints[i] is still too close
// - _pathPoints[i-1] is too far away
// => the end point is somewhere on the line between the two
while ( 1 )
2014-07-04 22:22:28 +01:00
{
2018-04-06 18:09:55 +02:00
// we know that pathPoints[i] is too close already (from the previous iteration)
if (( _pathPoints [ i - 1 ] - target ). squaredLength () >= distSq )
break ; // bingo!
2019-09-13 21:06:28 +02:00
// check if the shortened path is still in LoS with the target
_sourceUnit -> GetHitSpherePointFor ({ _pathPoints [ i - 1 ]. x , _pathPoints [ i - 1 ]. y , _pathPoints [ i - 1 ]. z + collisionHeight }, x , y , z );
if ( ! _sourceUnit -> GetMap () -> isInLineOfSight ( _sourceUnit -> GetPhaseShift (), x , y , z , _pathPoints [ i - 1 ]. x , _pathPoints [ i - 1 ]. y , _pathPoints [ i - 1 ]. z + collisionHeight , LINEOFSIGHT_ALL_CHECKS , VMAP :: ModelIgnoreFlags :: Nothing ))
{
// whenver we find a point that is not in LoS anymore, simply use last valid path
_pathPoints . resize ( i + 1 );
return ;
}
2018-04-06 18:09:55 +02:00
if ( !-- i )
2014-07-04 22:22:28 +01:00
{
2018-04-06 18:09:55 +02:00
// no point found that fulfills the condition
_pathPoints [ 0 ] = _pathPoints [ 1 ];
2014-07-04 22:22:28 +01:00
_pathPoints . resize ( 2 );
2018-04-06 18:09:55 +02:00
return ;
2014-07-04 22:22:28 +01:00
}
}
2018-04-06 18:09:55 +02:00
// ok, _pathPoints[i] is too close, _pathPoints[i-1] is not, so our target point is somewhere between the two...
// ... settle for a guesstimate since i'm not confident in doing trig on every chase motion tick...
// (@todo review this)
_pathPoints [ i ] += ( _pathPoints [ i - 1 ] - _pathPoints [ i ]). direction () * ( dist - ( _pathPoints [ i ] - target ). length ());
_pathPoints . resize ( i + 1 );
2014-07-04 22:22:28 +01:00
}
2016-07-06 10:23:12 +02:00
bool PathGenerator :: IsInvalidDestinationZ ( Unit const * target ) const
{
return ( target -> GetPositionZ () - GetActualEndPosition (). z ) > 5.0f ;
}