]> icculus.org git repositories - taylor/freespace2.git/blob - src/ship/aibig.cpp
ryan's struct patch for gcc 2.95
[taylor/freespace2.git] / src / ship / aibig.cpp
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
6  * the source.
7  */
8
9 /*
10  * $Logfile: /Freespace2/code/Ship/AiBig.cpp $
11  * $Revision$
12  * $Date$
13  * $Author$
14  *
15  * C module for AI code related to large ships
16  *
17  * $Log$
18  * Revision 1.5  2002/06/17 06:33:10  relnev
19  * ryan's struct patch for gcc 2.95
20  *
21  * Revision 1.4  2002/06/09 04:41:26  relnev
22  * added copyright header
23  *
24  * Revision 1.3  2002/06/01 07:12:33  relnev
25  * a few NDEBUG updates.
26  *
27  * removed a few warnings.
28  *
29  * Revision 1.2  2002/05/07 03:16:51  theoddone33
30  * The Great Newline Fix
31  *
32  * Revision 1.1.1.1  2002/05/03 03:28:10  root
33  * Initial import.
34  *
35  * 
36  * 13    8/31/99 4:24p Andsager
37  * Reduce collisions when attacking big ships.
38  * 
39  * 12    7/19/99 2:18p Mikeb
40  * DA:  DOH!!
41  * 
42  * 11    7/19/99 2:11p Mikeb
43  * DA: sometimes big_ship_attack_normal is not known
44  * 
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.
48  * 
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
52  * 
53  * 8     5/13/99 2:31p Jamesa
54  * DA:  Temp fix when attacking subsys of big ship and BS is blown up.
55  * 
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.
59  * 
60  * 6     4/23/99 12:01p Johnson
61  * Added SIF_HUGE_SHIP
62  * 
63  * 5     4/20/99 3:40p Andsager
64  * Changes to big ship ai.  Uses bounding box as limit where to fly to
65  * when flying away.
66  * 
67  * 4     4/16/99 5:54p Dave
68  * Support for on/off style "stream" weapons. Real early support for
69  * target-painting lasers.
70  * 
71  * 3     10/13/98 9:29a Dave
72  * Started neatening up freespace.h. Many variables renamed and
73  * reorganized. Added AlphaColors.[h,cpp]
74  * 
75  * 2     10/07/98 10:53a Dave
76  * Initial checkin.
77  * 
78  * 1     10/07/98 10:51a Dave
79  * 
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.
84  * 
85  * 76    5/23/98 2:38a Mike
86  * Improve balance of cheat-firing Synaptics.
87  * 
88  * 75    5/23/98 12:40a Mike
89  * Hook for Delta wing to fire Synaptic bombs in sm3-09a.
90  * 
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
94  * 
95  * 73    5/21/98 1:25p Lawrance
96  * Fix exception in ai_bpap() when no verts are in closest octant
97  * 
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
101  * aspect lock.
102  * 
103  * 71    5/12/98 10:06a Mike
104  * Fix a jillion bugs preventing ships from firing bombs at Lucifer in
105  * sm3-09.
106  * 
107  * 70    5/10/98 11:30p Mike
108  * Better firing of bombs, less likely to go into strafe mode.
109  * 
110  * 69    5/10/98 3:41a Lawrance
111  * Fix bug that was preventing ships from leaving strafe mode
112  * 
113  * 68    5/08/98 4:39p Mike
114  * Make ships match bank when attacking large ships.
115  * 
116  * 67    5/07/98 11:59p Mike
117  * Remove unneeded code, computation of num_verts.
118  * 
119  * 66    4/29/98 5:01p Mike
120  * Large overhaul in how turrets fire.
121  * 
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.
125  * 
126  * 64    4/23/98 12:32a Mike
127  * Fix bogus math which looked at (1-fov) instead of fov.
128  * 
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
132  * 
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
136  * 
137  * 61    4/12/98 2:02p Mike
138  * Make small ships avoid big ships.
139  * Turn on Collide_friendly flag.
140  * 
141  * 60    4/01/98 9:21p John
142  * Made NDEBUG, optimized build with no warnings or errors.
143  * 
144  * 59    3/25/98 12:00a Allender
145  * MK: make sure that output from function gets a valid valud (ai_pbap I
146  * think)
147  * 
148  * 58    3/24/98 10:07p Adam
149  * AL: Add another Assert() to try and catch bogus wp->big_attack_point
150  * 
151  * 57    3/24/98 9:56p Adam
152  * AL: Assert that local_attack_point is valid in ai_bpap
153  * 
154  * 56    3/21/98 3:36p Mike
155  * Fix/optimize attacking of big ships.
156  * 
157  * 55    3/16/98 5:17p Mike
158  * Make ships not get stuck on big ships when attacking.
159  * 
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.
163  * 
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.
167  * 
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.
172  * 
173  * 51    3/03/98 11:51p Lawrance
174  * Ensure target is a ship before looking at SIF flags
175  * 
176  * 50    3/02/98 11:35a Mike
177  * Fix logic with ai_do_default_behavior -- return after call.
178  * 
179  * 49    2/19/98 7:37p Lawrance
180  * Fix bug that was causing problems with subsystem path following.
181  * 
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.
185  * 
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.
189  * 
190  * 46    2/11/98 4:30p Lawrance
191  * Improve disarm/disable behavior.
192  * 
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.
196  * 
197  * 44    2/05/98 12:51a Mike
198  * Early asteroid stuff.
199  * 
200  * 43    1/30/98 11:28a Comet
201  * Fix indexing problem into verts in ai_bpap()
202  * 
203  * 42    1/29/98 10:46p Mike
204  * In ai_big_pick_attack_point, handle case of octant with no points.
205  * 
206  * 41    1/29/98 1:39p Mike
207  * Better heat seeking homing on big ships.
208  * 
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.
212  * 
213  * 39    1/22/98 5:14p Lawrance
214  * clean up ai_big code, clear path info when stop attacking a subsystem
215  * 
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.
219  * 
220  * 37    1/20/98 9:47a Mike
221  * Suppress optimized compiler warnings.
222  * Some secondary weapon work.
223  * 
224  * 36    1/17/98 4:45p Mike
225  * Better support for AI selection of secondary weapons.
226  * 
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.
230  * 
231  * 34    1/16/98 11:32a Mike
232  * Fix bug when a ship is attacking a dead subsystem on a protected ship.
233  * 
234  * 33    1/07/98 1:32p Lawrance
235  * Remove some nprintf output
236  * 
237  * 32    1/07/98 1:21p Sandeep
238  * Fix bug where accessing uninited pointer
239  * 
240  * 31    1/06/98 6:58p Lawrance
241  * Attack turrets (sometimes) when fired upon while attacking a ship.
242  * 
243  * 30    12/31/97 6:28p Lawrance
244  * Improve attacking subsystems on moving ships.
245  * 
246  * 29    12/31/97 4:16p Lawrance
247  * Use different fov's for attacking subsystems depending if ship is
248  * moving
249  * 
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.
253  * 
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.
257  * 
258  * 26    12/30/97 5:20p Mike
259  * Don't fire secondaries at a protected ship if its hull is very weak.
260  * 
261  * 25    12/30/97 4:26p Lawrance
262  * Take out some debug statements
263  * 
264  * 24    12/15/97 7:16p Lawrance
265  * improving subsystem attacking
266  * 
267  * 23    12/02/97 11:58a Lawrance
268  * avoid during strafe mode only when endangered by a weapon
269  * 
270  * 22    12/01/97 5:11p Lawrance
271  * make strafe mode more effective... slow down when approaching and use
272  * afterburner in avoids
273  * 
274  * 21    11/27/97 4:21p Lawrance
275  * set submode to -1 when switching to AIM_NONE from AIM_STRAFE
276  * 
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
280  * 
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
284  * 
285  * 18    11/13/97 11:51a Johnson
286  * ALAN: comment out Int3() in AiBig... need to test on my own machine
287  * 
288  * 17    11/12/97 11:51p Lawrance
289  * only check target_objnum == goal_objnum when following path
290  * 
291  * 16    11/12/97 11:01p Lawrance
292  * change call to ai_find_path()
293  * ensure goal_objnum == target_objnum in STRAFE
294  * 
295  * 15    11/12/97 11:18a Lawrance
296  * fix bug in path following of subsystems
297  * 
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.
303  * 
304  * 13    11/06/97 6:27p Lawrance
305  * fix bug that was messing up retreat point in STRAFE mode
306  * 
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.
310  * 
311  * 11    11/06/97 12:27a Mike
312  * Better avoid behavior.
313  * Modify ai_turn_towards_vector() to take a flag parameter.
314  * 
315  * 10    11/02/97 10:55p Lawrance
316  * removed some unused code, added some comments
317  * 
318  * 9     11/01/97 4:01p Mike
319  * Adapt to new ai_find_path().
320  * 
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
324  * 
325  * 7     10/30/97 10:06p Lawrance
326  * chg some timing values so ship will reach retreat point
327  * 
328  * 6     10/30/97 9:17p Lawrance
329  * work on getting AIM_STRAFE working well with disable/disarm, try to
330  * balance
331  * 
332  * 5     10/30/97 12:32a Lawrance
333  * further work on AIM_STRAFE
334  * 
335  * 4     10/29/97 6:25p Lawrance
336  * add AIM_STRAFE functionality
337  * 
338  * 3     10/26/97 4:19p Lawrance
339  * added ai_get_weapon_dist()
340  * 
341  * 2     10/26/97 3:24p Lawrance
342  * split off large ship ai code into AiBig.cpp
343  *
344  * $NoKeywords: $
345  */
346
347 #include "pstypes.h"
348 #include "fix.h"
349 #include "linklist.h"
350 #include "object.h"
351 #include "physics.h"
352 #include "vecmat.h"
353 #include "ship.h"
354 #include "model.h"
355 #include "2d.h"
356 #include "3d.h"
357 #include "ai.h"
358 #include "aibig.h"
359 #include "floating.h"
360 #include "player.h"
361 #include "freespace.h"
362 #include "weapon.h"
363 #include "missiongoals.h"
364 #include "missionlog.h"
365 #include "timer.h"
366 #include "sound.h"
367 #include "aigoals.h"
368 #include "gamesnd.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"
375 #include "hudets.h"
376 #include "shipfx.h"
377 #include "shiphit.h"
378 #include "missionparse.h"
379
380 #ifndef PLAT_UNIX
381 #pragma optimize("", off)
382 #pragma auto_inline(off)
383 #endif
384
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
388
389 #define ENTER_STRAFE_THREAT_DIST_SQUARED        360000  // use squared distance, instead of 600
390
391 #define MIN_DOT_TO_ATTACK_SUBSYS                                0.7f
392 #define MIN_DOT_TO_ATTACK_MOVING_SUBSYS 0.97f
393
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
398
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
401
402 #define ATTACK_STOP_DISTANCE                            150             // when distance to target is less than this, put on brakes
403
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
409
410
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);
416
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);
420
421
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)
428 {
429         float           nearest_dist;
430         vector  result_point, best_point;
431         vector  rel_point;
432         int             num_tries;
433         model_octant    *octp;
434         polymodel       *pm;
435         int             i, q, octs[4];  
436
437         best_point = objp->pos;
438         nearest_dist = weapon_travel_dist;
439
440         model_which_octant_distant_many(attacker_objp_pos, Ships[objp->instance].modelnum, &objp->orient, &objp->pos, &pm, octs);
441
442         num_tries = (int) (vm_vec_dist(&objp->pos, attacker_objp_pos)/objp->radius);
443
444         if (num_tries >= 4)
445                 num_tries = 1;
446         else
447                 num_tries = 4 - num_tries;
448
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.
452         } else {
453                 vm_vec_zero(local_attack_point);
454         }
455
456         for (q=0; q<4; q++) {
457                 octp = &pm->octants[octs[q]];
458                 if (octp->nverts > 0) {
459
460                         if (num_tries > octp->nverts)
461                                 num_tries = octp->nverts;
462
463                         if (num_tries > octp->nverts)
464                                 num_tries = octp->nverts;
465
466                         int     best_index = -1;
467
468                         for (i=0; i<num_tries; i++) {
469                                 int     index;
470                                 float   dist, dot;
471                                 vector  v2p;
472
473                                 index = (int) (frand() * (octp->nverts));
474
475                                 rel_point = *octp->verts[index];
476                                 vm_vec_unrotate(&result_point, &rel_point, &objp->orient);
477                                 vm_vec_add2(&result_point, &objp->pos);
478
479                                 dist = vm_vec_normalized_dir(&v2p, &result_point, attacker_objp_pos);
480                                 dot = vm_vec_dot(&v2p, attacker_objp_fvec);
481
482                                 if (dot > fov) {
483                                         if (dist < nearest_dist) {
484                                                 best_index = index;
485                                                 nearest_dist = 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.
490                                                         goto done_1;
491                                         }
492                                 }
493                         }
494                 }
495         }
496 done_1:
497
498         *attack_point = best_point;
499
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]
502
503         // do in world coords to get attack point, then translate to local for local_attack_point
504         vector attack_dir, end_point, temp;
505         float dist;
506         dist = vm_vec_normalized_dir(&attack_dir, attack_point, attacker_objp_pos);
507
508         if (dist > 0.1) {
509                 vm_vec_scale_add(&end_point, attack_point, &attack_dir, 30.0f);
510         } else {
511                 vm_vec_scale_add(&end_point, attack_point, attacker_objp_fvec, 30.0f);
512         }
513         
514         mc_info mc;
515         mc.model_num = Ships[objp->instance].modelnum;
516         mc.orient = &objp->orient;
517         mc.pos = &objp->pos;
518         mc.p0 = attacker_objp_pos;
519         mc.p1 = &end_point;
520         mc.flags = MC_CHECK_MODEL;
521         mc.radius = 0.0f;
522         model_collide(&mc);
523
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);
530                 }
531         } else {
532                 vm_vec_zero(local_attack_point);
533                 *attack_point = objp->pos;
534                 if (surface_normal) {
535                         vm_vec_zero(surface_normal);
536                 }
537         }
538 }
539
540 //      Stuff a point to attack based on nearest octant.
541 //      If no points in that octant, leave attack_point unmodified.
542 //
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)
546 {
547         if (!timestamp_elapsed(ssp->turret_pick_big_attack_point_timestamp)) {
548                 vector  result_point;
549                 vm_vec_unrotate(&result_point, &ssp->turret_big_attack_point, &objp->orient);
550                 vm_vec_add(attack_point, &result_point, &objp->pos);
551         } else {
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;
556         }
557 }
558
559 //      Stuff a point to attack based on nearest octant.
560 //      If no points in that octant, leave attack_point unmodified.
561 //
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)
566 {
567         Assert(objp->instance > -1);
568         Assert(objp->type == OBJ_SHIP);
569
570         vector  local_attack_point;
571
572         switch (attacker_objp->type) {
573         case OBJ_SHIP: {
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)) {
577                         vector  result_point;
578
579                         vm_vec_unrotate(&result_point, &attacker_aip->big_attack_point, &objp->orient);
580                         vm_vec_add(attack_point, &result_point, &objp->pos);
581
582                         return;
583                 }
584         
585                 attacker_aip->pick_big_attack_point_timestamp = timestamp(2000 + (int) (frand()*500.0f));
586                 break;
587                                                 }
588         case OBJ_WEAPON: {
589                 weapon  *wp = &Weapons[attacker_objp->instance];
590
591                 if (!timestamp_elapsed(wp->pick_big_attack_point_timestamp)) {
592                         vector  result_point;
593
594                         vm_vec_unrotate(&result_point, &wp->big_attack_point, &objp->orient);
595                         vm_vec_add(attack_point, &result_point, &objp->pos);
596
597                         return;
598                 }
599                 wp->pick_big_attack_point_timestamp = timestamp(2000 + (int) (frand()*500.0f));
600         
601                 break;
602                                                   }
603         }
604
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);
608
609         switch (attacker_objp->type) {
610         case OBJ_SHIP: {
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) {
614                         return;
615                 }
616
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;
620                 break;
621                                                 }
622         case OBJ_WEAPON: {
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) );
626                 break;
627                                                   }
628         }
629
630         return;
631 }
632
633 // Handler for SM_EVADE submode ( called from ai_big_chase() )
634 void ai_big_evade_ship()
635 {
636         vector  player_pos, enemy_pos;
637         float           dist;
638         ship            *shipp = &Ships[Pl_objp->instance];
639         ai_info *aip = &Ai_info[shipp->ai_index];
640         vector  randvec, semi_enemy_pos;
641
642         ai_set_positions(Pl_objp, En_objp, aip, &player_pos, &enemy_pos);
643
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;
649         } else {
650                 semi_enemy_pos = aip->prev_goal_point;
651         }
652         
653         accelerate_ship(aip, 1.0f - ((Missiontime>>8) & 0x3f)/128.0f );
654         turn_away_from_point(Pl_objp, &semi_enemy_pos, 0.0f);
655
656         float box_dist;
657         vector box_vec;
658         int is_inside;
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;
666         }
667
668         //TODO TEST
669
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;
677                 }
678         } */
679 }
680
681 // Handler for SM_AVOID submode ( called from ai_big_chase() )
682 void ai_big_avoid_ship()
683 {
684         ai_big_evade_ship();
685 }
686
687 // reset path following information
688 void ai_big_subsys_path_cleanup(ai_info *aip)
689 {
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;
694                 aip->path_cur = -1;
695                 aip->path_length = 0;
696         }
697 }
698
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)
704 {
705         ai_info *aip;
706         float           dot = 1.0f, min_dot;
707         object  *target_objp;
708
709         aip = &Ai_info[Ships[Pl_objp->instance].ai_index];
710         target_objp = &Objects[aip->target_objnum];
711
712         if ( (aip->targeted_subsys != NULL) && (aip->target_objnum >= 0) && (aip->targeted_subsys->system_info->path_num >= 0) ) {
713                 polymodel       *pm;
714                 int                     subsys_path_num, subsys_in_sight, checked_sight;
715                 float                   dist;
716
717                 pm = model_get( Ships[Pl_objp->instance].modelnum );
718         
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
722
723                 // only check for subsystem sight every N milliseconds
724                 checked_sight = 0;
725                 if ( timestamp_elapsed(aip->path_subsystem_next_check) ) {
726                         vector          geye, gsubpos;
727                         eye                     *ep;
728
729                         aip->path_subsystem_next_check = timestamp(1500);
730
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 );
734                         
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);
738
739                         checked_sight = 1;
740
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);
748                                 }
749                         }
750
751                         if ( ship_subsystem_in_sight(En_objp, aip->targeted_subsys, &geye, &gsubpos, 1) ) {
752                                 subsys_in_sight = 1;
753                         }
754                 }
755
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)) ) )  {
759
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;
769                                         }
770                                 }
771                         }
772                 }
773
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);
777                 }
778
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 ) {
784                         int path_done=0;
785                         int in_view=0;
786
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);
789
790                         if ( aip->path_cur >= (aip->path_start+aip->path_length-1) ) {
791                                 path_done=1;
792                         }
793
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) ) {
796                                 in_view=1;
797                         }
798
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;
811                                                         aip->submode = -1;
812                                                         return 1;
813                                                 }
814                                                 ai_path();
815                                                 return 1;
816                                         }
817                                 }
818                         }
819                 }
820         }
821
822         return 0;
823 }
824
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;
828 //
829 // input: aip   =>      ai_info pointer for Pl_objp
830 //                       sip    =>      ship_info pointer for Pl_objp
831 //
832 // exit:                1       =>      ship should enter strafe mode
833 //                              0       => ship should not change ai mode, no fighter/bomber threats are near
834 //
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)
837 {
838         // if moving slowly (or stopped), and SIF_SMALL_SHIP, then enter STRAFE mode if enemy fighter/bombers
839         // are near
840         if ( sip->flags & SIF_SMALL_SHIP ) {
841                 if ( timestamp_elapsed(aip->scan_for_enemy_timestamp) ) {
842                         ship_obj        *so;
843                         object  *test_objp;
844                         ship            *test_sp;
845                         float           dist_squared;
846                         
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];
853
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 ) {
858                                                         return 1;
859                                                 }
860                                         }
861                                 }
862                                 so = GET_NEXT(so);
863                         }
864                 }
865         }
866         // If we've reached here, there are no enemy fighter/bombers near
867         return 0;
868 }
869
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)
872 {
873         int             start_bank;
874         float           dot_to_enemy, time_to_hit;
875         polymodel *po = model_get( sip->modelnum );
876
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;
882         } else {
883                 //      If moving slowly, maybe evade incoming fire.
884                 if (Pl_objp->phys_info.speed < 3.0f) {
885                         object *objp;
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) {
889                                                 vector  in_vec;
890                                                 float           dist;
891
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;
900                                                                 } else {
901                                                                         aip->submode = SM_EVADE;
902                                                                         aip->submode_start_time = Missiontime;
903                                                                         aip->prev_goal_point = En_objp->pos;
904                                                                 }
905                                                         }
906                                                 }
907                                         }
908                         }
909                         
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;
918                                 return;
919                         }
920
921                 } // end if ( Pl_objp->phys_info.speed < 3.0f ) 
922
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() ) {
925                         return;
926                 }
927
928                 vector  *rel_pos, vec_to_enemy;
929                 float           weapon_travel_dist;
930
931                 start_bank = Ships[aip->shipnum].weapons.current_primary_bank;
932
933                 if ((po->n_guns) && (start_bank != -1)) {
934                         rel_pos = &po->gun_banks[start_bank].pnt[0];
935                 } else
936                         rel_pos = NULL;
937
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);
940
941                 vector  rvec_vec, *rvec = &rvec_vec;
942
943                 if (dist_to_enemy > 500.0f)
944                         compute_desired_rvec(rvec, enemy_pos, &Pl_objp->pos);
945                 else
946                         rvec = NULL;
947
948                 ai_turn_towards_vector(enemy_pos, Pl_objp, flFrametime, sip->srotation_time, NULL, rel_pos, 0.0f, 0, rvec);
949
950                 // calc range of primary weapon
951                 weapon_travel_dist = ai_get_weapon_dist(&Ships[Pl_objp->instance].weapons);
952
953                 if ( aip->targeted_subsys != NULL ) {
954                         if (dist_to_enemy > (weapon_travel_dist-20))
955                                 accelerate_ship(aip, 1.0f);
956                         else {
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));
959                         }
960
961                 } else {
962                         float accel;
963                         if (dot_to_enemy < 0.0f) {
964                                 accel = 0.5f;
965                         } else if (dot_to_enemy < 0.75f) {
966                                 accel = 0.75f;
967                         } else {
968                                 if (dist_to_enemy > weapon_travel_dist/2.0f) {
969                                         accel = 1.0f;
970                                 } else {
971                                         accel = 1.0f - dot_to_enemy;
972                                 }
973                         }
974
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);
979                         } else {
980                                 accelerate_ship(aip, accel);
981                         }
982
983                 }
984         }
985 }
986
987 // Handler for submode SM_CONTINUOUS_TURN
988 void ai_big_chase_ct()
989 {
990         ai_chase_ct();
991 }
992
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);
997
998 extern void maybe_cheat_fire_synaptic(object *objp, ai_info *aip);
999
1000 // Determine if Pl_objp should fire weapons at current target, based on input parameters
1001 //
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
1004 //
1005 void ai_big_maybe_fire_weapons(float dist_to_enemy, float dot_to_enemy, vector *firing_pos, vector *enemy_pos, vector *enemy_vel)
1006 {
1007         ai_info         *aip;
1008         ship_weapon     *swp;
1009
1010         aip = &Ai_info[Ships[Pl_objp->instance].ai_index];
1011         swp = &Ships[Pl_objp->instance].weapons;
1012
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;
1015                 
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))) {
1019
1020                         ship *temp_shipp;
1021                         temp_shipp = &Ships[Pl_objp->instance];
1022                         ship_weapon *tswp = &temp_shipp->weapons;
1023
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]];
1027
1028                                 if (dist_to_enemy < wip->max_speed * wip->lifetime)
1029                                         ai_fire_primary_weapon(Pl_objp);
1030
1031                                 int     priority1, priority2;
1032
1033                                 priority1 = -1;
1034                                 priority2 = -1;
1035
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;
1041
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
1044
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]];
1048
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));
1054                                                         }
1055                                                 }
1056
1057                                                 if (timestamp_elapsed(swp->next_secondary_fire_stamp[current_bank])) {
1058
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;
1064                                                         }
1065
1066                                                         float t = 0.25f;        //      default delay in seconds until next fire.
1067
1068                                                         if (dist_to_enemy < firing_range*1.0f) {
1069
1070
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;
1076                                                                                 } else {
1077                                                                                         t = set_secondary_fire_delay(aip, temp_shipp, swip);
1078                                                                                 }
1079
1080                                                                                 swp->next_secondary_fire_stamp[current_bank] = timestamp((int) (t*1000.0f));
1081                                                                         }
1082                                                                 //}
1083                                                         }
1084                                                         swp->next_secondary_fire_stamp[current_bank] = timestamp((int) (t*1000.0f));
1085                                                 }
1086                                         }
1087                                 }
1088                         }
1089                 }
1090         } else {
1091                 aip->time_enemy_in_range *= (1.0f - flFrametime);
1092         }
1093 }
1094
1095 // switch ai ship into chase mode
1096 void ai_big_switch_to_chase_mode(ai_info *aip)
1097 {
1098         aip->previous_mode = aip->mode;
1099         aip->mode = AIM_CHASE;
1100         aip->submode = SM_ATTACK;
1101         aip->submode_start_time = Missiontime;
1102 }
1103
1104 extern int ai_big_strafe_maybe_retreat(float dist, vector *target_pos);
1105
1106 // Make object Pl_objp chase object En_objp, which is a big ship, not a small ship.
1107 void ai_big_chase()
1108 {
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;
1116
1117         Assert(aip->mode == AIM_CHASE);
1118
1119         maybe_cheat_fire_synaptic(Pl_objp, aip);
1120
1121         enemy_ship_type = Ship_info[Ships[En_objp->instance].ship_info_index].flags;
1122
1123         ai_set_positions(Pl_objp, En_objp, aip, &player_pos, &enemy_pos);
1124
1125         player_pos = Pl_objp->pos;
1126         ai_big_pick_attack_point(En_objp, Pl_objp, &enemy_pos, 0.8f);
1127
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);
1133         }       else
1134                 predicted_enemy_pos = En_objp->pos;
1135
1136         if (aip->targeted_subsys != NULL) {
1137                 get_subsystem_pos(&enemy_pos, En_objp, aip->targeted_subsys);
1138         }
1139
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);
1142
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
1148                         return;
1149                 }
1150         }
1151
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;
1158                 }
1159         }
1160
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);
1177                         return;
1178                 }
1179         } else {
1180                 update_aspect_lock_information(aip, &vec_to_enemy, dist_to_enemy - En_objp->radius, En_objp->radius);
1181         }
1182
1183         //      If recently acquired target and not looking at target, just turn a bit.
1184         switch (aip->submode) {
1185         case SM_ATTACK:
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))
1189                                 return;
1190                 }
1191
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;
1198                         }
1199                 break;
1200         }
1201
1202         //
1203         //      Set turn and acceleration based on submode.
1204         //
1205         switch (aip->submode) {
1206         case SM_CONTINUOUS_TURN:
1207                 ai_big_chase_ct();
1208                 break;
1209
1210         case SM_ATTACK:
1211         case SM_SUPER_ATTACK:
1212         case SM_ATTACK_FOREVER:
1213                 ai_big_chase_attack(aip, sip, &enemy_pos, dist_to_enemy);
1214                 break;
1215
1216         case SM_EVADE:
1217                 ai_big_evade_ship();
1218                 break;
1219
1220         case SM_AVOID:
1221                 ai_big_avoid_ship();
1222                 break;
1223
1224         case SM_EVADE_WEAPON:
1225                 evade_weapon();
1226                 break;
1227
1228         default:
1229                 aip->last_attack_time = Missiontime;
1230                 aip->submode = SM_ATTACK;
1231                 break;
1232         }
1233
1234         // maybe Pl_objp was forced into strafe mode, if so return now
1235         if ( aip->mode == AIM_STRAFE )
1236                 return;
1237
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;
1246                         }
1247                 }
1248
1249                 //      If a collision is expected, pull out!
1250                 float dist_normal_to_enemy;
1251
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)));
1254                 } else {
1255                         // don;t have normal so use a conservative value here
1256                         dist_normal_to_enemy = 0.3f * dist_to_enemy;
1257                 }
1258
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));
1262 //              }
1263                 
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);
1267
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));
1274                                 } else {
1275                                         // a big time
1276                                         time_to_enemy = 100.0f;
1277                                 }
1278                         } else {
1279                                 if (Pl_objp->phys_info.speed > 0.1) {
1280                                         time_to_enemy = dist_normal_to_enemy / Pl_objp->phys_info.speed;
1281                                 } else {
1282                                         // a big time
1283                                         time_to_enemy = 100.0f;
1284                                 }
1285                         }
1286
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) ) {
1295                                 // slow down
1296                                 accelerate_ship(aip, -1.0f);
1297                         }
1298                 }
1299
1300                 /*
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);
1305                                 } else {
1306                                         aip->submode = SM_AVOID;
1307                                         aip->submode_start_time = Missiontime;
1308                                 }
1309                         }
1310                 } */
1311         }
1312
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;
1319                 }
1320                 break;
1321
1322         case SM_ATTACK:
1323         case SM_ATTACK_FOREVER:
1324                 break;
1325                 
1326         case SM_EVADE:
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;
1331                 }
1332                 break;
1333
1334         case SM_SUPER_ATTACK:
1335                 break;
1336
1337         case SM_AVOID:
1338                 // if outside box by > 300 m
1339                 // DA 4/20/98  can never get here attacking a big ship
1340                 {
1341                 int is_inside;
1342                 float box_dist = get_world_closest_box_point_with_delta(NULL, En_objp, &Pl_objp->pos, &is_inside, 0.0f);
1343                 
1344                 if (box_dist > 300) {
1345                         aip->submode = SM_ATTACK;
1346                         aip->last_attack_time = Missiontime;
1347                         aip->submode_start_time = Missiontime;
1348                 }
1349                 }
1350                 break;
1351
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;
1357                         } else {
1358                                 aip->submode = SM_ATTACK;
1359                                 aip->last_attack_time = Missiontime;
1360                                 aip->submode_start_time = Missiontime;
1361                         }
1362                 }
1363
1364                 break;*/
1365
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;
1371                 }
1372                 break;
1373
1374         default:
1375                 aip->submode = SM_ATTACK;
1376                 aip->last_attack_time = Missiontime;
1377                 aip->submode_start_time = Missiontime;
1378         }
1379
1380         //
1381         //      Maybe fire primary weapon and update time_enemy_in_range
1382         //
1383         //nprintf(("AI", "time_enemy_in_range = %7.3f, dot = %7.3f\n", aip->time_enemy_in_range, dot_to_enemy));
1384
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
1388         //                               a big ship
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);
1391         } else
1392                 aip->time_enemy_in_range *= (1.0f - flFrametime);
1393 }
1394
1395 void ai_big_ship(object *objp)
1396 {
1397         // do nothing
1398 }
1399
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)
1407 {
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];
1412
1413         Assert(aip->mode == AIM_STRAFE);
1414
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);
1418                 return;
1419         }
1420
1421         ai_set_positions(Pl_objp, En_objp, aip, &player_pos, enemy_pos);
1422
1423         player_pos = Pl_objp->pos;
1424
1425         if (aip->targeted_subsys != NULL) {
1426                 Assert(aip->targeted_subsys != NULL);
1427                 get_subsystem_pos(enemy_pos, En_objp, aip->targeted_subsys);
1428         } else {
1429                 // checks valid line to target
1430                 ai_big_pick_attack_point(En_objp, Pl_objp, enemy_pos, 0.8f);
1431         }
1432
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); 
1435
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;
1442         } else {
1443                 vector  gun_pos, pnt;
1444                 polymodel *po = model_get( Ship_info[shipp->ship_info_index].modelnum );
1445                 float           weapon_speed;
1446
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);
1452                 
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);
1454         }
1455
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);
1459
1460         *enemy_pos = predicted_enemy_pos;
1461 }
1462
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)
1466 {
1467         ai_info *aip;
1468         aip = &Ai_info[Ships[Pl_objp->instance].ai_index];
1469
1470         vector vec_to_target;
1471         vm_vec_sub(&vec_to_target, target_pos, &Pl_objp->pos);
1472
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);
1477         } else {
1478                 dist_normal_to_target = 0.2f * vm_vec_mag_quick(&vec_to_target);
1479         }
1480
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;
1483
1484         // add distance penalty for going too fast
1485         float speed_to_dist_penalty = max(0.0f, (Pl_objp->phys_info.speed-50));
1486
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))
1488
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);
1496                 } else {
1497                         // too close for comfort so fly to box point + 300
1498                         aip->submode = AIS_STRAFE_RETREAT1;
1499                         aip->submode_start_time = Missiontime;
1500
1501                         float box_dist;
1502                         int is_inside;
1503                         vector goal_point;
1504                         box_dist = get_world_closest_box_point_with_delta(&goal_point, En_objp, &Pl_objp->pos, &is_inside, STRAFE_RETREAT_BOX_DIST);
1505
1506                         // set goal point
1507                         aip->goal_point = goal_point;
1508
1509                         if (ai_maybe_fire_afterburner(Pl_objp, aip)) {
1510                                 afterburners_start(Pl_objp);
1511                                 aip->afterburner_stop_time = Missiontime + 3*F1_0;
1512                         }
1513                 }
1514
1515                 return 1;
1516         } else {
1517                 return 0;
1518         }
1519 }
1520
1521 // attack directly to the turret and fire weapons 
1522 void ai_big_strafe_attack()
1523 {
1524         ai_info *aip;
1525         vector  target_pos;
1526         vector  rand_vec;
1527         float           target_dist, target_dot, accel, t;
1528         object  *target_objp;
1529
1530         aip = &Ai_info[Ships[Pl_objp->instance].ai_index];
1531
1532         if ( ai_big_maybe_follow_subsys_path(0) ) {
1533                 return;
1534         }
1535
1536         ai_big_attack_get_data(&target_pos, &target_dist, &target_dot);
1537         if ( ai_big_strafe_maybe_retreat(target_dist, &target_pos) )
1538                 return;
1539
1540         target_objp = &Objects[aip->target_objnum];
1541
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;
1550                         }
1551                         return;
1552                 }
1553         }
1554
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;
1560                         switch(rand()%4) {
1561                         case 0:
1562                                 vm_vec_scale_add(&rand_vec, &Pl_objp->orient.v.fvec, &Pl_objp->orient.v.uvec, 2.0f);
1563                                 break;
1564                         case 1:
1565                                 vm_vec_scale_add(&rand_vec, &Pl_objp->orient.v.fvec, &Pl_objp->orient.v.uvec, -2.0f);
1566                                 break;
1567                         case 2:
1568                                 vm_vec_scale_add(&rand_vec, &Pl_objp->orient.v.fvec, &Pl_objp->orient.v.rvec, 2.0f);
1569                                 break;
1570                         case 3:
1571                                 vm_vec_scale_add(&rand_vec, &Pl_objp->orient.v.fvec, &Pl_objp->orient.v.rvec, -2.0f);
1572                                 break;
1573                         } // end switch
1574
1575                         vm_vec_scale(&rand_vec, 1000.0f);
1576                         vm_vec_add2(&aip->goal_point, &rand_vec);
1577
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));
1581
1582                         if (ai_maybe_fire_afterburner(Pl_objp, aip)) {
1583                                 afterburners_start(Pl_objp);
1584                                 aip->afterburner_stop_time = Missiontime + fl2f(0.5f);
1585                         }
1586                 }
1587         }
1588
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);
1592
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) {
1596                 accel = 1.0f;
1597         } else {
1598                 float attack_time;
1599                 attack_time = f2fl(Missiontime - aip->submode_start_time);
1600                 if ( attack_time > 15 ) {
1601                         accel = 0.2f;
1602                 } else if ( attack_time > 10 ) {
1603                         accel = 0.4f;
1604                 } else if ( attack_time > 8 ) {
1605                         accel = 0.6f;
1606                 } else if ( attack_time > 5 ) {
1607                         accel = 0.8f;
1608                 } else {
1609                         accel = 1.0f;
1610                 }
1611         }
1612
1613         accel = 1.0f;
1614         accelerate_ship(aip, accel);
1615
1616         // if haven't been hit in quite a while, leave strafe mode
1617         fix long_enough;
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);
1621         }
1622 }
1623
1624 // pick a new attack point when entering this state, and keep using it
1625 void ai_big_strafe_avoid()
1626 {
1627         ai_info *aip;
1628         vector  target_pos;
1629         float           target_dist, target_dot;
1630         fix             mode_time;
1631
1632         aip = &Ai_info[Ships[Pl_objp->instance].ai_index];
1633
1634         mode_time = Missiontime - aip->submode_start_time;
1635
1636         ai_big_attack_get_data(&target_pos, &target_dist, &target_dot);
1637         if ( ai_big_strafe_maybe_retreat(target_dist, &target_pos) )
1638                 return;
1639
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));
1644         }
1645
1646         turn_towards_point(Pl_objp, &aip->goal_point, NULL, 0.0f);
1647         accelerate_ship(aip, 1.0f);
1648 }
1649
1650 // move towards aip->goal_point in an evasive manner
1651 void ai_big_strafe_retreat1()
1652 {
1653         float dist;
1654         ai_info *aip;
1655         vector  rand_vec;
1656
1657         aip = &Ai_info[Ships[Pl_objp->instance].ai_index];
1658
1659         dist = vm_vec_dist_quick(&Pl_objp->pos, &aip->goal_point);
1660         if ( dist < 70 ) {
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));
1664                 return;
1665         }
1666
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;
1670                 switch(rand()%4) {
1671                 case 0:
1672                         vm_vec_add(&rand_vec, &Pl_objp->orient.v.fvec, &Pl_objp->orient.v.uvec);
1673                         break;
1674                 case 1:
1675                         vm_vec_scale_add(&rand_vec, &Pl_objp->orient.v.fvec, &Pl_objp->orient.v.uvec, -1.0f);
1676                         break;
1677                 case 2:
1678                         vm_vec_add(&rand_vec, &Pl_objp->orient.v.fvec, &Pl_objp->orient.v.rvec);
1679                         break;
1680                 case 3:
1681                         vm_vec_scale_add(&rand_vec, &Pl_objp->orient.v.fvec, &Pl_objp->orient.v.rvec, -1.0f);
1682                         break;
1683                 } // end switch
1684
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);
1688
1689                 aip->submode = AIS_STRAFE_RETREAT2;
1690                 aip->submode_start_time = Missiontime;
1691         }
1692
1693         turn_towards_point(Pl_objp, &aip->goal_point, NULL, 0.0f);
1694         accelerate_ship(aip, 1.0f);
1695 }
1696
1697 void ai_big_strafe_retreat2()
1698 {
1699         float dist;
1700         ai_info *aip;
1701
1702         aip = &Ai_info[Ships[Pl_objp->instance].ai_index];
1703
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);
1708                 }
1709         }
1710
1711         dist = vm_vec_dist_quick(&Pl_objp->pos, &aip->goal_point);
1712         if ( dist < 70 ) {
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));
1716                 return;
1717         }
1718
1719         if (Missiontime - aip->submode_start_time > fl2f(0.70f)) {
1720                 aip->submode = AIS_STRAFE_RETREAT1;
1721                 aip->submode_start_time = Missiontime;
1722
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;
1727                         }
1728                 }
1729         }
1730
1731         turn_towards_point(Pl_objp, &aip->prev_goal_point, NULL, 0.0f);
1732         accelerate_ship(aip, 1.0f);
1733 }
1734
1735 // reposition self to begin another strafing run
1736 void ai_big_strafe_position()
1737 {
1738         ai_info *aip;
1739         aip = &Ai_info[Ships[Pl_objp->instance].ai_index];
1740         // TODO: loop around or something!!
1741
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));
1745 }
1746
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
1753 //
1754 void ai_big_strafe()
1755 {
1756         ai_info *aip;
1757
1758         aip = &Ai_info[Ships[Pl_objp->instance].ai_index];
1759
1760         Assert(aip->mode == AIM_STRAFE);
1761
1762 /*
1763         if ( aip->goal_objnum != aip->target_objnum ) {
1764                 Int3(); // what is going on here? - Get Alan
1765                 aip->mode = AIM_NONE;
1766                 return;
1767         }
1768 */
1769
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);
1773                 return;
1774         }
1775         
1776         switch (aip->submode) {
1777         case AIS_STRAFE_ATTACK:
1778                 ai_big_strafe_attack();
1779                 break;
1780         case AIS_STRAFE_AVOID:
1781                 ai_big_strafe_avoid();
1782                 break;
1783         case AIS_STRAFE_RETREAT1:
1784                 ai_big_strafe_retreat1();
1785                 break;
1786         case AIS_STRAFE_RETREAT2:
1787                 ai_big_strafe_retreat2();
1788                 break;
1789         case AIS_STRAFE_POSITION:
1790                 ai_big_strafe_position();
1791                 break;
1792         default:
1793
1794                 Int3();         //      Illegal submode for AIM_STRAFE
1795                 break;
1796         }
1797 }
1798
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
1801 //
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)
1805 {
1806         ai_info         *aip;
1807         ship_info       *sip;
1808         object          *weapon_objp, *parent_objp;
1809
1810         aip = &Ai_info[Ships[pl_objp->instance].ai_index];
1811         Assert(aip->mode != AIM_STRAFE);                // can't happen
1812
1813         // if Pl_objp has no target, then we can't enter strafe mode
1814         if ( aip->target_objnum < 0 ) {
1815                 return 0;
1816         }
1817
1818         // if target is not a ship, stafe mode is not possible
1819         if ( Objects[aip->target_objnum].type != OBJ_SHIP ) {
1820                 return 0;
1821         }
1822
1823         sip = &Ship_info[Ships[Objects[aip->target_objnum].instance].ship_info_index];
1824
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) ) {
1828                 return 0;
1829         }
1830
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)) ) {
1833                 return 0;
1834         }
1835
1836         Assert(weapon_objnum >= 0 && weapon_objnum < MAX_OBJECTS);
1837         weapon_objp = &Objects[weapon_objnum];
1838         Assert(weapon_objp->type == OBJ_WEAPON);
1839
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) ) {
1843                 return 0;
1844         }
1845
1846         // Maybe the ship which fired the weapon isn't the current target
1847         if ( OBJ_INDEX(parent_objp) != aip->target_objnum ) {
1848
1849 //JAS IMPOSSIBLE                if (1) { // consider_target_only ) {
1850 //JAS IMPOSSIBLE                        return 0;
1851 //JAS IMPOSSIBLE                } else {
1852                         // switch targets
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) ) {
1855                                 return 0;
1856                         }
1857                         set_target_objnum(aip, OBJ_INDEX(parent_objp));
1858 //JAS IMPOSSIBLE                }
1859         }
1860
1861         ai_big_strafe_maybe_attack_turret(pl_objp, weapon_objp);
1862
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));
1871
1872         return 1;
1873 }
1874
1875 // Consider attacking a turret, if a turret actually fired the weapon
1876 // input:       ship_objp       =>      ship that will attack the turret
1877 //                              weapon_objp     =>      
1878 void ai_big_strafe_maybe_attack_turret(object *ship_objp, object *weapon_objp)
1879 {
1880         ai_info *aip;
1881         object  *parent_objp;
1882
1883         Assert(ship_objp->type == OBJ_SHIP);
1884         aip = &Ai_info[Ships[ship_objp->instance].ai_index];
1885
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) )
1889                 return;
1890
1891         // If ship is already attacking a subsystem, don't switch
1892         if ( aip->targeted_subsys != NULL ) {
1893                 return;
1894         }
1895
1896         // If the weapon is not from a turret, return
1897         if ( Weapons[weapon_objp->instance].turret_subsys == NULL ) {
1898                 return;
1899         }
1900
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) ) {
1905                 return;
1906         }
1907
1908         if ( aip->target_objnum != OBJ_INDEX(parent_objp) ) {
1909                 return;
1910         }
1911
1912         // attack the turret
1913         set_targeted_subsys(aip, Weapons[weapon_objp->instance].turret_subsys, OBJ_INDEX(parent_objp));
1914 }