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