1 #include "CPathfinder.h"
2
3 #include <math.h>
4 #include <limits>
5
6 #include "CAI.h"
7 #include "CTaskHandler.h"
8 #include "CGroup.h"
9 #include "CUnit.h"
10 #include "CUnitTable.h"
11 #include "CThreatMap.h"
12 #include "MathUtil.h"
13 #include "Util.hpp"
14 #include "CScopedTimer.h"
15
16 #include "LegacyCpp/IAICallback.h"
17
18
19 std::vector<CPathfinder::Node*> CPathfinder::graph;
20 int CPathfinder::drawPathGraph = -2;
21
CPathfinder(AIClasses * ai)22 CPathfinder::CPathfinder(AIClasses *ai): ARegistrar(600) {
23 this->ai = ai;
24
25 /* NOTE:
26 slopemap = 1/2 heightmap = 1/8 realmap. GetMapWidth() and
27 GetMapHeight() give map dimensions in heightmap resolution.
28 */
29
30 // NOTE: X and Z are in slope map resolution
31 X = ai->cb->GetMapWidth() / SLOPE2HEIGHT;
32 Z = ai->cb->GetMapHeight() / SLOPE2HEIGHT;
33 // NOTE: XX and ZZ are in pathgraph map resolution
34 XX = X / PATH2SLOPE;
35 ZZ = Z / PATH2SLOPE;
36
37 graphSize = XX * ZZ;
38 update = 0;
39 repathGroup = -1;
40 drawPaths = false;
41
42 hm = ai->cb->GetHeightMap();
43 sm = ai->cb->GetSlopeMap();
44
45 if (CPathfinder::graph.empty()) {
46 const std::string cacheVersion(CACHE_VERSION);
47 const std::string modShortName(ai->cb->GetModShortName());
48 const std::string modVersion(ai->cb->GetModVersion());
49 const std::string modHash = util::IntToString(ai->cb->GetModHash(), "%x");
50 const std::string mapName(ai->cb->GetMapName());
51 const std::string mapHash = util::IntToString(ai->cb->GetMapHash(), "%x");
52
53 bool readOk = false;
54 unsigned int N;
55 std::string cacheMarker;
56 std::string filename =
57 std::string(CACHE_FOLDER) + modShortName + "-" + modVersion + "-" +
58 modHash + "/" + mapName + "-" + mapHash + "-graph.bin";
59
60 util::SanitizeFileNameInPlace(filename);
61 filename = util::GetAbsFileName(ai->cb, filename);
62
63 cacheMarker.resize(cacheVersion.size());
64
65 /* See if we can read from binary */
66 std::ifstream fin;
67 fin.open(filename.c_str(), std::ios::binary | std::ios::in);
68 if (fin.good() && fin.is_open()) {
69 LOG_II("CPathfinder reading graph from " << filename)
70 fin.read(&cacheMarker[0], cacheMarker.size());
71 if (!fin.eof() && cacheMarker == cacheVersion) {
72 fin.read((char*)&N, sizeof(N));
73 if(N == graphSize) {
74 for (unsigned int i = 0; i < N; i++) {
75 Node *n = Node::unserialize(fin);
76 CPathfinder::graph.push_back(n);
77 }
78 LOG_II("CPathfinder parsed " << CPathfinder::graph.size() << " nodes")
79 readOk = true;
80 }
81 }
82 fin.close();
83 if(!readOk)
84 LOG_WW("CPathfinder detected invalid cache data")
85 }
86
87 if (!readOk)
88 {
89 std::ofstream fout;
90
91 LOG_II("CPathfinder creating graph at " << filename)
92
93 calcGraph();
94
95 fout.open(filename.c_str(), std::ios::binary | std::ios::out);
96 N = CPathfinder::graph.size();
97 fout.write(&cacheVersion[0], cacheVersion.size());
98 fout.write((char*)&N, sizeof(N));
99 for (unsigned int i = 0; i < N; i++)
100 CPathfinder::graph[i]->serialize(fout);
101 fout.close();
102 }
103 }
104
105 LOG_II("CPathfinder::CPathfinder Heightmap dimensions: " << ai->cb->GetMapWidth() << "x" << ai->cb->GetMapHeight())
106 LOG_II("CPathfinder::CPathfinder Pathmap dimensions: " << XX << "x" << ZZ)
107 }
108
~CPathfinder()109 CPathfinder::~CPathfinder() {
110 if(!ai->isSole())
111 return; // there are another instances of current AI type
112
113 for (unsigned int i = 0; i < CPathfinder::graph.size(); i++)
114 delete CPathfinder::graph[i];
115
116 CPathfinder::graph.clear();
117 }
118
serialize(std::ostream & os)119 void CPathfinder::Node::serialize(std::ostream &os) {
120 char M, N = (char) neighbours.size();
121 os.write((char*)&id, sizeof(unsigned short int));
122 os.write((char*)&x, sizeof(unsigned char));
123 os.write((char*)&z, sizeof(unsigned char));
124
125 os.write((char*)&N, sizeof(char));
126 std::map<int, std::vector<unsigned short int> >::iterator i;
127 for (i = neighbours.begin(); i != neighbours.end(); i++) {
128 M = (char) i->first;
129 os.write((char*)&M, sizeof(char));
130 N = (char) i->second.size();
131 os.write((char*)&N, sizeof(char));
132 for (unsigned int j = 0; j < N; j++)
133 os.write((char*)&(i->second[j]), sizeof(unsigned short int));
134 }
135 }
136
isBlocked(float x,float z,int movetype)137 bool CPathfinder::isBlocked(float x, float z, int movetype) {
138 static const float slope2real = SLOPE2HEIGHT * HEIGHT2REAL;
139
140 int smX, smZ;
141
142 // convert real coordinates into slopemap coordinates...
143 smX = int(round(x / slope2real));
144 smZ = int(round(z / slope2real));
145
146 return isBlocked(smX, smZ, movetype);
147 }
148
isBlocked(int x,int z,int movetype)149 bool CPathfinder::isBlocked(int x, int z, int movetype) {
150 const MoveData* md = ai->unittable->moveTypes[movetype];
151 if (md == NULL)
152 return false;
153
154 // block out of bounds...
155 if (z < 0 || z >= Z || x < 0 || x >= X)
156 return true;
157
158 int smidx = ID(x, z);
159 int hmidx = (z*SLOPE2HEIGHT)*(X*SLOPE2HEIGHT) + (x*SLOPE2HEIGHT);
160
161 /* Block too steep slopes */
162 if (sm[smidx] > md->maxSlope)
163 return true;
164
165 switch(md->moveType) {
166 case MoveData::Ship_Move: {
167 if (-hm[hmidx] < md->depth)
168 return true;
169 } break;
170
171 case MoveData::Ground_Move: {
172 if (-hm[hmidx] > md->depth)
173 return true;
174 } break;
175
176 case MoveData::Hover_Move: {
177 /* can go everywhere */
178 } break;
179 }
180
181 return false;
182 }
183
calcGraph()184 void CPathfinder::calcGraph() {
185 assert(CPathfinder::graph.empty());
186
187 /* Initialize nodes */
188 for (int z = 0; z < ZZ; z++)
189 for (int x = 0; x < XX; x++)
190 CPathfinder::graph.push_back(new Node(ID_GRAPH(x, z), x, z, 1.0f));
191
192 /* Precalculate surrounding nodes */
193 std::vector<int> surrounding;
194 float radius = 0.0f;
195 float threshold = PATH2SLOPE*SLOPE2HEIGHT + EPS;
196 while (radius < threshold) {
197 radius += 1.0f;
198 for (int z = -radius; z <= radius; z++) {
199 for (int x = -radius; x <= radius; x++) {
200 if (x == 0 && z == 0) continue;
201 float length = sqrt(float(x*x + z*z));
202 if (length > (radius - 0.5f) && length < (radius + 0.5f)) {
203 surrounding.push_back(z);
204 surrounding.push_back(x);
205 }
206 }
207 }
208 }
209
210 /* Create the 8 quadrants a node can be in */
211 float r[9]; float angle = 22.5f;
212 for (int g = 0; g < 8; g++) {
213 r[g] = angle;
214 angle += 45.0f;
215 }
216 r[8] = -22.5f;
217
218 /* Define closest neighbours */
219 std::map<int, MoveData*>::iterator k;
220 for (k = ai->unittable->moveTypes.begin(); k != ai->unittable->moveTypes.end(); ++k) {
221 int map = k->first;
222 // NOTE: x & z are in slopemap resolution
223 for (int x = 0; x < X; x += PATH2SLOPE) {
224 for (int z = 0; z < Z; z += PATH2SLOPE) {
225 if (isBlocked(x, z, map))
226 continue;
227
228 Node* parent = CPathfinder::graph[ID_GRAPH(x/PATH2SLOPE, z/PATH2SLOPE)];
229
230 bool s[] = { false, false, false, false, false, false, false, false };
231 for (size_t p = 0; p < surrounding.size(); p += 2) {
232 int i = surrounding[p]; //z
233 int j = surrounding[p + 1]; //x
234
235 int zz = i + z;
236 int xx = j + x;
237
238 if (xx < 0) {s[5] = s[6] = s[7] = true; continue;} // west
239 if (xx > X-1) {s[1] = s[2] = s[3] = true; continue;} // east
240 if (zz < 0) {s[7] = s[0] = s[1] = true; continue;} // north
241 if (zz > Z-1) {s[5] = s[4] = s[3] = true; continue;} // south
242
243 if (
244 (xx % PATH2SLOPE != 0 || zz % PATH2SLOPE != 0)
245 && !isBlocked(xx, zz, map)
246 ) continue;
247
248 float a;
249 if (j == 0) {
250 if (i >= 0)
251 a = 0.5f * M_PI;
252 else
253 a = 1.5f * M_PI;
254 }
255 else if (j > 0) {
256 if (i < 0)
257 a = 2.0f * M_PI + atan(float(i/j));
258 else
259 a = atan(float(i/j));
260 }
261 else
262 a = M_PI + atan(float(i/j));
263
264 a = int((2.5 * M_PI - a) / M_PI * 180) % 360;
265
266 float min = r[8];
267 int index = 0;
268 for (; index < 8; index++) {
269 float max = r[index];
270 if (a > min && a < max)
271 break;
272 min = max;
273 }
274
275 if (!s[index]) {
276 s[index] = true;
277 if (!isBlocked(xx, zz, map))
278 parent->neighbours[map].push_back(ID_GRAPH(xx/PATH2SLOPE, zz/PATH2SLOPE));
279 }
280
281 if (s[0] && s[1] && s[2] && s[3] && s[4] && s[5] && s[6] && s[7]) {
282 break;
283 }
284 }
285 }
286 }
287 }
288 }
289
resetWeights(CGroup & group)290 void CPathfinder::resetWeights(CGroup &group) {
291 if ((group.cats&LAND).any()) {
292 // FIXME: do not consider slope for hovers on water surface
293 int idSlopeMap = 0;
294 for(unsigned int id = 0; id < graphSize; id++) {
295 Node* n = graph[id];
296 n->reset();
297 n->w = 1.0f + sm[idSlopeMap] * 5.0f;
298 idSlopeMap += PATH2SLOPE;
299 }
300 }
301 else {
302 for(unsigned int id = 0; id < graphSize; id++) {
303 Node* n = graph[id];
304 n->reset();
305 n->w = 1.0f;
306 }
307 }
308 }
309
applyThreatMap(ThreatMapType tm_type)310 void CPathfinder::applyThreatMap(ThreatMapType tm_type) {
311 const float* threatMap = ai->threatmap->getMap(tm_type);
312
313 if (threatMap == NULL)
314 return;
315
316 for(unsigned int id = 0; id < graphSize; id++) {
317 graph[id]->w += threatMap[id];
318 }
319 }
320
getPathLength(CGroup & group,float3 & pos)321 float CPathfinder::getPathLength(CGroup &group, float3 &pos) {
322 float result = -1.0f;
323 float3 gpos = group.pos(true);
324
325 if (group.pathType < 0) {
326 float distance = gpos.distance2D(pos);
327
328 if (distance < EPS)
329 result = 0.0f;
330 else {
331 unitCategory cats = group.cats;
332
333 if ((cats&STATIC).any()) {
334 if ((cats&BUILDER).any() && distance < group.buildRange)
335 result = 0.0f;
336 }
337 else if((cats&AIR).any())
338 result = distance;
339 }
340 }
341 else {
342 result = ai->cb->GetPathLength(gpos, pos, group.pathType);
343 }
344
345 return result;
346 }
347
getETA(CGroup & group,float3 & pos)348 float CPathfinder::getETA(CGroup &group, float3 &pos) {
349 float result = getPathLength(group, pos);
350
351 if (result < 0.0f) {
352 result = std::numeric_limits<float>::max(); // pos is unreachable
353 } else if (result > EPS) {
354 if (group.speed > EPS) {
355 result /= group.speed;
356 } else {
357 result = std::numeric_limits<float>::max(); // can't move to remote pos
358 }
359 }
360
361 return result;
362 }
363
updateFollowers()364 void CPathfinder::updateFollowers() {
365 const int currentFrame = ai->cb->GetCurrentFrame();
366
367 unsigned int groupnr = 0;
368 std::map<int, std::vector<float3> >::iterator path;
369 std::map<int, CUnit*>::iterator u;
370 std::map<float, CUnit*> M; // key = <distance>
371
372 repathGroup = -1;
373
374 /* Go through all the paths */
375 for (path = paths.begin(); path != paths.end(); ++path) {
376 CGroup *group = groups[path->first];
377
378 if (group->isMicroing()
379 || (currentFrame - regrouping[group->key]) <= FRAMES_TO_REGROUP) {
380 groupnr++;
381 continue;
382 }
383
384 int segment = 1;
385 int waypoint = std::min<int>((group->cats&AIR).any() ? 2 * MOVE_BUFFER : MOVE_BUFFER, path->second.size() - segment - 1);
386 float maxGroupLength = group->maxLength();
387
388 if (maxGroupLength < 200.0f)
389 maxGroupLength += 200.0f;
390 else
391 maxGroupLength *= 2.0f;
392
393 // NOTE: among aircraft units only Brawler type units (ASSAULT)
394 // can regroup (TODO: detect this by moving behaviour?)
395 bool enableRegrouping =
396 group->units.size() < GROUP_CRITICAL_MASS
397 && ((group->cats&AIR).none() || (group->cats&(ASSAULT|SNIPER|ARTILLERY|ANTIAIR)) == ASSAULT);
398
399 M.clear();
400
401 /* Go through all the units in a group */
402 for (u = group->units.begin(); u != group->units.end(); ++u) {
403 int s1 = 0, s2 = 1;
404 float sl1 = MAX_FLOAT, sl2 = MAX_FLOAT;
405 float l1, l2;
406 float length = 0.0f;
407 CUnit *unit = u->second;
408
409 float3 upos = unit->pos();
410
411 // go through the path to determine the unit's segment
412 // on the path...
413 for (segment = 1; segment < path->second.size() - waypoint; segment++) {
414 if (segment == 1)
415 l1 = upos.distance2D(path->second[segment - 1]);
416 else
417 l1 = l2;
418 l2 = upos.distance2D(path->second[segment]);
419
420 // when the dist between the unit and the segment
421 // is increasing then break...
422 length += (path->second[s1] - path->second[s2]).Length2D();
423 if (l1 > sl1 || l2 > sl2) break;
424 s1 = segment - 1;
425 s2 = segment;
426 sl1 = l1;
427 sl2 = l2;
428 }
429
430 segment--;
431
432 if (enableRegrouping) {
433 /* Now calculate the projection of upos onto the line spanned by
434 * s2-s1 */
435 float3 uP = (path->second[s1] - path->second[s2]);
436 uP.y = 0.0f;
437 uP.SafeNormalize();
438 float3 up = upos - path->second[s2];
439 /* proj_P(x) = (x dot u) * u */
440 float3 uproj = uP * (up.x * uP.x + up.z * uP.z);
441 /* calc pos on total path */
442 float uposonpath = length - uproj.Length2D();
443 /* A map sorts on key (low to high) by default */
444 M[uposonpath] = unit;
445 }
446 }
447
448 /* Regroup when units are getting to much apart from eachother */
449 if (M.size() > 1) {
450 float lateralDisp = M.rbegin()->first - M.begin()->first;
451 if (lateralDisp > maxGroupLength) {
452 regrouping[group->key] = currentFrame;
453 group->updateLatecomer(M.begin()->second);
454 } else if (lateralDisp < maxGroupLength*0.6f) {
455 regrouping[group->key] = 0;
456 }
457 } else {
458 regrouping[group->key] = 0;
459 }
460
461 if (regrouping[group->key] == 0) {
462 /* If not under fine control, advance on the path */
463 group->move(path->second[segment + waypoint]);
464 } else {
465 /* If regrouping, finish that first */
466 float3 gpos = group->pos();
467 group->move(gpos);
468 }
469
470 /* See who will get their path updated by updatePaths() */
471 if (update % paths.size() == groupnr)
472 repathGroup = path->first;
473
474 groupnr++;
475 }
476 }
477
updatePaths()478 void CPathfinder::updatePaths() {
479 update++;
480
481 // nothing to update
482 if (repathGroup < 0)
483 return;
484
485 std::map<int, CGroup*>::iterator it = groups.find(repathGroup);
486 // group is absent
487 if (it == groups.end())
488 return;
489 CGroup* group = it->second;
490
491 // group has no real task
492 if (!group->busy)
493 return;
494
495 // group is not following main path
496 if (group->isMicroing())
497 return;
498
499 float3 start = group->pos(true);
500 float3 goal = ai->tasks->getPos(*group);
501
502 if (!addPath(*group, start, goal)) {
503 LOG_EE("CPathfinder::updatePaths failed for " << (*group))
504 }
505 }
506
remove(ARegistrar & obj)507 void CPathfinder::remove(ARegistrar &obj) {
508 if (groups.find(obj.key) == groups.end())
509 return;
510
511 CGroup *group = dynamic_cast<CGroup*>(&obj);
512 LOG_II("CPathfinder::remove " << (*group))
513 paths.erase(group->key);
514 groups.erase(group->key);
515 regrouping.erase(group->key);
516 group->unreg(*this);
517 }
518
addGroup(CGroup & group)519 bool CPathfinder::addGroup(CGroup &group) {
520 float3 s = group.pos(true);
521 float3 g = ai->tasks->getPos(group);
522 bool success = addPath(group, s, g);
523
524 if (success) {
525 LOG_II("CPathfinder::addGroup " << group)
526 groups[group.key] = &group;
527 regrouping[group.key] = 0;
528 group.reg(*this);
529 }
530
531 return success;
532 }
533
pathExists(CGroup & group,const float3 & s,const float3 & g)534 bool CPathfinder::pathExists(CGroup& group, const float3& s, const float3& g) {
535 activeMap = group.pathType;
536
537 resetWeights(group);
538
539 this->start = getClosestNode(s);
540 this->goal = getClosestNode(g, group.getRange());
541
542 return (start != NULL && goal != NULL && findPath());
543 }
544
addPath(CGroup & group,float3 & start,float3 & goal)545 bool CPathfinder::addPath(CGroup& group, float3& start, float3& goal) {
546 // NOTE: activeMap is used in subsequent calls
547 activeMap = group.pathType;
548
549 // reset node weights...
550 resetWeights(group);
551
552 // apply threatmaps...
553 if ((group.cats&AIR).any())
554 applyThreatMap(TMT_AIR);
555 if ((group.cats&SUB).any())
556 applyThreatMap(TMT_UNDERWATER);
557 // NOTE: hovers (LAND|SEA) can't be hit by underwater weapons that is why
558 // LAND tag check is a priority over SEA below
559 if ((group.cats&LAND).any())
560 applyThreatMap(TMT_SURFACE);
561 else if ((group.cats&SEA).any() && (group.cats&SUB).none())
562 applyThreatMap(TMT_UNDERWATER);
563
564 /* If we found a path, add it */
565 std::vector<float3> path;
566 bool success = getPath(start, goal, path, group);
567 if (success && !path.empty())
568 paths[group.key] = path;
569
570 return success;
571 }
572
getClosestPos(const float3 & f,float radius,CGroup * group)573 float3 CPathfinder::getClosestPos(const float3& f, float radius, CGroup* group) {
574 CPathfinder::Node* node = getClosestNode(f, radius, group);
575 if (node)
576 return node->toFloat3();
577 return ERRORVECTOR;
578 }
579
getClosestNode(const float3 & f,float radius,CGroup * group)580 CPathfinder::Node* CPathfinder::getClosestNode(const float3& f, float radius, CGroup* group) {
581 if(f == ERRORVECTOR)
582 return NULL;
583
584 int pgX = int(round(f.x / PATH2REAL));
585 int pgZ = int(round(f.z / PATH2REAL));
586 // TODO: round pgRadius when searching within circle is implemented below
587 int pgRadius = int(radius / PATH2REAL);
588 int pathType;
589
590 if (group)
591 pathType = group->pathType;
592 else
593 pathType = activeMap;
594
595 Node* bestNode = NULL;
596 float bestScore = std::numeric_limits<float>::max();
597
598 // FIXME: search within circle instead of square
599 for (int i = -pgRadius; i <= pgRadius; i++) {
600 for (int j = -pgRadius; j <= pgRadius; j++) {
601 int x = pgX + j;
602 int z = pgZ + i;
603
604 if (z < 0 || z >= ZZ || x < 0 || x >= XX)
605 continue;
606
607 if (!isBlocked(x * PATH2SLOPE, z * PATH2SLOPE, pathType)) {
608 Node *n = CPathfinder::graph[ID_GRAPH(x, z)];
609 float3 s = n->toFloat3();
610 float dist = (s - f).Length2D();
611 if(dist < bestScore) {
612 bestNode = n;
613 bestScore = dist;
614 }
615 }
616 }
617 }
618
619 if (bestNode == NULL)
620 LOG_EE("CPathfinder::getClosestNode failed to lock node(" << pgX << "," << pgZ << ") for pos("<<f.x<<","<<f.z<<")")
621
622 return bestNode;
623 }
624
getPath(float3 & s,float3 & g,std::vector<float3> & path,CGroup & group)625 bool CPathfinder::getPath(float3 &s, float3 &g, std::vector<float3> &path, CGroup &group) {
626 this->start = getClosestNode(s);
627 this->goal = getClosestNode(g, group.getRange());
628
629 std::list<ANode*> nodepath;
630
631 bool success = (start != NULL && goal != NULL && findPath(&nodepath));
632 if (success) {
633 /* Insert a pre-waypoint at the starting of the path */
634 int waypoint = std::min<int>(4, nodepath.size() - 1);
635 std::list<ANode*>::iterator wp;
636 int x = 0;
637 for (wp = nodepath.begin(); wp != nodepath.end(); ++wp) {
638 if (x >= waypoint) break;
639 x++;
640 }
641
642 float3 ss = dynamic_cast<Node*>(*wp)->toFloat3();
643 float3 seg = s - ss;
644 seg *= 1000.0f; /* Blow up the pre-waypoint */
645 seg += s;
646 seg.y = ai->cb->GetElevation(seg.x, seg.z) + 10.0f;
647 path.push_back(seg);
648
649 for (std::list<ANode*>::iterator i = nodepath.begin(); i != nodepath.end(); ++i) {
650 Node *n = dynamic_cast<Node*>(*i);
651 float3 f = n->toFloat3();
652 f.y = ai->cb->GetElevation(f.x, f.z) + 20.0f;
653 path.push_back(f);
654 }
655 path.push_back(g);
656
657 if (drawPaths) {
658 int life = MULTIPLEXER * path.size(); // in frames
659 for (unsigned i = 2; i < path.size(); i++)
660 ai->cb->CreateLineFigure(path[i - 1], path[i], 8.0f, 0, life, group.key + 10);
661 ai->cb->SetFigureColor(group.key + 10, group.key/float(CGroup::getCounter()), 1.0f-group.key/float(CGroup::getCounter()), 1.0f, 1.0f);
662 }
663 }
664 else {
665 LOG_EE("CPathfinder::getPath failed for " << (group) << " start("<<s.x<<","<<s.z<<") goal("<<g.x<<","<<g.z<<")")
666 }
667
668 return success;
669 }
670
heuristic(ANode * an1,ANode * an2)671 float CPathfinder::heuristic(ANode *an1, ANode *an2) {
672 // tie breaker, gives preference to existing paths
673 static const float p = 1.0f / graphSize + 1.0f;
674 Node *n1 = dynamic_cast<Node*>(an1);
675 Node *n2 = dynamic_cast<Node*>(an2);
676 int dx1 = n1->x - n2->x;
677 int dz1 = n1->z - n2->z;
678 return sqrt(float(dx1*dx1 + dz1*dz1))*p;
679 }
680
successors(ANode * an,std::queue<ANode * > & succ)681 void CPathfinder::successors(ANode *an, std::queue<ANode*> &succ) {
682 std::vector<unsigned short int>& V = dynamic_cast<Node*>(an)->neighbours[activeMap];
683 for (size_t u = 0, N = V.size(); u < N; u++)
684 succ.push(CPathfinder::graph[V[u]]);
685 }
686
pathAssigned(CGroup & group)687 bool CPathfinder::pathAssigned(CGroup &group) {
688 return paths.find(group.key) != paths.end();
689 }
690
switchDebugMode(bool graph)691 bool CPathfinder::switchDebugMode(bool graph) {
692 if (!graph) {
693 drawPaths = !drawPaths;
694 return drawPaths;
695 }
696
697 if (!ai->isMaster())
698 return false;
699
700 if (ai->cb->GetSelectedUnits(&ai->unitIDs[0], 1) == 1) {
701 CUnit *unit = ai->unittable->getUnit(ai->unitIDs[0]);
702 if (unit && (unit->type->cats&STATIC).none()) {
703 int pathType;
704 MoveData *md = unit->def->movedata;
705
706 if (md)
707 pathType = md->pathType;
708 else
709 pathType = -1;
710
711 if (drawPathGraph != pathType) {
712 if (drawPathGraph > -2)
713 ai->cb->DeleteFigureGroup(10);
714 drawGraph(pathType);
715 drawPathGraph = pathType;
716 }
717
718 return true;
719 }
720 }
721
722 if (drawPathGraph > -2) {
723 ai->cb->DeleteFigureGroup(10);
724 drawPathGraph = -2;
725 }
726
727 return false;
728 }
729
drawGraph(int map)730 void CPathfinder::drawGraph(int map) {
731 static const int figureID = 10;
732
733 for (unsigned int i = 0; i < CPathfinder::graph.size(); i++) {
734 Node *p = CPathfinder::graph[i];
735 float3 fp = p->toFloat3();
736 fp.y = ai->cb->GetElevation(fp.x, fp.z) + 50.0f;
737 std::vector<unsigned short>* temp = &p->neighbours[map];
738 for (size_t j = 0; j < temp->size(); j++) {
739 Node *n = CPathfinder::graph[(*temp)[j]];
740 float3 fn = n->toFloat3();
741 fn.y = ai->cb->GetElevation(fn.x, fn.z) + 50.0f;
742 ai->cb->CreateLineFigure(fp, fn, 10.0f, 1, 10000, figureID);
743 ai->cb->SetFigureColor(figureID, 0.0f, 0.0f, 1.0f, 0.5f);
744 }
745 }
746 }
747
drawNode(Node * n)748 void CPathfinder::drawNode(Node *n) {
749 static const int figureID = 11;
750
751 float3 fp = n->toFloat3();
752 float3 fn = fp;
753 fp.y = ai->cb->GetElevation(fp.x, fp.z);
754 fn.y = fp.y + 150.0f;
755 ai->cb->CreateLineFigure(fp, fn, 10.0f, 1, 10000, figureID);
756 ai->cb->SetFigureColor(figureID, 0.0f, 1.0f, 1.0f, 0.5f);
757 }
758