2006-03-20 17:12:09 +00:00
/*
2014-06-01 17:55:23 +00:00
* Portions of this file are copyright Rebirth contributors and licensed as
* described in COPYING . txt .
* Portions of this file are copyright Parallax Software and licensed
* according to the Parallax license below .
* See COPYING . txt for license details .
2006-03-20 17:12:09 +00:00
THE COMPUTER CODE CONTAINED HEREIN IS THE SOLE PROPERTY OF PARALLAX
SOFTWARE CORPORATION ( " PARALLAX " ) . PARALLAX , IN DISTRIBUTING THE CODE TO
END - USERS , AND SUBJECT TO ALL OF THE TERMS AND CONDITIONS HEREIN , GRANTS A
ROYALTY - FREE , PERPETUAL LICENSE TO SUCH END - USERS FOR USE BY SUCH END - USERS
IN USING , DISPLAYING , AND CREATING DERIVATIVE WORKS THEREOF , SO LONG AS
SUCH USE , DISPLAY OR CREATION IS FOR NON - COMMERCIAL , ROYALTY OR REVENUE
FREE PURPOSES . IN NO EVENT SHALL THE END - USER USE THE COMPUTER CODE
CONTAINED HEREIN FOR REVENUE - BEARING PURPOSES . THE END - USER UNDERSTANDS
AND AGREES TO THE TERMS HEREIN AND ACCEPTS THE SAME BY USE OF THIS FILE .
COPYRIGHT 1993 - 1999 PARALLAX SOFTWARE CORPORATION . ALL RIGHTS RESERVED .
*/
/*
*
* AI path forming stuff .
*
*/
# include <stdio.h> // for printf()
# include <stdlib.h> // for d_rand() and qsort()
# include <string.h> // for memset()
# include "inferno.h"
2008-04-06 20:23:28 +00:00
# include "console.h"
2006-03-20 17:12:09 +00:00
# include "3d.h"
# include "object.h"
2012-07-07 18:35:06 +00:00
# include "dxxerror.h"
2006-03-20 17:12:09 +00:00
# include "ai.h"
# include "robot.h"
# include "fvi.h"
2013-12-26 04:18:28 +00:00
# include "gameseg.h"
2006-03-20 17:12:09 +00:00
# include "physics.h"
# include "wall.h"
2016-09-11 18:49:16 +00:00
# if DXX_USE_EDITOR
2006-03-20 17:12:09 +00:00
# include "editor/editor.h"
2013-03-16 03:10:55 +00:00
# include "editor/esegment.h"
2006-03-20 17:12:09 +00:00
# endif
# include "player.h"
# include "fireball.h"
# include "game.h"
2014-10-12 23:05:46 +00:00
# include "compiler-range_for.h"
2015-10-13 02:43:24 +00:00
# include "partial_range.h"
2014-10-12 23:05:46 +00:00
2006-03-20 17:12:09 +00:00
// Length in segments of avoidance path
# define AVOID_SEG_LENGTH 7
# ifdef NDEBUG
# define PATH_VALIDATION 0
# else
# define PATH_VALIDATION 1
# endif
2015-12-22 04:18:50 +00:00
namespace dsx {
2014-11-02 03:41:21 +00:00
static void ai_path_set_orient_and_vel ( const vobjptr_t objp , const vms_vector & goal_point
2013-03-03 01:03:33 +00:00
# if defined(DXX_BUILD_DESCENT_II)
2014-10-02 03:02:36 +00:00
, int player_visibility , const vms_vector * vec_to_player
2013-03-03 01:03:33 +00:00
# endif
) ;
2013-09-22 22:26:27 +00:00
static void maybe_ai_path_garbage_collect ( void ) ;
static void ai_path_garbage_collect ( void ) ;
2006-03-20 17:12:09 +00:00
# if PATH_VALIDATION
2015-12-22 04:18:50 +00:00
static void validate_all_paths ( ) ;
2015-10-13 02:43:24 +00:00
static int validate_path ( int , point_seg * psegs , uint_fast32_t num_points ) ;
2006-03-20 17:12:09 +00:00
# endif
2015-12-22 04:18:50 +00:00
}
2006-03-20 17:12:09 +00:00
// ------------------------------------------------------------------------
2015-02-14 22:48:28 +00:00
static void create_random_xlate ( array < uint8_t , MAX_SIDES_PER_SEGMENT > & xt )
2006-03-20 17:12:09 +00:00
{
2015-11-26 02:56:54 +00:00
for ( int i = 0 ; i < MAX_SIDES_PER_SEGMENT ; i + + )
2006-03-20 17:12:09 +00:00
xt [ i ] = i ;
2015-02-14 22:48:28 +00:00
range_for ( auto & i , xt )
{
uint_fast32_t j = ( d_rand ( ) * MAX_SIDES_PER_SEGMENT ) / ( D_RAND_MAX + 1 ) ;
Assert ( j < xt . size ( ) ) ;
using std : : swap ;
swap ( i , xt [ j ] ) ;
2006-03-20 17:12:09 +00:00
}
}
2015-12-22 04:18:50 +00:00
namespace dsx {
2006-03-20 17:12:09 +00:00
// -----------------------------------------------------------------------------------------------------------
// Insert the point at the center of the side connecting two segments between the two points.
// This is messy because we must insert into the list. The simplest (and not too slow) way to do this is to start
// at the end of the list and go backwards.
2015-02-14 22:48:28 +00:00
static uint_fast32_t insert_center_points ( point_seg * psegs , uint_fast32_t count )
2006-03-20 17:12:09 +00:00
{
2015-02-14 22:48:28 +00:00
if ( count < 2 )
return count ;
uint_fast32_t last_point = count - 1 ;
for ( uint_fast32_t i = last_point ; i ; - - i )
{
2006-03-20 17:12:09 +00:00
psegs [ 2 * i ] = psegs [ i ] ;
2015-07-12 01:04:19 +00:00
const auto & & seg1 = vcsegptr ( psegs [ i - 1 ] . segnum ) ;
auto connect_side = find_connect_side ( vcsegptridx ( psegs [ i ] . segnum ) , seg1 ) ;
2016-01-03 20:21:35 +00:00
Assert ( connect_side ! = side_none ) ; // Impossible! These two segments must be connected, they were created by create_path_points (which was created by mk!)
if ( connect_side = = side_none ) // Try to blow past the assert, this should at least prevent a hang.
2006-03-20 17:12:09 +00:00
connect_side = 0 ;
2015-07-12 01:04:19 +00:00
const auto & & center_point = compute_center_point_on_side ( seg1 , connect_side ) ;
2014-10-29 03:24:31 +00:00
auto new_point = vm_vec_sub ( psegs [ i - 1 ] . point , center_point ) ;
2006-03-20 17:12:09 +00:00
new_point . x / = 16 ;
new_point . y / = 16 ;
new_point . z / = 16 ;
2014-09-28 21:11:45 +00:00
vm_vec_sub ( psegs [ 2 * i - 1 ] . point , center_point , new_point ) ;
2013-03-03 01:03:33 +00:00
# if defined(DXX_BUILD_DESCENT_II)
2016-01-09 16:38:13 +00:00
const auto & & segp = segptridx ( psegs [ 2 * i ] . segnum ) ;
const auto & & temp_segnum = find_point_seg ( psegs [ 2 * i - 1 ] . point , segp ) ;
2013-12-26 22:21:16 +00:00
if ( temp_segnum = = segment_none ) {
2006-03-20 17:12:09 +00:00
psegs [ 2 * i - 1 ] . point = center_point ;
2016-01-09 16:38:13 +00:00
find_point_seg ( psegs [ 2 * i - 1 ] . point , segp ) ;
2006-03-20 17:12:09 +00:00
}
2013-03-03 01:03:33 +00:00
# endif
2006-03-20 17:12:09 +00:00
psegs [ 2 * i - 1 ] . segnum = psegs [ 2 * i ] . segnum ;
count + + ;
}
2015-02-14 22:48:28 +00:00
# if defined(DXX_BUILD_DESCENT_II)
2006-03-20 17:12:09 +00:00
// Now, remove unnecessary center points.
// A center point is unnecessary if it is close to the line between the two adjacent points.
// MK, OPTIMIZE! Can get away with about half the math since every vector gets computed twice.
2015-02-14 22:48:28 +00:00
for ( uint_fast32_t i = 1 ; i < count - 1 ; i + = 2 )
{
2006-03-20 17:12:09 +00:00
vms_vector temp1 , temp2 ;
fix dot ;
2014-09-28 21:11:48 +00:00
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 ) ) ;
2006-03-20 17:12:09 +00:00
2014-09-28 21:11:03 +00:00
if ( dot * 9 / 8 > fixmul ( vm_vec_mag ( temp1 ) , vm_vec_mag ( temp2 ) ) )
2013-12-26 22:21:16 +00:00
psegs [ i ] . segnum = segment_none ;
2006-03-20 17:12:09 +00:00
}
// Now, scan for points with segnum == -1
2014-08-08 02:05:52 +00:00
auto predicate = [ ] ( const point_seg & p ) { return p . segnum = = segment_none ; } ;
2015-02-14 22:48:28 +00:00
count = std : : distance ( psegs , std : : remove_if ( psegs , psegs + count , predicate ) ) ;
2013-03-03 01:03:33 +00:00
# endif
2015-02-14 22:48:28 +00:00
return count ;
2006-03-20 17:12:09 +00:00
}
2013-03-03 01:03:33 +00:00
# if defined(DXX_BUILD_DESCENT_II)
2006-03-20 17:12:09 +00:00
// -----------------------------------------------------------------------------------------------------------
// Move points halfway to outside of segment.
2014-10-02 03:02:34 +00:00
static void move_towards_outside ( point_seg * psegs , int * num_points , const vobjptridx_t objp , int rand_flag )
2006-03-20 17:12:09 +00:00
{
int i ;
2015-04-19 04:18:49 +00:00
array < point_seg , 200 > new_psegs ;
2006-03-20 17:12:09 +00:00
Assert ( * num_points < 200 ) ;
for ( i = 1 ; i < * num_points - 1 ; i + + ) {
fix segment_size ;
2013-12-29 04:28:07 +00:00
segnum_t segnum ;
2014-11-02 03:44:12 +00:00
vms_vector e ;
2006-03-20 17:12:09 +00:00
int count ;
2016-01-09 16:38:13 +00:00
const auto & & temp_segnum = find_point_seg ( psegs [ i ] . point , segptridx ( psegs [ i ] . segnum ) ) ;
2013-12-26 22:21:16 +00:00
Assert ( temp_segnum ! = segment_none ) ;
2006-03-20 17:12:09 +00:00
psegs [ i ] . segnum = temp_segnum ;
segnum = psegs [ i ] . segnum ;
// I don't think we can use quick version here and this is _very_ rarely called. --MK, 07/03/95
2014-10-29 03:24:31 +00:00
const auto a = vm_vec_normalized_quick ( vm_vec_sub ( psegs [ i ] . point , psegs [ i - 1 ] . point ) ) ;
const auto b = vm_vec_normalized_quick ( vm_vec_sub ( psegs [ i + 1 ] . point , psegs [ i ] . point ) ) ;
const auto c = vm_vec_sub ( psegs [ i + 1 ] . point , psegs [ i - 1 ] . point ) ;
2014-09-28 21:11:48 +00:00
if ( abs ( vm_vec_dot ( a , b ) ) > 3 * F1_0 / 4 ) {
2006-03-20 17:12:09 +00:00
if ( abs ( a . z ) < F1_0 / 2 ) {
if ( rand_flag ) {
e . x = ( d_rand ( ) - 16384 ) / 2 ;
e . y = ( d_rand ( ) - 16384 ) / 2 ;
e . z = abs ( e . x ) + abs ( e . y ) + 1 ;
2014-09-28 21:11:04 +00:00
vm_vec_normalize_quick ( e ) ;
2006-03-20 17:12:09 +00:00
} else {
e . x = 0 ;
e . y = 0 ;
e . z = F1_0 ;
}
} else {
if ( rand_flag ) {
e . y = ( d_rand ( ) - 16384 ) / 2 ;
e . z = ( d_rand ( ) - 16384 ) / 2 ;
e . x = abs ( e . y ) + abs ( e . z ) + 1 ;
2014-09-28 21:11:04 +00:00
vm_vec_normalize_quick ( e ) ;
2006-03-20 17:12:09 +00:00
} else {
e . x = F1_0 ;
e . y = 0 ;
e . z = 0 ;
}
}
} else {
2014-11-02 03:44:12 +00:00
const auto d = vm_vec_cross ( a , b ) ;
2014-10-01 02:28:42 +00:00
vm_vec_cross ( e , c , d ) ;
2014-09-28 21:11:04 +00:00
vm_vec_normalize_quick ( e ) ;
2006-03-20 17:12:09 +00:00
}
2014-09-28 21:11:03 +00:00
if ( vm_vec_mag_quick ( e ) < F1_0 / 2 )
2006-03-20 17:12:09 +00:00
Int3 ( ) ;
2014-10-01 02:28:41 +00:00
segment_size = vm_vec_dist_quick ( Vertices [ Segments [ segnum ] . verts [ 0 ] ] , Vertices [ Segments [ segnum ] . verts [ 6 ] ] ) ;
2006-03-20 17:12:09 +00:00
if ( segment_size > F1_0 * 40 )
segment_size = F1_0 * 40 ;
2014-11-02 03:43:57 +00:00
auto goal_pos = vm_vec_scale_add ( psegs [ i ] . point , e , segment_size / 4 ) ;
2006-03-20 17:12:09 +00:00
count = 3 ;
while ( count ) {
fvi_query fq ;
fvi_info hit_data ;
int hit_type ;
fq . p0 = & psegs [ i ] . point ;
fq . startseg = psegs [ i ] . segnum ;
fq . p1 = & goal_pos ;
fq . rad = objp - > size ;
2013-12-24 04:53:59 +00:00
fq . thisobjnum = objp ;
2015-02-05 03:03:51 +00:00
fq . ignore_obj_list . first = nullptr ;
2006-03-20 17:12:09 +00:00
fq . flags = 0 ;
2015-01-20 02:46:42 +00:00
hit_type = find_vector_intersection ( fq , hit_data ) ;
2006-03-20 17:12:09 +00:00
if ( hit_type = = HIT_NONE )
count = 0 ;
else {
if ( ( count = = 3 ) & & ( hit_type = = HIT_BAD_P0 ) )
Int3 ( ) ;
goal_pos . x = ( fq . p0 - > x + hit_data . hit_pnt . x ) / 2 ;
goal_pos . y = ( fq . p0 - > y + hit_data . hit_pnt . y ) / 2 ;
goal_pos . z = ( fq . p0 - > z + hit_data . hit_pnt . z ) / 2 ;
count - - ;
if ( count = = 0 ) { // Couldn't move towards outside, that's ok, sometimes things can't be moved.
goal_pos = psegs [ i ] . point ;
}
}
}
// Only move towards outside if remained inside segment.
2016-01-09 16:38:13 +00:00
const auto & & new_segnum = find_point_seg ( goal_pos , segptridx ( psegs [ i ] . segnum ) ) ;
2006-03-20 17:12:09 +00:00
if ( new_segnum = = psegs [ i ] . segnum ) {
new_psegs [ i ] . point = goal_pos ;
new_psegs [ i ] . segnum = new_segnum ;
} else {
new_psegs [ i ] . point = psegs [ i ] . point ;
new_psegs [ i ] . segnum = psegs [ i ] . segnum ;
}
}
for ( i = 1 ; i < * num_points - 1 ; i + + )
psegs [ i ] = new_psegs [ i ] ;
}
2013-03-03 01:03:33 +00:00
# endif
2006-03-20 17:12:09 +00:00
// -----------------------------------------------------------------------------------------------------------
// Create a path from objp->pos to the center of end_seg.
// Return a list of (segment_num, point_locations) at psegs
// Return number of points in *num_points.
// if max_depth == -1, then there is no maximum depth.
// If unable to create path, return -1, else return 0.
// If random_flag !0, then introduce randomness into path by looking at sides in random order. This means
// that a path between two segments won't always be the same, unless it is unique.
// If safety_flag is set, then additional points are added to "make sure" that points are reachable. I would
// like to say that it ensures that the object can move between the points, but that would require knowing what
// the object is (which isn't passed, right?) and making fvi calls (slow, right?). So, consider it the more_or_less_safe_flag.
// 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).
2014-10-02 03:02:34 +00:00
int create_path_points ( const vobjptridx_t objp , segnum_t start_seg , segnum_t end_seg , point_seg_array_t : : iterator psegs , short * num_points , int max_depth , int random_flag , int safety_flag , segnum_t avoid_seg )
2006-03-20 17:12:09 +00:00
{
2013-12-29 04:28:07 +00:00
segnum_t cur_seg ;
2006-03-20 17:12:09 +00:00
int sidenum ;
int qtail = 0 , qhead = 0 ;
int i ;
seg_seg seg_queue [ MAX_SEGMENTS ] ;
short depth [ MAX_SEGMENTS ] ;
int cur_depth ;
2015-02-14 22:48:28 +00:00
array < uint8_t , MAX_SIDES_PER_SEGMENT > random_xlate ;
2013-12-28 18:47:17 +00:00
point_seg_array_t : : iterator original_psegs = psegs ;
2006-03-20 17:12:09 +00:00
int l_num_points ;
# if PATH_VALIDATION
validate_all_paths ( ) ;
# endif
2015-04-02 02:36:57 +00:00
if ( ( objp - > type = = OBJ_ROBOT ) & & ( objp - > ctype . ai_info . behavior = = ai_behavior : : AIB_RUN_FROM ) ) {
2006-03-20 17:12:09 +00:00
random_flag = 1 ;
avoid_seg = ConsoleObject - > segnum ;
// Int3();
}
if ( max_depth = = - 1 )
max_depth = MAX_PATH_LENGTH ;
l_num_points = 0 ;
// for (i=0; i<=Highest_segment_index; i++) {
// depth[i] = 0;
// }
2013-12-18 23:54:37 +00:00
visited_segment_bitarray_t visited ;
2006-03-20 17:12:09 +00:00
memset ( depth , 0 , sizeof ( depth [ 0 ] ) * ( Highest_segment_index + 1 ) ) ;
// If there is a segment we're not allowed to visit, mark it.
2013-12-26 22:21:16 +00:00
if ( avoid_seg ! = segment_none ) {
2006-03-20 17:12:09 +00:00
Assert ( avoid_seg < = Highest_segment_index ) ;
if ( ( start_seg ! = avoid_seg ) & & ( end_seg ! = avoid_seg ) ) {
2013-12-18 23:54:37 +00:00
visited [ avoid_seg ] = true ;
2006-03-20 17:12:09 +00:00
depth [ avoid_seg ] = 0 ;
2008-04-06 20:23:28 +00:00
}
2006-03-20 17:12:09 +00:00
}
if ( random_flag )
create_random_xlate ( random_xlate ) ;
cur_seg = start_seg ;
2013-12-18 23:54:37 +00:00
visited [ cur_seg ] = true ;
2006-03-20 17:12:09 +00:00
cur_depth = 0 ;
while ( cur_seg ! = end_seg ) {
2015-07-12 01:04:19 +00:00
const auto & & segp = vcsegptr ( cur_seg ) ;
2013-03-03 01:03:33 +00:00
# if defined(DXX_BUILD_DESCENT_II)
2006-03-20 17:12:09 +00:00
if ( random_flag )
if ( d_rand ( ) < 8192 )
create_random_xlate ( random_xlate ) ;
2013-03-03 01:03:33 +00:00
# endif
2006-03-20 17:12:09 +00:00
for ( sidenum = 0 ; sidenum < MAX_SIDES_PER_SEGMENT ; sidenum + + ) {
int snum = sidenum ;
if ( random_flag )
snum = random_xlate [ sidenum ] ;
2013-03-03 01:03:33 +00:00
# if defined(DXX_BUILD_DESCENT_I)
if ( ( WALL_IS_DOORWAY ( segp , snum ) & WID_FLY_FLAG ) | | ( ai_door_is_openable ( objp , segp , snum ) ) )
# elif defined(DXX_BUILD_DESCENT_II)
2016-10-02 00:34:42 +00:00
auto & player_info = get_local_plrobj ( ) . ctype . player_info ;
if ( IS_CHILD ( segp - > children [ snum ] ) & & ( ( WALL_IS_DOORWAY ( segp , snum ) & WID_FLY_FLAG ) | | ( ai_door_is_openable ( objp , player_info . powerup_flags , segp , snum ) ) ) )
2013-03-03 01:03:33 +00:00
# endif
{
2014-11-20 03:00:36 +00:00
auto this_seg = segp - > children [ snum ] ;
2013-03-03 01:03:33 +00:00
# if defined(DXX_BUILD_DESCENT_II)
2013-12-26 22:21:16 +00:00
Assert ( this_seg ! = segment_none ) ;
2006-03-20 17:12:09 +00:00
if ( ( ( cur_seg = = avoid_seg ) | | ( this_seg = = avoid_seg ) ) & & ( ConsoleObject - > segnum = = avoid_seg ) ) {
fvi_query fq ;
fvi_info hit_data ;
int hit_type ;
2014-10-30 03:32:27 +00:00
const auto center_point = compute_center_point_on_side ( segp , snum ) ;
2006-03-20 17:12:09 +00:00
fq . p0 = & objp - > pos ;
fq . startseg = objp - > segnum ;
fq . p1 = & center_point ;
fq . rad = objp - > size ;
2014-01-11 22:55:21 +00:00
fq . thisobjnum = objp ;
2015-02-05 03:03:51 +00:00
fq . ignore_obj_list . first = nullptr ;
2006-03-20 17:12:09 +00:00
fq . flags = 0 ;
2015-01-20 02:46:42 +00:00
hit_type = find_vector_intersection ( fq , hit_data ) ;
2006-03-20 17:12:09 +00:00
if ( hit_type ! = HIT_NONE ) {
goto dont_add ;
}
}
2013-03-03 01:03:33 +00:00
# endif
2006-03-20 17:12:09 +00:00
if ( ! visited [ this_seg ] ) {
seg_queue [ qtail ] . start = cur_seg ;
seg_queue [ qtail ] . end = this_seg ;
2013-12-18 23:54:37 +00:00
visited [ this_seg ] = true ;
2006-03-20 17:12:09 +00:00
depth [ qtail + + ] = cur_depth + 1 ;
if ( depth [ qtail - 1 ] = = max_depth ) {
end_seg = seg_queue [ qtail - 1 ] . end ;
goto cpp_done1 ;
} // end if (depth[...
} // end if (!visited...
} // if (WALL_IS_DOORWAY(...
2013-03-03 01:03:33 +00:00
# if defined(DXX_BUILD_DESCENT_II)
2006-03-20 17:12:09 +00:00
dont_add : ;
2013-03-03 01:03:33 +00:00
# endif
2006-03-20 17:12:09 +00:00
} // for (sidenum...
2008-03-08 22:37:09 +00:00
if ( qtail < = 0 )
break ;
2007-11-23 21:14:57 +00:00
2006-03-20 17:12:09 +00:00
if ( qhead > = qtail ) {
// Couldn't get to goal, return a path as far as we got, which probably acceptable to the unparticular caller.
end_seg = seg_queue [ qtail - 1 ] . end ;
break ;
}
cur_seg = seg_queue [ qhead ] . end ;
cur_depth = depth [ qhead ] ;
qhead + + ;
cpp_done1 : ;
} // while (cur_seg ...
2008-03-08 22:37:09 +00:00
if ( qtail > 0 )
{
// Set qtail to the segment which ends at the goal.
while ( seg_queue [ - - qtail ] . end ! = end_seg )
if ( qtail < 0 ) {
* num_points = l_num_points ;
return - 1 ;
}
}
else
qtail = - 1 ;
2006-03-20 17:12:09 +00:00
2013-03-03 01:03:33 +00:00
# if defined(DXX_BUILD_DESCENT_I)
2016-09-11 18:49:16 +00:00
# if DXX_USE_EDITOR
2013-12-11 23:59:36 +00:00
Selected_segs . clear ( ) ;
2006-03-20 17:12:09 +00:00
# endif
2013-03-03 01:03:33 +00:00
# endif
2008-04-06 20:23:28 +00:00
2006-03-20 17:12:09 +00:00
while ( qtail > = 0 ) {
2013-12-29 04:28:07 +00:00
segnum_t parent_seg , this_seg ;
2006-03-20 17:12:09 +00:00
this_seg = seg_queue [ qtail ] . end ;
parent_seg = seg_queue [ qtail ] . start ;
psegs - > segnum = this_seg ;
2015-05-28 03:08:39 +00:00
compute_segment_center ( psegs - > point , vcsegptr ( this_seg ) ) ;
2006-03-20 17:12:09 +00:00
psegs + + ;
l_num_points + + ;
2013-03-03 01:03:33 +00:00
# if defined(DXX_BUILD_DESCENT_I)
2016-09-11 18:49:16 +00:00
# if DXX_USE_EDITOR
2013-12-11 23:59:36 +00:00
Selected_segs . emplace_back ( this_seg ) ;
2006-03-20 17:12:09 +00:00
# endif
2013-03-03 01:03:33 +00:00
# endif
2006-03-20 17:12:09 +00:00
if ( parent_seg = = start_seg )
break ;
while ( seg_queue [ - - qtail ] . end ! = parent_seg )
Assert ( qtail > = 0 ) ;
}
psegs - > segnum = start_seg ;
2015-05-28 03:08:39 +00:00
compute_segment_center ( psegs - > point , vcsegptr ( start_seg ) ) ;
2006-03-20 17:12:09 +00:00
psegs + + ;
l_num_points + + ;
# if PATH_VALIDATION
validate_path ( 1 , original_psegs , l_num_points ) ;
# endif
// Now, reverse point_segs in place.
for ( i = 0 ; i < l_num_points / 2 ; i + + ) {
point_seg temp_point_seg = * ( original_psegs + i ) ;
* ( original_psegs + i ) = * ( original_psegs + l_num_points - i - 1 ) ;
* ( original_psegs + l_num_points - i - 1 ) = temp_point_seg ;
}
# if PATH_VALIDATION
validate_path ( 2 , original_psegs , l_num_points ) ;
# endif
// Now, if safety_flag set, then insert the point at the center of the side connecting two segments
// between the two points. This is messy because we must insert into the list. The simplest (and not too slow)
// way to do this is to start at the end of the list and go backwards.
if ( safety_flag ) {
if ( psegs - Point_segs + l_num_points + 2 > MAX_POINT_SEGS ) {
// Ouch! Cannot insert center points in path. So return unsafe path.
ai_reset_all_paths ( ) ;
* num_points = l_num_points ;
return - 1 ;
} else {
2015-02-14 22:48:28 +00:00
l_num_points = insert_center_points ( original_psegs , l_num_points ) ;
2006-03-20 17:12:09 +00:00
}
}
# if PATH_VALIDATION
validate_path ( 3 , original_psegs , l_num_points ) ;
# endif
2013-03-03 01:03:33 +00:00
# if defined(DXX_BUILD_DESCENT_II)
2006-03-20 17:12:09 +00:00
// -- MK, 10/30/95 -- This code causes apparent discontinuities in the path, moving a point
// into a new segment. It is not necessarily bad, but it makes it hard to track down actual
// discontinuity problems.
if ( objp - > type = = OBJ_ROBOT )
2013-10-07 23:52:33 +00:00
if ( Robot_info [ get_robot_id ( objp ) ] . companion )
2006-03-20 17:12:09 +00:00
move_towards_outside ( original_psegs , & l_num_points , objp , 0 ) ;
2013-03-03 01:03:33 +00:00
# endif
2006-03-20 17:12:09 +00:00
# if PATH_VALIDATION
validate_path ( 4 , original_psegs , l_num_points ) ;
# endif
* num_points = l_num_points ;
return 0 ;
}
2013-03-03 01:03:33 +00:00
# if defined(DXX_BUILD_DESCENT_II)
2012-05-14 17:06:28 +00:00
int Last_buddy_polish_path_tick ;
2006-03-20 17:12:09 +00:00
// -------------------------------------------------------------------------------------------------------
// polish_path
// Takes an existing path and makes it nicer.
// Drops as many leading points as possible still maintaining direct accessibility
// from current position to first point.
// Will not shorten path to fewer than 3 points.
// Returns number of points.
// Starting position in psegs doesn't change.
// Changed, MK, 10/18/95. I think this was causing robots to get hung up on walls.
// Only drop up to the first three points.
2014-10-02 03:02:34 +00:00
int polish_path ( const vobjptridx_t objp , point_seg * psegs , int num_points )
2006-03-20 17:12:09 +00:00
{
int i , first_point = 0 ;
if ( num_points < = 4 )
return num_points ;
2012-05-14 17:06:28 +00:00
// Prevent the buddy from polishing his path twice in one tick, which can cause him to get hung up. Pretty ugly, huh?
2013-10-07 23:52:33 +00:00
if ( Robot_info [ get_robot_id ( objp ) ] . companion )
2006-03-20 17:12:09 +00:00
{
2012-05-14 17:06:28 +00:00
if ( d_tick_count = = Last_buddy_polish_path_tick )
2006-03-20 17:12:09 +00:00
return num_points ;
else
2012-05-14 17:06:28 +00:00
Last_buddy_polish_path_tick = d_tick_count ;
2006-03-20 17:12:09 +00:00
}
2013-03-03 01:03:33 +00:00
// -- MK: 10/18/95: for (i=0; i<num_points-3; i++)
2006-03-20 17:12:09 +00:00
for ( i = 0 ; i < 2 ; i + + ) {
fvi_query fq ;
fvi_info hit_data ;
int hit_type ;
fq . p0 = & objp - > pos ;
fq . startseg = objp - > segnum ;
fq . p1 = & psegs [ i ] . point ;
fq . rad = objp - > size ;
2013-12-24 04:53:59 +00:00
fq . thisobjnum = objp ;
2015-02-05 03:03:51 +00:00
fq . ignore_obj_list . first = nullptr ;
2006-03-20 17:12:09 +00:00
fq . flags = 0 ;
2015-01-20 02:46:42 +00:00
hit_type = find_vector_intersection ( fq , hit_data ) ;
2006-03-20 17:12:09 +00:00
if ( hit_type = = HIT_NONE )
first_point = i + 1 ;
else
break ;
}
if ( first_point ) {
// Scrunch down all the psegs.
for ( i = first_point ; i < num_points ; i + + )
psegs [ i - first_point ] = psegs [ i ] ;
}
return num_points - first_point ;
}
2013-03-03 01:03:33 +00:00
# endif
2006-03-20 17:12:09 +00:00
# ifndef NDEBUG
// -------------------------------------------------------------------------------------------------------
// Make sure that there are connections between all segments on path.
// Note that if path has been optimized, connections may not be direct, so this function is useless, or worse.
// Return true if valid, else return false.
2015-10-13 02:43:24 +00:00
int validate_path ( int , point_seg * psegs , uint_fast32_t num_points )
2006-03-20 17:12:09 +00:00
{
# if PATH_VALIDATION
2015-10-13 02:43:24 +00:00
auto curseg = psegs - > segnum ;
if ( curseg > Highest_segment_index )
{
2006-03-20 17:12:09 +00:00
Int3 ( ) ; // Contact Mike: Debug trap for elusive, nasty bug.
return 0 ;
}
2015-10-13 02:43:24 +00:00
range_for ( const auto & ps , unchecked_partial_range ( psegs , 1u , num_points ) )
{
auto nextseg = ps . segnum ;
2006-03-20 17:12:09 +00:00
if ( curseg ! = nextseg ) {
2015-10-13 02:43:24 +00:00
const auto & & csegp = vcsegptr ( curseg ) ;
const auto & children = csegp - > children ;
if ( std : : find ( children . begin ( ) , children . end ( ) , nextseg ) = = children . end ( ) )
{
2006-03-20 17:12:09 +00:00
// Assert(sidenum != MAX_SIDES_PER_SEGMENT); // Hey, created path is not contiguous, why!?
Int3 ( ) ;
return 0 ;
}
curseg = nextseg ;
}
}
# endif
return 1 ;
}
// -----------------------------------------------------------------------------------------------------------
void validate_all_paths ( void )
{
# if PATH_VALIDATION
2016-02-12 04:02:28 +00:00
range_for ( const auto & & objp , vobjptr )
2014-10-12 23:05:46 +00:00
{
2015-06-13 22:42:16 +00:00
if ( objp - > type = = OBJ_ROBOT ) {
2006-03-20 17:12:09 +00:00
ai_static * aip = & objp - > ctype . ai_info ;
if ( objp - > control_type = = CT_AI ) {
if ( ( aip - > hide_index ! = - 1 ) & & ( aip - > path_length > 0 ) )
if ( ! validate_path ( 4 , & Point_segs [ aip - > hide_index ] , aip - > path_length ) ) {
Int3 ( ) ; // This path is bogus! Who corrupted it! Danger! Danger!
// Contact Mike, he caused this mess.
//force_dump_ai_objects_all("Error in validate_all_paths");
aip - > path_length = 0 ; // This allows people to resume without harm...
}
}
}
}
# endif
}
# endif
// -------------------------------------------------------------------------------------------------------
// Creates a path from the objects current segment (objp->segnum) to the specified segment for the object to
// hide in Ai_local_info[objnum].goal_segment.
// Sets objp->ctype.ai_info.hide_index, a pointer into Point_segs, the first point_seg of the path.
// objp->ctype.ai_info.path_length, length of path
// Point_segs_free_ptr global pointer into Point_segs array
// Change, 10/07/95: Used to create path to ConsoleObject->pos. Now creates path to Believed_player_pos.
2014-10-02 03:02:34 +00:00
void create_path_to_player ( const vobjptridx_t objp , int max_length , int safety_flag )
2006-03-20 17:12:09 +00:00
{
ai_static * aip = & objp - > ctype . ai_info ;
2013-12-25 03:16:41 +00:00
ai_local * ailp = & objp - > ctype . ai_info . ail ;
2006-03-20 17:12:09 +00:00
if ( max_length = = - 1 )
max_length = MAX_DEPTH_TO_SEARCH_FOR_PLAYER ;
2010-12-22 00:17:59 +00:00
ailp - > time_player_seen = GameTime64 ; // Prevent from resetting path quickly.
2013-03-03 01:03:33 +00:00
# if defined(DXX_BUILD_DESCENT_I)
ailp - > goal_segment = ConsoleObject - > segnum ;
# elif defined(DXX_BUILD_DESCENT_II)
2006-03-20 17:12:09 +00:00
ailp - > goal_segment = Believed_player_seg ;
2013-03-03 01:03:33 +00:00
# endif
2006-03-20 17:12:09 +00:00
2013-12-29 04:28:07 +00:00
segnum_t start_seg , end_seg ;
2006-03-20 17:12:09 +00:00
start_seg = objp - > segnum ;
end_seg = ailp - > goal_segment ;
2013-12-26 22:21:16 +00:00
if ( end_seg = = segment_none ) {
2008-04-06 20:23:28 +00:00
;
2006-03-20 17:12:09 +00:00
} else {
2013-12-26 22:21:16 +00:00
create_path_points ( objp , start_seg , end_seg , Point_segs_free_ptr , & aip - > path_length , max_length , 1 , safety_flag , segment_none ) ;
2013-03-03 01:03:33 +00:00
# if defined(DXX_BUILD_DESCENT_II)
2006-03-20 17:12:09 +00:00
aip - > path_length = polish_path ( objp , Point_segs_free_ptr , aip - > path_length ) ;
2013-03-03 01:03:33 +00:00
# endif
2006-03-20 17:12:09 +00:00
aip - > hide_index = Point_segs_free_ptr - Point_segs ;
aip - > cur_path_index = 0 ;
2013-03-03 01:03:33 +00:00
# if defined(DXX_BUILD_DESCENT_I)
# ifndef NDEBUG
validate_path ( 6 , Point_segs_free_ptr , aip - > path_length ) ;
# endif
# endif
2006-03-20 17:12:09 +00:00
Point_segs_free_ptr + = aip - > path_length ;
if ( Point_segs_free_ptr - Point_segs + MAX_PATH_LENGTH * 2 > MAX_POINT_SEGS ) {
//Int3(); // Contact Mike: This is stupid. Should call maybe_ai_garbage_collect before the add.
//force_dump_ai_objects_all("Error in create_path_to_player");
ai_reset_all_paths ( ) ;
return ;
}
// Assert(Point_segs_free_ptr - Point_segs + MAX_PATH_LENGTH*2 < MAX_POINT_SEGS);
aip - > PATH_DIR = 1 ; // Initialize to moving forward.
2013-03-03 01:03:33 +00:00
# if defined(DXX_BUILD_DESCENT_I)
aip - > SUBMODE = AISM_GOHIDE ; // This forces immediate movement.
# endif
2015-04-26 20:15:51 +00:00
ailp - > mode = ai_mode : : AIM_FOLLOW_PATH ;
2015-04-02 02:36:53 +00:00
ailp - > player_awareness_type = player_awareness_type_t : : PA_NONE ; // If robot too aware of player, will set mode to chase
2006-03-20 17:12:09 +00:00
}
maybe_ai_path_garbage_collect ( ) ;
}
2013-03-03 01:03:33 +00:00
# if defined(DXX_BUILD_DESCENT_II)
2006-03-20 17:12:09 +00:00
// -------------------------------------------------------------------------------------------------------
// Creates a path from the object's current segment (objp->segnum) to segment goalseg.
2014-10-02 03:02:34 +00:00
void create_path_to_segment ( const vobjptridx_t objp , segnum_t goalseg , int max_length , int safety_flag )
2006-03-20 17:12:09 +00:00
{
ai_static * aip = & objp - > ctype . ai_info ;
2013-12-25 03:16:41 +00:00
ai_local * ailp = & objp - > ctype . ai_info . ail ;
2006-03-20 17:12:09 +00:00
if ( max_length = = - 1 )
max_length = MAX_DEPTH_TO_SEARCH_FOR_PLAYER ;
2010-12-22 00:17:59 +00:00
ailp - > time_player_seen = GameTime64 ; // Prevent from resetting path quickly.
2006-03-20 17:12:09 +00:00
ailp - > goal_segment = goalseg ;
2013-12-29 04:28:07 +00:00
segnum_t start_seg , end_seg ;
2006-03-20 17:12:09 +00:00
start_seg = objp - > segnum ;
end_seg = ailp - > goal_segment ;
2013-12-26 22:21:16 +00:00
if ( end_seg = = segment_none ) {
2006-03-20 17:12:09 +00:00
;
} else {
2013-12-26 22:21:16 +00:00
create_path_points ( objp , start_seg , end_seg , Point_segs_free_ptr , & aip - > path_length , max_length , 1 , safety_flag , segment_none ) ;
2006-03-20 17:12:09 +00:00
aip - > hide_index = Point_segs_free_ptr - Point_segs ;
aip - > cur_path_index = 0 ;
Point_segs_free_ptr + = aip - > path_length ;
if ( Point_segs_free_ptr - Point_segs + MAX_PATH_LENGTH * 2 > MAX_POINT_SEGS ) {
ai_reset_all_paths ( ) ;
return ;
}
aip - > PATH_DIR = 1 ; // Initialize to moving forward.
// -- UNUSED! aip->SUBMODE = AISM_GOHIDE; // This forces immediate movement.
2015-04-02 02:36:53 +00:00
ailp - > player_awareness_type = player_awareness_type_t : : PA_NONE ; // If robot too aware of player, will set mode to chase
2006-03-20 17:12:09 +00:00
}
maybe_ai_path_garbage_collect ( ) ;
}
2013-03-03 01:03:33 +00:00
# endif
2006-03-20 17:12:09 +00:00
// -------------------------------------------------------------------------------------------------------
// Creates a path from the objects current segment (objp->segnum) to the specified segment for the object to
// hide in Ai_local_info[objnum].goal_segment
// Sets objp->ctype.ai_info.hide_index, a pointer into Point_segs, the first point_seg of the path.
// objp->ctype.ai_info.path_length, length of path
// Point_segs_free_ptr global pointer into Point_segs array
2014-10-02 03:02:34 +00:00
void create_path_to_station ( const vobjptridx_t objp , int max_length )
2006-03-20 17:12:09 +00:00
{
ai_static * aip = & objp - > ctype . ai_info ;
2013-12-25 03:16:41 +00:00
ai_local * ailp = & objp - > ctype . ai_info . ail ;
2006-03-20 17:12:09 +00:00
if ( max_length = = - 1 )
max_length = MAX_DEPTH_TO_SEARCH_FOR_PLAYER ;
2010-12-22 00:17:59 +00:00
ailp - > time_player_seen = GameTime64 ; // Prevent from resetting path quickly.
2006-03-20 17:12:09 +00:00
2013-12-29 04:28:07 +00:00
segnum_t start_seg , end_seg ;
2006-03-20 17:12:09 +00:00
start_seg = objp - > segnum ;
end_seg = aip - > hide_segment ;
2013-12-26 22:21:16 +00:00
if ( end_seg = = segment_none ) {
2008-04-06 20:23:28 +00:00
;
2006-03-20 17:12:09 +00:00
} else {
2013-12-26 22:21:16 +00:00
create_path_points ( objp , start_seg , end_seg , Point_segs_free_ptr , & aip - > path_length , max_length , 1 , 1 , segment_none ) ;
2013-03-03 01:03:33 +00:00
# if defined(DXX_BUILD_DESCENT_II)
2006-03-20 17:12:09 +00:00
aip - > path_length = polish_path ( objp , Point_segs_free_ptr , aip - > path_length ) ;
2013-03-03 01:03:33 +00:00
# endif
2006-03-20 17:12:09 +00:00
aip - > hide_index = Point_segs_free_ptr - Point_segs ;
aip - > cur_path_index = 0 ;
2013-03-03 01:03:33 +00:00
# if defined(DXX_BUILD_DESCENT_I)
# ifndef NDEBUG
validate_path ( 7 , Point_segs_free_ptr , aip - > path_length ) ;
# endif
# endif
2006-03-20 17:12:09 +00:00
Point_segs_free_ptr + = aip - > path_length ;
if ( Point_segs_free_ptr - Point_segs + MAX_PATH_LENGTH * 2 > MAX_POINT_SEGS ) {
//Int3(); // Contact Mike: Stupid.
//force_dump_ai_objects_all("Error in create_path_to_station");
ai_reset_all_paths ( ) ;
return ;
}
// Assert(Point_segs_free_ptr - Point_segs + MAX_PATH_LENGTH*2 < MAX_POINT_SEGS);
aip - > PATH_DIR = 1 ; // Initialize to moving forward.
// aip->SUBMODE = AISM_GOHIDE; // This forces immediate movement.
2015-04-26 20:15:51 +00:00
ailp - > mode = ai_mode : : AIM_FOLLOW_PATH ;
2015-04-02 02:36:53 +00:00
ailp - > player_awareness_type = player_awareness_type_t : : PA_NONE ;
2006-03-20 17:12:09 +00:00
}
maybe_ai_path_garbage_collect ( ) ;
}
// -------------------------------------------------------------------------------------------------------
// Create a path of length path_length for an object, stuffing info in ai_info field.
2014-10-02 03:02:34 +00:00
void create_n_segment_path ( const vobjptridx_t objp , int path_length , segnum_t avoid_seg )
2006-03-20 17:12:09 +00:00
{
ai_static * aip = & objp - > ctype . ai_info ;
2013-12-25 03:16:41 +00:00
ai_local * ailp = & objp - > ctype . ai_info . ail ;
2006-03-20 17:12:09 +00:00
2013-12-26 22:21:16 +00:00
if ( create_path_points ( objp , objp - > segnum , segment_exit , Point_segs_free_ptr , & aip - > path_length , path_length , 1 , 0 , avoid_seg ) = = - 1 ) {
2006-03-20 17:12:09 +00:00
Point_segs_free_ptr + = aip - > path_length ;
2013-12-26 22:21:16 +00:00
while ( ( create_path_points ( objp , objp - > segnum , segment_exit , Point_segs_free_ptr , & aip - > path_length , - - path_length , 1 , 0 , segment_none ) = = - 1 ) ) {
2006-03-20 17:12:09 +00:00
Assert ( path_length ) ;
}
}
aip - > hide_index = Point_segs_free_ptr - Point_segs ;
aip - > cur_path_index = 0 ;
# if PATH_VALIDATION
validate_path ( 8 , Point_segs_free_ptr , aip - > path_length ) ;
# endif
Point_segs_free_ptr + = aip - > path_length ;
if ( Point_segs_free_ptr - Point_segs + MAX_PATH_LENGTH * 2 > MAX_POINT_SEGS ) {
//Int3(); // Contact Mike: This is curious, though not deadly. /eip++;g
//force_dump_ai_objects_all("Error in crete_n_segment_path 2");
ai_reset_all_paths ( ) ;
}
aip - > PATH_DIR = 1 ; // Initialize to moving forward.
2013-03-03 01:03:33 +00:00
# if defined(DXX_BUILD_DESCENT_I)
aip - > SUBMODE = - 1 ; // Don't know what this means.
# endif
2015-04-26 20:15:51 +00:00
ailp - > mode = ai_mode : : AIM_FOLLOW_PATH ;
2006-03-20 17:12:09 +00:00
2013-03-03 01:03:33 +00:00
# if defined(DXX_BUILD_DESCENT_II)
2006-03-20 17:12:09 +00:00
// If this robot is visible (player_visibility is not available) and it's running away, move towards outside with
// randomness to prevent a stream of bots from going away down the center of a corridor.
2013-12-25 03:16:41 +00:00
if ( ailp - > previous_visibility ) {
2006-03-20 17:12:09 +00:00
if ( aip - > path_length ) {
int t_num_points = aip - > path_length ;
move_towards_outside ( & Point_segs [ aip - > hide_index ] , & t_num_points , objp , 1 ) ;
aip - > path_length = t_num_points ;
}
}
2013-03-03 01:03:33 +00:00
# endif
2006-03-20 17:12:09 +00:00
maybe_ai_path_garbage_collect ( ) ;
}
// -------------------------------------------------------------------------------------------------------
2014-10-02 03:02:34 +00:00
void create_n_segment_path_to_door ( const vobjptridx_t objp , int path_length , segnum_t avoid_seg )
2006-03-20 17:12:09 +00:00
{
create_n_segment_path ( objp , path_length , avoid_seg ) ;
}
# define Int3_if(cond) if (!cond) Int3();
// -- too much work -- // ----------------------------------------------------------------------------------------------------------
// -- too much work -- // Return true if the object the companion wants to kill is reachable.
// -- too much work -- int attack_kill_object(object *objp)
// -- too much work -- {
// -- too much work -- object *kill_objp;
// -- too much work -- fvi_info hit_data;
// -- too much work -- int fate;
// -- too much work -- fvi_query fq;
// -- too much work --
// -- too much work -- if (Escort_kill_object == -1)
// -- too much work -- return 0;
// -- too much work --
// -- too much work -- kill_objp = &Objects[Escort_kill_object];
// -- too much work --
// -- too much work -- fq.p0 = &objp->pos;
// -- too much work -- fq.startseg = objp->segnum;
// -- too much work -- fq.p1 = &kill_objp->pos;
// -- too much work -- fq.rad = objp->size;
// -- too much work -- fq.thisobjnum = objp-Objects;
// -- too much work -- fq.ignore_obj_list = NULL;
// -- too much work -- fq.flags = 0;
// -- too much work --
// -- too much work -- fate = find_vector_intersection(&fq,&hit_data);
// -- too much work --
// -- too much work -- if (fate == HIT_NONE)
// -- too much work -- return 1;
// -- too much work -- else
// -- too much work -- return 0;
// -- too much work -- }
2013-03-03 01:03:33 +00:00
// -------------------------------------------------------------------------------------------------------
// Creates a path from the objects current segment (objp->segnum) to the specified segment for the object to
// hide in Ai_local_info[objnum].goal_segment.
// Sets objp->ctype.ai_info.hide_index, a pointer into Point_segs, the first point_seg of the path.
// objp->ctype.ai_info.path_length, length of path
// Point_segs_free_ptr global pointer into Point_segs array
# if defined(DXX_BUILD_DESCENT_I)
2014-10-02 03:02:34 +00:00
static void create_path ( const vobjptridx_t objp )
2013-03-03 01:03:33 +00:00
{
ai_static * aip = & objp - > ctype . ai_info ;
2013-12-25 03:16:41 +00:00
ai_local * ailp = & objp - > ctype . ai_info . ail ;
2013-12-29 04:28:07 +00:00
segnum_t start_seg , end_seg ;
2013-03-03 01:03:33 +00:00
start_seg = objp - > segnum ;
end_seg = ailp - > goal_segment ;
2013-12-26 22:21:16 +00:00
if ( end_seg = = segment_none )
create_n_segment_path ( objp , 3 , segment_none ) ;
2013-03-03 01:03:33 +00:00
2013-12-26 22:21:16 +00:00
if ( end_seg = = segment_none ) {
2013-03-03 01:03:33 +00:00
;
} else {
2013-12-26 22:21:16 +00:00
create_path_points ( objp , start_seg , end_seg , Point_segs_free_ptr , & aip - > path_length , - 1 , 0 , 0 , segment_none ) ;
2013-03-03 01:03:33 +00:00
aip - > hide_index = Point_segs_free_ptr - Point_segs ;
aip - > cur_path_index = 0 ;
# ifndef NDEBUG
validate_path ( 5 , Point_segs_free_ptr , aip - > path_length ) ;
# endif
Point_segs_free_ptr + = aip - > path_length ;
if ( Point_segs_free_ptr - Point_segs + MAX_PATH_LENGTH * 2 > MAX_POINT_SEGS ) {
//Int3(); // Contact Mike: This is curious, though not deadly. /eip++;g
//force_dump_ai_objects_all("Error in create_path");
ai_reset_all_paths ( ) ;
}
aip - > PATH_DIR = 1 ; // Initialize to moving forward.
aip - > SUBMODE = AISM_HIDING ; // Pretend we are hiding, so we sit here until bothered.
}
maybe_ai_path_garbage_collect ( ) ;
}
# endif
2006-03-20 17:12:09 +00:00
// ----------------------------------------------------------------------------------------------------------
// Optimization: If current velocity will take robot near goal, don't change velocity
2014-10-02 03:02:34 +00:00
void ai_follow_path ( const vobjptridx_t objp , int player_visibility , const vms_vector * vec_to_player )
2006-03-20 17:12:09 +00:00
{
2015-01-18 01:58:33 +00:00
# if defined(DXX_BUILD_DESCENT_I)
( void ) vec_to_player ;
# endif
2006-03-20 17:12:09 +00:00
ai_static * aip = & objp - > ctype . ai_info ;
vms_vector goal_point , new_goal_point ;
2013-03-03 01:03:33 +00:00
# if defined(DXX_BUILD_DESCENT_II)
2013-10-07 23:52:33 +00:00
robot_info * robptr = & Robot_info [ get_robot_id ( objp ) ] ;
2013-03-03 01:03:33 +00:00
# endif
2006-03-20 17:12:09 +00:00
int forced_break , original_dir , original_index ;
2013-12-25 03:16:41 +00:00
ai_local * ailp = & objp - > ctype . ai_info . ail ;
2006-03-20 17:12:09 +00:00
if ( ( aip - > hide_index = = - 1 ) | | ( aip - > path_length = = 0 ) )
{
2015-04-26 20:15:51 +00:00
if ( ailp - > mode = = ai_mode : : AIM_RUN_FROM_OBJECT ) {
2013-12-26 22:21:16 +00:00
create_n_segment_path ( objp , 5 , segment_none ) ;
2006-03-20 17:12:09 +00:00
//--Int3_if((aip->path_length != 0));
2015-04-26 20:15:51 +00:00
ailp - > mode = ai_mode : : AIM_RUN_FROM_OBJECT ;
2006-03-20 17:12:09 +00:00
} else {
2013-03-03 01:03:33 +00:00
# if defined(DXX_BUILD_DESCENT_I)
create_path ( objp ) ;
# elif defined(DXX_BUILD_DESCENT_II)
2013-12-26 22:21:16 +00:00
create_n_segment_path ( objp , 5 , segment_none ) ;
2013-03-03 01:03:33 +00:00
# endif
2006-03-20 17:12:09 +00:00
//--Int3_if((aip->path_length != 0));
}
}
if ( ( aip - > hide_index + aip - > path_length > Point_segs_free_ptr - Point_segs ) & & ( aip - > path_length > 0 ) ) {
2013-03-03 01:03:33 +00:00
# if defined(DXX_BUILD_DESCENT_II)
2006-03-20 17:12:09 +00:00
Int3 ( ) ; // Contact Mike: Bad. Path goes into what is believed to be free space.
// This is debugging code. Figure out why garbage collection
// didn't compress this object's path information.
ai_path_garbage_collect ( ) ;
2013-03-03 01:03:33 +00:00
# endif
2006-03-20 17:12:09 +00:00
ai_reset_all_paths ( ) ;
}
if ( aip - > path_length < 2 ) {
2013-03-03 01:03:33 +00:00
# if defined(DXX_BUILD_DESCENT_I)
2015-04-26 20:15:51 +00:00
if ( ailp - > mode = = ai_mode : : AIM_RUN_FROM_OBJECT )
2013-03-03 01:03:33 +00:00
# elif defined(DXX_BUILD_DESCENT_II)
2015-04-26 20:15:51 +00:00
if ( ( aip - > behavior = = ai_behavior : : AIB_SNIPE ) | | ( ailp - > mode = = ai_mode : : AIM_RUN_FROM_OBJECT ) )
2013-03-03 01:03:33 +00:00
# endif
{
2016-07-09 17:58:35 +00:00
create_n_segment_path ( objp , AVOID_SEG_LENGTH , ConsoleObject - > segnum = = objp - > segnum ? segment_none : ConsoleObject - > segnum ) ; // Can't avoid segment player is in, robot is already in it! (That's what the -1 is for)
2006-03-20 17:12:09 +00:00
//--Int3_if((aip->path_length != 0));
2013-03-03 01:03:33 +00:00
# if defined(DXX_BUILD_DESCENT_II)
2015-04-02 02:36:57 +00:00
if ( aip - > behavior = = ai_behavior : : AIB_SNIPE ) {
2013-11-03 22:27:28 +00:00
if ( robot_is_thief ( robptr ) )
2015-04-26 20:15:51 +00:00
ailp - > mode = ai_mode : : AIM_THIEF_ATTACK ; // It gets bashed in create_n_segment_path
2006-03-20 17:12:09 +00:00
else
2015-04-26 20:15:51 +00:00
ailp - > mode = ai_mode : : AIM_SNIPE_FIRE ; // It gets bashed in create_n_segment_path
2013-03-03 01:03:33 +00:00
} else
# endif
{
2015-04-26 20:15:51 +00:00
ailp - > mode = ai_mode : : AIM_RUN_FROM_OBJECT ; // It gets bashed in create_n_segment_path
2006-03-20 17:12:09 +00:00
}
2013-03-03 01:03:33 +00:00
}
# if defined(DXX_BUILD_DESCENT_I)
else {
2015-04-26 20:15:51 +00:00
ailp - > mode = ai_mode : : AIM_STILL ;
2013-03-03 01:03:33 +00:00
}
return ;
# elif defined(DXX_BUILD_DESCENT_II)
2013-11-03 22:27:28 +00:00
else if ( robot_is_companion ( robptr ) = = 0 ) {
2015-04-26 20:15:51 +00:00
ailp - > mode = ai_mode : : AIM_STILL ;
2006-03-20 17:12:09 +00:00
aip - > path_length = 0 ;
return ;
}
2013-03-03 01:03:33 +00:00
# endif
2006-03-20 17:12:09 +00:00
}
2013-03-03 01:03:33 +00:00
# if defined(DXX_BUILD_DESCENT_I)
Assert ( ( aip - > PATH_DIR = = - 1 ) | | ( aip - > PATH_DIR = = 1 ) ) ;
2015-04-02 02:36:57 +00:00
if ( ( aip - > SUBMODE = = AISM_HIDING ) & & ( aip - > behavior = = ai_behavior : : AIB_HIDE ) )
2013-03-03 01:03:33 +00:00
return ;
# endif
2006-03-20 17:12:09 +00:00
goal_point = Point_segs [ aip - > hide_index + aip - > cur_path_index ] . point ;
2015-03-12 02:21:19 +00:00
auto dist_to_goal = vm_vec_dist_quick ( goal_point , objp - > pos ) ;
2006-03-20 17:12:09 +00:00
// If running from player, only run until can't be seen.
2015-04-26 20:15:51 +00:00
if ( ailp - > mode = = ai_mode : : AIM_RUN_FROM_OBJECT ) {
2015-04-02 02:36:53 +00:00
if ( ( player_visibility = = 0 ) & & ( ailp - > player_awareness_type = = player_awareness_type_t : : PA_NONE ) ) {
2006-03-20 17:12:09 +00:00
fix vel_scale ;
vel_scale = F1_0 - FrameTime / 2 ;
if ( vel_scale < F1_0 / 2 )
vel_scale = F1_0 / 2 ;
2014-09-28 21:11:05 +00:00
vm_vec_scale ( objp - > mtype . phys_info . velocity , vel_scale ) ;
2006-03-20 17:12:09 +00:00
return ;
2013-03-03 01:03:33 +00:00
} else
# if defined(DXX_BUILD_DESCENT_II)
2013-12-24 04:53:59 +00:00
if ( ! ( d_tick_count ^ ( ( objp ) & 0x07 ) ) )
2013-03-03 01:03:33 +00:00
# endif
{ // Done 1/8 ticks.
2006-03-20 17:12:09 +00:00
// If player on path (beyond point robot is now at), then create a new path.
point_seg * curpsp = & Point_segs [ aip - > hide_index ] ;
2014-11-20 03:00:36 +00:00
auto player_segnum = ConsoleObject - > segnum ;
2006-03-20 17:12:09 +00:00
int i ;
// This is probably being done every frame, which is wasteful.
for ( i = aip - > cur_path_index ; i < aip - > path_length ; i + + ) {
if ( curpsp [ i ] . segnum = = player_segnum ) {
2016-07-09 17:58:35 +00:00
create_n_segment_path ( objp , AVOID_SEG_LENGTH , player_segnum ! = objp - > segnum ? player_segnum : segment_none ) ;
2013-03-03 01:03:33 +00:00
# if defined(DXX_BUILD_DESCENT_I)
2006-03-20 17:12:09 +00:00
Assert ( aip - > path_length ! = 0 ) ;
2013-03-03 01:03:33 +00:00
# endif
2015-04-26 20:15:51 +00:00
ailp - > mode = ai_mode : : AIM_RUN_FROM_OBJECT ; // It gets bashed in create_n_segment_path
2006-03-20 17:12:09 +00:00
break ;
}
}
if ( player_visibility ) {
2015-04-02 02:36:53 +00:00
ailp - > player_awareness_type = player_awareness_type_t : : PA_NEARBY_ROBOT_FIRED ;
2006-03-20 17:12:09 +00:00
ailp - > player_awareness_time = F1_0 ;
}
}
}
if ( aip - > cur_path_index < 0 ) {
aip - > cur_path_index = 0 ;
} else if ( aip - > cur_path_index > = aip - > path_length ) {
2015-04-26 20:15:51 +00:00
if ( ailp - > mode = = ai_mode : : AIM_RUN_FROM_OBJECT ) {
2006-03-20 17:12:09 +00:00
create_n_segment_path ( objp , AVOID_SEG_LENGTH , ConsoleObject - > segnum ) ;
2015-04-26 20:15:51 +00:00
ailp - > mode = ai_mode : : AIM_RUN_FROM_OBJECT ; // It gets bashed in create_n_segment_path
2013-03-03 01:03:33 +00:00
# if defined(DXX_BUILD_DESCENT_II)
2006-03-20 17:12:09 +00:00
Assert ( aip - > path_length ! = 0 ) ;
2013-03-03 01:03:33 +00:00
# endif
2006-03-20 17:12:09 +00:00
} else {
aip - > cur_path_index = aip - > path_length - 1 ;
}
}
goal_point = Point_segs [ aip - > hide_index + aip - > cur_path_index ] . point ;
// If near goal, pick another goal point.
forced_break = 0 ; // Gets set for short paths.
original_dir = aip - > PATH_DIR ;
original_index = aip - > cur_path_index ;
2015-03-12 02:21:19 +00:00
const vm_distance threshold_distance { fixmul ( vm_vec_mag_quick ( objp - > mtype . phys_info . velocity ) , FrameTime ) * 2 + F1_0 * 2 } ;
2006-03-20 17:12:09 +00:00
2013-03-03 01:03:33 +00:00
# if defined(DXX_BUILD_DESCENT_II)
2006-03-20 17:12:09 +00:00
new_goal_point = Point_segs [ aip - > hide_index + aip - > cur_path_index ] . point ;
//--Int3_if(((aip->cur_path_index >= 0) && (aip->cur_path_index < aip->path_length)));
2013-03-03 01:03:33 +00:00
# endif
2006-03-20 17:12:09 +00:00
while ( ( dist_to_goal < threshold_distance ) & & ! forced_break ) {
// Advance to next point on path.
aip - > cur_path_index + = aip - > PATH_DIR ;
// See if next point wraps past end of path (in either direction), and if so, deal with it based on mode.
if ( ( aip - > cur_path_index > = aip - > path_length ) | | ( aip - > cur_path_index < 0 ) ) {
2013-03-03 01:03:33 +00:00
# if defined(DXX_BUILD_DESCENT_II)
2006-03-20 17:12:09 +00:00
// Buddy bot. If he's in mode to get away from player and at end of line,
// if player visible, then make a new path, else just return.
2013-11-03 22:27:28 +00:00
if ( robot_is_companion ( robptr ) ) {
2006-03-20 17:12:09 +00:00
if ( Escort_special_goal = = ESCORT_GOAL_SCRAM )
{
if ( player_visibility ) {
2013-12-26 22:21:16 +00:00
create_n_segment_path ( objp , 16 + d_rand ( ) * 16 , segment_none ) ;
2006-03-20 17:12:09 +00:00
aip - > path_length = polish_path ( objp , & Point_segs [ aip - > hide_index ] , aip - > path_length ) ;
Assert ( aip - > path_length ! = 0 ) ;
2015-04-26 20:15:51 +00:00
ailp - > mode = ai_mode : : AIM_WANDER ; // Special buddy mode.
2006-03-20 17:12:09 +00:00
//--Int3_if(((aip->cur_path_index >= 0) && (aip->cur_path_index < aip->path_length)));
return ;
} else {
2015-04-26 20:15:51 +00:00
ailp - > mode = ai_mode : : AIM_WANDER ; // Special buddy mode.
2014-09-28 21:11:04 +00:00
vm_vec_zero ( objp - > mtype . phys_info . velocity ) ;
vm_vec_zero ( objp - > mtype . phys_info . rotvel ) ;
2006-03-20 17:12:09 +00:00
//!!Assert((aip->cur_path_index >= 0) && (aip->cur_path_index < aip->path_length));
return ;
}
}
}
2013-03-03 01:03:33 +00:00
# endif
2006-03-20 17:12:09 +00:00
2013-03-03 01:03:33 +00:00
# if defined(DXX_BUILD_DESCENT_I)
2015-04-26 20:15:51 +00:00
if ( ailp - > mode = = ai_mode : : AIM_HIDE ) {
ailp - > mode = ai_mode : : AIM_STILL ;
2013-03-03 01:03:33 +00:00
return ; // Stay here until bonked or hit by player.
}
# elif defined(DXX_BUILD_DESCENT_II)
2015-04-02 02:36:57 +00:00
if ( aip - > behavior = = ai_behavior : : AIB_FOLLOW ) {
2006-03-20 17:12:09 +00:00
create_n_segment_path ( objp , 10 , ConsoleObject - > segnum ) ;
//--Int3_if(((aip->cur_path_index >= 0) && (aip->cur_path_index < aip->path_length)));
2013-03-03 01:03:33 +00:00
}
# endif
2015-04-02 02:36:57 +00:00
else if ( aip - > behavior = = ai_behavior : : AIB_STATION ) {
2006-03-20 17:12:09 +00:00
create_path_to_station ( objp , 15 ) ;
2013-03-03 01:03:33 +00:00
if ( ( aip - > hide_segment ! = Point_segs [ aip - > hide_index + aip - > path_length - 1 ] . segnum )
# if defined(DXX_BUILD_DESCENT_II)
| | ( aip - > path_length = = 0 )
# endif
) {
2015-04-26 20:15:51 +00:00
ailp - > mode = ai_mode : : AIM_STILL ;
2006-03-20 17:12:09 +00:00
}
return ;
2015-04-26 20:15:51 +00:00
} else if ( ailp - > mode = = ai_mode : : AIM_FOLLOW_PATH
2013-03-03 01:03:33 +00:00
# if defined(DXX_BUILD_DESCENT_I)
2015-04-02 02:36:57 +00:00
& & ( aip - > behavior ! = ai_behavior : : AIB_FOLLOW_PATH )
2013-03-03 01:03:33 +00:00
# endif
) {
2006-03-20 17:12:09 +00:00
create_path_to_player ( objp , 10 , 1 ) ;
2013-03-03 01:03:33 +00:00
# if defined(DXX_BUILD_DESCENT_II)
2006-03-20 17:12:09 +00:00
if ( aip - > hide_segment ! = Point_segs [ aip - > hide_index + aip - > path_length - 1 ] . segnum ) {
2015-04-26 20:15:51 +00:00
ailp - > mode = ai_mode : : AIM_STILL ;
2006-03-20 17:12:09 +00:00
return ;
}
2013-03-03 01:03:33 +00:00
# endif
2015-04-26 20:15:51 +00:00
} else if ( ailp - > mode = = ai_mode : : AIM_RUN_FROM_OBJECT ) {
2006-03-20 17:12:09 +00:00
create_n_segment_path ( objp , AVOID_SEG_LENGTH , ConsoleObject - > segnum ) ;
2015-04-26 20:15:51 +00:00
ailp - > mode = ai_mode : : AIM_RUN_FROM_OBJECT ; // It gets bashed in create_n_segment_path
2013-03-03 01:03:33 +00:00
# if defined(DXX_BUILD_DESCENT_II)
2006-03-20 17:12:09 +00:00
if ( aip - > path_length < 1 ) {
create_n_segment_path ( objp , AVOID_SEG_LENGTH , ConsoleObject - > segnum ) ;
2015-04-26 20:15:51 +00:00
ailp - > mode = ai_mode : : AIM_RUN_FROM_OBJECT ; // It gets bashed in create_n_segment_path
2006-03-20 17:12:09 +00:00
if ( aip - > path_length < 1 ) {
2015-04-02 02:36:57 +00:00
aip - > behavior = ai_behavior : : AIB_NORMAL ;
2015-04-26 20:15:51 +00:00
ailp - > mode = ai_mode : : AIM_STILL ;
2006-03-20 17:12:09 +00:00
return ;
}
}
//--Int3_if(((aip->cur_path_index >= 0) && (aip->cur_path_index < aip->path_length)));
2013-03-03 01:03:33 +00:00
# endif
2006-03-20 17:12:09 +00:00
} else {
// Reached end of the line. First see if opposite end point is reachable, and if so, go there.
// If not, turn around.
int opposite_end_index ;
vms_vector * opposite_end_point ;
fvi_info hit_data ;
int fate ;
fvi_query fq ;
// See which end we're nearer and look at the opposite end point.
if ( abs ( aip - > cur_path_index - aip - > path_length ) < aip - > cur_path_index ) {
// Nearer to far end (ie, index not 0), so try to reach 0.
opposite_end_index = 0 ;
} else {
// Nearer to 0 end, so try to reach far end.
opposite_end_index = aip - > path_length - 1 ;
}
opposite_end_point = & Point_segs [ aip - > hide_index + opposite_end_index ] . point ;
fq . p0 = & objp - > pos ;
fq . startseg = objp - > segnum ;
fq . p1 = opposite_end_point ;
fq . rad = objp - > size ;
2013-12-24 04:53:59 +00:00
fq . thisobjnum = objp ;
2015-02-05 03:03:51 +00:00
fq . ignore_obj_list . first = nullptr ;
2006-03-20 17:12:09 +00:00
fq . flags = 0 ; //what about trans walls???
2015-01-20 02:46:42 +00:00
fate = find_vector_intersection ( fq , hit_data ) ;
2006-03-20 17:12:09 +00:00
if ( fate ! = HIT_WALL ) {
// We can be circular! Do it!
// Path direction is unchanged.
aip - > cur_path_index = opposite_end_index ;
} else {
aip - > PATH_DIR = - aip - > PATH_DIR ;
2013-03-03 01:03:33 +00:00
# if defined(DXX_BUILD_DESCENT_II)
2006-03-20 17:12:09 +00:00
aip - > cur_path_index + = aip - > PATH_DIR ;
2013-03-03 01:03:33 +00:00
# endif
2006-03-20 17:12:09 +00:00
}
}
break ;
} else {
new_goal_point = Point_segs [ aip - > hide_index + aip - > cur_path_index ] . point ;
goal_point = new_goal_point ;
2014-10-01 02:28:41 +00:00
dist_to_goal = vm_vec_dist_quick ( goal_point , objp - > pos ) ;
2006-03-20 17:12:09 +00:00
}
// If went all the way around to original point, in same direction, then get out of here!
if ( ( aip - > cur_path_index = = original_index ) & & ( aip - > PATH_DIR = = original_dir ) ) {
create_path_to_player ( objp , 3 , 1 ) ;
forced_break = 1 ;
}
} // end while
// Set velocity (objp->mtype.phys_info.velocity) and orientation (objp->orient) for this object.
2014-11-02 03:41:21 +00:00
ai_path_set_orient_and_vel ( objp , goal_point
2013-03-03 01:03:33 +00:00
# if defined(DXX_BUILD_DESCENT_II)
, player_visibility , vec_to_player
# endif
) ;
2006-03-20 17:12:09 +00:00
}
2015-12-22 04:18:50 +00:00
}
namespace {
int Last_tick_garbage_collected ;
2013-12-22 22:03:07 +00:00
struct obj_path {
2015-07-13 01:09:36 +00:00
short path_start ;
objnum_t objnum ;
2013-12-22 22:03:07 +00:00
} ;
2006-03-20 17:12:09 +00:00
2016-09-04 19:10:42 +00:00
static int path_index_compare ( const void * const v1 , const void * const v2 )
2006-03-20 17:12:09 +00:00
{
2016-09-04 19:10:42 +00:00
const auto i1 = reinterpret_cast < const obj_path * > ( v1 ) ;
const auto i2 = reinterpret_cast < const obj_path * > ( v2 ) ;
2006-03-20 17:12:09 +00:00
if ( i1 - > path_start < i2 - > path_start )
return - 1 ;
else if ( i1 - > path_start = = i2 - > path_start )
return 0 ;
else
return 1 ;
}
2015-12-22 04:18:50 +00:00
}
namespace dsx {
2006-03-20 17:12:09 +00:00
// ----------------------------------------------------------------------------------------------------------
// Set orientation matrix and velocity for objp based on its desire to get to a point.
2014-11-02 03:41:21 +00:00
void ai_path_set_orient_and_vel ( const vobjptr_t objp , const vms_vector & goal_point
2013-03-03 01:03:33 +00:00
# if defined(DXX_BUILD_DESCENT_II)
2014-10-02 03:02:36 +00:00
, int player_visibility , const vms_vector * vec_to_player
2013-03-03 01:03:33 +00:00
# endif
)
2006-03-20 17:12:09 +00:00
{
vms_vector cur_vel = objp - > mtype . phys_info . velocity ;
vms_vector cur_pos = objp - > pos ;
fix speed_scale ;
fix dot ;
2013-10-07 23:52:33 +00:00
robot_info * robptr = & Robot_info [ get_robot_id ( objp ) ] ;
2006-03-20 17:12:09 +00:00
fix max_speed ;
// If evading player, use highest difficulty level speed, plus something based on diff level
max_speed = robptr - > max_speed [ Difficulty_level ] ;
2013-12-25 03:16:41 +00:00
ai_local * ailp = & objp - > ctype . ai_info . ail ;
2015-04-26 20:15:51 +00:00
if ( ailp - > mode = = ai_mode : : AIM_RUN_FROM_OBJECT
2013-03-03 01:03:33 +00:00
# if defined(DXX_BUILD_DESCENT_II)
2015-04-02 02:36:57 +00:00
| | objp - > ctype . ai_info . behavior = = ai_behavior : : AIB_SNIPE
2013-03-03 01:03:33 +00:00
# endif
)
2006-03-20 17:12:09 +00:00
max_speed = max_speed * 3 / 2 ;
2014-11-02 03:41:21 +00:00
auto norm_vec_to_goal = vm_vec_normalized_quick ( vm_vec_sub ( goal_point , cur_pos ) ) ;
2014-10-29 03:24:31 +00:00
auto norm_cur_vel = vm_vec_normalized_quick ( cur_vel ) ;
const auto norm_fvec = vm_vec_normalized_quick ( objp - > orient . fvec ) ;
2006-03-20 17:12:09 +00:00
2014-09-28 21:11:48 +00:00
dot = vm_vec_dot ( norm_vec_to_goal , norm_fvec ) ;
2006-03-20 17:12:09 +00:00
// If very close to facing opposite desired vector, perturb vector
if ( dot < - 15 * F1_0 / 16 ) {
norm_cur_vel = norm_vec_to_goal ;
} else {
2016-06-05 01:04:26 +00:00
norm_cur_vel . x + = norm_vec_to_goal . x / 2 / ( static_cast < float > ( DESIGNATED_GAME_FRAMETIME ) / FrameTime ) ;
norm_cur_vel . y + = norm_vec_to_goal . y / 2 / ( static_cast < float > ( DESIGNATED_GAME_FRAMETIME ) / FrameTime ) ;
norm_cur_vel . z + = norm_vec_to_goal . z / 2 / ( static_cast < float > ( DESIGNATED_GAME_FRAMETIME ) / FrameTime ) ;
2006-03-20 17:12:09 +00:00
}
2014-09-28 21:11:04 +00:00
vm_vec_normalize_quick ( norm_cur_vel ) ;
2006-03-20 17:12:09 +00:00
// Set speed based on this robot type's maximum allowed speed and how hard it is turning.
// How hard it is turning is based on the dot product of (vector to goal) and (current velocity vector)
// Note that since 3*F1_0/4 is added to dot product, it is possible for the robot to back up.
// Set speed and orientation.
if ( dot < 0 )
dot / = - 4 ;
2013-03-03 01:03:33 +00:00
# if defined(DXX_BUILD_DESCENT_II)
2006-03-20 17:12:09 +00:00
// If in snipe mode, can move fast even if not facing that direction.
2015-04-02 02:36:57 +00:00
if ( objp - > ctype . ai_info . behavior = = ai_behavior : : AIB_SNIPE )
2006-03-20 17:12:09 +00:00
if ( dot < F1_0 / 2 )
dot = ( dot + F1_0 ) / 2 ;
2013-03-03 01:03:33 +00:00
# endif
2006-03-20 17:12:09 +00:00
speed_scale = fixmul ( max_speed , dot ) ;
2014-09-28 21:11:05 +00:00
vm_vec_scale ( norm_cur_vel , speed_scale ) ;
2006-03-20 17:12:09 +00:00
objp - > mtype . phys_info . velocity = norm_cur_vel ;
2016-04-09 21:40:27 +00:00
fix rate ;
2015-04-26 20:15:51 +00:00
if ( ailp - > mode = = ai_mode : : AIM_RUN_FROM_OBJECT
2013-03-03 01:03:33 +00:00
# if defined(DXX_BUILD_DESCENT_II)
2015-04-02 02:36:57 +00:00
| | robot_is_companion ( robptr ) = = 1 | | objp - > ctype . ai_info . behavior = = ai_behavior : : AIB_SNIPE
2013-03-03 01:03:33 +00:00
# endif
) {
# if defined(DXX_BUILD_DESCENT_II)
2015-04-26 20:15:51 +00:00
if ( ailp - > mode = = ai_mode : : AIM_SNIPE_RETREAT_BACKWARDS ) {
2006-03-20 17:12:09 +00:00
if ( ( player_visibility ) & & ( vec_to_player ! = NULL ) )
norm_vec_to_goal = * vec_to_player ;
else
2014-09-28 21:09:37 +00:00
vm_vec_negate ( norm_vec_to_goal ) ;
2006-03-20 17:12:09 +00:00
}
2013-03-03 01:03:33 +00:00
# endif
2016-04-09 21:40:27 +00:00
rate = robptr - > turn_time [ NDL - 1 ] / 2 ;
2006-03-20 17:12:09 +00:00
} else
2016-04-09 21:40:27 +00:00
rate = robptr - > turn_time [ Difficulty_level ] ;
ai_turn_towards_vector ( norm_vec_to_goal , objp , rate ) ;
2006-03-20 17:12:09 +00:00
}
// ----------------------------------------------------------------------------------------------------------
// Garbage colledion -- Free all unused records in Point_segs and compress all paths.
2015-12-22 04:18:50 +00:00
void ai_path_garbage_collect ( )
2006-03-20 17:12:09 +00:00
{
int free_path_index = 0 ;
int num_path_objects = 0 ;
int objind ;
obj_path object_list [ MAX_OBJECTS ] ;
# ifndef NDEBUG
force_dump_ai_objects_all ( " ***** Start ai_path_garbage_collect ***** " ) ;
# endif
2012-05-14 17:06:28 +00:00
Last_tick_garbage_collected = d_tick_count ;
2006-03-20 17:12:09 +00:00
# if PATH_VALIDATION
validate_all_paths ( ) ;
# endif
// Create a list of objects which have paths of length 1 or more.
2016-02-12 04:02:28 +00:00
range_for ( const auto & & objp , vcobjptridx )
2014-10-12 23:05:46 +00:00
{
2013-03-03 01:03:33 +00:00
if ( ( objp - > type = = OBJ_ROBOT ) & & ( ( objp - > control_type = = CT_AI )
# if defined(DXX_BUILD_DESCENT_II)
| | ( objp - > control_type = = CT_MORPH )
# endif
) ) {
2015-06-13 22:42:16 +00:00
const auto & aip = objp - > ctype . ai_info ;
if ( aip . path_length ) {
object_list [ num_path_objects ] . path_start = aip . hide_index ;
2015-12-22 04:18:51 +00:00
object_list [ num_path_objects + + ] . objnum = objp ;
2006-03-20 17:12:09 +00:00
}
}
}
qsort ( object_list , num_path_objects , sizeof ( object_list [ 0 ] ) ,
2016-09-04 19:10:42 +00:00
path_index_compare ) ;
2006-03-20 17:12:09 +00:00
for ( objind = 0 ; objind < num_path_objects ; objind + + ) {
ai_static * aip ;
int i ;
int old_index ;
2014-10-12 23:05:46 +00:00
auto objnum = object_list [ objind ] . objnum ;
2014-10-02 03:02:34 +00:00
auto objp = vobjptridx ( objnum ) ;
2006-03-20 17:12:09 +00:00
aip = & objp - > ctype . ai_info ;
old_index = aip - > hide_index ;
aip - > hide_index = free_path_index ;
for ( i = 0 ; i < aip - > path_length ; i + + )
Point_segs [ free_path_index + + ] = Point_segs [ old_index + + ] ;
}
2013-12-28 18:47:17 +00:00
Point_segs_free_ptr = Point_segs . begin ( ) + free_path_index ;
2006-03-20 17:12:09 +00:00
# ifndef NDEBUG
{
force_dump_ai_objects_all ( " ***** Finish ai_path_garbage_collect ***** " ) ;
2016-02-12 04:02:28 +00:00
range_for ( const auto & & objp , vcobjptr )
2014-10-12 23:05:46 +00:00
{
2015-06-13 22:42:16 +00:00
const auto & aip = objp - > ctype . ai_info ;
2006-03-20 17:12:09 +00:00
2015-06-13 22:42:16 +00:00
if ( objp - > type = = OBJ_ROBOT & & objp - > control_type = = CT_AI )
if ( ( aip . hide_index + aip . path_length > Point_segs_free_ptr - Point_segs ) & & ( aip . path_length > 0 ) )
2006-03-20 17:12:09 +00:00
Int3 ( ) ; // Contact Mike: Debug trap for nasty, elusive bug.
}
validate_all_paths ( ) ;
}
# endif
}
// -----------------------------------------------------------------------------
// Do garbage collection if not been done for awhile, or things getting really critical.
void maybe_ai_path_garbage_collect ( void )
{
if ( Point_segs_free_ptr - Point_segs > MAX_POINT_SEGS - MAX_PATH_LENGTH ) {
2012-05-14 17:06:28 +00:00
if ( Last_tick_garbage_collected + 1 > = d_tick_count ) {
2006-03-20 17:12:09 +00:00
// This is kind of bad. Garbage collected last frame or this frame.
// Just destroy all paths. Too bad for the robots. They are memory wasteful.
ai_reset_all_paths ( ) ;
} else {
// We are really close to full, but didn't just garbage collect, so maybe this is recoverable.
ai_path_garbage_collect ( ) ;
}
} else if ( Point_segs_free_ptr - Point_segs > 3 * MAX_POINT_SEGS / 4 ) {
2012-05-14 17:06:28 +00:00
if ( Last_tick_garbage_collected + 16 < d_tick_count ) {
2006-03-20 17:12:09 +00:00
ai_path_garbage_collect ( ) ;
}
} else if ( Point_segs_free_ptr - Point_segs > MAX_POINT_SEGS / 2 ) {
2012-05-14 17:06:28 +00:00
if ( Last_tick_garbage_collected + 256 < d_tick_count ) {
2006-03-20 17:12:09 +00:00
ai_path_garbage_collect ( ) ;
}
}
}
// -----------------------------------------------------------------------------
// Reset all paths. Do garbage collection.
// Should be called at the start of each level.
void ai_reset_all_paths ( void )
{
2016-02-12 04:02:28 +00:00
range_for ( const auto & & objp , vobjptr )
2015-01-28 03:42:52 +00:00
{
if ( objp - > type = = OBJ_ROBOT & & objp - > control_type = = CT_AI )
{
2015-06-13 22:42:16 +00:00
objp - > ctype . ai_info . hide_index = - 1 ;
objp - > ctype . ai_info . path_length = 0 ;
2006-03-20 17:12:09 +00:00
}
2015-01-28 03:42:52 +00:00
}
2006-03-20 17:12:09 +00:00
ai_path_garbage_collect ( ) ;
}
// ---------------------------------------------------------------------------------------------------------
// Probably called because a robot bashed a wall, getting a bunch of retries.
// Try to resume path.
2014-10-02 03:02:34 +00:00
void attempt_to_resume_path ( const vobjptridx_t objp )
2006-03-20 17:12:09 +00:00
{
2011-09-26 23:31:19 +00:00
ai_static * aip = & objp - > ctype . ai_info ;
int new_path_index ;
2006-03-20 17:12:09 +00:00
2015-04-02 02:36:57 +00:00
if ( aip - > behavior = = ai_behavior : : AIB_STATION
2013-03-03 01:03:33 +00:00
# if defined(DXX_BUILD_DESCENT_II)
2015-03-22 18:49:21 +00:00
& & Robot_info [ get_robot_id ( objp ) ] . companion ! = 1
2013-03-03 01:03:33 +00:00
# endif
)
2006-03-20 17:12:09 +00:00
if ( d_rand ( ) > 8192 ) {
2013-12-25 03:16:41 +00:00
ai_local * ailp = & objp - > ctype . ai_info . ail ;
2006-03-20 17:12:09 +00:00
aip - > hide_segment = objp - > segnum ;
2015-04-26 20:15:51 +00:00
ailp - > mode = ai_mode : : AIM_STILL ;
2006-03-20 17:12:09 +00:00
}
new_path_index = aip - > cur_path_index - aip - > PATH_DIR ;
if ( ( new_path_index > = 0 ) & & ( new_path_index < aip - > path_length ) ) {
aip - > cur_path_index = new_path_index ;
} else {
2011-09-26 23:31:19 +00:00
// At end of line and have nowhere to go.
2006-03-20 17:12:09 +00:00
move_towards_segment_center ( objp ) ;
create_path_to_station ( objp , 15 ) ;
}
}
// ----------------------------------------------------------------------------------------------------------
// DEBUG FUNCTIONS FOLLOW
// ----------------------------------------------------------------------------------------------------------
2016-09-11 18:49:16 +00:00
# if DXX_USE_EDITOR
2006-03-20 17:12:09 +00:00
2013-10-26 19:09:44 +00:00
static void test_create_path_many ( void ) __attribute_used ;
static void test_create_path_many ( void )
2006-03-20 17:12:09 +00:00
{
2015-04-19 04:18:49 +00:00
array < point_seg , 200 > point_segs ;
2006-03-20 17:12:09 +00:00
short num_points ;
int i ;
2015-12-04 03:36:31 +00:00
const unsigned Test_size = 1000 ;
2006-03-20 17:12:09 +00:00
for ( i = 0 ; i < Test_size ; i + + ) {
2015-07-13 01:09:37 +00:00
Cursegp = segptridx ( static_cast < segnum_t > ( ( d_rand ( ) * ( Highest_segment_index + 1 ) ) / D_RAND_MAX ) ) ;
Markedsegp = segptridx ( static_cast < segnum_t > ( ( d_rand ( ) * ( Highest_segment_index + 1 ) ) / D_RAND_MAX ) ) ;
2015-07-29 03:05:28 +00:00
create_path_points ( vobjptridx ( object_first ) , Cursegp , Markedsegp , point_segs . begin ( ) , & num_points , - 1 , 0 , 0 , segment_none ) ;
2006-03-20 17:12:09 +00:00
}
}
2013-10-26 19:09:44 +00:00
static void test_create_path ( void ) __attribute_used ;
static void test_create_path ( void )
2006-03-20 17:12:09 +00:00
{
2015-04-19 04:18:49 +00:00
array < point_seg , 200 > point_segs ;
2006-03-20 17:12:09 +00:00
short num_points ;
2015-07-29 03:05:28 +00:00
create_path_points ( vobjptridx ( object_first ) , Cursegp , Markedsegp , point_segs . begin ( ) , & num_points , - 1 , 0 , 0 , segment_none ) ;
2006-03-20 17:12:09 +00:00
}
// For all segments in mine, create paths to all segments in mine, print results.
2013-10-26 19:09:44 +00:00
static void test_create_all_paths ( void ) __attribute_used ;
static void test_create_all_paths ( void )
2006-03-20 17:12:09 +00:00
{
short resultant_length ;
2013-12-28 18:47:17 +00:00
Point_segs_free_ptr = Point_segs . begin ( ) ;
2006-03-20 17:12:09 +00:00
2016-02-12 04:02:28 +00:00
range_for ( const auto & & segp0 , vcsegptridx )
2014-10-12 23:10:05 +00:00
{
2015-06-13 22:42:16 +00:00
if ( segp0 - > segnum ! = segment_none )
{
2016-02-06 22:12:55 +00:00
range_for ( const auto & & segp1 , partial_range ( vcsegptridx , static_cast < segnum_t > ( segp0 ) , vcsegptridx . count ( ) ) )
2014-10-12 23:10:05 +00:00
{
2015-06-13 22:42:16 +00:00
if ( segp1 - > segnum ! = segment_none )
{
2015-12-22 04:18:51 +00:00
create_path_points ( vobjptridx ( object_first ) , segp0 , segp1 , Point_segs_free_ptr , & resultant_length , - 1 , 0 , 0 , segment_none ) ;
2006-03-20 17:12:09 +00:00
}
}
}
}
}
short Player_path_length = 0 ;
int Player_hide_index = - 1 ;
int Player_cur_path_index = 0 ;
int Player_following_path_flag = 0 ;
// ------------------------------------------------------------------------------------------------------------------
// Set orientation matrix and velocity for objp based on its desire to get to a point.
2016-04-23 17:59:47 +00:00
static void player_path_set_orient_and_vel ( object & objp , const vms_vector & goal_point )
2006-03-20 17:12:09 +00:00
{
2016-04-23 17:59:47 +00:00
const auto & cur_vel = objp . mtype . phys_info . velocity ;
const auto & cur_pos = objp . pos ;
2006-03-20 17:12:09 +00:00
fix speed_scale ;
fix dot ;
2016-04-23 17:59:47 +00:00
const fix max_speed =
2013-03-03 01:03:33 +00:00
# if defined(DXX_BUILD_DESCENT_I)
2016-04-23 17:59:47 +00:00
F1_0 * 50 ;
2013-03-03 01:03:33 +00:00
# elif defined(DXX_BUILD_DESCENT_II)
2016-04-23 17:59:47 +00:00
Robot_info [ get_robot_id ( objp ) ] . max_speed [ Difficulty_level ] ;
2013-03-03 01:03:33 +00:00
# endif
2006-03-20 17:12:09 +00:00
2014-11-02 03:41:39 +00:00
const auto norm_vec_to_goal = vm_vec_normalized_quick ( vm_vec_sub ( goal_point , cur_pos ) ) ;
2014-10-29 03:24:31 +00:00
auto norm_cur_vel = vm_vec_normalized_quick ( cur_vel ) ;
2016-04-23 17:59:47 +00:00
const auto & & norm_fvec = vm_vec_normalized_quick ( objp . orient . fvec ) ;
2006-03-20 17:12:09 +00:00
2014-09-28 21:11:48 +00:00
dot = vm_vec_dot ( norm_vec_to_goal , norm_fvec ) ;
2013-03-03 01:03:33 +00:00
# if defined(DXX_BUILD_DESCENT_II)
2016-04-23 17:59:47 +00:00
ai_local * const ailp = & objp . ctype . ai_info . ail ;
2015-04-26 20:15:51 +00:00
if ( ailp - > mode = = ai_mode : : AIM_SNIPE_RETREAT_BACKWARDS ) {
2006-03-20 17:12:09 +00:00
dot = - dot ;
}
2013-03-03 01:03:33 +00:00
# endif
2006-03-20 17:12:09 +00:00
// If very close to facing opposite desired vector, perturb vector
if ( dot < - 15 * F1_0 / 16 ) {
norm_cur_vel = norm_vec_to_goal ;
} else {
2016-06-05 01:04:26 +00:00
norm_cur_vel . x + = norm_vec_to_goal . x / 2 / ( static_cast < float > ( DESIGNATED_GAME_FRAMETIME ) / FrameTime ) ;
norm_cur_vel . y + = norm_vec_to_goal . y / 2 / ( static_cast < float > ( DESIGNATED_GAME_FRAMETIME ) / FrameTime ) ;
norm_cur_vel . z + = norm_vec_to_goal . z / 2 / ( static_cast < float > ( DESIGNATED_GAME_FRAMETIME ) / FrameTime ) ;
2006-03-20 17:12:09 +00:00
}
2014-09-28 21:11:04 +00:00
vm_vec_normalize_quick ( norm_cur_vel ) ;
2006-03-20 17:12:09 +00:00
// Set speed based on this robot type's maximum allowed speed and how hard it is turning.
// How hard it is turning is based on the dot product of (vector to goal) and (current velocity vector)
// Note that since 3*F1_0/4 is added to dot product, it is possible for the robot to back up.
// Set speed and orientation.
if ( dot < 0 )
dot / = 4 ;
speed_scale = fixmul ( max_speed , dot ) ;
2014-09-28 21:11:05 +00:00
vm_vec_scale ( norm_cur_vel , speed_scale ) ;
2016-04-23 17:59:47 +00:00
objp . mtype . phys_info . velocity = norm_cur_vel ;
2014-10-02 03:02:36 +00:00
ai_turn_towards_vector ( norm_vec_to_goal , objp , F1_0 ) ;
2006-03-20 17:12:09 +00:00
}
// ----------------------------------------------------------------------------------------------------------
// Optimization: If current velocity will take robot near goal, don't change velocity
2014-10-02 03:02:34 +00:00
void player_follow_path ( const vobjptr_t objp )
2006-03-20 17:12:09 +00:00
{
vms_vector goal_point ;
int count , forced_break , original_index ;
int goal_seg ;
if ( ! Player_following_path_flag )
return ;
if ( Player_hide_index = = - 1 )
return ;
if ( Player_path_length < 2 )
return ;
goal_point = Point_segs [ Player_hide_index + Player_cur_path_index ] . point ;
goal_seg = Point_segs [ Player_hide_index + Player_cur_path_index ] . segnum ;
Assert ( ( goal_seg > = 0 ) & & ( goal_seg < = Highest_segment_index ) ) ;
2011-09-26 23:31:19 +00:00
( void ) goal_seg ;
2015-03-12 02:21:19 +00:00
auto dist_to_goal = vm_vec_dist_quick ( goal_point , objp - > pos ) ;
2006-03-20 17:12:09 +00:00
if ( Player_cur_path_index < 0 )
Player_cur_path_index = 0 ;
else if ( Player_cur_path_index > = Player_path_length )
Player_cur_path_index = Player_path_length - 1 ;
goal_point = Point_segs [ Player_hide_index + Player_cur_path_index ] . point ;
count = 0 ;
// If near goal, pick another goal point.
forced_break = 0 ; // Gets set for short paths.
//original_dir = 1;
original_index = Player_cur_path_index ;
2015-03-12 02:21:19 +00:00
const vm_distance threshold_distance { fixmul ( vm_vec_mag_quick ( objp - > mtype . phys_info . velocity ) , FrameTime ) * 2 + F1_0 * 2 } ;
2006-03-20 17:12:09 +00:00
while ( ( dist_to_goal < threshold_distance ) & & ! forced_break ) {
// ----- Debug stuff -----
if ( count + + > 20 ) {
break ;
}
// Advance to next point on path.
Player_cur_path_index + = 1 ;
// See if next point wraps past end of path (in either direction), and if so, deal with it based on mode.
if ( ( Player_cur_path_index > = Player_path_length ) | | ( Player_cur_path_index < 0 ) ) {
Player_following_path_flag = 0 ;
forced_break = 1 ;
}
// If went all the way around to original point, in same direction, then get out of here!
if ( Player_cur_path_index = = original_index ) {
Player_following_path_flag = 0 ;
forced_break = 1 ;
}
goal_point = Point_segs [ Player_hide_index + Player_cur_path_index ] . point ;
2014-10-01 02:28:41 +00:00
dist_to_goal = vm_vec_dist_quick ( goal_point , objp - > pos ) ;
2006-03-20 17:12:09 +00:00
} // end while
// Set velocity (objp->mtype.phys_info.velocity) and orientation (objp->orient) for this object.
2014-11-02 03:41:39 +00:00
player_path_set_orient_and_vel ( objp , goal_point ) ;
2006-03-20 17:12:09 +00:00
}
// ------------------------------------------------------------------------------------------------------------------
// Create path for player from current segment to goal segment.
2013-12-29 04:28:07 +00:00
static void create_player_path_to_segment ( segnum_t segnum )
2006-03-20 17:12:09 +00:00
{
2014-12-23 04:20:27 +00:00
const auto objp = vobjptridx ( ConsoleObject ) ;
2006-03-20 17:12:09 +00:00
Player_path_length = 0 ;
Player_hide_index = - 1 ;
Player_cur_path_index = 0 ;
Player_following_path_flag = 0 ;
2013-12-26 22:21:16 +00:00
if ( create_path_points ( objp , objp - > segnum , segnum , Point_segs_free_ptr , & Player_path_length , 100 , 0 , 0 , segment_none ) = = - 1 )
2013-12-07 00:47:27 +00:00
con_printf ( CON_DEBUG , " Unable to form path of length %i for myself " , 100 ) ;
2006-03-20 17:12:09 +00:00
Player_following_path_flag = 1 ;
Player_hide_index = Point_segs_free_ptr - Point_segs ;
Player_cur_path_index = 0 ;
Point_segs_free_ptr + = Player_path_length ;
if ( Point_segs_free_ptr - Point_segs + MAX_PATH_LENGTH * 2 > MAX_POINT_SEGS ) {
//Int3(); // Contact Mike: This is curious, though not deadly. /eip++;g
ai_reset_all_paths ( ) ;
}
}
2013-12-29 04:28:07 +00:00
segnum_t Player_goal_segment = segment_none ;
2006-03-20 17:12:09 +00:00
void check_create_player_path ( void )
{
2013-12-26 22:21:16 +00:00
if ( Player_goal_segment ! = segment_none )
2006-03-20 17:12:09 +00:00
create_player_path_to_segment ( Player_goal_segment ) ;
2013-12-26 22:21:16 +00:00
Player_goal_segment = segment_none ;
2006-03-20 17:12:09 +00:00
}
# endif
2015-12-22 04:18:50 +00:00
}
2006-03-20 17:12:09 +00:00
// ----------------------------------------------------------------------------------------------------------
// DEBUG FUNCTIONS ENDED
// ----------------------------------------------------------------------------------------------------------