home *** CD-ROM | disk | FTP | other *** search
/ Amiga ACS 1998 #6 / amigaacscoverdisc1998-061998.iso / games / descent / source / main / aipath.c < prev    next >
Text File  |  1998-06-08  |  56KB  |  1,598 lines

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