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