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