FINDPATH.CPP (47315B)
1 // 2 // Copyright 2020 Electronic Arts Inc. 3 // 4 // TiberianDawn.DLL and RedAlert.dll and corresponding source code is free 5 // software: you can redistribute it and/or modify it under the terms of 6 // the GNU General Public License as published by the Free Software Foundation, 7 // either version 3 of the License, or (at your option) any later version. 8 9 // TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed 10 // in the hope that it will be useful, but with permitted additional restrictions 11 // under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT 12 // distributed with this program. You should have received a copy of the 13 // GNU General Public License along with permitted additional restrictions 14 // with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection 15 16 /* $Header: /CounterStrike/FINDPATH.CPP 1 3/03/97 10:24a Joe_bostic $ */ 17 /*********************************************************************************************** 18 *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** 19 *********************************************************************************************** 20 * * 21 * Project Name : Command & Conquer * 22 * * 23 * File Name : FINDPATH.CPP * 24 * * 25 * Programmer : Joe L. Bostic * 26 * * 27 * Start Date : September 10, 1993 * 28 * * 29 * Last Update : May 25, 1995 [PWG] * 30 * * 31 * The path algorithm works by following a LOS path to the target. If it * 32 * collides with an impassable spot, it uses an Edge following routine to * 33 * get around it. The edge follower moves along the edge in a clockwise or * 34 * counter clockwise fashion until finding the destination spot. The * 35 * destination is determined by Find_Path. It is the first passable that * 36 * can be reached (so it will handle the doughnut case, where there is * 37 * a passable in the center of an unreachable area). * 38 * * 39 *---------------------------------------------------------------------------------------------* 40 * Functions: * 41 * Clear_Path_Overlap -- clears the path overlap list * 42 * Find_Path -- Find a path from point a to point b. * 43 * Find_Path_Cell -- Finds a given cell on a specified path * 44 * Follow_Edge -- Follow an edge to get around an impassable spot. * 45 * FootClass::Unravel_Loop -- Unravels a loop in the movement path * 46 * Get_New_XY -- Get the new x,y based on current position and direction. * 47 * Optimize_Moves -- Optimize the move list. * 48 * Set_Path_Overlap -- Sets the overlap bit for given cell * 49 * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ 50 51 #include "function.h" 52 //#include <string.h> 53 54 /* 55 ** When an edge search is started, it can be performed CLOCKwise or 56 ** COUNTERCLOCKwise direction. 57 */ 58 #define CLOCK (FacingType)1 // Clockwise. 59 #define COUNTERCLOCK (FacingType)-1 // Counterclockwise. 60 61 /* 62 ** If defined, diagonal moves are allowed, else no diagonals. 63 */ 64 #define DIAGONAL 65 66 /* 67 ** This is the marker to signify the end of the path list. 68 */ 69 #define END FACING_NONE 70 71 /* 72 ** "- 1" test for bit manipulation. 73 */ 74 #define TEST 75 76 /* 77 ** If memory is more important than speed, set this define to 78 ** true. It will then perform intermediate optimizations to get the most 79 ** milage out of a limited movement list staging area. If this value 80 ** is true then it figures paths a bit more intelligently. 81 */ 82 #define SAVEMEM true 83 84 /* 85 ** Modify this macro so that given two cell values, it will return 86 ** a value between 0 and 7, with 0 being North and moving 87 ** clockwise (just like map degrees). 88 */ 89 #define CELL_FACING(a, b) Dir_Facing(::Direction((a), (b))) 90 91 92 /*-------------------------------------------------------------------------*/ 93 /* 94 ** Cells values are really indexes into the 'map'. The following value is 95 ** the X width of the map. 96 */ 97 #define MODULO MAP_CELL_W 98 99 /* 100 ** Maximum lookahead cells. Twice this value in bytes will be 101 ** reserved on the stack. The smaller this number, the faster the processing. 102 */ 103 #define MAX_MLIST_SIZE 300 104 #define THREAT_THRESHOLD 5 105 106 107 #define MAX_PATH_EDGE_FOLLOW 400 108 109 #ifdef NEVER 110 typedef enum { 111 FACING_N, // North 112 FACING_NE, // North-East 113 FACING_E, // East 114 FACING_SE, // South-East 115 FACING_S, // South 116 FACING_SW, // South-West 117 FACING_W, // West 118 FACING_NW, // North-West 119 120 FACING_COUNT // Total of 8 directions (0..7). 121 } FacingType; 122 #endif 123 124 125 /*-------------------------------------------------------------------------*/ 126 //static bool DrawPath; 127 128 inline FacingType Opposite(FacingType face) 129 { 130 return( (FacingType) (face ^ 4)); 131 } 132 133 134 inline static FacingType Next_Direction(FacingType facing, FacingType dir) 135 { 136 facing = facing + dir; 137 #ifndef DIAGONAL 138 facing = (FacingType)(facing & 0x06); 139 #endif 140 return(facing); 141 } 142 143 /*=========================================================================*/ 144 /* Define a couple of variables which are private to the module they are */ 145 /* declared in. */ 146 /*=========================================================================*/ 147 static unsigned long MainOverlap[MAP_CELL_TOTAL/32]; // overlap list for the main path 148 static unsigned long LeftOverlap[MAP_CELL_TOTAL/32]; // overlap list for the left path 149 static unsigned long RightOverlap[MAP_CELL_TOTAL/32]; // overlap list for the right path 150 151 152 //static CELL MoveMask = 0; 153 static CELL DestLocation; 154 static CELL StartLocation; 155 156 /*************************************************************************** 157 * Point_Relative_To_Line -- Relation between a point and a line * 158 * * 159 * If a point is on a line then the following function holds true: * 160 * (x - x2)(z1 - z2) = (z - z2)(x1 - x2) given x,z a point on the * 161 * line (x1,z1),(x2,z2). * 162 * If the right side is > then the left side then the point is on one * 163 * side of the line and if the right side is < the the left side, then* 164 * the point is on the other side of the line. By subtracting one side* 165 * from the other we can determine on what side (if any) the point is on* 166 * by testing the side of the resulting subtraction. * 167 * * 168 * INPUT: * 169 * int x - x pos of point. * 170 * int z - z pos of point. * 171 * int x1 - x pos of first end of line segment. * 172 * int z1 - z pos of first end of line segment. * 173 * int x1 - x pos of second end of line segment. * 174 * int z1 - z pos of second end of line segment. * 175 * * 176 * OUTPUT: * 177 * Assuming (x1,z1) is north, (x2,z2) is south: * 178 * 0 : point is on line. * 179 * > 0 : point is east of line. * 180 * < 0 : point is west of line. * 181 * * 182 * WARNINGS: * 183 * Remember that int means that assumes 16 bits of precision. * 184 * * 185 * HISTORY: * 186 * 10/28/1994 SKB : Created. * 187 *=========================================================================*/ 188 int Point_Relative_To_Line(int x, int z, int x1, int z1, int x2, int z2) 189 { 190 return((((long)x - (long)x2) * ((long)z1 - (long)z2)) - (((long)z - (long)z2) * ((long)x1 - (long)x2))); 191 } 192 193 194 /*************************************************************************** 195 * FootClass::Unravel_Loop -- Unravels a loop in the movement path * 196 * * 197 * While in the midst of the Follow Edge logic, it is possible (due to the * 198 * fact that we support diagonal movement) to begin looping around a * 199 * column of some type. The Unravel loop function will scan backward * 200 * through the list and fixup the path to try to prevent the loop. * 201 * * 202 * INPUT: path - pointer to the generated path so we can pull the * 203 * commands out of it. * 204 * cell - the cell we tried to enter that generated the * 205 * double overlap condition. * 206 * dir - the direction we tried to enter from when we * 207 * generated the double overlap condition * 208 * startx - the start x position of this path segment * 209 * starty - the start y position of this path segment * 210 * destx - the dest x position for this path segment * 211 * desty - the dest y position for this path segment * 212 * * 213 * OUTPUT: TRUE - loop has been successfully unravelled * 214 * FALSE - loop can not be unravelled so abort follow edge * 215 * * 216 * WARNINGS: none * 217 * * 218 * HISTORY: * 219 * 05/25/1995 PWG : Created. * 220 *=========================================================================*/ 221 bool FootClass::Unravel_Loop(PathType * path, CELL &cell, FacingType &dir, int sx, int sy, int dx, int dy, MoveType threshhold) 222 { 223 /* 224 ** Walk back to the actual cell before we advanced our position 225 */ 226 FacingType curr_dir = dir; 227 CELL curr_pos = Adjacent_Cell(cell, Opposite(curr_dir)); 228 int idx = path->Length; // start at the last position 229 FacingType * list = &path->Command[idx-1]; // point to the last command 230 int checkx; 231 int checky; 232 int last_was_line = false; 233 234 /* 235 ** loop backward through the list searching for a point that is 236 ** on the line. If the point was a diagonal move then adjust 237 ** it. 238 */ 239 while (idx) { 240 checkx = Cell_X(curr_pos); 241 checky = Cell_Y(curr_pos); 242 243 if (!Point_Relative_To_Line(checkx, checky, sx, sy, dx, dy) || last_was_line) { 244 245 /* 246 ** We have now found a point on the line. Now we must check to see 247 ** if we left the line on a diagonal. If we did then we need to fix 248 ** it up. 249 */ 250 if (curr_dir & 1 && curr_pos != path->LastFixup) { 251 cell = curr_pos; 252 dir = *(list-1); 253 path->Length = idx; 254 path->LastFixup = curr_pos; 255 return(true); 256 } 257 258 last_was_line = !last_was_line; 259 } 260 261 /* 262 ** Since this cell will not be in the list, then pull out its cost 263 */ 264 path->Cost -= Passable_Cell(curr_pos, *list, -1, threshhold); 265 266 /* 267 ** Remove this cells flag from the overlap list for the path 268 */ 269 #ifdef TEST 270 path->Overlap[curr_pos >> 5] &= ~(1 << ((curr_pos & 31))); 271 #else 272 path->Overlap[curr_pos >> 5] &= ~(1 << ((curr_pos & 31) - 1)); 273 #endif 274 275 /* 276 ** Adjust to the next list position and direction. 277 */ 278 curr_dir = *list--; 279 curr_pos = Adjacent_Cell(curr_pos, Opposite(curr_dir)); 280 idx--; 281 } 282 283 /* 284 ** If we can't modify the list to eliminate the problem, then we have 285 ** a larger problem in that we have deleted all of the cells in the 286 ** list. 287 */ 288 return(false); 289 } 290 291 292 /*************************************************************************** 293 * Register_Cell -- registers a cell on our path and check for backtrack * 294 * * 295 * This function adds a new cell to our path. If the cell has already * 296 * been recorded as part of our path, then this function moves back down * 297 * the list truncating it at the point we registered that cell. This * 298 * function will eliminate all backtracking from the list. * 299 * * 300 * INPUT: long * list - the list to set the overlap bit for * 301 * CELL cell - the cell to mark on the overlap list * 302 * * 303 * OUTPUT: BOOL - TRUE if bit has been set, FALSE if bit already set * 304 * * 305 * HISTORY: * 306 * 05/23/1995 PWG : Created. * 307 *=========================================================================*/ 308 bool FootClass::Register_Cell(PathType * path, CELL cell, FacingType dir, int cost, MoveType threshhold) 309 { 310 FacingType * list; 311 int pos = cell >> 5; 312 #ifdef TEST 313 int bit = (cell & 31); 314 #else 315 int bit = (cell & 31) - 1; 316 #endif 317 318 /* 319 ** See if this point has already been registered as on the list. If so 320 ** we need to truncate the list back to this point and register the 321 ** new direction. 322 */ 323 if (path->Overlap[pos] & (1 << bit)) { 324 /* 325 ** If this is not a case of immediate back tracking then handle 326 ** by searching the list to see what we find. However is this is 327 ** an immediate back track, then pop of the last direction 328 ** and unflag the cell we are in (not the cell we are moving to). 329 ** Note: That we do not check for a zero length cell because we 330 ** could not have a duplicate unless there are cells in the list. 331 */ 332 333 if (path->Command[path->Length - 1] == Opposite(dir)) { 334 CELL pos = Adjacent_Cell(cell, Opposite(dir)); 335 #ifdef TEST 336 path->Overlap[pos >> 5] &= ~(1 << ((pos & 31))); 337 #else 338 path->Overlap[pos >> 5] &= ~(1 << ((pos & 31) - 1)); 339 #endif 340 path->Length--; 341 } else { 342 /* 343 ** If this overlap is in the same place as we had our last overlap 344 ** then we are in a loop condition. We need to signify that we 345 ** cannot register this cell. 346 */ 347 if (path->LastOverlap == cell) { 348 return(false); 349 } else { 350 path->LastOverlap = cell; 351 } 352 353 CELL pos = path->Start; 354 int newlen = 0; 355 int idx = 0; 356 list = path->Command; 357 358 /* 359 ** Note that the cell has to be in this list, so theres no sense 360 ** in checking whether we found it (famous last words). 361 ** 362 ** PWG 8/16/95 - However there is no sense searching the list if 363 ** the cell we have overlapped on is the cell we 364 ** started in. 365 */ 366 367 if (pos != cell) { 368 while (idx < path->Length) { 369 pos = Adjacent_Cell(pos, *list); 370 if (pos == cell) { 371 idx++; 372 list++; 373 break; 374 } 375 idx++; 376 list++; 377 } 378 newlen = idx; 379 } 380 381 /* 382 ** Now we are pointing at the next command in the list. From here on 383 ** out we need to unmark the fact that we have entered these cells and 384 ** adjust the cost of our path to reflect that we have not entered 385 ** then. 386 */ 387 while (idx < path->Length) { 388 pos = Adjacent_Cell(pos, *list); 389 path->Cost -= Passable_Cell(pos, *list, -1, threshhold); 390 #ifdef TEST 391 path->Overlap[pos >> 5] &= ~(1 << ((pos & 31))); 392 #else 393 path->Overlap[pos >> 5] &= ~(1 << ((pos & 31) - 1)); 394 #endif 395 idx++; 396 list++; 397 } 398 path->Length = newlen; 399 } 400 } else { 401 /* 402 ** Now we need to register the new direction, updating the cell structure 403 ** and the cost. 404 */ 405 int cpos = path->Length++; 406 path->Command[cpos] = dir; // save of the direction we moved 407 path->Cost += cost; // figure new cost for cell 408 path->Overlap[pos] |= (1 << bit); // mark the we have entered point 409 } 410 return(true); 411 } 412 413 414 /*********************************************************************************************** 415 * Find_Path -- Find a path from point a to point b. * 416 * * 417 * INPUT: int source x,y, int destination x,y, char *final moves * 418 * array to store moves, int maximum moves we may attempt * 419 * * 420 * OUTPUT: int number of moves it took (IMPOSSIBLE_MOVES if we could * 421 * not reach the destination * 422 * * 423 * WARNINGS: This algorithm assumes that the target is NOT situated * 424 * inside an impassable. If this case may arise, the do-while * 425 * statement inside the inner while (true) must be changed * 426 * to include a check to se if the next_x,y is equal to the * 427 * dest_x,y. If it is, then return(IMPOSSIBLE_MOVES). * 428 * * 429 * HISTORY: * 430 * 07/08/1991 CY : Created. * 431 *=============================================================================================*/ 432 PathType * FootClass::Find_Path(CELL dest, FacingType * final_moves, int maxlen, MoveType threshhold) 433 { 434 CELL source = Coord_Cell(Coord); // Source expressed as cell 435 static PathType path; // Main path control. 436 CELL next; // Next cell to enter 437 CELL startcell; // Cell we started in 438 FacingType direction; // Working direction of look ahead. 439 FacingType newdir; // Tentative facing value. 440 441 bool left=false, // Was leftward path legal? 442 right=false; // Was rightward path legal? 443 444 int len; // Length of detour command list. 445 int unit_threat; // Calculated unit threat rating 446 int cost; // Cost to enter the square 447 FacingType moves_left[MAX_MLIST_SIZE+2], // Counterclockwise move list. 448 moves_right[MAX_MLIST_SIZE+2]; // Clockwise move list. 449 PathType pleft,pright; // Path control structures. 450 PathType * which; // Which path to actually use. 451 int threat = 0; // 452 int threat_stage = 0; //These weren't initialized. ST - 1/8/2019 12:03PM 453 454 455 /* 456 ** If we have been provided an illegal place to store our final moves 457 ** then forget it. 458 */ 459 if (!final_moves) return(NULL); 460 461 BStart(BENCH_FINDPATH); 462 463 PathCount++; 464 465 if (Team && Team->Class->IsRoundAbout) { 466 unit_threat = (Team) ? Team->Risk : Risk(); 467 threat_stage = 0; 468 threat = 0; 469 } else { 470 unit_threat = threat = -1; 471 } 472 473 StartLocation = source; 474 DestLocation = dest; 475 476 /* 477 ** Initialize the path structure so that we can keep track of the 478 ** path. 479 */ 480 path.Start = source; 481 path.Cost = 0; 482 path.Length = 0; 483 path.Command = final_moves; 484 path.Command[0] = END; 485 path.Overlap = MainOverlap; 486 path.LastOverlap = -1; 487 path.LastFixup = -1; 488 489 memset(path.Overlap, 0, sizeof(MainOverlap)); 490 491 /* 492 ** Clear the over lap list and then make sure that our starting position is marked 493 ** on the overlap list. (Otherwise the harvesters will drive in circles... ) 494 */ 495 #ifdef TEST 496 path.Overlap[source >> 5] |= (1 << ((source & 31))); 497 #else 498 path.Overlap[source >> 5] |= (1 << ((source & 31) - 1)); 499 #endif 500 501 startcell = source; 502 503 /* 504 ** Account for trailing end of list command, so reduce the maximum 505 ** allowed legal commands to reflect this. 506 */ 507 maxlen--; 508 509 /* 510 ** As long as there is room to put commands in the movement command list, 511 ** then put commands in it. We build the path using the following 512 ** methodology. 513 ** 514 ** 1. Scan through the desired straight line path until we either hit an 515 ** impassable or have created a valid path. 516 ** 517 ** 2. If we have hit an impassable, walk through the impassable to make 518 ** sure that there is a passable on the other side. If there is not 519 ** and we can not change the impassable, then this list is dead. 520 ** 521 ** 3. Walk around the impassable on both the left and right edges and 522 ** take the shorter of the two paths. 523 ** 524 ** 4. Taking the new location as our start location start again with 525 ** step #1. 526 */ 527 while (path.Length < maxlen) { 528 529 top_of_list: 530 /* 531 ** Have we reached the destination already? If so abort any further 532 ** command building. 533 */ 534 if (startcell == dest) { 535 break; 536 } 537 538 /* 539 ** Find the absolute correct direction to reach the next straight 540 ** line cell and what cell it is. 541 */ 542 direction = CELL_FACING(startcell, dest); 543 next = Adjacent_Cell(startcell, direction); 544 545 /* 546 ** If we can move here, then make this our next move. 547 */ 548 cost = Passable_Cell(next, direction, threat, threshhold); 549 if (cost) { 550 Register_Cell(&path, next, direction, cost, threshhold); 551 } else { 552 /* 553 ** If the impassable location is actually the destination, 554 ** then stop here and consider this "good enough". 555 */ 556 if (next == dest) break; 557 558 /* 559 ** We could not move to the next cell, so follow through the 560 ** impassable until we find a passable spot that can be reached. 561 ** Once we find a passable, figure out the shortest path to it. 562 ** Since we have variable passable conditions this is not as 563 ** simple as it used to be. The limiter loop below allows us to 564 ** step through ten doughnuts before we give up. 565 */ 566 for (int limiter = 0; limiter < 5; limiter++) { 567 568 /* 569 ** Get the next passable position by zipping through the 570 ** impassable positions until a passable position is found 571 ** or the destination is reached. 572 */ 573 for (;;) { 574 575 /* 576 ** Move one step closer toward destination. 577 */ 578 newdir = CELL_FACING(next, dest); 579 next = Adjacent_Cell(next, newdir); 580 581 /* 582 ** If the cell is passable then we have been completely 583 ** successful. If the cell is not passable then continue. 584 */ 585 if (Passable_Cell(next, FACING_NONE, threat, threshhold)) { 586 // if ((Passable_Cell(next, FACING_NONE, threat, threshhold)) || (next == dest)) { 587 break; 588 } 589 590 /* 591 ** If we reached destination while in this loop, we 592 ** know that either the destination is impassible (if 593 ** we are ignoring) or that we need to up our threat 594 ** tolerance and try again. 595 */ 596 if (next == dest) { 597 if (threat != -1) { 598 switch (threat_stage++) { 599 case 0: 600 threat = unit_threat >> 1; 601 break; 602 603 case 1: 604 threat += unit_threat; 605 break; 606 607 case 2: 608 threat = -1; 609 break; 610 } 611 goto top_of_list; 612 } 613 goto end_of_list; 614 } 615 } 616 617 /* 618 ** Try to find a path to the passable position by following 619 ** the edge of the blocking object in both CLOCKwise and 620 ** COUNTERCLOCKwise fashions. 621 */ 622 int follow_len = maxlen + (maxlen >> 1); 623 624 Mem_Copy(&path, &pleft, sizeof(PathType)); 625 pleft.Command = &moves_left[0]; 626 pleft.Overlap = LeftOverlap; 627 Mem_Copy(path.Command, pleft.Command, path.Length); 628 Mem_Copy(path.Overlap, pleft.Overlap, sizeof(LeftOverlap)); 629 630 // MBL 09.30.2019: We hit a runtime bounds crash where END (-1 / 0xFF) was being poked into +1 just past the end of the moves_right[] array; 631 // The FacingType moves_left[] and moves_right[] arrays already have MAX_MLIST_SIZE+2 as their size, which may have been a previous attempted fix; 632 // We are now passing MAX_MLIST_SIZE, since the sizeof calculations included the +2 buffering; 633 #if 0 634 left = Follow_Edge(startcell, next, &pleft, COUNTERCLOCK, direction, threat, threat_stage, sizeof(moves_left)/sizeof(moves_left[0]), threshhold); 635 // left = Follow_Edge(startcell, next, &pleft, COUNTERCLOCK, direction, threat, threat_stage, follow_len, threshhold); 636 #endif 637 left = Follow_Edge(startcell, next, &pleft, COUNTERCLOCK, direction, threat, threat_stage, MAX_MLIST_SIZE, threshhold); 638 639 640 if (left) { 641 follow_len = min(maxlen, pleft.Length + (pleft.Length >> 1)); 642 } 643 644 Mem_Copy(&path, &pright, sizeof(PathType)); 645 pright.Command = &moves_right[0]; 646 pright.Overlap = RightOverlap; 647 Mem_Copy(path.Command, pright.Command, path.Length); 648 Mem_Copy(path.Overlap, pright.Overlap, sizeof(RightOverlap)); 649 650 // MBL 09.30.2019: We hit a runtime bounds crash where END (-1 / 0xFF) was being poked into +1 just past the end of the moves_right[] array; 651 // The FacingType moves_left[] and moves_right[] arrays already have MAX_MLIST_SIZE+2 as their size, which may have been a previous attempted fix; 652 // We are now passing MAX_MLIST_SIZE, since the sizeof calculations included the +2 buffering; 653 #if 0 654 right = Follow_Edge(startcell, next, &pright, CLOCK, direction, threat, threat_stage, sizeof(moves_right)/sizeof(moves_right[0]), threshhold); 655 // right = Follow_Edge(startcell, next, &pright, CLOCK, direction, threat, threat_stage, follow_len, threshhold); 656 #endif 657 right = Follow_Edge(startcell, next, &pright, CLOCK, direction, threat, threat_stage, MAX_MLIST_SIZE, threshhold); 658 659 /* 660 ** If we could find a path, break from this loop. Otherwise this 661 ** means that we have found a "hole" of passable terrain that 662 ** cannot be reached by normal means. Scan forward looking for 663 ** the other side of the "doughnut". 664 */ 665 if (left || right) break; 666 667 /* 668 ** If no path can be found to the intermediate cell, then 669 ** presume we have found a doughnut of some sort. Scan 670 ** forward until the next impassable is found and then 671 ** process this loop again. 672 */ 673 do { 674 675 /* 676 ** If we reached destination while in this loop, we 677 ** know that either the destination is impassible (if 678 ** we are ignoring) or that we need to up our threat 679 ** tolerance and try again. 680 */ 681 if (next == dest) { 682 if (threat != -1) { 683 switch (threat_stage++) { 684 case 0: 685 threat = unit_threat >> 1; 686 break; 687 688 case 1: 689 threat += unit_threat; 690 break; 691 692 case 2: 693 threat = -1; 694 break; 695 } 696 goto top_of_list; 697 } 698 goto end_of_list; 699 } 700 701 newdir = CELL_FACING(next, dest); 702 next = Adjacent_Cell(next, newdir); 703 } while (Passable_Cell(next, newdir, threat, threshhold)); 704 } 705 706 if (!left && !right) break; 707 708 /* 709 ** We found a path around the impassable locations, so figure out 710 ** which one was the smallest and copy those moves into the 711 ** path.Command array. 712 */ 713 which = &pleft; 714 if (right) { 715 which = &pright; 716 if (left) { 717 if (pleft.Length < pright.Length) { 718 which = &pleft; 719 } else { 720 which = &pright; 721 } 722 } 723 } 724 725 /* 726 ** Record as much as possible of the shorter of the two 727 ** paths. The trailing EOL command is not copied because 728 ** this may not be the end of the find path logic. 729 */ 730 len = which->Length; 731 len = min(len, maxlen); 732 if (len > 0) { 733 memcpy(&path.Overlap[0], &which->Overlap[0], sizeof(LeftOverlap)); 734 memcpy(&path.Command[0], &which->Command[0], len * sizeof(FacingType)); 735 path.Length = len; 736 path.Cost = which->Cost; 737 path.LastOverlap = -1; 738 path.LastFixup = -1; 739 } else { 740 break; 741 } 742 } 743 startcell = next; 744 } 745 746 end_of_list: 747 /* 748 ** Poke in the stop command. 749 */ 750 if (path.Length < maxlen) { 751 path.Command[path.Length++] = END; 752 } 753 754 /* 755 ** Optimize the move list but only necessary if 756 ** diagonal moves are allowed. 757 */ 758 #ifdef DIAGONAL 759 Optimize_Moves(&path, threshhold); 760 #endif 761 762 BEnd(BENCH_FINDPATH); 763 764 return(&path); 765 } 766 767 768 /*********************************************************************************************** 769 * Follow_Edge -- Follow an edge to get around an impassable spot. * 770 * * 771 * INPUT: start -- cell to head from * 772 * * 773 * target -- Target cell to head to. * 774 * * 775 * path -- Pointer to path list structure. * 776 * * 777 * search -- Direction of search (1=clock, -1=counterclock). * 778 * * 779 * olddir -- Facing impassible direction from start. * 780 * * 781 * callback -- Function pointer for determining if a cell is * 782 * passable or not. * 783 * * 784 * OUTPUT: bool: Could a path be found to the desired cell? * 785 * * 786 * WARNINGS: none * 787 * * 788 * HISTORY: * 789 * 07/08/1991 CY : Created. * 790 * 06/01/1992 JLB : Optimized & commented. * 791 *=============================================================================================*/ 792 bool FootClass::Follow_Edge(CELL start, CELL target, PathType * path, FacingType search, FacingType olddir, int threat, int , int max_cells, MoveType threshhold) 793 { 794 FacingType newdir; // Direction of facing before surrounding cell check. 795 CELL oldcell, // Current cell. 796 newcell; // Tentative new cell. 797 int cost; // Working cost value. 798 int startx; 799 int starty; 800 int online=true; 801 int targetx; 802 int targety; 803 int oldval = 0; 804 int cellcount=0; 805 int forceout = false; 806 FacingType firstdir = (FacingType)-1; 807 CELL firstcell = -1; 808 bool stepped_off_line = false; 809 startx = Cell_X(start); 810 starty = Cell_Y(start); 811 targetx = Cell_X(target); 812 targety = Cell_Y(target); 813 814 if (!path) return(false); 815 path->LastOverlap = -1; 816 path->LastFixup = -1; 817 818 #ifndef DIAGONAL 819 /* 820 ** The edge following algorithm doesn't "do" diagonals. Force initial facing 821 ** to be an even 90 degree value. Adjust it in the direction it should be 822 ** rotating. 823 */ 824 if (olddir & 0x01) { 825 olddir = Next_Direction(olddir, search); 826 } 827 #endif 828 829 newdir = Next_Direction(olddir, search); 830 oldcell = start; 831 newcell = Adjacent_Cell(oldcell, newdir); 832 833 /* 834 ** Continue until we find our target, find our original starting spot, 835 ** or run out of moves. 836 */ 837 while (path->Length < max_cells) { 838 839 /* 840 ** Look in all the adjacent cells to determine a passable one that 841 ** most closely matches the desired direction (working in the specified 842 ** direction). 843 */ 844 newdir = olddir; 845 for (;;) { 846 bool forcefail; // Is failure forced? 847 848 forcefail = false; 849 850 #ifdef DIAGONAL 851 /* 852 ** Rotate 45/90 degrees in desired direction. 853 */ 854 newdir = Next_Direction(newdir, search); 855 856 /* 857 ** If facing a diagonal we must check the next 90 degree location 858 ** to make sure that we don't walk right by the destination. This 859 ** will happen if the destination it is at the corner edge of an 860 ** impassable that we are moving around. 861 */ 862 if (newdir & FACING_NE) { 863 CELL checkcell; // Non-diagonal check cell. 864 //int x,y; 865 866 checkcell = Adjacent_Cell(oldcell, Next_Direction(newdir, search)); 867 868 if (checkcell == target) { 869 870 /* 871 ** This only works if in fact, it is possible to move to the 872 ** cell from the current location. 873 */ 874 cost = Passable_Cell(checkcell, Next_Direction(newdir, search), threat, threshhold); 875 if (cost) { 876 /* 877 ** YES! The destination is at the corner of an impassable, so 878 ** set the direction to point directly at it and then the 879 ** scanning will terminate later. 880 */ 881 newdir = Next_Direction(newdir, search); 882 newcell = Adjacent_Cell(oldcell, newdir); 883 break; 884 } 885 } 886 887 /* 888 ** Perform special diagonal check. If the edge follower would cross the 889 ** diagonal or fall on the diagonal line from the source, then consider 890 ** that cell impassible. Otherwise, the find path algorithm will fail 891 ** when there are two impassible locations located on a diagonal 892 ** that is lined up between the source and destination location. 893 ** 894 ** P.S. It might help if you check the right cell rather than using 895 ** the value that just happened to be in checkcell. 896 */ 897 898 checkcell = Adjacent_Cell(oldcell, newdir); 899 900 int checkx = Cell_X(checkcell); 901 int checky = Cell_Y(checkcell); 902 int checkval = Point_Relative_To_Line(checkx, checky, startx, starty, targetx, targety); 903 if (checkval && !online) { 904 forcefail = ((checkval ^ oldval) < 0); 905 } else { 906 forcefail = false; 907 } 908 /* 909 ** The only exception to the above is when we are directly backtracking 910 ** because we could be trying to escape from a culdesack! 911 */ 912 if (forcefail && path->Length > 0 && (FacingType)(newdir ^ 4) == path->Command[path->Length - 1]) { 913 forcefail = false; 914 } 915 } 916 917 #else 918 newdir = Next_Direction(newdir, search*2); 919 #endif 920 921 /* 922 ** If we have just checked the same heading we started with, 923 ** we are surrounded by impassable characters and we exit. 924 */ 925 if (newdir == olddir) { 926 return(false); 927 } 928 929 /* 930 ** Get the new cell. 931 */ 932 newcell = Adjacent_Cell(oldcell, newdir); 933 934 /* 935 ** If we found a passable position, this is where we should move. 936 */ 937 if (!forcefail && ((cost = Passable_Cell(newcell, newdir, threat, threshhold)) != 0)) { 938 break; 939 } else { 940 if (newcell == target) { 941 forceout = true; 942 break; 943 } 944 } 945 946 } 947 948 /* 949 ** Record the direction. 950 */ 951 if (!forceout) { 952 /* 953 ** Mark the cell because this is where we need to be. If register 954 ** cell fails then the list has been shortened and we need to adjust 955 ** the new direction. 956 */ 957 if (!Register_Cell(path, newcell, newdir, cost, threshhold)) { 958 /* 959 ** The only reason we could not register a cell is that we are in 960 ** a looping situation. So we need to try and unravel the loop if 961 ** we can. 962 */ 963 if (!Unravel_Loop(path, newcell, newdir, startx, starty, targetx, targety, threshhold)) { 964 return(false); 965 } 966 /* 967 ** Since we need to eliminate a diagonal we must pretend the upon 968 ** attaining this square, we were moving turned further in the 969 ** search direction then we really were. 970 */ 971 newdir = Next_Direction(newdir, (FacingType)(search*2)); 972 } 973 /* 974 ** Find out which side of the line this cell is on. If it is on 975 ** a side, then store off that side. 976 */ 977 int newx = Cell_X(newcell); 978 int newy = Cell_Y(newcell); 979 int val = Point_Relative_To_Line(newx, newy, startx, starty, targetx, targety); 980 if (val) { 981 oldval = val; 982 online = false; 983 } else { 984 online = true; 985 } 986 cellcount++; 987 if (cellcount == MAX_PATH_EDGE_FOLLOW) { 988 return(false); 989 } 990 } 991 992 /* 993 ** If we have found the target spot, we are done. 994 */ 995 if (newcell == target) { 996 path->Command[path->Length] = END; 997 return(true); 998 } 999 1000 /* 1001 ** If we make a full circle back to our original spot, get out. 1002 */ 1003 if (newcell == firstcell && newdir == firstdir) { 1004 return(false); 1005 } 1006 1007 if (firstcell == -1) { 1008 firstcell = newcell; 1009 firstdir = newdir; 1010 } 1011 1012 /* 1013 ** Because we moved, our facing is now incorrect. We want to face toward 1014 ** the impassable edge we are following (well, not actually toward, but 1015 ** a little past so that we can turn corners). We have to turn 45/90 degrees 1016 ** more than expected in anticipation of the pending 45/90 degree turn at 1017 ** the start of this loop. 1018 */ 1019 #ifdef DIAGONAL 1020 olddir = Next_Direction(newdir, (FacingType)(-(int)search*3)); 1021 #else 1022 olddir = Next_Direction(newdir, (FacingType)(-(int)search*4)); 1023 #endif 1024 oldcell = newcell; 1025 } 1026 1027 /* 1028 ** The maximum search path is exhausted... abort with a failure. 1029 */ 1030 return(false); 1031 } 1032 1033 1034 /*********************************************************************************************** 1035 * Optimize_Moves -- Optimize the move list. * 1036 * * 1037 * INPUT: char *moves to optimize * 1038 * * 1039 * OUTPUT: none (list is optimized) * 1040 * * 1041 * WARNINGS: EMPTY moves are used to hold the place of eliminated * 1042 * commands. Also, NEVER call this routine with a list that * 1043 * contains illegal commands. The list MUST be terminated * 1044 * with a EOL command * 1045 * * 1046 * HISTORY: * 1047 * 07/08/1991 CY : Created. * 1048 * 06/01/1992 JLB : Optimized and commented. * 1049 *=============================================================================================*/ 1050 #define EMPTY (FacingType)-2 1051 int FootClass::Optimize_Moves(PathType * path, MoveType threshhold) 1052 //int Optimize_Moves(PathType *path, int (*callback)(CELL, FacingType), int threshold) 1053 { 1054 /* 1055 ** Facing command pair adjustment table. Compare the facing difference between 1056 ** the two commands. 0 means no optimization is possible. 3 means backtracking 1057 ** so eliminate both commands. Any other value adjusts the first command facing. 1058 */ 1059 #ifdef DIAGONAL 1060 static FacingType _trans[FACING_COUNT] = {(FacingType)0, (FacingType)0, (FacingType)1, (FacingType)2, (FacingType)3, (FacingType)-2, (FacingType)-1, (FacingType)0}; // Smoothing. 1061 #else 1062 static FacingType _trans[FACING_COUNT] = {(FacingType)0, (FacingType)0, (FacingType)0, (FacingType)2, (FacingType)3, (FacingType)-2, (FacingType)0, (FacingType)0}; 1063 #endif 1064 FacingType * cmd1, // Floating first command pointer. 1065 * cmd2, // Floating second command pointer. 1066 newcmd; // Calculated new optimized command. 1067 FacingType newdir; // Tentative new direction for smoothing. 1068 CELL cell; // Working cell (as it moves along path). 1069 1070 /* 1071 ** Abort if there is any illegal parameter. 1072 */ 1073 if (!path || !path->Command) return(0); 1074 1075 /* 1076 ** Optimization loop -- start scanning with the 1077 ** first pair of commands (if there are at least two 1078 ** in the command list). 1079 */ 1080 path->Command[path->Length] = END; // Force end of list. 1081 1082 if (path->Length == 0) return(0); 1083 1084 cell = path->Start; 1085 if (path->Length > 1) { 1086 cmd2 = path->Command + 1; 1087 while (*cmd2 != END) { 1088 1089 /* 1090 ** Set the cmd1 pointer to point to the valid command closest, but 1091 ** previous to cmd2. Be sure not to go previous to the head of the 1092 ** command list. 1093 */ 1094 cmd1 = cmd2-1; 1095 while (*cmd1 == EMPTY && cmd1 != path->Command) { 1096 cmd1--; 1097 } 1098 1099 /* 1100 ** If there isn't any valid previous command, then bump the 1101 ** cmd pointers to the next command pair and continue... 1102 */ 1103 if (*cmd1 == EMPTY) { 1104 cmd2++; 1105 continue; 1106 } 1107 1108 /* 1109 ** Fetch precalculated command change value. 0 means leave 1110 ** command set alone, 3 means backtrack and eliminate two 1111 ** commands. Any other value is new direction and eliminate 1112 ** one command. 1113 */ 1114 newcmd = (FacingType)(*cmd2 - *cmd1); 1115 if (newcmd < FACING_N) newcmd = (FacingType)(newcmd + FACING_COUNT); 1116 newcmd = _trans[newcmd]; 1117 1118 /* 1119 ** Check for backtracking. If this occurs, then eliminate the 1120 ** two commands. This is the easiest optimization. 1121 */ 1122 if (newcmd == FACING_SE) { 1123 *cmd1 = EMPTY; 1124 *cmd2++ = EMPTY; 1125 continue; 1126 } 1127 1128 /* 1129 ** If an optimization code was found the process it. The command is a facing 1130 ** offset to more directly travel toward the immediate destination cell. 1131 */ 1132 if (newcmd) { 1133 1134 /* 1135 ** Optimizations differ when dealing with diagonals. Especially when dealing 1136 ** with diagonals of 90 degrees. In such a case, 90 degree optimizations can 1137 ** only be optimized if the intervening cell is passable. The distance travelled 1138 ** is the same, but the path is less circuitous. 1139 */ 1140 if (*cmd1 & FACING_NE) { 1141 1142 /* 1143 ** Diagonal optimizations are always only 45 1144 ** degree adjustments. 1145 */ 1146 newdir = Next_Direction(*cmd1, (newcmd < FACING_N) ? (FacingType)-1 : (FacingType)1); 1147 1148 /* 1149 ** Diagonal 90 degree changes can be smoothed, although 1150 ** the path isn't any shorter. 1151 */ 1152 if (ABS((int)newcmd) == 1) { 1153 if (Passable_Cell(Adjacent_Cell(cell, newdir), newdir, -1, threshhold)) { 1154 *cmd2 = newdir; 1155 *cmd1 = newdir; 1156 } 1157 // BOB 16.12.92 1158 cell = Adjacent_Cell(cell, *cmd1); 1159 cmd2++; 1160 continue; 1161 } 1162 } else { 1163 newdir = Next_Direction(*cmd1, newcmd); 1164 } 1165 1166 /* 1167 ** Allow shortening turn only on right angle moves that are based on 1168 ** 90 degrees. Always allow 135 degree optimizations. 1169 */ 1170 *cmd2 = newdir; 1171 *cmd1 = EMPTY; 1172 1173 /* 1174 ** Backup what it thinks is the current cell. 1175 */ 1176 while (*cmd1 == EMPTY && cmd1 != path->Command) { 1177 cmd1--; 1178 } 1179 if (*cmd1 != EMPTY) { 1180 cell = Adjacent_Cell(cell, Next_Direction(*cmd1, FACING_S)); 1181 } else { 1182 cell = path->Start; 1183 } 1184 continue; 1185 } 1186 1187 /* 1188 ** Since we could not make an optimization, we move our 1189 ** head pointer forward. 1190 */ 1191 cell = Adjacent_Cell(cell, *cmd1); 1192 cmd2++; 1193 } 1194 } 1195 1196 /* 1197 ** Pack the command list to remove any EMPTY command entries. 1198 */ 1199 cmd1 = path->Command; 1200 cmd2 = path->Command; 1201 cell = path->Start; 1202 path->Cost = 0; 1203 path->Length = 0; 1204 while (*cmd2 != END) { 1205 if (*cmd2 != EMPTY) { 1206 cell = Adjacent_Cell(cell, *cmd2); 1207 path->Cost+= Passable_Cell(cell, *cmd2, -1, threshhold); 1208 path->Length++; 1209 *cmd1++ = *cmd2; 1210 } 1211 cmd2++; 1212 } 1213 path->Length++; 1214 *cmd1 = END; 1215 return(path->Length); 1216 } 1217 1218 1219 CELL FootClass::Safety_Point(CELL src, CELL dst, int start, int max) 1220 { 1221 FacingType dir; 1222 CELL next; 1223 int lp; 1224 1225 dir = (FacingType)(CELL_FACING(src, dst) ^ 4) - 1; 1226 1227 /* 1228 ** Loop through the different acceptable distances. 1229 */ 1230 for (int dist = start; dist < max; dist ++) { 1231 1232 /* 1233 ** Move to the starting location. 1234 */ 1235 next = dst; 1236 1237 for (lp = 0; lp < dist; lp ++) { 1238 next = Adjacent_Cell(next, dir); 1239 } 1240 1241 if (dir & 1) { 1242 /* 1243 ** If our direction is diagonal than we need to check 1244 ** only one side which is as long as both of the old sides 1245 ** together. 1246 */ 1247 for (lp = 0; lp < dist << 1; lp ++) { 1248 next = Adjacent_Cell(next, dir + 3); 1249 if (!Can_Enter_Cell(next)) { 1250 return(next); 1251 } 1252 } 1253 } else { 1254 /* 1255 ** If our direction is not diagonal than we need to check two 1256 ** sides so that we are checking a corner like location. 1257 */ 1258 for (lp = 0; lp < dist; lp ++) { 1259 next = Adjacent_Cell(next, dir + 2); 1260 if (!Can_Enter_Cell(next)) { 1261 return(next); 1262 } 1263 } 1264 1265 for (lp = 0; lp < dist; lp ++) { 1266 next = Adjacent_Cell(next, dir + 4); 1267 if (!Can_Enter_Cell(next)) { 1268 return(next); 1269 } 1270 } 1271 } 1272 } 1273 return(-1); 1274 } 1275 1276 1277 1278 1279 int FootClass::Passable_Cell(CELL cell, FacingType face, int threat, MoveType threshhold) 1280 { 1281 MoveType move = Can_Enter_Cell(cell, face); 1282 1283 if (move < MOVE_MOVING_BLOCK && Distance(Cell_Coord(cell)) > 0x0100) threshhold = MOVE_MOVING_BLOCK; 1284 1285 if (move > threshhold) return(0); 1286 1287 if (Session.Type == GAME_NORMAL) { 1288 if (threat != -1) { 1289 if (::Distance(Cell_Coord(cell), Cell_Coord(DestLocation)) > (THREAT_THRESHOLD * CELL_LEPTON_W)) { 1290 // if (Map.Cell_Distance(cell, DestLocation) > THREAT_THRESHOLD) { 1291 if (Map.Cell_Threat(cell, Owner()) > threat) 1292 return(0); 1293 } 1294 } 1295 } 1296 1297 static int _value[MOVE_COUNT] = { 1298 1, // MOVE_OK 1299 1, // MOVE_CLOAK 1300 3, // MOVE_MOVING_BLOCK 1301 8, // MOVE_DESTROYABLE 1302 10, // MOVE_TEMP 1303 0 // MOVE_NO 1304 }; 1305 return(_value[move]); 1306 } 1307