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