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