// Ryzom - MMORPG Framework // 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 . #ifndef NL_WORLD_MAP_H #define NL_WORLD_MAP_H #include "nel/misc/types_nl.h" #include "nel/misc/time_nl.h" #include "nel/misc/debug.h" #include "nel/misc/stream.h" #include "nel/pacs/u_global_position.h" #include "nel/misc/vectord.h" #include "16x16_layer.h" #include "ai_coord.h" #include "ai_vector.h" #include "ai_types.h" class CFollowPath; namespace RYPACSCRUNCH { class CPacsCruncher; } namespace RYAI_MAP_CRUNCH { enum TAStarFlag { Nothing = 0, Interior = 1, Water = 2, NoGo = 4, WaterAndNogo = 6, GroundFlags = WaterAndNogo }; const std::string& toString(TAStarFlag flag); TAStarFlag toAStarFlag(const std::string& str); uint const WorldMapGridSize = 256; double const WorldGridResolution = 1.; NLMISC::CVectorD const WorldStartOffset = NLMISC::CVectorD(0., 0., 0.); typedef sint TLevel; ////////////////////////////////////////////////////////////////////////////// /** * Slot for the 3 map layer (corresponding to 3 heights of deplacement) * \author Stephane le Dorze * \author Nevrax France * \date 2003 */ class CSlot { public: enum { INVALID_SLOT = 3 }; public: explicit CSlot(); explicit CSlot(uint slot); bool isValid() const { return _Slot!=INVALID_SLOT; } void setSlot(CSlot const& slot) { _Slot = slot._Slot; } bool operator ==(CSlot const& slot) const { return _Slot==slot._Slot; } bool operator !=(CSlot const& slot) const { return _Slot!=slot._Slot; } bool operator >(CSlot const& slot) const { return _Slot>slot._Slot; } bool operator <(CSlot const& slot) const { return _Slot=(CSlot const& slot) const { return _Slot>=slot._Slot; } bool operator <=(CSlot const& slot) const { return _Slot<=slot._Slot; } CSlot const& operator ++(); uint8 slot() const { return _Slot; } private: uint8 _Slot; }; inline CSlot::CSlot() : _Slot(INVALID_SLOT) { } inline CSlot::CSlot(uint slot) : _Slot(slot) { #ifdef NL_DEBUG nlassert(slot>=0 && slot<=3); #endif } inline CSlot const& CSlot::operator ++() { ++_Slot; #ifdef NL_DEBUG nlassert(_Slot<=INVALID_SLOT); #endif return *this; } ////////////////////////////////////////////////////////////////////////////// /** * Compressed link set for the 4 cardinal directions * \author Benjamin Legros * \author Nevrax France * \date 2003 */ class CCellLinkage // pass CCellLinkage parameters by references. (anyway) and remove validation checks. (big update to come). { public: enum { SouthSlotOffset = 0, SouthSlotMask = 0x03, EastSlotOffset = 2, EastSlotMask = 0x0c, NorthSlotOffset = 4, NorthSlotMask = 0x30, WestSlotOffset = 6, WestSlotMask = 0xc0, }; public: CCellLinkage(uint8 links = 0xff); CSlot SSlot() const { return CSlot((_Links & SouthSlotMask) >> SouthSlotOffset); } CSlot ESlot() const { return CSlot((_Links & EastSlotMask) >> EastSlotOffset); } CSlot NSlot() const { return CSlot((_Links & NorthSlotMask) >> NorthSlotOffset); } CSlot WSlot() const { return CSlot((_Links & WestSlotMask) >> WestSlotOffset); } bool isSSlotValid() const { return (_Links&SouthSlotMask)!=SouthSlotMask; } bool isESlotValid() const { return (_Links&EastSlotMask)!=EastSlotMask; } bool isNSlotValid() const { return (_Links&NorthSlotMask)!=NorthSlotMask; } bool isWSlotValid() const { return (_Links&WestSlotMask)!=WestSlotMask; } void setSSlot(CSlot const& slot); void setESlot(CSlot const& slot); void setNSlot(CSlot const& slot); void setWSlot(CSlot const& slot); CCellLinkage& operator |=(uint8 links); bool used() const { return _Links!=0xff; } uint8 getLinks() const { return _Links; } void serial(NLMISC::IStream &f); private: uint8 _Links; }; inline CCellLinkage::CCellLinkage(uint8 links) : _Links(links) { } inline void CCellLinkage::setSSlot(CSlot const& slot) { _Links = (_Links & (~SouthSlotMask)) + (slot.slot() << SouthSlotOffset); } inline void CCellLinkage::setESlot(CSlot const& slot) { _Links = (_Links & (~EastSlotMask)) + (slot.slot() << EastSlotOffset); } inline void CCellLinkage::setNSlot(CSlot const& slot) { _Links = (_Links & (~NorthSlotMask)) + (slot.slot() << NorthSlotOffset); } inline void CCellLinkage::setWSlot(CSlot const& slot) { _Links = (_Links & (~WestSlotMask)) + (slot.slot() << WestSlotOffset); } inline CCellLinkage& CCellLinkage::operator |=(uint8 links) { _Links |= links; return *this; } inline void CCellLinkage::serial(NLMISC::IStream &f) { f.serial(_Links); } ////////////////////////////////////////////////////////////////////////////// class CDirection { friend class CWorldMap; public: enum TDeltaDirection { HALF_TURN_LEFT = 1, // 45 degres HALF_TURN_RIGHT = -1, // -45 degres TURN_LEFT = 2, // 90 degres TURN_RIGHT = -2, // -90 degres HALF_TURN = 4 // 180 degres }; enum TDirection { E = 0, NE = 1, N = 2, NW = 3, W = 4, SW = 5, S = 6, SE = 7, UNDEFINED = 8 }; enum TMotifDirection { SHIFT_E = 1, SHIFT_NE = 2, SHIFT_N = 4, SHIFT_NW = 8, SHIFT_W = 16, SHIFT_SW = 32, SHIFT_S = 64, SHIFT_SE = 128, SHIFT_UNDEFINED = 256 // be aware, if you use a byte, it makes 0. }; enum TCost { NO_COST = 0, ORTHO_COST = 2, DIAG_COST = 3, MAX_COST = 3 }; public: explicit CDirection(); CDirection(CDirection const& dir); explicit CDirection(TDirection dir); /// @name user interface assuming the y inversion (sorry). //@{ explicit CDirection(int deltax, int deltay, bool toCalculate); // initialisation with a direction depending on deltas (-1,0 or 1) explicit CDirection(int deltax, int deltay); // initialisation with a direction depending on deltas (-1,0 or 1) explicit CDirection(CAngle const& angle); CAngle getAngle(); sint dx() const; sint dy() const; void dxdy(int& dx, int& dy); //@} bool isValid() { return _value!=UNDEFINED; } // add a number of 45 turn void addStep(TDeltaDirection step); NLMISC::CVector2f getDirFloat() const; TDirection getVal() const; TMotifDirection getShift() const; bool operator !=(TDirection dir) const; bool operator != (CDirection const& dir) const; struct CDirectionData { sint dx; sint dy; TCost weight; }; static CDirectionData const directionDatas[]; static TDirection const table[]; // used in pacs (nothing to do here but i haven't enought time to rewrite much code) uint32 getWeight(); static void getDirectionAround(CAngle const& Angle, CDirection& Dir0, CDirection& Dir1); private: void setDxDy(int deltax, int deltay); TDirection _value; }; inline CDirection::CDirection() : _value(UNDEFINED) { } inline CDirection::CDirection(CDirection const& dir) : _value(dir._value) { } inline CDirection::CDirection(TDirection dir) : _value(dir) { #ifdef NL_DEBUG nlassert(dir>=E && dir<=UNDEFINED); #endif } inline CDirection::CDirection(int deltax, int deltay, bool toCalculate) { #ifdef NL_DEBUG nlassert(deltax!=0 || deltay!=0); #endif uint absDeltax = abs(deltax); uint absDeltay = abs(deltay); if (deltax!=0 && deltay!=0) { float ratio = (float)((float)absDeltax/(float)absDeltay); if (ratio>=0.4141 && ratio<=2.4145) // cos(PI/8)/sin(PI/8) ou sin(PI/8)/cos(PI/8) { setDxDy(deltax>0?1:-1, deltay>0?1:-1); return; } } if (absDeltax>absDeltay) setDxDy(deltax>0?1:-1, 0); else setDxDy(0, deltay>0?1:-1); } inline CDirection::CDirection(int deltax, int deltay) { setDxDy(deltax, deltay); } inline CDirection::CDirection(CAngle const& angle) { _value = (TDirection)((((sint32)angle.asRawSint16()+65536+4096)/8192)&7); // add 4096 to have a correct angle magnet. } inline CAngle CDirection::getAngle() { #ifdef NL_DEBUG nlassert(isValid()); #endif return CAngle(_value*8192); } inline sint CDirection::dx() const { return directionDatas[_value].dx; } inline sint CDirection::dy() const { return directionDatas[_value].dy; } inline void CDirection::dxdy(int& dx, int& dy) { CDirectionData const& direction = directionDatas[_value]; dx = direction.dx; dy = direction.dy; } inline void CDirection::addStep(TDeltaDirection step) { #ifdef NL_DEBUG nlassert(isValid()); #endif _value = (TDirection)((_value+step)&7); } inline NLMISC::CVector2f CDirection::getDirFloat() const { return NLMISC::CVector2f((float)dx(), (float)dy()); } inline CDirection::TDirection CDirection::getVal() const { return _value; } inline CDirection::TMotifDirection CDirection::getShift() const { return (TMotifDirection)(1<<_value); } inline bool CDirection::operator !=(TDirection dir) const { return _value!=dir; } inline bool CDirection::operator != (CDirection const& dir) const { return _value!=dir._value; } inline uint32 CDirection::getWeight() { return directionDatas[_value].weight; } inline void CDirection::getDirectionAround(CAngle const& Angle, CDirection& Dir0, CDirection& Dir1) { sint value = Angle.asRawSint16()&(~16383); //8191); Dir0 = CDirection(CAngle(value)); value += 16384; //8192; Dir1 = CDirection(CAngle(value)); } inline void CDirection::setDxDy(int deltax, int deltay) { #ifdef NL_DEBUG nlassert( deltax>=-1 && deltax<=1 && deltay>=-1 && deltay<=1); #endif _value = table[1+deltax+(1+deltay)*3]; #ifdef NL_DEBUG nlassert(dx()==deltax && dy()==deltay); #endif } ////////////////////////////////////////////////////////////////////////////// /** * A map of accessible positions near a given position * \author Benjamin Legros * \author Nevrax France * \date 2003 */ class CNeighbourhood { public: /// One bit per direction bool isValid(CDirection::TDirection dir) const; bool isValid(CDirection const& dir) const; void set(CDirection::TDirection dir); CNeighbourhood(); private: CNeighbourhood(uint32 neighb); /// 8 bits for 8 directions uint32 Neighb; }; inline bool CNeighbourhood::isValid(CDirection::TDirection dir) const { return (Neighb&CDirection(dir).getShift())!=0; } inline bool CNeighbourhood::isValid(CDirection const& dir) const { return (Neighb&dir.getShift())!=0; } inline void CNeighbourhood::set(CDirection::TDirection dir) { Neighb|=CDirection(dir).getShift(); } inline CNeighbourhood::CNeighbourhood() : Neighb(0) { } inline CNeighbourhood::CNeighbourhood(uint32 neighb) : Neighb(neighb) { } ////////////////////////////////////////////////////////////////////////////// /** * A slot, defining pacs position and linkage to other slots * \author Benjamin Legros * \author Nevrax France * \date 2003 */ class CUnitSlot { public: // 12 bits 0-11 : instance id // 12 bits 12-23 : surface id // 2 bits 24-25 : north link // 2 bits 26-27 : east link // 2 bits 28-29 : south link // 2 bits 30-31 : west link enum { InstanceIdOffset = 0, InstanceIdMask = 0x00000fff, InstanceIdMax = 4096, SurfaceIdOffset = 12, SurfaceIdMask = 0x00fff000, SurfaceIdMax = 4096, SlotOffset = 24, SlotMask = 0xff000000 }; // 6 bits 0-5 topology // 2 bits 6-7 gabarit enum { TopologyMask = 0x3f, GabaritMask = 0xc0, GabaritShift = 6, }; // 1 bit 0 interior // 1 bit 1 water // 1 bit 2 nogo // 13 bits 3-15 height enum { InteriorMask = 0x0001, WaterMask = 0x0002, NoGoMask = 0x0004, HeightMask = 0xfff8, HeightShift = 3, }; public: /// @name Constructors //@{ explicit CUnitSlot(); explicit CUnitSlot(uint instanceId, uint surfaceId); explicit CUnitSlot(NLPACS::UGlobalPosition const& pos); //@} // Selectors uint instanceId() const { return (_Id&InstanceIdMask) >> InstanceIdOffset; } uint surfaceId () const { return (_Id&SurfaceIdMask) >> SurfaceIdOffset; } uint height () const { return _Height >> HeightShift; } CCellLinkage& cellLink() { return *((CCellLinkage*)(((char*)&_Id)+3)); } CCellLinkage getCellLink() const { return *((CCellLinkage*)(((char*)&_Id)+3)); } uint topology() const { return (_Topology & TopologyMask); } uint gabarit() const { return (_Topology & GabaritMask) >> GabaritShift; } bool interior() const { return (_Height & InteriorMask) != 0; } bool water() const { return (_Height & WaterMask) != 0; } bool nogo() const { return (_Height & NoGoMask) != 0; } /// @name Mutators //@{ void setInstanceId(uint instanceId); void setSurface(uint surfaceId); void setTopology(uint topology); void setGabarit(uint gabarit); void setHeight(sint height); void setInterior(bool interior); void setWater(bool water); void setNoGo(bool nogo); void reset(); //@} bool used() const; bool hasSameSurface(const CUnitSlot &slot) const; void serial(NLMISC::IStream &f); private: // _Id is divided this way // bits 0-11 : instance id // bits 12-23 : surface id // bits 24-25 : north link // bits 26-27 : east link // bits 28-29 : south link // bits 30-31 : west link uint32 _Id; // 6 bits 0-5 topology // 2 bits 6-7 gabarit uint8 _Topology; // 1 bit 0 interior // 1 bit 1 water // 1 bit 2 nogo // 13 bits 3-15 height sint16 _Height; }; // a cell is composed of units of 3 slots typedef CUnitSlot TCellUnit[3]; inline CUnitSlot::CUnitSlot() : _Id(0xffffffff) , _Topology(0) , _Height(0) { } inline CUnitSlot::CUnitSlot(uint instanceId, uint surfaceId) : _Topology(0) , _Height(0) { #ifdef NL_DEBUG nlassert(instanceId= -4096 && height <= 4095); #endif _Height = (_Height & (~HeightMask)) | (sint16)(height << HeightShift); } inline void CUnitSlot::setInterior(bool interior) { if (interior) _Height |= InteriorMask; else _Height &= ~InteriorMask; } inline void CUnitSlot::setWater(bool water) { if (water) _Height |= WaterMask; else _Height &= ~WaterMask; } inline void CUnitSlot::setNoGo(bool nogo) { if (nogo) _Height |= NoGoMask; else _Height &= ~NoGoMask; } inline void CUnitSlot::reset() { _Id = 0xffffffff; _Topology = 0; _Height = 0; } inline bool CUnitSlot::used() const { return _Id != 0xffffffff; } inline bool CUnitSlot::hasSameSurface(const CUnitSlot &slot) const { return ((slot._Id ^ _Id) & (~SlotMask)) == 0; } inline void CUnitSlot::serial(NLMISC::IStream &f) { f.serial(_Id); f.serial(_Topology); f.serial(_Height); } ////////////////////////////////////////////////////////////////////////////// class CMapPosition; typedef sint32 TMapCoordType; /** * A simple 1 axis coordinate * \author Benjamin Legros * \author Nevrax France * \date 2003 */ class CMapCoord { friend class CMapPosition; public: explicit CMapCoord(uint scellId, uint cellId, uint unitId); private: explicit CMapCoord() : c(0) { } explicit CMapCoord(CAICoord const& aiCoord) : c((sint)(floor(aiCoord.asDouble()+0.5))) { } explicit CMapCoord(sint cc) : c(cc) { } uint cellIdMask(uint val) const; uint fullCellIdMask(uint val) const; uint toUnitId(uint val) const; uint toCellId(uint val) const; uint toSuperCellId(uint val) const; uint unitIdToUInt(uint val) const; uint cellIdToUInt(uint val) const; uint superCellIdToUInt(uint val) const; uint fullCellIdToUInt(uint val) const; TMapCoordType& cRef(); public: uint getUnitId () const { return unitIdToUInt(c); } uint getCellId () const { return cellIdToUInt(c); } uint getSuperCellId () const { return superCellIdToUInt(c); } uint getFullCellId () const { return fullCellIdToUInt(c); } void setUnitId(uint id) { c = fullCellIdMask(c) | toUnitId(id); } void setCellId(uint id) { c = cellIdMask(c) | toCellId(id); } bool operator ==(CMapCoord const& cc) const { return c == cc.c; } bool operator !=(CMapCoord const& cc) const { return c != cc.c; } CMapCoord operator -(CMapCoord const& cc) const { return CMapCoord(c-cc.c); } TMapCoordType const& get() const { return c; } private: // coordinate TMapCoordType c; }; inline CMapCoord::CMapCoord(uint scellId, uint cellId, uint unitId) : c(toSuperCellId(scellId) + toCellId(cellId) + toUnitId(unitId)) { #ifdef NL_DEBUG nlassert(scellId < 256); #endif } inline uint CMapCoord::cellIdMask(uint val) const { return val&0xff0f; } inline uint CMapCoord::fullCellIdMask(uint val) const { return val&0x0fffffff0; } inline uint CMapCoord::toUnitId(uint val) const { return val&0x0f; } inline uint CMapCoord::toCellId(uint val) const { return (val<<4)&0x0f0; } inline uint CMapCoord::toSuperCellId(uint val) const { return (val<<8)&0x0ffff; } inline uint CMapCoord::unitIdToUInt(uint val) const { return val&0x0f; } inline uint CMapCoord::cellIdToUInt(uint val) const { return (val>>4)&0xf; } inline uint CMapCoord::superCellIdToUInt(uint val) const { return (val>>8)&0x0ff; } inline uint CMapCoord::fullCellIdToUInt(uint val) const { return (val>>4)&0x0fff; } inline TMapCoordType& CMapCoord::cRef() { return c; } ////////////////////////////////////////////////////////////////////////////// /** * A map position, consisting of 2 axis coordinates * \author Benjamin Legros * \author Nevrax France * \date 2003 */ class CMapPosition { friend class RYPACSCRUNCH::CPacsCruncher; public: CMapPosition() { } CMapPosition(sint x, sint y); CMapPosition(CMapCoord const& x, CMapCoord const& y); CMapPosition(CMapPosition const& pos); CMapPosition(NLMISC::CVectorD const& pos); CMapPosition(CAIVector const& pos); CMapPosition(CAICoord const& x, CAICoord const& y); NLMISC::CVectorD toVectorD() const; NLMISC::CVector2d toVector2d() const; CAIVector toAIVector() const; std::string toString() const; void setUnitId(uint const xId, uint const yId); void setNullUnitId(); CMapPosition stepCell(sint dx, sint dy) const; bool hasSameFullCellId(CMapPosition const& other) const; CMapPosition operator -(CMapPosition const& other) const; bool operator ==(CMapPosition const& cc) const; bool operator !=(CMapPosition const& cc) const; CMapCoord const& xCoord() const { return _x; } CMapCoord const& yCoord() const { return _y; } TMapCoordType const& x() const { return _x.get(); } TMapCoordType const& y() const { return _y.get(); } uint32 superCellFastIndex() const; uint32 rootCellFastIndex() const; uint32 cellUnitFastIndex() const; CMapPosition step(sint dx, sint dy) const; CMapPosition getStepS() const { return step(0, -1); } CMapPosition getStepN() const { return step(0, +1); } CMapPosition getStepE() const { return step(+1, 0); } CMapPosition getStepW() const { return step(-1, 0); } protected: // return if we have changed of RootCell .. bool stepS() { _y.cRef()-=1; return ((_y.cRef()&0xf)==0xf); } bool stepN() { _y.cRef()+=1; return ((_y.cRef()&0xf)==0x0); } bool stepW() { _x.cRef()-=1; return ((_x.cRef()&0xf)==0xf); } bool stepE() { _x.cRef()+=1; return ((_x.cRef()&0xf)==0x0); } private: CMapCoord _x; CMapCoord _y; }; inline CMapPosition::CMapPosition(sint x, sint y) : _x(CMapCoord(x)) , _y(CMapCoord(y)) { } inline CMapPosition::CMapPosition(CMapCoord const& x, CMapCoord const& y) : _x(x) , _y(y) { } inline CMapPosition::CMapPosition(CMapPosition const& pos) : _x(pos._x) , _y(pos._y) { } inline CMapPosition::CMapPosition(NLMISC::CVectorD const& pos) : _x((uint)floor((pos.x - WorldStartOffset.x)/WorldGridResolution + 0.5)) , _y((uint)floor((pos.y - WorldStartOffset.y)/WorldGridResolution + 0.5)) { } inline CMapPosition::CMapPosition(CAIVector const& pos) : _x(pos.x()) , _y(pos.y()) { } inline CMapPosition::CMapPosition(CAICoord const& x, CAICoord const& y) : _x(x) , _y(y) { } inline NLMISC::CVectorD CMapPosition::toVectorD() const { return NLMISC::CVectorD(WorldStartOffset.x + _x.get()*WorldGridResolution, WorldStartOffset.y + _y.get()*WorldGridResolution, 0.0); } inline NLMISC::CVector2d CMapPosition::toVector2d() const { return NLMISC::CVector2d(WorldStartOffset.x + _x.get()*WorldGridResolution, WorldStartOffset.y + _y.get()*WorldGridResolution); } inline CAIVector CMapPosition::toAIVector() const { return CAIVector(WorldStartOffset.x + _x.get()*WorldGridResolution, WorldStartOffset.y + _y.get()*WorldGridResolution); } inline std::string CMapPosition::toString() const { return toAIVector().toString(); } inline void CMapPosition::setUnitId(uint const xId, uint const yId) { _x.setUnitId(xId); _y.setUnitId(yId); } inline void CMapPosition::setNullUnitId() { _x.setUnitId(0); _y.setUnitId(0); } inline CMapPosition CMapPosition::stepCell(sint dx, sint dy) const { CMapPosition newMapPos(xCoord().get()+(dx<<4),yCoord().get()+(dy<<4)); newMapPos.setNullUnitId(); return newMapPos; } inline bool CMapPosition::hasSameFullCellId(CMapPosition const& other) const { return ( _x.getFullCellId()==other._x.getFullCellId() && _y.getFullCellId()==other._y.getFullCellId() ); } inline CMapPosition CMapPosition::operator -(CMapPosition const& other) const { return CMapPosition(_x-other._x,_y-other._y); } inline bool CMapPosition::operator ==(CMapPosition const& cc) const { return _x==cc._x && _y==cc._y; } inline bool CMapPosition::operator !=(CMapPosition const& cc) const { return _x!=cc._x || _y!=cc._y; } inline uint32 CMapPosition::superCellFastIndex() const { return ((((uint)y())&(0x0ff<<8)) + xCoord().getSuperCellId()); } inline uint32 CMapPosition::rootCellFastIndex() const { return ((((uint)y())&0x0f<<4) + xCoord().getCellId()); } inline uint32 CMapPosition::cellUnitFastIndex() const { return ((yCoord().getUnitId()<<4) + xCoord().getUnitId()); } inline CMapPosition CMapPosition::step(sint dx, sint dy) const { return CMapPosition(_x.get() + dx, _y.get() + dy); } ////////////////////////////////////////////////////////////////////////////// /** * Motion map interface * \author Benjamin Legros * \author Nevrax France * \date 2003 */ class CGridDirectionLayer : public I16x16Layer { public: CDirection getDirection(int y, int x) const; CDirection getDirection(CMapPosition const& pos) const; void setDirection(int y, int x, CDirection dir); }; inline CDirection CGridDirectionLayer::getDirection(int y, int x) const { return CDirection((CDirection::TDirection)get(y, x)); } inline CDirection CGridDirectionLayer::getDirection(CMapPosition const& pos) const { return CDirection((CDirection::TDirection)get(pos.yCoord().getUnitId(), pos.xCoord().getUnitId())); } inline void CGridDirectionLayer::setDirection(int y, int x, CDirection dir) { this->set((uint)y, (uint)x, (sint)dir.getVal()); } ////////////////////////////////////////////////////////////////////////////// class CDirectionLayer { public: CDirectionLayer(); void serial(NLMISC::IStream& f); CGridDirectionLayer* getGridLayer(int y, int x); void setGridLayer(int y, int x, I16x16Layer* layer); void dump(); private: // Grid[1][1] is central cell I16x16Layer *Grid[3][3]; }; inline CDirectionLayer::CDirectionLayer() { uint i, j; for (i=0; i<3; ++i) for (j=0; j<3; ++j) Grid[i][j] = NULL; } inline CGridDirectionLayer* CDirectionLayer::getGridLayer(int y, int x) { return static_cast(Grid[y][x]); } inline void CDirectionLayer::setGridLayer(int y, int x, I16x16Layer* layer) { Grid[y][x] = layer; } inline void CDirectionLayer::dump() { char output[16*3][16*3]; for (sint i=2; i>=0; --i) for (sint j=0; j<3; ++j) for (sint y=15; y>=0; --y) for (sint x=0; x<16; ++x) { CGridDirectionLayer* gridDirectionLayer = getGridLayer(i,j); CDirection motion; if (gridDirectionLayer) motion = gridDirectionLayer->getDirection(y,x); char c = ' '; switch (motion.getVal()) { case CDirection::N: c = '^'; break; case CDirection::S: c = 'v'; break; case CDirection::E: c = '>'; break; case CDirection::W: c = '<'; break; case CDirection::NE: c = '7'; break; case CDirection::SW: c = 'L'; break; case CDirection::NW: c = 'r'; break; case CDirection::SE: c = '\\'; break; // case 255: // c = ' '; // break; default: c = 'o'; break; } output[y+i*16][x+j*16] = c; } char op[256]; nlinfo(" \t0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF"); for (sint i=47; i>=0; --i) { sint j; for (j=0; j<48; ++j) op[j] = output[i][j]; op[j] = '\0'; nlinfo("%04X\t%s", (i&15), op); } } ////////////////////////////////////////////////////////////////////////////// class CDirectionMap { public: CDirectionLayer* Layers[3]; CDirectionMap(); void serial(NLMISC::IStream& f); void dump(); }; inline CDirectionMap::CDirectionMap() { Layers[0] = NULL; Layers[1] = NULL; Layers[2] = NULL; } inline void CDirectionMap::dump() { for (uint i=0; i<3; ++i) { nlinfo("Layer %d", i); if (Layers[i] != NULL) Layers[i]->dump(); else nlinfo("Empty"); } } ////////////////////////////////////////////////////////////////////////////// class CWorldMap; class CRootCell; class CWorldPosition; class CTopology { public: /// A topology Id, representing cell and topology class TTopologyId { public: enum { UNDEFINED_TOPOLOGY = 0xffffffff }; public: explicit TTopologyId(); explicit TTopologyId(CMapPosition const& pos, uint topology); // For test only explicit TTopologyId(uint32 id); bool haveSameCellPos(TTopologyId const& other) const; CMapPosition getMapPosition() const; uint getTopologyIndex() const; bool isValid() const; bool operator <(TTopologyId const& other) const; bool operator ==(TTopologyId const& other) const; bool operator !=(TTopologyId const& other) const; void serial(NLMISC::IStream& f); uint32 getVal() const; private: uint32 _value; }; class TTopologyRef : public TTopologyId { friend class CWorldPosition; public: TTopologyRef(CWorldPosition const& pos); TTopologyRef(); explicit TTopologyRef(TTopologyId const& id, CRootCell* rootCell); CTopology const& getCstTopologyNode() const; void setRootCell(CRootCell* rootCell); void serial(NLMISC::IStream& f); CRootCell const* getRootCell() const; private: TTopologyRef(CWorldPosition const& pos, CRootCell const* rootCell); CRootCell const* _RootCell; }; /// A link to a neighbour, including mean distance to it class CNeighbourLink { public: explicit CNeighbourLink(); explicit CNeighbourLink(TTopologyRef const& ref, float distance); CNeighbourLink(CNeighbourLink const& other); void serial(NLMISC::IStream& f); TTopologyRef const& getTopologyRef() const; void updateTopologyRef(CWorldMap* worldMapPtr); float getDistance() const; private: TTopologyRef _Ref; float _Distance; }; void updateTopologyRef(CWorldMap* worldMapPtr); bool isInInterior() const { return (Flags & Interior) != 0; } bool isInWater() const { return (Flags & Water) != 0; } bool isInNogo() const { return (Flags & NoGo) != 0; } TAStarFlag getFlags() const { return (TAStarFlag)Flags; } uint32 getMasterTopo(TAStarFlag const& flags) const; uint32 getMasterTopo(bool allowWater, bool allowNoGo) const; uint32& getMasterTopoRef(bool allowWater, bool allowNoGo); bool isCompatible(bool allowWater, bool allowNoGo) const; /// The Id of this topology TTopologyId Id; /// The direction map to access this topology CDirectionMap* DirectionMap; /// The neighbour topologies that have access to this topology std::vector Neighbours; /// Flags of the topology uint16 Flags; // why doing this (uint16), its not align so no gain in memory and it causes access cost ! /// Master topologies uint32 MasterTopL; // only landscape uint32 MasterTopLW; // landscape and water uint32 MasterTopLN; // landscape and nogo uint32 MasterTopLNW; // landscape and water and nogo /// Topology center, for A* purpose NLMISC::CVector Position; CTopology(); void serial(NLMISC::IStream& f); }; inline CTopology::TTopologyId::TTopologyId() : _value(UNDEFINED_TOPOLOGY) { } inline CTopology::TTopologyId::TTopologyId(CMapPosition const& pos, uint topology) { #ifdef NL_DEBUG nlassert(topology < 256); #endif _value = (pos.yCoord().getFullCellId() << 20) + (pos.xCoord().getFullCellId() << 8) + topology; } inline CTopology::TTopologyId::TTopologyId(uint32 id) { _value = id; } inline bool CTopology::TTopologyId::haveSameCellPos(TTopologyId const& other) const { return ((other._value^_value)&0x0ffffff00)==0; } inline CMapPosition CTopology::TTopologyId::getMapPosition() const { return CMapPosition((_value&0x000fff00) >> (8-4),(_value&0xfff00000) >> (20-4)); } inline uint CTopology::TTopologyId::getTopologyIndex() const { return _value&0x000000ff; } inline bool CTopology::TTopologyId::isValid() const { return _value!=UNDEFINED_TOPOLOGY; } inline bool CTopology::TTopologyId::operator <(TTopologyId const& other) const { return _value::iterator it=Neighbours.begin(), itEnd=Neighbours.end(); while (it!=itEnd) { (*it).updateTopologyRef(worldMapPtr); ++it; } } inline uint32 CTopology::getMasterTopo(TAStarFlag const& flags) const { switch (flags&WaterAndNogo) { case Nothing: default: return MasterTopL; case Water: return MasterTopLW; case NoGo: return MasterTopLN; case WaterAndNogo: return MasterTopLNW; } } inline uint32 CTopology::getMasterTopo(bool allowWater, bool allowNoGo) const { if (allowWater) { if (allowNoGo) return MasterTopLNW; return MasterTopLW; } if (allowNoGo) return MasterTopLN; return MasterTopL; } inline uint32& CTopology::getMasterTopoRef(bool allowWater, bool allowNoGo) { if (allowWater) { if (allowNoGo) return MasterTopLNW; return MasterTopLW; } if (allowNoGo) return MasterTopLN; return MasterTopL; } inline bool CTopology::isCompatible(bool allowWater, bool allowNoGo) const { return (allowWater || !isInWater()) && (allowNoGo || !isInNogo()); } inline CTopology::CTopology() : Id() , DirectionMap(NULL) , Flags(Nothing) , MasterTopL(TTopologyId::UNDEFINED_TOPOLOGY) , MasterTopLW(TTopologyId::UNDEFINED_TOPOLOGY) , MasterTopLN(TTopologyId::UNDEFINED_TOPOLOGY) , MasterTopLNW(TTopologyId::UNDEFINED_TOPOLOGY) { } inline void CTopology::serial(NLMISC::IStream& f) { uint version = 0; uint16 check = (uint16)'Tp'; f.serial(check); if (check != (uint16)'TP') { nlassert(check == (uint16)'Tp'); version = f.serialVersion(3); } f.serial(Id); if (f.isReading()) { delete DirectionMap; DirectionMap = NULL; } bool present = (DirectionMap != NULL); f.serial(present); if (present) { if (f.isReading()) DirectionMap = new CDirectionMap(); f.serial(*DirectionMap); } f.serialCont(Neighbours); f.serial(Position); if (version >= 3) { f.serial(Flags); f.serial(MasterTopL); f.serial(MasterTopLW); f.serial(MasterTopLN); f.serial(MasterTopLNW); } else { nlassert(f.isReading()); Flags = Nothing; if (version >= 1) { bool interior; f.serial(interior); if (interior) Flags |= Interior; } if (version >= 2) { bool water; f.serial(water); if (water) Flags |= Water; } MasterTopL = 0; MasterTopLW = 0; MasterTopLN = 0; MasterTopLNW = 0; } } ////////////////////////////////////////////////////////////////////////////// /** * CWorldPosition, position referenced by a map position and a slot * \author Benjamin Legros * \author Nevrax France * \date 2003 */ class CWorldPosition : public CMapPosition , public CSlot { friend class CWorldMap; friend class RYPACSCRUNCH::CPacsCruncher; public: explicit CWorldPosition(); explicit CWorldPosition(sint x, sint y); // resumes that the RootCell is valid; CRootCell const* getRootCell() const { return _RootCell; } /// In millimeters. sint32 getMetricHeight() const; sint32 h() const { return getMetricHeight(); } CCellLinkage const& getCellLinkage() const { return _cellLinkage; } CTopology::TTopologyRef getTopologyRef() const; bool isInInterior() const; CTopology const& getTopologyNode() const; TAStarFlag getFlags() const; /// Get a vector position from a world position NLMISC::CVectorD getPosition() const; bool operator ==(CWorldPosition const& cc) const { return CSlot::operator ==(cc) && CMapPosition::operator ==(cc); } bool operator !=(CWorldPosition const& cc) const { return CSlot::operator !=(cc) || CMapPosition::operator !=(cc); } private: CWorldPosition getPosS() const; CWorldPosition getPosN() const; CWorldPosition getPosE() const; CWorldPosition getPosW() const; void setPosS(CWorldPosition& pos) const; void setPosN(CWorldPosition& pos) const; void setPosE(CWorldPosition& pos) const; void setPosW(CWorldPosition& pos) const; bool moveS(); bool moveN(); bool moveE(); bool moveW(); void stepS(); void stepN(); void stepE(); void stepW(); CRootCell const* _RootCell; CCellLinkage _cellLinkage; explicit CWorldPosition(CRootCell const* cell, CMapPosition const& pos, CSlot const& slot); explicit CWorldPosition(CRootCell const* cell, CMapPosition const& pos, CSlot const& slot, bool generationOnly); }; inline CWorldPosition::CWorldPosition() : CMapPosition() , CSlot() , _RootCell(NULL) , _cellLinkage(0) { } inline CWorldPosition::CWorldPosition(sint x, sint y) : CMapPosition(x, y) , CSlot() , _RootCell(NULL) , _cellLinkage(0) { } inline CTopology::TTopologyRef CWorldPosition::getTopologyRef() const { return CTopology::TTopologyRef(*this, getRootCell()); } inline bool CWorldPosition::isInInterior() const { return getTopologyRef().getCstTopologyNode().isInInterior(); } inline CTopology const& CWorldPosition::getTopologyNode() const { return getTopologyRef().getCstTopologyNode(); } inline TAStarFlag CWorldPosition::getFlags() const { return getTopologyRef().getCstTopologyNode().getFlags(); //Flags; } ////////////////////////////////////////////////////////////////////////////// class CCompatibleResult { public: CCompatibleResult(TAStarFlag movementFlags = Interior, uint32 choosenMasterTopo = ~0); void set(TAStarFlag movementFlags, uint32 choosenMasterTopo); void setValid(bool valid = true); TAStarFlag const& movementFlags() const; uint32 const& choosenMasterTopo() const; bool const& isValid() const; private: TAStarFlag _MovementFlags; uint32 _ChoosenMasterTopo; bool _Valid; }; inline CCompatibleResult::CCompatibleResult(TAStarFlag movementFlags, uint32 choosenMasterTopo) : _MovementFlags(movementFlags) , _ChoosenMasterTopo(choosenMasterTopo) , _Valid(false) { } inline void CCompatibleResult::set(TAStarFlag movementFlags, uint32 choosenMasterTopo) { _MovementFlags = movementFlags; _ChoosenMasterTopo = choosenMasterTopo; } inline void CCompatibleResult::setValid(bool valid) { _Valid = valid; } inline TAStarFlag const& CCompatibleResult::movementFlags() const { return _MovementFlags; } inline uint32 const& CCompatibleResult::choosenMasterTopo() const { return _ChoosenMasterTopo; } inline bool const& CCompatibleResult::isValid() const { return _Valid; } ////////////////////////////////////////////////////////////////////////////// /** @relates CWorldPosition */ void areCompatiblesWithoutStartRestriction(CWorldPosition const& startPos, CWorldPosition const& endPos, TAStarFlag const& denyflags, CCompatibleResult& res, bool allowStartRestriction = false); ////////////////////////////////////////////////////////////////////////////// /** * CRootCell, interface for all 16x16 cells in world map * derivated into CComputeCell (for map computation only), into CSingleLayerCell and CMultiLayerCell * \author Benjamin Legros * \author Nevrax France * \date 2003 */ class CRootCell { public: enum TCellType { Root = 0, Compute, White, SingleLayer, MultiLayer }; public: CRootCell(CWorldMap const& worldMapPtr); virtual ~CRootCell() { } void setFlag(uint32 const flag) { _flag = flag; } uint32 getFlag() const { return _flag; } sint32 getMetricHeight(CWorldPosition const& wpos) const; CWorldMap const& getWorldMapPtr() const { return _WorldMapPtr; } // mutator (only for build purpose) CTopology& getTopologyNode(uint topology); CTopology const& getCstTopologyNode(uint topology) const; CTopology::TTopologyId getTopologyId(CWorldPosition const& wpos) const; CTopology::TTopologyRef getTopologyRef(const CWorldPosition&pos) const; CTopology const& getCstTopologyNode(CTopology::TTopologyId const& id) const; std::vector& getTopologiesNodes() { return _TopologiesNodes; } std::vector const& getTopologiesNodes() const { return _TopologiesNodes; } void setTopologiesNodes(std::vector const& nodes) { _TopologiesNodes = nodes; } CDirectionMap const* getDirectionMap(uint topology) const; void setDirectionMap(CDirectionMap* map, uint topology); void updateTopologyRef(CWorldMap* worldMap); CWorldPosition const& getWorldPosition(uint ind) const; void setWorldPosition(CWorldPosition const& pos, uint ind); virtual CCellLinkage getCellLink(CWorldPosition const& wpos) const = 0; virtual uint32 nbUsedSlots(CMapPosition const& pos) const = 0; virtual sint32 maxUsedSlot(CMapPosition const& pos) const = 0; virtual bool isSlotUsed(CMapPosition const& pos, CSlot const& slot) const = 0; virtual uint getTopology(CWorldPosition const& wpos) const = 0; virtual sint getHeight(CWorldPosition const& wpos) const = 0; // clear height map virtual void clearHeightMap() = 0; // serial virtual void serial(NLMISC::IStream& f) = 0; // load a cell static CRootCell* load(NLMISC::IStream& f, CWorldMap const& worldMap); // save a cell static void save(NLMISC::IStream& f, CRootCell* cell); private: CWorldMap const& _WorldMapPtr; // his owner map; std::vector _TopologiesNodes; CWorldPosition _WorldPosition[4]; // 4 random positions for each slots. uint32 _flag; }; inline CRootCell::CRootCell(CWorldMap const& worldMapPtr) : _WorldMapPtr(worldMapPtr) { _flag = 0; } inline sint32 CRootCell::getMetricHeight(CWorldPosition const& wpos) const { // check if wpos valid if (!wpos.isValid()) return 0; sint32 const z = (getHeight(wpos)*2000 - 1000)&(~3); uint const top = getTopology(wpos); if (top<_TopologiesNodes.size() && _TopologiesNodes[top].isInInterior()) return z+2; return z; } inline CTopology& CRootCell::getTopologyNode(uint topology) { if (topology >= _TopologiesNodes.size()) _TopologiesNodes.resize(topology+1); return _TopologiesNodes[topology]; } inline CTopology const& CRootCell::getCstTopologyNode(uint topology) const { #if !FINAL_VERSION nlassert(topology < _TopologiesNodes.size()); #endif return _TopologiesNodes[topology]; } inline CTopology::TTopologyId CRootCell::getTopologyId(CWorldPosition const& wpos) const { return CTopology::TTopologyId(wpos, getTopology(wpos)); } inline CTopology::TTopologyRef CRootCell::getTopologyRef(const CWorldPosition&pos) const { #ifdef NL_DEBUG nlassert(pos.getRootCell()!=this); #endif return CTopology::TTopologyRef(pos); } inline CTopology const& CRootCell::getCstTopologyNode(CTopology::TTopologyId const& id) const { return getCstTopologyNode(id.getTopologyIndex()); } inline CDirectionMap const* CRootCell::getDirectionMap(uint topology) const { return (topology<_TopologiesNodes.size()) ? getCstTopologyNode(topology).DirectionMap : NULL; } inline void CRootCell::setDirectionMap(CDirectionMap* map, uint topology) { getTopologyNode(topology).DirectionMap = map; } inline void CRootCell::updateTopologyRef(CWorldMap* worldMap) { std::vector::iterator it=_TopologiesNodes.begin(), itEnd=_TopologiesNodes.end(); while (it!=itEnd) { (*it).updateTopologyRef(worldMap); ++it; } } inline CWorldPosition const& CRootCell::getWorldPosition(uint ind) const { #ifdef NL_DEBUG nlassert(ind<4); #endif return _WorldPosition[ind]; } inline void CRootCell::setWorldPosition(CWorldPosition const& pos, uint ind) { #ifdef NL_DEBUG nlassert(ind<4); #endif _WorldPosition[ind] = pos; } ////////////////////////////////////////////////////////////////////////////// /** * A computed 16x16 cell * \author Benjamin Legros * \author Nevrax France * \date 2003 */ class CComputeCell : public CRootCell { public: CComputeCell(CWorldMap const& worldMapPtr); CCellLinkage getCellLink(CWorldPosition const& wpos) const; uint32 nbUsedSlots(CMapPosition const& pos) const; sint32 maxUsedSlot(CMapPosition const& pos) const; bool isSlotUsed(CMapPosition const& pos, CSlot const& slot) const; uint getTopology(CWorldPosition const& wpos) const; sint getHeight(CWorldPosition const& wpos) const; TCellUnit const& getCellUnitCst(CMapPosition const& pos) const; CUnitSlot const& getUnitSlotCst(CWorldPosition const& wpos) const; TCellUnit& getCellUnit(CMapPosition const& pos); CUnitSlot& getUnitSlot(CWorldPosition const& wpos); void serial(NLMISC::IStream& f); void clearHeightMap() { } private: TCellUnit const& getCellUnitCst(CWorldPosition const& wpos) const; // a cell is a grid of 16x16 units TCellUnit _Grid[16*16]; }; inline CComputeCell::CComputeCell(CWorldMap const& worldMapPtr) : CRootCell(worldMapPtr) { } inline CCellLinkage CComputeCell::getCellLink(CWorldPosition const& wpos) const { return getUnitSlotCst(wpos).getCellLink(); } inline uint32 CComputeCell::nbUsedSlots(CMapPosition const& pos) const { TCellUnit const& cellUnit = getCellUnitCst(pos); uint32 count = 0; for (uint slot=0; slot<3; ++slot) if (cellUnit[slot].getCellLink().used()) ++count; return count; } inline sint32 CComputeCell::maxUsedSlot(CMapPosition const& pos) const { TCellUnit const& cellUnit = getCellUnitCst(pos); sint32 maxs = -1; for (uint slot=0; slot<3; ++slot) if (cellUnit[slot].getCellLink().used()) maxs = slot; return maxs; } inline bool CComputeCell::isSlotUsed(CMapPosition const& pos, CSlot const& slot) const { TCellUnit const& cellUnit = getCellUnitCst(pos); return slot.isValid() ? cellUnit[slot.slot()].getCellLink().used() : false; } inline uint CComputeCell::getTopology(CWorldPosition const& wpos) const { return getUnitSlotCst(wpos).topology(); } inline sint CComputeCell::getHeight(CWorldPosition const& wpos) const { return getUnitSlotCst(wpos).height(); } inline TCellUnit const& CComputeCell::getCellUnitCst(CMapPosition const& pos) const { return _Grid[pos.cellUnitFastIndex()]; } inline CUnitSlot const& CComputeCell::getUnitSlotCst(CWorldPosition const& wpos) const { #ifdef NL_DEBUG nlassert(wpos.isValid()); #endif return getCellUnitCst(wpos)[wpos.slot()]; } inline TCellUnit& CComputeCell::getCellUnit(CMapPosition const& pos) { return _Grid[pos.cellUnitFastIndex()]; } inline CUnitSlot& CComputeCell::getUnitSlot(CWorldPosition const& wpos) { #ifdef NL_DEBUG nlassert(wpos.isValid()); #endif return getCellUnit(wpos)[wpos.slot()]; } inline TCellUnit const& CComputeCell::getCellUnitCst(CWorldPosition const& wpos) const { return _Grid[wpos.cellUnitFastIndex()]; } ////////////////////////////////////////////////////////////////////////////// /** * A Single layer 16x16 cell that has not walk constraint (map is completely white) * \author Benjamin Legros * \author Nevrax France * \date 2003 */ class CWhiteCell : public CRootCell { public: CWhiteCell(CWorldMap const& worldMapPtr); CCellLinkage getCellLink(CWorldPosition const& wpos) const; uint32 nbUsedSlots(CMapPosition const& pos) const; sint32 maxUsedSlot(CMapPosition const& pos) const; bool isSlotUsed(CMapPosition const& pos, CSlot const& slot) const; uint getTopology(CWorldPosition const& wpos) const; sint getHeight(CWorldPosition const& wpos) const; void serial(NLMISC::IStream& f); void clearHeightMap(); void setHeightMap(I16x16Layer* heightMap); private: I16x16Layer* _HeightMap; }; inline CWhiteCell::CWhiteCell(CWorldMap const& worldMapPtr) : CRootCell(worldMapPtr), _HeightMap(NULL) { } inline CCellLinkage CWhiteCell::getCellLink(CWorldPosition const& wpos) const { return CCellLinkage(0); } inline uint32 CWhiteCell::nbUsedSlots(CMapPosition const& pos) const { return 1; } inline sint32 CWhiteCell::maxUsedSlot(CMapPosition const& pos) const { return 0; } inline bool CWhiteCell::isSlotUsed(CMapPosition const& pos, CSlot const& slot) const { return slot.slot() == 0; } inline uint CWhiteCell::getTopology(CWorldPosition const& wpos) const { return 0; } inline sint CWhiteCell::getHeight(CWorldPosition const& wpos) const { return (_HeightMap != NULL) ? _HeightMap->get(wpos.yCoord().getUnitId(), wpos.xCoord().getUnitId()) : 0; } inline void CWhiteCell::serial(NLMISC::IStream& f) { f.serialCheck((uint16)'WC'); if (f.isReading()) _HeightMap = I16x16Layer::load(f); else I16x16Layer::save(f, _HeightMap); } inline void CWhiteCell::clearHeightMap() { if (_HeightMap) delete _HeightMap; _HeightMap = NULL; } inline void CWhiteCell::setHeightMap(I16x16Layer* heightMap) { _HeightMap = heightMap; } ////////////////////////////////////////////////////////////////////////////// /** * A Single layer 16x16 cell * \author Benjamin Legros * \author Nevrax France * \date 2003 */ class CSingleLayerCell : public CRootCell { protected: bool testPos(uint i, uint j) const { return (_Map[i] & _MaskMap[j]) != 0; } void setPos(uint i, uint j, bool p) { _Map[i] = (p ? (_Map[i] | _MaskMap[j]) : (_Map[i] & (~_MaskMap[j]))); } public: CSingleLayerCell(CWorldMap const& worldMapPtr); CCellLinkage getCellLink(CWorldPosition const& wpos) const; uint32 nbUsedSlots(CMapPosition const& pos) const; sint32 maxUsedSlot(CMapPosition const& pos) const; bool isSlotUsed(CMapPosition const& pos, CSlot const& slot) const; uint getTopology(CWorldPosition const& wpos) const; sint getHeight(CWorldPosition const& wpos) const; bool testPos(CMapPosition const& pos) const; void setPos(CMapPosition const& pos, bool p); void setSLink(uint i, bool p) { _SLinks = p ? (_SLinks | _MaskMap[i]) : (_SLinks & (~_MaskMap[i])); } void setNLink(uint i, bool p) { _NLinks = p ? (_NLinks | _MaskMap[i]) : (_NLinks & (~_MaskMap[i])); } void setELink(uint i, bool p) { _ELinks = p ? (_ELinks | _MaskMap[i]) : (_ELinks & (~_MaskMap[i])); } void setWLink(uint i, bool p) { _WLinks = p ? (_WLinks | _MaskMap[i]) : (_WLinks & (~_MaskMap[i])); } void setTopologies(I16x16Layer* topology); void setHeightMap(I16x16Layer* heightMap); virtual void serial(NLMISC::IStream& f); void clearHeightMap(); private: /// The map of accessible positions uint16 _Map[16]; /// The north links, true if north position is accessible uint16 _SLinks; uint16 _NLinks; uint16 _ELinks; uint16 _WLinks; // topology layer I16x16Layer* _Topologies; // height map I16x16Layer* _HeightMap; static uint16 _MaskMap[16]; static bool _Initialized; }; inline CSingleLayerCell::CSingleLayerCell(CWorldMap const& worldMapPtr) : CRootCell(worldMapPtr) { for (uint i=0; i<16; ++i) _Map[i] = 0; _SLinks = 0; _NLinks = 0; _ELinks = 0; _WLinks = 0; _Topologies = NULL; _HeightMap = NULL; if (!_Initialized) { for (uint i=0; i<16; ++i) _MaskMap[i] = (1 << (15-i)); _Initialized = true; } } inline CCellLinkage CSingleLayerCell::getCellLink(CWorldPosition const& wpos) const { uint xi = wpos.xCoord().getUnitId(); uint yi = wpos.yCoord().getUnitId(); CCellLinkage l(0); l |= ((xi > 0 && testPos(yi, xi-1)) || (xi == 0 && (_WLinks&_MaskMap[yi]))) ? 0 : CCellLinkage::WestSlotMask; l |= ((xi < 15 && testPos(yi, xi+1)) || (xi == 15 && (_ELinks&_MaskMap[yi]))) ? 0 : CCellLinkage::EastSlotMask; l |= ((yi > 0 && testPos(yi-1, xi)) || (yi == 0 && (_SLinks&_MaskMap[xi]))) ? 0 : CCellLinkage::SouthSlotMask; l |= ((yi < 15 && testPos(yi+1, xi)) || (yi == 15 && (_NLinks&_MaskMap[xi]))) ? 0 : CCellLinkage::NorthSlotMask; return l; } inline uint32 CSingleLayerCell::nbUsedSlots(CMapPosition const& pos) const { return testPos(pos) ? 1 : 0; } inline sint32 CSingleLayerCell::maxUsedSlot(CMapPosition const& pos) const { return testPos(pos) ? 0 : -1; } inline bool CSingleLayerCell::isSlotUsed(CMapPosition const& pos, CSlot const& slot) const { return slot.slot() == 0 && testPos(pos); } inline uint CSingleLayerCell::getTopology(CWorldPosition const& wpos) const { return (_Topologies != NULL) ? _Topologies->get(wpos.yCoord().getUnitId(), wpos.xCoord().getUnitId()) : 0; } inline sint CSingleLayerCell::getHeight(CWorldPosition const& wpos) const { return (_HeightMap != NULL) ? _HeightMap->get(wpos.yCoord().getUnitId(), wpos.xCoord().getUnitId()) : 0; } inline bool CSingleLayerCell::testPos(CMapPosition const& pos) const { return testPos(pos.yCoord().getUnitId(), pos.xCoord().getUnitId()); } inline void CSingleLayerCell::setPos(CMapPosition const& pos, bool p) { setPos(pos.yCoord().getUnitId(), pos.xCoord().getUnitId(), p); } inline void CSingleLayerCell::setTopologies(I16x16Layer* topology) { delete _Topologies; _Topologies = topology; } inline void CSingleLayerCell::setHeightMap(I16x16Layer* heightMap) { if (_HeightMap) delete _HeightMap; _HeightMap = heightMap; } inline void CSingleLayerCell::clearHeightMap() { delete _HeightMap; _HeightMap = NULL; } ////////////////////////////////////////////////////////////////////////////// /** * A Multiple layer 16x16 cell (maximum 3 layers) * \author Benjamin Legros * \author Nevrax France * \date 2003 */ class CMultiLayerCell : public CRootCell { protected: class CCellLayer { friend class CMultiLayerCell; // for build features. public: CCellLinkage const& getCellLinkage(CMapPosition const& pos) const; uint8 getTopology(CMapPosition const& pos) const; I16x16Layer* getHeightMap() { return _HeightMap; } private: CCellLinkage& cellLinkage(CMapPosition const& pos); private: CCellLinkage _Layer[16*16]; uint8 _Topology[16*16]; I16x16Layer* _HeightMap; }; public: CMultiLayerCell(CWorldMap const& worldMapPtr); virtual ~CMultiLayerCell(); CCellLinkage getCellLink(CWorldPosition const& wpos) const; uint32 nbUsedSlots(CMapPosition const& pos) const; sint32 maxUsedSlot(CMapPosition const& pos) const; bool isSlotUsed(CMapPosition const& pos, CSlot const& slot) const; uint getTopology(CWorldPosition const& wpos) const; sint getHeight(CWorldPosition const& wpos) const; virtual void serial(NLMISC::IStream& f); void setLinks(CWorldPosition const& wpos, CCellLinkage links); void setTopology(CWorldPosition const& wpos, uint topology); void setHeightMap(CSlot const& slot, I16x16Layer* heightMap); void clearHeightMap(); private: // each layer is allocated in memory // the 3 layers available CCellLayer* _Layers[3]; }; inline CCellLinkage const& CMultiLayerCell::CCellLayer::getCellLinkage(CMapPosition const& pos) const { return _Layer[pos.cellUnitFastIndex()]; //yCoord().getUnitId()][pos.xCoord().getUnitId()]; } inline uint8 CMultiLayerCell::CCellLayer::getTopology(CMapPosition const& pos) const { return _Topology[pos.cellUnitFastIndex()]; //yCoord().getUnitId()][pos.xCoord().getUnitId()]; } inline CCellLinkage& CMultiLayerCell::CCellLayer::cellLinkage(CMapPosition const& pos) { return _Layer[pos.cellUnitFastIndex()]; //yCoord().getUnitId()][pos.xCoord().getUnitId()]; } inline CMultiLayerCell::CMultiLayerCell(CWorldMap const& worldMapPtr) : CRootCell(worldMapPtr) { _Layers[0] = NULL; _Layers[1] = NULL; _Layers[2] = NULL; } inline CMultiLayerCell::~CMultiLayerCell() { delete [] _Layers[0]; delete [] _Layers[1]; delete [] _Layers[2]; } inline CCellLinkage CMultiLayerCell::getCellLink(CWorldPosition const& wpos) const { #ifdef NL_DEBUG nlassert(wpos.isValid()); #endif CCellLayer* cellLayerPt=_Layers[wpos.slot()]; return (!cellLayerPt) ? CCellLinkage() : cellLayerPt->getCellLinkage(wpos); } inline uint32 CMultiLayerCell::nbUsedSlots(CMapPosition const& pos) const { uint32 ns = 0; for (uint32 slot=0; slot<3; ++slot) { CCellLayer* cellLayerPt=_Layers[slot]; if (cellLayerPt && cellLayerPt->cellLinkage(pos).used()) ++ns; } return ns; } inline sint32 CMultiLayerCell::maxUsedSlot(CMapPosition const& pos) const { sint32 maxs = -1; for (uint32 slot=0; slot<3; ++slot) { CCellLayer* cellLayerPt=_Layers[slot]; if (cellLayerPt && cellLayerPt->cellLinkage(pos).used()) maxs = slot; } return maxs; } inline bool CMultiLayerCell::isSlotUsed(CMapPosition const& pos, CSlot const& slot) const { if (!slot.isValid()) return false; CCellLayer* cellLayerPt = _Layers[slot.slot()]; return (cellLayerPt && cellLayerPt->cellLinkage(pos).used()); } inline uint CMultiLayerCell::getTopology(CWorldPosition const& wpos) const { #ifdef NL_DEBUG nlassert(wpos.isValid()); #endif CCellLayer* cellLayerPt = _Layers[wpos.slot()]; return (!cellLayerPt) ? 0 : cellLayerPt->getTopology(wpos); } inline sint CMultiLayerCell::getHeight(CWorldPosition const& wpos) const { #ifdef NL_DEBUG nlassert(wpos.isValid()); #endif CCellLayer* cellLayerPt = _Layers[wpos.slot()]; return (!cellLayerPt || !cellLayerPt->getHeightMap()) ? 0 : cellLayerPt->getHeightMap()->get(wpos.yCoord().getUnitId(), wpos.xCoord().getUnitId()); } inline void CMultiLayerCell::setLinks(CWorldPosition const& wpos, CCellLinkage links) { #ifdef NL_DEBUG nlassert(wpos.isValid()); #endif CCellLayer* cellLayerPt = _Layers[wpos.slot()]; if (!cellLayerPt) { cellLayerPt = new CCellLayer(); _Layers[wpos.slot()] = cellLayerPt; for (uint i=0; i<16*16; ++i) cellLayerPt->_Topology[i] = 0; cellLayerPt->_HeightMap = NULL; } #ifdef NL_DEBUG nlassert(cellLayerPt); #endif cellLayerPt->cellLinkage(wpos)=links; } inline void CMultiLayerCell::setTopology(CWorldPosition const& wpos, uint topology) { #ifdef NL_DEBUG nlassert(topology < 256); nlassert(wpos.isValid()); #endif CCellLayer* cellLayerPt = _Layers[wpos.slot()]; #ifdef NL_DEBUG nlassert(cellLayerPt); #endif cellLayerPt->_Topology[wpos.cellUnitFastIndex()]=(uint8)topology; } inline void CMultiLayerCell::setHeightMap(CSlot const& slot, I16x16Layer* heightMap) { #ifdef NL_DEBUG nlassert(slot.isValid()); #endif CCellLayer* cellLayerPt = _Layers[slot.slot()]; #ifdef NL_DEBUG nlassert(cellLayerPt); #endif if (cellLayerPt->_HeightMap) delete cellLayerPt->_HeightMap; cellLayerPt->_HeightMap = heightMap; } inline void CMultiLayerCell::clearHeightMap() { for (CSlot i(0); i.isValid(); ++i) { CCellLayer* cellLayerPt = _Layers[i.slot()]; if (cellLayerPt) { delete cellLayerPt->_HeightMap; cellLayerPt->_HeightMap=NULL; } } } ////////////////////////////////////////////////////////////////////////////// /** * A CSuperCell of basically 16x16 CCells * \author Benjamin Legros * \author Nevrax France * \date 2003 */ class CSuperCell { public: CSuperCell(CWorldMap const& worldMap); virtual ~CSuperCell(); // get a const cell (might be NULL) CRootCell const* getRootCellCst(CMapPosition const& pos) const; // get a const cell (might be NULL) CRootCell* getRootCell(CMapPosition const& pos); void updateTopologyRef(CWorldMap* worldMap); void countCells(uint& compute, uint& white, uint& simple, uint& multi, uint& other) const; // set a cell void setRootCell(CMapPosition const& pos, CRootCell* cell); // // compute cell selectors // // get a cell CComputeCell* getComputeCell(CMapPosition const& pos); // get a const cell CComputeCell const* getComputeCell(CMapPosition const& pos) const; // get a slot in a cell CUnitSlot getUnitSlot(CWorldPosition const& wpos) const; // get a slot in super cell CUnitSlot& getUnitSlot(CWorldPosition const& wpos); TCellUnit& getCellUnit(CMapPosition const& pos); void serial(NLMISC::IStream& f); void markRootCell(uint32 const flag) const; private: // a super cell is composed of 16x16 pointers to cells, initially NULL CRootCell* _Grid[16*16]; CWorldMap const& _WorldMap; }; inline CSuperCell::CSuperCell(CWorldMap const& worldMap) : _WorldMap(worldMap) { for (uint i=0;i<16*16;i++) _Grid[i] = NULL; } inline CSuperCell::~CSuperCell() { for (uint i=0;i<16*16;i++) if (_Grid[i]) delete _Grid[i]; } inline CRootCell const* CSuperCell::getRootCellCst(CMapPosition const& pos) const { return _Grid[pos.rootCellFastIndex()]; } inline CRootCell* CSuperCell::getRootCell(CMapPosition const& pos) { return _Grid[pos.rootCellFastIndex()]; } inline void CSuperCell::setRootCell(CMapPosition const& pos, CRootCell* cell) { CRootCell*& rootCell = _Grid[pos.rootCellFastIndex()]; if (rootCell==cell) return; delete rootCell; rootCell = cell; } inline CComputeCell* CSuperCell::getComputeCell(CMapPosition const& pos) { CRootCell*& rootCell = _Grid[pos.rootCellFastIndex()]; if (!rootCell) rootCell = new CComputeCell(_WorldMap); #ifdef NL_DEBUG nlassert(rootCell); nlassert(dynamic_cast(rootCell)); #endif return static_cast(rootCell); } inline CComputeCell const* CSuperCell::getComputeCell(CMapPosition const& pos) const { CRootCell const* const& rootCell = _Grid[pos.rootCellFastIndex()]; if (!rootCell) return NULL; #ifdef NL_DEBUG nlassert(dynamic_cast(rootCell)); #endif return static_cast(rootCell); } inline CUnitSlot CSuperCell::getUnitSlot(CWorldPosition const& wpos) const { CComputeCell const* cell = getComputeCell(wpos); return !cell ? CUnitSlot() : cell->getUnitSlotCst(wpos); } inline CUnitSlot& CSuperCell::getUnitSlot(CWorldPosition const& wpos) { CComputeCell* cell = getComputeCell(wpos); return cell->getUnitSlot(wpos); } inline TCellUnit& CSuperCell::getCellUnit(CMapPosition const& pos) { CComputeCell* cell = getComputeCell(pos); return cell->getCellUnit(pos); } inline void CSuperCell::markRootCell(uint32 const flag) const { for (uint32 i=0; i<256; ++i) { if (_Grid[i]) _Grid[i]->setFlag(flag); } } ////////////////////////////////////////////////////////////////////////////// /** * The A* Path structure, computed by the WorldMap * \author Benjamin Legros * \author Nevrax France * \date 2003 */ class CAStarPath { friend class CWorldMap; public: std::vector const& topologiesPath() const { return _TopologiesPath; } std::vector& topologiesPathForCalc() { return _TopologiesPath; } CWorldPosition const& getStartPos() const { return _Start; } void setStartPos(CWorldPosition const& pos) { _Start = pos; } CWorldPosition const& getEndPos() const { return _End; } void setEndPos(CWorldPosition const& pos) { _End = pos; } private: std::vector _TopologiesPath; CWorldPosition _Start; CWorldPosition _End; }; ////////////////////////////////////////////////////////////////////////////// /** * The Inside A* Path structure, computed by the WorldMap * \author Benjamin Legros * \author Nevrax France * \date 2003 */ class CInsideAStarPath { friend class CWorldMap; public: CWorldPosition const& getStartPos() const { return _Start; } void setStartPos(CWorldPosition const& pos) { _Start = pos; } CWorldPosition const& getEndPos() const { return _End; } void setEndPos(CWorldPosition const& pos) { _End = pos; } std::vector& getStepPathForCalc() { return _StepPath; } private: CWorldPosition _Start; CWorldPosition _End; std::vector _StepPath; }; ////////////////////////////////////////////////////////////////////////////// /** * A template Heap implementation * \author Benjamin Legros * \author Nevrax France * \date 2003 */ template class CHeap { public: typedef std::pair THeapNode; public: CHeap(); /// clear Heap void clear() { _Heap.resize(1); } /// test heap is empty or not bool empty() const { return _Heap.size() <= 1; } /// push a value in heap and sort it void push(T key, V const& value); /// pop the minimal value of the heap V pop(); void backwardLeveling(uint index); void forwardLeveling(uint index); /// auto check of the internal heap state void check(); uint32 size() { return _Heap.size(); } std::vector& heapAsVector() { return _Heap; } private: std::vector _Heap; }; template CHeap::CHeap() { // node 0 unused (daniel's trick) clear(); } template void CHeap::push(T key, V const& value) { _Heap.push_back(THeapNode(key, value)); backwardLeveling((uint)_Heap.size()-1); } template V CHeap::pop() { #ifdef NL_DEBUG nlassert(_Heap.size() > 1); #endif // return best value V ret = _Heap[1].second; // if only 1 node in heap, pop it and leave if (_Heap.size() == 2) { _Heap.pop_back(); return ret; } // for more than 1 object, copy last at top and pop last _Heap[1] = _Heap.back(); _Heap.pop_back(); forwardLeveling (1); return ret; } template void CHeap::backwardLeveling(uint index) { while (index != 1 && _Heap[index].first < _Heap[index/2].first) { swap(_Heap[index], _Heap[index/2]); index /= 2; } } template void CHeap::forwardLeveling(uint index) { while (true) { uint min_index = 2*index; // if object has no child, leave if (min_index > _Heap.size()-1) break; // choose left or right child if (min_index+1 <= _Heap.size()-1 && _Heap[min_index].first>_Heap[min_index+1].first) ++min_index; // if swap needed, swap and step one more, else leave if (_Heap[index].first > _Heap[min_index].first) { swap(_Heap[index], _Heap[min_index]); index = min_index; } else { break; } } } template void CHeap::check() { for (uint index=1; index<=_Heap.size()-1; ++index) { if (2*index <= _Heap.size()-1) nlassert(_Heap[index].first <= _Heap[2*index].first); if (2*index+1 <= _Heap.size()-1) nlassert(_Heap[index].first <= _Heap[2*index+1].first); } } ////////////////////////////////////////////////////////////////////////////// /** * The world mapping * \author Benjamin Legros * \author Nevrax France * \date 2003 */ class CWorldMap { friend class RYPACSCRUNCH::CPacsCruncher; friend class CWorldPosition; friend class CTopology::CNeighbourLink; public: CWorldMap(); ~CWorldMap(); // serial -- beware, this method merges, use clear() before to load only one world map void serial(NLMISC::IStream& f); /// \name User Interface, path finding and complex moves // @{ /// Finds an A* path bool findAStarPath(CWorldPosition const& start, CWorldPosition const& end, std::vector& path, TAStarFlag denyflags = Nothing) const; /// Finds an A* path bool findAStarPath(CTopology::TTopologyId const& start, CTopology::TTopologyId const& end, CAStarPath& path, TAStarFlag denyflags = Nothing) const; /// Finds an A* inside a topoly bool findInsideAStarPath(CWorldPosition const& start, CWorldPosition const& end, std::vector& stepPath, TAStarFlag denyflags = Nothing) const; /// Moves to given topology, returns false if failed bool moveTowards(CWorldPosition& pos, CTopology::TTopologyRef const& topology) const; /// Moves according a to a given path, returns false if failed bool move(CWorldPosition& pos, CAStarPath& path, uint& currentstep) const; /// Moves from a position to another bool move(CWorldPosition& pos, CMapPosition const& end, TAStarFlag const denyFlags) const; // @} bool customCheckDiagMove(CWorldPosition const& pos, CDirection const& direction, RYAI_MAP_CRUNCH::TAStarFlag denyFlags) const; // clean void clear(); void setFlagOnPosAndRadius(CMapPosition const& pos, float radius, uint32 flag); // to remove. CNeighbourhood neighbours(CWorldPosition const& wpos) const; /// Moves in neighbourhood bool move(CWorldPosition& pos, CDirection const& direction) const; /// Moves in neighbourhood (corner moves don't avoid collision) bool moveSecure(CWorldPosition& pos, CDirection const& direction, uint16 maskFlags = 0xffff) const; /// Moves in neighbourhood with diag test on both sides bool moveDiagTestBothSide(CWorldPosition& pos, CDirection const& direction) const; CTopology::TTopologyId getTopologyId(CWorldPosition const& wpos) const; // LastRemoved CTopology const& getTopologyNode(CTopology::TTopologyId const& id) const; CTopology& getTopologyNode(CTopology::TTopologyId const& id); CGridDirectionLayer const* getGridDirectionLayer(CWorldPosition const& pos, CTopology::TTopologyRef const& topologyRef) const; /// Get CWorldPosition from a CMapPosition and a CSlot CWorldPosition getWorldPosition(CMapPosition const& mapPos, CSlot const& slot) const; /** * Get CWorldPosition from a CMapPosition and a TLevel * Assumes that 0 is the highest level at the given CMapPosition, and greater the level is, lower the height is. */ CWorldPosition getWorldPosition(CMapPosition const& mapPos, TLevel level) const; // do not initialise a bot with the position & the world position (or u must assume that pos have no fraction). bool setWorldPosition(AITYPES::TVerticalPos verticalPos, CWorldPosition& wpos, CAIVector const& pos, CRootCell const* originCell = NULL) const; // Alternate setWorldPosition(), with real z instead of TVerticalPos, in millimeters bool setWorldPosition(sint32 z, CWorldPosition& wpos, CAIVector const& pos, CRootCell const* originCell = NULL) const; // Double version isn't tested !!! Verify it's the same than above sint32 version ! bool setWorldPosition(double z, CWorldPosition& wpos, CAIVector const& pos, CRootCell const* originCell = NULL) const; CTopology::TTopologyRef getTopologyRef(CTopology::TTopologyId const& id) const; void buildMasterTopo(bool allowWater, bool allowNogo); // checks motion layers void checkMotionLayer(); void countSuperTopo(); CRootCell const* getRootCellCst(CMapPosition const& pos) const; CSuperCell const* getSuperCellCst(CMapPosition const& pos) const; void getBounds(CMapPosition& min, CMapPosition& max); CWorldPosition getSafeWorldPosition(CMapPosition const& mapPos, CSlot const& slot) const; protected: CWorldPosition getWorldPositionGeneration(CMapPosition const& mapPos, CSlot const& slot) const; /// Get a world position from a vector position template CWorldPosition getWorldPosition(T const& pos) const { CSlot slot; CMapPosition const mapPos(pos); CRootCell const* cell = getRootCellCst(mapPos); if (cell) { double bd = 1.0e10; // find best slot for (uint32 s=0; s<3; ++s) { CSlot const sslot = CSlot(s); if (!cell->isSlotUsed(mapPos, sslot)) continue; double const sh = cell->getHeight(CWorldPosition(cell,mapPos,sslot))*2.0 + 1.0; if (fabs(sh) < bd) { bd = fabs(sh); slot=sslot; } } } return CWorldPosition(cell,mapPos,slot); } // only for generation. CSuperCell* getSuperCell(CMapPosition const& pos); CComputeCell* getComputeCell(CMapPosition const& pos); CRootCell* getRootCell(CMapPosition const& pos); bool exist(CMapPosition const& pos) const; uint32 nbUsedSlots(CMapPosition const& pos) const; sint32 maxUsedSlot(CMapPosition const& pos) const; bool isSlotUsed(CMapPosition const& pos, CSlot slot) const; // used in build process. uint getTopology(CWorldPosition const& wpos) const; void countCells(uint& compute, uint& white, uint& simple, uint& multi, uint& other) const; // mutators void setRootCell(CMapPosition const& pos, CRootCell* cell); // clear height map void clearHeightMap(); // // compute cell selectors // TCellUnit& getCellUnit(CMapPosition const& pos); CUnitSlot& getUnitSlot(CWorldPosition const& wpos); CComputeCell const* getComputeCellCst(CMapPosition const& pos) const; void resetUnitSlotNLink(CWorldPosition const& wpos) { getUnitSlot(wpos).cellLink().setNSlot(CSlot()); } void resetUnitSlotSLink(CWorldPosition const& wpos) { getUnitSlot(wpos).cellLink().setSSlot(CSlot()); } void resetUnitSlotWLink(CWorldPosition const& wpos) { getUnitSlot(wpos).cellLink().setWSlot(CSlot()); } void resetUnitSlotELink(CWorldPosition const& wpos) { getUnitSlot(wpos).cellLink().setESlot(CSlot()); } void resetUnitSlot(CWorldPosition const& wpos); private: CSuperCell* _GridFastAccess[256*256]; public: /// \name Path finding related error handling // @{ enum TFindAStarPathReason { FASPR_NO_REASON = -1, FASPR_NO_ERROR, FASPR_INVALID_START_POS, FASPR_INVALID_END_POS, FASPR_INVALID_START_TOPO, FASPR_INVALID_END_TOPO, FASPR_INCOMPATIBLE_POSITIONS, FASPR_NOT_FOUND }; mutable TFindAStarPathReason _LastFASPReason; static std::string toString(TFindAStarPathReason reason); enum TFindInsideAStarPathReason { FIASPR_NO_REASON = -1, FIASPR_NO_ERROR, FIASPR_INVALID_START_POS, FIASPR_INVALID_END_POS, FIASPR_DIFFERENT_TOPO, FIASPR_NOT_FOUND }; mutable TFindInsideAStarPathReason _LastFIASPReason; static std::string toString(TFindInsideAStarPathReason reason); // @} }; inline CWorldPosition::CWorldPosition(const CRootCell *cell, const CMapPosition &pos, const CSlot &slot) : CMapPosition(pos), CSlot(slot), _RootCell(cell) { _cellLinkage=_RootCell->getCellLink(*this); } inline CWorldPosition::CWorldPosition(const CRootCell *cell, const CMapPosition &pos, const CSlot &slot,bool generationOnly) : CMapPosition(pos), CSlot(slot), _RootCell(cell) { } inline CWorldPosition CWorldPosition::getPosS() const { CWorldPosition wp(*this); wp.stepS(); return wp; } inline CWorldPosition CWorldPosition::getPosN() const { CWorldPosition wp(*this); wp.stepN(); return wp; } inline CWorldPosition CWorldPosition::getPosE() const { CWorldPosition wp(*this); wp.stepE(); return wp; } inline CWorldPosition CWorldPosition::getPosW() const { CWorldPosition wp(*this); wp.stepW(); return wp; } inline void CWorldPosition::setPosS(CWorldPosition& pos) const { pos = *this; pos.stepS(); } inline void CWorldPosition::setPosN(CWorldPosition& pos) const { pos = *this; pos.stepN(); } inline void CWorldPosition::setPosE(CWorldPosition& pos) const { pos = *this; pos.stepE(); } inline void CWorldPosition::setPosW(CWorldPosition& pos) const { pos = *this; pos.stepW(); } inline void CWorldPosition::stepS() { #ifdef NL_DEBUG nlassert(_RootCell); #endif setSlot(_cellLinkage.SSlot()); #ifdef NL_DEBUG nlassert(CSlot::isValid()); #endif if (CMapPosition::stepS()) // check if we have to recalculate the RootCell .. { _RootCell = getRootCell()->getWorldMapPtr().getRootCellCst(*this); // obtain the new _RootCell pointer } _cellLinkage = _RootCell->getCellLink(*this); } inline void CWorldPosition::stepN() { #ifdef NL_DEBUG nlassert(_RootCell); #endif setSlot(_cellLinkage.NSlot()); #ifdef NL_DEBUG nlassert(CSlot::isValid()); #endif if (CMapPosition::stepN()) // check if we have to recalculate the RootCell .. { _RootCell = getRootCell()->getWorldMapPtr().getRootCellCst(*this); // obtain the new _RootCell pointer } _cellLinkage = _RootCell->getCellLink(*this); } inline void CWorldPosition::stepE() { #ifdef NL_DEBUG nlassert(_RootCell); #endif setSlot(_cellLinkage.ESlot()); #ifdef NL_DEBUG nlassert(CSlot::isValid()); #endif if (CMapPosition::stepE()) // check if we have to recalculate the RootCell .. { _RootCell = getRootCell()->getWorldMapPtr().getRootCellCst(*this); // obtain the new _RootCell pointer } _cellLinkage = _RootCell->getCellLink(*this); } inline void CWorldPosition::stepW() { #ifdef NL_DEBUG nlassert(_RootCell); #endif setSlot(_cellLinkage.WSlot()); #ifdef NL_DEBUG nlassert(CSlot::isValid()); #endif if (CMapPosition::stepW()) // check if we have to recalculate the RootCell .. { _RootCell = getRootCell()->getWorldMapPtr().getRootCellCst(*this); // obtain the new _RootCell pointer } _cellLinkage = _RootCell->getCellLink(*this); } inline bool CWorldPosition::moveS() { if (getCellLinkage().isSSlotValid()) { stepS(); return true; } return false; } inline bool CWorldPosition::moveN() { if (getCellLinkage().isNSlotValid()) { stepN(); return true; } return false; } inline bool CWorldPosition::moveE() { if (getCellLinkage().isESlotValid()) { stepE(); return true; } return false; } inline bool CWorldPosition::moveW() { if (getCellLinkage().isWSlotValid()) { stepW(); return true; } return false; } inline NLMISC::CVectorD CWorldPosition::getPosition() const { NLMISC::CVectorD ret = toVectorD(); CRootCell const* cell = getRootCell(); if (cell) ret.z = cell->getHeight(*this)*2.0 + 1.0; return ret; } inline sint32 CWorldPosition::getMetricHeight() const { if (_RootCell) return _RootCell->getMetricHeight(*this); return 0; } inline CTopology const& CTopology::TTopologyRef::getCstTopologyNode() const { #ifdef NL_DEBUG nlassert(_RootCell); #endif return _RootCell->getCstTopologyNode(getTopologyIndex()); } inline CTopology::TTopologyRef::TTopologyRef(CWorldPosition const& pos, CRootCell const* rootCell) : TTopologyId(pos, rootCell->getTopology(pos)) , _RootCell(rootCell) { #ifdef NL_DEBUG nlassert(rootCell); #endif // TESTTOREMOVE TODO #if !FINAL_VERSION nlassert(&_RootCell->getCstTopologyNode(getTopologyIndex())!=NULL); #endif } inline CTopology::TTopologyRef::TTopologyRef(CWorldPosition const& pos) : TTopologyId(pos, pos.getRootCell()->getTopology(pos)) , _RootCell(pos.getRootCell()) { // TESTTOREMOVE TODO #if !FINAL_VERSION nlassert(&_RootCell->getCstTopologyNode(getTopologyIndex())!=NULL); #endif } inline void CTopology::CNeighbourLink::updateTopologyRef(CWorldMap* worldMapPtr) { _Ref.setRootCell(worldMapPtr->getRootCell(_Ref.getMapPosition())); // TESTTOREMOVE TODO #if !FINAL_VERSION nlassert(&_Ref.getCstTopologyNode()!=NULL); #endif } inline CWorldMap::CWorldMap() { for (uint i=0; i<65536; ++i) _GridFastAccess[i]=NULL; } inline CWorldMap::~CWorldMap() { for (uint i=0; i<65536; ++i) if (_GridFastAccess[i]) delete _GridFastAccess[i]; } inline CTopology::TTopologyId CWorldMap::getTopologyId(CWorldPosition const& wpos) const { CRootCell const* cell = getRootCellCst(wpos); return !cell ? CTopology::TTopologyId() : CTopology::TTopologyId(wpos, cell->getTopology(wpos)); } inline CTopology const& CWorldMap::getTopologyNode(CTopology::TTopologyId const& id) const { CMapPosition pos = id.getMapPosition(); uint topo = id.getTopologyIndex(); CRootCell const* cell = getRootCellCst(pos); #ifdef NL_DEBUG nlassert(cell); #endif return cell->getCstTopologyNode(topo); } inline CTopology& CWorldMap::getTopologyNode(CTopology::TTopologyId const& id) { CMapPosition pos = id.getMapPosition(); uint topo = id.getTopologyIndex(); CRootCell* cell = getRootCell(pos); #ifdef NL_DEBUG nlassert(cell); #endif return cell->getTopologyNode(topo); } inline CGridDirectionLayer const* CWorldMap::getGridDirectionLayer(CWorldPosition const& pos, CTopology::TTopologyRef const& topologyRef) const { if (!pos.isValid()) return NULL; CMapPosition tpos = topologyRef.getMapPosition(); sint dx = pos.xCoord().getFullCellId()-tpos.xCoord().getFullCellId(); sint dy = pos.yCoord().getFullCellId()-tpos.yCoord().getFullCellId(); if (abs(dx) > 1 || abs(dy) > 1) return NULL; CTopology const& node = topologyRef.getCstTopologyNode(); if (!node.DirectionMap) return NULL; CDirectionLayer* directionLayer = node.DirectionMap->Layers[pos.slot()]; if (!directionLayer) return NULL; return directionLayer->getGridLayer(dy+1,dx+1); } inline CWorldPosition CWorldMap::getWorldPosition(CMapPosition const& mapPos, CSlot const& slot) const { return CWorldPosition(getRootCellCst(mapPos), mapPos, slot); } inline CWorldPosition CWorldMap::getWorldPosition(CMapPosition const& mapPos, TLevel level) const { CRootCell const* cell = getRootCellCst(mapPos); std::vector slots; slots.reserve(4); // go through all slots and get their height for (uint s=0; s<3; ++s) { CSlot slot(s); if (!cell->isSlotUsed(mapPos, slot)) continue; CWorldPosition wPos = CWorldPosition(cell, mapPos, slot); slots.push_back((cell->getHeight(wPos) << 2) + s); } // sort them, from the lowest to the heightest std::sort(slots.begin(), slots.end()); // get heightest slot level = (RYAI_MAP_CRUNCH::TLevel)(slots.size()-1) - level; // if slot exists, return it or invalid position return (level < 0 && level >= (sint)slots.size()) ? CWorldPosition() : CWorldPosition(cell, mapPos, CSlot(slots[level]&3)); } inline bool CWorldMap::setWorldPosition(AITYPES::TVerticalPos verticalPos, CWorldPosition& wpos, CAIVector const& pos, CRootCell const* originCell) const { CSlot slot; CMapPosition const mapPos(pos); CRootCell const* cell = originCell ? originCell : getRootCellCst(mapPos); if (!cell) { wpos = CWorldPosition(cell, mapPos, slot, true); // addon (not agree but no choice). return false; } std::map orderedSlots; // find best slot for (uint32 s=0; s<3; ++s) { if (!cell->isSlotUsed(mapPos, CSlot(s))) continue; CSlot const sslot = CSlot(s); double const sh = cell->getHeight(CWorldPosition(cell,mapPos,sslot)); orderedSlots.insert(std::make_pair(sh, sslot)); } if (!orderedSlots.empty()) { if (verticalPos == AITYPES::vp_auto) { double h = orderedSlots.rbegin()->first; slot = orderedSlots.rbegin()->second; } else if (verticalPos == AITYPES::vp_upper) { double h = orderedSlots.rbegin()->first; slot = orderedSlots.rbegin()->second; } else if (verticalPos == AITYPES::vp_lower) { double h = orderedSlots.begin()->first; slot = orderedSlots.begin()->second; } else if (verticalPos == AITYPES::vp_middle) { if (orderedSlots.size() > 1) { orderedSlots.erase(orderedSlots.rbegin()->first); double h = orderedSlots.rbegin()->first; slot = orderedSlots.rbegin()->second; } else { double h = orderedSlots.begin()->first; slot = orderedSlots.begin()->second; } } else { nlwarning("invalid vertical pos type !"); } } if (!slot.isValid()) { wpos = CWorldPosition(cell,mapPos,slot,true); return false; } wpos = CWorldPosition(cell,mapPos,slot); return true; } inline CTopology::TTopologyRef CWorldMap::getTopologyRef(CTopology::TTopologyId const& id) const { return CTopology::TTopologyRef(id, const_cast(getRootCellCst(id.getMapPosition()))); } inline CRootCell const* CWorldMap::getRootCellCst(CMapPosition const& pos) const { CSuperCell const* scell = getSuperCellCst(pos); return !scell ? NULL : scell->getRootCellCst(pos); } inline CSuperCell const* CWorldMap::getSuperCellCst(CMapPosition const& pos) const { return _GridFastAccess[pos.superCellFastIndex()]; } inline CWorldPosition CWorldMap::getWorldPositionGeneration(CMapPosition const& mapPos, CSlot const& slot) const { return CWorldPosition(getRootCellCst(mapPos),mapPos,slot,true); // without assuring cellLink is valid .. :( } inline CWorldPosition CWorldMap::getSafeWorldPosition(CMapPosition const& mapPos, CSlot const& slot) const { CRootCell const* cell = getRootCellCst(mapPos); return (cell && cell->isSlotUsed(mapPos, slot)) ? CWorldPosition(getRootCellCst(mapPos),mapPos,slot) : CWorldPosition(); // without assuring cellLink is valid .. :( } inline CSuperCell* CWorldMap::getSuperCell(CMapPosition const& pos) { CSuperCell* superCellPtr = _GridFastAccess[pos.superCellFastIndex()]; if (!superCellPtr) _GridFastAccess[pos.superCellFastIndex()] = superCellPtr=new CSuperCell(*this); #ifdef NL_DEBUG nlassert(superCellPtr); #endif return superCellPtr; } inline CComputeCell* CWorldMap::getComputeCell(CMapPosition const& pos) { return getSuperCell(pos)->getComputeCell(pos); } inline CRootCell* CWorldMap::getRootCell(CMapPosition const& pos) { CSuperCell* scell = getSuperCell(pos); return !scell ? NULL : scell->getRootCell(pos); } inline bool CWorldMap::exist(CMapPosition const& pos) const { return getRootCellCst(pos)!=NULL; } inline uint32 CWorldMap::nbUsedSlots(CMapPosition const& pos) const { CRootCell const* cell = getRootCellCst(pos); return !cell ? 0:cell->nbUsedSlots(pos); } inline sint32 CWorldMap::maxUsedSlot(CMapPosition const& pos) const { CRootCell const* cell = getRootCellCst(pos); return !cell ? -1 : cell->maxUsedSlot(pos); } inline bool CWorldMap::isSlotUsed(CMapPosition const& pos, CSlot slot) const { CRootCell const* cell = getRootCellCst(pos); return !cell ? false : cell->isSlotUsed(pos, slot); } inline uint CWorldMap::getTopology(CWorldPosition const& wpos) const { CRootCell const* cell = wpos.getRootCell(); //getRootCellCst(wpos); return !cell ? 0 : cell->getTopology(wpos); } inline void CWorldMap::setRootCell(CMapPosition const& pos, CRootCell* cell) { getSuperCell(pos)->setRootCell(pos, cell); } inline TCellUnit& CWorldMap::getCellUnit(CMapPosition const& pos) { return getComputeCell(pos)->getCellUnit(pos); } inline CUnitSlot& CWorldMap::getUnitSlot(CWorldPosition const& wpos) { return getComputeCell(wpos)->getUnitSlot(wpos); } inline CComputeCell const* CWorldMap::getComputeCellCst(CMapPosition const& pos) const { CSuperCell const* scell = getSuperCellCst(pos); return !scell ? NULL : scell->getComputeCell(pos); } inline void CWorldMap::resetUnitSlot(CWorldPosition const& wpos) { CUnitSlot& us = getUnitSlot(wpos); if (us.getCellLink().isNSlotValid()) resetUnitSlotSLink(wpos.getPosN()); if (us.getCellLink().isSSlotValid()) resetUnitSlotNLink(wpos.getPosS()); if (us.getCellLink().isWSlotValid()) resetUnitSlotELink(wpos.getPosW()); if (us.getCellLink().isESlotValid()) resetUnitSlotWLink(wpos.getPosE()); us.reset(); } inline std::string CWorldMap::toString(TFindAStarPathReason reason) { switch (reason) { case FASPR_NO_ERROR: return "FASPR_NO_ERROR"; case FASPR_INVALID_START_POS: return "FASPR_INVALID_START_POS"; case FASPR_INVALID_END_POS: return "FASPR_INVALID_END_POS"; case FASPR_INVALID_START_TOPO: return "FASPR_INVALID_START_TOPO"; case FASPR_INVALID_END_TOPO: return "FASPR_INVALID_END_TOPO"; case FASPR_INCOMPATIBLE_POSITIONS: return "FASPR_INCOMPATIBLE_POSITIONS"; case FASPR_NOT_FOUND: return "FIASPR_NOT_FOUND"; default: return "UnknownReason(" + NLMISC::toString((int)reason) + ")"; } } inline std::string CWorldMap::toString(TFindInsideAStarPathReason reason) { switch (reason) { case FIASPR_NO_ERROR: return "FIASPR_NO_ERROR"; case FIASPR_INVALID_START_POS: return "FIASPR_INVALID_START_POS"; case FIASPR_INVALID_END_POS: return "FIASPR_INVALID_END_POS"; case FIASPR_DIFFERENT_TOPO: return "FIASPR_DIFFERENT_TOPO"; case FIASPR_NOT_FOUND: return "FIASPR_NOT_FOUND"; default: return "UnknownReason(" + NLMISC::toString((int)reason) + ")"; } } } ////////////////////////////////////////////////////////////////////////////// class CTimeEstimator { public: CTimeEstimator(uint total); void step(char const* str); private: NLMISC::TTime _StartTime; NLMISC::TTime _LastTime; uint _TotalCounter; uint _CurrentCounter; uint _LastCounter; double _AverageSpeed; std::string secToString(sint sec); }; inline CTimeEstimator::CTimeEstimator(uint total) : _TotalCounter(total) , _CurrentCounter(0) , _LastCounter(0) , _AverageSpeed(-1.0) { _StartTime = NLMISC::CTime::getLocalTime(); _LastTime = _StartTime; } inline void CTimeEstimator::step(char const* str) { ++_CurrentCounter; NLMISC::TTime currentTime = NLMISC::CTime::getLocalTime(); if (currentTime-_LastTime > 2000) { NLMISC::TTime dt = currentTime-_LastTime; double speed = 1000.0*(_CurrentCounter-_LastCounter)/dt; _AverageSpeed = (_AverageSpeed < 0.0 ? speed : _AverageSpeed*0.98 + speed*0.02); uint remaining = _TotalCounter-_CurrentCounter; NLMISC::TTime ellapsedTime = currentTime-_StartTime; NLMISC::TTime remainingTime = (NLMISC::TTime)(remaining*1000/_AverageSpeed); double percent = 100.0*_CurrentCounter/_TotalCounter; uint maxpercent = (percent > 100.0 ? 100 : (uint)percent); uint linesize = maxpercent/5; nlinfo("%s: %.1f%% [ellapsed:%s, togo:%s, total:%s]", str, percent, secToString((uint)(ellapsedTime/1000)).c_str(), secToString((uint)(remainingTime/1000)).c_str(), secToString((uint)((remainingTime+ellapsedTime)/1000)).c_str()); _LastCounter = _CurrentCounter; _LastTime = currentTime; } } inline std::string CTimeEstimator::secToString(sint sec) { return NLMISC::toString(sec/60)+"m"+NLMISC::toString(sec%60)+"s"; } #endif