1 /* $Id: aipath.c,v 1.5 2003-10-04 03:14:47 btb Exp $ */
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.
17 * AI path forming stuff.
20 * Revision 1.5 1995/10/26 14:12:03 allender
21 * prototype functions for mcc compiler
23 * Revision 1.4 1995/10/25 09:38:22 allender
24 * prototype some functions causing mcc grief
26 * Revision 1.3 1995/10/10 11:48:43 allender
29 * Revision 2.0 1995/02/27 11:30:48 john
30 * New version 2.0, which has no anonymous unions, builds with
31 * Watcom 10.0, and doesn't require parsing BITMAPS.TBL.
33 * Revision 1.101 1995/02/22 13:42:44 allender
34 * remove anonymous unions for object structure
36 * Revision 1.100 1995/02/10 16:20:04 mike
37 * fix bogosity in create_path_points, assumed all objects were robots.
39 * Revision 1.99 1995/02/07 21:09:30 mike
40 * make run_from guys have diff level based speed.
42 * Revision 1.98 1995/02/04 17:28:29 mike
43 * make station guys return better.
45 * Revision 1.97 1995/02/04 10:28:39 mike
48 * Revision 1.96 1995/02/04 10:03:37 mike
51 * Revision 1.95 1995/02/01 21:10:36 mike
52 * Array name was dereferenced. Not a bug, but unclean.
54 * Revision 1.94 1995/02/01 17:14:12 mike
55 * comment out some common mprintfs which didn't matter.
57 * Revision 1.93 1995/01/30 13:01:23 mike
58 * Make robots fire at player other than one they are controlled by sometimes.
60 * Revision 1.92 1995/01/29 22:29:32 mike
61 * add more debug info for guys that get lost.
63 * Revision 1.91 1995/01/20 16:56:05 mike
66 * Revision 1.90 1995/01/18 10:59:45 mike
67 * comment out some mprintfs.
69 * Revision 1.89 1995/01/17 16:58:34 mike
70 * make path following work for multiplayer.
72 * Revision 1.88 1995/01/17 14:21:44 mike
73 * make run_from guys run better.
75 * Revision 1.87 1995/01/14 17:09:04 mike
76 * playing with crazy josh, he's kinda slow and dumb now.
78 * Revision 1.86 1995/01/13 18:52:28 mike
81 * Revision 1.85 1995/01/05 09:42:11 mike
82 * compile out code based on SHAREWARE.
84 * Revision 1.84 1995/01/02 12:38:32 mike
85 * make crazy josh turn faster, therefore evade player better.
87 * Revision 1.83 1994/12/27 15:59:40 mike
88 * tweak ai_multiplayer_awareness constants.
90 * Revision 1.82 1994/12/19 17:07:10 mike
91 * deal with new ai_multiplayer_awareness which returns a value saying whether this object can be moved by this player.
93 * Revision 1.81 1994/12/15 13:04:30 mike
94 * Replace Players[Player_num].time_total references with GameTime.
96 * Revision 1.80 1994/12/09 16:13:23 mike
99 * Revision 1.79 1994/12/07 00:36:54 mike
100 * make robots get out of matcens better and be aware of player.
102 * Revision 1.78 1994/11/30 00:59:05 mike
105 * Revision 1.77 1994/11/27 23:13:39 matt
106 * Made changes for new mprintf calling convention
108 * Revision 1.76 1994/11/23 21:59:34 mike
109 * comment out some mprintfs.
111 * Revision 1.75 1994/11/21 16:07:14 mike
112 * flip PARALLAX flag, prevent annoying debug information.
114 * Revision 1.74 1994/11/19 15:13:28 mike
115 * remove unused code and data.
117 * Revision 1.73 1994/11/17 14:53:15 mike
118 * segment validation functions moved from editor to main.
120 * Revision 1.72 1994/11/16 23:38:42 mike
121 * new improved boss teleportation behavior.
123 * Revision 1.71 1994/11/13 17:18:30 mike
124 * debug code, then comment it out.
126 * Revision 1.70 1994/11/11 16:41:43 mike
127 * flip the PARALLAX flag.
129 * Revision 1.69 1994/11/11 16:33:45 mike
130 * twiddle the PARALLAX flag.
133 * Revision 1.68 1994/11/10 21:32:29 mike
136 * Revision 1.67 1994/11/10 20:15:07 mike
137 * fix stupid bug: uninitialized pointer.
139 * Revision 1.66 1994/11/10 17:45:15 mike
142 * Revision 1.65 1994/11/10 17:28:10 mike
151 #include <stdio.h> // for printf()
152 #include <stdlib.h> // for d_rand() and qsort()
153 #include <string.h> // for memset()
167 #include "editor/editor.h"
170 #include "fireball.h"
173 #define PARALLAX 0 // If !0, then special debugging for Parallax eyes enabled.
175 // Length in segments of avoidance path
176 #define AVOID_SEG_LENGTH 7
179 #define PATH_VALIDATION 0
181 #define PATH_VALIDATION 1
184 void validate_all_paths(void);
185 void ai_path_set_orient_and_vel(object *objp, vms_vector* goal_point, int player_visibility, vms_vector *vec_to_player);
186 void maybe_ai_path_garbage_collect(void);
187 void ai_path_garbage_collect(void);
189 int validate_path(int debug_flag, point_seg* psegs, int num_points);
192 // ------------------------------------------------------------------------
193 void create_random_xlate(sbyte *xt)
197 for (i=0; i<MAX_SIDES_PER_SEGMENT; i++)
200 for (i=0; i<MAX_SIDES_PER_SEGMENT; i++) {
201 int j = (d_rand()*MAX_SIDES_PER_SEGMENT)/(D_RAND_MAX+1);
203 Assert((j >= 0) && (j < MAX_SIDES_PER_SEGMENT));
212 // -----------------------------------------------------------------------------------------------------------
213 // Insert the point at the center of the side connecting two segments between the two points.
214 // This is messy because we must insert into the list. The simplest (and not too slow) way to do this is to start
215 // at the end of the list and go backwards.
216 void insert_center_points(point_seg *psegs, int *num_points)
218 int i, j, last_point;
219 int count=*num_points;
221 last_point = *num_points-1;
223 for (i=last_point; i>0; i--) {
224 int connect_side, temp_segnum;
225 vms_vector center_point, new_point;
227 psegs[2*i] = psegs[i];
228 connect_side = find_connect_side(&Segments[psegs[i].segnum], &Segments[psegs[i-1].segnum]);
229 Assert(connect_side != -1); // Impossible! These two segments must be connected, they were created by create_path_points (which was created by mk!)
230 if (connect_side == -1) // Try to blow past the assert, this should at least prevent a hang.
232 compute_center_point_on_side(¢er_point, &Segments[psegs[i-1].segnum], connect_side);
233 vm_vec_sub(&new_point, &psegs[i-1].point, ¢er_point);
237 vm_vec_sub(&psegs[2*i-1].point, ¢er_point, &new_point);
238 temp_segnum = find_point_seg(&psegs[2*i-1].point, psegs[2*i].segnum);
239 if (temp_segnum == -1) {
240 mprintf((1, "Warning: point not in ANY segment in aipath.c/insert_center_points.\n"));
241 psegs[2*i-1].point = center_point;
242 find_point_seg(&psegs[2*i-1].point, psegs[2*i].segnum);
245 psegs[2*i-1].segnum = psegs[2*i].segnum;
249 // Now, remove unnecessary center points.
250 // A center point is unnecessary if it is close to the line between the two adjacent points.
251 // MK, OPTIMIZE! Can get away with about half the math since every vector gets computed twice.
252 for (i=1; i<count-1; i+=2) {
253 vms_vector temp1, temp2;
256 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));
258 if (dot * 9/8 > fixmul(vm_vec_mag(&temp1), vm_vec_mag(&temp2)))
259 psegs[i].segnum = -1;
263 // Now, scan for points with segnum == -1
265 for (i=0; i<count; i++)
266 if (psegs[i].segnum != -1)
267 psegs[j++] = psegs[i];
273 int Safety_flag_override = 0;
274 int Random_flag_override = 0;
278 // -----------------------------------------------------------------------------------------------------------
279 // Move points halfway to outside of segment.
280 void move_towards_outside(point_seg *psegs, int *num_points, object *objp, int rand_flag)
283 point_seg new_psegs[200];
285 Assert(*num_points < 200);
287 for (i=1; i<*num_points-1; i++) {
291 vms_vector a, b, c, d, e;
296 // -- psegs[i].segnum = find_point_seg(&psegs[i].point, psegs[i].segnum);
297 temp_segnum = find_point_seg(&psegs[i].point, psegs[i].segnum);
298 Assert(temp_segnum != -1);
299 psegs[i].segnum = temp_segnum;
300 segnum = psegs[i].segnum;
302 vm_vec_sub(&a, &psegs[i].point, &psegs[i-1].point);
303 vm_vec_sub(&b, &psegs[i+1].point, &psegs[i].point);
304 vm_vec_sub(&c, &psegs[i+1].point, &psegs[i-1].point);
305 // I don't think we can use quick version here and this is _very_ rarely called. --MK, 07/03/95
306 vm_vec_normalize_quick(&a);
307 vm_vec_normalize_quick(&b);
308 if (abs(vm_vec_dot(&a, &b)) > 3*F1_0/4 ) {
309 if (abs(a.z) < F1_0/2) {
311 e.x = (d_rand()-16384)/2;
312 e.y = (d_rand()-16384)/2;
313 e.z = abs(e.x) + abs(e.y) + 1;
314 vm_vec_normalize_quick(&e);
322 e.y = (d_rand()-16384)/2;
323 e.z = (d_rand()-16384)/2;
324 e.x = abs(e.y) + abs(e.z) + 1;
325 vm_vec_normalize_quick(&e);
333 vm_vec_cross(&d, &a, &b);
334 vm_vec_cross(&e, &c, &d);
335 vm_vec_normalize_quick(&e);
338 if (vm_vec_mag_quick(&e) < F1_0/2)
341 //mprintf((0, "(%i) Moving to side: %6.3f %6.3f %6.3f\n", i, f2fl(e.x), f2fl(e.y), f2fl(e.z)));
343 segment_size = vm_vec_dist_quick(&Vertices[Segments[segnum].verts[0]], &Vertices[Segments[segnum].verts[6]]);
344 if (segment_size > F1_0*40)
345 segment_size = F1_0*40;
347 vm_vec_scale_add(&goal_pos, &psegs[i].point, &e, segment_size/4);
355 fq.p0 = &psegs[i].point;
356 fq.startseg = psegs[i].segnum;
359 fq.thisobjnum = objp-Objects;
360 fq.ignore_obj_list = NULL;
363 hit_type = find_vector_intersection(&fq, &hit_data);
365 if (hit_type == HIT_NONE)
368 if ((count == 3) && (hit_type == HIT_BAD_P0))
370 goal_pos.x = (fq.p0->x + hit_data.hit_pnt.x)/2;
371 goal_pos.y = (fq.p0->y + hit_data.hit_pnt.y)/2;
372 goal_pos.z = (fq.p0->z + hit_data.hit_pnt.z)/2;
374 if (count == 0) { // Couldn't move towards outside, that's ok, sometimes things can't be moved.
375 goal_pos = psegs[i].point;
380 // Only move towards outside if remained inside segment.
381 new_segnum = find_point_seg(&goal_pos, psegs[i].segnum);
382 if (new_segnum == psegs[i].segnum) {
383 new_psegs[i].point = goal_pos;
384 new_psegs[i].segnum = new_segnum;
386 new_psegs[i].point = psegs[i].point;
387 new_psegs[i].segnum = psegs[i].segnum;
392 for (i=1; i<*num_points-1; i++)
393 psegs[i] = new_psegs[i];
397 // -----------------------------------------------------------------------------------------------------------
398 // Create a path from objp->pos to the center of end_seg.
399 // Return a list of (segment_num, point_locations) at psegs
400 // Return number of points in *num_points.
401 // if max_depth == -1, then there is no maximum depth.
402 // If unable to create path, return -1, else return 0.
403 // If random_flag !0, then introduce randomness into path by looking at sides in random order. This means
404 // that a path between two segments won't always be the same, unless it is unique.
405 // If safety_flag is set, then additional points are added to "make sure" that points are reachable. I would
406 // like to say that it ensures that the object can move between the points, but that would require knowing what
407 // the object is (which isn't passed, right?) and making fvi calls (slow, right?). So, consider it the more_or_less_safe_flag.
408 // 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).
409 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)
413 int qtail = 0, qhead = 0;
415 sbyte visited[MAX_SEGMENTS];
416 seg_seg seg_queue[MAX_SEGMENTS];
417 short depth[MAX_SEGMENTS];
419 sbyte random_xlate[MAX_SIDES_PER_SEGMENT];
420 point_seg *original_psegs = psegs;
423 // -- mprintf((0, "cpp: frame = %4i obj %3i, psegs = %5i\n", FrameCount, objp-Objects, psegs-Point_segs));
425 validate_all_paths();
428 if ((objp->type == OBJ_ROBOT) && (objp->ctype.ai_info.behavior == AIB_RUN_FROM)) {
430 avoid_seg = ConsoleObject->segnum;
435 max_depth = MAX_PATH_LENGTH;
438 //random_flag = Random_flag_override; //!! debug!!
439 //safety_flag = Safety_flag_override; //!! debug!!
441 // for (i=0; i<=Highest_segment_index; i++) {
445 memset(visited, 0, sizeof(visited[0])*(Highest_segment_index+1));
446 memset(depth, 0, sizeof(depth[0])*(Highest_segment_index+1));
448 // If there is a segment we're not allowed to visit, mark it.
449 if (avoid_seg != -1) {
450 Assert(avoid_seg <= Highest_segment_index);
451 if ((start_seg != avoid_seg) && (end_seg != avoid_seg)) {
452 visited[avoid_seg] = 1;
453 depth[avoid_seg] = 0;
455 ; // -- mprintf((0, "Start/End/Avoid = %i %i %i\n", start_seg, end_seg, avoid_seg));
459 create_random_xlate(random_xlate);
462 visited[cur_seg] = 1;
465 while (cur_seg != end_seg) {
466 segment *segp = &Segments[cur_seg];
470 create_random_xlate(random_xlate);
472 // mprintf((0, "\n"));
473 for (sidenum = 0; sidenum < MAX_SIDES_PER_SEGMENT; sidenum++) {
478 snum = random_xlate[sidenum];
480 if (IS_CHILD(segp->children[snum]) && ((WALL_IS_DOORWAY(segp, snum) & WID_FLY_FLAG) || (ai_door_is_openable(objp, segp, snum)))) {
481 int this_seg = segp->children[snum];
482 Assert(this_seg != -1);
483 if (((cur_seg == avoid_seg) || (this_seg == avoid_seg)) && (ConsoleObject->segnum == avoid_seg)) {
484 vms_vector center_point;
490 compute_center_point_on_side(¢er_point, segp, snum);
493 fq.startseg = objp->segnum;
494 fq.p1 = ¢er_point;
496 fq.thisobjnum = objp-Objects;
497 fq.ignore_obj_list = NULL;
500 hit_type = find_vector_intersection(&fq, &hit_data);
501 if (hit_type != HIT_NONE) {
502 // -- mprintf((0, "hit_type = %i, object = %i\n", hit_type, hit_data.hit_object));
507 if (!visited[this_seg]) {
508 seg_queue[qtail].start = cur_seg;
509 seg_queue[qtail].end = this_seg;
510 visited[this_seg] = 1;
511 depth[qtail++] = cur_depth+1;
512 if (depth[qtail-1] == max_depth) {
513 // mprintf((0, "\ndepth == max_depth == %i\n", max_depth));
514 end_seg = seg_queue[qtail-1].end;
516 } // end if (depth[...
517 } // end if (!visited...
518 } // if (WALL_IS_DOORWAY(...
522 if (qhead >= qtail) {
523 // Couldn't get to goal, return a path as far as we got, which probably acceptable to the unparticular caller.
524 end_seg = seg_queue[qtail-1].end;
528 cur_seg = seg_queue[qhead].end;
529 cur_depth = depth[qhead];
533 } // while (cur_seg ...
535 // Set qtail to the segment which ends at the goal.
536 while (seg_queue[--qtail].end != end_seg)
538 // mprintf((0, "\nNo path!\n"));
539 // printf("UNABLE TO FORM PATH");
541 *num_points = l_num_points;
546 // -- N_selected_segs = 0;
548 //printf("Object #%3i, start: %3i ", objp-Objects, psegs-Point_segs);
550 int parent_seg, this_seg;
552 this_seg = seg_queue[qtail].end;
553 parent_seg = seg_queue[qtail].start;
554 Assert((this_seg >= 0) && (this_seg <= Highest_segment_index));
555 psegs->segnum = this_seg;
556 //printf("%3i ", this_seg);
557 compute_segment_center(&psegs->point,&Segments[this_seg]);
561 // -- Selected_segs[N_selected_segs++] = this_seg;
564 if (parent_seg == start_seg)
567 while (seg_queue[--qtail].end != parent_seg)
571 Assert((start_seg >= 0) && (start_seg <= Highest_segment_index));
572 psegs->segnum = start_seg;
573 //printf("%3i\n", start_seg);
574 compute_segment_center(&psegs->point,&Segments[start_seg]);
579 validate_path(1, original_psegs, l_num_points);
582 // Now, reverse point_segs in place.
583 for (i=0; i< l_num_points/2; i++) {
584 point_seg temp_point_seg = *(original_psegs + i);
585 *(original_psegs + i) = *(original_psegs + l_num_points - i - 1);
586 *(original_psegs + l_num_points - i - 1) = temp_point_seg;
589 validate_path(2, original_psegs, l_num_points);
592 // Now, if safety_flag set, then insert the point at the center of the side connecting two segments
593 // between the two points. This is messy because we must insert into the list. The simplest (and not too slow)
594 // way to do this is to start at the end of the list and go backwards.
596 if (psegs - Point_segs + l_num_points + 2 > MAX_POINT_SEGS) {
597 // Ouch! Cannot insert center points in path. So return unsafe path.
598 // Int3(); // Contact Mike: This is impossible.
599 // force_dump_ai_objects_all("Error in create_path_points");
600 mprintf((0, "Resetting all paths because of safety_flag.\n"));
601 ai_reset_all_paths();
602 *num_points = l_num_points;
605 // int old_num_points = l_num_points;
606 insert_center_points(original_psegs, &l_num_points);
607 // mprintf((0, "Saved %i/%i points.\n", 2*old_num_points - l_num_points - 1, old_num_points-1));
612 validate_path(3, original_psegs, l_num_points);
615 // -- MK, 10/30/95 -- This code causes apparent discontinuities in the path, moving a point
616 // into a new segment. It is not necessarily bad, but it makes it hard to track down actual
617 // discontinuity problems.
618 if (objp->type == OBJ_ROBOT)
619 if (Robot_info[objp->id].companion)
620 move_towards_outside(original_psegs, &l_num_points, objp, 0);
623 validate_path(4, original_psegs, l_num_points);
626 *num_points = l_num_points;
630 int Last_buddy_polish_path_frame;
632 // -------------------------------------------------------------------------------------------------------
634 // Takes an existing path and makes it nicer.
635 // Drops as many leading points as possible still maintaining direct accessibility
636 // from current position to first point.
637 // Will not shorten path to fewer than 3 points.
638 // Returns number of points.
639 // Starting position in psegs doesn't change.
640 // Changed, MK, 10/18/95. I think this was causing robots to get hung up on walls.
641 // Only drop up to the first three points.
642 int polish_path(object *objp, point_seg *psegs, int num_points)
644 int i, first_point=0;
649 // Prevent the buddy from polishing his path twice in one frame, which can cause him to get hung up. Pretty ugly, huh?
650 if (Robot_info[objp->id].companion)
652 if (FrameCount == Last_buddy_polish_path_frame)
655 Last_buddy_polish_path_frame = FrameCount;
658 // -- MK: 10/18/95: for (i=0; i<num_points-3; i++) {
659 for (i=0; i<2; i++) {
665 fq.startseg = objp->segnum;
666 fq.p1 = &psegs[i].point;
668 fq.thisobjnum = objp-Objects;
669 fq.ignore_obj_list = NULL;
672 hit_type = find_vector_intersection(&fq, &hit_data);
674 if (hit_type == HIT_NONE)
681 // Scrunch down all the psegs.
682 for (i=first_point; i<num_points; i++)
683 psegs[i-first_point] = psegs[i];
686 return num_points - first_point;
690 // -------------------------------------------------------------------------------------------------------
691 // Make sure that there are connections between all segments on path.
692 // Note that if path has been optimized, connections may not be direct, so this function is useless, or worse.
693 // Return true if valid, else return false.
694 int validate_path(int debug_flag, point_seg *psegs, int num_points)
699 curseg = psegs->segnum;
700 if ((curseg < 0) || (curseg > Highest_segment_index)) {
701 mprintf((0, "Path beginning at index %i, length=%i is bogus!\n", psegs-Point_segs, num_points));
702 Int3(); // Contact Mike: Debug trap for elusive, nasty bug.
706 if (debug_flag == 999)
707 mprintf((0, "That's curious...\n"));
712 // printf("(%i) Validating path at psegs=%i, num_points=%i, segments = %3i ", debug_flag, psegs-Point_segs, num_points, psegs[0].segnum);
713 for (i=1; i<num_points; i++) {
715 int nextseg = psegs[i].segnum;
717 if ((nextseg < 0) || (nextseg > Highest_segment_index)) {
718 mprintf((0, "Path beginning at index %i, length=%i is bogus!\n", psegs-Point_segs, num_points));
719 Int3(); // Contact Mike: Debug trap for elusive, nasty bug.
723 // printf("%3i ", nextseg);
724 if (curseg != nextseg) {
725 for (sidenum=0; sidenum<MAX_SIDES_PER_SEGMENT; sidenum++)
726 if (Segments[curseg].children[sidenum] == nextseg)
729 // Assert(sidenum != MAX_SIDES_PER_SEGMENT); // Hey, created path is not contiguous, why!?
730 if (sidenum == MAX_SIDES_PER_SEGMENT) {
731 mprintf((0, "Path beginning at index %i, length=%i is bogus!\n", psegs-Point_segs, num_points));
747 // -----------------------------------------------------------------------------------------------------------
748 void validate_all_paths(void)
754 for (i=0; i<=Highest_object_index; i++) {
755 if (Objects[i].type == OBJ_ROBOT) {
756 object *objp = &Objects[i];
757 ai_static *aip = &objp->ctype.ai_info;
758 //ai_local *ailp = &Ai_local_info[i];
760 if (objp->control_type == CT_AI) {
761 if ((aip->hide_index != -1) && (aip->path_length > 0))
762 if (!validate_path(4, &Point_segs[aip->hide_index], aip->path_length)) {
763 Int3(); // This path is bogus! Who corrupted it! Danger! Danger!
764 // Contact Mike, he caused this mess.
765 //force_dump_ai_objects_all("Error in validate_all_paths");
766 aip->path_length=0; // This allows people to resume without harm...
776 // -- // -------------------------------------------------------------------------------------------------------
777 // -- // Creates a path from the objects current segment (objp->segnum) to the specified segment for the object to
778 // -- // hide in Ai_local_info[objnum].goal_segment.
779 // -- // Sets objp->ctype.ai_info.hide_index, a pointer into Point_segs, the first point_seg of the path.
780 // -- // objp->ctype.ai_info.path_length, length of path
781 // -- // Point_segs_free_ptr global pointer into Point_segs array
782 // -- void create_path(object *objp)
784 // -- ai_static *aip = &objp->ctype.ai_info;
785 // -- ai_local *ailp = &Ai_local_info[objp-Objects];
786 // -- int start_seg, end_seg;
788 // -- start_seg = objp->segnum;
789 // -- end_seg = ailp->goal_segment;
791 // -- if (end_seg == -1)
792 // -- create_n_segment_path(objp, 3, -1);
794 // -- if (end_seg == -1) {
795 // -- ; //mprintf((0, "Object %i, hide_segment = -1, not creating path.\n", objp-Objects));
797 // -- create_path_points(objp, start_seg, end_seg, Point_segs_free_ptr, &aip->path_length, -1, 0, 0, -1);
798 // -- aip->hide_index = Point_segs_free_ptr - Point_segs;
799 // -- aip->cur_path_index = 0;
800 // -- #if PATH_VALIDATION
801 // -- validate_path(5, Point_segs_free_ptr, aip->path_length);
803 // -- Point_segs_free_ptr += aip->path_length;
804 // -- if (Point_segs_free_ptr - Point_segs + MAX_PATH_LENGTH*2 > MAX_POINT_SEGS) {
805 // -- //Int3(); // Contact Mike: This is curious, though not deadly. /eip++;g
806 // -- //force_dump_ai_objects_all("Error in create_path");
807 // -- ai_reset_all_paths();
809 // -- aip->PATH_DIR = 1; // Initialize to moving forward.
810 // -- aip->SUBMODE = AISM_HIDING; // Pretend we are hiding, so we sit here until bothered.
813 // -- maybe_ai_path_garbage_collect();
817 // -------------------------------------------------------------------------------------------------------
818 // Creates a path from the objects current segment (objp->segnum) to the specified segment for the object to
819 // hide in Ai_local_info[objnum].goal_segment.
820 // Sets objp->ctype.ai_info.hide_index, a pointer into Point_segs, the first point_seg of the path.
821 // objp->ctype.ai_info.path_length, length of path
822 // Point_segs_free_ptr global pointer into Point_segs array
823 // Change, 10/07/95: Used to create path to ConsoleObject->pos. Now creates path to Believed_player_pos.
824 void create_path_to_player(object *objp, int max_length, int safety_flag)
826 ai_static *aip = &objp->ctype.ai_info;
827 ai_local *ailp = &Ai_local_info[objp-Objects];
828 int start_seg, end_seg;
830 //mprintf((0, "Creating path to player.\n"));
831 if (max_length == -1)
832 max_length = MAX_DEPTH_TO_SEARCH_FOR_PLAYER;
834 ailp->time_player_seen = GameTime; // Prevent from resetting path quickly.
835 ailp->goal_segment = Believed_player_seg;
837 start_seg = objp->segnum;
838 end_seg = ailp->goal_segment;
840 // mprintf((0, "Creating path for object #%i, from segment #%i to #%i\n", objp-Objects, start_seg, end_seg));
843 ; //mprintf((0, "Object %i, hide_segment = -1, not creating path.\n", objp-Objects));
845 create_path_points(objp, start_seg, end_seg, Point_segs_free_ptr, &aip->path_length, max_length, 1, safety_flag, -1);
846 aip->path_length = polish_path(objp, Point_segs_free_ptr, aip->path_length);
847 aip->hide_index = Point_segs_free_ptr - Point_segs;
848 aip->cur_path_index = 0;
849 Point_segs_free_ptr += aip->path_length;
850 if (Point_segs_free_ptr - Point_segs + MAX_PATH_LENGTH*2 > MAX_POINT_SEGS) {
851 //Int3(); // Contact Mike: This is stupid. Should call maybe_ai_garbage_collect before the add.
852 //force_dump_ai_objects_all("Error in create_path_to_player");
853 ai_reset_all_paths();
856 // Assert(Point_segs_free_ptr - Point_segs + MAX_PATH_LENGTH*2 < MAX_POINT_SEGS);
857 aip->PATH_DIR = 1; // Initialize to moving forward.
858 // -- UNUSED! aip->SUBMODE = AISM_GOHIDE; // This forces immediate movement.
859 ailp->mode = AIM_FOLLOW_PATH;
860 ailp->player_awareness_type = 0; // If robot too aware of player, will set mode to chase
861 // mprintf((0, "Created %i segment path to player.\n", aip->path_length));
864 maybe_ai_path_garbage_collect();
868 // -------------------------------------------------------------------------------------------------------
869 // Creates a path from the object's current segment (objp->segnum) to segment goalseg.
870 void create_path_to_segment(object *objp, int goalseg, int max_length, int safety_flag)
872 ai_static *aip = &objp->ctype.ai_info;
873 ai_local *ailp = &Ai_local_info[objp-Objects];
874 int start_seg, end_seg;
876 if (max_length == -1)
877 max_length = MAX_DEPTH_TO_SEARCH_FOR_PLAYER;
879 ailp->time_player_seen = GameTime; // Prevent from resetting path quickly.
880 ailp->goal_segment = goalseg;
882 start_seg = objp->segnum;
883 end_seg = ailp->goal_segment;
888 create_path_points(objp, start_seg, end_seg, Point_segs_free_ptr, &aip->path_length, max_length, 1, safety_flag, -1);
889 aip->hide_index = Point_segs_free_ptr - Point_segs;
890 aip->cur_path_index = 0;
891 Point_segs_free_ptr += aip->path_length;
892 if (Point_segs_free_ptr - Point_segs + MAX_PATH_LENGTH*2 > MAX_POINT_SEGS) {
893 ai_reset_all_paths();
897 aip->PATH_DIR = 1; // Initialize to moving forward.
898 // -- UNUSED! aip->SUBMODE = AISM_GOHIDE; // This forces immediate movement.
899 ailp->player_awareness_type = 0; // If robot too aware of player, will set mode to chase
902 maybe_ai_path_garbage_collect();
906 // -------------------------------------------------------------------------------------------------------
907 // Creates a path from the objects current segment (objp->segnum) to the specified segment for the object to
908 // hide in Ai_local_info[objnum].goal_segment
909 // Sets objp->ctype.ai_info.hide_index, a pointer into Point_segs, the first point_seg of the path.
910 // objp->ctype.ai_info.path_length, length of path
911 // Point_segs_free_ptr global pointer into Point_segs array
912 void create_path_to_station(object *objp, int max_length)
914 ai_static *aip = &objp->ctype.ai_info;
915 ai_local *ailp = &Ai_local_info[objp-Objects];
916 int start_seg, end_seg;
918 if (max_length == -1)
919 max_length = MAX_DEPTH_TO_SEARCH_FOR_PLAYER;
921 ailp->time_player_seen = GameTime; // Prevent from resetting path quickly.
923 start_seg = objp->segnum;
924 end_seg = aip->hide_segment;
926 //1001: mprintf((0, "Back to station for object #%i, from segment #%i to #%i\n", objp-Objects, start_seg, end_seg));
929 ; //mprintf((0, "Object %i, hide_segment = -1, not creating path.\n", objp-Objects));
931 create_path_points(objp, start_seg, end_seg, Point_segs_free_ptr, &aip->path_length, max_length, 1, 1, -1);
932 aip->path_length = polish_path(objp, Point_segs_free_ptr, aip->path_length);
933 aip->hide_index = Point_segs_free_ptr - Point_segs;
934 aip->cur_path_index = 0;
936 Point_segs_free_ptr += aip->path_length;
937 if (Point_segs_free_ptr - Point_segs + MAX_PATH_LENGTH*2 > MAX_POINT_SEGS) {
938 //Int3(); // Contact Mike: Stupid.
939 //force_dump_ai_objects_all("Error in create_path_to_station");
940 ai_reset_all_paths();
943 // Assert(Point_segs_free_ptr - Point_segs + MAX_PATH_LENGTH*2 < MAX_POINT_SEGS);
944 aip->PATH_DIR = 1; // Initialize to moving forward.
945 // aip->SUBMODE = AISM_GOHIDE; // This forces immediate movement.
946 ailp->mode = AIM_FOLLOW_PATH;
947 ailp->player_awareness_type = 0;
951 maybe_ai_path_garbage_collect();
956 // -------------------------------------------------------------------------------------------------------
957 // Create a path of length path_length for an object, stuffing info in ai_info field.
958 void create_n_segment_path(object *objp, int path_length, int avoid_seg)
960 ai_static *aip=&objp->ctype.ai_info;
961 ai_local *ailp = &Ai_local_info[objp-Objects];
963 //mprintf((0, "Creating %i segment path.\n", path_length));
965 if (create_path_points(objp, objp->segnum, -2, Point_segs_free_ptr, &aip->path_length, path_length, 1, 0, avoid_seg) == -1) {
966 Point_segs_free_ptr += aip->path_length;
967 while ((create_path_points(objp, objp->segnum, -2, Point_segs_free_ptr, &aip->path_length, --path_length, 1, 0, -1) == -1)) {
973 aip->hide_index = Point_segs_free_ptr - Point_segs;
974 aip->cur_path_index = 0;
976 validate_path(8, Point_segs_free_ptr, aip->path_length);
978 Point_segs_free_ptr += aip->path_length;
979 if (Point_segs_free_ptr - Point_segs + MAX_PATH_LENGTH*2 > MAX_POINT_SEGS) {
980 //Int3(); // Contact Mike: This is curious, though not deadly. /eip++;g
981 //force_dump_ai_objects_all("Error in crete_n_segment_path 2");
982 ai_reset_all_paths();
985 aip->PATH_DIR = 1; // Initialize to moving forward.
986 // -- UNUSED! aip->SUBMODE = -1; // Don't know what this means.
987 ailp->mode = AIM_FOLLOW_PATH;
989 // If this robot is visible (player_visibility is not available) and it's running away, move towards outside with
990 // randomness to prevent a stream of bots from going away down the center of a corridor.
991 if (Ai_local_info[objp-Objects].previous_visibility) {
992 if (aip->path_length) {
993 int t_num_points = aip->path_length;
994 move_towards_outside(&Point_segs[aip->hide_index], &t_num_points, objp, 1);
995 aip->path_length = t_num_points;
998 //mprintf((0, "\n"));
1000 maybe_ai_path_garbage_collect();
1004 // -------------------------------------------------------------------------------------------------------
1005 void create_n_segment_path_to_door(object *objp, int path_length, int avoid_seg)
1007 create_n_segment_path(objp, path_length, avoid_seg);
1010 extern int Connected_segment_distance;
1012 #define Int3_if(cond) if (!cond) Int3();
1014 // ----------------------------------------------------------------------------------------------------
1015 void move_object_to_goal(object *objp, vms_vector *goal_point, int goal_seg)
1017 ai_static *aip = &objp->ctype.ai_info;
1020 if (aip->path_length < 2)
1023 Assert(objp->segnum != -1);
1025 // mprintf((0, "[%i -> %i]\n", objp-Objects, goal_seg));
1028 if (objp->segnum != goal_seg)
1029 if (find_connect_side(&Segments[objp->segnum], &Segments[goal_seg]) == -1) {
1031 dist = find_connected_distance(&objp->pos, objp->segnum, goal_point, goal_seg, 30, WID_FLY_FLAG);
1032 if (Connected_segment_distance > 2) { // This global is set in find_connected_distance
1034 mprintf((1, "Warning: Object %i hopped across %i segments, a distance of %7.3f.\n", objp-Objects, Connected_segment_distance, f2fl(dist)));
1039 Assert(aip->path_length >= 2);
1041 if (aip->cur_path_index <= 0) {
1042 if (aip->behavior == AIB_STATION) {
1043 // mprintf((0, "Object #%i, creating path back to station.\n", objp-Objects));
1044 create_path_to_station(objp, 15);
1047 aip->cur_path_index = 1;
1049 } else if (aip->cur_path_index >= aip->path_length - 1) {
1050 if (aip->behavior == AIB_STATION) {
1051 // mprintf((0, "Object #%i, creating path back to station.\n", objp-Objects));
1052 create_path_to_station(objp, 15);
1053 if (aip->path_length == 0) {
1054 ai_local *ailp = &Ai_local_info[objp-Objects];
1055 ailp->mode = AIM_STILL;
1059 Assert(aip->path_length != 0);
1060 aip->cur_path_index = aip->path_length-2;
1063 aip->cur_path_index += aip->PATH_DIR;
1065 //--Int3_if(((aip->cur_path_index >= 0) && (aip->cur_path_index < aip->path_length)));
1067 objp->pos = *goal_point;
1068 segnum = find_object_seg(objp);
1069 if (segnum != goal_seg)
1070 mprintf((1, "Object #%i goal supposed to be in segment #%i, but in segment #%i\n", objp-Objects, goal_seg, segnum));
1073 Int3(); // Oops, object is not in any segment.
1074 // Contact Mike: This is impossible.
1075 // Hack, move object to center of segment it used to be in.
1076 compute_segment_center(&objp->pos, &Segments[objp->segnum]);
1078 obj_relink(objp-Objects, segnum);
1081 // -- too much work -- // ----------------------------------------------------------------------------------------------------------
1082 // -- too much work -- // Return true if the object the companion wants to kill is reachable.
1083 // -- too much work -- int attack_kill_object(object *objp)
1084 // -- too much work -- {
1085 // -- too much work -- object *kill_objp;
1086 // -- too much work -- fvi_info hit_data;
1087 // -- too much work -- int fate;
1088 // -- too much work -- fvi_query fq;
1089 // -- too much work --
1090 // -- too much work -- if (Escort_kill_object == -1)
1091 // -- too much work -- return 0;
1092 // -- too much work --
1093 // -- too much work -- kill_objp = &Objects[Escort_kill_object];
1094 // -- too much work --
1095 // -- too much work -- fq.p0 = &objp->pos;
1096 // -- too much work -- fq.startseg = objp->segnum;
1097 // -- too much work -- fq.p1 = &kill_objp->pos;
1098 // -- too much work -- fq.rad = objp->size;
1099 // -- too much work -- fq.thisobjnum = objp-Objects;
1100 // -- too much work -- fq.ignore_obj_list = NULL;
1101 // -- too much work -- fq.flags = 0;
1102 // -- too much work --
1103 // -- too much work -- fate = find_vector_intersection(&fq,&hit_data);
1104 // -- too much work --
1105 // -- too much work -- if (fate == HIT_NONE)
1106 // -- too much work -- return 1;
1107 // -- too much work -- else
1108 // -- too much work -- return 0;
1109 // -- too much work -- }
1111 // ----------------------------------------------------------------------------------------------------------
1112 // Optimization: If current velocity will take robot near goal, don't change velocity
1113 void ai_follow_path(object *objp, int player_visibility, int previous_visibility, vms_vector *vec_to_player)
1115 ai_static *aip = &objp->ctype.ai_info;
1117 vms_vector goal_point, new_goal_point;
1119 robot_info *robptr = &Robot_info[objp->id];
1120 int forced_break, original_dir, original_index;
1123 ai_local *ailp = &Ai_local_info[objp-Objects];
1124 fix threshold_distance;
1127 // 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
1129 if ((aip->hide_index == -1) || (aip->path_length == 0))
1131 if (ailp->mode == AIM_RUN_FROM_OBJECT) {
1132 create_n_segment_path(objp, 5, -1);
1133 //--Int3_if((aip->path_length != 0));
1134 ailp->mode = AIM_RUN_FROM_OBJECT;
1136 // -- mprintf((0, "Object %i creating path for no apparent reason.\n", objp-Objects));
1137 create_n_segment_path(objp, 5, -1);
1138 //--Int3_if((aip->path_length != 0));
1142 if ((aip->hide_index + aip->path_length > Point_segs_free_ptr - Point_segs) && (aip->path_length>0)) {
1143 Int3(); // Contact Mike: Bad. Path goes into what is believed to be free space.
1144 // This is debugging code. Figure out why garbage collection
1145 // didn't compress this object's path information.
1146 ai_path_garbage_collect();
1147 //force_dump_ai_objects_all("Error in ai_follow_path");
1148 ai_reset_all_paths();
1151 if (aip->path_length < 2) {
1152 if ((aip->behavior == AIB_SNIPE) || (ailp->mode == AIM_RUN_FROM_OBJECT)) {
1153 if (ConsoleObject->segnum == objp->segnum) {
1154 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)
1155 //--Int3_if((aip->path_length != 0));
1157 create_n_segment_path(objp, AVOID_SEG_LENGTH, ConsoleObject->segnum);
1158 //--Int3_if((aip->path_length != 0));
1160 if (aip->behavior == AIB_SNIPE) {
1162 ailp->mode = AIM_THIEF_ATTACK; // It gets bashed in create_n_segment_path
1164 ailp->mode = AIM_SNIPE_FIRE; // It gets bashed in create_n_segment_path
1166 ailp->mode = AIM_RUN_FROM_OBJECT; // It gets bashed in create_n_segment_path
1168 } else if (robptr->companion == 0) {
1169 ailp->mode = AIM_STILL;
1170 aip->path_length = 0;
1175 //--Int3_if(((aip->PATH_DIR == -1) || (aip->PATH_DIR == 1)));
1176 //--Int3_if(((aip->cur_path_index >= 0) && (aip->cur_path_index < aip->path_length)));
1178 goal_point = Point_segs[aip->hide_index + aip->cur_path_index].point;
1179 goal_seg = Point_segs[aip->hide_index + aip->cur_path_index].segnum;
1180 dist_to_goal = vm_vec_dist_quick(&goal_point, &objp->pos);
1183 dist_to_player = vm_vec_dist_quick(&objp->pos, &Viewer->pos);
1185 dist_to_player = vm_vec_dist_quick(&objp->pos, &ConsoleObject->pos);
1187 // Efficiency hack: If far away from player, move in big quantized jumps.
1188 if (!(player_visibility || previous_visibility) && (dist_to_player > F1_0*200) && !(Game_mode & GM_MULTI)) {
1189 if (dist_to_goal < F1_0*2) {
1190 move_object_to_goal(objp, &goal_point, goal_seg);
1193 robot_info *robptr = &Robot_info[objp->id];
1194 fix cur_speed = robptr->max_speed[Difficulty_level]/2;
1195 fix distance_travellable = fixmul(FrameTime, cur_speed);
1197 // int connect_side = find_connect_side(objp->segnum, goal_seg);
1198 // Only move to goal if allowed to fly through the side.
1199 // Buddy-bot can create paths he can't fly, waiting for player.
1200 // -- 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) {
1201 if (!Robot_info[objp->id].companion && !Robot_info[objp->id].thief) {
1202 if (distance_travellable >= dist_to_goal) {
1203 move_object_to_goal(objp, &goal_point, goal_seg);
1205 fix prob = fixdiv(distance_travellable, dist_to_goal);
1207 int rand_num = d_rand();
1208 if ( (rand_num >> 1) < prob) {
1209 move_object_to_goal(objp, &goal_point, goal_seg);
1218 // If running from player, only run until can't be seen.
1219 if (ailp->mode == AIM_RUN_FROM_OBJECT) {
1220 if ((player_visibility == 0) && (ailp->player_awareness_type == 0)) {
1223 vel_scale = F1_0 - FrameTime/2;
1224 if (vel_scale < F1_0/2)
1227 vm_vec_scale(&objp->mtype.phys_info.velocity, vel_scale);
1230 } else if (!(FrameCount ^ ((objp-Objects) & 0x07))) { // Done 1/8 frames.
1231 // If player on path (beyond point robot is now at), then create a new path.
1232 point_seg *curpsp = &Point_segs[aip->hide_index];
1233 int player_segnum = ConsoleObject->segnum;
1236 // This is probably being done every frame, which is wasteful.
1237 for (i=aip->cur_path_index; i<aip->path_length; i++) {
1238 if (curpsp[i].segnum == player_segnum) {
1239 if (player_segnum != objp->segnum) {
1240 create_n_segment_path(objp, AVOID_SEG_LENGTH, player_segnum);
1242 create_n_segment_path(objp, AVOID_SEG_LENGTH, -1);
1244 Assert(aip->path_length != 0);
1245 ailp->mode = AIM_RUN_FROM_OBJECT; // It gets bashed in create_n_segment_path
1249 if (player_visibility) {
1250 ailp->player_awareness_type = 1;
1251 ailp->player_awareness_time = F1_0;
1256 //--Int3_if(((aip->cur_path_index >= 0) && (aip->cur_path_index < aip->path_length)));
1258 if (aip->cur_path_index < 0) {
1259 aip->cur_path_index = 0;
1260 } else if (aip->cur_path_index >= aip->path_length) {
1261 if (ailp->mode == AIM_RUN_FROM_OBJECT) {
1262 create_n_segment_path(objp, AVOID_SEG_LENGTH, ConsoleObject->segnum);
1263 ailp->mode = AIM_RUN_FROM_OBJECT; // It gets bashed in create_n_segment_path
1264 Assert(aip->path_length != 0);
1266 aip->cur_path_index = aip->path_length-1;
1270 goal_point = Point_segs[aip->hide_index + aip->cur_path_index].point;
1272 // If near goal, pick another goal point.
1273 forced_break = 0; // Gets set for short paths.
1274 original_dir = aip->PATH_DIR;
1275 original_index = aip->cur_path_index;
1276 threshold_distance = fixmul(vm_vec_mag_quick(&objp->mtype.phys_info.velocity), FrameTime)*2 + F1_0*2;
1278 new_goal_point = Point_segs[aip->hide_index + aip->cur_path_index].point;
1280 //--Int3_if(((aip->cur_path_index >= 0) && (aip->cur_path_index < aip->path_length)));
1282 while ((dist_to_goal < threshold_distance) && !forced_break) {
1284 // Advance to next point on path.
1285 aip->cur_path_index += aip->PATH_DIR;
1287 // See if next point wraps past end of path (in either direction), and if so, deal with it based on mode.
1288 if ((aip->cur_path_index >= aip->path_length) || (aip->cur_path_index < 0)) {
1290 //mprintf((0, "Object %i reached end of the line!\n", objp-Objects));
1291 // If mode = hiding, then stay here until get bonked or hit by player.
1292 // -- if (ailp->mode == AIM_BEHIND) {
1293 // -- ailp->mode = AIM_STILL;
1294 // -- return; // Stay here until bonked or hit by player.
1297 // Buddy bot. If he's in mode to get away from player and at end of line,
1298 // if player visible, then make a new path, else just return.
1299 if (robptr->companion) {
1300 if (Escort_special_goal == ESCORT_GOAL_SCRAM)
1302 if (player_visibility) {
1303 create_n_segment_path(objp, 16 + d_rand() * 16, -1);
1304 aip->path_length = polish_path(objp, &Point_segs[aip->hide_index], aip->path_length);
1305 Assert(aip->path_length != 0);
1306 // -- mprintf((0, "Buddy: Creating new path!\n"));
1307 ailp->mode = AIM_WANDER; // Special buddy mode.
1308 //--Int3_if(((aip->cur_path_index >= 0) && (aip->cur_path_index < aip->path_length)));
1311 ailp->mode = AIM_WANDER; // Special buddy mode.
1312 vm_vec_zero(&objp->mtype.phys_info.velocity);
1313 vm_vec_zero(&objp->mtype.phys_info.rotvel);
1314 // -- mprintf((0, "Buddy: I'm hidden!\n"));
1315 //!!Assert((aip->cur_path_index >= 0) && (aip->cur_path_index < aip->path_length));
1321 if (aip->behavior == AIB_FOLLOW) {
1322 // mprintf((0, "AIB_FOLLOW: Making new path.\n"));
1323 create_n_segment_path(objp, 10, ConsoleObject->segnum);
1324 //--Int3_if(((aip->cur_path_index >= 0) && (aip->cur_path_index < aip->path_length)));
1325 } else if (aip->behavior == AIB_STATION) {
1326 // mprintf((0, "Object %i reached end of line, creating path back to station.\n", objp-Objects));
1327 create_path_to_station(objp, 15);
1328 if ((aip->hide_segment != Point_segs[aip->hide_index+aip->path_length-1].segnum) || (aip->path_length == 0)) {
1329 ailp->mode = AIM_STILL;
1331 //--Int3_if(((aip->cur_path_index >= 0) && (aip->cur_path_index < aip->path_length)));
1334 } else if (ailp->mode == AIM_FOLLOW_PATH) {
1335 create_path_to_player(objp, 10, 1);
1336 if (aip->hide_segment != Point_segs[aip->hide_index+aip->path_length-1].segnum) {
1337 ailp->mode = AIM_STILL;
1340 //--Int3_if(((aip->cur_path_index >= 0) && (aip->cur_path_index < aip->path_length)));
1342 } else if (ailp->mode == AIM_RUN_FROM_OBJECT) {
1343 create_n_segment_path(objp, AVOID_SEG_LENGTH, ConsoleObject->segnum);
1344 ailp->mode = AIM_RUN_FROM_OBJECT; // It gets bashed in create_n_segment_path
1345 if (aip->path_length < 1) {
1346 create_n_segment_path(objp, AVOID_SEG_LENGTH, ConsoleObject->segnum);
1347 ailp->mode = AIM_RUN_FROM_OBJECT; // It gets bashed in create_n_segment_path
1348 if (aip->path_length < 1) {
1349 aip->behavior = AIB_NORMAL;
1350 ailp->mode = AIM_STILL;
1354 //--Int3_if(((aip->cur_path_index >= 0) && (aip->cur_path_index < aip->path_length)));
1356 // Reached end of the line. First see if opposite end point is reachable, and if so, go there.
1357 // If not, turn around.
1358 int opposite_end_index;
1359 vms_vector *opposite_end_point;
1364 // See which end we're nearer and look at the opposite end point.
1365 if (abs(aip->cur_path_index - aip->path_length) < aip->cur_path_index) {
1366 // Nearer to far end (ie, index not 0), so try to reach 0.
1367 opposite_end_index = 0;
1369 // Nearer to 0 end, so try to reach far end.
1370 opposite_end_index = aip->path_length-1;
1373 //--Int3_if(((opposite_end_index >= 0) && (opposite_end_index < aip->path_length)));
1375 opposite_end_point = &Point_segs[aip->hide_index + opposite_end_index].point;
1378 fq.startseg = objp->segnum;
1379 fq.p1 = opposite_end_point;
1380 fq.rad = objp->size;
1381 fq.thisobjnum = objp-Objects;
1382 fq.ignore_obj_list = NULL;
1383 fq.flags = 0; //what about trans walls???
1385 fate = find_vector_intersection(&fq,&hit_data);
1387 if (fate != HIT_WALL) {
1388 // We can be circular! Do it!
1389 // Path direction is unchanged.
1390 aip->cur_path_index = opposite_end_index;
1392 aip->PATH_DIR = -aip->PATH_DIR;
1393 aip->cur_path_index += aip->PATH_DIR;
1395 //--Int3_if(((aip->cur_path_index >= 0) && (aip->cur_path_index < aip->path_length)));
1399 new_goal_point = Point_segs[aip->hide_index + aip->cur_path_index].point;
1400 goal_point = new_goal_point;
1401 dist_to_goal = vm_vec_dist_quick(&goal_point, &objp->pos);
1402 //--Int3_if(((aip->cur_path_index >= 0) && (aip->cur_path_index < aip->path_length)));
1405 // If went all the way around to original point, in same direction, then get out of here!
1406 if ((aip->cur_path_index == original_index) && (aip->PATH_DIR == original_dir)) {
1407 create_path_to_player(objp, 3, 1);
1408 //--Int3_if(((aip->cur_path_index >= 0) && (aip->cur_path_index < aip->path_length)));
1411 //--Int3_if(((aip->cur_path_index >= 0) && (aip->cur_path_index < aip->path_length)));
1414 // Set velocity (objp->mtype.phys_info.velocity) and orientation (objp->orient) for this object.
1415 //--Int3_if(((aip->cur_path_index >= 0) && (aip->cur_path_index < aip->path_length)));
1416 ai_path_set_orient_and_vel(objp, &goal_point, player_visibility, vec_to_player);
1417 //--Int3_if(((aip->cur_path_index >= 0) && (aip->cur_path_index < aip->path_length)));
1422 short path_start, objnum;
1425 int path_index_compare(obj_path *i1, obj_path *i2)
1427 if (i1->path_start < i2->path_start)
1429 else if (i1->path_start == i2->path_start)
1435 // ----------------------------------------------------------------------------------------------------------
1436 // Set orientation matrix and velocity for objp based on its desire to get to a point.
1437 void ai_path_set_orient_and_vel(object *objp, vms_vector *goal_point, int player_visibility, vms_vector *vec_to_player)
1439 vms_vector cur_vel = objp->mtype.phys_info.velocity;
1440 vms_vector norm_cur_vel;
1441 vms_vector norm_vec_to_goal;
1442 vms_vector cur_pos = objp->pos;
1443 vms_vector norm_fvec;
1446 robot_info *robptr = &Robot_info[objp->id];
1449 // If evading player, use highest difficulty level speed, plus something based on diff level
1450 max_speed = robptr->max_speed[Difficulty_level];
1451 if ((Ai_local_info[objp-Objects].mode == AIM_RUN_FROM_OBJECT) || (objp->ctype.ai_info.behavior == AIB_SNIPE))
1452 max_speed = max_speed*3/2;
1454 vm_vec_sub(&norm_vec_to_goal, goal_point, &cur_pos);
1455 vm_vec_normalize_quick(&norm_vec_to_goal);
1457 norm_cur_vel = cur_vel;
1458 vm_vec_normalize_quick(&norm_cur_vel);
1460 norm_fvec = objp->orient.fvec;
1461 vm_vec_normalize_quick(&norm_fvec);
1463 dot = vm_vec_dot(&norm_vec_to_goal, &norm_fvec);
1465 // If very close to facing opposite desired vector, perturb vector
1466 if (dot < -15*F1_0/16) {
1467 //mprintf((0, "Facing away from goal, abruptly turning\n"));
1468 norm_cur_vel = norm_vec_to_goal;
1470 norm_cur_vel.x += norm_vec_to_goal.x/2;
1471 norm_cur_vel.y += norm_vec_to_goal.y/2;
1472 norm_cur_vel.z += norm_vec_to_goal.z/2;
1475 vm_vec_normalize_quick(&norm_cur_vel);
1477 // Set speed based on this robot type's maximum allowed speed and how hard it is turning.
1478 // How hard it is turning is based on the dot product of (vector to goal) and (current velocity vector)
1479 // Note that since 3*F1_0/4 is added to dot product, it is possible for the robot to back up.
1481 // Set speed and orientation.
1485 // If in snipe mode, can move fast even if not facing that direction.
1486 if (objp->ctype.ai_info.behavior == AIB_SNIPE)
1488 dot = (dot + F1_0)/2;
1490 speed_scale = fixmul(max_speed, dot);
1491 vm_vec_scale(&norm_cur_vel, speed_scale);
1492 objp->mtype.phys_info.velocity = norm_cur_vel;
1494 if ((Ai_local_info[objp-Objects].mode == AIM_RUN_FROM_OBJECT) || (robptr->companion == 1) || (objp->ctype.ai_info.behavior == AIB_SNIPE)) {
1495 if (Ai_local_info[objp-Objects].mode == AIM_SNIPE_RETREAT_BACKWARDS) {
1496 if ((player_visibility) && (vec_to_player != NULL))
1497 norm_vec_to_goal = *vec_to_player;
1499 vm_vec_negate(&norm_vec_to_goal);
1501 ai_turn_towards_vector(&norm_vec_to_goal, objp, robptr->turn_time[NDL-1]/2);
1503 ai_turn_towards_vector(&norm_vec_to_goal, objp, robptr->turn_time[Difficulty_level]);
1507 int Last_frame_garbage_collected = 0;
1509 // ----------------------------------------------------------------------------------------------------------
1510 // Garbage colledion -- Free all unused records in Point_segs and compress all paths.
1511 void ai_path_garbage_collect(void)
1513 int free_path_index = 0;
1514 int num_path_objects = 0;
1517 obj_path object_list[MAX_OBJECTS];
1520 force_dump_ai_objects_all("***** Start ai_path_garbage_collect *****");
1523 // -- mprintf((0, "Garbage collection frame %i, last frame %i! Old free index = %i ", FrameCount, Last_frame_garbage_collected, Point_segs_free_ptr - Point_segs));
1525 Last_frame_garbage_collected = FrameCount;
1528 validate_all_paths();
1530 // Create a list of objects which have paths of length 1 or more.
1531 for (objnum=0; objnum <= Highest_object_index; objnum++) {
1532 object *objp = &Objects[objnum];
1534 if ((objp->type == OBJ_ROBOT) && ((objp->control_type == CT_AI) || (objp->control_type == CT_MORPH))) {
1535 ai_static *aip = &objp->ctype.ai_info;
1537 if (aip->path_length) {
1538 object_list[num_path_objects].path_start = aip->hide_index;
1539 object_list[num_path_objects++].objnum = objnum;
1544 qsort(object_list, num_path_objects, sizeof(object_list[0]),
1545 (int (*)(void const *,void const *))path_index_compare);
1547 for (objind=0; objind < num_path_objects; objind++) {
1553 objnum = object_list[objind].objnum;
1554 objp = &Objects[objnum];
1555 aip = &objp->ctype.ai_info;
1556 old_index = aip->hide_index;
1558 aip->hide_index = free_path_index;
1559 for (i=0; i<aip->path_length; i++)
1560 Point_segs[free_path_index++] = Point_segs[old_index++];
1563 Point_segs_free_ptr = &Point_segs[free_path_index];
1565 // mprintf((0, "new = %i\n", free_path_index));
1566 //printf("After garbage collection, free index = %i\n", Point_segs_free_ptr - Point_segs);
1571 force_dump_ai_objects_all("***** Finish ai_path_garbage_collect *****");
1573 for (i=0; i<=Highest_object_index; i++) {
1574 ai_static *aip = &Objects[i].ctype.ai_info;
1576 if ((Objects[i].type == OBJ_ROBOT) && (Objects[i].control_type == CT_AI))
1577 if ((aip->hide_index + aip->path_length > Point_segs_free_ptr - Point_segs) && (aip->path_length>0))
1578 Int3(); // Contact Mike: Debug trap for nasty, elusive bug.
1581 validate_all_paths();
1587 // -----------------------------------------------------------------------------
1588 // Do garbage collection if not been done for awhile, or things getting really critical.
1589 void maybe_ai_path_garbage_collect(void)
1591 if (Point_segs_free_ptr - Point_segs > MAX_POINT_SEGS - MAX_PATH_LENGTH) {
1592 if (Last_frame_garbage_collected+1 >= FrameCount) {
1593 // This is kind of bad. Garbage collected last frame or this frame.
1594 // Just destroy all paths. Too bad for the robots. They are memory wasteful.
1595 ai_reset_all_paths();
1596 mprintf((1, "Warning: Resetting all paths. Point_segs buffer nearly exhausted.\n"));
1598 // We are really close to full, but didn't just garbage collect, so maybe this is recoverable.
1599 mprintf((1, "Warning: Almost full garbage collection being performed: "));
1600 ai_path_garbage_collect();
1601 mprintf((1, "Free records = %i/%i\n", MAX_POINT_SEGS - (Point_segs_free_ptr - Point_segs), MAX_POINT_SEGS));
1603 } else if (Point_segs_free_ptr - Point_segs > 3*MAX_POINT_SEGS/4) {
1604 if (Last_frame_garbage_collected + 16 < FrameCount) {
1605 ai_path_garbage_collect();
1607 } else if (Point_segs_free_ptr - Point_segs > MAX_POINT_SEGS/2) {
1608 if (Last_frame_garbage_collected + 256 < FrameCount) {
1609 ai_path_garbage_collect();
1614 // -----------------------------------------------------------------------------
1615 // Reset all paths. Do garbage collection.
1616 // Should be called at the start of each level.
1617 void ai_reset_all_paths(void)
1621 for (i=0; i<=Highest_object_index; i++)
1622 if (Objects[i].control_type == CT_AI) {
1623 Objects[i].ctype.ai_info.hide_index = -1;
1624 Objects[i].ctype.ai_info.path_length = 0;
1627 ai_path_garbage_collect();
1631 // ---------------------------------------------------------------------------------------------------------
1632 // Probably called because a robot bashed a wall, getting a bunch of retries.
1633 // Try to resume path.
1634 void attempt_to_resume_path(object *objp)
1636 //int objnum = objp-Objects;
1637 ai_static *aip = &objp->ctype.ai_info;
1638 // int goal_segnum, object_segnum,
1639 int abs_index, new_path_index;
1641 // mprintf((0, "Object %i trying to resume path at index %i\n", objp-Objects, aip->cur_path_index));
1643 if ((aip->behavior == AIB_STATION) && (Robot_info[objp->id].companion != 1))
1644 if (d_rand() > 8192) {
1645 ai_local *ailp = &Ai_local_info[objp-Objects];
1647 aip->hide_segment = objp->segnum;
1649 ailp->mode = AIM_STILL;
1650 mprintf((1, "Note: Bashing hide segment of robot %i to current segment because he's lost.\n", objp-Objects));
1653 // object_segnum = objp->segnum;
1654 abs_index = aip->hide_index+aip->cur_path_index;
1655 // goal_segnum = Point_segs[abs_index].segnum;
1657 // if (object_segnum == goal_segnum)
1658 // mprintf((0, "Very peculiar, goal segnum = object's segnum = %i.\n", goal_segnum));
1660 new_path_index = aip->cur_path_index - aip->PATH_DIR;
1662 if ((new_path_index >= 0) && (new_path_index < aip->path_length)) {
1663 // mprintf((0, "Trying path index of %i\n", new_path_index));
1664 aip->cur_path_index = new_path_index;
1666 // At end of line and have nowhere to go.
1667 // mprintf((0, "At end of line and can't get to goal. Creating new path. Frame %i\n", FrameCount));
1668 move_towards_segment_center(objp);
1669 create_path_to_station(objp, 15);
1674 // ----------------------------------------------------------------------------------------------------------
1675 // DEBUG FUNCTIONS FOLLOW
1676 // ----------------------------------------------------------------------------------------------------------
1679 int Test_size = 1000;
1681 void test_create_path_many(void)
1683 point_seg point_segs[200];
1688 for (i=0; i<Test_size; i++) {
1689 Cursegp = &Segments[(d_rand() * (Highest_segment_index + 1)) / D_RAND_MAX];
1690 Markedsegp = &Segments[(d_rand() * (Highest_segment_index + 1)) / D_RAND_MAX];
1691 create_path_points(&Objects[0], Cursegp-Segments, Markedsegp-Segments, point_segs, &num_points, -1, 0, 0, -1);
1696 void test_create_path(void)
1698 point_seg point_segs[200];
1701 create_path_points(&Objects[0], Cursegp-Segments, Markedsegp-Segments, point_segs, &num_points, -1, 0, 0, -1);
1705 void show_path(int start_seg, int end_seg, point_seg *psp, short length)
1707 printf("[%3i:%3i (%3i):] ", start_seg, end_seg, length);
1710 printf("%3i ", psp[length].segnum);
1715 // For all segments in mine, create paths to all segments in mine, print results.
1716 void test_create_all_paths(void)
1718 int start_seg, end_seg;
1719 short resultant_length;
1721 Point_segs_free_ptr = Point_segs;
1723 for (start_seg=0; start_seg<=Highest_segment_index-1; start_seg++) {
1724 // -- mprintf((0, "."));
1725 if (Segments[start_seg].segnum != -1) {
1726 for (end_seg=start_seg+1; end_seg<=Highest_segment_index; end_seg++) {
1727 if (Segments[end_seg].segnum != -1) {
1728 create_path_points(&Objects[0], start_seg, end_seg, Point_segs_free_ptr, &resultant_length, -1, 0, 0, -1);
1729 show_path(start_seg, end_seg, Point_segs_free_ptr, resultant_length);
1736 //--anchor--int Num_anchors;
1737 //--anchor--int Anchor_distance = 3;
1738 //--anchor--int End_distance = 1;
1739 //--anchor--int Anchors[MAX_SEGMENTS];
1741 //--anchor--int get_nearest_anchor_distance(int segnum)
1743 //--anchor-- short resultant_length, minimum_length;
1744 //--anchor-- int anchor_index;
1746 //--anchor-- minimum_length = 16383;
1748 //--anchor-- for (anchor_index=0; anchor_index<Num_anchors; anchor_index++) {
1749 //--anchor-- create_path_points(&Objects[0], segnum, Anchors[anchor_index], Point_segs_free_ptr, &resultant_length, -1, 0, 0, -1);
1750 //--anchor-- if (resultant_length != 0)
1751 //--anchor-- if (resultant_length < minimum_length)
1752 //--anchor-- minimum_length = resultant_length;
1755 //--anchor-- return minimum_length;
1759 //--anchor--void create_new_anchor(int segnum)
1761 //--anchor-- Anchors[Num_anchors++] = segnum;
1764 //--anchor--// A set of anchors is within N units of all segments in the graph.
1765 //--anchor--// Anchor_distance = how close anchors can be.
1766 //--anchor--// End_distance = how close you can be to the end.
1767 //--anchor--void test_create_all_anchors(void)
1769 //--anchor-- int nearest_anchor_distance;
1770 //--anchor-- int segnum,i;
1772 //--anchor-- Num_anchors = 0;
1774 //--anchor-- for (segnum=0; segnum<=Highest_segment_index; segnum++) {
1775 //--anchor-- if (Segments[segnum].segnum != -1) {
1776 //--anchor-- nearest_anchor_distance = get_nearest_anchor_distance(segnum);
1777 //--anchor-- if (nearest_anchor_distance > Anchor_distance)
1778 //--anchor-- create_new_anchor(segnum);
1782 //--anchor-- // Set selected segs.
1783 //--anchor-- for (i=0; i<Num_anchors; i++)
1784 //--anchor-- Selected_segs[i] = Anchors[i];
1785 //--anchor-- N_selected_segs = Num_anchors;
1789 //--anchor--int Test_path_length = 5;
1791 //--anchor--void test_create_n_segment_path(void)
1793 //--anchor-- point_seg point_segs[200];
1794 //--anchor-- short num_points;
1796 //--anchor-- create_path_points(&Objects[0], Cursegp-Segments, -2, point_segs, &num_points, Test_path_length, 0, 0, -1);
1799 short Player_path_length=0;
1800 int Player_hide_index=-1;
1801 int Player_cur_path_index=0;
1802 int Player_following_path_flag=0;
1804 // ------------------------------------------------------------------------------------------------------------------
1805 // Set orientation matrix and velocity for objp based on its desire to get to a point.
1806 void player_path_set_orient_and_vel(object *objp, vms_vector *goal_point)
1808 vms_vector cur_vel = objp->mtype.phys_info.velocity;
1809 vms_vector norm_cur_vel;
1810 vms_vector norm_vec_to_goal;
1811 vms_vector cur_pos = objp->pos;
1812 vms_vector norm_fvec;
1817 max_speed = Robot_info[objp->id].max_speed[Difficulty_level];
1819 vm_vec_sub(&norm_vec_to_goal, goal_point, &cur_pos);
1820 vm_vec_normalize_quick(&norm_vec_to_goal);
1822 norm_cur_vel = cur_vel;
1823 vm_vec_normalize_quick(&norm_cur_vel);
1825 norm_fvec = objp->orient.fvec;
1826 vm_vec_normalize_quick(&norm_fvec);
1828 dot = vm_vec_dot(&norm_vec_to_goal, &norm_fvec);
1829 if (Ai_local_info[objp-Objects].mode == AIM_SNIPE_RETREAT_BACKWARDS) {
1833 // If very close to facing opposite desired vector, perturb vector
1834 if (dot < -15*F1_0/16) {
1835 //mprintf((0, "Facing away from goal, abruptly turning\n"));
1836 norm_cur_vel = norm_vec_to_goal;
1838 norm_cur_vel.x += norm_vec_to_goal.x/2;
1839 norm_cur_vel.y += norm_vec_to_goal.y/2;
1840 norm_cur_vel.z += norm_vec_to_goal.z/2;
1843 vm_vec_normalize_quick(&norm_cur_vel);
1845 // Set speed based on this robot type's maximum allowed speed and how hard it is turning.
1846 // How hard it is turning is based on the dot product of (vector to goal) and (current velocity vector)
1847 // Note that since 3*F1_0/4 is added to dot product, it is possible for the robot to back up.
1849 // Set speed and orientation.
1853 speed_scale = fixmul(max_speed, dot);
1854 vm_vec_scale(&norm_cur_vel, speed_scale);
1855 objp->mtype.phys_info.velocity = norm_cur_vel;
1856 ai_turn_towards_vector(&norm_vec_to_goal, objp, F1_0);
1860 // ----------------------------------------------------------------------------------------------------------
1861 // Optimization: If current velocity will take robot near goal, don't change velocity
1862 void player_follow_path(object *objp)
1864 vms_vector goal_point;
1866 int count, forced_break, original_index;
1868 fix threshold_distance;
1870 if (!Player_following_path_flag)
1873 if (Player_hide_index == -1)
1876 if (Player_path_length < 2)
1879 goal_point = Point_segs[Player_hide_index + Player_cur_path_index].point;
1880 goal_seg = Point_segs[Player_hide_index + Player_cur_path_index].segnum;
1881 Assert((goal_seg >= 0) && (goal_seg <= Highest_segment_index));
1882 dist_to_goal = vm_vec_dist_quick(&goal_point, &objp->pos);
1884 if (Player_cur_path_index < 0)
1885 Player_cur_path_index = 0;
1886 else if (Player_cur_path_index >= Player_path_length)
1887 Player_cur_path_index = Player_path_length-1;
1889 goal_point = Point_segs[Player_hide_index + Player_cur_path_index].point;
1893 // If near goal, pick another goal point.
1894 forced_break = 0; // Gets set for short paths.
1896 original_index = Player_cur_path_index;
1897 threshold_distance = fixmul(vm_vec_mag_quick(&objp->mtype.phys_info.velocity), FrameTime)*2 + F1_0*2;
1899 while ((dist_to_goal < threshold_distance) && !forced_break) {
1901 // -- if (count > 1)
1902 // -- mprintf((0, "."));
1904 // ----- Debug stuff -----
1906 mprintf((1,"Problem following path for player. Aborting.\n"));
1910 // Advance to next point on path.
1911 Player_cur_path_index += 1;
1913 // See if next point wraps past end of path (in either direction), and if so, deal with it based on mode.
1914 if ((Player_cur_path_index >= Player_path_length) || (Player_cur_path_index < 0)) {
1915 Player_following_path_flag = 0;
1919 // If went all the way around to original point, in same direction, then get out of here!
1920 if (Player_cur_path_index == original_index) {
1921 mprintf((0, "Forcing break because player path wrapped, count = %i.\n", count));
1922 Player_following_path_flag = 0;
1926 goal_point = Point_segs[Player_hide_index + Player_cur_path_index].point;
1927 dist_to_goal = vm_vec_dist_quick(&goal_point, &objp->pos);
1931 // Set velocity (objp->mtype.phys_info.velocity) and orientation (objp->orient) for this object.
1932 player_path_set_orient_and_vel(objp, &goal_point);
1937 // ------------------------------------------------------------------------------------------------------------------
1938 // Create path for player from current segment to goal segment.
1939 void create_player_path_to_segment(int segnum)
1941 object *objp = ConsoleObject;
1943 Player_path_length=0;
1944 Player_hide_index=-1;
1945 Player_cur_path_index=0;
1946 Player_following_path_flag=0;
1948 if (create_path_points(objp, objp->segnum, segnum, Point_segs_free_ptr, &Player_path_length, 100, 0, 0, -1) == -1)
1949 mprintf((0, "Unable to form path of length %i for myself\n", 100));
1951 Player_following_path_flag = 1;
1953 Player_hide_index = Point_segs_free_ptr - Point_segs;
1954 Player_cur_path_index = 0;
1955 Point_segs_free_ptr += Player_path_length;
1956 if (Point_segs_free_ptr - Point_segs + MAX_PATH_LENGTH*2 > MAX_POINT_SEGS) {
1957 //Int3(); // Contact Mike: This is curious, though not deadly. /eip++;g
1958 ai_reset_all_paths();
1963 int Player_goal_segment = -1;
1965 void check_create_player_path(void)
1967 if (Player_goal_segment != -1)
1968 create_player_path_to_segment(Player_goal_segment);
1970 Player_goal_segment = -1;
1975 // ----------------------------------------------------------------------------------------------------------
1976 // DEBUG FUNCTIONS ENDED
1977 // ----------------------------------------------------------------------------------------------------------