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