1 /*
2 
3 Copyright (C) 2015-2018 Night Dive Studios, LLC.
4 
5 This program is free software: you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation, either version 3 of the License, or
8 (at your option) any later version.
9 
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 GNU General Public License for more details.
14 
15 You should have received a copy of the GNU General Public License
16 along with this program.  If not, see <http://www.gnu.org/licenses/>.
17 
18 */
19 /*
20  * $Source: r:/prj/cit/src/RCS/pathfind.c $
21  * $Revision: 1.13 $
22  * $Author: xemu $
23  * $Date: 1994/09/06 23:44:56 $
24  */
25 
26 #define __PATHFIND_SRC
27 
28 #include <assert.h>
29 #include <string.h>
30 
31 #include "pathfind.h"
32 #include "player.h"
33 #include "faketime.h"
34 #include "tilename.h"
35 #include "otrip.h"
36 #include "objgame.h"
37 #include "objprop.h"
38 #include "gameobj.h"
39 #include "objbit.h"
40 #include "cybmem.h"
41 #include "doorparm.h"
42 
43 //------------
44 //  PROTOTYPES
45 //------------
46 errtype find_path(char path_id);
47 short tile_height(MapElem *pme, char dir, uchar floor);
48 uchar pf_obj_height(MapElem *pme, uchar old_z);
49 
50 // So, the paradigm is that a given creature makes a pathfind request
51 // At the next suitable time, the pathfinder fills in all the pending requests
52 // If you want a step from your path and your request hasn't been filled yet,
53 // then you just wait.
54 
55 // Each path is a Source location and a set of NSEW movements, 64 move locations
56 // There are MAX_PATHS available paths, so if lots of things are already pathfinding
57 // then new requests cannot be made.
58 
59 // Which char is it?
60 #define HIGH_STEP(stepnum) ((stepnum) >> 2)
61 #define LOW_STEP(stepnum) ((stepnum)&0x3)
62 #define PATH_CHAR(pathid, stepnum) (paths[(pathid)].moves[HIGH_STEP(stepnum)])
63 #define PATH_STEP(pathid, stepnum) ((PATH_CHAR(pathid, stepnum) >> (LOW_STEP(stepnum) << 1)) & 0x3)
64 // Good god, there has got to be a better way to do this! -- Rob
65 // Clear out the old set of 2 bits, and or in the new 2 bits
66 #define SET_PATH_STEP(pathid, stepnum, newval)                                                                        \
67     do {                                                                                                              \
68         PATH_CHAR(pathid, stepnum) =                                                                                  \
69             PATH_CHAR(pathid, stepnum) & ~(0x3 << (LOW_STEP(stepnum) << 1)) | ((newval) << (LOW_STEP(stepnum) << 1)); \
70     } while (0)
71 #define CLEAR_PATH(pathid)                                                 \
72     do {                                                                   \
73         LG_memset((void *)(paths[(pathid)].moves), 0, NUM_PATH_STEPS / 4); \
74     } while (0)
75 
76 // Note that a dest_z of 0 means to ignore that whole concept
77 // dest_z and start_z are in objLoc height coordinates, since that is the easiest API
request_pathfind(LGPoint source,LGPoint dest,uchar dest_z,uchar start_z,uchar priority)78 char request_pathfind(LGPoint source, LGPoint dest, uchar dest_z, uchar start_z, uchar priority) {
79     char i = 0;
80 
81     // Find the first available path number
82     while ((i < MAX_PATHS) && (used_paths & (1 << i)))
83         i++;
84 
85     // If no paths free, return -1;
86     if (i == MAX_PATHS) {
87         return (-1);
88     }
89     if (PointsEqual(source, dest)) {
90         return (-1);
91     }
92 
93     // Grab the path
94     used_paths |= (1 << i);
95 
96     // Init the path struct
97     paths[i].source = source;
98     paths[i].dest = dest;
99     // Path height units and API height units are the same, so don't
100     paths[i].dest_z = dest_z;
101     paths[i].start_z = start_z;
102     paths[i].num_steps = priority ? -2 : -1;
103     paths[i].curr_step = -1; // to indicate that we haven't been filled yet
104 
105     // clear it
106     CLEAR_PATH(i);
107 
108     // go
109     return (i);
110 }
111 
112 // Updates pt to reflect the next step on path path_id from step number step_num.
113 // pt must already reflect the location reached by the path in step_num steps.
114 // A step_num of -1 indicates to use the curr_step contained in the path.
115 // Unlike next_step_on_path, this procedure does not affect the paths array at all,
116 // although it does modify it's pt parameter.
117 // Returns direction of that next step
compute_next_step(char path_id,LGPoint * pt,char step_num)118 char compute_next_step(char path_id, LGPoint *pt, char step_num) {
119     char movecode = -22;
120     if (step_num == -1)
121         step_num = paths[path_id].curr_step;
122     if (step_num != -1) {
123         movecode = PATH_STEP(path_id, paths[path_id].num_steps - step_num - 1);
124         switch (movecode) {
125         case 0:
126             pt->y++;
127             break; // N
128         case 1:
129             pt->x++;
130             break; // E
131         case 2:
132             pt->y--;
133             break; // S
134         case 3:
135             pt->x--;
136             break; // W
137         default:
138             break;
139         }
140     }
141     return (movecode);
142 }
143 
144 // Note that this is NOT an idempotent procedure.... as you call it
145 // it moves "source" along the path and increments the path step
146 // Since we still have the path information, it is possible to go
147 // backwards along the path, but I won't write that until it seems
148 // needed.
149 // Returns the direction one travels in to get to this next step
next_step_on_path(char path_id,LGPoint * next,char * steps_left)150 char next_step_on_path(char path_id, LGPoint *next, char *steps_left) {
151     char retval;
152     *next = paths[path_id].source;
153     retval = compute_next_step(path_id, next, -1); // -1 for "use path data"
154     paths[path_id].source = *next;
155     paths[path_id].curr_step++;
156     if (PointsEqual(*next, paths[path_id].dest)) {
157         *steps_left = 0;
158     } else {
159         *steps_left = paths[path_id].num_steps - paths[path_id].curr_step;
160     }
161     return (retval);
162 }
163 
164 #define LOOKAHEAD_STEPS 3
165 // Checks whether or not we have skipped ahead to some square within LOOKAHEAD_STEPS
166 // of the "current" location for the specified path.  If so, jumps the path to that point and
167 // returns TRUE.
check_path_cutting(LGPoint new_sq,char path_id)168 uchar check_path_cutting(LGPoint new_sq, char path_id) {
169     LGPoint pt;
170     char count;
171     // Hey, first check if we've finished the darn path...
172     if ((path_id == -1) || (paths[path_id].num_steps == 0))
173         return (FALSE);
174     if (PointsEqual(new_sq, paths[path_id].dest)) {
175         paths[path_id].num_steps = 0;
176         return (TRUE);
177     }
178 
179     // Now do the standard lookahead check...
180     pt = paths[path_id].source;
181     for (count = 0; count < LOOKAHEAD_STEPS; count++) {
182         compute_next_step(path_id, &pt, paths[path_id].curr_step + count);
183         if (PointsEqual(pt, new_sq)) {
184             // hey look, we cut ahead to a more advanced point in our pathfinding...
185             paths[path_id].source = new_sq;
186             paths[path_id].curr_step += count + 1;
187             return (TRUE);
188         }
189     }
190 
191     // We didn't find anything, how sad.
192     return (FALSE);
193 }
194 
195 #define PATHFIND_INTERVAL (CIT_CYCLE >> 2)
196 
197 // We could probably save a ulong by just doing this strictly on
198 // the clock rather than actually doing it right.
199 ulong last_pathfind_time = 0;
200 // priority means only check priority requests, but always check
check_requests(uchar priority_only)201 errtype check_requests(uchar priority_only) {
202     char i;
203     // Don't bother checking unless it has been at least N ticks,
204     if (priority_only || (player_struct.game_time > last_pathfind_time)) {
205         // If it is time to check, go through all the paths, find the
206         // unfilled requests among them, and go satisfy them.
207         for (i = 0; i < MAX_PATHS; i++) {
208             if (paths[i].num_steps == -2)
209                 find_path(i);
210             if (!priority_only) {
211                 if (paths[i].num_steps == -1)
212                     find_path(i);
213             }
214         }
215 
216         // Set requirements for next cycle of checking
217         if (!priority_only)
218             last_pathfind_time = player_struct.game_time + PATHFIND_INTERVAL;
219     }
220     return (OK);
221 }
222 
delete_path(char path_id)223 errtype delete_path(char path_id) {
224     // To delete, just mark the path as unused and zero out its data
225     if ((path_id < 0) || (path_id >= MAX_PATHS))
226         return (ERR_NOEFFECT);
227     LG_memset(&paths[path_id], 0, sizeof(Path));
228     used_paths &= ~(1 << path_id);
229     return (OK);
230 }
231 
232 // Resets appropriate secret pathfinding state not saved
233 // in the save game
reset_pathfinding()234 errtype reset_pathfinding() {
235     last_pathfind_time = 0;
236     return (OK);
237 }
238 
239 // THE ACTUAL GRUNGY PART
240 
241 // spt is a "point" which only has 8 bits each for x & y.
242 typedef short spt;
243 #define SPT_X(s)           ((s)&0xFF)
244 #define SPT_Y(s)           ((s) >> 8)
245 #define SPT_X_SET(s, newx) ((s) = ((s)&0xFF00) | (newx))
246 #define SPT_Y_SET(s, newy) ((s) = ((s)&0x00FF) | ((newy) << 8))
247 #define PT2SPT(pt)         ((((pt).y & 0xFF) << 8) | ((pt).x & 0xFF))
248 
249 #define FORALLINSPTLIST(pspt, iter, loop) for (iter = pspt[0], i = 0; SPT_X(pspt[i]) != 0; i++, iter = pspt[i])
250 
251 //#define CLEARSPTLIST(pspt, num, loop) do { for (loop=0; loop < num; loop++) { pspt[loop] = 0; } } while (0)
252 #define CLEARSPTLIST(pspt, num) LG_memset(pspt, 0, sizeof(spt) * num)
253 
254 // A given element in the pathfind buffer is 8 bits
255 // 5 bits of Z
256 // 2 bits of from-directionality
257 // 1 bit  of whether or not it's been visited
258 #define PFE_Z_MASK    0x1F
259 #define PFE_DIR_MASK  0x60
260 #define PFE_USE_MASK  0x80
261 #define PFE_DIR_SHIFT 5
262 #define PFE_USE_SHIFT 7
263 
264 #define PFE_OBJ_ZSHIFT 3
265 // PFE_Z just returns the raw stored z value (5 bits)
266 // PFE_Z_MAPHT returns the value as a map height (5 bits)
267 // PFE_Z_OBJHT returns the value as an object height (8 bits)
268 #define PFE_Z(ppfe)        (*(ppfe)&PFE_Z_MASK)
269 #define PFE_Z_MAPHT(ppfe)  PFE_Z(ppfe)
270 #define PFE_Z_OBJHT(ppfe)  PFE_Z(ppfe) << PFE_OBJ_ZSHIFT
271 #define OBJZ_TO_PFEZ(zval) ((zval) >> PFE_OBJ_ZSHIFT)
272 #define PFEZ_TO_OBJZ(zval) ((zval) << PFE_OBJ_ZSHIFT)
273 #define MAPZ_TO_PFEZ(zval) (zval)
274 #define PFEZ_TO_MAPZ(zval) (zval)
275 
276 // like above, but setting...
277 #define PFE_Z_SET_RAW(ppfe, z)      (*(ppfe) = ((*(ppfe) & ~PFE_Z_MASK) | (z)))
278 #define PFE_Z_SET_MAPHT(ppfe, mapz) PFE_Z_SET_RAW(ppfe, mapz)
279 #define PFE_Z_SET_OBJHT(ppfe, objz) PFE_Z_SET_RAW(ppfe, objz >> PFE_OBJ_ZSHIFT)
280 
281 #define PFE_DIR(ppfe)        ((*(ppfe)&PFE_DIR_MASK) >> PFE_DIR_SHIFT)
282 #define PFE_DIR_SET(ppfe, d) (*(ppfe) = (*(ppfe) & ~PFE_DIR_MASK) | ((d) << PFE_DIR_SHIFT))
283 
284 #define PFE_USED(ppfe)        ((*(ppfe)&PFE_USE_MASK) >> PFE_USE_SHIFT)
285 #define PFE_USED_SET(ppfe, d) (*(ppfe) = (*(ppfe) & ~PFE_USE_MASK) | ((d) << PFE_USE_SHIFT))
286 
287 // Hmm, I think this will work.... pathfind_buffer is a big chunk
288 // of memory, and we want to access it like a big array of uchars...
289 #define PFE_GET_XY(x, y) ((uchar *)pathfind_buffer + x + (y * MAP_XSIZE))
290 
291 // l1 and l2 are two lists of spts, and expand_into_list gets
292 // pointed to whichever is the current actual expand_into_list (the
293 // other being prepped to be the expand_into_list next time).
294 #define EXPAND_LIST_SIZE 64
295 spt *expand_into_list, *expand_from_list, *exp_l1, *exp_l2;
296 char expand_count;
297 uchar *pathfind_buffer;
298 
299 uchar map_connectivity(spt sq1, spt sq2, char dir, uchar flr1, uchar *new_z, uchar dest_z);
300 uchar expand_one_square(spt sq, char path_id);
301 uchar expand_fill_list(char path_id);
302 
303 // Returns the height at the edge of the tile pme, in the direction dir
304 // Return value is in map units!
tile_height(MapElem * pme,char dir,uchar floor)305 short tile_height(MapElem *pme, char dir, uchar floor) {
306     uchar retval;
307     if (floor)
308         retval = me_height_flr(pme);
309     else
310         retval = MAP_HEIGHTS - me_height_ceil(pme);
311     switch (me_tiletype(pme)) {
312     case TILE_SOLID:
313         return (-1);
314         break;
315     case TILE_SOLID_NW:
316         if ((dir == 2) || (dir == 1))
317             return (-1);
318         break;
319     case TILE_SOLID_NE:
320         if ((dir == 2) || (dir == 3))
321             return (-1);
322         break;
323     case TILE_SOLID_SE:
324         if ((dir == 0) || (dir == 3))
325             return (-1);
326         break;
327     case TILE_SOLID_SW:
328         if ((dir == 0) || (dir == 1))
329             return (-1);
330         break;
331     // Ask doug how to do the sloping cases right...
332     case TILE_SLOPEUP_N:
333         if (dir == 2)
334             break;
335         if (dir == 0)
336             retval += me_param(pme);
337         else
338             retval += me_param(pme) / 2;
339         break;
340     case TILE_SLOPEUP_S:
341         if (dir == 0)
342             break;
343         if (dir == 2)
344             retval += me_param(pme);
345         else
346             retval += me_param(pme) / 2;
347         break;
348     case TILE_SLOPEUP_E:
349         if (dir == 3)
350             break;
351         if (dir == 1)
352             retval += me_param(pme);
353         else
354             retval += me_param(pme) / 2;
355         break;
356     case TILE_SLOPEUP_W:
357         if (dir == 1)
358             break;
359         if (dir == 3)
360             retval += me_param(pme);
361         else
362             retval += me_param(pme) / 2;
363         break;
364     }
365     return (retval);
366 }
367 
368 #define CRITTERS_OPEN_UNLOCKED_DOORS
369 
pf_check_doors(MapElem * pme,char dir,ObjID * open_door)370 uchar pf_check_doors(MapElem *pme, char dir, ObjID *open_door) {
371     ObjRefID curr;
372     ObjID id, which_obj = OBJ_NULL;
373     curr = me_objref(pme);
374     *open_door = OBJ_NULL;
375     while (curr != OBJ_REF_NULL) {
376         id = objRefs[curr].obj;
377         if (objs[id].obclass == CLASS_DOOR) {
378             //         Warning(("contemplating id %x, loc = %x, %x, dir = %d\n",id,objs[id].loc.x,objs[id].loc.y,dir));
379             switch (dir) {
380             case 0: // N
381                 if (((objs[id].loc.y & 0xFF) >= 0x80) && !(objs[id].loc.h & 0x40))
382                     which_obj = id;
383                 break;
384             case 1: // E
385                 if (((objs[id].loc.x & 0xFF) >= 0x80) && (objs[id].loc.h & 0x40))
386                     which_obj = id;
387                 break;
388             case 2: // S
389                 if (((objs[id].loc.y & 0xFF) <= 0x80) && !(objs[id].loc.h & 0x40))
390                     which_obj = id;
391                 break;
392             case 3: // W
393                 if (((objs[id].loc.x & 0xFF) <= 0x80) && (objs[id].loc.h & 0x40))
394                     which_obj = id;
395                 break;
396             }
397         }
398         curr = objRefs[curr].next;
399     }
400     if (which_obj != OBJ_NULL) {
401         // If there is a door in the way, and it is closed, and
402         // it is either locked or requires access, we can't get through
403         if ((DOOR_CLOSED(which_obj)) && ((ObjProps[OPNUM(which_obj)].flags & TERRAIN_OBJECT) != 0) &&
404             ((QUESTBIT_GET(objDoors[objs[which_obj].specID].locked)) ||
405              (objDoors[objs[which_obj].specID].access_level))) {
406             return (FALSE);
407         } else
408             *open_door = which_obj;
409     }
410     return (TRUE);
411 }
412 
413 // Returns whether or not the two squares can be freely traveled
414 // between with respect to door-like objects in the squares.
pf_obj_doors(MapElem * pme1,MapElem * pme2,char dir,ObjID * open_door)415 uchar pf_obj_doors(MapElem *pme1, MapElem *pme2, char dir, ObjID *open_door) {
416     uchar retval;
417     //   Warning(("Top of pf_obj_door!\n"));
418     retval = pf_check_doors(pme1, dir, open_door);
419     //   Warning(("A: *open_door = %x\n",*open_door));
420     if (retval && (*open_door == OBJ_NULL)) {
421         retval = pf_check_doors(pme2, (dir + 2) % 4, open_door);
422         //      Warning(("B: *open_door = %x\n",*open_door));
423     }
424     return (retval);
425 }
426 
427 // Returns the height (not downshifted) attainable by entering the
428 // square at height old_z (downshifted).  Specifically, accounts for
429 // bridges and repulsorlifts keeping the player elevated.
430 
431 // Wow, this really doesn't deal with bridges right at all, I don't think
432 // Which is to say, I'm pretty sure it confuses doors with bridges outrageously
433 // maybe that is worth a specific hack...
434 
435 // old_z is almost certainly in PFEZ units, as is the return value
pf_obj_height(MapElem * pme,uchar old_z)436 uchar pf_obj_height(MapElem *pme, uchar old_z) {
437     ObjRefID curr;
438     ObjID id;
439     uchar retval = MAPZ_TO_PFEZ(me_height_flr(pme));
440 
441     curr = me_objref(pme);
442     //   Spew(DSRC_AI_Pathfind, ("pf_o_ht: initial retval = %x\n",retval));
443     while (curr != OBJ_REF_NULL) {
444         id = objRefs[curr].obj;
445 #ifdef PATHFIND_REPULSORS
446         if (ID2TRIP(id) == REPULSOR_TRIPLE) {
447             // Check to see if height is sufficient for entry
448             if ((objTraps[objs[id].specID].p2 < old_z) && (objTraps[objs[id].specID].p3 > old_z)) {
449                 retval = max(retval, objTraps[objs[id].specID].p3);
450                 //            Spew(DSRC_AI_Pathfind, ("pf_o_ht: repulsor retval = %x\n",retval));
451             }
452         } else
453 #endif
454             if (ObjProps[OPNUM(id)].flags & TERRAIN_OBJECT) {
455             switch (ObjProps[OPNUM(id)].render_type) {
456             case FAUBJ_TL_POLY:
457             case FAUBJ_TPOLY:
458             case FAUBJ_SPECIAL:
459                 retval = lg_max(retval, OBJZ_TO_PFEZ(objs[id].loc.z));
460                 //               Spew(DSRC_AI_Pathfind, ("pf_o_ht: obj retval = %x (id = %x)\n",retval,id));
461                 break;
462             }
463         }
464         curr = objRefs[curr].next;
465     }
466     return (retval);
467 }
468 
469     // Tells whether or not we can get from one square (at a given z)
470     // to a new square, and if so, what our new z will be.  This will
471     // start out very very stupid and hopefully get smarter as it
472     // needs to.
473 
474 #define PF_HEIGHT 1
475 #define PF_CLIMB 1
476 
477 // flr1, new_z, and dest are all in PFE Z units
map_connectivity(spt sq1,spt sq2,char dir,uchar flr1,uchar * new_z,uchar dest_z)478 uchar map_connectivity(spt sq1, spt sq2, char dir, uchar flr1, uchar *new_z, uchar dest_z) {
479     MapElem *pme1, *pme2;
480     ObjID temp;
481     short flr2, ceil2;
482     uchar retval;
483 
484     pme1 = MAP_GET_XY(SPT_X(sq1), SPT_Y(sq1));
485     pme2 = MAP_GET_XY(SPT_X(sq2), SPT_Y(sq2));
486     flr2 = MAPZ_TO_PFEZ(tile_height(pme2, (dir + 2) % 4, TRUE));
487     ceil2 = MAPZ_TO_PFEZ(tile_height(pme2, (dir + 2) % 4, FALSE));
488     if (flr2 == -1)
489         return (FALSE);
490 
491 #ifdef ALLOW_DESTZ_OVERIDE
492     // Allow final destination overriding, and downshift z
493     if (dest_z)
494         flr2 = dest_z;
495 #endif
496 
497     if ((ceil2 < flr1 + PF_HEIGHT) || (flr2 > flr1 + PF_CLIMB)) {
498         retval = FALSE;
499     } else {
500         retval = TRUE;
501         *new_z = pf_obj_height(pme2, flr1);
502     }
503     if (retval)
504         retval = pf_obj_doors(pme1, pme2, dir, &temp);
505 
506     return (retval);
507 }
508 
509 // Expand out a single square.  Don't let through any expansions
510 // that point to places that other expansions have been to or
511 // that we can't reach.
512 // Returns whether or not we reached the destination.
expand_one_square(spt sq,char path_id)513 uchar expand_one_square(spt sq, char path_id) {
514     spt newsq, dest = PT2SPT(paths[path_id].dest);
515     char i;
516     uchar *ppfe, *ppfe2;
517 
518     ppfe2 = PFE_GET_XY(SPT_X(sq), SPT_Y(sq));
519     //   Spew(DSRC_AI_Pathfind, ("expanding %x\n",sq));
520     for (i = 0; i < 4; i++) {
521         newsq = sq;
522         switch (i) {
523         case 0:
524             SPT_Y_SET(newsq, SPT_Y(newsq) + 1);
525             break; // N
526         case 1:
527             SPT_X_SET(newsq, SPT_X(newsq) + 1);
528             break; // E
529         case 2:
530             SPT_Y_SET(newsq, SPT_Y(newsq) - 1);
531             break; // S
532         case 3:
533             SPT_X_SET(newsq, SPT_X(newsq) - 1);
534             break; // W
535         }
536         ppfe = PFE_GET_XY(SPT_X(newsq), SPT_Y(newsq));
537         if (!PFE_USED(ppfe)) // dont bother if we can already get there
538         {
539             uchar new_z, dest_z = 0;
540             if (newsq == dest)
541                 dest_z = OBJZ_TO_PFEZ(paths[path_id].dest_z);
542             if (map_connectivity(sq, newsq, i, PFE_Z(ppfe2), &new_z, dest_z)) {
543                 PFE_USED_SET(ppfe, TRUE);
544                 // return value from map_conn is already in PFE Z units
545                 PFE_Z_SET_RAW(ppfe, new_z);
546                 PFE_DIR_SET(ppfe, i);
547                 expand_into_list[expand_count++] = newsq;
548                 //          Spew(DSRC_AI_Pathfind,("can reach %x\n",newsq));
549                 if (newsq == dest)
550                     return (TRUE);
551             }
552             //         else
553             //            Spew(DSRC_AI_Pathfind, ("%x does not connect\n",newsq));
554         }
555         //      else
556         //         Spew(DSRC_AI_Pathfind, ("%x already used\n",newsq));
557     }
558     return (FALSE);
559 }
560 
561 // Go through the list of last-iteration's reached squares, and
562 // generate a new list of places to go to.
563 // Returns whether or not we reached the destination.
expand_fill_list(char path_id)564 uchar expand_fill_list(char path_id) {
565     spt s;
566     char i;
567     uchar done = FALSE;
568     expand_count = 0;
569     CLEARSPTLIST(expand_into_list, EXPAND_LIST_SIZE);
570     FORALLINSPTLIST(expand_from_list, s, i) {
571         done = expand_one_square(s, path_id);
572         if (done)
573             break;
574     }
575     //   Spew(DSRC_AI_Pathfind, ("expand into: "));
576     //   FORALLINSPTLIST(expand_into_list, s, i)
577     //   {
578     //      Spew(DSRC_AI_Pathfind, ("%x ",s));
579     //   }
580     //   Spew(DSRC_AI_Pathfind, ("\n"));
581     return (done);
582 }
583 
584 // So, for each map square we keep track of what square we took to get
585 // here.  Since our fill is breadth-first, we know if a square has already
586 // been reached, it has been reached by a quicker or equally quick path,
587 // so we only need 2 bits of "from-directionality".  We also keep track of
588 // our z, so that we can have better gnosis of connectivity (you can go down
589 // cliffs, but not up them, unless you are on a bridge, for example).
590 
591 // The basic algorithm is that we expand our list of interesting squares
592 // out by one on each loop iteration, then check all of those squares for
593 // being our destination, and failing finding our target, assemble a new list
594 // of old squares to expand on the next iteration.
595 
596 // When we find the target, we just follow the from-directionality backwards to
597 // get the shortest path.  I fully admit this is far from the best, fastest,
598 // or cleverest algorithm in the world.
599 
600 // I don't think this algorithm deals at all with being able to jump over
601 // one-square pits.  In fact, I worry whether or not 2 bits of directionality
602 // is sufficient to the task.  I guess we'll find out.
603 
604 // use big_buffer rather than being le memory hog
605 #define PATHFIND_WITH_BIG_BUFFER
find_path(char path_id)606 errtype find_path(char path_id) {
607     uchar done = FALSE;
608     char i, j, step_count = 0;
609     uchar *ppfe;
610 
611     // Malloc our expand lists & the buffer
612     exp_l1 = (spt *)big_buffer;
613     exp_l2 = (spt *)(big_buffer + (sizeof(spt) * EXPAND_LIST_SIZE));
614     pathfind_buffer = (uchar *)(big_buffer + (sizeof(spt) * 2 * EXPAND_LIST_SIZE));
615 
616     // Clear the lists
617     CLEARSPTLIST(exp_l1, EXPAND_LIST_SIZE);
618     CLEARSPTLIST(exp_l2, EXPAND_LIST_SIZE);
619     LG_memset(pathfind_buffer, 0, MAP_XSIZE * MAP_YSIZE * sizeof(uchar));
620 #ifdef REALLY_SLOW_PATHFIND_CLEARING
621     for (i = 0; i < MAP_XSIZE; i++) {
622         for (j = 0; j < MAP_YSIZE; j++) {
623             PFE_USED_SET(PFE_GET_XY(i, j), FALSE);
624         }
625     }
626 #endif
627 
628     // set up initial pointings
629     expand_into_list = exp_l1;
630     expand_from_list = exp_l2;
631 
632     // Prep the first one to be our source
633     expand_from_list[0] = PT2SPT(paths[path_id].source);
634     ppfe = PFE_GET_XY(paths[path_id].source.x, paths[path_id].source.y);
635     PFE_Z_SET_OBJHT(ppfe, paths[path_id].start_z);
636     PFE_USED_SET(ppfe, TRUE);
637 
638     while (!done && step_count < NUM_PATH_STEPS) {
639         // Expand out last iteration's list
640         done = expand_fill_list(path_id);
641 
642         // If we haven't found it, swap pointers, etc.
643         if (!done) {
644             if (expand_count == 0) {
645                 //            Warning(("expand_count = 0!\n"));
646                 step_count = NUM_PATH_STEPS;
647             }
648             if (expand_into_list == exp_l1) {
649                 expand_into_list = exp_l2;
650                 expand_from_list = exp_l1;
651             } else {
652                 expand_into_list = exp_l1;
653                 expand_from_list = exp_l2;
654             }
655             step_count++;
656         } else
657         // If we HAVE found it, go poke in the right info into the path
658         {
659             LGPoint currpt;
660             i = 0;
661             currpt = paths[path_id].dest;
662             while (!PointsEqual(currpt, paths[path_id].source)) {
663                 assert(i < NUM_PATH_STEPS);
664                 j = PFE_DIR(PFE_GET_XY(currpt.x, currpt.y));
665                 SET_PATH_STEP(path_id, i, j);
666                 i++;
667 
668                 // Compute one step backwards, according to our direction, j
669                 // so that we have a new currpt
670                 switch (j) {
671                 case 0:
672                     currpt.y--;
673                     break; // N, so backwards is S
674                 case 1:
675                     currpt.x--;
676                     break; // E, so backwards is W
677                 case 2:
678                     currpt.y++;
679                     break; // S, so backwards is N
680                 case 3:
681                     currpt.x++;
682                     break; // W, so backwards is E
683                 }
684             }
685             paths[path_id].num_steps = i;
686         }
687     }
688     if (step_count >= NUM_PATH_STEPS) {
689         paths[path_id].num_steps = 0;
690         //      Warning(("Failed to find path from (%x,%x) to
691         //      (%x,%x)\n",paths[path_id].source.x,paths[path_id].source.y,
692         //         paths[path_id].dest.x, paths[path_id].dest.y));
693     }
694 
695     return (OK);
696 }
697