1 /*
2  * Copyright (C) Volition, Inc. 1999.  All rights reserved.
3  *
4  * All source code herein is the property of Volition, Inc. You may not sell
5  * or otherwise commercially exploit the source or things you created based on the
6  * source.
7  *
8 */
9 
10 
11 
12 #include "ai/aibig.h"
13 #include "globalincs/linklist.h"
14 #include "object/object.h"
15 #include "ship/ship.h"
16 #include "ship/afterburner.h"
17 #include "freespace2/freespace.h"
18 #include "weapon/weapon.h"
19 #include "io/timer.h"
20 #include "mission/missionparse.h"
21 #include "iff_defs/iff_defs.h"
22 #include "math/staticrand.h"
23 #include "ai/aigoals.h"
24 
25 
26 
27 #pragma optimize("", off)
28 #pragma auto_inline(off)
29 
30 #define SCAN_FIGHTERS_INTERVAL	2000		// how often an AI fighter/bomber should scan for enemy fighter/bombers
31 														// if sitting still and pounding on a big ship.  If enemy fighters are
32 														// close ( < ENTER_STRAFE_THREAT_DIST ), then enter AIM_STRAFE
33 
34 #define ENTER_STRAFE_THREAT_DIST_SQUARED	360000	// use squared distance, instead of 600
35 
36 #define MIN_DOT_TO_ATTACK_SUBSYS				0.7f
37 #define MIN_DOT_TO_ATTACK_MOVING_SUBSYS	0.97f
38 
39 // AI BIG MAGIC NUMBERS
40 #define	STRAFE_RETREAT_COLLIDE_TIME	2.0		// when anticipated collision time is less than this, begin retreat
41 #define	STRAFE_RETREAT_COLLIDE_DIST	100		// when perpendicular distance to *surface* is less than this, begin retreat
42 #define	STRAFE_RETREAT_BOX_DIST			300		// distance beyond the bounding box to retreat
43 #define STRAFE_MAX_UNHIT_TIME		20		// Maximum amount of time to stay in strafe mode if not hit
44 
45 #define	EVADE_BOX_BASE_DISTANCE			300		// standard distance to end evade submode
46 #define	EVADE_BOX_MIN_DISTANCE			200		// minimun distance to end evade submode, after long time
47 
48 #define	ATTACK_STOP_DISTANCE				150		// when distance to target is less than this, put on brakes
49 
50 #define	ATTACK_COLLIDE_BASE_DIST		300		// absolute distance at which to begin checking for possible collision
51 #define	ATTACK_COLLIDE_AVOID_DIST		60			// perpendicular distance to attack surface at which begin avoiding
52 #define	ATTACK_COLLIDE_AVOID_TIME		1.0		// anticipated collision time at which to begin evade
53 #define	ATTACK_COLLIDE_SLOW_DIST		150		// perpendicular distance to attack surface at which begin slowing down
54 #define	ATTACK_COLLIDE_SLOW_TIME		1.5		// anticipated collision time at which to begin slowing down
55 
56 #define GLIDE_STRAFE_DISTANCE			50.0f	//Distance from the ship to pass when glide strafing
57 #define GLIDE_STRAFE_MIN_TIME			2	//Minimum amount of time to stay on one glide strafe approach vector
58 #define GLIDE_STRAFE_MAX_TIME			15	//Maximum amount of time for each glide strafe pass
59 
60 // forward declarations
61 void	ai_big_evade_ship();
62 void	ai_big_chase_attack(ai_info *aip, ship_info *sip, vec3d *enemy_pos, float dist_to_enemy, int modelnum);
63 void	ai_big_avoid_ship();
64 int	ai_big_maybe_follow_subsys_path(int do_dot_check=1);
65 void ai_big_strafe_position();
66 
67 extern int model_which_octant_distant_many( vec3d *pnt, int model_num,matrix *model_orient, vec3d * model_pos, polymodel **pm, int *octs);
68 extern void compute_desired_rvec(vec3d *rvec, vec3d *goal_pos, vec3d *cur_pos);
69 extern void big_ship_collide_recover_start(object *objp, object *big_objp, vec3d *collide_pos, vec3d *collision_normal);
70 
71 
72 //	Called by ai_big_pick_attack_point.
73 //	Generates a random attack point.
74 //	If truly_random flag set (haha), then generate a pretty random number.  Otherwise, generate a static rand which
75 //	tends to not change from frame to frame.
76 //	Try four times and choose nearest point to increase chance of getting a good point.
ai_bpap(object * objp,vec3d * attacker_objp_pos,vec3d * attacker_objp_fvec,vec3d * attack_point,vec3d * local_attack_point,float fov,float weapon_travel_dist,vec3d * surface_normal,ship_subsys * ss)77 void ai_bpap(object *objp, vec3d *attacker_objp_pos, vec3d *attacker_objp_fvec, vec3d *attack_point, vec3d *local_attack_point, float fov, float weapon_travel_dist, vec3d *surface_normal, ship_subsys *ss)
78 {
79 	float		nearest_dist;
80 	vec3d	result_point, best_point;
81 	vec3d	rel_point;
82 	int		num_tries;
83 	model_octant	*octp;
84 	polymodel	*pm;
85 	int		i, q, octs[4];
86 	ship_info *sip = &Ship_info[Ships[objp->instance].ship_info_index];
87 	model_subsystem *tp = NULL;
88 	if (ss != NULL)
89 		tp = ss->system_info;
90 
91 	best_point = objp->pos;
92 	nearest_dist = weapon_travel_dist;
93 
94 	model_which_octant_distant_many(attacker_objp_pos, sip->model_num, &objp->orient, &objp->pos, &pm, octs);
95 
96 	num_tries = (int) (vm_vec_dist(&objp->pos, attacker_objp_pos)/objp->radius);
97 
98 	if (num_tries >= 4)
99 		num_tries = 1;
100 	else
101 		num_tries = 4 - num_tries;
102 
103 	//	Index #0 is best one.
104 	if ( pm->octants[octs[0]].verts ) {
105 		*local_attack_point = *pm->octants[octs[0]].verts[0];	//	Set just in case it doesn't get set below.
106 	} else {
107 		vm_vec_zero(local_attack_point);
108 	}
109 
110 	for (q=0; q<4; q++) {
111 		octp = &pm->octants[octs[q]];
112 		if (octp->nverts > 0) {
113 
114 			if (num_tries > octp->nverts)
115 				num_tries = octp->nverts;
116 
117 			if (num_tries > octp->nverts)
118 				num_tries = octp->nverts;
119 
120 			for (i=0; i<num_tries; i++) {
121 				int	index;
122 				float	dist, dot;
123 				vec3d	v2p;
124 
125 				index = (int) (frand() * (octp->nverts));
126 
127 				rel_point = *octp->verts[index];
128 				vm_vec_unrotate(&result_point, &rel_point, &objp->orient);
129 				vm_vec_add2(&result_point, &objp->pos);
130 
131 				dist = vm_vec_normalized_dir(&v2p, &result_point, attacker_objp_pos);
132 				bool in_fov = false;
133 
134 				dot = vm_vec_dot(&v2p, attacker_objp_fvec);
135 				if (tp == NULL) {
136 					if (dot > fov)
137 						in_fov = true;
138 				} else {
139 					in_fov = turret_fov_test(ss, attacker_objp_fvec, &v2p);
140 				}
141 
142 				if (in_fov) {
143 					if (dist < nearest_dist) {
144 						nearest_dist = dist;
145 						best_point = result_point;
146 						*local_attack_point = rel_point;
147 						Assert( !vm_is_vec_nan(local_attack_point) );
148 						if (dot > (1.0f + fov)/2.0f)	//	If this point is quite good, quit searching for a better one.
149 							goto done_1;
150 					}
151 				}
152 			}
153 		}
154 	}
155 done_1:
156 
157 	*attack_point = best_point;
158 
159 	// Cast from attack_objp_pos to local_attack_pos and check for nearest collision.
160 	// If no collision, cast to (0,0,0) [center of big ship]**  [best_point initialized to 000]
161 
162 	// do in world coords to get attack point, then translate to local for local_attack_point
163 	vec3d attack_dir, end_point, temp;
164 	float dist;
165 	dist = vm_vec_normalized_dir(&attack_dir, attack_point, attacker_objp_pos);
166 
167 	if (dist > 0.1) {
168 		vm_vec_scale_add(&end_point, attack_point, &attack_dir, 30.0f);
169 	} else {
170 		vm_vec_scale_add(&end_point, attack_point, attacker_objp_fvec, 30.0f);
171 	}
172 
173 	mc_info mc;
174 	mc_info_init(&mc);
175 
176 	mc.model_instance_num = Ships[objp->instance].model_instance_num;
177 	mc.model_num = sip->model_num;
178 	mc.orient = &objp->orient;
179 	mc.pos = &objp->pos;
180 	mc.p0 = attacker_objp_pos;
181 	mc.p1 = &end_point;
182 	mc.flags = MC_CHECK_MODEL;
183 	mc.radius = 0.0f;
184 	model_collide(&mc);
185 
186 	if (mc.num_hits > 0) {
187 		*attack_point = mc.hit_point_world;
188 		vm_vec_sub(&temp, attack_point, &objp->pos);
189 		vm_vec_rotate(local_attack_point, &temp, &objp->orient);
190 		if (surface_normal) {
191 			vm_vec_unrotate(surface_normal, &mc.hit_normal, &objp->orient);
192 		}
193 	} else {
194 		vm_vec_zero(local_attack_point);
195 		*attack_point = objp->pos;
196 		if (surface_normal) {
197 			vm_vec_zero(surface_normal);
198 		}
199 	}
200 }
201 
202 //	Stuff a point to attack based on nearest octant.
203 //	If no points in that octant, leave attack_point unmodified.
204 //
205 //	Note: Default value for fov is 1.0f  1.0f means don't use fov parameter.
206 //	If fov != 1.0f, try up to four times to find a point that's in the field of view.
ai_big_pick_attack_point_turret(object * objp,ship_subsys * ssp,vec3d * gpos,vec3d * gvec,vec3d * attack_point,float fov,float weapon_travel_dist)207 void ai_big_pick_attack_point_turret(object *objp, ship_subsys *ssp, vec3d *gpos, vec3d *gvec, vec3d *attack_point, float fov, float weapon_travel_dist)
208 {
209 	if (!timestamp_elapsed(ssp->turret_pick_big_attack_point_timestamp)) {
210 		vec3d	result_point;
211 		vm_vec_unrotate(&result_point, &ssp->turret_big_attack_point, &objp->orient);
212 		vm_vec_add(attack_point, &result_point, &objp->pos);
213 	} else {
214 		vec3d	local_attack_point;
215 		ssp->turret_pick_big_attack_point_timestamp = timestamp(2000 + (int) (frand()*500.0f));
216 		ai_bpap(objp, gpos, gvec, attack_point, &local_attack_point, fov, weapon_travel_dist, NULL, ssp);
217 		ssp->turret_big_attack_point = local_attack_point;
218 	}
219 }
220 
221 //	Stuff a point to attack based on nearest octant.
222 //	If no points in that octant, leave attack_point unmodified.
223 //
224 //	Note: Default value for fov is 1.0f  1.0f means don't use fov parameter.
225 //	If fov != 1.0f, try up to four times to find a point that's in the field of view.
226 //	Note, attacker_objp can be a ship or a weapon.
ai_big_pick_attack_point(object * objp,object * attacker_objp,vec3d * attack_point,float fov)227 void ai_big_pick_attack_point(object *objp, object *attacker_objp, vec3d *attack_point, float fov)
228 {
229 	Assert(objp->instance > -1);
230 	Assert(objp->type == OBJ_SHIP);
231 
232 	vec3d	local_attack_point;
233 
234 	switch (attacker_objp->type) {
235 	case OBJ_SHIP: {
236 		ai_info	*attacker_aip;
237 		attacker_aip = &Ai_info[Ships[attacker_objp->instance].ai_index];
238 		if (!timestamp_elapsed(attacker_aip->pick_big_attack_point_timestamp)) {
239 			vec3d	result_point;
240 
241 			vm_vec_unrotate(&result_point, &attacker_aip->big_attack_point, &objp->orient);
242 			vm_vec_add(attack_point, &result_point, &objp->pos);
243 
244 			return;
245 		}
246 
247 		attacker_aip->pick_big_attack_point_timestamp = timestamp(2000 + (int) (frand()*500.0f));
248 		break;
249 						}
250 	case OBJ_WEAPON: {
251 		weapon	*wp = &Weapons[attacker_objp->instance];
252 
253 		if (!timestamp_elapsed(wp->pick_big_attack_point_timestamp)) {
254 			vec3d	result_point;
255 
256 			vm_vec_unrotate(&result_point, &wp->big_attack_point, &objp->orient);
257 			vm_vec_add(attack_point, &result_point, &objp->pos);
258 
259 			return;
260 		}
261 		wp->pick_big_attack_point_timestamp = timestamp(2000 + (int) (frand()*500.0f));
262 
263 		break;
264 						  }
265 	}
266 
267 	// checks valid line to target
268 	vec3d surface_normal;
269 	ai_bpap(objp, &attacker_objp->pos, &attacker_objp->orient.vec.fvec, attack_point, &local_attack_point, fov, 99999.9f, &surface_normal, NULL);
270 
271 	switch (attacker_objp->type) {
272 	case OBJ_SHIP: {
273 		ai_info	*attacker_aip;
274 		// if we can't find a new local_attack_point don't change local_attack_point
275 		if (vm_vec_mag_squared(&local_attack_point) < 1) {
276 			return;
277 		}
278 
279 		attacker_aip = &Ai_info[Ships[attacker_objp->instance].ai_index];
280 		attacker_aip->big_attack_point = local_attack_point;
281 		attacker_aip->big_attack_surface_normal = surface_normal;
282 		break;
283 						}
284 	case OBJ_WEAPON: {
285 		weapon	*wp = &Weapons[attacker_objp->instance];
286 		wp->big_attack_point = local_attack_point;
287 		Assert( !vm_is_vec_nan(&wp->big_attack_point) );
288 		break;
289 						  }
290 	}
291 
292 	return;
293 }
294 
295 // Handler for SM_EVADE submode ( called from ai_big_chase() )
ai_big_evade_ship()296 void ai_big_evade_ship()
297 {
298 	vec3d	player_pos, enemy_pos;
299 	float		dist;
300 	ship		*shipp = &Ships[Pl_objp->instance];
301 	ai_info	*aip = &Ai_info[shipp->ai_index];
302 	vec3d	randvec, semi_enemy_pos;
303 
304 	ai_set_positions(Pl_objp, En_objp, aip, &player_pos, &enemy_pos);
305 
306 	dist = vm_vec_dist_quick(&player_pos, &enemy_pos);
307 	vm_vec_rand_vec_quick(&randvec);
308 	if ((Missiontime>>14) & 1) {
309 		vm_vec_scale_add(&semi_enemy_pos, &enemy_pos, &randvec, dist/2.0f);
310 		aip->prev_goal_point = semi_enemy_pos;
311 	} else {
312 		semi_enemy_pos = aip->prev_goal_point;
313 	}
314 
315 	accelerate_ship(aip, 1.0f - ((Missiontime>>8) & 0x3f)/128.0f );
316 	turn_away_from_point(Pl_objp, &semi_enemy_pos, 0.0f);
317 
318 	float box_dist;
319 	vec3d box_vec;
320 	int is_inside;
321 	box_dist = get_world_closest_box_point_with_delta(&box_vec, En_objp, &player_pos, &is_inside, EVADE_BOX_BASE_DISTANCE);
322 	if (box_dist > EVADE_BOX_BASE_DISTANCE) {
323 		aip->submode = SM_ATTACK;
324 		aip->submode_start_time = Missiontime;
325 	} else if ((box_dist > EVADE_BOX_MIN_DISTANCE) && (Missiontime - aip->submode_start_time > i2f(5)) ) {
326 		aip->submode = SM_ATTACK;
327 		aip->submode_start_time = Missiontime;
328 	}
329 
330 	//TODO TEST
331 
332 /*	if (dist > 4*En_objp->radius) {
333 		aip->submode = SM_ATTACK;
334 		aip->submode_start_time = Missiontime;
335 	} else if (dist > En_objp->radius) {
336 		if (Missiontime - aip->submode_start_time > i2f(5)) {
337 			aip->submode = SM_ATTACK;
338 			aip->submode_start_time = Missiontime;
339 		}
340 	} */
341 }
342 
343 // Handler for SM_AVOID submode ( called from ai_big_chase() )
ai_big_avoid_ship()344 void ai_big_avoid_ship()
345 {
346 	ai_big_evade_ship();
347 }
348 
349 // reset path following information
ai_big_subsys_path_cleanup(ai_info * aip)350 void ai_big_subsys_path_cleanup(ai_info *aip)
351 {
352 	if ( aip->ai_flags & AIF_ON_SUBSYS_PATH ) {
353 		aip->ai_flags &= ~AIF_ON_SUBSYS_PATH;
354 		aip->path_goal_dist = -1;
355 		aip->path_start = -1;
356 		aip->path_cur = -1;
357 		aip->path_length = 0;
358 	}
359 }
360 
361 // Maybe Pl_objp needs to follow a path to get in line-of-sight to a subsystem
362 // input:	do_dot_check	=>	default value 0, flag to indicate whether check should be done to ensure
363 //										subsystem is within certain field of view.  We don't want to check fov when
364 //										strafing, since ship is weaving to avoid turret fire
ai_big_maybe_follow_subsys_path(int do_dot_check)365 int ai_big_maybe_follow_subsys_path(int do_dot_check)
366 {
367 	ai_info	*aip;
368 	float		dot = 1.0f, min_dot;
369 	object	*target_objp;
370 
371 	aip = &Ai_info[Ships[Pl_objp->instance].ai_index];
372 	target_objp = &Objects[aip->target_objnum];
373 
374 	if ( (aip->targeted_subsys != NULL) && (aip->target_objnum >= 0) && (aip->targeted_subsys->system_info->path_num >= 0) ) {
375 		int			subsys_path_num, subsys_in_sight, checked_sight;
376 		float			dist;
377 
378 		// Get models of both source and target
379 		polymodel *pm = model_get( Ship_info[Ships[Pl_objp->instance].ship_info_index].model_num );
380 		polymodel *pm_t = model_get( Ship_info[Ships[target_objp->instance].ship_info_index].model_num );
381 
382 		// Necessary sanity check
383 		Assertion(aip->targeted_subsys->system_info->path_num < pm_t->n_paths, "Invalid Path number %d for subsystem %s on ship %s (Model: %s)\n", aip->targeted_subsys->system_info->path_num, aip->targeted_subsys->system_info->name, Ship_info[Ships[target_objp->instance].ship_info_index].name, pm_t->filename );
384 		if (aip->targeted_subsys->system_info->path_num >= pm_t->n_paths)
385 			return 0;
386 
387 		// If attacking a subsystem, ensure that we have an unobstructed line of sight... if not, then move
388 		// towards path linked to subsystem
389 		subsys_in_sight = 0;	// assume Pl_objp doesn't have line of sight to subys
390 
391 		// only check for subsystem sight every N milliseconds
392 		checked_sight = 0;
393 		if ( timestamp_elapsed(aip->path_subsystem_next_check) ) {
394 			vec3d		geye, gsubpos;
395 			eye			*ep;
396 
397 			aip->path_subsystem_next_check = timestamp(1500);
398 
399 			// get world pos of eye (stored in geye)
400 			ep = &(pm->view_positions[Ships[Pl_objp->instance].current_viewpoint]);
401 			model_find_world_point( &geye, &ep->pnt, pm->id, 0, &Pl_objp->orient, &Pl_objp->pos );
402 
403 			// get world pos of subsystem
404 			vm_vec_unrotate(&gsubpos, &aip->targeted_subsys->system_info->pnt, &En_objp->orient);
405 			vm_vec_add2(&gsubpos, &En_objp->pos);
406 
407 			checked_sight = 1;
408 
409 			// Calc dot between subsys normal (based on using path info), and subsys_to_eye vector.  This is
410 			// useful later when we want to decide whether we have a realistic line-of-sight to the subsystem.
411 			vec3d subsys_normal, subsys_to_eye;
412 			if ( do_dot_check ) {
413 				if ( !ship_return_subsys_path_normal(&Ships[target_objp->instance], aip->targeted_subsys, &gsubpos, &subsys_normal) ) {
414 					vm_vec_normalized_dir(&subsys_to_eye, &geye, &gsubpos);
415 					dot = vm_vec_dot(&subsys_normal, &subsys_to_eye);
416 				}
417 			}
418 
419 			if ( ship_subsystem_in_sight(En_objp, aip->targeted_subsys, &geye, &gsubpos, 1) ) {
420 				subsys_in_sight = 1;
421 			}
422 		}
423 
424 		// check if subsystem not in sight
425 		min_dot = (target_objp->phys_info.fspeed > 5.0f?MIN_DOT_TO_ATTACK_MOVING_SUBSYS:MIN_DOT_TO_ATTACK_SUBSYS);
426 		if ( (checked_sight && ((!subsys_in_sight) || (dot < min_dot)) ) )  {
427 
428 			aip->path_goal_dist = 5;
429 			subsys_path_num = aip->targeted_subsys->system_info->path_num;
430 			if ( ((aip->path_start) == -1 || (aip->mp_index != subsys_path_num)) && subsys_path_num < pm_t->n_paths ) {
431 				// maybe create a new path
432 				if ( subsys_path_num >= 0 ) {
433 					Assert(aip->target_objnum >= 0);
434 					ai_find_path(Pl_objp, aip->target_objnum, subsys_path_num, 0, 1);
435 					if ( aip->path_start >= 0 ) {
436 						aip->ai_flags |= AIF_ON_SUBSYS_PATH;
437 					}
438 				}
439 			}
440 		}
441 
442 		if ( checked_sight && subsys_in_sight && (dot > min_dot) ) {
443 			// we've got a clear shot, stay here for a bit
444 			aip->path_subsystem_next_check = timestamp_rand(5000,8000);
445 		}
446 
447 		// If there is a path that we are tracking, see if ship has gotten to within
448 		// aip->path_goal_dist units.  If so, then ship can stop following the path.  This
449 		// is required since we don't want to follow the path all the way to the subsystem,
450 		// and we want ships to stop different distances from their path destination
451 		if ( aip->path_length > 0 ) {
452 			int path_done=0;
453 			int in_view=0;
454 
455 			Assert(aip->path_length >= 2);
456 			dist = vm_vec_dist_quick(&Path_points[aip->path_start+aip->path_length-2].pos, &Pl_objp->pos);
457 
458 			if ( aip->path_cur >= (aip->path_start+aip->path_length-1) ) {
459 				path_done=1;
460 			}
461 
462 			min_dot = (target_objp->phys_info.fspeed > 5.0f?MIN_DOT_TO_ATTACK_MOVING_SUBSYS:MIN_DOT_TO_ATTACK_SUBSYS);
463 			if ( (checked_sight && subsys_in_sight) && (dot > min_dot) ) {
464 				in_view=1;
465 			}
466 
467 			// if we've reached the destination, then we can stop following the path
468 			if ( path_done || ( dist < aip->path_goal_dist ) || in_view ) {
469 				ai_big_subsys_path_cleanup(aip);
470 			} else if ( dist > aip->path_goal_dist ) {
471 				// If we have a path to follow, follow it and return
472 				if ( aip->path_start != -1 ) {
473 					// for now, only follow the path to the first point
474 					if ( aip->path_cur < (aip->path_start+aip->path_length-1) ) {
475 						if ( aip->goal_objnum != aip->target_objnum ) {
476 							aip->previous_mode = aip->mode;
477 							aip->mode = AIM_NONE;
478 							aip->submode = -1;
479 							aip->submode_start_time = Missiontime;
480 							return 1;
481 						}
482 						ai_path();
483 						return 1;
484 					}
485 				}
486 			}
487 		}
488 	}
489 
490 	return 0;
491 }
492 
493 // This function is only called from ai_big_chase_attack() when a ship is flying very slowly and
494 // attacking a big ship.  The ship should scan for enemy fighter/bombers... if any are close, then
495 // return 1, otherwise return 0;
496 //
497 // input: aip	=>	ai_info pointer for Pl_objp
498 //			 sip	=>	ship_info pointer for Pl_objp
499 //
500 // exit:		1	=>	ship should enter strafe mode
501 //				0	=> ship should not change ai mode, no fighter/bomber threats are near
502 //
503 // NOTE: uses SCAN_FIGHTERS_INTERVAL and ENTER_STRAFE_THREAT_DIST_SQUARED which are defined in AiBig.h
ai_big_maybe_start_strafe(ai_info * aip,ship_info * sip)504 int ai_big_maybe_start_strafe(ai_info *aip, ship_info *sip)
505 {
506 	// if moving slowly (or stopped), and SIF_SMALL_SHIP, then enter STRAFE mode if enemy fighter/bombers
507 	// are near
508 	if ( sip->flags & SIF_SMALL_SHIP ) {
509 		if ( timestamp_elapsed(aip->scan_for_enemy_timestamp) ) {
510 			ship_obj	*so;
511 			object	*test_objp;
512 			ship		*test_sp;
513 			float		dist_squared;
514 
515 			aip->scan_for_enemy_timestamp = timestamp(SCAN_FIGHTERS_INTERVAL);
516 			// iterate through ships, and see if any fighter/bomber from opposite team are near
517 			so = GET_FIRST(&Ship_obj_list);
518 			while( so != END_OF_LIST(&Ship_obj_list) ) {
519 				test_objp = &Objects[so->objnum];
520 				test_sp = &Ships[test_objp->instance];
521 
522 				if ( iff_x_attacks_y(Ships[Pl_objp->instance].team, test_sp->team) ) {
523 					if ( Ship_info[test_sp->ship_info_index].flags & SIF_SMALL_SHIP ) {
524 						dist_squared = vm_vec_dist_squared(&Pl_objp->pos, &test_objp->pos);
525 						if ( dist_squared < ENTER_STRAFE_THREAT_DIST_SQUARED ) {
526 							return 1;
527 						}
528 					}
529 				}
530 				so = GET_NEXT(so);
531 			}
532 		}
533 	}
534 	// If we've reached here, there are no enemy fighter/bombers near
535 	return 0;
536 }
537 
538 //	ATTACK submode handler for chase mode.
ai_big_chase_attack(ai_info * aip,ship_info * sip,vec3d * enemy_pos,float dist_to_enemy,int modelnum)539 void ai_big_chase_attack(ai_info *aip, ship_info *sip, vec3d *enemy_pos, float dist_to_enemy, int modelnum)
540 {
541 	int		start_bank;
542 	float		dot_to_enemy, time_to_hit;
543 	polymodel *po = model_get( modelnum );
544 
545 	//	Maybe evade an incoming weapon.
546 	if (((time_to_hit = ai_endangered_by_weapon(aip)) < 4.0f) && (time_to_hit >= 0.0f)) {
547 		aip->submode = SM_EVADE_WEAPON;
548 		aip->submode_start_time = Missiontime;
549 		aip->prev_goal_point = En_objp->pos;
550 	} else {
551 		//	If moving slowly, maybe evade incoming fire.
552 		if (Pl_objp->phys_info.speed < 3.0f) {
553 			object *objp;
554 			for ( objp = GET_FIRST(&obj_used_list); objp !=END_OF_LIST(&obj_used_list); objp = GET_NEXT(objp) ) {
555 				if ((objp->type == OBJ_WEAPON) && (iff_x_attacks_y(Ships[Pl_objp->instance].team, Weapons[objp->instance].team)))
556 					if (Weapon_info[Weapons[objp->instance].weapon_info_index].subtype == WP_LASER) {
557 						vec3d	in_vec;
558 						float		dist;
559 
560 						vm_vec_sub(&in_vec, &objp->pos, &Pl_objp->pos);
561 						if (vm_vec_dot(&in_vec, &objp->orient.vec.fvec) > 0.0f) {
562 							dist = vm_vec_normalize(&in_vec);
563 							if ((dist < 200.0f) && (vm_vec_dot(&in_vec, &objp->orient.vec.fvec) > 0.95f)) {
564 								if ((Objects[objp->parent].signature == objp->parent_sig) && (vm_vec_dist_quick(&objp->pos, &Objects[objp->parent].pos) < 300.0f)) {
565 									set_target_objnum(aip, objp->parent);
566 									aip->submode = SM_ATTACK;
567 									aip->submode_start_time = Missiontime;
568 								} else {
569 									aip->submode = SM_EVADE;
570 									aip->submode_start_time = Missiontime;
571 									aip->prev_goal_point = En_objp->pos;
572 								}
573 							}
574 						}
575 					}
576 			}
577 
578 			// Since ship is moving slowly and attacking a large ship, scan if enemy fighters are near, if so
579 			// then enter strafe mode
580 			if ( ai_big_maybe_start_strafe(aip, sip) ) {
581 				aip->previous_mode = aip->mode;
582 				aip->mode = AIM_STRAFE;
583 				aip->submode_parm0 = Missiontime;	// use parm0 as time strafe mode entered (i.e. MODE start time)
584 				ai_big_strafe_position();
585 				return;
586 			}
587 
588 		} // end if ( Pl_objp->phys_info.speed < 3.0f )
589 
590 		//Maybe enter glide strafe (check every 8 seconds, on a different schedule for each ship)
591 		if ((sip->can_glide == true) && !(aip->ai_flags & AIF_KAMIKAZE) && static_randf((Missiontime + static_rand(aip->shipnum)) >> 19) < aip->ai_glide_strafe_percent) {
592 			aip->previous_mode = aip->mode;
593 			aip->mode = AIM_STRAFE;
594 			aip->submode_parm0 = Missiontime;	// use parm0 as time strafe mode entered (i.e. MODE start time)
595 			aip->submode = AIS_STRAFE_GLIDE_ATTACK;
596 			aip->submode_parm1 = 0;
597 			aip->submode_start_time = Missiontime;
598 		}
599 
600 		// see if Pl_objp needs to reposition to get a good shot at subsystem which is being attacked
601 		if ( ai_big_maybe_follow_subsys_path() ) {
602 			if ((Pl_objp->phys_info.flags & PF_AFTERBURNER_ON) && !(aip->ai_flags & AIF_KAMIKAZE)) {
603 				afterburners_stop(Pl_objp);
604 			}
605 			return;
606 		}
607 
608 		vec3d	*rel_pos, vec_to_enemy;
609 		float		weapon_travel_dist;
610 
611 		start_bank = Ships[aip->shipnum].weapons.current_primary_bank;
612 
613 		if ((po->n_guns) && (start_bank != -1)) {
614 			rel_pos = &po->gun_banks[start_bank].pnt[0];
615 		} else
616 			rel_pos = NULL;
617 
618 		dist_to_enemy = vm_vec_normalized_dir(&vec_to_enemy, enemy_pos, &Pl_objp->pos);
619 		dot_to_enemy = vm_vec_dot(&vec_to_enemy, &Pl_objp->orient.vec.fvec);
620 
621 		vec3d	rvec_vec, *rvec = &rvec_vec;
622 
623 		if (dist_to_enemy > 500.0f)
624 			compute_desired_rvec(rvec, enemy_pos, &Pl_objp->pos);
625 		else
626 			rvec = NULL;
627 
628 		ai_turn_towards_vector(enemy_pos, Pl_objp, flFrametime, sip->srotation_time, NULL, rel_pos, 0.0f, 0, rvec);
629 
630 		// calc range of primary weapon
631 		weapon_travel_dist = ai_get_weapon_dist(&Ships[Pl_objp->instance].weapons);
632 
633 		if ( aip->targeted_subsys != NULL ) {
634 			if (dist_to_enemy > (weapon_travel_dist-20)) {
635 				accelerate_ship(aip, 1.0f);
636 
637 				ship	*shipp = &Ships[Pl_objp->instance];
638 				if ((aip->ai_flags & AIF_FREE_AFTERBURNER_USE) && !(shipp->flags2 & SF2_AFTERBURNER_LOCKED) && (dot_to_enemy > 0.75f)) {
639 					if (ai_maybe_fire_afterburner(Pl_objp, aip)) {
640 						afterburners_start(Pl_objp);
641 						aip->afterburner_stop_time = Missiontime + 3*F1_0;
642 					}
643 				} else if ((Pl_objp->phys_info.flags & PF_AFTERBURNER_ON) && !(aip->ai_flags & AIF_KAMIKAZE)) {
644 					afterburners_stop(Pl_objp);
645 				}
646 			} else {
647 				// AL 12-31-97: Move at least as quickly as your target is moving...
648 				accelerate_ship(aip, MAX(1.0f - dot_to_enemy, Objects[aip->target_objnum].phys_info.fspeed/sip->max_speed));
649 
650 				if ((Pl_objp->phys_info.flags & PF_AFTERBURNER_ON) && !(aip->ai_flags & AIF_KAMIKAZE)) {
651 					afterburners_stop(Pl_objp);
652 				}
653 			}
654 
655 		} else {
656 			float accel;
657 			if (dot_to_enemy < 0.0f) {
658 				accel = 0.5f;
659 			} else if (dot_to_enemy < 0.75f) {
660 				accel = 0.75f;
661 			} else {
662 				if (dist_to_enemy > weapon_travel_dist/2.0f) {
663 					accel = 1.0f;
664 				} else {
665 					accel = 1.0f - dot_to_enemy;
666 				}
667 			}
668 
669 			// use dist normal to enemy here (don't break 50 barrier)
670 			if (dist_to_enemy < ATTACK_STOP_DISTANCE) {
671 //				accelerate_ship(aip, accel * 0.5f);
672 				accelerate_ship(aip, -1.0f);
673 				if ((Pl_objp->phys_info.flags & PF_AFTERBURNER_ON) && !(aip->ai_flags & AIF_KAMIKAZE)) {
674 					afterburners_stop(Pl_objp);
675 				}
676 			} else {
677 				accelerate_ship(aip, accel);
678 
679 				ship	*shipp = &Ships[Pl_objp->instance];
680 				if ((aip->ai_flags & AIF_FREE_AFTERBURNER_USE) && !(shipp->flags2 & SF2_AFTERBURNER_LOCKED) && (accel > 0.95f)) {
681 					if (ai_maybe_fire_afterburner(Pl_objp, aip)) {
682 						afterburners_start(Pl_objp);
683 						aip->afterburner_stop_time = Missiontime + 3*F1_0;
684 					}
685 				} else if ((Pl_objp->phys_info.flags & PF_AFTERBURNER_ON) && !(aip->ai_flags & AIF_KAMIKAZE)) {
686 					afterburners_stop(Pl_objp);
687 				}
688 			}
689 		}
690 	}
691 }
692 
693 // Handler for submode SM_CONTINUOUS_TURN
ai_big_chase_ct()694 void ai_big_chase_ct()
695 {
696 	ai_chase_ct();
697 }
698 
699 extern void ai_select_secondary_weapon(object *objp, ship_weapon *swp, int priority1 = -1, int priority2 = -1);
700 extern float set_secondary_fire_delay(ai_info *aip, ship *shipp, weapon_info *swip, bool burst);
701 extern void ai_choose_secondary_weapon(object *objp, ai_info *aip, object *en_objp);
702 extern int maybe_avoid_big_ship(object *objp, object *ignore_objp, ai_info *aip, vec3d *goal_point, float delta_time);
703 
704 extern void maybe_cheat_fire_synaptic(object *objp, ai_info *aip);
705 
706 // Determine if Pl_objp should fire weapons at current target, based on input parameters
707 //
708 // dist_to_enemy	=>		distance (in m) to attack point on current target
709 // dot_to_enemy	=>		dot product between fvec of Pl_objp and vector from Pl_objp to attack point
710 //
ai_big_maybe_fire_weapons(float dist_to_enemy,float dot_to_enemy,vec3d * firing_pos,vec3d * enemy_pos,vec3d * enemy_vel)711 void ai_big_maybe_fire_weapons(float dist_to_enemy, float dot_to_enemy, vec3d *firing_pos, vec3d *enemy_pos, vec3d *enemy_vel)
712 {
713 	ai_info		*aip;
714 	ship_weapon	*swp;
715 	int has_fired = -1;
716 
717 	aip = &Ai_info[Ships[Pl_objp->instance].ai_index];
718 	swp = &Ships[Pl_objp->instance].weapons;
719 
720 	if (dot_to_enemy > 0.95f - 0.5f * En_objp->radius/MAX(1.0f, En_objp->radius + dist_to_enemy)) {
721 		aip->time_enemy_in_range += flFrametime;
722 
723 		//	Chance of hitting ship is based on dot product of firing ship's forward vector with vector to ship
724 		//	and also the size of the target relative to distance to target.
725 		if (dot_to_enemy > MAX(0.5f, 0.90f + aip->ai_accuracy/10.0f - En_objp->radius/MAX(1.0f,dist_to_enemy))) {
726 
727 			ship *temp_shipp;
728 			temp_shipp = &Ships[Pl_objp->instance];
729 			ship_weapon *tswp = &temp_shipp->weapons;
730 
731 			if ( tswp->num_primary_banks > 0 ) {
732 				Assertion(tswp->current_primary_bank < tswp->num_primary_banks, "AI tried to select primary bank %d. Might be a model error\n", tswp->current_primary_bank);
733 				weapon_info	*wip = &Weapon_info[tswp->primary_bank_weapons[tswp->current_primary_bank]];
734 
735 				if (dist_to_enemy < MIN((wip->max_speed * wip->lifetime), wip->weapon_range)){
736 					has_fired = 1;
737 					if(! ai_fire_primary_weapon(Pl_objp)){
738 						has_fired = -1;
739 					//	ship_stop_fire_primary(Pl_objp);
740 					}
741 				}
742 			}
743 
744 			if (tswp->num_secondary_banks > 0) {
745 
746 				int	priority1, priority2;
747 
748 				priority1 = -1;
749 				priority2 = -1;
750 
751 				//	Maybe favor selecting a bomb.
752 				//	Note, if you're firing a bomb, if it's aspect seeking, the firing conditions can be looser.
753 				if (Ship_info[Ships[En_objp->instance].ship_info_index].flags & (SIF_BIG_SHIP | SIF_HUGE_SHIP))
754 					if (En_objp->phys_info.speed * dist_to_enemy < 5000.0f)		//	Don't select a bomb if enemy moving fast relative to distance
755 						priority1 = WIF_BOMB;
756 
757 				if (!(En_objp->flags & OF_PROTECTED) || (aip->goals[0].ai_mode & (AI_GOAL_DISABLE_SHIP | AI_GOAL_DISARM_SHIP))) {
758 					//ai_select_secondary_weapon(Pl_objp, tswp, priority1, priority2);	//	Note, need to select to get weapon speed and lifetime
759 					if(aip->goals[0].ai_mode & (AI_GOAL_DISABLE_SHIP | AI_GOAL_DISARM_SHIP)) {
760 						priority1 = WIF_PUNCTURE;
761 					}
762 					ai_choose_secondary_weapon(Pl_objp, aip, En_objp);
763 					int current_bank = tswp->current_secondary_bank;
764 					weapon_info	*swip = &Weapon_info[tswp->secondary_bank_weapons[current_bank]];
765 
766 					if(!(En_objp->flags & OF_PROTECTED) || ((aip->goals[0].ai_mode & (AI_GOAL_DISABLE_SHIP | AI_GOAL_DISARM_SHIP)) && swip->wi_flags & WIF_PUNCTURE )) { //override lockdown on protected ships when using anti subsystem weapons - Valathil
767 						//	If ship is protected and very low on hits, don't fire missiles.
768 						if ((current_bank > -1) &&  (!(En_objp->flags & OF_PROTECTED) || (En_objp->hull_strength > 10*swip->damage))) {
769 							if (aip->ai_flags & AIF_UNLOAD_SECONDARIES) {
770 								if (timestamp_until(swp->next_secondary_fire_stamp[current_bank]) > swip->fire_wait*1000.0f) {
771 									swp->next_secondary_fire_stamp[current_bank] = timestamp((int) (swip->fire_wait*1000.0f));
772 								}
773 							}
774 
775 							if (timestamp_elapsed(swp->next_secondary_fire_stamp[current_bank])) {
776 								float firing_range;
777 								if (swip->wi_flags2 & WIF2_LOCAL_SSM)
778 									firing_range=swip->lssm_lock_range;
779 								else
780 									firing_range = MIN((swip->max_speed * swip->lifetime), swip->weapon_range);
781 								// reduce firing range of secondaries in nebula
782 								extern int Nebula_sec_range;
783 								if ((The_mission.flags & MISSION_FLAG_FULLNEB) && Nebula_sec_range) {
784 									firing_range *= 0.8f;
785 								}
786 
787 								float t = 0.25f;	//	default delay in seconds until next fire.
788 
789 								if (dist_to_enemy < firing_range*1.0f) {
790 
791 
792 									//vm_vec_scale_add(&future_enemy_pos, enemy_pos, enemy_vel, dist_to_enemy/swip->max_speed);
793 									//if (vm_vec_dist_quick(&future_enemy_pos, firing_pos) < firing_range * 0.8f) {
794 										if (ai_fire_secondary_weapon(Pl_objp)) {
795 
796 											int current_bank_adjusted = MAX_SHIP_PRIMARY_BANKS + current_bank;
797 
798 											if ((aip->ai_flags & AIF_UNLOAD_SECONDARIES) || (swip->burst_flags & WBF_FAST_FIRING)) {
799 												if (swip->burst_shots > swp->burst_counter[current_bank_adjusted]) {
800 													t = swip->burst_delay;
801 													swp->burst_counter[current_bank_adjusted]++;
802 												} else {
803 													t = swip->fire_wait;
804 													if ((swip->burst_shots > 0) && (swip->burst_flags & WBF_RANDOM_LENGTH)) {
805 														swp->burst_counter[current_bank_adjusted] = myrand() % swip->burst_shots;
806 													} else {
807  														swp->burst_counter[current_bank_adjusted] = 0;
808 													}
809 												}
810 											} else {
811 												if (swip->burst_shots > swp->burst_counter[current_bank_adjusted]) {
812 													t = set_secondary_fire_delay(aip, temp_shipp, swip, true);
813 													swp->burst_counter[current_bank_adjusted]++;
814 												} else {
815 													t = set_secondary_fire_delay(aip, temp_shipp, swip, false);
816 													if ((swip->burst_shots > 0) && (swip->burst_flags & WBF_RANDOM_LENGTH)) {
817 														swp->burst_counter[current_bank_adjusted] = myrand() % swip->burst_shots;
818 													} else {
819 														swp->burst_counter[current_bank_adjusted] = 0;
820 													}
821 												}
822 											}
823 											swp->next_secondary_fire_stamp[current_bank] = timestamp((int) (t*1000.0f));
824 										}
825 									//}
826 								}
827 								swp->next_secondary_fire_stamp[current_bank] = timestamp((int) (t*1000.0f));
828 							}
829 						}
830 					}
831 				}
832 			}
833 		}
834 	} else {
835 		if (flFrametime < 1.0f)
836 			aip->time_enemy_in_range *= (1.0f - flFrametime);
837 		else
838 			aip->time_enemy_in_range = 0;
839 	}
840 
841 	if(has_fired == -1){	//stuff that hapens when the ship stops fireing
842 		ship_stop_fire_primary(Pl_objp);
843 	}
844 
845 }
846 
847 // switch ai ship into chase mode
ai_big_switch_to_chase_mode(ai_info * aip)848 void ai_big_switch_to_chase_mode(ai_info *aip)
849 {
850 	aip->previous_mode = aip->mode;
851 	aip->mode = AIM_CHASE;
852 	aip->submode = SM_ATTACK;
853 	aip->submode_start_time = Missiontime;
854 }
855 
856 extern int ai_big_strafe_maybe_retreat(float dist, vec3d *target_pos);
857 
858 // Make object Pl_objp chase object En_objp, which is a big ship, not a small ship.
ai_big_chase()859 void ai_big_chase()
860 {
861 	float			dist_to_enemy, dot_to_enemy;
862 	vec3d		player_pos, enemy_pos, vec_to_enemy;
863 	ship_info	*sip = &Ship_info[Ships[Pl_objp->instance].ship_info_index];
864 	ship			*shipp = &Ships[Pl_objp->instance];
865 	ai_info		*aip = &Ai_info[shipp->ai_index];
866 	vec3d		predicted_enemy_pos;
867 
868 	Assert(aip->mode == AIM_CHASE);
869 
870 	maybe_cheat_fire_synaptic(Pl_objp, aip);
871 
872 	ai_set_positions(Pl_objp, En_objp, aip, &player_pos, &enemy_pos);
873 
874 	player_pos = Pl_objp->pos;
875 	ai_big_pick_attack_point(En_objp, Pl_objp, &enemy_pos, 0.8f);
876 
877 	//	Compute the predicted position of the center of the ship, then add the delta to the goal pos.
878 	if (En_objp->phys_info.speed > 3.0f) {
879 		set_predicted_enemy_pos(&predicted_enemy_pos, Pl_objp, &En_objp->pos, &En_objp->phys_info.vel, aip);
880 		vm_vec_add2(&enemy_pos, &predicted_enemy_pos);
881 		vm_vec_sub2(&enemy_pos, &En_objp->pos);
882 	}	else
883 		predicted_enemy_pos = En_objp->pos;
884 
885 	if (aip->targeted_subsys != NULL) {
886 		get_subsystem_pos(&enemy_pos, En_objp, aip->targeted_subsys);
887 	}
888 
889 	dist_to_enemy = vm_vec_normalized_dir(&vec_to_enemy, &enemy_pos, &player_pos); // - En_objp->radius;
890 	dot_to_enemy = vm_vec_dot(&vec_to_enemy, &Pl_objp->orient.vec.fvec);
891 
892 	if (aip->ai_flags & AIF_TARGET_COLLISION) {
893 		if ( ai_big_strafe_maybe_retreat(dist_to_enemy, &enemy_pos) ) {
894 			aip->mode = AIM_STRAFE;
895 			aip->submode_parm0 = Missiontime;	// use parm0 as time strafe mode entered (i.e. MODE start time)
896 			aip->submode = AIS_STRAFE_AVOID;
897 			aip->submode_start_time = Missiontime;
898 			if (Pl_objp->phys_info.flags & PF_AFTERBURNER_ON) {
899 				afterburners_stop(Pl_objp);
900 			}
901 			return;
902 		}
903 	}
904 
905 	if (aip->ai_flags & AIF_KAMIKAZE) {
906 		//nprintf(("AI", "Kamikaze: %7.3f %7.3f\n", dot_to_enemy, dist_to_enemy));
907 		accelerate_ship(aip, 1.0f);
908 		if ((dist_to_enemy < 400.0f) && ai_maybe_fire_afterburner(Pl_objp, aip)) {
909 			afterburners_start(Pl_objp);
910 			aip->afterburner_stop_time = Missiontime + 3*F1_0;
911 		}
912 	}
913 
914 	//	If just acquired target, or target is not in reasonable cone, don't refine believed enemy position.
915 	if ((dot_to_enemy < 0.25f) || (aip->target_time < 1.0f) || (aip->ai_flags & AIF_SEEK_LOCK)) {
916 		update_aspect_lock_information(aip, &vec_to_enemy, dist_to_enemy - En_objp->radius, En_objp->radius);
917 	} else if (aip->targeted_subsys != NULL) {
918 		Assert(aip->targeted_subsys != NULL);
919 		get_subsystem_pos(&enemy_pos, En_objp, aip->targeted_subsys);
920 		vm_vec_add2(&enemy_pos, &predicted_enemy_pos);
921 		vm_vec_sub2(&enemy_pos, &En_objp->pos);
922 		dist_to_enemy = vm_vec_normalized_dir(&vec_to_enemy, &enemy_pos, &player_pos); // - En_objp->radius;
923 		dot_to_enemy = vm_vec_dot(&vec_to_enemy, &Pl_objp->orient.vec.fvec);
924 		update_aspect_lock_information(aip, &vec_to_enemy, dist_to_enemy, En_objp->radius);
925 	} else if (En_objp->flags & OF_PROTECTED) {	//	If protected and we're not attacking a subsystem, stop attacking!
926 		update_aspect_lock_information(aip, &vec_to_enemy, dist_to_enemy - En_objp->radius, En_objp->radius);
927 		aip->target_objnum = -1;
928 		if (find_enemy(Pl_objp-Objects, MAX_ENEMY_DISTANCE, The_mission.ai_profile->max_attackers[Game_skill_level]) == -1) {
929 			ai_do_default_behavior(Pl_objp);
930 			return;
931 		}
932 	} else {
933 		update_aspect_lock_information(aip, &vec_to_enemy, dist_to_enemy - En_objp->radius, En_objp->radius);
934 	}
935 
936 	//	If recently acquired target and not looking at target, just turn a bit.
937 	switch (aip->submode) {
938 	case SM_ATTACK:
939 	case SM_SUPER_ATTACK:
940 		if (vm_vec_dist_quick(&Pl_objp->pos, &predicted_enemy_pos) > 100.0f + En_objp->radius * 2.0f) {
941 			if (maybe_avoid_big_ship(Pl_objp, En_objp, aip, &predicted_enemy_pos, 10.0f)) {
942 				if ((Pl_objp->phys_info.flags & PF_AFTERBURNER_ON) && !(aip->ai_flags & AIF_KAMIKAZE)) {
943 					afterburners_stop(Pl_objp);
944 				}
945 				return;
946 			}
947 		}
948 
949 		if (aip->target_time < 2.0f)
950 			if ((dot_to_enemy < 0.9f) || (dist_to_enemy > 300.0f)) {
951 				aip->submode = SM_CONTINUOUS_TURN;
952 				aip->submode_start_time = Missiontime - fl2f(2.75f);	//	This backdated start time allows immediate switchout.
953 				if (dot_to_enemy > 0.0f)
954 					aip->target_time += flFrametime * dot_to_enemy;
955 			}
956 		break;
957 	}
958 
959 	//
960 	//	Set turn and acceleration based on submode.
961 	//
962 
963 	if (((aip->submode != SM_ATTACK) && (aip->submode != SM_SUPER_ATTACK) && (aip->submode != SM_ATTACK_FOREVER) && (aip->submode != SM_EVADE_WEAPON)) || (aip->mode == AIM_STRAFE)){
964 		if ((Pl_objp->phys_info.flags & PF_AFTERBURNER_ON) && !(aip->ai_flags & AIF_KAMIKAZE)) {
965 			afterburners_stop(Pl_objp);
966 		}
967 	}
968 
969 	switch (aip->submode) {
970 	case SM_CONTINUOUS_TURN:
971 		ai_big_chase_ct();
972 		break;
973 
974 	case SM_ATTACK:
975 	case SM_SUPER_ATTACK:
976 	case SM_ATTACK_FOREVER:
977 		ai_big_chase_attack(aip, sip, &enemy_pos, dist_to_enemy, sip->model_num);
978 		break;
979 
980 	case SM_EVADE:
981 		ai_big_evade_ship();
982 		break;
983 
984 	case SM_AVOID:
985 		ai_big_avoid_ship();
986 		break;
987 
988 	case SM_EVADE_WEAPON:
989 		evade_weapon();
990 		break;
991 
992 	default:
993 		aip->last_attack_time = Missiontime;
994 		aip->submode = SM_ATTACK;
995 		aip->submode_start_time = Missiontime;
996 		break;
997 	}
998 
999 	// maybe Pl_objp was forced into strafe mode, if so return now
1000 	if ( aip->mode == AIM_STRAFE )
1001 		return;
1002 
1003 	//	Maybe choose a new submode.
1004 	if (aip->submode != SM_AVOID && aip->submode != SM_EVADE ) {
1005 		//	If a very long time since attacked, attack no matter what!
1006 		if (aip->submode != SM_SUPER_ATTACK) {
1007 			if (Missiontime - aip->last_attack_time > i2f(6)) {
1008 				aip->submode = SM_SUPER_ATTACK;
1009 				aip->submode_start_time = Missiontime;
1010 				aip->last_attack_time = Missiontime;
1011 			}
1012 		}
1013 
1014 		//	If a collision is expected, pull out!
1015 		float dist_normal_to_enemy;
1016 
1017 		if (vm_vec_mag_squared(&aip->big_attack_surface_normal) > 0.9) {
1018 			dist_normal_to_enemy = fl_abs((dist_to_enemy * vm_vec_dot(&vec_to_enemy, &aip->big_attack_surface_normal)));
1019 		} else {
1020 			// don;t have normal so use a conservative value here
1021 			dist_normal_to_enemy = 0.3f * dist_to_enemy;
1022 		}
1023 
1024 //		float time_to_enemy = dist_normal_to_enemy / Pl_objp->phys_info.speed * fl_abs(vm_vec_dot(&Pl_objp->phys_info.vel, &aip->big_attack_surface_normal));
1025 //		if (Framecount % 30 == 1) {
1026 //			mprintf(("normal dist; %.1f, time: %.1f\n", dist_normal_to_enemy, time_to_enemy));
1027 //		}
1028 
1029 		// since we're not in strafe and we may get a bad normal, cap dist_normal_to_enemy as MIN(0.3*dist_to_enemy, self)
1030 		// this will allow us to get closer on a bad normal
1031 		dist_normal_to_enemy = MAX(0.3f*dist_to_enemy, dist_normal_to_enemy);
1032 
1033 		if (dist_to_enemy < ATTACK_COLLIDE_BASE_DIST) {
1034 			// within 50m or 1sec
1035 			float time_to_enemy;
1036 			if (vm_vec_mag_squared(&aip->big_attack_surface_normal) > 0.9) {
1037 				if (Pl_objp->phys_info.speed > 0.1) {
1038 					time_to_enemy = dist_normal_to_enemy / fl_abs(vm_vec_dot(&Pl_objp->phys_info.vel, &aip->big_attack_surface_normal));
1039 				} else {
1040 					// a big time
1041 					time_to_enemy = 100.0f;
1042 				}
1043 			} else {
1044 				if (Pl_objp->phys_info.speed > 0.1) {
1045 					time_to_enemy = dist_normal_to_enemy / Pl_objp->phys_info.speed;
1046 				} else {
1047 					// a big time
1048 					time_to_enemy = 100.0f;
1049 				}
1050 			}
1051 
1052 			float speed_dist = MAX(0.0f, (Pl_objp->phys_info.speed-50) * 2);
1053 			if ((dist_normal_to_enemy < ATTACK_COLLIDE_AVOID_DIST + speed_dist) || (time_to_enemy < ATTACK_COLLIDE_AVOID_TIME) ) {
1054 				// get away, simulate crsh recovery (don't use avoid)
1055 //				accelerate_ship(aip, -1.0f);
1056 				big_ship_collide_recover_start(Pl_objp, En_objp, &Pl_objp->pos, NULL);
1057 //				aip->submode = SM_AVOID;
1058 //				aip->submode_start_time = Missiontime;
1059 			} else if ((dist_normal_to_enemy < ATTACK_COLLIDE_SLOW_DIST) || (time_to_enemy < ATTACK_COLLIDE_SLOW_TIME) ) {
1060 				// slow down
1061 				accelerate_ship(aip, -1.0f);
1062 			}
1063 		}
1064 
1065 		/*
1066 		if ((dot_to_enemy > 1.0f - 0.1f * En_objp->radius/(dist_to_enemy + 1.0f)) && (Pl_objp->phys_info.speed > dist_to_enemy/5.0f)) {
1067 			if (might_collide_with_ship(Pl_objp, En_objp, dot_to_enemy, dist_to_enemy, (aip->targeted_subsys == NULL)*2.0f + 1.5f)) {
1068 				if ((Missiontime - aip->last_hit_time > F1_0*4) && (dist_to_enemy < Pl_objp->radius*2 + En_objp->radius*2)) {
1069 					accelerate_ship(aip, -1.0f);
1070 				} else {
1071 					aip->submode = SM_AVOID;
1072 					aip->submode_start_time = Missiontime;
1073 				}
1074 			}
1075 		} */
1076 	}
1077 
1078 	switch (aip->submode) {
1079 	case SM_CONTINUOUS_TURN:
1080 		if (Missiontime - aip->submode_start_time > i2f(3)) {
1081 			aip->last_attack_time = Missiontime;
1082 			aip->submode = SM_ATTACK;
1083 			aip->submode_start_time = Missiontime;
1084 		}
1085 		break;
1086 
1087 	case SM_ATTACK:
1088 	case SM_ATTACK_FOREVER:
1089 		break;
1090 
1091 	case SM_EVADE:
1092 		if (dist_to_enemy > 4*En_objp->radius) {
1093 			aip->submode = SM_ATTACK;
1094 			aip->submode_start_time = Missiontime;
1095 			aip->last_attack_time = Missiontime;
1096 		}
1097 		break;
1098 
1099 	case SM_SUPER_ATTACK:
1100 		break;
1101 
1102 	case SM_AVOID:
1103 		// if outside box by > 300 m
1104 		// DA 4/20/98  can never get here attacking a big ship
1105 		{
1106 		int is_inside;
1107 		float box_dist = get_world_closest_box_point_with_delta(NULL, En_objp, &Pl_objp->pos, &is_inside, 0.0f);
1108 
1109 		if (box_dist > 300) {
1110 			aip->submode = SM_ATTACK;
1111 			aip->submode_start_time = Missiontime;
1112 			aip->last_attack_time = Missiontime;
1113 		}
1114 		}
1115 		break;
1116 
1117 /*		if (dot_to_enemy > -0.2f) {
1118 			aip->submode_start_time = Missiontime;
1119 		} else if (Missiontime - aip->submode_start_time > i2f(1)/2) {
1120 			if (might_collide_with_ship(Pl_objp, En_objp, dot_to_enemy, dist_to_enemy, 3.0f)) {
1121 				aip->submode_start_time = Missiontime;
1122 			} else {
1123 				aip->submode = SM_ATTACK;
1124 				aip->submode_start_time = Missiontime;
1125 				aip->last_attack_time = Missiontime;
1126 			}
1127 		}
1128 
1129 		break;*/
1130 
1131 	case SM_EVADE_WEAPON:
1132 		if (aip->danger_weapon_objnum == -1) {
1133 			aip->submode = SM_ATTACK;
1134 			aip->submode_start_time = Missiontime;
1135 			aip->last_attack_time = Missiontime;
1136 		}
1137 		break;
1138 
1139 	default:
1140 		aip->submode = SM_ATTACK;
1141 		aip->submode_start_time = Missiontime;
1142 		aip->last_attack_time = Missiontime;
1143 	}
1144 
1145 	//
1146 	//	Maybe fire primary weapon and update time_enemy_in_range
1147 	//
1148 	//nprintf(("AI", "time_enemy_in_range = %7.3f, dot = %7.3f\n", aip->time_enemy_in_range, dot_to_enemy));
1149 
1150 	// AL: add condition that Pl_objp must not be following a path to fire.  This may be too extreme, but
1151 	//     I noticed AI ships firing inappropriately when following a path near a big ship.
1152 	//		 TODO: investigate why ships fire (and aren't close to hitting ship) when following a path near
1153 	//				 a big ship
1154 	if (aip->mode != AIM_EVADE && aip->path_start == -1 ) {
1155 		ai_big_maybe_fire_weapons(dist_to_enemy, dot_to_enemy, &player_pos, &predicted_enemy_pos, &En_objp->phys_info.vel);
1156 	} else {
1157 		if (flFrametime < 1.0f)
1158 			aip->time_enemy_in_range *= (1.0f - flFrametime);
1159 		else
1160 			aip->time_enemy_in_range = 0;
1161 	}
1162 }
1163 
ai_big_ship(object * objp)1164 void ai_big_ship(object *objp)
1165 {
1166 	// do nothing
1167 }
1168 
1169 // all three parms are output parameters
1170 //	Enemy object is En_objp
1171 // pos	=>		world pos of attack point
1172 // dist	=>		distance from Pl_objp front to attack point
1173 // dot	=>		dot of Pl_objp fvec and vector to attack point
1174 //	fire_pos =>	world pos from which firing
ai_big_attack_get_data(vec3d * enemy_pos,float * dist_to_enemy,float * dot_to_enemy)1175 void ai_big_attack_get_data(vec3d *enemy_pos, float *dist_to_enemy, float *dot_to_enemy)
1176 {
1177 	vec3d		player_pos, vec_to_enemy, predicted_enemy_pos;
1178 	ship			*shipp = &Ships[Pl_objp->instance];
1179 	ai_info		*aip = &Ai_info[shipp->ai_index];
1180 	ship_info	*esip = &Ship_info[Ships[En_objp->instance].ship_info_index];
1181 
1182 	Assert(aip->mode == AIM_STRAFE);
1183 
1184 	// ensure that Pl_objp is still targeting a big ship
1185 	if ( !(esip->flags & (SIF_BIG_SHIP | SIF_HUGE_SHIP)) ) {
1186 		ai_big_switch_to_chase_mode(aip);
1187 		return;
1188 	}
1189 
1190 	ai_set_positions(Pl_objp, En_objp, aip, &player_pos, enemy_pos);
1191 
1192 	player_pos = Pl_objp->pos;
1193 
1194 	if (aip->targeted_subsys != NULL) {
1195 		Assert(aip->targeted_subsys != NULL);
1196 		get_subsystem_pos(enemy_pos, En_objp, aip->targeted_subsys);
1197 	} else {
1198 		// checks valid line to target
1199 		ai_big_pick_attack_point(En_objp, Pl_objp, enemy_pos, 0.8f);
1200 	}
1201 
1202 	// Take player pos to be center of ship + ship_radius
1203 	vm_vec_scale_add2(&player_pos, &Pl_objp->orient.vec.fvec, Pl_objp->radius);
1204 
1205 	//	If seeking lock, try to point directly at ship, else predict position so lasers can hit it.
1206 	//	If just acquired target, or target is not in reasonable cone, don't refine believed enemy position.
1207 	vm_vec_normalized_dir(&vec_to_enemy, enemy_pos, &player_pos);
1208 	*dot_to_enemy=vm_vec_dot(&vec_to_enemy, &Pl_objp->orient.vec.fvec);
1209 	if ((*dot_to_enemy < 0.25f) || (aip->target_time < 1.0f) || (aip->ai_flags & AIF_SEEK_LOCK)) {
1210 		predicted_enemy_pos=*enemy_pos;
1211 	} else {
1212 		vec3d	gun_pos, pnt;
1213 		polymodel *po = model_get( Ship_info[shipp->ship_info_index].model_num );
1214 		float		weapon_speed;
1215 
1216 		//	Compute position of gun in absolute space and use that as fire position.
1217 		if (po->n_guns > 0 && !(The_mission.ai_profile->flags2 & AIPF2_AI_AIMS_FROM_SHIP_CENTER)) {
1218 			pnt = po->gun_banks[0].pnt[0];
1219 			vm_vec_unrotate(&gun_pos, &pnt, &Pl_objp->orient);
1220 			vm_vec_add2(&gun_pos, &Pl_objp->pos);
1221 		} else {
1222 			//Use the convergence offset, if there is one
1223 			vm_vec_copy_scale(&pnt, &Ship_info[shipp->ship_info_index].convergence_offset, 1.0f);
1224 		}
1225 		weapon_speed = ai_get_weapon_speed(&shipp->weapons);
1226 
1227 		set_predicted_enemy_pos_turret(&predicted_enemy_pos, &gun_pos, Pl_objp, enemy_pos, &En_objp->phys_info.vel, weapon_speed, aip->time_enemy_in_range);
1228 	}
1229 
1230 	*dist_to_enemy = vm_vec_normalized_dir(&vec_to_enemy, &predicted_enemy_pos, &player_pos);
1231 	*dot_to_enemy = vm_vec_dot(&vec_to_enemy, &Pl_objp->orient.vec.fvec);
1232 	update_aspect_lock_information(aip, &vec_to_enemy, *dist_to_enemy, En_objp->radius);
1233 
1234 	*enemy_pos = predicted_enemy_pos;
1235 }
1236 
1237 // check to see if Pl_objp has gotten too close to attacking point.. if so, break off by entering
1238 // AIS_STRAFE_RETREAT
ai_big_strafe_maybe_retreat(float dist,vec3d * target_pos)1239 int ai_big_strafe_maybe_retreat(float dist, vec3d *target_pos)
1240 {
1241 	ai_info	*aip;
1242 	aip = &Ai_info[Ships[Pl_objp->instance].ai_index];
1243 
1244 	vec3d vec_to_target;
1245 	vm_vec_sub(&vec_to_target, target_pos, &Pl_objp->pos);
1246 
1247 	float dist_to_target, dist_normal_to_target, time_to_target;
1248 	dist_to_target = vm_vec_mag_quick(&vec_to_target);
1249 	if (vm_vec_mag_quick(&aip->big_attack_surface_normal) > 0.9) {
1250 		dist_normal_to_target = -vm_vec_dotprod(&vec_to_target, &aip->big_attack_surface_normal);
1251 	} else {
1252 		dist_normal_to_target = 0.2f * vm_vec_mag_quick(&vec_to_target);
1253 	}
1254 
1255 	dist_normal_to_target = MAX(0.2f*dist_to_target, dist_normal_to_target);
1256 	time_to_target = dist_normal_to_target / Pl_objp->phys_info.speed;
1257 
1258 	// add distance penalty for going too fast
1259 	float speed_to_dist_penalty = MAX(0.0f, (Pl_objp->phys_info.speed-50));
1260 
1261 	//if ((dot_to_enemy > 1.0f - 0.1f * En_objp->radius/(dist_to_enemy + 1.0f)) && (Pl_objp->phys_info.speed > dist_to_enemy/5.0f))
1262 
1263 	// Inside 2 sec retreat, setting goal point to box point + 300m
1264 	// If collision, use std collision resolution.
1265 	if ( !(aip->ai_flags & AIF_KAMIKAZE) && ((aip->ai_flags & AIF_TARGET_COLLISION) || (time_to_target < STRAFE_RETREAT_COLLIDE_TIME) || (dist_normal_to_target < STRAFE_RETREAT_COLLIDE_DIST + speed_to_dist_penalty)) ) {
1266 		if (aip->ai_flags & AIF_TARGET_COLLISION) {
1267 			// use standard collision resolution
1268 			aip->ai_flags &= ~AIF_TARGET_COLLISION;
1269 			big_ship_collide_recover_start(Pl_objp, En_objp, &Pl_objp->pos, NULL);
1270 		} else {
1271 			// too close for comfort so fly to box point + 300
1272 			aip->submode = AIS_STRAFE_RETREAT1;
1273 			aip->submode_start_time = Missiontime;
1274 
1275 			float box_dist;
1276 			int is_inside;
1277 			vec3d goal_point;
1278 			box_dist = get_world_closest_box_point_with_delta(&goal_point, En_objp, &Pl_objp->pos, &is_inside, STRAFE_RETREAT_BOX_DIST);
1279 
1280 			// set goal point
1281 			aip->goal_point = goal_point;
1282 
1283 			if (ai_maybe_fire_afterburner(Pl_objp, aip)) {
1284 				afterburners_start(Pl_objp);
1285 				aip->afterburner_stop_time = Missiontime + 3*F1_0;
1286 			}
1287 		}
1288 
1289 		return 1;
1290 	} else {
1291 		return 0;
1292 	}
1293 }
1294 
1295 // attack directly to the turret and fire weapons
ai_big_strafe_attack()1296 void ai_big_strafe_attack()
1297 {
1298 	ai_info	*aip;
1299 	vec3d	target_pos;
1300 	vec3d	rand_vec;
1301 	float		target_dist, target_dot, accel, t;
1302 
1303 	aip = &Ai_info[Ships[Pl_objp->instance].ai_index];
1304 
1305 	if ( ai_big_maybe_follow_subsys_path(0) ) {
1306 		return;
1307 	}
1308 
1309 	ai_big_attack_get_data(&target_pos, &target_dist, &target_dot);
1310 	if ( ai_big_strafe_maybe_retreat(target_dist, &target_pos) )
1311 		return;
1312 
1313 	if (aip->ai_flags & AIF_KAMIKAZE) {
1314 		if (target_dist < 1200.0f) {
1315 			ai_turn_towards_vector(&target_pos, Pl_objp, flFrametime, Ship_info[Ships[Pl_objp->instance].ship_info_index].srotation_time, NULL, NULL, 0.0f, 0);
1316 			accelerate_ship(aip, 1.0f);
1317 			if ((target_dist < 400.0f) && ai_maybe_fire_afterburner(Pl_objp, aip)) {
1318 				afterburners_start(Pl_objp);
1319 				aip->afterburner_stop_time = Missiontime + 3*F1_0;
1320 			}
1321 			return;
1322 		}
1323 	}
1324 
1325 	if (!(aip->ai_flags & AIF_SEEK_LOCK) || (aip->aspect_locked_time < 2.0f)) {
1326 		t  = ai_endangered_by_weapon(aip);
1327 		if ( t > 0.0f && t < 1.5f ) {
1328 			// set up goal_point for avoid path to turn towards
1329 			aip->goal_point = Pl_objp->pos;
1330 			switch(rand()%4) {
1331 			case 0:
1332 				vm_vec_scale_add(&rand_vec, &Pl_objp->orient.vec.fvec, &Pl_objp->orient.vec.uvec, 2.0f);
1333 				break;
1334 			case 1:
1335 				vm_vec_scale_add(&rand_vec, &Pl_objp->orient.vec.fvec, &Pl_objp->orient.vec.uvec, -2.0f);
1336 				break;
1337 			case 2:
1338 				vm_vec_scale_add(&rand_vec, &Pl_objp->orient.vec.fvec, &Pl_objp->orient.vec.rvec, 2.0f);
1339 				break;
1340 			case 3:
1341 				vm_vec_scale_add(&rand_vec, &Pl_objp->orient.vec.fvec, &Pl_objp->orient.vec.rvec, -2.0f);
1342 				break;
1343 			} // end switch
1344 
1345 			vm_vec_scale(&rand_vec, 1000.0f);
1346 			vm_vec_add2(&aip->goal_point, &rand_vec);
1347 
1348 			aip->submode = AIS_STRAFE_AVOID;
1349 			aip->submode_start_time = Missiontime;
1350 
1351 			if (ai_maybe_fire_afterburner(Pl_objp, aip)) {
1352 				afterburners_start(Pl_objp);
1353 				aip->afterburner_stop_time = Missiontime + fl2f(0.5f);
1354 			}
1355 		}
1356 	}
1357 
1358 	// maybe fire weapons at target
1359 	ai_big_maybe_fire_weapons(target_dist, target_dot, &Pl_objp->pos, &En_objp->pos, &En_objp->phys_info.vel);
1360 	turn_towards_point(Pl_objp, &target_pos, NULL, 0.0f);
1361 
1362 	// Slow down if we've not been hit for a while
1363 	fix last_hit = Missiontime - aip->last_hit_time;
1364 	if ( target_dist > 1200 || last_hit < F1_0*6) {
1365 		accel = 1.0f;
1366 	} else {
1367 		float attack_time;
1368 		attack_time = f2fl(Missiontime - aip->submode_start_time);
1369 		if ( attack_time > 15 ) {
1370 			accel = 0.2f;
1371 		} else if ( attack_time > 10 ) {
1372 			accel = 0.4f;
1373 		} else if ( attack_time > 8 ) {
1374 			accel = 0.6f;
1375 		} else if ( attack_time > 5 ) {
1376 			accel = 0.8f;
1377 		} else {
1378 			accel = 1.0f;
1379 		}
1380 	}
1381 
1382 	accel = 1.0f;
1383 	accelerate_ship(aip, accel);
1384 
1385 	// if haven't been hit in quite a while, leave strafe mode
1386 	fix long_enough;
1387 	long_enough = F1_0 * STRAFE_MAX_UNHIT_TIME;
1388 	if ( (last_hit > long_enough) && ( (Missiontime - aip->submode_parm0) > long_enough) ) {
1389 		ai_big_switch_to_chase_mode(aip);
1390 	}
1391 }
1392 
1393 // SUSHI: Strafe attack using glide
1394 //submode_parm1 encodes the stage we're in.
1395 //0: Choosing goal point to fly towards
1396 //1: Accelerating towards the goal point.
1397 //2: Raining down fire and brimstone on the target.
1398 //3: Dummy stage used when resetting or exiting the mode
ai_big_strafe_glide_attack()1399 void ai_big_strafe_glide_attack()
1400 {
1401 	ai_info *aip;
1402 	vec3d target_pos, vec_to_goal, predicted_enemy_pos, norm_vel_vec;
1403 	float target_dist, target_dot, target_ship_dist, dot_to_goal, flight_dot_to_enemy;
1404 	object *target_objp;
1405 
1406 	aip = &Ai_info[Ships[Pl_objp->instance].ai_index];
1407 	target_objp = &Objects[aip->target_objnum];
1408 
1409 	vm_vec_sub(&vec_to_goal, &aip->goal_point, &Pl_objp->pos);
1410 	vm_vec_normalize(&vec_to_goal);
1411 
1412 	if (vm_vec_mag(&Pl_objp->phys_info.vel) > 0.0f)
1413 		vm_vec_copy_normalize(&norm_vel_vec, &Pl_objp->phys_info.vel);
1414 	else
1415 		vm_vec_copy_normalize(&norm_vel_vec, &Pl_objp->orient.vec.fvec);
1416 
1417 	dot_to_goal = vm_vec_dot(&vec_to_goal, &norm_vel_vec);	//Angle between flight path and goal point
1418 	flight_dot_to_enemy = vm_vec_dot(&target_objp->pos, &norm_vel_vec);	//Angle between flight path and target ship
1419 
1420 	//If we're following a path, then we shouldn't be doing glide strafe
1421 	if ( ai_big_maybe_follow_subsys_path(0) ) {
1422 		return;
1423 	}
1424 
1425 	//Gets a point on the target ship to attack, as well as distance and the angle between the nose of the attacker and that point.
1426 	ai_big_attack_get_data(&target_pos, &target_dist, &target_dot);
1427 	target_ship_dist = vm_vec_dist(&target_objp->pos, &Pl_objp->pos);
1428 
1429 	//If we haven't chosen the goal point yet, do so now
1430 	if (aip->submode_parm1 == 0) {
1431 		//Pick goal point. This should be a random point that passes through the circle whose normal is the vector between attacker and target
1432 		vec3d targetToAttacker;
1433 		vec3d tangentPoint;
1434 		vm_vec_sub(&targetToAttacker, &Pl_objp->pos, &target_objp->pos);
1435 		matrix orient;
1436 		vm_vector_2_matrix(&orient, &targetToAttacker, NULL, NULL);
1437 		vm_vec_random_in_circle(&tangentPoint, &target_objp->pos, &orient, target_objp->radius + GLIDE_STRAFE_DISTANCE, 1);
1438 		//Get tangent point in coords relative to ship, scale it up so the actual goal point is far away, then put back in world coords
1439 		vm_vec_sub2(&tangentPoint, &Pl_objp->pos);
1440 		vm_vec_scale(&tangentPoint, 1000.0f);
1441 		vm_vec_add(&aip->goal_point, &tangentPoint, &Pl_objp->pos);
1442 		aip->submode_parm1 = 1;
1443 	}
1444 	if (aip->submode_parm1 == 1) {
1445 		//Stay in acceleration stage?
1446 		//Accelerate towards goal point until:
1447 		//	Mostly up to speed AND
1448 		//	Moving in the correct direction
1449 		if ((Pl_objp->phys_info.fspeed > (Pl_objp->phys_info.max_vel.xyz.z * 0.85f)) && (dot_to_goal > 0.99f))
1450 			aip->submode_parm1 = 2;
1451 	}
1452 	if (aip->submode_parm1 == 2) {
1453 		//Should we stay in the fire stage?
1454 		//Keep going until we are too far away.
1455 		//If we are still on approach but too far away, this will still trigger. This will allow us to reposition the target
1456 		//point and allow for a "jinking" effect.
1457 		if (target_ship_dist > (STRAFE_RETREAT_BOX_DIST + target_objp->radius) &&
1458 			Missiontime - aip->submode_start_time > i2f(GLIDE_STRAFE_MIN_TIME)) {
1459 			//This checks whether we are moving toward the target or away from it.  If moving towards, we reset the stage so that we
1460 			//pick a new attack vector (jinking). If moving away, we're at the end of a run so do a full reset (possibly allowing a
1461 			//switch back to conventional strafe).
1462 			if (flight_dot_to_enemy > 0.0f) {
1463 				aip->submode_parm1 = 0;
1464 			}
1465 			else {
1466 				aip->submode_parm1 = 3;
1467 				aip->submode = AIS_STRAFE_POSITION;
1468 			}
1469 			aip->submode_start_time = Missiontime;
1470 		}
1471 	}
1472 	//Time limit: if we've taken too long, reset
1473 	if (Missiontime - aip->submode_start_time > i2f(GLIDE_STRAFE_MAX_TIME)) {
1474 			aip->submode_parm1 = 3;
1475 			aip->submode = AIS_STRAFE_POSITION;
1476 			aip->submode_start_time = Missiontime;
1477 	}
1478 
1479 	//Acceleration stage
1480 	if (aip->submode_parm1 == 1) {
1481 		accelerate_ship(aip, 1.0f);
1482 		//Use afterburners if we have them and are pointed the right way
1483 		if (dot_to_goal > 0.99f) {
1484 			afterburners_start(Pl_objp);
1485 			aip->afterburner_stop_time = Missiontime + 3*F1_0;
1486 		}
1487 
1488 		turn_towards_point(Pl_objp, &aip->goal_point, NULL, 0.0f);
1489 	}
1490 	//Fire stage
1491 	if (aip->submode_parm1 == 2) {
1492 		Pl_objp->phys_info.flags |= PF_GLIDING;
1493 		accelerate_ship(aip, 0.0f);
1494 		afterburners_stop(Pl_objp);
1495 
1496 		//Compensate for motion of ship and target
1497 		set_predicted_enemy_pos(&predicted_enemy_pos, Pl_objp, &En_objp->pos, &En_objp->phys_info.vel, aip);
1498 		vm_vec_add2(&target_pos, &predicted_enemy_pos);
1499 		vm_vec_sub2(&target_pos, &En_objp->pos);
1500 
1501 		turn_towards_point(Pl_objp, &target_pos, NULL, 0.0f);
1502 		ai_big_maybe_fire_weapons(target_dist, target_dot, &Pl_objp->pos, &En_objp->pos, &En_objp->phys_info.vel);
1503 	}
1504 
1505 	// if haven't been hit in quite a while, leave strafe mode
1506 	// (same as ai_big_strafe_attack)
1507 	fix long_enough = F1_0 * STRAFE_MAX_UNHIT_TIME;
1508 	if ( (Missiontime - aip->last_hit_time > long_enough) && ( (Missiontime - aip->submode_parm0) > long_enough) ) {
1509 		ai_big_switch_to_chase_mode(aip);
1510 	}
1511 }
1512 
1513 // pick a new attack point when entering this state, and keep using it
ai_big_strafe_avoid()1514 void ai_big_strafe_avoid()
1515 {
1516 	ai_info	*aip;
1517 	vec3d	target_pos;
1518 	float		target_dist, target_dot;
1519 	fix		mode_time;
1520 
1521 	aip = &Ai_info[Ships[Pl_objp->instance].ai_index];
1522 
1523 	mode_time = Missiontime - aip->submode_start_time;
1524 
1525 	ai_big_attack_get_data(&target_pos, &target_dist, &target_dot);
1526 	if ( ai_big_strafe_maybe_retreat(target_dist, &target_pos) )
1527 		return;
1528 
1529 	if ( mode_time > fl2f(0.5)) {
1530 		ai_big_strafe_position();
1531 	}
1532 
1533 	turn_towards_point(Pl_objp, &aip->goal_point, NULL, 0.0f);
1534 	accelerate_ship(aip, 1.0f);
1535 }
1536 
1537 // move towards aip->goal_point in an evasive manner
ai_big_strafe_retreat1()1538 void ai_big_strafe_retreat1()
1539 {
1540 	float dist;
1541 	ai_info	*aip;
1542 	vec3d	rand_vec;
1543 
1544 	aip = &Ai_info[Ships[Pl_objp->instance].ai_index];
1545 
1546 	dist = vm_vec_dist_quick(&Pl_objp->pos, &aip->goal_point);
1547 	if ( dist < 70 ) {
1548 		aip->submode = AIS_STRAFE_POSITION;
1549 		aip->submode_start_time = Missiontime;
1550 //		nprintf(("Alan","Ship %s entering AIS_STRAFE_POSITION\n", Ships[aip->shipnum].ship_name));
1551 		return;
1552 	}
1553 
1554 	if (Missiontime - aip->submode_start_time > fl2f(1.50f)) {
1555 		// set up goal_point for avoid path to turn towards
1556 		aip->prev_goal_point = Pl_objp->pos;
1557 		switch(rand()%4) {
1558 		case 0:
1559 			vm_vec_add(&rand_vec, &Pl_objp->orient.vec.fvec, &Pl_objp->orient.vec.uvec);
1560 			break;
1561 		case 1:
1562 			vm_vec_scale_add(&rand_vec, &Pl_objp->orient.vec.fvec, &Pl_objp->orient.vec.uvec, -1.0f);
1563 			break;
1564 		case 2:
1565 			vm_vec_add(&rand_vec, &Pl_objp->orient.vec.fvec, &Pl_objp->orient.vec.rvec);
1566 			break;
1567 		case 3:
1568 			vm_vec_scale_add(&rand_vec, &Pl_objp->orient.vec.fvec, &Pl_objp->orient.vec.rvec, -1.0f);
1569 			break;
1570 		} // end switch
1571 
1572 		//vm_vec_scale(&rand_vec, 200.0f);
1573 		//vm_vec_add2(&aip->prev_goal_point, &rand_vec);
1574 		vm_vec_scale_add(&aip->prev_goal_point, &aip->goal_point, &rand_vec, 200.0f);
1575 
1576 		aip->submode = AIS_STRAFE_RETREAT2;
1577 		aip->submode_start_time = Missiontime;
1578 	}
1579 
1580 	turn_towards_point(Pl_objp, &aip->goal_point, NULL, 0.0f);
1581 	accelerate_ship(aip, 1.0f);
1582 }
1583 
ai_big_strafe_retreat2()1584 void ai_big_strafe_retreat2()
1585 {
1586 	float dist;
1587 	ai_info	*aip;
1588 
1589 	aip = &Ai_info[Ships[Pl_objp->instance].ai_index];
1590 
1591 	if (Pl_objp->phys_info.flags & PF_AFTERBURNER_ON ) {
1592 		if (Missiontime > aip->afterburner_stop_time) {
1593 			//nprintf(("AI", "Frame %i, turning off afterburner.\n", AI_FrameCount));
1594 			afterburners_stop(Pl_objp);
1595 		}
1596 	}
1597 
1598 	dist = vm_vec_dist_quick(&Pl_objp->pos, &aip->goal_point);
1599 	if ( dist < 70 ) {
1600 		aip->submode = AIS_STRAFE_POSITION;
1601 		aip->submode_start_time = Missiontime;
1602 //		nprintf(("Alan","Ship %s entering AIS_STRAFE_POSITION\n", Ships[aip->shipnum].ship_name));
1603 		return;
1604 	}
1605 
1606 	if (Missiontime - aip->submode_start_time > fl2f(0.70f)) {
1607 		aip->submode = AIS_STRAFE_RETREAT1;
1608 		aip->submode_start_time = Missiontime;
1609 
1610 		if ( (Missiontime - aip->last_hit_time) < F1_0*5 ) {
1611 			if (ai_maybe_fire_afterburner(Pl_objp, aip)) {
1612 				afterburners_start(Pl_objp);
1613 				aip->afterburner_stop_time = Missiontime + F1_0;
1614 			}
1615 		}
1616 	}
1617 
1618 	turn_towards_point(Pl_objp, &aip->prev_goal_point, NULL, 0.0f);
1619 	accelerate_ship(aip, 1.0f);
1620 }
1621 
1622 // reposition self to begin another strafing run
ai_big_strafe_position()1623 void ai_big_strafe_position()
1624 {
1625 	ship_info *sip;
1626 	ai_info	*aip;
1627 	sip = &Ship_info[Ships[Pl_objp->instance].ship_info_index];
1628 	aip = &Ai_info[Ships[Pl_objp->instance].ai_index];
1629 
1630 	//Maybe use AIS_STRAFE_GLIDE_ATTACK
1631 	if ((sip->can_glide == true) && !(aip->ai_flags & AIF_KAMIKAZE) && (frand() < aip->ai_glide_strafe_percent)) {
1632 		aip->submode = AIS_STRAFE_GLIDE_ATTACK;
1633 		aip->submode_parm1 = 0;
1634 	} else {
1635 		aip->submode = AIS_STRAFE_ATTACK;
1636 	}
1637 	aip->submode_start_time = Missiontime;
1638 }
1639 
1640 //	--------------------------------------------------------------------------
1641 // #define	AIS_STRAFE_ATTACK		201	// fly towards target and attack
1642 // #define	AIS_STRAFE_AVOID		202	// fly evasive vector to avoid incoming fire
1643 // #define	AIS_STRAFE_RETREAT1	203	// fly away from attack point (directly)
1644 // #define	AIS_STRAFE_RETREAT1	204	// fly away from attack point (on an avoid vector)
1645 // #define	AIS_STRAFE_POSITION	205	// re-position to resume strafing attack
1646 //
ai_big_strafe()1647 void ai_big_strafe()
1648 {
1649 	ai_info	*aip;
1650 
1651 	aip = &Ai_info[Ships[Pl_objp->instance].ai_index];
1652 
1653 	Assert(aip->mode == AIM_STRAFE);
1654 
1655 /*
1656 	if ( aip->goal_objnum != aip->target_objnum ) {
1657 		Int3();	// what is going on here? - Get Alan
1658 		aip->mode = AIM_NONE;
1659 		return;
1660 	}
1661 */
1662 	// check if target is still a big ship... if not enter chase mode
1663 	if ( !(Ship_info[Ships[En_objp->instance].ship_info_index].flags & (SIF_BIG_SHIP|SIF_HUGE_SHIP)) ) {
1664 		ai_big_switch_to_chase_mode(aip);
1665 		return;
1666 	}
1667 
1668 	switch (aip->submode) {
1669 	case AIS_STRAFE_ATTACK:
1670 		ai_big_strafe_attack();
1671 		break;
1672 	case AIS_STRAFE_AVOID:
1673 		ai_big_strafe_avoid();
1674 		break;
1675 	case AIS_STRAFE_RETREAT1:
1676 		ai_big_strafe_retreat1();
1677 		break;
1678 	case AIS_STRAFE_RETREAT2:
1679 		ai_big_strafe_retreat2();
1680 		break;
1681 	case AIS_STRAFE_POSITION:
1682 		ai_big_strafe_position();
1683 		break;
1684 	case AIS_STRAFE_GLIDE_ATTACK:
1685 		ai_big_strafe_glide_attack();
1686 		break;
1687 	default:
1688 
1689 		Int3();		//	Illegal submode for AIM_STRAFE
1690 		break;
1691 	}
1692 
1693 	//Maybe apply random sidethrust, depending on the current submode
1694 	//The following are valid targets for random sidethrust (circle strafe uses it too, but that is handled separately)
1695 	if (aip->submode == AIS_STRAFE_ATTACK ||
1696 		aip->submode == AIS_STRAFE_AVOID ||
1697 		aip->submode == AIS_STRAFE_RETREAT1 ||
1698 		aip->submode == AIS_STRAFE_RETREAT2 ||
1699 		aip->submode == AIS_STRAFE_POSITION)
1700 	{
1701 		//Re-roll for random sidethrust every 2 seconds
1702 		if (static_randf((Missiontime + static_rand(aip->shipnum)) >> 17) < aip->ai_random_sidethrust_percent) {
1703 			do_random_sidethrust(aip, &Ship_info[Ships[Objects[aip->target_objnum].instance].ship_info_index]);
1704 		}
1705 	}
1706 }
1707 
1708 // See if Pl_objp should enter strafe mode (This is called from maybe_evade_dumbfire_weapon(), and the
1709 // weapon_objnum is for a weapon that is about to collide with Pl_objp
1710 //
1711 // Check if weapon_objnum was fired by Pl_objp's target, and whether Pl_objp's target is a big ship, if
1712 // so, enter AIM_STRAFE
ai_big_maybe_enter_strafe_mode(object * pl_objp,int weapon_objnum,int consider_target_only)1713 int ai_big_maybe_enter_strafe_mode(object *pl_objp, int weapon_objnum, int consider_target_only)
1714 {
1715 	ai_info		*aip;
1716 	ship_info	*sip;
1717 	object		*weapon_objp, *parent_objp;
1718 
1719 	aip = &Ai_info[Ships[pl_objp->instance].ai_index];
1720 	Assert(aip->mode != AIM_STRAFE);		// can't happen
1721 
1722 	// if Pl_objp has no target, then we can't enter strafe mode
1723 	if ( aip->target_objnum < 0 ) {
1724 		return 0;
1725 	}
1726 
1727 	// if target is not a ship, stafe mode is not possible
1728 	if ( Objects[aip->target_objnum].type != OBJ_SHIP ) {
1729 		return 0;
1730 	}
1731 
1732 	sip = &Ship_info[Ships[Objects[aip->target_objnum].instance].ship_info_index];
1733 
1734 	// if Pl_objp's target is not a big/capital ship, then cannot enter strafe mode
1735 	// AL 12-31-97: Even though transports are considered big ships, don't enter strafe mode on them
1736 	if ( !(sip->flags & (SIF_BIG_SHIP | SIF_HUGE_SHIP)) || (sip->flags & SIF_TRANSPORT) ) {
1737 		return 0;
1738 	}
1739 
1740 	//	If Pl_objp not a fighter or bomber, don't enter strafe mode. -- MK, 11/11/97.
1741 	if ( !(Ship_info[Ships[pl_objp->instance].ship_info_index].flags & (SIF_FIGHTER | SIF_BOMBER)) ) {
1742 		return 0;
1743 	}
1744 
1745 	Assert(weapon_objnum >= 0 && weapon_objnum < MAX_OBJECTS);
1746 	weapon_objp = &Objects[weapon_objnum];
1747 	Assert(weapon_objp->type == OBJ_WEAPON);
1748 
1749 	Assert(weapon_objp->parent >= 0 && weapon_objp->parent < MAX_OBJECTS);
1750 	parent_objp = &Objects[weapon_objp->parent];
1751 	if ( (parent_objp->signature != weapon_objp->parent_sig) || (parent_objp->type != OBJ_SHIP) ) {
1752 		return 0;
1753 	}
1754 
1755 	// Maybe the ship which fired the weapon isn't the current target
1756 	if ( OBJ_INDEX(parent_objp) != aip->target_objnum ) {
1757 
1758 //JAS IMPOSSIBLE		if (1) { // consider_target_only ) {
1759 //JAS IMPOSSIBLE			return 0;
1760 //JAS IMPOSSIBLE		} else {
1761 			// switch targets
1762 			sip = &Ship_info[Ships[parent_objp->instance].ship_info_index];
1763 			if ( !(sip->flags & (SIF_BIG_SHIP | SIF_HUGE_SHIP)) || (sip->flags & SIF_TRANSPORT) ) {
1764 				return 0;
1765 			}
1766 			set_target_objnum(aip, OBJ_INDEX(parent_objp));
1767 //JAS IMPOSSIBLE		}
1768 	}
1769 
1770 	ai_big_strafe_maybe_attack_turret(pl_objp, weapon_objp);
1771 
1772 	// if we've got this far, the weapon must have come from the player's target, and it is a
1773 	// big/capital ship... so enter strafe mode
1774 	aip->previous_mode = aip->mode;
1775 	aip->mode = AIM_STRAFE;
1776 	aip->submode_parm0 = Missiontime;	// use parm0 as time strafe mode entered (i.e. MODE start time)
1777 	aip->submode = AIS_STRAFE_AVOID;
1778 	aip->submode_start_time = Missiontime;
1779 //	nprintf(("Alan","%s Accepted strafe mode\n", Ships[pl_objp->instance].ship_name));
1780 
1781 	return 1;
1782 }
1783 
1784 // Consider attacking a turret, if a turret actually fired the weapon
1785 // input:	ship_objp	=>	ship that will attack the turret
1786 //				weapon_objp	=>
ai_big_strafe_maybe_attack_turret(object * ship_objp,object * weapon_objp)1787 void ai_big_strafe_maybe_attack_turret(object *ship_objp, object *weapon_objp)
1788 {
1789 	ai_info	*aip;
1790 	object	*parent_objp;
1791 
1792 	Assert(ship_objp->type == OBJ_SHIP);
1793 	aip = &Ai_info[Ships[ship_objp->instance].ai_index];
1794 
1795 	// Make decision to attack turret based on AI class.  The better AI ships will realize that
1796 	// it is better to take out the turrets first on a big ship.
1797 	if ( (frand()*100) > (aip->ai_courage-15) )
1798 		return;
1799 
1800 	// If ship is already attacking a subsystem, don't switch
1801 	if ( aip->targeted_subsys != NULL ) {
1802 		return;
1803 	}
1804 
1805 	// If the weapon is not from a turret, return
1806 	if ( Weapons[weapon_objp->instance].turret_subsys == NULL ) {
1807 		return;
1808 	}
1809 
1810 	Assert(weapon_objp->parent >= 0 && weapon_objp->parent < MAX_OBJECTS);
1811 	parent_objp = &Objects[weapon_objp->parent];
1812 
1813 	// UnknownPlayer : Decide whether or not this weapon was a beam, in which case it might be a good
1814 	// idea to go after it even if its not on the current target. This is randomized so
1815 	// the ai will not always go after different ships firing beams at them.
1816 	// Approx 1/4 chance we'll go after the other ship's beam.
1817 
1818 	bool attack_turret_on_different_ship = (aip->ai_profile_flags & AIPF_BIG_SHIPS_CAN_ATTACK_BEAM_TURRETS_ON_UNTARGETED_SHIPS)
1819 		&& (Weapon_info[Weapons[weapon_objp->instance].weapon_info_index].wi_flags & WIF_BEAM) && (frand()*100 < 25.0f);
1820 
1821 	// unless we're making an exception, we should only attack a turret if it sits on the current target
1822 	if ( !attack_turret_on_different_ship )
1823 	{
1824 		if ( (parent_objp->signature != weapon_objp->parent_sig) || (parent_objp->type != OBJ_SHIP) ) {
1825 			return;
1826 		}
1827 
1828 		if ( aip->target_objnum != OBJ_INDEX(parent_objp) ) {
1829 			return;
1830 		}
1831 	}
1832 
1833 	// attack the turret
1834 	set_targeted_subsys(aip, Weapons[weapon_objp->instance].turret_subsys, OBJ_INDEX(parent_objp));
1835 }
1836