2 THE COMPUTER CODE CONTAINED HEREIN IS THE SOLE PROPERTY OF PARALLAX
3 SOFTWARE CORPORATION ("PARALLAX"). PARALLAX, IN DISTRIBUTING THE CODE TO
4 END-USERS, AND SUBJECT TO ALL OF THE TERMS AND CONDITIONS HEREIN, GRANTS A
5 ROYALTY-FREE, PERPETUAL LICENSE TO SUCH END-USERS FOR USE BY SUCH END-USERS
6 IN USING, DISPLAYING, AND CREATING DERIVATIVE WORKS THEREOF, SO LONG AS
7 SUCH USE, DISPLAY OR CREATION IS FOR NON-COMMERCIAL, ROYALTY OR REVENUE
8 FREE PURPOSES. IN NO EVENT SHALL THE END-USER USE THE COMPUTER CODE
9 CONTAINED HEREIN FOR REVENUE-BEARING PURPOSES. THE END-USER UNDERSTANDS
10 AND AGREES TO THE TERMS HEREIN AND ACCEPTS THE SAME BY USE OF THIS FILE.
11 COPYRIGHT 1993-1999 PARALLAX SOFTWARE CORPORATION. ALL RIGHTS RESERVED.
16 * AI path forming stuff.
24 #include <stdio.h> // for printf()
25 #include <stdlib.h> // for d_rand() and qsort()
26 #include <string.h> // for memset()
33 #include "editor/editor.h"
37 #define PARALLAX 0 // If !0, then special debugging for Parallax eyes enabled.
39 // Length in segments of avoidance path
40 #define AVOID_SEG_LENGTH 7
43 #define PATH_VALIDATION 0
45 #define PATH_VALIDATION 1
48 void validate_all_paths(void);
49 void ai_path_set_orient_and_vel(object *objp, vms_vector* goal_point, int player_visibility, vms_vector *vec_to_player);
50 void maybe_ai_path_garbage_collect(void);
51 void ai_path_garbage_collect(void);
53 int validate_path(int debug_flag, point_seg* psegs, int num_points);
56 // ------------------------------------------------------------------------
57 void create_random_xlate(sbyte *xt)
61 for (i=0; i<MAX_SIDES_PER_SEGMENT; i++)
64 for (i=0; i<MAX_SIDES_PER_SEGMENT; i++) {
65 int j = (d_rand()*MAX_SIDES_PER_SEGMENT)/(D_RAND_MAX+1);
67 Assert((j >= 0) && (j < MAX_SIDES_PER_SEGMENT));
76 // -----------------------------------------------------------------------------------------------------------
77 // Insert the point at the center of the side connecting two segments between the two points.
78 // This is messy because we must insert into the list. The simplest (and not too slow) way to do this is to start
79 // at the end of the list and go backwards.
80 void insert_center_points(point_seg *psegs, int *num_points)
83 int count=*num_points;
85 last_point = *num_points-1;
87 for (i=last_point; i>0; i--) {
88 int connect_side, temp_segnum;
89 vms_vector center_point, new_point;
91 psegs[2*i] = psegs[i];
92 connect_side = find_connect_side(&Segments[psegs[i].segnum], &Segments[psegs[i-1].segnum]);
93 Assert(connect_side != -1); // Impossible! These two segments must be connected, they were created by create_path_points (which was created by mk!)
94 if (connect_side == -1) // Try to blow past the assert, this should at least prevent a hang.
96 compute_center_point_on_side(¢er_point, &Segments[psegs[i-1].segnum], connect_side);
97 vm_vec_sub(&new_point, &psegs[i-1].point, ¢er_point);
101 vm_vec_sub(&psegs[2*i-1].point, ¢er_point, &new_point);
102 temp_segnum = find_point_seg(&psegs[2*i-1].point, psegs[2*i].segnum);
103 if (temp_segnum == -1) {
104 mprintf((1, "Warning: point not in ANY segment in aipath.c/insert_center_points.\n"));
105 psegs[2*i-1].point = center_point;
106 find_point_seg(&psegs[2*i-1].point, psegs[2*i].segnum);
109 psegs[2*i-1].segnum = psegs[2*i].segnum;
113 // Now, remove unnecessary center points.
114 // A center point is unnecessary if it is close to the line between the two adjacent points.
115 // MK, OPTIMIZE! Can get away with about half the math since every vector gets computed twice.
116 for (i=1; i<count-1; i+=2) {
117 vms_vector temp1, temp2;
120 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));
122 if (dot * 9/8 > fixmul(vm_vec_mag(&temp1), vm_vec_mag(&temp2)))
123 psegs[i].segnum = -1;
127 // Now, scan for points with segnum == -1
129 for (i=0; i<count; i++)
130 if (psegs[i].segnum != -1)
131 psegs[j++] = psegs[i];
137 int Safety_flag_override = 0;
138 int Random_flag_override = 0;
142 // -----------------------------------------------------------------------------------------------------------
143 // Move points halfway to outside of segment.
144 void move_towards_outside(point_seg *psegs, int *num_points, object *objp, int rand_flag)
147 point_seg new_psegs[200];
149 Assert(*num_points < 200);
151 for (i=1; i<*num_points-1; i++) {
155 vms_vector a, b, c, d, e;
160 // -- psegs[i].segnum = find_point_seg(&psegs[i].point, psegs[i].segnum);
161 temp_segnum = find_point_seg(&psegs[i].point, psegs[i].segnum);
162 Assert(temp_segnum != -1);
163 psegs[i].segnum = temp_segnum;
164 segnum = psegs[i].segnum;
166 vm_vec_sub(&a, &psegs[i].point, &psegs[i-1].point);
167 vm_vec_sub(&b, &psegs[i+1].point, &psegs[i].point);
168 vm_vec_sub(&c, &psegs[i+1].point, &psegs[i-1].point);
169 // I don't think we can use quick version here and this is _very_ rarely called. --MK, 07/03/95
170 vm_vec_normalize_quick(&a);
171 vm_vec_normalize_quick(&b);
172 if (abs(vm_vec_dot(&a, &b)) > 3*F1_0/4 ) {
173 if (abs(a.z) < F1_0/2) {
175 e.x = (d_rand()-16384)/2;
176 e.y = (d_rand()-16384)/2;
177 e.z = abs(e.x) + abs(e.y) + 1;
178 vm_vec_normalize_quick(&e);
186 e.y = (d_rand()-16384)/2;
187 e.z = (d_rand()-16384)/2;
188 e.x = abs(e.y) + abs(e.z) + 1;
189 vm_vec_normalize_quick(&e);
197 vm_vec_cross(&d, &a, &b);
198 vm_vec_cross(&e, &c, &d);
199 vm_vec_normalize_quick(&e);
202 if (vm_vec_mag_quick(&e) < F1_0/2)
205 //mprintf((0, "(%i) Moving to side: %6.3f %6.3f %6.3f\n", i, f2fl(e.x), f2fl(e.y), f2fl(e.z)));
207 segment_size = vm_vec_dist_quick(&Vertices[Segments[segnum].verts[0]], &Vertices[Segments[segnum].verts[6]]);
208 if (segment_size > F1_0*40)
209 segment_size = F1_0*40;
211 vm_vec_scale_add(&goal_pos, &psegs[i].point, &e, segment_size/4);
219 fq.p0 = &psegs[i].point;
220 fq.startseg = psegs[i].segnum;
223 fq.thisobjnum = OBJECT_NUMBER(objp);
224 fq.ignore_obj_list = NULL;
227 hit_type = find_vector_intersection(&fq, &hit_data);
229 if (hit_type == HIT_NONE)
232 if ((count == 3) && (hit_type == HIT_BAD_P0))
234 goal_pos.x = (fq.p0->x + hit_data.hit_pnt.x)/2;
235 goal_pos.y = (fq.p0->y + hit_data.hit_pnt.y)/2;
236 goal_pos.z = (fq.p0->z + hit_data.hit_pnt.z)/2;
238 if (count == 0) { // Couldn't move towards outside, that's ok, sometimes things can't be moved.
239 goal_pos = psegs[i].point;
244 // Only move towards outside if remained inside segment.
245 new_segnum = find_point_seg(&goal_pos, psegs[i].segnum);
246 if (new_segnum == psegs[i].segnum) {
247 new_psegs[i].point = goal_pos;
248 new_psegs[i].segnum = new_segnum;
250 new_psegs[i].point = psegs[i].point;
251 new_psegs[i].segnum = psegs[i].segnum;
256 for (i=1; i<*num_points-1; i++)
257 psegs[i] = new_psegs[i];
261 // -----------------------------------------------------------------------------------------------------------
262 // Create a path from objp->pos to the center of end_seg.
263 // Return a list of (segment_num, point_locations) at psegs
264 // Return number of points in *num_points.
265 // if max_depth == -1, then there is no maximum depth.
266 // If unable to create path, return -1, else return 0.
267 // If random_flag !0, then introduce randomness into path by looking at sides in random order. This means
268 // that a path between two segments won't always be the same, unless it is unique.
269 // If safety_flag is set, then additional points are added to "make sure" that points are reachable. I would
270 // like to say that it ensures that the object can move between the points, but that would require knowing what
271 // the object is (which isn't passed, right?) and making fvi calls (slow, right?). So, consider it the more_or_less_safe_flag.
272 // 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).
273 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)
277 int qtail = 0, qhead = 0;
279 sbyte visited[MAX_SEGMENTS];
280 seg_seg seg_queue[MAX_SEGMENTS];
281 short depth[MAX_SEGMENTS];
283 sbyte random_xlate[MAX_SIDES_PER_SEGMENT];
284 point_seg *original_psegs = psegs;
287 // -- mprintf((0, "cpp: frame = %4i obj %3i, psegs = %5i\n", FrameCount, OBJECT_NUMBER(objp), POINT_SEG_NUMBER(psegs)));
289 validate_all_paths();
292 if ((objp->type == OBJ_ROBOT) && (objp->ctype.ai_info.behavior == AIB_RUN_FROM)) {
294 avoid_seg = ConsoleObject->segnum;
299 max_depth = MAX_PATH_LENGTH;
302 //random_flag = Random_flag_override; //!! debug!!
303 //safety_flag = Safety_flag_override; //!! debug!!
305 // for (i=0; i<=Highest_segment_index; i++) {
309 memset(visited, 0, sizeof(visited[0])*(Highest_segment_index+1));
310 memset(depth, 0, sizeof(depth[0])*(Highest_segment_index+1));
312 // If there is a segment we're not allowed to visit, mark it.
313 if (avoid_seg != -1) {
314 Assert(avoid_seg <= Highest_segment_index);
315 if ((start_seg != avoid_seg) && (end_seg != avoid_seg)) {
316 visited[avoid_seg] = 1;
317 depth[avoid_seg] = 0;
319 ; // -- mprintf((0, "Start/End/Avoid = %i %i %i\n", start_seg, end_seg, avoid_seg));
323 create_random_xlate(random_xlate);
326 visited[cur_seg] = 1;
329 while (cur_seg != end_seg) {
330 segment *segp = &Segments[cur_seg];
334 create_random_xlate(random_xlate);
336 // mprintf((0, "\n"));
337 for (sidenum = 0; sidenum < MAX_SIDES_PER_SEGMENT; sidenum++) {
342 snum = random_xlate[sidenum];
344 if (IS_CHILD(segp->children[snum]) && ((WALL_IS_DOORWAY(segp, snum) & WID_FLY_FLAG) || (ai_door_is_openable(objp, segp, snum)))) {
345 int this_seg = segp->children[snum];
346 Assert(this_seg != -1);
347 if (((cur_seg == avoid_seg) || (this_seg == avoid_seg)) && (ConsoleObject->segnum == avoid_seg)) {
348 vms_vector center_point;
354 compute_center_point_on_side(¢er_point, segp, snum);
357 fq.startseg = objp->segnum;
358 fq.p1 = ¢er_point;
360 fq.thisobjnum = OBJECT_NUMBER(objp);
361 fq.ignore_obj_list = NULL;
364 hit_type = find_vector_intersection(&fq, &hit_data);
365 if (hit_type != HIT_NONE) {
366 // -- mprintf((0, "hit_type = %i, object = %i\n", hit_type, hit_data.hit_object));
371 if (!visited[this_seg]) {
372 seg_queue[qtail].start = cur_seg;
373 seg_queue[qtail].end = this_seg;
374 visited[this_seg] = 1;
375 depth[qtail++] = cur_depth+1;
376 if (depth[qtail-1] == max_depth) {
377 // mprintf((0, "\ndepth == max_depth == %i\n", max_depth));
378 end_seg = seg_queue[qtail-1].end;
380 } // end if (depth[...
381 } // end if (!visited...
382 } // if (WALL_IS_DOORWAY(...
386 if (qhead >= qtail) {
387 // Couldn't get to goal, return a path as far as we got, which probably acceptable to the unparticular caller.
388 end_seg = seg_queue[qtail-1].end;
392 cur_seg = seg_queue[qhead].end;
393 cur_depth = depth[qhead];
397 } // while (cur_seg ...
399 // Set qtail to the segment which ends at the goal.
400 while (seg_queue[--qtail].end != end_seg)
402 // mprintf((0, "\nNo path!\n"));
403 // printf("UNABLE TO FORM PATH");
405 *num_points = l_num_points;
410 // -- N_selected_segs = 0;
412 //printf("Object #%3i, start: %3i ", OBJECT_NUMBER(objp), POINT_SEG_NUMBER(psegs));
414 int parent_seg, this_seg;
416 this_seg = seg_queue[qtail].end;
417 parent_seg = seg_queue[qtail].start;
418 Assert((this_seg >= 0) && (this_seg <= Highest_segment_index));
419 psegs->segnum = this_seg;
420 //printf("%3i ", this_seg);
421 compute_segment_center(&psegs->point,&Segments[this_seg]);
425 // -- Selected_segs[N_selected_segs++] = this_seg;
428 if (parent_seg == start_seg)
431 while (seg_queue[--qtail].end != parent_seg)
435 Assert((start_seg >= 0) && (start_seg <= Highest_segment_index));
436 psegs->segnum = start_seg;
437 //printf("%3i\n", start_seg);
438 compute_segment_center(&psegs->point,&Segments[start_seg]);
443 validate_path(1, original_psegs, l_num_points);
446 // Now, reverse point_segs in place.
447 for (i=0; i< l_num_points/2; i++) {
448 point_seg temp_point_seg = *(original_psegs + i);
449 *(original_psegs + i) = *(original_psegs + l_num_points - i - 1);
450 *(original_psegs + l_num_points - i - 1) = temp_point_seg;
453 validate_path(2, original_psegs, l_num_points);
456 // Now, if safety_flag set, then insert the point at the center of the side connecting two segments
457 // between the two points. This is messy because we must insert into the list. The simplest (and not too slow)
458 // way to do this is to start at the end of the list and go backwards.
460 if (POINT_SEG_NUMBER(psegs) + l_num_points + 2 > MAX_POINT_SEGS) {
461 // Ouch! Cannot insert center points in path. So return unsafe path.
462 // Int3(); // Contact Mike: This is impossible.
463 // force_dump_ai_objects_all("Error in create_path_points");
464 mprintf((0, "Resetting all paths because of safety_flag.\n"));
465 ai_reset_all_paths();
466 *num_points = l_num_points;
469 // int old_num_points = l_num_points;
470 insert_center_points(original_psegs, &l_num_points);
471 // mprintf((0, "Saved %i/%i points.\n", 2*old_num_points - l_num_points - 1, old_num_points-1));
476 validate_path(3, original_psegs, l_num_points);
479 // -- MK, 10/30/95 -- This code causes apparent discontinuities in the path, moving a point
480 // into a new segment. It is not necessarily bad, but it makes it hard to track down actual
481 // discontinuity problems.
482 if (objp->type == OBJ_ROBOT)
483 if (Robot_info[objp->id].companion)
484 move_towards_outside(original_psegs, &l_num_points, objp, 0);
487 validate_path(4, original_psegs, l_num_points);
490 *num_points = l_num_points;
494 int Last_buddy_polish_path_frame;
496 // -------------------------------------------------------------------------------------------------------
498 // Takes an existing path and makes it nicer.
499 // Drops as many leading points as possible still maintaining direct accessibility
500 // from current position to first point.
501 // Will not shorten path to fewer than 3 points.
502 // Returns number of points.
503 // Starting position in psegs doesn't change.
504 // Changed, MK, 10/18/95. I think this was causing robots to get hung up on walls.
505 // Only drop up to the first three points.
506 int polish_path(object *objp, point_seg *psegs, int num_points)
508 int i, first_point=0;
513 // Prevent the buddy from polishing his path twice in one frame, which can cause him to get hung up. Pretty ugly, huh?
514 if (Robot_info[objp->id].companion)
516 if (FrameCount == Last_buddy_polish_path_frame)
519 Last_buddy_polish_path_frame = FrameCount;
522 // -- MK: 10/18/95: for (i=0; i<num_points-3; i++) {
523 for (i=0; i<2; i++) {
529 fq.startseg = objp->segnum;
530 fq.p1 = &psegs[i].point;
532 fq.thisobjnum = OBJECT_NUMBER(objp);
533 fq.ignore_obj_list = NULL;
536 hit_type = find_vector_intersection(&fq, &hit_data);
538 if (hit_type == HIT_NONE)
545 // Scrunch down all the psegs.
546 for (i=first_point; i<num_points; i++)
547 psegs[i-first_point] = psegs[i];
550 return num_points - first_point;
554 // -------------------------------------------------------------------------------------------------------
555 // Make sure that there are connections between all segments on path.
556 // Note that if path has been optimized, connections may not be direct, so this function is useless, or worse.
557 // Return true if valid, else return false.
558 int validate_path(int debug_flag, point_seg *psegs, int num_points)
563 curseg = psegs->segnum;
564 if ((curseg < 0) || (curseg > Highest_segment_index)) {
565 mprintf((0, "Path beginning at index %i, length=%i is bogus!\n", POINT_SEG_NUMBER(psegs), num_points));
566 Int3(); // Contact Mike: Debug trap for elusive, nasty bug.
570 if (debug_flag == 999)
571 mprintf((0, "That's curious...\n"));
576 // printf("(%i) Validating path at psegs=%i, num_points=%i, segments = %3i ", debug_flag, POINT_SEG_NUMBER(psegs), num_points, psegs[0].segnum);
577 for (i=1; i<num_points; i++) {
579 int nextseg = psegs[i].segnum;
581 if ((nextseg < 0) || (nextseg > Highest_segment_index)) {
582 mprintf((0, "Path beginning at index %i, length=%i is bogus!\n", POINT_SEG_NUMBER(psegs), num_points));
583 Int3(); // Contact Mike: Debug trap for elusive, nasty bug.
587 // printf("%3i ", nextseg);
588 if (curseg != nextseg) {
589 for (sidenum=0; sidenum<MAX_SIDES_PER_SEGMENT; sidenum++)
590 if (Segments[curseg].children[sidenum] == nextseg)
593 // Assert(sidenum != MAX_SIDES_PER_SEGMENT); // Hey, created path is not contiguous, why!?
594 if (sidenum == MAX_SIDES_PER_SEGMENT) {
595 mprintf((0, "Path beginning at index %i, length=%i is bogus!\n", POINT_SEG_NUMBER(psegs), num_points));
611 // -----------------------------------------------------------------------------------------------------------
612 void validate_all_paths(void)
618 for (i=0; i<=Highest_object_index; i++) {
619 if (Objects[i].type == OBJ_ROBOT) {
620 object *objp = &Objects[i];
621 ai_static *aip = &objp->ctype.ai_info;
622 //ai_local *ailp = &Ai_local_info[i];
624 if (objp->control_type == CT_AI) {
625 if ((aip->hide_index != -1) && (aip->path_length > 0))
626 if (!validate_path(4, &Point_segs[aip->hide_index], aip->path_length)) {
627 Int3(); // This path is bogus! Who corrupted it! Danger! Danger!
628 // Contact Mike, he caused this mess.
629 //force_dump_ai_objects_all("Error in validate_all_paths");
630 aip->path_length=0; // This allows people to resume without harm...
640 // -- // -------------------------------------------------------------------------------------------------------
641 // -- // Creates a path from the objects current segment (objp->segnum) to the specified segment for the object to
642 // -- // hide in Ai_local_info[objnum].goal_segment.
643 // -- // Sets objp->ctype.ai_info.hide_index, a pointer into Point_segs, the first point_seg of the path.
644 // -- // objp->ctype.ai_info.path_length, length of path
645 // -- // Point_segs_free_ptr global pointer into Point_segs array
646 // -- void create_path(object *objp)
648 // -- ai_static *aip = &objp->ctype.ai_info;
649 // -- ai_local *ailp = &Ai_local_info[OBJECT_NUMBER(objp)];
650 // -- int start_seg, end_seg;
652 // -- start_seg = objp->segnum;
653 // -- end_seg = ailp->goal_segment;
655 // -- if (end_seg == -1)
656 // -- create_n_segment_path(objp, 3, -1);
658 // -- if (end_seg == -1) {
659 // -- ; //mprintf((0, "Object %i, hide_segment = -1, not creating path.\n", OBJECT_NUMBER(objp)));
661 // -- create_path_points(objp, start_seg, end_seg, Point_segs_free_ptr, &aip->path_length, -1, 0, 0, -1);
662 // -- aip->hide_index = POINT_SEG_NUMBER(Point_segs_free_ptr);
663 // -- aip->cur_path_index = 0;
664 // -- #if PATH_VALIDATION
665 // -- validate_path(5, Point_segs_free_ptr, aip->path_length);
667 // -- Point_segs_free_ptr += aip->path_length;
668 // -- if (POINT_SEG_NUMBER(Point_segs_free_ptr) + MAX_PATH_LENGTH*2 > MAX_POINT_SEGS) {
669 // -- //Int3(); // Contact Mike: This is curious, though not deadly. /eip++;g
670 // -- //force_dump_ai_objects_all("Error in create_path");
671 // -- ai_reset_all_paths();
673 // -- aip->PATH_DIR = 1; // Initialize to moving forward.
674 // -- aip->SUBMODE = AISM_HIDING; // Pretend we are hiding, so we sit here until bothered.
677 // -- maybe_ai_path_garbage_collect();
681 // -------------------------------------------------------------------------------------------------------
682 // Creates a path from the objects current segment (objp->segnum) to the specified segment for the object to
683 // hide in Ai_local_info[objnum].goal_segment.
684 // Sets objp->ctype.ai_info.hide_index, a pointer into Point_segs, the first point_seg of the path.
685 // objp->ctype.ai_info.path_length, length of path
686 // Point_segs_free_ptr global pointer into Point_segs array
687 // Change, 10/07/95: Used to create path to ConsoleObject->pos. Now creates path to Believed_player_pos.
688 void create_path_to_player(object *objp, int max_length, int safety_flag)
690 ai_static *aip = &objp->ctype.ai_info;
691 ai_local *ailp = &Ai_local_info[OBJECT_NUMBER(objp)];
692 int start_seg, end_seg;
694 //mprintf((0, "Creating path to player.\n"));
695 if (max_length == -1)
696 max_length = MAX_DEPTH_TO_SEARCH_FOR_PLAYER;
698 ailp->time_player_seen = GameTime; // Prevent from resetting path quickly.
699 ailp->goal_segment = Believed_player_seg;
701 start_seg = objp->segnum;
702 end_seg = ailp->goal_segment;
704 // mprintf((0, "Creating path for object #%i, from segment #%i to #%i\n", OBJECT_NUMBER(objp), start_seg, end_seg));
707 ; //mprintf((0, "Object %i, hide_segment = -1, not creating path.\n", OBJECT_NUMBER(objp)));
709 create_path_points(objp, start_seg, end_seg, Point_segs_free_ptr, &aip->path_length, max_length, 1, safety_flag, -1);
710 aip->path_length = polish_path(objp, Point_segs_free_ptr, aip->path_length);
711 aip->hide_index = POINT_SEG_NUMBER(Point_segs_free_ptr);
712 aip->cur_path_index = 0;
713 Point_segs_free_ptr += aip->path_length;
714 if (POINT_SEG_NUMBER(Point_segs_free_ptr) + MAX_PATH_LENGTH*2 > MAX_POINT_SEGS) {
715 //Int3(); // Contact Mike: This is stupid. Should call maybe_ai_garbage_collect before the add.
716 //force_dump_ai_objects_all("Error in create_path_to_player");
717 ai_reset_all_paths();
720 // Assert(POINT_SEG_NUMBER(Point_segs_free_ptr) + MAX_PATH_LENGTH*2 < MAX_POINT_SEGS);
721 aip->PATH_DIR = 1; // Initialize to moving forward.
722 // -- UNUSED! aip->SUBMODE = AISM_GOHIDE; // This forces immediate movement.
723 ailp->mode = AIM_FOLLOW_PATH;
724 ailp->player_awareness_type = 0; // If robot too aware of player, will set mode to chase
725 // mprintf((0, "Created %i segment path to player.\n", aip->path_length));
728 maybe_ai_path_garbage_collect();
732 // -------------------------------------------------------------------------------------------------------
733 // Creates a path from the object's current segment (objp->segnum) to segment goalseg.
734 void create_path_to_segment(object *objp, int goalseg, int max_length, int safety_flag)
736 ai_static *aip = &objp->ctype.ai_info;
737 ai_local *ailp = &Ai_local_info[OBJECT_NUMBER(objp)];
738 int start_seg, end_seg;
740 if (max_length == -1)
741 max_length = MAX_DEPTH_TO_SEARCH_FOR_PLAYER;
743 ailp->time_player_seen = GameTime; // Prevent from resetting path quickly.
744 ailp->goal_segment = goalseg;
746 start_seg = objp->segnum;
747 end_seg = ailp->goal_segment;
752 create_path_points(objp, start_seg, end_seg, Point_segs_free_ptr, &aip->path_length, max_length, 1, safety_flag, -1);
753 aip->hide_index = POINT_SEG_NUMBER(Point_segs_free_ptr);
754 aip->cur_path_index = 0;
755 Point_segs_free_ptr += aip->path_length;
756 if (POINT_SEG_NUMBER(Point_segs_free_ptr) + MAX_PATH_LENGTH*2 > MAX_POINT_SEGS) {
757 ai_reset_all_paths();
761 aip->PATH_DIR = 1; // Initialize to moving forward.
762 // -- UNUSED! aip->SUBMODE = AISM_GOHIDE; // This forces immediate movement.
763 ailp->player_awareness_type = 0; // If robot too aware of player, will set mode to chase
766 maybe_ai_path_garbage_collect();
770 // -------------------------------------------------------------------------------------------------------
771 // Creates a path from the objects current segment (objp->segnum) to the specified segment for the object to
772 // hide in Ai_local_info[objnum].goal_segment
773 // Sets objp->ctype.ai_info.hide_index, a pointer into Point_segs, the first point_seg of the path.
774 // objp->ctype.ai_info.path_length, length of path
775 // Point_segs_free_ptr global pointer into Point_segs array
776 void create_path_to_station(object *objp, int max_length)
778 ai_static *aip = &objp->ctype.ai_info;
779 ai_local *ailp = &Ai_local_info[OBJECT_NUMBER(objp)];
780 int start_seg, end_seg;
782 if (max_length == -1)
783 max_length = MAX_DEPTH_TO_SEARCH_FOR_PLAYER;
785 ailp->time_player_seen = GameTime; // Prevent from resetting path quickly.
787 start_seg = objp->segnum;
788 end_seg = aip->hide_segment;
790 //1001: mprintf((0, "Back to station for object #%i, from segment #%i to #%i\n", OBJECT_NUMBER(objp), start_seg, end_seg));
793 ; //mprintf((0, "Object %i, hide_segment = -1, not creating path.\n", OBJECT_NUMBER(objp)));
795 create_path_points(objp, start_seg, end_seg, Point_segs_free_ptr, &aip->path_length, max_length, 1, 1, -1);
796 aip->path_length = polish_path(objp, Point_segs_free_ptr, aip->path_length);
797 aip->hide_index = POINT_SEG_NUMBER(Point_segs_free_ptr);
798 aip->cur_path_index = 0;
800 Point_segs_free_ptr += aip->path_length;
801 if (POINT_SEG_NUMBER(Point_segs_free_ptr) + MAX_PATH_LENGTH*2 > MAX_POINT_SEGS) {
802 //Int3(); // Contact Mike: Stupid.
803 //force_dump_ai_objects_all("Error in create_path_to_station");
804 ai_reset_all_paths();
807 // Assert(POINT_SEG_NUMBER(Point_segs_free_ptr) + MAX_PATH_LENGTH*2 < MAX_POINT_SEGS);
808 aip->PATH_DIR = 1; // Initialize to moving forward.
809 // aip->SUBMODE = AISM_GOHIDE; // This forces immediate movement.
810 ailp->mode = AIM_FOLLOW_PATH;
811 ailp->player_awareness_type = 0;
815 maybe_ai_path_garbage_collect();
820 // -------------------------------------------------------------------------------------------------------
821 // Create a path of length path_length for an object, stuffing info in ai_info field.
822 void create_n_segment_path(object *objp, int path_length, int avoid_seg)
824 ai_static *aip=&objp->ctype.ai_info;
825 ai_local *ailp = &Ai_local_info[OBJECT_NUMBER(objp)];
827 //mprintf((0, "Creating %i segment path.\n", path_length));
829 if (create_path_points(objp, objp->segnum, -2, Point_segs_free_ptr, &aip->path_length, path_length, 1, 0, avoid_seg) == -1) {
830 Point_segs_free_ptr += aip->path_length;
831 while ((create_path_points(objp, objp->segnum, -2, Point_segs_free_ptr, &aip->path_length, --path_length, 1, 0, -1) == -1)) {
837 aip->hide_index = POINT_SEG_NUMBER(Point_segs_free_ptr);
838 aip->cur_path_index = 0;
840 validate_path(8, Point_segs_free_ptr, aip->path_length);
842 Point_segs_free_ptr += aip->path_length;
843 if (POINT_SEG_NUMBER(Point_segs_free_ptr) + MAX_PATH_LENGTH*2 > MAX_POINT_SEGS) {
844 //Int3(); // Contact Mike: This is curious, though not deadly. /eip++;g
845 //force_dump_ai_objects_all("Error in crete_n_segment_path 2");
846 ai_reset_all_paths();
849 aip->PATH_DIR = 1; // Initialize to moving forward.
850 // -- UNUSED! aip->SUBMODE = -1; // Don't know what this means.
851 ailp->mode = AIM_FOLLOW_PATH;
853 // If this robot is visible (player_visibility is not available) and it's running away, move towards outside with
854 // randomness to prevent a stream of bots from going away down the center of a corridor.
855 if (Ai_local_info[OBJECT_NUMBER(objp)].previous_visibility) {
856 if (aip->path_length) {
857 int t_num_points = aip->path_length;
858 move_towards_outside(&Point_segs[aip->hide_index], &t_num_points, objp, 1);
859 aip->path_length = t_num_points;
862 //mprintf((0, "\n"));
864 maybe_ai_path_garbage_collect();
868 // -------------------------------------------------------------------------------------------------------
869 void create_n_segment_path_to_door(object *objp, int path_length, int avoid_seg)
871 create_n_segment_path(objp, path_length, avoid_seg);
874 extern int Connected_segment_distance;
876 #define Int3_if(cond) if (!cond) Int3();
878 // ----------------------------------------------------------------------------------------------------
879 void move_object_to_goal(object *objp, vms_vector *goal_point, int goal_seg)
881 ai_static *aip = &objp->ctype.ai_info;
884 if (aip->path_length < 2)
887 Assert(objp->segnum != -1);
889 // mprintf((0, "[%i -> %i]\n", OBJECT_NUMBER(objp), goal_seg));
892 if (objp->segnum != goal_seg)
893 if (find_connect_side(&Segments[objp->segnum], &Segments[goal_seg]) == -1) {
895 dist = find_connected_distance(&objp->pos, objp->segnum, goal_point, goal_seg, 30, WID_FLY_FLAG);
896 if (Connected_segment_distance > 2) { // This global is set in find_connected_distance
898 mprintf((1, "Warning: Object %i hopped across %i segments, a distance of %7.3f.\n", OBJECT_NUMBER(objp), Connected_segment_distance, f2fl(dist)));
903 Assert(aip->path_length >= 2);
905 if (aip->cur_path_index <= 0) {
906 if (aip->behavior == AIB_STATION) {
907 // mprintf((0, "Object #%i, creating path back to station.\n", OBJECT_NUMBER(objp)));
908 create_path_to_station(objp, 15);
911 aip->cur_path_index = 1;
913 } else if (aip->cur_path_index >= aip->path_length - 1) {
914 if (aip->behavior == AIB_STATION) {
915 // mprintf((0, "Object #%i, creating path back to station.\n", OBJECT_NUMBER(objp)));
916 create_path_to_station(objp, 15);
917 if (aip->path_length == 0) {
918 ai_local *ailp = &Ai_local_info[OBJECT_NUMBER(objp)];
919 ailp->mode = AIM_STILL;
923 Assert(aip->path_length != 0);
924 aip->cur_path_index = aip->path_length-2;
927 aip->cur_path_index += aip->PATH_DIR;
929 //--Int3_if(((aip->cur_path_index >= 0) && (aip->cur_path_index < aip->path_length)));
931 objp->pos = *goal_point;
932 segnum = find_object_seg(objp);
933 if (segnum != goal_seg)
934 mprintf((1, "Object #%i goal supposed to be in segment #%i, but in segment #%i\n", OBJECT_NUMBER(objp), goal_seg, segnum));
937 Int3(); // Oops, object is not in any segment.
938 // Contact Mike: This is impossible.
939 // Hack, move object to center of segment it used to be in.
940 compute_segment_center(&objp->pos, &Segments[objp->segnum]);
942 obj_relink(OBJECT_NUMBER(objp), segnum);
945 // -- too much work -- // ----------------------------------------------------------------------------------------------------------
946 // -- too much work -- // Return true if the object the companion wants to kill is reachable.
947 // -- too much work -- int attack_kill_object(object *objp)
948 // -- too much work -- {
949 // -- too much work -- object *kill_objp;
950 // -- too much work -- fvi_info hit_data;
951 // -- too much work -- int fate;
952 // -- too much work -- fvi_query fq;
953 // -- too much work --
954 // -- too much work -- if (Escort_kill_object == -1)
955 // -- too much work -- return 0;
956 // -- too much work --
957 // -- too much work -- kill_objp = &Objects[Escort_kill_object];
958 // -- too much work --
959 // -- too much work -- fq.p0 = &objp->pos;
960 // -- too much work -- fq.startseg = objp->segnum;
961 // -- too much work -- fq.p1 = &kill_objp->pos;
962 // -- too much work -- fq.rad = objp->size;
963 // -- too much work -- fq.thisobjnum = OBJECT_NUMBER(objp);
964 // -- too much work -- fq.ignore_obj_list = NULL;
965 // -- too much work -- fq.flags = 0;
966 // -- too much work --
967 // -- too much work -- fate = find_vector_intersection(&fq,&hit_data);
968 // -- too much work --
969 // -- too much work -- if (fate == HIT_NONE)
970 // -- too much work -- return 1;
971 // -- too much work -- else
972 // -- too much work -- return 0;
973 // -- too much work -- }
975 // ----------------------------------------------------------------------------------------------------------
976 // Optimization: If current velocity will take robot near goal, don't change velocity
977 void ai_follow_path(object *objp, int player_visibility, int previous_visibility, vms_vector *vec_to_player)
979 ai_static *aip = &objp->ctype.ai_info;
981 vms_vector goal_point, new_goal_point;
983 robot_info *robptr = &Robot_info[objp->id];
984 int forced_break, original_dir, original_index;
987 ai_local *ailp = &Ai_local_info[OBJECT_NUMBER(objp)];
988 fix threshold_distance;
991 // mprintf((0, "Obj %i, dist=%6.1f index=%i len=%i seg=%i pos = %6.1f %6.1f %6.1f.\n", OBJECT_NUMBER(objp), f2fl(vm_vec_dist_quick(&objp->pos, &ConsoleObject->pos)), aip->cur_path_index, aip->path_length, objp->segnum, f2fl(objp->pos.x), f2fl(objp->pos.y), f2
993 if ((aip->hide_index == -1) || (aip->path_length == 0))
995 if (ailp->mode == AIM_RUN_FROM_OBJECT) {
996 create_n_segment_path(objp, 5, -1);
997 //--Int3_if((aip->path_length != 0));
998 ailp->mode = AIM_RUN_FROM_OBJECT;
1000 // -- mprintf((0, "Object %i creating path for no apparent reason.\n", OBJECT_NUMBER(objp)));
1001 create_n_segment_path(objp, 5, -1);
1002 //--Int3_if((aip->path_length != 0));
1006 if ((aip->hide_index + aip->path_length > POINT_SEG_NUMBER(Point_segs_free_ptr)) && (aip->path_length>0)) {
1007 Int3(); // Contact Mike: Bad. Path goes into what is believed to be free space.
1008 // This is debugging code. Figure out why garbage collection
1009 // didn't compress this object's path information.
1010 ai_path_garbage_collect();
1011 //force_dump_ai_objects_all("Error in ai_follow_path");
1012 ai_reset_all_paths();
1015 if (aip->path_length < 2) {
1016 if ((aip->behavior == AIB_SNIPE) || (ailp->mode == AIM_RUN_FROM_OBJECT)) {
1017 if (ConsoleObject->segnum == objp->segnum) {
1018 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)
1019 //--Int3_if((aip->path_length != 0));
1021 create_n_segment_path(objp, AVOID_SEG_LENGTH, ConsoleObject->segnum);
1022 //--Int3_if((aip->path_length != 0));
1024 if (aip->behavior == AIB_SNIPE) {
1026 ailp->mode = AIM_THIEF_ATTACK; // It gets bashed in create_n_segment_path
1028 ailp->mode = AIM_SNIPE_FIRE; // It gets bashed in create_n_segment_path
1030 ailp->mode = AIM_RUN_FROM_OBJECT; // It gets bashed in create_n_segment_path
1032 } else if (robptr->companion == 0) {
1033 ailp->mode = AIM_STILL;
1034 aip->path_length = 0;
1039 //--Int3_if(((aip->PATH_DIR == -1) || (aip->PATH_DIR == 1)));
1040 //--Int3_if(((aip->cur_path_index >= 0) && (aip->cur_path_index < aip->path_length)));
1042 goal_point = Point_segs[aip->hide_index + aip->cur_path_index].point;
1043 goal_seg = Point_segs[aip->hide_index + aip->cur_path_index].segnum;
1044 dist_to_goal = vm_vec_dist_quick(&goal_point, &objp->pos);
1047 dist_to_player = vm_vec_dist_quick(&objp->pos, &Viewer->pos);
1049 dist_to_player = vm_vec_dist_quick(&objp->pos, &ConsoleObject->pos);
1051 // Efficiency hack: If far away from player, move in big quantized jumps.
1052 if (!(player_visibility || previous_visibility) && (dist_to_player > F1_0*200) && !(Game_mode & GM_MULTI)) {
1053 if (dist_to_goal < F1_0*2) {
1054 move_object_to_goal(objp, &goal_point, goal_seg);
1057 robot_info *robptr = &Robot_info[objp->id];
1058 fix cur_speed = robptr->max_speed[Difficulty_level]/2;
1059 fix distance_travellable = fixmul(FrameTime, cur_speed);
1061 // int connect_side = find_connect_side(objp->segnum, goal_seg);
1062 // Only move to goal if allowed to fly through the side.
1063 // Buddy-bot can create paths he can't fly, waiting for player.
1064 // -- 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) {
1065 if (!Robot_info[objp->id].companion && !Robot_info[objp->id].thief) {
1066 if (distance_travellable >= dist_to_goal) {
1067 move_object_to_goal(objp, &goal_point, goal_seg);
1069 fix prob = fixdiv(distance_travellable, dist_to_goal);
1071 int rand_num = d_rand();
1072 if ( (rand_num >> 1) < prob) {
1073 move_object_to_goal(objp, &goal_point, goal_seg);
1082 // If running from player, only run until can't be seen.
1083 if (ailp->mode == AIM_RUN_FROM_OBJECT) {
1084 if ((player_visibility == 0) && (ailp->player_awareness_type == 0)) {
1087 vel_scale = F1_0 - FrameTime/2;
1088 if (vel_scale < F1_0/2)
1091 vm_vec_scale(&objp->mtype.phys_info.velocity, vel_scale);
1094 } else if (!(FrameCount ^ ((OBJECT_NUMBER(objp)) & 0x07))) { // Done 1/8 frames.
1095 // If player on path (beyond point robot is now at), then create a new path.
1096 point_seg *curpsp = &Point_segs[aip->hide_index];
1097 int player_segnum = ConsoleObject->segnum;
1100 // This is probably being done every frame, which is wasteful.
1101 for (i=aip->cur_path_index; i<aip->path_length; i++) {
1102 if (curpsp[i].segnum == player_segnum) {
1103 if (player_segnum != objp->segnum) {
1104 create_n_segment_path(objp, AVOID_SEG_LENGTH, player_segnum);
1106 create_n_segment_path(objp, AVOID_SEG_LENGTH, -1);
1108 Assert(aip->path_length != 0);
1109 ailp->mode = AIM_RUN_FROM_OBJECT; // It gets bashed in create_n_segment_path
1113 if (player_visibility) {
1114 ailp->player_awareness_type = 1;
1115 ailp->player_awareness_time = F1_0;
1120 //--Int3_if(((aip->cur_path_index >= 0) && (aip->cur_path_index < aip->path_length)));
1122 if (aip->cur_path_index < 0) {
1123 aip->cur_path_index = 0;
1124 } else if (aip->cur_path_index >= aip->path_length) {
1125 if (ailp->mode == AIM_RUN_FROM_OBJECT) {
1126 create_n_segment_path(objp, AVOID_SEG_LENGTH, ConsoleObject->segnum);
1127 ailp->mode = AIM_RUN_FROM_OBJECT; // It gets bashed in create_n_segment_path
1128 Assert(aip->path_length != 0);
1130 aip->cur_path_index = aip->path_length-1;
1134 goal_point = Point_segs[aip->hide_index + aip->cur_path_index].point;
1136 // If near goal, pick another goal point.
1137 forced_break = 0; // Gets set for short paths.
1138 original_dir = aip->PATH_DIR;
1139 original_index = aip->cur_path_index;
1140 threshold_distance = fixmul(vm_vec_mag_quick(&objp->mtype.phys_info.velocity), FrameTime)*2 + F1_0*2;
1142 new_goal_point = Point_segs[aip->hide_index + aip->cur_path_index].point;
1144 //--Int3_if(((aip->cur_path_index >= 0) && (aip->cur_path_index < aip->path_length)));
1146 while ((dist_to_goal < threshold_distance) && !forced_break) {
1148 // Advance to next point on path.
1149 aip->cur_path_index += aip->PATH_DIR;
1151 // See if next point wraps past end of path (in either direction), and if so, deal with it based on mode.
1152 if ((aip->cur_path_index >= aip->path_length) || (aip->cur_path_index < 0)) {
1154 //mprintf((0, "Object %i reached end of the line!\n", OBJECT_NUMBER(objp)));
1155 // If mode = hiding, then stay here until get bonked or hit by player.
1156 // -- if (ailp->mode == AIM_BEHIND) {
1157 // -- ailp->mode = AIM_STILL;
1158 // -- return; // Stay here until bonked or hit by player.
1161 // Buddy bot. If he's in mode to get away from player and at end of line,
1162 // if player visible, then make a new path, else just return.
1163 if (robptr->companion) {
1164 if (Escort_special_goal == ESCORT_GOAL_SCRAM)
1166 if (player_visibility) {
1167 create_n_segment_path(objp, 16 + d_rand() * 16, -1);
1168 aip->path_length = polish_path(objp, &Point_segs[aip->hide_index], aip->path_length);
1169 Assert(aip->path_length != 0);
1170 // -- mprintf((0, "Buddy: Creating new path!\n"));
1171 ailp->mode = AIM_WANDER; // Special buddy mode.
1172 //--Int3_if(((aip->cur_path_index >= 0) && (aip->cur_path_index < aip->path_length)));
1175 ailp->mode = AIM_WANDER; // Special buddy mode.
1176 vm_vec_zero(&objp->mtype.phys_info.velocity);
1177 vm_vec_zero(&objp->mtype.phys_info.rotvel);
1178 // -- mprintf((0, "Buddy: I'm hidden!\n"));
1179 //!!Assert((aip->cur_path_index >= 0) && (aip->cur_path_index < aip->path_length));
1185 if (aip->behavior == AIB_FOLLOW) {
1186 // mprintf((0, "AIB_FOLLOW: Making new path.\n"));
1187 create_n_segment_path(objp, 10, ConsoleObject->segnum);
1188 //--Int3_if(((aip->cur_path_index >= 0) && (aip->cur_path_index < aip->path_length)));
1189 } else if (aip->behavior == AIB_STATION) {
1190 // mprintf((0, "Object %i reached end of line, creating path back to station.\n", OBJECT_NUMBER(objp)));
1191 create_path_to_station(objp, 15);
1192 if ((aip->hide_segment != Point_segs[aip->hide_index+aip->path_length-1].segnum) || (aip->path_length == 0)) {
1193 ailp->mode = AIM_STILL;
1195 //--Int3_if(((aip->cur_path_index >= 0) && (aip->cur_path_index < aip->path_length)));
1198 } else if (ailp->mode == AIM_FOLLOW_PATH) {
1199 create_path_to_player(objp, 10, 1);
1200 if (aip->hide_segment != Point_segs[aip->hide_index+aip->path_length-1].segnum) {
1201 ailp->mode = AIM_STILL;
1204 //--Int3_if(((aip->cur_path_index >= 0) && (aip->cur_path_index < aip->path_length)));
1206 } else if (ailp->mode == AIM_RUN_FROM_OBJECT) {
1207 create_n_segment_path(objp, AVOID_SEG_LENGTH, ConsoleObject->segnum);
1208 ailp->mode = AIM_RUN_FROM_OBJECT; // It gets bashed in create_n_segment_path
1209 if (aip->path_length < 1) {
1210 create_n_segment_path(objp, AVOID_SEG_LENGTH, ConsoleObject->segnum);
1211 ailp->mode = AIM_RUN_FROM_OBJECT; // It gets bashed in create_n_segment_path
1212 if (aip->path_length < 1) {
1213 aip->behavior = AIB_NORMAL;
1214 ailp->mode = AIM_STILL;
1218 //--Int3_if(((aip->cur_path_index >= 0) && (aip->cur_path_index < aip->path_length)));
1220 // Reached end of the line. First see if opposite end point is reachable, and if so, go there.
1221 // If not, turn around.
1222 int opposite_end_index;
1223 vms_vector *opposite_end_point;
1228 // See which end we're nearer and look at the opposite end point.
1229 if (abs(aip->cur_path_index - aip->path_length) < aip->cur_path_index) {
1230 // Nearer to far end (ie, index not 0), so try to reach 0.
1231 opposite_end_index = 0;
1233 // Nearer to 0 end, so try to reach far end.
1234 opposite_end_index = aip->path_length-1;
1237 //--Int3_if(((opposite_end_index >= 0) && (opposite_end_index < aip->path_length)));
1239 opposite_end_point = &Point_segs[aip->hide_index + opposite_end_index].point;
1242 fq.startseg = objp->segnum;
1243 fq.p1 = opposite_end_point;
1244 fq.rad = objp->size;
1245 fq.thisobjnum = OBJECT_NUMBER(objp);
1246 fq.ignore_obj_list = NULL;
1247 fq.flags = 0; //what about trans walls???
1249 fate = find_vector_intersection(&fq,&hit_data);
1251 if (fate != HIT_WALL) {
1252 // We can be circular! Do it!
1253 // Path direction is unchanged.
1254 aip->cur_path_index = opposite_end_index;
1256 aip->PATH_DIR = -aip->PATH_DIR;
1257 aip->cur_path_index += aip->PATH_DIR;
1259 //--Int3_if(((aip->cur_path_index >= 0) && (aip->cur_path_index < aip->path_length)));
1263 new_goal_point = Point_segs[aip->hide_index + aip->cur_path_index].point;
1264 goal_point = new_goal_point;
1265 dist_to_goal = vm_vec_dist_quick(&goal_point, &objp->pos);
1266 //--Int3_if(((aip->cur_path_index >= 0) && (aip->cur_path_index < aip->path_length)));
1269 // If went all the way around to original point, in same direction, then get out of here!
1270 if ((aip->cur_path_index == original_index) && (aip->PATH_DIR == original_dir)) {
1271 create_path_to_player(objp, 3, 1);
1272 //--Int3_if(((aip->cur_path_index >= 0) && (aip->cur_path_index < aip->path_length)));
1275 //--Int3_if(((aip->cur_path_index >= 0) && (aip->cur_path_index < aip->path_length)));
1278 // Set velocity (objp->mtype.phys_info.velocity) and orientation (objp->orient) for this object.
1279 //--Int3_if(((aip->cur_path_index >= 0) && (aip->cur_path_index < aip->path_length)));
1280 ai_path_set_orient_and_vel(objp, &goal_point, player_visibility, vec_to_player);
1281 //--Int3_if(((aip->cur_path_index >= 0) && (aip->cur_path_index < aip->path_length)));
1286 short path_start, objnum;
1289 int path_index_compare(obj_path *i1, obj_path *i2)
1291 if (i1->path_start < i2->path_start)
1293 else if (i1->path_start == i2->path_start)
1299 // ----------------------------------------------------------------------------------------------------------
1300 // Set orientation matrix and velocity for objp based on its desire to get to a point.
1301 void ai_path_set_orient_and_vel(object *objp, vms_vector *goal_point, int player_visibility, vms_vector *vec_to_player)
1303 vms_vector cur_vel = objp->mtype.phys_info.velocity;
1304 vms_vector norm_cur_vel;
1305 vms_vector norm_vec_to_goal;
1306 vms_vector cur_pos = objp->pos;
1307 vms_vector norm_fvec;
1310 robot_info *robptr = &Robot_info[objp->id];
1313 // If evading player, use highest difficulty level speed, plus something based on diff level
1314 max_speed = robptr->max_speed[Difficulty_level];
1315 if ((Ai_local_info[OBJECT_NUMBER(objp)].mode == AIM_RUN_FROM_OBJECT) || (objp->ctype.ai_info.behavior == AIB_SNIPE))
1316 max_speed = max_speed*3/2;
1318 vm_vec_sub(&norm_vec_to_goal, goal_point, &cur_pos);
1319 vm_vec_normalize_quick(&norm_vec_to_goal);
1321 norm_cur_vel = cur_vel;
1322 vm_vec_normalize_quick(&norm_cur_vel);
1324 norm_fvec = objp->orient.fvec;
1325 vm_vec_normalize_quick(&norm_fvec);
1327 dot = vm_vec_dot(&norm_vec_to_goal, &norm_fvec);
1329 // If very close to facing opposite desired vector, perturb vector
1330 if (dot < -15*F1_0/16) {
1331 //mprintf((0, "Facing away from goal, abruptly turning\n"));
1332 norm_cur_vel = norm_vec_to_goal;
1334 norm_cur_vel.x += norm_vec_to_goal.x/2;
1335 norm_cur_vel.y += norm_vec_to_goal.y/2;
1336 norm_cur_vel.z += norm_vec_to_goal.z/2;
1339 vm_vec_normalize_quick(&norm_cur_vel);
1341 // Set speed based on this robot type's maximum allowed speed and how hard it is turning.
1342 // How hard it is turning is based on the dot product of (vector to goal) and (current velocity vector)
1343 // Note that since 3*F1_0/4 is added to dot product, it is possible for the robot to back up.
1345 // Set speed and orientation.
1349 // If in snipe mode, can move fast even if not facing that direction.
1350 if (objp->ctype.ai_info.behavior == AIB_SNIPE)
1352 dot = (dot + F1_0)/2;
1354 speed_scale = fixmul(max_speed, dot);
1355 vm_vec_scale(&norm_cur_vel, speed_scale);
1356 objp->mtype.phys_info.velocity = norm_cur_vel;
1358 if ((Ai_local_info[OBJECT_NUMBER(objp)].mode == AIM_RUN_FROM_OBJECT) || (robptr->companion == 1) || (objp->ctype.ai_info.behavior == AIB_SNIPE)) {
1359 if (Ai_local_info[OBJECT_NUMBER(objp)].mode == AIM_SNIPE_RETREAT_BACKWARDS) {
1360 if ((player_visibility) && (vec_to_player != NULL))
1361 norm_vec_to_goal = *vec_to_player;
1363 vm_vec_negate(&norm_vec_to_goal);
1365 ai_turn_towards_vector(&norm_vec_to_goal, objp, robptr->turn_time[NDL-1]/2);
1367 ai_turn_towards_vector(&norm_vec_to_goal, objp, robptr->turn_time[Difficulty_level]);
1371 int Last_frame_garbage_collected = 0;
1373 // ----------------------------------------------------------------------------------------------------------
1374 // Garbage colledion -- Free all unused records in Point_segs and compress all paths.
1375 void ai_path_garbage_collect(void)
1377 int free_path_index = 0;
1378 int num_path_objects = 0;
1381 obj_path object_list[MAX_OBJECTS];
1384 force_dump_ai_objects_all("***** Start ai_path_garbage_collect *****");
1387 // -- mprintf((0, "Garbage collection frame %i, last frame %i! Old free index = %i ", FrameCount, Last_frame_garbage_collected, POINT_SEG_NUMBER(Point_segs_free_ptr)));
1389 Last_frame_garbage_collected = FrameCount;
1392 validate_all_paths();
1394 // Create a list of objects which have paths of length 1 or more.
1395 for (objnum=0; objnum <= Highest_object_index; objnum++) {
1396 object *objp = &Objects[objnum];
1398 if ((objp->type == OBJ_ROBOT) && ((objp->control_type == CT_AI) || (objp->control_type == CT_MORPH))) {
1399 ai_static *aip = &objp->ctype.ai_info;
1401 if (aip->path_length) {
1402 object_list[num_path_objects].path_start = aip->hide_index;
1403 object_list[num_path_objects++].objnum = objnum;
1408 qsort(object_list, num_path_objects, sizeof(object_list[0]),
1409 (int (*)(void const *,void const *))path_index_compare);
1411 for (objind=0; objind < num_path_objects; objind++) {
1417 objnum = object_list[objind].objnum;
1418 objp = &Objects[objnum];
1419 aip = &objp->ctype.ai_info;
1420 old_index = aip->hide_index;
1422 aip->hide_index = free_path_index;
1423 for (i=0; i<aip->path_length; i++)
1424 Point_segs[free_path_index++] = Point_segs[old_index++];
1427 Point_segs_free_ptr = &Point_segs[free_path_index];
1429 // mprintf((0, "new = %i\n", free_path_index));
1430 //printf("After garbage collection, free index = %i\n", POINT_SEG_NUMBER(Point_segs_free_ptr));
1435 force_dump_ai_objects_all("***** Finish ai_path_garbage_collect *****");
1437 for (i=0; i<=Highest_object_index; i++) {
1438 ai_static *aip = &Objects[i].ctype.ai_info;
1440 if ((Objects[i].type == OBJ_ROBOT) && (Objects[i].control_type == CT_AI))
1441 if ((aip->hide_index + aip->path_length > POINT_SEG_NUMBER(Point_segs_free_ptr)) && (aip->path_length>0))
1442 Int3(); // Contact Mike: Debug trap for nasty, elusive bug.
1445 validate_all_paths();
1451 // -----------------------------------------------------------------------------
1452 // Do garbage collection if not been done for awhile, or things getting really critical.
1453 void maybe_ai_path_garbage_collect(void)
1455 if (POINT_SEG_NUMBER(Point_segs_free_ptr) > MAX_POINT_SEGS - MAX_PATH_LENGTH) {
1456 if (Last_frame_garbage_collected+1 >= FrameCount) {
1457 // This is kind of bad. Garbage collected last frame or this frame.
1458 // Just destroy all paths. Too bad for the robots. They are memory wasteful.
1459 ai_reset_all_paths();
1460 mprintf((1, "Warning: Resetting all paths. Point_segs buffer nearly exhausted.\n"));
1462 // We are really close to full, but didn't just garbage collect, so maybe this is recoverable.
1463 mprintf((1, "Warning: Almost full garbage collection being performed: "));
1464 ai_path_garbage_collect();
1465 mprintf((1, "Free records = %i/%i\n", MAX_POINT_SEGS - (POINT_SEG_NUMBER(Point_segs_free_ptr)), MAX_POINT_SEGS));
1467 } else if (POINT_SEG_NUMBER(Point_segs_free_ptr) > 3*MAX_POINT_SEGS/4) {
1468 if (Last_frame_garbage_collected + 16 < FrameCount) {
1469 ai_path_garbage_collect();
1471 } else if (POINT_SEG_NUMBER(Point_segs_free_ptr) > MAX_POINT_SEGS/2) {
1472 if (Last_frame_garbage_collected + 256 < FrameCount) {
1473 ai_path_garbage_collect();
1478 // -----------------------------------------------------------------------------
1479 // Reset all paths. Do garbage collection.
1480 // Should be called at the start of each level.
1481 void ai_reset_all_paths(void)
1485 for (i=0; i<=Highest_object_index; i++)
1486 if (Objects[i].control_type == CT_AI) {
1487 Objects[i].ctype.ai_info.hide_index = -1;
1488 Objects[i].ctype.ai_info.path_length = 0;
1491 ai_path_garbage_collect();
1495 // ---------------------------------------------------------------------------------------------------------
1496 // Probably called because a robot bashed a wall, getting a bunch of retries.
1497 // Try to resume path.
1498 void attempt_to_resume_path(object *objp)
1500 //int objnum = OBJECT_NUMBER(objp);
1501 ai_static *aip = &objp->ctype.ai_info;
1502 // int goal_segnum, object_segnum,
1503 int abs_index, new_path_index;
1505 // mprintf((0, "Object %i trying to resume path at index %i\n", OBJECT_NUMBER(objp), aip->cur_path_index));
1507 if ((aip->behavior == AIB_STATION) && (Robot_info[objp->id].companion != 1))
1508 if (d_rand() > 8192) {
1509 ai_local *ailp = &Ai_local_info[OBJECT_NUMBER(objp)];
1511 aip->hide_segment = objp->segnum;
1513 ailp->mode = AIM_STILL;
1514 mprintf((1, "Note: Bashing hide segment of robot %i to current segment because he's lost.\n", OBJECT_NUMBER(objp)));
1517 // object_segnum = objp->segnum;
1518 abs_index = aip->hide_index+aip->cur_path_index;
1519 // goal_segnum = Point_segs[abs_index].segnum;
1521 // if (object_segnum == goal_segnum)
1522 // mprintf((0, "Very peculiar, goal segnum = object's segnum = %i.\n", goal_segnum));
1524 new_path_index = aip->cur_path_index - aip->PATH_DIR;
1526 if ((new_path_index >= 0) && (new_path_index < aip->path_length)) {
1527 // mprintf((0, "Trying path index of %i\n", new_path_index));
1528 aip->cur_path_index = new_path_index;
1530 // At end of line and have nowhere to go.
1531 // mprintf((0, "At end of line and can't get to goal. Creating new path. Frame %i\n", FrameCount));
1532 move_towards_segment_center(objp);
1533 create_path_to_station(objp, 15);
1538 // ----------------------------------------------------------------------------------------------------------
1539 // DEBUG FUNCTIONS FOLLOW
1540 // ----------------------------------------------------------------------------------------------------------
1543 int Test_size = 1000;
1545 void test_create_path_many(void)
1547 point_seg point_segs[200];
1552 for (i=0; i<Test_size; i++) {
1553 Cursegp = &Segments[(d_rand() * (Highest_segment_index + 1)) / D_RAND_MAX];
1554 Markedsegp = &Segments[(d_rand() * (Highest_segment_index + 1)) / D_RAND_MAX];
1555 create_path_points(&Objects[0], SEGMENT_NUMBER(Cursegp), SEGMENT_NUMBER(Markedsegp), point_segs, &num_points, -1, 0, 0, -1);
1560 void test_create_path(void)
1562 point_seg point_segs[200];
1565 create_path_points(&Objects[0], SEGMENT_NUMBER(Cursegp), SEGMENT_NUMBER(Markedsegp), point_segs, &num_points, -1, 0, 0, -1);
1569 void show_path(int start_seg, int end_seg, point_seg *psp, short length)
1571 printf("[%3i:%3i (%3i):] ", start_seg, end_seg, length);
1574 printf("%3i ", psp[length].segnum);
1579 // For all segments in mine, create paths to all segments in mine, print results.
1580 void test_create_all_paths(void)
1582 int start_seg, end_seg;
1583 short resultant_length;
1585 Point_segs_free_ptr = Point_segs;
1587 for (start_seg=0; start_seg<=Highest_segment_index-1; start_seg++) {
1588 // -- mprintf((0, "."));
1589 if (Segments[start_seg].segnum != -1) {
1590 for (end_seg=start_seg+1; end_seg<=Highest_segment_index; end_seg++) {
1591 if (Segments[end_seg].segnum != -1) {
1592 create_path_points(&Objects[0], start_seg, end_seg, Point_segs_free_ptr, &resultant_length, -1, 0, 0, -1);
1593 show_path(start_seg, end_seg, Point_segs_free_ptr, resultant_length);
1600 //--anchor--int Num_anchors;
1601 //--anchor--int Anchor_distance = 3;
1602 //--anchor--int End_distance = 1;
1603 //--anchor--int Anchors[MAX_SEGMENTS];
1605 //--anchor--int get_nearest_anchor_distance(int segnum)
1607 //--anchor-- short resultant_length, minimum_length;
1608 //--anchor-- int anchor_index;
1610 //--anchor-- minimum_length = 16383;
1612 //--anchor-- for (anchor_index=0; anchor_index<Num_anchors; anchor_index++) {
1613 //--anchor-- create_path_points(&Objects[0], segnum, Anchors[anchor_index], Point_segs_free_ptr, &resultant_length, -1, 0, 0, -1);
1614 //--anchor-- if (resultant_length != 0)
1615 //--anchor-- if (resultant_length < minimum_length)
1616 //--anchor-- minimum_length = resultant_length;
1619 //--anchor-- return minimum_length;
1623 //--anchor--void create_new_anchor(int segnum)
1625 //--anchor-- Anchors[Num_anchors++] = segnum;
1628 //--anchor--// A set of anchors is within N units of all segments in the graph.
1629 //--anchor--// Anchor_distance = how close anchors can be.
1630 //--anchor--// End_distance = how close you can be to the end.
1631 //--anchor--void test_create_all_anchors(void)
1633 //--anchor-- int nearest_anchor_distance;
1634 //--anchor-- int segnum,i;
1636 //--anchor-- Num_anchors = 0;
1638 //--anchor-- for (segnum=0; segnum<=Highest_segment_index; segnum++) {
1639 //--anchor-- if (Segments[segnum].segnum != -1) {
1640 //--anchor-- nearest_anchor_distance = get_nearest_anchor_distance(segnum);
1641 //--anchor-- if (nearest_anchor_distance > Anchor_distance)
1642 //--anchor-- create_new_anchor(segnum);
1646 //--anchor-- // Set selected segs.
1647 //--anchor-- for (i=0; i<Num_anchors; i++)
1648 //--anchor-- Selected_segs[i] = Anchors[i];
1649 //--anchor-- N_selected_segs = Num_anchors;
1653 //--anchor--int Test_path_length = 5;
1655 //--anchor--void test_create_n_segment_path(void)
1657 //--anchor-- point_seg point_segs[200];
1658 //--anchor-- short num_points;
1660 //--anchor-- create_path_points(&Objects[0], SEGMENT_NUMBER(Cursegp), -2, point_segs, &num_points, Test_path_length, 0, 0, -1);
1663 short Player_path_length=0;
1664 int Player_hide_index=-1;
1665 int Player_cur_path_index=0;
1666 int Player_following_path_flag=0;
1668 // ------------------------------------------------------------------------------------------------------------------
1669 // Set orientation matrix and velocity for objp based on its desire to get to a point.
1670 void player_path_set_orient_and_vel(object *objp, vms_vector *goal_point)
1672 vms_vector cur_vel = objp->mtype.phys_info.velocity;
1673 vms_vector norm_cur_vel;
1674 vms_vector norm_vec_to_goal;
1675 vms_vector cur_pos = objp->pos;
1676 vms_vector norm_fvec;
1681 max_speed = Robot_info[objp->id].max_speed[Difficulty_level];
1683 vm_vec_sub(&norm_vec_to_goal, goal_point, &cur_pos);
1684 vm_vec_normalize_quick(&norm_vec_to_goal);
1686 norm_cur_vel = cur_vel;
1687 vm_vec_normalize_quick(&norm_cur_vel);
1689 norm_fvec = objp->orient.fvec;
1690 vm_vec_normalize_quick(&norm_fvec);
1692 dot = vm_vec_dot(&norm_vec_to_goal, &norm_fvec);
1693 if (Ai_local_info[OBJECT_NUMBER(objp)].mode == AIM_SNIPE_RETREAT_BACKWARDS) {
1697 // If very close to facing opposite desired vector, perturb vector
1698 if (dot < -15*F1_0/16) {
1699 //mprintf((0, "Facing away from goal, abruptly turning\n"));
1700 norm_cur_vel = norm_vec_to_goal;
1702 norm_cur_vel.x += norm_vec_to_goal.x/2;
1703 norm_cur_vel.y += norm_vec_to_goal.y/2;
1704 norm_cur_vel.z += norm_vec_to_goal.z/2;
1707 vm_vec_normalize_quick(&norm_cur_vel);
1709 // Set speed based on this robot type's maximum allowed speed and how hard it is turning.
1710 // How hard it is turning is based on the dot product of (vector to goal) and (current velocity vector)
1711 // Note that since 3*F1_0/4 is added to dot product, it is possible for the robot to back up.
1713 // Set speed and orientation.
1717 speed_scale = fixmul(max_speed, dot);
1718 vm_vec_scale(&norm_cur_vel, speed_scale);
1719 objp->mtype.phys_info.velocity = norm_cur_vel;
1720 ai_turn_towards_vector(&norm_vec_to_goal, objp, F1_0);
1724 // ----------------------------------------------------------------------------------------------------------
1725 // Optimization: If current velocity will take robot near goal, don't change velocity
1726 void player_follow_path(object *objp)
1728 vms_vector goal_point;
1730 int count, forced_break, original_index;
1732 fix threshold_distance;
1734 if (!Player_following_path_flag)
1737 if (Player_hide_index == -1)
1740 if (Player_path_length < 2)
1743 goal_point = Point_segs[Player_hide_index + Player_cur_path_index].point;
1744 goal_seg = Point_segs[Player_hide_index + Player_cur_path_index].segnum;
1745 Assert((goal_seg >= 0) && (goal_seg <= Highest_segment_index));
1746 dist_to_goal = vm_vec_dist_quick(&goal_point, &objp->pos);
1748 if (Player_cur_path_index < 0)
1749 Player_cur_path_index = 0;
1750 else if (Player_cur_path_index >= Player_path_length)
1751 Player_cur_path_index = Player_path_length-1;
1753 goal_point = Point_segs[Player_hide_index + Player_cur_path_index].point;
1757 // If near goal, pick another goal point.
1758 forced_break = 0; // Gets set for short paths.
1760 original_index = Player_cur_path_index;
1761 threshold_distance = fixmul(vm_vec_mag_quick(&objp->mtype.phys_info.velocity), FrameTime)*2 + F1_0*2;
1763 while ((dist_to_goal < threshold_distance) && !forced_break) {
1765 // -- if (count > 1)
1766 // -- mprintf((0, "."));
1768 // ----- Debug stuff -----
1770 mprintf((1,"Problem following path for player. Aborting.\n"));
1774 // Advance to next point on path.
1775 Player_cur_path_index += 1;
1777 // See if next point wraps past end of path (in either direction), and if so, deal with it based on mode.
1778 if ((Player_cur_path_index >= Player_path_length) || (Player_cur_path_index < 0)) {
1779 Player_following_path_flag = 0;
1783 // If went all the way around to original point, in same direction, then get out of here!
1784 if (Player_cur_path_index == original_index) {
1785 mprintf((0, "Forcing break because player path wrapped, count = %i.\n", count));
1786 Player_following_path_flag = 0;
1790 goal_point = Point_segs[Player_hide_index + Player_cur_path_index].point;
1791 dist_to_goal = vm_vec_dist_quick(&goal_point, &objp->pos);
1795 // Set velocity (objp->mtype.phys_info.velocity) and orientation (objp->orient) for this object.
1796 player_path_set_orient_and_vel(objp, &goal_point);
1801 // ------------------------------------------------------------------------------------------------------------------
1802 // Create path for player from current segment to goal segment.
1803 void create_player_path_to_segment(int segnum)
1805 object *objp = ConsoleObject;
1807 Player_path_length=0;
1808 Player_hide_index=-1;
1809 Player_cur_path_index=0;
1810 Player_following_path_flag=0;
1812 if (create_path_points(objp, objp->segnum, segnum, Point_segs_free_ptr, &Player_path_length, 100, 0, 0, -1) == -1)
1813 mprintf((0, "Unable to form path of length %i for myself\n", 100));
1815 Player_following_path_flag = 1;
1817 Player_hide_index = POINT_SEG_NUMBER(Point_segs_free_ptr);
1818 Player_cur_path_index = 0;
1819 Point_segs_free_ptr += Player_path_length;
1820 if (POINT_SEG_NUMBER(Point_segs_free_ptr) + MAX_PATH_LENGTH*2 > MAX_POINT_SEGS) {
1821 //Int3(); // Contact Mike: This is curious, though not deadly. /eip++;g
1822 ai_reset_all_paths();
1827 int Player_goal_segment = -1;
1829 void check_create_player_path(void)
1831 if (Player_goal_segment != -1)
1832 create_player_path_to_segment(Player_goal_segment);
1834 Player_goal_segment = -1;
1839 // ----------------------------------------------------------------------------------------------------------
1840 // DEBUG FUNCTIONS ENDED
1841 // ----------------------------------------------------------------------------------------------------------