CPathfinder.cpp 7.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340
  1. #include "stdafx.h"
  2. #include "global.h"
  3. #include "CPathfinder.h"
  4. #include "CGameInfo.h"
  5. #include "hch\CAmbarCendamo.h"
  6. #include "mapHandler.h"
  7. #include "CGameState.h"
  8. using namespace std;
  9. vector<Coordinate>* CPathfinder::GetPath(Coordinate start, Coordinate end, const CGHeroInstance* hero)
  10. {
  11. Start = start;
  12. End = end;
  13. return GetPath(hero);
  14. }
  15. /*
  16. * Does basic input checking and setup for the path calculation.
  17. */
  18. vector<Coordinate>* CPathfinder::GetPath(const CGHeroInstance* hero)
  19. {
  20. //check input
  21. if ((Start.x < 0)||(Start.y < 0)||(Start.z < 0)||(End.x < 0)||(End.y < 0)||(End.z < 0))
  22. {
  23. return NULL;
  24. }
  25. if ((Start.x >= CGI->mh->sizes.x)||(Start.y >= CGI->mh->sizes.y)||(Start.z >= CGI->mh->sizes.z)
  26. ||(End.x >= CGI->mh->sizes.x)||(End.y >= CGI->mh->sizes.y)||(End.z >= CGI->mh->sizes.z))
  27. {
  28. return NULL;
  29. }
  30. Hero = hero;
  31. //Reset the queues
  32. Open = priority_queue < vector<Coordinate>, vector<vector<Coordinate>>, Compare>();
  33. Closed.clear();
  34. //Determine if the hero can move on water
  35. int3 hpos = Hero->getPosition(false);
  36. if (!Hero->canWalkOnSea())
  37. {
  38. if (CGI->mh->ttiles[hpos.x][hpos.y][hpos.z].terType==EterrainType::water)
  39. blockLandSea=false;
  40. else
  41. blockLandSea=true;
  42. }
  43. else
  44. {
  45. blockLandSea = boost::logic::indeterminate;
  46. }
  47. CalcG(&Start);
  48. Start.h = 0;
  49. CalcG(&End);
  50. CalcH(&End);
  51. //If it is impossible to get to where the user clicked, dont return a path.
  52. if(End.h == -1)
  53. {
  54. return NULL;
  55. }
  56. //Add the Start node to the open list.
  57. vector<Coordinate> startPath;
  58. startPath.push_back(Start);
  59. Open.push(startPath);
  60. //Calculate the path.
  61. vector<Coordinate>* toReturn = CalcPath();
  62. if(toReturn != NULL)
  63. {
  64. //Flip the route since it is calculated in reverse
  65. int size = toReturn->size();
  66. for(int i = 0; i < size/2; i++)
  67. {
  68. Coordinate t = toReturn->at(i);
  69. (*toReturn)[i] = toReturn->at(size-1-i);
  70. (*toReturn)[size-1-i] = t;
  71. }
  72. }
  73. return toReturn;
  74. }
  75. /*
  76. * Does the actual path calculation. Don't call this directly, call GetPath instead.
  77. */
  78. vector<Coordinate>* CPathfinder::CalcPath()
  79. {
  80. if(Open.empty())
  81. {
  82. return NULL;
  83. }
  84. //Find the path in open with the smallest cost (f = g + h)
  85. //and remove it from open
  86. vector<Coordinate>* branch = new vector<Coordinate>();
  87. *branch = Open.top();
  88. Open.pop();
  89. //Don't search elements in the closed list, for they have been visited already.
  90. if(!ExistsInClosed(branch->back()))
  91. {
  92. //Add it to the closed list.
  93. Closed.push_back(branch->back());
  94. //If it is the destination
  95. if(branch->back().x == End.x && branch->back().y == End.y && branch->back().z == End.z)
  96. {
  97. return branch;
  98. }
  99. //Add neighbors to the open list
  100. AddNeighbors(branch);
  101. }
  102. delete branch;
  103. return CalcPath();
  104. }
  105. /*
  106. * Determines if the given node exists in the closed list, returns true if it does. This is
  107. * used to ensure you dont check the same node twice.
  108. */
  109. bool CPathfinder::ExistsInClosed(Coordinate node)
  110. {
  111. for(int i = 0; i < Closed.size(); i++)
  112. {
  113. if(node.x == Closed[i].x && node.y == Closed[i].y && node.z == Closed[i].z)
  114. return true;
  115. }
  116. return false;
  117. }
  118. /*
  119. * Adds the neighbors of the current node to the open cue so they can be considered in the
  120. * path creation. If the node has a cost (f = g + h) less than zero, it isn't added to Open.
  121. */
  122. void CPathfinder::AddNeighbors(vector<Coordinate>* branch)
  123. {
  124. //8 possible Nodes to add
  125. //
  126. // 1 2 3
  127. // 4 X 5
  128. // 6 7 8
  129. Coordinate node = branch->back();
  130. Coordinate* c;
  131. for(int i = node.x-1; i<node.x+2;i++)
  132. {
  133. for(int j = node.y-1; j < node.y+2; j++)
  134. {
  135. if(i > 0 && j > 0 && i < CGI->mh->sizes.x-1 && j < CGI->mh->sizes.y-1)
  136. {
  137. c = new Coordinate(i,j,node.z);
  138. //Calculate the distance from the end node
  139. CalcG(c);
  140. //Calculate the movement cost
  141. CalcH(c);
  142. if(c->g != -1 && c->h != -1)
  143. {
  144. vector<Coordinate> toAdd = *branch;
  145. toAdd.push_back(*c);
  146. Open.push(toAdd);
  147. }
  148. }
  149. }
  150. }
  151. delete c;
  152. }
  153. /*
  154. * Calculates the movement cost of the node. Returns -1 if it is impossible to travel on.
  155. */
  156. void CPathfinder::CalcH(Coordinate* node)
  157. {
  158. /*
  159. * If the terrain is blocked
  160. * If the terrain is rock
  161. * If the Hero cant walk on water and node is in water
  162. * If the Hero is on water and the node is not.
  163. * If there is fog of war on the node.
  164. * => Impossible to move there.
  165. */
  166. if( (CGI->mh->ttiles[node->x][node->y][node->z].blocked && !(node->x==End.x && node->y==End.y && CGI->mh->ttiles[node->x][node->y][node->z].visitable)) ||
  167. (CGI->mh->ttiles[node->x][node->y][node->z].terType==EterrainType::rock) ||
  168. ((blockLandSea) && (CGI->mh->ttiles[node->x][node->y][node->z].terType==EterrainType::water)) ||
  169. (!CGI->state->players[Hero->tempOwner].fogOfWarMap[node->x][node->y][node->z]) ||
  170. ((!blockLandSea) && (CGI->mh->ttiles[node->x][node->y][node->z].terType!=EterrainType::water)))
  171. {
  172. //Impossible.
  173. node->h = -1;
  174. return;
  175. }
  176. int ret=-1;
  177. int x = node->x;
  178. int y = node->y;
  179. if(node->x>=CGI->mh->map->width)
  180. x = CGI->mh->map->width-1;
  181. if(node->y>=CGI->mh->map->height)
  182. y = CGI->mh->map->height-1;
  183. //Get the movement cost.
  184. ret = Hero->getTileCost(CGI->mh->ttiles[x][y][node->z].terType, CGI->mh->map->terrain[x][y].malle,CGI->mh->map->terrain[x][y].nuine);
  185. node->h = ret;
  186. }
  187. /*
  188. * Calculates distance from node to end node.
  189. */
  190. void CPathfinder::CalcG(Coordinate* node)
  191. {
  192. float dist = (End.x-node->x) * (End.x-node->x) + (End.y-node->y) * (End.y-node->y);
  193. node->g = sqrt(dist);
  194. return;
  195. }
  196. /*
  197. * Converts the old Pathfinder format for compatibility reasons. This should be replaced
  198. * eventually by making the need for it obsolete.
  199. */
  200. CPath* CPathfinder::ConvertToOldFormat(vector<Coordinate>* p)
  201. {
  202. if(p == NULL)
  203. return NULL;
  204. CPath* path = new CPath();
  205. for(int i = 0; i < p->size(); i++)
  206. {
  207. CPathNode temp;
  208. //Set coord
  209. temp.coord = int3(p->at(i).x,p->at(i).y,p->at(i).z);
  210. //Set accesible
  211. if(p->at(i).h == -1)
  212. {
  213. temp.accesible = false;
  214. }
  215. else
  216. {
  217. temp.accesible = true;
  218. }
  219. //set diagonality
  220. float diagonal = 1.0f; //by default
  221. if(i+1<p->size())
  222. {
  223. if(p->at(i+1).x != temp.coord.x && p->at(i+1).y != temp.coord.y)
  224. {
  225. diagonal = sqrt(2.0f);
  226. }
  227. }
  228. //Set distance
  229. if(i == 0)
  230. temp.dist = p->at(i).h * diagonal;
  231. else
  232. temp.dist = p->at(i).h * diagonal + path->nodes.back().dist;
  233. //theNodeBefore is never used outside of pathfinding?
  234. //Set visited
  235. temp.visited = false;
  236. path->nodes.push_back(temp);
  237. }
  238. //YOU ARE ALL BACKWARDS!! =P
  239. //Flip the distances.
  240. for(int i = 0; i < path->nodes.size()/2;i++)
  241. {
  242. int t = path->nodes[i].dist;
  243. path->nodes[i].dist = path->nodes[path->nodes.size()-i-1].dist;
  244. path->nodes[path->nodes.size()-i-1].dist = t;
  245. }
  246. return path;
  247. }
  248. void CPathfinder::convertPath(CPath * path, unsigned int mode) //mode=0 -> from 'manifest' to 'object'
  249. {
  250. if (mode==0)
  251. {
  252. for (int i=0;i<path->nodes.size();i++)
  253. {
  254. path->nodes[i].coord = CGHeroInstance::convertPosition(path->nodes[i].coord,true);
  255. }
  256. }
  257. }
  258. int3 CPath::startPos()
  259. {
  260. return int3(nodes[nodes.size()-1].coord.x,nodes[nodes.size()-1].coord.y,nodes[nodes.size()-1].coord.z);
  261. }
  262. int3 CPath::endPos()
  263. {
  264. return int3(nodes[0].coord.x,nodes[0].coord.y,nodes[0].coord.z);
  265. }
  266. /*
  267. * The function used by the priority cue to determine which node to put at the top.
  268. */
  269. bool Compare::operator()(const vector<Coordinate>& a, const vector<Coordinate>& b)
  270. {
  271. double aTotal=0;
  272. double bTotal=0;
  273. for(int i = 0; i < a.size(); i++)
  274. {
  275. aTotal += a[i].g*a[i].h;
  276. }
  277. for(int i = 0; i < b.size(); i++)
  278. {
  279. bTotal += b[i].g*b[i].h;
  280. }
  281. return aTotal > bTotal;
  282. }
  283. void Coordinate::operator =(const Coordinate &other)
  284. {
  285. this->x = other.x;
  286. this->y = other.y;
  287. this->z = other.z;
  288. this->g = other.g;
  289. this->h = other.h;
  290. }