CnC_Remastered_Collection

Command and Conquer: Red Alert
Log | Files | Refs | README | LICENSE

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 }