]> icculus.org git repositories - btb/d2x.git/blob - main/aipath.c
use the orientation parameter of g3_draw_bitmap
[btb/d2x.git] / main / aipath.c
1 /*
2 THE COMPUTER CODE CONTAINED HEREIN IS THE SOLE PROPERTY OF PARALLAX
3 SOFTWARE CORPORATION ("PARALLAX").  PARALLAX, IN DISTRIBUTING THE CODE TO
4 END-USERS, AND SUBJECT TO ALL OF THE TERMS AND CONDITIONS HEREIN, GRANTS A
5 ROYALTY-FREE, PERPETUAL LICENSE TO SUCH END-USERS FOR USE BY SUCH END-USERS
6 IN USING, DISPLAYING,  AND CREATING DERIVATIVE WORKS THEREOF, SO LONG AS
7 SUCH USE, DISPLAY OR CREATION IS FOR NON-COMMERCIAL, ROYALTY OR REVENUE
8 FREE PURPOSES.  IN NO EVENT SHALL THE END-USER USE THE COMPUTER CODE
9 CONTAINED HEREIN FOR REVENUE-BEARING PURPOSES.  THE END-USER UNDERSTANDS
10 AND AGREES TO THE TERMS HEREIN AND ACCEPTS THE SAME BY USE OF THIS FILE.
11 COPYRIGHT 1993-1999 PARALLAX SOFTWARE CORPORATION.  ALL RIGHTS RESERVED.
12 */
13
14 /*
15  *
16  * AI path forming stuff.
17  *
18  */
19
20 #ifdef HAVE_CONFIG_H
21 #include <conf.h>
22 #endif
23
24 #include <stdio.h>              //      for printf()
25 #include <stdlib.h>             // for d_rand() and qsort()
26 #include <string.h>             // for memset()
27
28 #include "inferno.h"
29 #include "mono.h"
30 #include "3d.h"
31 #include "dxxerror.h"
32 #ifdef EDITOR
33 #include "editor/editor.h"
34 #endif
35
36
37 #define PARALLAX        0               //      If !0, then special debugging for Parallax eyes enabled.
38
39 //      Length in segments of avoidance path
40 #define AVOID_SEG_LENGTH        7
41
42 #ifdef NDEBUG
43 #define PATH_VALIDATION 0
44 #else
45 #define PATH_VALIDATION 1
46 #endif
47
48 void validate_all_paths(void);
49 void ai_path_set_orient_and_vel(object *objp, vms_vector* goal_point, int player_visibility, vms_vector *vec_to_player);
50 void maybe_ai_path_garbage_collect(void);
51 void ai_path_garbage_collect(void);
52 #if PATH_VALIDATION
53 int validate_path(int debug_flag, point_seg* psegs, int num_points);
54 #endif
55
56 //      ------------------------------------------------------------------------
57 void create_random_xlate(sbyte *xt)
58 {
59         int     i;
60
61         for (i=0; i<MAX_SIDES_PER_SEGMENT; i++)
62                 xt[i] = i;
63
64         for (i=0; i<MAX_SIDES_PER_SEGMENT; i++) {
65                 int     j = (d_rand()*MAX_SIDES_PER_SEGMENT)/(D_RAND_MAX+1);
66                 sbyte temp_byte;
67                 Assert((j >= 0) && (j < MAX_SIDES_PER_SEGMENT));
68
69                 temp_byte = xt[j];
70                 xt[j] = xt[i];
71                 xt[i] = temp_byte;
72         }
73
74 }
75
76 //      -----------------------------------------------------------------------------------------------------------
77 //      Insert the point at the center of the side connecting two segments between the two points.
78 // This is messy because we must insert into the list.  The simplest (and not too slow) way to do this is to start
79 // at the end of the list and go backwards.
80 void insert_center_points(point_seg *psegs, int *num_points)
81 {
82         int     i, j, last_point;
83         int     count=*num_points;
84
85         last_point = *num_points-1;
86
87         for (i=last_point; i>0; i--) {
88                 int                     connect_side, temp_segnum;
89                 vms_vector      center_point, new_point;
90
91                 psegs[2*i] = psegs[i];
92                 connect_side = find_connect_side(&Segments[psegs[i].segnum], &Segments[psegs[i-1].segnum]);
93                 Assert(connect_side != -1);     //      Impossible!  These two segments must be connected, they were created by create_path_points (which was created by mk!)
94                 if (connect_side == -1)                 //      Try to blow past the assert, this should at least prevent a hang.
95                         connect_side = 0;
96                 compute_center_point_on_side(&center_point, &Segments[psegs[i-1].segnum], connect_side);
97                 vm_vec_sub(&new_point, &psegs[i-1].point, &center_point);
98                 new_point.x /= 16;
99                 new_point.y /= 16;
100                 new_point.z /= 16;
101                 vm_vec_sub(&psegs[2*i-1].point, &center_point, &new_point);
102                 temp_segnum = find_point_seg(&psegs[2*i-1].point, psegs[2*i].segnum);
103                 if (temp_segnum == -1) {
104                         mprintf((1, "Warning: point not in ANY segment in aipath.c/insert_center_points.\n"));
105                         psegs[2*i-1].point = center_point;
106                         find_point_seg(&psegs[2*i-1].point, psegs[2*i].segnum);
107                 }
108
109                 psegs[2*i-1].segnum = psegs[2*i].segnum;
110                 count++;
111         }
112
113         //      Now, remove unnecessary center points.
114         //      A center point is unnecessary if it is close to the line between the two adjacent points.
115         //      MK, OPTIMIZE!  Can get away with about half the math since every vector gets computed twice.
116         for (i=1; i<count-1; i+=2) {
117                 vms_vector      temp1, temp2;
118                 fix                     dot;
119
120                 dot = vm_vec_dot(vm_vec_sub(&temp1, &psegs[i].point, &psegs[i-1].point), vm_vec_sub(&temp2, &psegs[i+1].point, &psegs[i].point));
121
122                 if (dot * 9/8 > fixmul(vm_vec_mag(&temp1), vm_vec_mag(&temp2)))
123                         psegs[i].segnum = -1;
124
125         }
126
127         //      Now, scan for points with segnum == -1
128         j = 0;
129         for (i=0; i<count; i++)
130                 if (psegs[i].segnum != -1)
131                         psegs[j++] = psegs[i];
132
133         *num_points = j;
134 }
135
136 #ifdef EDITOR
137 int     Safety_flag_override = 0;
138 int     Random_flag_override = 0;
139 int     Ai_path_debug=0;
140 #endif
141
142 //      -----------------------------------------------------------------------------------------------------------
143 //      Move points halfway to outside of segment.
144 void move_towards_outside(point_seg *psegs, int *num_points, object *objp, int rand_flag)
145 {
146         int     i;
147         point_seg       new_psegs[200];
148
149         Assert(*num_points < 200);
150
151         for (i=1; i<*num_points-1; i++) {
152                 int                     new_segnum;
153                 fix                     segment_size;
154                 int                     segnum;
155                 vms_vector      a, b, c, d, e;
156                 vms_vector      goal_pos;
157                 int                     count;
158                 int                     temp_segnum;
159
160                 // -- psegs[i].segnum = find_point_seg(&psegs[i].point, psegs[i].segnum);
161                 temp_segnum = find_point_seg(&psegs[i].point, psegs[i].segnum);
162                 Assert(temp_segnum != -1);
163                 psegs[i].segnum = temp_segnum;
164                 segnum = psegs[i].segnum;
165
166                 vm_vec_sub(&a, &psegs[i].point, &psegs[i-1].point);
167                 vm_vec_sub(&b, &psegs[i+1].point, &psegs[i].point);
168                 vm_vec_sub(&c, &psegs[i+1].point, &psegs[i-1].point);
169                 //      I don't think we can use quick version here and this is _very_ rarely called. --MK, 07/03/95
170                 vm_vec_normalize_quick(&a);
171                 vm_vec_normalize_quick(&b);
172                 if (abs(vm_vec_dot(&a, &b)) > 3*F1_0/4 ) {
173                         if (abs(a.z) < F1_0/2) {
174                                 if (rand_flag) {
175                                         e.x = (d_rand()-16384)/2;
176                                         e.y = (d_rand()-16384)/2;
177                                         e.z = abs(e.x) + abs(e.y) + 1;
178                                         vm_vec_normalize_quick(&e);
179                                 } else {
180                                         e.x = 0;
181                                         e.y = 0;
182                                         e.z = F1_0;
183                                 }
184                         } else {
185                                 if (rand_flag) {
186                                         e.y = (d_rand()-16384)/2;
187                                         e.z = (d_rand()-16384)/2;
188                                         e.x = abs(e.y) + abs(e.z) + 1;
189                                         vm_vec_normalize_quick(&e);
190                                 } else {
191                                         e.x = F1_0;
192                                         e.y = 0;
193                                         e.z = 0;
194                                 }
195                         }
196                 } else {
197                         vm_vec_cross(&d, &a, &b);
198                         vm_vec_cross(&e, &c, &d);
199                         vm_vec_normalize_quick(&e);
200                 }
201
202 if (vm_vec_mag_quick(&e) < F1_0/2)
203         Int3();
204
205 //mprintf((0, "(%i) Moving to side: %6.3f %6.3f %6.3f\n", i, f2fl(e.x), f2fl(e.y), f2fl(e.z)));
206
207                 segment_size = vm_vec_dist_quick(&Vertices[Segments[segnum].verts[0]], &Vertices[Segments[segnum].verts[6]]);
208                 if (segment_size > F1_0*40)
209                         segment_size = F1_0*40;
210
211                 vm_vec_scale_add(&goal_pos, &psegs[i].point, &e, segment_size/4);
212
213                 count = 3;
214                 while (count) {
215                         fvi_query       fq;
216                         fvi_info                hit_data;
217                         int                     hit_type;
218         
219                         fq.p0                                           = &psegs[i].point;
220                         fq.startseg                             = psegs[i].segnum;
221                         fq.p1                                           = &goal_pos;
222                         fq.rad                                  = objp->size;
223                         fq.thisobjnum       = OBJECT_NUMBER(objp);
224                         fq.ignore_obj_list      = NULL;
225                         fq.flags                                        = 0;
226         
227                         hit_type = find_vector_intersection(&fq, &hit_data);
228         
229                         if (hit_type == HIT_NONE)
230                                 count = 0;
231                         else {
232                                 if ((count == 3) && (hit_type == HIT_BAD_P0))
233                                         Int3();
234                                 goal_pos.x = (fq.p0->x + hit_data.hit_pnt.x)/2;
235                                 goal_pos.y = (fq.p0->y + hit_data.hit_pnt.y)/2;
236                                 goal_pos.z = (fq.p0->z + hit_data.hit_pnt.z)/2;
237                                 count--;
238                                 if (count == 0) {       //      Couldn't move towards outside, that's ok, sometimes things can't be moved.
239                                         goal_pos = psegs[i].point;
240                                 }
241                         }
242                 }
243
244                 //      Only move towards outside if remained inside segment.
245                 new_segnum = find_point_seg(&goal_pos, psegs[i].segnum);
246                 if (new_segnum == psegs[i].segnum) {
247                         new_psegs[i].point = goal_pos;
248                         new_psegs[i].segnum = new_segnum;
249                 } else {
250                         new_psegs[i].point = psegs[i].point;
251                         new_psegs[i].segnum = psegs[i].segnum;
252                 }
253
254         }
255
256         for (i=1; i<*num_points-1; i++)
257                 psegs[i] = new_psegs[i];
258 }
259
260
261 //      -----------------------------------------------------------------------------------------------------------
262 //      Create a path from objp->pos to the center of end_seg.
263 //      Return a list of (segment_num, point_locations) at psegs
264 //      Return number of points in *num_points.
265 //      if max_depth == -1, then there is no maximum depth.
266 //      If unable to create path, return -1, else return 0.
267 //      If random_flag !0, then introduce randomness into path by looking at sides in random order.  This means
268 //      that a path between two segments won't always be the same, unless it is unique.
269 //      If safety_flag is set, then additional points are added to "make sure" that points are reachable.  I would
270 //      like to say that it ensures that the object can move between the points, but that would require knowing what
271 //      the object is (which isn't passed, right?) and making fvi calls (slow, right?).  So, consider it the more_or_less_safe_flag.
272 //      If end_seg == -2, then end seg will never be found and this routine will drop out due to depth (probably called by create_n_segment_path).
273 int create_path_points(object *objp, int start_seg, int end_seg, point_seg *psegs, short *num_points, int max_depth, int random_flag, int safety_flag, int avoid_seg)
274 {
275         int             cur_seg;
276         int             sidenum;
277         int             qtail = 0, qhead = 0;
278         int             i;
279         sbyte   visited[MAX_SEGMENTS];
280         seg_seg seg_queue[MAX_SEGMENTS];
281         short           depth[MAX_SEGMENTS];
282         int             cur_depth;
283         sbyte   random_xlate[MAX_SIDES_PER_SEGMENT];
284         point_seg       *original_psegs = psegs;
285         int             l_num_points;
286
287 // -- mprintf((0, "cpp: frame = %4i obj %3i, psegs = %5i\n", FrameCount, OBJECT_NUMBER(objp), POINT_SEG_NUMBER(psegs)));
288 #if PATH_VALIDATION
289         validate_all_paths();
290 #endif
291
292 if ((objp->type == OBJ_ROBOT) && (objp->ctype.ai_info.behavior == AIB_RUN_FROM)) {
293         random_flag = 1;
294         avoid_seg = ConsoleObject->segnum;
295         // Int3();
296 }
297
298         if (max_depth == -1)
299                 max_depth = MAX_PATH_LENGTH;
300
301         l_num_points = 0;
302 //random_flag = Random_flag_override; //!! debug!!
303 //safety_flag = Safety_flag_override; //!! debug!!
304
305 //      for (i=0; i<=Highest_segment_index; i++) {
306 //              visited[i] = 0;
307 //              depth[i] = 0;
308 //      }
309         memset(visited, 0, sizeof(visited[0])*(Highest_segment_index+1));
310         memset(depth, 0, sizeof(depth[0])*(Highest_segment_index+1));
311
312         //      If there is a segment we're not allowed to visit, mark it.
313         if (avoid_seg != -1) {
314                 Assert(avoid_seg <= Highest_segment_index);
315                 if ((start_seg != avoid_seg) && (end_seg != avoid_seg)) {
316                         visited[avoid_seg] = 1;
317                         depth[avoid_seg] = 0;
318                 } else
319                         ; // -- mprintf((0, "Start/End/Avoid = %i %i %i\n", start_seg, end_seg, avoid_seg));
320         }
321
322         if (random_flag)
323                 create_random_xlate(random_xlate);
324
325         cur_seg = start_seg;
326         visited[cur_seg] = 1;
327         cur_depth = 0;
328
329         while (cur_seg != end_seg) {
330                 segment *segp = &Segments[cur_seg];
331
332                 if (random_flag)
333                         if (d_rand() < 8192)
334                                 create_random_xlate(random_xlate);
335
336                 // mprintf((0, "\n"));
337                 for (sidenum = 0; sidenum < MAX_SIDES_PER_SEGMENT; sidenum++) {
338
339                         int     snum = sidenum;
340
341                         if (random_flag)
342                                 snum = random_xlate[sidenum];
343
344                         if (IS_CHILD(segp->children[snum]) && ((WALL_IS_DOORWAY(segp, snum) & WID_FLY_FLAG) || (ai_door_is_openable(objp, segp, snum)))) {
345                                 int                     this_seg = segp->children[snum];
346                                 Assert(this_seg != -1);
347                                 if (((cur_seg == avoid_seg) || (this_seg == avoid_seg)) && (ConsoleObject->segnum == avoid_seg)) {
348                                         vms_vector      center_point;
349
350                                         fvi_query       fq;
351                                         fvi_info                hit_data;
352                                         int                     hit_type;
353         
354                                         compute_center_point_on_side(&center_point, segp, snum);
355
356                                         fq.p0                                           = &objp->pos;
357                                         fq.startseg                             = objp->segnum;
358                                         fq.p1                                           = &center_point;
359                                         fq.rad                                  = objp->size;
360                                         fq.thisobjnum       = OBJECT_NUMBER(objp);
361                                         fq.ignore_obj_list      = NULL;
362                                         fq.flags                                        = 0;
363
364                                         hit_type = find_vector_intersection(&fq, &hit_data);
365                                         if (hit_type != HIT_NONE) {
366                                                 // -- mprintf((0, "hit_type = %i, object = %i\n", hit_type, hit_data.hit_object));
367                                                 goto dont_add;
368                                         }
369                                 }
370
371                                 if (!visited[this_seg]) {
372                                         seg_queue[qtail].start = cur_seg;
373                                         seg_queue[qtail].end = this_seg;
374                                         visited[this_seg] = 1;
375                                         depth[qtail++] = cur_depth+1;
376                                         if (depth[qtail-1] == max_depth) {
377                                                 // mprintf((0, "\ndepth == max_depth == %i\n", max_depth));
378                                                 end_seg = seg_queue[qtail-1].end;
379                                                 goto cpp_done1;
380                                         }       // end if (depth[...
381                                 }       // end if (!visited...
382                         }       // if (WALL_IS_DOORWAY(...
383 dont_add: ;
384                 }       //      for (sidenum...
385
386                 if (qhead >= qtail) {
387                         //      Couldn't get to goal, return a path as far as we got, which probably acceptable to the unparticular caller.
388                         end_seg = seg_queue[qtail-1].end;
389                         break;
390                 }
391
392                 cur_seg = seg_queue[qhead].end;
393                 cur_depth = depth[qhead];
394                 qhead++;
395
396 cpp_done1: ;
397         }       //      while (cur_seg ...
398
399         //      Set qtail to the segment which ends at the goal.
400         while (seg_queue[--qtail].end != end_seg)
401                 if (qtail < 0) {
402                         // mprintf((0, "\nNo path!\n"));
403                         // printf("UNABLE TO FORM PATH");
404                         // Int3();
405                         *num_points = l_num_points;
406                         return -1;
407                 }
408
409         #ifdef EDITOR
410         // -- N_selected_segs = 0;
411         #endif
412 //printf("Object #%3i, start: %3i ", OBJECT_NUMBER(objp), POINT_SEG_NUMBER(psegs));
413         while (qtail >= 0) {
414                 int     parent_seg, this_seg;
415
416                 this_seg = seg_queue[qtail].end;
417                 parent_seg = seg_queue[qtail].start;
418                 Assert((this_seg >= 0) && (this_seg <= Highest_segment_index));
419                 psegs->segnum = this_seg;
420 //printf("%3i ", this_seg);
421                 compute_segment_center(&psegs->point,&Segments[this_seg]);
422                 psegs++;
423                 l_num_points++;
424                 #ifdef EDITOR
425                 // -- Selected_segs[N_selected_segs++] = this_seg;
426                 #endif
427
428                 if (parent_seg == start_seg)
429                         break;
430
431                 while (seg_queue[--qtail].end != parent_seg)
432                         Assert(qtail >= 0);
433         }
434
435         Assert((start_seg >= 0) && (start_seg <= Highest_segment_index));
436         psegs->segnum = start_seg;
437 //printf("%3i\n", start_seg);
438         compute_segment_center(&psegs->point,&Segments[start_seg]);
439         psegs++;
440         l_num_points++;
441
442 #if PATH_VALIDATION
443         validate_path(1, original_psegs, l_num_points);
444 #endif
445
446         //      Now, reverse point_segs in place.
447         for (i=0; i< l_num_points/2; i++) {
448                 point_seg               temp_point_seg = *(original_psegs + i);
449                 *(original_psegs + i) = *(original_psegs + l_num_points - i - 1);
450                 *(original_psegs + l_num_points - i - 1) = temp_point_seg;
451         }
452 #if PATH_VALIDATION
453         validate_path(2, original_psegs, l_num_points);
454 #endif
455
456         //      Now, if safety_flag set, then insert the point at the center of the side connecting two segments
457         //      between the two points.  This is messy because we must insert into the list.  The simplest (and not too slow)
458         //      way to do this is to start at the end of the list and go backwards.
459         if (safety_flag) {
460                 if (POINT_SEG_NUMBER(psegs) + l_num_points + 2 > MAX_POINT_SEGS) {
461                         //      Ouch!  Cannot insert center points in path.  So return unsafe path.
462 //                      Int3(); // Contact Mike:  This is impossible.
463 //                      force_dump_ai_objects_all("Error in create_path_points");
464                         mprintf((0, "Resetting all paths because of safety_flag.\n"));
465                         ai_reset_all_paths();
466                         *num_points = l_num_points;
467                         return -1;
468                 } else {
469                         // int  old_num_points = l_num_points;
470                         insert_center_points(original_psegs, &l_num_points);
471                         // mprintf((0, "Saved %i/%i points.\n", 2*old_num_points - l_num_points - 1, old_num_points-1));
472                 }
473         }
474
475 #if PATH_VALIDATION
476         validate_path(3, original_psegs, l_num_points);
477 #endif
478
479 // -- MK, 10/30/95 -- This code causes apparent discontinuities in the path, moving a point
480 //      into a new segment.  It is not necessarily bad, but it makes it hard to track down actual
481 //      discontinuity problems.
482         if (objp->type == OBJ_ROBOT)
483                 if (Robot_info[objp->id].companion)
484                         move_towards_outside(original_psegs, &l_num_points, objp, 0);
485
486 #if PATH_VALIDATION
487         validate_path(4, original_psegs, l_num_points);
488 #endif
489
490         *num_points = l_num_points;
491         return 0;
492 }
493
494 int     Last_buddy_polish_path_frame;
495
496 //      -------------------------------------------------------------------------------------------------------
497 //      polish_path
498 //      Takes an existing path and makes it nicer.
499 //      Drops as many leading points as possible still maintaining direct accessibility
500 //      from current position to first point.
501 //      Will not shorten path to fewer than 3 points.
502 //      Returns number of points.
503 //      Starting position in psegs doesn't change.
504 //      Changed, MK, 10/18/95.  I think this was causing robots to get hung up on walls.
505 //                              Only drop up to the first three points.
506 int polish_path(object *objp, point_seg *psegs, int num_points)
507 {
508         int     i, first_point=0;
509
510         if (num_points <= 4)
511                 return num_points;
512
513         //      Prevent the buddy from polishing his path twice in one frame, which can cause him to get hung up.  Pretty ugly, huh?
514         if (Robot_info[objp->id].companion)
515         {
516                 if (FrameCount == Last_buddy_polish_path_frame)
517                         return num_points;
518                 else
519                         Last_buddy_polish_path_frame = FrameCount;
520         }
521
522         // -- MK: 10/18/95: for (i=0; i<num_points-3; i++) {
523         for (i=0; i<2; i++) {
524                 fvi_query       fq;
525                 fvi_info                hit_data;
526                 int                     hit_type;
527         
528                 fq.p0                                           = &objp->pos;
529                 fq.startseg                             = objp->segnum;
530                 fq.p1                                           = &psegs[i].point;
531                 fq.rad                                  = objp->size;
532                 fq.thisobjnum       = OBJECT_NUMBER(objp);
533                 fq.ignore_obj_list      = NULL;
534                 fq.flags                                        = 0;
535
536                 hit_type = find_vector_intersection(&fq, &hit_data);
537         
538                 if (hit_type == HIT_NONE)
539                         first_point = i+1;
540                 else
541                         break;          
542         }
543
544         if (first_point) {
545                 //      Scrunch down all the psegs.
546                 for (i=first_point; i<num_points; i++)
547                         psegs[i-first_point] = psegs[i];
548         }
549
550         return num_points - first_point;
551 }
552
553 #ifndef NDEBUG
554 //      -------------------------------------------------------------------------------------------------------
555 //      Make sure that there are connections between all segments on path.
556 //      Note that if path has been optimized, connections may not be direct, so this function is useless, or worse.
557 //      Return true if valid, else return false.
558 int validate_path(int debug_flag, point_seg *psegs, int num_points)
559 {
560 #if PATH_VALIDATION
561         int             i, curseg;
562
563         curseg = psegs->segnum;
564         if ((curseg < 0) || (curseg > Highest_segment_index)) {
565                 mprintf((0, "Path beginning at index %i, length=%i is bogus!\n", POINT_SEG_NUMBER(psegs), num_points));
566                 Int3();         //      Contact Mike: Debug trap for elusive, nasty bug.
567                 return 0;
568         }
569
570 if (debug_flag == 999)
571         mprintf((0, "That's curious...\n"));
572
573 if (num_points == 0)
574         return 1;
575
576 // printf("(%i) Validating path at psegs=%i, num_points=%i, segments = %3i ", debug_flag, POINT_SEG_NUMBER(psegs), num_points, psegs[0].segnum);
577         for (i=1; i<num_points; i++) {
578                 int     sidenum;
579                 int     nextseg = psegs[i].segnum;
580
581                 if ((nextseg < 0) || (nextseg > Highest_segment_index)) {
582                         mprintf((0, "Path beginning at index %i, length=%i is bogus!\n", POINT_SEG_NUMBER(psegs), num_points));
583                         Int3();         //      Contact Mike: Debug trap for elusive, nasty bug.
584                         return 0;
585                 }
586
587 // printf("%3i ", nextseg);
588                 if (curseg != nextseg) {
589                         for (sidenum=0; sidenum<MAX_SIDES_PER_SEGMENT; sidenum++)
590                                 if (Segments[curseg].children[sidenum] == nextseg)
591                                         break;
592
593                         // Assert(sidenum != MAX_SIDES_PER_SEGMENT);    //      Hey, created path is not contiguous, why!?
594                         if (sidenum == MAX_SIDES_PER_SEGMENT) {
595                                 mprintf((0, "Path beginning at index %i, length=%i is bogus!\n", POINT_SEG_NUMBER(psegs), num_points));
596                                 // printf("BOGUS");
597                                 Int3();
598                                 return 0;
599                         }
600                         curseg = nextseg;
601                 }
602         }
603 //printf("\n");
604 #endif
605         return 1;
606
607 }
608 #endif
609
610 #ifndef NDEBUG
611 //      -----------------------------------------------------------------------------------------------------------
612 void validate_all_paths(void)
613 {
614
615 #if PATH_VALIDATION
616         int     i;
617
618         for (i=0; i<=Highest_object_index; i++) {
619                 if (Objects[i].type == OBJ_ROBOT) {
620                         object          *objp = &Objects[i];
621                         ai_static       *aip = &objp->ctype.ai_info;
622                         //ai_local              *ailp = &Ai_local_info[i];
623
624                         if (objp->control_type == CT_AI) {
625                                 if ((aip->hide_index != -1) && (aip->path_length > 0))
626                                         if (!validate_path(4, &Point_segs[aip->hide_index], aip->path_length)) {
627                                                 Int3(); //      This path is bogus!  Who corrupted it!  Danger! Danger!
628                                                                         //      Contact Mike, he caused this mess.
629                                                 //force_dump_ai_objects_all("Error in validate_all_paths");
630                                                 aip->path_length=0;     //      This allows people to resume without harm...
631                                         }
632                         }
633                 }
634         }
635 #endif
636
637 }
638 #endif
639
640 // -- //        -------------------------------------------------------------------------------------------------------
641 // -- //        Creates a path from the objects current segment (objp->segnum) to the specified segment for the object to
642 // -- //        hide in Ai_local_info[objnum].goal_segment.
643 // -- //        Sets    objp->ctype.ai_info.hide_index,         a pointer into Point_segs, the first point_seg of the path.
644 // -- //                        objp->ctype.ai_info.path_length,                length of path
645 // -- //                        Point_segs_free_ptr                             global pointer into Point_segs array
646 // -- void create_path(object *objp)
647 // -- {
648 // --   ai_static       *aip = &objp->ctype.ai_info;
649 // --   ai_local    *ailp = &Ai_local_info[OBJECT_NUMBER(objp)];
650 // --   int                     start_seg, end_seg;
651 // --
652 // --   start_seg = objp->segnum;
653 // --   end_seg = ailp->goal_segment;
654 // --
655 // --   if (end_seg == -1)
656 // --           create_n_segment_path(objp, 3, -1);
657 // --
658 // --   if (end_seg == -1) {
659 // --           ; //mprintf((0, "Object %i, hide_segment = -1, not creating path.\n", OBJECT_NUMBER(objp)));
660 // --   } else {
661 // --           create_path_points(objp, start_seg, end_seg, Point_segs_free_ptr, &aip->path_length, -1, 0, 0, -1);
662 // --           aip->hide_index = POINT_SEG_NUMBER(Point_segs_free_ptr);
663 // --           aip->cur_path_index = 0;
664 // -- #if PATH_VALIDATION
665 // --           validate_path(5, Point_segs_free_ptr, aip->path_length);
666 // -- #endif
667 // --           Point_segs_free_ptr += aip->path_length;
668 // --           if (POINT_SEG_NUMBER(Point_segs_free_ptr) + MAX_PATH_LENGTH*2 > MAX_POINT_SEGS) {
669 // --                   //Int3();       //      Contact Mike: This is curious, though not deadly. /eip++;g
670 // --                   //force_dump_ai_objects_all("Error in create_path");
671 // --                   ai_reset_all_paths();
672 // --           }
673 // --           aip->PATH_DIR = 1;              //      Initialize to moving forward.
674 // --           aip->SUBMODE = AISM_HIDING;             //      Pretend we are hiding, so we sit here until bothered.
675 // --   }
676 // --
677 // --   maybe_ai_path_garbage_collect();
678 // --
679 // -- }
680
681 //      -------------------------------------------------------------------------------------------------------
682 //      Creates a path from the objects current segment (objp->segnum) to the specified segment for the object to
683 //      hide in Ai_local_info[objnum].goal_segment.
684 //      Sets    objp->ctype.ai_info.hide_index,         a pointer into Point_segs, the first point_seg of the path.
685 //                      objp->ctype.ai_info.path_length,                length of path
686 //                      Point_segs_free_ptr                             global pointer into Point_segs array
687 //      Change, 10/07/95: Used to create path to ConsoleObject->pos.  Now creates path to Believed_player_pos.
688 void create_path_to_player(object *objp, int max_length, int safety_flag)
689 {
690         ai_static       *aip = &objp->ctype.ai_info;
691         ai_local    *ailp = &Ai_local_info[OBJECT_NUMBER(objp)];
692         int                     start_seg, end_seg;
693
694 //mprintf((0, "Creating path to player.\n"));
695         if (max_length == -1)
696                 max_length = MAX_DEPTH_TO_SEARCH_FOR_PLAYER;
697
698         ailp->time_player_seen = GameTime;                      //      Prevent from resetting path quickly.
699         ailp->goal_segment = Believed_player_seg;
700
701         start_seg = objp->segnum;
702         end_seg = ailp->goal_segment;
703
704         // mprintf((0, "Creating path for object #%i, from segment #%i to #%i\n", OBJECT_NUMBER(objp), start_seg, end_seg));
705
706         if (end_seg == -1) {
707                 ; //mprintf((0, "Object %i, hide_segment = -1, not creating path.\n", OBJECT_NUMBER(objp)));
708         } else {
709                 create_path_points(objp, start_seg, end_seg, Point_segs_free_ptr, &aip->path_length, max_length, 1, safety_flag, -1);
710                 aip->path_length = polish_path(objp, Point_segs_free_ptr, aip->path_length);
711                 aip->hide_index = POINT_SEG_NUMBER(Point_segs_free_ptr);
712                 aip->cur_path_index = 0;
713                 Point_segs_free_ptr += aip->path_length;
714                 if (POINT_SEG_NUMBER(Point_segs_free_ptr) + MAX_PATH_LENGTH*2 > MAX_POINT_SEGS) {
715                         //Int3();       //      Contact Mike: This is stupid.  Should call maybe_ai_garbage_collect before the add.
716                         //force_dump_ai_objects_all("Error in create_path_to_player");
717                         ai_reset_all_paths();
718                         return;
719                 }
720 //              Assert(POINT_SEG_NUMBER(Point_segs_free_ptr) + MAX_PATH_LENGTH*2 < MAX_POINT_SEGS);
721                 aip->PATH_DIR = 1;              //      Initialize to moving forward.
722                 // -- UNUSED! aip->SUBMODE = AISM_GOHIDE;               //      This forces immediate movement.
723                 ailp->mode = AIM_FOLLOW_PATH;
724                 ailp->player_awareness_type = 0;                //      If robot too aware of player, will set mode to chase
725                 // mprintf((0, "Created %i segment path to player.\n", aip->path_length));
726         }
727
728         maybe_ai_path_garbage_collect();
729
730 }
731
732 //      -------------------------------------------------------------------------------------------------------
733 //      Creates a path from the object's current segment (objp->segnum) to segment goalseg.
734 void create_path_to_segment(object *objp, int goalseg, int max_length, int safety_flag)
735 {
736         ai_static       *aip = &objp->ctype.ai_info;
737         ai_local    *ailp = &Ai_local_info[OBJECT_NUMBER(objp)];
738         int                     start_seg, end_seg;
739
740         if (max_length == -1)
741                 max_length = MAX_DEPTH_TO_SEARCH_FOR_PLAYER;
742
743         ailp->time_player_seen = GameTime;                      //      Prevent from resetting path quickly.
744         ailp->goal_segment = goalseg;
745
746         start_seg = objp->segnum;
747         end_seg = ailp->goal_segment;
748
749         if (end_seg == -1) {
750                 ;
751         } else {
752                 create_path_points(objp, start_seg, end_seg, Point_segs_free_ptr, &aip->path_length, max_length, 1, safety_flag, -1);
753                 aip->hide_index = POINT_SEG_NUMBER(Point_segs_free_ptr);
754                 aip->cur_path_index = 0;
755                 Point_segs_free_ptr += aip->path_length;
756                 if (POINT_SEG_NUMBER(Point_segs_free_ptr) + MAX_PATH_LENGTH*2 > MAX_POINT_SEGS) {
757                         ai_reset_all_paths();
758                         return;
759                 }
760
761                 aip->PATH_DIR = 1;              //      Initialize to moving forward.
762                 // -- UNUSED! aip->SUBMODE = AISM_GOHIDE;               //      This forces immediate movement.
763                 ailp->player_awareness_type = 0;                //      If robot too aware of player, will set mode to chase
764         }
765
766         maybe_ai_path_garbage_collect();
767
768 }
769
770 //      -------------------------------------------------------------------------------------------------------
771 //      Creates a path from the objects current segment (objp->segnum) to the specified segment for the object to
772 //      hide in Ai_local_info[objnum].goal_segment
773 //      Sets    objp->ctype.ai_info.hide_index,         a pointer into Point_segs, the first point_seg of the path.
774 //                      objp->ctype.ai_info.path_length,                length of path
775 //                      Point_segs_free_ptr                             global pointer into Point_segs array
776 void create_path_to_station(object *objp, int max_length)
777 {
778         ai_static       *aip = &objp->ctype.ai_info;
779         ai_local    *ailp = &Ai_local_info[OBJECT_NUMBER(objp)];
780         int                     start_seg, end_seg;
781
782         if (max_length == -1)
783                 max_length = MAX_DEPTH_TO_SEARCH_FOR_PLAYER;
784
785         ailp->time_player_seen = GameTime;                      //      Prevent from resetting path quickly.
786
787         start_seg = objp->segnum;
788         end_seg = aip->hide_segment;
789
790         //1001: mprintf((0, "Back to station for object #%i, from segment #%i to #%i\n", OBJECT_NUMBER(objp), start_seg, end_seg));
791
792         if (end_seg == -1) {
793                 ; //mprintf((0, "Object %i, hide_segment = -1, not creating path.\n", OBJECT_NUMBER(objp)));
794         } else {
795                 create_path_points(objp, start_seg, end_seg, Point_segs_free_ptr, &aip->path_length, max_length, 1, 1, -1);
796                 aip->path_length = polish_path(objp, Point_segs_free_ptr, aip->path_length);
797                 aip->hide_index = POINT_SEG_NUMBER(Point_segs_free_ptr);
798                 aip->cur_path_index = 0;
799
800                 Point_segs_free_ptr += aip->path_length;
801                 if (POINT_SEG_NUMBER(Point_segs_free_ptr) + MAX_PATH_LENGTH*2 > MAX_POINT_SEGS) {
802                         //Int3();       //      Contact Mike: Stupid.
803                         //force_dump_ai_objects_all("Error in create_path_to_station");
804                         ai_reset_all_paths();
805                         return;
806                 }
807 //              Assert(POINT_SEG_NUMBER(Point_segs_free_ptr) + MAX_PATH_LENGTH*2 < MAX_POINT_SEGS);
808                 aip->PATH_DIR = 1;              //      Initialize to moving forward.
809                 // aip->SUBMODE = AISM_GOHIDE;          //      This forces immediate movement.
810                 ailp->mode = AIM_FOLLOW_PATH;
811                 ailp->player_awareness_type = 0;
812         }
813
814
815         maybe_ai_path_garbage_collect();
816
817 }
818
819
820 //      -------------------------------------------------------------------------------------------------------
821 //      Create a path of length path_length for an object, stuffing info in ai_info field.
822 void create_n_segment_path(object *objp, int path_length, int avoid_seg)
823 {
824         ai_static       *aip=&objp->ctype.ai_info;
825         ai_local    *ailp = &Ai_local_info[OBJECT_NUMBER(objp)];
826
827 //mprintf((0, "Creating %i segment path.\n", path_length));
828
829         if (create_path_points(objp, objp->segnum, -2, Point_segs_free_ptr, &aip->path_length, path_length, 1, 0, avoid_seg) == -1) {
830                 Point_segs_free_ptr += aip->path_length;
831                 while ((create_path_points(objp, objp->segnum, -2, Point_segs_free_ptr, &aip->path_length, --path_length, 1, 0, -1) == -1)) {
832                         //mprintf((0, "R"));
833                         Assert(path_length);
834                 }
835         }
836
837         aip->hide_index = POINT_SEG_NUMBER(Point_segs_free_ptr);
838         aip->cur_path_index = 0;
839 #if PATH_VALIDATION
840         validate_path(8, Point_segs_free_ptr, aip->path_length);
841 #endif
842         Point_segs_free_ptr += aip->path_length;
843         if (POINT_SEG_NUMBER(Point_segs_free_ptr) + MAX_PATH_LENGTH*2 > MAX_POINT_SEGS) {
844                 //Int3();       //      Contact Mike: This is curious, though not deadly. /eip++;g
845                 //force_dump_ai_objects_all("Error in crete_n_segment_path 2");
846                 ai_reset_all_paths();
847         }
848
849         aip->PATH_DIR = 1;              //      Initialize to moving forward.
850         // -- UNUSED! aip->SUBMODE = -1;                //      Don't know what this means.
851         ailp->mode = AIM_FOLLOW_PATH;
852
853         //      If this robot is visible (player_visibility is not available) and it's running away, move towards outside with
854         //      randomness to prevent a stream of bots from going away down the center of a corridor.
855         if (Ai_local_info[OBJECT_NUMBER(objp)].previous_visibility) {
856                 if (aip->path_length) {
857                         int     t_num_points = aip->path_length;
858                         move_towards_outside(&Point_segs[aip->hide_index], &t_num_points, objp, 1);
859                         aip->path_length = t_num_points;
860                 }
861         }
862         //mprintf((0, "\n"));
863
864         maybe_ai_path_garbage_collect();
865
866 }
867
868 //      -------------------------------------------------------------------------------------------------------
869 void create_n_segment_path_to_door(object *objp, int path_length, int avoid_seg)
870 {
871         create_n_segment_path(objp, path_length, avoid_seg);
872 }
873
874 extern int Connected_segment_distance;
875
876 #define Int3_if(cond) if (!cond) Int3();
877
878 //      ----------------------------------------------------------------------------------------------------
879 void move_object_to_goal(object *objp, vms_vector *goal_point, int goal_seg)
880 {
881         ai_static       *aip = &objp->ctype.ai_info;
882         int                     segnum;
883
884         if (aip->path_length < 2)
885                 return;
886
887         Assert(objp->segnum != -1);
888
889         // mprintf((0, "[%i -> %i]\n", OBJECT_NUMBER(objp), goal_seg));
890
891 #ifndef NDEBUG
892         if (objp->segnum != goal_seg)
893                 if (find_connect_side(&Segments[objp->segnum], &Segments[goal_seg]) == -1) {
894                         fix     dist;
895                         dist = find_connected_distance(&objp->pos, objp->segnum, goal_point, goal_seg, 30, WID_FLY_FLAG);
896                         if (Connected_segment_distance > 2) {   //      This global is set in find_connected_distance
897                                 // -- Int3();
898                                 mprintf((1, "Warning: Object %i hopped across %i segments, a distance of %7.3f.\n", OBJECT_NUMBER(objp), Connected_segment_distance, f2fl(dist)));
899                         }
900                 }
901 #endif
902
903         Assert(aip->path_length >= 2);
904
905         if (aip->cur_path_index <= 0) {
906                 if (aip->behavior == AIB_STATION) {
907                         // mprintf((0, "Object #%i, creating path back to station.\n", OBJECT_NUMBER(objp)));
908                         create_path_to_station(objp, 15);
909                         return;
910                 }
911                 aip->cur_path_index = 1;
912                 aip->PATH_DIR = 1;
913         } else if (aip->cur_path_index >= aip->path_length - 1) {
914                 if (aip->behavior == AIB_STATION) {
915                         // mprintf((0, "Object #%i, creating path back to station.\n", OBJECT_NUMBER(objp)));
916                         create_path_to_station(objp, 15);
917                         if (aip->path_length == 0) {
918                                 ai_local *ailp = &Ai_local_info[OBJECT_NUMBER(objp)];
919                                 ailp->mode = AIM_STILL;
920                         }
921                         return;
922                 }
923                 Assert(aip->path_length != 0);
924                 aip->cur_path_index = aip->path_length-2;
925                 aip->PATH_DIR = -1;
926         } else
927                 aip->cur_path_index += aip->PATH_DIR;
928
929         //--Int3_if(((aip->cur_path_index >= 0) && (aip->cur_path_index < aip->path_length)));
930
931         objp->pos = *goal_point;
932         segnum = find_object_seg(objp);
933         if (segnum != goal_seg)
934                 mprintf((1, "Object #%i goal supposed to be in segment #%i, but in segment #%i\n", OBJECT_NUMBER(objp), goal_seg, segnum));
935
936         if (segnum == -1) {
937                 Int3(); //      Oops, object is not in any segment.
938                                         // Contact Mike: This is impossible.
939                 //      Hack, move object to center of segment it used to be in.
940                 compute_segment_center(&objp->pos, &Segments[objp->segnum]);
941         } else
942                 obj_relink(OBJECT_NUMBER(objp), segnum);
943 }
944
945 // -- too much work -- //       ----------------------------------------------------------------------------------------------------------
946 // -- too much work -- //       Return true if the object the companion wants to kill is reachable.
947 // -- too much work -- int attack_kill_object(object *objp)
948 // -- too much work -- {
949 // -- too much work --  object          *kill_objp;
950 // -- too much work --  fvi_info                hit_data;
951 // -- too much work --  int                     fate;
952 // -- too much work --  fvi_query       fq;
953 // -- too much work --
954 // -- too much work --  if (Escort_kill_object == -1)
955 // -- too much work --          return 0;
956 // -- too much work --
957 // -- too much work --  kill_objp = &Objects[Escort_kill_object];
958 // -- too much work --
959 // -- too much work --  fq.p0                                           = &objp->pos;
960 // -- too much work --  fq.startseg                             = objp->segnum;
961 // -- too much work --  fq.p1                                           = &kill_objp->pos;
962 // -- too much work --  fq.rad                                  = objp->size;
963 // -- too much work --  fq.thisobjnum       = OBJECT_NUMBER(objp);
964 // -- too much work --  fq.ignore_obj_list      = NULL;
965 // -- too much work --  fq.flags                                        = 0;
966 // -- too much work --
967 // -- too much work --  fate = find_vector_intersection(&fq,&hit_data);
968 // -- too much work --
969 // -- too much work --  if (fate == HIT_NONE)
970 // -- too much work --          return 1;
971 // -- too much work --  else
972 // -- too much work --          return 0;
973 // -- too much work -- }
974
975 //      ----------------------------------------------------------------------------------------------------------
976 //      Optimization: If current velocity will take robot near goal, don't change velocity
977 void ai_follow_path(object *objp, int player_visibility, int previous_visibility, vms_vector *vec_to_player)
978 {
979         ai_static               *aip = &objp->ctype.ai_info;
980
981         vms_vector      goal_point, new_goal_point;
982         fix                     dist_to_goal;
983         robot_info      *robptr = &Robot_info[objp->id];
984         int                     forced_break, original_dir, original_index;
985         fix                     dist_to_player;
986         int                     goal_seg;
987         ai_local    *ailp = &Ai_local_info[OBJECT_NUMBER(objp)];
988         fix                     threshold_distance;
989
990
991 // mprintf((0, "Obj %i, dist=%6.1f index=%i len=%i seg=%i pos = %6.1f %6.1f %6.1f.\n", OBJECT_NUMBER(objp), f2fl(vm_vec_dist_quick(&objp->pos, &ConsoleObject->pos)), aip->cur_path_index, aip->path_length, objp->segnum, f2fl(objp->pos.x), f2fl(objp->pos.y), f2
992
993         if ((aip->hide_index == -1) || (aip->path_length == 0))
994         {
995                 if (ailp->mode == AIM_RUN_FROM_OBJECT) {
996                         create_n_segment_path(objp, 5, -1);
997                         //--Int3_if((aip->path_length != 0));
998                         ailp->mode = AIM_RUN_FROM_OBJECT;
999                 } else {
1000                         // -- mprintf((0, "Object %i creating path for no apparent reason.\n", OBJECT_NUMBER(objp)));
1001                         create_n_segment_path(objp, 5, -1);
1002                         //--Int3_if((aip->path_length != 0));
1003                 }
1004         }
1005
1006         if ((aip->hide_index + aip->path_length > POINT_SEG_NUMBER(Point_segs_free_ptr)) && (aip->path_length>0)) {
1007                 Int3(); //      Contact Mike: Bad.  Path goes into what is believed to be free space.
1008                 //      This is debugging code.  Figure out why garbage collection
1009                 //      didn't compress this object's path information.
1010                 ai_path_garbage_collect();
1011                 //force_dump_ai_objects_all("Error in ai_follow_path");
1012                 ai_reset_all_paths();
1013         }
1014
1015         if (aip->path_length < 2) {
1016                 if ((aip->behavior == AIB_SNIPE) || (ailp->mode == AIM_RUN_FROM_OBJECT)) {
1017                         if (ConsoleObject->segnum == objp->segnum) {
1018                                 create_n_segment_path(objp, AVOID_SEG_LENGTH, -1);                      //      Can't avoid segment player is in, robot is already in it! (That's what the -1 is for)
1019                                 //--Int3_if((aip->path_length != 0));
1020                         } else {
1021                                 create_n_segment_path(objp, AVOID_SEG_LENGTH, ConsoleObject->segnum);
1022                                 //--Int3_if((aip->path_length != 0));
1023                         }
1024                         if (aip->behavior == AIB_SNIPE) {
1025                                 if (robptr->thief)
1026                                         ailp->mode = AIM_THIEF_ATTACK;  //      It gets bashed in create_n_segment_path
1027                                 else
1028                                         ailp->mode = AIM_SNIPE_FIRE;    //      It gets bashed in create_n_segment_path
1029                         } else {
1030                                 ailp->mode = AIM_RUN_FROM_OBJECT;       //      It gets bashed in create_n_segment_path
1031                         }
1032                 } else if (robptr->companion == 0) {
1033                         ailp->mode = AIM_STILL;
1034                         aip->path_length = 0;
1035                         return;
1036                 }
1037         }
1038
1039         //--Int3_if(((aip->PATH_DIR == -1) || (aip->PATH_DIR == 1)));
1040         //--Int3_if(((aip->cur_path_index >= 0) && (aip->cur_path_index < aip->path_length)));
1041
1042         goal_point = Point_segs[aip->hide_index + aip->cur_path_index].point;
1043         goal_seg = Point_segs[aip->hide_index + aip->cur_path_index].segnum;
1044         dist_to_goal = vm_vec_dist_quick(&goal_point, &objp->pos);
1045
1046         if (Player_is_dead)
1047                 dist_to_player = vm_vec_dist_quick(&objp->pos, &Viewer->pos);
1048         else
1049                 dist_to_player = vm_vec_dist_quick(&objp->pos, &ConsoleObject->pos);
1050
1051         //      Efficiency hack: If far away from player, move in big quantized jumps.
1052         if (!(player_visibility || previous_visibility) && (dist_to_player > F1_0*200) && !(Game_mode & GM_MULTI)) {
1053                 if (dist_to_goal < F1_0*2) {
1054                         move_object_to_goal(objp, &goal_point, goal_seg);
1055                         return;
1056                 } else {
1057                         robot_info      *robptr = &Robot_info[objp->id];
1058                         fix     cur_speed = robptr->max_speed[Difficulty_level]/2;
1059                         fix     distance_travellable = fixmul(FrameTime, cur_speed);
1060
1061                         // int  connect_side = find_connect_side(objp->segnum, goal_seg);
1062                         //      Only move to goal if allowed to fly through the side.
1063                         //      Buddy-bot can create paths he can't fly, waiting for player.
1064                         // -- bah, this isn't good enough, buddy will fail to get through any door! if (WALL_IS_DOORWAY(&Segments[objp->segnum], connect_side) & WID_FLY_FLAG) {
1065                         if (!Robot_info[objp->id].companion && !Robot_info[objp->id].thief) {
1066                                 if (distance_travellable >= dist_to_goal) {
1067                                         move_object_to_goal(objp, &goal_point, goal_seg);
1068                                 } else {
1069                                         fix     prob = fixdiv(distance_travellable, dist_to_goal);
1070         
1071                                         int     rand_num = d_rand();
1072                                         if ( (rand_num >> 1) < prob) {
1073                                                 move_object_to_goal(objp, &goal_point, goal_seg);
1074                                         }
1075                                 }
1076                                 return;
1077                         }
1078                 }
1079
1080         }
1081
1082         //      If running from player, only run until can't be seen.
1083         if (ailp->mode == AIM_RUN_FROM_OBJECT) {
1084                 if ((player_visibility == 0) && (ailp->player_awareness_type == 0)) {
1085                         fix     vel_scale;
1086
1087                         vel_scale = F1_0 - FrameTime/2;
1088                         if (vel_scale < F1_0/2)
1089                                 vel_scale = F1_0/2;
1090
1091                         vm_vec_scale(&objp->mtype.phys_info.velocity, vel_scale);
1092
1093                         return;
1094                 } else if (!(FrameCount ^ ((OBJECT_NUMBER(objp)) & 0x07))) { // Done 1/8 frames.
1095                         //      If player on path (beyond point robot is now at), then create a new path.
1096                         point_seg       *curpsp = &Point_segs[aip->hide_index];
1097                         int                     player_segnum = ConsoleObject->segnum;
1098                         int                     i;
1099
1100                         //      This is probably being done every frame, which is wasteful.
1101                         for (i=aip->cur_path_index; i<aip->path_length; i++) {
1102                                 if (curpsp[i].segnum == player_segnum) {
1103                                         if (player_segnum != objp->segnum) {
1104                                                 create_n_segment_path(objp, AVOID_SEG_LENGTH, player_segnum);
1105                                         } else {
1106                                                 create_n_segment_path(objp, AVOID_SEG_LENGTH, -1);
1107                                         }
1108                                         Assert(aip->path_length != 0);
1109                                         ailp->mode = AIM_RUN_FROM_OBJECT;       //      It gets bashed in create_n_segment_path
1110                                         break;
1111                                 }
1112                         }
1113                         if (player_visibility) {
1114                                 ailp->player_awareness_type = 1;
1115                                 ailp->player_awareness_time = F1_0;
1116                         }
1117                 }
1118         }
1119
1120         //--Int3_if(((aip->cur_path_index >= 0) && (aip->cur_path_index < aip->path_length)));
1121
1122         if (aip->cur_path_index < 0) {
1123                 aip->cur_path_index = 0;
1124         } else if (aip->cur_path_index >= aip->path_length) {
1125                 if (ailp->mode == AIM_RUN_FROM_OBJECT) {
1126                         create_n_segment_path(objp, AVOID_SEG_LENGTH, ConsoleObject->segnum);
1127                         ailp->mode = AIM_RUN_FROM_OBJECT;       //      It gets bashed in create_n_segment_path
1128                         Assert(aip->path_length != 0);
1129                 } else {
1130                         aip->cur_path_index = aip->path_length-1;
1131                 }
1132         }
1133
1134         goal_point = Point_segs[aip->hide_index + aip->cur_path_index].point;
1135
1136         //      If near goal, pick another goal point.
1137         forced_break = 0;               //      Gets set for short paths.
1138         original_dir = aip->PATH_DIR;
1139         original_index = aip->cur_path_index;
1140         threshold_distance = fixmul(vm_vec_mag_quick(&objp->mtype.phys_info.velocity), FrameTime)*2 + F1_0*2;
1141
1142         new_goal_point = Point_segs[aip->hide_index + aip->cur_path_index].point;
1143
1144         //--Int3_if(((aip->cur_path_index >= 0) && (aip->cur_path_index < aip->path_length)));
1145
1146         while ((dist_to_goal < threshold_distance) && !forced_break) {
1147
1148                 //      Advance to next point on path.
1149                 aip->cur_path_index += aip->PATH_DIR;
1150
1151                 //      See if next point wraps past end of path (in either direction), and if so, deal with it based on mode.
1152                 if ((aip->cur_path_index >= aip->path_length) || (aip->cur_path_index < 0)) {
1153
1154                         //mprintf((0, "Object %i reached end of the line!\n", OBJECT_NUMBER(objp)));
1155                         //      If mode = hiding, then stay here until get bonked or hit by player.
1156                         // --   if (ailp->mode == AIM_BEHIND) {
1157                         // --           ailp->mode = AIM_STILL;
1158                         // --           return;         // Stay here until bonked or hit by player.
1159                         // --   } else
1160
1161                         //      Buddy bot.  If he's in mode to get away from player and at end of line,
1162                         //      if player visible, then make a new path, else just return.
1163                         if (robptr->companion) {
1164                                 if (Escort_special_goal == ESCORT_GOAL_SCRAM)
1165                                 {
1166                                         if (player_visibility) {
1167                                                 create_n_segment_path(objp, 16 + d_rand() * 16, -1);
1168                                                 aip->path_length = polish_path(objp, &Point_segs[aip->hide_index], aip->path_length);
1169                                                 Assert(aip->path_length != 0);
1170                                                 // -- mprintf((0, "Buddy: Creating new path!\n"));
1171                                                 ailp->mode = AIM_WANDER;        //      Special buddy mode.
1172                                                 //--Int3_if(((aip->cur_path_index >= 0) && (aip->cur_path_index < aip->path_length)));
1173                                                 return;
1174                                         } else {
1175                                                 ailp->mode = AIM_WANDER;        //      Special buddy mode.
1176                                                 vm_vec_zero(&objp->mtype.phys_info.velocity);
1177                                                 vm_vec_zero(&objp->mtype.phys_info.rotvel);
1178                                                 // -- mprintf((0, "Buddy: I'm hidden!\n"));
1179                                                 //!!Assert((aip->cur_path_index >= 0) && (aip->cur_path_index < aip->path_length));
1180                                                 return;
1181                                         }
1182                                 }
1183                         }
1184
1185                         if (aip->behavior == AIB_FOLLOW) {
1186                                 // mprintf((0, "AIB_FOLLOW: Making new path.\n"));
1187                                 create_n_segment_path(objp, 10, ConsoleObject->segnum);
1188                                 //--Int3_if(((aip->cur_path_index >= 0) && (aip->cur_path_index < aip->path_length)));
1189                         } else if (aip->behavior == AIB_STATION) {
1190                                 // mprintf((0, "Object %i reached end of line, creating path back to station.\n", OBJECT_NUMBER(objp)));
1191                                 create_path_to_station(objp, 15);
1192                                 if ((aip->hide_segment != Point_segs[aip->hide_index+aip->path_length-1].segnum) || (aip->path_length == 0)) {
1193                                         ailp->mode = AIM_STILL;
1194                                 } else {
1195                                         //--Int3_if(((aip->cur_path_index >= 0) && (aip->cur_path_index < aip->path_length)));
1196                                 }
1197                                 return;
1198                         } else if (ailp->mode == AIM_FOLLOW_PATH) {
1199                                 create_path_to_player(objp, 10, 1);
1200                                 if (aip->hide_segment != Point_segs[aip->hide_index+aip->path_length-1].segnum) {
1201                                         ailp->mode = AIM_STILL;
1202                                         return;
1203                                 } else {
1204                                         //--Int3_if(((aip->cur_path_index >= 0) && (aip->cur_path_index < aip->path_length)));
1205                                 }
1206                         } else if (ailp->mode == AIM_RUN_FROM_OBJECT) {
1207                                 create_n_segment_path(objp, AVOID_SEG_LENGTH, ConsoleObject->segnum);
1208                                 ailp->mode = AIM_RUN_FROM_OBJECT;       //      It gets bashed in create_n_segment_path
1209                                 if (aip->path_length < 1) {
1210                                         create_n_segment_path(objp, AVOID_SEG_LENGTH, ConsoleObject->segnum);
1211                                         ailp->mode = AIM_RUN_FROM_OBJECT;       //      It gets bashed in create_n_segment_path
1212                                         if (aip->path_length < 1) {
1213                                                 aip->behavior = AIB_NORMAL;
1214                                                 ailp->mode = AIM_STILL;
1215                                                 return;
1216                                         }
1217                                 }
1218                                 //--Int3_if(((aip->cur_path_index >= 0) && (aip->cur_path_index < aip->path_length)));
1219                         } else {
1220                                 //      Reached end of the line.  First see if opposite end point is reachable, and if so, go there.
1221                                 //      If not, turn around.
1222                                 int                     opposite_end_index;
1223                                 vms_vector      *opposite_end_point;
1224                                 fvi_info                hit_data;
1225                                 int                     fate;
1226                                 fvi_query       fq;
1227
1228                                 // See which end we're nearer and look at the opposite end point.
1229                                 if (abs(aip->cur_path_index - aip->path_length) < aip->cur_path_index) {
1230                                         //      Nearer to far end (ie, index not 0), so try to reach 0.
1231                                         opposite_end_index = 0;
1232                                 } else {
1233                                         //      Nearer to 0 end, so try to reach far end.
1234                                         opposite_end_index = aip->path_length-1;
1235                                 }
1236
1237                                 //--Int3_if(((opposite_end_index >= 0) && (opposite_end_index < aip->path_length)));
1238
1239                                 opposite_end_point = &Point_segs[aip->hide_index + opposite_end_index].point;
1240
1241                                 fq.p0                                           = &objp->pos;
1242                                 fq.startseg                             = objp->segnum;
1243                                 fq.p1                                           = opposite_end_point;
1244                                 fq.rad                                  = objp->size;
1245                                 fq.thisobjnum       = OBJECT_NUMBER(objp);
1246                                 fq.ignore_obj_list      = NULL;
1247                                 fq.flags                                        = 0;                            //what about trans walls???
1248
1249                                 fate = find_vector_intersection(&fq,&hit_data);
1250
1251                                 if (fate != HIT_WALL) {
1252                                         //      We can be circular!  Do it!
1253                                         //      Path direction is unchanged.
1254                                         aip->cur_path_index = opposite_end_index;
1255                                 } else {
1256                                         aip->PATH_DIR = -aip->PATH_DIR;
1257                                         aip->cur_path_index += aip->PATH_DIR;
1258                                 }
1259                                 //--Int3_if(((aip->cur_path_index >= 0) && (aip->cur_path_index < aip->path_length)));
1260                         }
1261                         break;
1262                 } else {
1263                         new_goal_point = Point_segs[aip->hide_index + aip->cur_path_index].point;
1264                         goal_point = new_goal_point;
1265                         dist_to_goal = vm_vec_dist_quick(&goal_point, &objp->pos);
1266                         //--Int3_if(((aip->cur_path_index >= 0) && (aip->cur_path_index < aip->path_length)));
1267                 }
1268
1269                 //      If went all the way around to original point, in same direction, then get out of here!
1270                 if ((aip->cur_path_index == original_index) && (aip->PATH_DIR == original_dir)) {
1271                         create_path_to_player(objp, 3, 1);
1272                         //--Int3_if(((aip->cur_path_index >= 0) && (aip->cur_path_index < aip->path_length)));
1273                         forced_break = 1;
1274                 }
1275                 //--Int3_if(((aip->cur_path_index >= 0) && (aip->cur_path_index < aip->path_length)));
1276         }       //      end while
1277
1278         //      Set velocity (objp->mtype.phys_info.velocity) and orientation (objp->orient) for this object.
1279         //--Int3_if(((aip->cur_path_index >= 0) && (aip->cur_path_index < aip->path_length)));
1280         ai_path_set_orient_and_vel(objp, &goal_point, player_visibility, vec_to_player);
1281         //--Int3_if(((aip->cur_path_index >= 0) && (aip->cur_path_index < aip->path_length)));
1282
1283 }
1284
1285 typedef struct {
1286         short   path_start, objnum;
1287 } obj_path;
1288
1289 int path_index_compare(obj_path *i1, obj_path *i2)
1290 {
1291         if (i1->path_start < i2->path_start)
1292                 return -1;
1293         else if (i1->path_start == i2->path_start)
1294                 return 0;
1295         else
1296                 return 1;
1297 }
1298
1299 //      ----------------------------------------------------------------------------------------------------------
1300 //      Set orientation matrix and velocity for objp based on its desire to get to a point.
1301 void ai_path_set_orient_and_vel(object *objp, vms_vector *goal_point, int player_visibility, vms_vector *vec_to_player)
1302 {
1303         vms_vector      cur_vel = objp->mtype.phys_info.velocity;
1304         vms_vector      norm_cur_vel;
1305         vms_vector      norm_vec_to_goal;
1306         vms_vector      cur_pos = objp->pos;
1307         vms_vector      norm_fvec;
1308         fix                     speed_scale;
1309         fix                     dot;
1310         robot_info      *robptr = &Robot_info[objp->id];
1311         fix                     max_speed;
1312
1313         //      If evading player, use highest difficulty level speed, plus something based on diff level
1314         max_speed = robptr->max_speed[Difficulty_level];
1315         if ((Ai_local_info[OBJECT_NUMBER(objp)].mode == AIM_RUN_FROM_OBJECT) || (objp->ctype.ai_info.behavior == AIB_SNIPE))
1316                 max_speed = max_speed*3/2;
1317
1318         vm_vec_sub(&norm_vec_to_goal, goal_point, &cur_pos);
1319         vm_vec_normalize_quick(&norm_vec_to_goal);
1320
1321         norm_cur_vel = cur_vel;
1322         vm_vec_normalize_quick(&norm_cur_vel);
1323
1324         norm_fvec = objp->orient.fvec;
1325         vm_vec_normalize_quick(&norm_fvec);
1326
1327         dot = vm_vec_dot(&norm_vec_to_goal, &norm_fvec);
1328
1329         //      If very close to facing opposite desired vector, perturb vector
1330         if (dot < -15*F1_0/16) {
1331                 //mprintf((0, "Facing away from goal, abruptly turning\n"));
1332                 norm_cur_vel = norm_vec_to_goal;
1333         } else {
1334                 norm_cur_vel.x += norm_vec_to_goal.x/2;
1335                 norm_cur_vel.y += norm_vec_to_goal.y/2;
1336                 norm_cur_vel.z += norm_vec_to_goal.z/2;
1337         }
1338
1339         vm_vec_normalize_quick(&norm_cur_vel);
1340
1341         //      Set speed based on this robot type's maximum allowed speed and how hard it is turning.
1342         //      How hard it is turning is based on the dot product of (vector to goal) and (current velocity vector)
1343         //      Note that since 3*F1_0/4 is added to dot product, it is possible for the robot to back up.
1344
1345         //      Set speed and orientation.
1346         if (dot < 0)
1347                 dot /= -4;
1348
1349         //      If in snipe mode, can move fast even if not facing that direction.
1350         if (objp->ctype.ai_info.behavior == AIB_SNIPE)
1351                 if (dot < F1_0/2)
1352                         dot = (dot + F1_0)/2;
1353
1354         speed_scale = fixmul(max_speed, dot);
1355         vm_vec_scale(&norm_cur_vel, speed_scale);
1356         objp->mtype.phys_info.velocity = norm_cur_vel;
1357
1358         if ((Ai_local_info[OBJECT_NUMBER(objp)].mode == AIM_RUN_FROM_OBJECT) || (robptr->companion == 1) || (objp->ctype.ai_info.behavior == AIB_SNIPE)) {
1359                 if (Ai_local_info[OBJECT_NUMBER(objp)].mode == AIM_SNIPE_RETREAT_BACKWARDS) {
1360                         if ((player_visibility) && (vec_to_player != NULL))
1361                                 norm_vec_to_goal = *vec_to_player;
1362                         else
1363                                 vm_vec_negate(&norm_vec_to_goal);
1364                 }
1365                 ai_turn_towards_vector(&norm_vec_to_goal, objp, robptr->turn_time[NDL-1]/2);
1366         } else
1367                 ai_turn_towards_vector(&norm_vec_to_goal, objp, robptr->turn_time[Difficulty_level]);
1368
1369 }
1370
1371 int     Last_frame_garbage_collected = 0;
1372
1373 //      ----------------------------------------------------------------------------------------------------------
1374 //      Garbage colledion -- Free all unused records in Point_segs and compress all paths.
1375 void ai_path_garbage_collect(void)
1376 {
1377         int     free_path_index = 0;
1378         int     num_path_objects = 0;
1379         int     objnum;
1380         int     objind;
1381         obj_path                object_list[MAX_OBJECTS];
1382
1383 #ifndef NDEBUG
1384         force_dump_ai_objects_all("***** Start ai_path_garbage_collect *****");
1385 #endif
1386
1387         // -- mprintf((0, "Garbage collection frame %i, last frame %i!  Old free index = %i ", FrameCount, Last_frame_garbage_collected, POINT_SEG_NUMBER(Point_segs_free_ptr)));
1388
1389         Last_frame_garbage_collected = FrameCount;
1390
1391 #if PATH_VALIDATION
1392         validate_all_paths();
1393 #endif
1394         //      Create a list of objects which have paths of length 1 or more.
1395         for (objnum=0; objnum <= Highest_object_index; objnum++) {
1396                 object  *objp = &Objects[objnum];
1397
1398                 if ((objp->type == OBJ_ROBOT) && ((objp->control_type == CT_AI) || (objp->control_type == CT_MORPH))) {
1399                         ai_static       *aip = &objp->ctype.ai_info;
1400
1401                         if (aip->path_length) {
1402                                 object_list[num_path_objects].path_start = aip->hide_index;
1403                                 object_list[num_path_objects++].objnum = objnum;
1404                         }
1405                 }
1406         }
1407
1408         qsort(object_list, num_path_objects, sizeof(object_list[0]),
1409                         (int (*)(void const *,void const *))path_index_compare);
1410
1411         for (objind=0; objind < num_path_objects; objind++) {
1412                 object          *objp;
1413                 ai_static       *aip;
1414                 int                     i;
1415                 int                     old_index;
1416
1417                 objnum = object_list[objind].objnum;
1418                 objp = &Objects[objnum];
1419                 aip = &objp->ctype.ai_info;
1420                 old_index = aip->hide_index;
1421
1422                 aip->hide_index = free_path_index;
1423                 for (i=0; i<aip->path_length; i++)
1424                         Point_segs[free_path_index++] = Point_segs[old_index++];
1425         }
1426
1427         Point_segs_free_ptr = &Point_segs[free_path_index];
1428
1429         // mprintf((0, "new = %i\n", free_path_index));
1430 //printf("After garbage collection, free index = %i\n", POINT_SEG_NUMBER(Point_segs_free_ptr));
1431 #ifndef NDEBUG
1432         {
1433         int i;
1434
1435         force_dump_ai_objects_all("***** Finish ai_path_garbage_collect *****");
1436
1437         for (i=0; i<=Highest_object_index; i++) {
1438                 ai_static       *aip = &Objects[i].ctype.ai_info;
1439
1440                 if ((Objects[i].type == OBJ_ROBOT) && (Objects[i].control_type == CT_AI))
1441                         if ((aip->hide_index + aip->path_length > POINT_SEG_NUMBER(Point_segs_free_ptr)) && (aip->path_length>0))
1442                                 Int3();         //      Contact Mike: Debug trap for nasty, elusive bug.
1443         }
1444
1445         validate_all_paths();
1446         }
1447 #endif
1448
1449 }
1450
1451 //      -----------------------------------------------------------------------------
1452 //      Do garbage collection if not been done for awhile, or things getting really critical.
1453 void maybe_ai_path_garbage_collect(void)
1454 {
1455         if (POINT_SEG_NUMBER(Point_segs_free_ptr) > MAX_POINT_SEGS - MAX_PATH_LENGTH) {
1456                 if (Last_frame_garbage_collected+1 >= FrameCount) {
1457                         //      This is kind of bad.  Garbage collected last frame or this frame.
1458                         //      Just destroy all paths.  Too bad for the robots.  They are memory wasteful.
1459                         ai_reset_all_paths();
1460                         mprintf((1, "Warning: Resetting all paths.  Point_segs buffer nearly exhausted.\n"));
1461                 } else {
1462                         //      We are really close to full, but didn't just garbage collect, so maybe this is recoverable.
1463                         mprintf((1, "Warning: Almost full garbage collection being performed: "));
1464                         ai_path_garbage_collect();
1465                         mprintf((1, "Free records = %i/%i\n", MAX_POINT_SEGS - (POINT_SEG_NUMBER(Point_segs_free_ptr)), MAX_POINT_SEGS));
1466                 }
1467         } else if (POINT_SEG_NUMBER(Point_segs_free_ptr) > 3*MAX_POINT_SEGS/4) {
1468                 if (Last_frame_garbage_collected + 16 < FrameCount) {
1469                         ai_path_garbage_collect();
1470                 }
1471         } else if (POINT_SEG_NUMBER(Point_segs_free_ptr) > MAX_POINT_SEGS/2) {
1472                 if (Last_frame_garbage_collected + 256 < FrameCount) {
1473                         ai_path_garbage_collect();
1474                 }
1475         }
1476 }
1477
1478 //      -----------------------------------------------------------------------------
1479 //      Reset all paths.  Do garbage collection.
1480 //      Should be called at the start of each level.
1481 void ai_reset_all_paths(void)
1482 {
1483         int     i;
1484
1485         for (i=0; i<=Highest_object_index; i++)
1486                 if (Objects[i].control_type == CT_AI) {
1487                         Objects[i].ctype.ai_info.hide_index = -1;
1488                         Objects[i].ctype.ai_info.path_length = 0;
1489                 }
1490
1491         ai_path_garbage_collect();
1492
1493 }
1494
1495 //      ---------------------------------------------------------------------------------------------------------
1496 //      Probably called because a robot bashed a wall, getting a bunch of retries.
1497 //      Try to resume path.
1498 void attempt_to_resume_path(object *objp)
1499 {
1500         //int             objnum = OBJECT_NUMBER(objp);
1501         ai_static               *aip = &objp->ctype.ai_info;
1502 //      int                             goal_segnum, object_segnum,
1503         int                             abs_index, new_path_index;
1504
1505         // mprintf((0, "Object %i trying to resume path at index %i\n", OBJECT_NUMBER(objp), aip->cur_path_index));
1506
1507         if ((aip->behavior == AIB_STATION) && (Robot_info[objp->id].companion != 1))
1508                 if (d_rand() > 8192) {
1509                         ai_local *ailp = &Ai_local_info[OBJECT_NUMBER(objp)];
1510
1511                         aip->hide_segment = objp->segnum;
1512 //Int3();
1513                         ailp->mode = AIM_STILL;
1514                         mprintf((1, "Note: Bashing hide segment of robot %i to current segment because he's lost.\n", OBJECT_NUMBER(objp)));
1515                 }
1516
1517 //      object_segnum = objp->segnum;
1518         abs_index = aip->hide_index+aip->cur_path_index;
1519 //      goal_segnum = Point_segs[abs_index].segnum;
1520
1521 //      if (object_segnum == goal_segnum)
1522 //              mprintf((0, "Very peculiar, goal segnum = object's segnum = %i.\n", goal_segnum));
1523
1524         new_path_index = aip->cur_path_index - aip->PATH_DIR;
1525
1526         if ((new_path_index >= 0) && (new_path_index < aip->path_length)) {
1527                 // mprintf((0, "Trying path index of %i\n", new_path_index));
1528                 aip->cur_path_index = new_path_index;
1529         } else {
1530                 //      At end of line and have nowhere to go.
1531                 // mprintf((0, "At end of line and can't get to goal.  Creating new path.  Frame %i\n", FrameCount));
1532                 move_towards_segment_center(objp);
1533                 create_path_to_station(objp, 15);
1534         }
1535
1536 }
1537
1538 //      ----------------------------------------------------------------------------------------------------------
1539 //                                      DEBUG FUNCTIONS FOLLOW
1540 //      ----------------------------------------------------------------------------------------------------------
1541
1542 #ifdef EDITOR
1543 int     Test_size = 1000;
1544
1545 void test_create_path_many(void)
1546 {
1547         point_seg       point_segs[200];
1548         short                   num_points;
1549
1550         int                     i;
1551
1552         for (i=0; i<Test_size; i++) {
1553                 Cursegp = &Segments[(d_rand() * (Highest_segment_index + 1)) / D_RAND_MAX];
1554                 Markedsegp = &Segments[(d_rand() * (Highest_segment_index + 1)) / D_RAND_MAX];
1555                 create_path_points(&Objects[0], SEGMENT_NUMBER(Cursegp), SEGMENT_NUMBER(Markedsegp), point_segs, &num_points, -1, 0, 0, -1);
1556         }
1557
1558 }
1559
1560 void test_create_path(void)
1561 {
1562         point_seg       point_segs[200];
1563         short                   num_points;
1564
1565         create_path_points(&Objects[0], SEGMENT_NUMBER(Cursegp), SEGMENT_NUMBER(Markedsegp), point_segs, &num_points, -1, 0, 0, -1);
1566
1567 }
1568
1569 void show_path(int start_seg, int end_seg, point_seg *psp, short length)
1570 {
1571         printf("[%3i:%3i (%3i):] ", start_seg, end_seg, length);
1572
1573         while (length--)
1574                 printf("%3i ", psp[length].segnum);
1575
1576         printf("\n");
1577 }
1578
1579 //      For all segments in mine, create paths to all segments in mine, print results.
1580 void test_create_all_paths(void)
1581 {
1582         int     start_seg, end_seg;
1583         short   resultant_length;
1584
1585         Point_segs_free_ptr = Point_segs;
1586
1587         for (start_seg=0; start_seg<=Highest_segment_index-1; start_seg++) {
1588                 // -- mprintf((0, "."));
1589                 if (Segments[start_seg].segnum != -1) {
1590                         for (end_seg=start_seg+1; end_seg<=Highest_segment_index; end_seg++) {
1591                                 if (Segments[end_seg].segnum != -1) {
1592                                         create_path_points(&Objects[0], start_seg, end_seg, Point_segs_free_ptr, &resultant_length, -1, 0, 0, -1);
1593                                         show_path(start_seg, end_seg, Point_segs_free_ptr, resultant_length);
1594                                 }
1595                         }
1596                 }
1597         }
1598 }
1599
1600 //--anchor--int Num_anchors;
1601 //--anchor--int Anchor_distance = 3;
1602 //--anchor--int End_distance = 1;
1603 //--anchor--int Anchors[MAX_SEGMENTS];
1604
1605 //--anchor--int get_nearest_anchor_distance(int segnum)
1606 //--anchor--{
1607 //--anchor--    short   resultant_length, minimum_length;
1608 //--anchor--    int     anchor_index;
1609 //--anchor--
1610 //--anchor--    minimum_length = 16383;
1611 //--anchor--
1612 //--anchor--    for (anchor_index=0; anchor_index<Num_anchors; anchor_index++) {
1613 //--anchor--            create_path_points(&Objects[0], segnum, Anchors[anchor_index], Point_segs_free_ptr, &resultant_length, -1, 0, 0, -1);
1614 //--anchor--            if (resultant_length != 0)
1615 //--anchor--                    if (resultant_length < minimum_length)
1616 //--anchor--                            minimum_length = resultant_length;
1617 //--anchor--    }
1618 //--anchor--
1619 //--anchor--    return minimum_length;
1620 //--anchor--
1621 //--anchor--}
1622 //--anchor--
1623 //--anchor--void create_new_anchor(int segnum)
1624 //--anchor--{
1625 //--anchor--    Anchors[Num_anchors++] = segnum;
1626 //--anchor--}
1627 //--anchor--
1628 //--anchor--//  A set of anchors is within N units of all segments in the graph.
1629 //--anchor--//  Anchor_distance = how close anchors can be.
1630 //--anchor--//  End_distance = how close you can be to the end.
1631 //--anchor--void test_create_all_anchors(void)
1632 //--anchor--{
1633 //--anchor--    int     nearest_anchor_distance;
1634 //--anchor--    int     segnum,i;
1635 //--anchor--
1636 //--anchor--    Num_anchors = 0;
1637 //--anchor--
1638 //--anchor--    for (segnum=0; segnum<=Highest_segment_index; segnum++) {
1639 //--anchor--            if (Segments[segnum].segnum != -1) {
1640 //--anchor--                    nearest_anchor_distance = get_nearest_anchor_distance(segnum);
1641 //--anchor--                    if (nearest_anchor_distance > Anchor_distance)
1642 //--anchor--                            create_new_anchor(segnum);
1643 //--anchor--            }
1644 //--anchor--    }
1645 //--anchor--
1646 //--anchor--    //      Set selected segs.
1647 //--anchor--    for (i=0; i<Num_anchors; i++)
1648 //--anchor--            Selected_segs[i] = Anchors[i];
1649 //--anchor--    N_selected_segs = Num_anchors;
1650 //--anchor--
1651 //--anchor--}
1652 //--anchor--
1653 //--anchor--int Test_path_length = 5;
1654 //--anchor--
1655 //--anchor--void test_create_n_segment_path(void)
1656 //--anchor--{
1657 //--anchor--    point_seg       point_segs[200];
1658 //--anchor--    short                   num_points;
1659 //--anchor--
1660 //--anchor--    create_path_points(&Objects[0], SEGMENT_NUMBER(Cursegp), -2, point_segs, &num_points, Test_path_length, 0, 0, -1);
1661 //--anchor--}
1662
1663 short   Player_path_length=0;
1664 int     Player_hide_index=-1;
1665 int     Player_cur_path_index=0;
1666 int     Player_following_path_flag=0;
1667
1668 //      ------------------------------------------------------------------------------------------------------------------
1669 //      Set orientation matrix and velocity for objp based on its desire to get to a point.
1670 void player_path_set_orient_and_vel(object *objp, vms_vector *goal_point)
1671 {
1672         vms_vector      cur_vel = objp->mtype.phys_info.velocity;
1673         vms_vector      norm_cur_vel;
1674         vms_vector      norm_vec_to_goal;
1675         vms_vector      cur_pos = objp->pos;
1676         vms_vector      norm_fvec;
1677         fix                     speed_scale;
1678         fix                     dot;
1679         fix                     max_speed;
1680
1681         max_speed = Robot_info[objp->id].max_speed[Difficulty_level];
1682
1683         vm_vec_sub(&norm_vec_to_goal, goal_point, &cur_pos);
1684         vm_vec_normalize_quick(&norm_vec_to_goal);
1685
1686         norm_cur_vel = cur_vel;
1687         vm_vec_normalize_quick(&norm_cur_vel);
1688
1689         norm_fvec = objp->orient.fvec;
1690         vm_vec_normalize_quick(&norm_fvec);
1691
1692         dot = vm_vec_dot(&norm_vec_to_goal, &norm_fvec);
1693         if (Ai_local_info[OBJECT_NUMBER(objp)].mode == AIM_SNIPE_RETREAT_BACKWARDS) {
1694                 dot = -dot;
1695         }
1696
1697         //      If very close to facing opposite desired vector, perturb vector
1698         if (dot < -15*F1_0/16) {
1699                 //mprintf((0, "Facing away from goal, abruptly turning\n"));
1700                 norm_cur_vel = norm_vec_to_goal;
1701         } else {
1702                 norm_cur_vel.x += norm_vec_to_goal.x/2;
1703                 norm_cur_vel.y += norm_vec_to_goal.y/2;
1704                 norm_cur_vel.z += norm_vec_to_goal.z/2;
1705         }
1706
1707         vm_vec_normalize_quick(&norm_cur_vel);
1708
1709         //      Set speed based on this robot type's maximum allowed speed and how hard it is turning.
1710         //      How hard it is turning is based on the dot product of (vector to goal) and (current velocity vector)
1711         //      Note that since 3*F1_0/4 is added to dot product, it is possible for the robot to back up.
1712
1713         //      Set speed and orientation.
1714         if (dot < 0)
1715                 dot /= 4;
1716
1717         speed_scale = fixmul(max_speed, dot);
1718         vm_vec_scale(&norm_cur_vel, speed_scale);
1719         objp->mtype.phys_info.velocity = norm_cur_vel;
1720         ai_turn_towards_vector(&norm_vec_to_goal, objp, F1_0);
1721
1722 }
1723
1724 //      ----------------------------------------------------------------------------------------------------------
1725 //      Optimization: If current velocity will take robot near goal, don't change velocity
1726 void player_follow_path(object *objp)
1727 {
1728         vms_vector      goal_point;
1729         fix                     dist_to_goal;
1730         int                     count, forced_break, original_index;
1731         int                     goal_seg;
1732         fix                     threshold_distance;
1733
1734         if (!Player_following_path_flag)
1735                 return;
1736
1737         if (Player_hide_index == -1)
1738                 return;
1739
1740         if (Player_path_length < 2)
1741                 return;
1742
1743         goal_point = Point_segs[Player_hide_index + Player_cur_path_index].point;
1744         goal_seg = Point_segs[Player_hide_index + Player_cur_path_index].segnum;
1745         Assert((goal_seg >= 0) && (goal_seg <= Highest_segment_index));
1746         dist_to_goal = vm_vec_dist_quick(&goal_point, &objp->pos);
1747
1748         if (Player_cur_path_index < 0)
1749                 Player_cur_path_index = 0;
1750         else if (Player_cur_path_index >= Player_path_length)
1751                 Player_cur_path_index = Player_path_length-1;
1752
1753         goal_point = Point_segs[Player_hide_index + Player_cur_path_index].point;
1754
1755         count=0;
1756
1757         //      If near goal, pick another goal point.
1758         forced_break = 0;               //      Gets set for short paths.
1759         //original_dir = 1;
1760         original_index = Player_cur_path_index;
1761         threshold_distance = fixmul(vm_vec_mag_quick(&objp->mtype.phys_info.velocity), FrameTime)*2 + F1_0*2;
1762
1763         while ((dist_to_goal < threshold_distance) && !forced_break) {
1764
1765 // --           if (count > 1)
1766 // --                   mprintf((0, "."));
1767
1768                 //      ----- Debug stuff -----
1769                 if (count++ > 20) {
1770                         mprintf((1,"Problem following path for player.  Aborting.\n"));
1771                         break;
1772                 }
1773
1774                 //      Advance to next point on path.
1775                 Player_cur_path_index += 1;
1776
1777                 //      See if next point wraps past end of path (in either direction), and if so, deal with it based on mode.
1778                 if ((Player_cur_path_index >= Player_path_length) || (Player_cur_path_index < 0)) {
1779                         Player_following_path_flag = 0;
1780                         forced_break = 1;
1781                 }
1782
1783                 //      If went all the way around to original point, in same direction, then get out of here!
1784                 if (Player_cur_path_index == original_index) {
1785                         mprintf((0, "Forcing break because player path wrapped, count = %i.\n", count));
1786                         Player_following_path_flag = 0;
1787                         forced_break = 1;
1788                 }
1789
1790                 goal_point = Point_segs[Player_hide_index + Player_cur_path_index].point;
1791                 dist_to_goal = vm_vec_dist_quick(&goal_point, &objp->pos);
1792
1793         }       //      end while
1794
1795         //      Set velocity (objp->mtype.phys_info.velocity) and orientation (objp->orient) for this object.
1796         player_path_set_orient_and_vel(objp, &goal_point);
1797
1798 }
1799
1800
1801 //      ------------------------------------------------------------------------------------------------------------------
1802 //      Create path for player from current segment to goal segment.
1803 void create_player_path_to_segment(int segnum)
1804 {
1805         object          *objp = ConsoleObject;
1806
1807         Player_path_length=0;
1808         Player_hide_index=-1;
1809         Player_cur_path_index=0;
1810         Player_following_path_flag=0;
1811
1812         if (create_path_points(objp, objp->segnum, segnum, Point_segs_free_ptr, &Player_path_length, 100, 0, 0, -1) == -1)
1813                 mprintf((0, "Unable to form path of length %i for myself\n", 100));
1814
1815         Player_following_path_flag = 1;
1816
1817         Player_hide_index = POINT_SEG_NUMBER(Point_segs_free_ptr);
1818         Player_cur_path_index = 0;
1819         Point_segs_free_ptr += Player_path_length;
1820         if (POINT_SEG_NUMBER(Point_segs_free_ptr) + MAX_PATH_LENGTH*2 > MAX_POINT_SEGS) {
1821                 //Int3();       //      Contact Mike: This is curious, though not deadly. /eip++;g
1822                 ai_reset_all_paths();
1823         }
1824
1825 }
1826
1827 int     Player_goal_segment = -1;
1828
1829 void check_create_player_path(void)
1830 {
1831         if (Player_goal_segment != -1)
1832                 create_player_path_to_segment(Player_goal_segment);
1833
1834         Player_goal_segment = -1;
1835 }
1836
1837 #endif
1838
1839 //      ----------------------------------------------------------------------------------------------------------
1840 //                                      DEBUG FUNCTIONS ENDED
1841 //      ----------------------------------------------------------------------------------------------------------
1842