855 lines
22 KiB
C++
855 lines
22 KiB
C++
// Ryzom - MMORPG Framework <http://dev.ryzom.com/projects/ryzom/>
|
|
// Copyright (C) 2010 Winch Gate Property Limited
|
|
//
|
|
// This program is free software: you can redistribute it and/or modify
|
|
// it under the terms of the GNU Affero General Public License as
|
|
// published by the Free Software Foundation, either version 3 of the
|
|
// License, or (at your option) any later version.
|
|
//
|
|
// 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 Affero General Public License for more details.
|
|
//
|
|
// You should have received a copy of the GNU Affero General Public License
|
|
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
#include "stdpch.h"
|
|
#include "path_behaviors.h"
|
|
#include "ais_actions.h"
|
|
#include "continent.h"
|
|
#include <typeinfo>
|
|
|
|
extern bool simulateBug(int bugId);
|
|
|
|
using namespace RYAI_MAP_CRUNCH;
|
|
using namespace CAISActionEnums;
|
|
|
|
|
|
NLMISC::CVariable<bool> ActivateStraightRepulsion("ai", "ActivateStraightRepulsion", "Activate the straight repulsion for follow route (only available with Ring shards for moment).", true, 0, true);
|
|
NLMISC::CVariable<bool> LogTimeConsumingAStar("ai", "LogTimeConsumingAStar", "Activate logging of time consuming path finding operations", false, 0, true);
|
|
|
|
|
|
//////////////////////////////////////////////////////////////////////////////
|
|
// Actions used when parsing primitives //
|
|
//////////////////////////////////////////////////////////////////////////////
|
|
|
|
DEFINE_ACTION(ContextGlobal,SETNOGO)
|
|
{
|
|
// get hold of the parameters and check their validity
|
|
float x,y;
|
|
if (!getArgs(args,name(),x,y))
|
|
return;
|
|
|
|
const RYAI_MAP_CRUNCH::CMapPosition pos(CAIVector (x,y));
|
|
|
|
const RYAI_MAP_CRUNCH::CWorldMap &worldMap=CWorldContainer::getWorldMap(/*0*/);
|
|
const CSuperCell *superCell=worldMap.getSuperCellCst(pos);
|
|
|
|
if (!superCell)
|
|
{
|
|
nlwarning("Unable to set flags at this position %.3f %.3f",x,y);
|
|
return;
|
|
}
|
|
}
|
|
|
|
DEFINE_ACTION(ContextGlobal,SAFEZONE)
|
|
{
|
|
// get hold of the parameters and check their validity
|
|
float x,y,r;
|
|
|
|
if (!getArgs(args,name(),x,y,r))
|
|
return;
|
|
|
|
const RYAI_MAP_CRUNCH::CMapPosition pos(CAIVector (x,y));
|
|
CWorldContainer::getWorldMapNoCst().setFlagOnPosAndRadius(pos,r,1);
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////////
|
|
// CFollowPathContext //
|
|
//////////////////////////////////////////////////////////////////////////////
|
|
|
|
CFollowPathContext::CFollowPathContext(const char* contextName, uint32 maxSearchDepth, bool forceMaxDepth)
|
|
{
|
|
if (contextName==NULL)
|
|
{
|
|
// nldebug("CFollowPathContext: NULL : first init");
|
|
_PrevContext= NULL;
|
|
_ContextName= "TopOfStack";
|
|
_MaxSearchDepth= maxSearchDepth;
|
|
_NextContext= NULL;
|
|
}
|
|
else
|
|
{
|
|
// nldebug("CFollowPathContext: %s",contextName);
|
|
_PrevContext= CFollowPath::getInstance()->_TopFollowPathContext;
|
|
nlassert(_PrevContext!=NULL);
|
|
CFollowPath::getInstance()->_TopFollowPathContext= this;
|
|
_PrevContext->_NextContext= this;
|
|
_ContextName= contextName;
|
|
_MaxSearchDepth= (forceMaxDepth)? maxSearchDepth: std::min(maxSearchDepth,_PrevContext->_MaxSearchDepth);
|
|
_NextContext= NULL;
|
|
}
|
|
}
|
|
|
|
CFollowPathContext::~CFollowPathContext()
|
|
{
|
|
// if we're not the bottom of stack then fixup the previous stack entry
|
|
if (_PrevContext!=NULL)
|
|
{
|
|
_PrevContext->_NextContext= _NextContext;
|
|
}
|
|
|
|
// if we're not at the top of stack then fixup the next stack entry
|
|
// otherwise fixup the stack top pointer in the CFollowPath singleton
|
|
if (_NextContext!=NULL)
|
|
{
|
|
_NextContext->_PrevContext= _PrevContext;
|
|
}
|
|
else
|
|
{
|
|
CFollowPath::getInstance()->_TopFollowPathContext= _PrevContext;
|
|
}
|
|
}
|
|
|
|
void CFollowPathContext::buildContextName(std::string &result) const
|
|
{
|
|
// concatenate previous stack entries
|
|
if (_PrevContext!=NULL)
|
|
{
|
|
_PrevContext->buildContextName(result);
|
|
result+=':';
|
|
}
|
|
else
|
|
{
|
|
result.clear();
|
|
}
|
|
|
|
// add our name to the result
|
|
result+= _ContextName;
|
|
}
|
|
|
|
|
|
//////////////////////////////////////////////////////////////////////////////
|
|
// CFollowPath //
|
|
//////////////////////////////////////////////////////////////////////////////
|
|
|
|
CFollowPath* CFollowPath::_Instance = NULL;
|
|
|
|
CFollowPath* CFollowPath::getInstance()
|
|
{
|
|
if (_Instance==NULL)
|
|
{
|
|
_Instance = new CFollowPath;
|
|
}
|
|
return _Instance;
|
|
}
|
|
|
|
void CFollowPath::destroyInstance()
|
|
{
|
|
if (_Instance!=NULL)
|
|
{
|
|
delete _Instance;
|
|
_Instance = NULL;
|
|
}
|
|
}
|
|
|
|
CFollowPath::CFollowPath()
|
|
: _LastReason(NO_REASON)
|
|
, _LastFIASPReason(CWorldMap::FIASPR_NO_REASON)
|
|
{
|
|
// initialise the _TopFollowPathContext stack
|
|
_TopFollowPathContext= new CFollowPathContext(NULL);
|
|
}
|
|
|
|
NLMISC::CVariable<uint> AStarNbStepsLogThreshold( "ais", "AStarNbStepsLogThreshold", "", 1000, 0, true );
|
|
namespace RYAI_MAP_CRUNCH
|
|
{
|
|
extern uint LastAStarNbSteps;
|
|
}
|
|
|
|
inline void logTimeConsumingAStar(CModEntityPhysical* bot, float dist, CPathCont& pathCont, CAIVector* realDestination )
|
|
{
|
|
if (!LogTimeConsumingAStar.get())
|
|
return;
|
|
|
|
if (RYAI_MAP_CRUNCH::LastAStarNbSteps >= AStarNbStepsLogThreshold.get() )
|
|
{
|
|
nlinfo( "ASTAR: Bot %s (type %s) used %u steps src=%s dest=%s dist=%g a*flag=%s context=%s",
|
|
bot ? bot->getPersistent().getOneLineInfoString().c_str() : "-",
|
|
bot ? typeid(*bot).name() : "-",
|
|
RYAI_MAP_CRUNCH::LastAStarNbSteps,
|
|
bot ? bot->pos().toString().c_str() : "-",
|
|
realDestination ? realDestination->toString().c_str() : pathCont.getDestination().toString().c_str(),
|
|
dist,
|
|
bot ? RYAI_MAP_CRUNCH::toString(bot->getAStarFlag()).c_str() : "-",
|
|
CFollowPath::getInstance()->getContextName() );
|
|
}
|
|
}
|
|
|
|
NLMISC::CMustConsume<CFollowPath::TFollowStatus> CFollowPath::followPath(
|
|
CModEntityPhysical* bot,
|
|
CPathPosition& pathPos,
|
|
CPathCont& pathCont,
|
|
float dist,
|
|
float modecalageDist,
|
|
float angleAmort,
|
|
bool focusOnTargetDirection,
|
|
CAIVector* realDestination,
|
|
float repulsSpeed,
|
|
bool updateOrient)
|
|
{
|
|
H_AUTO(followPath);
|
|
|
|
if (!bot->canMove())
|
|
return FOLLOW_BLOCKED;
|
|
|
|
TFollowStatus returnStatus = FOLLOWING;
|
|
CAngle motionAngle;
|
|
bool isMotionAngleComputed = false;
|
|
CAIVector add;
|
|
CAIVector addCorrection(0.0,0.0);
|
|
CWorldMap const& worldMap = CWorldContainer::getWorldMap();
|
|
|
|
CAIPos const startPos = CAIPos(bot->pos());
|
|
#ifdef NL_DEBUG
|
|
nlassert(angleAmort>=0.f && angleAmort<=1.f);
|
|
#endif
|
|
if (!bot->wpos().isValid())
|
|
return FOLLOWING;
|
|
|
|
bool haveRestart = false;
|
|
|
|
switch (pathPos._PathState)
|
|
{
|
|
case CPathPosition::NOT_INITIALIZED:
|
|
{
|
|
pathPos._Angle=bot->theta();
|
|
|
|
reStartFollowTopo:
|
|
if (!pathCont.getCalcPathForSource (pathPos,bot->wpos()))
|
|
{
|
|
pathPos._PathState = CPathPosition::NOT_INITIALIZED;
|
|
returnStatus = FOLLOW_NO_PATH;
|
|
_LastReason = FNP_NOT_INITIALIZED;
|
|
break;
|
|
}
|
|
logTimeConsumingAStar(bot, dist, pathCont, realDestination);
|
|
pathPos._PathState = CPathPosition::FOLLOWING_TOPO;
|
|
}
|
|
// No break
|
|
|
|
case CPathPosition::FOLLOWING_TOPO:
|
|
{
|
|
if (pathCont._TimeTopoChanged!=pathPos._TimeTopoChanged)
|
|
{
|
|
goto reStartFollowTopo;
|
|
}
|
|
|
|
// If we have changed our current topology, we have to take the next furthest topo in the same 'cell'.
|
|
CTopology::TTopologyRef botTopology(bot->wpos().getTopologyRef());
|
|
while ( pathPos.haveNextTopology(2)
|
|
&& botTopology==pathPos.getNextTopology())
|
|
{
|
|
pathPos.nextTopology();
|
|
}
|
|
|
|
if (!pathPos.isFinished())
|
|
{
|
|
CGridDirectionLayer const* layer = worldMap.getGridDirectionLayer(bot->wpos(),pathPos.getNextTopology());
|
|
|
|
if (!layer) // We are now to far to have a layer to lead us to the next topo.
|
|
{
|
|
goto reStartFollowTopo;
|
|
}
|
|
|
|
CDirection motion = layer->getDirection(bot->wpos());
|
|
if (motion.isValid())
|
|
{
|
|
motionAngle = motion.getAngle();
|
|
isMotionAngleComputed=true;
|
|
break;
|
|
}
|
|
}
|
|
pathPos._PathState = CPathPosition::FOLLOWING_INSIDE_TOPO;
|
|
}
|
|
// No break
|
|
|
|
case CPathPosition::FOLLOWING_INSIDE_TOPO:
|
|
{
|
|
if (pathCont._TimeTopoChanged!=pathPos._TimeTopoChanged)
|
|
{
|
|
goto reStartFollowTopo;
|
|
}
|
|
|
|
if (pathCont._TimeDestChanged!=pathPos._TimeDestChanged)
|
|
{
|
|
pathPos._InsidePath=NULL;
|
|
}
|
|
|
|
if (pathPos._InsidePath.isNull())
|
|
{
|
|
CInsidePath* insidePath = new CInsidePath();
|
|
pathPos._InsidePath = insidePath;
|
|
|
|
if (!worldMap.findInsideAStarPath(bot->wpos(), pathCont.getDestPos(), insidePath->_DirectionPath, pathCont.denyFlags()))
|
|
{
|
|
// If findInsideAStarPath returned false we have a topology change
|
|
pathPos._InsidePath=NULL;
|
|
|
|
#ifdef NL_DEBUG
|
|
nlassert(!haveRestart);
|
|
#endif
|
|
if (!haveRestart)
|
|
{
|
|
haveRestart = true;
|
|
goto reStartFollowTopo;
|
|
}
|
|
returnStatus = FOLLOW_NO_PATH;
|
|
_LastReason = FNP_NO_INSIDE_ASTAR_PATH;
|
|
_LastFIASPReason = worldMap._LastFIASPReason;
|
|
break;
|
|
}
|
|
|
|
pathPos._InsideIndex = 0;
|
|
|
|
pathPos._TimeDestChanged = pathCont._TimeDestChanged;
|
|
pathPos._TimeTopoChanged = pathCont._TimeTopoChanged;
|
|
|
|
// If we reached path end
|
|
if (pathPos._InsideIndex >= insidePath->_DirectionPath.size())
|
|
{
|
|
returnStatus = FOLLOW_ARRIVED;
|
|
pathPos._InsidePath = NULL;
|
|
}
|
|
else
|
|
{
|
|
pathPos._InsideStartPos = bot->wpos();
|
|
CDirection direction = insidePath->_DirectionPath[pathPos._InsideIndex];
|
|
pathPos._InsideDestPos = pathPos._InsideStartPos.step(direction.dx(), direction.dy());
|
|
}
|
|
}
|
|
|
|
if (!pathPos._InsidePath.isNull())
|
|
{
|
|
CInsidePath* insidePath = pathPos._InsidePath;
|
|
CMapPosition botWpos = bot->wpos();
|
|
|
|
while (pathPos._InsideIndex < insidePath->_DirectionPath.size())
|
|
{
|
|
if (botWpos == pathPos._InsideStartPos)
|
|
break;
|
|
|
|
if (botWpos == pathPos._InsideDestPos)
|
|
{
|
|
++pathPos._InsideIndex;
|
|
if (pathPos._InsideIndex >= insidePath->_DirectionPath.size())
|
|
{
|
|
returnStatus = FOLLOW_ARRIVED;
|
|
pathPos._InsidePath = NULL;
|
|
}
|
|
else
|
|
{
|
|
pathPos._InsideStartPos = botWpos;
|
|
CDirection direction = insidePath->_DirectionPath[pathPos._InsideIndex];
|
|
pathPos._InsideDestPos = pathPos._InsideStartPos.step(direction.dx(), direction.dy());
|
|
}
|
|
break;
|
|
}
|
|
else
|
|
{
|
|
pathPos._InsideIndex++;
|
|
}
|
|
}
|
|
|
|
if (returnStatus!=FOLLOW_ARRIVED && pathPos._InsideIndex >= insidePath->_DirectionPath.size())
|
|
{
|
|
pathPos._InsidePath = NULL;
|
|
}
|
|
else
|
|
{
|
|
if (returnStatus!=FOLLOW_ARRIVED)
|
|
{
|
|
motionAngle = pathPos._InsidePath->_DirectionPath[pathPos._InsideIndex].getAngle();
|
|
isMotionAngleComputed = true;
|
|
}
|
|
else
|
|
{
|
|
pathPos._PathState = CPathPosition::REACH_END;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
|
|
case CPathPosition::REACH_END:
|
|
{
|
|
if (pathCont._TimeDestChanged!=pathPos._TimeDestChanged)
|
|
{
|
|
goto reStartFollowTopo;
|
|
}
|
|
else
|
|
{
|
|
returnStatus=FOLLOW_ARRIVED;
|
|
}
|
|
}
|
|
break;
|
|
|
|
case CPathPosition::NO_PATH:
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
|
|
if (isMotionAngleComputed)
|
|
{
|
|
CAngle thisAngle = pathPos._Angle;
|
|
|
|
CAIVector deltaToDest = (realDestination!=NULL)?*realDestination:pathCont.getDestination();
|
|
deltaToDest -= CAIVector(bot->pos());
|
|
|
|
CAngle idealAngle = deltaToDest.asAngle();
|
|
|
|
sint32 absDeltaIdealAngle = abs((sint16)(idealAngle.asRawSint16()-motionAngle.asRawSint16()));
|
|
if (absDeltaIdealAngle>=32768)
|
|
{
|
|
absDeltaIdealAngle = abs((sint16)absDeltaIdealAngle-65536);
|
|
}
|
|
|
|
bool idealIsValid = true;
|
|
|
|
if (absDeltaIdealAngle>(32768*67.5/180)) // /*97*/ if a difference of slightly more than 95 degrees.
|
|
{
|
|
idealIsValid = false;
|
|
}
|
|
else
|
|
{
|
|
//////////////////////////////////////////////////////////////////////////
|
|
// verify that the direction is valid in the map at our position (very slow, to be optimized).
|
|
CDirection dir0, dir1;
|
|
CDirection::getDirectionAround(idealAngle, dir0, dir1);
|
|
CWorldPosition testPos(bot->wpos());
|
|
dir1.addStep(CDirection::HALF_TURN_RIGHT);
|
|
idealIsValid=worldMap.customCheckDiagMove(testPos, dir1, pathCont.denyFlags());
|
|
}
|
|
|
|
if (idealIsValid)
|
|
{
|
|
motionAngle = idealAngle;
|
|
}
|
|
|
|
{
|
|
sint16 deltaAngle = motionAngle.asRawSint16()-thisAngle.asRawSint16();
|
|
sint32 absDeltaAngle = abs(deltaAngle);
|
|
// Take the smallest angle must be add in CAngle.
|
|
if (absDeltaAngle >= 32768)
|
|
{
|
|
if (deltaAngle > 0)
|
|
deltaAngle = (sint16)deltaAngle-65536;
|
|
else
|
|
deltaAngle = (sint16)65536-deltaAngle;
|
|
}
|
|
|
|
// Only if significative.
|
|
if (absDeltaAngle>32)
|
|
{
|
|
deltaAngle = (sint16)((float)deltaAngle*angleAmort);
|
|
thisAngle += deltaAngle;
|
|
}
|
|
else
|
|
{
|
|
thisAngle = motionAngle;
|
|
}
|
|
}
|
|
|
|
pathPos._Angle = thisAngle;
|
|
|
|
// Verify that dist is not too high !
|
|
{
|
|
float reachDist = (float)(bot->pos().quickDistTo(pathCont.getDestination())*0.5+0.1);
|
|
if (dist > reachDist)
|
|
dist = reachDist;
|
|
}
|
|
|
|
add = CAIVector(thisAngle.asVector2d()*dist);
|
|
if (idealIsValid)
|
|
{
|
|
deltaToDest.normalize(dist*1000);
|
|
addCorrection = deltaToDest-add;
|
|
}
|
|
}
|
|
|
|
CAIVector moveDecalage=bot->moveDecalage();
|
|
bot->resetDecalage();
|
|
moveDecalage.normalize(modecalageDist*1000);
|
|
|
|
CAIVector rAdd;
|
|
if (simulateBug(8))
|
|
{
|
|
rAdd = add;
|
|
if (IsRingShard.get() && ActivateStraightRepulsion.get())
|
|
{
|
|
// straight repulsion
|
|
CAIVector repulsion;
|
|
if (bot->calcStraightRepulsion(startPos+rAdd, repulsion))
|
|
{
|
|
rAdd += moveDecalage;
|
|
rAdd += repulsion;
|
|
}
|
|
else
|
|
{
|
|
return FOLLOW_BLOCKED;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// repulsion
|
|
CAIVector repulsion = bot->calcRepulsion(startPos+rAdd);
|
|
rAdd += moveDecalage;
|
|
repulsion.normalize((float)(std::max((float)rAdd.norm(),repulsSpeed)*710)); // minimum of 0.25m/s.
|
|
rAdd += repulsion;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
rAdd = add + moveDecalage;
|
|
if (IsRingShard.get() && ActivateStraightRepulsion.get())
|
|
{
|
|
// straight repulsion
|
|
CAIVector repulsion;
|
|
if (bot->calcStraightRepulsion(startPos+rAdd, repulsion))
|
|
{
|
|
rAdd += repulsion;
|
|
}
|
|
else
|
|
{
|
|
return FOLLOW_BLOCKED;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// repulsion
|
|
CAIVector repulsion = bot->calcRepulsion(startPos+rAdd);
|
|
repulsion.normalize((float)(std::max((float)rAdd.norm(),repulsSpeed)*710)); // minimum of 0.25m/s.
|
|
rAdd += repulsion;
|
|
}
|
|
}
|
|
|
|
if (!rAdd.isNull() && !bot->moveBy(rAdd, pathCont.denyFlags()))
|
|
{
|
|
if (rAdd==add || !bot->moveBy(add, pathCont.denyFlags())) // Try without correction / repulsion, etc .. (if there's any).
|
|
{
|
|
if (!isMotionAngleComputed || pathPos._Angle==motionAngle)
|
|
{
|
|
// try a 1 meter step
|
|
add = add.normalize()*1000;
|
|
if (!bot->moveBy(add, pathCont.denyFlags()))
|
|
return FOLLOW_BLOCKED;
|
|
}
|
|
|
|
if (isMotionAngleComputed)
|
|
{
|
|
pathPos._Angle = motionAngle;
|
|
if (updateOrient)
|
|
bot->setTheta(motionAngle);
|
|
}
|
|
return returnStatus;
|
|
}
|
|
else
|
|
{
|
|
if (updateOrient)
|
|
bot->setTheta(pathPos._Angle);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (updateOrient && !rAdd.isNull())
|
|
bot->setTheta(rAdd.asAngle());
|
|
}
|
|
return returnStatus;
|
|
}
|
|
|
|
const char* CFollowPath::getContextName() const
|
|
{
|
|
static std::string name;
|
|
|
|
// the following should never happen but it's quite unimportant
|
|
if (_TopFollowPathContext==NULL)
|
|
return "<no context stack>";
|
|
|
|
// delegate to the top context to build the contextname
|
|
_TopFollowPathContext->buildContextName(name);
|
|
|
|
// we're allowed to return this value because 'name' is a static
|
|
return name.c_str();
|
|
}
|
|
|
|
|
|
//////////////////////////////////////////////////////////////////////////////
|
|
// CPathCont //
|
|
//////////////////////////////////////////////////////////////////////////////
|
|
|
|
CPathCont::CPathCont(RYAI_MAP_CRUNCH::TAStarFlag denyFlags)
|
|
: _TimeTopoChanged(~0)
|
|
, _TimeDestChanged(~0)
|
|
, _denyFlags(denyFlags)
|
|
{
|
|
}
|
|
|
|
CPathCont::CPathCont(CPathCont const& pathCont)
|
|
: _denyFlags(pathCont._denyFlags)
|
|
{
|
|
}
|
|
|
|
void CPathCont::clearPaths()
|
|
{
|
|
_SourcePaths.clear();
|
|
}
|
|
|
|
void CPathCont::setDestination(RYAI_MAP_CRUNCH::CWorldPosition const& destPos)
|
|
{
|
|
RYAI_MAP_CRUNCH::CMapPosition mapPos(destPos);
|
|
|
|
if (_DestinationMapPos==mapPos)
|
|
return;
|
|
|
|
_DestinationMapPos = mapPos;
|
|
_Destination = destPos;
|
|
_VerticalPos = AITYPES::vp_auto;
|
|
uint32 gameCycle = CTimeInterface::gameCycle();
|
|
|
|
RYAI_MAP_CRUNCH::CWorldPosition tmpPos = destPos;
|
|
|
|
if (tmpPos!=_DestPos)
|
|
{
|
|
_DestPos = tmpPos;
|
|
_TimeDestChanged = gameCycle;
|
|
|
|
if (!_DestPos.isValid())
|
|
{
|
|
clearPaths();
|
|
}
|
|
else
|
|
{
|
|
RYAI_MAP_CRUNCH::CTopology::TTopologyRef newTopo = _DestPos.getTopologyRef();
|
|
if (((RYAI_MAP_CRUNCH::CTopology::TTopologyId)newTopo)!=_TopoRef )
|
|
{
|
|
_TimeTopoChanged = gameCycle;
|
|
clearPaths();
|
|
_TopoRef = newTopo;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void CPathCont::setDestination(AITYPES::TVerticalPos verticalPos, CAIVector const& destPos)
|
|
{
|
|
RYAI_MAP_CRUNCH::CMapPosition mapPos(destPos);
|
|
|
|
if (_DestinationMapPos==mapPos)
|
|
return;
|
|
|
|
_DestinationMapPos = mapPos;
|
|
_Destination = destPos;
|
|
_VerticalPos = verticalPos;
|
|
uint32 gameCycle = CTimeInterface::gameCycle();
|
|
|
|
RYAI_MAP_CRUNCH::CWorldPosition tmpPos;
|
|
CWorldContainer::getWorldMap().setWorldPosition(verticalPos, tmpPos,destPos);
|
|
|
|
if (tmpPos!=_DestPos)
|
|
{
|
|
_DestPos = tmpPos;
|
|
_TimeDestChanged = gameCycle;
|
|
|
|
if (!_DestPos.isValid())
|
|
{
|
|
clearPaths ();
|
|
}
|
|
else
|
|
{
|
|
RYAI_MAP_CRUNCH::CTopology::TTopologyRef newTopo = _DestPos.getTopologyRef();
|
|
if (((RYAI_MAP_CRUNCH::CTopology::TTopologyId)newTopo)!=_TopoRef )
|
|
{
|
|
_TimeTopoChanged = gameCycle;
|
|
clearPaths();
|
|
_TopoRef = newTopo;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
bool CPathCont::getPathForSource(CPathPosition& pathPos, RYAI_MAP_CRUNCH::CWorldPosition const& startPos) const
|
|
{
|
|
H_AUTO(getPathForSource);
|
|
RYAI_MAP_CRUNCH::CTopology::TTopologyRef const startTopo = startPos.getTopologyRef();
|
|
|
|
std::vector<NLMISC::CSmartPtr<CAIPath> >::const_iterator it = _SourcePaths.begin();
|
|
const std::vector<NLMISC::CSmartPtr<CAIPath> >::const_iterator itEnd = _SourcePaths.end();
|
|
|
|
while (it!=itEnd)
|
|
{
|
|
CAIPath *path=*(it);
|
|
|
|
std::vector<RYAI_MAP_CRUNCH::CTopology::TTopologyRef>::const_iterator const topoItBegin=path->topologiesPath().begin();
|
|
std::vector<RYAI_MAP_CRUNCH::CTopology::TTopologyRef>::const_iterator const topoItEnd=path->topologiesPath().end();
|
|
std::vector<RYAI_MAP_CRUNCH::CTopology::TTopologyRef>::const_iterator const topoItFind = std::find(topoItBegin,topoItEnd,startTopo);
|
|
|
|
if (topoItFind!=topoItEnd)
|
|
{
|
|
pathPos._Index = (uint)(topoItFind-topoItBegin);
|
|
pathPos._Path = *it;
|
|
return true;
|
|
}
|
|
++it;
|
|
}
|
|
pathPos._Path = NULL;
|
|
return false; // No path found.
|
|
}
|
|
|
|
bool CPathCont::calcPathForSource(CPathPosition& pathPos, RYAI_MAP_CRUNCH::CWorldPosition const& startPos)
|
|
{
|
|
H_AUTO(calcPathForSource);
|
|
|
|
RYAI_MAP_CRUNCH::CWorldMap const& worldMap = CWorldContainer::getWorldMap();
|
|
|
|
{
|
|
// Get a free path.
|
|
std::vector<NLMISC::CSmartPtr<CAIPath> >::iterator it, itEnd = _SourcePaths.end();
|
|
for (it = _SourcePaths.begin(); it!=itEnd; ++it)
|
|
if ((*it)->getRefCount()==1) // one refcount is reserved for the current list.
|
|
break;
|
|
|
|
// If we didn't found a free path we have to extend the vector..
|
|
if (it == itEnd)
|
|
{
|
|
_SourcePaths.push_back(new CAIPath());
|
|
pathPos._Path = _SourcePaths.back();
|
|
}
|
|
else
|
|
{
|
|
pathPos._Path = (*it);
|
|
}
|
|
}
|
|
|
|
pathPos._Index = 0; // current topology index.
|
|
|
|
CAIPath& pathRef = *(pathPos._Path);
|
|
|
|
// Check start position validity
|
|
if (!startPos.isValid())
|
|
{
|
|
RYAI_MAP_CRUNCH::CWorldPosition newpos;
|
|
CWorldContainer::calcNearestWPosFromPosAnRadius(_VerticalPos, newpos, startPos, 6, 100, CWorldContainer::CPosValidatorDefault());
|
|
#ifdef NL_DEBUG
|
|
if (newpos.isValid() && LogAcceptablePos)
|
|
nlwarning("Path pos error at position %s: an acceptable position could be %s", startPos.toString().c_str(), newpos.toString().c_str() );
|
|
if (!newpos.isValid())
|
|
nlwarning("Path pos error at position %s: no acceptable position found around", startPos.toString().c_str());
|
|
#endif
|
|
pathRef.setStartPos(newpos);
|
|
}
|
|
else
|
|
{
|
|
pathRef.setStartPos(startPos);
|
|
}
|
|
|
|
// Check dest position validity
|
|
if (!_DestPos.isValid())
|
|
{
|
|
RYAI_MAP_CRUNCH::CWorldPosition newpos;
|
|
CWorldContainer::calcNearestWPosFromPosAnRadius(_VerticalPos, newpos, _DestPos, 6, 100, CWorldContainer::CPosValidatorDefault());
|
|
#ifdef NL_DEBUG
|
|
if (newpos.isValid() && LogAcceptablePos)
|
|
nlwarning("Path pos error at position %s, an acceptable position could be %s", _DestPos.toString().c_str(), newpos.toString().c_str() );
|
|
if (!newpos.isValid())
|
|
nlwarning("Path pos error at position %s, no acceptable position found around", _DestPos.toString().c_str());
|
|
#endif
|
|
_DestPos = newpos;
|
|
}
|
|
|
|
pathRef.setEndPos(_DestPos);
|
|
|
|
if (!worldMap.findAStarPath(pathRef.getStartPos(), _DestPos, pathRef.topologiesPathForCalc(), _denyFlags))
|
|
{
|
|
pathPos._Path = NULL;
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool CPathCont::getCalcPathForSource(CPathPosition& pathPos, RYAI_MAP_CRUNCH::CWorldPosition const& startPos)
|
|
{
|
|
H_AUTO(GetCalcPathForSource);
|
|
static uint32 getCount = 0;
|
|
static uint32 calcCount = 0;
|
|
pathPos._TimeDestChanged = _TimeDestChanged;
|
|
pathPos._TimeTopoChanged = _TimeTopoChanged;
|
|
|
|
if (getPathForSource(pathPos, startPos))
|
|
{
|
|
++getCount;
|
|
return true;
|
|
}
|
|
if (calcPathForSource(pathPos, startPos))
|
|
{
|
|
++calcCount;
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////////
|
|
// CPathPosition //
|
|
//////////////////////////////////////////////////////////////////////////////
|
|
|
|
CPathPosition::CPathPosition(CAngle const& angle)
|
|
: _Angle(angle)
|
|
, _Path(NULL)
|
|
, _PathState(NOT_INITIALIZED)
|
|
{
|
|
}
|
|
|
|
bool CPathPosition::isPathValid() const
|
|
{
|
|
return !_Path.isNull();
|
|
}
|
|
|
|
RYAI_MAP_CRUNCH::CTopology::TTopologyRef const& CPathPosition::getTopology() const
|
|
{
|
|
#ifdef NL_DEBUG
|
|
nlassert(_Index<_Path->topologiesPath().size());
|
|
#endif
|
|
return _Path->topologiesPath()[_Index];
|
|
}
|
|
|
|
RYAI_MAP_CRUNCH::CTopology::TTopologyRef const& CPathPosition::getNextTopology() const
|
|
{
|
|
#ifdef NL_DEBUG
|
|
nlassert((_Index+1)<_Path->topologiesPath().size());
|
|
#endif
|
|
return _Path->topologiesPath()[_Index+1];
|
|
}
|
|
|
|
bool CPathPosition::isFinished() const
|
|
{
|
|
uint32 size = (uint32)_Path->topologiesPath().size();
|
|
return (size==0 || _Index==size-1);
|
|
}
|
|
|
|
bool CPathPosition::nextTopology()
|
|
{
|
|
++_Index;
|
|
return _Index<_Path->topologiesPath().size();
|
|
}
|
|
|
|
bool CPathPosition::haveNextTopology(uint nbTopo)
|
|
{
|
|
return (_Index+nbTopo)<_Path->topologiesPath().size();
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////////
|
|
//////////////////////////////////////////////////////////////////////////////
|
|
//////////////////////////////////////////////////////////////////////////////
|
|
|