2 * Copyright (C) Volition, Inc. 1999. All rights reserved.
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
10 * $Logfile: /Freespace2/code/Ship/AiBig.cpp $
15 * C module for AI code related to large ships
18 * Revision 1.5 2002/06/17 06:33:10 relnev
19 * ryan's struct patch for gcc 2.95
21 * Revision 1.4 2002/06/09 04:41:26 relnev
22 * added copyright header
24 * Revision 1.3 2002/06/01 07:12:33 relnev
25 * a few NDEBUG updates.
27 * removed a few warnings.
29 * Revision 1.2 2002/05/07 03:16:51 theoddone33
30 * The Great Newline Fix
32 * Revision 1.1.1.1 2002/05/03 03:28:10 root
36 * 13 8/31/99 4:24p Andsager
37 * Reduce collisions when attacking big ships.
39 * 12 7/19/99 2:18p Mikeb
42 * 11 7/19/99 2:11p Mikeb
43 * DA: sometimes big_ship_attack_normal is not known
45 * 10 6/14/99 3:21p Andsager
46 * Allow collisions between ship and its debris. Fix up collision pairs
47 * when large ship is warping out.
49 * 9 6/02/99 5:41p Andsager
50 * Reduce range of secondary weapons not fired from turrets in nebula.
51 * Reduce range of beams fired from turrrets in nebula
53 * 8 5/13/99 2:31p Jamesa
54 * DA: Temp fix when attacking subsys of big ship and BS is blown up.
56 * 7 5/06/99 11:46a Andsager
57 * Bug fixes. Don't get into illegal strafe submode. Don't choose turret
58 * enemy objnum for beam protected.
60 * 6 4/23/99 12:01p Johnson
63 * 5 4/20/99 3:40p Andsager
64 * Changes to big ship ai. Uses bounding box as limit where to fly to
67 * 4 4/16/99 5:54p Dave
68 * Support for on/off style "stream" weapons. Real early support for
69 * target-painting lasers.
71 * 3 10/13/98 9:29a Dave
72 * Started neatening up freespace.h. Many variables renamed and
73 * reorganized. Added AlphaColors.[h,cpp]
75 * 2 10/07/98 10:53a Dave
78 * 1 10/07/98 10:51a Dave
80 * 77 5/25/98 12:12a Mike
81 * Improve big ship avoidance to avoid big ships when in a nearby dock
82 * mode. Fixes very dumb looking behavior in sm2-01a. Verified sm1-08a
83 * not too dumb at start.
85 * 76 5/23/98 2:38a Mike
86 * Improve balance of cheat-firing Synaptics.
88 * 75 5/23/98 12:40a Mike
89 * Hook for Delta wing to fire Synaptic bombs in sm3-09a.
91 * 74 5/22/98 12:08p Mike
92 * Force reset of aspect locking when AI ships switch banks due to
93 * good-time-to-unload-secondaries
95 * 73 5/21/98 1:25p Lawrance
96 * Fix exception in ai_bpap() when no verts are in closest octant
98 * 72 5/21/98 9:57a Mike
99 * Massively improve firing of bombs at big ships. Improve willingness to
100 * take incoming fire to deliver bomb. Make a little easier to gain
103 * 71 5/12/98 10:06a Mike
104 * Fix a jillion bugs preventing ships from firing bombs at Lucifer in
107 * 70 5/10/98 11:30p Mike
108 * Better firing of bombs, less likely to go into strafe mode.
110 * 69 5/10/98 3:41a Lawrance
111 * Fix bug that was preventing ships from leaving strafe mode
113 * 68 5/08/98 4:39p Mike
114 * Make ships match bank when attacking large ships.
116 * 67 5/07/98 11:59p Mike
117 * Remove unneeded code, computation of num_verts.
119 * 66 4/29/98 5:01p Mike
120 * Large overhaul in how turrets fire.
122 * 65 4/27/98 11:59p Mike
123 * Intermediate checkin. Getting big ship turrets firing at big ships to
124 * use pick_big_attack_point.
126 * 64 4/23/98 12:32a Mike
127 * Fix bogus math which looked at (1-fov) instead of fov.
129 * 63 4/13/98 4:52p Allender
130 * remove AI_frametime and us flFrametime instead. Make lock warning work
131 * in multiplayer for aspect seeking missiles. Debris fixups
133 * 62 4/13/98 3:27p Lawrance
134 * Fix bug that caused ships to leave strafe mode right away since they
135 * hadn't taken damage for a while
137 * 61 4/12/98 2:02p Mike
138 * Make small ships avoid big ships.
139 * Turn on Collide_friendly flag.
141 * 60 4/01/98 9:21p John
142 * Made NDEBUG, optimized build with no warnings or errors.
144 * 59 3/25/98 12:00a Allender
145 * MK: make sure that output from function gets a valid valud (ai_pbap I
148 * 58 3/24/98 10:07p Adam
149 * AL: Add another Assert() to try and catch bogus wp->big_attack_point
151 * 57 3/24/98 9:56p Adam
152 * AL: Assert that local_attack_point is valid in ai_bpap
154 * 56 3/21/98 3:36p Mike
155 * Fix/optimize attacking of big ships.
157 * 55 3/16/98 5:17p Mike
158 * Make ships not get stuck on big ships when attacking.
160 * 54 3/16/98 12:03a Mike
161 * Add support for kamikaze mode. Add AIF_NO_DYNAMIC which means
162 * relentlessly pursue current goal.
164 * 53 3/12/98 4:04p Lawrance
165 * Leave strafe mode if haven't been hit for a while and no enemy
166 * fighter/bombers are close by.
168 * 52 3/09/98 5:12p Mike
169 * Make sure Pl_objp uses are valid.
170 * Throw asteroids at ships in asteroid field, not "Gal"
171 * Support ships warp out if no goals after 30 seconds.
173 * 51 3/03/98 11:51p Lawrance
174 * Ensure target is a ship before looking at SIF flags
176 * 50 3/02/98 11:35a Mike
177 * Fix logic with ai_do_default_behavior -- return after call.
179 * 49 2/19/98 7:37p Lawrance
180 * Fix bug that was causing problems with subsystem path following.
182 * 48 2/14/98 3:38p Mike
183 * Make bombs have arm time, drop for 1/2 second, have hull_strength.
184 * Make them not fired until closer to target.
186 * 47 2/13/98 3:56p Mike
187 * Make ships more likely to stick to player orders, not get so distracted
188 * by getting hit by someone else.
190 * 46 2/11/98 4:30p Lawrance
191 * Improve disarm/disable behavior.
193 * 45 2/11/98 1:16a Mike
194 * Mods to AI behavior to make it harder to stay on a ship's tail. Not
195 * too much improvement.
197 * 44 2/05/98 12:51a Mike
198 * Early asteroid stuff.
200 * 43 1/30/98 11:28a Comet
201 * Fix indexing problem into verts in ai_bpap()
203 * 42 1/29/98 10:46p Mike
204 * In ai_big_pick_attack_point, handle case of octant with no points.
206 * 41 1/29/98 1:39p Mike
207 * Better heat seeking homing on big ships.
209 * 40 1/27/98 4:18p Mike
210 * Not fire weapons too early when attacking large ships. Aspect lock
211 * take proper amount of time for AI ships.
213 * 39 1/22/98 5:14p Lawrance
214 * clean up ai_big code, clear path info when stop attacking a subsystem
216 * 38 1/20/98 11:41p Mike
217 * Ships use new system of preferred big weapons to fire. Also, support
218 * AIF_UNLOAD_SECONDARIES which means fire as fast as possible.
220 * 37 1/20/98 9:47a Mike
221 * Suppress optimized compiler warnings.
222 * Some secondary weapon work.
224 * 36 1/17/98 4:45p Mike
225 * Better support for AI selection of secondary weapons.
227 * 35 1/16/98 3:57p Mike
228 * Clean up some bugs with attacking of protected ships. Make turrets
229 * only fire at targets that are within range.
231 * 34 1/16/98 11:32a Mike
232 * Fix bug when a ship is attacking a dead subsystem on a protected ship.
234 * 33 1/07/98 1:32p Lawrance
235 * Remove some nprintf output
237 * 32 1/07/98 1:21p Sandeep
238 * Fix bug where accessing uninited pointer
240 * 31 1/06/98 6:58p Lawrance
241 * Attack turrets (sometimes) when fired upon while attacking a ship.
243 * 30 12/31/97 6:28p Lawrance
244 * Improve attacking subsystems on moving ships.
246 * 29 12/31/97 4:16p Lawrance
247 * Use different fov's for attacking subsystems depending if ship is
250 * 28 12/31/97 4:05p Mike
251 * Support for all teams optionally attacking all teams.
252 * Lead big ships when attacking their hull.
254 * 27 12/31/97 12:26p Lawrance
255 * Don't enter strafe mode for transports, fix bug that prevented strafe
256 * mode from getting entered.
258 * 26 12/30/97 5:20p Mike
259 * Don't fire secondaries at a protected ship if its hull is very weak.
261 * 25 12/30/97 4:26p Lawrance
262 * Take out some debug statements
264 * 24 12/15/97 7:16p Lawrance
265 * improving subsystem attacking
267 * 23 12/02/97 11:58a Lawrance
268 * avoid during strafe mode only when endangered by a weapon
270 * 22 12/01/97 5:11p Lawrance
271 * make strafe mode more effective... slow down when approaching and use
272 * afterburner in avoids
274 * 21 11/27/97 4:21p Lawrance
275 * set submode to -1 when switching to AIM_NONE from AIM_STRAFE
277 * 20 11/24/97 2:27a Lawrance
278 * if attacking a big ship and moving very slow, enter strafe mode if
279 * enemy fighter/bombers are close by
281 * 19 11/14/97 11:27a Johnson
282 * if goal_objnum is -1, and in STRAFE, return 1 from
283 * ai_maybe_follow_subsys_path(), so break out of STRAFE code
285 * 18 11/13/97 11:51a Johnson
286 * ALAN: comment out Int3() in AiBig... need to test on my own machine
288 * 17 11/12/97 11:51p Lawrance
289 * only check target_objnum == goal_objnum when following path
291 * 16 11/12/97 11:01p Lawrance
292 * change call to ai_find_path()
293 * ensure goal_objnum == target_objnum in STRAFE
295 * 15 11/12/97 11:18a Lawrance
296 * fix bug in path following of subsystems
298 * 14 11/11/97 5:22p Mike
299 * Don't allow ships to attack navbuoys.
300 * Make ships lead big ships with dumbfire.
301 * Make ships smarter about leaving evade weapon mode.
302 * Fix a couple Assert() bugs.
304 * 13 11/06/97 6:27p Lawrance
305 * fix bug that was messing up retreat point in STRAFE mode
307 * 12 11/06/97 4:53p Mike
308 * Limit number of ships that can simultaneously attack another. Make
309 * turrets inherit AI class from parent ship.
311 * 11 11/06/97 12:27a Mike
312 * Better avoid behavior.
313 * Modify ai_turn_towards_vector() to take a flag parameter.
315 * 10 11/02/97 10:55p Lawrance
316 * removed some unused code, added some comments
318 * 9 11/01/97 4:01p Mike
319 * Adapt to new ai_find_path().
321 * 8 10/31/97 11:24a Lawrance
322 * If taget changes from a big ship to small ship while in AIM_STRAFE,
323 * switch to AIM_CHASE
325 * 7 10/30/97 10:06p Lawrance
326 * chg some timing values so ship will reach retreat point
328 * 6 10/30/97 9:17p Lawrance
329 * work on getting AIM_STRAFE working well with disable/disarm, try to
332 * 5 10/30/97 12:32a Lawrance
333 * further work on AIM_STRAFE
335 * 4 10/29/97 6:25p Lawrance
336 * add AIM_STRAFE functionality
338 * 3 10/26/97 4:19p Lawrance
339 * added ai_get_weapon_dist()
341 * 2 10/26/97 3:24p Lawrance
342 * split off large ship ai code into AiBig.cpp
349 #include "linklist.h"
359 #include "floating.h"
361 #include "freespace.h"
363 #include "missiongoals.h"
364 #include "missionlog.h"
369 #include "hudmessage.h"
370 #include "missionmessage.h"
371 #include "cmeasure.h"
372 #include "staticrand.h"
373 #include "multimsgs.h"
374 #include "afterburner.h"
378 #include "missionparse.h"
381 #pragma optimize("", off)
382 #pragma auto_inline(off)
385 #define SCAN_FIGHTERS_INTERVAL 2000 // how often an AI fighter/bomber should scan for enemy fighter/bombers
386 // if sitting still and pounding on a big ship. If enemy fighters are
387 // close ( < ENTER_STRAFE_THREAT_DIST ), then enter AIM_STRAFE
389 #define ENTER_STRAFE_THREAT_DIST_SQUARED 360000 // use squared distance, instead of 600
391 #define MIN_DOT_TO_ATTACK_SUBSYS 0.7f
392 #define MIN_DOT_TO_ATTACK_MOVING_SUBSYS 0.97f
394 // AI BIG MAGIC NUMBERS
395 #define STRAFE_RETREAT_COLLIDE_TIME 2.0 // when anticipated collision time is less than this, begin retreat
396 #define STRAFE_RETREAT_COLLIDE_DIST 100 // when perpendicular distance to *surface* is less than this, begin retreat
397 #define STRAFE_RETREAT_BOX_DIST 300 // distance beyond the bounding box to retreat
399 #define EVADE_BOX_BASE_DISTANCE 300 // standard distance to end evade submode
400 #define EVADE_BOX_MIN_DISTANCE 200 // minimun distance to end evade submode, after long time
402 #define ATTACK_STOP_DISTANCE 150 // when distance to target is less than this, put on brakes
404 #define ATTACK_COLLIDE_BASE_DIST 300 // absolute distance at which to begin checking for possible collision
405 #define ATTACK_COLLIDE_AVOID_DIST 60 // perpendicular distance to attack surface at which begin avoiding
406 #define ATTACK_COLLIDE_AVOID_TIME 1.0 // anticipated collision time at which to begin evade
407 #define ATTACK_COLLIDE_SLOW_DIST 150 // perpendicular distance to attack surface at which begin slowing down
408 #define ATTACK_COLLIDE_SLOW_TIME 1.5 // anticipated collision time at which to begin slowing down
411 // forward declarations
412 void ai_big_evade_ship();
413 void ai_big_chase_attack(ai_info *aip, ship_info *sip, vector *enemy_pos, float dist_to_enemy);
414 void ai_big_avoid_ship();
415 int ai_big_maybe_follow_subsys_path(int do_dot_check=1);
417 extern int model_which_octant_distant_many( vector *pnt, int model_num,matrix *model_orient, vector * model_pos, polymodel **pm, int *octs);
418 extern void compute_desired_rvec(vector *rvec, vector *goal_pos, vector *cur_pos);
419 extern void big_ship_collide_recover_start(object *objp, object *big_objp, vector *collide_pos, vector *collision_normal);
422 // Called by ai_big_pick_attack_point.
423 // Generates a random attack point.
424 // If truly_random flag set (haha), then generate a pretty random number. Otherwise, generate a static rand which
425 // tends to not change from frame to frame.
426 // Try four times and choose nearest point to increase chance of getting a good point.
427 void ai_bpap(object *objp, vector *attacker_objp_pos, vector *attacker_objp_fvec, vector *attack_point, vector *local_attack_point, float fov, float weapon_travel_dist, vector *surface_normal)
430 vector result_point, best_point;
437 best_point = objp->pos;
438 nearest_dist = weapon_travel_dist;
440 model_which_octant_distant_many(attacker_objp_pos, Ships[objp->instance].modelnum, &objp->orient, &objp->pos, &pm, octs);
442 num_tries = (int) (vm_vec_dist(&objp->pos, attacker_objp_pos)/objp->radius);
447 num_tries = 4 - num_tries;
449 // Index #0 is best one.
450 if ( pm->octants[octs[0]].verts ) {
451 *local_attack_point = *pm->octants[octs[0]].verts[0]; // Set just in case it doesn't get set below.
453 vm_vec_zero(local_attack_point);
456 for (q=0; q<4; q++) {
457 octp = &pm->octants[octs[q]];
458 if (octp->nverts > 0) {
460 if (num_tries > octp->nverts)
461 num_tries = octp->nverts;
463 if (num_tries > octp->nverts)
464 num_tries = octp->nverts;
468 for (i=0; i<num_tries; i++) {
473 index = (int) (frand() * (octp->nverts));
475 rel_point = *octp->verts[index];
476 vm_vec_unrotate(&result_point, &rel_point, &objp->orient);
477 vm_vec_add2(&result_point, &objp->pos);
479 dist = vm_vec_normalized_dir(&v2p, &result_point, attacker_objp_pos);
480 dot = vm_vec_dot(&v2p, attacker_objp_fvec);
483 if (dist < nearest_dist) {
486 best_point = result_point;
487 *local_attack_point = rel_point;
488 Assert( !vm_is_vec_nan(local_attack_point) );
489 if (dot > (1.0f + fov)/2.0f) // If this point is quite good, quit searching for a better one.
498 *attack_point = best_point;
500 // Cast from attack_objp_pos to local_attack_pos and check for nearest collision.
501 // If no collision, cast to (0,0,0) [center of big ship]** [best_point initialized to 000]
503 // do in world coords to get attack point, then translate to local for local_attack_point
504 vector attack_dir, end_point, temp;
506 dist = vm_vec_normalized_dir(&attack_dir, attack_point, attacker_objp_pos);
509 vm_vec_scale_add(&end_point, attack_point, &attack_dir, 30.0f);
511 vm_vec_scale_add(&end_point, attack_point, attacker_objp_fvec, 30.0f);
515 mc.model_num = Ships[objp->instance].modelnum;
516 mc.orient = &objp->orient;
518 mc.p0 = attacker_objp_pos;
520 mc.flags = MC_CHECK_MODEL;
524 if (mc.num_hits > 0) {
525 *attack_point = mc.hit_point_world;
526 vm_vec_sub(&temp, attack_point, &objp->pos);
527 vm_vec_rotate(local_attack_point, &temp, &objp->orient);
528 if (surface_normal) {
529 vm_vec_unrotate(surface_normal, &mc.hit_normal, &objp->orient);
532 vm_vec_zero(local_attack_point);
533 *attack_point = objp->pos;
534 if (surface_normal) {
535 vm_vec_zero(surface_normal);
540 // Stuff a point to attack based on nearest octant.
541 // If no points in that octant, leave attack_point unmodified.
543 // Note: Default value for fov is 1.0f 1.0f means don't use fov parameter.
544 // If fov != 1.0f, try up to four times to find a point that's in the field of view.
545 void ai_big_pick_attack_point_turret(object *objp, ship_subsys *ssp, vector *gpos, vector *gvec, vector *attack_point, float fov, float weapon_travel_dist)
547 if (!timestamp_elapsed(ssp->turret_pick_big_attack_point_timestamp)) {
549 vm_vec_unrotate(&result_point, &ssp->turret_big_attack_point, &objp->orient);
550 vm_vec_add(attack_point, &result_point, &objp->pos);
552 vector local_attack_point;
553 ssp->turret_pick_big_attack_point_timestamp = timestamp(2000 + (int) (frand()*500.0f));
554 ai_bpap(objp, gpos, gvec, attack_point, &local_attack_point, fov, weapon_travel_dist, NULL);
555 ssp->turret_big_attack_point = local_attack_point;
559 // Stuff a point to attack based on nearest octant.
560 // If no points in that octant, leave attack_point unmodified.
562 // Note: Default value for fov is 1.0f 1.0f means don't use fov parameter.
563 // If fov != 1.0f, try up to four times to find a point that's in the field of view.
564 // Note, attacker_objp can be a ship or a weapon.
565 void ai_big_pick_attack_point(object *objp, object *attacker_objp, vector *attack_point, float fov)
567 Assert(objp->instance > -1);
568 Assert(objp->type == OBJ_SHIP);
570 vector local_attack_point;
572 switch (attacker_objp->type) {
574 ai_info *attacker_aip;
575 attacker_aip = &Ai_info[Ships[attacker_objp->instance].ai_index];
576 if (!timestamp_elapsed(attacker_aip->pick_big_attack_point_timestamp)) {
579 vm_vec_unrotate(&result_point, &attacker_aip->big_attack_point, &objp->orient);
580 vm_vec_add(attack_point, &result_point, &objp->pos);
585 attacker_aip->pick_big_attack_point_timestamp = timestamp(2000 + (int) (frand()*500.0f));
589 weapon *wp = &Weapons[attacker_objp->instance];
591 if (!timestamp_elapsed(wp->pick_big_attack_point_timestamp)) {
594 vm_vec_unrotate(&result_point, &wp->big_attack_point, &objp->orient);
595 vm_vec_add(attack_point, &result_point, &objp->pos);
599 wp->pick_big_attack_point_timestamp = timestamp(2000 + (int) (frand()*500.0f));
605 // checks valid line to target
606 vector surface_normal;
607 ai_bpap(objp, &attacker_objp->pos, &attacker_objp->orient.v.fvec, attack_point, &local_attack_point, fov, 99999.9f, &surface_normal);
609 switch (attacker_objp->type) {
611 ai_info *attacker_aip;
612 // if we can't find a new local_attack_point don't change local_attack_point
613 if (vm_vec_mag_squared(&local_attack_point) < 1) {
617 attacker_aip = &Ai_info[Ships[attacker_objp->instance].ai_index];
618 attacker_aip->big_attack_point = local_attack_point;
619 attacker_aip->big_attack_surface_normal = surface_normal;
623 weapon *wp = &Weapons[attacker_objp->instance];
624 wp->big_attack_point = local_attack_point;
625 Assert( !vm_is_vec_nan(&wp->big_attack_point) );
633 // Handler for SM_EVADE submode ( called from ai_big_chase() )
634 void ai_big_evade_ship()
636 vector player_pos, enemy_pos;
638 ship *shipp = &Ships[Pl_objp->instance];
639 ai_info *aip = &Ai_info[shipp->ai_index];
640 vector randvec, semi_enemy_pos;
642 ai_set_positions(Pl_objp, En_objp, aip, &player_pos, &enemy_pos);
644 dist = vm_vec_dist_quick(&player_pos, &enemy_pos);
645 vm_vec_rand_vec_quick(&randvec);
646 if ((Missiontime>>14) & 1) {
647 vm_vec_scale_add(&semi_enemy_pos, &enemy_pos, &randvec, dist/2.0f);
648 aip->prev_goal_point = semi_enemy_pos;
650 semi_enemy_pos = aip->prev_goal_point;
653 accelerate_ship(aip, 1.0f - ((Missiontime>>8) & 0x3f)/128.0f );
654 turn_away_from_point(Pl_objp, &semi_enemy_pos, 0.0f);
659 box_dist = get_world_closest_box_point_with_delta(&box_vec, En_objp, &player_pos, &is_inside, EVADE_BOX_BASE_DISTANCE);
660 if (box_dist > EVADE_BOX_BASE_DISTANCE) {
661 aip->submode = SM_ATTACK;
662 aip->submode_start_time = Missiontime;
663 } else if ((box_dist > EVADE_BOX_MIN_DISTANCE) && (Missiontime - aip->submode_start_time > i2f(5)) ) {
664 aip->submode = SM_ATTACK;
665 aip->submode_start_time = Missiontime;
670 /* if (dist > 4*En_objp->radius) {
671 aip->submode = SM_ATTACK;
672 aip->submode_start_time = Missiontime;
673 } else if (dist > En_objp->radius) {
674 if (Missiontime - aip->submode_start_time > i2f(5)) {
675 aip->submode = SM_ATTACK;
676 aip->submode_start_time = Missiontime;
681 // Handler for SM_AVOID submode ( called from ai_big_chase() )
682 void ai_big_avoid_ship()
687 // reset path following information
688 void ai_big_subsys_path_cleanup(ai_info *aip)
690 if ( aip->ai_flags & AIF_ON_SUBSYS_PATH ) {
691 aip->ai_flags &= ~AIF_ON_SUBSYS_PATH;
692 aip->path_goal_dist = -1;
693 aip->path_start = -1;
695 aip->path_length = 0;
699 // Maybe Pl_objp needs to follow a path to get in line-of-sight to a subsystem
700 // input: do_dot_check => default value 0, flag to indicate whether check should be done to ensure
701 // subsystem is within certain field of view. We don't want to check fov when
702 // strafing, since ship is weaving to avoid turret fire
703 int ai_big_maybe_follow_subsys_path(int do_dot_check)
706 float dot = 1.0f, min_dot;
709 aip = &Ai_info[Ships[Pl_objp->instance].ai_index];
710 target_objp = &Objects[aip->target_objnum];
712 if ( (aip->targeted_subsys != NULL) && (aip->target_objnum >= 0) && (aip->targeted_subsys->system_info->path_num >= 0) ) {
714 int subsys_path_num, subsys_in_sight, checked_sight;
717 pm = model_get( Ships[Pl_objp->instance].modelnum );
719 // If attacking a subsystem, ensure that we have an unobstructed line of sight... if not, then move
720 // towards path linked to subsystem
721 subsys_in_sight = 0; // assume Pl_objp doesn't have line of sight to subys
723 // only check for subsystem sight every N milliseconds
725 if ( timestamp_elapsed(aip->path_subsystem_next_check) ) {
726 vector geye, gsubpos;
729 aip->path_subsystem_next_check = timestamp(1500);
731 // get world pos of eye (stored in geye)
732 ep = &(pm->view_positions[0] );
733 model_find_world_point( &geye, &ep->pnt, Ships[Pl_objp->instance].modelnum, 0, &Pl_objp->orient, &Pl_objp->pos );
735 // get world pos of subsystem
736 vm_vec_unrotate(&gsubpos, &aip->targeted_subsys->system_info->pnt, &En_objp->orient);
737 vm_vec_add2(&gsubpos, &En_objp->pos);
741 // Calc dot between subsys normal (based on using path info), and subsys_to_eye vector. This is
742 // useful later when we want to decide whether we have a realistic line-of-sight to the subsystem.
743 vector subsys_normal, subsys_to_eye;
744 if ( do_dot_check ) {
745 if ( !ship_return_subsys_path_normal(&Ships[target_objp->instance], aip->targeted_subsys, &gsubpos, &subsys_normal) ) {
746 vm_vec_normalized_dir(&subsys_to_eye, &geye, &gsubpos);
747 dot = vm_vec_dot(&subsys_normal, &subsys_to_eye);
751 if ( ship_subsystem_in_sight(En_objp, aip->targeted_subsys, &geye, &gsubpos, 1) ) {
756 // check if subsystem not in sight
757 min_dot = (target_objp->phys_info.fspeed > 5.0f?MIN_DOT_TO_ATTACK_MOVING_SUBSYS:MIN_DOT_TO_ATTACK_SUBSYS);
758 if ( (checked_sight && ((!subsys_in_sight) || (dot < min_dot)) ) ) {
760 aip->path_goal_dist = 5;
761 subsys_path_num = aip->targeted_subsys->system_info->path_num;
762 if ( (aip->path_start) == -1 || (aip->mp_index != subsys_path_num) ) {
763 // maybe create a new path
764 if ( subsys_path_num >= 0 ) {
765 Assert(aip->target_objnum >= 0);
766 ai_find_path(Pl_objp, aip->target_objnum, subsys_path_num, 0, 1);
767 if ( aip->path_start >= 0 ) {
768 aip->ai_flags |= AIF_ON_SUBSYS_PATH;
774 if ( checked_sight && subsys_in_sight && (dot > min_dot) ) {
775 // we've got a clear shot, stay here for a bit
776 aip->path_subsystem_next_check = timestamp_rand(5000,8000);
779 // If there is a path that we are tracking, see if ship has gotten to within
780 // aip->path_goal_dist units. If so, then ship can stop following the path. This
781 // is required since we don't want to follow the path all the way to the subsystem,
782 // and we want ships to stop different distances from their path destination
783 if ( aip->path_length > 0 ) {
787 Assert(aip->path_length >= 2);
788 dist = vm_vec_dist_quick(&Path_points[aip->path_start+aip->path_length-2].pos, &Pl_objp->pos);
790 if ( aip->path_cur >= (aip->path_start+aip->path_length-1) ) {
794 min_dot = (target_objp->phys_info.fspeed > 5.0f?MIN_DOT_TO_ATTACK_MOVING_SUBSYS:MIN_DOT_TO_ATTACK_SUBSYS);
795 if ( (checked_sight && subsys_in_sight) && (dot > min_dot) ) {
799 // if we've reached the destination, then we can stop following the path
800 if ( path_done || ( dist < aip->path_goal_dist ) || in_view ) {
801 ai_big_subsys_path_cleanup(aip);
802 } else if ( dist > aip->path_goal_dist ) {
803 // If we have a path to follow, follow it and return
804 if ( aip->path_start != -1 ) {
805 // for now, only follow the path to the first point
806 if ( aip->path_cur < (aip->path_start+aip->path_length-1) ) {
807 if ( aip->goal_objnum != aip->target_objnum ) {
808 //Int3(); // what is going on here? - Get Alan
809 aip->previous_mode = aip->mode;
810 aip->mode = AIM_NONE;
825 // This function is only called from ai_big_chase_attack() when a ship is flying very slowly and
826 // attacking a big ship. The ship should scan for enemy fighter/bombers... if any are close, then
827 // return 1, otherwise return 0;
829 // input: aip => ai_info pointer for Pl_objp
830 // sip => ship_info pointer for Pl_objp
832 // exit: 1 => ship should enter strafe mode
833 // 0 => ship should not change ai mode, no fighter/bomber threats are near
835 // NOTE: uses SCAN_FIGHTERS_INTERVAL and ENTER_STRAFE_THREAT_DIST_SQUARED which are defined in AiBig.h
836 int ai_big_maybe_start_strafe(ai_info *aip, ship_info *sip)
838 // if moving slowly (or stopped), and SIF_SMALL_SHIP, then enter STRAFE mode if enemy fighter/bombers
840 if ( sip->flags & SIF_SMALL_SHIP ) {
841 if ( timestamp_elapsed(aip->scan_for_enemy_timestamp) ) {
847 aip->scan_for_enemy_timestamp = timestamp(SCAN_FIGHTERS_INTERVAL);
848 // iterate through ships, and see if any fighter/bomber from opposite team are near
849 so = GET_FIRST(&Ship_obj_list);
850 while( so != END_OF_LIST(&Ship_obj_list) ) {
851 test_objp = &Objects[so->objnum];
852 test_sp = &Ships[test_objp->instance];
854 if ( test_sp->team != Ships[Pl_objp->instance].team ) {
855 if ( Ship_info[test_sp->ship_info_index].flags & SIF_SMALL_SHIP ) {
856 dist_squared = vm_vec_dist_squared(&Pl_objp->pos, &test_objp->pos);
857 if ( dist_squared < ENTER_STRAFE_THREAT_DIST_SQUARED ) {
866 // If we've reached here, there are no enemy fighter/bombers near
870 // ATTACK submode handler for chase mode.
871 void ai_big_chase_attack(ai_info *aip, ship_info *sip, vector *enemy_pos, float dist_to_enemy)
874 float dot_to_enemy, time_to_hit;
875 polymodel *po = model_get( sip->modelnum );
877 // Maybe evade an incoming weapon.
878 if (((time_to_hit = ai_endangered_by_weapon(aip)) < 4.0f) && (time_to_hit >= 0.0f)) {
879 aip->submode = SM_EVADE_WEAPON;
880 aip->submode_start_time = Missiontime;
881 aip->prev_goal_point = En_objp->pos;
883 // If moving slowly, maybe evade incoming fire.
884 if (Pl_objp->phys_info.speed < 3.0f) {
886 for ( objp = GET_FIRST(&obj_used_list); objp !=END_OF_LIST(&obj_used_list); objp = GET_NEXT(objp) ) {
887 if ((objp->type == OBJ_WEAPON) && (Weapons[objp->instance].team != Ships[Pl_objp->instance].team))
888 if (Weapon_info[objp->instance].subtype == WP_LASER) {
892 vm_vec_sub(&in_vec, &objp->pos, &Pl_objp->pos);
893 if (vm_vec_dot(&in_vec, &objp->orient.v.fvec) > 0.0f) {
894 dist = vm_vec_normalize(&in_vec);
895 if ((dist < 200.0f) && (vm_vec_dot(&in_vec, &objp->orient.v.fvec) > 0.95f)) {
896 if ((Objects[objp->parent].signature == objp->parent_sig) && (vm_vec_dist_quick(&objp->pos, &Objects[objp->parent].pos) < 300.0f)) {
897 set_target_objnum(aip, objp->parent);
898 aip->submode = SM_ATTACK;
899 aip->submode_start_time = Missiontime;
901 aip->submode = SM_EVADE;
902 aip->submode_start_time = Missiontime;
903 aip->prev_goal_point = En_objp->pos;
910 // Since ship is moving slowly and attacking a large ship, scan if enemy fighters are near, if so
911 // then enter strafe mode
912 if ( ai_big_maybe_start_strafe(aip, sip) ) {
913 aip->previous_mode = aip->mode;
914 aip->mode = AIM_STRAFE;
915 aip->submode_parm0 = Missiontime; // use parm0 as time strafe mode entered
916 aip->submode = AIS_STRAFE_ATTACK;
917 aip->submode_start_time = Missiontime;
921 } // end if ( Pl_objp->phys_info.speed < 3.0f )
923 // see if Pl_objp needs to reposition to get a good shot at subsystem which is being attacked
924 if ( ai_big_maybe_follow_subsys_path() ) {
928 vector *rel_pos, vec_to_enemy;
929 float weapon_travel_dist;
931 start_bank = Ships[aip->shipnum].weapons.current_primary_bank;
933 if ((po->n_guns) && (start_bank != -1)) {
934 rel_pos = &po->gun_banks[start_bank].pnt[0];
938 dist_to_enemy = vm_vec_normalized_dir(&vec_to_enemy, enemy_pos, &Pl_objp->pos);
939 dot_to_enemy = vm_vec_dot(&vec_to_enemy, &Pl_objp->orient.v.fvec);
941 vector rvec_vec, *rvec = &rvec_vec;
943 if (dist_to_enemy > 500.0f)
944 compute_desired_rvec(rvec, enemy_pos, &Pl_objp->pos);
948 ai_turn_towards_vector(enemy_pos, Pl_objp, flFrametime, sip->srotation_time, NULL, rel_pos, 0.0f, 0, rvec);
950 // calc range of primary weapon
951 weapon_travel_dist = ai_get_weapon_dist(&Ships[Pl_objp->instance].weapons);
953 if ( aip->targeted_subsys != NULL ) {
954 if (dist_to_enemy > (weapon_travel_dist-20))
955 accelerate_ship(aip, 1.0f);
957 // AL 12-31-97: Move at least as quickly as your target is moving...
958 accelerate_ship(aip, max(1.0f - dot_to_enemy, Objects[aip->target_objnum].phys_info.fspeed/sip->max_speed));
963 if (dot_to_enemy < 0.0f) {
965 } else if (dot_to_enemy < 0.75f) {
968 if (dist_to_enemy > weapon_travel_dist/2.0f) {
971 accel = 1.0f - dot_to_enemy;
975 // use dist normal to enemy here (dont break 50 barrier)
976 if (dist_to_enemy < ATTACK_STOP_DISTANCE) {
977 // accelerate_ship(aip, accel * 0.5f);
978 accelerate_ship(aip, -1.0f);
980 accelerate_ship(aip, accel);
987 // Handler for submode SM_CONTINUOUS_TURN
988 void ai_big_chase_ct()
993 extern void ai_select_secondary_weapon(object *objp, ship_weapon *swp, int priority1 = -1, int priority2 = -1);
994 extern float set_secondary_fire_delay(ai_info *aip, ship *shipp, weapon_info *swip);
995 extern void ai_choose_secondary_weapon(object *objp, ai_info *aip, object *en_objp);
996 extern int maybe_avoid_big_ship(object *objp, object *ignore_objp, ai_info *aip, vector *goal_point, float delta_time);
998 extern void maybe_cheat_fire_synaptic(object *objp, ai_info *aip);
1000 // Determine if Pl_objp should fire weapons at current target, based on input parameters
1002 // dist_to_enemy => distance (in m) to attack point on current target
1003 // dot_to_enemy => dot product between fvec of Pl_objp and vector from Pl_objp to attack point
1005 void ai_big_maybe_fire_weapons(float dist_to_enemy, float dot_to_enemy, vector *firing_pos, vector *enemy_pos, vector *enemy_vel)
1010 aip = &Ai_info[Ships[Pl_objp->instance].ai_index];
1011 swp = &Ships[Pl_objp->instance].weapons;
1013 if (dot_to_enemy > 0.95f - 0.5f * En_objp->radius/max(1.0f, En_objp->radius + dist_to_enemy)) {
1014 aip->time_enemy_in_range += flFrametime;
1016 // Chance of hitting ship is based on dot product of firing ship's forward vector with vector to ship
1017 // and also the size of the target relative to distance to target.
1018 if (dot_to_enemy > max(0.5f, 0.90f + aip->ai_accuracy/10.0f - En_objp->radius/max(1.0f,dist_to_enemy))) {
1021 temp_shipp = &Ships[Pl_objp->instance];
1022 ship_weapon *tswp = &temp_shipp->weapons;
1024 if ( tswp->num_primary_banks > 0 ) {
1025 Assert(tswp->current_primary_bank < tswp->num_primary_banks);
1026 weapon_info *wip = &Weapon_info[tswp->primary_bank_weapons[tswp->current_primary_bank]];
1028 if (dist_to_enemy < wip->max_speed * wip->lifetime)
1029 ai_fire_primary_weapon(Pl_objp);
1031 int priority1, priority2;
1036 // Maybe favor selecting a bomb.
1037 // Note, if you're firing a bomb, if it's aspect seeking, the firing conditions can be looser.
1038 if (Ship_info[Ships[En_objp->instance].ship_info_index].flags & (SIF_BIG_SHIP | SIF_HUGE_SHIP))
1039 if (En_objp->phys_info.speed * dist_to_enemy < 5000.0f) // Don't select a bomb if enemy moving fast relative to distance
1040 priority1 = WIF_BOMB;
1042 if (!(En_objp->flags & OF_PROTECTED)) {
1043 //ai_select_secondary_weapon(Pl_objp, tswp, priority1, priority2); // Note, need to select to get weapon speed and lifetime
1045 ai_choose_secondary_weapon(Pl_objp, aip, En_objp);
1046 int current_bank = tswp->current_secondary_bank;
1047 weapon_info *swip = &Weapon_info[tswp->secondary_bank_weapons[current_bank]];
1049 // If ship is protected and very low on hits, don't fire missiles.
1050 if ((current_bank > -1) && (!(En_objp->flags & OF_PROTECTED) || (En_objp->hull_strength > 10*swip->damage))) {
1051 if (aip->ai_flags & AIF_UNLOAD_SECONDARIES) {
1052 if (timestamp_until(swp->next_secondary_fire_stamp[current_bank]) > swip->fire_wait*1000.0f) {
1053 swp->next_secondary_fire_stamp[current_bank] = timestamp((int) (swip->fire_wait*1000.0f));
1057 if (timestamp_elapsed(swp->next_secondary_fire_stamp[current_bank])) {
1059 float firing_range = swip->max_speed * swip->lifetime;
1060 // reduce firing range of secondaries in nebula
1061 extern int Nebula_sec_range;
1062 if ((The_mission.flags & MISSION_FLAG_FULLNEB) && Nebula_sec_range) {
1063 firing_range *= 0.8f;
1066 float t = 0.25f; // default delay in seconds until next fire.
1068 if (dist_to_enemy < firing_range*1.0f) {
1071 //vm_vec_scale_add(&future_enemy_pos, enemy_pos, enemy_vel, dist_to_enemy/swip->max_speed);
1072 //if (vm_vec_dist_quick(&future_enemy_pos, firing_pos) < firing_range * 0.8f) {
1073 if (ai_fire_secondary_weapon(Pl_objp)) {
1074 if (aip->ai_flags & AIF_UNLOAD_SECONDARIES) {
1075 t = swip->fire_wait;
1077 t = set_secondary_fire_delay(aip, temp_shipp, swip);
1080 swp->next_secondary_fire_stamp[current_bank] = timestamp((int) (t*1000.0f));
1084 swp->next_secondary_fire_stamp[current_bank] = timestamp((int) (t*1000.0f));
1091 aip->time_enemy_in_range *= (1.0f - flFrametime);
1095 // switch ai ship into chase mode
1096 void ai_big_switch_to_chase_mode(ai_info *aip)
1098 aip->previous_mode = aip->mode;
1099 aip->mode = AIM_CHASE;
1100 aip->submode = SM_ATTACK;
1101 aip->submode_start_time = Missiontime;
1104 extern int ai_big_strafe_maybe_retreat(float dist, vector *target_pos);
1106 // Make object Pl_objp chase object En_objp, which is a big ship, not a small ship.
1109 float dist_to_enemy, dot_to_enemy;
1110 vector player_pos, enemy_pos, vec_to_enemy;
1111 ship_info *sip = &Ship_info[Ships[Pl_objp->instance].ship_info_index];
1112 ship *shipp = &Ships[Pl_objp->instance];
1113 ai_info *aip = &Ai_info[shipp->ai_index];
1114 int enemy_ship_type;
1115 vector predicted_enemy_pos;
1117 Assert(aip->mode == AIM_CHASE);
1119 maybe_cheat_fire_synaptic(Pl_objp, aip);
1121 enemy_ship_type = Ship_info[Ships[En_objp->instance].ship_info_index].flags;
1123 ai_set_positions(Pl_objp, En_objp, aip, &player_pos, &enemy_pos);
1125 player_pos = Pl_objp->pos;
1126 ai_big_pick_attack_point(En_objp, Pl_objp, &enemy_pos, 0.8f);
1128 // Compute the predicted position of the center of the ship, then add the delta to the goal pos.
1129 if (En_objp->phys_info.speed > 3.0f) {
1130 set_predicted_enemy_pos(&predicted_enemy_pos, Pl_objp, En_objp, aip);
1131 vm_vec_add2(&enemy_pos, &predicted_enemy_pos);
1132 vm_vec_sub2(&enemy_pos, &En_objp->pos);
1134 predicted_enemy_pos = En_objp->pos;
1136 if (aip->targeted_subsys != NULL) {
1137 get_subsystem_pos(&enemy_pos, En_objp, aip->targeted_subsys);
1140 dist_to_enemy = vm_vec_normalized_dir(&vec_to_enemy, &enemy_pos, &player_pos); // - En_objp->radius;
1141 dot_to_enemy = vm_vec_dot(&vec_to_enemy, &Pl_objp->orient.v.fvec);
1143 if (aip->ai_flags & AIF_TARGET_COLLISION) {
1144 if ( ai_big_strafe_maybe_retreat(dist_to_enemy, &enemy_pos) ) {
1145 aip->mode = AIM_STRAFE;
1146 aip->submode = AIS_STRAFE_AVOID;
1147 aip->submode_parm0 = Missiontime; // use parm0 as time strafe mode entered
1152 if (aip->ai_flags & AIF_KAMIKAZE) {
1153 //nprintf(("AI", "Kamikaze: %7.3f %7.3f\n", dot_to_enemy, dist_to_enemy));
1154 accelerate_ship(aip, 1.0f);
1155 if ((dist_to_enemy < 400.0f) && ai_maybe_fire_afterburner(Pl_objp, aip)) {
1156 afterburners_start(Pl_objp);
1157 aip->afterburner_stop_time = Missiontime + 3*F1_0;
1161 // If just acquired target, or target is not in reasonable cone, don't refine believed enemy position.
1162 if ((dot_to_enemy < 0.25f) || (aip->target_time < 1.0f) || (aip->ai_flags & AIF_SEEK_LOCK)) {
1163 update_aspect_lock_information(aip, &vec_to_enemy, dist_to_enemy - En_objp->radius, En_objp->radius);
1164 } else if (aip->targeted_subsys != NULL) {
1165 Assert(aip->targeted_subsys != NULL);
1166 get_subsystem_pos(&enemy_pos, En_objp, aip->targeted_subsys);
1167 vm_vec_add2(&enemy_pos, &predicted_enemy_pos);
1168 vm_vec_sub2(&enemy_pos, &En_objp->pos);
1169 dist_to_enemy = vm_vec_normalized_dir(&vec_to_enemy, &enemy_pos, &player_pos); // - En_objp->radius;
1170 dot_to_enemy = vm_vec_dot(&vec_to_enemy, &Pl_objp->orient.v.fvec);
1171 update_aspect_lock_information(aip, &vec_to_enemy, dist_to_enemy, En_objp->radius);
1172 } else if (En_objp->flags & OF_PROTECTED) { // If protected and we're not attacking a subsystem, stop attacking!
1173 update_aspect_lock_information(aip, &vec_to_enemy, dist_to_enemy - En_objp->radius, En_objp->radius);
1174 aip->target_objnum = -1;
1175 if (find_enemy(Pl_objp-Objects, MAX_ENEMY_DISTANCE, Skill_level_max_attackers[Game_skill_level]) == -1) {
1176 ai_do_default_behavior(Pl_objp);
1180 update_aspect_lock_information(aip, &vec_to_enemy, dist_to_enemy - En_objp->radius, En_objp->radius);
1183 // If recently acquired target and not looking at target, just turn a bit.
1184 switch (aip->submode) {
1186 case SM_SUPER_ATTACK:
1187 if (vm_vec_dist_quick(&Pl_objp->pos, &predicted_enemy_pos) > 100.0f + En_objp->radius * 2.0f) {
1188 if (maybe_avoid_big_ship(Pl_objp, En_objp, aip, &predicted_enemy_pos, 10.0f))
1192 if (aip->target_time < 2.0f)
1193 if ((dot_to_enemy < 0.9f) || (dist_to_enemy > 300.0f)) {
1194 aip->submode = SM_CONTINUOUS_TURN;
1195 aip->submode_start_time = Missiontime - fl2f(2.75f); // This backdated start time allows immediate switchout.
1196 if (dot_to_enemy > 0.0f)
1197 aip->target_time += flFrametime * dot_to_enemy;
1203 // Set turn and acceleration based on submode.
1205 switch (aip->submode) {
1206 case SM_CONTINUOUS_TURN:
1211 case SM_SUPER_ATTACK:
1212 case SM_ATTACK_FOREVER:
1213 ai_big_chase_attack(aip, sip, &enemy_pos, dist_to_enemy);
1217 ai_big_evade_ship();
1221 ai_big_avoid_ship();
1224 case SM_EVADE_WEAPON:
1229 aip->last_attack_time = Missiontime;
1230 aip->submode = SM_ATTACK;
1234 // maybe Pl_objp was forced into strafe mode, if so return now
1235 if ( aip->mode == AIM_STRAFE )
1238 // Maybe choose a new submode.
1239 if (aip->submode != SM_AVOID && aip->submode != SM_EVADE ) {
1240 // If a very long time since attacked, attack no matter what!
1241 if (aip->submode != SM_SUPER_ATTACK) {
1242 if (Missiontime - aip->last_attack_time > i2f(6)) {
1243 aip->submode = SM_SUPER_ATTACK;
1244 aip->submode_start_time = Missiontime;
1245 aip->last_attack_time = Missiontime;
1249 // If a collision is expected, pull out!
1250 float dist_normal_to_enemy;
1252 if (vm_vec_mag_squared(&aip->big_attack_surface_normal) > 0.9) {
1253 dist_normal_to_enemy = fl_abs((dist_to_enemy * vm_vec_dot(&vec_to_enemy, &aip->big_attack_surface_normal)));
1255 // don;t have normal so use a conservative value here
1256 dist_normal_to_enemy = 0.3f * dist_to_enemy;
1259 // 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));
1260 // if (Framecount % 30 == 1) {
1261 // mprintf(("normal dist; %.1f, time: %.1f\n", dist_normal_to_enemy, time_to_enemy));
1264 // 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)
1265 // this will allow us to get closer on a bad normal
1266 dist_normal_to_enemy = max(0.3f*dist_to_enemy, dist_normal_to_enemy);
1268 if (dist_to_enemy < ATTACK_COLLIDE_BASE_DIST) {
1269 // within 50m or 1sec
1270 float time_to_enemy;
1271 if (vm_vec_mag_squared(&aip->big_attack_surface_normal) > 0.9) {
1272 if (Pl_objp->phys_info.speed > 0.1) {
1273 time_to_enemy = dist_normal_to_enemy / fl_abs(vm_vec_dot(&Pl_objp->phys_info.vel, &aip->big_attack_surface_normal));
1276 time_to_enemy = 100.0f;
1279 if (Pl_objp->phys_info.speed > 0.1) {
1280 time_to_enemy = dist_normal_to_enemy / Pl_objp->phys_info.speed;
1283 time_to_enemy = 100.0f;
1287 float speed_dist = max(0.0f, (Pl_objp->phys_info.speed-50) * 2);
1288 if ((dist_normal_to_enemy < ATTACK_COLLIDE_AVOID_DIST + speed_dist) || (time_to_enemy < ATTACK_COLLIDE_AVOID_TIME) ) {
1289 // get away, simulate crsh recovery (don't use avoid)
1290 // accelerate_ship(aip, -1.0f);
1291 big_ship_collide_recover_start(Pl_objp, En_objp, &Pl_objp->pos, NULL);
1292 // aip->submode = SM_AVOID;
1293 // aip->submode_start_time = Missiontime;
1294 } else if ((dist_normal_to_enemy < ATTACK_COLLIDE_SLOW_DIST) || (time_to_enemy < ATTACK_COLLIDE_SLOW_TIME) ) {
1296 accelerate_ship(aip, -1.0f);
1301 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)) {
1302 if (might_collide_with_ship(Pl_objp, En_objp, dot_to_enemy, dist_to_enemy, (aip->targeted_subsys == NULL)*2.0f + 1.5f)) {
1303 if ((Missiontime - aip->last_hit_time > F1_0*4) && (dist_to_enemy < Pl_objp->radius*2 + En_objp->radius*2)) {
1304 accelerate_ship(aip, -1.0f);
1306 aip->submode = SM_AVOID;
1307 aip->submode_start_time = Missiontime;
1313 switch (aip->submode) {
1314 case SM_CONTINUOUS_TURN:
1315 if (Missiontime - aip->submode_start_time > i2f(3)) {
1316 aip->last_attack_time = Missiontime;
1317 aip->submode = SM_ATTACK;
1318 aip->submode_start_time = Missiontime;
1323 case SM_ATTACK_FOREVER:
1327 if (dist_to_enemy > 4*En_objp->radius) {
1328 aip->submode = SM_ATTACK;
1329 aip->submode_start_time = Missiontime;
1330 aip->last_attack_time = Missiontime;
1334 case SM_SUPER_ATTACK:
1338 // if outside box by > 300 m
1339 // DA 4/20/98 can never get here attacking a big ship
1342 float box_dist = get_world_closest_box_point_with_delta(NULL, En_objp, &Pl_objp->pos, &is_inside, 0.0f);
1344 if (box_dist > 300) {
1345 aip->submode = SM_ATTACK;
1346 aip->last_attack_time = Missiontime;
1347 aip->submode_start_time = Missiontime;
1352 /* if (dot_to_enemy > -0.2f) {
1353 aip->submode_start_time = Missiontime;
1354 } else if (Missiontime - aip->submode_start_time > i2f(1)/2) {
1355 if (might_collide_with_ship(Pl_objp, En_objp, dot_to_enemy, dist_to_enemy, 3.0f)) {
1356 aip->submode_start_time = Missiontime;
1358 aip->submode = SM_ATTACK;
1359 aip->last_attack_time = Missiontime;
1360 aip->submode_start_time = Missiontime;
1366 case SM_EVADE_WEAPON:
1367 if (aip->danger_weapon_objnum == -1) {
1368 aip->submode = SM_ATTACK;
1369 aip->submode_start_time = Missiontime;
1370 aip->last_attack_time = Missiontime;
1375 aip->submode = SM_ATTACK;
1376 aip->last_attack_time = Missiontime;
1377 aip->submode_start_time = Missiontime;
1381 // Maybe fire primary weapon and update time_enemy_in_range
1383 //nprintf(("AI", "time_enemy_in_range = %7.3f, dot = %7.3f\n", aip->time_enemy_in_range, dot_to_enemy));
1385 // AL: add condition that Pl_objp must not be following a path to fire. This may be too extreme, but
1386 // I noticed AI ships firing inappropriately when following a path near a big ship.
1387 // TODO: investigate why ships fire (and aren't close to hitting ship) when following a path near
1389 if (aip->mode != AIM_EVADE && aip->path_start == -1 ) {
1390 ai_big_maybe_fire_weapons(dist_to_enemy, dot_to_enemy, &player_pos, &predicted_enemy_pos, &En_objp->phys_info.vel);
1392 aip->time_enemy_in_range *= (1.0f - flFrametime);
1395 void ai_big_ship(object *objp)
1400 // all three parms are output parameters
1401 // Enemy object is En_objp
1402 // pos => world pos of attack point
1403 // dist => distance from Pl_objp front to attack point
1404 // dot => dot of Pl_objp fvec and vector to attack point
1405 // fire_pos => world pos from which firing
1406 void ai_big_attack_get_data(vector *enemy_pos, float *dist_to_enemy, float *dot_to_enemy)
1408 vector player_pos, vec_to_enemy, predicted_enemy_pos;
1409 ship *shipp = &Ships[Pl_objp->instance];
1410 ai_info *aip = &Ai_info[shipp->ai_index];
1411 ship_info *esip = &Ship_info[Ships[En_objp->instance].ship_info_index];
1413 Assert(aip->mode == AIM_STRAFE);
1415 // ensure that Pl_objp is still targeting a big ship
1416 if ( !(esip->flags & (SIF_BIG_SHIP | SIF_HUGE_SHIP)) ) {
1417 ai_big_switch_to_chase_mode(aip);
1421 ai_set_positions(Pl_objp, En_objp, aip, &player_pos, enemy_pos);
1423 player_pos = Pl_objp->pos;
1425 if (aip->targeted_subsys != NULL) {
1426 Assert(aip->targeted_subsys != NULL);
1427 get_subsystem_pos(enemy_pos, En_objp, aip->targeted_subsys);
1429 // checks valid line to target
1430 ai_big_pick_attack_point(En_objp, Pl_objp, enemy_pos, 0.8f);
1433 // Take player pos to be center of ship + ship_radius
1434 vm_vec_scale_add2(&player_pos, &Pl_objp->orient.v.fvec, Pl_objp->radius);
1436 // If seeking lock, try to point directly at ship, else predict position so lasers can hit it.
1437 // If just acquired target, or target is not in reasonable cone, don't refine believed enemy position.
1438 vm_vec_normalized_dir(&vec_to_enemy, enemy_pos, &player_pos);
1439 *dot_to_enemy=vm_vec_dot(&vec_to_enemy, &Pl_objp->orient.v.fvec);
1440 if ((*dot_to_enemy < 0.25f) || (aip->target_time < 1.0f) || (aip->ai_flags & AIF_SEEK_LOCK)) {
1441 predicted_enemy_pos=*enemy_pos;
1443 vector gun_pos, pnt;
1444 polymodel *po = model_get( Ship_info[shipp->ship_info_index].modelnum );
1447 // Compute position of gun in absolute space and use that as fire position.
1448 pnt = po->gun_banks[0].pnt[0];
1449 vm_vec_unrotate(&gun_pos, &pnt, &Pl_objp->orient);
1450 vm_vec_add2(&gun_pos, &Pl_objp->pos);
1451 weapon_speed = ai_get_weapon_speed(&shipp->weapons);
1453 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);
1456 *dist_to_enemy = vm_vec_normalized_dir(&vec_to_enemy, &predicted_enemy_pos, &player_pos);
1457 *dot_to_enemy = vm_vec_dot(&vec_to_enemy, &Pl_objp->orient.v.fvec);
1458 update_aspect_lock_information(aip, &vec_to_enemy, *dist_to_enemy, En_objp->radius);
1460 *enemy_pos = predicted_enemy_pos;
1463 // check to see if Pl_objp has gotten too close to attacking point.. if so, break off by entering
1464 // AIS_STRAFE_RETREAT
1465 int ai_big_strafe_maybe_retreat(float dist, vector *target_pos)
1468 aip = &Ai_info[Ships[Pl_objp->instance].ai_index];
1470 vector vec_to_target;
1471 vm_vec_sub(&vec_to_target, target_pos, &Pl_objp->pos);
1473 float dist_to_target, dist_normal_to_target, time_to_target;
1474 dist_to_target = vm_vec_mag_quick(&vec_to_target);
1475 if (vm_vec_mag_quick(&aip->big_attack_surface_normal) > 0.9) {
1476 dist_normal_to_target = -vm_vec_dotprod(&vec_to_target, &aip->big_attack_surface_normal);
1478 dist_normal_to_target = 0.2f * vm_vec_mag_quick(&vec_to_target);
1481 dist_normal_to_target = max(0.2f*dist_to_target, dist_normal_to_target);
1482 time_to_target = dist_normal_to_target / Pl_objp->phys_info.speed;
1484 // add distance penalty for going too fast
1485 float speed_to_dist_penalty = max(0.0f, (Pl_objp->phys_info.speed-50));
1487 //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))
1489 // Inside 2 sec retreat, setting goal point to box point + 300m
1490 // If collision, use std collision resolution.
1491 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)) ) {
1492 if (aip->ai_flags & AIF_TARGET_COLLISION) {
1493 // use standard collision resolution
1494 aip->ai_flags &= ~AIF_TARGET_COLLISION;
1495 big_ship_collide_recover_start(Pl_objp, En_objp, &Pl_objp->pos, NULL);
1497 // too close for comfort so fly to box point + 300
1498 aip->submode = AIS_STRAFE_RETREAT1;
1499 aip->submode_start_time = Missiontime;
1504 box_dist = get_world_closest_box_point_with_delta(&goal_point, En_objp, &Pl_objp->pos, &is_inside, STRAFE_RETREAT_BOX_DIST);
1507 aip->goal_point = goal_point;
1509 if (ai_maybe_fire_afterburner(Pl_objp, aip)) {
1510 afterburners_start(Pl_objp);
1511 aip->afterburner_stop_time = Missiontime + 3*F1_0;
1521 // attack directly to the turret and fire weapons
1522 void ai_big_strafe_attack()
1527 float target_dist, target_dot, accel, t;
1528 object *target_objp;
1530 aip = &Ai_info[Ships[Pl_objp->instance].ai_index];
1532 if ( ai_big_maybe_follow_subsys_path(0) ) {
1536 ai_big_attack_get_data(&target_pos, &target_dist, &target_dot);
1537 if ( ai_big_strafe_maybe_retreat(target_dist, &target_pos) )
1540 target_objp = &Objects[aip->target_objnum];
1542 if (aip->ai_flags & AIF_KAMIKAZE) {
1543 if (target_dist < 1200.0f) {
1544 //nprintf(("AI", "Kamikaze: %7.3f %7.3f\n", target_dot, target_dist));
1545 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);
1546 accelerate_ship(aip, 1.0f);
1547 if ((target_dist < 400.0f) && ai_maybe_fire_afterburner(Pl_objp, aip)) {
1548 afterburners_start(Pl_objp);
1549 aip->afterburner_stop_time = Missiontime + 3*F1_0;
1555 if (!(aip->ai_flags & AIF_SEEK_LOCK) || (aip->aspect_locked_time < 2.0f)) {
1556 t = ai_endangered_by_weapon(aip);
1557 if ( t > 0.0f && t < 1.5f ) {
1558 // set up goal_point for avoid path to turn towards
1559 aip->goal_point = Pl_objp->pos;
1562 vm_vec_scale_add(&rand_vec, &Pl_objp->orient.v.fvec, &Pl_objp->orient.v.uvec, 2.0f);
1565 vm_vec_scale_add(&rand_vec, &Pl_objp->orient.v.fvec, &Pl_objp->orient.v.uvec, -2.0f);
1568 vm_vec_scale_add(&rand_vec, &Pl_objp->orient.v.fvec, &Pl_objp->orient.v.rvec, 2.0f);
1571 vm_vec_scale_add(&rand_vec, &Pl_objp->orient.v.fvec, &Pl_objp->orient.v.rvec, -2.0f);
1575 vm_vec_scale(&rand_vec, 1000.0f);
1576 vm_vec_add2(&aip->goal_point, &rand_vec);
1578 aip->submode = AIS_STRAFE_AVOID;
1579 aip->submode_start_time = Missiontime;
1580 // nprintf(("Alan","Ship %s entering AIS_STRAFE_AVOID at frame %d\n", Ships[aip->shipnum].ship_name, Framecount));
1582 if (ai_maybe_fire_afterburner(Pl_objp, aip)) {
1583 afterburners_start(Pl_objp);
1584 aip->afterburner_stop_time = Missiontime + fl2f(0.5f);
1589 // maybe fire weapons at target
1590 ai_big_maybe_fire_weapons(target_dist, target_dot, &Pl_objp->pos, &En_objp->pos, &En_objp->phys_info.vel);
1591 turn_towards_point(Pl_objp, &target_pos, NULL, 0.0f);
1593 // Slow down if we've not been hit for a while
1594 fix last_hit = Missiontime - aip->last_hit_time;
1595 if ( target_dist > 1200 || last_hit < F1_0*6) {
1599 attack_time = f2fl(Missiontime - aip->submode_start_time);
1600 if ( attack_time > 15 ) {
1602 } else if ( attack_time > 10 ) {
1604 } else if ( attack_time > 8 ) {
1606 } else if ( attack_time > 5 ) {
1614 accelerate_ship(aip, accel);
1616 // if haven't been hit in quite a while, leave strafe mode
1618 long_enough = F1_0*20;
1619 if ( (last_hit > long_enough) && ( (Missiontime - aip->submode_parm0) > long_enough) ) {
1620 ai_big_switch_to_chase_mode(aip);
1624 // pick a new attack point when entering this state, and keep using it
1625 void ai_big_strafe_avoid()
1629 float target_dist, target_dot;
1632 aip = &Ai_info[Ships[Pl_objp->instance].ai_index];
1634 mode_time = Missiontime - aip->submode_start_time;
1636 ai_big_attack_get_data(&target_pos, &target_dist, &target_dot);
1637 if ( ai_big_strafe_maybe_retreat(target_dist, &target_pos) )
1640 if ( mode_time > fl2f(0.5)) {
1641 aip->submode = AIS_STRAFE_ATTACK;
1642 aip->submode_start_time = Missiontime;
1643 // nprintf(("Alan","Ship %s entering AIS_STRAFE_ATTACK at frame %d\n", Ships[aip->shipnum].ship_name, Framecount));
1646 turn_towards_point(Pl_objp, &aip->goal_point, NULL, 0.0f);
1647 accelerate_ship(aip, 1.0f);
1650 // move towards aip->goal_point in an evasive manner
1651 void ai_big_strafe_retreat1()
1657 aip = &Ai_info[Ships[Pl_objp->instance].ai_index];
1659 dist = vm_vec_dist_quick(&Pl_objp->pos, &aip->goal_point);
1661 aip->submode = AIS_STRAFE_POSITION;
1662 aip->submode_start_time = Missiontime;
1663 // nprintf(("Alan","Ship %s entering AIS_STRAFE_POSITION\n", Ships[aip->shipnum].ship_name));
1667 if (Missiontime - aip->submode_start_time > fl2f(1.50f)) {
1668 // set up goal_point for avoid path to turn towards
1669 aip->prev_goal_point = Pl_objp->pos;
1672 vm_vec_add(&rand_vec, &Pl_objp->orient.v.fvec, &Pl_objp->orient.v.uvec);
1675 vm_vec_scale_add(&rand_vec, &Pl_objp->orient.v.fvec, &Pl_objp->orient.v.uvec, -1.0f);
1678 vm_vec_add(&rand_vec, &Pl_objp->orient.v.fvec, &Pl_objp->orient.v.rvec);
1681 vm_vec_scale_add(&rand_vec, &Pl_objp->orient.v.fvec, &Pl_objp->orient.v.rvec, -1.0f);
1685 //vm_vec_scale(&rand_vec, 200.0f);
1686 //vm_vec_add2(&aip->prev_goal_point, &rand_vec);
1687 vm_vec_scale_add(&aip->prev_goal_point, &aip->goal_point, &rand_vec, 200.0f);
1689 aip->submode = AIS_STRAFE_RETREAT2;
1690 aip->submode_start_time = Missiontime;
1693 turn_towards_point(Pl_objp, &aip->goal_point, NULL, 0.0f);
1694 accelerate_ship(aip, 1.0f);
1697 void ai_big_strafe_retreat2()
1702 aip = &Ai_info[Ships[Pl_objp->instance].ai_index];
1704 if (Pl_objp->phys_info.flags & PF_AFTERBURNER_ON ) {
1705 if (Missiontime > aip->afterburner_stop_time) {
1706 //nprintf(("AI", "Frame %i, turning off afterburner.\n", AI_FrameCount));
1707 afterburners_stop(Pl_objp);
1711 dist = vm_vec_dist_quick(&Pl_objp->pos, &aip->goal_point);
1713 aip->submode = AIS_STRAFE_POSITION;
1714 aip->submode_start_time = Missiontime;
1715 // nprintf(("Alan","Ship %s entering AIS_STRAFE_POSITION\n", Ships[aip->shipnum].ship_name));
1719 if (Missiontime - aip->submode_start_time > fl2f(0.70f)) {
1720 aip->submode = AIS_STRAFE_RETREAT1;
1721 aip->submode_start_time = Missiontime;
1723 if ( (Missiontime - aip->last_hit_time) < F1_0*5 ) {
1724 if (ai_maybe_fire_afterburner(Pl_objp, aip)) {
1725 afterburners_start(Pl_objp);
1726 aip->afterburner_stop_time = Missiontime + F1_0;
1731 turn_towards_point(Pl_objp, &aip->prev_goal_point, NULL, 0.0f);
1732 accelerate_ship(aip, 1.0f);
1735 // reposition self to begin another strafing run
1736 void ai_big_strafe_position()
1739 aip = &Ai_info[Ships[Pl_objp->instance].ai_index];
1740 // TODO: loop around or something!!
1742 aip->submode = AIS_STRAFE_ATTACK;
1743 aip->submode_start_time = Missiontime;
1744 // nprintf(("Alan","Ship %s entering AIS_STRAFE_ATTACK\n", Ships[aip->shipnum].ship_name));
1747 // --------------------------------------------------------------------------
1748 // #define AIS_STRAFE_ATTACK 201 // fly towards target and attack
1749 // #define AIS_STRAFE_AVOID 202 // fly evasive vector to avoid incoming fire
1750 // #define AIS_STRAFE_RETREAT1 203 // fly away from attack point (directly)
1751 // #define AIS_STRAFE_RETREAT1 204 // fly away from attack point (on an avoid vector)
1752 // #define AIS_STRAFE_POSITION 205 // re-position to resume strafing attack
1754 void ai_big_strafe()
1758 aip = &Ai_info[Ships[Pl_objp->instance].ai_index];
1760 Assert(aip->mode == AIM_STRAFE);
1763 if ( aip->goal_objnum != aip->target_objnum ) {
1764 Int3(); // what is going on here? - Get Alan
1765 aip->mode = AIM_NONE;
1770 // check if target is still a big ship... if not enter chase mode
1771 if ( !(Ship_info[Ships[En_objp->instance].ship_info_index].flags & (SIF_BIG_SHIP|SIF_HUGE_SHIP)) ) {
1772 ai_big_switch_to_chase_mode(aip);
1776 switch (aip->submode) {
1777 case AIS_STRAFE_ATTACK:
1778 ai_big_strafe_attack();
1780 case AIS_STRAFE_AVOID:
1781 ai_big_strafe_avoid();
1783 case AIS_STRAFE_RETREAT1:
1784 ai_big_strafe_retreat1();
1786 case AIS_STRAFE_RETREAT2:
1787 ai_big_strafe_retreat2();
1789 case AIS_STRAFE_POSITION:
1790 ai_big_strafe_position();
1794 Int3(); // Illegal submode for AIM_STRAFE
1799 // See if Pl_objp should enter strafe mode (This is called from maybe_evade_dumbfire_weapon(), and the
1800 // weapon_objnum is for a weapon that is about to collide with Pl_objp
1802 // Check if weapon_objnum was fired by Pl_objp's target, and whether Pl_objp's target is a big ship, if
1803 // so, enter AIM_STRAFE
1804 int ai_big_maybe_enter_strafe_mode(object *pl_objp, int weapon_objnum, int consider_target_only)
1808 object *weapon_objp, *parent_objp;
1810 aip = &Ai_info[Ships[pl_objp->instance].ai_index];
1811 Assert(aip->mode != AIM_STRAFE); // can't happen
1813 // if Pl_objp has no target, then we can't enter strafe mode
1814 if ( aip->target_objnum < 0 ) {
1818 // if target is not a ship, stafe mode is not possible
1819 if ( Objects[aip->target_objnum].type != OBJ_SHIP ) {
1823 sip = &Ship_info[Ships[Objects[aip->target_objnum].instance].ship_info_index];
1825 // if Pl_objp's target is not a big/capital ship, then cannot enter strafe mode
1826 // AL 12-31-97: Even though transports are considered big ships, don't enter strafe mode on them
1827 if ( !(sip->flags & (SIF_BIG_SHIP | SIF_HUGE_SHIP)) || (sip->flags & SIF_TRANSPORT) ) {
1831 // If Pl_objp not a fighter or bomber, don't enter strafe mode. -- MK, 11/11/97.
1832 if ( !(Ship_info[Ships[pl_objp->instance].ship_info_index].flags & (SIF_FIGHTER | SIF_BOMBER)) ) {
1836 Assert(weapon_objnum >= 0 && weapon_objnum < MAX_OBJECTS);
1837 weapon_objp = &Objects[weapon_objnum];
1838 Assert(weapon_objp->type == OBJ_WEAPON);
1840 Assert(weapon_objp->parent >= 0 && weapon_objp->parent < MAX_OBJECTS);
1841 parent_objp = &Objects[weapon_objp->parent];
1842 if ( (parent_objp->signature != weapon_objp->parent_sig) || (parent_objp->type != OBJ_SHIP) ) {
1846 // Maybe the ship which fired the weapon isn't the current target
1847 if ( OBJ_INDEX(parent_objp) != aip->target_objnum ) {
1849 //JAS IMPOSSIBLE if (1) { // consider_target_only ) {
1850 //JAS IMPOSSIBLE return 0;
1851 //JAS IMPOSSIBLE } else {
1853 sip = &Ship_info[Ships[parent_objp->instance].ship_info_index];
1854 if ( !(sip->flags & (SIF_BIG_SHIP | SIF_HUGE_SHIP)) || (sip->flags & SIF_TRANSPORT) ) {
1857 set_target_objnum(aip, OBJ_INDEX(parent_objp));
1861 ai_big_strafe_maybe_attack_turret(pl_objp, weapon_objp);
1863 // if we've got this far, the weapon must have come from the player's target, and it is a
1864 // big/capital ship... so enter strafe mode
1865 aip->previous_mode = aip->mode;
1866 aip->mode = AIM_STRAFE;
1867 aip->submode_parm0 = Missiontime; // use parm0 as time strafe mode entered
1868 aip->submode = AIS_STRAFE_AVOID;
1869 aip->submode_start_time = Missiontime;
1870 // nprintf(("Alan","%s Accepted strafe mode\n", Ships[pl_objp->instance].ship_name));
1875 // Consider attacking a turret, if a turret actually fired the weapon
1876 // input: ship_objp => ship that will attack the turret
1878 void ai_big_strafe_maybe_attack_turret(object *ship_objp, object *weapon_objp)
1881 object *parent_objp;
1883 Assert(ship_objp->type == OBJ_SHIP);
1884 aip = &Ai_info[Ships[ship_objp->instance].ai_index];
1886 // Make decision to attack turret based on AI class. The better AI ships will realize that
1887 // it is better to take out the turrets first on a big ship.
1888 if ( (frand()*100) > (aip->ai_courage-15) )
1891 // If ship is already attacking a subsystem, don't switch
1892 if ( aip->targeted_subsys != NULL ) {
1896 // If the weapon is not from a turret, return
1897 if ( Weapons[weapon_objp->instance].turret_subsys == NULL ) {
1901 // Only attack turret if it sits on current target
1902 Assert(weapon_objp->parent >= 0 && weapon_objp->parent < MAX_OBJECTS);
1903 parent_objp = &Objects[weapon_objp->parent];
1904 if ( (parent_objp->signature != weapon_objp->parent_sig) || (parent_objp->type != OBJ_SHIP) ) {
1908 if ( aip->target_objnum != OBJ_INDEX(parent_objp) ) {
1912 // attack the turret
1913 set_targeted_subsys(aip, Weapons[weapon_objp->instance].turret_subsys, OBJ_INDEX(parent_objp));