1 //       _________ __                 __
2 //      /   _____//  |_____________ _/  |______     ____  __ __  ______
3 //      \_____  \\   __\_  __ \__  \\   __\__  \   / ___\|  |  \/  ___/
4 //      /        \|  |  |  | \// __ \|  |  / __ \_/ /_/  >  |  /\___ |
5 //     /_______  /|__|  |__|  (____  /__| (____  /\___  /|____//____  >
6 //             \/                  \/          \//_____/            \/
7 //  ______________________                           ______________________
8 //                        T H E   W A R   B E G I N S
9 //         Stratagus - A free fantasy real time strategy game engine
10 //
11 /**@name astar.cpp - The a* path finder routines. */
12 //
13 //      (c) Copyright 1999-2008 by Lutz Sammer, Fabrice Rossi, Russell Smith,
14 //                                 Francois Beerten, Jimmy Salmon.
15 //
16 //      This program is free software; you can redistribute it and/or modify
17 //      it under the terms of the GNU General Public License as published by
18 //      the Free Software Foundation; only version 2 of the License.
19 //
20 //      This program is distributed in the hope that it will be useful,
21 //      but WITHOUT ANY WARRANTY; without even the implied warranty of
22 //      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
23 //      GNU General Public License for more details.
24 //
25 //      You should have received a copy of the GNU General Public License
26 //      along with this program; if not, write to the Free Software
27 //      Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
28 //      02111-1307, USA.
29 //
30 
31 //@{
32 
33 /*----------------------------------------------------------------------------
34 --  Includes
35 ----------------------------------------------------------------------------*/
36 
37 #include "stratagus.h"
38 
39 #include "map/map.h"
40 #include "map/map_layer.h"
41 #include "map/tileset.h"
42 #include "settings.h"
43 #include "time/time_of_day.h"
44 #include "unit/unit.h"
45 #include "unit/unit_find.h"
46 
47 #include "pathfinder.h"
48 
49 #include <stdio.h>
50 
51 /*----------------------------------------------------------------------------
52 --  Declarations
53 ----------------------------------------------------------------------------*/
54 
55 struct Node {
56 	int CostFromStart;  /// Real costs to reach this point
57 	short int CostToGoal;     /// Estimated cost to goal
58 	char InGoal;        /// is this point in the goal
59 	char Direction;     /// Direction for trace back
60 };
61 
62 struct Open {
63 	Vec2i pos;
64 	short int Costs; /// complete costs to goal
65 	//Wyrmgus start
66 //	unsigned short int O;     /// Offset into matrix
67 	unsigned int O;     /// Offset into matrix
68 	//Wyrmgus end
69 };
70 
71 //for 32 bit signed int
MyAbs(int x)72 inline int MyAbs(int x) { return (x ^ (x >> 31)) - (x >> 31); }
73 
74 /// heuristic cost function for a*
AStarCosts(const Vec2i & pos,const Vec2i & goalPos)75 static inline int AStarCosts(const Vec2i &pos, const Vec2i &goalPos)
76 {
77 	const Vec2i diff = pos - goalPos;
78 	return std::max<int>(MyAbs(diff.x), MyAbs(diff.y));
79 }
80 
81 /*----------------------------------------------------------------------------
82 --  Variables
83 ----------------------------------------------------------------------------*/
84 
85 //  Convert heading into direction.
86 //                      //  N NE  E SE  S SW  W NW
87 const int Heading2X[9] = {  0, +1, +1, +1, 0, -1, -1, -1, 0 };
88 const int Heading2Y[9] = { -1, -1, 0, +1, +1, +1, 0, -1, 0 };
89 //Wyrmgus start
90 //int Heading2O[9];//heading to offset
91 std::vector<int> Heading2O[9];//heading to offset
92 //Wyrmgus end
93 const int XY2Heading[3][3] = { {7, 6, 5}, {0, 0, 4}, {1, 2, 3}};
94 
95 /// cost matrix
96 //Wyrmgus start
97 //static Node *AStarMatrix;
98 static std::vector<Node *> AStarMatrix;
99 //Wyrmgus end
100 
101 /// a list of close nodes, helps to speed up the matrix cleaning
102 //Wyrmgus start
103 //static int *CloseSet;
104 //static int CloseSetSize;
105 //static int Threshold;
106 //static int OpenSetMaxSize;
107 //static int AStarMatrixSize;
108 static std::vector<int *> CloseSet;
109 static std::vector<int> CloseSetSize;
110 static std::vector<int> Threshold;
111 static std::vector<int> OpenSetMaxSize;
112 static std::vector<int> AStarMatrixSize;
113 //Wyrmgus end
114 #define MAX_CLOSE_SET_RATIO 4
115 #define MAX_OPEN_SET_RATIO 8 // 10,16 to small
116 
117 /// see pathfinder.h
118 int AStarFixedUnitCrossingCost;// = MaxMapWidth * MaxMapHeight;
119 int AStarMovingUnitCrossingCost = 5;
120 bool AStarKnowUnseenTerrain = false;
121 int AStarUnknownTerrainCost = 2;
122 
123 //Wyrmgus start
124 //static int AStarMapWidth;
125 //static int AStarMapHeight;
126 static std::vector<int> AStarMapWidth;
127 static std::vector<int> AStarMapHeight;
128 //Wyrmgus end
129 
130 static int AStarGoalX;
131 static int AStarGoalY;
132 
133 /**
134 **  The Open set is handled by a stored array
135 **  the end of the array holds the item with the smallest cost.
136 */
137 
138 /// The set of Open nodes
139 //Wyrmgus start
140 //static Open *OpenSet;
141 static std::vector<Open *> OpenSet;
142 //Wyrmgus end
143 /// The size of the open node set
144 //Wyrmgus start
145 //static int OpenSetSize;
146 static std::vector<int> OpenSetSize;
147 //Wyrmgus end
148 
149 //Wyrmgus start
150 //static int *CostMoveToCache;
151 static std::vector<int *> CostMoveToCache;
152 //Wyrmgus end
153 static const int CacheNotSet = -5;
154 
155 /*----------------------------------------------------------------------------
156 --  Profile
157 ----------------------------------------------------------------------------*/
158 
159 #ifdef ASTAR_PROFILE
160 
161 #include <map>
162 #ifdef USE_WIN32
163 #include <windows.h>
164 #else
165 
166 union LARGE_INTEGER {
167 	uint64_t QuadPart;
168 	uint32_t DoublePart[2];
169 };
QueryPerformanceCounter(LARGE_INTEGER * ptr)170 inline int QueryPerformanceCounter(LARGE_INTEGER *ptr)
171 {
172 	unsigned int lo, hi;
173 	__asm__ __volatile__(       // serialize
174 		"xorl %%eax,%%eax \n        cpuid"
175 		::: "%rax", "%rbx", "%rcx", "%rdx");
176 	/* We cannot use "=A", since this would use %rax on x86_64 */
177 	__asm__ __volatile__("rdtsc" : "=a"(lo), "=d"(hi));
178 	ptr->DoublePart[0] = lo;
179 	ptr->DoublePart[1] = hi;
180 	return 1;
181 };
182 
QueryPerformanceFrequency(LARGE_INTEGER * ptr)183 inline int QueryPerformanceFrequency(LARGE_INTEGER *ptr)
184 {
185 	ptr->QuadPart = 1000;
186 	return 1;
187 }
188 
189 #endif
190 
191 #undef max
192 #undef min
193 static std::map<const char *const, LARGE_INTEGER> functionTimerMap;
194 struct ProfileData {
195 	unsigned long Calls;
196 	unsigned long TotalTime;
197 };
198 static std::map<const char *const, ProfileData> functionProfiles;
199 
ProfileInit()200 inline void ProfileInit()
201 {
202 	functionTimerMap.clear();
203 	functionProfiles.clear();
204 }
205 
ProfileBegin(const char * const function)206 inline void ProfileBegin(const char *const function)
207 {
208 	LARGE_INTEGER counter;
209 	if (!QueryPerformanceCounter(&counter)) {
210 		return;
211 	}
212 	functionTimerMap[function] = counter;
213 }
214 
ProfileEnd(const char * const function)215 inline void ProfileEnd(const char *const function)
216 {
217 	LARGE_INTEGER counter;
218 	if (!QueryPerformanceCounter(&counter)) {
219 		return;
220 	}
221 	unsigned long time = (unsigned long)(counter.QuadPart - functionTimerMap[function].QuadPart);
222 	ProfileData *data = &functionProfiles[function];
223 	data->Calls++;
224 	data->TotalTime += time;
225 }
226 
compProfileData(const ProfileData * lhs,const ProfileData * rhs)227 static bool compProfileData(const ProfileData *lhs, const ProfileData *rhs)
228 {
229 	return (lhs->TotalTime > rhs->TotalTime);
230 }
231 
232 
ProfilePrint()233 inline void ProfilePrint()
234 {
235 	LARGE_INTEGER frequency;
236 	if (!QueryPerformanceFrequency(&frequency)) {
237 		return;
238 	}
239 	std::vector<ProfileData *> prof;
240 	for (std::map<const char *const, ProfileData>::iterator i = functionProfiles.begin();
241 		 i != functionProfiles.end(); ++i) {
242 		ProfileData *data = &i->second;
243 		prof.insert(std::upper_bound(prof.begin(), prof.end(), data, compProfileData), data);
244 	}
245 
246 	FILE *fd = fopen("profile.txt", "wb");
247 	fprintf(fd, "    total\t    calls\t      per\tname\n");
248 
249 	for (std::vector<ProfileData *>::iterator i = prof.begin(); i != prof.end(); ++i) {
250 		ProfileData *data = (*i);
251 		fprintf(fd, "%9.3f\t%9lu\t%9.3f\t",
252 				(double)data->TotalTime / frequency.QuadPart * 1000.0,
253 				data->Calls,
254 				(double)data->TotalTime / frequency.QuadPart * 1000.0 / data->Calls);
255 		for (std::map<const char *const, ProfileData>::iterator j =
256 				 functionProfiles.begin(); j != functionProfiles.end(); ++j) {
257 			ProfileData *data2 = &j->second;
258 			if (data == data2) {
259 				fprintf(fd, "%s\n", j->first);
260 			}
261 		}
262 
263 	}
264 
265 	fclose(fd);
266 }
267 
268 #else
269 #define ProfileInit()
270 #define ProfileBegin(f)
271 #define ProfileEnd(f)
272 #define ProfilePrint()
273 #endif
274 
275 /*----------------------------------------------------------------------------
276 --  Functions
277 ----------------------------------------------------------------------------*/
278 
279 /**
280 **  Init A* data structures
281 */
282 //Wyrmgus start
283 //void InitAStar(int mapWidth, int mapHeight)
InitAStar()284 void InitAStar()
285 //Wyrmgus end
286 {
287 	//Wyrmgus start
288 	/*
289 	// Should only be called once
290 	Assert(!AStarMatrix);
291 
292 	AStarMapWidth = mapWidth;
293 	AStarMapHeight = mapHeight;
294 
295 	AStarMatrixSize = sizeof(Node) * AStarMapWidth * AStarMapHeight;
296 	AStarMatrix = new Node[AStarMapWidth * AStarMapHeight];
297 	memset(AStarMatrix, 0, AStarMatrixSize);
298 
299 	Threshold = AStarMapWidth * AStarMapHeight / MAX_CLOSE_SET_RATIO;
300 	CloseSet = new int[Threshold];
301 
302 	OpenSetMaxSize = AStarMapWidth * AStarMapHeight / MAX_OPEN_SET_RATIO;
303 	OpenSet = new Open[OpenSetMaxSize];
304 
305 	CostMoveToCache = new int[AStarMapWidth * AStarMapHeight];
306 
307 	for (int i = 0; i < 9; ++i) {
308 		Heading2O[i] = Heading2Y[i] * AStarMapWidth;
309 	}
310 	*/
311 
312 	for (size_t z = 0; z < Map.MapLayers.size(); ++z) {
313 		// Should only be called once
314 		Assert(AStarMatrix.size() <= z);
315 
316 		AStarMapWidth.push_back(Map.Info.MapWidths[z]);
317 		AStarMapHeight.push_back(Map.Info.MapHeights[z]);
318 
319 		AStarMatrixSize.push_back(sizeof(Node) * AStarMapWidth[z] * AStarMapHeight[z]);
320 		AStarMatrix.push_back(new Node[AStarMapWidth[z] * AStarMapHeight[z]]);
321 		memset(AStarMatrix[z], 0, AStarMatrixSize[z]);
322 
323 		Threshold.push_back(AStarMapWidth[z] * AStarMapHeight[z] / MAX_CLOSE_SET_RATIO);
324 		CloseSet.push_back(new int[Threshold[z]]);
325 		CloseSetSize.push_back(0);
326 
327 		OpenSetMaxSize.push_back(AStarMapWidth[z] * AStarMapHeight[z] / MAX_OPEN_SET_RATIO);
328 		OpenSet.push_back(new Open[OpenSetMaxSize[z]]);
329 		OpenSetSize.push_back(0);
330 
331 		CostMoveToCache.push_back(new int[AStarMapWidth[z] * AStarMapHeight[z]]);
332 
333 		for (int i = 0; i < 9; ++i) {
334 			Heading2O[i].push_back(Heading2Y[i] * AStarMapWidth[z]);
335 		}
336 	}
337 	//Wyrmgus end
338 
339 	ProfileInit();
340 }
341 
342 /**
343 **  Free A* data structure
344 */
FreeAStar()345 void FreeAStar()
346 {
347 	//Wyrmgus start
348 	AStarMapWidth.clear();
349 	AStarMapHeight.clear();
350 	//Wyrmgus end
351 	//Wyrmgus start
352 	/*
353 	delete[] AStarMatrix;
354 	AStarMatrix = nullptr;
355 	delete[] CloseSet;
356 	CloseSet = nullptr;
357 	CloseSetSize = 0;
358 	delete[] OpenSet;
359 	OpenSet = nullptr;
360 	OpenSetSize = 0;
361 	delete[] CostMoveToCache;
362 	CostMoveToCache = nullptr;
363 	*/
364 	for (size_t z = 0; z < AStarMatrix.size(); ++z) {
365 		delete[] AStarMatrix[z];
366 		AStarMatrix[z] = nullptr;
367 	}
368 	AStarMatrix.clear();
369 	AStarMatrixSize.clear();
370 	Threshold.clear();
371 	for (size_t z = 0; z < CloseSet.size(); ++z) {
372 		delete[] CloseSet[z];
373 		CloseSet[z] = nullptr;
374 	}
375 	CloseSet.clear();
376 	CloseSetSize.clear();
377 	for (size_t z = 0; z < OpenSet.size(); ++z) {
378 		delete[] OpenSet[z];
379 		OpenSet[z] = nullptr;
380 	}
381 	OpenSet.clear();
382 	OpenSetSize.clear();
383 	OpenSetMaxSize.clear();
384 	for (size_t z = 0; z < CostMoveToCache.size(); ++z) {
385 		delete[] CostMoveToCache[z];
386 		CostMoveToCache[z] = nullptr;
387 	}
388 	CostMoveToCache.clear();
389 
390 	for (int i = 0; i < 9; ++i) {
391 		Heading2O[i].clear();
392 	}
393 	//Wyrmgus end
394 
395 	ProfilePrint();
396 }
397 
398 /**
399 **  Prepare pathfinder.
400 */
401 //Wyrmgus start
402 //static void AStarPrepare()
AStarPrepare(int z)403 static void AStarPrepare(int z)
404 //Wyrmgus end
405 {
406 	//Wyrmgus start
407 //	memset(AStarMatrix, 0, AStarMatrixSize);
408 	memset(AStarMatrix[z], 0, AStarMatrixSize[z]);
409 	//Wyrmgus end
410 }
411 
412 /**
413 **  Clean up A*
414 */
415 //Wyrmgus start
416 //static void AStarCleanUp()
AStarCleanUp(int z)417 static void AStarCleanUp(int z)
418 //Wyrmgus end
419 {
420 	ProfileBegin("AStarCleanUp");
421 
422 	//Wyrmgus start
423 //	if (CloseSetSize >= Threshold) {
424 	if (CloseSetSize[z] >= Threshold[z]) {
425 	//Wyrmgus end
426 		//Wyrmgus start
427 //		AStarPrepare();
428 		AStarPrepare(z);
429 		//Wyrmgus end
430 	} else {
431 		for (int i = 0; i < CloseSetSize[z]; ++i) {
432 			//Wyrmgus start
433 //			AStarMatrix[CloseSet[i]].CostFromStart = 0;
434 //			AStarMatrix[CloseSet[i]].InGoal = 0;
435 			AStarMatrix[z][CloseSet[z][i]].CostFromStart = 0;
436 			AStarMatrix[z][CloseSet[z][i]].InGoal = 0;
437 			//Wyrmgus end
438 		}
439 	}
440 	ProfileEnd("AStarCleanUp");
441 }
442 
443 //Wyrmgus start
444 //static void CostMoveToCacheCleanUp()
CostMoveToCacheCleanUp(int z)445 static void CostMoveToCacheCleanUp(int z)
446 //Wyrmgus end
447 {
448 	ProfileBegin("CostMoveToCacheCleanUp");
449 	//Wyrmgus start
450 //	int AStarMapMax =  AStarMapWidth * AStarMapHeight;
451 	int AStarMapMax =  AStarMapWidth[z] * AStarMapHeight[z];
452 	//Wyrmgus end
453 #if 1
454 	//Wyrmgus start
455 //	int *ptr = CostMoveToCache;
456 	int *ptr = CostMoveToCache[z];
457 	//Wyrmgus end
458 #ifdef __x86_64__
459 	union {
460 		intptr_t d;
461 		int i[2];
462 	} conv;
463 	conv.i[0] = CacheNotSet;
464 	conv.i[1] = CacheNotSet;
465 
466 	if (((uintptr_t)ptr) & 4) {
467 		*ptr++ = CacheNotSet;
468 		--AStarMapMax;
469 	}
470 #endif
471 	while (AStarMapMax > 3) {
472 #ifdef __x86_64__
473 		*((intptr_t *)ptr) = conv.d;
474 		*((intptr_t *)(ptr + 2)) = conv.d;
475 		ptr += 4;
476 #else
477 		*ptr++ = CacheNotSet;
478 		*ptr++ = CacheNotSet;
479 		*ptr++ = CacheNotSet;
480 		*ptr++ = CacheNotSet;
481 #endif
482 		AStarMapMax -= 4;
483 	};
484 	while (AStarMapMax) {
485 		*ptr++ = CacheNotSet;
486 		--AStarMapMax;
487 	}
488 #else
489 	for (int i = 0; i < AStarMapMax; ++i) {
490 		//Wyrmgus start
491 //		CostMoveToCache[i] = CacheNotSet;
492 		CostMoveToCache[z][i] = CacheNotSet;
493 		//Wyrmgus end
494 	}
495 #endif
496 	ProfileEnd("CostMoveToCacheCleanUp");
497 }
498 
499 /**
500 **  Find the best node in the current open node set
501 **  Returns the position of this node in the open node set
502 */
503 //Wyrmgus start
504 //#define AStarFindMinimum() (OpenSetSize - 1)
505 #define AStarFindMinimum(z) (OpenSetSize[(z)] - 1)
506 //Wyrmgus end
507 
508 /**
509 **  Remove the minimum from the open node set
510 */
511 //Wyrmgus start
512 //static void AStarRemoveMinimum(int pos)
AStarRemoveMinimum(int pos,int z)513 static void AStarRemoveMinimum(int pos, int z)
514 //Wyrmgus end
515 {
516 	//Wyrmgus start
517 //	Assert(pos == OpenSetSize - 1);
518 	Assert(pos == OpenSetSize[z] - 1);
519 	//Wyrmgus end
520 
521 	//Wyrmgus start
522 //	OpenSetSize--;
523 	OpenSetSize[z]--;
524 	//Wyrmgus end
525 }
526 
527 /**
528 **  Add a new node to the open set (and update the heap structure)
529 **
530 **  @return  0 or PF_FAILED
531 */
532 //Wyrmgus start
533 //static inline int AStarAddNode(const Vec2i &pos, int o, int costs)
AStarAddNode(const Vec2i & pos,int o,int costs,int z)534 static inline int AStarAddNode(const Vec2i &pos, int o, int costs, int z)
535 //Wyrmgus end
536 {
537 	ProfileBegin("AStarAddNode");
538 
539 	//Wyrmgus start
540 //	int bigi = 0, smalli = OpenSetSize;
541 	int bigi = 0, smalli = OpenSetSize[z];
542 	//Wyrmgus end
543 	int midcost;
544 	int midi;
545 	int midCostToGoal;
546 	int midDist;
547 	const Open *open;
548 
549 	//Wyrmgus start
550 //	if (OpenSetSize + 1 >= OpenSetMaxSize) {
551 	if (OpenSetSize[z] + 1 >= OpenSetMaxSize[z]) {
552 	//Wyrmgus end
553 		fprintf(stderr, "A* internal error: raise Open Set Max Size "
554 				//Wyrmgus start
555 //				"(current value %d)\n", OpenSetMaxSize);
556 				"(current value %d)\n", OpenSetMaxSize[z]);
557 				//Wyrmgus end
558 		ProfileEnd("AStarAddNode");
559 		return PF_FAILED;
560 	}
561 
562 	//Wyrmgus start
563 //	const int costToGoal = AStarMatrix[o].CostToGoal;
564 	const int costToGoal = AStarMatrix[z][o].CostToGoal;
565 	//Wyrmgus end
566 	const int dist = MyAbs(pos.x - AStarGoalX) + MyAbs(pos.y - AStarGoalY);
567 
568 	// find where we should insert this node.
569 	// binary search where to insert the new node
570 	while (bigi < smalli) {
571 		midi = (smalli + bigi) >> 1;
572 		//Wyrmgus start
573 //		open = &OpenSet[midi];
574 		open = &OpenSet[z][midi];
575 		//Wyrmgus end
576 		midcost = open->Costs;
577 		//Wyrmgus start
578 //		midCostToGoal = AStarMatrix[open->O].CostToGoal;
579 		midCostToGoal = AStarMatrix[z][open->O].CostToGoal;
580 		//Wyrmgus end
581 		midDist = MyAbs(open->pos.x - AStarGoalX) + MyAbs(open->pos.y - AStarGoalY);
582 		if (costs > midcost || (costs == midcost
583 								&& (costToGoal > midCostToGoal || (costToGoal == midCostToGoal
584 																   && dist > midDist)))) {
585 			smalli = midi;
586 		} else if (costs < midcost || (costs == midcost
587 									   && (costToGoal < midCostToGoal || (costToGoal == midCostToGoal
588 											   && dist < midDist)))) {
589 			if (bigi == midi) {
590 				bigi++;
591 			} else {
592 				bigi = midi;
593 			}
594 		} else {
595 			bigi = midi;
596 			smalli = midi;
597 		}
598 	}
599 
600 	//Wyrmgus start
601 //	if (OpenSetSize > bigi) {
602 	if (OpenSetSize[z] > bigi) {
603 	//Wyrmgus end
604 		// free a the slot for our node
605 		//Wyrmgus start
606 //		memmove(&OpenSet[bigi + 1], &OpenSet[bigi], (OpenSetSize - bigi) * sizeof(Open));
607 		memmove(&OpenSet[z][bigi + 1], &OpenSet[z][bigi], (OpenSetSize[z] - bigi) * sizeof(Open));
608 		//Wyrmgus end
609 	}
610 
611 	// fill our new node
612 	//Wyrmgus start
613 //	OpenSet[bigi].pos = pos;
614 //	OpenSet[bigi].O = o;
615 //	OpenSet[bigi].Costs = costs;
616 //	++OpenSetSize;
617 	OpenSet[z][bigi].pos = pos;
618 	OpenSet[z][bigi].O = o;
619 	OpenSet[z][bigi].Costs = costs;
620 	++OpenSetSize[z];
621 	//Wyrmgus end
622 
623 	ProfileEnd("AStarAddNode");
624 
625 	return 0;
626 }
627 
628 /**
629 **  Change the cost associated to an open node.
630 **  Can be further optimised knowing that the new cost MUST BE LOWER
631 **  than the old one.
632 */
633 //Wyrmgus start
634 //static void AStarReplaceNode(int pos)
AStarReplaceNode(int pos,int z)635 static void AStarReplaceNode(int pos, int z)
636 //Wyrmgus end
637 {
638 	ProfileBegin("AStarReplaceNode");
639 
640 	Open node;
641 
642 	// Remove the outdated node
643 	//Wyrmgus start
644 //	node = OpenSet[pos];
645 //	OpenSetSize--;
646 //	memmove(&OpenSet[pos], &OpenSet[pos+1], sizeof(Open) * (OpenSetSize-pos));
647 	node = OpenSet[z][pos];
648 	OpenSetSize[z]--;
649 	memmove(&OpenSet[z][pos], &OpenSet[z][pos+1], sizeof(Open) * (OpenSetSize[z]-pos));
650 	//Wyrmgus end
651 
652 	// Re-add the node with the new cost
653 	//Wyrmgus start
654 //	AStarAddNode(node.pos, node.O, node.Costs);
655 	AStarAddNode(node.pos, node.O, node.Costs, z);
656 	//Wyrmgus end
657 	ProfileEnd("AStarReplaceNode");
658 }
659 
660 
661 /**
662 **  Check if a node is already in the open set.
663 **
664 **  @return  -1 if not found and the position of the node in the table if found.
665 */
666 //Wyrmgus start
667 //static int AStarFindNode(int eo)
AStarFindNode(int eo,int z)668 static int AStarFindNode(int eo, int z)
669 //Wyrmgus end
670 {
671 	ProfileBegin("AStarFindNode");
672 
673 	//Wyrmgus start
674 //	for (int i = 0; i < OpenSetSize; ++i) {
675 	for (int i = 0; i < OpenSetSize[z]; ++i) {
676 	//Wyrmgus end
677 		//Wyrmgus start
678 //		if (OpenSet[i].O == eo) {
679 		if (OpenSet[z][i].O == eo) {
680 		//Wyrmgus end
681 			ProfileEnd("AStarFindNode");
682 			return i;
683 		}
684 	}
685 	ProfileEnd("AStarFindNode");
686 	return -1;
687 }
688 
689 /**
690 **  Add a node to the closed set
691 */
692 //Wyrmgus start
693 //static void AStarAddToClose(int node)
AStarAddToClose(int node,int z)694 static void AStarAddToClose(int node, int z)
695 //Wyrmgus end
696 {
697 	//Wyrmgus start
698 //	if (CloseSetSize < Threshold) {
699 	if (CloseSetSize[z] < Threshold[z]) {
700 	//Wyrmgus end
701 		//Wyrmgus start
702 //		CloseSet[CloseSetSize++] = node;
703 		CloseSet[z][CloseSetSize[z]++] = node;
704 		//Wyrmgus end
705 	}
706 }
707 
708 //Wyrmgus start
709 //#define GetIndex(x, y) (x) + (y) * AStarMapWidth
710 #define GetIndex(x, y, z) (x) + (y) * AStarMapWidth[(z)]
711 //Wyrmgus end
712 
713 /* build-in costmoveto code */
CostMoveToCallBack_Default(unsigned int index,const CUnit & unit,int z)714 static int CostMoveToCallBack_Default(unsigned int index, const CUnit &unit, int z)
715 {
716 #ifdef DEBUG
717 	{
718 		Vec2i pos;
719 		pos.y = index / Map.Info.MapWidths[z];
720 		pos.x = index - pos.y * Map.Info.MapWidths[z];
721 		Assert(Map.Info.IsPointOnMap(pos, z));
722 	}
723 #endif
724 	int cost = 0;
725 	const int mask = unit.Type->MovementMask;
726 	const CUnitTypeFinder unit_finder((UnitTypeType)unit.Type->UnitType);
727 
728 	// verify each tile of the unit.
729 	int h = unit.Type->TileSize.y;
730 	const int w = unit.Type->TileSize.x;
731 	do {
732 		const CMapField *mf = Map.Field(index, z);
733 		int i = w;
734 		do {
735 			//Wyrmgus start
736 //			const int flag = mf->Flags & mask;
737 			//for purposes of this check, don't count MapFieldWaterAllowed and MapFieldCoastAllowed if there is a bridge present
738 			unsigned long check_flags = mf->Flags;
739 			if (check_flags & MapFieldBridge) {
740 				check_flags &= ~(MapFieldWaterAllowed | MapFieldCoastAllowed);
741 			}
742 			const unsigned long flag = check_flags & mask;
743 			//Wyrmgus end
744 
745 			if (flag && (AStarKnowUnseenTerrain || mf->playerInfo.IsTeamExplored(*unit.Player))) {
746 				if (flag & ~(MapFieldLandUnit | MapFieldAirUnit | MapFieldSeaUnit)) {
747 					// we can't cross fixed units and other unpassable things
748 					return -1;
749 				}
750 				CUnit *goal = mf->UnitCache.find(unit_finder);
751 				if (!goal) {
752 					// Shouldn't happen, mask says there is something on this tile
753 					Assert(0);
754 					return -1;
755 				}
756 				//Wyrmgus start
757 //				if (goal->Moving)  {
758 				if (&unit != goal && goal->Moving)  {
759 				//Wyrmgus end
760 					// moving unit are crossable
761 					cost += AStarMovingUnitCrossingCost;
762 				} else {
763 					// for non moving unit Always Fail unless goal is unit, or unit can attack the target
764 					if (&unit != goal) {
765 						//Wyrmgus start
766 						/*
767 //						if (goal->Player->IsEnemy(unit) && unit.IsAgressive() && CanTarget(*unit.Type, *goal->Type)
768 						//Wyrmgus end
769 							&& goal->Variable[UNHOLYARMOR_INDEX].Value == 0 && goal->IsVisibleAsGoal(*unit.Player)) {
770 								cost += 2 * AStarMovingUnitCrossingCost;
771 						} else {
772 						*/
773 						// FIXME: Need support for moving a fixed unit to add cost
774 							return -1;
775 						//Wyrmgus start
776 //						}
777 						//Wyrmgus end
778 						//cost += AStarFixedUnitCrossingCost;
779 					}
780 				}
781 			}
782 			// Add cost of crossing unknown tiles if required
783 			if (!AStarKnowUnseenTerrain && !mf->playerInfo.IsTeamExplored(*unit.Player)) {
784 				// Tend against unknown tiles.
785 				cost += AStarUnknownTerrainCost;
786 			}
787 
788 			//Wyrmgus start
789 			if (
790 				(mf->Flags & MapFieldDesert)
791 				&& mf->Owner != unit.Player->Index
792 				&& unit.Type->BoolFlag[ORGANIC_INDEX].value
793 				&& unit.MapLayer->GetTimeOfDay()
794 				&& unit.MapLayer->GetTimeOfDay()->Day
795 				&& unit.Variable[DEHYDRATIONIMMUNITY_INDEX].Value <= 0
796 			) {
797 				cost += 32; //increase the cost of moving through deserts for units affected by dehydration, as we want the pathfinding to try to avoid that
798 			}
799 			//Wyrmgus end
800 
801 			// Add tile movement cost
802 			if (unit.Type->UnitType == UnitTypeFly || unit.Type->UnitType == UnitTypeFlyLow) {
803 				cost += DefaultTileMovementCost;
804 			} else {
805 				cost += mf->getCost();
806 			}
807 			++mf;
808 		} while (--i);
809 		//Wyrmgus start
810 //		index += AStarMapWidth;
811 		index += AStarMapWidth[z];
812 		//Wyrmgus end
813 	} while (--h);
814 	return cost;
815 }
816 
817 
818 /**
819 **  Compute the cost of crossing tile (x,y)
820 **
821 **  @param x     X tile where to move.
822 **  @param y     Y tile where to move.
823 **  @param data  user data.
824 **
825 **  @return      -1 -> impossible to cross.
826 **                0 -> no induced cost, except move
827 **               >0 -> costly tile
828 */
829 //Wyrmgus start
830 //static inline int CostMoveTo(unsigned int index, const CUnit &unit)
CostMoveTo(unsigned int index,const CUnit & unit,int z)831 static inline int CostMoveTo(unsigned int index, const CUnit &unit, int z)
832 //Wyrmgus end
833 {
834 	//Wyrmgus start
835 	if (!&unit) {
836 		fprintf(stderr, "Error in CostMoveTo(unsigned int index, const CUnit &unit): Unit is null.\n");
837 		return -1;
838 	}
839 	//Wyrmgus end
840 	//Wyrmgus start
841 //	int *c = &CostMoveToCache[index];
842 	int *c = &CostMoveToCache[z][index];
843 	//Wyrmgus end
844 	if (*c != CacheNotSet) {
845 		return *c;
846 	}
847 	//Wyrmgus start
848 //	*c = CostMoveToCallBack_Default(index, unit);
849 	*c = CostMoveToCallBack_Default(index, unit, z);
850 	//Wyrmgus end
851 	return *c;
852 }
853 
854 class AStarGoalMarker
855 {
856 public:
AStarGoalMarker(const CUnit & unit,bool * goal_reachable)857 	AStarGoalMarker(const CUnit &unit, bool *goal_reachable) :
858 		unit(unit), goal_reachable(goal_reachable)
859 	{}
860 
861 	//Wyrmgus start
862 //	void operator()(int offset) const
operator ()(int offset,int z) const863 	void operator()(int offset, int z) const
864 	//Wyrmgus end
865 	{
866 		//Wyrmgus start
867 //		if (CostMoveTo(offset, unit) >= 0) {
868 		if (CostMoveTo(offset, unit, z) >= 0) {
869 		//Wyrmgus end
870 			//Wyrmgus start
871 //			AStarMatrix[offset].InGoal = 1;
872 			AStarMatrix[z][offset].InGoal = 1;
873 			//Wyrmgus end
874 			*goal_reachable = true;
875 		}
876 		//Wyrmgus start
877 //		AStarAddToClose(offset);
878 		AStarAddToClose(offset, z);
879 		//Wyrmgus end
880 	}
881 private:
882 	const CUnit &unit;
883 	bool *goal_reachable;
884 };
885 
886 
887 template <typename T>
888 class MinMaxRangeVisitor
889 {
890 public:
MinMaxRangeVisitor(const T & func)891 	explicit MinMaxRangeVisitor(const T &func) : func(func), minrange(0), maxrange(0) {}
892 
893 	//Wyrmgus start
894 //	void SetGoal(Vec2i goalTopLeft, Vec2i goalBottomRight)
SetGoal(Vec2i goalTopLeft,Vec2i goalBottomRight,int z)895 	void SetGoal(Vec2i goalTopLeft, Vec2i goalBottomRight, int z)
896 	//Wyrmgus end
897 	{
898 		this->goalTopLeft = goalTopLeft;
899 		this->goalBottomRight = goalBottomRight;
900 		//Wyrmgus start
901 		this->z = z;
902 		//Wyrmgus end
903 	}
904 
SetRange(int minrange,int maxrange)905 	void SetRange(int minrange, int maxrange)
906 	{
907 		this->minrange = minrange;
908 		this->maxrange = maxrange;
909 	}
910 
SetUnitSize(const Vec2i & tileSize)911 	void SetUnitSize(const Vec2i &tileSize)
912 	{
913 		this->unitExtraTileSize.x = tileSize.x - 1;
914 		this->unitExtraTileSize.y = tileSize.y - 1;
915 	}
916 
Visit() const917 	void Visit() const
918 	{
919 		TopHemicycle();
920 		TopHemicycleNoMinRange();
921 		Center();
922 		BottomHemicycleNoMinRange();
923 		BottomHemicycle();
924 	}
925 
926 private:
GetMaxOffsetX(int dy,int range) const927 	int GetMaxOffsetX(int dy, int range) const
928 	{
929 		return isqrt(square(range + 1) - square(dy) - 1);
930 	}
931 
932 	// Distance are computed between bottom of unit and top of goal
TopHemicycle() const933 	void TopHemicycle() const
934 	{
935 		const int miny = std::max(0, goalTopLeft.y - maxrange - unitExtraTileSize.y);
936 		const int maxy = std::min(goalTopLeft.y - minrange - unitExtraTileSize.y, goalTopLeft.y - 1 - unitExtraTileSize.y);
937 		for (int y = miny; y <= maxy; ++y) {
938 			const int offsetx = GetMaxOffsetX(y - goalTopLeft.y, maxrange);
939 			const int minx = std::max(0, goalTopLeft.x - offsetx - unitExtraTileSize.x);
940 			//Wyrmgus start
941 //			const int maxx = std::min(Map.Info.MapWidth - 1 - unitExtraTileSize.x, goalBottomRight.x + offsetx);
942 			const int maxx = std::min(Map.Info.MapWidths[z] - 1 - unitExtraTileSize.x, goalBottomRight.x + offsetx);
943 			//Wyrmgus end
944 			Vec2i mpos(minx, y);
945 			//Wyrmgus start
946 //			const unsigned int offset = mpos.y * Map.Info.MapWidth;
947 			const unsigned int offset = mpos.y * Map.Info.MapWidths[z];
948 			//Wyrmgus end
949 
950 			for (mpos.x = minx; mpos.x <= maxx; ++mpos.x) {
951 				//Wyrmgus start
952 //				func(offset + mpos.x);
953 				func(offset + mpos.x, z);
954 				//Wyrmgus end
955 			}
956 		}
957 	}
958 
HemiCycleRing(int y,int offsetminx,int offsetmaxx) const959 	void HemiCycleRing(int y, int offsetminx, int offsetmaxx) const
960 	{
961 		const int minx = std::max(0, goalTopLeft.x - offsetmaxx - unitExtraTileSize.x);
962 		//Wyrmgus start
963 //		const int maxx = std::min(Map.Info.MapWidth - 1 - unitExtraTileSize.x, goalBottomRight.x + offsetmaxx);
964 		const int maxx = std::min(Map.Info.MapWidths[z] - 1 - unitExtraTileSize.x, goalBottomRight.x + offsetmaxx);
965 		//Wyrmgus end
966 		Vec2i mpos(minx, y);
967 		//Wyrmgus start
968 //		const unsigned int offset = mpos.y * Map.Info.MapWidth;
969 		const unsigned int offset = mpos.y * Map.Info.MapWidths[z];
970 		//Wyrmgus end
971 
972 		for (mpos.x = minx; mpos.x <= goalTopLeft.x - offsetminx - unitExtraTileSize.x; ++mpos.x) {
973 			//Wyrmgus start
974 //			func(offset + mpos.x);
975 			func(offset + mpos.x, z);
976 			//Wyrmgus end
977 		}
978 		for (mpos.x = goalBottomRight.x + offsetminx; mpos.x <= maxx; ++mpos.x) {
979 			//Wyrmgus start
980 //			func(offset + mpos.x);
981 			func(offset + mpos.x, z);
982 			//Wyrmgus end
983 		}
984 	}
985 
TopHemicycleNoMinRange() const986 	void TopHemicycleNoMinRange() const
987 	{
988 		const int miny = std::max(0, goalTopLeft.y - (minrange - 1) - unitExtraTileSize.y);
989 		const int maxy = goalTopLeft.y - 1 - unitExtraTileSize.y;
990 		for (int y = miny; y <= maxy; ++y) {
991 			const int offsetmaxx = GetMaxOffsetX(y - goalTopLeft.y, maxrange);
992 			const int offsetminx = GetMaxOffsetX(y - goalTopLeft.y, minrange - 1) + 1;
993 
994 			HemiCycleRing(y, offsetminx, offsetmaxx);
995 		}
996 	}
997 
Center() const998 	void Center() const
999 	{
1000 		const int miny = std::max(0, goalTopLeft.y - unitExtraTileSize.y);
1001 		//Wyrmgus start
1002 //		const int maxy = std::min<int>(Map.Info.MapHeight - 1 - unitExtraTileSize.y, goalBottomRight.y);
1003 		const int maxy = std::min<int>(Map.Info.MapHeights[z] - 1 - unitExtraTileSize.y, goalBottomRight.y);
1004 		//Wyrmgus end
1005 		const int minx = std::max(0, goalTopLeft.x - maxrange - unitExtraTileSize.x);
1006 		//Wyrmgus start
1007 //		const int maxx = std::min<int>(Map.Info.MapWidth - 1 - unitExtraTileSize.x, goalBottomRight.x + maxrange);
1008 		const int maxx = std::min<int>(Map.Info.MapWidths[z] - 1 - unitExtraTileSize.x, goalBottomRight.x + maxrange);
1009 		//Wyrmgus end
1010 
1011 		if (minrange == 0) {
1012 			for (int y = miny; y <= maxy; ++y) {
1013 				Vec2i mpos(minx, y);
1014 				//Wyrmgus start
1015 //				const unsigned int offset = mpos.y * Map.Info.MapWidth;
1016 				const unsigned int offset = mpos.y * Map.Info.MapWidths[z];
1017 				//Wyrmgus end
1018 
1019 				for (mpos.x = minx; mpos.x <= maxx; ++mpos.x) {
1020 					//Wyrmgus start
1021 //					func(offset + mpos.x);
1022 					func(offset + mpos.x, z);
1023 					//Wyrmgus end
1024 				}
1025 			}
1026 		} else {
1027 			for (int y = miny; y <= maxy; ++y) {
1028 				Vec2i mpos(minx, y);
1029 				//Wyrmgus start
1030 //				const unsigned int offset = mpos.y * Map.Info.MapWidth;
1031 				const unsigned int offset = mpos.y * Map.Info.MapWidths[z];
1032 				//Wyrmgus end
1033 
1034 				for (mpos.x = minx; mpos.x <= goalTopLeft.x - minrange - unitExtraTileSize.x; ++mpos.x) {
1035 					//Wyrmgus start
1036 //					func(offset + mpos.x);
1037 					func(offset + mpos.x, z);
1038 					//Wyrmgus end
1039 				}
1040 				for (mpos.x = goalBottomRight.x + minrange; mpos.x <= maxx; ++mpos.x) {
1041 					//Wyrmgus start
1042 //					func(offset + mpos.x);
1043 					func(offset + mpos.x, z);
1044 					//Wyrmgus end
1045 				}
1046 			}
1047 		}
1048 	}
1049 
BottomHemicycleNoMinRange() const1050 	void BottomHemicycleNoMinRange() const
1051 	{
1052 		const int miny = goalBottomRight.y + 1;
1053 		//Wyrmgus start
1054 //		const int maxy = std::min(Map.Info.MapHeight - 1 - unitExtraTileSize.y, goalBottomRight.y + (minrange - 1));
1055 		const int maxy = std::min(Map.Info.MapHeights[z] - 1 - unitExtraTileSize.y, goalBottomRight.y + (minrange - 1));
1056 		//Wyrmgus end
1057 
1058 		for (int y = miny; y <= maxy; ++y) {
1059 			const int offsetmaxx = GetMaxOffsetX(y - goalBottomRight.y, maxrange);
1060 			const int offsetminx = GetMaxOffsetX(y - goalBottomRight.y, minrange - 1) + 1;
1061 
1062 			HemiCycleRing(y, offsetminx, offsetmaxx);
1063 		}
1064 	}
1065 
BottomHemicycle() const1066 	void BottomHemicycle() const
1067 	{
1068 		const int miny = std::max(goalBottomRight.y + minrange, goalBottomRight.y + 1);
1069 		//Wyrmgus start
1070 //		const int maxy = std::min(Map.Info.MapHeight - 1 - unitExtraTileSize.y, goalBottomRight.y + maxrange);
1071 		const int maxy = std::min(Map.Info.MapHeights[z] - 1 - unitExtraTileSize.y, goalBottomRight.y + maxrange);
1072 		//Wyrmgus end
1073 		for (int y = miny; y <= maxy; ++y) {
1074 			const int offsetx = GetMaxOffsetX(y - goalBottomRight.y, maxrange);
1075 			const int minx = std::max(0, goalTopLeft.x - offsetx - unitExtraTileSize.x);
1076 			//Wyrmgus start
1077 //			const int maxx = std::min(Map.Info.MapWidth - 1 - unitExtraTileSize.x, goalBottomRight.x + offsetx);
1078 			const int maxx = std::min(Map.Info.MapWidths[z] - 1 - unitExtraTileSize.x, goalBottomRight.x + offsetx);
1079 			//WYrmgus end
1080 			Vec2i mpos(minx, y);
1081 			//Wyrmgus start
1082 //			const unsigned int offset = mpos.y * Map.Info.MapWidth;
1083 			const unsigned int offset = mpos.y * Map.Info.MapWidths[z];
1084 			//Wyrmgus end
1085 
1086 			for (mpos.x = minx; mpos.x <= maxx; ++mpos.x) {
1087 				//Wyrmgus start
1088 //				func(offset + mpos.x);
1089 				func(offset + mpos.x, z);
1090 				//Wyrmgus end
1091 			}
1092 		}
1093 	}
1094 
1095 private:
1096 	T func;
1097 	Vec2i goalTopLeft;
1098 	Vec2i goalBottomRight;
1099 	Vec2i unitExtraTileSize;
1100 	int minrange;
1101 	int maxrange;
1102 	//Wyrmgus start
1103 	int z;
1104 	//Wyrmgus end
1105 };
1106 
1107 
1108 
1109 /**
1110 **  MarkAStarGoal
1111 */
AStarMarkGoal(const Vec2i & goal,int gw,int gh,int tilesizex,int tilesizey,int minrange,int maxrange,const CUnit & unit,int z)1112 static int AStarMarkGoal(const Vec2i &goal, int gw, int gh,
1113 						 //Wyrmgus start
1114 //						 int tilesizex, int tilesizey, int minrange, int maxrange, const CUnit &unit)
1115 						 int tilesizex, int tilesizey, int minrange, int maxrange, const CUnit &unit, int z)
1116 						 //Wyrmgus end
1117 {
1118 	ProfileBegin("AStarMarkGoal");
1119 
1120 	if (minrange == 0 && maxrange == 0 && gw == 0 && gh == 0) {
1121 		//Wyrmgus start
1122 //		if (goal.x + tilesizex > AStarMapWidth || goal.y + tilesizey > AStarMapHeight) {
1123 		if (goal.x + tilesizex - 1 > AStarMapWidth[z] || goal.y + tilesizey - 1 > AStarMapHeight[z]) {
1124 		//Wyrmgus end
1125 			ProfileEnd("AStarMarkGoal");
1126 			return 0;
1127 		}
1128 		//Wyrmgus start
1129 //		unsigned int offset = GetIndex(goal.x, goal.y);
1130 //		if (CostMoveTo(offset, unit) >= 0) {
1131 		unsigned int offset = GetIndex(goal.x, goal.y, z);
1132 		if (CostMoveTo(offset, unit, z) >= 0) {
1133 		//Wyrmgus end
1134 			//Wyrmgus start
1135 //			AStarMatrix[offset].InGoal = 1;
1136 			AStarMatrix[z][offset].InGoal = 1;
1137 			//Wyrmgus end
1138 			ProfileEnd("AStarMarkGoal");
1139 			return 1;
1140 		} else {
1141 			ProfileEnd("AStarMarkGoal");
1142 			return 0;
1143 		}
1144 	}
1145 
1146 	bool goal_reachable = false;
1147 
1148 	gw = std::max(gw, 1);
1149 	gh = std::max(gh, 1);
1150 
1151 	AStarGoalMarker aStarGoalMarker(unit, &goal_reachable);
1152 	MinMaxRangeVisitor<AStarGoalMarker> visitor(aStarGoalMarker);
1153 
1154 	const Vec2i goalBottomRigth(goal.x + gw - 1, goal.y + gh - 1);
1155 	//Wyrmgus start
1156 //	visitor.SetGoal(goal, goalBottomRigth);
1157 	visitor.SetGoal(goal, goalBottomRigth, z);
1158 	//Wyrmgus end
1159 	visitor.SetRange(minrange, maxrange);
1160 	const Vec2i tileSize(tilesizex, tilesizey);
1161 	visitor.SetUnitSize(tileSize);
1162 
1163 	const Vec2i extratilesize(tilesizex - 1, tilesizey - 1);
1164 
1165 	visitor.Visit();
1166 
1167 	ProfileEnd("AStarMarkGoal");
1168 	return goal_reachable;
1169 }
1170 
1171 /**
1172 **  Save the path
1173 **
1174 **  @return  The length of the path
1175 */
1176 //Wyrmgus start
1177 //static int AStarSavePath(const Vec2i &startPos, const Vec2i &endPos, char *path, int pathLen)
AStarSavePath(const Vec2i & startPos,const Vec2i & endPos,char * path,int pathLen,int z)1178 static int AStarSavePath(const Vec2i &startPos, const Vec2i &endPos, char *path, int pathLen, int z)
1179 //Wyrmgus end
1180 {
1181 	ProfileBegin("AStarSavePath");
1182 
1183 	int fullPathLength;
1184 	int pathPos;
1185 	int direction;
1186 
1187 	// Figure out the full path length
1188 	fullPathLength = 0;
1189 	Vec2i curr = endPos;
1190 	//Wyrmgus start
1191 //	int currO = curr.y * AStarMapWidth;
1192 	int currO = curr.y * AStarMapWidth[z];
1193 	//Wyrmgus end
1194 	while (curr != startPos) {
1195 		//Wyrmgus start
1196 //		direction = AStarMatrix[currO + curr.x].Direction;
1197 		direction = AStarMatrix[z][currO + curr.x].Direction;
1198 		//Wyrmgus end
1199 		curr.x -= Heading2X[direction];
1200 		curr.y -= Heading2Y[direction];
1201 		//Wyrmgus start
1202 //		currO -= Heading2O[direction];
1203 		currO -= Heading2O[direction][z];
1204 		//Wyrmgus end
1205 		fullPathLength++;
1206 	}
1207 
1208 	// Save as much of the path as we can
1209 	if (path) {
1210 		pathLen = std::min<int>(fullPathLength, pathLen);
1211 		pathPos = fullPathLength;
1212 		curr = endPos;
1213 		//Wyrmgus start
1214 //		currO = curr.y * AStarMapWidth;
1215 		currO = curr.y * AStarMapWidth[z];
1216 		//Wyrmgus end
1217 		while (curr != startPos) {
1218 			//Wyrmgus start
1219 //			direction = AStarMatrix[currO + curr.x].Direction;
1220 			direction = AStarMatrix[z][currO + curr.x].Direction;
1221 			//Wyrmgus end
1222 			curr.x -= Heading2X[direction];
1223 			curr.y -= Heading2Y[direction];
1224 			//Wyrmgus start
1225 //			currO -= Heading2O[direction];
1226 			currO -= Heading2O[direction][z];
1227 			//Wyrmgus end
1228 			--pathPos;
1229 			if (pathPos < pathLen) {
1230 				path[pathLen - pathPos - 1] = direction;
1231 			}
1232 		}
1233 	}
1234 
1235 	ProfileEnd("AStarSavePath");
1236 	return fullPathLength;
1237 }
1238 
1239 /**
1240 **  Optimization to find a simple path
1241 **  Check if we're at the goal or if it's 1 tile away
1242 */
AStarFindSimplePath(const Vec2i & startPos,const Vec2i & goal,int gw,int gh,int,int,int minrange,int maxrange,char * path,const CUnit & unit,int z,bool allow_diagonal)1243 static int AStarFindSimplePath(const Vec2i &startPos, const Vec2i &goal, int gw, int gh,
1244 							   int, int, int minrange, int maxrange,
1245 							   //Wyrmgus start
1246 //							   char *path, const CUnit &unit)
1247 							   char *path, const CUnit &unit, int z, bool allow_diagonal)
1248 							   //Wyrmgus end
1249 {
1250 	ProfileBegin("AStarFindSimplePath");
1251 	// At exact destination point already
1252 	if (goal == startPos && minrange == 0) {
1253 		ProfileEnd("AStarFindSimplePath");
1254 		return PF_REACHED;
1255 	}
1256 
1257 	// Don't allow unit inside destination area
1258 	if (goal.x <= startPos.x && startPos.x <= goal.x + gw - 1
1259 		&& goal.y <= startPos.y && startPos.y <= goal.y + gh - 1) {
1260 		//Wyrmgus start
1261 		ProfileEnd("AStarFindSimplePath"); // seems like this should be here
1262 		//Wyrmgus end
1263 		return PF_FAILED;
1264 	}
1265 
1266 	const Vec2i diff = goal - startPos;
1267 	const int distance = isqrt(square(diff.x) + square(diff.y));
1268 
1269 	// Within range of destination
1270 	if (minrange <= distance && distance <= maxrange) {
1271 		ProfileEnd("AStarFindSimplePath");
1272 		return PF_REACHED;
1273 	}
1274 
1275 	//Wyrmgus start
1276 //	if (MyAbs(diff.x) <= 1 && MyAbs(diff.y) <= 1) {
1277 	if (minrange <= distance && MyAbs(diff.x) <= 1 && MyAbs(diff.y) <= 1 && (allow_diagonal || diff.x == 0 || diff.y == 0)) {
1278 	//Wyrmgus end
1279 		// Move to adjacent cell
1280 		//Wyrmgus start
1281 //		if (CostMoveTo(GetIndex(goal.x, goal.y), unit) == -1) {
1282 		if (CostMoveTo(GetIndex(goal.x, goal.y, z), unit, z) == -1) {
1283 		//Wyrmgus end
1284 			ProfileEnd("AStarFindSimplePath");
1285 			return PF_UNREACHABLE;
1286 		}
1287 
1288 		if (path) {
1289 			path[0] = XY2Heading[diff.x + 1][diff.y + 1];
1290 		}
1291 		ProfileEnd("AStarFindSimplePath");
1292 		return 1;
1293 	}
1294 
1295 	ProfileEnd("AStarFindSimplePath");
1296 	return PF_FAILED;
1297 }
1298 
1299 /**
1300 **  Find path.
1301 */
AStarFindPath(const Vec2i & startPos,const Vec2i & goalPos,int gw,int gh,int tilesizex,int tilesizey,int minrange,int maxrange,char * path,int pathlen,const CUnit & unit,int max_length,int z,bool allow_diagonal)1302 int AStarFindPath(const Vec2i &startPos, const Vec2i &goalPos, int gw, int gh,
1303 				  int tilesizex, int tilesizey, int minrange, int maxrange,
1304 				  //Wyrmgus start
1305 //				  char *path, int pathlen, const CUnit &unit)
1306 				  char *path, int pathlen, const CUnit &unit, int max_length, int z, bool allow_diagonal)
1307 				  //Wyrmgus end
1308 {
1309 	Assert(Map.Info.IsPointOnMap(startPos, z));
1310 
1311 	//Wyrmgus start
1312 	if (unit.MapLayer->ID != z) {
1313 		return PF_UNREACHABLE;
1314 	}
1315 
1316 	allow_diagonal = allow_diagonal && !unit.Type->BoolFlag[RAIL_INDEX].value; //rail units cannot move diagonally
1317 	//Wyrmgus end
1318 
1319 	ProfileBegin("AStarFindPath");
1320 
1321 	AStarGoalX = goalPos.x;
1322 	AStarGoalY = goalPos.y;
1323 
1324 	//  Check for simple cases first
1325 	int ret = AStarFindSimplePath(startPos, goalPos, gw, gh, tilesizex, tilesizey,
1326 								  //Wyrmgus start
1327 //								  minrange, maxrange, path, unit);
1328 								  minrange, maxrange, path, unit, z, allow_diagonal);
1329 								  //Wyrmgus end
1330 	if (ret != PF_FAILED) {
1331 		ProfileEnd("AStarFindPath");
1332 		return ret;
1333 	}
1334 
1335 	//  Initialize
1336 	//Wyrmgus start
1337 //	AStarCleanUp();
1338 //	CostMoveToCacheCleanUp();
1339 	AStarCleanUp(z);
1340 	CostMoveToCacheCleanUp(z);
1341 	//Wyrmgus end
1342 
1343 	//Wyrmgus start
1344 //	OpenSetSize = 0;
1345 //	CloseSetSize = 0;
1346 	OpenSetSize[z] = 0;
1347 	CloseSetSize[z] = 0;
1348 	//Wyrmgus end
1349 
1350 	//Wyrmgus start
1351 //	if (!AStarMarkGoal(goalPos, gw, gh, tilesizex, tilesizey, minrange, maxrange, unit)) {
1352 	if (!AStarMarkGoal(goalPos, gw, gh, tilesizex, tilesizey, minrange, maxrange, unit, z)) {
1353 	//Wyrmgus end
1354 		// goal is not reachable
1355 		ret = PF_UNREACHABLE;
1356 		ProfileEnd("AStarFindPath");
1357 		return ret;
1358 	}
1359 
1360 	//Wyrmgus start
1361 //	int eo = startPos.y * AStarMapWidth + startPos.x;
1362 	int eo = startPos.y * AStarMapWidth[z] + startPos.x;
1363 	//Wyrmgus end
1364 	// it is quite important to start from 1 rather than 0, because we use
1365 	// 0 as a way to represent nodes that we have not visited yet.
1366 	//Wyrmgus start
1367 //	AStarMatrix[eo].CostFromStart = 1;
1368 	AStarMatrix[z][eo].CostFromStart = 1;
1369 	//Wyrmgus end
1370 	// 8 to say we are came from nowhere.
1371 	//Wyrmgus start
1372 //	AStarMatrix[eo].Direction = 8;
1373 	AStarMatrix[z][eo].Direction = 8;
1374 	//Wyrmgus end
1375 
1376 	// place start point in open, it that failed, try another pathfinder
1377 	int costToGoal = AStarCosts(startPos, goalPos);
1378 	//Wyrmgus start
1379 //	AStarMatrix[eo].CostToGoal = costToGoal;
1380 //	if (AStarAddNode(startPos, eo, 1 + costToGoal) == PF_FAILED) {
1381 	AStarMatrix[z][eo].CostToGoal = costToGoal;
1382 	if (AStarAddNode(startPos, eo, 1 + costToGoal, z) == PF_FAILED) {
1383 	//Wyrmgus end
1384 		ret = PF_FAILED;
1385 		ProfileEnd("AStarFindPath");
1386 		return ret;
1387 	}
1388 	//Wyrmgus start
1389 //	AStarAddToClose(OpenSet[0].O);
1390 //	if (AStarMatrix[eo].InGoal) {
1391 	AStarAddToClose(OpenSet[z][0].O, z);
1392 	if (AStarMatrix[z][eo].InGoal) {
1393 	//Wyrmgus end
1394 		ret = PF_REACHED;
1395 		ProfileEnd("AStarFindPath");
1396 		return ret;
1397 	}
1398 	Vec2i endPos;
1399 
1400 	//Wyrmgus start
1401 	int length = 0;
1402 	//Wyrmgus end
1403 
1404 	//  Begin search
1405 	while (1) {
1406 		//Wyrmgus start
1407 		if (max_length != 0 && length > max_length) {
1408 			ret = PF_FAILED;
1409 			ProfileEnd("AStarFindPath");
1410 			return ret;
1411 		}
1412 		//Wyrmgus end
1413 
1414 		// Find the best node of from the open set
1415 		//Wyrmgus start
1416 //		const int shortest = AStarFindMinimum();
1417 //		const int x = OpenSet[shortest].pos.x;
1418 //		const int y = OpenSet[shortest].pos.y;
1419 //		const int o = OpenSet[shortest].O;
1420 		const int shortest = AStarFindMinimum(z);
1421 		const int x = OpenSet[z][shortest].pos.x;
1422 		const int y = OpenSet[z][shortest].pos.y;
1423 		const int o = OpenSet[z][shortest].O;
1424 		//Wyrmgus end
1425 
1426 		//Wyrmgus start
1427 //		AStarRemoveMinimum(shortest);
1428 		AStarRemoveMinimum(shortest, z);
1429 		//Wyrmgus end
1430 
1431 		// If we have reached the goal, then exit.
1432 		//Wyrmgus start
1433 //		if (AStarMatrix[o].InGoal == 1) {
1434 		if (AStarMatrix[z][o].InGoal == 1) {
1435 		//Wyrmgus end
1436 			endPos.x = x;
1437 			endPos.y = y;
1438 			break;
1439 		}
1440 
1441 #if 0
1442 		// If we have looked too long, then exit.
1443 		if (!counter--) {
1444 			// FIXME: Select a "good" point from the open set.
1445 			// Nearest point to goal.
1446 			AstarDebugPrint("way too long\n");
1447 			ret = PF_FAILED;
1448 			ProfileEnd("AStarFindPath");
1449 			return ret;
1450 		}
1451 #endif
1452 
1453 		// Generate successors of this node.
1454 
1455 		// Node that this node was generated from.
1456 		//Wyrmgus start
1457 //		const int px = x - Heading2X[(int)AStarMatrix[o].Direction];
1458 //		const int py = y - Heading2Y[(int)AStarMatrix[o].Direction];
1459 		const int px = x - Heading2X[(int)AStarMatrix[z][o].Direction];
1460 		const int py = y - Heading2Y[(int)AStarMatrix[z][o].Direction];
1461 		//Wyrmgus end
1462 
1463 		for (int i = 0; i < 8; ++i) {
1464 			//Wyrmgus start
1465 			if (!allow_diagonal && Heading2X[i] != 0 && Heading2Y[i] != 0) { //can't move diagonally
1466 				continue;
1467 			}
1468 			//Wyrmgus end
1469 
1470 			endPos.x = x + Heading2X[i];
1471 			endPos.y = y + Heading2Y[i];
1472 
1473 			// Don't check the tile we came from, it's not going to be better
1474 			// Should reduce load on A*
1475 
1476 			if (endPos.x == px && endPos.y == py) {
1477 				continue;
1478 			}
1479 
1480 			// Outside the map or can't be entered.
1481 			//Wyrmgus start
1482 //			if (endPos.x < 0 || endPos.x + tilesizex - 1 >= AStarMapWidth
1483 			if (endPos.x < 0 || endPos.x + tilesizex - 1 >= AStarMapWidth[z]
1484 			//Wyrmgus end
1485 				//Wyrmgus start
1486 //				|| endPos.y < 0 || endPos.y + tilesizey - 1 >= AStarMapHeight) {
1487 				|| endPos.y < 0 || endPos.y + tilesizey - 1 >= AStarMapHeight[z]
1488 				|| !Map.Info.IsPointOnMap(endPos, z)) {
1489 				//Wyrmgus end
1490 				continue;
1491 			}
1492 
1493 			//eo = GetIndex(ex, ey);
1494 			//Wyrmgus start
1495 //			eo = endPos.x + (o - x) + Heading2O[i];
1496 			eo = endPos.x + (o - x) + Heading2O[i][z];
1497 			//Wyrmgus end
1498 
1499 			// if the point is "move to"-able and
1500 			// if we have not reached this point before,
1501 			// or if we have a better path to it, we add it to open set
1502 			//Wyrmgus start
1503 //			int new_cost = CostMoveTo(eo, unit);
1504 			int new_cost = CostMoveTo(eo, unit, z);
1505 			//Wyrmgus end
1506 			if (new_cost == -1) {
1507 				// uncrossable tile
1508 				continue;
1509 			}
1510 
1511 			// Add a cost for walking to make paths more realistic for the user.
1512 			new_cost++;
1513 			//Wyrmgus start
1514 //			new_cost += AStarMatrix[o].CostFromStart;
1515 //			if (AStarMatrix[eo].CostFromStart == 0) {
1516 			new_cost += AStarMatrix[z][o].CostFromStart;
1517 			if (AStarMatrix[z][eo].CostFromStart == 0) {
1518 			//Wyrmgus end
1519 				// we are sure the current node has not been already visited
1520 				//Wyrmgus start
1521 //				AStarMatrix[eo].CostFromStart = new_cost;
1522 //				AStarMatrix[eo].Direction = i;
1523 				AStarMatrix[z][eo].CostFromStart = new_cost;
1524 				AStarMatrix[z][eo].Direction = i;
1525 				//Wyrmgus end
1526 				costToGoal = AStarCosts(endPos, goalPos);
1527 				//Wyrmgus start
1528 //				AStarMatrix[eo].CostToGoal = costToGoal;
1529 //				if (AStarAddNode(endPos, eo, AStarMatrix[eo].CostFromStart + costToGoal) == PF_FAILED) {
1530 				AStarMatrix[z][eo].CostToGoal = costToGoal;
1531 				if (AStarAddNode(endPos, eo, AStarMatrix[z][eo].CostFromStart + costToGoal, z) == PF_FAILED) {
1532 				//Wyrmgus end
1533 					ret = PF_FAILED;
1534 					ProfileEnd("AStarFindPath");
1535 					return ret;
1536 				}
1537 				// we add the point to the close set
1538 				//Wyrmgus start
1539 //				AStarAddToClose(eo);
1540 				AStarAddToClose(eo, z);
1541 				//Wyrmgus end
1542 			//Wyrmgus start
1543 //			} else if (new_cost < AStarMatrix[eo].CostFromStart) {
1544 			} else if (new_cost < AStarMatrix[z][eo].CostFromStart) {
1545 			//Wyrmgus end
1546 				// Already visited node, but we have here a better path
1547 				// I know, it's redundant (but simpler like this)
1548 				//Wyrmgus start
1549 //				AStarMatrix[eo].CostFromStart = new_cost;
1550 //				AStarMatrix[eo].Direction = i;
1551 				AStarMatrix[z][eo].CostFromStart = new_cost;
1552 				AStarMatrix[z][eo].Direction = i;
1553 				//Wyrmgus end
1554 				// this point might be already in the OpenSet
1555 				//Wyrmgus start
1556 //				const int j = AStarFindNode(eo);
1557 				const int j = AStarFindNode(eo, z);
1558 				//Wyrmgus end
1559 				if (j == -1) {
1560 					costToGoal = AStarCosts(endPos, goalPos);
1561 					//Wyrmgus start
1562 //					AStarMatrix[eo].CostToGoal = costToGoal;
1563 //					if (AStarAddNode(endPos, eo, AStarMatrix[eo].CostFromStart + costToGoal) == PF_FAILED) {
1564 					AStarMatrix[z][eo].CostToGoal = costToGoal;
1565 					if (AStarAddNode(endPos, eo, AStarMatrix[z][eo].CostFromStart + costToGoal, z) == PF_FAILED) {
1566 					//Wyrmgus end
1567 						ret = PF_FAILED;
1568 						ProfileEnd("AStarFindPath");
1569 						return ret;
1570 					}
1571 				} else {
1572 					costToGoal = AStarCosts(endPos, goalPos);
1573 					//Wyrmgus start
1574 //					AStarMatrix[eo].CostToGoal = costToGoal;
1575 //					AStarReplaceNode(j);
1576 					AStarMatrix[z][eo].CostToGoal = costToGoal;
1577 					AStarReplaceNode(j, z);
1578 					//Wyrmgus end
1579 				}
1580 				// we don't have to add this point to the close set
1581 			}
1582 		}
1583 		//Wyrmgus start
1584 //		if (OpenSetSize <= 0) { // no new nodes generated
1585 		if (OpenSetSize[z] <= 0) { // no new nodes generated
1586 		//Wyrmgus end
1587 			ret = PF_UNREACHABLE;
1588 			ProfileEnd("AStarFindPath");
1589 			return ret;
1590 		}
1591 
1592 		//Wyrmgus start
1593 		length += 1;
1594 		//Wyrmgus end
1595 	}
1596 
1597 	//Wyrmgus start
1598 //	const int path_length = AStarSavePath(startPos, endPos, path, pathlen);
1599 	const int path_length = AStarSavePath(startPos, endPos, path, pathlen, z);
1600 	//Wyrmgus end
1601 
1602 	ret = path_length;
1603 
1604 	ProfileEnd("AStarFindPath");
1605 	return ret;
1606 }
1607 
1608 struct StatsNode {
StatsNodeStatsNode1609 	StatsNode() : Direction(0), InGoal(0), CostFromStart(0), Costs(0), CostToGoal(0) {}
1610 
1611 	int Direction;
1612 	int InGoal;
1613 	int CostFromStart;
1614 	int Costs;
1615 	int CostToGoal;
1616 };
1617 
1618 //Wyrmgus start
1619 //StatsNode *AStarGetStats()
AStarGetStats(int z)1620 StatsNode *AStarGetStats(int z)
1621 //Wyrmgus end
1622 {
1623 	//Wyrmgus start
1624 //	StatsNode *stats = new StatsNode[AStarMapWidth * AStarMapHeight];
1625 	StatsNode *stats = new StatsNode[AStarMapWidth[z] * AStarMapHeight[z]];
1626 	//Wyrmgus end
1627 	StatsNode *s = stats;
1628 	//Wyrmgus start
1629 //	Node *m = AStarMatrix;
1630 	Node *m = AStarMatrix[z];
1631 	//Wyrmgus end
1632 
1633 	//Wyrmgus start
1634 //	for (int j = 0; j < AStarMapHeight; ++j) {
1635 	for (int j = 0; j < AStarMapHeight[z]; ++j) {
1636 	//Wyrmgus end
1637 		//Wyrmgus start
1638 //		for (int i = 0; i < AStarMapWidth; ++i) {
1639 		for (int i = 0; i < AStarMapWidth[z]; ++i) {
1640 		//Wyrmgus end
1641 			s->Direction = m->Direction;
1642 			s->InGoal = m->InGoal;
1643 			s->CostFromStart = m->CostFromStart;
1644 			s->CostToGoal = m->CostToGoal;
1645 			++s;
1646 			++m;
1647 		}
1648 	}
1649 
1650 	//Wyrmgus start
1651 //	for (int i = 0; i < OpenSetSize; ++i) {
1652 	for (int i = 0; i < OpenSetSize[z]; ++i) {
1653 	//Wyrmgus end
1654 		//Wyrmgus start
1655 //		stats[OpenSet[i].O].Costs = OpenSet[i].Costs;
1656 		stats[OpenSet[z][i].O].Costs = OpenSet[z][i].Costs;
1657 		//Wyrmgus end
1658 	}
1659 	return stats;
1660 }
1661 
AStarFreeStats(StatsNode * stats)1662 void AStarFreeStats(StatsNode *stats)
1663 {
1664 	delete[] stats;
1665 }
1666 
1667 /*----------------------------------------------------------------------------
1668 --  Configurable costs
1669 ----------------------------------------------------------------------------*/
1670 
1671 // AStarFixedUnitCrossingCost
SetAStarFixedUnitCrossingCost(int cost)1672 void SetAStarFixedUnitCrossingCost(int cost)
1673 {
1674 	if (cost <= 3) {
1675 		fprintf(stderr, "AStarFixedUnitCrossingCost must be greater than 3\n");
1676 	}
1677 }
GetAStarFixedUnitCrossingCost()1678 int GetAStarFixedUnitCrossingCost()
1679 {
1680 	return AStarFixedUnitCrossingCost;
1681 }
1682 
1683 // AStarMovingUnitCrossingCost
SetAStarMovingUnitCrossingCost(int cost)1684 void SetAStarMovingUnitCrossingCost(int cost)
1685 {
1686 	if (cost <= 3) {
1687 		fprintf(stderr, "AStarMovingUnitCrossingCost must be greater than 3\n");
1688 	}
1689 }
GetAStarMovingUnitCrossingCost()1690 int GetAStarMovingUnitCrossingCost()
1691 {
1692 	return AStarMovingUnitCrossingCost;
1693 }
1694 
1695 // AStarUnknownTerrainCost
SetAStarUnknownTerrainCost(int cost)1696 void SetAStarUnknownTerrainCost(int cost)
1697 {
1698 	if (cost < 0) {
1699 		fprintf(stderr, "AStarUnknownTerrainCost must be non-negative\n");
1700 		return;
1701 	}
1702 	AStarUnknownTerrainCost = cost;
1703 }
GetAStarUnknownTerrainCost()1704 int GetAStarUnknownTerrainCost()
1705 {
1706 	return AStarUnknownTerrainCost;
1707 }
1708 
1709 //@}
1710