75 #pragma optimize("", off)
76 #pragma auto_inline(off)
79 #define UNINITIALIZED_VALUE -99999.9f
81 #define INSTRUCTOR_SHIP_NAME NOX("instructor")
83 #define AICODE_SMALL_MAGNITUDE 0.001f // cosider a vector NULL if mag is less than this
85 #define NEXT_REARM_TIMESTAMP (60*1000) // Ships will re-request rearm, typically, after this long.
87 #define CIRCLE_STRAFE_MAX_DIST 300.0f //Maximum distance for circle strafe behavior.
92 #define SM_SF_BEHIND 1
96 #define SM_SS_SET_GOAL -1
172 #define REARM_SOUND_DELAY (3*F1_0) // Amount of time to delay rearm/repair after mode start
173 #define REARM_BREAKOFF_DELAY (3*F1_0) // Amount of time to wait after fully rearmed to breakoff.
175 #define MIN_DIST_TO_WAYPOINT_GOAL 5.0f
176 #define MAX_GUARD_DIST 250.0f
177 #define BIG_GUARD_RADIUS 500.0f
179 #define MAX_EVADE_TIME (15 * 1000) // Max time to evade a weapon.
182 #define MAX_REPAIR_SPEED 25.0f
183 #define MAX_UNDOCK_ABORT_SPEED 2.0f
186 #define MAX_EMP_INACCURACY 50.0f
189 #define MAX_STEALTH_INACCURACY 50.0f // at max view dist
190 #define STEALTH_MAX_VIEW_DIST 400 // dist at which 1) stealth no longer visible 2) firing inaccuracy is greatest
191 #define STEALTH_VIEW_CONE_DOT 0.707 // (half angle of 45 degrees)
210 const char *str = NULL;
212 #if NUM_SKILL_LEVELS != 5
213 #error Number of skill levels is wrong!
219 str =
XSTR(
"Very Easy", 469);
222 str =
XSTR(
"Easy", 470);
225 str =
XSTR(
"Medium", 471);
228 str =
XSTR(
"Hard", 472);
231 str =
XSTR(
"Insane", 473);
239 str =
NOX(
"Very Easy");
261 #define DELAY_TARGET_TIME (12*1000) // time in milliseconds until a ship can target a new enemy after an order.
338 Ai_huge_fire_info.push_back(new_info);
350 int firer_team, target_signature;
356 firer_team = firer_ship->
team;
359 target_signature = target_objp->
signature;
360 for (hfi = Ai_huge_fire_info.begin();hfi != Ai_huge_fire_info.end(); ++hfi ) {
361 int ship_index, signature;
363 if ( hfi->weapon_index == -1 )
367 if ( ship_index == -1 )
373 if ( (signature == target_signature) && (hfi->weapon_index == weapon_num) && (hfi->team == firer_team) )
378 if ( hfi == Ai_huge_fire_info.end() )
382 return hfi->max_fire_count;
390 Ai_huge_fire_info.clear();
447 Ppfp = &Path_points[xlt];
468 if (i != pp_xlate[i])
469 Path_points[pp_xlate[
i]] = Path_points[
i];
479 int addval = curval & 1;
483 curval |= 0x80000000;
496 unsigned int hashval = 0;
499 ip = (
int *) &objp->
orient;
501 for (i=0; i<9; i++) {
502 hashval =
hash(hashval, *ip);
506 ip = (
int *) &objp->
pos;
508 for (i=0; i<3; i++) {
509 hashval =
hash(hashval, *ip);
518 if(Ai_classes != NULL)
777 aicp = &Ai_classes[
i];
783 #define AI_CLASS_INCREMENT 10
838 mprintf((
"TABLES: Unable to parse '%s'! Error message = %s.\n",
"ai.tbl", e.what()));
906 #define STEALTH_NOT_IN_FRUSTUM 0
907 #define STEALTH_IN_FRUSTUM 1
908 #define STEALTH_FULLY_TARGETABLE 2
969 vec3d vec_to_stealth;
970 float dot_to_stealth, dist_to_stealth, max_stealth_dist;
983 dot_to_stealth =
vm_vec_dot(&viewer_objp->
orient.
vec.fvec, &vec_to_stealth) / dist_to_stealth;
989 float needed_dot_to_stealth;
990 if (dist_to_stealth < 100) {
991 needed_dot_to_stealth = 0.0f;
995 if (dot_to_stealth > needed_dot_to_stealth) {
996 if (dist_to_stealth < max_stealth_dist) {
1026 if (from_dot != NULL)
1041 object *stealth_objp;
1058 object *
objp, *weapon_objp;
1060 float old_dist, new_dist;
1061 float old_dot, new_dot;
1062 object *old_weapon_objp = NULL;
1064 if ((attacked_objnum == -1) || (weapon_objnum == -1)) {
1068 objp = &
Objects[attacked_objnum];
1076 weapon_objp = &
Objects[weapon_objnum];
1102 new_dist =
compute_dots(weapon_objp, objp, &new_dot, NULL);
1105 if (new_dist < 1500.0
f) {
1106 if (new_dot > 0.5
f) {
1112 Assert(old_weapon_objp != NULL);
1113 old_dist =
compute_dots(old_weapon_objp, objp, &old_dot, NULL);
1115 if (old_dot < 0.5
f) {
1120 if ((new_dot > 0.5
f) && (new_dot > old_dot-0.01
f)) {
1121 if (new_dist < old_dist) {
1134 vec3d vel_in, vel_out, desired_fvec,
src;
1137 vec3d vel_limit, acc_limit;
1150 curr_orient = objp->
orient;
1153 if(turn_time > 0.0
f)
1156 if (!(flags &
AITTV_FAST) && !(sexp_flags & AITTV_VIA_SEXP) ){
1165 vel_limit.
xyz.x =
PI2/turn_time;
1166 vel_limit.
xyz.y =
PI2/turn_time;
1167 vel_limit.
xyz.z =
PI2/turn_time;
1176 acc_limit = vel_limit;
1182 if (rel_pos != NULL) {
1193 if (slide_vec != NULL) {
1203 delta_bank = bank_override;
1206 delta_bank = 100.0f * (1.0f - delta_bank);
1208 delta_bank = -delta_bank;
1221 vel_in_copy = vel_in;
1222 objp_orient_copy = objp->
orient;
1224 vel_in = vel_in_copy;
1225 objp->
orient = objp_orient_copy;
1229 matrix out_orient, goal_orient;
1232 vm_matrix_interpolate(&goal_orient, &curr_orient, &vel_in, delta_time, &out_orient, &vel_out, &vel_limit, &acc_limit);
1233 objp->
orient = out_orient;
1381 if (delta_accel < 0.0
f) {
1389 if (new_accel > 1.0
f)
1391 else if (new_accel < -1.0
f)
1407 max_speed = Ships[objp->
instance].current_max_speed;
1409 if (max_speed > 0.0
f) {
1410 AI_ci.
forward = tspeed/max_speed;
1459 vec3d dest_vec, v2c, perp_vec, temp_vec,
v2;
1476 ratio = dist / radius;
1515 vec3d opposite_point;
1538 vec3d up_vec, perp_vec;
1560 vec3d r_vec, theta_vec;
1561 vec3d center_vec, vec_on_cylinder, sph_r_vec;
1582 Assert(mag > 0.9999 && mag < 1.0001);
1591 Assert( dot >0.9999 && dot < 1.0001);
1613 vec3d up_vec, perp_vec;
1666 if ( radar_return > 0.4 ) {
1723 float lowest_max_speed;
1734 wingnum = aip->
wing;
1738 if ( wingnum == -1 )
1739 return lowest_max_speed;
1758 lowest_max_speed = cur_max;
1763 return lowest_max_speed;
1773 float lowest_max_av_ab_speed;
1774 float recharge_scale;
1787 wingnum = aip->
wing;
1800 if ( wingnum == -1 )
1801 return lowest_max_av_ab_speed;
1830 lowest_max_av_ab_speed = cur_max;
1835 return lowest_max_av_ab_speed;
1851 else if (*ignore_objnum >= 0)
1854 if (
Objects[*ignore_objnum].signature != *ignore_signature)
1860 else if (*ignore_objnum == objnum)
1898 if (!just_the_original)
2000 dist *= (
float) (num_attacking+2)/2.0f;
2007 if (dist < eno->nearest_dist) {
2031 object *danger_weapon_objp;
2060 if (danger_weapon_objp->
parent >= 0) {
2099 nearest_objnum = -1;
2100 nearest_dist =
range;
2122 if (dist < nearest_dist) {
2123 nearest_dist = dist;
2131 return nearest_objnum;
2188 int enemy_team_mask;
2207 return target_objnum;
2262 Assert(attacker != NULL);
2271 if (attacker == attacked) {
2278 nprintf((
"AI",
"AI ship %s is large ship ordered to attack %s\n", Ships[attacker->
instance].ship_name, Ships[attacked->
instance].ship_name));
2282 if (attacked == NULL) {
2330 Assert(attacker != NULL);
2371 if (evaded == evader) {
2396 int next_occupied_index = -1;
2416 next_occupied_index =
i;
2421 if (next_occupied_index < 0)
2422 return current_index;
2448 Assert(ignorer != ignored);
2516 if (modify_index == -1) {
2522 pnp = &Path_points[modify_index];
2545 tvec.
xyz.x = -tvec.
xyz.z;
2547 tvec.
xyz.y = -tvec.
xyz.x;
2582 radius = goalobjp->
radius;
2627 int start_index, finish_index;
2628 vec3d submodel_offset, local_vert;
2629 bool rotating_submodel;
2643 start_index = mp->
nverts-1;
2644 finish_index =
MAX(-1, mp->
nverts-1-count);
2653 rotating_submodel =
true;
2659 rotating_submodel =
false;
2663 for (i=start_index; i != finish_index; i += dir)
2667 if (rotating_submodel)
2680 if ( randomize_pnt == i ) {
2688 pp_index = pnp-Path_points +
offset;
2727 Assert(path_num < pm->n_paths);
2729 mp = &pm->
paths[path_num];
2741 perim_point2 = pl_objp->
pos;
2761 if ( subsys_path ) {
2819 Assert(path_num < pm->n_paths);
2821 mp = &pm->
paths[path_num];
2849 if (big_only_flag) {
2854 if ((objp != ignore_objp1) && (objp != ignore_objp2)) {
2855 if (
pp_collide(curpos, goalpos, objp, radius))
2875 void ai_find_path(
object *pl_objp,
int objnum,
int path_num,
int exit_flag,
int subsys_path)
2891 Error(
LOCATION,
"ai_find_path tring to find a path (%d) that doesn't exist, on ship %s", path_num, shipp->
ship_name);
2914 vec3d cur_pos, new_goal_pos;
2915 object *player_objp;
2916 vec3d n_vec_to_goal, n_vec_to_player;
2934 if (obj_obj_dist > speed_time*2.0
f)
2937 cur_pos = objp->
pos;
2939 new_goal_pos = *goal_pos;
2944 if (dist > speed_time*2.0f) {
2953 vm_vec_sub(&avoid_vec, &n_vec_to_goal, &n_vec_to_player);
2998 if (view_pos != NULL)
3010 Assert((docker != NULL) && (dockee != NULL));
3015 Warning(
LOCATION,
"Call to ai_do_objects_docked_stuff when objects are already docked! Trace out and fix!\n");
3056 Assert((docker != NULL) && (dockee != NULL));
3061 Warning(
LOCATION,
"Call to ai_do_objects_undocked_stuff when objects are already undocked! Trace out and fix!\n");
3108 Assert(docker_index >= 0);
3109 Assert(dockee_index >= 0);
3118 switch (dock_type) {
3173 Error(
LOCATION,
"Cannot find a dock path for ship %s, dock index %d. Aborting dock.\n", Ships[dockee->
instance].ship_name, dockee_index);
3226 Warning(
LOCATION,
"aip->wp_index should have been assigned already!");
3258 Assert(objp != other_objp);
3299 if ( objp == goal_objp ) {
3304 Assert(objp != goal_objp);
3311 nprintf((
"AI",
"Warning: Ship %s tried to form on player's wing, but not fighter or bomber.\n", shipp->
ship_name));
3351 #define BIGNUM 100000.0f
3363 vec3d vec_to_target;
3368 vm_vec_sub(&vec_to_target, targpos, attackpos);
3369 pos_dot_vel =
vm_vec_dot(&vec_to_target, targvel);
3370 vel_sqr =
vm_vec_dot(targvel, targvel) - weapon_speed*weapon_speed;
3371 discrim = pos_dot_vel*pos_dot_vel - vel_sqr*
vm_vec_dot(&vec_to_target, &vec_to_target);
3373 if (discrim > 0.0
f) {
3374 float t1,
t2, t_solve;
3376 t1 = (-pos_dot_vel +
fl_sqrt(discrim)) / vel_sqr;
3377 t2 = (-pos_dot_vel -
fl_sqrt(discrim)) / vel_sqr;
3383 if ((t2 > 0.0
f) && (t2 < t_solve))
3405 float time_to_enemy;
3407 float max_laser_distance, max_laser_speed;
3408 int bank_num, weapon_num;
3419 if (dist_to_enemy > 1.5 * max_laser_distance) {
3420 if (pl_speed > 0.0
f)
3421 time_to_enemy = dist_to_enemy/pl_speed;
3423 time_to_enemy = 1.0f;
3424 }
else if (dist_to_enemy > 1.1*max_laser_distance) {
3425 if (pl_speed > 0.1
f) {
3428 scale = (
float) ((dist_to_enemy - max_laser_distance) / max_laser_distance);
3430 time_to_enemy = (
float) (dist_to_enemy/(pl_speed * scale + max_laser_speed * (1.0
f - scale)));
3432 time_to_enemy = 2.0f;
3434 time_to_enemy = (
float) (dist_to_enemy/max_laser_speed);
3464 *tts = dist * 100.0f;
3471 *player_pos = pl_objp->
pos;
3476 *enemy_pos = en_objp->
pos;
3511 pnode *pn = &Path_points[path_cur];
3548 Assert((path_num >= 0) && (path_num < pm->n_paths));
3587 force_recreate_flag = 1;
3611 if (force_recreate_flag || (dist > 2.0
f)) {
3651 }
else if ((prev_dot_to_goal-dot) > 0.01) {
3652 if (prev_dot_to_goal > dot + 0.05
f) {
3665 float speed_mult = FLT_MIN;
3669 speed_mult = gsip->
pathMetadata[pathName].arrive_speed_mult;
3672 if (speed_mult == FLT_MIN) {
3677 speed_mult = gsip->
pathMetadata[pathName].depart_speed_mult;
3680 if (speed_mult == FLT_MIN) {
3685 if (speed_mult != FLT_MIN && speed_mult != 1.0
f) {
3698 max_bay_speed = sip->
max_speed * (speed_mult + (1.0f - speed_mult) * (dist_to_end / total_path_length));
3703 if (max_allowed_speed <= 0)
3704 max_allowed_speed = max_bay_speed;
3706 max_allowed_speed =
MIN(max_allowed_speed, max_bay_speed);
3710 }
else if ((dot_to_next >= dot * .9) || (dist_to_next > 100.0
f)) {
3711 if (dist_to_goal > 200.0
f) {
3717 xdot = (dot_to_next + dot)/2.0
f;
3735 xdot =
MAX(dot_to_next, 0.1
f);
3741 speed = dist_to_goal/8.0f + 2.0f;
3742 }
else if (dist_to_goal < 4*objp->radius + 50.0
f) {
3743 speed = dist_to_goal/4.0f + 4.0f;
3745 speed = max_allowed_speed * (3*xdot + 1.0f)/4.0
f;
3748 speed = speed * 2.0f + 1.0f;
3783 float dot, dist_to_goal, dist_to_next, dot_to_next;
3789 vec3d temp_vec, *slop_vec;
3791 vec3d *cvp, *nvp, next_vec, gcvp, gnvp;
3873 else if (mag > 5.0
f) {
3876 if ((nv_dot > 0.5
f) && (nv_dot < 0.97
f)) {
3877 slop_vec = &temp_vec;
3882 if (dist_to_goal > 0.1
f)
3896 vec3d nearest_point;
3897 float r, min_dist_to_goal;
3918 return dist_to_goal;
3927 float dot, dist_to_goal, dist_to_next, dot_to_next;
3934 vec3d *pvp, *cvp, *nvp, next_vec, gpvp, gcvp, gnvp;
3981 if (prev_point >= aip->
path_start && prev_point <= aip->path_start + num_points)
3982 pvp = &Path_points[prev_point].
pos;
3986 gpvp = (pvp != NULL) ? *pvp : *cvp;
4007 vec3d *prvec = NULL;
4015 if (dist_to_goal > 0.1
f) {
4017 vec3d closest_point;
4024 vm_vec_avg3(&midpoint, &gcvp, &closest_point, &closest_point);
4038 float max_allowed_speed = 0.0f;
4043 set_accel_for_docking(Pl_objp, aip, dot, dot_to_next, dist_to_next, dist_to_goal, sip, max_allowed_speed, gobjp);
4049 vec3d nearest_point;
4050 float r, min_dist_to_goal;
4063 float goal_mag = 0.0f;
4084 return dist_to_goal;
4108 else if (val > *max)
4127 *min_vec = objp->
pos;
4128 *max_vec = objp->
pos;
4150 int enemy_team_mask;
4151 vec3d min_vec, max_vec;
4152 vec3d vec_to_center, center;
4186 dot_val = (1.1f + dot) / 2.0
f;
4187 if (dist > 200.0
f) {
4249 float dot, dist_to_goal;
4255 float prev_dot_to_goal;
4259 bool ab_allowed =
true;
4261 Assert( target_pos != NULL );
4263 if ( pl_done_p != NULL ) {
4296 }
else if (mag > 5.0
f) {
4299 if ((nv_dot > 0.5
f) && (nv_dot < 0.97
f)) {
4300 slop_vec = &temp_vec;
4308 if (aip->
wing >= 0) {
4310 scale = (
int) ((scale+1)/2);
4321 vec3d perp, goal_point;
4333 Assertion( Autopilot_flight_leader != NULL,
"When under autopilot there must be a flight leader" );
4336 if (aip->
wing != -1) {
4340 if (wing_leader != Pl_objp) {
4351 vm_vec_add(&goal_point, &Autopilot_flight_leader->
pos, &perp);
4358 vm_vec_add(&goal_point, &Autopilot_flight_leader->
pos, &perp);
4364 Pl_objp->
pos = goal_point;
4374 if (dist_to_goal > 0.1
f) {
4388 }
else if (prev_dot_to_goal > dot+0.01
f) {
4392 }
else if (dist_to_goal < 100.0
f) {
4394 if (
fl_abs(slew_dot) < 0.9
f) {
4396 }
else if (dot < 0.88
f + 0.1
f*(100.0
f - dist_to_goal)/100.0
f) {
4404 if (dist_to_goal < 250.0
f) {
4414 if (dist_to_goal > 100.0
f + Pl_objp->
radius * 2) {
4434 float max_allowed_speed = 9999.9f;
4435 float max_allowed_ab_speed = 10000.1f;
4436 float self_ab_speed = 10000.0f;
4451 max_allowed_ab_speed = max_allowed_speed;
4454 if ((self_ab_speed < 5.0
f) || (max_allowed_ab_speed < 5.0f)){
4456 }
else if (max_allowed_ab_speed < shipp->current_max_speed) {
4457 if (max_allowed_speed < max_allowed_ab_speed) {
4458 max_allowed_speed = max_allowed_ab_speed;
4466 if (self_ab_speed <= (1.001 * max_allowed_ab_speed)){
4469 if (percent_left > 30.0
f) {
4476 if ((self_ab_speed - max_allowed_ab_speed) > (0.2
f * max_allowed_ab_speed)) {
4477 switch_value = 0.2f * max_allowed_ab_speed;
4479 switch_value = self_ab_speed - max_allowed_ab_speed;
4483 if ((Pl_objp->
phys_info.
speed < (max_allowed_ab_speed - switch_value)) && (percent_left > 30.0f)){
4488 if (Pl_objp->
phys_info.
speed > (max_allowed_ab_speed + 0.9 * switch_value)){
4509 vec3d nearest_point;
4523 if ( Ships[Pl_objp->
instance].wingnum != -1 ) {
4537 if ( pl_done_p != NULL ) {
4539 if ( pl_treat_as_ship_p != NULL ) {
4540 if ( treat_as_ship ) {
4541 *pl_treat_as_ship_p =
true;
4543 *pl_treat_as_ship_p =
false;
4548 "pl_done_p cannot be NULL while pl_treat_as_ship_p is not NULL" );
4573 bool done, treat_as_ship;
4593 if ( treat_as_ship ) {
4610 object* target_p = NULL;
4616 "ai_fly_to_ship called for '%s' when ai_info.mode not equal to AIM_FLY_TO_SHIP. Is actually '%d'",
4617 Ships[Pl_objp->
instance].ship_name,
4624 "'%s' is trying to fly-to a ship without an active AI_GOAL\n\n"
4625 "Active ai mode is '%d'",
4626 Ships[Pl_objp->
instance].ship_name,
4633 Warning(
LOCATION,
"'%s' is trying to fly-to-ship without a name for the ship", Ships[Pl_objp->
instance].ship_name);
4643 target_p = &
Objects[Ships[j].objnum];
4648 if ( target_p == NULL ) {
4654 mprintf((
"Ship '%s' told to fly to target ship '%s'",
4655 Ships[Pl_objp->
instance].ship_name,
4660 Warning(
LOCATION,
"Ship '%s' told to fly to a ship but none of the ships it was told to fly to exist.\n"
4661 "See log before this message for list of ships set as fly-to tagets",
4662 Ships[Pl_objp->
instance].ship_name);
4667 bool done, treat_as_ship;
4674 if ( treat_as_ship ) {
4705 vec3d player_pos, enemy_pos;
4733 if (away_dot < 0.0
f) {
4747 else if (dist < 2*radsum)
4784 if ((En_objp != NULL) && (En_objp->
type ==
OBJ_SHIP)) {
4818 return !(t % modulus);
4842 if (dist < 150.0
f) {
4850 if (dot * dist > 50.0
f)
4875 if (en_objp != NULL) {
4906 object *weapon_objp = NULL;
4907 object *unlocked_weapon_objp = NULL, *locked_weapon_objp = NULL;
4908 vec3d weapon_pos, player_pos, goal_point;
4909 vec3d vec_from_enemy;
4910 float dot_from_enemy, dot_to_enemy;
4932 if (locked_weapon_objp != NULL) {
4933 if (unlocked_weapon_objp != NULL) {
4935 weapon_objp = locked_weapon_objp;
4937 weapon_objp = unlocked_weapon_objp;
4939 weapon_objp = locked_weapon_objp;
4940 }
else if (unlocked_weapon_objp != NULL)
4941 weapon_objp = unlocked_weapon_objp;
4948 Assert(weapon_objp != NULL);
4956 weapon_pos = weapon_objp->
pos;
4957 player_pos = Pl_objp->
pos;
4968 if (dot_from_enemy < 0.3
f) {
4969 if (weapon_objp == unlocked_weapon_objp)
4972 }
else if (dot_from_enemy > 0.7
f) {
4973 if (dist < 200.0
f) {
4983 if ((dot_to_enemy < -0.5
f) || (dot_to_enemy > 0.5
f)) {
4994 if ((udot < -0.5
f) || (udot > 0.5
f))
5001 if ((rdot < -0.5
f) || (rdot > 0.5
f))
5030 float dot_from_enemy;
5031 vec3d vec_from_enemy, vec_to_goal;
5069 if (dist < 200.0
f) {
5070 if (dot_from_enemy < 0.7
f)
5075 if (dot_from_enemy < 0.7
f) {
5089 vec3d player_pos, enemy_pos, goal_point;
5090 vec3d vec_from_enemy;
5091 float dot_from_enemy;
5096 float bank_override = 0.0f;
5113 if (percent_left > 30.0
f + ((
OBJ_INDEX(Pl_objp)) & 0x0f)) {
5124 vm_vec_sub(&vec_from_enemy, &player_pos, &enemy_pos);
5129 if (dist > 250.0
f) {
5138 }
else if (dot_from_enemy < 0.1
f) {
5141 }
else if (dot_from_enemy > 0.9
f) {
5146 vm_vec_sub(&vec_to_enemy, &enemy_pos, &player_pos);
5150 if (dot_to_enemy > 0.75
f) {
5168 psrandval = psrandval/16.0f;
5180 temp = ((temp * (temp+1)) % 16)/2 - 4;
5181 if ((psrandval == 0) && (temp == 0))
5184 scale = 200.0f *
temp;