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