mirror of
https://port.numenaute.org/aleajactaest/khanat-opennel-code.git
synced 2024-12-23 09:28:45 +00:00
2337 lines
58 KiB
C++
2337 lines
58 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 "game_share/utils.h"
|
|
#include "world_map.h"
|
|
|
|
//extern bool simulateBug(int bugId);
|
|
|
|
using namespace std;
|
|
using namespace NLMISC;
|
|
|
|
namespace RYAI_MAP_CRUNCH
|
|
{
|
|
|
|
NL_BEGIN_STRING_CONVERSION_TABLE (TAStarFlag)
|
|
NL_STRING_CONVERSION_TABLE_ENTRY(Nothing)
|
|
NL_STRING_CONVERSION_TABLE_ENTRY(Interior)
|
|
NL_STRING_CONVERSION_TABLE_ENTRY(Water)
|
|
NL_STRING_CONVERSION_TABLE_ENTRY(NoGo)
|
|
NL_STRING_CONVERSION_TABLE_ENTRY(WaterAndNogo)
|
|
NL_STRING_CONVERSION_TABLE_ENTRY(GroundFlags)
|
|
NL_END_STRING_CONVERSION_TABLE(TAStarFlag, AStarFlagConversion, Nothing)
|
|
|
|
const std::string& toString(TAStarFlag flag)
|
|
{
|
|
return AStarFlagConversion.toString(flag);
|
|
}
|
|
|
|
TAStarFlag toAStarFlag(const std::string& str)
|
|
{
|
|
return AStarFlagConversion.fromString(str);
|
|
}
|
|
|
|
|
|
//////////////////////////////////////////////////////////////////////////////
|
|
// Helper classes and data //
|
|
//////////////////////////////////////////////////////////////////////////////
|
|
|
|
const struct CDirection::CDirectionData CDirection::directionDatas[] =
|
|
{
|
|
{ +1, 0, ORTHO_COST},
|
|
{ +1, +1, DIAG_COST},
|
|
{ 0, +1, ORTHO_COST},
|
|
{ -1, +1, DIAG_COST},
|
|
{ -1, 0, ORTHO_COST},
|
|
{ -1, -1, DIAG_COST},
|
|
{ 0, -1, ORTHO_COST},
|
|
{ +1, -1, DIAG_COST},
|
|
{ 0, 0, NO_COST}
|
|
};
|
|
|
|
// Enum vals ..
|
|
// 5 6 7
|
|
// 4 8 0
|
|
// 3 2 1
|
|
const CDirection::TDirection CDirection::table[] =
|
|
{
|
|
CDirection::SW, CDirection::S, CDirection::SE,
|
|
CDirection::W, CDirection::UNDEFINED, CDirection::E,
|
|
CDirection::NW, CDirection::N, CDirection::NE
|
|
};
|
|
|
|
//////////////////////////////////////////////////////////////////////////////
|
|
// //
|
|
//////////////////////////////////////////////////////////////////////////////
|
|
|
|
class CABaseStarNode
|
|
{
|
|
public:
|
|
CABaseStarNode(uint father, float distance, bool open);
|
|
|
|
void setOpen(bool open) { _Open = open; }
|
|
bool isOpened() const { return _Open; }
|
|
float getDistance() const { return _Distance; }
|
|
uint getFather() const { return _Father; }
|
|
|
|
private:
|
|
uint _Father; ///< Parent node in the path from the start position
|
|
float _Distance;
|
|
bool _Open; ///< Is the node in the OPEN or CLOSED set?
|
|
};
|
|
|
|
inline
|
|
CABaseStarNode::CABaseStarNode(uint father, float distance, bool open)
|
|
: _Father(father)
|
|
, _Distance(distance)
|
|
, _Open(open)
|
|
{
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////////
|
|
// CAStarHeapNode //
|
|
//////////////////////////////////////////////////////////////////////////////
|
|
|
|
class CAStarHeapNode : public CABaseStarNode
|
|
{
|
|
public:
|
|
explicit CAStarHeapNode(CTopology::TTopologyRef Ref, uint Father, float Distance, bool Open);
|
|
|
|
CTopology::TTopologyRef const& getRef() const { return _Ref; }
|
|
|
|
private:
|
|
CTopology::TTopologyRef _Ref;
|
|
};
|
|
|
|
inline
|
|
CAStarHeapNode::CAStarHeapNode(CTopology::TTopologyRef Ref, uint Father, float Distance, bool Open)
|
|
: CABaseStarNode(Father, Distance, Open)
|
|
, _Ref(Ref)
|
|
{
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////////
|
|
// CAStarNode //
|
|
//////////////////////////////////////////////////////////////////////////////
|
|
|
|
class CAStarNode
|
|
{
|
|
public:
|
|
CAStarNode() { }
|
|
explicit CAStarNode(CWorldPosition const& pos) :
|
|
_x(pos.xCoord().getUnitId()) , _y(pos.yCoord().getUnitId()), _slot(pos.slot())
|
|
{ }
|
|
|
|
CAStarNode(const CAStarNode & other) :
|
|
_x(other._x), _y(other._y), _slot(other._slot)
|
|
{ }
|
|
|
|
void updateMapPosition(CMapPosition& _mapPos) const;
|
|
|
|
bool operator==(CAStarNode const& other) const;
|
|
bool operator!=(CAStarNode const& other) const;
|
|
bool operator<(CAStarNode const& other) const;
|
|
|
|
CSlot const& slot() const { return _slot; }
|
|
|
|
private:
|
|
uint8 _x;
|
|
uint8 _y;
|
|
CSlot _slot;
|
|
};
|
|
|
|
inline
|
|
void CAStarNode::updateMapPosition(CMapPosition& _mapPos) const
|
|
{
|
|
_mapPos.setUnitId(_x, _y);
|
|
}
|
|
|
|
inline
|
|
bool CAStarNode::operator==(CAStarNode const& other) const
|
|
{
|
|
return _x==other._x && _y==other._y && _slot==other._slot;
|
|
}
|
|
|
|
inline
|
|
bool CAStarNode::operator!=(CAStarNode const& other) const
|
|
{
|
|
return _x!=other._x || _y!=other._y || _slot!=other._slot;
|
|
}
|
|
|
|
inline
|
|
bool CAStarNode::operator<(CAStarNode const& other) const
|
|
{
|
|
if (_x!=other._x)
|
|
return _x<other._x;
|
|
if (_y!=other._y)
|
|
return _y<other._y;
|
|
return _slot<other._slot;
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////////
|
|
// //
|
|
//////////////////////////////////////////////////////////////////////////////
|
|
|
|
class CInsideAStarHeapNode : public CABaseStarNode
|
|
{
|
|
public:
|
|
friend class CAStarNode;
|
|
|
|
explicit CInsideAStarHeapNode(const CAStarNode &node, uint Father, CDirection Direction, float Distance, bool Open) : CABaseStarNode(Father,Distance,Open), _Direction(Direction), _Node(node)
|
|
{
|
|
}
|
|
|
|
inline const CDirection &getDirection() const
|
|
{
|
|
return _Direction;
|
|
}
|
|
|
|
inline const CAStarNode &getNode() const
|
|
{
|
|
return _Node;
|
|
}
|
|
|
|
private:
|
|
CDirection _Direction;
|
|
CAStarNode _Node;
|
|
};
|
|
|
|
|
|
//////////////////////////////////////////////////////////////////////////////
|
|
// CDirectionLayer //
|
|
//////////////////////////////////////////////////////////////////////////////
|
|
|
|
void CDirectionLayer::serial(NLMISC::IStream& f)
|
|
{
|
|
uint i, j;
|
|
for (i=0; i<3; ++i)
|
|
{
|
|
for (j=0; j<3; ++j)
|
|
{
|
|
if (f.isReading())
|
|
{
|
|
delete Grid[i][j];
|
|
Grid[i][j] = NULL;
|
|
}
|
|
|
|
bool present = (Grid[i][j] != NULL);
|
|
f.serial(present);
|
|
|
|
if (present)
|
|
{
|
|
if (f.isReading())
|
|
Grid[i][j] = I16x16Layer::load(f);
|
|
else
|
|
I16x16Layer::save(f, Grid[i][j]);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////////
|
|
// CDirectionMap //
|
|
//////////////////////////////////////////////////////////////////////////////
|
|
|
|
void CDirectionMap::serial(NLMISC::IStream& f)
|
|
{
|
|
uint i;
|
|
for (i=0; i<3; ++i)
|
|
{
|
|
if (f.isReading())
|
|
{
|
|
delete Layers[i];
|
|
Layers[i] = NULL;
|
|
}
|
|
|
|
bool present = (Layers[i] != NULL);
|
|
f.serial(present);
|
|
|
|
if (present)
|
|
{
|
|
if (f.isReading())
|
|
Layers[i] = new CDirectionLayer();
|
|
f.serial(*Layers[i]);
|
|
}
|
|
}
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////////
|
|
// CRootCell //
|
|
//////////////////////////////////////////////////////////////////////////////
|
|
|
|
CRootCell* CRootCell::load(NLMISC::IStream& f, CWorldMap const& worldMap)
|
|
{
|
|
TCellType type = Compute;
|
|
CRootCell *result = NULL;
|
|
|
|
f.serialEnum(type);
|
|
|
|
switch (type)
|
|
{
|
|
case Compute:
|
|
result = new CComputeCell(worldMap);
|
|
static_cast<CComputeCell*>(result)->serial(f);
|
|
break;
|
|
case White:
|
|
result = new CWhiteCell(worldMap);
|
|
static_cast<CWhiteCell*>(result)->serial(f);
|
|
break;
|
|
case SingleLayer:
|
|
result = new CSingleLayerCell(worldMap);
|
|
static_cast<CSingleLayerCell*>(result)->serial(f);
|
|
break;
|
|
case MultiLayer:
|
|
result = new CMultiLayerCell(worldMap);
|
|
static_cast<CMultiLayerCell*>(result)->serial(f);
|
|
break;
|
|
default:
|
|
nlassert(false);
|
|
nlwarning("Unknown type of cell %d to load, abort", type);
|
|
return result;
|
|
break;
|
|
}
|
|
|
|
// allow us to optimize access.
|
|
f.serialCont(result->_TopologiesNodes);
|
|
|
|
return result;
|
|
}
|
|
|
|
void CRootCell::save(NLMISC::IStream& f, CRootCell* cell)
|
|
{
|
|
if (dynamic_cast<CComputeCell*>(cell) != NULL)
|
|
{
|
|
TCellType type = Compute;
|
|
f.serialEnum(type);
|
|
static_cast<CComputeCell*>(cell)->serial(f);
|
|
}
|
|
else if (dynamic_cast<CWhiteCell*>(cell) != NULL)
|
|
{
|
|
TCellType type = White;
|
|
f.serialEnum(type);
|
|
static_cast<CWhiteCell*>(cell)->serial(f);
|
|
}
|
|
else if (dynamic_cast<CSingleLayerCell*>(cell) != NULL)
|
|
{
|
|
TCellType type = SingleLayer;
|
|
f.serialEnum(type);
|
|
static_cast<CSingleLayerCell*>(cell)->serial(f);
|
|
}
|
|
else if (dynamic_cast<CMultiLayerCell*>(cell) != NULL)
|
|
{
|
|
TCellType type = MultiLayer;
|
|
f.serialEnum(type);
|
|
static_cast<CMultiLayerCell*>(cell)->serial(f);
|
|
}
|
|
else
|
|
{
|
|
nlassert(false);
|
|
nlwarning("Unknown type of cell to save, abort");
|
|
return;
|
|
}
|
|
|
|
f.serialCont(cell->_TopologiesNodes);
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////////
|
|
// CComputeCell //
|
|
//////////////////////////////////////////////////////////////////////////////
|
|
|
|
void CComputeCell::serial(NLMISC::IStream& f)
|
|
{
|
|
// Version
|
|
// 0: initial version
|
|
uint version = f.serialVersion(0);
|
|
|
|
for (uint32 i=0; i<16*16; ++i)
|
|
for (uint32 k=0; k<3; ++k)
|
|
f.serial(_Grid[i][k]);
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////////
|
|
// CSingleLayerCell //
|
|
//////////////////////////////////////////////////////////////////////////////
|
|
|
|
bool CSingleLayerCell::_Initialized = false;
|
|
uint16 CSingleLayerCell::_MaskMap[16];
|
|
|
|
void CSingleLayerCell::serial(NLMISC::IStream& f)
|
|
{
|
|
f.serialCheck((uint16)'SL');
|
|
|
|
uint i;
|
|
for (i=0; i<16; ++i)
|
|
f.serial(_Map[i]);
|
|
|
|
f.serial(_SLinks);
|
|
f.serial(_NLinks);
|
|
f.serial(_ELinks);
|
|
f.serial(_WLinks);
|
|
|
|
if (f.isReading())
|
|
{
|
|
delete _Topologies;
|
|
delete _HeightMap;
|
|
_Topologies = I16x16Layer::load(f);
|
|
_HeightMap = I16x16Layer::load(f);
|
|
}
|
|
else
|
|
{
|
|
I16x16Layer::save(f, _Topologies);
|
|
I16x16Layer::save(f, _HeightMap);
|
|
}
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////////
|
|
// CMultiLayerCell //
|
|
//////////////////////////////////////////////////////////////////////////////
|
|
|
|
void CMultiLayerCell::serial(NLMISC::IStream& f)
|
|
{
|
|
f.serialCheck((uint16)'ML');
|
|
|
|
uint slot;
|
|
|
|
for (slot=0; slot<3; ++slot)
|
|
{
|
|
// delete layer if any previously
|
|
if (f.isReading())
|
|
{
|
|
if (_Layers[slot] != NULL)
|
|
delete _Layers[slot]->_HeightMap;
|
|
delete _Layers[slot];
|
|
_Layers[slot] = NULL;
|
|
}
|
|
|
|
bool present = (_Layers[slot] != NULL);
|
|
f.serial(present);
|
|
|
|
if (present)
|
|
{
|
|
if (f.isReading())
|
|
{
|
|
_Layers[slot] = new CCellLayer();
|
|
_Layers[slot]->_HeightMap = I16x16Layer::load(f);
|
|
}
|
|
else
|
|
{
|
|
I16x16Layer::save(f, _Layers[slot]->_HeightMap);
|
|
}
|
|
|
|
nlassert(_Layers[slot] != NULL);
|
|
|
|
for (uint32 i=0; i<16*16; ++i)
|
|
{
|
|
f.serial(_Layers[slot]->_Layer[i]);
|
|
f.serial(_Layers[slot]->_Topology[i]);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////////
|
|
// CSuperCell //
|
|
//////////////////////////////////////////////////////////////////////////////
|
|
|
|
void CSuperCell::serial(NLMISC::IStream& f)
|
|
{
|
|
// Version
|
|
// 0: initial version
|
|
uint version = f.serialVersion(0);
|
|
|
|
if (f.isReading())
|
|
{
|
|
for (uint32 i=0; i<16*16; ++i)
|
|
{
|
|
bool present;
|
|
f.serial(present);
|
|
|
|
if (_Grid[i] != NULL)
|
|
delete _Grid[i];
|
|
|
|
if (present)
|
|
_Grid[i] = CRootCell::load(f,_WorldMap);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
for (uint32 i=0; i<16*16; ++i)
|
|
{
|
|
bool present = (_Grid[i] != NULL);
|
|
f.serial(present);
|
|
|
|
if (present)
|
|
CRootCell::save(f, _Grid[i]);
|
|
}
|
|
}
|
|
}
|
|
|
|
void CSuperCell::updateTopologyRef(CWorldMap* worldMap)
|
|
{
|
|
for (uint32 i=0; i<16*16; ++i)
|
|
{
|
|
if (_Grid[i])
|
|
_Grid[i]->updateTopologyRef (worldMap);
|
|
}
|
|
}
|
|
|
|
void CSuperCell::countCells(uint& compute, uint& white, uint& simple, uint& multi, uint& other) const
|
|
{
|
|
for (uint32 i=0; i<16*16; ++i)
|
|
{
|
|
if (!_Grid[i])
|
|
continue;
|
|
if (dynamic_cast<const CWhiteCell*>(_Grid[i]) != NULL)
|
|
++white;
|
|
else if (dynamic_cast<const CSingleLayerCell*>(_Grid[i]) != NULL)
|
|
++simple;
|
|
else if (dynamic_cast<const CComputeCell*>(_Grid[i]) != NULL)
|
|
++compute;
|
|
else if (dynamic_cast<const CMultiLayerCell*>(_Grid[i]) != NULL)
|
|
++multi;
|
|
else
|
|
++other;
|
|
}
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////////
|
|
// CWorldMap //
|
|
//////////////////////////////////////////////////////////////////////////////
|
|
|
|
void CWorldMap::getBounds(CMapPosition& min, CMapPosition& max)
|
|
{
|
|
uint i, j;
|
|
uint mini = 256, maxi = 0, minj = 256, maxj = 0;
|
|
|
|
for (i=0; i<256; ++i)
|
|
{
|
|
for (j=0; j<256; ++j)
|
|
{
|
|
if (_GridFastAccess[i*256+j])
|
|
{
|
|
if (i < mini) mini = i;
|
|
if (i > maxi) maxi = i;
|
|
if (j < minj) minj = j;
|
|
if (j > maxj) maxj = j;
|
|
}
|
|
}
|
|
}
|
|
|
|
min = CMapPosition(CMapCoord(minj, 0, 0), CMapCoord(mini, 0, 0) );
|
|
max = CMapPosition(CMapCoord(maxj+1, 0, 0), CMapCoord(maxi+1, 0, 0) );
|
|
}
|
|
|
|
void CWorldMap::clear()
|
|
{
|
|
for (uint i=0;i<65536;i++)
|
|
{
|
|
if (_GridFastAccess[i])
|
|
{
|
|
delete _GridFastAccess[i];
|
|
_GridFastAccess[i]=NULL;
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
void CWorldMap::serial(NLMISC::IStream &f)
|
|
{
|
|
f.serialCheck((uint32)'WMAP');
|
|
|
|
// Version
|
|
// 0: initial version
|
|
uint version = f.serialVersion(0);
|
|
|
|
if (f.isReading())
|
|
{
|
|
uint32 i;
|
|
for (i=0;i<65536;i++)
|
|
{
|
|
bool present;
|
|
f.serial(present);
|
|
|
|
if (present)
|
|
{
|
|
CSuperCell *scell = _GridFastAccess[i];
|
|
if (!scell)
|
|
_GridFastAccess[i] = scell =new CSuperCell(*this);
|
|
f.serial(*scell);
|
|
}
|
|
|
|
}
|
|
|
|
// made to update RootCell pointers in TTopologyRef ..
|
|
for (i=0;i<65536;i++)
|
|
{
|
|
CSuperCell *scell = _GridFastAccess[i];
|
|
if (scell)
|
|
scell->updateTopologyRef (this);
|
|
}
|
|
|
|
// made to calculate some random pos ..
|
|
{
|
|
CMapPosition min, max;
|
|
getBounds(min, max);
|
|
CMapPosition scan, scanline;
|
|
NLMISC::CRandom random;
|
|
|
|
for (scan = min; scan.y() != max.y(); scan = scan.stepCell(0, 1))
|
|
{
|
|
for (scanline = scan; scanline.x() != max.x(); scanline = scanline.stepCell(1, 0))
|
|
{
|
|
CRootCell *rootCell=getRootCell(scanline);
|
|
|
|
if (!rootCell)
|
|
continue;
|
|
|
|
CMapPosition pos(scanline.x(),0xffff0000|scanline.y());
|
|
|
|
uint ind=0;
|
|
uint maxTries=256;
|
|
|
|
for (;ind<4 && maxTries>0;maxTries--)
|
|
{
|
|
CWorldPosition wpos;
|
|
|
|
uint i= uint32(random.rand()) & 0xf;
|
|
uint j= uint32(random.rand()) & 0xf;
|
|
#ifdef NL_DEBUG
|
|
nlassert(i<16 && j<16);
|
|
#endif
|
|
pos.setUnitId(i,j);
|
|
CAIVector vecPos=CAIVector(pos);
|
|
if (setWorldPosition (AITYPES::vp_auto, wpos, vecPos))
|
|
{
|
|
#ifdef NL_DEBUG
|
|
nlassert(wpos.getRootCell()==rootCell);
|
|
nlassert(wpos.y()<=0 && wpos.x()>=0);
|
|
#endif
|
|
rootCell->setWorldPosition(wpos, ind);
|
|
ind++;
|
|
}
|
|
|
|
}
|
|
|
|
// if we have found some valid positions but not all, fill the array with the last pos found.
|
|
if (ind<4 && ind>0)
|
|
{
|
|
while (ind<4)
|
|
{
|
|
rootCell->setWorldPosition(rootCell->getWorldPosition(ind-1), ind);
|
|
ind++;
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
else
|
|
{
|
|
for (uint32 i=0;i<65536;i++)
|
|
{
|
|
bool present = (_GridFastAccess[i]!=NULL);
|
|
f.serial(present);
|
|
|
|
if (present)
|
|
{
|
|
//nldebug("Save SuperCell %d/%d", i, j);
|
|
f.serial(*(_GridFastAccess[i]));
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
void CWorldMap::setFlagOnPosAndRadius(const CMapPosition &pos,float radius, uint32 flag)
|
|
{
|
|
float minx=pos.x()-radius;
|
|
float maxx=pos.x()+radius;
|
|
|
|
float miny=pos.y()-radius;
|
|
float maxy=pos.y()+radius;
|
|
|
|
const float radius2=radius*radius;
|
|
|
|
for (float ty=miny;ty<=maxy;ty++)
|
|
{
|
|
const float dy=ty-pos.y();
|
|
for (float tx=minx;tx<=maxx;tx++)
|
|
{
|
|
const float dx=tx-pos.x();
|
|
|
|
if ((dy*dy+dx*dx)>radius2)
|
|
continue;
|
|
|
|
CRootCell *rootCell=getRootCell(CMapPosition((int)tx,(int)ty));
|
|
if (!rootCell)
|
|
continue;
|
|
rootCell->setFlag(flag);
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
void CWorldMap::countCells(uint &compute, uint &white, uint &simple, uint &multi, uint &other) const
|
|
{
|
|
for (uint32 i=0; i<65536; ++i)
|
|
{
|
|
if (_GridFastAccess[i])
|
|
_GridFastAccess[i]->countCells(compute, white, simple, multi, other);
|
|
}
|
|
|
|
// uint i, j;
|
|
// for (i=0; i<256; ++i)
|
|
// for (j=0; j<256; ++j)
|
|
// if (_Grid[i][j] != NULL)
|
|
// _Grid[i][j]->countCells(compute, white, simple, multi, other);
|
|
}
|
|
|
|
//
|
|
CNeighbourhood CWorldMap::neighbours(const CWorldPosition &wpos) const
|
|
{
|
|
CNeighbourhood neighbs;
|
|
const CCellLinkage lnks=wpos.getCellLinkage();
|
|
|
|
if (lnks.isSSlotValid())
|
|
{
|
|
neighbs.set(CDirection::S);
|
|
|
|
const CCellLinkage &nlnk = wpos.getPosS().getCellLinkage(); //CWorldPosition(wpos.getPosS(),lnks.NSlot()));
|
|
if (nlnk.isESlotValid())
|
|
neighbs.set(CDirection::SE);
|
|
if (nlnk.isWSlotValid())
|
|
neighbs.set(CDirection::SW);
|
|
}
|
|
|
|
if (lnks.isNSlotValid())
|
|
{
|
|
neighbs.set(CDirection::N);
|
|
|
|
const CCellLinkage &slnk = wpos.getPosN().getCellLinkage();
|
|
if (slnk.isESlotValid())
|
|
neighbs.set(CDirection::NE);
|
|
if (slnk.isWSlotValid())
|
|
neighbs.set(CDirection::NW);
|
|
}
|
|
|
|
if (lnks.isESlotValid())
|
|
{
|
|
neighbs.set(CDirection::E);
|
|
|
|
const CCellLinkage &elnk = wpos.getPosE().getCellLinkage();
|
|
if (elnk.isSSlotValid())
|
|
neighbs.set(CDirection::SE);
|
|
if (elnk.isNSlotValid())
|
|
neighbs.set(CDirection::NE);
|
|
}
|
|
|
|
if (lnks.isWSlotValid())
|
|
{
|
|
neighbs.set(CDirection::W);
|
|
|
|
const CCellLinkage &wlnk = wpos.getPosW().getCellLinkage();
|
|
if (wlnk.isSSlotValid())
|
|
neighbs.set(CDirection::SW);
|
|
if (wlnk.isNSlotValid())
|
|
neighbs.set(CDirection::NW);
|
|
}
|
|
return neighbs;
|
|
}
|
|
|
|
|
|
//
|
|
bool CWorldMap::customCheckDiagMove(const CWorldPosition &pos, const CDirection &direction, TAStarFlag denyFlags) const
|
|
{
|
|
H_AUTO(AI_WorldMap_move);
|
|
// get straight links
|
|
const CCellLinkage &lnk = pos.getCellLinkage();
|
|
|
|
switch (direction.getVal())
|
|
{
|
|
case CDirection::SE:
|
|
{
|
|
{
|
|
CWorldPosition tmpPos(pos);
|
|
if (!tmpPos.getCellLinkage().isSSlotValid())
|
|
return false;
|
|
|
|
tmpPos=tmpPos.getPosS();
|
|
if ((tmpPos.getTopologyRef().getCstTopologyNode().getFlags()&denyFlags)!=0)
|
|
return false;
|
|
|
|
if (!tmpPos.getCellLinkage().isESlotValid())
|
|
return false;
|
|
|
|
tmpPos=tmpPos.getPosE();
|
|
if ((tmpPos.getTopologyRef().getCstTopologyNode().getFlags()&denyFlags)!=0)
|
|
return false;
|
|
}
|
|
|
|
{
|
|
CWorldPosition tmpPos(pos);
|
|
if (!tmpPos.getCellLinkage().isESlotValid())
|
|
return false;
|
|
|
|
tmpPos=tmpPos.getPosE();
|
|
if ((tmpPos.getTopologyRef().getCstTopologyNode().getFlags()&denyFlags)!=0)
|
|
return false;
|
|
|
|
if (!tmpPos.getCellLinkage().isSSlotValid())
|
|
return false;
|
|
|
|
// tmpPos=tmpPos.getPosS();
|
|
// if ((tmpPos.getTopologyRef().getCstTopologyNode().getFlags()&denyFlags)!=0)
|
|
// return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
case CDirection::NE:
|
|
{
|
|
{
|
|
CWorldPosition tmpPos(pos);
|
|
if (!tmpPos.getCellLinkage().isNSlotValid())
|
|
return false;
|
|
|
|
tmpPos=tmpPos.getPosN();
|
|
if ((tmpPos.getTopologyRef().getCstTopologyNode().getFlags()&denyFlags)!=0)
|
|
return false;
|
|
|
|
if (!tmpPos.getCellLinkage().isESlotValid())
|
|
return false;
|
|
|
|
tmpPos=tmpPos.getPosE();
|
|
if ((tmpPos.getTopologyRef().getCstTopologyNode().getFlags()&denyFlags)!=0)
|
|
return false;
|
|
}
|
|
|
|
{
|
|
CWorldPosition tmpPos(pos);
|
|
if (!tmpPos.getCellLinkage().isESlotValid())
|
|
return false;
|
|
|
|
tmpPos=tmpPos.getPosE();
|
|
if ((tmpPos.getTopologyRef().getCstTopologyNode().getFlags()&denyFlags)!=0)
|
|
return false;
|
|
|
|
if (!tmpPos.getCellLinkage().isNSlotValid())
|
|
return false;
|
|
|
|
// tmpPos=tmpPos.getPosN();
|
|
// if ((tmpPos.getTopologyRef().getCstTopologyNode().getFlags()&denyFlags)!=0)
|
|
// return false;
|
|
}
|
|
return true;
|
|
}
|
|
case CDirection::NW:
|
|
{
|
|
{
|
|
CWorldPosition tmpPos(pos);
|
|
if (!tmpPos.getCellLinkage().isNSlotValid())
|
|
return false;
|
|
|
|
tmpPos=tmpPos.getPosN();
|
|
if ((tmpPos.getTopologyRef().getCstTopologyNode().getFlags()&denyFlags)!=0)
|
|
return false;
|
|
|
|
if (!tmpPos.getCellLinkage().isWSlotValid())
|
|
return false;
|
|
|
|
tmpPos=tmpPos.getPosW();
|
|
if ((tmpPos.getTopologyRef().getCstTopologyNode().getFlags()&denyFlags)!=0)
|
|
return false;
|
|
}
|
|
|
|
{
|
|
CWorldPosition tmpPos(pos);
|
|
if (!tmpPos.getCellLinkage().isWSlotValid())
|
|
return false;
|
|
|
|
tmpPos=tmpPos.getPosW();
|
|
if ((tmpPos.getTopologyRef().getCstTopologyNode().getFlags()&denyFlags)!=0)
|
|
return false;
|
|
|
|
if (!tmpPos.getCellLinkage().isNSlotValid())
|
|
return false;
|
|
|
|
// tmpPos=tmpPos.getPosN();
|
|
// if ((tmpPos.getTopologyRef().getCstTopologyNode().getFlags()&denyFlags)!=0)
|
|
// return false;
|
|
}
|
|
return true;
|
|
}
|
|
case CDirection::SW:
|
|
{
|
|
{
|
|
CWorldPosition tmpPos(pos);
|
|
if (!tmpPos.getCellLinkage().isSSlotValid())
|
|
return false;
|
|
|
|
tmpPos=tmpPos.getPosS();
|
|
if ((tmpPos.getTopologyRef().getCstTopologyNode().getFlags()&denyFlags)!=0)
|
|
return false;
|
|
|
|
if (!tmpPos.getCellLinkage().isWSlotValid())
|
|
return false;
|
|
|
|
tmpPos=tmpPos.getPosW();
|
|
if ((tmpPos.getTopologyRef().getCstTopologyNode().getFlags()&denyFlags)!=0)
|
|
return false;
|
|
}
|
|
|
|
{
|
|
CWorldPosition tmpPos(pos);
|
|
if (!tmpPos.getCellLinkage().isWSlotValid())
|
|
return false;
|
|
|
|
tmpPos=tmpPos.getPosW();
|
|
if ((tmpPos.getTopologyRef().getCstTopologyNode().getFlags()&denyFlags)!=0)
|
|
return false;
|
|
|
|
if (!tmpPos.getCellLinkage().isSSlotValid())
|
|
return false;
|
|
|
|
// tmpPos=tmpPos.getPosS();
|
|
// if ((tmpPos.getTopologyRef().getCstTopologyNode().getFlags()&denyFlags)!=0)
|
|
// return false;
|
|
}
|
|
return true;
|
|
}
|
|
break;
|
|
default:
|
|
break;
|
|
|
|
}
|
|
return false;
|
|
}
|
|
|
|
/*
|
|
bool CWorldMap::customCheckDiagMove(const CWorldPosition &pos, const CDirection &direction, TAStarFlag denyFlags) const
|
|
{
|
|
H_AUTO(AI_WorldMap_move);
|
|
// get straight links
|
|
const CCellLinkage &lnk = pos.getCellLinkage();
|
|
|
|
switch (direction.getVal())
|
|
{
|
|
case CDirection::SE:
|
|
{
|
|
if (!lnk.isSSlotValid())
|
|
return false;
|
|
if (!lnk.isESlotValid())
|
|
return false;
|
|
|
|
return ( pos.getPosS().getCellLinkage().isESlotValid()
|
|
|| pos.getPosE().getCellLinkage().isSSlotValid());
|
|
}
|
|
|
|
case CDirection::NE:
|
|
{
|
|
if (!lnk.isNSlotValid())
|
|
return false;
|
|
if (!lnk.isESlotValid())
|
|
return false;
|
|
|
|
return ( pos.getPosN().getCellLinkage().isESlotValid()
|
|
|| pos.getPosE().getCellLinkage().isNSlotValid());
|
|
}
|
|
case CDirection::NW:
|
|
{
|
|
if (!lnk.isNSlotValid())
|
|
return false;
|
|
if (!lnk.isWSlotValid())
|
|
return false;
|
|
|
|
return ( pos.getPosN().getCellLinkage().isWSlotValid()
|
|
|| pos.getPosW().getCellLinkage().isNSlotValid());
|
|
}
|
|
case CDirection::SW:
|
|
{
|
|
if (!lnk.isSSlotValid())
|
|
return false;
|
|
if (!lnk.isWSlotValid())
|
|
return false;
|
|
|
|
return ( pos.getPosS().getCellLinkage().isWSlotValid()
|
|
|| pos.getPosW().getCellLinkage().isSSlotValid());
|
|
}
|
|
|
|
}
|
|
return false;
|
|
}
|
|
*/
|
|
|
|
//
|
|
bool CWorldMap::move(CWorldPosition &pos, const CDirection &direction) const
|
|
{
|
|
H_AUTO(AI_WorldMap_move);
|
|
// get straight links
|
|
const CCellLinkage &lnk = pos.getCellLinkage();
|
|
|
|
switch (direction.getVal())
|
|
{
|
|
case CDirection::S:
|
|
{
|
|
if (lnk.isSSlotValid())
|
|
{
|
|
pos.stepS();
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
case CDirection::SE:
|
|
{
|
|
if (lnk.isSSlotValid())
|
|
{
|
|
const CWorldPosition temp(pos.getPosS());
|
|
if (temp.getCellLinkage().isESlotValid())
|
|
{
|
|
temp.setPosE(pos);
|
|
return true;
|
|
}
|
|
}
|
|
|
|
if (lnk.isESlotValid())
|
|
{
|
|
const CWorldPosition temp(pos.getPosE());
|
|
if (temp.getCellLinkage().isSSlotValid())
|
|
{
|
|
temp.setPosS(pos);
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
case CDirection::E:
|
|
{
|
|
if (lnk.isESlotValid())
|
|
{
|
|
pos.stepE();
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
case CDirection::NE:
|
|
{
|
|
if (lnk.isESlotValid())
|
|
{
|
|
const CWorldPosition temp(pos.getPosE());
|
|
if (temp.getCellLinkage().isNSlotValid())
|
|
{
|
|
temp.setPosN(pos);
|
|
return true;
|
|
}
|
|
|
|
}
|
|
|
|
if (lnk.isNSlotValid())
|
|
{
|
|
const CWorldPosition temp(pos.getPosN());
|
|
if (temp.getCellLinkage().isESlotValid())
|
|
{
|
|
temp.setPosE(pos);
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
case CDirection::N:
|
|
{
|
|
if (lnk.isNSlotValid())
|
|
{
|
|
pos.stepN();
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
case CDirection::NW:
|
|
{
|
|
if (lnk.isWSlotValid())
|
|
{
|
|
const CWorldPosition temp(pos.getPosW());
|
|
if (temp.getCellLinkage().isNSlotValid())
|
|
{
|
|
temp.setPosN(pos);
|
|
return true;
|
|
}
|
|
}
|
|
|
|
if (lnk.isNSlotValid())
|
|
{
|
|
const CWorldPosition temp(pos.getPosN());
|
|
if (temp.getCellLinkage().isWSlotValid())
|
|
{
|
|
temp.setPosW(pos);
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
case CDirection::W:
|
|
{
|
|
if (lnk.isWSlotValid())
|
|
{
|
|
pos.stepW();
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
case CDirection::SW:
|
|
{
|
|
if (lnk.isSSlotValid())
|
|
{
|
|
const CWorldPosition temp(pos.getPosS());
|
|
if (temp.getCellLinkage().isWSlotValid())
|
|
{
|
|
temp.setPosW(pos);
|
|
return true;
|
|
}
|
|
}
|
|
|
|
if (lnk.isWSlotValid())
|
|
{
|
|
const CWorldPosition temp(pos.getPosW());
|
|
if (temp.getCellLinkage().isSSlotValid())
|
|
{
|
|
temp.setPosS(pos);
|
|
return true;
|
|
}
|
|
|
|
}
|
|
return false;
|
|
}
|
|
default:
|
|
break;
|
|
|
|
}
|
|
return false;
|
|
}
|
|
|
|
//
|
|
bool CWorldMap::moveSecure(CWorldPosition &pos, const CDirection &direction, uint16 maskFlags) const
|
|
{
|
|
H_AUTO(AI_WorldMap_moveSecure);
|
|
// get straight links
|
|
|
|
uint16 pflags = pos.getFlags();
|
|
|
|
switch (direction.getVal())
|
|
{
|
|
case CDirection::S:
|
|
{
|
|
return pos.moveS();
|
|
}
|
|
case CDirection::SE:
|
|
{
|
|
CWorldPosition p1(pos), p2(pos);
|
|
if (p1.moveS() && (((p1.getFlags()^pflags)&maskFlags) == 0) && p1.moveE() && (((p1.getFlags()^pflags)&maskFlags) == 0) &&
|
|
p2.moveE() && (((p2.getFlags()^pflags)&maskFlags) == 0) && p2.moveS() && (((p2.getFlags()^pflags)&maskFlags) == 0) &&
|
|
p1 == p2)
|
|
{
|
|
pos = p1;
|
|
return true;
|
|
}
|
|
break;
|
|
}
|
|
case CDirection::E:
|
|
{
|
|
return pos.moveE();
|
|
}
|
|
case CDirection::NE:
|
|
{
|
|
CWorldPosition p1(pos), p2(pos);
|
|
if (p1.moveN() && (((p1.getFlags()^pflags)&maskFlags) == 0) && p1.moveE() && (((p1.getFlags()^pflags)&maskFlags) == 0) &&
|
|
p2.moveE() && (((p2.getFlags()^pflags)&maskFlags) == 0) && p2.moveN() && (((p2.getFlags()^pflags)&maskFlags) == 0) &&
|
|
p1 == p2)
|
|
{
|
|
pos = p1;
|
|
return true;
|
|
}
|
|
break;
|
|
}
|
|
case CDirection::N:
|
|
{
|
|
return pos.moveN();
|
|
}
|
|
case CDirection::NW:
|
|
{
|
|
CWorldPosition p1(pos), p2(pos);
|
|
if (p1.moveN() && (((p1.getFlags()^pflags)&maskFlags) == 0) && p1.moveW() && (((p1.getFlags()^pflags)&maskFlags) == 0) &&
|
|
p2.moveW() && (((p2.getFlags()^pflags)&maskFlags) == 0) && p2.moveN() && (((p2.getFlags()^pflags)&maskFlags) == 0) &&
|
|
p1 == p2)
|
|
{
|
|
pos = p1;
|
|
return true;
|
|
}
|
|
break;
|
|
}
|
|
case CDirection::W:
|
|
{
|
|
return pos.moveW();
|
|
}
|
|
case CDirection::SW:
|
|
{
|
|
CWorldPosition p1(pos), p2(pos);
|
|
if (p1.moveS() && (((p1.getFlags()^pflags)&maskFlags) == 0) && p1.moveW() && (((p1.getFlags()^pflags)&maskFlags) == 0) &&
|
|
p2.moveW() && (((p2.getFlags()^pflags)&maskFlags) == 0) && p2.moveS() && (((p2.getFlags()^pflags)&maskFlags) == 0) &&
|
|
p1 == p2)
|
|
{
|
|
pos = p1;
|
|
return true;
|
|
}
|
|
break;
|
|
}
|
|
default:
|
|
break;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
//
|
|
bool CWorldMap::moveDiagTestBothSide(CWorldPosition &pos, const CDirection &direction) const
|
|
{
|
|
H_AUTO(AI_WorldMap_move);
|
|
// get straight links
|
|
const CCellLinkage &lnk = pos.getCellLinkage();
|
|
|
|
switch (direction.getVal())
|
|
{
|
|
case CDirection::S:
|
|
{
|
|
if (lnk.isSSlotValid())
|
|
{
|
|
pos.stepS();
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
case CDirection::SE:
|
|
{
|
|
if (!lnk.isSSlotValid() || !lnk.isESlotValid())
|
|
return false;
|
|
|
|
CSlot eSlot=pos.getPosS().getCellLinkage().ESlot();
|
|
if (!eSlot.isValid())
|
|
return false;
|
|
|
|
const CWorldPosition temp(pos.getPosE());
|
|
if (temp.getCellLinkage().SSlot()!=eSlot)
|
|
return false;
|
|
|
|
temp.setPosS(pos);
|
|
return true;
|
|
}
|
|
case CDirection::E:
|
|
{
|
|
if (lnk.isESlotValid())
|
|
{
|
|
pos.stepE();
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
case CDirection::NE:
|
|
{
|
|
if (!lnk.isNSlotValid() || !lnk.isESlotValid())
|
|
return false;
|
|
|
|
CSlot eSlot=pos.getPosN().getCellLinkage().ESlot();
|
|
if (!eSlot.isValid())
|
|
return false;
|
|
|
|
const CWorldPosition temp(pos.getPosE());
|
|
if (temp.getCellLinkage().NSlot()!=eSlot)
|
|
return false;
|
|
|
|
temp.setPosN(pos);
|
|
return true;
|
|
}
|
|
case CDirection::N:
|
|
{
|
|
if (lnk.isNSlotValid())
|
|
{
|
|
pos.stepN();
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
case CDirection::NW:
|
|
{
|
|
if (!lnk.isNSlotValid() || !lnk.isWSlotValid())
|
|
return false;
|
|
|
|
CSlot wSlot=pos.getPosN().getCellLinkage().WSlot();
|
|
if (!wSlot.isValid())
|
|
return false;
|
|
|
|
const CWorldPosition temp(pos.getPosW());
|
|
if (temp.getCellLinkage().NSlot()!=wSlot)
|
|
return false;
|
|
|
|
temp.setPosN(pos);
|
|
return true;
|
|
}
|
|
case CDirection::W:
|
|
{
|
|
if (lnk.isWSlotValid())
|
|
{
|
|
pos.stepW();
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
case CDirection::SW:
|
|
{
|
|
if (!lnk.isSSlotValid() || !lnk.isWSlotValid())
|
|
return false;
|
|
|
|
CSlot wSlot=pos.getPosS().getCellLinkage().WSlot();
|
|
if (!wSlot.isValid())
|
|
return false;
|
|
|
|
const CWorldPosition temp(pos.getPosW());
|
|
if (temp.getCellLinkage().SSlot()!=wSlot)
|
|
return false;
|
|
|
|
temp.setPosS(pos);
|
|
return true;
|
|
}
|
|
default:
|
|
break;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
|
|
void areCompatiblesWithoutStartRestriction(const CWorldPosition &startPos, const CWorldPosition& endPos, const TAStarFlag &denyflags, CCompatibleResult &res, bool allowStartRestriction)
|
|
{
|
|
res.setValid(false);
|
|
|
|
if (&startPos == NULL)
|
|
{
|
|
nlwarning("Invalid startPos (NULL)");
|
|
return;
|
|
}
|
|
|
|
if (&endPos == NULL)
|
|
{
|
|
nlwarning("Invalid endPos (NULL)");
|
|
return;
|
|
}
|
|
|
|
const CTopology &startTopoNode=startPos.getTopologyRef().getCstTopologyNode();
|
|
const CTopology &endTopoNode=endPos.getTopologyRef().getCstTopologyNode();
|
|
TAStarFlag startFlag=(TAStarFlag)(startTopoNode.getFlags()&WaterAndNogo);
|
|
// if (!allowStartRestriction)
|
|
startFlag=Nothing;
|
|
|
|
|
|
for (TAStarFlag possibleFlag=Nothing;possibleFlag<=WaterAndNogo;possibleFlag=(TAStarFlag)(possibleFlag+2)) // tricky !! -> to replace with a defined list of flags to checks.
|
|
{
|
|
const uint32 incompatibilityFlags=(possibleFlag&(denyflags&~startFlag))&WaterAndNogo;
|
|
|
|
if (incompatibilityFlags)
|
|
continue;
|
|
|
|
const uint32 startMasterTopo=startTopoNode.getMasterTopo(possibleFlag);
|
|
const uint32 endMasterTopo=endTopoNode.getMasterTopo(possibleFlag);
|
|
if ( (startMasterTopo^endMasterTopo)!=0
|
|
|| startMasterTopo == std::numeric_limits<uint32>::max()) // if not same masterTopo or invalid masterTopo then bypass ..
|
|
continue;
|
|
|
|
res.set(possibleFlag, startMasterTopo);
|
|
res.setValid();
|
|
|
|
if (((possibleFlag&denyflags)&WaterAndNogo)==0) // it was the optimal case ?
|
|
break;
|
|
}
|
|
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////////
|
|
// Path finding //
|
|
//////////////////////////////////////////////////////////////////////////////
|
|
|
|
//#define CHECK_HEAP
|
|
std::map<uint, uint> MapAStarNbSteps;
|
|
uint LastAStarNbSteps = 0;
|
|
|
|
NLMISC_CATEGORISED_COMMAND(ais, dumpAStarSteps, "Dump the distribution of A* number of steps", "")
|
|
{
|
|
log.displayNL( "Distribution of the %u nb steps:", MapAStarNbSteps.size() );
|
|
log.displayNL( "NbSteps\tNbOccurrences", MapAStarNbSteps.size() );
|
|
for( std::map<uint, uint>::const_iterator first=MapAStarNbSteps.begin(), last=MapAStarNbSteps.end(); first!=last; ++first )
|
|
{
|
|
log.displayNL( "%u\t%u", first->first, first->second );
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool CWorldMap::findAStarPath(CWorldPosition const& start, CWorldPosition const& end, std::vector<CTopology::TTopologyRef>& path, TAStarFlag denyflags) const
|
|
{
|
|
H_AUTO(findAStarPath1);
|
|
|
|
// Clear destination path
|
|
path.clear();
|
|
|
|
// Check start position validity
|
|
if (!start.isValid())
|
|
{
|
|
_LastFASPReason = FASPR_INVALID_START_POS;
|
|
return false;
|
|
}
|
|
// Check end position validity
|
|
if (!end.isValid())
|
|
{
|
|
_LastFASPReason = FASPR_INVALID_END_POS;
|
|
return false;
|
|
}
|
|
|
|
// Get start and end topologies
|
|
CTopology::TTopologyRef startTopo = start.getTopologyRef();
|
|
CTopology::TTopologyRef endTopo = end.getTopologyRef();
|
|
|
|
// Get associated topology nodes
|
|
CTopology const& startTopoNode = startTopo.getCstTopologyNode();
|
|
CTopology const& endTopoNode = endTopo.getCstTopologyNode();
|
|
|
|
// Check start point
|
|
if (!startTopo.isValid())
|
|
{
|
|
_LastFASPReason = FASPR_INVALID_START_TOPO;
|
|
return false;
|
|
}
|
|
// Check end point
|
|
if (!endTopo.isValid())
|
|
{
|
|
_LastFASPReason = FASPR_INVALID_END_TOPO;
|
|
return false;
|
|
}
|
|
|
|
// Check compatibility of start and end points depending on flags to avoid
|
|
RYAI_MAP_CRUNCH::CCompatibleResult res;
|
|
areCompatiblesWithoutStartRestriction(start, end, denyflags, res, true);
|
|
if (!res.isValid())
|
|
{
|
|
_LastFASPReason = FASPR_INCOMPATIBLE_POSITIONS;
|
|
return false;
|
|
}
|
|
|
|
// Get flags to use to compute the path
|
|
TAStarFlag movementFlags = res.movementFlags();
|
|
// Get the master topology inside which to compute the path (reminder: no path between different master topo)
|
|
uint32 choosenMasterTopo=res.choosenMasterTopo();
|
|
|
|
// A list of A* nodes
|
|
vector<CAStarHeapNode> nodes;
|
|
// List of visited topologies, with associated node in 'nodes' vector
|
|
map<CTopology::TTopologyId, uint> visited;
|
|
|
|
// The heap used to store A* nodes
|
|
// :TODO: Check if STL heap is not better suited, or if another data structure would be more useful in AIS
|
|
CHeap<float, uint> heap;
|
|
|
|
// Get end position
|
|
CVector const& endPoint = endTopo.getCstTopologyNode().Position;
|
|
|
|
// Create a heap node for the start point
|
|
CAStarHeapNode hnode(startTopo, 0xffffffff, 0.0f, true);
|
|
// Push it in the node list
|
|
nodes.push_back(hnode);
|
|
// Take it as first father
|
|
uint father = (uint)nodes.size()-1;
|
|
|
|
// Add start topology to visited nodes (father holds start topo node index for the moment)
|
|
visited.insert(make_pair<CTopology::TTopologyId,uint>(startTopo, father));
|
|
// Push start node in the heap with a zero cost
|
|
heap.push(0.0f, father);
|
|
|
|
// Boolean to notify that end point has been reached
|
|
bool found = false;
|
|
|
|
#ifdef CHECK_HEAP
|
|
static uint32 maxHeap = 65535;
|
|
static uint32 maxHeapMeasure = 0;
|
|
#endif
|
|
|
|
uint nbHeapSteps = 0;
|
|
while (!heap.empty())
|
|
{
|
|
#ifdef CHECK_HEAP
|
|
if (heap.size()>maxHeap) // if too much calculs, not found (to remove when debugged).
|
|
break;
|
|
#endif
|
|
|
|
++nbHeapSteps;
|
|
|
|
// Get best node (popping it)
|
|
father = std::numeric_limits<uint>::max(); // :TODO: Remove that useless statement (since do while first loop ALWAYS overwrite it)
|
|
do
|
|
{
|
|
father = heap.pop();
|
|
}
|
|
while (!nodes[father].isOpened() && !heap.empty());
|
|
|
|
if (father == std::numeric_limits<uint>::max())
|
|
break;
|
|
|
|
// Mark current node as closed
|
|
hnode.setOpen(false);
|
|
|
|
// Make best node the current one
|
|
hnode = nodes[father];
|
|
|
|
// Get the current node itself
|
|
CTopology::TTopologyRef const& current = hnode.getRef();
|
|
|
|
// If we reached the end node, stop search
|
|
if (current==endTopo)
|
|
{
|
|
found=true;
|
|
break;
|
|
}
|
|
|
|
// Get current node topology
|
|
CTopology const& ctp = current.getCstTopologyNode();
|
|
|
|
// Get g(n) for current node
|
|
float dist = hnode.getDistance();
|
|
|
|
// Examine each neighbour of the current node
|
|
for (vector<CTopology::CNeighbourLink>::const_iterator it=ctp.Neighbours.begin(), itEnd=ctp.Neighbours.end();it!=itEnd;++it)
|
|
{
|
|
// Get the neighbour topology node
|
|
CTopology::CNeighbourLink const& neighbourLink = (*it);
|
|
CTopology::TTopologyRef const& next = neighbourLink.getTopologyRef();
|
|
|
|
// If it's not in the same master topo skip it
|
|
if (next.getCstTopologyNode().getMasterTopo(movementFlags)!=choosenMasterTopo)
|
|
continue;
|
|
|
|
// Compute neighbour node g(n)
|
|
float distance = dist + neighbourLink.getDistance();
|
|
|
|
uint child;
|
|
// Check if node has already been visited
|
|
map<CTopology::TTopologyId, uint>::iterator itv = visited.find(next);
|
|
if (itv!=visited.end())
|
|
{
|
|
// Assume child is that node
|
|
child = (*itv).second;
|
|
// If that node's previous distance is better than the new one skip it
|
|
if (nodes[child].getDistance() <= distance)
|
|
continue;
|
|
|
|
// Close the old node
|
|
nodes[child].setOpen(false);
|
|
// Remove it from visited
|
|
visited.erase(itv);
|
|
}
|
|
// Create a new node for that cell
|
|
child = (uint)nodes.size();
|
|
nodes.push_back(CAStarHeapNode(next, father, distance, true));
|
|
// Compute h(n) as an euclidian distance heuristic
|
|
float heuristic = (endPoint-next.getCstTopologyNode().Position).norm();
|
|
// Add node to heap with a computed f(n)=g(n)+h(n)
|
|
heap.push(distance + heuristic, child);
|
|
// Add node to visited
|
|
visited.insert(make_pair<CTopology::TTopologyId,uint>(next, child));
|
|
}
|
|
}
|
|
|
|
#ifdef CHECK_HEAP
|
|
if (heap.size()>maxHeapMeasure)
|
|
{
|
|
maxHeapMeasure=heap.size();
|
|
}
|
|
#endif
|
|
|
|
++MapAStarNbSteps[nbHeapSteps];
|
|
LastAStarNbSteps = nbHeapSteps;
|
|
|
|
#ifdef NL_DEBUG
|
|
nlassert(found);
|
|
#else
|
|
if (!found)
|
|
{
|
|
nlwarning("(!!Appeler StepH!!)Path not found from %s : %d to %s : %d", start.toString().c_str(), start.slot(), end.toString().c_str(), end.slot());
|
|
}
|
|
#endif
|
|
|
|
// If not found, return error
|
|
if (!found)
|
|
{
|
|
_LastFASPReason = FASPR_NOT_FOUND;
|
|
return false;
|
|
}
|
|
|
|
// Backtrack path
|
|
while (father != 0xffffffff)
|
|
{
|
|
CAStarHeapNode const& node = nodes[father];
|
|
path.push_back(node.getRef());
|
|
father = node.getFather();
|
|
}
|
|
|
|
// Reverse path container
|
|
std::reverse(path.begin(), path.end());
|
|
|
|
_LastFASPReason = FASPR_NO_ERROR;
|
|
return true;
|
|
}
|
|
|
|
// Finds an A* path
|
|
bool CWorldMap::findAStarPath(const CTopology::TTopologyId &start, const CTopology::TTopologyId &end, CAStarPath &path, TAStarFlag denyflags) const
|
|
{
|
|
H_AUTO(findAStarPath2)
|
|
path._TopologiesPath.clear();
|
|
|
|
CTopology::TTopologyRef startTopo = getTopologyRef(start);
|
|
CTopology::TTopologyRef endTopo = getTopologyRef(end);
|
|
|
|
// if not found start point or end point, abort
|
|
if (!startTopo.isValid() || !endTopo.isValid())
|
|
return false;
|
|
|
|
vector<CAStarHeapNode> nodes;
|
|
map<CTopology::TTopologyId, uint> visited; // topology + node index in vector
|
|
CHeap<float, uint> heap;
|
|
|
|
const CVector &endPoint = endTopo.getCstTopologyNode().Position;
|
|
|
|
// add current to heap
|
|
CAStarHeapNode hnode(startTopo,0xffffffff,0.0f,true);
|
|
|
|
nodes.push_back(hnode);
|
|
uint father = (uint)nodes.size()-1;
|
|
|
|
// add current to visited nodes
|
|
visited.insert(make_pair<CTopology::TTopologyId,uint>(startTopo, father));
|
|
heap.push(0.0f, father);
|
|
|
|
bool found=false;
|
|
|
|
while (!heap.empty())
|
|
{
|
|
// pop best node
|
|
father = heap.pop();
|
|
hnode = nodes[father];
|
|
|
|
const CTopology::TTopologyRef ¤t = hnode.getRef();
|
|
|
|
// if reached end node, leave
|
|
if (current==endTopo)
|
|
{
|
|
found=true;
|
|
break;
|
|
}
|
|
|
|
const CTopology &ctp = current.getCstTopologyNode();
|
|
float dist = hnode.getDistance();
|
|
|
|
for (uint i=0; i<ctp.Neighbours.size(); ++i)
|
|
{
|
|
const CTopology::CNeighbourLink &neightBourgLink=ctp.Neighbours[i];
|
|
|
|
const CTopology::TTopologyRef &next=neightBourgLink.getTopologyRef();
|
|
|
|
const CTopology &ntp = next.getCstTopologyNode();
|
|
|
|
// don't examine not accessible nodes
|
|
if ((ntp.Flags & denyflags) != 0)
|
|
continue;
|
|
|
|
// compute actual node distance
|
|
float distance = dist + neightBourgLink.getDistance();
|
|
|
|
// if node is open or closed and previous visit has shorter path, skip node
|
|
map<CTopology::TTopologyId, uint>::iterator itv = visited.find(next);
|
|
if (itv != visited.end() && nodes[(*itv).second].getDistance() <= distance)
|
|
continue;
|
|
|
|
// compute heuristic
|
|
float heuristic = distance + (endPoint-ntp.Position).norm();
|
|
|
|
// setup node
|
|
CAStarHeapNode cnode(next,father,distance,true);
|
|
uint child;
|
|
|
|
// setup node
|
|
if (itv == visited.end())
|
|
{
|
|
// if node is not open nor closed, create an entry
|
|
child = (uint)nodes.size();
|
|
nodes.push_back(cnode);
|
|
}
|
|
else
|
|
{
|
|
// else recover previous entry -- reopen node
|
|
child = (*itv).second;
|
|
nodes[child]=cnode;
|
|
}
|
|
|
|
// add node to visited and to heap
|
|
heap.push(heuristic, child);
|
|
visited.insert(make_pair<CTopology::TTopologyId,uint>(next, child));
|
|
}
|
|
}
|
|
|
|
// if not found, return error
|
|
if (!found)
|
|
return false;
|
|
|
|
// backtrack path
|
|
while (father != 0xffffffff)
|
|
{
|
|
const CAStarHeapNode& node=nodes[father];
|
|
|
|
path._TopologiesPath.push_back(node.getRef());
|
|
father = node.getFather();
|
|
}
|
|
|
|
// reverse path
|
|
reverse(path._TopologiesPath.begin(), path._TopologiesPath.end());
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
// This whole routine MUST be optimized !! Its slow .. (to much).
|
|
bool CWorldMap::findInsideAStarPath(CWorldPosition const& start, CWorldPosition const& end, std::vector<CDirection>& stepPath, TAStarFlag denyflags) const
|
|
{
|
|
H_AUTO(findInsideAStarPath);
|
|
|
|
// Check start and end position validity
|
|
if (!start.isValid())
|
|
{
|
|
_LastFIASPReason = FIASPR_INVALID_START_POS;
|
|
return false;
|
|
}
|
|
if (!end.isValid())
|
|
{
|
|
_LastFIASPReason = FIASPR_INVALID_END_POS;
|
|
return false;
|
|
}
|
|
// Verify that they are in the same topology
|
|
if (start.getTopologyRef()!=end.getTopologyRef())
|
|
{
|
|
_LastFIASPReason = FIASPR_DIFFERENT_TOPO;
|
|
return false;
|
|
}
|
|
|
|
// A list of A* nodes
|
|
vector<CInsideAStarHeapNode> nodes;
|
|
// List of visited nodes, with associated node index in 'nodes' vector
|
|
map<CAStarNode, uint> visited;
|
|
// The heap used to store A* nodes
|
|
// :TODO: Check if STL heap is not better suited, or if another data structure would be more useful in AIS
|
|
CHeap<float, uint> heap;
|
|
|
|
// Get end point
|
|
CVector endPoint = end.toVectorD();
|
|
// Build a node for the end point
|
|
CAStarNode endId(end);
|
|
// Build a node for the start point
|
|
CAStarNode startNode(start);
|
|
|
|
// Create a heap node for the start point and push it in the node list
|
|
nodes.push_back(CInsideAStarHeapNode(startNode, 0xffffffff, CDirection(), 0.f, true));
|
|
// Take it as first father
|
|
uint father = (uint)nodes.size()-1;
|
|
|
|
// Add start node to visited nodes (father holds start node index for the moment)
|
|
visited.insert(make_pair<CAStarNode, uint>(startNode, father));
|
|
// Push start node in the heap with a zero cost
|
|
heap.push(0.0f, father);
|
|
|
|
// Boolean to notify that end point has been reached
|
|
bool found = false;
|
|
|
|
while (!heap.empty())
|
|
{
|
|
// Get best node (popping it)
|
|
father = heap.pop();
|
|
// Get associated heap node
|
|
CInsideAStarHeapNode& hnode = nodes[father];
|
|
// Get associated A* node
|
|
CAStarNode const& current = hnode.getNode();
|
|
|
|
// If we reached the end node, stop search
|
|
if (current==endId)
|
|
{
|
|
found=true;
|
|
break;
|
|
}
|
|
|
|
// Get g(n) for current node
|
|
float dist = hnode.getDistance();
|
|
|
|
// Get current node slot
|
|
CSlot slot = current.slot();
|
|
// Compute a map position
|
|
CMapPosition pos = start; // The map has the same cell than the start pos (coz in same topo)
|
|
current.updateMapPosition(pos); // Just update the unit id
|
|
|
|
// For each neighbour (8 directions)
|
|
CNeighbourhood neighbourhood = neighbours(getWorldPosition(pos,slot));
|
|
for (uint i=0; i<8; ++i)
|
|
{
|
|
// Compute a CDirection
|
|
CDirection dir((CDirection::TDirection)i);
|
|
|
|
// If neighbour in that direction is not valid skip it
|
|
if (!neighbourhood.isValid(dir))
|
|
continue;
|
|
|
|
// :TODO: Continue documentation
|
|
// If we cannot move in that direction skip it
|
|
CWorldPosition mv(getWorldPosition(pos, slot));
|
|
if (!moveDiagTestBothSide(mv, dir))
|
|
continue;
|
|
|
|
// If that new point is not in the same cell skip it
|
|
if (!mv.hasSameFullCellId(start))
|
|
continue;
|
|
|
|
// If that point's flags are not compatible skip it
|
|
if ((denyflags & mv.getTopologyRef().getCstTopologyNode().getFlags()) != 0)
|
|
continue;
|
|
|
|
// Build an A* node
|
|
CAStarNode next(mv);
|
|
|
|
// Compute g(n) (diagonal)
|
|
float distance = dist + (((i & 1) != 0) ? 1.4142f : 1.0f);
|
|
|
|
// If node has already been visited and previous distance was better skip it
|
|
map<CAStarNode, uint>::iterator itv = visited.find(next);
|
|
if ( itv != visited.end()
|
|
&& nodes[(*itv).second].getDistance() <= distance )
|
|
continue;
|
|
|
|
uint child;
|
|
// If node has already been visited update it
|
|
if (itv!=visited.end())
|
|
{
|
|
child = (*itv).second;
|
|
nodes[child] = CInsideAStarHeapNode(next, father, dir, distance, true);
|
|
}
|
|
// Else create a new node
|
|
else
|
|
{
|
|
child = (uint)nodes.size();
|
|
nodes.push_back(CInsideAStarHeapNode(next, father, dir, distance, true));
|
|
}
|
|
// Compute h(n) as an euclidian distance heuristic
|
|
float heuristic = (endPoint-mv.toVectorD()).norm();
|
|
// Add node to heap with a computed f(n)=g(n)+h(n)
|
|
heap.push(distance + heuristic, child);
|
|
// Add node to visited
|
|
visited.insert(make_pair<CAStarNode, uint>(next, child));
|
|
}
|
|
}
|
|
|
|
#ifdef NL_DEBUG
|
|
nlassert(found);
|
|
#endif
|
|
// If not found, return error
|
|
if (!found)
|
|
{
|
|
_LastFIASPReason = FIASPR_NOT_FOUND;
|
|
return false;
|
|
}
|
|
|
|
stepPath.clear();
|
|
|
|
// Backtrack path
|
|
while (father != 0xffffffff)
|
|
{
|
|
if (nodes[father].getFather() != 0xffffffff)
|
|
stepPath.push_back(nodes[father].getDirection());
|
|
father = nodes[father].getFather();
|
|
}
|
|
|
|
// Reverse path container
|
|
std::reverse(stepPath.begin(), stepPath.end());
|
|
|
|
_LastFIASPReason = FIASPR_NO_ERROR;
|
|
return true;
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////////
|
|
// //
|
|
//////////////////////////////////////////////////////////////////////////////
|
|
|
|
bool CWorldMap::moveTowards(CWorldPosition& pos, CTopology::TTopologyRef const& topology) const
|
|
{
|
|
CGridDirectionLayer const* layer = getGridDirectionLayer(pos, topology);
|
|
if (!layer)
|
|
return false;
|
|
|
|
CDirection motion=layer->getDirection(pos);
|
|
if (!motion.isValid())
|
|
return false;
|
|
|
|
return move(pos,motion);
|
|
}
|
|
|
|
// Moves according a to a given path, returns false if failed
|
|
bool CWorldMap::move(CWorldPosition &pos, CAStarPath &path, uint ¤tstep) const
|
|
{
|
|
if (currentstep >= path._TopologiesPath.size())
|
|
return false;
|
|
|
|
CTopology::TTopologyRef cid(pos);
|
|
|
|
if (!cid.isValid())
|
|
return false;
|
|
|
|
if (cid==path._TopologiesPath[currentstep])
|
|
{
|
|
++currentstep;
|
|
if (currentstep==path._TopologiesPath.size())
|
|
return false;
|
|
}
|
|
return moveTowards(pos, path._TopologiesPath[currentstep]);
|
|
}
|
|
|
|
|
|
// Moves from a position to another
|
|
bool CWorldMap::move(CWorldPosition& pos, CMapPosition const& end, TAStarFlag const denyFlags) const
|
|
{
|
|
CWorldPosition tempPos(pos);
|
|
BOMB_IF((tempPos.getTopologyRef().getCstTopologyNode().getFlags()&denyFlags)!=0, "Error in CWorldMap::mode, invalid flag "<<RYAI_MAP_CRUNCH::toString(tempPos.getTopologyRef().getCstTopologyNode().getFlags())
|
|
<<"on world pos "<<pos.toString()<<" while going to map pos "<<end.toString()<<" with denyflags "<<RYAI_MAP_CRUNCH::toString(denyFlags), return false);
|
|
// not optimum but it will be rewrite for each specialized rootcell type.
|
|
const sint32 x0 = pos.x();
|
|
const sint32 y0 = pos.y();
|
|
|
|
const sint32 deltax = end.x() - x0;
|
|
const sint32 deltay = end.y() - y0;
|
|
|
|
const sint32 d = std::max(abs(deltax), abs(deltay));
|
|
|
|
for (sint32 i=1; i<=d; ++i)
|
|
{
|
|
const sint dx = x0 + (deltax*i)/d - pos.x();
|
|
const sint dy = y0 + (deltay*i)/d - pos.y();
|
|
|
|
|
|
if ( !move(tempPos, CDirection(dx,dy))
|
|
|| (tempPos.getTopologyRef().getCstTopologyNode().getFlags()&denyFlags)!=0) // Arghh !!
|
|
return false;
|
|
pos=tempPos;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
// Clears height map
|
|
void CWorldMap::clearHeightMap()
|
|
{
|
|
CMapPosition min, max;
|
|
getBounds(min, max);
|
|
|
|
CMapPosition scan, scanline;
|
|
|
|
for (scan = min; scan.yCoord() != max.yCoord(); scan = scan.stepCell(0, 1))
|
|
{
|
|
for (scanline = scan; scanline.x() != max.x(); scanline = scanline.stepCell(1, 0))
|
|
{
|
|
CRootCell* rootCell=getRootCell(scanline);
|
|
if (!rootCell)
|
|
continue;
|
|
rootCell->clearHeightMap();
|
|
}
|
|
}
|
|
}
|
|
|
|
// checks motion layers
|
|
void CWorldMap::checkMotionLayer()
|
|
{
|
|
CMapPosition min, max;
|
|
getBounds(min, max);
|
|
|
|
uint compute = 0, white = 0, simple = 0, multi = 0, other = 0;
|
|
countCells(compute, white, simple, multi, other);
|
|
uint total = compute+white+simple+multi+other;
|
|
uint compCells = 0;
|
|
|
|
CMapPosition scan, scanline;
|
|
CTimeEstimator timeest(total);
|
|
|
|
for (scan = min; scan.yCoord() != max.yCoord(); scan = scan.stepCell(0, 1))
|
|
{
|
|
for (scanline = scan; scanline.x() != max.x(); scanline = scanline.stepCell(1, 0))
|
|
{
|
|
|
|
CRootCell* rootCell=getRootCell(scanline);
|
|
if (!rootCell)
|
|
continue;
|
|
|
|
timeest.step("checkMotionLayer");
|
|
|
|
vector<CTopology> &topologies = rootCell->getTopologiesNodes();
|
|
|
|
// move from any point to the current topology
|
|
uint i;
|
|
for (i=0; i<topologies.size(); ++i)
|
|
{
|
|
CTopology &topology = topologies[i];
|
|
CTopology::TTopologyRef tref = getTopologyRef(topology.Id);
|
|
set<CTopology::TTopologyId> neighbours;
|
|
|
|
//
|
|
uint16 toflags = topology.Flags;
|
|
|
|
uint j;
|
|
for (j=0; j<topology.Neighbours.size(); ++j)
|
|
neighbours.insert(topology.Neighbours[j].getTopologyRef());
|
|
|
|
sint x, y, slot;
|
|
|
|
bool failed = false;
|
|
|
|
for (y=-16; y<32; ++y)
|
|
{
|
|
for (x=-16; x<32; ++x)
|
|
{
|
|
CMapPosition cpos(scanline.x()+x, scanline.y()+y);
|
|
|
|
for (slot=0; slot<3; ++slot)
|
|
{
|
|
// build world pos and test it is valid
|
|
CWorldPosition cwpos = getSafeWorldPosition(cpos, CSlot(slot));
|
|
if (!cwpos.isValid())
|
|
continue;
|
|
|
|
// if current topo is not a neighbour of checked topo, go to the next pos
|
|
CTopology::TTopologyId ctopoId = getTopologyId(cwpos);
|
|
if (neighbours.find(ctopoId) == neighbours.end())
|
|
continue;
|
|
|
|
//
|
|
CTopology &starttopo = getTopologyNode(ctopoId);
|
|
uint16 fromflags = starttopo.Flags;
|
|
|
|
cwpos = getWorldPosition(cpos, CSlot(slot));
|
|
|
|
bool found = false;
|
|
uint32 numSteps = 0;
|
|
|
|
do
|
|
{
|
|
if (getTopologyId(cwpos) == tref)
|
|
{
|
|
found = true;
|
|
break;
|
|
}
|
|
|
|
CTopology &ctopo = getTopologyNode(getTopologyId(cwpos));
|
|
uint16 cflags = ctopo.Flags;
|
|
|
|
uint16 allow = ((~fromflags) & (~toflags) & cflags);
|
|
|
|
if (allow != 0)
|
|
{
|
|
nlwarning("Unallowed move from (%04X,%04X,%d-topo=%08X) to topo %08X, fromflags=%04X, toflags=%04X, cflags=%04X", cpos.x(), cpos.y(), slot, ctopoId.getVal(), tref.getVal(), fromflags, toflags, cflags);
|
|
failed = true;
|
|
}
|
|
}
|
|
while (moveTowards(cwpos, tref) && cwpos.isValid() && numSteps++ < 128);
|
|
|
|
if (!found)
|
|
{
|
|
nlwarning("Failed to go from (%d,%d,%d-topo=%08X) to topo %08X", cpos.x(), cpos.y(), slot, ctopoId.getVal(), tref.getVal());
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (failed)
|
|
{
|
|
nlinfo("---- scanline = %04X %04X topo = %d ----", scanline.x(), scanline.y(), i);
|
|
topology.DirectionMap->dump();
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
//
|
|
void CWorldMap::buildMasterTopo(bool allowWater, bool allowNogo)
|
|
{
|
|
nlinfo("buildMasterTopo");
|
|
|
|
CMapPosition min, max;
|
|
getBounds(min, max);
|
|
|
|
CMapPosition scan, scanline;
|
|
|
|
uint masterTopo = 0;
|
|
uint totalTopos = 0;
|
|
|
|
for (scan = min; scan.y() != max.y(); scan = scan.stepCell(0, 1))
|
|
{
|
|
for (scanline = scan; scanline.x() != max.x(); scanline = scanline.stepCell(1, 0))
|
|
{
|
|
CRootCell *cell = getRootCell(scanline);
|
|
if (cell == NULL)
|
|
continue;
|
|
|
|
vector<CTopology> &topos = cell->getTopologiesNodes();
|
|
|
|
uint i;
|
|
for (i=0; i<topos.size(); ++i)
|
|
{
|
|
CTopology& tp = topos[i];
|
|
|
|
++totalTopos;
|
|
|
|
if (!tp.isCompatible(allowWater, allowNogo) ||
|
|
tp.getMasterTopo(allowWater, allowNogo) != CTopology::TTopologyId::UNDEFINED_TOPOLOGY)
|
|
continue;
|
|
|
|
set<CTopology::TTopologyId> tovisit;
|
|
|
|
// set mastertopo id
|
|
tp.getMasterTopoRef(allowWater, allowNogo) = masterTopo;
|
|
tovisit.insert(tp.Id);
|
|
|
|
while (!tovisit.empty())
|
|
{
|
|
CTopology& t = getTopologyNode(*(tovisit.begin()));
|
|
tovisit.erase(tovisit.begin());
|
|
|
|
uint j;
|
|
for (j=0; j<t.Neighbours.size(); ++j)
|
|
{
|
|
CTopology& neighb = getTopologyNode(t.Neighbours[j].getTopologyRef());
|
|
|
|
// can go to the neighbour and neighb not visited yet ?
|
|
if (!neighb.isCompatible(allowWater, allowNogo) ||
|
|
neighb.getMasterTopo(allowWater, allowNogo) != CTopology::TTopologyId::UNDEFINED_TOPOLOGY)
|
|
continue;
|
|
|
|
// set mastertopo id
|
|
neighb.getMasterTopoRef(allowWater, allowNogo) = masterTopo;
|
|
tovisit.insert(neighb.Id);
|
|
}
|
|
}
|
|
++masterTopo;
|
|
}
|
|
}
|
|
}
|
|
nlinfo("Built %d master topologies (%d topos tested)", masterTopo, totalTopos);
|
|
}
|
|
|
|
//
|
|
void CWorldMap::countSuperTopo()
|
|
{
|
|
nlinfo("Count super topologies");
|
|
|
|
CMapPosition min, max;
|
|
getBounds(min, max);
|
|
|
|
CMapPosition scan, scanline;
|
|
|
|
uint superTopo = 0;
|
|
set<CTopology::TTopologyId> visited;
|
|
|
|
uint totalTopos = 0;
|
|
|
|
for (scan = min; scan.y() != max.y(); scan = scan.stepCell(0, 1))
|
|
{
|
|
for (scanline = scan; scanline.x() != max.x(); scanline = scanline.stepCell(1, 0))
|
|
{
|
|
const CRootCell *cell = getRootCellCst(scanline);
|
|
if (cell == NULL)
|
|
continue;
|
|
|
|
const vector<CTopology> &topos = cell->getTopologiesNodes();
|
|
|
|
uint i;
|
|
for (i=0; i<topos.size(); ++i)
|
|
{
|
|
CTopology::TTopologyId topoid = topos[i].Id;
|
|
|
|
++totalTopos;
|
|
|
|
if (visited.find(topoid) == visited.end())
|
|
{
|
|
set<CTopology::TTopologyId> tovisit;
|
|
|
|
tovisit.insert(topoid);
|
|
visited.insert(topoid);
|
|
|
|
while (!tovisit.empty())
|
|
{
|
|
CTopology::TTopologyId id = *(tovisit.begin());
|
|
tovisit.erase(tovisit.begin());
|
|
|
|
const CTopology &t = getTopologyNode(id);
|
|
|
|
uint j;
|
|
for (j=0; j<t.Neighbours.size(); ++j)
|
|
{
|
|
CTopology::TTopologyId neighb = t.Neighbours[j].getTopologyRef();
|
|
if (visited.find(neighb) == visited.end())
|
|
{
|
|
visited.insert(neighb);
|
|
tovisit.insert(neighb);
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
++superTopo;
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
nlinfo("Found %d super topologies (%d topos tested, %d topos visited)", superTopo, totalTopos, visited.size());
|
|
}
|
|
|
|
bool CWorldMap::setWorldPosition(sint32 z, CWorldPosition &wpos, const CAIVector &pos, const CRootCell *originCell) const
|
|
{
|
|
CSlot slot;
|
|
const CMapPosition mapPos(pos);
|
|
|
|
const CRootCell *cell = originCell ? originCell : getRootCellCst(mapPos);
|
|
if (!cell)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
sint32 minDistZ = INT_MAX;
|
|
CSlot bestSlot;
|
|
sint32 bestZ = 0;
|
|
// Find best slot
|
|
for (uint32 s=0; s<3; ++s)
|
|
{
|
|
if (!cell->isSlotUsed(mapPos, CSlot(s)))
|
|
continue;
|
|
|
|
CSlot sslot=CSlot(s);
|
|
sint32 sh = cell->getMetricHeight(CWorldPosition(cell, mapPos, sslot));
|
|
sint32 dist = z-sh;
|
|
dist = dist<0?-dist:dist;
|
|
if (dist < minDistZ)
|
|
{
|
|
nlassert(dist>=0);
|
|
minDistZ = dist;
|
|
bestZ = sh;
|
|
bestSlot = sslot;
|
|
}
|
|
}
|
|
if (!bestSlot.isValid())
|
|
{
|
|
wpos = CWorldPosition(cell,mapPos,bestSlot,true);
|
|
return false;
|
|
}
|
|
wpos = CWorldPosition(cell,mapPos,bestSlot);
|
|
// if (simulateBug(5))
|
|
// {
|
|
// if (minDistZ > 2000)
|
|
// {
|
|
// return false;
|
|
// }
|
|
// }
|
|
// else
|
|
{
|
|
// :KLUDGE: Water hack
|
|
// If error is too big and player is either not in water or under slot (ie not floating above slot at surface)
|
|
if (minDistZ > 2000 && ((wpos.getFlags()&RYAI_MAP_CRUNCH::Water)==0 || z<bestZ))
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool CWorldMap::setWorldPosition( double z, CWorldPosition &wpos, const CAIVector &pos, const CRootCell *originCell) const
|
|
{
|
|
nlassert(false && "Double version of setWorldPosition isn't tested !!! Verify it's the same than above sint32 version !");
|
|
CSlot slot;
|
|
const CMapPosition mapPos(pos);
|
|
|
|
const CRootCell *cell = originCell ? originCell : getRootCellCst(mapPos);
|
|
if (!cell)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
double minDistZ = DBL_MAX;
|
|
CSlot bestSlot;
|
|
double bestZ;
|
|
// find best slot
|
|
for (uint32 s=0; s<3; ++s)
|
|
{
|
|
if (!cell->isSlotUsed(mapPos, CSlot(s)))
|
|
continue;
|
|
|
|
CSlot sslot=CSlot(s);
|
|
double sh = ((double)cell->getMetricHeight(CWorldPosition(cell, mapPos, sslot)))/1000.0;
|
|
double dist = fabs(z-sh);
|
|
if (dist < minDistZ)
|
|
{
|
|
nlassert(dist>=0);
|
|
minDistZ = dist;
|
|
bestZ = sh;
|
|
bestSlot = sslot;
|
|
}
|
|
}
|
|
|
|
if (!bestSlot.isValid())
|
|
{
|
|
wpos=CWorldPosition(cell,mapPos,bestSlot,true);
|
|
return false;
|
|
}
|
|
if (minDistZ > 2.000)
|
|
{
|
|
// nldebug("Setting a WorldPosition too far from specified z: x=%d y=%d z=%f slotz=%f", pos.x(), pos.y(), z, bestZ);
|
|
wpos = CWorldPosition(cell,mapPos,bestSlot,true);
|
|
return false;
|
|
}
|
|
wpos=CWorldPosition(cell,mapPos,bestSlot);
|
|
return true;
|
|
}
|
|
|
|
}
|
|
|