1 // Ryzom - MMORPG Framework <http://dev.ryzom.com/projects/ryzom/>
2 // Copyright (C) 2010 Winch Gate Property Limited
4 // This source file has been modified by the following contributors:
5 // Copyright (C) 2015-2020 Jan BOON (Kaetemi) <jan.boon@kaetemi.be>
7 // This program is free software: you can redistribute it and/or modify
8 // it under the terms of the GNU Affero General Public License as
9 // published by the Free Software Foundation, either version 3 of the
10 // License, or (at your option) any later version.
12 // This program is distributed in the hope that it will be useful,
13 // but WITHOUT ANY WARRANTY; without even the implied warranty of
14 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 // GNU Affero General Public License for more details.
17 // You should have received a copy of the GNU Affero General Public License
18 // along with this program. If not, see <http://www.gnu.org/licenses/>.
22 #include "game_share/utils.h"
23 #include "world_map.h"
25 //extern bool simulateBug(int bugId);
28 using namespace NLMISC
;
30 namespace RYAI_MAP_CRUNCH
33 NL_BEGIN_STRING_CONVERSION_TABLE (TAStarFlag
)
34 NL_STRING_CONVERSION_TABLE_ENTRY(Nothing
)
35 NL_STRING_CONVERSION_TABLE_ENTRY(Interior
)
36 NL_STRING_CONVERSION_TABLE_ENTRY(Water
)
37 NL_STRING_CONVERSION_TABLE_ENTRY(NoGo
)
38 NL_STRING_CONVERSION_TABLE_ENTRY(WaterAndNogo
)
39 NL_STRING_CONVERSION_TABLE_ENTRY(GroundFlags
)
40 NL_END_STRING_CONVERSION_TABLE(TAStarFlag
, AStarFlagConversion
, Nothing
)
42 const std::string
& toString(TAStarFlag flag
)
44 return AStarFlagConversion
.toString(flag
);
47 TAStarFlag
toAStarFlag(const std::string
& str
)
49 return AStarFlagConversion
.fromString(str
);
53 //////////////////////////////////////////////////////////////////////////////
54 // Helper classes and data //
55 //////////////////////////////////////////////////////////////////////////////
57 const struct CDirection::CDirectionData
CDirection::directionDatas
[] =
74 const CDirection::TDirection
CDirection::table
[] =
76 CDirection::SW
, CDirection::S
, CDirection::SE
,
77 CDirection::W
, CDirection::UNDEFINED
, CDirection::E
,
78 CDirection::NW
, CDirection::N
, CDirection::NE
81 //////////////////////////////////////////////////////////////////////////////
83 //////////////////////////////////////////////////////////////////////////////
88 CABaseStarNode(uint father
, float distance
, bool open
);
90 void setOpen(bool open
) { _Open
= open
; }
91 bool isOpened() const { return _Open
; }
92 float getDistance() const { return _Distance
; }
93 uint
getFather() const { return _Father
; }
96 uint _Father
; ///< Parent node in the path from the start position
98 bool _Open
; ///< Is the node in the OPEN or CLOSED set?
102 CABaseStarNode::CABaseStarNode(uint father
, float distance
, bool open
)
104 , _Distance(distance
)
109 //////////////////////////////////////////////////////////////////////////////
111 //////////////////////////////////////////////////////////////////////////////
113 class CAStarHeapNode
: public CABaseStarNode
116 explicit CAStarHeapNode(CTopology::TTopologyRef Ref
, uint Father
, float Distance
, bool Open
);
118 CTopology::TTopologyRef
const& getRef() const { return _Ref
; }
121 CTopology::TTopologyRef _Ref
;
125 CAStarHeapNode::CAStarHeapNode(CTopology::TTopologyRef Ref
, uint Father
, float Distance
, bool Open
)
126 : CABaseStarNode(Father
, Distance
, Open
)
131 //////////////////////////////////////////////////////////////////////////////
133 //////////////////////////////////////////////////////////////////////////////
139 explicit CAStarNode(CWorldPosition
const& pos
) :
140 _x(pos
.xCoord().getUnitId()) , _y(pos
.yCoord().getUnitId()), _slot(pos
.slot())
143 CAStarNode(const CAStarNode
& other
) :
144 _x(other
._x
), _y(other
._y
), _slot(other
._slot
)
147 void updateMapPosition(CMapPosition
& _mapPos
) const;
149 bool operator==(CAStarNode
const& other
) const;
150 bool operator!=(CAStarNode
const& other
) const;
151 bool operator<(CAStarNode
const& other
) const;
153 CSlot
const& slot() const { return _slot
; }
162 void CAStarNode::updateMapPosition(CMapPosition
& _mapPos
) const
164 _mapPos
.setUnitId(_x
, _y
);
168 bool CAStarNode::operator==(CAStarNode
const& other
) const
170 return _x
==other
._x
&& _y
==other
._y
&& _slot
==other
._slot
;
174 bool CAStarNode::operator!=(CAStarNode
const& other
) const
176 return _x
!=other
._x
|| _y
!=other
._y
|| _slot
!=other
._slot
;
180 bool CAStarNode::operator<(CAStarNode
const& other
) const
186 return _slot
<other
._slot
;
189 //////////////////////////////////////////////////////////////////////////////
191 //////////////////////////////////////////////////////////////////////////////
193 class CInsideAStarHeapNode
: public CABaseStarNode
196 friend class CAStarNode
;
198 explicit CInsideAStarHeapNode(const CAStarNode
&node
, uint Father
, CDirection Direction
, float Distance
, bool Open
) : CABaseStarNode(Father
,Distance
,Open
), _Direction(Direction
), _Node(node
)
202 inline const CDirection
&getDirection() const
207 inline const CAStarNode
&getNode() const
213 CDirection _Direction
;
218 //////////////////////////////////////////////////////////////////////////////
219 // CDirectionLayer //
220 //////////////////////////////////////////////////////////////////////////////
222 void CDirectionLayer::serial(NLMISC::IStream
& f
)
235 bool present
= (Grid
[i
][j
] != NULL
);
241 Grid
[i
][j
] = I16x16Layer::load(f
);
243 I16x16Layer::save(f
, Grid
[i
][j
]);
249 //////////////////////////////////////////////////////////////////////////////
251 //////////////////////////////////////////////////////////////////////////////
253 void CDirectionMap::serial(NLMISC::IStream
& f
)
264 bool present
= (Layers
[i
] != NULL
);
270 Layers
[i
] = new CDirectionLayer();
272 f
.serial(*Layers
[i
]);
277 //////////////////////////////////////////////////////////////////////////////
279 //////////////////////////////////////////////////////////////////////////////
281 CRootCell
* CRootCell::load(NLMISC::IStream
& f
, CWorldMap
const& worldMap
)
283 TCellType type
= Compute
;
284 CRootCell
*result
= NULL
;
291 result
= new CComputeCell(worldMap
);
292 static_cast<CComputeCell
*>(result
)->serial(f
);
295 result
= new CWhiteCell(worldMap
);
296 static_cast<CWhiteCell
*>(result
)->serial(f
);
299 result
= new CSingleLayerCell(worldMap
);
300 static_cast<CSingleLayerCell
*>(result
)->serial(f
);
303 result
= new CMultiLayerCell(worldMap
);
304 static_cast<CMultiLayerCell
*>(result
)->serial(f
);
308 nlwarning("Unknown type of cell %d to load, abort", type
);
313 // allow us to optimize access.
314 f
.serialCont(result
->_TopologiesNodes
);
319 void CRootCell::save(NLMISC::IStream
& f
, CRootCell
* cell
)
321 if (dynamic_cast<CComputeCell
*>(cell
) != NULL
)
323 TCellType type
= Compute
;
325 static_cast<CComputeCell
*>(cell
)->serial(f
);
327 else if (dynamic_cast<CWhiteCell
*>(cell
) != NULL
)
329 TCellType type
= White
;
331 static_cast<CWhiteCell
*>(cell
)->serial(f
);
333 else if (dynamic_cast<CSingleLayerCell
*>(cell
) != NULL
)
335 TCellType type
= SingleLayer
;
337 static_cast<CSingleLayerCell
*>(cell
)->serial(f
);
339 else if (dynamic_cast<CMultiLayerCell
*>(cell
) != NULL
)
341 TCellType type
= MultiLayer
;
343 static_cast<CMultiLayerCell
*>(cell
)->serial(f
);
348 nlwarning("Unknown type of cell to save, abort");
352 f
.serialCont(cell
->_TopologiesNodes
);
355 //////////////////////////////////////////////////////////////////////////////
357 //////////////////////////////////////////////////////////////////////////////
359 void CComputeCell::serial(NLMISC::IStream
& f
)
362 // 0: initial version
363 uint version
= f
.serialVersion(0);
365 for (uint32 i
=0; i
<16*16; ++i
)
366 for (uint32 k
=0; k
<3; ++k
)
367 f
.serial(_Grid
[i
][k
]);
370 //////////////////////////////////////////////////////////////////////////////
371 // CSingleLayerCell //
372 //////////////////////////////////////////////////////////////////////////////
374 bool CSingleLayerCell::_Initialized
= false;
375 uint16
CSingleLayerCell::_MaskMap
[16];
377 void CSingleLayerCell::serial(NLMISC::IStream
& f
)
379 f
.serialCheck(NELID16("SL"));
394 _Topologies
= I16x16Layer::load(f
);
395 _HeightMap
= I16x16Layer::load(f
);
399 I16x16Layer::save(f
, _Topologies
);
400 I16x16Layer::save(f
, _HeightMap
);
404 //////////////////////////////////////////////////////////////////////////////
405 // CMultiLayerCell //
406 //////////////////////////////////////////////////////////////////////////////
408 void CMultiLayerCell::serial(NLMISC::IStream
& f
)
410 f
.serialCheck(NELID16("ML"));
414 for (slot
=0; slot
<3; ++slot
)
416 // delete layer if any previously
419 if (_Layers
[slot
] != NULL
)
420 delete _Layers
[slot
]->_HeightMap
;
421 delete _Layers
[slot
];
422 _Layers
[slot
] = NULL
;
425 bool present
= (_Layers
[slot
] != NULL
);
432 _Layers
[slot
] = new CCellLayer();
433 _Layers
[slot
]->_HeightMap
= I16x16Layer::load(f
);
437 nlassert(_Layers
[slot
]);
438 I16x16Layer::save(f
, _Layers
[slot
]->_HeightMap
);
441 nlassert(_Layers
[slot
] != NULL
);
443 for (uint32 i
=0; i
<16*16; ++i
)
445 f
.serial(_Layers
[slot
]->_Layer
[i
]);
446 f
.serial(_Layers
[slot
]->_Topology
[i
]);
452 //////////////////////////////////////////////////////////////////////////////
454 //////////////////////////////////////////////////////////////////////////////
456 void CSuperCell::serial(NLMISC::IStream
& f
)
459 // 0: initial version
460 uint version
= f
.serialVersion(0);
464 for (uint32 i
=0; i
<16*16; ++i
)
469 if (_Grid
[i
] != NULL
)
473 _Grid
[i
] = CRootCell::load(f
,_WorldMap
);
478 for (uint32 i
=0; i
<16*16; ++i
)
480 bool present
= (_Grid
[i
] != NULL
);
484 CRootCell::save(f
, _Grid
[i
]);
489 void CSuperCell::updateTopologyRef(CWorldMap
* worldMap
)
491 for (uint32 i
=0; i
<16*16; ++i
)
494 _Grid
[i
]->updateTopologyRef (worldMap
);
498 void CSuperCell::countCells(uint
& compute
, uint
& white
, uint
& simple
, uint
& multi
, uint
& other
) const
500 for (uint32 i
=0; i
<16*16; ++i
)
504 if (dynamic_cast<const CWhiteCell
*>(_Grid
[i
]) != NULL
)
506 else if (dynamic_cast<const CSingleLayerCell
*>(_Grid
[i
]) != NULL
)
508 else if (dynamic_cast<const CComputeCell
*>(_Grid
[i
]) != NULL
)
510 else if (dynamic_cast<const CMultiLayerCell
*>(_Grid
[i
]) != NULL
)
517 //////////////////////////////////////////////////////////////////////////////
519 //////////////////////////////////////////////////////////////////////////////
521 void CWorldMap::getBounds(CMapPosition
& min
, CMapPosition
& max
)
524 uint mini
= 256, maxi
= 0, minj
= 256, maxj
= 0;
526 for (i
=0; i
<256; ++i
)
528 for (j
=0; j
<256; ++j
)
530 if (_GridFastAccess
[i
*256+j
])
532 if (i
< mini
) mini
= i
;
533 if (i
> maxi
) maxi
= i
;
534 if (j
< minj
) minj
= j
;
535 if (j
> maxj
) maxj
= j
;
540 min
= CMapPosition(CMapCoord(minj
, 0, 0), CMapCoord(mini
, 0, 0) );
541 max
= CMapPosition(CMapCoord(maxj
+1, 0, 0), CMapCoord(maxi
+1, 0, 0) );
544 void CWorldMap::clear()
546 for (uint i
=0;i
<65536;i
++)
548 if (_GridFastAccess
[i
])
550 delete _GridFastAccess
[i
];
551 _GridFastAccess
[i
]=NULL
;
559 void CWorldMap::serial(NLMISC::IStream
&f
)
561 f
.serialCheck(NELID("WMAP"));
564 // 0: initial version
565 uint version
= f
.serialVersion(0);
570 for (i
=0;i
<65536;i
++)
577 CSuperCell
*scell
= _GridFastAccess
[i
];
579 _GridFastAccess
[i
] = scell
=new CSuperCell(*this);
585 // made to update RootCell pointers in TTopologyRef ..
586 for (i
=0;i
<65536;i
++)
588 CSuperCell
*scell
= _GridFastAccess
[i
];
590 scell
->updateTopologyRef (this);
593 // made to calculate some random pos ..
595 CMapPosition min
, max
;
597 CMapPosition scan
, scanline
;
598 NLMISC::CRandom random
;
600 for (scan
= min
; scan
.y() != max
.y(); scan
= scan
.stepCell(0, 1))
602 for (scanline
= scan
; scanline
.x() != max
.x(); scanline
= scanline
.stepCell(1, 0))
604 CRootCell
*rootCell
=getRootCell(scanline
);
609 CMapPosition
pos(scanline
.x(),0xffff0000|scanline
.y());
614 for (;ind
<4 && maxTries
>0;maxTries
--)
618 uint i
= uint32(random
.rand()) & 0xf;
619 uint j
= uint32(random
.rand()) & 0xf;
621 nlassert(i
<16 && j
<16);
624 CAIVector vecPos
=CAIVector(pos
);
625 if (setWorldPosition (AITYPES::vp_auto
, wpos
, vecPos
))
628 nlassert(wpos
.getRootCell()==rootCell
);
629 nlassert(wpos
.y()<=0 && wpos
.x()>=0);
631 rootCell
->setWorldPosition(wpos
, ind
);
637 // if we have found some valid positions but not all, fill the array with the last pos found.
642 rootCell
->setWorldPosition(rootCell
->getWorldPosition(ind
-1), ind
);
657 for (uint32 i
=0;i
<65536;i
++)
659 bool present
= (_GridFastAccess
[i
]!=NULL
);
664 //nldebug("Save SuperCell %d/%d", i, j);
665 nlassert(_GridFastAccess
[i
]);
666 f
.serial(*(_GridFastAccess
[i
]));
675 void CWorldMap::setFlagOnPosAndRadius(const CMapPosition
&pos
,float radius
, uint32 flag
)
677 float minx
=pos
.x()-radius
;
678 float maxx
=pos
.x()+radius
;
680 float miny
=pos
.y()-radius
;
681 float maxy
=pos
.y()+radius
;
683 const float radius2
=radius
*radius
;
685 for (float ty
=miny
;ty
<=maxy
;ty
++)
687 const float dy
=ty
-pos
.y();
688 for (float tx
=minx
;tx
<=maxx
;tx
++)
690 const float dx
=tx
-pos
.x();
692 if ((dy
*dy
+dx
*dx
)>radius2
)
695 CRootCell
*rootCell
=getRootCell(CMapPosition((int)tx
,(int)ty
));
698 rootCell
->setFlag(flag
);
705 void CWorldMap::countCells(uint
&compute
, uint
&white
, uint
&simple
, uint
&multi
, uint
&other
) const
707 for (uint32 i
=0; i
<65536; ++i
)
709 if (_GridFastAccess
[i
])
710 _GridFastAccess
[i
]->countCells(compute
, white
, simple
, multi
, other
);
714 // for (i=0; i<256; ++i)
715 // for (j=0; j<256; ++j)
716 // if (_Grid[i][j] != NULL)
717 // _Grid[i][j]->countCells(compute, white, simple, multi, other);
721 CNeighbourhood
CWorldMap::neighbours(const CWorldPosition
&wpos
) const
723 CNeighbourhood neighbs
;
724 const CCellLinkage lnks
=wpos
.getCellLinkage();
726 if (lnks
.isSSlotValid())
728 neighbs
.set(CDirection::S
);
730 const CCellLinkage
&nlnk
= wpos
.getPosS().getCellLinkage(); //CWorldPosition(wpos.getPosS(),lnks.NSlot()));
731 if (nlnk
.isESlotValid())
732 neighbs
.set(CDirection::SE
);
733 if (nlnk
.isWSlotValid())
734 neighbs
.set(CDirection::SW
);
737 if (lnks
.isNSlotValid())
739 neighbs
.set(CDirection::N
);
741 const CCellLinkage
&slnk
= wpos
.getPosN().getCellLinkage();
742 if (slnk
.isESlotValid())
743 neighbs
.set(CDirection::NE
);
744 if (slnk
.isWSlotValid())
745 neighbs
.set(CDirection::NW
);
748 if (lnks
.isESlotValid())
750 neighbs
.set(CDirection::E
);
752 const CCellLinkage
&elnk
= wpos
.getPosE().getCellLinkage();
753 if (elnk
.isSSlotValid())
754 neighbs
.set(CDirection::SE
);
755 if (elnk
.isNSlotValid())
756 neighbs
.set(CDirection::NE
);
759 if (lnks
.isWSlotValid())
761 neighbs
.set(CDirection::W
);
763 const CCellLinkage
&wlnk
= wpos
.getPosW().getCellLinkage();
764 if (wlnk
.isSSlotValid())
765 neighbs
.set(CDirection::SW
);
766 if (wlnk
.isNSlotValid())
767 neighbs
.set(CDirection::NW
);
774 bool CWorldMap::customCheckDiagMove(const CWorldPosition
&pos
, const CDirection
&direction
, TAStarFlag denyFlags
) const
776 H_AUTO(AI_WorldMap_move
);
777 // get straight links
778 const CCellLinkage
&lnk
= pos
.getCellLinkage();
780 switch (direction
.getVal())
785 CWorldPosition
tmpPos(pos
);
786 if (!tmpPos
.getCellLinkage().isSSlotValid())
789 tmpPos
=tmpPos
.getPosS();
790 if ((tmpPos
.getTopologyRef().getCstTopologyNode().getFlags()&denyFlags
)!=0)
793 if (!tmpPos
.getCellLinkage().isESlotValid())
796 tmpPos
=tmpPos
.getPosE();
797 if ((tmpPos
.getTopologyRef().getCstTopologyNode().getFlags()&denyFlags
)!=0)
802 CWorldPosition
tmpPos(pos
);
803 if (!tmpPos
.getCellLinkage().isESlotValid())
806 tmpPos
=tmpPos
.getPosE();
807 if ((tmpPos
.getTopologyRef().getCstTopologyNode().getFlags()&denyFlags
)!=0)
810 if (!tmpPos
.getCellLinkage().isSSlotValid())
813 // tmpPos=tmpPos.getPosS();
814 // if ((tmpPos.getTopologyRef().getCstTopologyNode().getFlags()&denyFlags)!=0)
823 CWorldPosition
tmpPos(pos
);
824 if (!tmpPos
.getCellLinkage().isNSlotValid())
827 tmpPos
=tmpPos
.getPosN();
828 if ((tmpPos
.getTopologyRef().getCstTopologyNode().getFlags()&denyFlags
)!=0)
831 if (!tmpPos
.getCellLinkage().isESlotValid())
834 tmpPos
=tmpPos
.getPosE();
835 if ((tmpPos
.getTopologyRef().getCstTopologyNode().getFlags()&denyFlags
)!=0)
840 CWorldPosition
tmpPos(pos
);
841 if (!tmpPos
.getCellLinkage().isESlotValid())
844 tmpPos
=tmpPos
.getPosE();
845 if ((tmpPos
.getTopologyRef().getCstTopologyNode().getFlags()&denyFlags
)!=0)
848 if (!tmpPos
.getCellLinkage().isNSlotValid())
851 // tmpPos=tmpPos.getPosN();
852 // if ((tmpPos.getTopologyRef().getCstTopologyNode().getFlags()&denyFlags)!=0)
860 CWorldPosition
tmpPos(pos
);
861 if (!tmpPos
.getCellLinkage().isNSlotValid())
864 tmpPos
=tmpPos
.getPosN();
865 if ((tmpPos
.getTopologyRef().getCstTopologyNode().getFlags()&denyFlags
)!=0)
868 if (!tmpPos
.getCellLinkage().isWSlotValid())
871 tmpPos
=tmpPos
.getPosW();
872 if ((tmpPos
.getTopologyRef().getCstTopologyNode().getFlags()&denyFlags
)!=0)
877 CWorldPosition
tmpPos(pos
);
878 if (!tmpPos
.getCellLinkage().isWSlotValid())
881 tmpPos
=tmpPos
.getPosW();
882 if ((tmpPos
.getTopologyRef().getCstTopologyNode().getFlags()&denyFlags
)!=0)
885 if (!tmpPos
.getCellLinkage().isNSlotValid())
888 // tmpPos=tmpPos.getPosN();
889 // if ((tmpPos.getTopologyRef().getCstTopologyNode().getFlags()&denyFlags)!=0)
897 CWorldPosition
tmpPos(pos
);
898 if (!tmpPos
.getCellLinkage().isSSlotValid())
901 tmpPos
=tmpPos
.getPosS();
902 if ((tmpPos
.getTopologyRef().getCstTopologyNode().getFlags()&denyFlags
)!=0)
905 if (!tmpPos
.getCellLinkage().isWSlotValid())
908 tmpPos
=tmpPos
.getPosW();
909 if ((tmpPos
.getTopologyRef().getCstTopologyNode().getFlags()&denyFlags
)!=0)
914 CWorldPosition
tmpPos(pos
);
915 if (!tmpPos
.getCellLinkage().isWSlotValid())
918 tmpPos
=tmpPos
.getPosW();
919 if ((tmpPos
.getTopologyRef().getCstTopologyNode().getFlags()&denyFlags
)!=0)
922 if (!tmpPos
.getCellLinkage().isSSlotValid())
925 // tmpPos=tmpPos.getPosS();
926 // if ((tmpPos.getTopologyRef().getCstTopologyNode().getFlags()&denyFlags)!=0)
940 bool CWorldMap::customCheckDiagMove(const CWorldPosition &pos, const CDirection &direction, TAStarFlag denyFlags) const
942 H_AUTO(AI_WorldMap_move);
943 // get straight links
944 const CCellLinkage &lnk = pos.getCellLinkage();
946 switch (direction.getVal())
950 if (!lnk.isSSlotValid())
952 if (!lnk.isESlotValid())
955 return ( pos.getPosS().getCellLinkage().isESlotValid()
956 || pos.getPosE().getCellLinkage().isSSlotValid());
961 if (!lnk.isNSlotValid())
963 if (!lnk.isESlotValid())
966 return ( pos.getPosN().getCellLinkage().isESlotValid()
967 || pos.getPosE().getCellLinkage().isNSlotValid());
971 if (!lnk.isNSlotValid())
973 if (!lnk.isWSlotValid())
976 return ( pos.getPosN().getCellLinkage().isWSlotValid()
977 || pos.getPosW().getCellLinkage().isNSlotValid());
981 if (!lnk.isSSlotValid())
983 if (!lnk.isWSlotValid())
986 return ( pos.getPosS().getCellLinkage().isWSlotValid()
987 || pos.getPosW().getCellLinkage().isSSlotValid());
996 bool CWorldMap::move(CWorldPosition
&pos
, const CDirection
&direction
) const
998 H_AUTO(AI_WorldMap_move
);
999 // get straight links
1000 const CCellLinkage
&lnk
= pos
.getCellLinkage();
1002 switch (direction
.getVal())
1006 if (lnk
.isSSlotValid())
1013 case CDirection::SE
:
1015 if (lnk
.isSSlotValid())
1017 const CWorldPosition
temp(pos
.getPosS());
1018 if (temp
.getCellLinkage().isESlotValid())
1025 if (lnk
.isESlotValid())
1027 const CWorldPosition
temp(pos
.getPosE());
1028 if (temp
.getCellLinkage().isSSlotValid())
1038 if (lnk
.isESlotValid())
1045 case CDirection::NE
:
1047 if (lnk
.isESlotValid())
1049 const CWorldPosition
temp(pos
.getPosE());
1050 if (temp
.getCellLinkage().isNSlotValid())
1058 if (lnk
.isNSlotValid())
1060 const CWorldPosition
temp(pos
.getPosN());
1061 if (temp
.getCellLinkage().isESlotValid())
1072 if (lnk
.isNSlotValid())
1079 case CDirection::NW
:
1081 if (lnk
.isWSlotValid())
1083 const CWorldPosition
temp(pos
.getPosW());
1084 if (temp
.getCellLinkage().isNSlotValid())
1091 if (lnk
.isNSlotValid())
1093 const CWorldPosition
temp(pos
.getPosN());
1094 if (temp
.getCellLinkage().isWSlotValid())
1105 if (lnk
.isWSlotValid())
1112 case CDirection::SW
:
1114 if (lnk
.isSSlotValid())
1116 const CWorldPosition
temp(pos
.getPosS());
1117 if (temp
.getCellLinkage().isWSlotValid())
1124 if (lnk
.isWSlotValid())
1126 const CWorldPosition
temp(pos
.getPosW());
1127 if (temp
.getCellLinkage().isSSlotValid())
1144 bool CWorldMap::moveSecure(CWorldPosition
&pos
, const CDirection
&direction
, uint16 maskFlags
) const
1146 H_AUTO(AI_WorldMap_moveSecure
);
1147 // get straight links
1149 uint16 pflags
= pos
.getFlags();
1151 switch (direction
.getVal())
1157 case CDirection::SE
:
1159 CWorldPosition
p1(pos
), p2(pos
);
1160 if (p1
.moveS() && (((p1
.getFlags()^pflags
)&maskFlags
) == 0) && p1
.moveE() && (((p1
.getFlags()^pflags
)&maskFlags
) == 0) &&
1161 p2
.moveE() && (((p2
.getFlags()^pflags
)&maskFlags
) == 0) && p2
.moveS() && (((p2
.getFlags()^pflags
)&maskFlags
) == 0) &&
1173 case CDirection::NE
:
1175 CWorldPosition
p1(pos
), p2(pos
);
1176 if (p1
.moveN() && (((p1
.getFlags()^pflags
)&maskFlags
) == 0) && p1
.moveE() && (((p1
.getFlags()^pflags
)&maskFlags
) == 0) &&
1177 p2
.moveE() && (((p2
.getFlags()^pflags
)&maskFlags
) == 0) && p2
.moveN() && (((p2
.getFlags()^pflags
)&maskFlags
) == 0) &&
1189 case CDirection::NW
:
1191 CWorldPosition
p1(pos
), p2(pos
);
1192 if (p1
.moveN() && (((p1
.getFlags()^pflags
)&maskFlags
) == 0) && p1
.moveW() && (((p1
.getFlags()^pflags
)&maskFlags
) == 0) &&
1193 p2
.moveW() && (((p2
.getFlags()^pflags
)&maskFlags
) == 0) && p2
.moveN() && (((p2
.getFlags()^pflags
)&maskFlags
) == 0) &&
1205 case CDirection::SW
:
1207 CWorldPosition
p1(pos
), p2(pos
);
1208 if (p1
.moveS() && (((p1
.getFlags()^pflags
)&maskFlags
) == 0) && p1
.moveW() && (((p1
.getFlags()^pflags
)&maskFlags
) == 0) &&
1209 p2
.moveW() && (((p2
.getFlags()^pflags
)&maskFlags
) == 0) && p2
.moveS() && (((p2
.getFlags()^pflags
)&maskFlags
) == 0) &&
1224 bool CWorldMap::moveDiagTestBothSide(CWorldPosition
&pos
, const CDirection
&direction
) const
1226 H_AUTO(AI_WorldMap_move
);
1227 // get straight links
1228 const CCellLinkage
&lnk
= pos
.getCellLinkage();
1230 switch (direction
.getVal())
1234 if (lnk
.isSSlotValid())
1241 case CDirection::SE
:
1243 if (!lnk
.isSSlotValid() || !lnk
.isESlotValid())
1246 CSlot eSlot
=pos
.getPosS().getCellLinkage().ESlot();
1247 if (!eSlot
.isValid())
1250 const CWorldPosition
temp(pos
.getPosE());
1251 if (temp
.getCellLinkage().SSlot()!=eSlot
)
1259 if (lnk
.isESlotValid())
1266 case CDirection::NE
:
1268 if (!lnk
.isNSlotValid() || !lnk
.isESlotValid())
1271 CSlot eSlot
=pos
.getPosN().getCellLinkage().ESlot();
1272 if (!eSlot
.isValid())
1275 const CWorldPosition
temp(pos
.getPosE());
1276 if (temp
.getCellLinkage().NSlot()!=eSlot
)
1284 if (lnk
.isNSlotValid())
1291 case CDirection::NW
:
1293 if (!lnk
.isNSlotValid() || !lnk
.isWSlotValid())
1296 CSlot wSlot
=pos
.getPosN().getCellLinkage().WSlot();
1297 if (!wSlot
.isValid())
1300 const CWorldPosition
temp(pos
.getPosW());
1301 if (temp
.getCellLinkage().NSlot()!=wSlot
)
1309 if (lnk
.isWSlotValid())
1316 case CDirection::SW
:
1318 if (!lnk
.isSSlotValid() || !lnk
.isWSlotValid())
1321 CSlot wSlot
=pos
.getPosS().getCellLinkage().WSlot();
1322 if (!wSlot
.isValid())
1325 const CWorldPosition
temp(pos
.getPosW());
1326 if (temp
.getCellLinkage().SSlot()!=wSlot
)
1339 void areCompatiblesWithoutStartRestriction(const CWorldPosition
&startPos
, const CWorldPosition
& endPos
, const TAStarFlag
&denyflags
, CCompatibleResult
&res
, bool allowStartRestriction
)
1341 res
.setValid(false);
1343 if (&startPos
== NULL
)
1345 nlwarning("Invalid startPos (NULL)");
1349 if (&endPos
== NULL
)
1351 nlwarning("Invalid endPos (NULL)");
1355 const CTopology
&startTopoNode
=startPos
.getTopologyRef().getCstTopologyNode();
1356 const CTopology
&endTopoNode
=endPos
.getTopologyRef().getCstTopologyNode();
1357 TAStarFlag startFlag
=(TAStarFlag
)(startTopoNode
.getFlags()&WaterAndNogo
);
1358 // if (!allowStartRestriction)
1362 for (TAStarFlag possibleFlag
=Nothing
;possibleFlag
<=WaterAndNogo
;possibleFlag
=(TAStarFlag
)(possibleFlag
+2)) // tricky !! -> to replace with a defined list of flags to checks.
1364 const uint32 incompatibilityFlags
=(possibleFlag
&(denyflags
&~startFlag
))&WaterAndNogo
;
1366 if (incompatibilityFlags
)
1369 const uint32 startMasterTopo
=startTopoNode
.getMasterTopo(possibleFlag
);
1370 const uint32 endMasterTopo
=endTopoNode
.getMasterTopo(possibleFlag
);
1371 if ( (startMasterTopo
^endMasterTopo
)!=0
1372 || startMasterTopo
== std::numeric_limits
<uint32
>::max()) // if not same masterTopo or invalid masterTopo then bypass ..
1375 res
.set(possibleFlag
, startMasterTopo
);
1378 if (((possibleFlag
&denyflags
)&WaterAndNogo
)==0) // it was the optimal case ?
1384 //////////////////////////////////////////////////////////////////////////////
1386 //////////////////////////////////////////////////////////////////////////////
1388 //#define CHECK_HEAP
1389 std::map
<uint
, uint
> MapAStarNbSteps
;
1390 uint LastAStarNbSteps
= 0;
1392 NLMISC_CATEGORISED_COMMAND(ais
, dumpAStarSteps
, "Dump the distribution of A* number of steps", "")
1394 log
.displayNL( "Distribution of the %u nb steps:", MapAStarNbSteps
.size() );
1395 log
.displayNL( "NbSteps\tNbOccurrences", MapAStarNbSteps
.size() );
1396 for( std::map
<uint
, uint
>::const_iterator first
=MapAStarNbSteps
.begin(), last
=MapAStarNbSteps
.end(); first
!=last
; ++first
)
1398 log
.displayNL( "%u\t%u", first
->first
, first
->second
);
1403 bool CWorldMap::findAStarPath(CWorldPosition
const& start
, CWorldPosition
const& end
, std::vector
<CTopology::TTopologyRef
>& path
, TAStarFlag denyflags
) const
1405 H_AUTO(findAStarPath1
);
1407 // Clear destination path
1410 // Check start position validity
1411 if (!start
.isValid())
1413 _LastFASPReason
= FASPR_INVALID_START_POS
;
1416 // Check end position validity
1419 _LastFASPReason
= FASPR_INVALID_END_POS
;
1423 // Get start and end topologies
1424 CTopology::TTopologyRef startTopo
= start
.getTopologyRef();
1425 CTopology::TTopologyRef endTopo
= end
.getTopologyRef();
1427 // Get associated topology nodes
1428 CTopology
const& startTopoNode
= startTopo
.getCstTopologyNode();
1429 CTopology
const& endTopoNode
= endTopo
.getCstTopologyNode();
1431 // Check start point
1432 if (!startTopo
.isValid())
1434 _LastFASPReason
= FASPR_INVALID_START_TOPO
;
1438 if (!endTopo
.isValid())
1440 _LastFASPReason
= FASPR_INVALID_END_TOPO
;
1444 // Check compatibility of start and end points depending on flags to avoid
1445 RYAI_MAP_CRUNCH::CCompatibleResult res
;
1446 areCompatiblesWithoutStartRestriction(start
, end
, denyflags
, res
, true);
1449 _LastFASPReason
= FASPR_INCOMPATIBLE_POSITIONS
;
1453 // Get flags to use to compute the path
1454 TAStarFlag movementFlags
= res
.movementFlags();
1455 // Get the master topology inside which to compute the path (reminder: no path between different master topo)
1456 uint32 choosenMasterTopo
=res
.choosenMasterTopo();
1458 // A list of A* nodes
1459 vector
<CAStarHeapNode
> nodes
;
1460 // List of visited topologies, with associated node in 'nodes' vector
1461 map
<CTopology::TTopologyId
, uint
> visited
;
1463 // The heap used to store A* nodes
1464 // :TODO: Check if STL heap is not better suited, or if another data structure would be more useful in AIS
1465 CHeap
<float, uint
> heap
;
1468 CVector
const& endPoint
= endTopo
.getCstTopologyNode().Position
;
1470 // Create a heap node for the start point
1471 CAStarHeapNode
hnode(startTopo
, 0xffffffff, 0.0f
, true);
1472 // Push it in the node list
1473 nodes
.push_back(hnode
);
1474 // Take it as first father
1475 uint father
= (uint
)nodes
.size()-1;
1477 // Add start topology to visited nodes (father holds start topo node index for the moment)
1478 visited
.insert(std::pair
<CTopology::TTopologyId
, uint
>(startTopo
, father
));
1479 // Push start node in the heap with a zero cost
1480 heap
.push(0.0f
, father
);
1482 // Boolean to notify that end point has been reached
1486 static uint32 maxHeap
= 65535;
1487 static uint32 maxHeapMeasure
= 0;
1490 uint nbHeapSteps
= 0;
1491 while (!heap
.empty())
1494 if (heap
.size()>maxHeap
) // if too much calculs, not found (to remove when debugged).
1500 // Get best node (popping it)
1501 father
= std::numeric_limits
<uint
>::max(); // :TODO: Remove that useless statement (since do while first loop ALWAYS overwrite it)
1504 father
= heap
.pop();
1506 while (!nodes
[father
].isOpened() && !heap
.empty());
1508 if (father
== std::numeric_limits
<uint
>::max())
1511 // Mark current node as closed
1512 hnode
.setOpen(false);
1514 // Make best node the current one
1515 hnode
= nodes
[father
];
1517 // Get the current node itself
1518 CTopology::TTopologyRef
const& current
= hnode
.getRef();
1520 // If we reached the end node, stop search
1521 if (current
==endTopo
)
1527 // Get current node topology
1528 CTopology
const& ctp
= current
.getCstTopologyNode();
1530 // Get g(n) for current node
1531 float dist
= hnode
.getDistance();
1533 // Examine each neighbour of the current node
1534 for (vector
<CTopology::CNeighbourLink
>::const_iterator it
=ctp
.Neighbours
.begin(), itEnd
=ctp
.Neighbours
.end();it
!=itEnd
;++it
)
1536 // Get the neighbour topology node
1537 CTopology::CNeighbourLink
const& neighbourLink
= (*it
);
1538 CTopology::TTopologyRef
const& next
= neighbourLink
.getTopologyRef();
1540 // If it's not in the same master topo skip it
1541 if (next
.getCstTopologyNode().getMasterTopo(movementFlags
)!=choosenMasterTopo
)
1544 // Compute neighbour node g(n)
1545 float distance
= dist
+ neighbourLink
.getDistance();
1548 // Check if node has already been visited
1549 map
<CTopology::TTopologyId
, uint
>::iterator itv
= visited
.find(next
);
1550 if (itv
!=visited
.end())
1552 // Assume child is that node
1553 child
= (*itv
).second
;
1554 // If that node's previous distance is better than the new one skip it
1555 if (nodes
[child
].getDistance() <= distance
)
1558 // Close the old node
1559 nodes
[child
].setOpen(false);
1560 // Remove it from visited
1563 // Create a new node for that cell
1564 child
= (uint
)nodes
.size();
1565 nodes
.push_back(CAStarHeapNode(next
, father
, distance
, true));
1566 // Compute h(n) as an euclidian distance heuristic
1567 float heuristic
= (endPoint
-next
.getCstTopologyNode().Position
).norm();
1568 // Add node to heap with a computed f(n)=g(n)+h(n)
1569 heap
.push(distance
+ heuristic
, child
);
1570 // Add node to visited
1571 visited
.insert(std::pair
<CTopology::TTopologyId
, uint
>(next
, child
));
1576 if (heap
.size()>maxHeapMeasure
)
1578 maxHeapMeasure
=heap
.size();
1582 ++MapAStarNbSteps
[nbHeapSteps
];
1583 LastAStarNbSteps
= nbHeapSteps
;
1590 nlwarning("(!!Appeler StepH!!)Path not found from %s : %d to %s : %d", start
.toString().c_str(), start
.slot(), end
.toString().c_str(), end
.slot());
1594 // If not found, return error
1597 _LastFASPReason
= FASPR_NOT_FOUND
;
1602 while (father
!= 0xffffffff)
1604 CAStarHeapNode
const& node
= nodes
[father
];
1605 path
.push_back(node
.getRef());
1606 father
= node
.getFather();
1609 // Reverse path container
1610 std::reverse(path
.begin(), path
.end());
1612 _LastFASPReason
= FASPR_NO_ERROR
;
1617 bool CWorldMap::findAStarPath(const CTopology::TTopologyId
&start
, const CTopology::TTopologyId
&end
, CAStarPath
&path
, TAStarFlag denyflags
) const
1619 H_AUTO(findAStarPath2
)
1620 path
._TopologiesPath
.clear();
1622 CTopology::TTopologyRef startTopo
= getTopologyRef(start
);
1623 CTopology::TTopologyRef endTopo
= getTopologyRef(end
);
1625 // if not found start point or end point, abort
1626 if (!startTopo
.isValid() || !endTopo
.isValid())
1629 vector
<CAStarHeapNode
> nodes
;
1630 map
<CTopology::TTopologyId
, uint
> visited
; // topology + node index in vector
1631 CHeap
<float, uint
> heap
;
1633 const CVector
&endPoint
= endTopo
.getCstTopologyNode().Position
;
1635 // add current to heap
1636 CAStarHeapNode
hnode(startTopo
,0xffffffff,0.0f
,true);
1638 nodes
.push_back(hnode
);
1639 uint father
= (uint
)nodes
.size()-1;
1641 // add current to visited nodes
1642 visited
.insert(std::pair
<CTopology::TTopologyId
, uint
>(startTopo
, father
));
1643 heap
.push(0.0f
, father
);
1647 while (!heap
.empty())
1650 father
= heap
.pop();
1651 hnode
= nodes
[father
];
1653 const CTopology::TTopologyRef
¤t
= hnode
.getRef();
1655 // if reached end node, leave
1656 if (current
==endTopo
)
1662 const CTopology
&ctp
= current
.getCstTopologyNode();
1663 float dist
= hnode
.getDistance();
1665 for (uint i
=0; i
<ctp
.Neighbours
.size(); ++i
)
1667 const CTopology::CNeighbourLink
&neightBourgLink
=ctp
.Neighbours
[i
];
1669 const CTopology::TTopologyRef
&next
=neightBourgLink
.getTopologyRef();
1671 const CTopology
&ntp
= next
.getCstTopologyNode();
1673 // don't examine not accessible nodes
1674 if ((ntp
.Flags
& denyflags
) != 0)
1677 // compute actual node distance
1678 float distance
= dist
+ neightBourgLink
.getDistance();
1680 // if node is open or closed and previous visit has shorter path, skip node
1681 map
<CTopology::TTopologyId
, uint
>::iterator itv
= visited
.find(next
);
1682 if (itv
!= visited
.end() && nodes
[(*itv
).second
].getDistance() <= distance
)
1685 // compute heuristic
1686 float heuristic
= distance
+ (endPoint
-ntp
.Position
).norm();
1689 CAStarHeapNode
cnode(next
,father
,distance
,true);
1693 if (itv
== visited
.end())
1695 // if node is not open nor closed, create an entry
1696 child
= (uint
)nodes
.size();
1697 nodes
.push_back(cnode
);
1701 // else recover previous entry -- reopen node
1702 child
= (*itv
).second
;
1706 // add node to visited and to heap
1707 heap
.push(heuristic
, child
);
1708 visited
.insert(std::pair
<CTopology::TTopologyId
, uint
>(next
, child
));
1712 // if not found, return error
1717 while (father
!= 0xffffffff)
1719 const CAStarHeapNode
& node
=nodes
[father
];
1721 path
._TopologiesPath
.push_back(node
.getRef());
1722 father
= node
.getFather();
1726 reverse(path
._TopologiesPath
.begin(), path
._TopologiesPath
.end());
1732 // This whole routine MUST be optimized !! Its slow .. (to much).
1733 bool CWorldMap::findInsideAStarPath(CWorldPosition
const& start
, CWorldPosition
const& end
, std::vector
<CDirection
>& stepPath
, TAStarFlag denyflags
) const
1735 H_AUTO(findInsideAStarPath
);
1737 // Check start and end position validity
1738 if (!start
.isValid())
1740 _LastFIASPReason
= FIASPR_INVALID_START_POS
;
1745 _LastFIASPReason
= FIASPR_INVALID_END_POS
;
1748 // Verify that they are in the same topology
1749 if (start
.getTopologyRef()!=end
.getTopologyRef())
1751 _LastFIASPReason
= FIASPR_DIFFERENT_TOPO
;
1755 // A list of A* nodes
1756 vector
<CInsideAStarHeapNode
> nodes
;
1757 // List of visited nodes, with associated node index in 'nodes' vector
1758 map
<CAStarNode
, uint
> visited
;
1759 // The heap used to store A* nodes
1760 // :TODO: Check if STL heap is not better suited, or if another data structure would be more useful in AIS
1761 CHeap
<float, uint
> heap
;
1764 CVector endPoint
= end
.toVectorD();
1765 // Build a node for the end point
1766 CAStarNode
endId(end
);
1767 // Build a node for the start point
1768 CAStarNode
startNode(start
);
1770 // Create a heap node for the start point and push it in the node list
1771 nodes
.push_back(CInsideAStarHeapNode(startNode
, 0xffffffff, CDirection(), 0.f
, true));
1772 // Take it as first father
1773 uint father
= (uint
)nodes
.size()-1;
1775 // Add start node to visited nodes (father holds start node index for the moment)
1776 visited
.insert(std::pair
<CAStarNode
, uint
>(startNode
, father
));
1777 // Push start node in the heap with a zero cost
1778 heap
.push(0.0f
, father
);
1780 // Boolean to notify that end point has been reached
1783 while (!heap
.empty())
1785 // Get best node (popping it)
1786 father
= heap
.pop();
1787 // Get associated heap node
1788 CInsideAStarHeapNode
& hnode
= nodes
[father
];
1789 // Get associated A* node
1790 CAStarNode
const& current
= hnode
.getNode();
1792 // If we reached the end node, stop search
1799 // Get g(n) for current node
1800 float dist
= hnode
.getDistance();
1802 // Get current node slot
1803 CSlot slot
= current
.slot();
1804 // Compute a map position
1805 CMapPosition pos
= start
; // The map has the same cell than the start pos (coz in same topo)
1806 current
.updateMapPosition(pos
); // Just update the unit id
1808 // For each neighbour (8 directions)
1809 CNeighbourhood neighbourhood
= neighbours(getWorldPosition(pos
,slot
));
1810 for (uint i
=0; i
<8; ++i
)
1812 // Compute a CDirection
1813 CDirection
dir((CDirection::TDirection
)i
);
1815 // If neighbour in that direction is not valid skip it
1816 if (!neighbourhood
.isValid(dir
))
1819 // :TODO: Continue documentation
1820 // If we cannot move in that direction skip it
1821 CWorldPosition
mv(getWorldPosition(pos
, slot
));
1822 if (!moveDiagTestBothSide(mv
, dir
))
1825 // If that new point is not in the same cell skip it
1826 if (!mv
.hasSameFullCellId(start
))
1829 // If that point's flags are not compatible skip it
1830 if ((denyflags
& mv
.getTopologyRef().getCstTopologyNode().getFlags()) != 0)
1834 CAStarNode
next(mv
);
1836 // Compute g(n) (diagonal)
1837 float distance
= dist
+ (((i
& 1) != 0) ? 1.4142f
: 1.0f
);
1839 // If node has already been visited and previous distance was better skip it
1840 map
<CAStarNode
, uint
>::iterator itv
= visited
.find(next
);
1841 if ( itv
!= visited
.end()
1842 && nodes
[(*itv
).second
].getDistance() <= distance
)
1846 // If node has already been visited update it
1847 if (itv
!=visited
.end())
1849 child
= (*itv
).second
;
1850 nodes
[child
] = CInsideAStarHeapNode(next
, father
, dir
, distance
, true);
1852 // Else create a new node
1855 child
= (uint
)nodes
.size();
1856 nodes
.push_back(CInsideAStarHeapNode(next
, father
, dir
, distance
, true));
1858 // Compute h(n) as an euclidian distance heuristic
1859 float heuristic
= (endPoint
-mv
.toVectorD()).norm();
1860 // Add node to heap with a computed f(n)=g(n)+h(n)
1861 heap
.push(distance
+ heuristic
, child
);
1862 // Add node to visited
1863 visited
.insert(std::pair
<CAStarNode
, uint
>(next
, child
));
1870 // If not found, return error
1873 _LastFIASPReason
= FIASPR_NOT_FOUND
;
1880 while (father
!= 0xffffffff)
1882 if (nodes
[father
].getFather() != 0xffffffff)
1883 stepPath
.push_back(nodes
[father
].getDirection());
1884 father
= nodes
[father
].getFather();
1887 // Reverse path container
1888 std::reverse(stepPath
.begin(), stepPath
.end());
1890 _LastFIASPReason
= FIASPR_NO_ERROR
;
1894 //////////////////////////////////////////////////////////////////////////////
1896 //////////////////////////////////////////////////////////////////////////////
1898 bool CWorldMap::moveTowards(CWorldPosition
& pos
, CTopology::TTopologyRef
const& topology
) const
1900 CGridDirectionLayer
const* layer
= getGridDirectionLayer(pos
, topology
);
1904 CDirection motion
=layer
->getDirection(pos
);
1905 if (!motion
.isValid())
1908 return move(pos
,motion
);
1911 // Moves according a to a given path, returns false if failed
1912 bool CWorldMap::move(CWorldPosition
&pos
, CAStarPath
&path
, uint
¤tstep
) const
1914 if (currentstep
>= path
._TopologiesPath
.size())
1917 CTopology::TTopologyRef
cid(pos
);
1922 if (cid
==path
._TopologiesPath
[currentstep
])
1925 if (currentstep
==path
._TopologiesPath
.size())
1928 return moveTowards(pos
, path
._TopologiesPath
[currentstep
]);
1932 // Moves from a position to another
1933 bool CWorldMap::move(CWorldPosition
& pos
, CMapPosition
const& end
, TAStarFlag
const denyFlags
) const
1935 CWorldPosition
tempPos(pos
);
1936 BOMB_IF((tempPos
.getTopologyRef().getCstTopologyNode().getFlags()&denyFlags
)!=0, "Error in CWorldMap::mode, invalid flag "<<RYAI_MAP_CRUNCH::toString(tempPos
.getTopologyRef().getCstTopologyNode().getFlags())
1937 <<"on world pos "<<pos
.toString()<<" while going to map pos "<<end
.toString()<<" with denyflags "<<RYAI_MAP_CRUNCH::toString(denyFlags
), return false);
1938 // not optimum but it will be rewrite for each specialized rootcell type.
1939 const sint32 x0
= pos
.x();
1940 const sint32 y0
= pos
.y();
1942 const sint32 deltax
= end
.x() - x0
;
1943 const sint32 deltay
= end
.y() - y0
;
1945 const sint32 d
= std::max(abs(deltax
), abs(deltay
));
1947 for (sint32 i
=1; i
<=d
; ++i
)
1949 const sint dx
= x0
+ (deltax
*i
)/d
- pos
.x();
1950 const sint dy
= y0
+ (deltay
*i
)/d
- pos
.y();
1953 if ( !move(tempPos
, CDirection(dx
,dy
))
1954 || (tempPos
.getTopologyRef().getCstTopologyNode().getFlags()&denyFlags
)!=0) // Arghh !!
1961 // Clears height map
1962 void CWorldMap::clearHeightMap()
1964 CMapPosition min
, max
;
1965 getBounds(min
, max
);
1967 CMapPosition scan
, scanline
;
1969 for (scan
= min
; scan
.yCoord() != max
.yCoord(); scan
= scan
.stepCell(0, 1))
1971 for (scanline
= scan
; scanline
.x() != max
.x(); scanline
= scanline
.stepCell(1, 0))
1973 CRootCell
* rootCell
=getRootCell(scanline
);
1976 rootCell
->clearHeightMap();
1981 // checks motion layers
1982 void CWorldMap::checkMotionLayer()
1984 CMapPosition min
, max
;
1985 getBounds(min
, max
);
1987 uint compute
= 0, white
= 0, simple
= 0, multi
= 0, other
= 0;
1988 countCells(compute
, white
, simple
, multi
, other
);
1989 uint total
= compute
+white
+simple
+multi
+other
;
1992 CMapPosition scan
, scanline
;
1993 CTimeEstimator
timeest(total
);
1995 for (scan
= min
; scan
.yCoord() != max
.yCoord(); scan
= scan
.stepCell(0, 1))
1997 for (scanline
= scan
; scanline
.x() != max
.x(); scanline
= scanline
.stepCell(1, 0))
2000 CRootCell
* rootCell
=getRootCell(scanline
);
2004 timeest
.step("checkMotionLayer");
2006 vector
<CTopology
> &topologies
= rootCell
->getTopologiesNodes();
2008 // move from any point to the current topology
2010 for (i
=0; i
<topologies
.size(); ++i
)
2012 CTopology
&topology
= topologies
[i
];
2013 CTopology::TTopologyRef tref
= getTopologyRef(topology
.Id
);
2014 set
<CTopology::TTopologyId
> neighbours
;
2017 uint16 toflags
= topology
.Flags
;
2020 for (j
=0; j
<topology
.Neighbours
.size(); ++j
)
2021 neighbours
.insert(topology
.Neighbours
[j
].getTopologyRef());
2025 bool failed
= false;
2027 for (y
=-16; y
<32; ++y
)
2029 for (x
=-16; x
<32; ++x
)
2031 CMapPosition
cpos(scanline
.x()+x
, scanline
.y()+y
);
2033 for (slot
=0; slot
<3; ++slot
)
2035 // build world pos and test it is valid
2036 CWorldPosition cwpos
= getSafeWorldPosition(cpos
, CSlot(slot
));
2037 if (!cwpos
.isValid())
2040 // if current topo is not a neighbour of checked topo, go to the next pos
2041 CTopology::TTopologyId ctopoId
= getTopologyId(cwpos
);
2042 if (neighbours
.find(ctopoId
) == neighbours
.end())
2046 CTopology
&starttopo
= getTopologyNode(ctopoId
);
2047 uint16 fromflags
= starttopo
.Flags
;
2049 cwpos
= getWorldPosition(cpos
, CSlot(slot
));
2052 uint32 numSteps
= 0;
2056 if (getTopologyId(cwpos
) == tref
)
2062 CTopology
&ctopo
= getTopologyNode(getTopologyId(cwpos
));
2063 uint16 cflags
= ctopo
.Flags
;
2065 uint16 allow
= ((~fromflags
) & (~toflags
) & cflags
);
2069 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
);
2073 while (moveTowards(cwpos
, tref
) && cwpos
.isValid() && numSteps
++ < 128);
2077 nlwarning("Failed to go from (%d,%d,%d-topo=%08X) to topo %08X", cpos
.x(), cpos
.y(), slot
, ctopoId
.getVal(), tref
.getVal());
2088 nlinfo("---- scanline = %04X %04X topo = %d ----", scanline
.x(), scanline
.y(), i
);
2089 topology
.DirectionMap
->dump();
2101 void CWorldMap::buildMasterTopo(bool allowWater
, bool allowNogo
)
2103 nlinfo("buildMasterTopo");
2105 CMapPosition min
, max
;
2106 getBounds(min
, max
);
2108 CMapPosition scan
, scanline
;
2110 uint masterTopo
= 0;
2111 uint totalTopos
= 0;
2113 for (scan
= min
; scan
.y() != max
.y(); scan
= scan
.stepCell(0, 1))
2115 for (scanline
= scan
; scanline
.x() != max
.x(); scanline
= scanline
.stepCell(1, 0))
2117 CRootCell
*cell
= getRootCell(scanline
);
2121 vector
<CTopology
> &topos
= cell
->getTopologiesNodes();
2124 for (i
=0; i
<topos
.size(); ++i
)
2126 CTopology
& tp
= topos
[i
];
2130 if (!tp
.isCompatible(allowWater
, allowNogo
) ||
2131 tp
.getMasterTopo(allowWater
, allowNogo
) != CTopology::TTopologyId::UNDEFINED_TOPOLOGY
)
2134 set
<CTopology::TTopologyId
> tovisit
;
2136 // set mastertopo id
2137 tp
.getMasterTopoRef(allowWater
, allowNogo
) = masterTopo
;
2138 tovisit
.insert(tp
.Id
);
2140 while (!tovisit
.empty())
2142 CTopology
& t
= getTopologyNode(*(tovisit
.begin()));
2143 tovisit
.erase(tovisit
.begin());
2146 for (j
=0; j
<t
.Neighbours
.size(); ++j
)
2148 CTopology
& neighb
= getTopologyNode(t
.Neighbours
[j
].getTopologyRef());
2150 // can go to the neighbour and neighb not visited yet ?
2151 if (!neighb
.isCompatible(allowWater
, allowNogo
) ||
2152 neighb
.getMasterTopo(allowWater
, allowNogo
) != CTopology::TTopologyId::UNDEFINED_TOPOLOGY
)
2155 // set mastertopo id
2156 neighb
.getMasterTopoRef(allowWater
, allowNogo
) = masterTopo
;
2157 tovisit
.insert(neighb
.Id
);
2164 nlinfo("Built %d master topologies (%d topos tested)", masterTopo
, totalTopos
);
2168 void CWorldMap::countSuperTopo()
2170 nlinfo("Count super topologies");
2172 CMapPosition min
, max
;
2173 getBounds(min
, max
);
2175 CMapPosition scan
, scanline
;
2178 set
<CTopology::TTopologyId
> visited
;
2180 uint totalTopos
= 0;
2182 for (scan
= min
; scan
.y() != max
.y(); scan
= scan
.stepCell(0, 1))
2184 for (scanline
= scan
; scanline
.x() != max
.x(); scanline
= scanline
.stepCell(1, 0))
2186 const CRootCell
*cell
= getRootCellCst(scanline
);
2190 const vector
<CTopology
> &topos
= cell
->getTopologiesNodes();
2193 for (i
=0; i
<topos
.size(); ++i
)
2195 CTopology::TTopologyId topoid
= topos
[i
].Id
;
2199 if (visited
.find(topoid
) == visited
.end())
2201 set
<CTopology::TTopologyId
> tovisit
;
2203 tovisit
.insert(topoid
);
2204 visited
.insert(topoid
);
2206 while (!tovisit
.empty())
2208 CTopology::TTopologyId id
= *(tovisit
.begin());
2209 tovisit
.erase(tovisit
.begin());
2211 const CTopology
&t
= getTopologyNode(id
);
2214 for (j
=0; j
<t
.Neighbours
.size(); ++j
)
2216 CTopology::TTopologyId neighb
= t
.Neighbours
[j
].getTopologyRef();
2217 if (visited
.find(neighb
) == visited
.end())
2219 visited
.insert(neighb
);
2220 tovisit
.insert(neighb
);
2234 nlinfo("Found %d super topologies (%d topos tested, %d topos visited)", superTopo
, totalTopos
, visited
.size());
2237 bool CWorldMap::setWorldPosition(sint32 z
, CWorldPosition
&wpos
, const CAIVector
&pos
, const CRootCell
*originCell
) const
2240 const CMapPosition
mapPos(pos
);
2242 const CRootCell
*cell
= originCell
? originCell
: getRootCellCst(mapPos
);
2248 sint32 minDistZ
= INT_MAX
;
2252 for (uint32 s
=0; s
<3; ++s
)
2254 if (!cell
->isSlotUsed(mapPos
, CSlot(s
)))
2257 CSlot sslot
=CSlot(s
);
2258 sint32 sh
= cell
->getMetricHeight(CWorldPosition(cell
, mapPos
, sslot
));
2260 dist
= dist
<0?-dist
:dist
;
2261 if (dist
< minDistZ
)
2269 if (!bestSlot
.isValid())
2271 wpos
= CWorldPosition(cell
,mapPos
,bestSlot
,true);
2274 wpos
= CWorldPosition(cell
,mapPos
,bestSlot
);
2275 // if (simulateBug(5))
2277 // if (minDistZ > 2000)
2284 // :KLUDGE: Water hack
2285 // If error is too big and player is either not in water or under slot (ie not floating above slot at surface)
2286 if (minDistZ
> 2000 && ((wpos
.getFlags()&RYAI_MAP_CRUNCH::Water
)==0 || z
<bestZ
))
2294 bool CWorldMap::setWorldPosition( double z
, CWorldPosition
&wpos
, const CAIVector
&pos
, const CRootCell
*originCell
) const
2296 nlassert(false && "Double version of setWorldPosition isn't tested !!! Verify it's the same than above sint32 version !");
2298 const CMapPosition
mapPos(pos
);
2300 const CRootCell
*cell
= originCell
? originCell
: getRootCellCst(mapPos
);
2306 double minDistZ
= DBL_MAX
;
2310 for (uint32 s
=0; s
<3; ++s
)
2312 if (!cell
->isSlotUsed(mapPos
, CSlot(s
)))
2315 CSlot sslot
=CSlot(s
);
2316 double sh
= ((double)cell
->getMetricHeight(CWorldPosition(cell
, mapPos
, sslot
)))/1000.0;
2317 double dist
= fabs(z
-sh
);
2318 if (dist
< minDistZ
)
2327 if (!bestSlot
.isValid())
2329 wpos
=CWorldPosition(cell
,mapPos
,bestSlot
,true);
2332 if (minDistZ
> 2.000)
2334 // nldebug("Setting a WorldPosition too far from specified z: x=%d y=%d z=%f slotz=%f", pos.x(), pos.y(), z, bestZ);
2335 wpos
= CWorldPosition(cell
,mapPos
,bestSlot
,true);
2338 wpos
=CWorldPosition(cell
,mapPos
,bestSlot
);