]> icculus.org git repositories - taylor/freespace2.git/blob - src/ship/aicode.cpp
a few NDEBUG updates.
[taylor/freespace2.git] / src / ship / aicode.cpp
1 /*
2  * $Logfile: /Freespace2/code/Ship/AiCode.cpp $
3  * $Revision$
4  * $Date$
5  * $Author$
6  * 
7  * AI code that does interesting stuff
8  *
9  * $Log$
10  * Revision 1.3  2002/06/01 07:12:34  relnev
11  * a few NDEBUG updates.
12  *
13  * removed a few warnings.
14  *
15  * Revision 1.2  2002/05/03 13:34:33  theoddone33
16  * More stuff compiles
17  *
18  * Revision 1.1.1.1  2002/05/03 03:28:10  root
19  * Initial import.
20  *
21  * 
22  * 107   9/15/99 4:42a Mikek
23  * Make any big ship attacking Colossus, or Colossus attacking any large
24  * ship not use big cruiser movement code.
25  * 
26  * 106   9/15/99 3:28a Jimb
27  * Make all big ships in sm3-08 not do cruiser chase code when attacking
28  * Colossus.  Added so Beast doesn't swerve away from Colossus.
29  * 
30  * 105   9/14/99 4:18p Andsager
31  * hack for mission sm3-08 to abort cruiser_chase as sathanas is about to
32  * begin circling colossus.
33  * 
34  * 104   9/08/99 10:44p Andsager
35  * Make HUGE ships not die when warping out, after warp effect started.
36  * 
37  * 103   9/03/99 11:40p Mikek
38  * Comment out an annoying nprintf().
39  * 
40  * 102   9/01/99 11:26p Dave
41  * Fixed release build warnings.
42  * 
43  * 101   9/01/99 9:12p Mikek
44  * Make it a boatload harder to become a traitor from hitting a large
45  * ship.
46  * 
47  * 100   9/01/99 4:01p Andsager
48  * Make sure BIG|HUGE ships do not respond to shockwaves
49  * 
50  * 99    9/01/99 10:09a Dave
51  * Pirate bob.
52  * 
53  * 98    8/31/99 4:24p Andsager
54  * Reduce collisions when attacking big ships.
55  * 
56  * 97    8/31/99 7:33a Mikek
57  * Improvements in formation flying, less silly behavior, especially when
58  * leader is moving very slowly.
59  * 
60  * 96    8/31/99 5:48a Mikek
61  * Making ships not overshoot so much in formation flying.  Intermediate
62  * checkin.
63  * 
64  * 95    8/30/99 12:03a Mikek
65  * Make guard behavior much less annoying.  Guarders don't get quite so
66  * close and they try to avoid striking the target they are guarding.
67  * 
68  * 94    8/29/99 4:18p Andsager
69  * New "burst" limit for friendly damage.  Also credit more damage done
70  * against large friendly ships.
71  * 
72  * 93    8/28/99 7:29p Dave
73  * Fixed wingmen persona messaging. Make sure locked turrets don't count
74  * towards the # attacking a player.
75  * 
76  * 92    8/26/99 10:46p Andsager
77  * Apply shockwave damage to lethality.
78  * 
79  * 91    8/26/99 8:52p Dave
80  * Gave multiplayer TvT messaging a heavy dose of sanity. Cheat codes.
81  * 
82  * 90    8/26/99 5:14p Andsager
83  * 
84  * 89    8/24/99 8:55p Dave
85  * Make sure nondimming pixels work properly in tech menu.
86  * 
87  * 88    8/23/99 6:21p Jefff
88  * added "no traitor" option to missions (and fred)
89  * 
90  * 87    8/20/99 3:36p Andsager
91  * Make sure we don;t miss stealth sweep points.
92  * 
93  * 86    8/16/99 8:21a Andsager
94  * fix link error
95  * 
96  * 85    8/16/99 8:19a Andsager
97  * Add project_point_onto_bbox() to fvi and include in aicode
98  * 
99  * 84    8/15/99 1:30p Dave
100  * Removed some bounding box code because of link errors. Assuming needed
101  * function just needs to get checked in by DaveA.
102  * 
103  * 83    8/15/99 11:59a Andsager
104  * For targing big/huge ships, find nearest distance to bbox, not center.
105  * 
106  * 82    8/13/99 2:20p Andsager
107  * Add speed modification to chances turret will find stealth ship
108  * 
109  * 81    8/13/99 10:49a Andsager
110  * Knossos and HUGE ship warp out.  HUGE ship warp in.  Stealth search
111  * modes dont collide big ships.
112  * 
113  * 80    8/10/99 5:02p Andsager
114  * Fix bug where AI gets stuck in SM_EVADE_WEAPON with no target.
115  * 
116  * 79    8/10/99 11:58a Andsager
117  * Allow turrets to sometimes see stealth.
118  * 
119  * 78    7/31/99 2:57p Dave
120  * Scaled flak aim and jitter by weapon subsystem strength.
121  * 
122  * 77    7/27/99 10:33p Andsager
123  * improve ai for attacking stealth.  reduced jitter in aim.  reduced
124  * error in position when avoiding.  skill level support for attacking
125  * stealth.  Made target error same for team vs. team.
126  * 
127  * 76    7/27/99 10:49a Andsager
128  * Make turret fire rate independent of team for HUGE turrets, and also
129  * for mult team vs. team.
130  * 
131  * 75    7/26/99 12:14p Andsager
132  * Apply cap to how much slower a transport flies with cargo.  Remove
133  * limit on waypoint speed for training.  Enemy ai get stealth exact pos
134  * when stealth fires
135  * 
136  * 74    7/20/99 1:49p Dave
137  * Peter Drake build. Fixed some release build warnings.
138  * 
139  * 73    7/19/99 2:13p Dave
140  * Added some new strings for Heiko.
141  * 
142  * 72    7/19/99 12:02p Andsager
143  * Allow AWACS on any ship subsystem. Fix sexp_set_subsystem_strength to
144  * only blow up subsystem if its strength is > 0
145  * 
146  * 71    7/15/99 9:20a Andsager
147  * FS2_DEMO initial checkin
148  * 
149  * 70    7/14/99 1:44p Andsager
150  * modify ai_guard for BIG ships to circle around the long axis
151  * 
152  * 69    7/09/99 5:54p Dave
153  * Seperated cruiser types into individual types. Added tons of new
154  * briefing icons. Campaign screen.
155  * 
156  * 68    7/08/99 4:32p Andsager
157  * fix bug with turret-tagged-only
158  * 
159  * 67    7/08/99 12:06p Andsager
160  * Add turret-tagged-only and turret-tagged-clear sexp.
161  * 
162  * 66    7/02/99 3:49p Andsager
163  * Remove debug code.  Allow targeting of stealth from any weapon it
164  * fires.
165  * 
166  * 65    7/02/99 2:01p Andsager
167  * Fix bug where big ship tries to evade dumpfire weapon.
168  * 
169  * 64    7/02/99 10:58a Andsager
170  * Put in big ship - big ship attack mode.  Modify stealth sweep ai.
171  * 
172  * 63    6/30/99 5:53p Dave
173  * Put in new anti-camper code.
174  * 
175  * 62    6/28/99 3:22p Anoop
176  * Fix turret optimization, where ship may not have any valid subsystems
177  * (all blown off).
178  * 
179  * 61    6/25/99 5:56p Andsager
180  * First real pass on stealth ai.
181  * 
182  * 60    6/25/99 3:08p Dave
183  * Multiple flyby sounds.
184  * 
185  * 59    6/25/99 1:12p Danw
186  * DKA:  Make sure big ship has subsystems before trying to target them.
187  * 
188  * 58    6/25/99 10:56a Johnson
189  * Fixed dumb ai code.
190  * 
191  * 57    6/24/99 5:15p Dave
192  * Make sure stride is always at least one for checking turret subsystem
193  * targets.
194  * 
195  * 56    6/24/99 4:59p Dave
196  * Significant speedups to turret firing.
197  * 
198  * 55    6/23/99 5:51p Andsager
199  * Add waypoint-cap-speed.  Checkin stealth ai - inactive.
200  * 
201  * 54    6/16/99 10:21a Dave
202  * Added send-message-list sexpression.
203  * 
204  * 53    6/15/99 9:25a Andsager
205  * Make guard and dynamic chase (who hit you) work with stealth
206  * 
207  * 52    6/14/99 3:21p Andsager
208  * Allow collisions between ship and its debris.  Fix up collision pairs
209  * when large ship is warping out.
210  * 
211  * 51    6/14/99 10:45a Dave
212  * Made beam weapons specify accuracy by skill level in the weapons.tbl
213  * 
214  * 50    6/03/99 8:11a Andsager
215  * 
216  * 49    6/02/99 5:41p Andsager
217  * Reduce range of secondary weapons not fired from turrets in nebula.
218  * Reduce range of beams fired from turrrets in nebula
219  * 
220  * 48    6/02/99 3:23p Andsager
221  * Make AI aware of team visibility.  Allow player targeting with team
222  * visibility info.  Make stealth ships not targetable by AI in nebula
223  * unless tagged.
224  * 
225  * 47    6/02/99 12:52p Andsager
226  * Added team-wide ship visibility.  Implemented for player.
227  * 
228  * 46    6/01/99 8:35p Dave
229  * Finished lockarm weapons. Added proper supercap weapons/damage. Added
230  * awacs-set-radius sexpression.
231  * 
232  * 45    5/28/99 5:35p Andsager
233  * Make ai nebula aware
234  * 
235  * 44    5/24/99 9:55a Dave
236  * Fixed stream weapon ai firing problem. ick.
237  * 
238  * 43    5/20/99 7:00p Dave
239  * Added alternate type names for ships. Changed swarm missile table
240  * entries.
241  * 
242  * 42    5/18/99 1:30p Dave
243  * Added muzzle flash table stuff.
244  * 
245  * 41    5/12/99 2:55p Andsager
246  * Implemented level 2 tag as priority in turret object selection
247  * 
248  * 40    5/12/99 10:42a Andsager
249  * Fix turret bug allowing HUGE turrets to fire at fighters
250  * 
251  * 39    5/06/99 11:46a Andsager
252  * Bug fixes.  Don't get into illegal strafe submode.  Don't choose turret
253  * enemy objnum for beam protected.
254  * 
255  * 38    5/03/99 10:50p Andsager
256  * Make Asteroid_obj_list.  Change get_nearest_turret_objnum() to use
257  * Asteroid_obj_list, Ship_obj_list and Missile_obj_list vs.
258  * obj_used_list.
259  * 
260  * 37    4/29/99 2:29p Dave
261  * Made flak work much better in multiplayer.
262  * 
263  * 36    4/28/99 11:36p Dave
264  * Tweaked up subspace missile strike a bit,
265  * 
266  * 35    4/28/99 3:11p Andsager
267  * Stagger turret weapon fire times.  Make turrets smarter when target is
268  * protected or beam protected.  Add weaopn range to weapon info struct.
269  * 
270  * 34    4/26/99 10:58a Andsager
271  * Add OF_BEAM_PROTECTED flag to keep object from being targeted for zing.
272  * 
273  * 33    4/23/99 12:12p Andsager
274  * Modify wing positions when player is wing leader to prevent some
275  * collisions.
276  * 
277  * 32    4/23/99 12:01p Johnson
278  * Added SIF_HUGE_SHIP
279  * 
280  * 31    4/22/99 11:06p Dave
281  * Final pass at beam weapons. Solidified a lot of stuff. All that remains
282  * now is to tweak and fix bugs as they come up. No new beam weapon
283  * features.
284  * 
285  * 30    4/20/99 6:39p Dave
286  * Almost done with artillery targeting. Added support for downloading
287  * images on the PXO screen.
288  * 
289  * 29    4/20/99 3:40p Andsager
290  * Changes to big ship ai.  Uses bounding box as limit where to fly to
291  * when flying away.
292  * 
293  * 28    4/16/99 5:54p Dave
294  * Support for on/off style "stream" weapons. Real early support for
295  * target-painting lasers.
296  * 
297  * 27    4/02/99 9:55a Dave
298  * Added a few more options in the weapons.tbl for beam weapons. Attempt
299  * at putting "pain" packets into multiplayer.
300  * 
301  * 26    3/28/99 5:58p Dave
302  * Added early demo code. Make objects move. Nice and framerate
303  * independant, but not much else. Don't use yet unless you're me :)
304  * 
305  * 25    3/19/99 9:51a Dave
306  * Checkin to repair massive source safe crash. Also added support for
307  * pof-style nebulae, and some new weapons code.
308  * 
309  * 24    3/08/99 7:03p Dave
310  * First run of new object update system. Looks very promising.
311  * 
312  * 23    3/05/99 3:55p Anoop
313  * Handle some asserts properly.
314  * 
315  * 22    3/04/99 6:09p Dave
316  * Added in sexpressions for firing beams and checking for if a ship is
317  * tagged.
318  * 
319  * 21    3/02/99 9:25p Dave
320  * Added a bunch of model rendering debug code. Started work on fixing
321  * beam weapon wacky firing.
322  * 
323  * 20    2/25/99 2:32p Anoop
324  * (Alan). Fixed ai path following code for AI_BAY_EMERGE. Put in sanity
325  * check so that when the last point on the path is reached, it finishes.
326  * 
327  * 19    2/19/99 2:11p Anoop
328  * Put in some nice handling code for wacky support ship problems (like no
329  * docking paths)
330  * 
331  * 18    2/17/99 2:11p Dave
332  * First full run of squad war. All freespace and tracker side stuff
333  * works.
334  * 
335  * 17    2/11/99 5:22p Andsager
336  * Fixed bugs, generalized block Sexp_variables
337  * 
338  * 16    1/29/99 5:07p Dave
339  * Fixed multiplayer stuff. Put in multiplayer support for rapid fire
340  * missiles.
341  * 
342  * 15    1/29/99 2:25p Andsager
343  * Added turret_swarm_missiles
344  * 
345  * 14    1/27/99 9:56a Dave
346  * Temporary checkin of beam weapons for Dan to make cool sounds.
347  * 
348  * 13    1/24/99 11:37p Dave
349  * First full rev of beam weapons. Very customizable. Removed some bogus
350  * Int3()'s in low level net code.
351  * 
352  * 12    1/21/99 10:44a Dave
353  * More beam weapon stuff. Put in warmdown time.
354  * 
355  * 11    1/12/99 5:45p Dave
356  * Moved weapon pipeline in multiplayer to almost exclusively client side.
357  * Very good results. Bandwidth goes down, playability goes up for crappy
358  * connections. Fixed object update problem for ship subsystems.
359  * 
360  * 10    1/08/99 2:08p Dave
361  * Fixed software rendering for pofview. Super early support for AWACS and
362  * beam weapons.
363  * 
364  * 9     12/23/98 2:53p Andsager
365  * Added ship activation and gas collection subsystems, removed bridge
366  * 
367  * 8     11/12/98 12:13a Dave
368  * Tidied code up for multiplayer test. Put in network support for flak
369  * guns.
370  * 
371  * 7     11/05/98 5:55p Dave
372  * Big pass at reducing #includes
373  * 
374  * 6     10/26/98 9:42a Dave
375  * Early flak gun support.
376  * 
377  * 5     10/23/98 3:51p Dave
378  * Full support for tstrings.tbl and foreign languages. All that remains
379  * is to make it active in Fred.
380  * 
381  * 4     10/20/98 1:39p Andsager
382  * Make so sparks follow animated ship submodels.  Modify
383  * ship_weapon_do_hit_stuff() and ship_apply_local_damage() to add
384  * submodel_num.  Add submodel_num to multiplayer hit packet.
385  * 
386  * 3     10/13/98 9:29a Dave
387  * Started neatening up freespace.h. Many variables renamed and
388  * reorganized. Added AlphaColors.[h,cpp]
389  * 
390  * 2     10/07/98 10:53a Dave
391  * Initial checkin.
392  * 
393  * 1     10/07/98 10:51a Dave
394  * 
395  * 
396  * $NoKeywords: $
397  */
398
399 // This module contains the actual AI code that does interesting stuff
400 // to objects.   The code in Ai.cpp is just for bookeeping, allocating
401 // ai slots and linking them to ships.
402
403 #include "pstypes.h"
404 #include "fix.h"
405 #include "linklist.h"
406 #include "object.h"
407 #include "physics.h"
408 #include "vecmat.h"
409 #include "ship.h"
410 #include "model.h"
411 #include "2d.h"
412 #include "3d.h"
413 #include "ai.h"
414 #include "floating.h"
415 #include "player.h"
416 #include "freespace.h"
417 #include "weapon.h"
418 #include "missiongoals.h"
419 #include "missionlog.h"
420 #include "timer.h"
421 #include "sound.h"
422 #include "aigoals.h"
423 #include "gamesnd.h"
424 #include "hudmessage.h"
425 #include "missionmessage.h"
426 #include "cmeasure.h"
427 #include "staticrand.h"
428 #include "multimsgs.h"
429 #include "afterburner.h"
430 #include "hudets.h"
431 #include "shipfx.h"
432 #include "shiphit.h"
433 #include "aibig.h"
434 #include "multiutil.h"
435 #include "hud.h"
436 #include "objcollide.h"
437 #include "asteroid.h"
438 #include "hudlock.h"
439 #include "missiontraining.h"
440 #include "gamesequence.h"
441 #include "joy_ff.h"
442 #include "localize.h"
443 #include "flak.h"
444 #include "beam.h"
445 #include "multi.h"
446 #include "swarm.h"
447 #include "multi_team.h"
448 #include "awacs.h"
449 #include "fvi.h"
450
451 #ifndef PLAT_UNIX
452 #pragma optimize("", off)
453 #pragma auto_inline(off)
454 #endif
455
456 #define UNINITIALIZED_VALUE     -99999.9f
457
458 #define INSTRUCTOR_SHIP_NAME NOX("instructor")
459
460 #define AICODE_SMALL_MAGNITUDE  0.001f          // cosider a vector NULL if mag is less than this
461
462 #define NEXT_REARM_TIMESTAMP (60*1000)                  //      Ships will re-request rearm, typically, after this long.
463
464 #define BEAM_NEBULA_RANGE_REDUCE_FACTOR         0.8
465
466 // AIM_CHASE submode defines
467 // SM_STEALTH_FIND
468 #define SM_SF_AHEAD             0
469 #define SM_SF_BEHIND    1
470 #define SM_SF_BAIL              2
471
472 // SM_STEALTH_SWEEP
473 #define SM_SS_SET_GOAL  -1
474 #define SM_SS_BOX0              0
475 #define SM_SS_LR                        1
476 #define SM_SS_UL                        2
477 #define SM_SS_BOX1              3
478 #define SM_SS_UR                        4
479 #define SM_SS_LL                        5
480 #define SM_SS_BOX2              6
481 #define SM_SS_DONE              7
482
483 //XSTR:OFF
484
485 char *Mode_text[MAX_AI_BEHAVIORS] = {
486         "CHASE",
487         "EVADE",
488         "GET_BEHIND",
489         "CHASE_LONG",
490         "SQUIGGLE",
491         "GUARD",
492         "AVOID",
493         "WAYPOINTS",
494         "DOCK",
495         "NONE",
496         "BIGSHIP",
497         "PATH",
498         "BE_REARMED",
499         "SAFETY",
500         "EV_WEAPON",
501         "STRAFE",
502         "PLAY_DEAD",
503         "BAY_EMERGE",
504         "BAY_DEPART",
505         "SENTRYGUN",
506         "WARP_OUT",
507 };
508
509 //      Submode text is only valid for CHASE mode.
510 char *Submode_text[] = {
511 "undefined",
512 "CONT_TURN",
513 "ATTACK   ",
514 "E_SQUIG  ",
515 "E_BRAKE  ",
516 "EVADE    ",
517 "SUP_ATTAK",
518 "AVOID    ",
519 "BEHIND   ",
520 "GET_AWAY ",
521 "E_WEAPON ",
522 "FLY_AWAY ",
523 "ATK_4EVER",
524 "STLTH_FND",
525 "STLTH_SWP",
526 "BIG_APPR",
527 "BIG_CIRC",
528 "BIG_PARL"
529 };
530
531 char *Strafe_submode_text[5] = {
532 "ATTACK",
533 "AVOID",
534 "RETREAT1",
535 "RETREAT2",
536 "POSITION"
537 };
538 //XSTR:ON
539
540 /*
541 //      Used for global ignore of objects.  If an object appears in the Ignore_objects array,
542 //      no one will attack it.
543 #define MAX_IGNORE_OBJECTS      16
544 typedef struct {
545         int     objnum;
546         int     signature;
547 } ignore_object;
548
549 ignore_object   Ignore_objects[MAX_IGNORE_OBJECTS];
550 */
551
552 typedef struct eval_enemy_obj_struct {
553         int                     turret_parent_objnum;                   // parent of turret
554         float                   weapon_travel_dist;                             // max targeting range of turret weapon
555         int                     enemy_team_mask;
556         int                     weapon_system_ok;                                       // is the weapon subsystem of turret ship ok
557         int                     big_only_flag;                                          // turret fires only at big and huge ships
558         vector          *tpos;
559         vector          *tvec;
560         ship_subsys *turret_subsys;
561         int                     current_enemy;
562
563
564         float                   nearest_attacker_dist;                  // nearest ship 
565         int                     nearest_attacker_objnum;
566
567         float                   nearest_homing_bomb_dist;               // nearest homing bomb
568         int                     nearest_homing_bomb_objnum;
569
570         float                   nearest_bomb_dist;                              // nearest non-homing bomb
571         int                     nearest_bomb_objnum;
572
573         float                   nearest_dist;                                           // nearest ship attacking this turret
574         int                     nearest_objnum;
575 }       eval_enemy_obj_struct;
576
577
578 control_info    AI_ci;
579
580 object *Pl_objp;
581 object *En_objp;
582
583 waypoint_list Waypoint_lists[MAX_WAYPOINT_LISTS];
584
585 // How close a turret has to be point at its target before it
586 // can fire.  If the dot of the gun normal and the vector from gun
587 // to target is greater than this, the turret fires.  The smaller
588 // the sloppier the shooting.
589 #define AICODE_TURRET_DUMBFIRE_ANGLE            (0.8f)  
590 #define AICODE_TURRET_HEATSEEK_ANGLE            (0.7f)  
591 #define AICODE_TURRET_MAX_TIME_IN_RANGE (5.0f)
592
593 #define REARM_SOUND_DELAY               (3*F1_0)                //      Amount of time to delay rearm/repair after mode start
594 #define REARM_BREAKOFF_DELAY    (3*F1_0)                //      Amount of time to wait after fully rearmed to breakoff.
595
596 #define MIN_DIST_TO_WAYPOINT_GOAL       5.0f
597 #define MAX_GUARD_DIST                                  250.0f
598 #define BIG_GUARD_RADIUS                                500.0f
599
600 #define MAX_EVADE_TIME                  (15 * 1000)     //      Max time to evade a weapon.
601
602 // defines for repair ship stuff.
603 #define MAX_REPAIR_SPEED                        25.0f
604 #define MAX_UNDOCK_ABORT_SPEED  2.0f
605
606 // defines for EMP effect stuff
607 #define MAX_EMP_INACCURACY              50.0f
608
609 // defines for stealth
610 #define MAX_STEALTH_INACCURACY  50.0f           // at max view dist
611 #define STEALTH_MAX_VIEW_DIST   400             // dist at which 1) stealth no longer visible 2) firing inaccuracy is greatest
612 #define STEALTH_VIEW_CONE_DOT   0.707           // (half angle of 45 degrees)
613
614
615 ai_class        Ai_classes[MAX_AI_CLASSES];
616 int     Ai_firing_enabled = 1;
617 int     Num_ai_classes;
618
619 int     AI_FrameCount = 0;
620 int     Ship_info_inited = 0;
621 int     AI_watch_object = 0; // Debugging, object to spew debug info for.
622 int     Num_waypoint_lists = 0;
623 int     Mission_all_attack = 0;                                 //      !0 means all teams attack all teams.
624
625 char *Skill_level_names(int level, int translate)
626 {
627         char *str = NULL;
628
629         #if NUM_SKILL_LEVELS != 5
630         #error Number of skill levels is wrong!
631         #endif
632
633         if(translate){
634                 switch( level ) {
635                 case 0:
636                         str = XSTR("Very Easy", 469);
637                         break;
638                 case 1:
639                         str = XSTR("Easy", 470);
640                         break;
641                 case 2:
642                         str = XSTR("Medium", 471);
643                         break;
644                 case 3:
645                         str = XSTR("Hard", 472);
646                         break;
647                 case 4:
648                         str = XSTR("Insane", 473);
649                         break;
650                 default:        
651                         Int3();
652                 }
653         } else {
654                 switch( level ) {
655                 case 0:
656                         str = NOX("Very Easy");
657                         break;
658                 case 1:
659                         str = NOX("Easy");
660                         break;
661                 case 2:
662                         str = NOX("Medium");
663                         break;
664                 case 3:
665                         str = NOX("Hard");
666                         break;
667                 case 4:
668                         str = NOX("Insane");
669                         break;
670                 default:        
671                         Int3();
672                 }
673         }
674
675         return str;
676 }
677
678 #define DELAY_TARGET_TIME       (12*1000)               //      time in milliseconds until a ship can target a new enemy after an order.
679
680 //      Make enemy ships turn more slowly at lower skill levels.
681 float   Turn_time_skill_level_scale[NUM_SKILL_LEVELS] = {3.0f, 2.2f, 1.6f, 1.3f, 1.0f};
682
683 //      Maximum number of simultaneous homing weapons on player based on skill level.
684 int     Max_allowed_player_homers[NUM_SKILL_LEVELS] = {2, 3, 4, 7, 99};
685
686 //      Number of ships that can attack another ship at a given skill level.
687 int     Skill_level_max_attackers[NUM_SKILL_LEVELS] = {2, 3, 4, 5, 99};
688
689 //      How long until next predict position.
690 fix Skill_level_delay[NUM_SKILL_LEVELS] = {2*F1_0, 3*F1_0/2, 4*F1_0/3, F1_0/2, 0};
691
692 //      AI ships link primary weapons if energy levels greater than the following amounts:
693 float   Link_energy_levels_always[NUM_SKILL_LEVELS] = {100.0f, 80.0f, 60.0f, 40.0f, 20.0f};     //      always link
694 float   Link_energy_levels_maybe[NUM_SKILL_LEVELS] = {90.0f, 60.0f, 40.0f, 20.0f, 10.0f};       //      link if hull strength low
695
696 //      Seconds to add to time it takes to get enemy in range.  Only for player's enemies.
697 float   In_range_time[NUM_SKILL_LEVELS] = {2.0f, 1.4f, 0.75f, 0.0f, -1.0f};
698
699 //      No matter what, a random unit vector gets scaled by this amount in firing at an enemy.
700 //      Note that for shorter in-range times, these values get scaled, so a value of 0.5f is meaningful.
701 float   Aiming_error[NUM_SKILL_LEVELS] = {3.0f, 2.2f, 1.3f, 0.7f, 0.2f};
702
703 //      Chance a countermeasure will be fired based on skill level.
704 float Cmeasure_fire_chance[NUM_SKILL_LEVELS] = {0.2f, 0.3f, 0.5f, 0.9f, 1.1f};  //      Note, this gets scaled by ai_class
705
706 float Shield_manage_delays[NUM_SKILL_LEVELS] = {5.0f, 4.0f, 2.5f, 1.2f, 0.1f};
707
708 // accuracy we feed into the beam weapons based upon skill system
709 // float Beam_accuracy[NUM_SKILL_LEVELS] = {2.0f, 1.5f, 1.0f, 0.7f, 0.4f};
710
711 extern float Ship_fire_delay_scale_hostile[NUM_SKILL_LEVELS];
712 extern float Ship_fire_delay_scale_friendly[NUM_SKILL_LEVELS];
713
714 pnode           Path_points[MAX_PATH_POINTS];
715 pnode           *Ppfp;                  //      Free pointer in path points.
716
717 float   AI_frametime;
718
719 char *Ai_class_names[MAX_AI_CLASSES];
720
721 // global for rearm status for teams
722 int Ai_friendly_rearm_timestamp, Ai_hostile_rearm_timestamp, Ai_neutral_rearm_timestamp, Ai_traitor_rearm_timestamp, Ai_unknown_rearm_timestamp;
723
724 // globals for dealing with when to fire huge secondary weapons
725 #define MAX_HUGE_SECONDARY_INFO 10
726
727 typedef struct {
728         int team;
729         int weapon_index;
730         int max_fire_count;
731         char    *shipname;
732 } huge_fire_info;
733
734 huge_fire_info Ai_huge_fire_info[MAX_HUGE_SECONDARY_INFO];
735
736 int Ai_last_arrive_path;        // index of ship_bay path used by last arrival from a fighter bay
737
738 // forward declarations
739 int     ai_return_path_num_from_dockbay(object *dockee_objp, int dockbay_index);
740 void    create_model_exit_path(object *pl_objp, object *mobjp, int path_num, int count=1);
741 void    copy_xlate_model_path_points(object *objp, model_path *mp, int dir, int count, int path_num, pnode *pnp, int randomize_pnt=-1);
742
743 // ai_set_rearm_status takes a team (friendly, hostile, neutral) and a time.  This function
744 // sets the timestamp used to tell is it is a good time for this team to rearm.  Once the timestamp
745 // is no longer valid, then rearming is not a "good time"
746 // not safe.  Called from sexpression code.
747 void ai_set_rearm_status( int team, int time )
748 {
749         Assert( time >= 0 );
750
751         switch (team) {
752         case TEAM_FRIENDLY:
753                 Ai_friendly_rearm_timestamp = timestamp( time * 1000 );
754                 break;
755         case TEAM_HOSTILE:
756                 Ai_hostile_rearm_timestamp = timestamp( time * 1000 );
757                 break;
758         case TEAM_NEUTRAL:
759                 Ai_neutral_rearm_timestamp = timestamp( time * 1000 );
760                 break;
761         case TEAM_TRAITOR:
762                 Ai_traitor_rearm_timestamp = timestamp( time * 1000 );
763                 break;
764         case TEAM_UNKNOWN:
765                 Ai_traitor_rearm_timestamp = timestamp( time * 1000 );
766                 break;
767         default:
768                 Int3();
769                 break;
770         }
771 }
772
773 // int ai_good_time_to_rearm returns true(1) or false(0) if it is "safe" for the given
774 // object to rearm.  "safe" is currently defined by the mission designer using the good/bad
775 // time to rearm sexpressions.  This status is currently team based.  This function could
776 // be easily expended to further the definition of "safe"
777 int ai_good_time_to_rearm( object *objp )
778 {
779         int team, status;
780
781         Assert(objp->type == OBJ_SHIP);
782         team = Ships[objp->instance].team;
783         status = 0;
784
785         switch(team) {
786         case TEAM_FRIENDLY:
787                 status = timestamp_valid(Ai_friendly_rearm_timestamp);
788                 break;
789         case TEAM_HOSTILE:
790                 status = timestamp_valid(Ai_hostile_rearm_timestamp);
791                 break;
792         case TEAM_NEUTRAL:
793                 status = timestamp_valid(Ai_neutral_rearm_timestamp);
794                 break;
795         case TEAM_TRAITOR:
796                 status = timestamp_valid(Ai_traitor_rearm_timestamp);
797                 break;
798         case TEAM_UNKNOWN:
799                 status = timestamp_valid(Ai_unknown_rearm_timestamp);
800                 break;
801         default:
802                 Int3();
803                 break;
804         }
805
806         return status;
807 }
808
809 // functions to deal with letting the ai know about good times to fire powerful secondary
810 // weapons.
811
812 // this function is entry point from sexpression code to set internal data for use by ai code.
813 void ai_good_secondary_time( int team, int weapon_index, int max_fire_count, char *shipname )
814 {
815         int i, index;
816
817         // find an open slot to put this data
818         for ( i = 0; i < MAX_HUGE_SECONDARY_INFO; i++ ) {
819                 if ( Ai_huge_fire_info[i].weapon_index == -1 )
820                         break;
821         }
822
823         Assert( i < MAX_HUGE_SECONDARY_INFO );                  // we've run out of room
824
825         Ai_huge_fire_info[i].weapon_index = weapon_index;
826         Ai_huge_fire_info[i].team = team;
827         Ai_huge_fire_info[i].max_fire_count = max_fire_count;
828
829         Ai_huge_fire_info[i].shipname = ai_get_goal_ship_name( shipname, &index );
830 }
831
832 // function called internally to the ai code to tell whether or not weapon_num can be fired
833 // from firer_objp at target_objp.  This function will resolve the team for the firer.
834 // returns:
835 //              -1  -- when conditions don't allow firer to fire weapon_num on target_objp
836 //              >=0 -- when conditions allow firer to fire.  Return value is max number of weapon_nums
837 //           which can be fired on target_objp
838 int is_preferred_weapon(int weapon_num, object *firer_objp, object *target_objp)
839 {
840         int i, firer_team, target_signature;
841         ship *firer_ship;
842         huge_fire_info *hfi = NULL;
843
844         Assert( firer_objp->type == OBJ_SHIP );
845         firer_ship = &Ships[firer_objp->instance];
846         firer_team = firer_ship->team;
847
848         // get target object's signature and try to find it in the list.
849         target_signature = target_objp->signature;
850         for ( i = 0; i < MAX_HUGE_SECONDARY_INFO; i++ ) {
851                 int ship_index, signature;
852
853                 hfi = &Ai_huge_fire_info[i];
854                 if ( hfi->weapon_index == -1 )
855                         continue;
856
857                 ship_index = ship_name_lookup( hfi->shipname );
858                 if ( ship_index == -1 )
859                         continue;
860
861                 signature = Objects[Ships[ship_index].objnum].signature;
862
863                 // sigatures, weapon_index, and team must match
864                 if ( (signature == target_signature) && (hfi->weapon_index == weapon_num) && (hfi->team == firer_team) )
865                         break;
866         }
867
868         // return -1 if not found
869         if ( i == MAX_HUGE_SECONDARY_INFO )
870                 return -1;
871
872         // otherwise, we can return the max number of weapons we can fire against target_objps
873
874         return hfi->max_fire_count;
875 }
876
877 // function to clear out secondary firing infomration between levels
878 void ai_init_secondary_info()
879 {
880         int i;
881
882         // clear out the data for dealing with when ai ships can fire huge secondary weapons
883         for (i = 0; i < MAX_HUGE_SECONDARY_INFO; i++ ) {
884                 Ai_huge_fire_info[i].weapon_index = -1;
885                 Ai_huge_fire_info[i].team = -1;
886                 Ai_huge_fire_info[i].max_fire_count = -1;
887                 Ai_huge_fire_info[i].shipname = NULL;
888         }
889 }
890
891
892 //      Garbage collect the Path_points buffer.
893 //      Scans all objects, looking for used Path_points records.
894 //      Compresses Path_points buffer, updating aip->path_start and aip->path_cur indices.
895 //      Updates Ppfp to point to first free record.
896 //      This function is fairly fast.  Its worst-case running time is proportional to
897 //      3*MAX_PATH_POINTS + MAX_OBJECTS
898 //      Things to do to optimize this function:
899 //              1. if (t != 0) xlt++; can be replaced by xlt += t; assuming t can only be 0 or 1.
900 //              2. When pp_xlate is getting stuffed the first time, note highest index and use that 
901 //                      instead of MAX_PATH_POINTS in following two for loops.
902 void garbage_collect_path_points()
903 {
904         int     i;
905         int     pp_xlate[MAX_PATH_POINTS];
906         object  *A;
907         ship_obj        *so;
908
909         //      Scan all objects and create Path_points xlate table.
910         for (i=0; i<MAX_PATH_POINTS; i++)
911                 pp_xlate[i] = 0;
912
913         //      in pp_xlate, mark all used Path_point records
914         for ( so = GET_FIRST(&Ship_obj_list); so != END_OF_LIST(&Ship_obj_list); so = GET_NEXT(so) ) {
915                 A = &Objects[so->objnum];
916                 ship    *shipp = &Ships[A->instance];
917                 if (shipp->ai_index != -1) {
918                         ai_info *aip = &Ai_info[shipp->ai_index];
919
920                         if ((aip->path_length > 0) && (aip->path_start > -1)) {
921
922                                 for (int i=aip->path_start; i<aip->path_start + aip->path_length; i++) {
923                                         Assert(pp_xlate[i] == 0);       //      If this is not 0, then two paths use this point!
924                                         pp_xlate[i] = 1;
925                                 }
926                         }
927                 }
928         }
929
930         //      Now, stuff xlate index in pp_xlate.  This is the number to translate any path_start
931         //      or path_cur index to.
932         int     xlt = 0;
933         for (i=0; i<MAX_PATH_POINTS; i++) {
934                 int     t = pp_xlate[i];
935
936                 pp_xlate[i] = xlt;
937                 if (t != 0)
938                         xlt++;
939         }
940         
941         //      Update global Path_points free pointer.
942         Ppfp = &Path_points[xlt];
943
944         //      Now, using pp_xlate, fixup all aip->path_cur and aip->path_start indices
945         for ( so = GET_FIRST(&Ship_obj_list); so != END_OF_LIST(&Ship_obj_list); so = GET_NEXT(so) ) {
946                 A = &Objects[so->objnum];
947                 ship    *shipp = &Ships[A->instance];
948                 if (shipp->ai_index != -1) {
949                         ai_info *aip = &Ai_info[shipp->ai_index];
950
951                         if ((aip->path_length > 0) && (aip->path_start > -1)) {
952                                 Assert(aip->path_start < MAX_PATH_POINTS);
953                                 aip->path_start = pp_xlate[aip->path_start];
954
955                                 Assert((aip->path_cur >= 0) && (aip->path_cur < MAX_PATH_POINTS));
956                                 aip->path_cur = pp_xlate[aip->path_cur];
957                         }
958                 }
959         }
960
961         //      Now, compress the buffer.
962         for (i=0; i<MAX_PATH_POINTS; i++)
963                 if (i != pp_xlate[i])
964                         Path_points[pp_xlate[i]] = Path_points[i];
965
966 }
967
968 //      Hash two values together, return result.
969 //      Hash function: curval shifted right circular by one, newval xored in.
970 int hash(unsigned int curval, int newval)
971 {
972         int     addval = curval & 1;
973
974         curval >>= 1;
975         if (addval)
976                 curval |= 0x80000000;
977         curval ^= newval;
978
979         return curval;
980 }
981
982 //      Hash some information in an object together.
983 //      On 2/20/97, the information is position and orientation.
984 int create_object_hash(object *objp)
985 {
986         int     *ip;
987         unsigned int    hashval = 0;
988         int     i;
989
990         ip = (int *) &objp->orient;
991
992         for (i=0; i<9; i++) {
993                 hashval = hash(hashval, *ip);
994                 ip++;
995         }
996
997         ip = (int *) &objp->pos;
998
999         for (i=0; i<3; i++) {
1000                 hashval = hash(hashval, *ip);
1001                 ip++;
1002         }
1003
1004         return hashval;
1005 }
1006
1007 //      Stuff a list of NUM_SKILL_LEVELS floats at *plist.
1008 void parse_float_list(float *plist)
1009 {
1010         int     i;
1011
1012         for (i=0; i<NUM_SKILL_LEVELS; i++) {
1013                 stuff_float(&plist[i]);
1014         }
1015 }
1016
1017 void parse_ai_class()
1018 {
1019         ai_class        *aicp = &Ai_classes[Num_ai_classes];
1020
1021         required_string("$Name:");
1022         stuff_string(aicp->name, F_NAME, NULL);
1023
1024         Ai_class_names[Num_ai_classes] = aicp->name;
1025
1026         required_string("$accuracy:");
1027         parse_float_list(aicp->ai_accuracy);
1028
1029         required_string("$evasion:");
1030         parse_float_list(aicp->ai_evasion);
1031
1032         required_string("$courage:");
1033         parse_float_list(aicp->ai_courage);
1034
1035         required_string("$patience:");
1036         parse_float_list(aicp->ai_patience);
1037 }
1038
1039 void parse_aitbl()
1040 {
1041         // open localization
1042         lcl_ext_open();
1043
1044         read_file_text("ai.tbl");
1045
1046         reset_parse();
1047
1048         Num_ai_classes = 0;
1049
1050         required_string("#AI Classes");
1051
1052         while (required_string_either("#End", "$Name:")) {
1053                 Assert( Num_ai_classes < MAX_AI_CLASSES);
1054
1055                 parse_ai_class();
1056
1057                 Num_ai_classes++;
1058         }
1059
1060         // close localization
1061         lcl_ext_close();
1062 }
1063
1064 LOCAL int ai_inited = 0;
1065
1066 //========================= BOOK-KEEPING FUNCTIONS =======================
1067
1068 // Called once at game start-up
1069 void ai_init()
1070 {
1071         if ( !ai_inited )       {
1072                 // Do the first time initialization stuff here
1073                 int     rval;
1074
1075                 if ((rval = setjmp(parse_abort)) != 0) {
1076                         Error(LOCATION, "Error parsing 'ai.tbl'\r\nError code = %i.\r\n", rval);
1077                 } else {                        
1078                         parse_aitbl();                  
1079                 }
1080
1081                 ai_inited = 1;
1082         }
1083
1084         init_semirand();
1085         
1086         ai_level_init();
1087 }
1088
1089 // this inits the ai.  You should be able to call this between
1090 // levels to reset everything.
1091 void ai_level_init()
1092 {
1093         int i;
1094  
1095         // Do the stuff to reset all ai stuff here
1096         for (i=0; i<MAX_AI_INFO ; i++) {
1097                 Ai_info[i].shipnum = -1;
1098         }
1099         Ai_goal_signature = 0;
1100         Ai_friendly_rearm_timestamp = timestamp(-1);
1101         Ai_hostile_rearm_timestamp = timestamp(-1);
1102         Ai_neutral_rearm_timestamp = timestamp(-1);
1103         Ai_traitor_rearm_timestamp = timestamp(-1);
1104
1105         // clear out the stuff needed for AI firing powerful secondary weapons
1106         ai_init_secondary_info();
1107
1108         Ai_last_arrive_path=0;
1109 }
1110
1111 // BEGIN STEALTH
1112 // -----------------------------------------------------------------------------
1113 // Check if object is a stealth ship
1114 int is_object_stealth_ship(object* objp)
1115 {
1116         if (objp->type == OBJ_SHIP) {
1117                 if (Ship_info[Ships[objp->instance].ship_info_index].flags & SIF_STEALTH) {
1118                         return 1;
1119                 }
1120         }
1121
1122         // not stealth ship
1123         return 0;
1124 }
1125
1126 // -----------------------------------------------------------------------------
1127 // Init necessary ai info for new stealth target
1128 void init_ai_stealth_info(ai_info *aip, object *stealth_objp)
1129 {
1130         Assert(is_object_stealth_ship(stealth_objp));
1131
1132         // set necessary ai info for new stealth target
1133         aip->stealth_last_pos = stealth_objp->pos;
1134         aip->stealth_velocity = stealth_objp->phys_info.vel;
1135         aip->stealth_last_visible_stamp = timestamp();
1136 }
1137
1138 // -----------------------------------------------------------------------------
1139 // Check whether Pl_objp can see a stealth ship object
1140 #define STEALTH_INVISIBLE                       0
1141 #define STEALTH_VISIBLE                         1
1142 #define STEALTH_FULLY_TARGETABLE        2
1143
1144 float get_skill_stealth_dist_scaler()
1145 {
1146         // return dist scaler based on skill level
1147         switch (Game_skill_level) {
1148         case 0: // very easy
1149                 return 0.65f;
1150
1151         case 1: // easy
1152                 return 0.9f;
1153
1154         case 2: // medium
1155                 return 1.0f;
1156
1157         case 3: // hard
1158                 return 1.1f;
1159
1160         case 4: // insane
1161                 return 1.3f;
1162
1163         default:
1164                 Int3();
1165         }
1166
1167         return 1.0f;
1168 }
1169
1170 float get_skill_stealth_dot_scaler()
1171 {
1172         // return multiplier on dot based on skill level
1173         switch (Game_skill_level) {
1174         case 0: // very easy
1175                 return 1.3f;
1176
1177         case 1: // easy
1178                 return 1.1f;
1179
1180         case 2: // medium
1181                 return 1.0f;
1182
1183         case 3: // hard
1184                 return 0.9f;
1185
1186         case 4: // insane
1187                 return 0.7f;
1188
1189         default:
1190                 Int3();
1191         }
1192
1193         return 1.0f;
1194 }
1195
1196 int ai_is_stealth_visible(object *viewer_objp, object *stealth_objp)
1197 {
1198         ship *shipp;
1199         vector vec_to_stealth;
1200         float dot_to_stealth, dist_to_stealth, max_stealth_dist;
1201
1202         Assert(stealth_objp->type == OBJ_SHIP);
1203         shipp = &Ships[stealth_objp->instance];
1204         Assert(viewer_objp->type == OBJ_SHIP);
1205
1206         // check if stealth ship
1207         Assert(Ship_info[shipp->ship_info_index].flags & SIF_STEALTH);
1208
1209         // check if in neb and below awac level for visible
1210         if ( !ship_is_visible_by_team(stealth_objp->instance, Ships[viewer_objp->instance].team) ) {
1211                 vm_vec_sub(&vec_to_stealth, &stealth_objp->pos, &viewer_objp->pos);
1212                 dist_to_stealth = vm_vec_mag_quick(&vec_to_stealth);
1213                 dot_to_stealth = vm_vec_dotprod(&viewer_objp->orient.fvec, &vec_to_stealth) / dist_to_stealth;
1214
1215                 // get max dist at which stealth is visible
1216                 max_stealth_dist = get_skill_stealth_dist_scaler() * STEALTH_MAX_VIEW_DIST;
1217
1218                 // now check if within view frustrum
1219                 float needed_dot_to_stealth;
1220                 if (dist_to_stealth < 100) {
1221                         needed_dot_to_stealth = 0.0f;
1222                 } else {
1223                         needed_dot_to_stealth = get_skill_stealth_dot_scaler() * float(STEALTH_VIEW_CONE_DOT) * (dist_to_stealth / max_stealth_dist);
1224                 }
1225                 if (dot_to_stealth > needed_dot_to_stealth) {
1226                         if (dist_to_stealth < max_stealth_dist) {
1227                                 return STEALTH_VISIBLE;
1228                         }
1229                 }
1230
1231                 // not within frustrum
1232                 return STEALTH_INVISIBLE;
1233         }
1234
1235         // visible by awacs level
1236         return STEALTH_FULLY_TARGETABLE;
1237 }
1238
1239 // END STEALTH
1240
1241 //      Compute dot product of direction vector and forward vector.
1242 //      Direction vector is vector from one object to other object.
1243 //      Forward vector is the forward vector of the ship.
1244 //      If from_dot == NULL, don't fill it in.
1245 float compute_dots(object *objp, object *other_objp, float *to_dot, float *from_dot)
1246 {
1247         vector  v2o;
1248         float           dist;
1249
1250         dist = vm_vec_normalized_dir(&v2o, &other_objp->pos, &objp->pos);
1251
1252         *to_dot = vm_vec_dot(&objp->orient.fvec, &v2o);
1253
1254         if (from_dot != NULL)
1255                 *from_dot = - vm_vec_dot(&other_objp->orient.fvec, &v2o);
1256
1257         return dist;
1258 }
1259
1260 // -----------------------------------------------------------------------------
1261 // update estimated stealth info
1262 // this is a "cheat" update
1263 // error increases with time not seen, true distance away, dot to enemey
1264 // this is done only if we can not see the stealth target
1265 // need to infer its position either by weapon fire pos or last know pos
1266 void update_ai_stealth_info_with_error(ai_info *aip/*, int no_error*/)
1267 {
1268         object *ship;
1269         object *stealth_objp;
1270         /*
1271         float error_time_mult, error_dist_mult, error_dot_mult, error_mult;
1272         float pos_error, vel_error;
1273         vector error_vec, vec_to_stealth;
1274         float dist_to_stealth, dot_to_stealth;
1275         float delta_time, delta_capped;
1276         */
1277
1278         // make sure I am targeting a stealth ship
1279         Assert( is_object_stealth_ship(&Objects[aip->target_objnum]) );
1280         stealth_objp = &Objects[aip->target_objnum];
1281
1282         // my_ship
1283         ship = &Objects[Ships[aip->shipnum].objnum];
1284
1285         // if update is due to weapon fire, get exact stealth position
1286 //      if (no_error) {
1287         aip->stealth_last_pos = stealth_objp->pos;
1288         aip->stealth_velocity = stealth_objp->phys_info.vel;
1289         aip->stealth_last_visible_stamp = timestamp();
1290 //              return;
1291 //      }
1292 /*
1293         // get time since last seen
1294         delta_time = 0.001f * (timestamp() - aip->stealth_last_visible_stamp);
1295
1296         // we don't want our "cheat" guess to more off than what we would get from extrapolating from last visible
1297         // only update if stealth info is "old"
1298         if ( (delta_time) < 0.5 ) {
1299                 return;
1300         }
1301
1302         // find vec_to_stealth and dist
1303         vm_vec_sub(&vec_to_stealth, &stealth_objp->pos, &ship->pos);
1304         dist_to_stealth = vm_vec_normalize_quick(&vec_to_stealth);
1305         dot_to_stealth = vm_vec_dotprod(&vec_to_stealth, &ship->orient.fvec);
1306
1307         // put cap on time
1308         delta_capped = delta_time;
1309         if (delta_time > 5.0) {
1310                 delta_capped = 5.0f;
1311         }
1312
1313         // erorr_time_mult (for 0-5) -> (1-6)
1314         error_time_mult = (1.0f + delta_capped);
1315
1316         // error_dot_mult (-1 to 1) -> (1-3)
1317         error_dot_mult = (2 - dot_to_stealth);
1318
1319         // error_dist_mult (0-1000+) -> (1-4)
1320         error_dist_mult = dist_to_stealth * 4.0f * 0.001f;
1321         if (error_dist_mult < 1) {
1322                 error_dist_mult = 1.0f;
1323         } else if (error_dist_mult > 4) {
1324                 error_dist_mult = 4.0f;
1325         }
1326
1327         // multiply error out
1328         error_mult = error_time_mult * error_dot_mult * error_dist_mult;
1329
1330         float base_pos_error = 10;
1331         float base_vel_error = 2;
1332
1333         // find the position and velocity error magnitude;
1334         pos_error = base_pos_error * error_mult;
1335         vel_error = base_vel_error * error_mult;
1336
1337         // get an error that changes slowly over time
1338         static_randvec( ((int)aip ^ (Missiontime >> 18)) & 7, &error_vec);
1339         vm_vec_zero(&error_vec);
1340
1341         // update pos and vel with error
1342         vm_vec_scale_add(&aip->stealth_velocity, &stealth_objp->phys_info.vel, &error_vec, vel_error);
1343
1344         // revise last "known" position to arrive at last pos with given error
1345         vm_vec_scale_add(&aip->stealth_last_pos, &stealth_objp->pos, &error_vec, pos_error);
1346         vm_vec_scale_add2(&aip->stealth_last_pos, &aip->stealth_velocity, -(0.001f * delta_time));
1347         */
1348 }
1349
1350 //      Update danger_weapon_objnum and signature in ai_info to say this weapon is to be avoided.
1351 void ai_update_danger_weapon(int attacked_objnum, int weapon_objnum)
1352 {
1353         object  *objp, *weapon_objp;
1354         ai_info *aip;
1355         float           old_dist, new_dist;
1356         float           old_dot, new_dot;
1357         object  *old_weapon_objp = NULL;
1358
1359         if ((attacked_objnum == -1) || (weapon_objnum == -1)) {
1360                 return;
1361         }
1362
1363         objp = &Objects[attacked_objnum];
1364
1365         // AL 2-24-98: If this isn't a ship, we don't need to worry about updating weapon_objnum (ie it would be
1366         //                                      an asteroid or bomb).
1367         if ( objp->type != OBJ_SHIP ) {
1368                 return;
1369         }
1370
1371         weapon_objp = &Objects[weapon_objnum];
1372
1373         aip = &Ai_info[Ships[objp->instance].ai_index];
1374
1375         // if my taraget is a stealth ship and is not visible
1376         if (aip->target_objnum >= 0) {
1377                 if ( is_object_stealth_ship(&Objects[aip->target_objnum]) ) {
1378                         if ( ai_is_stealth_visible(objp, &Objects[aip->target_objnum]) == STEALTH_INVISIBLE ) {
1379                                 // and the weapon is coming from that stealth ship
1380                                 if (weapon_objp->parent == aip->target_objnum) {
1381                                         // update my position estimate for stealth ship
1382                                         update_ai_stealth_info_with_error(aip/*, 1*/);
1383                                 }
1384                         }
1385                 }
1386         }
1387
1388         if (aip->danger_weapon_objnum != -1) {
1389                 old_weapon_objp = &Objects[aip->danger_weapon_objnum];
1390                 if ((old_weapon_objp->type == OBJ_WEAPON) && (old_weapon_objp->signature == aip->danger_weapon_signature)) {
1391                         ;
1392                 } else {
1393                         aip->danger_weapon_objnum = -1;
1394                 }
1395         }
1396
1397         new_dist = compute_dots(weapon_objp, objp, &new_dot, NULL);
1398
1399         if (aip->danger_weapon_objnum == -1) {
1400                 if (new_dist < 1500.0f) {
1401                         if (new_dot > 0.5f) {
1402                                 aip->danger_weapon_objnum = weapon_objnum;
1403                                 aip->danger_weapon_signature = weapon_objp->signature;
1404                         }
1405                 }
1406         } else {
1407                 Assert(old_weapon_objp != NULL);
1408                 old_dist = compute_dots(old_weapon_objp, objp, &old_dot, NULL);
1409         
1410                 if (old_dot < 0.5f) {
1411                         aip->danger_weapon_objnum = -1;
1412                         old_dist = 9999.9f;
1413                 }
1414
1415                 if ((new_dot > 0.5f) && (new_dot > old_dot-0.01f)) {
1416                         if (new_dist < old_dist) {
1417                                 aip->danger_weapon_objnum = weapon_objnum;
1418                                 aip->danger_weapon_signature = weapon_objp->signature;
1419                         }
1420                 }
1421         }
1422 }
1423
1424 //      If rvec != NULL, use it to match bank by calling vm_matrix_interpolate.
1425 //      (rvec defaults to NULL)
1426 void ai_turn_towards_vector(vector *dest, object *objp, 
1427                                                                          float frametime, float turn_time, vector *slide_vec, vector *rel_pos, float bank_override, int flags, vector *rvec)
1428 {
1429         //matrix        goal_orient;
1430         matrix  curr_orient;
1431         vector  vel_in, vel_out, desired_fvec, src;
1432         float           delta_time;
1433         physics_info    *pip;
1434         vector  vel_limit, acc_limit;
1435         float           delta_bank;
1436
1437         //      Don't allow a ship to turn if it has no engine strength.
1438         // AL 3-12-98: objp may not always be a ship!
1439         if ( objp->type == OBJ_SHIP ) {
1440                 if (ship_get_subsystem_strength(&Ships[objp->instance], SUBSYSTEM_ENGINE) <= 0.0f)
1441                         return;
1442         }
1443                         
1444         //nprintf(("AI", "Ship %s turning towards point %7.3f %7.3f %7.3f\n", Ships[objp->instance].ship_name, dest->x, dest->y, dest->z));
1445         pip = &objp->phys_info;
1446
1447         vel_in = pip->rotvel;
1448         curr_orient = objp->orient;
1449         delta_time = flFrametime;
1450
1451         Assert(turn_time > 0.0f);
1452         
1453         //      Scale turn_time based on skill level and team.
1454         if (!(flags & AITTV_FAST)){
1455                 if (objp->type == OBJ_SHIP){
1456                         if (Ships[objp->instance].team != Ships[Player_obj->instance].team){
1457                                 turn_time *= Turn_time_skill_level_scale[Game_skill_level];
1458                         }
1459                 }
1460         }
1461
1462         //      Set max turn rate.
1463         vel_limit.x = 2*PI/turn_time;
1464         vel_limit.y = 2*PI/turn_time;
1465         vel_limit.z = 2*PI/turn_time;
1466
1467         //      Set rate at which ship can accelerate to its rotational velocity.
1468         //      For now, weapons just go much faster.
1469         acc_limit = vel_limit;
1470         if (objp->type == OBJ_WEAPON)
1471                 vm_vec_scale(&acc_limit, 8.0f);
1472
1473         src = objp->pos;
1474
1475         if (rel_pos != NULL) {
1476                 vector  gun_point;
1477                 vm_vec_unrotate(&gun_point, rel_pos, &objp->orient);
1478                 vm_vec_add2(&src, &gun_point);
1479         }
1480
1481         vm_vec_normalized_dir(&desired_fvec, dest, &src);
1482
1483         //      Since ship isn't necessarily moving in the direction it's pointing, sometimes it's better
1484         //      to be moving towards goal rather than just pointing.  So, if slide_vec is !NULL, try to
1485         //      make ship move towards goal, not point at goal.
1486         if (slide_vec != NULL) {
1487                 vm_vec_add2(&desired_fvec, slide_vec);
1488                 vm_vec_normalize(&desired_fvec);
1489         }
1490
1491         //      Should be more general case here.  Currently, anything that is not a weapon will bank when it turns.
1492         if (objp->type == OBJ_WEAPON)
1493                 delta_bank = 0.0f;
1494         else if ((bank_override) && (Ships[objp->instance].team & opposing_team_mask(Player_ship->team))) {     //      Theoretically, this will only happen for Shivans.
1495                 delta_bank = bank_override;
1496                 //nprintf(("AI", "%i: %7.3f\n", Framecount, bank_override));
1497         } else {
1498                 delta_bank = vm_vec_dot(&curr_orient.rvec, &objp->last_orient.rvec);
1499                 delta_bank = 100.0f * (1.0f - delta_bank);
1500                 if (vm_vec_dot(&objp->last_orient.fvec, &objp->orient.rvec) < 0.0f)
1501                         delta_bank = -delta_bank;
1502
1503                 //nprintf(("AI", "%s: Frame %i: delta bank = %7.3f\n", Ships[objp->instance].ship_name, Framecount, delta_bank));
1504         }
1505
1506         //      Dave Andsager: The non-indented lines here are debug code to help you track down the problem in the physics
1507         //      that is causing ships to inexplicably rotate very far.  If you hit the Int3(), set the next statement to be
1508         //      the one marked "HERE".  (Do this clicking the cursor there, then right clicking.  Choose the right option.)
1509         //      This will allow you to rerun vm_forward_interpolate() with the values that caused the error.
1510         //      Note, you'll need to enable the Int3() about ten lines below.
1511 #ifndef NDEBUG
1512 vector tvec = objp->orient.fvec;
1513 vector  vel_in_copy;
1514 matrix  objp_orient_copy;
1515
1516 vel_in_copy = vel_in;
1517 objp_orient_copy = objp->orient;
1518
1519 vel_in = vel_in_copy;   //      HERE
1520 objp->orient = objp_orient_copy;
1521 #endif
1522         if (rvec != NULL) {
1523                 matrix  out_orient, goal_orient;
1524
1525                 vm_vector_2_matrix(&goal_orient, &desired_fvec, NULL, rvec);
1526                 vm_matrix_interpolate(&goal_orient, &curr_orient, &vel_in, delta_time, &out_orient, &vel_out, &vel_limit, &acc_limit);
1527                 objp->orient = out_orient;
1528         } else {
1529                 vm_forward_interpolate(&desired_fvec, &curr_orient, &vel_in, delta_time, delta_bank, &objp->orient, &vel_out, &vel_limit, &acc_limit);
1530         }
1531 #ifndef NDEBUG
1532 if (!((objp->type == OBJ_WEAPON) && (Weapon_info[Weapons[objp->instance].weapon_info_index].subtype == WP_MISSILE))) {
1533         if (delta_time < 0.25f && vm_vec_dot(&objp->orient.fvec, &tvec) < 0.1f)
1534                 Int3(); //      Get Andsager.  A ship has turned too far in one frame.
1535 }
1536 #endif
1537
1538         pip->rotvel = vel_out;
1539 }
1540
1541 void init_ship_info()
1542 {
1543         int     i;
1544
1545         if (Ship_info_inited)
1546                 return;
1547
1548         for (i=0; i<MAX_SHIP_TYPES; i++) {
1549                 Ship_info[i].min_speed = - Ship_info[i].max_rear_vel;
1550                 Ship_info[i].max_accel = Ship_info[i].max_vel.z;
1551         }
1552
1553         Ship_info_inited = 1;
1554
1555 }
1556
1557 //      Set aip->target_objnum to objnum
1558 //      Update aip->previous_target_objnum.
1559 //      If new target (objnum) is different than old target, reset target_time.
1560 int set_target_objnum(ai_info *aip, int objnum)
1561 {
1562 /*
1563         char    old_name[32], new_name[32];
1564
1565         if (!timestamp_elapsed(aip->ok_to_target_timestamp))
1566                 return aip->target_objnum;
1567
1568         if (Player_ship && (Ships[aip->shipnum].team == Player_ship->team)) {
1569                 if (aip->target_objnum == -1)
1570                         strcpy(old_name, "none");
1571                 else
1572                         strcpy(old_name, Ships[Objects[aip->target_objnum].instance].ship_name);
1573
1574                 if (objnum == -1)
1575                         strcpy(new_name, "none");
1576                 else
1577                         strcpy(new_name, Ships[Objects[objnum].instance].ship_name);
1578
1579                 nprintf(("AI", "Ship %s changing target from %s to %s\n", Ships[aip->shipnum].ship_name, old_name, new_name));
1580         }
1581 */
1582
1583         // AL 2-25-97: Ensure that a protected ship isn't being set as a target (for non-players only)
1584         /*
1585         if ( objnum >= 0 ) {
1586                 if ( !(Objects[Ships[aip->shipnum].objnum].flags & OF_PLAYER_SHIP) ) {
1587                         if ( Objects[objnum].flags & OF_PROTECTED ) {
1588                                 // AL 2-26-97: removing Int3() until issue with setting OF_PROTECTED in ai_set_attack_subsystem()
1589                                 //Int3();                                                               // this should not happen
1590                                 return aip->target_objnum;              // don't change targets
1591                         }
1592                 }
1593         }
1594         */
1595
1596         if ((aip != Player_ai) && (!timestamp_elapsed(aip->ok_to_target_timestamp))) {
1597                 return aip->target_objnum;
1598         }
1599
1600         if (aip->target_objnum == objnum) {
1601                 aip->previous_target_objnum = aip->target_objnum;
1602         } else {
1603                 aip->previous_target_objnum = aip->target_objnum;
1604
1605                 // ignore this assert if a multiplayer observer
1606                 if((Game_mode & GM_MULTIPLAYER) && (aip == Player_ai) && (Player_obj->type == OBJ_OBSERVER)){
1607                 } else {
1608                         Assert(objnum != Ships[aip->shipnum].objnum);   //      make sure not targeting self
1609                 }
1610
1611                 // if stealth target, init ai_info for stealth
1612                 if ( (objnum > 0) && is_object_stealth_ship(&Objects[objnum]) ) {
1613                         init_ai_stealth_info(aip, &Objects[objnum]);
1614                 }
1615
1616                 aip->target_objnum = objnum;
1617                 aip->target_time = 0.0f;
1618                 aip->target_signature = Objects[objnum].signature;
1619                 // clear targeted subsystem
1620                 set_targeted_subsys(aip, NULL, -1);
1621         }
1622         
1623         return aip->target_objnum;
1624 }
1625
1626 int ai_select_primary_weapon(object *objp, object *other_objp, int flags);
1627
1628 //      Make new_subsys the targeted subsystem of ship *aip.
1629 ship_subsys *set_targeted_subsys(ai_info *aip, ship_subsys *new_subsys, int parent_objnum)
1630 {
1631         Assert(aip != NULL);
1632
1633         aip->last_subsys_target = aip->targeted_subsys;
1634         aip->targeted_subsys = new_subsys;
1635         aip->targeted_subsys_parent = parent_objnum;
1636
1637         if ( new_subsys ) {
1638                 // Make new_subsys target
1639                 if (new_subsys->system_info->type == SUBSYSTEM_ENGINE) {
1640                         if ( aip != Player_ai ) {
1641                                 ai_select_primary_weapon(&Objects[Ships[aip->shipnum].objnum], &Objects[parent_objnum], WIF_PUNCTURE);
1642                                 ship_primary_changed(&Ships[aip->shipnum]);     // AL: maybe send multiplayer information when AI ship changes primaries
1643                         }
1644                 }
1645
1646                 if ( aip == Player_ai ) {
1647                         hud_lock_reset(0.5f);
1648                 }
1649
1650         } else {
1651                 // Cleanup any subsys path information if it exists
1652                 ai_big_subsys_path_cleanup(aip);
1653         }
1654         
1655         return aip->targeted_subsys;
1656 }                                                                                         
1657
1658 // called to init the data for single ai object.  At this point,
1659 // the ship and the object and the ai_info are are correctly
1660 // linked together. Ai_info[ai_index].shipnum is the only valid field 
1661 // in ai_info.
1662 //      This is called right when the object is parsed, so you can't assume much
1663 //      has been initialized.  For example, wings, waypoints, goals are probably
1664 //      not yet loaded. --MK, 10/8/96
1665 void ai_object_init(object * obj, int ai_index)
1666 {
1667         ai_info *aip;
1668         Assert(ai_index >= 0 && ai_index < MAX_AI_INFO);
1669
1670         aip = &Ai_info[ai_index];
1671
1672         aip->type = 0;          //      0 means not in use.
1673         aip->wing = -1;         //      Member of what wing? -1 means none.
1674         aip->ai_class = Ship_info[Ships[obj->instance].ship_info_index].ai_class;
1675         aip->behavior = AIM_NONE;
1676 }
1677
1678 //      If *aip is docked, set max acceleration to A->mass/(A->mass + B->mass) where A is *aip and B is dock object
1679 void adjust_accel_for_docking(ai_info *aip)
1680 {
1681         if (aip->dock_objnum != -1) {
1682                 object  *obj2p = &Objects[aip->dock_objnum];
1683                 object  *obj1p;
1684
1685                 obj1p = &Objects[Ships[aip->shipnum].objnum];
1686
1687                 if (obj2p->signature == aip->dock_signature) {
1688                         float   ratio;
1689
1690                         ratio = obj1p->phys_info.mass / (obj1p->phys_info.mass + obj2p->phys_info.mass);
1691
1692                         // put cap on how much ship can slow down
1693                         if (ratio < 0.8) {
1694                                 ratio = 0.8f;
1695                         }
1696
1697                         if (AI_ci.forward > ratio) {
1698                                 AI_ci.forward = ratio;
1699                         }
1700                 }
1701         }
1702 }
1703
1704 // -------------------------------------------------------------------
1705 void accelerate_ship(ai_info *aip, float accel)
1706 {
1707         aip->prev_accel = accel;
1708         AI_ci.forward = accel;
1709         adjust_accel_for_docking(aip);
1710 }
1711
1712 //      --------------------------------------------------------------------------
1713 void change_acceleration(ai_info *aip, float delta_accel)
1714 {
1715         float   new_accel;
1716
1717         if (delta_accel < 0.0f) {
1718                 if (aip->prev_accel > 0.0f)
1719                         aip->prev_accel = 0.0f;
1720         } else if (aip->prev_accel < 0.0f)
1721                 aip->prev_accel = 0.0f;
1722
1723         new_accel = aip->prev_accel + delta_accel * flFrametime;
1724
1725         if (new_accel > 1.0f)
1726                 new_accel = 1.0f;
1727         else if (new_accel < -1.0f)
1728                 new_accel = -1.0f;
1729         
1730         aip->prev_accel = new_accel;
1731
1732         AI_ci.forward = new_accel;
1733         adjust_accel_for_docking(aip);
1734 }
1735
1736 void set_accel_for_target_speed(object *objp, float tspeed)
1737 {
1738         float   max_speed;
1739         ai_info *aip;
1740
1741         aip = &Ai_info[Ships[objp->instance].ai_index];
1742
1743         max_speed = Ships[objp->instance].current_max_speed;
1744
1745         AI_ci.forward = tspeed/max_speed;
1746         aip->prev_accel = AI_ci.forward;
1747
1748         adjust_accel_for_docking(aip);
1749 }
1750
1751 //      Stuff perim_point with a point on the perimeter of the sphere defined by object *objp
1752 //      on the vector from the center of *objp through the point *vp.
1753 void project_point_to_perimeter(vector *perim_point, vector *pos, float radius, vector *vp)
1754 {
1755         vector  v1;
1756         float           mag;
1757
1758         vm_vec_sub(&v1, vp, pos);
1759         mag = vm_vec_mag(&v1);
1760
1761         if (mag == 0.0f) {
1762                 Warning(LOCATION, "projectable point is at center of sphere.");
1763                 vm_vec_make(&v1, 0.0f, radius, 0.0f);
1764         } else {
1765                 vm_vec_normalize(&v1);
1766                 vm_vec_scale(&v1, 1.1f * radius + 10.0f);
1767         }
1768
1769         vm_vec_add2(&v1, pos);
1770         *perim_point = v1;
1771 }
1772
1773 //      Stuff tan1 with tangent point on sphere.  tan1 is point nearer to *p1
1774 //      *p0 is point through which tangents pass.
1775 //      *centerp is center of sphere.
1776 //      *p1 is another point in space to define the plane in which tan1, tan2 reside.
1777 //      radius is the radius of the sphere.
1778 //      Note, this is a very approximate function just for AI.
1779 //      Note also: On 12/26/96, p1 is used to define the plane perpendicular to that which
1780 //      contains the tangent point.
1781 void get_tangent_point(vector *tan1, vector *p0, vector *centerp, vector *p1, float radius)
1782 {
1783         vector  dest_vec, v2c, perp_vec, temp_vec, v2;
1784         float           dist, ratio;
1785
1786         //      Detect condition of point inside sphere.
1787         if (vm_vec_dist(p0, centerp) < radius)
1788                 project_point_to_perimeter(tan1, centerp, radius, p0);
1789         else {
1790                 vm_vec_normalized_dir(&v2c, centerp, p0);
1791
1792                 //      Compute perpendicular vector using p0, centerp, p1
1793                 vm_vec_normal(&temp_vec, p0, centerp, p1);
1794                 vm_vec_sub(&v2, centerp, p0);
1795                 vm_vec_cross(&perp_vec, &temp_vec, &v2);
1796
1797                 vm_vec_normalize(&perp_vec);
1798
1799                 dist = vm_vec_dist_quick(p0, centerp);
1800                 ratio = dist / radius;
1801
1802                 if (ratio < 2.0f)
1803                         vm_vec_scale_add(&dest_vec, &perp_vec, &v2c, ratio-1.0f);
1804                 else
1805                         vm_vec_scale_add(&dest_vec, &v2c, &perp_vec, (1.0f + 1.0f/ratio));
1806
1807                 vm_vec_scale_add(tan1, p0, &dest_vec, dist + radius);
1808         }
1809 }
1810
1811 //      --------------------------------------------------------------------------
1812 //      Given an object and a point, turn towards the point, resulting in
1813 // approach behavior.
1814 void turn_towards_point(object *objp, vector *point, vector *slide_vec, float bank_override)
1815 {
1816         ai_info *aip;
1817         aip = &Ai_info[Ships[Pl_objp->instance].ai_index];
1818         
1819         // check if in formation and if not leader, don't change rotvel.z (bank to match leader elsewhere)
1820         if (aip->ai_flags & AIF_FORMATION) {
1821                 if (&Objects[aip->goal_objnum] != objp) {
1822                         float rotvel_z = objp->phys_info.rotvel.z;
1823                         ai_turn_towards_vector(point, objp, flFrametime, Ship_info[Ships[objp->instance].ship_info_index].srotation_time, slide_vec, NULL, bank_override, 0);
1824                         objp->phys_info.rotvel.z = rotvel_z;
1825                 }
1826         } else {
1827                 // normal turn
1828                 ai_turn_towards_vector(point, objp, flFrametime, Ship_info[Ships[objp->instance].ship_info_index].srotation_time, slide_vec, NULL, bank_override, 0);
1829         }
1830 }
1831
1832 //      --------------------------------------------------------------------------
1833 //      Given an object and a point, turn away from the point, resulting in avoidance behavior.
1834 //      Note: Turn away at full speed, not scaled down by skill level.
1835 void turn_away_from_point(object *objp, vector *point, float bank_override)
1836 {
1837         vector  opposite_point;
1838
1839         vm_vec_sub(&opposite_point, &objp->pos, point);
1840         vm_vec_add2(&opposite_point, &objp->pos);
1841
1842         ai_turn_towards_vector(&opposite_point, objp, flFrametime, Ship_info[Ships[objp->instance].ship_info_index].srotation_time, NULL, NULL, bank_override, AITTV_FAST);
1843 }
1844
1845
1846 //      --------------------------------------------------------------------------
1847 //      Given an object and a point, turn tangent to the point, resulting in
1848 // a circling behavior.
1849 //      Make object *objp turn around the point *point with a radius of radius.
1850 //      Note that this isn't the same as following a circle of radius radius with
1851 //      center *point, but it should be adequate.
1852 //      Note that if you want to circle an object without hitting it, you should use
1853 //      about twice that object's radius for radius, else you'll certainly bump into it.
1854 //      Return dot product to goal point.
1855 float turn_towards_tangent(object *objp, vector *point, float radius)
1856 {
1857         vector  vec_to_point;
1858         vector  goal_point;
1859         vector  perp_point;                             //      point radius away from *point on vector to objp->pos
1860         vector  up_vec, perp_vec;
1861
1862         vm_vec_normalized_dir(&vec_to_point, point, &objp->pos);
1863         vm_vec_crossprod(&up_vec, &vec_to_point, &objp->orient.fvec);
1864         vm_vec_crossprod(&perp_vec, &vec_to_point, &up_vec);
1865
1866         vm_vec_scale_add(&perp_point, point, &vec_to_point, -radius);
1867         if (vm_vec_dot(&objp->orient.fvec, &perp_vec) > 0.0f) {
1868                 vm_vec_scale_add(&goal_point, &perp_point, &perp_vec, radius);
1869         } else {
1870                 vm_vec_scale_add(&goal_point, &perp_point, &perp_vec, -radius);
1871         }
1872
1873 //      Ai_info[Ships[objp->instance].ai_index].goal_point = goal_point;
1874         turn_towards_point(objp, &goal_point, NULL, 0.0f);
1875
1876         vector  v2g;
1877
1878         vm_vec_normalized_dir(&v2g, &goal_point, &objp->pos);
1879         return vm_vec_dot(&objp->orient.fvec, &v2g);
1880 }
1881
1882 float turn_toward_tangent_with_axis(object *objp, object *center_objp, float radius)
1883 {
1884         vector r_vec, theta_vec;
1885         vector center_vec, vec_on_cylinder, sph_r_vec;
1886         float center_obj_z;
1887
1888         // find closest z of center objp
1889         vm_vec_sub(&sph_r_vec, &objp->pos, &center_objp->pos);
1890         center_obj_z = vm_vec_dotprod(&sph_r_vec, &center_objp->orient.fvec);
1891
1892         // find pt on axis with closest z
1893         vm_vec_scale_add(&center_vec, &center_objp->pos, &center_objp->orient.fvec, center_obj_z);
1894
1895         // get r_vec
1896         vm_vec_sub(&r_vec, &objp->pos, &center_vec);
1897 //      float r_mag = vm_vec_normalize_quick(&r_vec);
1898 //      mprintf(("cur_r: %.1f, desired_r: %.1f\n", r_mag, radius));
1899         Assert( (vm_vec_dotprod(&r_vec, &center_objp->orient.fvec) < 0.0001));
1900
1901         // get theta vec - perp to r_vec and z_vec
1902         vm_vec_crossprod(&theta_vec, &center_objp->orient.fvec, &r_vec);
1903
1904 #ifndef NDEBUG
1905         float mag = vm_vec_normalize(&theta_vec);
1906         Assert(mag > 0.9999 && mag < 1.0001);
1907 #endif
1908
1909         vector temp;
1910         vm_vec_crossprod(&temp, &r_vec, &theta_vec);
1911
1912 #ifndef NDEBUG
1913         float dot = vm_vec_dotprod(&temp, &center_objp->orient.fvec);
1914         Assert( dot >0.9999 && dot < 1.0001);
1915 #endif
1916
1917         // find pt on clylinder with closest z
1918         vm_vec_scale_add(&vec_on_cylinder, &center_vec, &r_vec, radius);
1919
1920         vector goal_pt, v2g;
1921         vm_vec_scale_add(&goal_pt, &vec_on_cylinder, &theta_vec, radius);
1922
1923 //      Ai_info[Ships[objp->instance].ai_index].goal_point = goal_pt;
1924         turn_towards_point(objp, &goal_pt, NULL, 0.0f);
1925
1926         vm_vec_normalized_dir(&v2g, &goal_pt, &objp->pos);
1927         return vm_vec_dot(&objp->orient.fvec, &v2g);
1928 }
1929
1930 //      Returns a point radius units away from *point that *objp should turn towards to orbit *point
1931 void get_tangent_point(vector *goal_point, object *objp, vector *point, float radius)
1932 {
1933         vector  vec_to_point;
1934         vector  perp_point;                             //      point radius away from *point on vector to objp->pos
1935         vector  up_vec, perp_vec;
1936
1937         vm_vec_normalized_dir(&vec_to_point, point, &objp->pos);
1938         vm_vec_crossprod(&up_vec, &vec_to_point, &objp->orient.fvec);
1939         vm_vec_crossprod(&perp_vec, &vec_to_point, &up_vec);
1940         vm_vec_normalize(&perp_vec);
1941
1942         vm_vec_scale_add(&perp_point, point, &vec_to_point, -radius);
1943
1944         if (vm_vec_dot(&objp->orient.fvec, &perp_vec) > 0.0f) {
1945                 vm_vec_scale_add(goal_point, &perp_point, &perp_vec, radius);
1946         } else {
1947                 vm_vec_scale_add(goal_point, &perp_point, &perp_vec, -radius);
1948         }
1949 }
1950
1951 int     Player_attacking_enabled = 1;
1952
1953 // -----------------------------------------------------------------------------
1954 // Determine whether an object is targetable within a nebula
1955 int object_is_targetable(object *target, ship *viewer)
1956 {
1957         int stealth_ship = 0;
1958
1959         // if target is ship, check if visible by team
1960         if (target->type == OBJ_SHIP) {
1961                 stealth_ship = (Ship_info[Ships[target->instance].ship_info_index].flags & SIF_STEALTH);
1962                 if ( ship_is_visible_by_team(target->instance, viewer->team) == 1) {
1963                         return 1;
1964                 }
1965         }
1966
1967         // for AI partially targetable works as fully targetable, except for stealth ship
1968         if (stealth_ship) {
1969                 // if not team targetable, check if within frustrum
1970                 if ( ai_is_stealth_visible(&Objects[viewer->objnum], target) == STEALTH_VISIBLE ) {
1971                         return 1;
1972                 } else {
1973                         return 0;
1974                 }
1975         }
1976
1977         // if not fully targetable by team, check awacs level with viewer
1978         // allow targeting even if only only partially targetable to player
1979         float radar_return = awacs_get_level(target, viewer);
1980         if ( radar_return > 0.4 ) {
1981                 return 1;
1982         } else {
1983                 return 0;
1984         }
1985 }
1986
1987 //      Return number of enemies attacking object objnum
1988 //
1989 // AL 10.26.97: Also include turrets on large ships when couting enemies attacking
1990 int num_enemies_attacking(int objnum)
1991 {
1992         object          *objp;
1993         ship                    *sp;
1994         ship_subsys     *ssp;
1995         ship_obj                *so;
1996         int                     count;
1997
1998         count = 0;
1999
2000         for ( so = GET_FIRST(&Ship_obj_list); so != END_OF_LIST(&Ship_obj_list); so = GET_NEXT(so) ) {
2001                 objp = &Objects[so->objnum];
2002                 Assert(objp->instance != -1);
2003                 sp = &Ships[objp->instance];
2004
2005                 if (Ai_info[sp->ai_index].target_objnum == objnum)
2006                         count++;
2007
2008                 // consider turrets that may be attacking objnum (but only turrets on SIF_BIG_SHIP ships)
2009                 if ( Ship_info[sp->ship_info_index].flags & SIF_BIG_SHIP ) {
2010
2011                         // loop through all the subsystems, check if turret has objnum as a target
2012                         ssp = GET_FIRST(&sp->subsys_list);
2013                         while ( ssp != END_OF_LIST( &sp->subsys_list ) ) {
2014
2015                                 if ( ssp->system_info->type == SUBSYSTEM_TURRET ) {
2016                                         if ( (ssp->turret_enemy_objnum == objnum) && (ssp->current_hits > 0) ) {
2017                                                 count++;
2018                                         }
2019                                 }
2020                                 ssp = GET_NEXT( ssp );
2021                         } // end while
2022                 }
2023         }
2024
2025         return count;
2026 }
2027
2028 //      Get the team to fire on given an object.
2029 int get_enemy_team_mask(int objnum)
2030 {
2031         int     my_team, enemy_team_mask;
2032
2033         my_team = Ships[Objects[objnum].instance].team;
2034
2035         if (Mission_all_attack) {
2036                 //      All teams attack all teams.
2037                 switch (my_team) {
2038                 case TEAM_FRIENDLY:
2039                         enemy_team_mask = TEAM_HOSTILE | TEAM_NEUTRAL | TEAM_TRAITOR;
2040                         break;
2041                 case TEAM_HOSTILE:
2042                         enemy_team_mask = TEAM_FRIENDLY | TEAM_NEUTRAL | TEAM_TRAITOR;
2043                         break;
2044                 case TEAM_NEUTRAL:
2045                         enemy_team_mask = TEAM_FRIENDLY | TEAM_HOSTILE | TEAM_TRAITOR;
2046                         break;
2047                 case TEAM_UNKNOWN:
2048                         enemy_team_mask = TEAM_HOSTILE;
2049                         break;
2050                 case TEAM_TRAITOR:
2051                         enemy_team_mask = TEAM_FRIENDLY | TEAM_NEUTRAL | TEAM_HOSTILE | TEAM_TRAITOR;
2052                         break;
2053                 default:
2054                         enemy_team_mask = TEAM_HOSTILE;
2055                         Int3();                 //      Illegal value for team!
2056                         break;
2057                 }
2058         } else {
2059                 switch (my_team) {
2060                 case TEAM_FRIENDLY:
2061                         enemy_team_mask = TEAM_HOSTILE | TEAM_NEUTRAL | TEAM_TRAITOR;
2062                         break;
2063                 case TEAM_HOSTILE:
2064                         enemy_team_mask = TEAM_FRIENDLY | TEAM_NEUTRAL | TEAM_TRAITOR;
2065                         break;
2066                 case TEAM_NEUTRAL:
2067                         enemy_team_mask = TEAM_FRIENDLY | TEAM_TRAITOR;
2068                         break;
2069                 case TEAM_UNKNOWN:
2070                         enemy_team_mask = TEAM_HOSTILE;
2071                         break;
2072                 case TEAM_TRAITOR:
2073                         enemy_team_mask = TEAM_FRIENDLY | TEAM_NEUTRAL | TEAM_HOSTILE | TEAM_TRAITOR;
2074                         break;
2075                 default:
2076                         enemy_team_mask = TEAM_HOSTILE;
2077                         Int3();                 //      Illegal value for team!
2078                         break;
2079                 }
2080         }
2081
2082         return enemy_team_mask;
2083 }
2084
2085 //      Scan all the ships in *objp's wing.
2086 //      Return the lowest maximum speed of a ship in the wing.
2087 //      Current maximum speed (based on energy settings) is shipp->current_max_speed
2088 float get_wing_lowest_max_speed(object *objp)
2089 {
2090         ship            *shipp;
2091         ai_info *aip;
2092         float           lowest_max_speed;
2093         int             wingnum;
2094         object  *o;
2095         ship_obj        *so;
2096
2097         Assert(objp->type == OBJ_SHIP);
2098         Assert((objp->instance >= 0) && (objp->instance < MAX_OBJECTS));
2099         shipp = &Ships[objp->instance];
2100         Assert((shipp->ai_index >= 0) && (shipp->ai_index < MAX_AI_INFO));
2101         aip = &Ai_info[shipp->ai_index];
2102
2103         wingnum = aip->wing;
2104
2105         lowest_max_speed = shipp->current_max_speed;
2106
2107         if ( wingnum == -1 )
2108                 return lowest_max_speed;
2109
2110         Assert(wingnum >= 0);
2111
2112         for ( so = GET_FIRST(&Ship_obj_list); so != END_OF_LIST(&Ship_obj_list); so = GET_NEXT(so) ) {
2113                 o = &Objects[so->objnum];
2114                 ship    *oshipp = &Ships[o->instance];
2115                 ai_info *oaip = &Ai_info[oshipp->ai_index];
2116
2117                 if ((oaip->mode == AIM_WAYPOINTS) && (oaip->wing == wingnum)) {
2118                         //      Note: If a ship in the wing has a super low max speed, probably its engines are disabled.  So, fly along and
2119                         //      ignore the poor guy.
2120                         float   cur_max = oshipp->current_max_speed;
2121
2122                         if (oaip->ai_flags & AIF_DOCKED) {
2123                                 if (oaip->dock_objnum > -1)
2124                                         if (Objects[oaip->dock_objnum].type == OBJ_SHIP) 
2125                                                 cur_max *= o->phys_info.mass/(o->phys_info.mass + Objects[oaip->dock_objnum].phys_info.mass);
2126                         }
2127                                                         
2128                         if ((oshipp->current_max_speed > 5.0f) && (cur_max < lowest_max_speed)) {
2129                                 lowest_max_speed = cur_max;
2130                         }
2131                 }
2132         }
2133
2134         return lowest_max_speed;
2135 }
2136
2137 /*
2138 //      Tell everyone to ignore object objnum.
2139 void set_global_ignore_object(int objnum)
2140 {
2141         int     i;
2142
2143         Assert(Objects[objnum].type == OBJ_SHIP);
2144
2145         nprintf(("AI", "Telling everyone to ignore object %s\n", Ships[Objects[objnum].instance].ship_name));
2146
2147         for (i=0; i<MAX_IGNORE_OBJECTS; i++) {
2148                 if (Ignore_objects[i].objnum == -1) {
2149                         Ignore_objects[i].objnum = objnum;
2150                         Ignore_objects[i].signature = Objects[objnum].signature;
2151                         break;
2152                 }
2153         }
2154
2155         if (i == MAX_IGNORE_OBJECTS) {
2156                 //      Couldn't find a free slot, but maybe one of these objects has died.
2157                 for (i=0; i<MAX_IGNORE_OBJECTS; i++) {
2158                         int     o = Ignore_objects[i].objnum;
2159                         if (Objects[o].type != OBJ_SHIP)
2160                                 break;          //      Not a ship, so use this slot.
2161                         if (Objects[o].signature != Ignore_objects[i].signature)
2162                                 break;          //      Signatures don't match, so use this slot.
2163                 }
2164
2165                 if (i != MAX_IGNORE_OBJECTS) {
2166                         Ignore_objects[i].objnum = objnum;
2167                         Ignore_objects[i].signature = Objects[objnum].signature;
2168                 } else {
2169                         nprintf(("Warning", "Ignore_objects buffer full.  Stealing a slot to ignore object #%i\n"));
2170                         Int3();
2171
2172                         int     r;
2173
2174                         r = objnum % MAX_IGNORE_OBJECTS;
2175
2176                         Ignore_objects[r].objnum = objnum;
2177                         Ignore_objects[r].signature = Objects[objnum].signature;
2178                 }
2179         }
2180 }
2181
2182 */
2183
2184 //      Determine if object objnum is supposed to be ignored by object with ai_info *aip.
2185 //      Return:
2186 //              TRUE    if objnum is aip->ignore_objnum (and signatures match)
2187 //                              or objnum is in ignore wing
2188 //              FALSE   otherwise
2189 int is_ignore_object(ai_info *aip, int objnum)
2190 {
2191
2192 /*      //      First, scan all objects in global array of objects to be ignored.
2193         for (int i=0; i<MAX_IGNORE_OBJECTS; i++)
2194                 if (Ignore_objects[i].objnum != -1)
2195                         if (objnum == Ignore_objects[i].objnum)
2196                                 if (Objects[Ignore_objects[i].objnum].signature == Ignore_objects[i].signature)
2197                                         return 1;
2198 */
2199
2200         //      Didn't find in global list.  Now check 
2201         if (aip->ignore_objnum == UNUSED_OBJNUM)
2202                 return 0;                                                                       //      Not ignoring anything.
2203         else if (aip->ignore_objnum >= 0) {             //      This means it's ignoring an object, not a wing.
2204                 if (aip->ignore_objnum == objnum) {
2205                         if (Objects[aip->ignore_objnum].signature == aip->ignore_signature) {
2206                                 return 1;
2207                         } else {
2208                                 aip->ignore_objnum = UNUSED_OBJNUM;
2209                                 return 0;
2210                         }
2211                 } else {
2212                         return 0;
2213                 }
2214         } else {                                                                                        //      Ignoring a wing.
2215                 Int3(); // Should never happen.  I thought I removed this behavior! -- MK, 5/17/98
2216                 return 0;
2217 /*              int     ignore_wingnum = -(aip->ignore_objnum + 1);
2218
2219                 Assert(ignore_wingnum < MAX_WINGS);
2220                 Assert(aip->shipnum >= 0);
2221                 return (Ships[Objects[objnum].instance].wingnum == ignore_wingnum);
2222 */      }
2223 }
2224
2225 // -----------------------------------------------------------------------------
2226
2227 // given a ship with bounding box and a point, find the closest point on the bbox
2228 int get_nearest_bbox_point(object *ship_obj, vector *start, vector *box_pt)
2229 {
2230         vector temp, rf_start;
2231         polymodel *pm;
2232         pm = model_get(Ship_info[Ships[ship_obj->instance].ship_info_index].modelnum);
2233
2234         // get start in ship rf
2235         vm_vec_sub(&temp, start, &ship_obj->pos);
2236         vm_vec_rotate(&rf_start, &temp, &ship_obj->orient);
2237
2238         // find box_pt
2239         int inside = project_point_onto_bbox(&pm->mins, &pm->maxs, &rf_start, &temp);
2240
2241         // get box_pt in world rf
2242         vm_vec_unrotate(box_pt, &temp, &ship_obj->orient);
2243         vm_vec_add2(box_pt, &ship_obj->pos);
2244
2245         return inside;
2246 }
2247
2248
2249 typedef struct eval_nearest_objnum {
2250         int     objnum;
2251         object *trial_objp;
2252         int     enemy_team_mask;
2253         int     enemy_wing;
2254         float   range;
2255         int     max_attackers;
2256         int     nearest_objnum;
2257         float   nearest_dist;
2258         int     check_danger_weapon_objnum;
2259 } eval_nearest_objnum;
2260
2261
2262 void evaluate_object_as_nearest_objnum(eval_nearest_objnum *eno)
2263 {
2264         ai_info *aip;
2265         ship_subsys     *attacking_subsystem;
2266
2267         aip = &Ai_info[Ships[Objects[eno->objnum].instance].ai_index];
2268
2269         attacking_subsystem = aip->targeted_subsys;
2270
2271         if ((attacking_subsystem != NULL) || !(eno->trial_objp->flags & OF_PROTECTED)) {
2272                 if ( OBJ_INDEX(eno->trial_objp) != eno->objnum ) {
2273 #ifndef NDEBUG
2274                         if (!Player_attacking_enabled && (eno->trial_objp == Player_obj))
2275                                 return;
2276 #endif
2277                         //      If only supposed to attack ship in a specific wing, don't attack other ships.
2278                         if ((eno->enemy_wing != -1) && (Ships[eno->trial_objp->instance].wingnum != eno->enemy_wing))
2279                                 return;
2280
2281                         //      Don't keep firing at a ship that is in its death throes.
2282                         if (Ships[eno->trial_objp->instance].flags & SF_DYING)
2283                                 return;
2284
2285                         if (is_ignore_object(aip, ((eno->trial_objp)-Objects)))
2286                                 return;
2287
2288                         if (eno->trial_objp->flags & OF_PROTECTED)
2289                                 return;
2290
2291                         if (Ships[eno->trial_objp->instance].flags & SF_ARRIVING)
2292                                 return;
2293
2294                         ship_info *sip = &Ship_info[Ships[eno->trial_objp->instance].ship_info_index];
2295
2296                         if (sip->flags & (SIF_NO_SHIP_TYPE | SIF_NAVBUOY))
2297                                 return;
2298
2299                         if (Ships[eno->trial_objp->instance].team & eno->enemy_team_mask) {
2300                                 float   dist;
2301                                 int     num_attacking;
2302
2303                                 // Allow targeting of stealth in nebula by his firing at me
2304                                 // This is done for a specific ship, not generally.
2305                                 if ( !eno->check_danger_weapon_objnum ) {
2306                                         // check if can be targeted if inside nebula
2307                                         if ( !object_is_targetable(eno->trial_objp, &Ships[Objects[eno->objnum].instance]) ) {
2308                                                 // check if stealth ship is visible, but not "targetable"
2309                                                 if ( !((sip->flags & SIF_STEALTH) && ai_is_stealth_visible(&Objects[eno->objnum], eno->trial_objp)) ) {
2310                                                         return;
2311                                                 }
2312                                         }
2313                                 }
2314
2315                                 // if objnum is BIG or HUGE, find distance to bbox
2316                                 if (sip->flags & (SIF_BIG_SHIP | SIF_HUGE_SHIP)) {
2317                                         vector box_pt;
2318                                         // check if inside bbox
2319                                         int inside = get_nearest_bbox_point(eno->trial_objp, &Objects[eno->objnum].pos, &box_pt);
2320                                         if (inside) {
2321                                                 dist = 10.0f;
2322                                                 // on the box
2323                                         } else {
2324                                                 dist = vm_vec_dist_quick(&Objects[eno->objnum].pos, &box_pt);
2325                                         }
2326                                 } else {
2327                                         dist = vm_vec_dist_quick(&Objects[eno->objnum].pos, &eno->trial_objp->pos);
2328                                 }
2329                                 
2330                                 //      Make it more likely that fighters (or bombers) will be picked as an enemy by scaling up distance for other types.
2331                                 if ((Ship_info[Ships[eno->trial_objp->instance].ship_info_index].flags & (SIF_FIGHTER | SIF_BOMBER))) {
2332                                         dist = dist * 0.5f;
2333                                 }
2334
2335                                 num_attacking = num_enemies_attacking(eno->trial_objp-Objects);
2336                                 if ((sip->flags & (SIF_BIG_SHIP | SIF_HUGE_SHIP)) || (num_attacking < eno->max_attackers)) {
2337                                         if (!(sip->flags & (SIF_BIG_SHIP | SIF_HUGE_SHIP))){
2338                                                 dist *= (float) (num_attacking+2)/2.0f;                         //      prevents lots of ships from attacking same target
2339                                         }
2340
2341                                         if (eno->trial_objp->flags & OF_PLAYER_SHIP){
2342                                                 dist *= 1.0f + (NUM_SKILL_LEVELS - Game_skill_level - 1)/NUM_SKILL_LEVELS;      //      Favor attacking non-players based on skill level.
2343                                         }
2344
2345                                         if (dist < eno->nearest_dist) {
2346                                                 eno->nearest_dist = dist;
2347                                                 eno->nearest_objnum = eno->trial_objp-Objects;
2348                                         }
2349                                 }
2350                         }
2351                 }
2352         }
2353
2354 }
2355
2356
2357 //      Given an object and an enemy team, return the index of the nearest enemy object.
2358 //      Unless aip->targeted_subsys != NULL, don't allow to attack objects
2359 //      with OF_PROTECTED bit set.
2360 //      Ship must be within range "range".
2361 //      Don't attack a ship that already has at least max_attackers attacking it.
2362 int get_nearest_objnum(int objnum, int enemy_team_mask, int enemy_wing, float range, int max_attackers)
2363 {
2364         object  *danger_weapon_objp;
2365         ai_info *aip;
2366         ship_obj        *so;
2367
2368         // initialize eno struct
2369         eval_nearest_objnum eno;
2370         eno.enemy_team_mask = enemy_team_mask;
2371         eno.enemy_wing = enemy_wing;
2372         eno.max_attackers = max_attackers;
2373         eno.objnum = objnum;
2374         eno.range = range;
2375         eno.nearest_dist = range;
2376         eno.nearest_objnum = -1;
2377         eno.check_danger_weapon_objnum = 0;
2378
2379         // go through the list of all ships and evaluate as potential targets
2380         for ( so = GET_FIRST(&Ship_obj_list); so != END_OF_LIST(&Ship_obj_list); so = GET_NEXT(so) ) {
2381                 eno.trial_objp = &Objects[so->objnum];
2382                 evaluate_object_as_nearest_objnum(&eno);
2383
2384         }
2385
2386         // check if danger_weapon_objnum has will show a stealth ship
2387         aip = &Ai_info[Ships[Objects[objnum].instance].ai_index];
2388         if (aip->danger_weapon_objnum >= 0) {
2389                 danger_weapon_objp = &Objects[aip->danger_weapon_objnum];
2390                 // validate weapon
2391                 if (danger_weapon_objp->signature == aip->danger_weapon_signature) {
2392                         Assert(danger_weapon_objp->type == OBJ_WEAPON);
2393                         // check if parent is a ship
2394                         if (danger_weapon_objp->parent >= 0) {
2395                                 if ( is_object_stealth_ship(&Objects[danger_weapon_objp->parent]) ) {
2396                                         // check if stealthy
2397                                         if ( ai_is_stealth_visible(&Objects[objnum], &Objects[danger_weapon_objp->parent]) != STEALTH_FULLY_TARGETABLE ) {
2398                                                 // check if weapon is laser
2399                                                 if (Weapon_info[Weapons[danger_weapon_objp->instance].weapon_info_index].subtype == WP_LASER) {
2400                                                         // check stealth ship by its laser fire
2401                                                         eno.check_danger_weapon_objnum = 1;
2402                                                         eno.trial_objp = &Objects[danger_weapon_objp->parent];
2403                                                         evaluate_object_as_nearest_objnum(&eno);
2404                                                 }
2405                                         }
2406                                 }
2407                         }
2408                 }
2409         }
2410
2411         //      If only looking for target in certain wing and couldn't find anything in
2412         //      that wing, look for any object.
2413         if ((eno.nearest_objnum == -1) && (enemy_wing != -1)) {
2414                 return get_nearest_objnum(objnum, enemy_team_mask, -1, range, max_attackers);
2415         }
2416
2417         return eno.nearest_objnum;
2418 }
2419
2420 //      Given an object and an enemy team, return the index of the nearest enemy object.
2421 //      Unlike find_enemy or find_nearest_objnum, this doesn't care about things like the protected flag or number
2422 //      of enemies attacking.
2423 //      It is used to find the nearest enemy to determine things like whether to rearm.
2424 int find_nearby_hostile(int objnum, int enemy_team_mask, float range, int *count)
2425 {
2426         int             nearest_objnum;
2427         float           nearest_dist;
2428         object  *objp;
2429         ai_info *aip;
2430         ship_obj        *so;
2431
2432         nearest_objnum = -1;
2433         nearest_dist = range;
2434
2435         aip = &Ai_info[Ships[Objects[objnum].instance].ai_index];
2436
2437         *count = 0;
2438
2439         for ( so = GET_FIRST(&Ship_obj_list); so != END_OF_LIST(&Ship_obj_list); so = GET_NEXT(so) ) {
2440                 objp = &Objects[so->objnum];
2441
2442                 if ( OBJ_INDEX(objp) != objnum ) {
2443                         if (Ships[objp->instance].flags & SF_DYING)
2444                                 continue;
2445
2446                         if (Ship_info[Ships[objp->instance].ship_info_index].flags & (SIF_NO_SHIP_TYPE | SIF_NAVBUOY))
2447                                 continue;
2448
2449                         if (Ships[objp->instance].team & enemy_team_mask) {
2450                                 float   dist;
2451
2452                                 dist = vm_vec_dist_quick(&Objects[objnum].pos, &objp->pos) - objp->radius*0.75f;
2453                                 
2454                                 if (dist < range) {
2455                                         (*count)++;
2456
2457                                         if (dist < nearest_dist) {
2458                                                 nearest_dist = dist;
2459                                                 nearest_objnum = objp-Objects;
2460                                         }
2461                                 }
2462                         }
2463                 }
2464         }
2465
2466         return nearest_objnum;
2467 }
2468
2469 // return !0 if objp can be considered for a turret target, 0 otherwise
2470 // input:       objp                            =>      object that turret is considering as an enemy
2471 //                              turret_parent   =>      object index for ship that turret sits on
2472 int valid_turret_enemy(object *objp, object *turret_parent)
2473 {
2474         if ( objp == turret_parent ) {
2475                 return 0;
2476         }
2477
2478         if ( objp->type == OBJ_ASTEROID ) {
2479                 return 1;
2480         }
2481
2482         if ( (objp->type == OBJ_SHIP) ) {
2483                 ship *shipp;
2484                 shipp = &Ships[objp->instance];
2485
2486                 // don't fire at ships with protected bit set!!!
2487                 if ( objp->flags & OF_PROTECTED ) {
2488                         return 0;
2489                 }
2490
2491                 if ( !(Ship_info[shipp->ship_info_index].flags & SIF_DO_COLLISION_CHECK)) {
2492                         return 0;
2493                 }
2494
2495                 if (shipp->flags & SF_ARRIVING) {
2496                         return 0;
2497                 }
2498
2499                 return 1;
2500         }
2501
2502         if ( objp->type == OBJ_WEAPON ) {
2503                 if ( Weapon_info[Weapons[objp->instance].weapon_info_index].wi_flags & WIF_BOMB ) {
2504                         if ( obj_team(turret_parent) != Weapons[objp->instance].team ) {
2505                                 return 1;
2506                         }
2507                 }
2508         }
2509
2510         return 0;
2511 }
2512
2513 // return 1 if objp is in fov of the specified turret, tp.  Otherwise return 0.
2514 //      dist = distance from turret to center point of object
2515 int object_in_turret_fov(object *objp, model_subsystem *tp, vector *tvec, vector *tpos, float dist)
2516 {
2517         vector  v2e;
2518         float           dot;
2519         vm_vec_normalized_dir(&v2e, &objp->pos, tpos);
2520         dot = vm_vec_dot(&v2e, tvec);
2521
2522         dot += objp->radius / (dist + objp->radius);
2523
2524         if ( dot >= tp->turret_fov ) {
2525                 return 1;
2526         }
2527
2528         return 0;
2529 }
2530
2531 // return 1 if bomb_objp is headed towards ship_objp
2532 int bomb_headed_towards_ship(object *bomb_objp, object *ship_objp)
2533 {
2534         float           dot;
2535         vector  bomb_to_ship_vector;
2536
2537         vm_vec_normalized_dir(&bomb_to_ship_vector, &ship_objp->pos, &bomb_objp->pos);
2538         dot = vm_vec_dot(&bomb_objp->orient.fvec, &bomb_to_ship_vector);
2539
2540         if ( dot > 0 ) {
2541                 return 1;
2542         }
2543
2544         return 0;
2545 }
2546
2547 // nubmer of live turrets with target_objnum 
2548 int num_turrets_attacking(object *turret_parent, int target_objnum) 
2549 {
2550         ship_subsys *ss;
2551         ship *shipp;
2552         int count = 0;
2553         shipp = &Ships[turret_parent->instance];
2554
2555         Assert(turret_parent->type == OBJ_SHIP);
2556         Assert(Objects[target_objnum].type == OBJ_SHIP);
2557
2558         for (ss=GET_FIRST(&shipp->subsys_list); ss!=END_OF_LIST(&shipp->subsys_list); ss=GET_NEXT(ss)) {
2559                 // check if subsys is alive
2560                 if (ss->current_hits <= 0.0f) {
2561                         continue;
2562                 }
2563
2564                 // check if it's a turret
2565                 if (ss->system_info->type != SUBSYSTEM_TURRET) {
2566                         continue;
2567                 }
2568
2569                 // if the turret is locked
2570                 if(ss->weapons.flags & SW_FLAG_TURRET_LOCK){
2571                         continue;
2572                 }               
2573
2574                 // check if turret is targeting target_objnum
2575                 if (ss->turret_enemy_objnum == target_objnum) {
2576                         count++;
2577                 }
2578         }
2579
2580         return count;
2581 }
2582
2583 float Lethality_range_const = 2.0f;
2584 DCF(lethality_range, "N for modifying range: 1 / (1+N) at 100")
2585 {
2586         dc_get_arg(ARG_FLOAT);
2587         Lethality_range_const = Dc_arg_float;
2588 }
2589
2590 float Player_lethality_bump[NUM_SKILL_LEVELS] = {
2591         // 0.0f, 5.0f, 10.0f, 25.0f, 40.0f
2592         0.0f, 0.0f, 0.0f, 0.0f, 0.0f
2593 };
2594
2595 // evaluate obj as posssible target for turret
2596 void evaluate_obj_as_target(object *objp, eval_enemy_obj_struct *eeo)
2597 {
2598         object  *turret_parent_obj = &Objects[eeo->turret_parent_objnum];
2599         ship            *shipp;
2600         model_subsystem *tp = eeo->turret_subsys->system_info;
2601         float dist;
2602
2603         // Don't look for bombs when weapon system is not ok
2604         if (objp->type == OBJ_WEAPON && !eeo->weapon_system_ok) {
2605                 return;
2606         }
2607
2608         if ( !valid_turret_enemy(objp, turret_parent_obj) ) {
2609                 return;
2610         }
2611
2612 #ifndef NDEBUG
2613         if (!Player_attacking_enabled && (objp == Player_obj)) {
2614                 return;
2615         }
2616 #endif
2617
2618         if ( objp->type == OBJ_SHIP ) {
2619                 shipp = &Ships[objp->instance];
2620
2621                 // check on enemy team
2622                 if ( !(shipp->team & eeo->enemy_team_mask) ) {
2623                         return;
2624                 }
2625
2626                 // check if protected
2627                 if (objp->flags & OF_PROTECTED) {
2628                         return;
2629                 }
2630
2631                 // check if beam protected
2632                 if (Weapon_info[tp->turret_weapon_type].wi_flags & WIF_BEAM) {
2633                         if (objp->flags & OF_BEAM_PROTECTED) {
2634                                 return;
2635                         }
2636                 }
2637
2638                 if (eeo->big_only_flag) {
2639                         if (!(Ship_info[shipp->ship_info_index].flags & (SIF_BIG_SHIP | SIF_HUGE_SHIP))) {
2640                                 return;
2641                         }
2642                 }
2643
2644                 // check if     turret flagged to only target tagged ships
2645                 if ( (eeo->turret_subsys->weapons.flags & SW_FLAG_TAGGED_ONLY) && !ship_is_tagged(objp) ) {
2646                         return;
2647                 }
2648
2649                 // check if valid target in nebula
2650                 if ( !object_is_targetable(objp, &Ships[Objects[eeo->turret_parent_objnum].instance]) ) {
2651                         // BYPASS ocassionally for stealth
2652                         int try_anyway = FALSE;
2653                         if ( is_object_stealth_ship(objp) ) {
2654                                 float turret_stealth_find_chance = 0.5f;
2655                                 float speed_mod = -0.1f + vm_vec_mag_quick(&objp->phys_info.vel) / 70.0f;
2656                                 if (frand() > (turret_stealth_find_chance + speed_mod)) {
2657                                         try_anyway = TRUE;
2658                                 }
2659                         }
2660
2661                         if (!try_anyway) {
2662                                 return;
2663                         }
2664                 }
2665
2666         } else {
2667                 shipp = NULL;
2668         }
2669
2670         // modify dist for BIG|HUGE, getting closest point on bbox, if not inside
2671         dist = vm_vec_dist_quick(eeo->tpos, &objp->pos) - objp->radius;
2672         if (dist < 0.0f) {
2673                 dist = 0.0f;
2674         }
2675
2676         // check if object is a bomb attacking the turret parent
2677         // check if bomb is homing on the turret parent ship
2678         if (objp->type == OBJ_WEAPON) {
2679                 if ( Weapons[objp->instance].homing_object == &Objects[eeo->turret_parent_objnum] ) {
2680                         if ( dist < eeo->nearest_homing_bomb_dist ) {
2681                                 if ( (eeo->current_enemy == -1) || object_in_turret_fov(objp, tp, eeo->tvec, eeo->tpos, dist + objp->radius) ) {
2682                                         eeo->nearest_homing_bomb_dist = dist;
2683                                         eeo->nearest_homing_bomb_objnum = OBJ_INDEX(objp);
2684                                 }
2685                         }
2686                 // if not homing, check if bomb is flying towards ship
2687                 } else if ( bomb_headed_towards_ship(objp, &Objects[eeo->turret_parent_objnum]) ) {
2688                         if ( dist < eeo->nearest_bomb_dist ) {
2689                                 if ( (eeo->current_enemy == -1) || object_in_turret_fov(objp, tp, eeo->tvec, eeo->tpos, dist + objp->radius) ) {
2690                                         eeo->nearest_bomb_dist = dist;
2691                                         eeo->nearest_bomb_objnum = OBJ_INDEX(objp);
2692                                 }
2693                         }
2694                 }
2695         } // end weapon section
2696
2697         // maybe recalculate dist for big or huge ship
2698 //      if (shipp && (Ship_info[shipp->ship_info_index].flags & (SIF_BIG_SHIP | SIF_HUGE_SHIP))) {
2699 //              fvi_ray_boundingbox(min, max, start, direction, hit);
2700 //              dist = vm_vec_dist_quick(hit, tvec);
2701 //      }
2702
2703         // check for nearest attcker
2704         if ( (shipp) && (dist < eeo->weapon_travel_dist) ) {
2705                 ai_info *aip = &Ai_info[shipp->ai_index];
2706
2707                 // modify distance based on number of turrets from my ship attacking enemy (add 10% per turret)
2708                 // dist *= (num_enemies_attacking(OBJ_INDEX(objp))+2)/2;        //      prevents lots of ships from attacking same target
2709                 int num_att_turrets = num_turrets_attacking(turret_parent_obj, OBJ_INDEX(objp));
2710                 dist *= (1.0f + 0.1f*num_att_turrets);
2711
2712                 // return if we're over the cap
2713                 int max_turrets = 3 + Game_skill_level * Game_skill_level;
2714                 if (num_att_turrets > max_turrets) {
2715                         return;
2716                 }
2717
2718                 // modify distance based on lethality of objp to my ship
2719                 float active_lethality = aip->lethality;
2720                 if (objp->flags & OF_PLAYER_SHIP) {
2721                         active_lethality += Player_lethality_bump[Game_skill_level];
2722                 }
2723
2724                 dist /= (1.0f + 0.01f*Lethality_range_const*active_lethality);
2725
2726                 // Make level 2 tagged ships more likely to be targeted
2727                 if (shipp->level2_tag_left > 0.0f) {
2728                         dist *= 0.3f;
2729                 }
2730
2731                 // check if objp is targeting the turret's ship, or if objp has just hit the turret's ship
2732                 if ( aip->target_objnum == eeo->turret_parent_objnum || aip->last_objsig_hit == Objects[eeo->turret_parent_objnum].signature ) {
2733                         // A turret will always target a ship that is attacking itself... self-preservation!
2734                         if ( aip->targeted_subsys == eeo->turret_subsys ) {
2735                                 dist *= 0.5f;   // highest priority
2736                         }
2737                 }
2738
2739                 // maybe update nearest attacker
2740                 if ( dist < eeo->nearest_attacker_dist ) {
2741                         if ( (eeo->current_enemy == -1) || object_in_turret_fov(objp, tp, eeo->tvec, eeo->tpos, dist + objp->radius) ) {
2742                                 // nprintf(("AI", "Nearest enemy = %s, dist = %7.3f, dot = %6.3f, fov = %6.3f\n", Ships[objp->instance].ship_name, dist, vm_vec_dot(&v2e, tvec), tp->turret_fov));
2743                                 eeo->nearest_attacker_dist = dist;
2744                                 eeo->nearest_attacker_objnum = OBJ_INDEX(objp);
2745                         }
2746                 }
2747         } // end ship section
2748 }
2749
2750 // return 0 only if objnum is beam protected and turret is beam turret
2751 int is_target_beam_valid(ship_subsys *turret_subsys, int objnum)
2752 {
2753         // check if turret has beam weapon
2754         model_subsystem *tp = turret_subsys->system_info;
2755
2756         if (Weapon_info[tp->turret_weapon_type].wi_flags & WIF_BEAM) {
2757                 if (Objects[objnum].flags & OF_BEAM_PROTECTED) {
2758                         return 0;
2759                 }
2760
2761                 if (Weapon_info[tp->turret_weapon_type].wi_flags & WIF_HUGE) {
2762                         if (Objects[objnum].type == OBJ_SHIP && !(Ship_info[Ships[Objects[objnum].instance].ship_info_index].flags & (SIF_BIG_SHIP|SIF_HUGE_SHIP)) ) {
2763                                 return 0;
2764                         }
2765                 }
2766         }
2767
2768         return 1;
2769 }
2770
2771
2772 //      Given an object and an enemy team, return the index of the nearest enemy object.
2773 //
2774 // input:
2775 //                              turret_parent_objnum    => parent objnum for the turret
2776 //                              turret_subsys                   => pointer to system_info for the turret subsystem
2777 //                              enemy_team_mask         => OR'ed TEAM_ flags for the enemy of the turret parent ship
2778 //                              tpos                                            => position of turret (world coords)
2779 //                              tvec                                            => forward vector of turret (world coords)
2780 //                              current_enemy                   =>      objnum of current turret target
2781 int get_nearest_turret_objnum(int turret_parent_objnum, ship_subsys *turret_subsys, int enemy_team_mask, vector *tpos, vector *tvec, int current_enemy, int big_only_flag)
2782 {
2783         float                                   weapon_travel_dist;
2784         int                                     weapon_system_ok;
2785         object                          *objp;
2786         model_subsystem *tp;
2787         eval_enemy_obj_struct eeo;
2788
2789         // list of stuff to go thru
2790         ship_obj                *so;
2791         missile_obj *mo;
2792
2793         tp = turret_subsys->system_info;
2794         weapon_travel_dist = min(Weapon_info[tp->turret_weapon_type].lifetime * Weapon_info[tp->turret_weapon_type].max_speed, Weapon_info[tp->turret_weapon_type].weapon_range);
2795
2796         // Set flag based on strength of weapons subsystem.  If weapons subsystem is destroyed, don't let turrets fire at bombs
2797         weapon_system_ok = 0;
2798         if ( ship_get_subsystem_strength( &Ships[Objects[turret_parent_objnum].instance], SUBSYSTEM_WEAPONS ) > 0 ) {
2799                 weapon_system_ok = 1;
2800         }
2801
2802         // Initialize eeo struct.
2803         eeo.turret_parent_objnum = turret_parent_objnum;
2804         eeo.weapon_system_ok = weapon_system_ok;
2805         eeo.weapon_travel_dist = weapon_travel_dist;
2806         eeo.big_only_flag = big_only_flag;
2807         eeo.enemy_team_mask = enemy_team_mask;
2808         eeo.current_enemy = current_enemy;
2809         eeo.tpos = tpos;
2810         eeo.tvec = tvec;
2811         eeo.turret_subsys = turret_subsys;
2812
2813         eeo.nearest_attacker_dist = 99999.0f;
2814         eeo.nearest_attacker_objnum = -1;
2815
2816         eeo.nearest_homing_bomb_dist = 99999.0f;
2817         eeo.nearest_homing_bomb_objnum = -1;
2818
2819         eeo.nearest_bomb_dist = 99999.0f;
2820         eeo.nearest_bomb_objnum = -1;
2821
2822         eeo.nearest_dist = 99999.0f;
2823         eeo.nearest_objnum = -1;
2824
2825
2826         // Missile_obj_list
2827         for( mo = GET_FIRST(&Missile_obj_list); mo != END_OF_LIST(&Missile_obj_list); mo = GET_NEXT(mo) ) {
2828                 objp = &Objects[mo->objnum];
2829                 evaluate_obj_as_target(objp, &eeo);
2830         }
2831         // highest priority
2832         if ( eeo.nearest_homing_bomb_objnum != -1 ) {                                   // highest priority is an incoming homing bomb
2833                 return eeo.nearest_homing_bomb_objnum;
2834         } else if ( eeo.nearest_bomb_objnum != -1 ) {                                   // next highest priority is an incoming dumbfire bomb
2835                 return eeo.nearest_bomb_objnum;
2836         }
2837
2838
2839         // Ship_used_list
2840         for ( so = GET_FIRST(&Ship_obj_list); so != END_OF_LIST(&Ship_obj_list); so = GET_NEXT(so) ) {
2841                 objp = &Objects[so->objnum];
2842                 evaluate_obj_as_target(objp, &eeo);
2843         }
2844
2845         Assert(eeo.nearest_attacker_objnum < 0 || is_target_beam_valid(turret_subsys, eeo.nearest_attacker_objnum));
2846                 // next highest priority is attacking ship
2847         if ( eeo.nearest_attacker_objnum != -1 ) {                      // next highest priority is an attacking ship
2848                 return eeo.nearest_attacker_objnum;
2849          }
2850
2851
2852 #ifndef FS2_DEMO
2853                 asteroid_obj *ao;
2854         // Asteroid_obj_list
2855         for( ao = GET_FIRST(&Asteroid_obj_list); ao != END_OF_LIST(&Asteroid_obj_list); ao = GET_NEXT(ao) ) {
2856                 objp = &Objects[ao->objnum];
2857                 evaluate_obj_as_target(objp, &eeo);
2858         }
2859 #endif
2860
2861         return eeo.nearest_objnum;                                                                              // lowest priority is the closest enemy objnum
2862 }
2863
2864 //      Return timestamp until a ship can find an enemy.
2865 //      Yes, no parameters.  Based solely on skill level.
2866 int get_enemy_timestamp()
2867 {
2868         return (NUM_SKILL_LEVELS - Game_skill_level) * ( (myrand() % 500) + 500);
2869 }
2870
2871 // -------------------------------------------------------------------
2872 //      Return objnum if enemy found, else return -1;
2873 //      Don't attack a ship that already has at least max_attackers attacking it.
2874 int find_enemy(int objnum, float range, int max_attackers)
2875 {
2876         int     enemy_team_mask;
2877
2878         enemy_team_mask = get_enemy_team_mask(objnum);
2879
2880         //      if target_objnum != -1, use that as goal.
2881         ai_info *aip = &Ai_info[Ships[Objects[objnum].instance].ai_index];
2882         if (timestamp_elapsed(aip->choose_enemy_timestamp)) {
2883                 aip->choose_enemy_timestamp = timestamp(get_enemy_timestamp());
2884                 if (aip->target_objnum != -1) {
2885                         int     target_objnum = aip->target_objnum;
2886
2887                         // DKA don't undo object as target in nebula missions.
2888                         // This could cause attack on ship on fringe on nebula to stop if attackee moves our of nebula range.  (BAD)
2889                         if ( (Objects[target_objnum].signature == aip->target_signature) ) {
2890                                 if (Ships[Objects[target_objnum].instance].team & enemy_team_mask) {
2891                                         if (!(Objects[target_objnum].flags & OF_PROTECTED)) {
2892                                                 // nprintf(("AI", "Frame %i: Object %i resuming goal of object %i\n", AI_FrameCount, objnum, target_objnum));
2893                                                 return target_objnum;
2894                                         }
2895                                 }
2896                         } else {
2897                                 aip->target_objnum = -1;
2898                                 aip->target_signature = -1;
2899                         }
2900                 }
2901                 return get_nearest_objnum(objnum, enemy_team_mask, aip->enemy_wing, range, max_attackers);
2902         } else {
2903                 aip->target_objnum = -1;
2904                 aip->target_signature = -1;
2905                 return -1;
2906         }
2907
2908 }
2909
2910 int Use_parent_target = 0;
2911 DCF_BOOL(use_parent_target, Use_parent_target)
2912
2913 // -------------------------------------------------------------------
2914 //      Return objnum if enemy found, else return -1;
2915 //
2916 // input:
2917 //                              turret_subsys   => pointer to turret subsystem
2918 //                              objnum                  => parent objnum for the turret
2919 //                              tpos                            => position of turret (world coords)
2920 //                              tvec                            => forward vector of turret (world coords)
2921 //                              current_enemy   =>      objnum of current turret target
2922 int find_turret_enemy(ship_subsys *turret_subsys, int objnum, vector *tpos, vector *tvec, int current_enemy, float fov, int big_only_flag = 0)
2923 {
2924         int                                     enemy_team_mask, enemy_objnum;
2925         model_subsystem *tp;
2926         ship_info                       *sip;
2927
2928         tp = turret_subsys->system_info;
2929         enemy_team_mask = get_enemy_team_mask(objnum);
2930
2931         //      If a small ship and target_objnum != -1, use that as goal.
2932         ai_info *aip = &Ai_info[Ships[Objects[objnum].instance].ai_index];
2933         sip = &Ship_info[Ships[Objects[objnum].instance].ship_info_index];
2934
2935         if ((sip->flags & SIF_SMALL_SHIP) && (aip->target_objnum != -1)) {
2936                 int target_objnum = aip->target_objnum;
2937
2938                 if (Objects[target_objnum].signature == aip->target_signature) {
2939                         if (Ships[Objects[target_objnum].instance].team & enemy_team_mask) {
2940                                 if ( !(Objects[target_objnum].flags & OF_PROTECTED) ) {         // check this flag as well.
2941                                         // nprintf(("AI", "Frame %i: Object %i resuming goal of object %i\n", AI_FrameCount, objnum, target_objnum));
2942                                         return target_objnum;
2943                                 }
2944                         }
2945                 } else {
2946                         aip->target_objnum = -1;
2947                         aip->target_signature = -1;
2948                 }
2949         // Not small or small with target objnum
2950         } else {
2951                 // maybe use aip->target_objnum as next target
2952                 if ((frand() < 0.8f) && (aip->target_objnum != -1) && Use_parent_target) {
2953
2954                         //check if aip->target_objnum is valid target
2955                         int target_flags = Objects[aip->target_objnum].flags;
2956                         if ( target_flags & OF_PROTECTED ) {
2957                                 // AL 2-27-98: why is a protected ship being targeted?
2958                                 set_target_objnum(aip, -1);
2959                                 return -1;
2960                         }
2961
2962                         // maybe use ship target_objnum if valid for turret
2963                         // check for beam weapon and beam protected
2964                         if ( !((target_flags & OF_BEAM_PROTECTED) && (Weapon_info[tp->turret_weapon_type].wi_flags & WIF_BEAM)) ) {
2965                                 if ( Objects[aip->target_objnum].type == OBJ_SHIP ) {
2966                                         // check for huge weapon and huge ship
2967                                         if ( !big_only_flag || (Ship_info[Ships[Objects[aip->target_objnum].instance].ship_info_index].flags & (SIF_BIG_SHIP|SIF_HUGE_SHIP)) ) {
2968                                                 // check for tagged only and tagged ship
2969                                                 if ( (turret_subsys->weapons.flags & SW_FLAG_TAGGED_ONLY) && ship_is_tagged(&Objects[aip->target_objnum]) ) {
2970                                                         // select new target if aip->target_objnum is out of field of view
2971                                                         vector v2e;
2972                                                         float dot, dist;
2973                                                         dist = vm_vec_normalized_dir(&v2e, &Objects[aip->target_objnum].pos, tpos);
2974                                                         dot = vm_vec_dot(&v2e, tvec);
2975                                                         //      MODIFY FOR ATTACKING BIG SHIP
2976                                                         // dot += (0.5f * Objects[aip->target_objnum].radius / dist);
2977                                                         if (dot > fov) {
2978                                                                 return aip->target_objnum;
2979                                                         }
2980                                                 }
2981                                         }
2982                                 }
2983                         }
2984                 }
2985         }
2986
2987         enemy_objnum = get_nearest_turret_objnum(objnum, turret_subsys, enemy_team_mask, tpos, tvec, current_enemy, big_only_flag);
2988         if ( enemy_objnum >= 0 ) {
2989                 Assert( !((Objects[enemy_objnum].flags & OF_BEAM_PROTECTED) && (Weapon_info[tp->turret_weapon_type].wi_flags & WIF_BEAM)) );
2990                 if ( Objects[enemy_objnum].flags & OF_PROTECTED ) {
2991                         Int3();
2992                         enemy_objnum = aip->target_objnum;
2993                 }
2994         }
2995
2996         return enemy_objnum;
2997 }
2998
2999 //      If issued an order to a ship that's awaiting repair, abort that process.
3000 //      However, do not abort process for an object that is currently being repaired -- let it finish.
3001 void ai_set_goal_maybe_abort_dock(object *objp, ai_info *aip)
3002 {
3003         if (aip->ai_flags & AIF_AWAITING_REPAIR) {
3004                 object  *repair_obj;
3005
3006                 if (aip->dock_objnum == -1) {
3007                         repair_obj = NULL;
3008                 } else {
3009                         repair_obj = &Objects[aip->dock_objnum];
3010                 }
3011                 ai_do_objects_repairing_stuff( objp, repair_obj, REPAIR_INFO_ABORT );
3012         }
3013         aip->next_rearm_request_timestamp = timestamp(NEXT_REARM_TIMESTAMP);    //      Might request again after 30 seconds.
3014 }
3015
3016 void force_avoid_player_check(object *objp, ai_info *aip)
3017 {
3018         if (Ships[objp->instance].team == Player_ship->team){
3019                 aip->avoid_check_timestamp = timestamp(0);              //      Force a check for collision next frame.
3020         }
3021 }
3022
3023 //      --------------------------------------------------------------------------
3024 //      Set *attacked as object to attack for object *attacker
3025 //      If attacked == NULL, then attack any enemy object.
3026 //      Attack point *rel_pos on object.  This is for supporting attacking subsystems.
3027 void ai_attack_object(object *attacker, object *attacked, int priority, ship_subsys *ssp)
3028 {
3029         ai_info *aip;
3030
3031         Assert(attacker != NULL);
3032         Assert(attacker->instance != -1);
3033         Assert(Ships[attacker->instance].ai_index != -1);
3034
3035         aip = &Ai_info[Ships[attacker->instance].ai_index];
3036         force_avoid_player_check(attacker, aip);
3037
3038         aip->ok_to_target_timestamp = timestamp(0);             //      Guarantee we can target.
3039
3040 //      if (!strnicmp(Ships[attacker->instance].ship_name, NOX("Kami"), 4)) {
3041 //              aip->ai_flags |= AIF_KAMIKAZE;
3042 //              aip->ai_flags |= AIF_NO_DYNAMIC;
3043 //      }
3044
3045         if (attacker == attacked) {
3046                 Int3();         //      Bogus!  Who tried to get me to attack myself!  Trace out and fix!
3047                 return;
3048         }
3049
3050         //      Only set to chase if a fighter or bomber, otherwise just return.
3051         if (!(Ship_info[Ships[attacker->instance].ship_info_index].flags & SIF_SMALL_SHIP)) {
3052 //              nprintf(("AI","Note: AI ship %s refusing to set AI mode to AIM_CHASE\n", Ships[attacker->instance].ship_name));
3053 //              return;
3054                 nprintf(("AI", "AI ship %s is large ship ordered to attack %s\n", Ships[attacker->instance].ship_name, Ships[attacked->instance].ship_name));
3055         }
3056
3057         //      This is how "engage enemy" gets processed
3058         if (attacked == NULL) {
3059                 aip->choose_enemy_timestamp = timestamp(0);
3060                 // nebula safe
3061                 set_target_objnum(aip, find_enemy(attacker-Objects, 99999.9f, 4));
3062         } else {
3063                 // check if we can see atacked in nebula
3064                 if (aip->target_objnum != attacked - Objects) {
3065                         aip->aspect_locked_time = 0.0f;
3066                 }
3067                 set_target_objnum(aip, attacked - Objects);
3068         }
3069
3070         ai_set_goal_maybe_abort_dock(attacker, aip);
3071         aip->ok_to_target_timestamp = timestamp(DELAY_TARGET_TIME);     //      No dynamic targeting for 7 seconds.
3072
3073         if (is_ignore_object(aip, aip->target_objnum)) {
3074                 aip->ignore_objnum = UNUSED_OBJNUM;
3075         }
3076
3077         aip->mode = AIM_CHASE;
3078         aip->submode = SM_ATTACK;       // AL 12-15-97: need to set submode?  I got an assert() where submode was bogus
3079                                                                                 //                                       for AIM_CHASE... it may have been not set correctly here
3080         if (ssp == NULL) {
3081                 set_targeted_subsys(aip, NULL, -1);
3082                 if (aip->target_objnum != -1) {
3083                         //nprintf(("AI", "Unprotecting ship %s\n", Ships[Objects[aip->target_objnum].instance].ship_name));
3084                         Objects[aip->target_objnum].flags &= ~OF_PROTECTED;     //      If ship had been protected, unprotect it.
3085                 }
3086         } else {
3087                 Int3(); //      Not supported yet!
3088         }
3089 }
3090
3091 //      --------------------------------------------------------------------------
3092 //      Set *attacked as object to attack for object *attacker
3093 //      Attack point *rel_pos on object.  This is for supporting attacking subsystems.
3094 void ai_attack_wing(object *attacker, int wingnum, int priority)
3095 {
3096         ai_info *aip;
3097
3098         Assert(attacker != NULL);
3099         Assert(attacker->instance != -1);
3100         Assert(Ships[attacker->instance].ai_index != -1);
3101
3102         aip = &Ai_info[Ships[attacker->instance].ai_index];
3103
3104         aip->enemy_wing = wingnum;
3105         aip->mode = AIM_CHASE;
3106         aip->submode = SM_ATTACK;       // AL 12-15-97: need to set submode?  I got an assert() where submode was bogus
3107                                                                                 //                                       for AIM_CHASE... it may have been not set correctly here
3108
3109         aip->ok_to_target_timestamp = timestamp(0);             //      Guarantee we can target.
3110
3111         int count = Wings[wingnum].current_count;
3112         if (count > 0) {
3113                 int     index;
3114
3115                 index = (int) (frand() * count);
3116
3117                 if (index >= count)
3118                         index = 0;
3119
3120                 set_target_objnum(aip, Ships[Wings[wingnum].ship_index[index]].objnum);
3121
3122                 ai_set_goal_maybe_abort_dock(attacker, aip);
3123                 aip->ok_to_target_timestamp = timestamp(DELAY_TARGET_TIME);     //      No dynamic targeting for 7 seconds.
3124         }
3125 }
3126
3127 //      --------------------------------------------------------------------------
3128 //      Set *evaded as object for *evader to evade.
3129 void ai_evade_object(object *evader, object *evaded, int priority)
3130 {
3131         ai_info *aip;
3132
3133         Assert(evader != NULL);
3134         Assert(evaded != NULL);
3135         Assert(evader->instance != -1);
3136         Assert(Ships[evader->instance].ai_index != -1);
3137
3138         if (evaded == evader) {
3139                 Int3(); //      Bogus!  Who tried to get me to evade myself!  Trace out and fix!
3140                 return;
3141         }
3142
3143         aip = &Ai_info[Ships[evader->instance].ai_index];
3144
3145         set_target_objnum(aip, evaded - Objects);
3146         aip->mode = AIM_EVADE;
3147
3148 }
3149
3150 //      Ignore some object without changing mode.
3151 void ai_ignore_object(object *ignorer, object *ignored, int priority)
3152 {
3153         ai_info *aip;
3154
3155         Assert(ignorer != NULL);
3156         Assert(ignored != NULL);
3157         Assert(ignorer->instance != -1);
3158         Assert(Ships[ignorer->instance].ai_index != -1);
3159         Assert(ignorer != ignored);
3160
3161         aip = &Ai_info[Ships[ignorer->instance].ai_index];
3162
3163         //      MK, 5/17/98, removing ignoring of wings.
3164         //      It's too confusing.  It often causes mysterious behavior in which fighters unexpectedly refuse to attack anything.
3165 /*      if (Ships[ignored->instance].wingnum > -1) {
3166                 int wingnum, i;
3167
3168                 wingnum = Ships[ignored->instance].wingnum;
3169                 aip->ignore_objnum = -(wingnum+1);
3170                 // set protected bit for each ship in a wing
3171                 //      MK, 4/23/98: Only set for fighters if they are the original "ignored" object
3172                 for (i = 0; i < Wings[wingnum].current_count; i++ ) {
3173                         object  *objp;
3174
3175                         objp = &Objects[Ships[Wings[wingnum].ship_index[i]].objnum];
3176                         if (objp != ignored) {
3177                                 if (Ship_info[Ships[objp->instance].ship_info_index].flags & (SIF_FIGHTER | SIF_BOMBER))
3178                                         continue;
3179                         }
3180
3181                         Objects[Ships[Wings[wingnum].ship_index[i]].objnum].flags |= OF_PROTECTED;
3182                 }
3183
3184         } else {
3185         */ {
3186                 aip->ignore_objnum = ignored - Objects;
3187                 aip->ignore_signature = ignored->signature;
3188                 aip->ai_flags &= ~AIF_TEMPORARY_IGNORE;
3189                 ignored->flags |= OF_PROTECTED;                                 // set protected bit of ignored ship.
3190         }
3191
3192 }
3193
3194 //      Ignore some object without changing mode.
3195 void ai_ignore_wing(object *ignorer, int wingnum, int priority)
3196 {
3197         ai_info *aip;
3198
3199         Assert(ignorer != NULL);
3200         Assert(ignorer->instance != -1);
3201         Assert(Ships[ignorer->instance].ai_index != -1);
3202         Assert((wingnum >= 0) && (wingnum < MAX_WINGS));
3203
3204         aip = &Ai_info[Ships[ignorer->instance].ai_index];
3205
3206         aip->ignore_objnum = -(wingnum +1);
3207         aip->ai_flags &= ~AIF_TEMPORARY_IGNORE;
3208 }
3209
3210
3211 //      Add a path point in the global buffer Path_points.
3212 //      modify_index = index in Path_points at which to store path point.
3213 //      If modify_index == -1, then create a new point.
3214 //      If a new point is created (ie, modify_index == -1), then Ppfp is updated.
3215 void add_path_point(vector *pos, int path_num, int path_index, int modify_index)
3216 {
3217         pnode   *pnp;
3218
3219         if (modify_index == -1) {
3220                 Assert(Ppfp-Path_points < MAX_PATH_POINTS-1);
3221                 pnp = Ppfp;
3222                 Ppfp++;
3223         } else {
3224                 Assert((modify_index >= 0) && (modify_index < MAX_PATH_POINTS-1));
3225                 pnp = &Path_points[modify_index];
3226         }
3227
3228         pnp->pos = *pos;
3229         pnp->path_num = path_num;
3230         pnp->path_index = path_index;
3231 }
3232
3233 //      Given two points on a sphere, the center of the sphere and the radius, return a
3234 //      point on the vector through the midpoint of the chord on the sphere.
3235 void bisect_chord(vector *p0, vector *p1, vector *centerp, float radius)
3236 {
3237         vector  tvec;
3238         vector  new_pnt;
3239
3240         vm_vec_add(&tvec, p0, p1);
3241         vm_vec_sub2(&tvec, centerp);
3242         vm_vec_sub2(&tvec, centerp);
3243         if (vm_vec_mag_quick(&tvec) < 0.1f) {
3244                 vm_vec_sub(&tvec, p0, p1);
3245                 if (fl_abs(tvec.x) <= fl_abs(tvec.z)){
3246                         tvec.x = -tvec.z;
3247                 } else {
3248                         tvec.y = -tvec.x;
3249                 }
3250         }
3251
3252         vm_vec_normalize(&tvec);
3253         vm_vec_scale(&tvec, radius);
3254         vm_vec_add(&new_pnt, centerp, &tvec);
3255
3256         add_path_point(&new_pnt, -1, -1, -1);
3257 }
3258                         
3259 //      Create a path from the current position to a goal position.
3260 //      The current position is in the current object and the goal position is
3261 //      in the goal object.
3262 //      It is ok to intersect the current object, but not the goal object.
3263 //      This function is useful for creating a path to an initial point near a large
3264 //      object.
3265 //
3266 // input:       subsys_path:    optional param (default 0), indicates this is a path to a subsystem
3267 void create_path_to_point(vector *curpos, vector *goalpos, object *curobjp, object *goalobjp, int subsys_path)
3268 {
3269         //      If can't cast vector to goalpos, then create an intermediate point.
3270         if (pp_collide(curpos, goalpos, goalobjp, curobjp->radius)) {
3271                 vector  tan1;
3272                 float           radius;
3273
3274                 // If this is a path to a subsystem, use SUBSYS_PATH_DIST as the radius for the object you are
3275                 // trying to avoid.  This is needed since subsystem paths extend out to SUBSYS_PATH_DIST, and we
3276                 // want ships to reach their path destination without flying to points that sit on the radius of
3277                 // a small ship
3278                 radius = goalobjp->radius;
3279                 if (subsys_path) {
3280                         if ( SUBSYS_PATH_DIST > goalobjp->radius ) {
3281                                 radius = SUBSYS_PATH_DIST;
3282                         }
3283                 }
3284
3285                 //      The intermediate point is at the intersection of:
3286                 //              tangent to *goalobjp sphere at point *goalpos
3287                 //              tangent to *goalobjp sphere through point *curpos in plane defined by *curpos, *goalpos, goalobjp->pos
3288                 //      Note, there are two tangents through *curpos, unless *curpos is on the
3289                 //      sphere.  The tangent that causes the nearer intersection (to *goalpos) is chosen.
3290                 get_tangent_point(&tan1, curpos, &goalobjp->pos, goalpos, radius);
3291
3292                 //      If we can't reach tan1 from curpos, insert a new point.
3293                 if (pp_collide(&tan1, curpos, goalobjp, curobjp->radius))
3294                         bisect_chord(curpos, &tan1, &goalobjp->pos, radius);
3295
3296                 add_path_point(&tan1, -1, -1, -1);
3297
3298                 //      If we can't reach goalpos from tan1, insert a new point.
3299                 if (pp_collide(goalpos, &tan1, goalobjp, curobjp->radius))
3300                         bisect_chord(goalpos, &tan1, &goalobjp->pos, radius);
3301         }
3302
3303 }
3304
3305 //      Given an object and a model path, globalize the points on the model
3306 //      and copy into the global path list.
3307 //      If pnp != NULL, then modify, in place, the path points.  This is used to create new
3308 //      globalized points when the base object has moved.
3309 // input:       randomize_pnt   => optional parameter (default value -1), add random vector in sphere to this path point
3310 void copy_xlate_model_path_points(object *objp, model_path *mp, int dir, int count, int path_num, pnode *pnp, int randomize_pnt)
3311 {
3312         matrix  m;
3313         int             i;
3314         vector  v1;
3315         int             pp_index;               //      index in Path_points at which to store point, if this is a modify-in-place (pnp ! NULL)
3316         int             start_index, finish_index;
3317         
3318         // nprintf(("AI", "Creating path for object %s in frame #%i\n", Ships[objp->instance].ship_name, AI_FrameCount));
3319         
3320         //      Initialize pp_index.
3321         //      If pnp == NULL, that means we're creating new points.  If not NULL, then modify in place.
3322         if (pnp == NULL)
3323                 pp_index = -1;                  //      This tells add_path_point to create a new point.
3324         else
3325                 pp_index = 0;                   //      pp_index will get assigned to index in Path_points to reuse.
3326
3327         vm_copy_transpose_matrix(&m, &objp->orient);
3328
3329         if (dir == 1) {
3330                 start_index = 0;
3331                 finish_index = min(count, mp->nverts);
3332         } else {
3333                 Assert(dir == -1);      //      direction must be up by 1 or down by 1 and it's neither!
3334                 start_index = mp->nverts-1;
3335                 finish_index = max(-1, mp->nverts-1-count);
3336         }
3337
3338         int offset = 0;
3339         for (i=start_index; i != finish_index; i += dir) {
3340                 //      Globalize the point.
3341                 vm_vec_rotate(&v1, &mp->verts[i].pos, &m);
3342                 vm_vec_add2(&v1, &objp->pos);
3343
3344                 if ( randomize_pnt == i ) {
3345                         vector v_rand;
3346                         static_randvec(OBJ_INDEX(objp), &v_rand);
3347                         vm_vec_scale(&v_rand, 30.0f);
3348                         vm_vec_add2(&v1, &v_rand);
3349                 }
3350
3351                 if (pp_index != -1)
3352                         pp_index = pnp-Path_points + offset;
3353
3354                 add_path_point(&v1, path_num, i, pp_index);
3355                 offset++;
3356         }
3357 }
3358
3359
3360 //      For pl_objp, create a path along path path_num into mobjp.
3361 //      The tricky part of this problem is creating the entry to the first point on the
3362 //      predefined path.  The points on this entry path are based on the location of Pl_objp
3363 //      relative to the start of the path.
3364 //
3365 // input:
3366 //                              subsys_path:    optional param (default 0), indicating this is a path to a subsystem
3367 void create_model_path(object *pl_objp, object *mobjp, int path_num, int subsys_path)
3368 {       
3369         ship                    *shipp = &Ships[pl_objp->instance];
3370         ai_info         *aip = &Ai_info[shipp->ai_index];
3371
3372         ship_info       *osip = &Ship_info[Ships[mobjp->instance].ship_info_index];
3373         polymodel       *pm = model_get(osip->modelnum);
3374         int                     num_points;
3375         model_path      *mp;
3376         pnode                   *ppfp_start = Ppfp;
3377         matrix          m;
3378         vector          gp0;
3379
3380         Assert(path_num >= 0);
3381
3382         //      Do garbage collection if necessary.
3383         if (Ppfp-Path_points + 64 > MAX_PATH_POINTS) {
3384                 garbage_collect_path_points();
3385                 ppfp_start = Ppfp;
3386         }
3387
3388         aip->path_start = Ppfp - Path_points;
3389         Assert(path_num < pm->n_paths);
3390         
3391         mp = &pm->paths[path_num];
3392         num_points = mp->nverts;
3393
3394         Assert(Ppfp-Path_points + num_points + 4 < MAX_PATH_POINTS);
3395
3396         vm_copy_transpose_matrix(&m, &mobjp->orient);
3397         vm_vec_rotate(&gp0, &mp->verts[0].pos, &m);
3398         vm_vec_add2(&gp0, &mobjp->pos);
3399
3400         if (pp_collide(&pl_objp->pos, &gp0, mobjp, pl_objp->radius)) {
3401                 vector  perim_point1;
3402                 vector  perim_point2;
3403
3404                 perim_point2 = pl_objp->pos;
3405                 
3406                 //      If object that wants to dock is inside bounding sphere of object it wants to dock with, make it fly out.
3407                 //      Assume it can fly "straight" out to the bounding sphere.
3408                 if (vm_vec_dist_quick(&pl_objp->pos, &mobjp->pos) < mobjp->radius) {
3409                         project_point_to_perimeter(&perim_point2, &mobjp->pos, mobjp->radius, &pl_objp->pos);
3410                         add_path_point(&perim_point2, path_num, -1, -1);
3411                 }
3412
3413                 //      If last point on pre-defined path is inside bounding sphere, create a new point on the surface of the sphere.
3414                 if (vm_vec_dist_quick(&mobjp->pos, &gp0) < mobjp->radius) {
3415                         project_point_to_perimeter(&perim_point1, &mobjp->pos, mobjp->radius, &gp0);
3416                         create_path_to_point(&perim_point2, &perim_point1, pl_objp, mobjp, subsys_path);
3417                         add_path_point(&perim_point1, path_num, -1, -1);
3418                 } else {                //      The predefined path extends outside the sphere.  Create path to that point.
3419                         create_path_to_point(&perim_point2, &gp0, pl_objp, mobjp, subsys_path);
3420                 }
3421         }
3422
3423         // AL 12-31-97: If following a subsystem path, add random vector to second last path point
3424         if ( subsys_path ) {
3425                 copy_xlate_model_path_points(mobjp, mp, 1, mp->nverts, path_num, NULL, mp->nverts-2);
3426         } else {
3427                 copy_xlate_model_path_points(mobjp, mp, 1, mp->nverts, path_num, NULL);
3428         }
3429
3430         aip->path_cur = aip->path_start;
3431         aip->path_dir = PD_FORWARD;
3432         aip->path_objnum = mobjp-Objects;
3433         aip->mp_index = path_num;
3434         aip->path_length = Ppfp - ppfp_start;
3435         aip->path_next_check_time = timestamp(1);
3436
3437         aip->path_goal_obj_hash = create_object_hash(&Objects[aip->path_objnum]);
3438
3439         aip->path_next_create_time = timestamp(1000);   //      OK to try to create one second later
3440         aip->path_create_pos = pl_objp->pos;
3441         aip->path_create_orient = pl_objp->orient;
3442
3443         aip->ai_flags &= ~AIF_USE_EXIT_PATH;                    // ensure this flag is cleared
3444 }
3445
3446 //      For pl_objp, create a path along path path_num into mobjp.
3447 //      The tricky part of this problem is creating the entry to the first point on the
3448 //      predefined path.  The points on this entry path are based on the location of pl_objp
3449 //      relative to the start of the path.
3450 void create_model_exit_path(object *pl_objp, object *mobjp, int path_num, int count)
3451 {       
3452         ship                    *shipp = &Ships[pl_objp->instance];
3453         ai_info         *aip = &Ai_info[shipp->ai_index];
3454
3455         ship_info       *osip = &Ship_info[Ships[mobjp->instance].ship_info_index];
3456         polymodel       *pm = model_get(osip->modelnum);
3457         int                     num_points;
3458         model_path      *mp;
3459         pnode                   *ppfp_start = Ppfp;
3460
3461         aip->path_start = Ppfp - Path_points;
3462         Assert(path_num < pm->n_paths);
3463         
3464         mp = &pm->paths[path_num];
3465         num_points = mp->nverts;
3466
3467         Assert(Ppfp-Path_points + num_points + 4 < MAX_PATH_POINTS);
3468
3469         copy_xlate_model_path_points(mobjp, mp, -1, count, path_num, NULL);
3470
3471         aip->path_cur = aip->path_start;
3472         aip->path_dir = PD_FORWARD;
3473         aip->path_objnum = mobjp-Objects;
3474         aip->mp_index = path_num;
3475         aip->path_length = Ppfp - ppfp_start;
3476         aip->path_next_check_time = timestamp(1);
3477
3478         aip->ai_flags |= AIF_USE_EXIT_PATH;             // mark as exit path, referenced in maybe
3479 }
3480
3481 //      Return true if the vector from curpos to goalpos intersects with any ship other than the ignore objects.
3482 //      Calls pp_collide
3483 int pp_collide_any(vector *curpos, vector *goalpos, float radius, object *ignore_objp1, object *ignore_objp2, int big_only_flag)
3484 {
3485         ship_obj        *so;    
3486
3487         for ( so = GET_FIRST(&Ship_obj_list); so != END_OF_LIST(&Ship_obj_list); so = GET_NEXT(so) ) {
3488                 object *objp = &Objects[so->objnum];
3489
3490                 if (big_only_flag) {
3491                         if (!(Ship_info[Ships[objp->instance].ship_info_index].flags & (SIF_BIG_SHIP | SIF_HUGE_SHIP)))
3492                                 continue;
3493                 }
3494
3495                 if ((objp != ignore_objp1) && (objp != ignore_objp2)) {
3496                         if (pp_collide(curpos, goalpos, objp, radius))
3497                                 return OBJ_INDEX(objp);
3498                 }
3499         }
3500
3501         return -1;
3502 }
3503
3504 //      Used to create docking paths and other pre-defined paths through ships.
3505 //      Creates a path in absolute space.
3506 //      Create a path into the object objnum.
3507 //
3508 // input:
3509 //      pl_objp:                        object that will use the path
3510 //      objnum:                 Object to find path to.
3511 //      path_num:               model path index to use
3512 //      exit_flag:              true means this is an exit path in the model
3513 // subsys_path: optional param (default 0) that indicates this is a path to a subsystem
3514 //      Exit:
3515 //      ai_info struct in Pl_objp gets stuffed with information to enable Pl_objp to fly the path.
3516 void ai_find_path(object *pl_objp, int objnum, int path_num, int exit_flag, int subsys_path)
3517 {
3518         ai_info *aip = &Ai_info[Ships[pl_objp->instance].ai_index];
3519
3520         Assert(path_num >= 0);
3521
3522         //      This is test code, find an object with paths.
3523         if (objnum != -1) {
3524                 object  *objp = &Objects[objnum];
3525
3526                 if (objp->type == OBJ_SHIP) {
3527                         polymodel *pm;
3528
3529                         ship    *shipp = &Ships[objp->instance];
3530                         pm = model_get( shipp->modelnum );
3531                         Assert(pm->n_paths > path_num);
3532                         aip->goal_objnum = objp-Objects;
3533                         aip->goal_signature = objp->signature;
3534                         if (exit_flag)
3535                                 create_model_exit_path(pl_objp, objp, path_num);
3536                         else
3537                                 create_model_path(pl_objp, objp, path_num, subsys_path);
3538                         return;
3539                 }
3540
3541         }
3542 }
3543
3544 extern int vector_object_collision(vector *start_pos, vector *end_pos, object *objp, float radius_scale);
3545
3546 //      Maybe make *objp avoid a player object.
3547 //      For now, 4/6/98, only check Player_obj.
3548 //      If player collision would occur, set AIF_AVOIDING_SMALL_SHIP bit in ai_flags.
3549 //      Set aip->avoid_goal_point
3550 int maybe_avoid_player(object *objp, vector *goal_pos)
3551 {
3552         ai_info *aip;
3553         vector  cur_pos, new_goal_pos;
3554         object  *player_objp;
3555         vector  n_vec_to_goal, n_vec_to_player;
3556
3557         aip = &Ai_info[Ships[objp->instance].ai_index];
3558
3559         if (!timestamp_elapsed(aip->avoid_check_timestamp))
3560                 return 0;
3561
3562         player_objp = Player_obj;
3563
3564         float   speed_time;
3565
3566         //      How far two ships could be apart and still collide within one second.
3567         speed_time = player_objp->phys_info.speed + objp->phys_info.speed;
3568
3569         float   obj_obj_dist;
3570
3571         obj_obj_dist = vm_vec_dist_quick(&player_objp->pos, &objp->pos);
3572
3573         if (obj_obj_dist > speed_time*2.0f)
3574                 return 0;
3575
3576         cur_pos = objp->pos;
3577
3578         new_goal_pos = *goal_pos;
3579
3580         float dist = vm_vec_normalized_dir(&n_vec_to_goal, goal_pos, &objp->pos);
3581         vm_vec_normalized_dir(&n_vec_to_player, &player_objp->pos, &objp->pos);
3582
3583         if (dist > speed_time*2.0f) {
3584                 vm_vec_scale_add(&new_goal_pos, &objp->pos, &n_vec_to_goal, 200.0f);
3585         }
3586
3587         if (vector_object_collision(&objp->pos, &new_goal_pos, player_objp, 1.5f)) {
3588                 aip->ai_flags |= AIF_AVOIDING_SMALL_SHIP;
3589
3590                 vector  avoid_vec;
3591
3592                 vm_vec_sub(&avoid_vec, &n_vec_to_goal, &n_vec_to_player);
3593                 if (vm_vec_mag_quick(&avoid_vec) < 0.01f) {
3594                         vm_vec_copy_scale(&avoid_vec, &objp->orient.rvec, frand()-0.5f);
3595                         vm_vec_scale_add2(&avoid_vec, &objp->orient.uvec, frand()-0.5f);
3596                         vm_vec_normalize(&avoid_vec);
3597                 } else {
3598                         vector  tvec1;
3599                         vm_vec_normalize(&avoid_vec);
3600                         vm_vec_crossprod(&tvec1, &n_vec_to_goal, &avoid_vec);
3601                         vm_vec_crossprod(&avoid_vec, &tvec1, &n_vec_to_player);
3602                 }
3603
3604                 //      Now, avoid_vec is a vector perpendicular to the vector to the player and the direction *objp
3605                 //      should fly in to avoid the player while still approaching its goal.
3606                 vm_vec_scale_add(&aip->avoid_goal_point, &player_objp->pos, &avoid_vec, 400.0f);
3607
3608                 aip->avoid_check_timestamp = timestamp(1000);
3609
3610                 return 1;
3611         } else {
3612                 aip->ai_flags &= ~AIF_AVOIDING_SMALL_SHIP;
3613                 aip->avoid_check_timestamp = timestamp((int) (obj_obj_dist/200.0f) + 500);
3614
3615                 return 0;
3616         }
3617 }
3618
3619 //      Make object *still_objp enter AIM_STILL mode.
3620 //      Make it point at view_pos.
3621 void ai_stay_still(object *still_objp, vector *view_pos)
3622 {
3623         ship    *shipp;
3624         ai_info *aip;
3625
3626         Assert(still_objp->type == OBJ_SHIP);
3627         Assert((still_objp->instance >= 0) && (still_objp->instance < MAX_OBJECTS));
3628
3629         shipp = &Ships[still_objp->instance];
3630         Assert((shipp->ai_index >= 0) && (shipp->ai_index < MAX_AI_INFO));
3631
3632         aip = &Ai_info[shipp->ai_index];
3633
3634         aip->mode = AIM_STILL;
3635
3636         //      If view_pos not NULL, point at that point.  Else, point at a point directly in front of ship.  Ie, don't turn.
3637         if (view_pos != NULL)
3638                 aip->goal_point = *view_pos;
3639         else
3640                 vm_vec_scale_add(&aip->goal_point, &still_objp->pos, &still_objp->orient.fvec, 100.0f);
3641 }
3642
3643 // code which is called from ai_dock_with_object and ai_dock to set flags and apprioriate variable
3644 // when two objects have completed docking.  used because we can dock object initially at misison load
3645 // time (meaning that ai_dock() might never get called).  docker has docked with dockee (i.e. docker
3646 // would be a freighter and dockee would be a cargo).
3647 void ai_do_objects_docked_stuff(object *docker, object *dockee)
3648 {
3649         ai_info *aip, *other_aip;
3650
3651         aip = &Ai_info[Ships[docker->instance].ai_index];
3652         other_aip = &Ai_info[Ships[dockee->instance].ai_index];
3653
3654         // set the flags and dock_objnum for both objects
3655         aip->ai_flags |= AIF_DOCKED;
3656         aip->dock_objnum = OBJ_INDEX(dockee);
3657         other_aip->ai_flags |= AIF_DOCKED;
3658         other_aip->dock_objnum = OBJ_INDEX(docker);
3659         aip->dock_signature = dockee->signature;
3660         other_aip->dock_signature = docker->signature;
3661
3662         // add multiplayer hook here to deal with docked objects.  We need to only send information
3663         // about the object that is docking.  Both flags will get updated.
3664         if ( MULTIPLAYER_MASTER )
3665                 send_ai_info_update_packet( docker, AI_UPDATE_DOCK );
3666
3667 }
3668
3669 // code which is called when objects become undocked. Equivalent of above function.
3670 // dockee might not be valid since this code can get called to cleanup after a ship
3671 // has blown up!
3672 void ai_do_objects_undocked_stuff( object *docker, object *dockee )
3673 {
3674         ai_info *aip, *other_aip;
3675
3676         // add multiplayer hook here to deal with undocked objects.  Do it before we
3677         // do anything else.  We don't need to send info for both objects, since we can find
3678         // it be dock_objnum
3679         if ( MULTIPLAYER_MASTER )
3680                 send_ai_info_update_packet( docker, AI_UPDATE_UNDOCK );
3681
3682         aip = &Ai_info[Ships[docker->instance].ai_index];
3683
3684         // set the flags and dock_objnum for both objects
3685         aip->ai_flags &= ~(AIF_DOCKED | AIF_BEING_REPAIRED);
3686         aip->dock_objnum = -1;
3687         
3688         if ( dockee != NULL ) {
3689                 other_aip = &Ai_info[Ships[dockee->instance].ai_index];
3690                 other_aip->ai_flags &= ~(AIF_DOCKED | AIF_BEING_REPAIRED);
3691                 other_aip->dock_objnum = -1;
3692         }
3693
3694 }
3695
3696
3697 //      --------------------------------------------------------------------------
3698 //      Interface from goals code to AI.
3699 //      Cause *docker to dock with *dockee.
3700 //      priority is priority of goal from goals code.
3701 //      dock_type is:
3702 //              AIDO_DOCK               set goal of docking
3703 //              AIDO_DOCK_NOW   immediately dock, used for ships that need to be docked at mission start
3704 //              AIDO_UNDOCK             set goal of undocking
3705 void ai_dock_with_object(object *docker, object *dockee, int priority, int dock_type, int docker_index, int dockee_index)
3706 {
3707         ai_info         *aip;
3708         polymodel       *pm;
3709         ai_info         *dockee_aip;
3710
3711         Assert(docker != NULL);
3712         Assert(dockee != NULL);
3713         Assert(docker->instance != -1);
3714         Assert(Ships[docker->instance].ai_index != -1);
3715         Assert(Ships[dockee->instance].ai_index != -1);
3716         Assert( docker_index != -1 );
3717         Assert( dockee_index != -1 );
3718
3719         aip = &Ai_info[Ships[docker->instance].ai_index];
3720
3721         if ((aip->ai_flags & AIF_DOCKED) && (dock_type == AIDO_DOCK)) {
3722                 object  *dockee2;
3723                 int             docker_index2, dockee_index2;
3724
3725                 Assert(aip->dock_objnum > -1);
3726                 dockee2 = &Objects[aip->dock_objnum];
3727                 docker_index2 = aip->dock_index;
3728                 dockee_index2 = aip->dockee_index;
3729                 // MWA -- 2/9/98.  use the goal code to undock the ships since goals might need to get removed
3730                 // and that code will do it properly.  I'd actually be surprised if we got into this code anymore
3731                 // since the outer layer goal code should deal with this issue....but who knows...
3732                 ai_add_goal_ship_internal( aip, AI_GOAL_UNDOCK, NULL, -1, -1, 0 );
3733
3734                 // old code below
3735                 //ai_dock_with_object(docker, dockee2, priority, AIDO_UNDOCK, docker_index2, dockee_index2);
3736                 nprintf(("AI", "Ship %s told to dock with %s, but it was already docked with %s.\n", Ships[docker->instance].ship_name, Ships[dockee->instance].ship_name, Ships[Objects[aip->dock_objnum].instance].ship_name));
3737                 nprintf(("AI", "...so ship %s will now undock.\n", Ships[docker->instance].ship_name));
3738                 return;
3739         }
3740
3741         dockee_aip = &Ai_info[Ships[dockee->instance].ai_index];
3742
3743         aip->goal_objnum = dockee - Objects;
3744         aip->goal_signature = dockee->signature;
3745
3746         aip->mode = AIM_DOCK;
3747
3748         switch (dock_type) {
3749         case AIDO_DOCK:
3750                 aip->submode = AIS_DOCK_0;
3751                 break;
3752         case AIDO_DOCK_NOW:
3753                 aip->submode = AIS_DOCK_3A;
3754                 break;
3755         case AIDO_UNDOCK:
3756                 aip->submode = AIS_UNDOCK_0;
3757                 break;
3758         default:
3759                 Int3();         //      Bogus dock_type.
3760         }
3761
3762         aip->submode_start_time = Missiontime;
3763         aip->dock_index = docker_index;
3764         aip->dockee_index = dockee_index;
3765
3766         dockee_aip->dock_index = dockee_index;
3767         dockee_aip->dockee_index = docker_index;
3768
3769         // get the path number to the docking point on the dockee.  Each docking point contains a list
3770         // of paths that the point can be reached by.  Pick the first path in the path list for now.
3771         // We only want to do this stuff if we are docking!!!  Be sure to set the path index
3772         if ((dock_type == AIDO_DOCK) || (dock_type == AIDO_DOCK_NOW)) {
3773                 pm = model_get( Ships[dockee->instance].modelnum );
3774                 Assert( pm->docking_bays[dockee_index].num_spline_paths > 0 );
3775
3776                 // only set the dock path index if we are docking.  undocking will assume that dock_path_index
3777                 // already set from some other docking command
3778                 aip->dock_path_index = dockee_index;
3779                 dockee_aip->dock_path_index = docker_index;
3780         }
3781
3782         if (dock_type != AIDO_DOCK_NOW) {
3783                 int path_num;
3784                 //      Note: Second parameter is dock path index.  This should be specified as an
3785                 //      _input_ to this function and passed through.  The path index should be already
3786                 // set for the undock function
3787                 path_num = ai_return_path_num_from_dockbay(dockee, dockee_index);
3788                 ai_find_path(docker, dockee-Objects, path_num, 0);
3789 //              ai_find_path(dockee-Objects, dockee_index, 0);
3790         } else {
3791                 dock_orient_and_approach(docker, dockee, DOA_DOCK_STAY);
3792                 //aip->dock_objnum = OBJ_INDEX(dockee);
3793                 ai_do_objects_docked_stuff( docker, dockee );
3794         }
3795
3796 }
3797
3798 //      Cause a ship to fly its waypoints.
3799 //      flags tells:
3800 //              WPF_REPEAT      Set -> repeat waypoints.
3801 void ai_start_waypoints(object *objp, int waypoint_list_index, int wp_flags)
3802 {
3803         ai_info *aip;
3804
3805         Assert(waypoint_list_index < Num_waypoint_lists);
3806
3807         //nprintf(("AI", "Frame %i: Ship %s instructed to fly waypoint list #%i\n", AI_FrameCount, Ships[objp->instance].ship_name, waypoint_list_index));
3808         aip = &Ai_info[Ships[objp->instance].ai_index];
3809
3810         if ( (aip->mode == AIM_WAYPOINTS) && (aip->wp_index == waypoint_list_index) )
3811                 return;
3812
3813         aip->ai_flags |= AIF_FORMATION_WING;
3814         aip->ai_flags &= ~AIF_FORMATION_OBJECT;
3815         aip->wp_list = waypoint_list_index;
3816         aip->wp_index = 0;
3817         aip->wp_flags = wp_flags;
3818         aip->mode = AIM_WAYPOINTS;
3819
3820         Assert(aip->active_goal != AI_ACTIVE_GOAL_DYNAMIC);
3821 }
3822
3823 //      Make *objp stay within dist units of *other_objp
3824 void ai_do_stay_near(object *objp, object *other_objp, float dist)
3825 {
3826         ai_info *aip;
3827
3828         Assert(objp != other_objp);             //      Bogus!  Told to stay near self.
3829         Assert(objp->type == OBJ_SHIP);
3830         Assert((objp->instance >= 0) && (objp->instance < MAX_SHIPS));
3831
3832         aip = &Ai_info[Ships[objp->instance].ai_index];
3833
3834         aip->mode = AIM_STAY_NEAR;
3835         aip->submode = -1;
3836         aip->stay_near_distance = dist;
3837         aip->goal_objnum = other_objp-Objects;
3838         aip->goal_signature = other_objp->signature;
3839
3840 }
3841
3842 //      Make object *objp form on wing of object *goal_objp
3843 void ai_form_on_wing(object *objp, object *goal_objp)
3844 {
3845         ai_info *aip;
3846         ship                    *shipp;
3847         ship_info       *sip;
3848
3849         // objp == goal_objp sometimes in multiplayer when someone leaves a game -- make a simple
3850         // out for this case.
3851         if ( Game_mode & GM_MULTIPLAYER ) {
3852                 if ( objp == goal_objp ) {
3853                         return;
3854                 }
3855         }
3856
3857         Assert(objp != goal_objp);              //      Bogus!  Told to form on own's wing!
3858
3859         shipp = &Ships[objp->instance];
3860         sip = &Ship_info[shipp->ship_info_index];
3861
3862         //      Only fighters or bombers allowed to form on wing.
3863         if (!(sip->flags & (SIF_FIGHTER | SIF_BOMBER))) {
3864                 nprintf(("AI", "Warning: Ship %s tried to form on player's wing, but not fighter or bomber.\n", shipp->ship_name));
3865                 return;
3866         }
3867
3868         aip = &Ai_info[Ships[objp->instance].ai_index];
3869
3870         aip->ai_flags &= ~AIF_FORMATION_WING;
3871         aip->ai_flags |= AIF_FORMATION_OBJECT;
3872
3873         aip->goal_objnum = goal_objp-Objects;
3874         ai_set_goal_maybe_abort_dock(objp, aip);
3875         aip->ok_to_target_timestamp = timestamp(DELAY_TARGET_TIME*4);           //      Super extra long time until can target another ship.
3876
3877 }
3878
3879 //      Given an object and an object on whose wing to form, return slot to use.
3880 //      Optimize:
3881 //              This function is called per object in formation per frame.  Should store slot in ai_info struct.
3882 int ai_formation_object_get_slotnum(int objnum, object *objp)
3883 {
3884         int     slotnum = 1;                    //      Note: Slot #0 means leader, which isn't someone who was told to form-on-wing.
3885         object *o;
3886
3887         for ( o = GET_FIRST(&obj_used_list); o != END_OF_LIST(&obj_used_list); o = GET_NEXT(o) ) {
3888                 if (objp == o)
3889                         break;
3890                 else if (o->type == OBJ_SHIP)
3891                         if (Ai_info[Ships[o->instance].ai_index].ai_flags & AIF_FORMATION_OBJECT)
3892                                 if (Ai_info[Ships[o->instance].ai_index].goal_objnum == objnum)
3893                                         slotnum++;
3894         }
3895
3896         Assert(o != END_OF_LIST(&obj_used_list));       //      Didn't find objp in list of used ships.  Impossible!
3897
3898         return slotnum;
3899 }
3900
3901 #define BIGNUM  100000.0f
3902
3903 int Debug_k = 0;
3904
3905 //      Given an attacker's position and a target's position and velocity, compute the time of
3906 //      intersection of a weapon fired by the attacker with speed weapon_speed.
3907 //      Return this value.  Return value of 0.0f means no collision is possible.
3908 float compute_collision_time(vector *targpos, vector *targvel, vector *attackpos, float weapon_speed)
3909 {
3910         vector  vec_to_target;
3911         float           pos_dot_vel;
3912         float           vel_sqr;
3913         float           discrim;
3914
3915         vm_vec_sub(&vec_to_target, targpos, attackpos);
3916         pos_dot_vel = vm_vec_dot(&vec_to_target, targvel);
3917         vel_sqr = vm_vec_dot(targvel, targvel) - weapon_speed*weapon_speed;
3918         discrim = pos_dot_vel*pos_dot_vel - vel_sqr*vm_vec_dot(&vec_to_target, &vec_to_target);
3919
3920         if (discrim > 0.0f) {
3921                 float   t1, t2, t_solve;
3922
3923                 t1 = (-pos_dot_vel + fl_sqrt(discrim)) / vel_sqr;
3924                 t2 = (-pos_dot_vel - fl_sqrt(discrim)) / vel_sqr;
3925
3926                 t_solve = BIGNUM;
3927
3928                 if (t1 > 0.0f)
3929                         t_solve = t1;
3930                 if ((t2 > 0.0f) && (t2 < t_solve))
3931                         t_solve = t2;
3932
3933                 if (t_solve < BIGNUM-1.0f) {
3934                         return t_solve + Debug_k * flFrametime;
3935                 }
3936         }
3937
3938         return 0.0f;
3939 }
3940
3941
3942 //      --------------------------------------------------------------------------
3943 //      If far away, use player's speed.
3944 //      If in between, lerp between player and laser speed
3945 //      If close, use laser speed.
3946 // Want to know how much time it will take to get to the enemy.
3947 // This function doesn't account for the fact that by the time the player
3948 // (or his laser) gets to the current enemy position, the enemy will have moved.
3949 // This is dealt with in polish_predicted_enemy_pos.
3950 float compute_time_to_enemy(float dist_to_enemy, object *pobjp, object *eobjp)
3951 {
3952         float   time_to_enemy;
3953         float   pl_speed = pobjp->phys_info.speed;
3954         float   max_laser_distance, max_laser_speed;
3955         int     bank_num, weapon_num;
3956         ship    *shipp = &Ships[pobjp->instance];
3957
3958         bank_num = shipp->weapons.current_primary_bank;
3959         weapon_num = shipp->weapons.primary_bank_weapons[bank_num];
3960         max_laser_speed = Weapon_info[weapon_num].max_speed;
3961         max_laser_distance = max_laser_speed * Weapon_info[weapon_num].lifetime;
3962
3963         //      If pretty far away, use player's speed to predict position, else
3964         //      use laser's speed because when close, we care more about hitting
3965         //      with a laser than about causing ship:ship rendezvous.
3966         if (dist_to_enemy > 1.5 * max_laser_distance) {
3967                 if (pl_speed > 0.0f)
3968                         time_to_enemy = dist_to_enemy/pl_speed;
3969                 else
3970                         time_to_enemy = 1.0f;
3971         } else if (dist_to_enemy > 1.1*max_laser_distance) {
3972                 if (pl_speed > 0.1f) {
3973                         float   scale;
3974
3975                         scale = (float) ((dist_to_enemy - max_laser_distance) / max_laser_distance);
3976                 
3977                         time_to_enemy = (float) (dist_to_enemy/(pl_speed * scale + max_laser_speed * (1.0f - scale)));
3978                 } else
3979                         time_to_enemy = 2.0f;
3980         } else
3981                 time_to_enemy = (float) (dist_to_enemy/max_laser_speed);
3982
3983         // return time_to_enemy * (1.0f + Ai_info[Ships[pobjp->instance].ai_index].lead_scale);
3984         return time_to_enemy + flFrametime;
3985 }
3986
3987 //      Stuff *dot and *tts.
3988 //      *dot is always computed.  If dot is less than zero, the magnitude is
3989 //      incorrect, not having been divided by distance.
3990 //      If *dot is > 0.0f, then tts is computed.  This is the time it will take object
3991 //      *objp to get to *pos, assuming it moves right at it.
3992 void fds_aux(float *dot, float *tts, vector *pos, float dtime, object *objp)
3993 {
3994         vector  v2s;
3995
3996         vm_vec_sub(&v2s, pos, &objp->pos);
3997         *dot = vm_vec_dot(&v2s, &objp->orient.fvec);
3998
3999         if (*dot > 0.0f) {
4000                 float   dist;
4001
4002                 dist = vm_vec_dist(&objp->pos, pos);
4003
4004                 if (dist > 0.1f)
4005                         *dot /= dist;
4006                 else
4007                         *dot = 1.0f;
4008
4009                 if (objp->phys_info.speed > 0.1f)
4010                         *tts = dist / objp->phys_info.speed;
4011                 else
4012                         *tts = dist * 100.0f;
4013         }
4014 }
4015
4016 /*
4017 //      Return index of weapon that could hit object *sobjp within dtime seconds.
4018 //      Actual time until impact returned in *atime.
4019 int find_danger_weapon(object *sobjp, float dtime, float *atime, float dot_threshhold)
4020 {
4021         object  *objp, *best_objp = NULL;
4022         float           best_tts = 1000.0f;
4023
4024         for ( objp = GET_FIRST(&obj_used_list); objp !=END_OF_LIST(&obj_used_list); objp = GET_NEXT(objp) ) {
4025                 if ((objp->type == OBJ_WEAPON) && (sobjp-Objects != objp->parent)) {
4026                         float           dot, tts;
4027                         // vector       psp;            //      Predicted ship position.
4028
4029                         //      Get dot and time to current ship position.
4030                         fds_aux(&dot, &tts, &sobjp->pos, dtime, objp);
4031
4032                         //      If dot and tts are in plausible range, do more expensive stuff.
4033                         if (dot > 0.98f) {
4034 //                              float   dot_from_sobjp;
4035                                 vector  v2e;
4036
4037                                 vm_vec_normalized_dir(&v2e, &objp->pos, &sobjp->pos);
4038 //                              dot_from_sobjp = vm_vec_dot(&sobjp->orient.fvec, &v2e);
4039 //                              if (dot_from_sobjp >= dot_threshhold)
4040                                         if (tts < dtime) {
4041                                                 if (tts < best_tts) {
4042                                                         best_tts = tts;
4043                                                         best_objp = objp;
4044                                                 }
4045                                         }
4046                         }
4047                 }
4048         }
4049
4050         *atime = best_tts;
4051
4052         if (best_objp != NULL)
4053                 return best_objp-Objects;
4054         else
4055                 return -1;
4056 }
4057 */
4058
4059 //      --------------------------------------------------------------------------
4060 void ai_set_positions(object *pl_objp, object *en_objp, ai_info *aip, vector *player_pos, vector *enemy_pos)
4061 {
4062         *player_pos = pl_objp->pos;
4063
4064         if (aip->next_predict_pos_time > Missiontime) {
4065                 *enemy_pos = aip->last_predicted_enemy_pos;
4066         } else {
4067                 *enemy_pos = en_objp->pos;
4068
4069                 aip->next_predict_pos_time = Missiontime + Skill_level_delay[Game_skill_level];
4070                 aip->last_predicted_enemy_pos = *enemy_pos;
4071         }
4072
4073
4074 }
4075
4076 //      --------------------------------------------------------------------------
4077 int find_nearest_waypoint(object *objp)
4078 {
4079         int     i;
4080         float   dist, min_dist, dot;
4081         int     min_ind;
4082         ship    *shipp;
4083         int     wp_listnum;
4084         waypoint_list   *wpl;
4085
4086         shipp = &Ships[objp->instance];
4087         wp_listnum = Ai_info[Ships[objp->instance].ai_index].wp_list;
4088         Assert(wp_listnum > 0);
4089         wpl = &Waypoint_lists[wp_listnum];
4090
4091         min_dist = 999999.0f;
4092         min_ind = -1;
4093
4094         for (i=0; i<wpl->count; i++) {
4095                 dist = vm_vec_dist_quick(&objp->pos, &wpl->waypoints[i]);
4096                 dot = vm_vec_dot_to_point(&objp->orient.fvec, &objp->pos, &wpl->waypoints[i]);
4097                 dist = (float) (dist * (1.25 - dot));
4098                 if (dist < min_dist) {
4099                         min_dist = dist;
4100                         min_ind = i;
4101                 }
4102         }
4103
4104         Assert(min_ind != -1);
4105
4106         return min_ind;
4107 }
4108
4109 //      Given an ai_info struct, by reading current goal and path information,
4110 //      extract base path information and return in pmp and pmpv.
4111 //      Return true if found, else return false.
4112 //      false means the current point is not on the original path.
4113 int get_base_path_info(int path_cur, int goal_objnum, model_path **pmp, mp_vert **pmpv)
4114 {
4115         pnode                   *pn = &Path_points[path_cur];
4116         ship_info       *sip = &Ship_info[Ships[Objects[goal_objnum].instance].ship_info_index];
4117         polymodel       *pm = model_get(sip->modelnum);
4118         static          int     debug_last_index = -1;
4119         *pmpv = NULL;
4120         *pmp = NULL;
4121
4122         if (pn->path_num != -1) {
4123                 *pmp = &pm->paths[pn->path_num];
4124                 if (pn->path_index != -1)
4125                         *pmpv = &(*pmp)->verts[pn->path_index];
4126                 else
4127                         return 0;
4128         } else
4129                 return 0;
4130
4131 /*      if (debug_last_index != *pmpv-(*pmp)->verts) {
4132                 debug_last_index = *pmpv-(*pmp)->verts;
4133                 nprintf(("AI", "Point %i has %i turrets: ", *pmpv-(*pmp)->verts, (*pmpv)->nturrets));
4134                 for (int i=0; i<(*pmpv)->nturrets; i++) {
4135                         nprintf(("AI", "%i ", (*pmpv)->turret_ids[i]));
4136                 }
4137                 nprintf(("AI", "\n"));
4138         }
4139 */
4140         return 1;
4141 }
4142
4143 //      Modify, in place, the points in a global model path.
4144 //      Only modify those points that are defined in the model path.  Don't modify the
4145 //      leadin points, such as those that are necessary to get the model on the path.
4146 void modify_model_path_points(object *objp)
4147 {       
4148         ai_info         *aip = &Ai_info[Ships[objp->instance].ai_index];
4149         object          *mobjp = &Objects[aip->path_objnum];
4150         ship_info       *osip = &Ship_info[Ships[mobjp->instance].ship_info_index];
4151         polymodel       *pm = model_get(osip->modelnum);
4152         pnode                   *pnp;
4153         int                     path_num, dir;
4154
4155         Assert((aip->path_start >= 0) && (aip->path_start < MAX_PATH_POINTS));
4156
4157         pnp = &Path_points[aip->path_start];
4158         while ((pnp->path_index == -1) && (pnp-Path_points - aip->path_start < aip->path_length))
4159                 pnp++;
4160
4161         path_num = pnp->path_num;
4162         Assert((path_num >= 0) && (path_num < pm->n_paths));
4163         
4164         Assert(pnp->path_index != -1);  //      If this is -1, that means we never found the model path points
4165
4166         dir = 1;
4167         if ( aip->ai_flags & AIF_USE_EXIT_PATH ) {
4168                 dir = -1;
4169         }
4170
4171         copy_xlate_model_path_points(mobjp, &pm->paths[path_num], dir, pm->paths[path_num].nverts, path_num, pnp);
4172 }
4173
4174 //      Return an indication of the distance between two matrices.
4175 //      This is the sum of the distances of their dot products from 1.0f.
4176 float ai_matrix_dist(matrix *mat1, matrix *mat2)
4177 {
4178         float   t;
4179
4180         t =  1.0f - vm_vec_dot(&mat1->fvec, &mat2->fvec);
4181         t += 1.0f - vm_vec_dot(&mat1->uvec, &mat2->uvec);
4182         t += 1.0f - vm_vec_dot(&mat1->rvec, &mat2->rvec);
4183
4184         return t;
4185 }
4186
4187
4188 //      Paths are created in absolute space, so a moving object needs to have model paths within it recreated.
4189 //      This uses the hash functions which means the slightest movement will cause a recreate, though the timestamp
4190 //      prevents this from happening too often.
4191 //      force_recreate_flag TRUE means to recreate regardless of timestamp.
4192 //      Returns TRUE if path recreated.
4193 float maybe_recreate_path(object *objp, ai_info *aip, int force_recreate_flag)
4194 {
4195         int     hashval;
4196
4197         Assert(&Ai_info[Ships[objp->instance].ai_index] == aip);
4198
4199         if ((aip->mode == AIM_BAY_EMERGE) || (aip->mode == AIM_BAY_DEPART))
4200                 if ((OBJ_INDEX(objp) % 4) == (Framecount % 4))
4201                         force_recreate_flag = 1;
4202
4203         //      If no path, that means we don't need one.
4204         if (aip->path_start == -1)
4205                 return 0.0f;
4206
4207         // AL 11-12-97: If AIF_USE_STATIC_PATH is set, don't try to recreate.  This is needed when ships
4208         //                                  emerge from fighter bays.  We don't need to recreate the path.. and in case the 
4209         //              parent ship dies, we still want to be able to continue on the path
4210         if ( aip->ai_flags & AIF_USE_STATIC_PATH ) 
4211                 return 0.0f;
4212
4213         if (force_recreate_flag || timestamp_elapsed(aip->path_next_create_time)) {
4214                 object  *path_objp;
4215
4216                 path_objp = &Objects[aip->path_objnum];
4217
4218                 if ((hashval = create_object_hash(path_objp)) != aip->path_goal_obj_hash) {
4219                         float dist;
4220                         
4221                         dist = vm_vec_dist_quick(&path_objp->pos, &aip->path_create_pos);
4222                         dist += ai_matrix_dist(&path_objp->orient, &aip->path_create_orient) * 25.0f;
4223
4224                         if (force_recreate_flag || (dist > 2.0f)) {
4225                                 aip->path_next_create_time = timestamp(1000);   //      Update again in as little as 1000 milliseconds, ie 1 second.
4226                                 aip->path_goal_obj_hash = hashval;
4227                                 modify_model_path_points(objp);
4228
4229                                 aip->path_create_pos = path_objp->pos;
4230                                 aip->path_create_orient = path_objp->orient;
4231                                 
4232                                 return dist;
4233                         }
4234                 }
4235         }
4236
4237         return 0.0f;
4238 }
4239
4240 //      Set acceleration for ai_dock().
4241 void set_accel_for_docking(object *objp, ai_info *aip, float dot, float dot_to_next, float dist_to_next, float dist_to_goal, ship_info *sip)
4242 {
4243         float prev_dot_to_goal = aip->prev_dot_to_goal;
4244         
4245         aip->prev_dot_to_goal = dot;
4246
4247         if (objp->phys_info.speed < 0.0f) {
4248                 accelerate_ship(aip, 1.0f/32.0f);
4249         } else if ((prev_dot_to_goal-dot) > 0.01) {
4250                 if (prev_dot_to_goal > dot + 0.05f) {
4251                         accelerate_ship(aip, 0.0f);
4252                 } else {
4253                         change_acceleration(aip, -1.0f);        //      -1.0f means subtract off flFrametime from acceleration value in 0.0..1.0
4254                 }
4255         } else {
4256                 if ((aip->mode == AIM_DOCK) && (dist_to_next < 150.0f) && (aip->path_start + aip->path_length - 2 == aip->path_cur)) {
4257                         set_accel_for_target_speed(objp, sip->max_speed * max(dist_to_next/500.0f, 1.0f));
4258                         //mprintf(("dist = %7.3f, speed = %7.3f\n", dist_to_next, objp->phys_info.speed));
4259                 } else if ((dot_to_next >= dot * .9) || (dist_to_next > 100.0f)) {
4260                         if (dist_to_goal > 200.0f)
4261                                 set_accel_for_target_speed(objp, sip->max_speed * (dot + 1.0f) / 2.0f);
4262                         else {
4263                                 float   xdot;
4264
4265                                 xdot = (dot_to_next + dot)/2.0f;
4266                                 if (xdot < 0.0f)
4267                                         xdot = 0.0f;
4268
4269                                 // AL: if following a path not in dock mode, move full speed
4270                                 if (( aip->mode != AIM_DOCK ) && (dot > 0.9f)) {
4271                                         set_accel_for_target_speed(objp, sip->max_speed*dot*dot*dot);
4272                                 } else {
4273                                         if ((aip->path_cur - aip->path_start < aip->path_length-2) && (dist_to_goal < 2*objp->radius)) {
4274                                                 //nprintf(("AI", "Target speed = %7.3f\n", dist_to_goal/8.0f));
4275                                                 set_accel_for_target_speed(objp, dist_to_goal/8.0f + 2.0f);
4276                                         } else {
4277                                                 set_accel_for_target_speed(objp, sip->max_speed * (2*xdot + 0.25f)/4.0f);
4278                                         }
4279                                 }
4280                         }
4281                 } else {
4282                         float   xdot;
4283
4284                         xdot = max(dot_to_next, 0.1f);
4285                         if ( aip->mode != AIM_DOCK ) {
4286                                 set_accel_for_target_speed(objp, sip->max_speed);
4287                         } else {
4288                                 float   speed;
4289                                 if ((aip->path_cur - aip->path_start < aip->path_length-2) && (dist_to_goal < 2*objp->radius)) {
4290                                         speed = dist_to_goal/8.0f + 2.0f;
4291                                 } else if (dist_to_goal < 4*objp->radius + 50.0f) {
4292                                         speed = dist_to_goal/4.0f + 4.0f;
4293                                 } else {
4294                                         speed = sip->max_speed * (3*xdot + 1.0f)/4.0f;
4295                                 }
4296                                 if (aip->mode == AIM_DOCK) {
4297                                         speed = speed * 2.0f + 1.0f;
4298                                         if (aip->goal_objnum != -1) {
4299                                                 speed += Objects[aip->goal_objnum].phys_info.speed;
4300                                         }
4301                                 }
4302
4303                                 set_accel_for_target_speed(objp, speed);
4304                         }
4305                 }
4306         }
4307 }
4308
4309 //      --------------------------------------------------------------------------
4310 //      Follow a path associated with a large object, such as a capital ship.
4311 //      The points defined on the path are in the object's reference frame.
4312 //      The object of interest is goal_objnum.
4313 //      The paths are defined in the model.  The path of interest is wp_list.
4314 //      The next goal point in the path is wp_index.
4315 //      wp_flags contain special information specific to the path.
4316
4317 // The path vertices are defined by model_path structs:
4318 //              typedef struct model_path {
4319 //                      char            name[MAX_NAME_LEN];                                     // name of the subsystem.  Probably displayed on HUD
4320 //                      int             nverts;
4321 //                      vector  *verts;
4322 //              } model_path;
4323
4324 //      The polymodel struct for the object contains the following:
4325 //              int                     n_paths;
4326 //              model_path      *paths;
4327
4328 //      Returns distance to goal point.
4329 float ai_path()
4330 {
4331         polymodel       *pm;
4332         int             num_paths, num_points;
4333         float           dot, dist_to_goal, dist_to_next, speed, dot_to_next;
4334         ship            *shipp = &Ships[Pl_objp->instance];
4335         ship_info       *sip = &Ship_info[shipp->ship_info_index];
4336         ai_info *aip;
4337         vector  nvel_vec;
4338         float           mag, prev_dot_to_goal;
4339         vector  temp_vec, *slop_vec;
4340         object  *gobjp;
4341         ship            *gshipp;
4342         vector  *cvp, *nvp, next_vec, gcvp, gnvp;               //      current and next vertices in global coordinates.
4343
4344         aip = &Ai_info[Ships[Pl_objp->instance].ai_index];
4345
4346         Assert(aip->goal_objnum != -1);
4347         Assert(Objects[aip->goal_objnum].type == OBJ_SHIP);
4348
4349         gobjp = &Objects[aip->goal_objnum];
4350         gshipp = &Ships[gobjp->instance];
4351
4352         pm = model_get( gshipp->modelnum );
4353         num_paths = pm->n_paths;
4354         Assert(num_paths > 0);
4355
4356         if (aip->path_start == -1) {
4357                 int path_num;
4358                 path_num = ai_return_path_num_from_dockbay(&Objects[aip->goal_objnum], aip->dockee_index);
4359                 Assert(aip->goal_objnum >= 0 && aip->goal_objnum < MAX_OBJECTS);
4360                 ai_find_path(Pl_objp, aip->goal_objnum, path_num, 0);
4361         }
4362
4363         // nprintf(("AI", "Frame: %i, Path index = %i/%i\n", AI_FrameCount, aip->path_cur-aip->path_start, aip->path_length));
4364
4365         maybe_recreate_path(Pl_objp, aip, 0);
4366
4367         num_points = aip->path_length;
4368
4369         //      Set cvp and nvp as pointers to current and next vertices of interest on path.
4370         cvp = &Path_points[aip->path_cur].pos;
4371         if ((aip->path_cur + aip->path_dir - aip->path_start < num_points) || (aip->path_cur + aip->path_dir < aip->path_start))
4372                 nvp = &Path_points[aip->path_cur + aip->path_dir].pos;
4373         else {
4374                 //      If this is 0, then path length must be 1 which means we have no direction!
4375                 Assert((aip->path_cur - aip->path_dir >= aip->path_start) && (aip->path_cur - aip->path_dir - aip->path_start < num_points));
4376                 //      Cleanup for above Assert() which we hit too near release. -- MK, 5/24/98.
4377                 if (aip->path_cur - aip->path_dir - aip->path_start >= num_points) {
4378                         if (aip->path_dir == 1)
4379                                 aip->path_cur = aip->path_start;
4380                         else
4381                                 aip->path_cur = aip->path_start + num_points - 1;
4382                 }
4383
4384                 vector  delvec;
4385                 vm_vec_sub(&delvec, cvp, &Path_points[aip->path_cur - aip->path_dir].pos);
4386                 vm_vec_normalize(&delvec);
4387                 vm_vec_scale_add(&next_vec, cvp, &delvec, 10.0f);
4388                 nvp = &next_vec;
4389         }
4390
4391         //      Interrupt if can't get to current goal point.  Debug only.
4392 /*      if (pp_collide(&Pl_objp->pos, cvp, gobjp, Pl_objp->radius)) {
4393                 Int3();
4394         }
4395 */
4396         //      See if can reach next point (as opposed to current point)
4397         //      However, don't do this if docking and next point is last point.
4398         //      That is, we don't want to pursue the last point under control of the
4399         //      path code.  In docking, this is a special hack.
4400         if ((aip->mode != AIM_DOCK) || ((aip->path_cur-aip->path_start) < num_points - 2)) {
4401                 if ((aip->path_cur + aip->path_dir > aip->path_start) && (aip->path_cur + aip->path_dir < aip->path_start + num_points-2)) {
4402                         if ( timestamp_elapsed(aip->path_next_check_time)) {
4403                                 aip->path_next_check_time = timestamp( 3000 );
4404                                 if (!pp_collide(&Pl_objp->pos, nvp, gobjp, 1.1f * Pl_objp->radius)) {
4405                                         cvp = nvp;
4406                                         aip->path_cur += aip->path_dir;
4407                                         nvp = &Path_points[aip->path_cur].pos;
4408                                         //nprintf(("AI", "Reach: Advancing from point %i to %i of %i points.\n", aip->path_cur-aip->path_dir, aip->path_cur, num_points));
4409                                 }
4410                         }
4411                 }
4412         }
4413
4414         gcvp = *cvp;
4415         gnvp = *nvp;
4416
4417         speed = Pl_objp->phys_info.speed;
4418
4419         dist_to_goal = vm_vec_dist_quick(&Pl_objp->pos, &gcvp);
4420         dist_to_next = vm_vec_dist_quick(&Pl_objp->pos, &gnvp);
4421         //      Can't use fvec, need to use velocity vector because we aren't necessarily
4422         //      moving in the direction we're facing.
4423
4424 //      if (IS_VEC_NULL(&Pl_objp->phys_info.vel)) {
4425         if ( vm_vec_mag_quick(&Pl_objp->phys_info.vel) < AICODE_SMALL_MAGNITUDE ) {
4426                 mag = 0.0f;
4427                 vm_vec_zero(&nvel_vec);
4428         } else
4429                 mag = vm_vec_copy_normalize(&nvel_vec, &Pl_objp->phys_info.vel);
4430
4431         //      If moving not-very-slowly and sliding, then try to slide at goal, rather than
4432         //      point at goal.
4433         slop_vec = NULL;
4434         if (mag < 1.0f)
4435                 nvel_vec = Pl_objp->orient.fvec;
4436         else if (mag > 5.0f) {
4437                 float   nv_dot;
4438                 nv_dot = vm_vec_dot(&Pl_objp->orient.fvec, &nvel_vec);
4439                 if ((nv_dot > 0.5f) && (nv_dot < 0.97f)) {
4440                         slop_vec = &temp_vec;
4441                         vm_vec_sub(slop_vec, &nvel_vec, &Pl_objp->orient.fvec);
4442                 }
4443         }
4444
4445         if (dist_to_goal > 0.1f)
4446                 ai_turn_towards_vector(&gcvp, Pl_objp, flFrametime, sip->srotation_time, slop_vec, NULL, 0.0f, 0);
4447
4448         //      Code to control speed is MUCH less forgiving in path following than in waypoint
4449         //      following.  Must be very close to path or might hit objects.
4450         prev_dot_to_goal = aip->prev_dot_to_goal;
4451         dot = vm_vec_dot_to_point(&nvel_vec, &Pl_objp->pos, &gcvp);
4452         dot_to_next = vm_vec_dot_to_point(&nvel_vec, &Pl_objp->pos, &gnvp);
4453
4454         set_accel_for_docking(Pl_objp, aip, dot, dot_to_next, dist_to_next, dist_to_goal, sip);
4455         aip->prev_dot_to_goal = dot;
4456
4457 //mprintf(("Goal index = %i, dist = %7.3f, dot = %7.3f\n", wp_index, dist_to_goal, dot));
4458
4459         //      If moving at a non-tiny velocity, detect attaining path point by its being close to
4460         //      line between previous and current object location.
4461         if ((dist_to_goal < MIN_DIST_TO_WAYPOINT_GOAL) || (vm_vec_dist_quick(&Pl_objp->last_pos, &Pl_objp->pos) > 0.1f)) {
4462                 vector  nearest_point;
4463                 float           r, min_dist_to_goal;
4464
4465                 r = find_nearest_point_on_line(&nearest_point, &Pl_objp->last_pos, &Pl_objp->pos, &gcvp);
4466
4467                 //      Set min_dist_to_goal = how close must be to waypoint to pick next one.
4468                 //      If docking and this is the second last waypoint, must be very close.
4469                 if ((aip->mode == AIM_DOCK) && (aip->path_cur >= aip->path_length-2))
4470                         min_dist_to_goal = MIN_DIST_TO_WAYPOINT_GOAL;
4471                 else
4472                         min_dist_to_goal = MIN_DIST_TO_WAYPOINT_GOAL + Pl_objp->radius;
4473
4474                 if ( (vm_vec_dist_quick(&Pl_objp->pos, &gcvp) < min_dist_to_goal) ||
4475                         ((r >= 0.0f) && (r <= 1.0f)) && (vm_vec_dist_quick(&nearest_point, &gcvp) < (MIN_DIST_TO_WAYPOINT_GOAL + Pl_objp->radius))) {
4476                         aip->path_cur += aip->path_dir;
4477                         //nprintf(("AI", " Near: Advancing from point %i to %i of %i points.\n", aip->path_cur-aip->path_dir, aip->path_cur, num_points));
4478                         if (((aip->path_cur - aip->path_start) > (num_points+1)) || (aip->path_cur < aip->path_start)) {
4479                                 Assert(aip->mode != AIM_DOCK);          //      If docking, should never get this far, getting to last point handled outside ai_path()
4480                                 aip->path_dir = -aip->path_dir;
4481 //                              aip->path_cur += aip->path_dir;
4482                         }
4483                 }
4484         }
4485
4486         return dist_to_goal;
4487 }
4488
4489 void update_min_max(float val, float *min, float *max)
4490 {
4491         if (val < *min)
4492                 *min = val;
4493         else if (val > *max)
4494                 *max = val;
4495 }
4496
4497 //      Stuff bounding box of all enemy objects within "range" units of object *my_objp.
4498 //      Stuff ni min_vec and max_vec.
4499 //      Return value: Number of enemy objects in bounding box.
4500 int get_enemy_team_range(object *my_objp, float range, int enemy_team_mask, vector *min_vec, vector *max_vec)
4501 {
4502         object  *objp;
4503         ship_obj        *so;
4504         int             count = 0;
4505
4506         for ( so = GET_FIRST(&Ship_obj_list); so != END_OF_LIST(&Ship_obj_list); so = GET_NEXT(so) ) {
4507                 objp = &Objects[so->objnum];
4508                 if (Ships[objp->instance].team & enemy_team_mask) {
4509                         if (Ship_info[Ships[objp->instance].ship_info_index].flags & (SIF_FIGHTER | SIF_BOMBER | SIF_CRUISER | SIF_CAPITAL | SIF_SUPERCAP | SIF_DRYDOCK | SIF_CORVETTE | SIF_AWACS | SIF_GAS_MINER))
4510                                 if (vm_vec_dist_quick(&my_objp->pos, &objp->pos) < range) {
4511                                         if (count == 0) {
4512                                                 *min_vec = objp->pos;
4513                                                 *max_vec = objp->pos;
4514                                                 count++;
4515                                         } else {
4516                                                 update_min_max(objp->pos.x, &min_vec->x, &max_vec->x);
4517                                                 update_min_max(objp->pos.y, &min_vec->y, &max_vec->y);
4518                                                 update_min_max(objp->pos.z, &min_vec->z, &max_vec->z);
4519                                         }
4520                                 }
4521
4522                 }
4523         }
4524
4525         return count;
4526 }
4527
4528 //      Pick a relatively safe spot for objp to fly to.
4529 //      Problem:
4530 //              Finds a spot away from any enemy within a bounding box.
4531 //              Doesn't verify that "safe spot" is not near some other enemy.
4532 void ai_safety_pick_spot(object *objp)
4533 {
4534         int             objnum;
4535         int             enemy_team_mask;
4536         vector  min_vec, max_vec;
4537         vector  vec_to_center, center;
4538         vector  goal_pos;
4539
4540         objnum = OBJ_INDEX(objp);
4541
4542         enemy_team_mask = get_enemy_team_mask(objnum);
4543
4544         if (get_enemy_team_range(objp, 1000.0f, enemy_team_mask, &min_vec, &max_vec)) {
4545                 vm_vec_avg(&center, &min_vec, &max_vec);
4546                 vm_vec_normalized_dir(&vec_to_center, &center, &objp->pos);
4547
4548                 vm_vec_scale_add(&goal_pos, &center, &vec_to_center, 2000.0f);
4549         } else
4550                 vm_vec_scale_add(&goal_pos, &objp->pos, &objp->orient.fvec, 100.0f);
4551
4552         Ai_info[Ships[objp->instance].ai_index].goal_point = goal_pos;
4553 }
4554
4555 //      Fly to desired safe point.
4556 // Returns distance to that point.
4557 float ai_safety_goto_spot(object *objp)
4558 {
4559         float   dot, dist;
4560         ai_info *aip;
4561         vector  vec_to_goal;
4562         ship_info       *sip;
4563         float   dot_val;
4564
4565         sip = &Ship_info[Ships[objp->instance].ship_info_index];
4566
4567         aip = &Ai_info[Ships[objp->instance].ai_index];
4568         dist = vm_vec_normalized_dir(&vec_to_goal, &aip->goal_point, &objp->pos);
4569         dot = vm_vec_dot(&vec_to_goal, &objp->orient.fvec);
4570
4571         dot_val = (1.1f + dot) / 2.0f;
4572         if (dist > 200.0f) {
4573                 set_accel_for_target_speed(objp, sip->max_speed * dot_val);
4574         } else
4575                 set_accel_for_target_speed(objp, sip->max_speed * dot_val * (dist/200.0f + 0.2f));
4576
4577         return dist;
4578 }
4579
4580 void ai_safety_circle_spot(object *objp)
4581 {
4582         vector  goal_point;
4583         ship_info       *sip;
4584         float           dot;
4585
4586         sip = &Ship_info[Ships[objp->instance].ship_info_index];
4587
4588         goal_point = Ai_info[Ships[objp->instance].ai_index].goal_point;
4589         dot = turn_towards_tangent(objp, &goal_point, 250.0f);  //      Increased from 50 to 250 to make circling not look so wacky.
4590
4591         set_accel_for_target_speed(objp, 0.5f * (1.0f + dot) * sip->max_speed/4.0f);
4592
4593 //      float dist = vm_vec_dist_quick(&goal_point, &Pl_objp->pos);
4594 //      nprintf(("AI", "Ship %s circling %7.3f %7.3f %7.3f.  Distance = %7.3f\n", Ships[Pl_objp->instance].ship_name, goal_point.x, goal_point.y, goal_point.z, dist));
4595
4596 }
4597
4598 //      --------------------------------------------------------------------------
4599 void ai_safety()
4600 {
4601         ai_info *aip;
4602
4603         aip = &Ai_info[Ships[Pl_objp->instance].ai_index];
4604
4605         switch (aip->submode) {
4606         case AISS_1:
4607                 ai_safety_pick_spot(Pl_objp);
4608                 aip->submode = AISS_2;
4609                 aip->submode_start_time = Missiontime;
4610                 break;
4611         case AISS_1a:   //      Pick a safe point because we just got whacked!
4612                 Int3();
4613                 break;
4614         case AISS_2:
4615                 if (ai_safety_goto_spot(Pl_objp) < 25.0f) {
4616                         aip->submode = AISS_3;
4617                         aip->submode_start_time = Missiontime;
4618                 }
4619                 break;
4620         case AISS_3:
4621                 ai_safety_circle_spot(Pl_objp);
4622                 break;
4623         default:
4624                 Int3();         //      Illegal submode for ai_safety();
4625                 break;
4626         }
4627 }
4628
4629 //      --------------------------------------------------------------------------
4630 //      make Pl_objp fly waypoints.
4631 void ai_waypoints()
4632 {
4633         int             wp_index;
4634         vector  *wp_cur, *wp_next;
4635         float           dot, dist_to_goal, dist_to_next, speed, dot_to_next;
4636         ship            *shipp = &Ships[Pl_objp->instance];
4637         ship_info       *sip = &Ship_info[shipp->ship_info_index];
4638         waypoint_list   *wpl;
4639         ai_info *aip;
4640         vector  nvel_vec;
4641         float           mag;
4642         float           prev_dot_to_goal;
4643         vector  temp_vec;
4644         vector  *slop_vec;
4645
4646         aip = &Ai_info[Ships[Pl_objp->instance].ai_index];
4647
4648         wp_index = aip->wp_index;
4649
4650         if (wp_index == -1) {
4651                 ai_start_waypoints(Pl_objp, 0, WPF_REPEAT);
4652                 wp_index = aip->wp_index;
4653                 aip->wp_dir = 1;
4654         }
4655
4656         wpl = &Waypoint_lists[Ai_info[Ships[Pl_objp->instance].ai_index].wp_list];
4657
4658         Assert(wpl->count);     // What? Is this zero? Probably wp_index never got initialized!
4659
4660         wp_cur = &wpl->waypoints[wp_index];
4661         wp_next = &wpl->waypoints[(wp_index+1) % wpl->count];
4662         speed = Pl_objp->phys_info.speed;
4663
4664         dist_to_goal = vm_vec_dist_quick(&Pl_objp->pos, wp_cur);
4665         dist_to_next = vm_vec_dist_quick(&Pl_objp->pos, wp_next);
4666
4667         //      Can't use fvec, need to use velocity vector because we aren't necessarily
4668         //      moving in the direction we're facing.
4669         // AL 23-3-98: Account for very small velocities by checking result of vm_vec_mag().
4670         //                                      If we don't vm_vec_copy_normalize() will think it is normalizing a null vector.
4671 //      if (IS_VEC_NULL(&Pl_objp->phys_info.vel)) {
4672         if ( vm_vec_mag_quick(&Pl_objp->phys_info.vel) < AICODE_SMALL_MAGNITUDE ) {
4673                 mag = 0.0f;
4674                 vm_vec_zero(&nvel_vec);
4675         } else {
4676                 mag = vm_vec_copy_normalize(&nvel_vec, &Pl_objp->phys_info.vel);
4677         }
4678
4679         //      If moving not-very-slowly and sliding, then try to slide at goal, rather than
4680         //      point at goal.
4681         slop_vec = NULL;
4682         if (mag < 1.0f) {
4683                 nvel_vec = Pl_objp->orient.fvec;
4684         } else if (mag > 5.0f) {
4685                 float   nv_dot;
4686                 nv_dot = vm_vec_dot(&Pl_objp->orient.fvec, &nvel_vec);
4687                 if ((nv_dot > 0.5f) && (nv_dot < 0.97f)) {
4688                         slop_vec = &temp_vec;
4689                         vm_vec_sub(slop_vec, &nvel_vec, &Pl_objp->orient.fvec);
4690                 }
4691         }
4692
4693         //      If a wing leader, take turns more slowly, based on size of wing.
4694         int     scale;
4695
4696         if (Ai_info[Ships[Pl_objp->instance].ai_index].wing >= 0) {
4697                 scale = Wings[Ai_info[Ships[Pl_objp->instance].ai_index].wing].current_count;
4698                 scale = (int) ((scale+1)/2);
4699         } else {
4700                 scale = 1;
4701         }
4702
4703         if (dist_to_goal > 0.1f) {
4704                 ai_turn_towards_vector(wp_cur, Pl_objp, flFrametime, sip->srotation_time*3.0f*scale, slop_vec, NULL, 0.0f, 0);
4705         }
4706
4707         prev_dot_to_goal = aip->prev_dot_to_goal;
4708         dot = vm_vec_dot_to_point(&nvel_vec, &Pl_objp->pos, wp_cur);
4709         dot_to_next = vm_vec_dot_to_point(&nvel_vec, &Pl_objp->pos, wp_next);
4710         aip->prev_dot_to_goal = dot;
4711
4712         //      If there is no next point on the path, don't care about dot to next.
4713         if (wp_index + 1 >= wpl->count) {
4714                 dot_to_next = dot;
4715         }
4716
4717         // nprintf(("AI", "Wp #%i, dot = %6.3f, next dot = %6.3f, dist = %7.2f\n", wp_index, dot, dot_to_next, dist_to_goal));
4718
4719         if (Pl_objp->phys_info.speed < 0.0f) {
4720                 accelerate_ship(aip, 1.0f/32);
4721         } else if (prev_dot_to_goal > dot+0.01f) {
4722                 //      We are further from pointing at our goal this frame than last frame, so slow down.
4723                 set_accel_for_target_speed(Pl_objp, Pl_objp->phys_info.speed * 0.95f);
4724         } else if (dist_to_goal < 100.0f) {
4725                 float slew_dot = vm_vec_dot(&Pl_objp->orient.fvec, &nvel_vec);
4726                 if (fl_abs(slew_dot) < 0.9f) {
4727                         accelerate_ship(aip, 0.0f);
4728                 } else if (dot < 0.88f + 0.1f*(100.0f - dist_to_goal)/100.0f) {
4729                         accelerate_ship(aip, 0.0f);
4730                 } else {
4731                         accelerate_ship(aip, 0.5f * dot * dot);
4732                 }
4733         } else {
4734                 float   dot1;
4735                 if (dist_to_goal < 250.0f) {
4736                         dot1 = dot*dot*dot;                             //      Very important to be pointing towards goal when nearby.  Note, cubing preserves sign.
4737                 } else {
4738                         if (dot > 0.0f) {
4739                                 dot1 = dot*dot;
4740                         } else {
4741                                 dot1 = dot;
4742                         }
4743                 }
4744
4745                 if (dist_to_goal > 100.0f + Pl_objp->radius * 2) {
4746                         if (dot < 0.2f) {
4747                                 dot1 = 0.2f;
4748                         }
4749                 }
4750
4751                 if (sip->flags & SIF_SMALL_SHIP) {
4752                         set_accel_for_target_speed(Pl_objp, dot1 * dist_to_goal/5.0f);
4753                 } else {
4754                         set_accel_for_target_speed(Pl_objp, dot1 * dist_to_goal/10.0f);
4755                 }
4756         }
4757
4758         //      Make sure not travelling too fast for someone to keep up.
4759         float   max_allowed_speed = 9999.9f;
4760
4761         if (shipp->wingnum != -1) {
4762                 max_allowed_speed = 0.9f * get_wing_lowest_max_speed(Pl_objp);
4763         }
4764
4765         // check if waypoint speed cap is set and adjust max speed
4766         if (aip->waypoint_speed_cap > 0) {
4767                 max_allowed_speed = (float) aip->waypoint_speed_cap;
4768         }
4769
4770         if (aip->prev_accel * shipp->current_max_speed > max_allowed_speed) {
4771                 accelerate_ship(aip, max_allowed_speed / shipp->current_max_speed);
4772         }
4773
4774         if (vm_vec_dist_quick(&Pl_objp->last_pos, &Pl_objp->pos) > 0.1f) {
4775                 vector  nearest_point;
4776                 float           r;
4777
4778                 r = find_nearest_point_on_line(&nearest_point, &Pl_objp->last_pos, &Pl_objp->pos, wp_cur);
4779
4780                 if ( (vm_vec_dist_quick(&Pl_objp->pos, wp_cur) < (MIN_DIST_TO_WAYPOINT_GOAL + fl_sqrt(Pl_objp->radius) + vm_vec_dist_quick(&Pl_objp->pos, &Pl_objp->last_pos))) ||
4781                         ((r >= 0.0f) && (r <= 1.0f)) && (vm_vec_dist_quick(&nearest_point, wp_cur) < (MIN_DIST_TO_WAYPOINT_GOAL + fl_sqrt(Pl_objp->radius)))) {
4782                         wp_index++;
4783                         if (wp_index >= wpl->count)
4784                                 if (aip->wp_flags & WPF_REPEAT) {
4785                                         wp_index = 0;
4786                                 } else {
4787                                         int treat_as_ship;
4788
4789                                         // when not repeating waypoints -- mark the goal as done and put and entry into the mission log
4790                                         // we must be careful when dealing with wings.  A ship in a wing might be completing
4791                                         // a waypoint for for the entire wing, or it might be completing a goal for itself.  If
4792                                         // for itself and in a wing, treat the completion as we would a ship
4793                                         treat_as_ship = 1;
4794                                         if ( Ships[Pl_objp->instance].wingnum != -1 ) {
4795                                                 int type;
4796
4797                                                 // I don't think that you can fly waypoints as dynamic goals!!!
4798                                                 // -- This is legal, just stupid. -- Assert( (aip->active_goal != AI_GOAL_NONE) && (aip->active_goal != AI_ACTIVE_GOAL_DYNAMIC) );
4799                                                 
4800                                                 //      Clean up from above Assert, just in case we ship without fixing it.  (Encountered by JimB on 2/9/98)
4801                                                 if ( (aip->active_goal == AI_GOAL_NONE) || (aip->active_goal == AI_ACTIVE_GOAL_DYNAMIC) ) {
4802                                                         aip->mode = AIM_NONE;
4803                                                         Int3(); //      Look at the ship, find out of it's supposed to be flying waypoints. -- MK.
4804                                                 }
4805
4806                                                 type = aip->goals[aip->active_goal].type;
4807                                                 if ( (type == AIG_TYPE_EVENT_WING) || (type == AIG_TYPE_PLAYER_WING) ) {
4808                                                         treat_as_ship = 0;
4809                                                 } else {
4810                                                         treat_as_ship = 1;
4811                                                 }
4812                                         }
4813
4814                                         // if the ship is not in a wing, remove the goal and continue on
4815                                         if ( treat_as_ship ) {
4816                                                 ai_mission_goal_complete( aip );                                        // this call should reset the AI mode
4817                                                 mission_log_add_entry(LOG_WAYPOINTS_DONE, Ships[Pl_objp->instance].ship_name, wpl->name, -1 );
4818                                         } else {
4819                                                 // this ship is in a wing.  We must mark the goal as being completed for all ships
4820                                                 // in the wing.  We will also mark an entry in the log that the wing completed the goal
4821                                                 // not the individual ship.
4822                                                 ai_mission_wing_goal_complete( Ships[Pl_objp->instance].wingnum, &(aip->goals[aip->active_goal]) );
4823                                                 mission_log_add_entry( LOG_WAYPOINTS_DONE, Wings[Ships[Pl_objp->instance].wingnum].name, wpl->name, -1 );
4824                                         }
4825                                         //wp_index = wpl->count-1;
4826                                 }
4827
4828                         aip->wp_index = wp_index;
4829                 }
4830         }
4831 }
4832
4833 //      Make Pl_objp avoid En_objp
4834 //      Not like evading.  This is for avoiding a collision!
4835 //      Note, use sliding if available.
4836 void avoid_ship()
4837 {
4838         //      To avoid an object, turn towards right or left vector until facing away from object.
4839         //      To choose right vs. left, pick one that is further from center of avoid object.
4840         //      Keep turning away from until pointing away from ship.
4841         //      Stay in avoid mode until at least 3 enemy ship radii away.
4842
4843         //      Speed setting:
4844         //      If inside sphere, zero speed and turn towards outside.
4845         //      If outside sphere, inside 2x sphere, set speed percent of max to:
4846         //              max(away_dot, (dist-rad)/rad)
4847         //      where away_dot is dot(Pl_objp->fvec, vec_En_objp_to_Pl_objp)
4848
4849         vector  vec_to_enemy;
4850         float           away_dot;
4851         float           dist;
4852         ship            *shipp = &Ships[Pl_objp->instance];
4853         ship_info       *sip = &Ship_info[shipp->ship_info_index];
4854         ai_info *aip = &Ai_info[shipp->ai_index];
4855         vector  player_pos, enemy_pos;
4856
4857         // if we're avoiding a stealth ship, then we know where he is, update with no error
4858         if ( is_object_stealth_ship(En_objp) ) {
4859                 update_ai_stealth_info_with_error(aip/*, 1*/);
4860         }
4861
4862         ai_set_positions(Pl_objp, En_objp, aip, &player_pos, &enemy_pos);
4863         vm_vec_sub(&vec_to_enemy, &enemy_pos, &Pl_objp->pos);
4864
4865         dist = vm_vec_normalize(&vec_to_enemy);
4866         away_dot = -vm_vec_dot(&Pl_objp->orient.fvec, &vec_to_enemy);
4867         
4868         if ((sip->max_vel.x > 0.0f) || (sip->max_vel.y > 0.0f)) {
4869                 if (vm_vec_dot(&Pl_objp->orient.rvec, &vec_to_enemy) > 0.0f) {
4870                         AI_ci.sideways = -1.0f;
4871                 } else {
4872                         AI_ci.sideways = 1.0f;
4873                 }
4874                 if (vm_vec_dot(&Pl_objp->orient.uvec, &vec_to_enemy) > 0.0f) {
4875                         AI_ci.vertical = -1.0f;
4876                 } else {
4877                         AI_ci.vertical = 1.0f;
4878                 }
4879         }               
4880
4881         //nprintf(("AI", "Frame %i: Sliding: %s %s\n", Framecount, AI_ci.sideways < 0 ? "left" : "right", AI_ci.vertical < 0 ? "down" : "up" ));
4882         // nprintf(("AI", "away_dot = %6.3f, dist = %7.2f, dist/radsum = %6.3f\n", away_dot, dist, dist/(Pl_objp->radius + En_objp->radius)));
4883
4884         //      If in front of enemy, turn away from it.
4885         //      If behind enemy, try to get fully behind it.
4886         if (away_dot < 0.0f) {
4887                 turn_away_from_point(Pl_objp, &enemy_pos, Pl_objp->phys_info.speed);
4888         } else {
4889                 vector  goal_pos;
4890
4891                 vm_vec_scale_add(&goal_pos, &En_objp->pos, &En_objp->orient.fvec, -100.0f);
4892                 turn_towards_point(Pl_objp, &goal_pos, NULL, Pl_objp->phys_info.speed);
4893         }
4894
4895         //      Set speed.
4896         float   radsum = Pl_objp->radius + En_objp->radius;
4897
4898         if (dist < radsum)
4899                 accelerate_ship(aip, max(away_dot, 0.2f));
4900         else if (dist < 2*radsum)
4901                 accelerate_ship(aip, max(away_dot, (dist - radsum) / radsum)+0.2f);
4902         else
4903                 accelerate_ship(aip, 1.0f);
4904
4905 }
4906
4907 //      Maybe it's time to resume the previous AI mode in aip->previous_mode.
4908 //      Each type of previous_mode has its own criteria on when to resume.
4909 //      Return true if previous mode was resumed.
4910 int maybe_resume_previous_mode(object *objp, ai_info *aip)
4911 {
4912         //      Only (maybe) resume previous goal if current goal is dynamic.
4913         if (aip->active_goal != AI_ACTIVE_GOAL_DYNAMIC)
4914                 return 0;
4915
4916         if (aip->mode == AIM_EVADE_WEAPON) {
4917                 if (timestamp_elapsed(aip->mode_time) || (((aip->nearest_locked_object == -1) || (Objects[aip->nearest_locked_object].type != OBJ_WEAPON)) && (aip->danger_weapon_objnum == -1))) {
4918                         Assert(aip->previous_mode != AIM_EVADE_WEAPON);
4919                         aip->mode = aip->previous_mode;
4920                         aip->submode = aip->previous_submode;
4921                         aip->submode_start_time = Missiontime;
4922                         aip->active_goal = AI_GOAL_NONE;
4923                         aip->mode_time = -1;                    //      Means do forever.
4924                         return 1;
4925                 }
4926         } else if ( aip->previous_mode == AIM_GUARD) {
4927                 if ((aip->guard_objnum != -1) && (aip->guard_signature == Objects[aip->guard_objnum].signature)) {
4928                         object  *guard_objp;
4929                         float   dist;
4930
4931                         guard_objp = &Objects[aip->guard_objnum];
4932                         dist = vm_vec_dist_quick(&guard_objp->pos, &objp->pos);
4933
4934                         //      If guarding ship is far away from guardee and enemy is far away from guardee,
4935                         //      then stop chasing and resume guarding.
4936                         if (dist > (MAX_GUARD_DIST + guard_objp->radius) * 6) {
4937                                 if ((En_objp != NULL) && (En_objp->type == OBJ_SHIP)) {
4938                                         if (vm_vec_dist_quick(&guard_objp->pos, &En_objp->pos) > (MAX_GUARD_DIST + guard_objp->radius) * 6) {
4939                                                 Assert(aip->previous_mode == AIM_GUARD);
4940                                                 aip->mode = aip->previous_mode;
4941                                                 aip->submode = AIS_GUARD_PATROL;
4942                                                 aip->active_goal = AI_GOAL_NONE;
4943                                                 return 1;
4944                                         }
4945                                 }
4946                         }
4947                 }
4948         }
4949
4950         return 0;
4951
4952 }
4953
4954 //      Call this function if you want something to happen on average every N quarters of a second.
4955 //      The truth value returned by this function will be the same for any given quarter second interval.
4956 //      The value "num" is only passed in to get asynchronous behavior for different objects.
4957 //      modulus == 1 will always return true.
4958 //      modulus == 2 will return true half the time.
4959 //      modulus == 16 will return true for one quarter second interval every four seconds.
4960 int static_rand_timed(int num, int modulus)
4961 {
4962         if (modulus < 2)
4963                 return 1;
4964         else {
4965                 int     t;
4966
4967                 t = Missiontime >> 18;          //      Get time in quarters of a second
4968                 t += num;
4969
4970                 return !(t % modulus);
4971         }
4972 }
4973
4974 //      Maybe fire afterburner based on AI class
4975 int ai_maybe_fire_afterburner(object *objp, ai_info *aip)
4976 {
4977         if (aip->ai_class == 0)
4978                 return 0;               //      Lowest level never aburners away
4979         else  {
4980                 //      Maybe don't afterburner because of a potential collision with the player.
4981                 //      If not multiplayer, near player and player in front, probably don't afterburner.
4982                 if (!(Game_mode & GM_MULTIPLAYER)) {
4983                         if (Ships[objp->instance].team == Player_ship->team) {
4984                                 float   dist;
4985
4986                                 dist = vm_vec_dist_quick(&objp->pos, &Player_obj->pos) - Player_obj->radius - objp->radius;
4987                                 if (dist < 150.0f) {
4988                                         vector  v2p;
4989                                         float           dot;
4990
4991                                         vm_vec_normalized_dir(&v2p, &Player_obj->pos, &objp->pos);
4992                                         dot = vm_vec_dot(&v2p, &objp->orient.fvec);
4993
4994                                         if (dot > 0.0f) {
4995                                                 if (dot * dist > 50.0f)
4996                                                         return 0;
4997                                         }
4998                                 }
4999                         }
5000                 }
5001
5002                 if (aip->ai_class >= Num_ai_classes-2)
5003                         return 1;               //      Highest two levels always aburner away.
5004                 else {
5005                         return static_rand_timed(objp-Objects, Num_ai_classes - aip->ai_class);
5006                 }
5007         }
5008 }
5009
5010 //      Maybe engage afterburner after being hit by an object.
5011 void maybe_afterburner_after_ship_hit(object *objp, ai_info *aip, object *en_objp)
5012 {
5013         //      Only do if facing a little away.
5014         if (en_objp != NULL) {
5015                 vector  v2e;
5016
5017                 vm_vec_normalized_dir(&v2e, &en_objp->pos, &objp->pos);
5018                 if (vm_vec_dot(&v2e, &objp->orient.fvec) > -0.5f)
5019                         return;
5020         }
5021
5022         if (!( objp->phys_info.flags & PF_AFTERBURNER_ON )) {
5023                 if (ai_maybe_fire_afterburner(objp, aip)) {
5024                         afterburners_start(objp);
5025                         aip->afterburner_stop_time = Missiontime + F1_0/2;
5026                 }
5027         }
5028 }
5029
5030 //      Return true if object *objp is an instructor.
5031 //      Is an instructor if name begins INSTRUCTOR_SHIP_NAME else not.
5032 int is_instructor(object *objp)
5033 {
5034         return !strnicmp(Ships[objp->instance].ship_name, INSTRUCTOR_SHIP_NAME, strlen(INSTRUCTOR_SHIP_NAME));
5035 }
5036
5037 //      Evade the weapon aip->danger_weapon_objnum
5038 //      If it's not valid, do a quick out.
5039 //      Evade by accelerating hard.
5040 //      If necessary, turn hard left or hard right.
5041 void evade_weapon()
5042 {
5043         object  *weapon_objp = NULL;
5044         object  *unlocked_weapon_objp = NULL, *locked_weapon_objp = NULL;
5045         vector  weapon_pos, player_pos, goal_point;
5046         vector  vec_from_enemy;
5047         float           dot_from_enemy, dot_to_enemy;
5048         float           dist;
5049         ship            *shipp = &Ships[Pl_objp->instance];
5050         ai_info *aip = &Ai_info[shipp->ai_index];
5051
5052         if (is_instructor(Pl_objp))
5053                 return;
5054
5055         //      Make sure we're actually being attacked.
5056         //      Favor locked objects.
5057         if (aip->nearest_locked_object != -1) {
5058                 if (Objects[aip->nearest_locked_object].type == OBJ_WEAPON)
5059                         locked_weapon_objp = &Objects[aip->nearest_locked_object];
5060         }
5061         
5062         if (aip->danger_weapon_objnum != -1)
5063                 if (Objects[aip->danger_weapon_objnum].signature == aip->danger_weapon_signature)
5064                         unlocked_weapon_objp = &Objects[aip->danger_weapon_objnum];
5065                 else
5066                         aip->danger_weapon_objnum = -1;         //      Signatures don't match, so no longer endangered.
5067
5068         if (locked_weapon_objp != NULL) {
5069                 if (unlocked_weapon_objp != NULL) {
5070                         if (vm_vec_dist_quick(&locked_weapon_objp->pos, &Pl_objp->pos) < 1.5f * vm_vec_dist_quick(&unlocked_weapon_objp->pos, &Pl_objp->pos))
5071                                 weapon_objp = locked_weapon_objp;
5072                         else
5073                                 weapon_objp = unlocked_weapon_objp;
5074                 } else
5075                         weapon_objp = locked_weapon_objp;
5076         } else if (unlocked_weapon_objp != NULL)
5077                 weapon_objp = unlocked_weapon_objp;
5078         else {
5079                 if (aip->mode == AIM_EVADE_WEAPON)
5080                         maybe_resume_previous_mode(Pl_objp, aip);
5081                 return;
5082         }
5083
5084         Assert(weapon_objp != NULL);
5085
5086         if (weapon_objp->type != OBJ_WEAPON) {
5087                 if (aip->mode == AIM_EVADE_WEAPON)
5088                         maybe_resume_previous_mode(Pl_objp, aip);
5089                 return;
5090         }
5091         
5092         weapon_pos = weapon_objp->pos;
5093         player_pos = Pl_objp->pos;
5094
5095         //      Make speed based on skill level, varying at highest skill level, which is harder to hit.
5096         accelerate_ship(aip, 1.0f);
5097
5098         dist = vm_vec_normalized_dir(&vec_from_enemy, &player_pos, &weapon_pos);
5099
5100         dot_to_enemy = -vm_vec_dot(&Pl_objp->orient.fvec, &vec_from_enemy);
5101         dot_from_enemy = vm_vec_dot(&weapon_objp->orient.fvec, &vec_from_enemy);
5102         //nprintf(("AI", "dot from enemy = %7.3f\n", dot_from_enemy));
5103
5104         //      If shot is incoming...
5105         if (dot_from_enemy < 0.3f) {
5106                 if (weapon_objp == unlocked_weapon_objp)
5107                         aip->danger_weapon_objnum = -1;
5108                 return;
5109         } else if (dot_from_enemy > 0.7f) {
5110                 if (dist < 200.0f) {
5111                         if (!( Pl_objp->phys_info.flags & PF_AFTERBURNER_ON )) {
5112                                 if (ai_maybe_fire_afterburner(Pl_objp, aip)) {
5113                                         //nprintf(("AI", "Frame %i, turning on afterburner.\n", AI_FrameCount));
5114                                         afterburners_start(Pl_objp);
5115                                         aip->afterburner_stop_time = Missiontime + F1_0/2;
5116                                 }
5117                         }
5118                 }
5119
5120                 //      If we're sort of pointing towards it...
5121                 if ((dot_to_enemy < -0.5f) || (dot_to_enemy > 0.5f)) {
5122                         float   rdot;
5123
5124                         //      Turn hard left or right, depending on which gets out of way quicker.
5125                         rdot = vm_vec_dot(&Pl_objp->orient.rvec, &vec_from_enemy);
5126
5127                         if ((rdot < -0.5f) || (rdot > 0.5f))
5128                                 vm_vec_scale_add(&goal_point, &Pl_objp->pos, &Pl_objp->orient.rvec, -200.0f);
5129                         else
5130                                 vm_vec_scale_add(&goal_point, &Pl_objp->pos, &Pl_objp->orient.rvec, 200.0f);
5131
5132                         turn_towards_point(Pl_objp, &goal_point, NULL, 0.0f);
5133                 }
5134         }
5135
5136 }
5137
5138 //      Use sliding and backwards moving to face enemy.
5139 //      (Coded 2/20/98.  Works fine, but it's hard to see how to integrate it into the AI system.
5140 //       Typically ships are moving so fast that a little sliding isn't enough to gain an advantage.
5141 //       It's currently used to avoid collisions and could be used to evade weapon fire, but the latter
5142 //       would be frustrating, I think.
5143 //       This function is currently not called.)
5144 void slide_face_ship()
5145 {
5146         ship_info       *sip;
5147
5148         sip = &Ship_info[Ships[Pl_objp->instance].ship_info_index];
5149
5150         //      If can't slide, return.
5151         if ((sip->max_vel.x == 0.0f) && (sip->max_vel.y == 0.0f))
5152                 return;
5153
5154         vector  goal_pos;
5155         float           dot_from_enemy, dot_to_enemy;
5156         vector  vec_from_enemy, vec_to_goal;
5157         float           dist;
5158         float           up, right;
5159         ai_info         *aip;
5160
5161         aip = &Ai_info[Ships[Pl_objp->instance].ai_index];
5162
5163         dist = vm_vec_normalized_dir(&vec_from_enemy, &Pl_objp->pos, &En_objp->pos);
5164
5165         ai_turn_towards_vector(&En_objp->pos, Pl_objp, flFrametime, sip->srotation_time, NULL, NULL, 0.0f, 0);
5166
5167         dot_from_enemy = vm_vec_dot(&vec_from_enemy, &En_objp->orient.fvec);
5168         dot_to_enemy = -vm_vec_dot(&vec_from_enemy, &Pl_objp->orient.fvec);
5169
5170         if (vm_vec_dot(&vec_from_enemy, &En_objp->orient.rvec) > 0.0f)
5171                 right = 1.0f;
5172         else
5173                 right = -1.0f;
5174
5175         if (vm_vec_dot(&vec_from_enemy, &En_objp->orient.uvec) > 0.0f)
5176                 up = 1.0f;
5177         else
5178                 up = -1.0f;
5179
5180         vm_vec_scale_add(&goal_pos, &En_objp->pos, &En_objp->orient.rvec, right * 200.0f);
5181         vm_vec_scale_add(&goal_pos, &En_objp->pos, &En_objp->orient.uvec, up * 200.0f);
5182
5183         vm_vec_normalized_dir(&vec_to_goal, &goal_pos, &Pl_objp->pos);
5184
5185         if (vm_vec_dot(&vec_to_goal, &Pl_objp->orient.rvec) > 0.0f)
5186                 AI_ci.sideways = 1.0f;
5187         else
5188                 AI_ci.sideways = -1.0f;
5189
5190         if (vm_vec_dot(&vec_to_goal, &Pl_objp->orient.uvec) > 0.0f)
5191                 AI_ci.vertical = 1.0f;
5192         else
5193                 AI_ci.vertical = -1.0f;
5194
5195         if (dist < 200.0f) {
5196                 if (dot_from_enemy < 0.7f)
5197                         accelerate_ship(aip, -1.0f);
5198                 else
5199                         accelerate_ship(aip, dot_from_enemy + 0.5f);
5200         } else {
5201                 if (dot_from_enemy < 0.7f) {
5202                         accelerate_ship(aip, 0.2f);
5203                 } else {
5204                         accelerate_ship(aip, 1.0f);
5205                 }
5206         }
5207 }
5208
5209 //      General code for handling one ship evading another.
5210 //      Problem: This code is also used for avoiding an impending collision.
5211 //      In such a case, it is not good to go to max speed, which is often good
5212 //      for a certain kind of evasion.
5213 void evade_ship()
5214 {
5215         vector  player_pos, enemy_pos, goal_point;
5216         vector  vec_from_enemy;
5217         float           dot_from_enemy;
5218         float           dist;
5219         ship            *shipp = &Ships[Pl_objp->instance];
5220         ship_info       *sip = &Ship_info[shipp->ship_info_index];
5221         ai_info *aip = &Ai_info[shipp->ai_index];
5222         float           bank_override = 0.0f;
5223
5224         ai_set_positions(Pl_objp, En_objp, aip, &player_pos, &enemy_pos);
5225
5226         //      Make speed based on skill level, varying at highest skill level, which is harder to hit.
5227         if (Game_skill_level == NUM_SKILL_LEVELS-1) {
5228                 int     rand_int;
5229                 float   accel_val;
5230
5231                 rand_int = static_rand(Pl_objp-Objects);
5232                 accel_val = (float) (((Missiontime^rand_int) >> 14) & 0x0f)/32.0f + 0.5f;
5233                 accelerate_ship(aip, accel_val);
5234                 //nprintf(("AI", "Accel value = %7.3f\n", accel_val));
5235         } else
5236                 accelerate_ship(aip, (float) (Game_skill_level+2) / (NUM_SKILL_LEVELS+1));
5237
5238         if ((Missiontime - aip->submode_start_time > F1_0/2) && (sip->afterburner_fuel_capacity > 0.0f)) {
5239                 float percent_left = 100.0f * shipp->afterburner_fuel / sip->afterburner_fuel_capacity;
5240                 if (percent_left > 30.0f + ((Pl_objp-Objects) & 0x0f)) {
5241                         afterburners_start(Pl_objp);
5242                         aip->afterburner_stop_time = Missiontime + F1_0 + static_rand(Pl_objp-Objects)/4;
5243                 }
5244         }
5245
5246         vm_vec_sub(&vec_from_enemy, &player_pos, &enemy_pos);
5247
5248         dist = vm_vec_normalize(&vec_from_enemy);
5249         dot_from_enemy = vm_vec_dot(&En_objp->orient.fvec, &vec_from_enemy);
5250
5251         if (dist > 250.0f) {
5252                 vector  gp1, gp2;
5253                 //      If far away from enemy, circle, going to nearer of point far off left or right wing
5254                 vm_vec_scale_add(&gp1, &enemy_pos, &En_objp->orient.rvec, 250.0f);
5255                 vm_vec_scale_add(&gp2, &enemy_pos, &En_objp->orient.rvec, -250.0f);
5256                 if (vm_vec_dist_quick(&gp1, &Pl_objp->pos) < vm_vec_dist_quick(&gp2, &Pl_objp->pos))
5257                         goal_point = gp1;
5258                 else
5259                         goal_point = gp2;
5260         } else if (dot_from_enemy < 0.1f) {
5261                 //      If already close to behind, goal is to get completely behind.
5262                 vm_vec_scale_add(&goal_point, &enemy_pos, &En_objp->orient.fvec, -1000.0f);
5263         } else if (dot_from_enemy > 0.9f) {
5264                 //      If enemy pointing almost right at self, and self pointing close to enemy, turn away from
5265                 vector  vec_to_enemy;
5266                 float           dot_to_enemy;
5267
5268                 vm_vec_sub(&vec_to_enemy, &enemy_pos, &player_pos);
5269
5270                 vm_vec_normalize(&vec_to_enemy);
5271                 dot_to_enemy = vm_vec_dot(&Pl_objp->orient.fvec, &vec_to_enemy);
5272                 if (dot_to_enemy > 0.75f) {
5273                         //      Used to go to En_objp's right vector, but due to banking while turning, that
5274                         //      caused flying in an odd spiral.
5275                         vm_vec_scale_add(&goal_point, &enemy_pos, &Pl_objp->orient.rvec, 1000.0f);
5276                         if (dist < 100.0f)
5277                                 bank_override = Pl_objp->phys_info.speed; 
5278                 } else {
5279                         bank_override = Pl_objp->phys_info.speed;                       //      In enemy's sights, not pointing at him, twirl away.
5280                         // nprintf(("Mike", " Do sumpin' else."));
5281                         goto evade_ship_l1;
5282                 }
5283         } else {
5284 evade_ship_l1: ;
5285                 if (aip->ai_evasion > myrand()*100.0f/32767.0f) {
5286                         int     temp;
5287                         float   scale;
5288                         float   psrandval;      //      some value close to zero to choose whether to turn right or left.
5289
5290                         psrandval = (float) (((Missiontime >> 14) & 0x0f) - 8); //      Value between -8 and 7
5291                         psrandval = psrandval/16.0f;                                                    //      Value between -1/2 and 1/2 (approx)
5292
5293                         //      If not close to behind, turn towards his right or left vector, whichever won't cross his path.
5294                         if (vm_vec_dot(&vec_from_enemy, &En_objp->orient.rvec) > psrandval) {
5295                                 scale = 1000.0f;
5296                         } else {
5297                                 scale = -1000.0f;
5298                         }
5299
5300                         vm_vec_scale_add(&goal_point, &enemy_pos, &En_objp->orient.rvec, scale);
5301
5302                         temp = ((Missiontime >> 16) & 0x07);
5303                         temp = ((temp * (temp+1)) % 16)/2 - 4;
5304                         if ((psrandval == 0) && (temp == 0))
5305                                 temp = 3;
5306
5307                         scale = 200.0f * temp;
5308
5309                         vm_vec_scale_add2(&goal_point, &En_objp->orient.uvec, scale);
5310                 } else {
5311                         //      No evasion this frame, but continue with previous turn.
5312                         //      Reason: If you don't, you lose rotational momentum.  Turning every other frame,
5313                         //      and not in between results in a very slow turn because of loss of momentum.
5314                         if ((aip->prev_goal_point.x != 0.0f) || (aip->prev_goal_point.y != 0.0f) || (aip->prev_goal_point.z != 0.0f))
5315                                 goal_point = aip->prev_goal_point;
5316                         else
5317                                 vm_vec_scale_add(&goal_point, &enemy_pos, &En_objp->orient.rvec, 100.0f);
5318                 }
5319         }
5320
5321         // nprintf(("Mike", "Goal point = %7.1f %7.1f %7.1f\n", goal_point.x, goal_point.y, goal_point.z));
5322         turn_towards_point(Pl_objp, &goal_point, NULL, bank_override);
5323
5324         aip->prev_goal_point = goal_point;
5325 }
5326
5327 //      --------------------------------------------------------------------------
5328 //      Fly in a manner making it difficult for opponent to attack.
5329 void ai_evade()
5330 {
5331         evade_ship();
5332 }
5333
5334 /*
5335 // -------------------------------------------------------------------
5336 //      Refine predicted enemy position because enemy will move while we move
5337 //      towards predicted enemy position.
5338 //      last_delta_vec is stuffed with size of polishing in last step.  This small amount
5339 //      can be used to perturb the predicted position to make firing not be exact.
5340 //      This function will almost always undershoot actual position, assuming both ships
5341 //      are moving at constant speed.  But with even one polishing step, the error should
5342 //      be under 1%. The number of polishing steps is specified in the parameter num_polish_steps.
5343 void polish_predicted_enemy_pos(vector *predicted_enemy_pos, object *pobjp, object *eobjp, float dist_to_enemy, vector *last_delta_vec, int num_polish_steps) // Not used:, float time_to_enemy)
5344 {
5345         int     iteration;
5346         vector  player_pos = pobjp->pos;
5347         vector  enemy_pos = *predicted_enemy_pos;
5348         physics_info    *en_physp = &eobjp->phys_info;
5349         float           time_to_enemy;
5350         vector  last_predicted_enemy_pos = *predicted_enemy_pos;
5351         
5352         vm_vec_zero(last_delta_vec);
5353
5354         for (iteration=0; iteration < num_polish_steps; iteration++) {
5355                 dist_to_enemy = vm_vec_dist_quick(predicted_enemy_pos, &player_pos);
5356                 time_to_enemy = compute_time_to_enemy(dist_to_enemy, pobjp, eobjp);
5357                 vm_vec_scale_add(predicted_enemy_pos, &eobjp->pos, &eobjp->phys_info.vel, time_to_enemy);
5358                 vm_vec_sub(last_delta_vec, predicted_enemy_pos, &last_predicted_enemy_pos);
5359                 last_predicted_enemy_pos= *predicted_enemy_pos;
5360         }
5361 }
5362 */
5363
5364 /*
5365 Relevant variables are:
5366         best_dot_to_enemy               best dot product to enemy in last BEST_DOT_TIME seconds
5367         best_dot_to_time                time at which best dot occurred
5368         best_dot_from_enemy     best dot product for enemy to player in last BEST_DOT_TIME seconds
5369         best_dot_from_time      time at which best dot occurred
5370         submode_start_time      time at which we entered the current submode
5371         previous_submode                previous submode, get it?
5372 Legal submodes are:
5373         CONTINUOUS_TURN vector_id {0..3 = right, -right, up, -up}
5374         ATTACK
5375         EVADE_SQUIGGLE
5376         EVADE_BRAKE
5377 */
5378
5379 float   G_collision_time;
5380 vector  G_predicted_pos, G_fire_pos;
5381
5382 /*
5383 void show_firing_diag()
5384 {
5385         float           dot;
5386         vector  v2t;
5387         vector  pos1, pos2;
5388         float           dist;
5389
5390         if (G_collision_time == 0.0f)
5391                 return;
5392
5393         mprintf(("Fired from %5.1f, %5.1f %5.1f at time = %5.1f, predict collision in %5.2f seconds at %5.1f %5.1f %5.1f\n",
5394                 Pl_objp->pos.x, Pl_objp->pos.y, Pl_objp->pos.z, (float) Missiontime/1000.0f, G_collision_time, G_predicted_pos.x, G_predicted_pos.y, G_predicted_pos.z));
5395         vm_vec_normalized_dir(&v2t, &G_predicted_pos, &G_fire_pos);
5396         dot = vm_vec_dot(&v2t, &Pl_objp->orient.fvec);
5397         mprintf(("Dot of fvec and vector to predicted position = %10.7f (%7.3f degrees)\n", dot, acos(dot)*180.0f/3.141592654f));
5398
5399         vm_vec_scale_add(&pos1, &En_objp->pos, &En_objp->phys_info.vel, G_collision_time);
5400         vm_vec_scale_add(&pos2, &G_fire_pos, &Pl_objp->orient.fvec, G_collision_time*300.0f);
5401         dist = vm_vec_dist(&pos1, &pos2);
5402
5403         mprintf(("Enemy, laser pos, distance: [%5.1f %5.1f %5.1f]  [%5.1f %5.1f %5.1f]  %6.2f\n", pos1.x, pos1.y, pos1.z, pos2.x, pos2.y, pos2.z, dist));
5404 }
5405 */
5406
5407 //      If:
5408 //              flags & WIF_PUNCTURE
5409 //      Then Select a Puncture weapon.
5410 //      Else
5411 //              Select Any ol' weapon.
5412 //      Returns primary_bank index.
5413 int ai_select_primary_weapon(object *objp, object *other_objp, int flags)
5414 {
5415         ship    *shipp = &Ships[objp->instance];
5416         ship_weapon *swp = &shipp->weapons;
5417         ship_info *sip;
5418
5419         //Assert( other_objp != NULL );
5420         Assert( shipp->ship_info_index >= 0 && shipp->ship_info_index < MAX_SHIP_TYPES);
5421
5422         sip = &Ship_info[shipp->ship_info_index];
5423
5424         if (flags & WIF_PUNCTURE) {
5425                 if (swp->current_primary_bank >= 0) {
5426                         int     bank_index;
5427
5428                         bank_index = swp->current_primary_bank;
5429
5430                         if (Weapon_info[swp->primary_bank_weapons[bank_index]].wi_flags & WIF_PUNCTURE) {
5431                                 //nprintf(("AI", "%i: Ship %s selecting weapon %s\n", Framecount, Ships[objp->instance].ship_name, Weapon_info[swp->primary_bank_weapons[bank_index]].name));
5432                                 return swp->current_primary_bank;
5433                         }
5434                 }
5435                 for (int i=0; i<swp->num_primary_banks; i++) {
5436                         int     weapon_info_index;
5437
5438                         weapon_info_index = swp->primary_bank_weapons[i];
5439
5440                         if (weapon_info_index > -1){
5441                                 if (Weapon_info[weapon_info_index].wi_flags & WIF_PUNCTURE) {
5442                                         swp->current_primary_bank = i;
5443                                         //nprintf(("AI", "%i: Ship %s selecting weapon %s\n", Framecount, Ships[objp->instance].ship_name, Weapon_info[swp->primary_bank_weapons[i]].name));
5444                                         return i;
5445                                 }
5446                         }
5447                 }
5448                 
5449                 // AL 26-3-98: If we couldn't find a puncture weapon, pick first available weapon if one isn't active
5450                 if ( swp->current_primary_bank < 0 ) {
5451                         if ( swp->num_primary_banks > 0 ) {
5452                                 swp->current_primary_bank = 0;
5453                         }
5454                 }
5455
5456         } else {                //      Don't need to be using a puncture weapon.
5457                 if (swp->current_primary_bank >= 0) {
5458                         if (!(Weapon_info[swp->primary_bank_weapons[swp->current_primary_bank]].wi_flags & WIF_PUNCTURE)){
5459                                 return swp->current_primary_bank;
5460                         }
5461                 }
5462                 for (int i=0; i<swp->num_primary_banks; i++) {
5463                         if (swp->primary_bank_weapons[i] > -1) {
5464                                 if (!(Weapon_info[swp->primary_bank_weapons[i]].wi_flags & WIF_PUNCTURE)) {
5465                                         swp->current_primary_bank = i;
5466                                         nprintf(("AI", "%i: Ship %s selecting weapon %s\n", Framecount, Ships[objp->instance].ship_name, Weapon_info[swp->primary_bank_weapons[i]].name));
5467                                         return i;
5468                                 }
5469                         }
5470                 }
5471                 //      Wasn't able to find a non-puncture weapon.  Stick with what we have.
5472         }
5473
5474         Assert( swp->current_primary_bank != -1 );              // get Alan or Allender
5475
5476         return swp->current_primary_bank;
5477 }
5478
5479 //      --------------------------------------------------------------------------
5480 //      Maybe link primary weapons.
5481 void set_primary_weapon_linkage(object *objp)
5482 {
5483         ship            *shipp;
5484         ai_info *aip;
5485
5486         shipp = &Ships[objp->instance];
5487         aip     = &Ai_info[shipp->ai_index];
5488
5489         shipp->flags &= ~SF_PRIMARY_LINKED;
5490
5491         if (Num_weapons > (int) (MAX_WEAPONS * 0.75f)) {
5492                 if (shipp->flags & SF_PRIMARY_LINKED)
5493                         nprintf(("AI", "Frame %i, ship %s: Unlinking primaries.\n", Framecount, shipp->ship_name));
5494                 shipp->flags &= ~SF_PRIMARY_LINKED;
5495                 return;         //      If low on slots, don't link.
5496         }
5497
5498         shipp->flags &= ~SF_PRIMARY_LINKED;
5499
5500         // AL: ensure target is a ship!
5501         if ( Objects[aip->target_objnum].type == OBJ_SHIP ) {
5502                 // If trying to destroy a big ship (i.e., not disable/disarm), always unleash all weapons
5503                 if ( ship_get_SIF(&Ships[Objects[aip->target_objnum].instance]) & SIF_BIG_SHIP) {
5504                         if ( aip->targeted_subsys == NULL ) {
5505                                 shipp->flags |= SF_PRIMARY_LINKED;
5506                                 shipp->flags |= SF_SECONDARY_DUAL_FIRE;
5507                                 return;
5508                         }
5509                 }
5510         }
5511
5512         // AL 2-11-98: If ship has a disarm or disable goal, don't link unless both weapons are
5513         //                                      puncture weapons
5514         if ( (aip->active_goal != AI_GOAL_NONE) && (aip->active_goal != AI_ACTIVE_GOAL_DYNAMIC) ) {
5515                 if ( aip->goals[aip->active_goal].ai_mode & (AI_GOAL_DISABLE_SHIP|AI_GOAL_DISARM_SHIP) ) {
5516                         ship_weapon     *swp;
5517                         swp = &shipp->weapons;
5518                         // only continue if both primaries are puncture weapons
5519                         if ( swp->num_primary_banks == 2 ) {
5520                                 if ( !(Weapon_info[swp->primary_bank_weapons[0]].wi_flags & WIF_PUNCTURE) ) 
5521                                         return;
5522                                 if ( !(Weapon_info[swp->primary_bank_weapons[1]].wi_flags & WIF_PUNCTURE) ) 
5523                                         return;
5524                         }
5525                 }
5526         }
5527
5528         //      Don't want all ships always linking weapons at start, so asynchronize.
5529         if (Missiontime < i2f(30))
5530                 return;
5531         else if (Missiontime < i2f(120)) {
5532                 int r = static_rand((Missiontime >> 17) ^ OBJ_INDEX(objp));
5533                 if ( (r&3) != 0)
5534                         return;
5535         }
5536
5537         if (shipp->weapon_energy > Link_energy_levels_always[Game_skill_level]) {
5538                 shipp->flags |= SF_PRIMARY_LINKED;
5539         } else if (shipp->weapon_energy > Link_energy_levels_maybe[Game_skill_level]) {
5540                 if (objp->hull_strength < Ship_info[shipp->ship_info_index].initial_hull_strength/3.0f)
5541                         shipp->flags |= SF_PRIMARY_LINKED;
5542         }
5543 }
5544
5545 //      --------------------------------------------------------------------------
5546 //      Fire the current primary weapon.
5547 //      *objp is the object to fire from.
5548 void ai_fire_primary_weapon(object *objp)
5549 {
5550         ship                    *shipp = &Ships[objp->instance];
5551         ship_weapon     *swp = &shipp->weapons;
5552         ship_info       *sip;
5553         ai_info         *aip;
5554         object          *enemy_objp;
5555
5556         Assert( shipp->ship_info_index >= 0 && shipp->ship_info_index < MAX_SHIP_TYPES);
5557         sip = &Ship_info[shipp->ship_info_index];
5558
5559         aip = &Ai_info[shipp->ai_index];
5560
5561         //      If low on slots, fire a little less often.
5562         if (Num_weapons > (int) (0.9f * MAX_WEAPONS)) {
5563                 if (frand() > 0.5f) {
5564                         nprintf(("AI", "Frame %i, %s not fire.\n", Framecount, shipp->ship_name));
5565                         return;
5566                 }
5567         }
5568
5569         if (!Ai_firing_enabled){
5570                 return;
5571         }
5572
5573         if (aip->target_objnum != -1){
5574                 enemy_objp = &Objects[aip->target_objnum];
5575         } else {
5576                 enemy_objp = NULL;
5577         }
5578
5579         if ( (swp->current_primary_bank < 0) || (swp->current_primary_bank >= swp->num_primary_banks) || timestamp_elapsed(aip->primary_select_timestamp)) {
5580                 int     flags = 0;
5581                 // AL 2-11-98: If attacking any subsystem (not just engines), use disrupter weapon
5582 //              if ((aip->targeted_subsys != NULL) && (aip->targeted_subsys->system_info->type == SUBSYSTEM_ENGINE)) {
5583                 if ( aip->targeted_subsys != NULL ) {
5584                         flags = WIF_PUNCTURE;
5585                 }
5586                 ai_select_primary_weapon(objp, enemy_objp, flags);
5587                 ship_primary_changed(shipp);    // AL: maybe send multiplayer information when AI ship changes primaries
5588                 aip->primary_select_timestamp = timestamp(5 * 1000);    //      Maybe change primary weapon five seconds from now.
5589         }
5590
5591         //      If pointing nearly at predicted collision point of target, bash orientation to be perfectly pointing.
5592         float   dot;
5593         vector  v2t;
5594
5595 //      if (!IS_VEC_NULL(&G_predicted_pos)) {
5596         if (!( vm_vec_mag_quick(&G_predicted_pos) < AICODE_SMALL_MAGNITUDE )) {
5597                 if ( !vm_vec_cmp(&G_predicted_pos, &G_fire_pos) ) {
5598                         nprintf(("Warning", "Avoid NULL vector assert.. why are G_predicted_pos and G_fire_pos the same?\n"));
5599                 } else {
5600                         vm_vec_normalized_dir(&v2t, &G_predicted_pos, &G_fire_pos);
5601                         dot = vm_vec_dot(&v2t, &objp->orient.fvec);
5602                         if (dot > .998629534f){ //      if within 3.0 degrees of desired heading, bash
5603                                 vm_vector_2_matrix(&objp->orient, &v2t, &objp->orient.uvec, NULL);
5604                         }
5605                 }
5606         }
5607
5608         //      Make sure not firing at a protected ship unless firing at a live subsystem.
5609         //      Note: This happens every time the ship tries to fire, perhaps every frame.
5610         //      Should be wrapped in a timestamp, same one that enables it to fire, but that is complicated
5611         //      by multiple banks it can fire from.
5612         if (aip->target_objnum != -1) {
5613                 object  *tobjp = &Objects[aip->target_objnum];
5614                 if (tobjp->flags & OF_PROTECTED) {
5615                         if (aip->targeted_subsys != NULL) {
5616                                 int     type;
5617
5618                                 type = aip->targeted_subsys->system_info->type;
5619                                 if (ship_get_subsystem_strength(&Ships[tobjp->instance], type) == 0.0f) {
5620                                         aip->target_objnum = -1;
5621                                         return;
5622                                 }
5623                         } else {
5624                                 aip->target_objnum = -1;
5625                                 return;
5626                         }
5627                 }
5628         }
5629
5630         //      If enemy is protected, not firing a puncture weapon and enemy's hull is low, don't fire.
5631         if ((enemy_objp != NULL) && (enemy_objp->flags & OF_PROTECTED)) {
5632                 // AL: 3-6-98: Check if current_primary_bank is valid
5633                 if ((enemy_objp->hull_strength < 750.0f) && 
5634                         ((aip->targeted_subsys == NULL) || (enemy_objp->hull_strength < aip->targeted_subsys->current_hits + 50.0f)) &&
5635                         (swp->current_primary_bank >= 0) ) {
5636                         if (!(Weapon_info[swp->primary_bank_weapons[swp->current_primary_bank]].wi_flags & WIF_PUNCTURE)) {
5637                                 //nprintf(("AI", "Ship %s not firing at protected ship %s because not using disruptor.\n", Ships[objp->instance].ship_name, Ships[enemy_objp->instance].ship_name));
5638                                 swp->next_primary_fire_stamp[swp->current_primary_bank] = timestamp(1000);
5639                                 return;
5640                         }
5641
5642                         /*
5643                         int     num_attacking;
5644                         num_attacking = num_enemies_attacking(enemy_objp-Objects);
5645                         if (enemy_objp->hull_strength / num_attacking < 200.0f) {
5646                                 if (frand() < 0.75f) {
5647                                         nprintf(("AI", "Ship %s not firing at protected ship %s because too many attacking.\n", Ships[objp->instance].ship_name, Ships[enemy_objp->instance].ship_name));
5648                                         swp->next_primary_fire_stamp[swp->current_primary_bank] = timestamp(500);
5649                                         return;
5650                                 }
5651                         }
5652                         */
5653                 }
5654         }
5655
5656         set_primary_weapon_linkage(objp);
5657         
5658         // I think this will properly solve the problem
5659         // fire non-streaming weapons
5660         ship_fire_primary(objp, 0);
5661         
5662         // fire streaming weapons
5663         shipp->flags |= SF_TRIGGER_DOWN;
5664         ship_fire_primary(objp, 1);
5665         shipp->flags &= ~SF_TRIGGER_DOWN;
5666 }
5667
5668 //      --------------------------------------------------------------------------
5669 //      Return number of nearby enemy fighters.
5670 //      threshold is the distance within which a ship is considered near.
5671 //
5672 // input:       enemy_team_mask =>      teams that are considered as an enemy
5673 //                              pos                                     =>      world position to measure ship distances from
5674 //                              threshold                       =>      max distance from pos to be considered "near"
5675 //
5676 // exit:                number of ships within threshold units of pos
5677 int num_nearby_fighters(int enemy_team_mask, vector *pos, float threshold)
5678 {
5679         ship_obj        *so;
5680         object  *ship_objp;
5681         int             count = 0;
5682
5683         for ( so = GET_FIRST(&Ship_obj_list); so != END_OF_LIST(&Ship_obj_list); so = GET_NEXT(so) ) {
5684
5685                 ship_objp = &Objects[so->objnum];
5686
5687                 if (Ships[ship_objp->instance].team & enemy_team_mask) {
5688                         if (Ship_info[Ships[ship_objp->instance].ship_info_index].flags & (SIF_FIGHTER | SIF_BOMBER)) {
5689                                 if (vm_vec_dist_quick(pos, &ship_objp->pos) < threshold)
5690                                         count++;
5691                         }
5692                 }
5693         }
5694
5695         return count;
5696 }
5697
5698 //      --------------------------------------------------------------------------
5699 //      Select secondary weapon to fire.
5700 //      Currently, 1/16/98:
5701 //              If 0 secondary weapons available, return -1
5702 //              If 1 available, use it.
5703 //              If 2 or more, if the current weapon is one of them, stick with it, otherwise choose a random one.
5704 //      priority1 and priority2 are Weapon_info[] bitmasks such as WIF_HOMING_ASPECT.  If any weapon has any bit in priority1
5705 //      set, that weapon will be selected.  If not, apply to priority2.  If neither, return -1, meaning no weapon selected.
5706 //      Note, priorityX have default values of -1, meaning if not set, they will match any weapon.
5707 //      Return value:
5708 //              bank index
5709 //      Should do this:
5710 //              Favor aspect seekers when attacking small ships faraway.
5711 //              Favor rapid fire dumbfire when attacking a large ship.
5712 //              Ignore heat seekers because we're not sure how they'll work.
5713 void ai_select_secondary_weapon(object *objp, ship_weapon *swp, int priority1 = -1, int priority2 = -1)
5714 {
5715         int     num_weapon_types;
5716         int     weapon_id_list[MAX_WEAPON_TYPES], weapon_bank_list[MAX_WEAPON_TYPES];
5717         int     i;
5718         int     ignore_mask;
5719         int     initial_bank;
5720
5721         initial_bank = swp->current_secondary_bank;
5722
5723         //      Ignore bombs unless one of the priorities asks for them to be selected.
5724         if (WIF_HUGE & (priority1 | priority2))
5725                 ignore_mask = 0;
5726         else
5727                 ignore_mask = WIF_HUGE;
5728
5729         if (!(WIF_BOMBER_PLUS & (priority1 | priority2)))
5730                 ignore_mask |= WIF_BOMBER_PLUS;
5731
5732 #ifndef NDEBUG
5733         for (i=0; i<MAX_WEAPON_TYPES; i++) {
5734                 weapon_id_list[i] = -1;
5735                 weapon_bank_list[i] = -1;
5736         }
5737 #endif
5738
5739         //      Stuff weapon_bank_list with bank index of available weapons.
5740         num_weapon_types = get_available_secondary_weapons(objp, weapon_id_list, weapon_bank_list);
5741
5742         int     priority2_index = -1;
5743
5744         for (i=0; i<num_weapon_types; i++) {
5745                 int     wi_flags;
5746
5747                 wi_flags = Weapon_info[swp->secondary_bank_weapons[weapon_bank_list[i]]].wi_flags;
5748                 if (!(wi_flags & ignore_mask)) {                                        //      Maybe bombs are illegal.
5749                         if (wi_flags & priority1) {
5750                                 swp->current_secondary_bank = weapon_bank_list[i];                              //      Found first priority, return it.
5751                                 break;
5752                         } else if (wi_flags & priority2)
5753                                 priority2_index = weapon_bank_list[i];  //      Found second priority, but might still find first priority.
5754                 }
5755         }
5756
5757         //      If didn't find anything above, then pick any secondary weapon.
5758         if (i == num_weapon_types) {
5759                 swp->current_secondary_bank = priority2_index;  //      Assume we won't find anything.
5760                 if (priority2_index == -1) {
5761                         for (i=0; i<num_weapon_types; i++) {
5762                                 int     wi_flags;
5763
5764                                 wi_flags = Weapon_info[swp->secondary_bank_weapons[weapon_bank_list[i]]].wi_flags;
5765                                 if (!(wi_flags & ignore_mask)) {                                        //      Maybe bombs are illegal.
5766                                         if (swp->secondary_bank_ammo[i] > 0) {
5767                                                 swp->current_secondary_bank = i;
5768                                                 break;
5769                                         }
5770                                 }
5771                         }
5772                 }
5773         }
5774
5775         //      If switched banks, force reacquisition of aspect lock.
5776         if (swp->current_secondary_bank != initial_bank) {
5777                 ai_info *aip = &Ai_info[Ships[objp->instance].ai_index];
5778                 
5779                 aip->aspect_locked_time = 0.0f;
5780                 aip->current_target_is_locked = 0;
5781         }
5782
5783
5784         ship_secondary_changed(&Ships[objp->instance]); // AL: let multiplayer know if secondary bank has changed
5785         // nprintf(("AI", "Ship %s selected weapon %s\n", Ships[objp->instance].ship_name, Weapon_info[swp->secondary_bank_weapons[swp->current_secondary_bank]].name));
5786 }
5787
5788 //      Return number of objects homing on object *target_objp
5789 int compute_num_homing_objects(object *target_objp)
5790 {
5791         object  *objp;
5792         int             count = 0;
5793
5794         for ( objp = GET_FIRST(&obj_used_list); objp !=END_OF_LIST(&obj_used_list); objp = GET_NEXT(objp) ) {
5795                 if (objp->type == OBJ_WEAPON) {
5796                         if (Weapon_info[Weapons[objp->instance].weapon_info_index].wi_flags & WIF_HOMING) {
5797                                 if (Weapons[objp->instance].homing_object == target_objp) {
5798                                         count++;
5799                                 }
5800                         }
5801                 }
5802         }
5803
5804         return count;
5805 }
5806
5807 //      Object *firing_objp just fired weapon weapon_index (index in Weapon_info).
5808 //      If it's a shockwave weapon, tell your team about it!
5809 void ai_maybe_announce_shockwave_weapon(object *firing_objp, int weapon_index)
5810 {
5811         if ((firing_objp->type == OBJ_SHIP) && (Weapon_info[weapon_index].shockwave_speed > 0.0f)) {
5812                 ship_obj        *so;
5813                 int             firing_ship_team;
5814
5815                 firing_ship_team = Ships[firing_objp->instance].team;
5816
5817                 for ( so = GET_FIRST(&Ship_obj_list); so != END_OF_LIST(&Ship_obj_list); so = GET_NEXT(so) ) {
5818                         object  *A = &Objects[so->objnum];
5819                         Assert(A->type == OBJ_SHIP);
5820
5821                         if (Ships[A->instance].team == firing_ship_team) {
5822                                 ai_info *aip = &Ai_info[Ships[A->instance].ai_index];
5823                                 // AL 1-5-98: only avoid shockwave if not docked or repairing
5824                                 if ( !(aip->ai_flags & (AIF_DOCKED|AIF_BEING_REPAIRED)) ) {
5825                                         aip->ai_flags |= AIF_AVOID_SHOCKWAVE_WEAPON;
5826                                 }
5827                         }
5828                 }
5829         }
5830 }
5831
5832 //      Return total payload of all incoming missiles.
5833 float compute_incoming_payload(object *target_objp)
5834 {
5835         missile_obj     *mo;
5836         float                   payload = 0.0f;
5837
5838         for ( mo = GET_NEXT(&Missile_obj_list); mo != END_OF_LIST(&Missile_obj_list); mo = GET_NEXT(mo) ) {
5839                 object  *objp;
5840
5841                 objp = &Objects[mo->objnum];
5842                 Assert(objp->type == OBJ_WEAPON);
5843                 if (Weapons[objp->instance].homing_object == target_objp) {
5844                         payload += Weapon_info[Weapons[objp->instance].weapon_info_index].damage;
5845                 }
5846         }
5847
5848         return payload;
5849 }
5850
5851 //      --------------------------------------------------------------------------
5852 //      Return true if OK for *aip to fire its current weapon at its current target.
5853 //      Only reason this function returns false is:
5854 //              weapon is a homer
5855 //              targeted at player
5856 //                      OR:     player has too many homers targeted at him
5857 //                                      Missiontime in that dead zone in which can't fire at this player
5858 //      Note: If player is attacking a ship, that ship is allowed to fire at player.  Otherwise, we get in a situation in which
5859 //      player is attacking a large ship, but that large ship is not defending itself with missiles.
5860 int check_ok_to_fire(int objnum, int target_objnum, weapon_info *wip)
5861 {
5862         int     num_homers = 0;
5863         object  *tobjp = &Objects[target_objnum];
5864
5865         if (target_objnum > -1) {
5866                 // AL 3-4-98: Ensure objp target is a ship first 
5867                 if ( tobjp->type == OBJ_SHIP ) {
5868
5869                         // should not get this far. check if ship is protected from beam and weapon is type beam
5870                         if ( (wip->wi_flags & WIF_BEAM) && (tobjp->flags & OF_BEAM_PROTECTED) ) {
5871                                 Int3();
5872                                 return 0;
5873                         }
5874                         if (Ship_info[Ships[tobjp->instance].ship_info_index].flags & SIF_SMALL_SHIP) {
5875                                 num_homers = compute_num_homing_objects(&Objects[target_objnum]);
5876                         }
5877                 }
5878
5879                 //      If player, maybe fire based on Skill_level and number of incoming weapons.
5880                 //      If non-player, maybe fire based on payload of incoming weapons.
5881                 if (wip->wi_flags & WIF_HOMING) {
5882                         if ((target_objnum > -1) && (tobjp->flags & OF_PLAYER_SHIP)) {
5883                                 if (Ai_info[Ships[tobjp->instance].ai_index].target_objnum != objnum) {
5884                                         //      Don't allow AI ships to fire at player for fixed periods of time based on skill level.
5885                                         //      With 5 skill levels, at Very Easy, they fire in 1/7 of every 10 second interval.
5886                                         //      At Easy, 2/7...at Expert, 5/7
5887                                         int t = ((Missiontime /(65536*10)) ^ target_objnum ^ 0x01) % (NUM_SKILL_LEVELS+2);
5888                                         if (t > Game_skill_level) {
5889                                                 //nprintf(("AI", "Not OK to fire homer at time thing %i\n", t));
5890                                                 return 0;
5891                                         }
5892                                 }
5893                                 //nprintf(("AI", " IS OK to fire homer at time thing %i ***\n", t));
5894                                 int     swarmers = 0;
5895                                 if (wip->wi_flags & WIF_SWARM)
5896                                         swarmers = 2;   //      Note, always want to be able to fire swarmers if no currently incident homers.
5897                                 if (Max_allowed_player_homers[Game_skill_level] < num_homers + swarmers) {
5898                                         return 0;
5899                                 }
5900                         } else if (num_homers > 3) {
5901                                 float   incoming_payload;
5902
5903                                 incoming_payload = compute_incoming_payload(&Objects[target_objnum]);
5904
5905                                 if (incoming_payload > tobjp->hull_strength) {
5906                                         return 0;
5907                                 }
5908                         }
5909                 }
5910         }
5911
5912         return 1;
5913 }
5914
5915 //      --------------------------------------------------------------------------
5916 //      Fire a secondary weapon.
5917 //      Maybe choose to fire a different one.
5918 //      priority1 and priority2 are optional parameters with defaults = -1
5919 int ai_fire_secondary_weapon(object *objp, int priority1, int priority2)
5920 {
5921         ship_weapon *swp;
5922         ship    *shipp;
5923         ship_info *sip;
5924         int             current_bank;
5925         int             rval = 0;
5926
5927 #ifndef NDEBUG
5928         if (!Ai_firing_enabled)
5929                 return rval;
5930 #endif
5931
5932         Assert( objp != NULL );
5933         Assert(objp->type == OBJ_SHIP);
5934         shipp = &Ships[objp->instance];
5935         swp = &shipp->weapons;
5936
5937         Assert( shipp->ship_info_index >= 0 && shipp->ship_info_index < MAX_SHIP_TYPES);
5938         sip = &Ship_info[shipp->ship_info_index];
5939
5940         //      Select secondary weapon.
5941         current_bank = swp->current_secondary_bank; //ai_select_secondary_weapon(objp, swp, priority1, priority2);
5942
5943         //nprintf(("AI", "Frame %i: Current bank = %i, ammo remaining = %i\n", Framecount, current_bank, swp->secondary_bank_ammo[current_bank]));
5944         if (current_bank == -1) {
5945                 swp->next_secondary_fire_stamp[current_bank] = timestamp(500);
5946                 return rval;
5947         }
5948
5949         Assert(current_bank < shipp->weapons.num_secondary_banks);
5950
5951         weapon_info     *wip = &Weapon_info[shipp->weapons.secondary_bank_weapons[current_bank]];
5952
5953         if ((wip->wi_flags & WIF_HOMING_ASPECT) && (!Ai_info[shipp->ai_index].current_target_is_locked)) {
5954                 //nprintf(("AI", "Not firing secondary weapon because not aspect locked.\n"));
5955                 swp->next_secondary_fire_stamp[current_bank] = timestamp(250);
5956         } else if ((wip->wi_flags & WIF_BOMB) || (vm_vec_dist_quick(&objp->pos, &En_objp->pos) > 50.0f)) {
5957                 //      This might look dumb, firing a bomb even if closer than 50 meters, but the reason is, if you're carrying
5958                 //      bombs, delivering them is probably more important than surviving.
5959                 ai_info *aip;
5960
5961                 aip = &Ai_info[shipp->ai_index];
5962                 
5963                 //      Note, maybe don't fire if firing at player and any homers yet fired.
5964                 //      Decreasing chance to fire the more homers are incoming on player.
5965                 if (check_ok_to_fire(OBJ_INDEX(objp), aip->target_objnum, wip)) {
5966                         if (ship_fire_secondary(objp)) {
5967                                 rval = 1;
5968                                 swp->next_secondary_fire_stamp[current_bank] = timestamp(500);
5969                                 //nprintf(("AI", "Frane %i: Ship %s fired secondary %s\n", Framecount, Ships[objp->instance].ship_name, Weapon_info[swp->secondary_bank_weapons[current_bank]].name));
5970                         }
5971
5972                 } else {
5973                         swp->next_secondary_fire_stamp[current_bank] = timestamp(500);
5974                 }
5975         }
5976
5977         return rval;
5978 }
5979
5980 //      Return true if it looks like obj1, if continuing to move along current vector, will
5981 //      collide with obj2.
5982 int might_collide_with_ship(object *obj1, object *obj2, float dot_to_enemy, float dist_to_enemy, float duration)
5983 {
5984         if (obj1->phys_info.speed * duration + 2*(obj1->radius + obj2->radius) > dist_to_enemy)
5985                 if (dot_to_enemy > 0.8f - 2*(obj1->radius + obj2->radius)/dist_to_enemy)
5986                         return objects_will_collide(obj1, obj2, duration, 2.0f);
5987
5988 //              BABY - 
5989 //              CONDITION 1, dist_to_enemy < o1_rad + o2_rad + (obj1.speed + obj2.speed) * time + 50
5990         
5991         return 0;
5992
5993 }
5994
5995 //      --------------------------------------------------------------------------
5996 //      Return true if ship *objp firing a laser believes it will hit a teammate.
5997 int might_hit_teammate(object *firing_objp)
5998 {
5999         int             team;
6000         object  *objp;
6001         ship_obj        *so;
6002
6003         team = Ships[firing_objp->instance].team;
6004
6005         for ( so = GET_FIRST(&Ship_obj_list); so != END_OF_LIST(&Ship_obj_list); so = GET_NEXT(so) ) {
6006                 objp = &Objects[so->objnum];
6007                 if (Ships[objp->instance].team == team) {
6008                         float           dist, dot;
6009                         vector  vec_to_objp;
6010
6011                         vm_vec_sub(&vec_to_objp, &firing_objp->pos, &objp->pos);
6012                         dist = vm_vec_mag_quick(&vec_to_objp);
6013                         dot = vm_vec_dot(&firing_objp->orient.fvec, &vec_to_objp)/dist;
6014                         if (might_collide_with_ship(firing_objp, objp, dot, dist, 2.0f))
6015                                 return 1;
6016                 }
6017         }
6018
6019         return 0;
6020
6021 }
6022
6023 //int   Team_not_fire_count=0, Team_hit_count = 0;
6024
6025 void render_all_ship_bay_paths(object *objp)
6026 {
6027         int             i,j,color;
6028         ship            *sp = &Ships[objp->instance];
6029         polymodel       *pm;
6030         model_path      *mp;
6031
6032         pm = model_get(sp->modelnum);
6033         vector  global_path_point;
6034         vertex  v, prev_vertex;
6035
6036         if ( pm->ship_bay == NULL )
6037                 return;
6038
6039         for ( i = 0; i < pm->ship_bay->num_paths; i++ ) {
6040                 mp = &pm->paths[pm->ship_bay->paths[i]];
6041
6042                 for ( j = 0; j < mp->nverts; j++ ) {
6043                         vm_vec_unrotate(&global_path_point, &mp->verts[j].pos, &objp->orient);
6044                         vm_vec_add2(&global_path_point, &objp->pos);
6045                         g3_rotate_vertex(&v, &global_path_point);
6046                         color = 255 - j*50;
6047                         if ( color < 50 ) 
6048                                 color = 100;
6049                         gr_set_color(0, color, 0);
6050
6051                         if ( j == mp->nverts-1 ) {
6052                                 gr_set_color(255, 0, 0);
6053                         }
6054
6055                         g3_draw_sphere( &v, 1.5f);
6056
6057                         if ( j > 0 )
6058                                 g3_draw_line(&v, &prev_vertex);
6059
6060                         prev_vertex = v;
6061         
6062                 }
6063         }
6064 }
6065
6066 // debug function to show all path points associated with an object
6067 void render_all_subsys_paths(object *objp)
6068 {
6069         int             i,j,color;
6070         ship            *sp = &Ships[objp->instance];
6071         polymodel       *pm;
6072         model_path      *mp;
6073
6074         pm = model_get(sp->modelnum);
6075         vector  global_path_point;
6076         vertex  v, prev_vertex;
6077
6078         if ( pm->ship_bay == NULL )
6079                 return;
6080
6081         for ( i = 0; i < pm->n_paths; i++ ) {
6082                 mp = &pm->paths[i];
6083                 for ( j = 0; j < mp->nverts; j++ ) {
6084                         vm_vec_unrotate(&global_path_point, &mp->verts[j].pos, &objp->orient);
6085                         vm_vec_add2(&global_path_point, &objp->pos);
6086                         g3_rotate_vertex(&v, &global_path_point);
6087                         color = 255 - j*50;
6088                         if ( color < 50 ) 
6089                                 color = 100;
6090                         gr_set_color(0, color, 0);
6091
6092                         if ( j == mp->nverts-1 ) {
6093                                 gr_set_color(255, 0, 0);
6094                         }
6095
6096                         g3_draw_sphere( &v, 1.5f);
6097
6098                         if ( j > 0 )
6099                                 g3_draw_line(&v, &prev_vertex);
6100
6101                         prev_vertex = v;
6102                 }
6103         }
6104 }
6105
6106 void render_path_points(object *objp)
6107 {
6108         ship            *shipp = &Ships[objp->instance];
6109         ai_info *aip = &Ai_info[shipp->ai_index];
6110         object  *dobjp;
6111         polymodel       *pm;
6112
6113         render_all_subsys_paths(objp);
6114         render_all_ship_bay_paths(objp);
6115
6116         if (aip->goal_objnum < 0)
6117                 return;
6118
6119         dobjp = &Objects[aip->goal_objnum];
6120         pm = model_get(Ships[dobjp->instance].modelnum);
6121         vector  dock_point, global_dock_point;
6122         vertex  v;
6123
6124         ship_model_start(&Objects[aip->goal_objnum]);
6125         if (pm->n_docks) {
6126                 dock_point = pm->docking_bays[0].pnt[0];
6127                 model_find_world_point(&global_dock_point, &dock_point, Ships[dobjp->instance].modelnum, 0, &dobjp->orient, &dobjp->pos );
6128                 g3_rotate_vertex(&v, &global_dock_point);
6129                 gr_set_color(255, 255, 255);
6130                 g3_draw_sphere( &v, 1.5f);
6131         }
6132
6133         if (aip->path_start != -1) {
6134                 vertex          prev_vertex;
6135                 pnode                   *pp = &Path_points[aip->path_start];
6136                 int                     num_points = aip->path_length;
6137                 int                     i;
6138
6139                 for (i=0; i<num_points; i++) {
6140                         vertex  v0;
6141
6142                         g3_rotate_vertex( &v0, &pp->pos );
6143
6144                         gr_set_color(0, 128, 96);
6145                         if (i != 0)
6146                                 g3_draw_line(&v0, &prev_vertex);
6147
6148                         if (pp-Path_points == aip->path_cur)
6149                                 gr_set_color(255,255,0);
6150                         
6151                         g3_draw_sphere( &v0, 4.5f);
6152
6153                         //      Connect all the turrets that can fire upon this point to this point.
6154 /*                      if (0) { //pp->path_index != -1) {
6155                                 model_path      *pmp;
6156                                 mp_vert         *pmpv;
6157
6158                                 get_base_path_info(pp->path_index, aip->goal_objnum, &pmp, &pmpv);
6159
6160                                 if (pmpv->nturrets) {
6161                                         for (int j = 0; j<pmpv->nturrets; j++) {
6162                                                 vertex  v1;
6163                                                 vector  turret_pos;
6164                                                 ship_subsys     *ssp;
6165
6166                                                 ssp = ship_get_indexed_subsys(&Ships[Objects[aip->goal_objnum].instance], pmpv->turret_ids[j]);
6167
6168 model_find_world_point(&turret_pos, &ssp->system_info->pnt, Ships[dobjp->instance].modelnum, 0, &dobjp->orient, &dobjp->pos );
6169         
6170                                                 g3_rotate_vertex(&v1, &turret_pos);
6171                                                 gr_set_color(255, 255, 0);
6172                                                 g3_draw_line(&v0, &v1);
6173                                                 g3_draw_sphere( &v1, 1.5f);
6174                                         }
6175                                 }
6176                         } */
6177
6178                         prev_vertex = v0;
6179
6180                         pp++;
6181                 }
6182         }
6183
6184         ship_model_stop(&Objects[aip->goal_objnum]);
6185 }
6186
6187 // Return the distance that the current AI weapon will travel
6188 float ai_get_weapon_dist(ship_weapon *swp)
6189 {
6190         int     bank_num, weapon_num;
6191
6192         bank_num = swp->current_primary_bank;
6193         weapon_num = swp->primary_bank_weapons[bank_num];
6194
6195         //      If weapon_num is illegal, return a reasonable value.  A valid weapon
6196         //      will get selected when this ship tries to fire.
6197         if (weapon_num == -1) {
6198                 // Int3();
6199                 return 1000.0f;
6200         }
6201
6202         return Weapon_info[weapon_num].max_speed * Weapon_info[weapon_num].lifetime;
6203 }
6204
6205 float ai_get_weapon_speed(ship_weapon *swp)
6206 {
6207         int     bank_num, weapon_num;
6208
6209         bank_num = swp->current_primary_bank;
6210         if (bank_num < 0)
6211                 return 100.0f;
6212
6213         weapon_num = swp->primary_bank_weapons[bank_num];
6214
6215         if (weapon_num == -1) {
6216                 //Int3();
6217                 return 100.0f;
6218         }
6219
6220         return Weapon_info[weapon_num].max_speed;
6221 }
6222
6223 //      Compute the predicted position of a ship to be fired upon from a turret.
6224 //      This is based on position of firing gun, enemy object, weapon speed and skill level constraints.
6225 //      Return value in *predicted_enemy_pos.
6226 //      Also, stuff globals G_predicted_pos, G_collision_time and G_fire_pos.
6227 //      *pobjp          object firing the weapon
6228 //      *eobjp          object being fired upon
6229 void set_predicted_enemy_pos_turret(vector *predicted_enemy_pos, vector *gun_pos, object *pobjp, vector *enemy_pos, vector *enemy_vel, float weapon_speed, float time_enemy_in_range)
6230 {
6231         ship    *shipp = &Ships[pobjp->instance];
6232         float   range_time;
6233
6234         //weapon_speed = ai_get_weapon_speed(&shipp->weapons);
6235
6236         if (weapon_speed < 1.0f)
6237                 weapon_speed = 1.0f;
6238
6239         range_time = 2.0f;
6240
6241         //      Make it take longer for enemies to get player's allies in range based on skill level.
6242         if (Ships[pobjp->instance].team != Ships[Player_obj->instance].team)
6243                 range_time += In_range_time[Game_skill_level];
6244
6245         //nprintf(("AI", "time enemy in range = %7.3f\n", aip->time_enemy_in_range));
6246
6247         if (time_enemy_in_range < range_time) {
6248                 float   dist;
6249
6250                 dist = vm_vec_dist_quick(&pobjp->pos, enemy_pos);
6251                 vm_vec_scale_add(predicted_enemy_pos, enemy_pos, enemy_vel, time_enemy_in_range * dist/weapon_speed);
6252         } else {
6253                 float   collision_time, scale;
6254                 vector  rand_vec;
6255                 ai_info *aip = &Ai_info[shipp->ai_index];
6256
6257                 collision_time = compute_collision_time(enemy_pos, enemy_vel, gun_pos, weapon_speed);
6258
6259                 if (collision_time == 0.0f){
6260                         collision_time = 100.0f;
6261                 }
6262
6263                 vm_vec_scale_add(predicted_enemy_pos, enemy_pos, enemy_vel, collision_time);
6264                 if (time_enemy_in_range > 2*range_time){
6265                         scale = (1.0f - aip->ai_accuracy) * 4.0f;
6266                 } else {
6267                         scale = (1.0f - aip->ai_accuracy) * 4.0f * (1.0f + 4.0f * (1.0f - time_enemy_in_range/(2*range_time)));
6268                 }               
6269
6270                 static_randvec(((pobjp-Objects) ^ (Missiontime >> 16)) & 7, &rand_vec);
6271
6272                 vm_vec_scale_add2(predicted_enemy_pos, &rand_vec, scale);
6273                 G_collision_time = collision_time;
6274                 G_fire_pos = *gun_pos;
6275         }
6276
6277         G_predicted_pos = *predicted_enemy_pos;
6278 }
6279
6280 //      Compute the predicted position of a ship to be fired upon.
6281 //      This is based on current position of firing object, enemy object, relative position of gun on firing object,
6282 //      weapon speed and skill level constraints.
6283 //      Return value in *predicted_enemy_pos.
6284 //      Also, stuff globals G_predicted_pos, G_collision_time and G_fire_pos.
6285 void set_predicted_enemy_pos(vector *predicted_enemy_pos, object *pobjp, object *eobjp, ai_info *aip)
6286 {
6287         float   weapon_speed, range_time;
6288         ship    *shipp = &Ships[pobjp->instance];
6289
6290         weapon_speed = ai_get_weapon_speed(&shipp->weapons);
6291         weapon_speed = max(weapon_speed, 1.0f);         // set not less than 1
6292
6293         range_time = 2.0f;
6294
6295         //      Make it take longer for enemies to get player's allies in range based on skill level.
6296         // but don't bias team v. team missions
6297         if ( !((Game_mode & GM_MULTIPLAYER) && (Netgame.type_flags & NG_TYPE_TEAM)) ) {
6298                 if (Ships[pobjp->instance].team != Ships[Player_obj->instance].team) {
6299                         range_time += In_range_time[Game_skill_level];
6300                 }
6301         }
6302         //nprintf(("AI", "time enemy in range = %7.3f\n", aip->time_enemy_in_range));
6303
6304         if (aip->time_enemy_in_range < range_time) {
6305                 float   dist;
6306
6307                 dist = vm_vec_dist_quick(&pobjp->pos, &eobjp->pos);
6308                 vm_vec_scale_add(predicted_enemy_pos, &eobjp->pos, &eobjp->phys_info.vel, aip->time_enemy_in_range * dist/weapon_speed);
6309         } else {
6310                 float   collision_time;
6311                 vector  gun_pos, pnt;
6312                 polymodel *po = model_get( Ship_info[shipp->ship_info_index].modelnum );
6313
6314                 //      Compute position of gun in absolute space and use that as fire position.
6315                 if(po->gun_banks != NULL){
6316                         pnt = po->gun_banks[0].pnt[0];
6317                 } else {
6318                         pnt = Objects[shipp->objnum].pos;
6319                 }
6320                 vm_vec_unrotate(&gun_pos, &pnt, &pobjp->orient);
6321                 vm_vec_add2(&gun_pos, &pobjp->pos);
6322
6323                 collision_time = compute_collision_time(&eobjp->pos, &eobjp->phys_info.vel, &gun_pos, weapon_speed);
6324
6325                 if (collision_time == 0.0f) {
6326                         collision_time = 100.0f;
6327                 }
6328
6329                 vm_vec_scale_add(predicted_enemy_pos, &eobjp->pos, &eobjp->phys_info.vel, collision_time);
6330
6331                 // set globals
6332                 G_collision_time = collision_time;
6333                 G_fire_pos = gun_pos;
6334         }
6335
6336         // Now add error terms (1) regular aim (2) EMP (3) stealth
6337         float scale = 0.0f;
6338         vector rand_vec;
6339
6340         // regular skill level error in aim
6341         if (aip->time_enemy_in_range > 2*range_time) {
6342                 scale = (1.0f - aip->ai_accuracy) * 4.0f;
6343         } else {
6344                 scale = (1.0f - aip->ai_accuracy) * 4.0f * (1.0f + 4.0f * (1.0f - aip->time_enemy_in_range/(2*range_time)));
6345         }
6346
6347         // if this ship is under the effect of an EMP blast, throw his aim off a bit
6348         if (shipp->emp_intensity > 0.0f) {
6349                 // never go lower than 1/2 of the EMP effect max, otherwise things aren't noticeable
6350                 scale += (MAX_EMP_INACCURACY * (shipp->emp_intensity < 0.5f ? 0.5f : shipp->emp_intensity));
6351                 mprintf(("AI miss scale factor (EMP) %f\n",scale));
6352         }
6353
6354         // if stealthy ship, throw his aim off, more when farther away and when dot is small
6355         if ( aip->ai_flags & AIF_STEALTH_PURSIUT ) {
6356                 float dist = vm_vec_dist_quick(&pobjp->pos, &eobjp->pos);
6357                 vector temp;
6358                 vm_vec_sub(&temp, &eobjp->pos, &pobjp->pos);
6359                 vm_vec_normalize_quick(&temp);
6360                 float dot = vm_vec_dotprod(&temp, &pobjp->orient.fvec);
6361                 float st_err = 3.0f * (1.4f - dot) * (1.0f + dist / (get_skill_stealth_dist_scaler() * STEALTH_MAX_VIEW_DIST)) * (1 - aip->ai_accuracy);
6362                 scale += st_err;
6363                 // mprintf(("error term: %.1f, total %.1f, dot %.3f\n", st_err, scale, dot));
6364         }
6365
6366         // get a random vector that changes slowly over time (1x / sec)
6367         static_randvec(((pobjp-Objects) ^ (Missiontime >> 16)) & 7, &rand_vec);
6368
6369         vm_vec_scale_add2(predicted_enemy_pos, &rand_vec, scale);
6370
6371         // set global
6372         G_predicted_pos = *predicted_enemy_pos;
6373 }
6374
6375 //      Handler of submode for Chase.  Go into a continuous turn for awhile.
6376 void ai_chase_ct()
6377 {
6378         vector          tvec;
6379         ship_info       *sip;
6380         ai_info         *aip;
6381
6382         Assert(Ships[Pl_objp->instance].ship_info_index >= 0);
6383         sip = &Ship_info[Ships[Pl_objp->instance].ship_info_index];
6384         Assert(Ships[Pl_objp->instance].ai_index >= 0);
6385         aip = &Ai_info[Ships[Pl_objp->instance].ai_index];
6386
6387         //      Make a continuous turn towards any combination of possibly negated
6388         // up and right vectors.
6389         tvec = Pl_objp->pos;
6390
6391         if (aip->submode_parm0 & 0x01)
6392                 vm_vec_add2(&tvec, &Pl_objp->orient.rvec);
6393         if (aip->submode_parm0 & 0x02)
6394                 vm_vec_sub2(&tvec, &Pl_objp->orient.rvec);
6395         if (aip->submode_parm0 & 0x04)
6396                 vm_vec_add2(&tvec, &Pl_objp->orient.uvec);
6397         if (aip->submode_parm0 & 0x08)
6398                 vm_vec_sub2(&tvec, &Pl_objp->orient.uvec);
6399
6400         //      Detect degenerate cases that cause tvec to be same as player pos.
6401         if (vm_vec_dist_quick(&tvec, &Pl_objp->pos) < 0.1f) {
6402                 aip->submode_parm0 &= 0x05;
6403                 if (aip->submode_parm0 == 0)
6404                         aip->submode_parm0 = 1;
6405                 vm_vec_add2(&tvec, &Pl_objp->orient.rvec);
6406         }
6407
6408         ai_turn_towards_vector(&tvec, Pl_objp, flFrametime, sip->srotation_time, NULL, NULL, 0.0f, 0);
6409         accelerate_ship(aip, 1.0f);
6410 }
6411
6412 //      ATTACK submode handler for chase mode.
6413 void ai_chase_eb(ai_info *aip, ship_info *sip, vector *predicted_enemy_pos, float dist_to_enemy)
6414 {
6415         vector  _pep;
6416         float           dot_to_enemy, dot_from_enemy;
6417
6418         compute_dots(Pl_objp, En_objp, &dot_to_enemy, &dot_from_enemy);
6419
6420         //      If we're trying to slow down to get behind, then point to turn towards is different.
6421         _pep = *predicted_enemy_pos;
6422         if ((dot_to_enemy > dot_from_enemy + 0.1f) || (dot_to_enemy > 0.9f))
6423                 vm_vec_scale_add(&_pep, &Pl_objp->pos, &En_objp->orient.fvec, 100.0f);
6424
6425         ai_turn_towards_vector(&_pep, Pl_objp, flFrametime, sip->srotation_time, NULL, NULL, 0.0f, 0);
6426
6427         accelerate_ship(aip, 0.0f);
6428 }
6429
6430 //      Return time until weapon_objp might hit ship_objp.
6431 //      Assumes ship_objp is not moving.
6432 //      Returns negative time if not going to hit.
6433 //      This is a very approximate function, but is pretty fast.
6434 float ai_endangered_time(object *ship_objp, object *weapon_objp)
6435 {
6436         float           to_dot, from_dot, dist;
6437
6438         dist = compute_dots(ship_objp, weapon_objp, &to_dot, &from_dot);
6439
6440         //      Note, this is bogus.  It assumes only the weapon is moving.
6441         //      Only proceed if weapon sort of pointing at object and object pointing towards or away from weapon
6442         //      (Ie, if object moving at right angle to weapon, just continue for now...)
6443         if (weapon_objp->phys_info.speed < 1.0f)
6444                 return dist + 1.0f;
6445         else if ((from_dot > 0.1f) && (dist/(from_dot*from_dot) < 48*ship_objp->radius)) //: don't require them to see it, they have instruments!: && (fl_abs(to_dot) > 0.5f))
6446                 return dist / weapon_objp->phys_info.speed;
6447         else
6448                 return -1.0f;
6449 }
6450
6451 //      Return time until danger weapon could hit this ai object.
6452 //      Return negative time if not endangered.
6453 float ai_endangered_by_weapon(ai_info *aip)
6454 {
6455         object  *weapon_objp;
6456
6457         if (aip->danger_weapon_objnum == -1) {
6458                 return -1.0f;
6459         }
6460
6461         weapon_objp = &Objects[aip->danger_weapon_objnum];
6462
6463         if (weapon_objp->signature != aip->danger_weapon_signature) {
6464                 aip->danger_weapon_objnum = -1;
6465                 return -1.0f;
6466         }
6467
6468         return ai_endangered_time(&Objects[Ships[aip->shipnum].objnum], weapon_objp);
6469 }
6470
6471 //      Return true if this ship is near full strength.
6472 int ai_near_full_strength(object *objp, ship_info *sip)
6473 {
6474         return (objp->hull_strength/sip->initial_hull_strength > 0.9f) || (get_shield_strength(objp)/sip->shields > 0.8f);
6475 }
6476                                 
6477 //      Set acceleration while in attack mode.
6478 void attack_set_accel(ai_info *aip, float dist_to_enemy, float dot_to_enemy, float dot_from_enemy)
6479 {
6480         float   speed_ratio;
6481
6482         if (En_objp->phys_info.speed > 1.0f)
6483                 speed_ratio = Pl_objp->phys_info.speed/En_objp->phys_info.speed;
6484         else
6485                 speed_ratio = 5.0f;
6486
6487         //      Sometimes, told to attack slowly.  Allows to get in more hits.
6488         if (aip->ai_flags & AIF_ATTACK_SLOWLY) {
6489                 if ((dist_to_enemy > 200.0f) && (dist_to_enemy < 800.0f)) {
6490                         if ((dot_from_enemy < 0.9f) || ai_near_full_strength(Pl_objp, &Ship_info[Ships[Pl_objp->instance].ship_info_index])) {
6491                                 //nprintf(("AI", " slowly "));
6492                                 accelerate_ship(aip, max(1.0f - (dist_to_enemy-200.0f)/600.0f, 0.1f));
6493                                 return;
6494                         }
6495                 } else
6496                         aip->ai_flags &= ~AIF_ATTACK_SLOWLY;
6497         }
6498
6499         if (dist_to_enemy > 200.0f + vm_vec_mag_quick(&En_objp->phys_info.vel) * dot_from_enemy + Pl_objp->phys_info.speed * speed_ratio) {
6500                 //nprintf(("AI", "1"));
6501                 if (ai_maybe_fire_afterburner(Pl_objp, aip)) {
6502                         if (dist_to_enemy > 800.0f) {
6503                                 if (!( Pl_objp->phys_info.flags & PF_AFTERBURNER_ON )) {
6504                                         float percent_left;
6505                                         ship    *shipp;
6506                                         ship_info *sip;
6507
6508                                         shipp = &Ships[Pl_objp->instance];
6509                                         sip = &Ship_info[shipp->ship_info_index];
6510
6511                                         if (sip->afterburner_fuel_capacity > 0.0f) {
6512                                                 percent_left = 100.0f * shipp->afterburner_fuel / sip->afterburner_fuel_capacity;
6513                                                 if (percent_left > 30.0f + ((Pl_objp-Objects) & 0x0f)) {
6514                                                         afterburners_start(Pl_objp);
6515                                                         aip->afterburner_stop_time = Missiontime + F1_0 + static_rand(Pl_objp-Objects)/4;
6516                                                 }
6517                                         }
6518                                 }
6519                         }
6520                 }
6521
6522                 accelerate_ship(aip, 1.0f);
6523         } else if ((Missiontime - aip->last_hit_time > F1_0*7)
6524                 && (En_objp->phys_info.speed < 10.0f) 
6525                 && (dist_to_enemy > 25.0f) 
6526                 && (dot_to_enemy > 0.8f)
6527                 && (dot_from_enemy < 0.8f)) {
6528                 accelerate_ship(aip, 0.0f);             //      No one attacking us, so don't need to move.
6529         } else if ((dot_from_enemy < 0.25f) && (dot_to_enemy > 0.5f)) {
6530                 set_accel_for_target_speed(Pl_objp, En_objp->phys_info.speed);
6531         } else if (Pl_objp->phys_info.speed < 15.0f) {
6532                 accelerate_ship(aip, 1.0f);
6533         } else if (Pl_objp->phys_info.speed > En_objp->phys_info.speed - 1.0f) {
6534                 if (dot_from_enemy > 0.75f)
6535                         accelerate_ship(aip, 1.0f);
6536                 else
6537                         set_accel_for_target_speed(Pl_objp, En_objp->phys_info.speed*0.75f + 3.0f);
6538         } else {
6539                 change_acceleration(aip, 0.5f);
6540         }
6541 }
6542
6543 //      Pl_objp (aip) tries to get behind En_objp.
6544 //      New on 2/21/98: If this ship can move backwards and slide, maybe do that to get behind.
6545 void get_behind_ship(ai_info *aip, ship_info *sip, float dist_to_enemy)
6546 {
6547         vector  new_pos;
6548         float           dot;
6549         vector  vec_from_enemy;
6550         float           dist;
6551
6552         dist = vm_vec_normalized_dir(&vec_from_enemy, &Pl_objp->pos, &En_objp->pos);
6553
6554         vm_vec_scale_add(&new_pos, &En_objp->pos, &En_objp->orient.fvec, -100.0f);              //      Pick point 100 units behind.
6555         ai_turn_towards_vector(&new_pos, Pl_objp, flFrametime, sip->srotation_time, NULL, NULL, 0.0f, 0);
6556
6557         dot = vm_vec_dot(&vec_from_enemy, &En_objp->orient.fvec);
6558
6559         if (dot > 0.25f) {
6560                 accelerate_ship(aip, 1.0f);
6561         } else {
6562                 accelerate_ship(aip, (dot + 1.0f)/2.0f);
6563         }
6564 }
6565
6566 int avoid_player(object *objp, vector *goal_pos)
6567 {
6568         maybe_avoid_player(Pl_objp, goal_pos);
6569         ai_info *aip = &Ai_info[Ships[objp->instance].ai_index];
6570
6571         if (aip->ai_flags & AIF_AVOIDING_SMALL_SHIP) {
6572                 ship_info *sip = &Ship_info[Ships[objp->instance].ship_info_index];
6573
6574                 if (aip->ai_flags & AIF_AVOIDING_SMALL_SHIP) {
6575                         ai_turn_towards_vector(&aip->avoid_goal_point, objp, flFrametime, sip->srotation_time, NULL, NULL, 0.0f, 0);
6576                         accelerate_ship(aip, 0.5f);
6577                         return 1;
6578                 }
6579         }
6580
6581         return 0;
6582 }
6583
6584 //      Determine if a cylinder of width radius from p0 to p1 will collide with big_objp.
6585 //      If so, stuff *collision_point.
6586 int will_collide_pp(vector *p0, vector *p1, float radius, object *big_objp, vector *collision_point)
6587 {
6588         mc_info mc;
6589
6590         mc.model_num = Ships[big_objp->instance].modelnum;              // Fill in the model to check
6591         mc.orient = &big_objp->orient;                  // The object's orient
6592         mc.pos = &big_objp->pos;                                        // The object's position
6593         mc.p0 = p0;                                                                             // Point 1 of ray to check
6594         mc.p1 = p1;
6595         mc.flags = MC_CHECK_MODEL | MC_CHECK_SPHERELINE | MC_SUBMODEL;                                  // flags
6596
6597         mc.radius = radius;
6598
6599         // Only check the 2nd lowest hull object
6600         polymodel *pm = model_get(Ships[big_objp->instance].modelnum);
6601         mc.submodel_num = pm->detail[0]; //pm->submodel->num_details-2];
6602         model_collide(&mc);
6603
6604         if (mc.num_hits)
6605                 *collision_point = mc.hit_point_world;
6606
6607         return mc.num_hits;
6608 }
6609
6610 //      Return true/false if *objp will collide with *big_objp
6611 //      Stuff distance in *distance to collision point if *objp will collide with *big_objp within delta_time seconds.
6612 //      Global collision point stuffed in *collision_point
6613 int will_collide_with_big_ship(object *objp, vector *goal_point, object *big_objp, vector *collision_point, float delta_time)
6614 {
6615         float           radius;
6616         vector  end_pos;
6617
6618         radius = big_objp->radius + delta_time * objp->phys_info.speed;
6619
6620         if (vm_vec_dist_quick(&big_objp->pos, &objp->pos) > radius) {
6621                 return 0;
6622         }
6623
6624         if (goal_point == NULL) {
6625                 vm_vec_scale_add(&end_pos, &objp->pos, &objp->phys_info.vel, delta_time);                                       // Point 2 of ray to check
6626         } else {
6627                 end_pos = *goal_point;
6628         }
6629
6630         return will_collide_pp(&objp->pos, &end_pos, objp->radius, big_objp, collision_point);
6631 }
6632
6633 //      Return true if *objp is expected to collide with a large ship.
6634 //      Stuff global collision point in *collision_point.
6635 //      If *goal_point is not NULL, use that as the point towards which *objp will be flying.  Don't use *objp velocity
6636 //      *ignore_objp will typically be the target this ship is pursuing, either to attack or guard.  We don't want to avoid it.
6637 int will_collide_with_big_ship_all(object *objp, object *ignore_objp, vector *goal_point, vector *collision_point, float *distance, float delta_time)
6638 {
6639         ship_obj        *so;
6640         object  *big_objp;
6641         int             collision_obj_index = -1;
6642         float           min_dist = 999999.9f;
6643         float           collision_time = -1.0f;
6644
6645         for ( so = GET_FIRST(&Ship_obj_list); so != END_OF_LIST(&Ship_obj_list); so = GET_NEXT(so) ) {
6646                 float   time = 0.0f;
6647                 big_objp = &Objects[so->objnum];
6648
6649                 if (big_objp == ignore_objp)
6650                         continue;
6651
6652                 if (Ship_info[Ships[big_objp->instance].ship_info_index].flags & (SIF_BIG_SHIP | SIF_HUGE_SHIP)) {
6653                         vector  cur_collision_point;
6654                         float           cur_dist;
6655
6656                         if (will_collide_with_big_ship(objp, goal_point, big_objp, &cur_collision_point, delta_time)) {
6657
6658                                 cur_dist = vm_vec_dist(&cur_collision_point, &objp->pos);
6659
6660                                 if (cur_dist < min_dist) {
6661                                         min_dist = cur_dist;
6662                                         *collision_point = cur_collision_point;
6663                                         collision_time = time;
6664                                         collision_obj_index = OBJ_INDEX(big_objp);
6665                                 }
6666                         }
6667                 }
6668         }
6669
6670         *distance = min_dist;
6671         return collision_obj_index;
6672
6673 }
6674
6675 typedef struct {
6676         float           dist;
6677         int             collide;
6678         vector  pos;
6679 } sgoal;
6680
6681 //int will_collide_pp(vector *p0, vector *p1, float radius, object *big_objp, vector *collision_point)
6682 //      Pick a point for *objp to fly towards to avoid a collision with *big_objp at *collision_point
6683 //      Return result in *avoid_pos
6684 void mabs_pick_goal_point(object *objp, object *big_objp, vector *collision_point, vector *avoid_pos)
6685 {
6686         matrix  mat1;
6687         sgoal           goals[4];
6688         vector  v2b;
6689
6690         vm_vec_normalized_dir(&v2b, collision_point, &objp->pos);
6691         vm_vector_2_matrix(&mat1, &v2b, NULL, NULL);
6692
6693         int     found = 0;
6694
6695         //      Try various scales, in 0.5f, 0.75f, 1.0f, 1.25f.
6696         //      First try 0.5f to see if we can find a point that near the center of the target ship, which presumably
6697         //      means less of a turn.
6698         //      Try going as far as 1.25f * radius.
6699         float   s;
6700         for (s=0.5f; s<1.3f; s += 0.25f) {
6701                 int     i;
6702                 for (i=0; i<4; i++) {
6703                         vector p = big_objp->pos;
6704                         float ku = big_objp->radius*s + objp->radius * (OBJ_INDEX(objp) % 4)/4;         //      This objp->radius stuff to prevent ships from glomming together at one point
6705                         float kr = big_objp->radius*s + objp->radius * ((OBJ_INDEX(objp) % 4) ^ 2)/4;
6706                         if (i&1)
6707                                 ku = -ku;
6708                         if (i&2)
6709                                 kr = -kr;
6710                         vm_vec_scale_add2(&p, &mat1.uvec, ku);
6711                         vm_vec_scale_add2(&p, &mat1.rvec, kr);
6712                         goals[i].pos = p;
6713                         goals[i].dist = vm_vec_dist_quick(&objp->pos, &p);
6714                         goals[i].collide = will_collide_pp(&objp->pos, &p, objp->radius, big_objp, collision_point);
6715                         if (!goals[i].collide)
6716                                 found = 1;
6717                 }
6718
6719                 //      If we found a point that doesn't collide, find the nearest one and make that the *avoid_pos.
6720                 if (found) {
6721                         float   min_dist = 9999999.9f;
6722                         int     min_index = -1;
6723
6724                         for (i=0; i<4; i++) {
6725                                 if (!goals[i].collide && (goals[i].dist < min_dist)) {
6726                                         min_dist = goals[i].dist;
6727                                         min_index = i;
6728                                 }
6729                         }
6730
6731                         Assert(i != -1);
6732                         if (i != -1) {
6733                                 *avoid_pos = goals[min_index].pos;
6734                                 return;
6735                         }
6736                 }
6737         }
6738
6739         //      Drat.  We tried and tried and could not find a point that did not cause a collision.
6740         //      Get this dump pilot far away from the problem ship.
6741         vector  away_vec;
6742         vm_vec_normalized_dir(&away_vec, &objp->pos, collision_point);
6743         vm_vec_scale_add(avoid_pos, &objp->pos, &away_vec, big_objp->radius*1.5f);
6744
6745 }
6746
6747 //      Return true if a large ship is being ignored.
6748 int maybe_avoid_big_ship(object *objp, object *ignore_objp, ai_info *aip, vector *goal_point, float delta_time)
6749 {
6750         if (timestamp_elapsed(aip->avoid_check_timestamp)) {
6751                 float           distance;
6752                 vector  collision_point;
6753                 int             ship_num;
6754                 if ((ship_num = will_collide_with_big_ship_all(Pl_objp, ignore_objp, goal_point, &collision_point, &distance, delta_time)) != -1) {
6755                         aip->ai_flags |= AIF_AVOIDING_BIG_SHIP;
6756                         mabs_pick_goal_point(objp, &Objects[ship_num], &collision_point, &aip->avoid_goal_point);
6757                         float dist = vm_vec_dist_quick(&aip->avoid_goal_point, &objp->pos);
6758                         aip->avoid_check_timestamp = timestamp(2000 + min(1000, (int) (dist * 2.0f)));  //      Delay until check again is based on distance to avoid point.
6759                         aip->avoid_ship_num = ship_num;
6760                 } else {
6761                         aip->ai_flags &= ~AIF_AVOIDING_BIG_SHIP;
6762                         aip->ai_flags &= ~AIF_AVOIDING_SMALL_SHIP;
6763                         aip->avoid_ship_num = -1;
6764                         aip->avoid_check_timestamp = timestamp(1500);
6765                 }
6766         }
6767         
6768         if (aip->ai_flags & AIF_AVOIDING_BIG_SHIP) {
6769                 ship_info *sip = &Ship_info[Ships[Pl_objp->instance].ship_info_index];
6770
6771                 vector  v2g;
6772
6773                 ai_turn_towards_vector(&aip->avoid_goal_point, Pl_objp, flFrametime, sip->srotation_time, NULL, NULL, 0.0f, 0);
6774                 vm_vec_normalized_dir(&v2g, &aip->avoid_goal_point, &Pl_objp->pos);
6775                 float dot = vm_vec_dot(&objp->orient.fvec, &v2g);
6776                 float d2 = (1.0f + dot) * (1.0f + dot);
6777                 accelerate_ship(aip, d2/4.0f);
6778                 return 1;
6779         }
6780
6781         return 0;
6782 }
6783
6784 //      Set desired right vector for ships flying towards another ship.
6785 //      Since this is governed only by vector to target, it causes ships to align bank and look less chaotic.
6786 void compute_desired_rvec(vector *rvec, vector *goal_pos, vector *cur_pos)
6787 {
6788         vector  v2e;
6789
6790         vm_vec_normalized_dir(&v2e, goal_pos, cur_pos);
6791         rvec->x = v2e.z;
6792         rvec->y = 0.0f;
6793         rvec->z = -v2e.x;
6794         if (vm_vec_mag_squared(rvec) < 0.001f)
6795                 rvec->y = 1.0f;
6796 }
6797
6798 // Handler for stealth find submode of Chase.
6799 void ai_stealth_find()
6800 {
6801         ai_info         *aip;
6802         ship_info       *sip;
6803
6804         vector new_pos, vec_to_enemy;
6805         float dist_to_enemy, dot_to_enemy, dot_from_enemy;
6806
6807         Assert(Ships[Pl_objp->instance].ship_info_index >= 0);
6808         sip = &Ship_info[Ships[Pl_objp->instance].ship_info_index];
6809         Assert(Ships[Pl_objp->instance].ai_index >= 0);
6810         aip = &Ai_info[Ships[Pl_objp->instance].ai_index];
6811
6812         // get time since last seen
6813         int delta_time = (timestamp() - aip->stealth_last_visible_stamp);
6814
6815         // if delta_time is really big, i'm real confused, start sweep
6816         if (delta_time > 10000) {
6817                 aip->submode_parm0 = SM_SF_BAIL;
6818         }
6819
6820         // guestimate new position
6821         vm_vec_scale_add(&new_pos, &aip->stealth_last_pos, &aip->stealth_velocity, (delta_time * 0.001f));
6822
6823         // if I think he's behind me, go to the goal point
6824         if ( aip->submode_parm0 == SM_SF_BEHIND ) {
6825                 new_pos = aip->goal_point;
6826         }
6827
6828         // check for collision with big ships
6829         if (maybe_avoid_big_ship(Pl_objp, En_objp, aip, &new_pos, 10.0f)) {
6830                 // reset ai submode to chase
6831                 return;
6832         }
6833
6834         // if dist is near max and dot is close to 1, accel, afterburn
6835         vm_vec_sub(&vec_to_enemy, &new_pos, &Pl_objp->pos);
6836         dist_to_enemy = vm_vec_normalize_quick(&vec_to_enemy);
6837         dot_to_enemy = vm_vec_dotprod(&vec_to_enemy, &Pl_objp->orient.fvec);
6838
6839         // if i think i should see him ahead and i don't, set goal pos and turn around, but only if I haven't seen him for a while
6840         if ( (delta_time > 800) && (aip->submode_parm0 == SM_SF_AHEAD) && (dot_to_enemy > .94) && (dist_to_enemy < get_skill_stealth_dist_scaler()*STEALTH_MAX_VIEW_DIST + 50) ) {
6841                 // do turn around)
6842                 vm_vec_scale_add(&aip->goal_point, &Pl_objp->pos, &Pl_objp->orient.fvec, -300.0f);
6843                 aip->submode_parm0 = SM_SF_BEHIND;
6844                 vm_vec_sub(&vec_to_enemy, &new_pos, &Pl_objp->pos);
6845                 dist_to_enemy = vm_vec_normalize_quick(&vec_to_enemy);
6846                 dot_to_enemy = vm_vec_dotprod(&vec_to_enemy, &Pl_objp->orient.fvec);
6847         }
6848
6849         if ( (dist_to_enemy > get_skill_stealth_dist_scaler()*STEALTH_MAX_VIEW_DIST) && (dot_to_enemy > 0.94f) ) {              // 20 degree half angle
6850                 // accelerate ship
6851                 accelerate_ship(aip, 1.0f);
6852
6853                 // engage afterburner
6854                 if (!( Pl_objp->phys_info.flags & PF_AFTERBURNER_ON )) {
6855                         if (ai_maybe_fire_afterburner(Pl_objp, aip)) {
6856                                 afterburners_start(Pl_objp);
6857                                 aip->afterburner_stop_time = Missiontime + 3*F1_0/2;
6858                         }
6859                 }
6860
6861                 ai_turn_towards_vector(&new_pos, Pl_objp, flFrametime, sip->srotation_time, NULL, NULL, 0.0f, 0);
6862                 return;
6863         }
6864
6865         //      If enemy more than 500 meters away, all ships flying there will tend to match bank.
6866         //      They do this by using their vector to their target to compute their right vector and causing ai_turn_towards_vector
6867         //      to interpolate a matrix rather than just a vector.
6868         if (dist_to_enemy > 500.0f) {
6869                 vector  rvec;
6870                 compute_desired_rvec(&rvec, &new_pos, &Pl_objp->pos);
6871                 ai_turn_towards_vector(&new_pos, Pl_objp, flFrametime, sip->srotation_time, NULL, NULL, 0.0f, 0, &rvec);
6872         } else {
6873                 ai_turn_towards_vector(&new_pos, Pl_objp, flFrametime, sip->srotation_time, NULL, NULL, 0.0f, 0);
6874         }
6875
6876         dot_from_enemy = -vm_vec_dotprod(&vec_to_enemy, &En_objp->orient.fvec);
6877
6878         attack_set_accel(aip, dist_to_enemy, dot_to_enemy, dot_from_enemy);
6879 }
6880
6881 // -----------------------------------------------------------------------------
6882 // try to find stealth ship by sweeping an area
6883 void ai_stealth_sweep()
6884 {
6885         ai_info         *aip;
6886         ship_info       *sip;
6887
6888         Assert(Ships[Pl_objp->instance].ship_info_index >= 0);
6889         sip = &Ship_info[Ships[Pl_objp->instance].ship_info_index];
6890         Assert(Ships[Pl_objp->instance].ai_index >= 0);
6891         aip = &Ai_info[Ships[Pl_objp->instance].ai_index];
6892
6893         vector goal_pt;
6894         vector forward, right, up;
6895         int lost_time;
6896
6897         // time since stealth last seen
6898         lost_time = (timestamp() - aip->stealth_last_visible_stamp);
6899
6900         // determine which pt to fly to in sweep by keeping track of parm0
6901         if (aip->submode_parm0 == SM_SS_SET_GOAL) {
6902
6903                 // don't make goal pt more than 2k from current pos
6904                 vm_vec_scale_add(&goal_pt, &aip->stealth_last_pos, &aip->stealth_velocity, (0.001f * lost_time));
6905
6906                 // make box size based on speed of stealth and expected time to intercept (keep box in range 200-500)
6907                 float box_size = vm_vec_mag_quick(&aip->stealth_velocity) * (0.001f * lost_time);
6908                 box_size = min(200.0f, box_size);
6909                 box_size = max(500.0f, box_size);
6910                 aip->stealth_sweep_box_size = box_size;
6911
6912                 aip->goal_point = goal_pt;
6913                 aip->submode_parm0 = SM_SS_BOX0;
6914         }
6915
6916         // GET UP, RIGHT, FORWARD FOR BOX based on stealth ship's velocity
6917         // if velocity changes in stealth mode, then ship is *seen*, and falls out of sweep mode
6918         // if stealth has no velocity make a velocity
6919         if ( vm_vec_mag_quick(&aip->stealth_velocity) < 1 ) {
6920                 vm_vec_rand_vec_quick(&aip->stealth_velocity);
6921         }
6922
6923         // get "right" vector for box
6924         vm_vec_crossprod(&right, &aip->stealth_velocity, &vmd_y_vector);
6925
6926         if ( vm_vec_mag_quick(&right) < 0.01 ) {
6927                 vm_vec_crossprod(&right, &aip->stealth_velocity, &vmd_z_vector);
6928         }
6929
6930         vm_vec_normalize_quick(&right);
6931
6932         // get forward for box
6933         vm_vec_copy_normalize_quick(&forward, &aip->stealth_velocity);
6934
6935         // get "up" for box
6936         vm_vec_crossprod(&up, &forward, &right);
6937         
6938         // lost far away ahead (do box)
6939         switch(aip->submode_parm0) {
6940         case SM_SS_BOX0:
6941                 goal_pt = aip->goal_point;
6942                 break;
6943
6944         // pt1 -U +R
6945         case SM_SS_LR:
6946                 vm_vec_scale_add(&goal_pt, &aip->goal_point, &up, -aip->stealth_sweep_box_size);
6947                 vm_vec_scale_add2(&goal_pt, &right, aip->stealth_sweep_box_size);
6948                 vm_vec_scale_add2(&goal_pt, &forward, 0.5f*aip->stealth_sweep_box_size);
6949                 break;
6950
6951         // pt2 +U -R
6952         case SM_SS_UL:
6953                 vm_vec_scale_add(&goal_pt, &aip->goal_point, &up, aip->stealth_sweep_box_size);
6954                 vm_vec_scale_add2(&goal_pt, &right, -aip->stealth_sweep_box_size);
6955                 vm_vec_scale_add2(&goal_pt, &forward, 0.5f*aip->stealth_sweep_box_size);
6956                 break;
6957
6958         // pt3 back
6959         case SM_SS_BOX1:
6960                 goal_pt = aip->goal_point;
6961                 break;
6962
6963         // pt4 +U +R
6964         case SM_SS_UR:
6965                 vm_vec_scale_add(&goal_pt, &aip->goal_point, &up, aip->stealth_sweep_box_size);
6966                 vm_vec_scale_add2(&goal_pt, &right, aip->stealth_sweep_box_size);
6967                 vm_vec_scale_add2(&goal_pt, &forward, 0.5f*aip->stealth_sweep_box_size);
6968                 break;
6969
6970         // pt5 -U -R
6971         case SM_SS_LL:
6972                 vm_vec_scale_add(&goal_pt, &aip->goal_point, &up, -aip->stealth_sweep_box_size);
6973                 vm_vec_scale_add2(&goal_pt, &right, -aip->stealth_sweep_box_size);
6974                 vm_vec_scale_add2(&goal_pt, &forward, 0.5f*aip->stealth_sweep_box_size);
6975                 break;
6976
6977         // pt6 back
6978         case SM_SS_BOX2:
6979                 goal_pt = aip->goal_point;
6980                 break;
6981
6982         default:
6983                 Int3();
6984
6985         }
6986
6987         // when close to goal_pt, update next goal pt
6988         float dist_to_goal = vm_vec_dist(&goal_pt, &Pl_objp->pos);
6989         if (dist_to_goal < 15) {
6990                 aip->submode_parm0++;
6991         }
6992
6993         // check for collision with big ship
6994         if (maybe_avoid_big_ship(Pl_objp, En_objp, aip, &goal_pt, 10.0f)) {
6995                 // skip to the next pt on box
6996                 aip->submode_parm0++;
6997                 return;
6998         }
6999
7000         ai_turn_towards_vector(&goal_pt, Pl_objp, flFrametime, sip->srotation_time, NULL, NULL, 0.0f, 0);
7001
7002         float dot = 1.0f;
7003         if (dist_to_goal < 100) {
7004                 vector vec_to_goal;
7005                 vm_vec_normalized_dir(&vec_to_goal, &goal_pt, &Pl_objp->pos);
7006                 dot = vm_vec_dotprod(&vec_to_goal, &Pl_objp->orient.fvec);
7007         }
7008
7009         accelerate_ship(aip, 0.8f*dot);
7010 }
7011
7012 //      ATTACK submode handler for chase mode.
7013 void ai_chase_attack(ai_info *aip, ship_info *sip, vector *predicted_enemy_pos, float dist_to_enemy)
7014 {
7015         int             start_bank;
7016         float           dot_to_enemy, dot_from_enemy; //, time_to_hit;
7017         float           bank_override = 0.0f;
7018
7019         if (avoid_player(Pl_objp, predicted_enemy_pos))
7020                 return;
7021
7022         compute_dots(Pl_objp, En_objp, &dot_to_enemy, &dot_from_enemy);
7023
7024         polymodel *po = model_get( sip->modelnum );
7025
7026         vector  *rel_pos;
7027         float           scale;
7028         vector  randvec;
7029         vector  new_pos;
7030
7031         start_bank = Ships[aip->shipnum].weapons.current_primary_bank;
7032         if (po->n_guns && start_bank != -1 ) {
7033                 rel_pos = &po->gun_banks[start_bank].pnt[0];
7034         } else
7035                 rel_pos = NULL;
7036
7037         //      If ship moving slowly relative to its size, then don't attack its center point.
7038         //      How far from center we attack is based on speed, size and distance to enemy
7039         if (En_objp->radius > En_objp->phys_info.speed) {
7040                 static_randvec(Pl_objp-Objects, &randvec);
7041                 scale = dist_to_enemy/(dist_to_enemy + En_objp->radius) * En_objp->radius;
7042                 scale *= 0.5f * En_objp->radius/(En_objp->phys_info.speed + En_objp->radius);   // scale downward by 1/2 to 1/4
7043                 vm_vec_scale_add(&new_pos, predicted_enemy_pos, &randvec, scale);
7044         } else
7045                 new_pos = *predicted_enemy_pos;
7046
7047         if (dist_to_enemy < 250.0f) {
7048                 if (dot_from_enemy > 0.7f) {
7049                         bank_override = Pl_objp->phys_info.speed;
7050                 }
7051         }
7052
7053         //      If enemy more than 500 meters away, all ships flying there will tend to match bank.
7054         //      They do this by using their vector to their target to compute their right vector and causing ai_turn_towards_vector
7055         //      to interpolate a matrix rather than just a vector.
7056         if (dist_to_enemy > 500.0f) {
7057                 vector  rvec;
7058                 compute_desired_rvec(&rvec, predicted_enemy_pos, &Pl_objp->pos);
7059                 ai_turn_towards_vector(&new_pos, Pl_objp, flFrametime, sip->srotation_time, NULL, rel_pos, bank_override, 0, &rvec);
7060         } else {
7061                 ai_turn_towards_vector(&new_pos, Pl_objp, flFrametime, sip->srotation_time, NULL, rel_pos, bank_override, 0);
7062         }
7063
7064         attack_set_accel(aip, dist_to_enemy, dot_to_enemy, dot_from_enemy);
7065 }
7066
7067 //      EVADE_SQUIGGLE submode handler for chase mode.
7068 //      Changed by MK on 5/5/97.
7069 //      Used to evade towards a point off the right or up vector.
7070 //      Now, evade straight away to try to get far away.
7071 //      The squiggling should protect against laser fire.
7072 void ai_chase_es(ai_info *aip, ship_info *sip)
7073 {
7074         vector  tvec;
7075         fix             timeslice;
7076         fix             scale;
7077         float           bank_override = 0.0f;
7078
7079         tvec = Pl_objp->pos;
7080
7081         timeslice = (Missiontime >> 16) & 0x0f;
7082         scale = ((Missiontime >> 16) & 0x0f) << 14;
7083
7084         if (timeslice & 0x01)
7085                 vm_vec_scale_add2(&tvec, &Pl_objp->orient.rvec, f2fl(scale ^ 0x10000));
7086         if (timeslice & 0x02)
7087                 vm_vec_scale_sub2(&tvec, &Pl_objp->orient.rvec, f2fl(scale));
7088         if (timeslice & 0x04)
7089                 vm_vec_scale_add2(&tvec, &Pl_objp->orient.uvec, f2fl(scale ^ 0x10000));
7090         if (timeslice & 0x08)
7091                 vm_vec_scale_sub2(&tvec, &Pl_objp->orient.uvec, f2fl(scale));
7092
7093         while (vm_vec_dist_quick(&tvec, &Pl_objp->pos) < 0.1f) {
7094                 tvec.x += frand();
7095                 tvec.y += frand();
7096         }
7097
7098         bank_override = Pl_objp->phys_info.speed;
7099
7100         ai_turn_towards_vector(&tvec, Pl_objp, flFrametime/2, sip->srotation_time, NULL, NULL, bank_override, 0);
7101         accelerate_ship(aip, 1.0f);
7102 }
7103
7104 //      Trying to get away from opponent.
7105 void ai_chase_ga(ai_info *aip, ship_info *sip)
7106 {
7107         //      If not near end of this submode, evade squiggly.  If near end, just fly straight for a bit
7108         vector  tvec;
7109         float           bank_override;
7110         vector  vec_from_enemy;
7111
7112         if (En_objp != NULL) {
7113                 vm_vec_normalized_dir(&vec_from_enemy, &Pl_objp->pos, &En_objp->pos);
7114         } else
7115                 vec_from_enemy = Pl_objp->orient.fvec;
7116
7117         static_randvec(Missiontime >> 15, &tvec);
7118         vm_vec_scale(&tvec, 100.0f);
7119         vm_vec_scale_add2(&tvec, &vec_from_enemy, 300.0f);
7120         vm_vec_add2(&tvec, &Pl_objp->pos);
7121
7122         bank_override = Pl_objp->phys_info.speed;
7123
7124         ai_turn_towards_vector(&tvec, Pl_objp, flFrametime/2, sip->srotation_time, NULL, NULL, bank_override, 0);
7125
7126         accelerate_ship(aip, 2.0f);
7127
7128         if (ai_maybe_fire_afterburner(Pl_objp, aip)) {
7129                 if (!(Pl_objp->phys_info.flags & PF_AFTERBURNER_ON )) {
7130                         float percent_left = 100.0f * Ships[Pl_objp->instance].afterburner_fuel / sip->afterburner_fuel_capacity;
7131                         if (percent_left > 30.0f + ((Pl_objp-Objects) & 0x0f)) {
7132                                 afterburners_start(Pl_objp);
7133                                 aip->afterburner_stop_time = Missiontime + 3*F1_0/2;
7134                         }
7135                         afterburners_start(Pl_objp);
7136                         aip->afterburner_stop_time = Missiontime + 3*F1_0/2;
7137                 }
7138         }
7139
7140 }
7141
7142 //      Make object *objp attack subsystem with ID = subnum.
7143 //      Return true if found a subsystem to attack, else return false.
7144 //      Note, can fail if subsystem exists, but has no hits.
7145 int ai_set_attack_subsystem(object *objp, int subnum)
7146 {
7147         ship                    *shipp, *attacker_shipp;
7148         ai_info         *aip;
7149         ship_subsys     *ssp;
7150         object          *attacked_objp;
7151
7152         Assert(objp->type == OBJ_SHIP);
7153         Assert(objp->instance >= 0);
7154
7155         attacker_shipp = &Ships[objp->instance];
7156         Assert(attacker_shipp->ai_index >= 0);
7157
7158         aip = &Ai_info[attacker_shipp->ai_index];
7159
7160         // MWA -- 2/27/98.  Due to AL's changes, target_objnum is now not always valid (at least sometimes
7161         // in terms of goals).  So, bail if we don't have a valid target.
7162         if ( aip->target_objnum == -1 )
7163                 return 0;
7164
7165         attacked_objp = &Objects[aip->target_objnum];
7166         shipp = &Ships[attacked_objp->instance];                //  need to get our target's ship pointer!!!
7167
7168         ssp = ship_get_indexed_subsys(shipp, subnum, &objp->pos);
7169         if (ssp == NULL)
7170                 return 0;
7171
7172         set_targeted_subsys(aip, ssp, aip->target_objnum);
7173         
7174         if (aip->ignore_objnum == aip->target_objnum)
7175                 aip->ignore_objnum = UNUSED_OBJNUM;
7176
7177         // -- Done at caller in ai_process_mission_orders -- attacked_objp->flags |= OF_PROTECTED;
7178
7179         ai_set_goal_maybe_abort_dock(objp, aip);
7180         aip->ok_to_target_timestamp = timestamp(DELAY_TARGET_TIME);
7181
7182         return 1;
7183 }
7184
7185 void ai_set_guard_vec(object *objp, object *guard_objp)
7186 {
7187         ai_info *aip;
7188         float   radius;
7189
7190         aip = &Ai_info[Ships[objp->instance].ai_index];
7191
7192         //      Handle case of bogus call in which ship is told to guard self.
7193         Assert(objp != guard_objp);
7194         if (objp == guard_objp) {
7195                 vm_vec_rand_vec_quick(&aip->guard_vec);
7196                 vm_vec_scale(&aip->guard_vec, 100.0f);
7197                 return;
7198         }
7199
7200         // check if guard_objp is BIG
7201         radius = 5.0f * (objp->radius + guard_objp->radius) + 50.0f;
7202         if (radius > 300.0f) {
7203                 radius = guard_objp->radius * 1.25f;
7204         }
7205
7206         vm_vec_sub(&aip->guard_vec, &objp->pos, &guard_objp->pos);
7207
7208         if (vm_vec_mag(&aip->guard_vec) > 3.0f*radius) {
7209                 //      Far away, don't just use vector to object, causes clustering of guard ships.
7210                 vector  tvec, rvec;
7211                 float   mag;
7212                 mag = vm_vec_copy_normalize(&tvec, &aip->guard_vec);
7213                 vm_vec_rand_vec_quick(&rvec);                   
7214                 vm_vec_scale_add2(&tvec, &rvec, 0.5f);
7215                 vm_vec_copy_scale(&aip->guard_vec, &tvec, mag);
7216         }
7217
7218         vm_vec_normalize_quick(&aip->guard_vec);
7219         vm_vec_scale(&aip->guard_vec, radius);
7220 }
7221
7222 //      Make object *objp guard object *other_objp.
7223 //      To be called from the goals code.
7224 void ai_set_guard_wing(object *objp, int wingnum)
7225 {
7226         ship            *shipp;
7227         ai_info *aip;
7228         int             leader_objnum, leader_shipnum;
7229
7230         Assert(wingnum >= 0);
7231
7232         Assert(objp->type == OBJ_SHIP);
7233         Assert(objp->instance >= 0);
7234
7235         // shouldn't set the ai mode for the player
7236         if ( objp == Player_obj ) {
7237                 return;
7238         }
7239
7240         shipp = &Ships[objp->instance];
7241
7242         Assert(shipp->ai_index >= 0);
7243
7244         aip = &Ai_info[shipp->ai_index];
7245         force_avoid_player_check(objp, aip);
7246
7247         ai_set_goal_maybe_abort_dock(objp, aip);
7248         aip->ok_to_target_timestamp = timestamp(DELAY_TARGET_TIME);
7249
7250         //      This function is called whenever a guarded ship is destroyed, so this code
7251         //      prevents a ship from trying to guard a non-existent wing.
7252         if (Wings[wingnum].current_count < 1) {
7253                 aip->guard_objnum = -1;
7254                 aip->guard_wingnum = -1;
7255                 aip->mode = AIM_NONE;
7256         } else {
7257                 leader_shipnum = Wings[wingnum].ship_index[0];
7258                 leader_objnum = Ships[leader_shipnum].objnum;
7259
7260                 Assert((leader_objnum >= 0) && (leader_objnum < MAX_OBJECTS));
7261                 //Assert(leader_objnum != objp-Objects);        //      Don't allow ships to guard themselves.
7262                 if (leader_objnum == OBJ_INDEX(objp)) {
7263                         //Int3();       //      Seems illegal, but let's clean up.  Get MikeK.
7264                         return;
7265                 }
7266
7267                 aip->guard_wingnum = wingnum;
7268                 aip->guard_objnum = leader_objnum;
7269                 aip->guard_signature = Objects[leader_objnum].signature;
7270                 aip->mode = AIM_GUARD;
7271                 aip->submode = AIS_GUARD_STATIC;
7272
7273                 ai_set_guard_vec(objp, &Objects[leader_objnum]);
7274         }
7275 }
7276
7277 //      Make object *objp guard object *other_objp.
7278 //      To be called from the goals code.
7279 void ai_set_evade_object(object *objp, object *other_objp)
7280 {
7281         ship            *shipp;
7282         ai_info *aip;
7283         int             other_objnum;
7284
7285         Assert(objp->type == OBJ_SHIP);
7286         Assert(objp->instance >= 0);
7287
7288         shipp = &Ships[objp->instance];
7289
7290         Assert(shipp->ai_index >= 0);
7291
7292         aip = &Ai_info[shipp->ai_index];
7293
7294         other_objnum = OBJ_INDEX(other_objp);
7295         Assert(other_objnum >= 0);
7296
7297         Assert(other_objnum != Ships[aip->shipnum].objnum);     //      make sure not targeting self
7298         aip->target_objnum = other_objnum;
7299
7300         aip->mode = AIM_EVADE;
7301 }
7302
7303 //      Make objp guard other_objp
7304 //      If other_objp is a member of a wing, objp will guard that whole wing
7305 //      UNLESS objp is also a member of the wing!
7306 void ai_set_guard_object(object *objp, object *other_objp)
7307 {
7308         ship            *shipp;
7309         ai_info *aip;
7310         int             other_objnum;
7311
7312         Assert(objp->type == OBJ_SHIP);
7313         Assert(objp->instance >= 0);
7314         Assert(objp != other_objp);
7315
7316         shipp = &Ships[objp->instance];
7317
7318         Assert(shipp->ai_index >= 0);
7319
7320         aip = &Ai_info[shipp->ai_index];
7321         aip->avoid_check_timestamp = timestamp(1);
7322
7323         //      If ship to guard is in a wing, guard that whole wing.
7324         ai_info *other_aip = &Ai_info[Ships[other_objp->instance].ai_index];
7325         if ((other_aip->wing != -1) && (other_aip->wing != aip->wing)) {
7326                 ai_set_guard_wing(objp, Ai_info[Ships[other_objp->instance].ai_index].wing);
7327         } else {
7328
7329                 other_objnum = other_objp-Objects;
7330
7331                 aip->guard_objnum = other_objnum;
7332                 aip->guard_signature = other_objp->signature;
7333                 aip->guard_wingnum = -1;
7334
7335                 aip->mode = AIM_GUARD;
7336                 aip->submode = AIS_GUARD_STATIC;
7337
7338                 Assert(other_objnum >= 0);      //      Hmm, bogus object and we need its position for guard_vec.
7339
7340                 // vm_vec_sub(&aip->guard_vec, &objp->pos, &Objects[other_objnum].pos);
7341                 ai_set_guard_vec(objp, &Objects[other_objnum]);
7342
7343                 ai_set_goal_maybe_abort_dock(objp, aip);
7344                 aip->ok_to_target_timestamp = timestamp(DELAY_TARGET_TIME);
7345         }
7346 }
7347
7348 //      Update the aspect_locked_time field based on whether enemy is in view cone.
7349 //      Also set/clear AIF_SEEK_LOCK.
7350 void update_aspect_lock_information(ai_info *aip, vector *vec_to_enemy, float dist_to_enemy, float enemy_radius)
7351 {
7352         float   dot_to_enemy;
7353         int     num_weapon_types;
7354         int     weapon_id_list[MAX_WEAPON_TYPES], weapon_bank_list[MAX_WEAPON_TYPES];
7355         ship    *shipp;
7356         ship_weapon     *swp;
7357         weapon_info     *wip;
7358
7359         shipp = &Ships[aip->shipnum];
7360         swp = &shipp->weapons;
7361
7362         // AL 3-7-98: This probably should never happen, but check to ensure that current_secondary_bank is valid
7363         if ( (swp->current_secondary_bank < 0) || (swp->current_secondary_bank > swp->num_secondary_banks) ) {
7364                 return;
7365         }
7366
7367         num_weapon_types = get_available_secondary_weapons(Pl_objp, weapon_id_list, weapon_bank_list);
7368
7369         wip = &Weapon_info[swp->secondary_bank_weapons[swp->current_secondary_bank]];
7370
7371         if (num_weapon_types && (wip->wi_flags & WIF_HOMING_ASPECT)) {
7372                 if (dist_to_enemy > 300.0f - min(enemy_radius, 100.0f))
7373                         aip->ai_flags |= AIF_SEEK_LOCK;
7374                 else
7375                         aip->ai_flags &= ~AIF_SEEK_LOCK;
7376
7377                 //      Update locking information for aspect seeking missiles.
7378                 aip->current_target_is_locked = 0;
7379                 dot_to_enemy = vm_vec_dot(vec_to_enemy, &Pl_objp->orient.fvec);
7380
7381                 float   needed_dot = 0.9f - 0.5f * enemy_radius/(dist_to_enemy + enemy_radius); //      Replaced MIN_TRACKABLE_DOT with 0.9f
7382                 if (dot_to_enemy > needed_dot) {
7383                         aip->aspect_locked_time += flFrametime;
7384                         // nprintf(("AI", "+ Lock time = %7.3f\n", aip->aspect_locked_time));
7385                         if (aip->aspect_locked_time >= wip->min_lock_time) {
7386                                 aip->aspect_locked_time = wip->min_lock_time;
7387                                 aip->current_target_is_locked = 1;
7388                         }
7389                 } else {
7390                         aip->aspect_locked_time -= flFrametime*2;
7391                         // nprintf(("AI", "- Lock time = %7.3f\n", aip->aspect_locked_time));
7392                         if (aip->aspect_locked_time < 0.0f)
7393                                 aip->aspect_locked_time = 0.0f;
7394                 }
7395                 //nprintf(("AI", "dot = %7.3f, time = %7.3f\n", dot_to_enemy, aip->aspect_locked_time));
7396         
7397         } else {
7398                 aip->current_target_is_locked = 0;
7399                 aip->aspect_locked_time = 0.0f; // Used to be this, why?: wip->min_lock_time;
7400                 aip->ai_flags &= ~AIF_SEEK_LOCK;
7401         }
7402
7403 }
7404
7405 //      We're in chase mode and we've recently collided with our target.
7406 //      Fly away from it!
7407 void ai_chase_fly_away(object *objp, ai_info *aip)
7408 {
7409         int     abort_flag = 0;
7410
7411         if (aip->ai_flags & AIF_TARGET_COLLISION) {
7412                 aip->ai_flags &= ~AIF_TARGET_COLLISION; //      Don't process this hit again next frame.
7413                 aip->submode = SM_FLY_AWAY;                                     //      Focus on avoiding target
7414                 aip->submode_start_time = Missiontime;
7415         }
7416
7417         if ((aip->target_objnum == -1) || (Objects[aip->target_objnum].signature != aip->target_signature)) {
7418                 abort_flag = 1;
7419         }
7420
7421         if (abort_flag || (Missiontime > aip->submode_start_time + F1_0)) {
7422                 aip->last_attack_time = Missiontime;
7423                 aip->submode = SM_ATTACK;
7424                 aip->submode_start_time = Missiontime;
7425         } else {
7426                 vector  v2e;
7427                 float           dot;
7428
7429                 vm_vec_normalized_dir(&v2e, &Objects[aip->target_objnum].pos, &objp->pos);
7430
7431                 dot = vm_vec_dot(&objp->orient.fvec, &v2e);
7432                 if (dot < 0.0f)
7433                         accelerate_ship(aip, 1.0f);
7434                 else
7435                         accelerate_ship(aip, 1.0f - dot);
7436                 turn_away_from_point(objp, &Objects[aip->target_objnum].pos, 0.0f);
7437         }
7438 }
7439
7440 //      Return bank index of favored secondary weapon.
7441 //      Return -1 if nothing favored.
7442 //      "favored" means SEXPs have specified the weapon as being good to fire at en_objp.
7443 int has_preferred_secondary(object *objp, object *en_objp, ship_weapon *swp)
7444 {
7445 // int is_preferred_weapon(int weapon_num, object *firer_objp, object *target_objp)
7446         int     i;
7447
7448         for (i=0; i<swp->num_secondary_banks; i++) {
7449                 if (swp->secondary_bank_capacity[i] > 0) {
7450                         if (swp->secondary_bank_ammo[i] > 0) {
7451                                 if (is_preferred_weapon(swp->secondary_bank_weapons[i], objp, en_objp) != -1){
7452                                         return i;
7453                                 }
7454                         }
7455                 }
7456         }
7457
7458         return -1;
7459 }
7460
7461 //      Choose which secondary weapon to fire.
7462 //      Note, this is not like ai_select_secondary_weapon().  "choose" means make a choice.
7463 //      "select" means execute an order.  Get it?
7464 //      This function calls ai_select_secondary_weapon() with the characteristics it should search for.
7465 void ai_choose_secondary_weapon(object *objp, ai_info *aip, object *en_objp)
7466 {
7467         float                   subsystem_strength = 0.0f;
7468         int                     is_big_ship, priority1, priority2;
7469         ship_weapon     *swp;
7470         ship_info       *esip;
7471
7472         if ( en_objp->type == OBJ_SHIP ) {
7473                 esip = &Ship_info[Ships[en_objp->instance].ship_info_index];
7474         } else {
7475                 esip = NULL;
7476         }
7477
7478         swp = &Ships[objp->instance].weapons;
7479
7480         // AL 3-5-98: do a quick out if the ship has no secondaries
7481         if ( swp->num_secondary_banks <= 0 ) {
7482                 swp->current_secondary_bank = -1;
7483                 return;
7484         }
7485
7486         int preferred_secondary = has_preferred_secondary(objp, en_objp, swp);
7487
7488         if (preferred_secondary != -1) {
7489                 if (swp->current_secondary_bank != preferred_secondary) {
7490                         aip->current_target_is_locked = 0;
7491                         aip->aspect_locked_time = 0.0f;
7492                         swp->current_secondary_bank = preferred_secondary;
7493                 }
7494                 //nprintf(("AI", "Favored secondary = %s\n", Weapon_info[swp->secondary_bank_weapons[swp->current_secondary_bank]].name));
7495                 aip->ai_flags |= AIF_UNLOAD_SECONDARIES;
7496         } else {
7497                 aip->ai_flags &= ~AIF_UNLOAD_SECONDARIES;
7498                 if (aip->targeted_subsys) {
7499                         subsystem_strength = aip->targeted_subsys->current_hits;
7500                 }
7501
7502                 if ( esip ) {
7503                         is_big_ship = esip->flags & (SIF_HUGE_SHIP | SIF_BIG_SHIP);
7504                 } else {
7505                         is_big_ship=0;
7506                 }
7507
7508                 if (is_big_ship) {
7509                         priority1 = WIF_HUGE;
7510                         priority2 = WIF_HOMING;
7511                 } else if ( (esip != NULL) && (esip->flags & SIF_BOMBER) ) {
7512                         priority1 = WIF_BOMBER_PLUS;
7513                         priority2 = WIF_HOMING;
7514                 } else if (subsystem_strength > 100.0f) {
7515                         priority1 = WIF_PUNCTURE;
7516                         priority2 = WIF_HOMING;
7517                 } else {
7518                         priority1 = WIF_HOMING;
7519                         priority2 = 0;
7520                 }
7521                 
7522                 ai_select_secondary_weapon(objp, swp, priority1, priority2);
7523         }
7524
7525         // nprintf(("AI", "Frame %i: Chose secondary %s\n", Framecount, Weapon_info[swp->secondary_bank_weapons[swp->current_secondary_bank]].name));
7526 }
7527
7528 //      Return time, in seconds, at which this ship can next fire its current secondary weapon.
7529 float set_secondary_fire_delay(ai_info *aip, ship *shipp, weapon_info *swip)
7530 {
7531         float t = swip->fire_wait;              //      Base delay for this weapon.
7532         if (shipp->team == Player_ship->team) {
7533                 //      On player's team, _lower_ skill level = faster firing
7534                 t = t * (Game_skill_level+2) / (NUM_SKILL_LEVELS);
7535         } else {                //      Not on player's team, higher skill level = faster firing
7536                 t = t * (NUM_SKILL_LEVELS - Game_skill_level+2) / (NUM_SKILL_LEVELS);
7537         }
7538
7539         t += (Num_ai_classes - aip->ai_class + 1) * 0.5f;
7540         t *= frand_range(0.8f, 1.2f);
7541
7542         //      For the missiles that fire fairly quickly, occasionally add an additional substantial delay.
7543         if (t < 5.0f)
7544                 if (frand() < 0.5f)
7545                         t = t * 2.0f + 2.0f;
7546
7547         return t;
7548 }
7549
7550
7551 void ai_chase_big_approach_set_goal(vector *goal_pos, object *attack_objp, object *target_objp, float *accel)
7552 {
7553         float dist_to_goal;
7554
7555         // head straight toward him and maybe circle later
7556         vm_vec_avg(goal_pos, &attack_objp->pos, &target_objp->pos);
7557
7558         // get distance to goal
7559         dist_to_goal = vm_vec_dist(goal_pos, &attack_objp->pos);
7560         
7561         // set accel
7562         if (dist_to_goal > 400.0f) {
7563                 *accel = 1.0f;
7564         } else {
7565                 *accel = dist_to_goal/400.0f;
7566         }
7567 }
7568
7569 void ai_chase_big_circle_set_goal(vector *goal_pos, object *attack_objp, object *target_objp, float *accel)
7570 {
7571         get_tangent_point(goal_pos, attack_objp, &target_objp->pos, attack_objp->radius + target_objp->radius + 100.0f);
7572
7573         *accel = 1.0f;
7574 }
7575
7576 // get the current and desired horizontal separations between target
7577 void ai_chase_big_get_separations(object *attack_objp, object *target_objp, vector *horz_vec_to_target, float *desired_separation, float *cur_separation)
7578 {
7579         float temp, r_target, r_attacker, h_attacker, h_target;
7580         float perp_dist;
7581         vector vec_to_target;
7582         polymodel *pm;
7583
7584         // get parameters of ships (as cylinders - radius and height)
7585         // get radius of attacker (for rotations about forward)
7586         pm = model_get(Ships[attack_objp->instance].modelnum);
7587         temp = max(pm->maxs.x, pm->maxs.y);
7588         r_attacker = max(-pm->mins.x, -pm->mins.y);
7589         r_attacker = max(temp, r_attacker);
7590         h_attacker = max(-pm->mins.z, pm->maxs.z);
7591
7592         // get radius of target (for rotations about forward)
7593         pm = model_get(Ships[attack_objp->instance].modelnum);
7594         temp = max(pm->maxs.x, pm->maxs.y);
7595         r_target = max(-pm->mins.x, -pm->mins.y);
7596         r_target = max(temp, r_target);
7597         h_target = max(-pm->mins.z, pm->maxs.z);
7598
7599         // find separation between cylinders [if parallel]
7600         vm_vec_sub(&vec_to_target, &attack_objp->pos, &target_objp->pos);
7601
7602         // find the distance between centers along forward direction of ships
7603         perp_dist = vm_vec_dotprod(&vec_to_target, &target_objp->orient.fvec);
7604
7605         // subtract off perp component to get "horizontal" separation vector between cylinders [ASSUMING parallel]
7606         vm_vec_scale_add(horz_vec_to_target, &vec_to_target, &target_objp->orient.fvec, -perp_dist);
7607         *cur_separation = vm_vec_mag_quick(horz_vec_to_target);
7608
7609         // choose "optimal" separation of 1000 + r_target + r_attacker
7610         *desired_separation = 1000 + r_target + r_attacker;
7611 }
7612
7613 void ai_chase_big_parallel_set_goal(vector *goal_pos, object *attack_objp, object *target_objp, float *accel)
7614 {
7615         int opposing;
7616         float temp, r_target, r_attacker, h_attacker, h_target;
7617         float separation, optimal_separation;
7618         vector  horz_vec_to_target;
7619         polymodel *pm;
7620
7621         // get parameters of ships (as cylinders - radius and height)
7622         // get radius of attacker (for rotations about forward)
7623         pm = model_get(Ships[attack_objp->instance].modelnum);
7624         temp = max(pm->maxs.x, pm->maxs.y);
7625         r_attacker = max(-pm->mins.x, -pm->mins.y);
7626         r_attacker = max(temp, r_attacker);
7627         h_attacker = max(-pm->mins.z, pm->maxs.z);
7628
7629         // get radius of target (for rotations about forward)
7630         pm = model_get(Ships[attack_objp->instance].modelnum);
7631         temp = max(pm->maxs.x, pm->maxs.y);
7632         r_target = max(-pm->mins.x, -pm->mins.y);
7633         r_target = max(temp, r_target);
7634         h_target = max(-pm->mins.z, pm->maxs.z);
7635
7636         // are we opposing (only when other ship is not moving)
7637         opposing = ( vm_vec_dotprod(&attack_objp->orient.fvec, &target_objp->orient.fvec) < 0 );
7638
7639         ai_chase_big_get_separations(attack_objp, target_objp, &horz_vec_to_target, &optimal_separation, &separation);
7640
7641         // choose dist (2000) so that we don't bash
7642         float dist = 2000;
7643         if (opposing) {
7644                 dist = - dist;
7645         }
7646
7647         // set the goal pos as dist forward from target along target forward
7648         vm_vec_scale_add(goal_pos, &target_objp->pos, &target_objp->orient.fvec, dist);
7649         // then add horizontal separation
7650         vm_vec_scale_add2(goal_pos, &horz_vec_to_target, optimal_separation/separation);
7651
7652         // find the distance between centers along forward direction of ships
7653         vector vec_to_target;
7654         vm_vec_sub(&vec_to_target, &target_objp->pos, &attack_objp->pos);
7655         float perp_dist = vm_vec_dotprod(&vec_to_target, &target_objp->orient.fvec);
7656
7657         float match_accel = target_objp->phys_info.vel.z / Ship_info[Ships[attack_objp->instance].ship_info_index].max_vel.z;
7658         float length_scale = attack_objp->radius;
7659
7660         // if we're heading toward enemy ship, we want to keep going if we're ahead
7661         if (opposing) {
7662                 perp_dist = -perp_dist;
7663         }
7664
7665         if (perp_dist > 0) {
7666                 // falling behind, so speed up
7667                 *accel = match_accel + (1.0f - match_accel) / length_scale * (perp_dist);
7668         } else {
7669                 // up in front, so slow down
7670                 *accel = match_accel  - match_accel / length_scale * -perp_dist;
7671                 *accel = max(0.0f, *accel);
7672         }
7673
7674 }
7675
7676
7677 //      Return *goal_pos for one cruiser to attack another (big ship).
7678 //      Choose point fairly nearby that is not occupied by another cruiser.
7679 void ai_cruiser_chase_set_goal_pos(vector *goal_pos, object *pl_objp, object *en_objp)
7680 {
7681         ai_info *aip;
7682
7683         aip = &Ai_info[Ships[pl_objp->instance].ai_index];
7684         float accel;
7685
7686         switch (aip->submode) {
7687         case SM_BIG_APPROACH:
7688                 // do approach stuff;
7689                 ai_chase_big_approach_set_goal(goal_pos, pl_objp, en_objp, &accel);
7690                 break;
7691
7692         case SM_BIG_CIRCLE:
7693                 // do circle stuff
7694                 ai_chase_big_circle_set_goal(goal_pos, pl_objp, en_objp, &accel);
7695                 break;
7696
7697         case SM_BIG_PARALLEL:
7698                 // do parallel stuff
7699                 ai_chase_big_parallel_set_goal(goal_pos, pl_objp, en_objp, &accel);
7700                 break;
7701         }
7702 }
7703
7704 int maybe_hack_cruiser_chase_abort()
7705 {
7706         ship                    *shipp = &Ships[Pl_objp->instance];     
7707         ship                    *eshipp = &Ships[En_objp->instance];
7708         ai_info         *aip = &Ai_info[shipp->ai_index];
7709
7710         // mission sm3-08, sathanos chasing collosus
7711         if ( stricmp(Mission_filename, "sm3-08.fs2") == 0 ) {
7712                 if (( stricmp(eshipp->ship_name, "colossus") == 0 ) || ( stricmp(shipp->ship_name, "colossus") == 0 )) {
7713                         // Changed so all big ships attacking the Colossus will not do the chase code.
7714                         // Did this so Beast wouldn't swerve away from Colossus. -- MK, 9/14/99
7715                         //if ( stricmp(shipp->ship_name, "Sathanas") == 0 ) {
7716                                 // do cool hack stuff here
7717                                 ai_clear_ship_goals( aip );
7718                                 aip->mode = AIM_NONE;
7719                                 return 1;
7720                         //}
7721                 }
7722         }
7723
7724         return 0;
7725 }
7726
7727 //      Make a big ship pursue another big ship.
7728 //      (Note, called "ai_cruiser_chase" because we already have ai_chase_big() which means fighter chases big ship.
7729 void ai_cruiser_chase()
7730 {
7731         ship_info       *sip = &Ship_info[Ships[Pl_objp->instance].ship_info_index];
7732         ship                    *shipp = &Ships[Pl_objp->instance];     
7733         ai_info         *aip = &Ai_info[shipp->ai_index];
7734
7735         if (!(sip->flags & (SIF_BIG_SHIP | SIF_HUGE_SHIP))) {
7736                 Int3(); //      Hmm, not a very big ship, how did we get in this function?
7737                 aip->mode = AIM_NONE;
7738                 return;
7739         }
7740
7741         if (En_objp->type != OBJ_SHIP) {
7742                 Int3();
7743                 return;
7744         }
7745
7746         if (En_objp->instance < 0) {
7747                 Int3();
7748                 return;
7749         }
7750
7751         ship                    *eshipp;
7752         ship_info       *esip;
7753
7754         eshipp = &Ships[En_objp->instance];
7755         esip = &Ship_info[eshipp->ship_info_index];
7756
7757         if (!(esip->flags & (SIF_BIG_SHIP | SIF_HUGE_SHIP))) {
7758                 // Int3();      //      Hmm, we're big and we're pursuing something other than a big ship?
7759                 aip->mode = AIM_NONE;
7760                 return;
7761         }
7762
7763         vector  goal_pos;
7764         float turn_time = Ship_info[Ships[Pl_objp->instance].ship_info_index].srotation_time;
7765
7766         // kamikaze - ram and explode
7767         if (aip->ai_flags & AIF_KAMIKAZE) {
7768                 ai_turn_towards_vector(&En_objp->pos, Pl_objp, flFrametime, turn_time, NULL, NULL, 0.0f, 0);
7769                 accelerate_ship(aip, 1.0f);
7770         } 
7771         
7772         // really track down and chase
7773         else {
7774                 // check valid submode
7775                 Assert( (aip->submode == SM_ATTACK) || (aip->submode == SM_BIG_APPROACH) || (aip->submode == SM_BIG_CIRCLE) || (aip->submode == SM_BIG_PARALLEL) );
7776
7777                 // just entering, approach enemy ship
7778                 if (aip->submode == SM_ATTACK) {
7779                         aip->submode = SM_BIG_APPROACH;
7780                 }
7781
7782                 // desired accel
7783                 float accel = 0.0f;
7784                 vector *rvecp = NULL;
7785
7786                 switch (aip->submode) {
7787                 case SM_BIG_APPROACH:
7788                         // do approach stuff;
7789                         ai_chase_big_approach_set_goal(&goal_pos, Pl_objp, En_objp, &accel);
7790                         // maybe set rvec
7791                         break;
7792
7793                 case SM_BIG_CIRCLE:
7794                         // do circle stuff
7795                         ai_chase_big_circle_set_goal(&goal_pos, Pl_objp, En_objp, &accel);
7796                         // maybe set rvec
7797                         break;
7798
7799                 case SM_BIG_PARALLEL:
7800                         // do parallel stuff
7801                         ai_chase_big_parallel_set_goal(&goal_pos, Pl_objp, En_objp, &accel);
7802                         //maybe set rvec
7803                         break;
7804                 }
7805
7806
7807                 // now move as desired
7808                 ai_turn_towards_vector(&goal_pos, Pl_objp, flFrametime, turn_time, NULL, NULL, 0.0f, 0, rvecp);
7809                 accelerate_ship(aip, accel);
7810
7811
7812                 // maybe switch to new mode
7813                 vector vec_to_enemy;
7814                 float dist_to_enemy;
7815                 int moving = (En_objp->phys_info.vel.z > 0.5f);
7816                 vm_vec_sub(&vec_to_enemy, &En_objp->pos, &Pl_objp->pos);
7817                 dist_to_enemy = vm_vec_mag_quick(&vec_to_enemy);
7818
7819                 switch (aip->submode) {
7820                 case SM_BIG_APPROACH:
7821                         if ( dist_to_enemy < (Pl_objp->radius + En_objp->radius)*1.25f + 200.0f ) {
7822                                 // moving
7823                                 if (moving) {
7824                                         // if within 90 degrees of en forward, go into parallel, otherwise circle
7825                                         if ( vm_vec_dotprod(&En_objp->orient.fvec, &Pl_objp->orient.fvec) > 0 ) {
7826                                                 aip->submode = SM_BIG_PARALLEL;
7827                                         }
7828                                 }
7829
7830                                 // otherwise cirle
7831                                 if ( !maybe_hack_cruiser_chase_abort() ) {
7832                                         aip->submode = SM_BIG_CIRCLE;
7833                                 }
7834                         }
7835                         break;
7836
7837                 case SM_BIG_CIRCLE:
7838                         // moving
7839                         if (moving) {
7840                                 vector temp;
7841                                 float desired_sep, cur_sep;
7842                                 // we're behind the enemy ship
7843                                 if (vm_vec_dotprod(&vec_to_enemy, &En_objp->orient.fvec) > 0) {
7844                                         // and we're turning toward the enemy
7845                                         if (vm_vec_dotprod(&En_objp->orient.fvec, &Pl_objp->orient.fvec) > 0) {
7846                                                 // get separation
7847                                                 ai_chase_big_get_separations(Pl_objp, En_objp, &temp, &desired_sep, &cur_sep);
7848                                                 // and the separation is > 0.9 desired
7849                                                 if (cur_sep > 0.9 * desired_sep) {
7850                                                         aip->submode = SM_BIG_PARALLEL;
7851                                                 }
7852                                         }
7853                                 }
7854                         } else {
7855                                 // still
7856                                 vector temp;
7857                                 float desired_sep, cur_sep;
7858                                 // we're behind the enemy ship
7859                                 if (vm_vec_dotprod(&vec_to_enemy, &En_objp->orient.fvec) > 0) {
7860                                         // and we're turning toward the enemy
7861                                         if (vm_vec_dotprod(&En_objp->orient.fvec, &Pl_objp->orient.fvec) > 0) {
7862                                                 // get separation
7863                                                 ai_chase_big_get_separations(Pl_objp, En_objp, &temp, &desired_sep, &cur_sep);
7864                                                 //and the separation is [0.9 to 1.1] desired
7865                                                 if ( (cur_sep > 0.9f * desired_sep) ) {
7866                                                         aip->submode = SM_BIG_PARALLEL;
7867                                                 }
7868                                         }
7869                                 }
7870                                 // in front of ship
7871                                 else {
7872                                         // and we're turning toward the enemy
7873                                         if (vm_vec_dotprod(&En_objp->orient.fvec, &Pl_objp->orient.fvec) < 0) {
7874                                                 // get separation
7875                                                 ai_chase_big_get_separations(Pl_objp, En_objp, &temp, &desired_sep, &cur_sep);
7876                                                 //and the separation is [0.9 to 1.1] desired
7877                                                 if ( (cur_sep > 0.9f * desired_sep) ) {
7878                                                         aip->submode = SM_BIG_PARALLEL;
7879                                                 }
7880                                         }
7881                                 }
7882                         }
7883                         break;
7884
7885                 case SM_BIG_PARALLEL:
7886                         // we're opposing
7887                         if ( vm_vec_dotprod(&Pl_objp->orient.fvec, &En_objp->orient.fvec) < 0 ) {
7888                                 // and the other ship is moving
7889                                 if (moving) {
7890                                         // and we no longer overlap
7891                                         if ( dist_to_enemy > (0.75 * (En_objp->radius + Pl_objp->radius)) ) {
7892                                                 aip->submode = SM_BIG_APPROACH;
7893                                         }
7894                                 }
7895                         }
7896                         break;
7897                 }
7898         }
7899 }
7900
7901 // --------------------------------------------------------------------------
7902 // Make object Pl_objp chase object En_objp
7903 void ai_chase()
7904 {
7905         float                   dist_to_enemy, time_to_enemy;
7906         float                   dot_to_enemy, dot_from_enemy, real_dot_to_enemy;
7907         vector          player_pos, enemy_pos, predicted_enemy_pos, real_vec_to_enemy, predicted_vec_to_enemy;
7908         ship_info       *sip = &Ship_info[Ships[Pl_objp->instance].ship_info_index];
7909         ship                    *shipp = &Ships[Pl_objp->instance];
7910         ship_weapon     *swp = &shipp->weapons;
7911         ai_info         *aip = &Ai_info[shipp->ai_index];
7912         int                     enemy_sip_flags;
7913
7914         if (aip->mode != AIM_CHASE) {
7915                 Int3();
7916         }
7917
7918         if (sip->flags & (SIF_BIG_SHIP | SIF_HUGE_SHIP)) {
7919                 ai_cruiser_chase();
7920                 return;
7921         }
7922
7923         if (!(sip->flags & (SIF_FIGHTER | SIF_BOMBER | SIF_ESCAPEPOD))) {
7924                 Warning(LOCATION, "Ship %s is not 'small', but is in chase mode.\nSwitching to AI=none.\n", shipp->ship_name);
7925                 aip->mode = AIM_NONE;
7926                 return;
7927         }
7928
7929         //nprintf(("AI", "%7s ", Submode_text[aip->submode]));
7930
7931         if ( En_objp->type == OBJ_SHIP ) {
7932                 enemy_sip_flags = Ship_info[Ships[En_objp->instance].ship_info_index].flags;
7933         } else {
7934                 enemy_sip_flags = 0;
7935         }
7936
7937         if ( enemy_sip_flags > 0 ) {
7938                 if (enemy_sip_flags & (SIF_BIG_SHIP | SIF_HUGE_SHIP)) {
7939                         ai_big_chase();
7940                         return;
7941                 }
7942         }
7943
7944         //      If collided with target_objnum last frame, avoid that ship.
7945         //      This should prevent the embarrassing behavior of ships getting stuck on each other
7946         //      as if they were magnetically attracted. -- MK, 11/13/97.
7947         if ((aip->ai_flags & AIF_TARGET_COLLISION) || (aip->submode == SM_FLY_AWAY)) {
7948                 ai_chase_fly_away(Pl_objp, aip);
7949                 return;
7950         }
7951
7952         ai_set_positions(Pl_objp, En_objp, aip, &player_pos, &enemy_pos);
7953         dist_to_enemy = vm_vec_dist_quick(&player_pos, &enemy_pos);
7954         time_to_enemy = compute_time_to_enemy(dist_to_enemy, Pl_objp, En_objp);
7955         vm_vec_sub(&real_vec_to_enemy, &enemy_pos, &player_pos);
7956
7957         vm_vec_normalize(&real_vec_to_enemy);
7958
7959         real_dot_to_enemy = vm_vec_dot(&real_vec_to_enemy, &Pl_objp->orient.fvec);
7960
7961         int is_stealthy_ship = 0;
7962         if ( (enemy_sip_flags > 0) && (enemy_sip_flags & SIF_STEALTH) ) {
7963                 if ( ai_is_stealth_visible(Pl_objp, En_objp) != STEALTH_FULLY_TARGETABLE ) {
7964                         is_stealthy_ship = 1;
7965                 }
7966         }
7967
7968         // Can only acquire lock on a target that isn't hidden from sensors
7969         if ( !(Ships[En_objp->instance].flags & SF_HIDDEN_FROM_SENSORS) && !is_stealthy_ship ) {
7970                 update_aspect_lock_information(aip, &real_vec_to_enemy, dist_to_enemy, En_objp->radius);
7971         } else {
7972                 aip->current_target_is_locked = 0;
7973                 aip->ai_flags &= ~AIF_SEEK_LOCK;
7974         }
7975
7976         //      If seeking lock, try to point directly at ship, else predict position so lasers can hit it.
7977         //      If just acquired target, or target is not in reasonable cone, don't refine believed enemy position.
7978         if ((real_dot_to_enemy < 0.25f) || (aip->target_time < 1.0f) || (aip->ai_flags & AIF_SEEK_LOCK)) {
7979                 predicted_enemy_pos = enemy_pos;
7980         } else {
7981                 //      Set predicted_enemy_pos.
7982                 //      See if attacking a subsystem.
7983                 if (aip->targeted_subsys != NULL) {
7984                         Assert(En_objp->type == OBJ_SHIP);
7985                         ship_info       *esip = &Ship_info[Ships[En_objp->instance].ship_info_index];
7986                         if (get_shield_strength(En_objp)/esip->shields < HULL_DAMAGE_THRESHOLD_PERCENT) {
7987                                 //int   rval;
7988
7989                                 if (aip->targeted_subsys != NULL) {
7990                                         get_subsystem_pos(&enemy_pos, En_objp, aip->targeted_subsys);
7991                                         predicted_enemy_pos = enemy_pos;
7992                                         predicted_vec_to_enemy = real_vec_to_enemy;
7993                                 } else {
7994                                         set_predicted_enemy_pos(&predicted_enemy_pos, Pl_objp, En_objp, aip);
7995                                         set_target_objnum(aip, -1);
7996                                 }
7997                                 // nprintf(("AI", "Attacking subsystem: rval = %i, pos = %7.3f %7.3f %7.3f\n", rval, predicted_enemy_pos.x, predicted_enemy_pos.y, predicted_enemy_pos.z));
7998
7999                         } else {
8000                                 set_predicted_enemy_pos(&predicted_enemy_pos, Pl_objp, En_objp, aip);
8001                                 // nprintf(("AI", "Attacking subsystem: pos = %7.3f %7.3f %7.3f\n", predicted_enemy_pos.x, predicted_enemy_pos.y, predicted_enemy_pos.z));
8002                         }
8003                 } else {
8004                         set_predicted_enemy_pos(&predicted_enemy_pos, Pl_objp, En_objp, aip);
8005                 }
8006         }
8007
8008         vm_vec_sub(&predicted_vec_to_enemy, &predicted_enemy_pos, &player_pos);
8009
8010         vm_vec_normalize(&predicted_vec_to_enemy);
8011
8012         dot_to_enemy = vm_vec_dot(&Pl_objp->orient.fvec, &predicted_vec_to_enemy);
8013         dot_from_enemy= - vm_vec_dot(&En_objp->orient.fvec, &real_vec_to_enemy);
8014
8015         //
8016         //      Set turn and acceleration based on submode.
8017         //
8018         switch (aip->submode) {
8019         case SM_CONTINUOUS_TURN:
8020                 ai_chase_ct();
8021                 break;
8022
8023         case SM_STEALTH_FIND:
8024                 ai_stealth_find();
8025                 break;
8026
8027         case SM_STEALTH_SWEEP:
8028                 ai_stealth_sweep();
8029                 break;
8030
8031         case SM_ATTACK:
8032         case SM_SUPER_ATTACK:
8033         case SM_ATTACK_FOREVER:
8034                 if (vm_vec_dist_quick(&Pl_objp->pos, &predicted_enemy_pos) > 100.0f + En_objp->radius * 2.0f) {
8035                         if (maybe_avoid_big_ship(Pl_objp, En_objp, aip, &predicted_enemy_pos, 10.0f))
8036                                 return;
8037                 }
8038
8039                 ai_chase_attack(aip, sip, &predicted_enemy_pos, dist_to_enemy);
8040                 break;
8041
8042         case SM_EVADE_SQUIGGLE:
8043                 ai_chase_es(aip, sip);
8044                 break;
8045
8046         case SM_EVADE_BRAKE:
8047                 ai_chase_eb(aip, sip, &predicted_enemy_pos, dist_to_enemy);
8048                 break;
8049
8050         case SM_EVADE:
8051                 evade_ship();
8052                 break;
8053
8054         case SM_AVOID:
8055                 avoid_ship();
8056                 break;
8057
8058         case SM_GET_BEHIND:
8059                 get_behind_ship(aip, sip, dist_to_enemy);
8060                 break;
8061
8062         case SM_GET_AWAY:               //      Used to get away from opponent to prevent endless circling.
8063                 ai_chase_ga(aip, sip);
8064                 break;
8065
8066         case SM_EVADE_WEAPON:
8067                 evade_weapon();
8068                 break;
8069
8070         default:
8071                 // Int3();
8072                 aip->last_attack_time = Missiontime;
8073                 aip->submode = SM_ATTACK;
8074                 aip->submode_start_time = Missiontime;
8075         }
8076
8077         //
8078         //      Maybe choose a new submode.
8079         //
8080         if ( (aip->submode != SM_AVOID) && (aip->submode != SM_ATTACK_FOREVER) ) {
8081                 //      If a very long time since attacked, attack no matter what!
8082                 if ( (aip->submode != SM_SUPER_ATTACK) && (aip->submode != SM_GET_AWAY) && !(aip->ai_flags & AIF_STEALTH_PURSIUT) ) {
8083                         if (Missiontime - aip->last_attack_time > i2f(6)) {
8084                                 aip->submode = SM_SUPER_ATTACK;
8085                                 aip->submode_start_time = Missiontime;
8086                                 aip->last_attack_time = Missiontime;
8087                         }
8088                 }
8089
8090                 //      If a collision is expected, pull out!
8091                 //      If enemy is pointing away and moving a bit, don't worry about collision detection.
8092                 if ((dot_from_enemy > 0.5f) || (En_objp->phys_info.speed < 10.0f)) {
8093                         if (might_collide_with_ship(Pl_objp, En_objp, dot_to_enemy, dist_to_enemy, 4.0f)) {
8094                                 if ((Missiontime - aip->last_hit_time > F1_0*4) && (dist_to_enemy < Pl_objp->radius*2 + En_objp->radius*2)) {
8095                                         accelerate_ship(aip, -1.0f);
8096                                 } else {
8097                                         aip->submode = SM_AVOID;
8098                                         aip->submode_start_time = Missiontime;
8099                                 }
8100                         }
8101                 }
8102         }
8103
8104         switch (aip->submode) {
8105         case SM_CONTINUOUS_TURN:
8106                 if (Missiontime - aip->submode_start_time > i2f(3)) {
8107                         aip->last_attack_time = Missiontime;
8108                         aip->submode = SM_ATTACK;
8109                         aip->submode_start_time = Missiontime;
8110                 }
8111                 break;
8112
8113         case SM_ATTACK:
8114                 // if taraget is stealth and stealth not visible, then enter stealth find mode
8115                 if ( (aip->ai_flags & AIF_STEALTH_PURSIUT) && (ai_is_stealth_visible(Pl_objp, En_objp) == STEALTH_INVISIBLE) ) {
8116                         aip->submode = SM_STEALTH_FIND;
8117                         aip->submode_start_time = Missiontime;
8118                         aip->submode_parm0 = SM_SF_AHEAD;
8119                 } else if (ai_near_full_strength(Pl_objp, sip) && (Missiontime - aip->last_hit_target_time > i2f(3)) && (dist_to_enemy < 500.0f) && (dot_to_enemy < 0.5f)) {
8120                         aip->submode = SM_SUPER_ATTACK;
8121                         aip->submode_start_time = Missiontime;
8122                         aip->last_attack_time = Missiontime;
8123                 } else if ((Missiontime - aip->last_hit_target_time > i2f(6)) &&
8124                         (dist_to_enemy < 500.0f) && (dot_to_enemy < 0.2f) &&
8125                         (frand() < (float) Game_skill_level/NUM_SKILL_LEVELS)) {
8126                         aip->submode = SM_GET_AWAY;
8127                         aip->submode_start_time = Missiontime;
8128                         aip->last_hit_target_time = Missiontime;
8129                 } else if ((enemy_sip_flags & SIF_SMALL_SHIP)
8130                         && (dot_to_enemy < dot_from_enemy)
8131                         && (En_objp->phys_info.speed > 15.0f) 
8132                         && (dist_to_enemy < 200.0f) 
8133                         && (dist_to_enemy > 50.0f)
8134                         && (dot_to_enemy < 0.1f)
8135                         && (Missiontime - aip->submode_start_time > i2f(2))) {
8136                         aip->submode = SM_EVADE_BRAKE;
8137                         aip->submode_start_time = Missiontime;
8138                 } else if ((dot_to_enemy > 0.2f) && (dot_from_enemy > -0.2f) && (dot_from_enemy < 0.1f)) {
8139                         aip->submode = SM_GET_BEHIND;
8140                         aip->submode_start_time = Missiontime;
8141                 } else if ((enemy_sip_flags & SIF_SMALL_SHIP) && (dist_to_enemy < 150.0f) && (dot_from_enemy > dot_to_enemy + 0.5f + aip->ai_courage*.002)) {
8142                         if ((Missiontime - aip->last_hit_target_time > i2f(5)) && (frand() < (float) (aip->ai_class + Game_skill_level)/(Num_ai_classes + NUM_SKILL_LEVELS))) {                         aip->submode = SM_GET_AWAY;
8143                                 aip->submode_start_time = Missiontime;
8144                                 aip->last_hit_target_time = Missiontime;
8145                         } else {
8146                                 aip->submode = SM_EVADE_SQUIGGLE;
8147                                 aip->submode_start_time = Missiontime;
8148                         }
8149                 } else if ((enemy_sip_flags & SIF_SMALL_SHIP) && (Missiontime - aip->submode_start_time > F1_0*2)) {
8150                         if ((dot_to_enemy < 0.8f) && (dot_from_enemy > dot_to_enemy)) {
8151                                 if (frand() > 0.5f) {
8152                                         aip->submode = SM_CONTINUOUS_TURN;
8153                                         aip->submode_parm0 = myrand() & 0x0f;
8154                                         aip->submode_start_time = Missiontime;
8155                                 } else {
8156                                         aip->submode = SM_EVADE;
8157                                         aip->submode_start_time = Missiontime;
8158                                 }
8159                         } else {
8160                                 aip->submode_start_time = Missiontime;
8161                         }
8162                 }
8163
8164                 aip->last_attack_time = Missiontime;
8165
8166                 break;
8167                 
8168         case SM_EVADE_SQUIGGLE:
8169                 if ((Missiontime - aip->submode_start_time > i2f(5)) || (dist_to_enemy > 300.0f)) {
8170                         if ((dist_to_enemy < 100.0f) && (dot_to_enemy < 0.0f) && (dot_from_enemy > 0.5f)) {
8171                                 aip->submode = SM_EVADE_BRAKE;
8172                                 aip->submode_start_time = Missiontime;
8173                         } else {
8174                                 aip->last_attack_time = Missiontime;
8175                                 aip->submode = SM_ATTACK;
8176                                 aip->submode_start_time = Missiontime;
8177                         }
8178                 }
8179                 break;
8180         
8181         case SM_EVADE_BRAKE:
8182                 if ((dist_to_enemy < 15.0f) || (En_objp->phys_info.speed < 10.0f)) {
8183                         aip->submode = SM_AVOID;
8184                         aip->submode_start_time = Missiontime;
8185                 } else if ((dot_to_enemy > 0.9f) || ((dot_from_enemy > 0.9f) && (Missiontime - aip->submode_start_time > i2f(1)))) {
8186                         aip->last_attack_time = Missiontime;
8187                         aip->submode = SM_ATTACK;
8188                         aip->submode_start_time = Missiontime;
8189                 } else if (Missiontime - aip->submode_start_time > i2f(4)) {
8190                         aip->last_attack_time = Missiontime;
8191                         aip->submode = SM_ATTACK;
8192                         aip->submode_start_time = Missiontime;
8193                 }
8194                 break;
8195
8196         case SM_EVADE:
8197                 //      Modified by MK on 5/5/97 to keep trying to regain attack mode.  It's what a human would do.
8198                 if ((dot_to_enemy < 0.2f) && (dot_from_enemy < 0.8f) && (dist_to_enemy < 100.0f) && (En_objp->phys_info.speed > 15.0f)) {
8199                         aip->last_attack_time = Missiontime;
8200                         aip->submode = SM_EVADE_BRAKE;
8201                         aip->submode_start_time = Missiontime;
8202                 } else if (((dot_to_enemy > dot_from_enemy - 0.1f)
8203                         && (Missiontime > aip->submode_start_time + i2f(1)))
8204                         || (dist_to_enemy > 150.0f + 2*(Pl_objp->radius + En_objp->radius))) {
8205                         aip->last_attack_time = Missiontime;
8206                         aip->submode = SM_ATTACK;
8207                         aip->submode_start_time = Missiontime;
8208                 } else if (Missiontime - aip->submode_start_time > i2f(2))
8209                         if (dot_from_enemy > 0.8f) {
8210                                 aip->submode = SM_EVADE_SQUIGGLE;
8211                                 aip->submode_start_time = Missiontime;
8212                         }
8213
8214                 break;
8215
8216         case SM_SUPER_ATTACK:
8217                 // if stealth and invisible, enter stealth find mode
8218                 if ( (aip->ai_flags & AIF_STEALTH_PURSIUT) && (ai_is_stealth_visible(Pl_objp, En_objp) == STEALTH_INVISIBLE) ) {
8219                         aip->submode = SM_STEALTH_FIND;
8220                         aip->submode_start_time = Missiontime;
8221                         aip->submode_parm0 = SM_SF_AHEAD;
8222                 } else if ((dist_to_enemy < 100.0f) && (dot_to_enemy < 0.8f) && (enemy_sip_flags & SIF_SMALL_SHIP) && (Missiontime - aip->submode_start_time > i2f(5) )) {
8223                         aip->ai_flags &= ~AIF_ATTACK_SLOWLY;    //      Just in case, clear here.
8224
8225                         switch (myrand() % 5) {
8226                         case 0:
8227                                 aip->submode = SM_CONTINUOUS_TURN;
8228                                 aip->submode_start_time = Missiontime;
8229                                 break;
8230                         case 1:
8231                                 aip->submode_start_time = Missiontime;  //      Stay in super attack mode
8232                                 break;
8233                         case 2:
8234                         case 3:
8235                                 if (frand() < (float) 0.5f * (aip->ai_class + Game_skill_level)/(Num_ai_classes + NUM_SKILL_LEVELS)) {
8236                                         aip->submode = SM_GET_AWAY;
8237                                         aip->submode_start_time = Missiontime;
8238                                 } else {
8239                                         aip->submode = SM_EVADE;
8240                                         aip->submode_start_time = Missiontime;
8241                                 }
8242                                 break;
8243                         case 4:
8244                                 if (dot_from_enemy + (NUM_SKILL_LEVELS - Game_skill_level) * 0.1f > dot_to_enemy) {     //      Less likely to GET_AWAY at lower skill levels.
8245                                         aip->submode = SM_EVADE;
8246                                         aip->submode_start_time = Missiontime;
8247                                 } else {
8248                                         aip->submode = SM_GET_AWAY;
8249                                         aip->submode_start_time = Missiontime;
8250                                 }
8251                                 break;
8252                         default:
8253                                 Int3(); //      Impossible!
8254                         }
8255                 }
8256
8257                 aip->last_attack_time = Missiontime;
8258
8259                 break;
8260
8261         case SM_AVOID:
8262                 if ((dot_to_enemy > -0.2f) && (dist_to_enemy / (dot_to_enemy + 0.3f) < 100.0f)) {
8263                         aip->submode_start_time = Missiontime;
8264                 } else if (Missiontime - aip->submode_start_time > i2f(1)/2)
8265                         if (might_collide_with_ship(Pl_objp, En_objp, dot_to_enemy, dist_to_enemy, 3.0f)) {
8266                                 aip->submode_start_time = Missiontime;
8267                         } else {
8268                                 aip->submode = SM_GET_BEHIND;
8269                                 aip->submode_start_time = Missiontime;
8270                         }
8271
8272                 break;
8273
8274         case SM_GET_BEHIND:
8275                 if ((dot_from_enemy < -0.7f) || (Missiontime - aip->submode_start_time > i2f(2))) {
8276                         aip->submode = SM_ATTACK;
8277                         aip->submode_start_time = Missiontime;
8278                         aip->last_attack_time = Missiontime;
8279                 }
8280                 break;
8281
8282         case SM_GET_AWAY:
8283                 if (Missiontime - aip->submode_start_time > i2f(2)) {
8284                         float   rand_dist;
8285
8286                         rand_dist = ((Missiontime >> 17) & 0x03) * 100.0f + 200.0f;     //      Some value in 200..500
8287                         if ((Missiontime - aip->submode_start_time > i2f(5)) || (dist_to_enemy > rand_dist) || (dot_from_enemy < 0.4f)) {
8288                                 aip->ai_flags |= AIF_ATTACK_SLOWLY;
8289                                 aip->submode = SM_ATTACK;
8290                                 aip->time_enemy_in_range = 2.0f;                //      Cheat.  Presumably if they were running away from you, they were monitoring you!
8291                                 aip->submode_start_time = Missiontime;
8292                                 aip->last_attack_time = Missiontime;
8293                         }
8294                 }
8295                 break;
8296
8297         case SM_EVADE_WEAPON:
8298                 if (aip->danger_weapon_objnum == -1) {
8299                         aip->submode = SM_ATTACK;
8300                         aip->submode_start_time = Missiontime;
8301                         aip->last_attack_time = Missiontime;
8302                 }
8303                 break;
8304
8305         // Either change to SM_ATTACK or AIM_FIND_STEALTH
8306         case SM_STEALTH_FIND:
8307                 // if time > 5 sec change mode to sweep
8308                 if ( !(aip->ai_flags & AIF_STEALTH_PURSIUT) || (ai_is_stealth_visible(Pl_objp, En_objp) == STEALTH_VISIBLE) ) {
8309                         aip->submode = SM_ATTACK;
8310                         aip->submode_start_time = Missiontime;
8311                         aip->last_attack_time = Missiontime;
8312                         // sweep if I can't find in 5 sec or bail from find
8313                 } else if ( ((Missiontime - aip->submode_start_time) > i2f(5)) || (aip->submode_parm0 == SM_SF_BAIL) ) {
8314                         // begin sweep mode
8315                         aip->submode = SM_STEALTH_SWEEP;
8316                         aip->submode_start_time = Missiontime;
8317                         aip->last_attack_time = Missiontime;
8318                         aip->submode_parm0 = SM_SS_SET_GOAL;
8319                 }
8320                 break;
8321
8322         case SM_STEALTH_SWEEP:
8323                 if ( !(aip->ai_flags & AIF_STEALTH_PURSIUT) || (ai_is_stealth_visible(Pl_objp, En_objp) == STEALTH_VISIBLE) ) {
8324                         aip->submode = SM_ATTACK;
8325                         aip->submode_start_time = Missiontime;
8326                         aip->last_attack_time = Missiontime;
8327                 } else if ( (timestamp() - aip->stealth_last_visible_stamp) < 5000 ) {
8328                         // go back to find mode
8329                         aip->submode = SM_STEALTH_FIND;
8330                         aip->submode_start_time = Missiontime;
8331                         aip->submode_parm0 = SM_SF_AHEAD;
8332                 } else if ( /*(Missiontime - aip->submode_start_time) > i2f(30) || */(aip->submode_parm0 == SM_SS_DONE) ) {
8333                         // set target objnum = -1
8334                         set_target_objnum(aip, -1);
8335
8336                         // set submode to attack
8337                         aip->submode = SM_ATTACK;
8338                         aip->submode_start_time = Missiontime;
8339                         aip->last_attack_time = Missiontime;
8340                 }
8341                 break;
8342
8343         case SM_ATTACK_FOREVER: //      Engines blown, just attack.
8344                 break;
8345
8346         default:
8347                 //Int3();
8348                 aip->submode = SM_ATTACK;
8349                 aip->last_attack_time = Missiontime;
8350
8351                 aip->submode_start_time = Missiontime;
8352         }
8353
8354         //
8355         //      Maybe fire primary weapon and update time_enemy_in_range
8356         //
8357         //nprintf(("AI", "time_enemy_in_range = %7.3f, dot = %7.3f\n", aip->time_enemy_in_range, dot_to_enemy));
8358
8359         if (aip->mode != AIM_EVADE) {
8360                 if (dot_to_enemy > 0.95f - 0.5f * En_objp->radius/max(1.0f, En_objp->radius + dist_to_enemy)) {
8361                         aip->time_enemy_in_range += flFrametime;
8362                         
8363                         //      Chance of hitting ship is based on dot product of firing ship's forward vector with vector to ship
8364                         //      and also the size of the target relative to distance to target.
8365                         if (dot_to_enemy > max(0.5f, 0.90f + aip->ai_accuracy/10.0f - En_objp->radius/max(1.0f,dist_to_enemy))) {
8366
8367                                 ship *temp_shipp;
8368                                 ship_weapon *tswp;
8369
8370                                 temp_shipp = &Ships[Pl_objp->instance];
8371                                 tswp = &temp_shipp->weapons;
8372                                 if ( tswp->num_primary_banks > 0 ) {
8373                                         float   scale;
8374                                         Assert(tswp->current_primary_bank < tswp->num_primary_banks);
8375                                         weapon_info     *pwip = &Weapon_info[tswp->primary_bank_weapons[tswp->current_primary_bank]];
8376
8377                                         //      Less likely to fire if far away and moving.
8378                                         scale = pwip->max_speed/(En_objp->phys_info.speed + pwip->max_speed);
8379                                         if (scale > 0.6f)
8380                                                 scale = (scale - 0.6f) * 1.5f;
8381                                         else
8382                                                 scale = 0.0f;
8383                                         if (dist_to_enemy < pwip->max_speed * (1.0f + scale)) {
8384                                                 ai_fire_primary_weapon(Pl_objp);
8385                                         }
8386
8387                                         //      Don't fire secondaries at a protected ship.
8388                                         if (!(En_objp->flags & OF_PROTECTED)) {
8389                                                 ai_choose_secondary_weapon(Pl_objp, aip, En_objp);
8390                                                 int current_bank = tswp->current_secondary_bank;
8391                                                 weapon_info     *swip = &Weapon_info[tswp->secondary_bank_weapons[tswp->current_secondary_bank]];
8392
8393                                                 if (current_bank > -1) {
8394                                                         if (aip->ai_flags & AIF_UNLOAD_SECONDARIES) {
8395                                                                 if (timestamp_until(swp->next_secondary_fire_stamp[current_bank]) > swip->fire_wait*1000.0f) {
8396                                                                         swp->next_secondary_fire_stamp[current_bank] = timestamp((int) (swip->fire_wait*1000.0f));
8397                                                                 }
8398                                                         }
8399
8400                                                         if (timestamp_elapsed(swp->next_secondary_fire_stamp[current_bank])) {
8401                                                                 if (tswp->current_secondary_bank >= 0) {
8402                                                                         weapon_info     *swip = &Weapon_info[tswp->secondary_bank_weapons[tswp->current_secondary_bank]];
8403                                                                         float firing_range;
8404                                                                         
8405                                                                         if (swip->wi_flags & WIF_BOMB)
8406                                                                                 firing_range = swip->max_speed * swip->lifetime * 0.75f;
8407                                                                         else
8408                                                                                 firing_range = swip->max_speed * swip->lifetime * (Game_skill_level + 1 + aip->ai_class/2)/NUM_SKILL_LEVELS;
8409
8410                                                                         // reduce firing range in nebula
8411                                                                         extern int Nebula_sec_range;
8412                                                                         if ((The_mission.flags & MISSION_FLAG_FULLNEB) && Nebula_sec_range) {
8413                                                                                 firing_range *= 0.8f;
8414                                                                         }
8415
8416                                                                         //      If firing a spawn weapon, distance doesn't matter.
8417                                                                         int     spawn_fire = 0;
8418
8419                                                                         if (swip->wi_flags & WIF_SPAWN) {
8420                                                                                 int     count;
8421
8422                                                                                 count = num_nearby_fighters(get_enemy_team_mask(OBJ_INDEX(Pl_objp)), &Pl_objp->pos, 1000.0f);
8423
8424                                                                                 if (count > 3)
8425                                                                                         spawn_fire = 1;
8426                                                                                 else if (count >= 1) {
8427                                                                                         float hull_percent = Pl_objp->hull_strength/sip->initial_hull_strength;
8428
8429                                                                                         if (hull_percent < 0.01f)
8430                                                                                                 hull_percent = 0.01f;
8431
8432                                                                                         if (frand() < 0.25f/(30.0f*hull_percent) * count)       //      With timestamp below, this means could fire in 30 seconds if one enemy.
8433                                                                                                 spawn_fire = 1;
8434                                                                                 }
8435                                                                         }
8436
8437                                                                         if (spawn_fire || (dist_to_enemy < firing_range)) {
8438                                                                                 if (ai_fire_secondary_weapon(Pl_objp)) {
8439                                                                                         //      Only if weapon was fired do we specify time until next fire.  If not fired, done in ai_fire_secondary...
8440                                                                                         float t;
8441                                                                                         
8442                                                                                         if (aip->ai_flags & AIF_UNLOAD_SECONDARIES) {
8443                                                                                                 t = swip->fire_wait;
8444                                                                                         } else {
8445                                                                                                 t = set_secondary_fire_delay(aip, temp_shipp, swip);
8446                                                                                         }
8447                                                                                         //nprintf(("AI", "Next secondary to be fired in %7.3f seconds.\n", t));
8448                                                                                         swp->next_secondary_fire_stamp[current_bank] = timestamp((int) (t*1000.0f));
8449                                                                                 }
8450                                                                         } else {
8451                                                                                 swp->next_secondary_fire_stamp[current_bank] = timestamp(250);
8452                                                                         }
8453                                                                 }
8454                                                         }
8455                                                 }
8456                                         }
8457                                 }
8458                         }
8459                 } else {
8460                         aip->time_enemy_in_range *= (1.0f - flFrametime);
8461                 }
8462         } else
8463                 aip->time_enemy_in_range *= (1.0f - flFrametime);
8464
8465 }
8466
8467 //      Make the object *objp move so that the point *dp on the object moves towards the point *vp
8468 //      Return distance.
8469 void dock_move_towards_point(object *objp, vector *dp, vector *vp, float speed_scale, float other_obj_speed = 0.0f)
8470 {
8471         physics_info    *pi = &objp->phys_info;
8472         float                           dist;                   //      dist to goal
8473         vector                  v2g;                    //      vector to goal
8474         vector                  abs_pnt;                //      location of dock point, ie objp->pos + db
8475
8476         if (dp == NULL)
8477                 abs_pnt = objp->pos;
8478         else
8479                 vm_vec_add(&abs_pnt, &objp->pos, dp);
8480
8481         dist = vm_vec_dist_quick(vp, &abs_pnt);
8482         if (dist > 0.0f) {
8483                 float   speed;
8484
8485                 dist = vm_vec_normalized_dir(&v2g, vp, &abs_pnt);
8486                 speed = fl_sqrt(dist) * speed_scale;
8487                 if (other_obj_speed < MAX_REPAIR_SPEED*0.75f)
8488                         speed += other_obj_speed;
8489                 else
8490                         speed += MAX_REPAIR_SPEED*0.75f;
8491
8492                 vm_vec_copy_scale(&pi->desired_vel, &v2g, speed);
8493         } else
8494                 vm_vec_zero(&pi->desired_vel);
8495 }
8496
8497 //      Set the orientation in the global reference frame for an object to attain
8498 //      to dock with another object.
8499 //      *dom            resultant global matrix
8500 //      *db_dest        pointer to destination docking bay information
8501 //      *db_src pointer to source docking bay information
8502 //      *dorient        pointer to global orientation of docking bay (ie, the dockee object's orient)
8503 //      *sorient        pointer to global orientation of docker
8504 void set_goal_dock_orient(matrix *dom, dock_bay *db_dest, dock_bay *db_src, matrix *dorient, matrix *sorient)
8505 {
8506         vector  fvec, uvec;
8507         matrix  m1, m2, m3;
8508
8509         //      Compute the global orientation of the docker's (dest) docking bay.
8510         fvec = db_dest->norm[0];
8511         vm_vec_negate(&fvec);
8512
8513         vm_vec_normalized_dir(&uvec, &db_dest->pnt[1], &db_dest->pnt[0]);
8514         vm_vector_2_matrix(&m1, &fvec, &uvec, NULL);
8515
8516         vm_matrix_x_matrix(&m3, dorient, &m1);
8517
8518         //      Compute the matrix given by the source docking bay.
8519         //      Pre-multiply the orientation of the source object (sorient) by the transpose
8520         //      of the docking bay's orientation, ie unrotate the source object's matrix.
8521         fvec = db_src->norm[0];
8522         vm_vec_normalized_dir(&uvec, &db_src->pnt[1], &db_src->pnt[0]);
8523         vm_vector_2_matrix(&m2, &fvec, &uvec, NULL);
8524         vm_transpose(&m2);
8525
8526         vm_matrix_x_matrix(dom, &m3, &m2);
8527 }
8528
8529 #define DOCK_BACKUP_RETURN_VAL  99999.9f
8530
8531 //      Make objp dock with dobjp
8532 //      Returns distance to goal, defined as distance between corresponding dock points, plus 10.0f * rotational velocity vector (DOA_DOCK only)
8533 //      DOA_APPROACH    means   approach point aip->path_cur
8534 //      DOA_DOCK                        means dock
8535 //      DOA_UNDOCK_1    means undock, moving to point nearest dock bay
8536 //      DOA_UNDOCK_2    means undock, moving to point nearest dock bay and facing away from ship
8537 //      DOA_DOCK_STAY   means rigidly maintain position in dock bay.
8538 float dock_orient_and_approach(object *objp, object *dobjp, int dock_mode)
8539 {
8540         ship_info       *sip0, *sip1;
8541         polymodel       *pm0, *pm1;
8542         ai_info         *aip;
8543         matrix          dom, nm;
8544         vector          goal_point, docker_point;
8545         float                   fdist = UNINITIALIZED_VALUE;
8546         int                     docker_index, dockee_index;             // index into docking_bays[] array for objects docking
8547                                                                                                                                 // docker is Pl_objp -- dockee is dobjp
8548         aip = &Ai_info[Ships[objp->instance].ai_index];
8549
8550         //      If dockee has moved much, then path will be recreated.
8551         //      Might need to change state if moved too far.
8552         if ((dock_mode != DOA_DOCK_STAY) && (dock_mode != DOA_DOCK)) {
8553                 if (maybe_recreate_path(objp, &Ai_info[Ships[objp->instance].ai_index], 0) > 5.0f) {
8554 /*                      if (dock_mode == DOA_APPROACH) {
8555                                 return DOCK_BACKUP_RETURN_VAL;
8556                         } else if (dock_mode == DOA_DOCK) {
8557                                 return DOCK_BACKUP_RETURN_VAL;          
8558                         }
8559 */              }
8560         }
8561
8562         objp->phys_info.forward_thrust = 0.0f;          //      Kill thrust so we don't have a sputtering thruster.
8563
8564         sip0 = &Ship_info[Ships[objp->instance].ship_info_index];
8565         sip1 = &Ship_info[Ships[dobjp->instance].ship_info_index];
8566         pm0 = model_get( sip0->modelnum );
8567         pm1 = model_get( sip1->modelnum );
8568
8569         docker_index = aip->dock_index;
8570         dockee_index = aip->dockee_index;
8571
8572         Assert( docker_index >= 0 );
8573         Assert( dockee_index >= 0 );
8574
8575         Assert(pm0->docking_bays[docker_index].num_slots == 2);
8576         Assert(pm1->docking_bays[dockee_index].num_slots == 2);
8577
8578         float speed_scale = 1.0f;
8579         if (sip0->flags & SIF_SUPPORT) {
8580                 speed_scale = 3.0f;
8581         }
8582
8583         switch (dock_mode) {
8584         case DOA_APPROACH:
8585                 {
8586                 if (ship_get_subsystem_strength(&Ships[objp->instance], SUBSYSTEM_ENGINE) <= 0.0f) {
8587                         return 9999.9f;
8588                 }
8589                 
8590                 //      Compute the desired global orientation matrix for the docker's station.
8591                 //      That is, the normal vector of the docking station must be the same as the
8592                 //      forward vector and the vector between its two points must be the uvec.
8593                 set_goal_dock_orient(&dom, &pm1->docking_bays[dockee_index], &pm0->docking_bays[docker_index], &dobjp->orient, &objp->orient);
8594
8595                 //      Compute new orientation matrix and update rotational velocity.
8596                 vector  w_in, w_out, vel_limit, acc_limit;
8597                 float           tdist, mdist, ss1;
8598
8599                 w_in = objp->phys_info.rotvel;
8600                 vel_limit = objp->phys_info.max_rotvel;
8601                 vm_vec_copy_scale(&acc_limit, &vel_limit, 0.3f);
8602                 
8603                 if (sip0->flags & SIF_SUPPORT)
8604                         vm_vec_scale(&acc_limit, 2.0f);
8605
8606                 // 1 at end of line prevent overshoot
8607                 vm_matrix_interpolate(&dom, &objp->orient, &w_in, flFrametime, &nm, &w_out, &vel_limit, &acc_limit, 1);
8608                 objp->phys_info.rotvel = w_out;
8609                 objp->orient = nm;
8610
8611                 //      Translate towards goal and note distance to goal.
8612                 goal_point = Path_points[aip->path_cur].pos;
8613                 mdist = ai_matrix_dist(&objp->orient, &dom);
8614                 tdist = vm_vec_dist_quick(&objp->pos, &goal_point);
8615
8616                 //      If translation is badly lagging rotation, speed up translation.
8617                 if (mdist > 0.1f) {
8618                         ss1 = tdist/(10.0f * mdist);
8619                         if (ss1 > 2.0f)
8620                                 ss1 = 2.0f;
8621                 } else
8622                         ss1 = 2.0f;
8623
8624                 // nprintf(("AI", "speed scale = %7.3f\n", ss1));
8625                 speed_scale *= 1.0f + ss1;
8626
8627                 dock_move_towards_point(objp, NULL, &goal_point, speed_scale, dobjp->phys_info.speed);
8628
8629                 fdist = vm_vec_dist_quick(&objp->pos, &goal_point);
8630
8631                 //      Note, we're interested in distance from goal, so if we're still turning, bash that into return value.
8632                 // nprintf(("AI", "matrix dist = %7.3f, threshold = %7.3f\n", mdist, 2*flFrametime));
8633                 fdist += 2.0f * mdist;
8634
8635                 break;
8636         }
8637         case DOA_DOCK:
8638                 if (ship_get_subsystem_strength(&Ships[objp->instance], SUBSYSTEM_ENGINE) <= 0.0f) {
8639                         return 9999.9f;
8640                 }
8641         case DOA_DOCK_STAY:
8642                 //      Compute the desired global orientation matrix for the docker's station.
8643                 //      That is, the normal vector of the docking station must be the same as the
8644                 //      forward vector and the vector between its two points must be the uvec.
8645                 set_goal_dock_orient(&dom, &pm1->docking_bays[dockee_index], &pm0->docking_bays[docker_index], &dobjp->orient, &objp->orient);
8646
8647                 //      Compute distance between dock bay points.
8648                 vector  db0, db1, db2, db3;
8649
8650                 vm_vec_unrotate(&db0, &pm0->docking_bays[docker_index].pnt[0], &objp->orient);
8651                 vm_vec_add2(&db0, &objp->pos);
8652
8653                 vm_vec_unrotate(&db1, &pm0->docking_bays[docker_index].pnt[1], &objp->orient);
8654                 vm_vec_add2(&db1, &objp->pos);
8655
8656                 vm_vec_unrotate(&db2, &pm1->docking_bays[dockee_index].pnt[0], &dobjp->orient);
8657                 vm_vec_add2(&db2, &dobjp->pos);
8658
8659                 vm_vec_unrotate(&db3, &pm1->docking_bays[dockee_index].pnt[1], &dobjp->orient);
8660                 vm_vec_add2(&db3, &dobjp->pos);
8661
8662                 vm_vec_avg(&goal_point, &db2, &db3);
8663
8664                 vm_vec_avg(&docker_point, &db0, &db1);
8665                 vm_vec_sub2(&docker_point, &objp->pos);
8666
8667                 if (dock_mode == DOA_DOCK) {
8668                         vector  t1, t2;
8669                         vector  w_in, w_out, vel_limit, acc_limit;
8670
8671                         fdist = vm_vec_dist_quick(vm_vec_avg(&t1, &db0, &db1), vm_vec_avg(&t2, &db2, &db3));
8672
8673                         //      Compute new orientation matrix and update rotational velocity.
8674                         w_in = objp->phys_info.rotvel;
8675                         vel_limit = objp->phys_info.max_rotvel;
8676                         vm_vec_copy_scale(&acc_limit, &vel_limit, 0.3f);
8677
8678                         if (sip0->flags & SIF_SUPPORT)
8679                                 vm_vec_scale(&acc_limit, 2.0f);
8680
8681                         vm_matrix_interpolate(&dom, &objp->orient, &w_in, flFrametime, &nm, &w_out, &vel_limit, &acc_limit);
8682                         objp->phys_info.rotvel = w_out;
8683                         objp->orient = nm;
8684
8685                         //      Note, we're interested in distance from goal, so if we're still turning, bash that into return value.
8686                         fdist += 10.0f * vm_vec_mag_quick(&w_out);
8687
8688                         dock_move_towards_point(objp, &docker_point, &goal_point, speed_scale, dobjp->phys_info.speed);
8689                 } else {
8690                         Assert(dock_mode == DOA_DOCK_STAY);
8691                         objp->orient = dom;
8692                         vector  temp;
8693                         vm_vec_sub(&temp, &goal_point, &docker_point);
8694                         vm_vec_sub(&objp->pos, &goal_point, &docker_point);
8695                 }
8696
8697                 break;
8698         case DOA_UNDOCK_1: {
8699                 if (ship_get_subsystem_strength(&Ships[objp->instance], SUBSYSTEM_ENGINE) <= 0.0f) {
8700                         return 9999.9f;
8701                 }
8702
8703                 //      Undocking.
8704                 //      Move to point on dock path nearest to dock station.
8705                 Assert(aip->path_length >= 2);
8706                 goal_point = Path_points[aip->path_start + aip->path_length-2].pos;
8707
8708                 vm_vec_zero(&docker_point);
8709                 fdist = vm_vec_dist_quick(&objp->pos, &goal_point);
8710
8711                 dock_move_towards_point(objp, &docker_point, &goal_point, speed_scale);
8712
8713                 break;
8714                           }
8715
8716         case DOA_UNDOCK_2: {
8717                 //      Undocking.
8718                 //      Move to point on dock path nearest to dock station and orient away from big ship.
8719                 int             desired_index;
8720
8721                 if (ship_get_subsystem_strength(&Ships[objp->instance], SUBSYSTEM_ENGINE) <= 0.0f) {
8722                         return 9999.9f;
8723                 }
8724
8725                 Assert(aip->path_length >= 2);
8726 //              if (aip->path_length >= 3)
8727 //                      desired_index = aip->path_length-3;
8728 //              else
8729                         desired_index = aip->path_length-2;
8730
8731                 goal_point = Path_points[aip->path_start + desired_index].pos;
8732
8733                 dock_move_towards_point(objp, NULL, &goal_point, speed_scale);
8734
8735                 fdist = vm_vec_dist_quick(&objp->pos, &goal_point);
8736                 break;
8737                           }
8738         case DOA_UNDOCK_3: {
8739                 float           dist, goal_dist;
8740                 vector  away_vec;
8741
8742                 goal_dist = objp->radius + dobjp->radius + 25.0f;
8743
8744                 dist = vm_vec_normalized_dir(&away_vec, &objp->pos, &dobjp->pos);
8745                 vm_vec_scale_add(&goal_point, &dobjp->pos, &away_vec, goal_dist);
8746                 if (vm_vec_dist_quick(&goal_point, &dobjp->pos) < vm_vec_dist_quick(&objp->pos, &dobjp->pos))
8747                         fdist = 0.0f;
8748                 else {
8749                         float   dot, accel;
8750                         float turn_time = Ship_info[Ships[objp->instance].ship_info_index].srotation_time;
8751                         ai_turn_towards_vector(&goal_point, objp, flFrametime, turn_time, NULL, NULL, 0.0f, 0);
8752
8753                         dot = vm_vec_dot(&objp->orient.fvec, &away_vec);
8754                         accel = 0.1f;
8755                         if (dot > accel)
8756                                 accel = dot;
8757                         if (dist > goal_dist/2)
8758                                 accel *= 1.2f - 0.5f*goal_dist/dist;
8759
8760                         accelerate_ship(aip, accel);
8761                         fdist = vm_vec_dist_quick(&objp->pos, &goal_point);
8762                 }
8763
8764                 break;
8765                                                          }
8766         }
8767
8768 #ifndef NDEBUG
8769         //      For debug purposes, compute global orientation of both dock vectors and show
8770         //      how close they are.
8771         vector  d0, d1;
8772
8773         vm_vec_unrotate(&d0, &pm0->docking_bays[docker_index].norm[0], &objp->orient);
8774         vm_vec_unrotate(&d1, &pm1->docking_bays[dockee_index].norm[0], &dobjp->orient);
8775
8776         //nprintf(("AI", "or/app: dist = %7.3f/%7.3f, dot = %7.3f, global dot = %7.3f\n", 
8777         //      vm_vec_dist_quick(&goal_point, &objp->pos), fdist,
8778         //      vm_vec_dot(&objp->orient.fvec, &dom.fvec), 
8779         //      vm_vec_dot(&d0, &d1)));
8780 #endif
8781
8782         // -- Note, A lot of callers don't care about fdist, so OK to return ERROR value: Assert(fdist != UNINITIALIZED_VALUE);
8783         return fdist;
8784
8785 }
8786
8787 void debug_find_guard_object()
8788 {
8789         ship                    *shipp = &Ships[Pl_objp->instance];     
8790         object          *objp;
8791
8792         for ( objp = GET_FIRST(&obj_used_list); objp !=END_OF_LIST(&obj_used_list); objp = GET_NEXT(objp) ) {
8793                 if ((Pl_objp != objp) && (objp->type == OBJ_SHIP)) {
8794                         if (objp->instance != -1) {
8795                                 if (Ships[objp->instance].team == shipp->team)  {
8796                                         // nprintf(("AI", "Setting guard object for %s to %s\n", shipp->ship_name, Ships[objp->instance].ship_name));
8797                                         ai_set_guard_object(Pl_objp, objp);
8798                                 }
8799                         }
8800                 }
8801         }
8802
8803 }
8804
8805 //      Given an object number, return the number of ships attacking it.
8806 int num_ships_attacking(int objnum)
8807 {
8808         object  *objp;
8809         ship_obj        *so;
8810         int             count = 0;
8811
8812         for ( so = GET_FIRST(&Ship_obj_list); so != END_OF_LIST(&Ship_obj_list); so = GET_NEXT(so) ) {
8813                 objp = &Objects[so->objnum];
8814                 if (objp->instance != -1) {
8815                         ai_info *aip;
8816                         aip = &Ai_info[Ships[objp->instance].ai_index];
8817
8818                         if ((aip->mode == AIM_CHASE) && (aip->target_objnum == objnum))
8819                                 if (Ships[objp->instance].team != Ships[Objects[objnum].instance].team)
8820                                         count++;
8821                 }
8822         }
8823
8824         return count;
8825 }
8826
8827 //      For all objects attacking object #objnum, remove the one that is farthest away.
8828 //      Do this by resuming previous behavior, if any.  If not, set target_objnum to -1.
8829 void remove_farthest_attacker(int objnum)
8830 {
8831         object  *objp, *objp2, *farthest_objp;
8832         ship_obj        *so;
8833         float           farthest_dist;
8834
8835         objp2 = &Objects[objnum];
8836
8837         farthest_dist = 9999999.9f;
8838         farthest_objp = NULL;
8839
8840         for ( so = GET_FIRST(&Ship_obj_list); so != END_OF_LIST(&Ship_obj_list); so = GET_NEXT(so) ) {
8841                 objp = &Objects[so->objnum];
8842                 if ( !(objp->flags & OF_PLAYER_SHIP)) {
8843                         if (objp->instance != -1) {
8844                                 ai_info *aip2;
8845
8846                                 aip2 = &Ai_info[Ships[objp->instance].ai_index];
8847
8848                                 if ((aip2->mode == AIM_CHASE) && (aip2->target_objnum == objnum)) {
8849                                         if (Ships[objp->instance].team != Ships[Objects[objnum].instance].team) {
8850                                                 float   dist;
8851
8852                                                 dist = vm_vec_dist_quick(&objp->pos, &objp2->pos);
8853                                                 if (dist < farthest_dist) {
8854                                                         farthest_dist = dist;
8855                                                         farthest_objp = objp;
8856                                                 }
8857                                         }
8858                                 }
8859                         }
8860                 }
8861         }
8862
8863         if (farthest_objp != NULL) {
8864                 ai_info *aip;
8865                 Assert(farthest_objp->type == OBJ_SHIP);
8866                 Assert((farthest_objp->instance > -1) && (farthest_objp->instance < MAX_SHIPS));
8867                 Assert(Ships[farthest_objp->instance].ai_index > -1);
8868
8869                 aip = &Ai_info[Ships[farthest_objp->instance].ai_index];
8870
8871                 if (!maybe_resume_previous_mode(Pl_objp, aip)) {
8872                         //      If already ignoring something under player's orders, don't ignore current target.
8873                         if ((aip->ignore_objnum == UNUSED_OBJNUM) || (aip->ai_flags & AIF_TEMPORARY_IGNORE)) {
8874                                 aip->ignore_objnum = aip->target_objnum;
8875                                 aip->ignore_signature = Objects[aip->target_objnum].signature;
8876                                 aip->ai_flags |= AIF_TEMPORARY_IGNORE;
8877                                 aip->ignore_expire_timestamp = timestamp(((myrand() % 10) + 20) * 1000);        //      OK to attack again in 20 to 24 seconds.
8878                         }
8879                         aip->target_objnum = -1;
8880                         ai_do_default_behavior(farthest_objp);
8881                 }
8882         }
8883 }
8884
8885 // Maybe limit the number of attackers on attack_objnum.  For now, only limit attackers
8886 // in attacked_objnum is the player
8887 // input:       attacked_objnum =>              object index for ship we want to limit attacks on
8888 //
8889 //      exit:                   1       =>      num attackers exceeds maximum, abort
8890 //                                      0       =>      removed the farthest attacker
8891 //                                      -1      =>      nothing was done
8892 int ai_maybe_limit_attackers(int attacked_objnum)
8893 {
8894         int rval=-1;
8895
8896         // limit the number of ships attacking the _player_ only
8897 //      if ( attacked_objnum == OBJ_INDEX(Player_obj) ) {
8898         if ( Objects[attacked_objnum].flags & OF_PLAYER_SHIP) {
8899                 int num_attacking;
8900                 num_attacking = num_ships_attacking(attacked_objnum);
8901
8902                 if (num_attacking == Skill_level_max_attackers[Game_skill_level]) {
8903                         remove_farthest_attacker(attacked_objnum);
8904                         rval=0;
8905                 } else if (num_attacking > Skill_level_max_attackers[Game_skill_level]) {
8906                         rval=1;
8907                 }
8908                 //nprintf(("AI", "Num attacking player = %i\n", num_attacking));
8909         }
8910
8911         return rval;
8912 }
8913
8914 //      Object being guarded by object *guard_objp was hit by object *hitter_objp
8915 void guard_object_was_hit(object *guard_objp, object *hitter_objp)
8916 {
8917         int             hitter_objnum;
8918         ai_info *aip;
8919
8920         aip = &Ai_info[Ships[guard_objp->instance].ai_index];
8921
8922         if (guard_objp == hitter_objp) {
8923                 // Int3();      //      Bogus!  Who tried to get me to attack myself!  Trace out and fix!
8924                 return;
8925         }
8926
8927         if (guard_objp->type == OBJ_GHOST || hitter_objp->type == OBJ_GHOST)
8928                 return;
8929
8930         if (aip->ai_flags & AIF_NO_DYNAMIC)     //      Not allowed to pursue dynamic goals.  So, why are we guarding?
8931                 return;
8932
8933         Assert( (hitter_objp->type == OBJ_SHIP) || (hitter_objp->type == OBJ_ASTEROID) || (hitter_objp->type == OBJ_WEAPON) );
8934
8935         hitter_objnum = OBJ_INDEX(hitter_objp);
8936
8937         if ( hitter_objp->type == OBJ_SHIP ) {
8938                 //      If the hitter object is the ignore object, don't attack it.
8939                 if (is_ignore_object(aip, hitter_objp-Objects))
8940                         return;
8941
8942                 //      If hitter is on same team as me, don't attack him.
8943                 if (Ships[guard_objp->instance].team == Ships[hitter_objp->instance].team)
8944                         return;
8945
8946                 // limit the number of ships attacking hitter_objnum (for now, only if hitter_objnum is player)
8947                 if ( ai_maybe_limit_attackers(hitter_objnum) == 1 ) {
8948                         return;
8949                 }
8950
8951                 // dont attack if you can't see him
8952                 if ( awacs_get_level(hitter_objp, &Ships[aip->shipnum], 1) < 1 ) {
8953                         // if he's a stealth and visible, but not targetable, ok to attack.
8954                         if ( is_object_stealth_ship(hitter_objp) ) {
8955                                 if ( ai_is_stealth_visible(guard_objp, hitter_objp) != STEALTH_VISIBLE ) {
8956                                         return;
8957                                 }
8958                         }
8959                 }
8960         }
8961
8962         if (aip->target_objnum == -1) {
8963                 aip->ok_to_target_timestamp = timestamp(0);
8964         }
8965
8966         if ((aip->submode == AIS_GUARD_PATROL) || (aip->submode == AIS_GUARD_STATIC)) {
8967
8968                 if ( hitter_objp->type == OBJ_SHIP ) {
8969                         if (!(Ship_info[Ships[guard_objp->instance].ship_info_index].flags & SIF_SMALL_SHIP)) {
8970                                 return;
8971                         }
8972
8973                         // limit the number of ships attacking hitter_objnum (for now, only if hitter_objnum is player)
8974                         if ( ai_maybe_limit_attackers(hitter_objnum) == 1 ) {
8975                                 return;
8976                         }
8977                 }
8978
8979                 if (aip->target_objnum != hitter_objnum) {
8980                         aip->aspect_locked_time = 0.0f;
8981                 }
8982
8983                 aip->ok_to_target_timestamp = timestamp(0);
8984
8985                 set_target_objnum(aip, hitter_objnum);
8986                 //if (aip->target_objnum == -1) nprintf(("AI", "Frame %i: Attacking NONE\n",Framecount)); else nprintf(("AI", "Frame %i: Attacking %s\n", Framecount, Ships[Objects[aip->target_objnum].instance].ship_name));
8987                 aip->previous_mode = AIM_GUARD;
8988                 aip->previous_submode = aip->submode;
8989                 aip->mode = AIM_CHASE;
8990                 aip->submode = SM_ATTACK;
8991                 aip->submode_start_time = Missiontime;
8992                 aip->active_goal = AI_ACTIVE_GOAL_DYNAMIC;
8993         } else if (aip->previous_mode == AIM_GUARD) {
8994                 if (aip->target_objnum == -1) {
8995
8996                         if ( hitter_objp->type == OBJ_SHIP ) {
8997                                 // limit the number of ships attacking hitter_objnum (for now, only if hitter_objnum is player)
8998                                 if ( ai_maybe_limit_attackers(hitter_objnum) == 1 ) {
8999                                         return;
9000                                 }
9001                         }
9002
9003                         set_target_objnum(aip, hitter_objnum);
9004                 //if (aip->target_objnum == -1) nprintf(("AI", "Frame %i: Attacking NONE\n",Framecount)); else nprintf(("AI", "Frame %i: Attacking %s\n", Framecount, Ships[Objects[aip->target_objnum].instance].ship_name));
9005                         aip->mode = AIM_CHASE;
9006                         aip->submode = SM_ATTACK;
9007                         aip->submode_start_time = Missiontime;
9008                         aip->active_goal = AI_ACTIVE_GOAL_DYNAMIC;
9009                 } else {
9010                         int     num_attacking_cur, num_attacking_new;
9011
9012                         num_attacking_cur = num_ships_attacking(aip->target_objnum);
9013                         if (num_attacking_cur > 1) {
9014                                 num_attacking_new = num_ships_attacking(hitter_objnum);
9015
9016                                 if (num_attacking_new < num_attacking_cur) {
9017
9018                                         if ( hitter_objp->type == OBJ_SHIP ) {
9019                                                 // limit the number of ships attacking hitter_objnum (for now, only if hitter_objnum is player)
9020                                                 if ( ai_maybe_limit_attackers(hitter_objnum) == 1 ) {
9021                                                         return;
9022                                                 }
9023                                         }
9024                                         set_target_objnum(aip, hitter_objp-Objects);
9025                 //if (aip->target_objnum == -1) nprintf(("AI", "Frame %i: Attacking NONE\n",Framecount)); else nprintf(("AI", "Frame %i: Attacking %s\n", Framecount, Ships[Objects[aip->target_objnum].instance].ship_name));
9026                                         aip->mode = AIM_CHASE;
9027                                         aip->submode = SM_ATTACK;
9028                                         aip->submode_start_time = Missiontime;
9029                                         aip->active_goal = AI_ACTIVE_GOAL_DYNAMIC;
9030                                 }
9031                         }
9032                 }
9033         }
9034 }
9035
9036 //      Ship object *hit_objp was hit by ship object *hitter_objp.
9037 //      See if anyone is guarding hit_objp and, if so, do something useful.
9038 void maybe_update_guard_object(object *hit_objp, object *hitter_objp)
9039 {
9040         object  *objp;
9041         ship_obj        *so;
9042
9043         for ( so = GET_FIRST(&Ship_obj_list); so != END_OF_LIST(&Ship_obj_list); so = GET_NEXT(so) ) {
9044                 objp = &Objects[so->objnum];
9045                 if (objp->instance != -1) {
9046                         ai_info *aip;
9047                         aip = &Ai_info[Ships[objp->instance].ai_index];
9048
9049                         if ((aip->mode == AIM_GUARD) || (aip->active_goal == AI_ACTIVE_GOAL_DYNAMIC)) {
9050                                 if (aip->guard_objnum == hit_objp-Objects) {
9051                                         guard_object_was_hit(objp, hitter_objp);
9052                                 } else if ((aip->guard_wingnum != -1) && (aip->guard_wingnum == Ai_info[Ships[hit_objp->instance].ai_index].wing)) {
9053                                         guard_object_was_hit(objp, hitter_objp);
9054                                 }
9055                         }
9056                 }
9057         }
9058 }
9059
9060 // Scan missile list looking for bombs homing on guarded_objp
9061 // return 1 if bomb is found (and targeted by guarding_objp), otherwise return 0
9062 int ai_guard_find_nearby_bomb(object *guarding_objp, object *guarded_objp)
9063 {       
9064         missile_obj     *mo;
9065         object          *bomb_objp, *closest_bomb_objp=NULL;
9066         float                   dist, dist_to_guarding_obj,closest_dist_to_guarding_obj=999999.0f;
9067         weapon          *wp;
9068         weapon_info     *wip;
9069
9070         for ( mo = GET_NEXT(&Missile_obj_list); mo != END_OF_LIST(&Missile_obj_list); mo = GET_NEXT(mo) ) {
9071                 Assert(mo->objnum >= 0 && mo->objnum < MAX_OBJECTS);
9072                 bomb_objp = &Objects[mo->objnum];
9073
9074                 wp = &Weapons[bomb_objp->instance];
9075                 wip = &Weapon_info[wp->weapon_info_index];
9076
9077                 if ( !(wip->wi_flags & WIF_BOMB) ) {
9078                         continue;
9079                 }
9080
9081                 if ( wp->homing_object != guarded_objp ) {
9082                         continue;
9083                 }
9084
9085                 dist = vm_vec_dist_quick(&bomb_objp->pos, &guarded_objp->pos);
9086
9087                 if (dist < (MAX_GUARD_DIST + guarded_objp->radius)*3) {
9088                         dist_to_guarding_obj = vm_vec_dist_quick(&bomb_objp->pos, &guarding_objp->pos);
9089                         if ( dist_to_guarding_obj < closest_dist_to_guarding_obj ) {
9090                                 closest_dist_to_guarding_obj = dist_to_guarding_obj;
9091                                 closest_bomb_objp = bomb_objp;
9092                         }
9093                 }
9094         }
9095
9096         if ( closest_bomb_objp ) {
9097                 guard_object_was_hit(guarding_objp, closest_bomb_objp);
9098                 return 1;
9099         }
9100
9101         return 0;
9102 }
9103
9104 //      Scan enemy ships and see if one is near enough to guard object to be pursued.
9105 void ai_guard_find_nearby_ship(object *guarding_objp, object *guarded_objp)
9106 {
9107         ship            *guarding_shipp = &Ships[guarding_objp->instance];
9108         ai_info *guarding_aip = &Ai_info[guarding_shipp->ai_index];
9109         ship_obj        *so;
9110         object  *enemy_objp;
9111         float           dist;
9112
9113         for ( so = GET_FIRST(&Ship_obj_list); so != END_OF_LIST(&Ship_obj_list); so = GET_NEXT(so) ) {
9114                 enemy_objp = &Objects[so->objnum];
9115
9116                 if (enemy_objp->instance < 0) {
9117                         continue;
9118                 }
9119
9120                 ship    *eshipp = &Ships[enemy_objp->instance];
9121
9122                 //      Don't attack a cargo container or other harmless ships
9123                 if (!(Ship_info[eshipp->ship_info_index].flags & SIF_HARMLESS)) {
9124                         if (guarding_shipp->team != eshipp->team)       {
9125                                 dist = vm_vec_dist_quick(&enemy_objp->pos, &guarded_objp->pos);
9126                                 if (dist < (MAX_GUARD_DIST + guarded_objp->radius)*3) {
9127                                         guard_object_was_hit(guarding_objp, enemy_objp);
9128                                 } else if ((dist < 3000.0f) && (Ai_info[eshipp->ai_index].target_objnum == guarding_aip->guard_objnum)) {
9129                                         //nprintf(("AI", "%i: Enemy %s targeting guard object (%s), %s will attack!!\n", Framecount, eshipp->ship_name, Ships[Objects[guarding_aip->guard_objnum].instance].ship_name, guarding_shipp->ship_name));
9130                                         guard_object_was_hit(guarding_objp, enemy_objp);
9131                                 }
9132                         }
9133                 }
9134         }
9135 }
9136
9137 // Scan for nearby asteroids.  Favor asteroids which have their collide_objnum set to that of the
9138 // guarded ship.  Also, favor asteroids that are closer to the guarding ship, since it looks cooler
9139 // when a ship blows up an asteroid then goes after the pieces that break off.
9140 void ai_guard_find_nearby_asteroid(object *guarding_objp, object *guarded_objp)
9141 {       
9142         float           dist;
9143
9144         object  *closest_asteroid_objp=NULL, *danger_asteroid_objp=NULL, *asteroid_objp;
9145         float           dist_to_self, closest_danger_asteroid_dist=999999.0f, closest_asteroid_dist=999999.0f;
9146
9147         for ( asteroid_objp = GET_FIRST(&obj_used_list); asteroid_objp != END_OF_LIST(&obj_used_list); asteroid_objp = GET_NEXT(asteroid_objp) ) {
9148                 if ( asteroid_objp->type == OBJ_ASTEROID ) {
9149                         // Attack asteroid if near guarded ship
9150                         dist = vm_vec_dist_quick(&asteroid_objp->pos, &guarded_objp->pos);
9151                         if ( dist < (MAX_GUARD_DIST + guarded_objp->radius)*2) {
9152                                 dist_to_self = vm_vec_dist_quick(&asteroid_objp->pos, &guarding_objp->pos);
9153                                 if ( OBJ_INDEX(guarded_objp) == asteroid_collide_objnum(asteroid_objp) ) {
9154                                         if( dist_to_self < closest_danger_asteroid_dist ) {
9155                                                 danger_asteroid_objp=asteroid_objp;
9156                                                 closest_danger_asteroid_dist=dist_to_self;
9157                                         }
9158                                 } 
9159                                 if ( dist_to_self < closest_asteroid_dist ) {
9160                                         // only attack if moving slower than own max speed
9161                                         if ( vm_vec_mag_quick(&asteroid_objp->phys_info.vel) < guarding_objp->phys_info.max_vel.z ) {
9162                                                 closest_asteroid_dist = dist_to_self;
9163                                                 closest_asteroid_objp = asteroid_objp;
9164                                         }
9165                                 }
9166                         }
9167                 }
9168         }
9169
9170         if ( danger_asteroid_objp ) {
9171                 guard_object_was_hit(guarding_objp, danger_asteroid_objp);
9172         } else if ( closest_asteroid_objp ) {
9173                 guard_object_was_hit(guarding_objp, closest_asteroid_objp);
9174         }
9175 }
9176
9177 //      Scan potential harmful objects and see if one is near enough to guard object to be pursued.
9178 void ai_guard_find_nearby_object()
9179 {
9180         ship                    *shipp = &Ships[Pl_objp->instance];
9181         ai_info         *aip = &Ai_info[shipp->ai_index];
9182         object          *guardobjp;
9183         int                     bomb_found=0;
9184
9185         guardobjp = &Objects[aip->guard_objnum];
9186         
9187         // highest priority is a bomb fired on guarded ship
9188         bomb_found = ai_guard_find_nearby_bomb(Pl_objp, guardobjp);
9189
9190         if ( !bomb_found ) {
9191                 // check for ships if there are no bombs fired at guarded ship
9192                 ai_guard_find_nearby_ship(Pl_objp, guardobjp);
9193
9194                 // if not attacking anything, go for asteroid close to guarded ship
9195                 if ( (aip->target_objnum == -1) && asteroid_count() ) {
9196                         ai_guard_find_nearby_asteroid(Pl_objp, guardobjp);
9197                 }
9198         }
9199 }
9200
9201 // gets closest point on extended axis of cylinder, r_vec, and radius of cylinder
9202 // returns z of axis_point in cyl_objp reference frame
9203 float get_cylinder_points(object *other_objp, object *cyl_objp, vector *axis_pt, vector *r_vec, float *radius)
9204 {
9205         Assert(other_objp->type == OBJ_SHIP);
9206         Assert(cyl_objp->type == OBJ_SHIP);
9207
9208         // get radius of cylinder
9209         polymodel *pm = model_get(Ships[cyl_objp->instance].modelnum);
9210         float tempx, tempy;
9211         tempx = max(-pm->mins.x, pm->maxs.x);
9212         tempy = max(-pm->mins.y, pm->maxs.y);
9213         *radius = max(tempx, tempy);
9214
9215         // get vec from cylinder to other_obj
9216         vector r_sph;
9217         vm_vec_sub(&r_sph, &other_objp->pos, &cyl_objp->pos);
9218
9219         // get point on axis and on cylinder
9220         // extended_cylinder_z is along extended cylinder
9221         // cylinder_z is capped within cylinder
9222         float extended_cylinder_z = vm_vec_dotprod(&r_sph, &cyl_objp->orient.fvec);
9223
9224         // get pt on axis of extended cylinder
9225         vm_vec_scale_add(axis_pt, &cyl_objp->pos, &cyl_objp->orient.fvec, extended_cylinder_z);
9226
9227         // get r_vec (pos - axis_pt) normalized
9228         vm_vec_normalized_dir(r_vec, &other_objp->pos, axis_pt);
9229
9230         return extended_cylinder_z;
9231 }
9232
9233 // handler for guard behavior when guarding BIG ships
9234 //      When someone has attacked guarded ship, then attack that ship.
9235 // To attack another ship, switch out of guard mode into chase mode.
9236 void ai_big_guard()
9237 {
9238         
9239         ship                    *shipp = &Ships[Pl_objp->instance];
9240         ai_info         *aip = &Ai_info[shipp->ai_index];
9241         object          *guard_objp;
9242
9243         // sanity checks already done in ai_guard()
9244         guard_objp = &Objects[aip->guard_objnum];
9245
9246         switch (aip->submode) {
9247         case AIS_GUARD_STATIC:
9248         case AIS_GUARD_PATROL:
9249                 {
9250                 vector axis_pt, r_vec, theta_vec;
9251                 float radius, extended_z;
9252
9253                 // get random [0 to 1] based on OBJNUM
9254                 float objval = static_randf(Pl_objp-Objects);
9255
9256                 // get position relative to cylinder of guard_objp              
9257                 extended_z = get_cylinder_points(Pl_objp, guard_objp, &axis_pt, &r_vec, &radius);
9258                 vm_vec_crossprod(&theta_vec, &guard_objp->orient.fvec, &r_vec);
9259
9260                 // half ships circle each way
9261                 if (objval > 0.5f) {
9262                         vm_vec_negate(&theta_vec);
9263                 }
9264
9265                 float min_guard_dist = radius + Pl_objp->radius + 50.0f;
9266                 float desired_guard_dist = min_guard_dist + 0.5f * ((1.0f + objval) * MAX_GUARD_DIST);
9267                 float max_guard_dist =     min_guard_dist + 1.0f * ((1.0f + objval) * MAX_GUARD_DIST);
9268
9269                 // get z extents
9270                 float min_z, max_z, length;
9271                 polymodel *pm = model_get(Ships[guard_objp->instance].modelnum);
9272                 min_z = pm->mins.z;
9273                 max_z = pm->maxs.z;
9274                 length = max_z - min_z;
9275
9276                 // get desired z
9277                 // how often to choose new desired_z
9278                 // 1*(64) sec < 2000, 2*(64) < 2-4000 3*(64) > 4-8000, etc (Missiontime >> 22 is 64 sec intervals)
9279                 int time_choose = int(floor(log(length * 0.001) / log(2)));
9280                 float desired_z = min_z + length * static_randf( Pl_objp-Objects ^ (Missiontime >> (22 + time_choose)) );
9281
9282                 // get r from guard_ship
9283                 float cur_guard_rad = vm_vec_dist(&Pl_objp->pos, &axis_pt);
9284
9285                 // is ship within extents of cylinder of ship it is guarding
9286                 int inside = (extended_z > min_z) && (extended_z < min_z + length);
9287
9288                 vector goal_pt;
9289                 // maybe go into orbit mode
9290                 if (cur_guard_rad < max_guard_dist) {
9291                         if ( cur_guard_rad > min_guard_dist ) {
9292                                 if (inside) {
9293                                         // orbit
9294                                         vm_vec_scale_add(&goal_pt, &axis_pt, &r_vec, desired_guard_dist);
9295                                         vm_vec_scale_add2(&goal_pt, &theta_vec, desired_guard_dist);
9296                                 } else {
9297                                         // move to where I can orbit
9298                                         if (extended_z < min_z) {
9299                                                 vm_vec_scale_add(&goal_pt, &guard_objp->pos, &guard_objp->orient.fvec, min_z);
9300                                         } else {
9301                                                 vm_vec_scale_add(&goal_pt, &guard_objp->pos, &guard_objp->orient.fvec, max_z);
9302                                         }
9303                                         vm_vec_scale_add2(&goal_pt, &r_vec, desired_guard_dist);
9304                                         vm_vec_scale_add2(&goal_pt, &theta_vec, desired_guard_dist);
9305                                 }
9306                         } else {
9307                                 // too close for orbit mode
9308                                 if (inside) {
9309                                         // inside (fly straight out and return circle)
9310                                         vm_vec_scale_add(&goal_pt, &axis_pt, &r_vec, max_guard_dist);
9311                                 } else {
9312                                         // outside (fly to edge and circle)
9313                                         if (extended_z < min_z) {
9314                                                 vm_vec_scale_add(&goal_pt, &guard_objp->pos, &guard_objp->orient.fvec, min_z);
9315                                         } else {
9316                                                 vm_vec_scale_add(&goal_pt, &guard_objp->pos, &guard_objp->orient.fvec, max_z);
9317                                         }
9318                                         vm_vec_scale_add2(&goal_pt, &r_vec, max_guard_dist);
9319                                         vm_vec_scale_add2(&goal_pt, &theta_vec, desired_guard_dist);
9320                                 }
9321                         }
9322
9323                         if (Pl_objp->phys_info.fspeed > 0) {
9324                                 // modify goal_pt to take account moving guard objp
9325                                 float dist = vm_vec_dist_quick(&Pl_objp->pos, &goal_pt);
9326                                 float time = dist / Pl_objp->phys_info.fspeed;
9327                                 vm_vec_scale_add2(&goal_pt, &guard_objp->phys_info.vel, time);
9328
9329                                 // now modify to move to desired z (at a max of 20 m/s)
9330                                 float delta_z = desired_z - extended_z;
9331                                 float v_z = delta_z * 0.2f;
9332                                 if (v_z < -20) {
9333                                         v_z = -20.0f;
9334                                 } else if (v_z > 20) {
9335                                         v_z = 20.0f;
9336                                 }
9337
9338                                 vm_vec_scale_add2(&goal_pt, &guard_objp->orient.fvec, v_z*time);
9339                         }
9340
9341                 } else {
9342                         // cast vector to center of guard_ship adjusted by desired_z
9343                         float delta_z = desired_z - extended_z;
9344                         vm_vec_scale_add(&goal_pt, &guard_objp->pos, &guard_objp->orient.fvec, delta_z);
9345                 }
9346
9347                 // try not to bump into things along the way
9348                 if ( (cur_guard_rad > max_guard_dist) || (extended_z < min_z) || (extended_z > max_z) ) {
9349                         if (maybe_avoid_big_ship(Pl_objp, guard_objp, aip, &goal_pt, 5.0f)) {
9350                                 return;
9351                         }
9352
9353                         if (avoid_player(Pl_objp, &goal_pt)) {
9354                                 return;
9355                         }
9356                 } else {
9357                         if (maybe_avoid_big_ship(Pl_objp, guard_objp, aip, &goal_pt, 5.0f)) {
9358                                 return;
9359                         }
9360                 }
9361
9362                 // got the point, now let's go there
9363                 ai_turn_towards_vector(&goal_pt, Pl_objp, flFrametime, Ship_info[Ships[Pl_objp->instance].ship_info_index].srotation_time, NULL, NULL, 0.0f, 0);
9364 //              aip->goal_point = goal_pt;
9365                 accelerate_ship(aip, 1.0f);
9366
9367                 //      Periodically, scan for a nearby ship to attack.
9368                 if (((AI_FrameCount ^ (Pl_objp-Objects)) & 0x07) == 0) {
9369                         ai_guard_find_nearby_object();
9370                 }
9371                 }
9372                 break;
9373
9374         case AIS_GUARD_ATTACK:
9375                 //      The guarded ship has been attacked.  Do something useful!
9376                 ai_chase();
9377                 break;
9378
9379         default:
9380                 //Int3();       //      Illegal submode for Guard mode.
9381                 // AL 06/03/97 comment out Int3() to allow milestone to get out the door
9382                 aip->submode = AIS_GUARD_PATROL;
9383                 break;
9384         }
9385 }
9386
9387 //      Main handler for guard behavior.
9388 //      When someone has attacked guarded ship, then attack that ship.
9389 // To attack another ship, switch out of guard mode into chase mode.
9390 void ai_guard()
9391 {
9392         ship                    *shipp = &Ships[Pl_objp->instance];
9393         ai_info         *aip = &Ai_info[shipp->ai_index];
9394         object          *guard_objp;    
9395         ship                    *gshipp;
9396         float                   dist_to_guardobj, dot_to_guardobj;
9397         vector          vec_to_guardobj;
9398
9399         /*      //      Debug code, find an object to guard.
9400         int finding_guard_objnum = 0;   //      Debug code, to see if body of "if" below gets executed. 
9401         if (aip->guard_objnum == -1) {
9402                 finding_guard_objnum = 1;
9403                 debug_find_guard_object();
9404                 if (aip->guard_objnum == -1)
9405                         return;
9406         }
9407 */
9408         if (aip->guard_objnum == -1) {
9409                 aip->mode = AIM_NONE;
9410                 return;
9411         }
9412
9413         Assert(aip->guard_objnum != -1);
9414
9415         guard_objp = &Objects[aip->guard_objnum];
9416
9417         if (guard_objp == Pl_objp) {
9418                 Int3();         //      This seems illegal.  Why is a ship guarding itself?
9419                 aip->guard_objnum = -1;
9420                 return;
9421         }
9422
9423         // check that I have someone to guard
9424         if (guard_objp->instance == -1) {
9425                 return;
9426         }
9427
9428         //      Not sure whether this should be impossible, or a reasonable cleanup condition.
9429         //      For now (3/31/97), it's getting trapped by an Assert, so clean it up.
9430         if (guard_objp->type != OBJ_SHIP) {
9431                 aip->guard_objnum = -1;
9432                 return;
9433         }
9434
9435         // handler for gurad object with BIG radius
9436         if (guard_objp->radius > BIG_GUARD_RADIUS) {
9437                 ai_big_guard();
9438                 return;
9439         }
9440
9441         gshipp = &Ships[guard_objp->instance];
9442
9443         float                   objval;
9444         vector          goal_point;
9445         vector          rel_vec;
9446         float                   dist_to_goal_point, dot_to_goal_point, accel_scale;
9447         vector          v2g, rvec;
9448
9449         // get random [0 to 1] based on OBJNUM
9450         objval = static_randf(Pl_objp-Objects);
9451
9452         switch (aip->submode) {
9453         case AIS_GUARD_STATIC:
9454         case AIS_GUARD_PATROL:
9455                 //      Stay near ship
9456                 dist_to_guardobj = vm_vec_normalized_dir(&vec_to_guardobj, &guard_objp->pos, &Pl_objp->pos);
9457                 dot_to_guardobj = vm_vec_dot(&Pl_objp->orient.fvec, &vec_to_guardobj);
9458
9459                 rel_vec = aip->guard_vec;
9460                 vm_vec_add(&goal_point, &guard_objp->pos, &rel_vec);
9461
9462                 vm_vec_normalized_dir(&v2g, &goal_point, &Pl_objp->pos);
9463                 dist_to_goal_point = vm_vec_dist_quick(&goal_point, &Pl_objp->pos);
9464                 dot_to_goal_point = vm_vec_dot(&v2g, &Pl_objp->orient.fvec);
9465                 accel_scale = (1.0f + dot_to_goal_point)/2.0f;
9466
9467                 //      If far away, get closer
9468                 if (dist_to_goal_point > MAX_GUARD_DIST + 1.5 * (Pl_objp->radius + guard_objp->radius)) {
9469                         if (maybe_avoid_big_ship(Pl_objp, guard_objp, aip, &goal_point, 5.0f)) {
9470                                 return;
9471                         }
9472
9473                         if (avoid_player(Pl_objp, &goal_point)) {
9474                                 return;
9475                         }
9476
9477                         // quite far away, so try to go straight to 
9478                         compute_desired_rvec(&rvec, &goal_point, &Pl_objp->pos);
9479                         ai_turn_towards_vector(&goal_point, Pl_objp, flFrametime, Ship_info[shipp->ship_info_index].srotation_time, NULL, NULL, 0.0f, 0, &rvec);
9480
9481                         accelerate_ship(aip, accel_scale * (0.25f + dist_to_goal_point/700.0f));
9482                 } else {
9483                         if (maybe_avoid_big_ship(Pl_objp, guard_objp, aip, &goal_point, 2.0f)) {
9484                                 return;
9485                         }
9486
9487                         // get max of guard_objp (1) normal speed (2) dock speed
9488                         float speed = guard_objp->phys_info.speed;
9489
9490                         if (guard_objp->type == OBJ_SHIP) {
9491                                 ai_info *guard_aip = &Ai_info[Ships[guard_objp->instance].ai_index];
9492
9493                                 if (guard_aip->dock_objnum != -1) {
9494                                         speed = max(speed, Objects[guard_aip->dock_objnum].phys_info.speed);
9495                                 }
9496                         }
9497                         
9498                         //      Deal with guarding a small object.
9499                         //      If going to guard_vec might cause a collision with guarded object, pick a new guard point.
9500                         if (vm_vec_dot(&v2g, &vec_to_guardobj) > 0.8f) {
9501                                 if (dist_to_guardobj < dist_to_goal_point) {
9502                                         ai_set_guard_vec(Pl_objp, guard_objp);  //      OK to return here.
9503                                         return;
9504                                 }
9505                         } 
9506
9507                         if (speed > 10.0f) {
9508                                 //      If goal ship is moving more than a tiny bit, don't orbit it, get near it.
9509                                 if (vm_vec_dist_quick(&goal_point, &Pl_objp->pos) > 40.0f) {
9510                                         if (vm_vec_dot(&Pl_objp->orient.fvec, &v2g) < 0.0f) {
9511                                                 //      Just slow down, don't turn.
9512                                                 set_accel_for_target_speed(Pl_objp, guard_objp->phys_info.speed - dist_to_goal_point/10.0f);
9513                                         } else {
9514                                                 //      Goal point is in front.
9515
9516                                                 //      If close to goal point, don't change direction, just change speed.
9517                                                 if (dist_to_goal_point > Pl_objp->radius + 10.0f) {
9518                                                         turn_towards_point(Pl_objp, &goal_point, NULL, 0.0f);
9519                                                 }
9520                                                 
9521                                                 set_accel_for_target_speed(Pl_objp, guard_objp->phys_info.speed + (dist_to_goal_point-40.0f)/20.0f);
9522                                         }
9523                                 } else {
9524                                         if (dot_to_goal_point > 0.8f) {
9525                                                 turn_towards_point(Pl_objp, &goal_point, NULL, 0.0f);
9526                                                 set_accel_for_target_speed(Pl_objp, guard_objp->phys_info.speed + dist_to_goal_point*0.1f);
9527                                         } else {
9528                                                 set_accel_for_target_speed(Pl_objp, guard_objp->phys_info.speed - dist_to_goal_point*0.1f - 1.0f);
9529                                         }
9530                                 }
9531                         // consider guard object STILL
9532                         } else if (guard_objp->radius < 50.0f) {
9533                                 if (dist_to_goal_point > 15.0f) {
9534                                         turn_towards_point(Pl_objp, &goal_point, NULL, 0.0f);
9535                                         set_accel_for_target_speed(Pl_objp, (dist_to_goal_point-10.0f)/2.0f);
9536                                 } else if (Pl_objp->phys_info.speed < 1.0f) {
9537                                         turn_away_from_point(Pl_objp, &guard_objp->pos, 0.0f);
9538                                 }
9539                                 //      It's a big ship
9540                         } else if (dist_to_guardobj > MAX_GUARD_DIST + Pl_objp->radius + guard_objp->radius) {
9541                                 //      Orbiting ship, too far away
9542                                 float dot = turn_towards_tangent(Pl_objp, &guard_objp->pos, (1.0f + objval/2) * guard_objp->radius);
9543                                 accelerate_ship(aip, (1.0f + dot)/2.0f);
9544                         } else if (dist_to_guardobj < Pl_objp->radius + guard_objp->radius) {
9545                                 //      Orbiting ship, got too close
9546                                 turn_away_from_point(Pl_objp, &guard_objp->pos, 0.0f);
9547                                 if ((dist_to_guardobj > guard_objp->radius + Pl_objp->radius + 50.0f) && (guard_objp->phys_info.speed > Pl_objp->phys_info.speed - 1.0f))
9548                                         change_acceleration(aip, 0.25f);
9549                                 else
9550                                         accelerate_ship(aip, 0.5f + objval/4.0f);
9551                         } else {
9552                                 //      Orbiting ship, about the right distance away.
9553                                 float dot = turn_towards_tangent(Pl_objp, &guard_objp->pos, (1.5f + objval/2.0f)*guard_objp->radius);
9554                                 if ((dist_to_guardobj > guard_objp->radius + Pl_objp->radius + 50.0f) && (guard_objp->phys_info.speed > Pl_objp->phys_info.speed - 1.0f))
9555                                         set_accel_for_target_speed(Pl_objp, (0.5f * (1.0f + dot)) * (guard_objp->phys_info.speed + (dist_to_guardobj - guard_objp->radius - Pl_objp->radius)/10.0f));
9556                                 else
9557                                         accelerate_ship(aip, 0.5f * (1.0f + dot) * (0.3f + objval/3.0f));
9558                         }
9559                 }
9560
9561                 //      Periodically, scan for a nearby ship to attack.
9562                 if (((AI_FrameCount ^ (Pl_objp-Objects)) & 0x07) == 0) {
9563                         ai_guard_find_nearby_object();
9564                 }
9565                 break;
9566
9567         case AIS_GUARD_ATTACK:
9568                 //      The guarded ship has been attacked.  Do something useful!
9569                 ai_chase();
9570
9571                 break;
9572         default:
9573                 //Int3();       //      Illegal submode for Guard mode.
9574                 // AL 06/03/97 comment out Int3() to allow milestone to get out the door
9575                 aip->submode = AIS_GUARD_PATROL;
9576                 break;
9577         }
9578
9579 }
9580
9581 // Return the object of the ship that the given object is docked
9582 // with.  Currently, we know a ship is docked when his ai_mode is AIM_DOCK,
9583 // and his submode is AIS_DOCK_3.  I suppose that this is likely to change though.
9584 // Also, the objnum that was is passed in may not be the object that actually
9585 // performed the docking maneuver.  This code will account for that case.
9586 object *ai_find_docked_object( object *docker )
9587 {
9588         ai_info *aip;
9589
9590         // we are trying to find the dockee of docker.  (Note that that these terms
9591         // are totally relative to what is passed in as a parameter.)
9592
9593         // first thing to attempt is to check and see if this object is docked with something.
9594         Assert( docker->type == OBJ_SHIP );             // this had probably better be a ship!!!
9595         aip = &Ai_info[Ships[docker->instance].ai_index];
9596         if ( !(aip->ai_flags & AIF_DOCKED) )            // flag not set if not docked with anything
9597                 return NULL;
9598
9599         if ( aip->dock_objnum == -1 ) {
9600                 Int3();                                                                                 // mwa says this is wrong wrong wrong
9601                 ai_do_objects_undocked_stuff( docker, NULL );
9602                 return NULL;
9603         }
9604
9605         return &Objects[aip->dock_objnum];
9606
9607 }
9608
9609
9610 // define for the points subtracted from score for a rearm started on a player.
9611 #define REPAIR_PENALTY          50
9612
9613
9614 // function to clean up ai flags, variables, and other interesting information
9615 // for a ship that was getting repaired.  The how parameter is useful for multiplayer
9616 // only in that it tells us why the repaired ship is being cleaned up.
9617 void ai_do_objects_repairing_stuff( object *repaired_objp, object *repair_objp, int how )
9618 {
9619         ai_info *aip, *repair_aip;
9620         int             stamp = -1;
9621
9622         Assert( repaired_objp->type == OBJ_SHIP);
9623         aip = &Ai_info[Ships[repaired_objp->instance].ai_index];
9624
9625         // multiplayer
9626         int p_index;
9627         p_index = -1;
9628         if(Game_mode & GM_MULTIPLAYER){
9629                 p_index = multi_find_player_by_object(repaired_objp);           
9630         }               
9631         else {          
9632                 if(repaired_objp == Player_obj){
9633                         p_index = Player_num;
9634                 }
9635         }
9636
9637         switch( how ) {
9638         case REPAIR_INFO_BEGIN:
9639                 aip->ai_flags |= AIF_BEING_REPAIRED;
9640                 aip->ai_flags &= ~AIF_AWAITING_REPAIR;
9641                 stamp = timestamp(-1);
9642
9643                 // if this is a player ship, then subtract the repair penalty from this player's score
9644                 if ( repaired_objp->flags & OF_PLAYER_SHIP ) {
9645                         if ( !(Game_mode & GM_MULTIPLAYER) ) {
9646                                 Player->stats.m_score -= (int)(REPAIR_PENALTY * scoring_get_scale_factor());                    // subtract the penalty
9647                         } else {
9648                                 /*
9649                                 int pnum;
9650
9651                                 // multiplayer game -- find the player, then subtract the score
9652                                 pnum = multi_find_player_by_object( repaired_objp );
9653                                 if ( pnum != -1 ) {
9654                                         Net_players[pnum].player->stats.m_score -= (int)(REPAIR_PENALTY * scoring_get_scale_factor());
9655
9656                                         // squad war
9657                                         multi_team_maybe_add_score(-(int)(REPAIR_PENALTY * scoring_get_scale_factor()), Net_players[pnum].p_info.team);
9658                                 } else {
9659                                         nprintf(("Network", "Couldn't find player for ship %s for repair penalty\n", Ships[repaired_objp->instance].ship_name));
9660                                 }
9661                                 */
9662                         }
9663                 }
9664                 break;
9665
9666         case REPAIR_INFO_BROKEN:
9667                 aip->ai_flags &= ~AIF_BEING_REPAIRED;
9668                 aip->ai_flags |= AIF_AWAITING_REPAIR;
9669                 stamp = timestamp((int) ((30 + 10*frand()) * 1000));
9670                 break;
9671
9672         case REPAIR_INFO_END:
9673                 // when only awaiting repair, and the repair is ended, then set dock_objnum to -1.
9674                 if ( aip->ai_flags & AIF_AWAITING_REPAIR ){
9675                         aip->dock_objnum = -1;
9676                 }
9677                 aip->ai_flags &= ~(AIF_AWAITING_REPAIR | AIF_BEING_REPAIRED );
9678                 stamp = timestamp((int) ((30 + 10*frand()) * 1000));
9679                 break;
9680
9681         case REPAIR_INFO_QUEUE:
9682                 aip->ai_flags |= AIF_AWAITING_REPAIR;
9683                 if ( aip == Player_ai ){
9684                         hud_support_view_start();
9685                 }
9686                 stamp = timestamp(-1);
9687                 break;
9688
9689         case REPAIR_INFO_ABORT:
9690         case REPAIR_INFO_KILLED:
9691                 // 5/4/98 -- MWA -- Need to set dock objnum to -1 to let code know this guy who was getting
9692                 // repaired (or queued for repair), isn't really going to be docked with anyone anymore.
9693                 aip->dock_objnum = -1;
9694                 aip->ai_flags &= ~AIF_DOCKED;
9695                 aip->ai_flags &= ~(AIF_AWAITING_REPAIR | AIF_BEING_REPAIRED );
9696                 if (repair_objp != NULL) {
9697                         repair_aip = &Ai_info[Ships[repair_objp->instance].ai_index];
9698                         repair_aip->ai_flags &= ~(AIF_AWAITING_REPAIR | AIF_BEING_REPAIRED );
9699                 }               
9700
9701                 if ( p_index >= 0 ) {
9702                         hud_support_view_abort();
9703
9704                         // send appropriate message to player here
9705                         if ( how == REPAIR_INFO_KILLED ){
9706                                 message_send_builtin_to_player( MESSAGE_SUPPORT_KILLED, NULL, MESSAGE_PRIORITY_HIGH, MESSAGE_TIME_SOON, 0, 0, p_index, -1 );
9707                         } else {
9708                                 if ( repair_objp ){
9709                                         message_send_builtin_to_player( MESSAGE_REPAIR_ABORTED, &Ships[repair_objp->instance], MESSAGE_PRIORITY_NORMAL, MESSAGE_TIME_SOON, 0, 0, p_index, -1 );
9710                                 }
9711                         }
9712                 }
9713
9714                 // add log entry if this is a player
9715                 if ( repaired_objp->flags & OF_PLAYER_SHIP ){
9716                         mission_log_add_entry(LOG_PLAYER_REARM_ABORT, Ships[repaired_objp->instance].ship_name, NULL);
9717                 }
9718
9719                 stamp = timestamp((int) ((30 + 10*frand()) * 1000));
9720                 break;
9721
9722         case REPAIR_INFO_COMPLETE:
9723                 // clear the being repaired flag -- and 
9724                 if ( p_index >= 0 ) {
9725                         Assert( repair_objp );
9726                         
9727                         hud_support_view_stop();                        
9728
9729                         message_send_builtin_to_player(MESSAGE_REPAIR_DONE, &Ships[repair_objp->instance], MESSAGE_PRIORITY_LOW, MESSAGE_TIME_SOON, 0, 0, p_index, -1);
9730                 }
9731                 stamp = timestamp((int) ((30 + 10*frand()) * 1000));
9732                 break;
9733
9734         case REPAIR_INFO_ONWAY:
9735                 // need to set the dock_signature so that clients in multiplayer games rearm correctly
9736                 Assert( repair_objp );
9737                 aip->dock_signature = repair_objp->signature; 
9738                 aip->dock_objnum = OBJ_INDEX(repair_objp);
9739                 stamp = timestamp(-1);
9740                 break;
9741
9742         default:
9743                 Int3();                 // bogus type of repair info
9744         }
9745
9746         if (repair_objp){
9747                 Ai_info[Ships[repair_objp->instance].ai_index].warp_out_timestamp = stamp;
9748         }
9749
9750         // repair_objp might be NULL is we are cleaning up this mode because of the support ship
9751         // getting killed.
9752         if ( repair_objp ) {
9753                 aip = &Ai_info[Ships[repair_objp->instance].ai_index];
9754                 switch ( how ) {
9755                 case REPAIR_INFO_ONWAY:
9756                         Assert( repaired_objp != NULL );
9757                         aip->goal_objnum = OBJ_INDEX(repaired_objp);
9758                         aip->ai_flags |= AIF_REPAIRING;
9759                         break;
9760
9761                 case REPAIR_INFO_BROKEN:
9762                         break;
9763
9764                 case REPAIR_INFO_END:
9765                 case REPAIR_INFO_ABORT:
9766                 case REPAIR_INFO_KILLED:
9767                         if ( how == REPAIR_INFO_ABORT )
9768                                 aip->goal_objnum = -1;
9769
9770                         aip->ai_flags &= ~AIF_REPAIRING;
9771                         break;
9772                         
9773                 case REPAIR_INFO_QUEUE:
9774                         ai_add_rearm_goal( repaired_objp, repair_objp );
9775                         break;
9776
9777                 case REPAIR_INFO_BEGIN:
9778                 case REPAIR_INFO_COMPLETE:
9779                         break;
9780
9781                 default:
9782                         Int3();         // bogus type of repair info
9783                 }
9784         }
9785
9786         multi_maybe_send_repair_info( repaired_objp, repair_objp, how );
9787 }
9788
9789 //      Cleanup AI stuff for when a ship was supposed to dock with another, but the ship
9790 //      it was supposed to dock with is no longer valid.
9791 void ai_cleanup_dock_mode(ai_info *aip, ship *shipp)
9792 {
9793         object *objp;
9794
9795         objp = &Objects[shipp->objnum];
9796         aip->mode = AIM_NONE;
9797
9798         if (aip->ai_flags & AIF_REPAIRING) {
9799                 Assert( aip->goal_objnum != -1 );
9800                 ai_do_objects_repairing_stuff( &Objects[aip->goal_objnum], &Objects[shipp->objnum], REPAIR_INFO_KILLED );
9801         } else if ( aip->ai_flags & AIF_BEING_REPAIRED ) {
9802                 // MWA -- note that we have to use dock_objnum here instead of goal_objnum.
9803                 Assert( aip->dock_objnum != -1 );
9804                 ai_do_objects_repairing_stuff( &Objects[shipp->objnum], &Objects[aip->dock_objnum], REPAIR_INFO_KILLED );
9805         } else if ( aip->ai_flags & AIF_AWAITING_REPAIR ) {
9806                 // need to find the support ship that has me as a goal_objnum
9807                 // MWA -- note that we have to use dock_objnum here instead of goal_objnum.
9808                 // MWA -- 3/38/98  Check to see if this guy is queued for a support ship, or there is already
9809                 // one in the mission
9810                 if ( mission_is_repair_scheduled(objp) ) {
9811                         mission_remove_scheduled_repair( objp );                        // this function will notify multiplayer clients.
9812                 } else {
9813                         if ( aip->dock_objnum != -1 )
9814                                 ai_do_objects_repairing_stuff( objp, &Objects[aip->dock_objnum], REPAIR_INFO_ABORT );
9815                         else
9816                                 ai_do_objects_repairing_stuff( objp, NULL, REPAIR_INFO_ABORT );
9817                 }
9818         }
9819
9820         if ( aip->ai_flags & AIF_DOCKED ) {
9821                 ai_info *other_aip;
9822
9823                 Assert( aip->dock_objnum != -1 );
9824
9825                 // if docked, and the dock_objnum is not undocking, force them to near last stage
9826                 other_aip = &Ai_info[Ships[Objects[aip->dock_objnum].instance].ai_index];
9827                 if ( (other_aip->mode == AIM_DOCK) && (other_aip->submode < AIS_UNDOCK_3) )
9828                         other_aip->submode = AIS_UNDOCK_3;
9829                 ai_do_objects_undocked_stuff( objp, &Objects[aip->dock_objnum] );
9830         }
9831 }
9832
9833 /*
9834 //      Make dockee_objp shake a bit due to docking.
9835 void ai_dock_shake(object *docker_objp, object *dockee_objp)
9836 {
9837         vector  tangles;
9838         matrix  rotmat, tmp;
9839         float           scale;
9840         angles  *ap;
9841
9842         scale = 0.25f;          //      Compute this based on mass and speed at time of docking.
9843
9844         vm_vec_rand_vec_quick(&tangles);
9845         vm_vec_scale(&tangles, scale);
9846
9847         ap = (angles *) &tangles;
9848
9849         vm_angles_2_matrix(&rotmat, ap);
9850         vm_matrix_x_matrix( &tmp, &dockee_objp->orient, &rotmat );
9851         dockee_objp->orient = tmp;
9852
9853         vm_orthogonalize_matrix(&dockee_objp->orient);
9854
9855         dock_orient_and_approach(docker_objp, dockee_objp, DOA_DOCK_STAY);
9856
9857 }
9858 */
9859
9860 //      Make Pl_objp point at aip->goal_point.
9861 void ai_still()
9862 {
9863         ship    *shipp;
9864         ai_info *aip;
9865
9866         Assert(Pl_objp->type == OBJ_SHIP);
9867         Assert((Pl_objp->instance >= 0) && (Pl_objp->instance < MAX_OBJECTS));
9868
9869         shipp = &Ships[Pl_objp->instance];
9870         Assert((shipp->ai_index >= 0) && (shipp->ai_index < MAX_AI_INFO));
9871
9872         aip = &Ai_info[shipp->ai_index];
9873
9874         turn_towards_point(Pl_objp, &aip->goal_point, NULL, 0.0f);
9875 }
9876
9877 //      Make *Pl_objp stay near another ship.
9878 void ai_stay_near()
9879 {
9880         ai_info *aip;
9881         int             goal_objnum;
9882
9883         aip = &Ai_info[Ships[Pl_objp->instance].ai_index];
9884
9885         goal_objnum = aip->goal_objnum;
9886
9887         if ((goal_objnum < 0) || (Objects[goal_objnum].type != OBJ_SHIP) || (Objects[goal_objnum].signature != aip->goal_signature)) {
9888                 aip->mode = AIM_NONE;
9889         } else {
9890                 float           dist, max_dist, scale;
9891                 vector  rand_vec, goal_pos, vec_to_goal;
9892                 object  *goal_objp;
9893
9894                 goal_objp = &Objects[goal_objnum];
9895
9896                 //      Make not all ships pursue same point.
9897                 static_randvec(Pl_objp-Objects, &rand_vec);
9898
9899                 //      Make sure point is in front hemisphere (relative to Pl_objp's position.
9900                 vm_vec_sub(&vec_to_goal, &goal_objp->pos, &Pl_objp->pos);
9901                 if (vm_vec_dot(&rand_vec, &vec_to_goal) > 1.0f) {
9902                         vm_vec_negate(&rand_vec);
9903                 }
9904
9905                 //      Scale the random vector by an amount proportional to the distance from Pl_objp to the true goal.
9906                 dist = vm_vec_dist_quick(&goal_objp->pos, &Pl_objp->pos);
9907                 max_dist = aip->stay_near_distance;
9908                 scale = dist - max_dist/2;
9909                 if (scale < 0.0f)
9910                         scale = 0.0f;
9911
9912                 vm_vec_scale_add(&goal_pos, &goal_objp->pos, &rand_vec, scale);
9913
9914                 if (max_dist < Pl_objp->radius + goal_objp->radius + 25.0f)
9915                         max_dist = Pl_objp->radius + goal_objp->radius + 25.0f;
9916
9917                 if (dist > max_dist) {
9918                         turn_towards_point(Pl_objp, &goal_pos, NULL, 0.0f);
9919                         accelerate_ship(aip, dist / max_dist - 0.8f);
9920                 }
9921         
9922         }
9923
9924 }
9925
9926 //      Warn player if dock path is obstructed.
9927 int maybe_dock_obstructed(object *cur_objp, object *goal_objp, int big_only_flag)
9928 {
9929         vector  *goalpos, *curpos;
9930         float           radius;
9931         ai_info *aip;
9932         int             collide_objnum;
9933
9934         aip = &Ai_info[Ships[cur_objp->instance].ai_index];
9935
9936         Ai_info[Ships[goal_objp->instance].ai_index].ai_flags &= ~AIF_REPAIR_OBSTRUCTED;
9937
9938         if (goal_objp != Player_obj)
9939                 return -1;
9940
9941         curpos = &cur_objp->pos;
9942         radius = cur_objp->radius;
9943         goalpos = &Path_points[aip->path_cur].pos;
9944         collide_objnum = pp_collide_any(curpos, goalpos, radius, cur_objp, goal_objp, big_only_flag);
9945
9946         if (collide_objnum != -1)
9947                 Ai_info[Ships[goal_objp->instance].ai_index].ai_flags |= AIF_REPAIR_OBSTRUCTED;
9948
9949         return collide_objnum;
9950 }
9951
9952
9953 int Dock_path_warning_given = 0;
9954
9955 //      Docking behavior.
9956 //      Approach a ship, follow path to docking platform, approach platform, after awhile,
9957 //      undock.
9958 void ai_dock()
9959 {
9960         ship                    *shipp = &Ships[Pl_objp->instance];
9961         ai_info         *aip = &Ai_info[shipp->ai_index];
9962         object          *goal_objp;
9963         ship_info       *sip = &Ship_info[shipp->ship_info_index];
9964
9965         //      Make sure object we're supposed to dock with still exists.
9966         if ((aip->goal_objnum == -1) || (Objects[aip->goal_objnum].signature != aip->goal_signature)) {
9967                 ai_cleanup_dock_mode(aip, shipp);
9968                 return;
9969         }
9970
9971         goal_objp = &Objects[aip->goal_objnum];
9972
9973         //      For docking submodes (ie, not undocking), follow path.  Once at second last
9974         //      point on path (point just before point on dock platform), orient into position.
9975         // For undocking, first mode pushes docked ship straight back from docking point
9976         // second mode turns ship and moves to point on docking radius
9977         switch (aip->submode) {
9978
9979                 //      This mode means to find the path to the docking point.
9980         case AIS_DOCK_0:
9981                 //aip->path_start = -1;
9982                 //nprintf(("AI", "Time = %7.3f, submode = %i\n", f2fl(Missiontime), aip->submode));
9983                 ai_path();
9984                 if (!Dock_path_warning_given && (aip->path_length < 4)) {
9985                         Warning( LOCATION, "Ship '%s' has only %i points on dock path.  Docking will look strange.  Contact Adam.", shipp->ship_name, aip->path_length );
9986                         Dock_path_warning_given = 1;            //      This is on a mission-wide basis, but it's just a hack for now...
9987                 }
9988
9989                 aip->submode = AIS_DOCK_1;
9990                 aip->path_start = -1;
9991                 aip->submode_start_time = Missiontime;
9992                 break;
9993
9994                 //      This mode means to follow the path until just before the end.
9995         case AIS_DOCK_1: {
9996                 float   dist;
9997                 int     r;
9998
9999                 if ((r = maybe_dock_obstructed(Pl_objp, goal_objp, 1)) != -1) {
10000                         int     r1;
10001                         if ((r1 = maybe_avoid_big_ship(Pl_objp, goal_objp, aip, &goal_objp->pos, 7.0f)) != 0) {
10002                                 nprintf(("AI", "Support ship %s avoiding large ship %s\n", Ships[Pl_objp->instance].ship_name, Ships[Objects[r1].instance].ship_name));
10003                                 break;
10004                         } /*else {
10005                                 nprintf(("AI", "Dock 1: Obstructed by %s\n", Ships[Objects[r].instance].ship_name));
10006                                 accelerate_ship(aip, 0.0f);
10007                                 aip->submode = AIS_DOCK_0;
10008                         } */
10009                 } //else {
10010                 {
10011                         dist = ai_path();
10012                         //nprintf(("AI", "Time = %7.3f, submode = %i\n", f2fl(Missiontime), aip->submode));
10013                         //nprintf(("AI", "Dock 1: Frame: %i, goal point = %i, dist = %7.3f\n", Framecount, aip->path_cur-aip->path_start, dist));
10014
10015                         if (aip->path_cur-aip->path_start >= aip->path_length-1) {              //      If got this far, advance no matter what.
10016                                 aip->submode = AIS_DOCK_2;
10017                                 aip->submode_start_time = Missiontime;
10018                                 aip->path_cur--;
10019                                 Assert(aip->path_cur-aip->path_start >= 0);
10020                         } else if (aip->path_cur-aip->path_start >= aip->path_length-2) {
10021                                 if (Pl_objp->phys_info.speed > goal_objp->phys_info.speed + 1.5f) {
10022                                         set_accel_for_target_speed(Pl_objp, goal_objp->phys_info.speed);
10023                                 } else {
10024                                         aip->submode = AIS_DOCK_2;
10025                                         aip->submode_start_time = Missiontime;
10026                                 }
10027                         }
10028                 }
10029                 break;
10030                                           }
10031         //      This mode means to drag oneself right to the second last point on the path.
10032         //      Path code allows it to overshoot.
10033         case AIS_DOCK_2: {
10034                 float           dist;
10035                 int     r;
10036
10037                 if ((r = maybe_dock_obstructed(Pl_objp, goal_objp,0)) != -1) {
10038                         nprintf(("AI", "Dock 2: Obstructed by %s\n", Ships[Objects[r].instance].ship_name));
10039                         accelerate_ship(aip, 0.0f);
10040                         aip->submode = AIS_DOCK_1;
10041                 } else {
10042                         //nprintf(("AI", "Time = %7.3f, submode = %i\n", f2fl(Missiontime), aip->submode));
10043                         dist = dock_orient_and_approach(Pl_objp, &Objects[aip->goal_objnum], DOA_APPROACH);
10044                         Assert(dist != UNINITIALIZED_VALUE);
10045
10046                         if (dist == DOCK_BACKUP_RETURN_VAL) {
10047                                 int path_num;
10048                                 aip->submode = AIS_DOCK_1;
10049                                 path_num = ai_return_path_num_from_dockbay(&Objects[aip->goal_objnum], aip->dockee_index);
10050                                 Assert(aip->goal_objnum >= 0 && aip->goal_objnum < MAX_OBJECTS);
10051                                 ai_find_path(Pl_objp, aip->goal_objnum, path_num, 0);
10052                                 break;
10053                         }
10054
10055                         //nprintf(("AI", "Dock 2: dist = %7.3f\n", vm_vec_dist_quick(&Pl_objp->pos, &goal_point)));
10056                         float   tolerance;
10057                         if (Objects[aip->goal_objnum].flags & OF_PLAYER_SHIP)
10058                                 tolerance = 6*flFrametime + 1.0f;
10059                         else
10060                                 tolerance = 4*flFrametime + 0.5f;
10061
10062                         if ( dist < tolerance) {
10063                                 aip->submode = AIS_DOCK_3;
10064                                 aip->submode_start_time = Missiontime;
10065                                 aip->path_cur++;
10066                         }
10067                 }
10068                 break;
10069                                                   }
10070
10071         case AIS_DOCK_3:
10072         case AIS_DOCK_3A:
10073                 {
10074                 Assert(aip->goal_objnum != -1);
10075                 int     r;
10076
10077                 if ((r = maybe_dock_obstructed(Pl_objp, goal_objp,0)) != -1) {
10078                         nprintf(("AI", "Dock 1: Obstructed by %s\n", Ships[Objects[r].instance].ship_name));
10079                         accelerate_ship(aip, 0.0f);
10080                         aip->submode = AIS_DOCK_2;
10081                 } else {
10082
10083                         //nprintf(("AI", "Time = %7.3f, submode = %i\n", f2fl(Missiontime), aip->submode));
10084                         float dist = dock_orient_and_approach(Pl_objp, &Objects[aip->goal_objnum], DOA_DOCK);
10085                         Assert(dist != UNINITIALIZED_VALUE);
10086
10087                         if (dist == DOCK_BACKUP_RETURN_VAL) {
10088                                 aip->submode = AIS_DOCK_2;
10089                                 break;
10090                         }
10091
10092                         //nprintf(("AI", "Dock 3: dist = %7.3f\n", dist));
10093
10094                         if (dist < 2*flFrametime * (1.0f + fl_sqrt(goal_objp->phys_info.speed))) {
10095                                 // - Removed by MK on 11/7/97, causes errors for ships docked at mission start: maybe_recreate_path(Pl_objp, aip, 1);
10096                                 dist = dock_orient_and_approach(Pl_objp, &Objects[aip->goal_objnum], DOA_DOCK);
10097                                 Assert(dist != UNINITIALIZED_VALUE);
10098
10099                                 physics_ship_init(Pl_objp);
10100
10101                                 ai_do_objects_docked_stuff( Pl_objp, goal_objp );
10102
10103                                 if (aip->submode == AIS_DOCK_3) {
10104                                         snd_play_3d( &Snds[SND_DOCK_ATTACH], &Pl_objp->pos, &View_position );
10105                                         hud_maybe_flash_docking_text(Pl_objp);
10106                                         // ai_dock_shake(Pl_objp, goal_objp);
10107
10108                                         if ((Pl_objp == Player_obj) || (goal_objp == Player_obj))
10109                                                 joy_ff_docked();  // shake player's joystick a little
10110                                 }
10111
10112                                 //      If this ship is repairing another ship...
10113                                 if (aip->ai_flags & AIF_REPAIRING) {
10114                                         aip->submode = AIS_DOCK_4;                      //      Special rearming only dock mode.
10115                                         aip->submode_start_time = Missiontime;
10116                                 } else {
10117                                         aip->submode = AIS_DOCK_4A;
10118                                         aip->submode_start_time = Missiontime;
10119                                 }
10120                         }
10121                 }
10122                 break;
10123                 }
10124
10125                 //      Yes, we just sit here.  We wait for further orders.  No, it's not a bug.
10126         case AIS_DOCK_4A:
10127                 //nprintf(("AI", "Time = %7.3f, submode = %i\n", f2fl(Missiontime), aip->submode));
10128                 //nprintf(("AI", "."));
10129                 if (aip->active_goal >= 0) {
10130                         mission_log_add_entry(LOG_SHIP_DOCK, Ships[Pl_objp->instance].ship_name, Ships[goal_objp->instance].ship_name);
10131
10132                         if (aip->goals[aip->active_goal].ai_mode == AI_GOAL_DOCK) {
10133                                 ai_mission_goal_complete( aip );                                        // Note, this calls ai_set_default_behavior().
10134                         } 
10135                 } else {        //      Can happen for initially docked ships.
10136                         ai_do_default_behavior( &Objects[Ships[aip->shipnum].objnum] );         // do the default behavior
10137                 }
10138                 
10139                 break;
10140
10141         case AIS_DOCK_4: {
10142                 //      This mode is only for rearming/repairing.
10143                 //      The ship that is performing the rearm enters this mode after it docks.
10144                 Assert((aip->goal_objnum >= -1) && (aip->goal_objnum < MAX_OBJECTS));
10145
10146                 //nprintf(("AI", "Time = %7.3f, submode = %i\n", f2fl(Missiontime), aip->submode));
10147                 float dist = dock_orient_and_approach(Pl_objp, &Objects[aip->goal_objnum], DOA_DOCK);
10148                 Assert(dist != UNINITIALIZED_VALUE);
10149
10150                 object  *goal_objp = &Objects[aip->goal_objnum];
10151                 Assert(goal_objp->type == OBJ_SHIP);
10152                 ship                    *goal_shipp = &Ships[goal_objp->instance];              
10153                 ai_info         *goal_aip = &Ai_info[goal_shipp->ai_index];
10154
10155                 //nprintf(("AI", "Dock 4: dist = %7.3f\n", dist));
10156
10157                 //      Make sure repair has not broken off.
10158                 if (dist > 5.0f) {      //      Oops, too far away!
10159                         if ( goal_aip->ai_flags & AIF_BEING_REPAIRED )
10160                                 ai_do_objects_repairing_stuff( goal_objp, Pl_objp, REPAIR_INFO_BROKEN);
10161
10162                         if (dist > Pl_objp->radius*2 + goal_objp->radius*2) {
10163                                 //      Got real far away from goal, so move back a couple modes and try again.
10164                                 aip->submode = AIS_DOCK_2;
10165                                 aip->submode_start_time = Missiontime;
10166                         }
10167                 } else {
10168                         if ( goal_aip->ai_flags & AIF_AWAITING_REPAIR )
10169                                 ai_do_objects_repairing_stuff( goal_objp, Pl_objp, REPAIR_INFO_BEGIN );
10170                 }
10171
10172                 break;
10173                                                   }
10174
10175         case AIS_UNDOCK_0: {
10176                 int path_num;
10177                 //      First stage of undocking.
10178
10179                 //nprintf(("AI", "Undock 0:\n"));
10180
10181                 aip->submode = AIS_UNDOCK_1;
10182                 aip->submode_start_time = Missiontime;
10183                 if (aip->dock_objnum == -1) {
10184                         aip->submode = AIS_UNDOCK_3;
10185                 } else {
10186
10187                         // set up the path points for the undocking procedure.  dock_path_index member should
10188                         // have gotten set in the docking code.
10189                         Assert( aip->dock_path_index != -1 );
10190                         path_num = ai_return_path_num_from_dockbay(goal_objp, aip->dock_path_index);
10191                         ai_find_path(Pl_objp, goal_objp-Objects, path_num, 0);
10192
10193                         // Play a ship docking detach sound
10194                         snd_play_3d( &Snds[SND_DOCK_DETACH], &Pl_objp->pos, &View_position );
10195                 }
10196                 break;
10197                                                          }
10198         case AIS_UNDOCK_1: {
10199                 //      Using thrusters, exit from dock station to nearest next dock path point.
10200                 float   dist;
10201                 
10202                 //nprintf(("AI", "Undock 1: time in this mode = %7.3f\n", f2fl(Missiontime - aip->submode_start_time)));
10203
10204                 if (Missiontime - aip->submode_start_time < REARM_BREAKOFF_DELAY) {
10205                         break;          //      Waiting for one second to elapse to let detach sound effect play out.
10206                 }
10207                 else {  // AL - added 05/16/97.  Hack to play depart sound.  Will probably take out.
10208                                         // Assumes that the submode_start_time is not used for AIS_UNDOCK_1 anymore
10209                         if ( aip->submode_start_time != 0 )
10210                                 snd_play_3d( &Snds[SND_DOCK_DEPART], &Pl_objp->pos, &View_position );
10211                         aip->submode_start_time = 0;
10212                 }
10213
10214                 dist = dock_orient_and_approach(Pl_objp, &Objects[aip->goal_objnum], DOA_UNDOCK_1);
10215                 Assert(dist != UNINITIALIZED_VALUE);
10216
10217                 float dist_to_dock_obj = vm_vec_dist_quick(&Pl_objp->pos, &Objects[aip->goal_objnum].pos);
10218
10219                 //      Move to within 0.1 units of second last point on path before orienting, or just plain far away from docked-to ship.
10220                 //      This allows undock to complete if first ship flies away.
10221                 if ((dist < 2*flFrametime) || (dist_to_dock_obj > 2*Pl_objp->radius)) {
10222                         aip->submode = AIS_UNDOCK_2;
10223                         aip->submode_start_time = Missiontime;
10224                 }
10225                 break;
10226                                                          }
10227         case AIS_UNDOCK_2: {
10228                 float dist;
10229                 ai_info *other_aip;
10230
10231                 // get pointer to docked object's aip to reset flags, etc
10232                 Assert( aip->dock_objnum != -1 );
10233                 other_aip = &Ai_info[Ships[Objects[aip->dock_objnum].instance].ai_index];
10234
10235                 //      Second stage of undocking.
10236                 dist = dock_orient_and_approach(Pl_objp, &Objects[aip->goal_objnum], DOA_UNDOCK_2);
10237                 Assert(dist != UNINITIALIZED_VALUE);
10238
10239
10240                 //nprintf(("AI", "Undock 2: dist = %7.3f\n", dist));
10241                 
10242                 //      If at goal point, or quite far away from dock object
10243                 if ((dist < 2.0f) || (vm_vec_dist_quick(&Pl_objp->pos, &goal_objp->pos) > (Pl_objp->radius + goal_objp->radius)*2) || (goal_objp->phys_info.speed > MAX_UNDOCK_ABORT_SPEED) ) {
10244                         // reset the dock flags.  If rearm/repair, reset rearm repair flags for those ships as well.
10245                         if ( sip->flags & SIF_SUPPORT ) {
10246                                 ai_do_objects_repairing_stuff( &Objects[aip->dock_objnum], Pl_objp, REPAIR_INFO_END );
10247                         }
10248
10249                         // clear out flags for AIF_DOCKED for both objects.
10250                         ai_do_objects_undocked_stuff( Pl_objp, goal_objp );
10251                         physics_ship_init(Pl_objp);
10252                         aip->submode = AIS_UNDOCK_3;                            //      The do-nothing mode, until another order is issued
10253
10254                         //aip->ai_flags &= ~AIF_DOCKED;         //      @MK, 9/18/97
10255                         //other_aip->ai_flags &= ~AIF_DOCKED;
10256                         //aip->dock_objnum = -1;                                        // invalidate who obj is docked with
10257                         //other_aip->dock_objnum = -1;                  // MWA 10/07/97 invalide docked objects dock_objnum value as well
10258
10259                         // don't add undock log entries for support ships.
10260                         if ( !(sip->flags & SIF_SUPPORT) )
10261                                 mission_log_add_entry(LOG_SHIP_UNDOCK, Ships[Pl_objp->instance].ship_name, Ships[goal_objp->instance].ship_name);
10262
10263                 }
10264                 break;
10265                 }
10266         case AIS_UNDOCK_3: {
10267                 float dist = dock_orient_and_approach(Pl_objp, &Objects[aip->goal_objnum], DOA_UNDOCK_3);
10268                 Assert(dist != UNINITIALIZED_VALUE);
10269
10270                 if (dist < Pl_objp->radius/2 + 5.0f) {
10271                         aip->submode = AIS_UNDOCK_4;
10272                 }
10273
10274                 // possible that this flag hasn't been cleared yet.  When aborting a rearm, this submode might
10275                 // be entered directly.
10276                 if ( (sip->flags & SIF_SUPPORT) && (aip->ai_flags & AIF_REPAIRING) ) {
10277                         ai_do_objects_repairing_stuff( &Objects[aip->goal_objnum], Pl_objp, REPAIR_INFO_ABORT );
10278                 }
10279
10280                 break;
10281                                                  }
10282         case AIS_UNDOCK_4: {
10283                 ai_info *other_aip;
10284
10285                 // MWA 10/07/97  I'm slightly confused by the dual use of goal_objnum and dock_objnum.  Seems to me
10286                 // that goal_objnum and dock_objnum are the same through this whole docking/undocking process, although
10287                 // I could be wrong.  dock_objnum was reset in undock_2 submode so try to use goal_objnum here to
10288                 // get other ships ai_info pointer
10289                 Assert( aip->goal_objnum != -1 );
10290                 other_aip = &Ai_info[Ships[Objects[aip->goal_objnum].instance].ai_index];
10291
10292                 aip->mode = AIM_NONE;
10293                 aip->dock_path_index = -1;              // invalidate the docking path index
10294
10295                 // these flags should have been cleared long ago!
10296                 // Get Allender if you hit one of these!!!!!
10297                 // removed by allender on 2/16 since a ship may be docked with some other ship, but still be the
10298                 // goal_objnum of this ship ending it's undocking mode.
10299                 //Assert( !(aip->ai_flags & AIF_DOCKED) );
10300                 //Assert( !(other_aip->ai_flags & AIF_DOCKED) );
10301                 //Assert( !(aip->ai_flags & AIF_REPAIRING) );
10302                 //Assert( !(other_aip->ai_flags & AIF_BEING_REPAIRED) );
10303                 //Assert( !(other_aip->ai_flags & AIF_AWAITING_REPAIR) );
10304
10305                 // only call mission goal complete if this was indeed an undock goal
10306                 if ( aip->active_goal > -1 ) {
10307                         if ( aip->goals[aip->active_goal].ai_mode == AI_GOAL_UNDOCK )
10308                                 ai_mission_goal_complete( aip );                        // this call should reset the AI mode
10309                         //else
10310                         //      aip->active_goal = -1;                                          // this ensures that this ship might get new goal
10311                 }
10312
10313                 break;
10314                                                          }
10315         default:
10316                 Int3(); //      Error, bogus submode
10317         }
10318
10319 }
10320
10321 // TURRET BEGIN
10322
10323 //      Given an object and a turret on that object, return the global position and forward vector
10324 //      of the turret.   The gun normal is the unrotated gun normal, (the center of the FOV cone), not
10325 // the actual gun normal given using the current turret heading.  But it _is_ rotated into the model's orientation
10326 //      in global space.
10327 void ship_get_global_turret_info(object *objp, model_subsystem *tp, vector *gpos, vector *gvec)
10328 {
10329         matrix  m;
10330         vm_copy_transpose_matrix(&m, &objp->orient);
10331 //      vm_vec_rotate(gpos, &tp->turret_avg_firing_point, &m);
10332         vm_vec_rotate(gpos, &tp->pnt, &m);
10333         vm_vec_add2(gpos, &objp->pos);
10334         vm_vec_rotate(gvec, &tp->turret_norm, &m);      
10335 }
10336
10337 // Given an object and a turret on that object, return the actual firing point of the gun
10338 // and its normal.   This uses the current turret angles.  We are keeping track of which
10339 // gun to fire next in the ship specific info for this turret subobject.  Use this info
10340 // to determine which position to fire from next.
10341 //      Stuffs:
10342 //              *gpos: absolute position of gun firing point
10343 //              *gvec: vector fro *gpos to *targetp
10344 void ship_get_global_turret_gun_info(object *objp, ship_subsys *ssp, vector *gpos, vector *gvec, int use_angles, vector *targetp)
10345 {
10346         vector * gun_pos;
10347         model_subsystem *tp = ssp->system_info;
10348
10349         ship_model_start(objp);
10350
10351         gun_pos = &tp->turret_firing_point[ssp->turret_next_fire_pos % tp->turret_num_firing_points];
10352
10353         model_find_world_point(gpos, gun_pos, tp->model_num, tp->turret_gun_sobj, &objp->orient, &objp->pos );
10354
10355         if (use_angles)
10356                 model_find_world_dir(gvec, &tp->turret_norm, tp->model_num, tp->turret_gun_sobj, &objp->orient, &objp->pos );
10357         else {
10358                 //vector        gun_pos2;
10359                 //vm_vec_add(&gun_pos2, gpos, gun_pos);
10360                 vm_vec_normalized_dir(gvec, targetp, gpos);
10361         }
10362
10363         ship_model_stop(objp);  
10364 }
10365
10366 //      Rotate a turret towards an enemy.
10367 //      Return TRUE if caller should use angles in subsequent rotations.
10368 //      Some obscure model thing only John Slagel knows about.
10369 //      Sets predicted enemy position.
10370 //      If the turret (*ss) has a subsystem targeted, the subsystem is used as the predicted point.
10371 int aifft_rotate_turret(ship *shipp, int parent_objnum, ship_subsys *ss, object *objp, object *lep, vector *predicted_enemy_pos, vector *gvec)
10372 {
10373         if (ss->turret_enemy_objnum != -1)      {
10374                 model_subsystem *tp = ss->system_info;
10375                 vector  gun_pos, gun_vec;
10376                 float           weapon_speed;
10377                 float           weapon_system_strength;
10378
10379                 //      weapon_system_strength scales time enemy in range in 0..1.  So, the lower this is, the worse the aiming will be.
10380                 weapon_system_strength = ship_get_subsystem_strength(shipp, SUBSYSTEM_WEAPONS);
10381
10382                 ship_get_global_turret_info(&Objects[parent_objnum], tp, &gun_pos, &gun_vec);
10383
10384                 weapon_speed = Weapon_info[tp->turret_weapon_type].max_speed;
10385                 float weapon_travel_dist = weapon_speed * Weapon_info[tp->turret_weapon_type].lifetime;
10386
10387                 vector  enemy_point;
10388                 if (ss->targeted_subsys != NULL) {
10389                         if (ss->turret_enemy_objnum != -1) {
10390                                 vm_vec_unrotate(&enemy_point, &ss->targeted_subsys->system_info->pnt, &Objects[ss->turret_enemy_objnum].orient);
10391                                 vm_vec_add2(&enemy_point, &Objects[ss->turret_enemy_objnum].pos);
10392                         }
10393                 } else {
10394                         if ((lep->type == OBJ_SHIP) && (Ship_info[Ships[lep->instance].ship_info_index].flags & (SIF_BIG_SHIP | SIF_HUGE_SHIP))) {
10395                                 ai_big_pick_attack_point_turret(lep, ss, &gun_pos, &gun_vec, &enemy_point, tp->turret_fov, min(weapon_travel_dist, Weapon_info[tp->turret_weapon_type].weapon_range));
10396                         } else {
10397                                 enemy_point = lep->pos;
10398                         }
10399                 }
10400
10401                 set_predicted_enemy_pos_turret(predicted_enemy_pos, &gun_pos, objp, &enemy_point, &lep->phys_info.vel, weapon_speed, ss->turret_time_enemy_in_range * (weapon_system_strength + 1.0f)/2.0f);
10402
10403                 if (weapon_system_strength < 0.7f) {
10404                         vector  rand_vec;
10405
10406                         static_randvec(Missiontime >> 18, &rand_vec);   //      Return same random number for two seconds.
10407                         //      Add to predicted_enemy_pos value in .45 to 1.5x radius of enemy ship, so will often miss, but not by a huge amount.
10408                         vm_vec_scale_add2(predicted_enemy_pos, &rand_vec, (1.0f - weapon_system_strength)*1.5f * lep->radius);
10409                 }
10410
10411                 vector  v2e;
10412                 vm_vec_normalized_dir(&v2e, predicted_enemy_pos, &gun_pos);
10413                 if (vm_vec_dot(&v2e, gvec) > tp->turret_fov) {
10414                         int     rval;
10415
10416                         rval = model_rotate_gun(shipp->modelnum, ss->system_info, &Objects[parent_objnum].orient, 
10417                                 &ss->submodel_info_1.angs, &ss->submodel_info_2.angs,
10418                                 &Objects[parent_objnum].pos, predicted_enemy_pos);
10419                 }
10420         }
10421
10422         return 0;
10423 }
10424
10425 //      Determine if subsystem *enemy_subsysp is hittable from objp.
10426 //      If so, return dot product of vector from point abs_gunposp to *enemy_subsysp
10427 float   aifft_compute_turret_dot(object *objp, object *enemy_objp, vector *abs_gunposp, ship_subsys *turret_subsysp, ship_subsys *enemy_subsysp)
10428 {
10429         float   dot_out;
10430         vector  subobj_pos, vector_out;
10431
10432         vm_vec_unrotate(&subobj_pos, &enemy_subsysp->system_info->pnt, &enemy_objp->orient);
10433         vm_vec_add2(&subobj_pos, &enemy_objp->pos);
10434
10435         if (ship_subsystem_in_sight(enemy_objp, enemy_subsysp, abs_gunposp, &subobj_pos, 1, &dot_out, &vector_out)) {
10436                 vector  turret_norm;
10437
10438                 vm_vec_rotate(&turret_norm, &turret_subsysp->system_info->turret_norm, &objp->orient);
10439                 return vm_vec_dot(&turret_norm, &vector_out);
10440         } else
10441                 return -1.0f;
10442
10443 }
10444
10445 #define MAX_AIFFT_TURRETS                       60
10446 ship_subsys *aifft_list[MAX_AIFFT_TURRETS];
10447 float aifft_rank[MAX_AIFFT_TURRETS];
10448 int aifft_list_size = 0;
10449 int aifft_max_checks = 5;
10450 DCF(mf, "")
10451 {
10452         dc_get_arg(ARG_INT);
10453         aifft_max_checks = Dc_arg_int;
10454 }
10455
10456
10457 //      Pick a subsystem to attack on enemy_objp.
10458 //      Only pick one if enemy_objp is a big ship or a capital ship.
10459 //      Returns dot product from turret to subsystem in *dot_out
10460 ship_subsys *aifft_find_turret_subsys(object *objp, ship_subsys *ssp, object *enemy_objp, float *dot_out)
10461 {
10462         ship    *eshipp, *shipp;
10463         ship_info       *esip;
10464         ship_subsys     *best_subsysp = NULL;
10465         float dot;
10466
10467         Assert(enemy_objp->type == OBJ_SHIP);
10468
10469         eshipp = &Ships[enemy_objp->instance];
10470         esip = &Ship_info[eshipp->ship_info_index];
10471
10472         shipp = &Ships[objp->instance];
10473
10474         float   best_dot = 0.0f;
10475         *dot_out = best_dot;
10476
10477         //      Compute absolute gun position.
10478         vector  abs_gun_pos;
10479         vm_vec_unrotate(&abs_gun_pos, &ssp->system_info->pnt, &objp->orient);
10480         vm_vec_add2(&abs_gun_pos, &objp->pos);
10481
10482         //      Only pick a turret to attack on large ships.
10483         if (!(esip->flags & (SIF_BIG_SHIP | SIF_HUGE_SHIP)))
10484                 return best_subsysp;
10485
10486         // Make sure big or huge ship *actually* has subsystems  (ie, knossos)
10487         if (esip->n_subsystems == 0) {
10488                 return best_subsysp;
10489         }
10490
10491         // first build up a list subsystems to traverse
10492         ship_subsys     *pss;
10493         aifft_list_size = 0;
10494         for ( pss = GET_FIRST(&eshipp->subsys_list); pss !=END_OF_LIST(&eshipp->subsys_list); pss = GET_NEXT(pss) ) {
10495                 model_subsystem *psub = pss->system_info;
10496
10497                 // if we've reached max turrets bail
10498                 if(aifft_list_size >= MAX_AIFFT_TURRETS){
10499                         break;
10500                 }
10501
10502                 // Don't process destroyed objects
10503                 if ( pss->current_hits <= 0.0f ){
10504                         continue;
10505                 }
10506                 
10507                 switch (psub->type) {
10508                 case SUBSYSTEM_WEAPONS:
10509                         aifft_list[aifft_list_size] = pss;
10510                         aifft_rank[aifft_list_size++] = 1.4f;
10511                         break;
10512
10513                 case SUBSYSTEM_TURRET:
10514                         aifft_list[aifft_list_size] = pss;
10515                         aifft_rank[aifft_list_size++] = 1.2f;
10516                         break;
10517
10518                 case SUBSYSTEM_SENSORS:
10519                 case SUBSYSTEM_ENGINE:
10520                         aifft_list[aifft_list_size] = pss;
10521                         aifft_rank[aifft_list_size++] = 1.0f;
10522                         break;
10523                 }
10524         }
10525
10526         // DKA:  6/28/99 all subsystems can be destroyed.
10527         //Assert(aifft_list_size > 0);
10528         if (aifft_list_size == 0) {
10529                 return best_subsysp;
10530         }
10531
10532         // determine a stride value so we're not checking too many turrets
10533         int stride = aifft_list_size > aifft_max_checks ? aifft_list_size / aifft_max_checks : 1;
10534         if(stride <= 0){
10535                 stride = 1;
10536         }
10537         int offset = (int)frand_range(0.0f, (float)(aifft_list_size % stride));
10538         int idx;
10539         for(idx=offset; idx<aifft_list_size; idx+=stride){
10540                 dot = aifft_compute_turret_dot(objp, enemy_objp, &abs_gun_pos, ssp, aifft_list[idx]);                   
10541
10542                 if (dot* aifft_rank[idx] > best_dot) {
10543                         best_dot = dot*aifft_rank[idx];
10544                         best_subsysp = aifft_list[idx];
10545                 }
10546         }
10547
10548         Assert(best_subsysp != &eshipp->subsys_list);
10549
10550         *dot_out = best_dot;
10551         return best_subsysp;
10552 }
10553
10554 // Set active weapon for turret
10555 void ai_turret_select_default_weapon(ship_subsys *turret)
10556 {
10557         ship_weapon *twp;
10558
10559         twp = &turret->weapons;
10560
10561         // If a primary weapon is available, select it
10562         if ( twp->num_primary_banks > 0 ) {
10563                 turret->system_info->turret_weapon_type = twp->primary_bank_weapons[0];
10564         } else if ( twp->num_secondary_banks > 0 ) {
10565                 turret->system_info->turret_weapon_type = twp->secondary_bank_weapons[0];
10566         }
10567 }
10568
10569 // return !0 if the specified target should scan for a new target, otherwise return 0
10570 int turret_should_pick_new_target(ship_subsys *turret)
10571 {
10572 //      int target_type;
10573
10574         if ( timestamp_elapsed(turret->turret_next_enemy_check_stamp) ) {
10575                 return 1;
10576         }
10577
10578         return 0;
10579
10580 /*
10581         if ( turret->turret_enemy_objnum == -1 ) {
10582                 return 1;
10583         }
10584                 
10585         target_type = Objects[turret->turret_enemy_objnum].type;
10586         if ( (target_type != OBJ_SHIP) && (target_type != OBJ_ASTEROID) ) {
10587                 return 1;
10588         }
10589
10590         return 0;
10591 */
10592 }
10593
10594 // Set the next fire timestamp for a turret, based on weapon type and ai class
10595 void turret_set_next_fire_timestamp(ship_subsys *turret, ai_info *aip)
10596 {
10597         float   wait;
10598         int     weapon_id;
10599
10600         weapon_id = turret->system_info->turret_weapon_type;
10601
10602         wait = Weapon_info[weapon_id].fire_wait * 1000.0f;
10603
10604         // make side even for team vs. team
10605         if ((Game_mode & GM_MULTIPLAYER) && (Netgame.type_flags & NG_TYPE_TEAM)) {
10606                 // flak guns need to fire more rapidly
10607                 if (Weapon_info[weapon_id].wi_flags & WIF_FLAK) {
10608                         wait *= Ship_fire_delay_scale_friendly[Game_skill_level] * 0.5f;
10609                         wait += (Num_ai_classes - aip->ai_class - 1) * 40.0f;
10610                 } else {
10611                         wait *= Ship_fire_delay_scale_friendly[Game_skill_level];
10612                         wait += (Num_ai_classes - aip->ai_class - 1) * 100.0f;
10613                 }
10614         } else {
10615                 // flak guns need to fire more rapidly
10616                 if (Weapon_info[weapon_id].wi_flags & WIF_FLAK) {
10617                         if (Ships[aip->shipnum].team == TEAM_FRIENDLY) {
10618                                 wait *= Ship_fire_delay_scale_friendly[Game_skill_level] * 0.5f;
10619                         } else {
10620                                 wait *= Ship_fire_delay_scale_hostile[Game_skill_level] * 0.5f;
10621                         }       
10622                         wait += (Num_ai_classes - aip->ai_class - 1) * 40.0f;
10623
10624                 } else if (Weapon_info[weapon_id].wi_flags & WIF_HUGE) {
10625                         // make huge weapons fire independently of team
10626                         wait *= Ship_fire_delay_scale_friendly[Game_skill_level];
10627                         wait += (Num_ai_classes - aip->ai_class - 1) * 100.0f;
10628                 } else {
10629                         // give team friendly an advantage
10630                         if (Ships[aip->shipnum].team == TEAM_FRIENDLY) {
10631                                 wait *= Ship_fire_delay_scale_friendly[Game_skill_level];
10632                         } else {
10633                                 wait *= Ship_fire_delay_scale_hostile[Game_skill_level];
10634                         }       
10635                         wait += (Num_ai_classes - aip->ai_class - 1) * 100.0f;
10636                 }
10637         }
10638
10639         // vary wait time +/- 10%
10640         wait *= frand_range(0.9f, 1.1f);
10641         turret->turret_next_fire_stamp = timestamp((int) wait);
10642 }
10643
10644 // Decide  if a turret should launch an aspect seeking missile
10645 int turret_should_fire_aspect(ship_subsys *turret, float dot, int weapon_class)
10646 {
10647         weapon_info *wip;
10648
10649         wip = &Weapon_info[weapon_class];
10650
10651         if ( (dot > AICODE_TURRET_DUMBFIRE_ANGLE) && (turret->turret_time_enemy_in_range >= min(wip->min_lock_time,AICODE_TURRET_MAX_TIME_IN_RANGE)) ) {
10652                 return 1;
10653         }
10654
10655         return 0;
10656 }
10657
10658 // Update how long current target has been in this turrets range
10659 void turret_update_enemy_in_range(ship_subsys *turret, float seconds)
10660 {
10661         turret->turret_time_enemy_in_range += seconds;
10662
10663         if ( turret->turret_time_enemy_in_range < 0.0f ) {
10664                 turret->turret_time_enemy_in_range = 0.0f;
10665         }
10666
10667         if ( turret->turret_time_enemy_in_range > AICODE_TURRET_MAX_TIME_IN_RANGE ) {
10668                 turret->turret_time_enemy_in_range = AICODE_TURRET_MAX_TIME_IN_RANGE;
10669         }
10670 }
10671
10672
10673
10674 // Fire a weapon from a turret
10675 void turret_fire_weapon(ship_subsys *turret, int parent_objnum, vector *turret_pos, vector *turret_fvec, vector *predicted_pos = NULL)
10676 {
10677         matrix  turret_orient;
10678         int             turret_weapon_class, weapon_objnum;
10679         ai_info *parent_aip;
10680         ship            *parent_ship;
10681         beam_fire_info fire_info;
10682         float flak_range = 0.0f;
10683
10684         parent_aip = &Ai_info[Ships[Objects[parent_objnum].instance].ai_index];
10685         parent_ship = &Ships[Objects[parent_objnum].instance];
10686         turret_weapon_class = turret->system_info->turret_weapon_type;
10687
10688         if (check_ok_to_fire(parent_objnum, turret->turret_enemy_objnum, &Weapon_info[turret_weapon_class])) {
10689                 vm_vector_2_matrix(&turret_orient, turret_fvec, NULL, NULL);
10690                 turret->turret_last_fire_direction = *turret_fvec;
10691
10692                 // set next fire timestamp for the turret
10693                 turret_set_next_fire_timestamp(turret, parent_aip);
10694
10695                 // if this weapon is a beam weapon, handle it specially
10696                 if(Weapon_info[turret_weapon_class].wi_flags & WIF_BEAM){
10697                         // if this beam isn't free to fire
10698                         if (!(turret->weapons.flags & SW_FLAG_BEAM_FREE)) {
10699                                 Int3(); // should never get this far
10700                                 return;
10701                         }
10702
10703                         // stuff beam firing info
10704                         memset(&fire_info, 0, sizeof(beam_fire_info));
10705                         fire_info.accuracy = 1.0f;
10706                         fire_info.beam_info_index = turret_weapon_class;
10707                         fire_info.beam_info_override = NULL;
10708                         fire_info.shooter = &Objects[parent_objnum];
10709                         fire_info.target = &Objects[turret->turret_enemy_objnum];
10710                         fire_info.target_subsys = NULL;
10711                         fire_info.turret = turret;
10712
10713                         // fire a beam weapon
10714                         beam_fire(&fire_info);
10715                 } else {
10716
10717                         // don't fire swarm, but set up swarm info
10718                         if (Weapon_info[turret_weapon_class].wi_flags & WIF_SWARM) {
10719                                 turret_swarm_set_up_info(parent_objnum, turret, turret_weapon_class);
10720                                 return;
10721                         } else {
10722                                 weapon_objnum = weapon_create( turret_pos, &turret_orient, turret_weapon_class, parent_objnum, 0, -1, 1);
10723                                 weapon_set_tracking_info(weapon_objnum, parent_objnum, turret->turret_enemy_objnum, 1, turret->targeted_subsys);                
10724                         }
10725
10726                         //nprintf(("AI", "Turret_time_enemy_in_range = %7.3f\n", ss->turret_time_enemy_in_range));              
10727                         if (weapon_objnum != -1) {
10728                                 Weapons[Objects[weapon_objnum].instance].target_num = turret->turret_enemy_objnum;
10729                                 // AL 1-6-97: Store pointer to turret subsystem
10730                                 Weapons[Objects[weapon_objnum].instance].turret_subsys = turret;
10731
10732                                 if ( Weapon_info[turret_weapon_class].launch_snd != -1 ) {
10733                                         // Don't play turret firing sound if turret sits on player ship... it gets annoying.
10734                                         if ( parent_objnum != OBJ_INDEX(Player_obj) ) {                                         
10735                                                 snd_play_3d( &Snds[Weapon_info[turret_weapon_class].launch_snd], turret_pos, &View_position );                                          
10736                                         }
10737                                 }               
10738
10739                                 // if the gun is a flak gun
10740                                 if(Weapon_info[turret_weapon_class].wi_flags & WIF_FLAK){                       
10741                                         // show a muzzle flash
10742                                         flak_muzzle_flash(turret_pos, turret_fvec, turret_weapon_class);
10743
10744                                         // pick a firing range so that it detonates properly                    
10745                                         flak_pick_range(&Objects[weapon_objnum], predicted_pos, ship_get_subsystem_strength(parent_ship, SUBSYSTEM_WEAPONS));
10746
10747                                         // determine what that range was
10748                                         flak_range = flak_get_range(&Objects[weapon_objnum]);
10749                                 }
10750
10751                                 // in multiplayer (and the master), then send a turret fired packet.
10752                                 if ( MULTIPLAYER_MASTER && (weapon_objnum != -1) ) {
10753                                         int subsys_index;
10754
10755                                         subsys_index = ship_get_index_from_subsys(turret, parent_objnum );
10756                                         Assert( subsys_index != -1 );
10757                                         if(Weapon_info[turret_weapon_class].wi_flags & WIF_FLAK){                       
10758                                                 send_flak_fired_packet( parent_objnum, subsys_index, weapon_objnum, flak_range );
10759                                         } else {
10760                                                 send_turret_fired_packet( parent_objnum, subsys_index, weapon_objnum );
10761                                         }
10762                                 }
10763                         }
10764                 }
10765         } else {
10766                 float wait = 1000.0f * frand_range(0.9f, 1.1f);
10767                 turret->turret_next_fire_stamp = timestamp((int) wait);
10768         }
10769 }
10770
10771 void turret_swarm_fire_from_turret(ship_subsys *turret, int parent_objnum, int target_objnum, ship_subsys *target_subsys)
10772 {
10773         int turret_weapon_class, weapon_objnum;
10774         matrix turret_orient;
10775         vector turret_pos, turret_fvec;
10776
10777         // parent not alive, quick out.
10778         if (Objects[parent_objnum].type != OBJ_SHIP) {
10779                 return;
10780         }
10781
10782         //      change firing point
10783         ship_get_global_turret_gun_info(&Objects[parent_objnum], turret, &turret_pos, &turret_fvec, 1, NULL);
10784         turret->turret_next_fire_pos++;
10785
10786         // get class [index into Weapon_info array
10787         turret_weapon_class = turret->system_info->turret_weapon_type;
10788         Assert(Weapon_info[turret_weapon_class].wi_flags & WIF_SWARM);
10789
10790         // make turret_orient from turret_fvec -- turret->turret_last_fire_direction
10791         vm_vector_2_matrix(&turret_orient, &turret_fvec, NULL, NULL);
10792
10793         // create weapon and homing info
10794         weapon_objnum = weapon_create(&turret_pos, &turret_orient, turret_weapon_class, parent_objnum, 0, -1, 1);
10795         weapon_set_tracking_info(weapon_objnum, parent_objnum, target_objnum, 1, target_subsys);
10796
10797         // do other cool stuff if weapon is created.
10798         if (weapon_objnum > -1) {
10799                 Weapons[Objects[weapon_objnum].instance].turret_subsys = turret;
10800                 Weapons[Objects[weapon_objnum].instance].target_num = turret->turret_enemy_objnum;
10801
10802                 // maybe sound
10803                 if ( Weapon_info[turret_weapon_class].launch_snd != -1 ) {
10804                         // Don't play turret firing sound if turret sits on player ship... it gets annoying.
10805                         if ( parent_objnum != OBJ_INDEX(Player_obj) ) {
10806                                 snd_play_3d( &Snds[Weapon_info[turret_weapon_class].launch_snd], &turret_pos, &View_position );
10807                         }
10808                 }
10809                 
10810                 // in multiplayer (and the master), then send a turret fired packet.
10811                 if ( MULTIPLAYER_MASTER && (weapon_objnum != -1) ) {
10812                         int subsys_index;
10813
10814                         subsys_index = ship_get_index_from_subsys(turret, parent_objnum );
10815                         Assert( subsys_index != -1 );
10816                         send_turret_fired_packet( parent_objnum, subsys_index, weapon_objnum );
10817                 }
10818         }
10819 }
10820
10821 int Num_ai_firing = 0;
10822 int Num_find_turret_enemy = 0;
10823 int Num_turrets_fired = 0;
10824 //      Given a turret tp and its parent parent_objnum, fire from the turret at its enemy.
10825 void ai_fire_from_turret(ship *shipp, ship_subsys *ss, int parent_objnum)
10826 {
10827         float           weapon_firing_range;
10828         vector  v2e;
10829         object  *lep;           //      Last enemy pointer
10830         model_subsystem *tp = ss->system_info;
10831         int             use_angles, turret_weapon_class;
10832         vector  predicted_enemy_pos;
10833         object  *objp;
10834         ai_info *aip;
10835
10836         if (!Ai_firing_enabled) {
10837                 return;
10838         }
10839
10840         if (ss->current_hits < 0.0f) {
10841                 return;
10842         }
10843
10844         if ( ship_subsys_disrupted(ss) ){               // AL 1/19/98: Make sure turret isn't suffering disruption effects
10845                 return;
10846         }
10847
10848         // Check turret free
10849         if (ss->weapons.flags & SW_FLAG_TURRET_LOCK) {
10850                 return;
10851         }
10852
10853         // If beam weapon, check beam free
10854         if ( (Weapon_info[tp->turret_weapon_type].wi_flags & WIF_BEAM) && !(ss->weapons.flags & SW_FLAG_BEAM_FREE) ) {
10855                 return;
10856         }
10857
10858         Assert( shipp->objnum == parent_objnum );
10859
10860         if ( tp->turret_weapon_type < 0 ){
10861                 return;
10862         }
10863
10864         // Monitor number of calls to ai_fire_from_turret
10865         Num_ai_firing++;
10866
10867         turret_weapon_class = tp->turret_weapon_type;
10868
10869         // AL 09/14/97: ensure ss->turret_enemy_objnum != -1 before setting lep
10870         if ( (ss->turret_enemy_objnum >= 0 && ss->turret_enemy_objnum < MAX_OBJECTS) && (ss->turret_enemy_sig == Objects[ss->turret_enemy_objnum].signature)) {
10871                 lep = &Objects[ss->turret_enemy_objnum];
10872
10873                 // MK -- here is where turret is targeting a bomb.  I simply return for now.  We should force
10874                 // a target change -- or better yet, never pick a weapon when this turret has a "huge" weapon
10875                 // loaded.
10876
10877                 // we only care about targets which are ships.
10878                 //if ( lep->type != OBJ_SHIP )
10879                 //      return;
10880
10881                 //      If targeted a small ship and have a huge weapon, don't fire.  But this shouldn't happen, as a small ship should not get selected.
10882                 if ( Weapon_info[turret_weapon_class].wi_flags & WIF_HUGE ) {
10883                         if ( lep->type != OBJ_SHIP ) {
10884                                 return;
10885                         }
10886                         if ( !(Ship_info[Ships[lep->instance].ship_info_index].flags & (SIF_BIG_SHIP | SIF_HUGE_SHIP)) ) {
10887                                 return;
10888                         }
10889                 }
10890
10891                 // If targeting protected or beam protected ship, don't fire.  Reset enemy objnum
10892                 if (lep->type == OBJ_SHIP) {
10893                         // Check if we're targeting a protected ship
10894                         if (lep->flags & OF_PROTECTED) {
10895                                 ss->turret_enemy_objnum = -1;
10896                                 ss->turret_time_enemy_in_range = 0.0f;
10897                                 return;
10898                         }
10899
10900                         // Check if we're targeting a beam protected ship with a beam weapon
10901                         if ( (lep->flags & OF_BEAM_PROTECTED) && (Weapon_info[turret_weapon_class].wi_flags & WIF_BEAM) ) {
10902                                 ss->turret_enemy_objnum = -1;
10903                                 ss->turret_time_enemy_in_range = 0.0f;
10904                                 return;
10905                         }
10906                 }
10907         } else {
10908                 ss->turret_enemy_objnum = -1;
10909                 lep = NULL;
10910         }
10911         
10912         Assert((parent_objnum >= 0) && (parent_objnum < MAX_OBJECTS));
10913         objp = &Objects[parent_objnum];
10914         Assert(objp->type == OBJ_SHIP);
10915         aip = &Ai_info[Ships[objp->instance].ai_index];
10916
10917         // Use the turret info for all guns, not one gun in particular.
10918         vector   gvec, gpos;
10919         ship_get_global_turret_info(&Objects[parent_objnum], tp, &gpos, &gvec);
10920
10921         // Rotate the turret even if time hasn't elapsed, since it needs to turn to face its target.
10922         use_angles = aifft_rotate_turret(shipp, parent_objnum, ss, objp, lep, &predicted_enemy_pos, &gvec);
10923
10924         if ( !timestamp_elapsed(ss->turret_next_fire_stamp)){
10925                 return;
10926         }
10927
10928         // Don't try to fire beyond weapon_limit_range
10929         weapon_firing_range = min(Weapon_info[tp->turret_weapon_type].lifetime * Weapon_info[tp->turret_weapon_type].max_speed, Weapon_info[tp->turret_weapon_type].weapon_range);
10930
10931         // if beam weapon in nebula and target not tagged, decrase firing range
10932         extern int Nebula_sec_range;
10933         if (Weapon_info[turret_weapon_class].wi_flags & WIF_BEAM) {
10934                 if ( !((shipp->tag_left > 0) || (shipp->level2_tag_left > 0)) ) {
10935                         if (Nebula_sec_range) {
10936                                 weapon_firing_range *= float(BEAM_NEBULA_RANGE_REDUCE_FACTOR);
10937                         }
10938                 }
10939         }
10940
10941         if (ss->turret_enemy_objnum != -1) {
10942                 float dist_to_enemy = vm_vec_normalized_dir(&v2e, &predicted_enemy_pos, &gpos) - lep->radius;
10943                 if (dist_to_enemy > weapon_firing_range) {
10944                         ss->turret_enemy_objnum = -1;           //      Force picking of new enemy.
10945                 }
10946         }
10947
10948         // Turret spawn weapons are a special case.  They fire if there are enough enemies in the 
10949         // immediate area (not necessarily in the turret fov).
10950         if ( Weapon_info[turret_weapon_class].wi_flags & WIF_SPAWN ) {
10951                 int num_ships_nearby;
10952                 num_ships_nearby = num_nearby_fighters(get_enemy_team_mask(parent_objnum), &gpos, 1500.0f);
10953                 if (( num_ships_nearby >= 3 ) || ((num_ships_nearby >= 2) && (frand() < 0.1f))) {
10954                         turret_fire_weapon(ss, parent_objnum, &gpos, &ss->turret_last_fire_direction);
10955                 } else {
10956                         ss->turret_next_fire_stamp = timestamp(1000);   //      Regardless of firing rate, don't check whether should fire for awhile.
10957                 }
10958                 return;
10959         }
10960
10961         //      Maybe pick a new enemy.
10962         if ( turret_should_pick_new_target(ss) ) {
10963                 Num_find_turret_enemy++;
10964                 int objnum = find_turret_enemy(ss, parent_objnum, &gpos, &gvec, ss->turret_enemy_objnum, tp->turret_fov, Weapon_info[turret_weapon_class].wi_flags & WIF_HUGE);
10965                 Assert(objnum < 0 || is_target_beam_valid(ss, objnum));
10966
10967                 if (objnum != -1) {
10968                         if (ss->turret_enemy_objnum == -1) {
10969                                 ss->turret_enemy_objnum = objnum;
10970                                 ss->turret_enemy_sig = Objects[objnum].signature;
10971                                 // why return?
10972                                 return;
10973                         } else {
10974                                 ss->turret_enemy_objnum = objnum;
10975                                 ss->turret_enemy_sig = Objects[objnum].signature;
10976                         }
10977                 } else {
10978                         ss->turret_enemy_objnum = -1;
10979                 }
10980
10981                 if (ss->turret_enemy_objnum != -1) {
10982                         float   dot = 1.0f;
10983                         lep = &Objects[ss->turret_enemy_objnum];
10984                         if ( lep->type == OBJ_SHIP ) {
10985                                 ss->targeted_subsys = aifft_find_turret_subsys(objp, ss, lep, &dot);                            
10986                         }
10987                         ss->turret_next_enemy_check_stamp = timestamp((int) (max(dot, 0.5f)*2000.0f) + 1000);
10988                 } else {
10989                         ss->turret_next_enemy_check_stamp = timestamp((int) (2000.0f * frand_range(0.9f, 1.1f)));       //      Check every two seconds
10990                 }
10991         }
10992
10993         //      If still don't have an enemy, return.  Or, if enemy is protected, return.
10994         if (ss->turret_enemy_objnum != -1) {
10995                 //      Don't shoot at ship we're going to dock with.
10996                 if (ss->turret_enemy_objnum == aip->dock_objnum) {
10997                         ss->turret_enemy_objnum = -1;
10998                         return;
10999                 }
11000
11001                 if (Objects[ss->turret_enemy_objnum].flags & OF_PROTECTED) {
11002                         //      This can happen if the enemy was selected before it became protected.
11003                         ss->turret_enemy_objnum = -1;
11004                         return;
11005                 }
11006                 lep = &Objects[ss->turret_enemy_objnum];
11007         } else {
11008                 if (timestamp_until(ss->turret_next_fire_stamp) < 500) {
11009                         ss->turret_next_fire_stamp = timestamp(500);
11010                 }
11011                 return;
11012         }
11013
11014         if ( lep == NULL ){
11015                 return;
11016         }
11017
11018         Assert(ss->turret_enemy_objnum != -1);
11019
11020         float dot = vm_vec_dot(&v2e, &gvec);
11021
11022         if (dot > tp->turret_fov ) {
11023                 // Ok, the turret is lined up... now line up a particular gun.
11024                 int ok_to_fire = 0;
11025                 float dist_to_enemy;
11026
11027                 // We're ready to fire... now get down to specifics, like where is the
11028                 // actual gun point and normal, not just the one for whole turret.
11029                 ship_get_global_turret_gun_info(&Objects[parent_objnum], ss, &gpos, &gvec, use_angles, &predicted_enemy_pos);
11030                 ss->turret_next_fire_pos++;
11031
11032                 // Fire in the direction the turret is facing, not right at the target regardless of turret dir.
11033                 vm_vec_sub(&v2e, &predicted_enemy_pos, &gpos);
11034                 dist_to_enemy = vm_vec_normalize(&v2e);
11035                 dot = vm_vec_dot(&v2e, &gvec);
11036
11037                 // if the weapon is a flak gun, add some jitter to its aim so it fires in a "cone" to make a cool visual effect
11038                 // and make them less lethal
11039                 if(Weapon_info[turret_weapon_class].wi_flags & WIF_FLAK){
11040                         flak_jitter_aim(&v2e, dist_to_enemy, ship_get_subsystem_strength(shipp, SUBSYSTEM_WEAPONS));
11041                 }
11042
11043                 // Fire if:
11044                 //              dumbfire and nearly pointing at target.
11045                 //              heat seeking and target in a fairly wide cone.
11046                 //              aspect seeking and target is locked.
11047                 turret_weapon_class = tp->turret_weapon_type;
11048
11049                 // if dumbfire (lasers and non-homing missiles)
11050                 if ( !(Weapon_info[turret_weapon_class].wi_flags & WIF_HOMING) ) {
11051                         if ((dist_to_enemy < 75.0f) || (dot > AICODE_TURRET_DUMBFIRE_ANGLE )) {
11052                                 turret_update_enemy_in_range(ss, 2*Weapon_info[turret_weapon_class].fire_wait);
11053                                 ok_to_fire = 1;
11054                         }
11055                 } else if ( Weapon_info[turret_weapon_class].wi_flags & WIF_HOMING_HEAT ) {     // if heat seekers
11056                         if ((dist_to_enemy < 50.0f) || (dot > AICODE_TURRET_HEATSEEK_ANGLE )) {
11057                                 turret_update_enemy_in_range(ss, 2*Weapon_info[turret_weapon_class].fire_wait);
11058                                 ok_to_fire = 1;
11059                         }
11060                 } else if ( Weapon_info[turret_weapon_class].wi_flags & WIF_HOMING_ASPECT ) {   // if aspect seeker
11061                         if ((dist_to_enemy < 50.0f) || (dot > AICODE_TURRET_DUMBFIRE_ANGLE )) {
11062                                 turret_update_enemy_in_range(ss, 2*Weapon_info[turret_weapon_class].fire_wait);
11063                         }
11064                         if ( turret_should_fire_aspect(ss, dot, turret_weapon_class) ) {
11065                                 ok_to_fire = 1;
11066                         }
11067                 }
11068
11069                 if ( ok_to_fire ) {
11070                         Num_turrets_fired++;
11071                         
11072                         turret_fire_weapon(ss, parent_objnum, &gpos, &v2e, &predicted_enemy_pos);                                               
11073                 } else {
11074                         turret_update_enemy_in_range(ss, -4*Weapon_info[tp->turret_weapon_type].fire_wait);
11075                         ss->turret_next_fire_stamp = timestamp(500);
11076                 }
11077         } else {
11078                 // Lost him!
11079                 ss->turret_enemy_objnum = -1;           //      Reset enemy objnum, find a new one next frame.
11080                 ss->turret_time_enemy_in_range = 0.0f;
11081         }
11082 }
11083
11084 // TURRET END
11085
11086 #ifndef NDEBUG
11087 #define MAX_AI_DEBUG_RENDER_STUFF       100
11088 typedef struct ai_render_stuff {
11089         ship_subsys     *ss;
11090         int                     parent_objnum;
11091 } ai_render_stuff;
11092
11093 ai_render_stuff AI_debug_render_stuff[MAX_AI_DEBUG_RENDER_STUFF];
11094
11095 int     Num_AI_debug_render_stuff = 0;
11096
11097 void ai_debug_render_stuff()
11098 {
11099         vertex  vert1, vert2;
11100         vector  gpos2;
11101         int             i;
11102
11103         for (i=0; i<Num_AI_debug_render_stuff; i++) {
11104                 ship_subsys     *ss;
11105                 int     parent_objnum;
11106                 vector  gpos, gvec;
11107                 model_subsystem *tp;
11108
11109                 ss = AI_debug_render_stuff[i].ss;
11110                 tp = ss->system_info;
11111
11112                 parent_objnum = AI_debug_render_stuff[i].parent_objnum;
11113
11114                 ship_get_global_turret_info(&Objects[parent_objnum], tp, &gpos, &gvec);
11115                 g3_rotate_vertex(&vert1, &gpos);
11116                 vm_vec_scale_add(&gpos2, &gpos, &gvec, 20.0f);
11117                 g3_rotate_vertex(&vert2, &gpos2);
11118                 gr_set_color(0, 0, 255);
11119                 g3_draw_sphere(&vert1, 2.0f);
11120                 gr_set_color(255, 0, 255);
11121                 g3_draw_sphere(&vert2, 2.0f);
11122                 g3_draw_line(&vert1, &vert2);
11123         }
11124
11125         // draw from beta to its goal point
11126 /*      for (i=0; i<6; i++) {
11127                 ai_info *aip = &Ai_info[i];
11128                 gr_set_color(0, 0, 255);
11129                 g3_rotate_vertex(&vert1, &Objects[i].pos);
11130                 g3_rotate_vertex(&vert2, &aip->goal_point);
11131                 g3_draw_line(&vert1, &vert2);
11132         } */
11133         
11134
11135         Num_AI_debug_render_stuff = 0;
11136 }
11137
11138 #endif
11139
11140 #ifndef NDEBUG
11141 int     Msg_count_4996 = 0;
11142 #endif
11143
11144 //      --------------------------------------------------------------------------
11145 // Process subobjects of object objnum.
11146 //      Deal with engines disabled.
11147 void process_subobjects(int objnum)
11148 {
11149         model_subsystem *psub;
11150         ship_subsys     *pss;
11151         object  *objp = &Objects[objnum];
11152         ship            *shipp = &Ships[objp->instance];
11153         ai_info *aip = &Ai_info[shipp->ai_index];
11154         ship_info       *sip = &Ship_info[shipp->ship_info_index];
11155
11156         for ( pss = GET_FIRST(&shipp->subsys_list); pss !=END_OF_LIST(&shipp->subsys_list); pss = GET_NEXT(pss) ) {
11157                 psub = pss->system_info;
11158
11159                 // Don't process destroyed objects
11160                 if ( pss->current_hits <= 0.0f ) 
11161                         continue;
11162
11163                 switch (psub->type) {
11164                 case SUBSYSTEM_TURRET:
11165                         if ( psub->turret_num_firing_points > 0 )       {
11166                                 ai_fire_from_turret(shipp, pss, objnum);
11167                         } else {
11168 #ifndef NDEBUG
11169                                 if (!Msg_count_4996) {
11170                                         Warning( LOCATION, "Ship '%s' has turrets with no guns!\nProbably a model problem, so get an artist!", shipp->ship_name );
11171                                         Msg_count_4996++;
11172                                 }
11173 #endif
11174                                 }
11175                         break;
11176
11177                 case SUBSYSTEM_ENGINE:
11178                 case SUBSYSTEM_NAVIGATION:
11179                 case SUBSYSTEM_COMMUNICATION:
11180                 case SUBSYSTEM_WEAPONS:
11181                 case SUBSYSTEM_SENSORS:
11182                 case SUBSYSTEM_UNKNOWN:
11183                         break;
11184
11185                 // next set of subsystems may rotation
11186                 case SUBSYSTEM_RADAR:
11187                 case SUBSYSTEM_SOLAR:
11188                 case SUBSYSTEM_GAS_COLLECT:
11189                 case SUBSYSTEM_ACTIVATION:
11190                         break;
11191                 default:
11192                         Error(LOCATION, "Illegal subsystem type.\n");
11193                 }
11194
11195                 // do solar/radar/gas/activator rotation here
11196                 if ( psub->flags & MSS_FLAG_ROTATES )   {
11197                         if (psub->flags & MSS_FLAG_STEPPED_ROTATE       ) {
11198                                 submodel_stepped_rotate(psub, &pss->submodel_info_1);
11199                         } else {
11200                                 submodel_rotate(psub, &pss->submodel_info_1 );
11201                         }
11202                 }
11203
11204         }
11205
11206         //      Deal with a ship with blown out engines.
11207         if (ship_get_subsystem_strength(shipp, SUBSYSTEM_ENGINE) == 0.0f) {
11208                 if (sip->flags & (SIF_FIGHTER | SIF_BOMBER)) {
11209                         // AL: Only attack forever if not trying to depart to a docking bay.  Need to have this in, since
11210                         //     a ship may get repaired... and it should still try to depart.  Since docking bay departures
11211                         //     are not handled as goals, we don't want to leave the AIM_BAY_DEPART mode.
11212                         if ( aip->mode != AIM_BAY_DEPART ) {
11213                                 ai_attack_object(objp, NULL, 99, NULL);         //      Regardless of current mode, enter attack mode.
11214                                 aip->submode = SM_ATTACK_FOREVER;                               //      Never leave attack submode, don't avoid, evade, etc.
11215                         }
11216                 }
11217         }
11218
11219
11220 }
11221
11222 //      Given an object and the wing it's in, return its index in the wing list.
11223 //      This defines its location in the wing formation.
11224 //      If the object can't be found in the wing, return -1.
11225 //      *objp           object of interest
11226 //      wingnum the wing *objp is in
11227 int get_wing_index(object *objp, int wingnum)
11228 {
11229         wing    *wingp;
11230         int     i;
11231
11232         Assert((wingnum >= 0) && (wingnum < MAX_WINGS));
11233
11234         wingp = &Wings[wingnum];
11235
11236         for (i=wingp->current_count-1; i>=0; i--)
11237                 if ( objp->instance == wingp->ship_index[i] )
11238                         break;
11239
11240         return i;               //      Note, returns -1 if string not found.
11241 }
11242
11243 //      Given a wing, return a pointer to the object of its leader.
11244 //      Asserts if object not found.
11245 //      Currently, the wing leader is defined as the first object in the wing.
11246 //      wingnum         Wing number in Wings array.
11247 //      If wing leader is disabled, swap it with another ship.
11248 object * get_wing_leader(int wingnum)
11249 {
11250         wing            *wingp;
11251         int             ship_num;
11252
11253         Assert((wingnum >= 0) && (wingnum < MAX_WINGS));
11254
11255         wingp = &Wings[wingnum];
11256
11257         Assert(wingp->current_count != 0);                      //      Make sure there is a leader
11258
11259         ship_num = wingp->ship_index[0];
11260
11261         //      If this ship is disabled, try another ship in the wing.
11262         int n = 0;
11263         while (ship_get_subsystem_strength(&Ships[ship_num], SUBSYSTEM_ENGINE) == 0.0f) {
11264                 n++;
11265                 if (n >= wingp->current_count)
11266                         break;  
11267                 ship_num = wingp->ship_index[n];
11268         }
11269
11270         if (( n != 0) && (n != wingp->current_count)) {
11271                 int t = wingp->ship_index[0];
11272                 wingp->ship_index[0] = wingp->ship_index[n];
11273                 wingp->ship_index[n] = t;
11274         }
11275
11276         return &Objects[Ships[ship_num].objnum];
11277 }
11278
11279 #define DEFAULT_WING_X_DELTA            1.0f
11280 #define DEFAULT_WING_Y_DELTA            0.25f
11281 #define DEFAULT_WING_Z_DELTA            0.75f
11282 #define DEFAULT_WING_MAG                (fl_sqrt(DEFAULT_WING_X_DELTA*DEFAULT_WING_X_DELTA + DEFAULT_WING_Y_DELTA*DEFAULT_WING_Y_DELTA + DEFAULT_WING_Z_DELTA*DEFAULT_WING_Z_DELTA))
11283 // next constant is higher that MAX_SHIPS_IN_WINGS to deal with forming on player's wing
11284 #define MAX_FORMATION_ROWS              4
11285
11286 //      Given a position in a wing, return the desired location of the ship relative to the leader
11287 //      *_delta_vec             OUTPUT.  delta vector based on wing_index
11288 //      wing_index              position in wing.
11289 void get_wing_delta(vector *_delta_vec, int wing_index)
11290 {
11291         int     wi0;
11292
11293         Assert(wing_index >= 0);
11294
11295         int     k, row, column;
11296
11297         int bank = wing_index / (MAX_FORMATION_ROWS*(MAX_FORMATION_ROWS+1)/2);
11298         wi0 = wing_index % (MAX_FORMATION_ROWS * (MAX_FORMATION_ROWS+1)/2);
11299
11300         k = 0;
11301         for (row=1; row<MAX_FORMATION_ROWS+1; row++) {
11302                 k += row;
11303                 if (wi0 < k)
11304                         break;
11305         }
11306
11307         row--;
11308         column = wi0 - k + row + 1;
11309
11310         _delta_vec->x = ((float) column - (float) row/2.0f) * DEFAULT_WING_X_DELTA/DEFAULT_WING_MAG;
11311         _delta_vec->y = ((float)row + (float)bank*2.25f) * DEFAULT_WING_Y_DELTA/DEFAULT_WING_MAG;
11312         _delta_vec->z = - ((float)row + 0.5f * (float) bank) * DEFAULT_WING_Z_DELTA/DEFAULT_WING_MAG;
11313         
11314 }
11315
11316 //      Compute the largest radius of a ship in a *objp's wing.
11317 float gwlr_1(object *objp, ai_info *aip)
11318 {
11319         int             wingnum = aip->wing;
11320         float           max_radius;
11321         object  *o;
11322         ship_obj        *so;
11323
11324         Assert(wingnum >= 0);
11325
11326         max_radius = objp->radius;
11327
11328         for ( so = GET_FIRST(&Ship_obj_list); so != END_OF_LIST(&Ship_obj_list); so = GET_NEXT(so) ) {
11329                 o = &Objects[so->objnum];
11330                 if (Ai_info[Ships[o->instance].ai_index].wing == wingnum)
11331                         if (o->radius > max_radius)
11332                                 max_radius = o->radius;
11333         }
11334
11335         return max_radius;
11336 }
11337
11338 //      Compute the largest radius of a ship forming on *objp's wing.
11339 float gwlr_object_1(object *objp, ai_info *aip)
11340 {
11341         float           max_radius;
11342         object  *o;
11343         ship_obj        *so;
11344
11345         max_radius = objp->radius;
11346
11347         for ( so = GET_FIRST(&Ship_obj_list); so != END_OF_LIST(&Ship_obj_list); so = GET_NEXT(so) ) {
11348                 o = &Objects[so->objnum];
11349                 if (Ai_info[Ships[o->instance].ai_index].goal_objnum == OBJ_INDEX(objp))
11350                         if (o->radius > max_radius)
11351                                 max_radius = o->radius;
11352         }
11353
11354         return max_radius;
11355 }
11356
11357 //      For the wing that *objp is part of, return the largest ship radius in that wing.
11358 float get_wing_largest_radius(object *objp, int formation_object_flag)
11359 {
11360         ship            *shipp;
11361         ai_info *aip;
11362
11363         Assert(objp->type == OBJ_SHIP);
11364         Assert((objp->instance >= 0) && (objp->instance < MAX_OBJECTS));
11365         shipp = &Ships[objp->instance];
11366         Assert((shipp->ai_index >= 0) && (shipp->ai_index < MAX_AI_INFO));
11367         aip = &Ai_info[shipp->ai_index];
11368
11369         if (formation_object_flag) {
11370                 return gwlr_object_1(objp, aip);
11371         } else {
11372                 return gwlr_1(objp, aip);
11373         }
11374
11375 }
11376
11377 float Wing_y_scale = 2.0f;
11378 float Wing_scale = 1.0f;
11379 DCF(wing_y_scale, "")
11380 {
11381         dc_get_arg(ARG_FLOAT);
11382         Wing_y_scale = Dc_arg_float;
11383 }
11384
11385 DCF(wing_scale, "")
11386 {
11387         dc_get_arg(ARG_FLOAT);
11388         Wing_scale = Dc_arg_float;
11389 }
11390
11391 // Given a wing leader and a position in the wing formation, return the desired absolute location to fly to.
11392 //      Returns result in *result_pos.
11393 void get_absolute_wing_pos(vector *result_pos, object *leader_objp, int wing_index, int formation_object_flag)
11394 {
11395         vector  wing_delta, rotated_wing_delta;
11396         float           wing_spread_size;
11397
11398         get_wing_delta(&wing_delta, wing_index);                //      Desired location in leader's reference frame
11399
11400         wing_spread_size = max(50.0f, 3.0f * get_wing_largest_radius(leader_objp, formation_object_flag) + 15.0f);
11401
11402         // for player obj (1) move ships up 20% (2) scale formation up 20%
11403         if (leader_objp->flags & OF_PLAYER_SHIP) {
11404                 wing_delta.y *= Wing_y_scale;
11405                 wing_spread_size *= Wing_scale;
11406         }
11407
11408         vm_vec_scale(&wing_delta, wing_spread_size * (1.0f + leader_objp->phys_info.speed/70.0f));
11409
11410         vm_vec_unrotate(&rotated_wing_delta, &wing_delta, &leader_objp->orient);        //      Rotate into leader's reference.
11411
11412         vm_vec_add(result_pos, &leader_objp->pos, &rotated_wing_delta); //      goal_point is absolute 3-space point.
11413 }
11414
11415 #ifndef NDEBUG
11416 int Debug_render_wing_phantoms;
11417
11418 void render_wing_phantoms(object *objp)
11419 {
11420         int             i;
11421         ship            *shipp;
11422         ai_info *aip;
11423         int             wingnum;
11424         int             wing_index;             //      Index in wing struct, defines 3-space location in wing.
11425         vector  goal_point;
11426         
11427         Assert(objp->type == OBJ_SHIP);
11428         Assert((objp->instance >= 0) && (objp->instance < MAX_SHIPS));
11429
11430         shipp = &Ships[objp->instance];
11431         Assert((shipp->ai_index >= 0) && (shipp->ai_index < MAX_AI_INFO));
11432
11433         aip = &Ai_info[shipp->ai_index];
11434
11435         wingnum = aip->wing;
11436
11437         if (wingnum == -1)
11438                 return;
11439
11440         wing_index = get_wing_index(objp, wingnum);
11441
11442         //      If this ship is NOT the leader, abort.
11443         if (wing_index != 0)
11444                 return;
11445
11446         for (i=0; i<32; i++)
11447                 if (Debug_render_wing_phantoms & (1 << i)) {
11448                         get_absolute_wing_pos(&goal_point, objp, i, 0);
11449         
11450                         vertex  vert;
11451                         gr_set_color(255, 0, 128);
11452                         g3_rotate_vertex(&vert, &goal_point);
11453                         g3_draw_sphere(&vert, 2.0f);
11454                 }
11455
11456         Debug_render_wing_phantoms = 0;
11457
11458 }
11459
11460 void render_wing_phantoms_all()
11461 {
11462         object  *objp;
11463         ship_obj        *so;
11464
11465         for ( so = GET_FIRST(&Ship_obj_list); so != END_OF_LIST(&Ship_obj_list); so = GET_NEXT(so) ) {
11466                 ship            *shipp;
11467                 ai_info *aip;
11468                 int             wingnum;
11469                 int             wing_index;             //      Index in wing struct, defines 3-space location in wing.
11470
11471                 objp = &Objects[so->objnum];
11472                 
11473                 Assert((objp->instance >= 0) && (objp->instance < MAX_SHIPS));
11474                 shipp = &Ships[objp->instance];
11475                 Assert((shipp->ai_index >= 0) && (shipp->ai_index < MAX_AI_INFO));
11476
11477                 aip = &Ai_info[shipp->ai_index];
11478
11479                 wingnum = aip->wing;
11480
11481                 if (wingnum == -1)
11482                         continue;
11483
11484                 wing_index = get_wing_index(objp, wingnum);
11485
11486                 //      If this ship is NOT the leader, abort.
11487                 if (wing_index != 0)
11488                         continue;
11489                 
11490                 render_wing_phantoms(objp);
11491
11492                 return;
11493         }
11494 }
11495
11496 #endif
11497
11498 //      Hook from goals code to AI.
11499 //      Force a wing to fly in formation.
11500 //      Sets AIF_FORMATION bit in ai_flags.
11501 //      wingnum         Wing to force to fly in formation
11502 void ai_fly_in_formation(int wingnum)
11503 {
11504         object  *objp;
11505         ship            *shipp;
11506         ship_obj        *so;
11507
11508         for ( so = GET_FIRST(&Ship_obj_list); so != END_OF_LIST(&Ship_obj_list); so = GET_NEXT(so) ) {
11509                 objp = &Objects[so->objnum];
11510                 Assert((objp->instance >= 0) && (objp->instance < MAX_SHIPS));
11511
11512                 shipp = &Ships[objp->instance];
11513                 Assert((shipp->ai_index >= 0) && (shipp->ai_index < MAX_AI_INFO));
11514
11515                 if (Ai_info[shipp->ai_index].wing == wingnum) {
11516                         Ai_info[shipp->ai_index].ai_flags |= AIF_FORMATION_WING;
11517                         Ai_info[shipp->ai_index].ai_flags &= ~AIF_FORMATION_OBJECT;
11518                 }
11519         }
11520 }
11521
11522 //      Hook from goals code to AI.
11523 //      Force a wing to abandon formation flying.
11524 //      Clears AIF_FORMATION bit in ai_flags.
11525 //      wingnum         Wing to force to fly in formation
11526 void ai_disband_formation(int wingnum)
11527 {
11528         object  *objp;
11529         ship            *shipp;
11530         ship_obj        *so;
11531
11532         for ( so = GET_FIRST(&Ship_obj_list); so != END_OF_LIST(&Ship_obj_list); so = GET_NEXT(so) ) {
11533                 objp = &Objects[so->objnum];
11534                 Assert((objp->instance >= 0) && (objp->instance < MAX_SHIPS));
11535
11536                 shipp = &Ships[objp->instance];
11537                 Assert((shipp->ai_index >= 0) && (shipp->ai_index < MAX_AI_INFO));
11538
11539                 if (Ai_info[shipp->ai_index].wing == wingnum) {
11540                         Ai_info[shipp->ai_index].ai_flags &= ~AIF_FORMATION_WING;
11541                 }
11542         }
11543 }
11544
11545 float   Leader_chaos = 0.0f;
11546 int Chaos_frame = -1;
11547
11548 //      Return true if objp is flying in an erratic manner
11549 //      Only true if objp is a player
11550 int formation_is_leader_chaotic(object *objp)
11551 {
11552         if (Game_mode & GM_MULTIPLAYER)
11553                 return 0;
11554
11555         if (objp != Player_obj)
11556                 return 0;
11557
11558         if (Framecount != Chaos_frame) {
11559                 float   speed_scale;
11560                 float   fdot, udot;
11561
11562                 speed_scale = 3.0f + objp->phys_info.speed * 0.1f;
11563
11564                 fdot = 5.0f * (1.0f - vm_vec_dot(&objp->orient.fvec, &objp->last_orient.fvec)) * flFrametime;
11565                 udot = 8.0f * (1.0f - vm_vec_dot(&objp->orient.uvec, &objp->last_orient.uvec)) * flFrametime;
11566
11567                 Leader_chaos += fdot * speed_scale + udot * speed_scale;
11568
11569                 Leader_chaos *= (1.0f - flFrametime*0.2f);
11570
11571                 if (Leader_chaos < 0.0f)
11572                         Leader_chaos = 0.0f;
11573                 else if (Leader_chaos > 1.7f)
11574                         Leader_chaos = 1.7f;
11575
11576                 //nprintf(("AI", "Frame %i: chaos = %7.4f\n", Framecount, Leader_chaos));
11577
11578                 Chaos_frame = Framecount;
11579         }
11580
11581         return (Leader_chaos > 1.0f);
11582 }
11583
11584 // Fly in formation.
11585 //      Make Pl_objp assume its proper place in formation.
11586 //      If the leader of the wing is doing something stupid, like fighting a battle,
11587 //      then the poor sap wingmates will be in for a "world of hurt"
11588 //      Return TRUE if we need to process this object's normal mode
11589 int ai_formation()
11590 {
11591         object  *leader_objp;
11592         ship            *shipp;
11593         ai_info *aip, *laip;
11594         int             wingnum;
11595         int             wing_index;             //      Index in wing struct, defines 3-space location in wing.
11596         int             player_wing;    // index of the players wingnum
11597         vector  goal_point, future_goal_point_5, future_goal_point_2, future_goal_point_x, future_goal_point_1000x, vec_to_goal, dir_to_goal;
11598         float           dot_to_goal, dist_to_goal, leader_speed;
11599
11600         Assert(Pl_objp->type == OBJ_SHIP);
11601         Assert((Pl_objp->instance >= 0) && (Pl_objp->instance < MAX_SHIPS));
11602
11603         shipp = &Ships[Pl_objp->instance];
11604
11605         Assert((shipp->ai_index >= 0) && (shipp->ai_index < MAX_AI_INFO));
11606
11607         aip = &Ai_info[shipp->ai_index];
11608
11609         Assert((aip->ai_flags & AIF_FORMATION) != AIF_FORMATION);       //      Make sure not both types of formation flying in effect.
11610
11611         //      Determine which kind of formation flying.
11612         //      If tracking an object, not in waypoint mode:
11613         if (aip->ai_flags & AIF_FORMATION_OBJECT) {
11614                 if ((aip->goal_objnum < 0) || (aip->goal_objnum >= MAX_OBJECTS)) {
11615                         aip->ai_flags &= ~AIF_FORMATION_OBJECT;
11616                         return 1;
11617                 }
11618                 
11619                 wing_index = ai_formation_object_get_slotnum(aip->goal_objnum, Pl_objp);
11620                 leader_objp = &Objects[aip->goal_objnum];
11621         } else {        //      Formation flying in waypoint mode.
11622                 Assert(aip->ai_flags & AIF_FORMATION_WING);
11623                 if (aip->mode != AIM_WAYPOINTS) {
11624                         aip->ai_flags &= ~AIF_FORMATION_WING;
11625                         return 1;
11626                 }
11627
11628                 wingnum = aip->wing;
11629
11630                 if (wingnum == -1)
11631                         return 1;
11632
11633                 // disable formation flying for any ship in the players wing
11634                 player_wing = Ships[Player_obj->instance].wingnum;
11635                 if ( (player_wing != -1) && (wingnum == player_wing) )
11636                         return 1;
11637
11638                 wing_index = get_wing_index(Pl_objp, wingnum);
11639
11640                 leader_objp = get_wing_leader(wingnum);
11641
11642         }
11643
11644         //      If docked with a ship in this wing, only the more massive one actually flies in formation.
11645         if (aip->dock_objnum != -1) {
11646                 object  *other_objp = &Objects[aip->dock_objnum];
11647                 ai_info *other_aip = &Ai_info[Ships[other_objp->instance].ai_index];
11648
11649                 if (aip->wing == other_aip->wing) {
11650                         if (Pl_objp->phys_info.mass < other_objp->phys_info.mass)
11651                                 return 0;
11652                         else if (Pl_objp->phys_info.mass == other_objp->phys_info.mass) {
11653                                 if (Pl_objp->signature < other_objp->signature)
11654                                         return 0;
11655                         }
11656                 }
11657         }
11658
11659         Assert(leader_objp != NULL);
11660         laip = &Ai_info[Ships[leader_objp->instance].ai_index];
11661
11662         //      Make sure we're really in this wing.
11663         if (wing_index == -1)
11664                 return 1;
11665
11666         //      If this ship is the leader, abort, as he doesn't have to follow anyone.
11667         if (wing_index == 0) {
11668                 // nprintf(("AI", "Hmm, wing leader %s in ai_formation for no good reason.\n", shipp->ship_name));
11669                 return 1;
11670         }
11671
11672         if (aip->mode == AIM_WAYPOINTS) {
11673                 aip->wp_list = laip->wp_list;
11674                 if (laip->wp_index < Waypoint_lists[laip->wp_list].count)
11675                         aip->wp_index = laip->wp_index;
11676                 else
11677                         aip->wp_index = Waypoint_lists[laip->wp_list].count - 1;
11678                 aip->wp_flags = laip->wp_flags;
11679                 aip->wp_dir = laip->wp_dir;
11680         }
11681
11682         #ifndef NDEBUG
11683         Debug_render_wing_phantoms |= (1 << wing_index);
11684         #endif
11685
11686         leader_speed = leader_objp->phys_info.speed;
11687         vector leader_vec = leader_objp->phys_info.vel;
11688
11689         get_absolute_wing_pos(&goal_point, leader_objp, wing_index, aip->ai_flags & AIF_FORMATION_OBJECT);
11690         vm_vec_scale_add(&future_goal_point_5, &goal_point, &leader_vec, 10.0f);
11691         vm_vec_scale_add(&future_goal_point_2, &goal_point, &leader_vec, 5.0f);
11692         vm_vec_scale_add(&future_goal_point_x, &goal_point, &leader_objp->orient.fvec, 10.0f);  //      used when very close to destination
11693         vm_vec_scale_add(&future_goal_point_1000x, &goal_point, &leader_objp->orient.fvec, 1000.0f);    //      used when very close to destination
11694
11695         //      Now, get information telling this object how to turn and accelerate to get to its
11696         //      desired location.
11697         vm_vec_sub(&vec_to_goal, &goal_point, &Pl_objp->pos);
11698         if ( vm_vec_mag_quick(&vec_to_goal) < AICODE_SMALL_MAGNITUDE )
11699                 vec_to_goal.x += 0.1f;
11700
11701         vm_vec_copy_normalize(&dir_to_goal, &vec_to_goal);
11702         //dot_to_goal = vm_vec_dot(&dir_to_goal, &leader_objp->orient.fvec);
11703         dot_to_goal = vm_vec_dot(&dir_to_goal, &Pl_objp->orient.fvec);
11704         dist_to_goal = vm_vec_dist_quick(&Pl_objp->pos, &goal_point);
11705         float   dist_to_goal_2 = vm_vec_dist_quick(&Pl_objp->pos, &future_goal_point_2);
11706
11707         // nprintf(("AI", "dot = %7.3f, dist = %8.3f, speed = %7.3f, leader speed = %7.3f\n", dot_to_goal, dist_to_goal, Pl_objp->phys_info.speed, leader_objp->phys_info.speed));
11708
11709         int     chaotic_leader = 0;
11710
11711         chaotic_leader = formation_is_leader_chaotic(leader_objp);      //      Set to 1 if leader is player and flying erratically.  Causes ships to not aggressively pursue formation location.
11712
11713         if (dist_to_goal > 500.0f) {
11714                 turn_towards_point(Pl_objp, &goal_point, NULL, 0.0f);
11715                 accelerate_ship(aip, 1.0f);
11716         } else if (dist_to_goal > 200.0f) {
11717                 if (dot_to_goal > -0.5f) {
11718                         turn_towards_point(Pl_objp, &goal_point, NULL, 0.0f);
11719                         float range_speed = shipp->current_max_speed - leader_speed;
11720                         if (range_speed > 0.0f)
11721                                 set_accel_for_target_speed(Pl_objp, leader_speed + range_speed * (dist_to_goal+100.0f)/500.0f);
11722                         else
11723                                 set_accel_for_target_speed(Pl_objp, shipp->current_max_speed);
11724                 } else {
11725                         turn_towards_point(Pl_objp, &future_goal_point_5, NULL, 0.0f);
11726                         if (leader_speed > 10.0f)
11727                                 set_accel_for_target_speed(Pl_objp, leader_speed *(1.0f + dot_to_goal));
11728                         else
11729                                 set_accel_for_target_speed(Pl_objp, 10.0f);
11730                 }
11731         } else {
11732                 vector  v2f2;
11733                 float   dot_to_f2;
11734                 float   dist_to_f2;
11735
11736                 dist_to_f2 = vm_vec_normalized_dir(&v2f2, &future_goal_point_2, &Pl_objp->pos);
11737                 dot_to_f2 = vm_vec_dot(&v2f2, &Pl_objp->orient.fvec);
11738
11739                 //      Leader flying like a maniac.  Don't try hard to form on wing.
11740                 if (chaotic_leader) {
11741                         turn_towards_point(Pl_objp, &future_goal_point_2, NULL, 0.0f);
11742                         set_accel_for_target_speed(Pl_objp, min(leader_speed*0.8f, 20.0f));
11743                 } else if (dist_to_goal > 75.0f) {
11744                         turn_towards_point(Pl_objp, &future_goal_point_2, NULL, 0.0f);
11745                         float   delta_speed;
11746                         float range_speed = shipp->current_max_speed - leader_speed;
11747                         if (range_speed > 0.0f)
11748                                 delta_speed = dist_to_goal_2/500.0f * range_speed;
11749                         else
11750                                 delta_speed = shipp->current_max_speed - leader_speed;
11751                         if (dot_to_goal < 0.0f) {
11752                                 delta_speed = -delta_speed;
11753                                 if (-delta_speed > leader_speed/2)
11754                                         delta_speed = -leader_speed/2;
11755                         }
11756
11757                         if (leader_speed < 5.0f)
11758                                 if (delta_speed < 5.0f)
11759                                         delta_speed = 5.0f;
11760
11761                         float scale = dot_to_f2;
11762                         if (scale < 0.1f)
11763                                 scale = 0.0f;
11764                         else
11765                                 scale *= scale;
11766
11767                         set_accel_for_target_speed(Pl_objp, scale * (leader_speed + delta_speed));
11768                 } else {
11769                         //nprintf(("AI", "Dot = %7.3f\n", dot_to_goal));
11770
11771                         if (leader_speed < 5.0f) {
11772                                 //      Leader very slow.  If not close to goal point, get very close.  Note, keep trying to get close unless
11773                                 //      moving very slowly, else momentum can carry far away from goal.
11774
11775                                 if ((dist_to_goal > 10.0f) || ((Pl_objp->phys_info.speed > leader_speed + 2.5f) && (dot_to_goal > 0.5f))) {
11776                                         //nprintf(("MK", "(1) "));
11777                                         turn_towards_point(Pl_objp, &goal_point, NULL, 0.0f);
11778                                         set_accel_for_target_speed(Pl_objp, leader_speed + dist_to_goal/10.0f);
11779                                 } else {
11780                                         if (Pl_objp->phys_info.speed < 0.5f) {
11781                                                 //nprintf(("MK", "(2) "));
11782                                                 turn_towards_point(Pl_objp, &future_goal_point_1000x, NULL, 0.0f);
11783                                         } else {
11784                                                 //nprintf(("MK", "(3) "));
11785                                         }
11786                                         set_accel_for_target_speed(Pl_objp, leader_speed);
11787                                 }
11788                                 //nprintf(("MK", "dist: %7.3f, dot: %6.3f, speed: %7.3f\n", dist_to_goal, dot_to_goal, Pl_objp->phys_info.speed));
11789                         } else if (dist_to_goal > 10.0f) {
11790                                 float   dv;
11791
11792                                 future_goal_point_2;
11793
11794                                 turn_towards_point(Pl_objp, &future_goal_point_2, NULL, 0.0f);
11795
11796                                 if (dist_to_goal > 25.0f) {
11797                                         if (dot_to_goal < 0.3f)
11798                                                 dv = -0.1f;
11799                                         else
11800                                                 dv = dot_to_goal - 0.2f;
11801
11802                                         set_accel_for_target_speed(Pl_objp, leader_speed + dist_to_goal/5.0f * dv);
11803                                 } else {
11804                                         set_accel_for_target_speed(Pl_objp, leader_speed + 1.5f * dot_to_goal - 1.0f);
11805                                 }
11806                         } else {
11807                                 if (Pl_objp->phys_info.speed < 0.1f)
11808                                         turn_towards_point(Pl_objp, &future_goal_point_1000x, NULL, 0.0f);
11809                                 else
11810                                         turn_towards_point(Pl_objp, &future_goal_point_x, NULL, 0.0f);
11811                                 set_accel_for_target_speed(Pl_objp, 0.0f);
11812                         }
11813                 }
11814
11815         }
11816
11817         //      See how different this ship's bank is relative to wing leader
11818         float   up_dot = vm_vec_dot(&leader_objp->orient.uvec, &Pl_objp->orient.uvec);
11819         if (up_dot < 0.996f) {
11820                 vector  w_out;
11821                 matrix  new_orient;
11822                 vector  angular_accel;
11823
11824                 vm_vec_copy_scale(&angular_accel, &Pl_objp->phys_info.max_rotvel, 0.2f);
11825                 vm_matrix_interpolate(&leader_objp->orient, &Pl_objp->orient, &Pl_objp->phys_info.rotvel, flFrametime, &new_orient, &w_out, &Pl_objp->phys_info.max_rotvel, &angular_accel, 1);
11826
11827         //      nprintf(("AI", "Frame %d Bashing formation orient.  Dot was %6.3f, becomes %6.3f\n", Framecount, up_dot, vm_vec_dot(&leader_objp->orient.uvec, &new_orient.uvec)));
11828                 Pl_objp->orient = new_orient;
11829                 Pl_objp->phys_info.rotvel = w_out;
11830         //      Pl_objp->phys_info.desired_rotvel = w_out;
11831         } else {
11832                 Pl_objp->phys_info.rotvel.z = 0.0f;
11833         }
11834
11835         return 0;
11836 }
11837
11838 //      Return index of object repairing object objnum.
11839 int find_repairing_objnum(int objnum)
11840 {
11841         object          *objp;
11842         ship                    *shipp;
11843         ship_info       *sip;
11844         ship_obj                *so;
11845
11846         for ( so = GET_FIRST(&Ship_obj_list); so != END_OF_LIST(&Ship_obj_list); so = GET_NEXT(so) ) {
11847                 objp = &Objects[so->objnum];
11848
11849                 Assert((objp->instance >= 0) && (objp->instance < MAX_SHIPS));
11850
11851                 shipp = &Ships[objp->instance];
11852                 sip = &Ship_info[shipp->ship_info_index];
11853
11854                 if (sip->flags & SIF_SUPPORT) {
11855                         ai_info *aip;
11856
11857                         aip = &Ai_info[shipp->ai_index];
11858
11859                         if (aip->goal_objnum == objnum) {
11860                                 return objp-Objects;
11861                         }
11862                 }
11863         }
11864
11865         return -1;
11866 }
11867
11868 //      If object *objp is being repaired, deal with it!
11869 void ai_do_repair_frame(object *objp, ai_info *aip, float frametime)
11870 {
11871         if (Ships[objp->instance].team == TEAM_TRAITOR) {
11872                 ai_abort_rearm_request(objp);
11873                 return;
11874         }
11875
11876         if (aip->ai_flags & (AIF_BEING_REPAIRED | AIF_AWAITING_REPAIR)) {
11877                 int     dock_objnum;
11878                 ai_info *repair_aip;
11879
11880                 dock_objnum = aip->dock_objnum; // find_repairing_objnum(objp-Objects);
11881                 //Assert(dock_objnum != -1);
11882                 if (dock_objnum == -1)
11883                         return;
11884                 if (Objects[dock_objnum].signature != aip->dock_signature) {
11885                         Int3();         //      Curious -- object numbers match, but signatures do not.
11886                                                         //      Must mean original repair ship died and was replaced by current ship.
11887                         return;
11888                 }
11889         
11890                 repair_aip = &Ai_info[Ships[Objects[dock_objnum].instance].ai_index];
11891                 //Assert(repair_aip->mode == AIM_DOCK);
11892
11893                 if (aip->ai_flags & AIF_BEING_REPAIRED) {
11894                         // Assert(repair_aip->submode == AIS_DOCK_4);
11895
11896                         //      Wait awhile into the mode to synchronize with sound effect.
11897                         if (Missiontime - repair_aip->submode_start_time > REARM_SOUND_DELAY) {
11898                                 int repaired;
11899
11900                                 repaired = ship_do_rearm_frame( objp, frametime );              // hook to do missile rearming
11901
11902                                 //      See if fully repaired.  If so, cause process to stop.
11903                                 if ( repaired && (repair_aip->submode == AIS_DOCK_4)) {
11904
11905                                         repair_aip->submode = AIS_UNDOCK_0;
11906                                         repair_aip->submode_start_time = Missiontime;
11907
11908                                         // if repairing player object -- tell him done with repair
11909                                         if ( !MULTIPLAYER_CLIENT ){
11910                                                 ai_do_objects_repairing_stuff( objp, &Objects[dock_objnum], REPAIR_INFO_COMPLETE );
11911                                         }
11912                                 }
11913                         }
11914                 } else if (aip->ai_flags & AIF_AWAITING_REPAIR) {
11915                         //      If this ship has been awaiting repair for 90+ seconds, abort.
11916                         if ( !MULTIPLAYER_CLIENT ) {
11917                                 if ((Game_mode & GM_MULTIPLAYER) || (objp != Player_obj)) {
11918                                         if ((repair_aip->goal_objnum == OBJ_INDEX(objp)) && (timestamp_elapsed(aip->abort_rearm_timestamp))) {
11919                                                 ai_abort_rearm_request(objp);
11920                                                 aip->next_rearm_request_timestamp = timestamp(NEXT_REARM_TIMESTAMP);
11921                                         }
11922                                 }
11923                         }
11924                 }
11925         } else {
11926                 // AL 11-24-97: If this is the player ship, ensure the repair sound isn't playing.  We need to
11927                 //              do this check, since this is a looping sound, and may continue on if rearm/repair
11928                 //              finishes abnormally once sound begins looping.
11929                 if ( objp == Player_obj ) {
11930                         player_stop_repair_sound();
11931                 }
11932         }
11933 }
11934
11935 //      Shell around dock_orient_and_approach to detect whether dock process should be aborted.
11936 //      obj1 is the ship performing the repair.
11937 //      obj2 is the ship being repaired.
11938 void call_doa(object *obj1, object *obj2, ship_info *sip1)
11939 {
11940         if (sip1->flags & SIF_SUPPORT) {
11941                 if (obj2->phys_info.speed > MAX_REPAIR_SPEED) {
11942
11943                         // call the ai_abort rearm request code
11944                         ai_abort_rearm_request( obj2 );
11945                 } else
11946                         dock_orient_and_approach(obj1, obj2, DOA_DOCK_STAY);
11947         } else {
11948                 if (Ship_info[Ships[obj1->instance].ship_info_index].flags & SIF_CARGO)
11949                         dock_orient_and_approach(obj1, obj2, DOA_DOCK_STAY);
11950                 else if (Ship_info[Ships[obj2->instance].ship_info_index].flags & SIF_CARGO)
11951                         dock_orient_and_approach(obj2, obj1, DOA_DOCK_STAY);
11952                 else {
11953                         //mprintf(("Warning: Not sure, but making %s [%s] move to stay docked with %s [%s]\n",
11954                         //      Ships[obj1->instance].ship_name, Ship_info[Ships[obj1->instance].ship_info_index].name, Ships[obj2->instance].ship_name, Ship_info[Ships[obj2->instance].ship_info_index].name));
11955                         dock_orient_and_approach(obj1, obj2, DOA_DOCK_STAY);
11956
11957                 }
11958         }
11959
11960 }
11961
11962 //      Maybe launch a countermeasure.
11963 //      Also, detect a supposed homing missile that no longer exists.
11964 void ai_maybe_launch_cmeasure(object *objp, ai_info *aip)
11965 {
11966         float                   dist;
11967         ship_info       *sip;
11968         ship                    *shipp;
11969
11970         shipp = &Ships[objp->instance];
11971         sip = &Ship_info[shipp->ship_info_index];
11972
11973         if (!(sip->flags & (SIF_SMALL_SHIP | SIF_TRANSPORT)))
11974                 return;
11975
11976         if (!shipp->cmeasure_count)
11977                 return;
11978
11979         if ( !timestamp_elapsed(shipp->cmeasure_fire_stamp) )
11980                 return;
11981
11982         //      If not on player's team and Skill_level + ai_class is low, never fire a countermeasure.  The ship is too dumb.
11983         if (shipp->team != Player_ship->team) {
11984                 if (Game_skill_level + aip->ai_class < 4){
11985                         return;
11986                 }
11987         }
11988
11989         if ((aip->nearest_locked_object != -1) && (Objects[aip->nearest_locked_object].type == OBJ_WEAPON)) {
11990                 object  *weapon_objp;
11991                 weapon  *weaponp;
11992                 weapon_info     *wip;
11993
11994                 weapon_objp = &Objects[aip->nearest_locked_object];
11995                 weaponp = &Weapons[weapon_objp->instance];
11996                 wip = &Weapon_info[weaponp->weapon_info_index];
11997
11998                 if ((dist = vm_vec_dist_quick(&objp->pos, &weapon_objp->pos)) < weapon_objp->phys_info.speed*2.0f) {
11999         
12000                         aip->nearest_locked_distance = dist;
12001                         //      Verify that this object is really homing on us.
12002                         object  *weapon_objp;
12003
12004                         weapon_objp = &Objects[aip->nearest_locked_object];
12005
12006                         float   fire_chance;
12007
12008                         //      For ships on player's team, have constant, average chance to fire.
12009                         //      For enemies, increasing chance with higher skill level.
12010                         if (shipp->team == Player_ship->team)
12011                                 fire_chance = Cmeasure_fire_chance[NUM_SKILL_LEVELS/2];
12012                         else
12013                                 fire_chance = Cmeasure_fire_chance[Game_skill_level];
12014
12015                         //      Decrease chance to fire at lower ai class.
12016                         fire_chance *= (float) aip->ai_class/Num_ai_classes;
12017
12018                         float r = frand();
12019                         if (fire_chance < r) {
12020                                 //nprintf(("AI", "Not firing countermeasure due to skill level: %7.3f < %7.3f\n", fire_chance, r));
12021                                 shipp->cmeasure_fire_stamp = timestamp(CMEASURE_WAIT + (int) (fire_chance*2000));               //      Wait 1/2 second (CMEASURE_WAIT) + additional delay to decrease chance of firing very soon.
12022                                 return;
12023                         }
12024
12025                         if (weapon_objp->type == OBJ_WEAPON) {
12026                                 if (weapon_objp->instance >= 0) {
12027                                         //nprintf(("AI", "Firing countermeasure at time t=%7.3f\n", f2fl(Missiontime)));
12028                                         ship_launch_countermeasure(objp);
12029                                         shipp->cmeasure_fire_stamp = timestamp(2*CMEASURE_WAIT);
12030                                         return;
12031                                 }
12032                         }
12033         
12034                 }
12035         }
12036
12037         return;
12038 }
12039
12040 //      --------------------------------------------------------------------------
12041 void ai_preprocess_ignore_objnum(object *objp, ai_info *aip)
12042 {
12043 //      if (aip->ignore_objnum == UNUSED_OBJNUM)
12044 //              return;
12045
12046         if (aip->ai_flags & AIF_TEMPORARY_IGNORE) {
12047                 if (timestamp_elapsed(aip->ignore_expire_timestamp)) {
12048                         aip->ignore_objnum = UNUSED_OBJNUM;
12049                 }
12050         }
12051
12052         if (is_ignore_object(aip, aip->goal_objnum)) {
12053                 aip->goal_objnum = -1;
12054                 // AL 12-11-97: If in STRAFE mode, we need to ensure that target_objnum is also
12055                 //              set to -1
12056                 if ( aip->mode == AIM_STRAFE ) {
12057                         aip->target_objnum = -1;
12058                 }
12059         }
12060
12061         if (is_ignore_object(aip, aip->target_objnum))
12062                 aip->target_objnum = -1;
12063 }
12064
12065 /*
12066 void ai_safety_circle_spot()
12067 {
12068         vector  goal_point;
12069         ship_info       *sip;
12070
12071         sip = &Ship_info[Ships[Pl_objp->instance].ship_info_index];
12072
12073         goal_point = Ai_info[Ships[Pl_objp->instance].ai_index].goal_point;
12074         turn_towards_tangent(Pl_objp, &goal_point, 50.0f);
12075
12076         set_accel_for_target_speed(Pl_objp, sip->max_speed/4.0f);
12077
12078 //      float dist = vm_vec_dist_quick(&goal_point, &Pl_objp->pos);
12079 //      nprintf(("AI", "Ship %s circling %7.3f %7.3f %7.3f.  Distance = %7.3f\n", Ships[Pl_objp->instance].ship_name, goal_point.x, goal_point.y, goal_point.z, dist));
12080
12081 }
12082 */
12083
12084 #define CHASE_CIRCLE_DIST               100.0f
12085
12086 void ai_chase_circle(object *objp)
12087 {
12088         float           dist_to_goal;
12089         float           target_speed;
12090         vector  goal_point;
12091         ship_info       *sip;
12092         ai_info         *aip;
12093
12094         sip = &Ship_info[Ships[Pl_objp->instance].ship_info_index];
12095
12096         target_speed = sip->max_speed/4.0f;
12097         aip = &Ai_info[Ships[Pl_objp->instance].ai_index];
12098
12099         Assert(vm_vec_mag(&aip->goal_point) >= 0.0f);           //      Supposedly detects bogus vector
12100
12101         goal_point = aip->goal_point;
12102
12103         if (aip->ignore_objnum == UNUSED_OBJNUM) {
12104                 dist_to_goal = vm_vec_dist_quick(&aip->goal_point, &objp->pos);
12105
12106                 if (dist_to_goal > 2*CHASE_CIRCLE_DIST) {
12107                         vector  vec_to_goal;
12108                         //      Too far from circle goal, create a new goal point.
12109                         vm_vec_normalized_dir(&vec_to_goal, &aip->goal_point, &objp->pos);
12110                         vm_vec_scale_add(&aip->goal_point, &objp->pos, &vec_to_goal, CHASE_CIRCLE_DIST);
12111                 }
12112
12113                 goal_point = aip->goal_point;
12114         } else if (is_ignore_object(aip, aip->ignore_objnum)) {
12115                 object  *ignore_objp = &Objects[aip->ignore_objnum];
12116
12117                 vector  tvec1;
12118                 float           dist;
12119
12120                 dist = vm_vec_normalized_dir(&tvec1, &Pl_objp->pos, &ignore_objp->pos);
12121
12122                 if (dist < ignore_objp->radius*2 + 1500.0f) {
12123                         vm_vec_scale_add(&goal_point, &Pl_objp->pos, &tvec1, ignore_objp->radius*2 + 1400.0f);
12124                         if (dist < ignore_objp->radius*2 + 1300.0f)
12125                                 target_speed = sip->max_speed * (1.25f - dist/(ignore_objp->radius*2 + 1500.0f));
12126                 }
12127         }
12128
12129         Assert(vm_vec_mag(&aip->goal_point) >= 0.0f);           //      Supposedly detects bogus vector
12130
12131         turn_towards_tangent(Pl_objp, &goal_point, 10*objp->radius + 200.0f);
12132
12133         set_accel_for_target_speed(Pl_objp, target_speed);
12134
12135 }
12136
12137 #define SHIELD_BALANCE_RATE     0.2f            //      0.1f -> takes 10 seconds to equalize shield.
12138
12139 //      Transfer shield energy to most recently hit section from others.
12140 void ai_transfer_shield(object *objp, int quadrant_num)
12141 {
12142         int     i;
12143         float   transfer_amount;
12144         float   transfer_delta;
12145         ship_info       *sip;
12146         float   max_quadrant_strength;
12147
12148         sip = &Ship_info[Ships[objp->instance].ship_info_index];
12149         max_quadrant_strength = sip->shields/MAX_SHIELD_SECTIONS;
12150
12151         transfer_amount = 0.0f;
12152         transfer_delta = (SHIELD_BALANCE_RATE/2) * max_quadrant_strength;
12153
12154         if (objp->shields[quadrant_num] + (MAX_SHIELD_SECTIONS-1)*transfer_delta > max_quadrant_strength)
12155                 transfer_delta = (max_quadrant_strength - objp->shields[quadrant_num])/(MAX_SHIELD_SECTIONS-1);
12156
12157         for (i=0; i<MAX_SHIELD_SECTIONS; i++)
12158                 if (i != quadrant_num) {
12159                         if (objp->shields[i] >= transfer_delta) {
12160                                 objp->shields[i] -= transfer_delta;
12161                                 transfer_amount += transfer_delta;
12162                         } else {
12163                                 transfer_amount += objp->shields[i];
12164                                 objp->shields[i] = 0.0f;
12165                         }
12166                 }
12167
12168         objp->shields[quadrant_num] += transfer_amount;
12169 }
12170
12171 void ai_balance_shield(object *objp)
12172 {
12173         int     i;
12174         float   shield_strength_avg;
12175         float   delta;
12176
12177
12178         shield_strength_avg = get_shield_strength(objp)/MAX_SHIELD_SECTIONS;
12179
12180         delta = SHIELD_BALANCE_RATE * shield_strength_avg;
12181
12182         for (i=0; i<MAX_SHIELD_SECTIONS; i++)
12183                 if (objp->shields[i] < shield_strength_avg) {
12184                         add_shield_strength(objp, delta);
12185                         if (objp->shields[i] > shield_strength_avg)
12186                                 objp->shields[i] = shield_strength_avg;
12187                 } else {
12188                         add_shield_strength(objp, -delta);
12189                         if (objp->shields[i] < shield_strength_avg)
12190                                 objp->shields[i] = shield_strength_avg;
12191                 }
12192 }
12193
12194 //      Manage the shield for this ship.
12195 //      Try to max out the side that was most recently hit.
12196 void ai_manage_shield(object *objp, ai_info *aip)
12197 {
12198         ship_info *sip;
12199
12200         sip = &Ship_info[Ships[objp->instance].ship_info_index];
12201
12202         if (timestamp_elapsed(aip->shield_manage_timestamp)) {
12203                 float           delay;
12204
12205                 //      Scale time until next manage shield based on Skill_level.
12206                 //      Ships on player's team are treated as if Skill_level is average.
12207                 if (Ships[objp->instance].team != Player_ship->team){
12208                         delay = Shield_manage_delays[Game_skill_level];
12209                 } else {
12210                         delay = Shield_manage_delays[NUM_SKILL_LEVELS/2];
12211                 }
12212
12213                 //      Scale between 1x and 3x based on ai_class
12214                 delay = delay + delay * (float) (3*(Num_ai_classes - aip->ai_class - 1) / (Num_ai_classes - 1));
12215                 aip->shield_manage_timestamp = timestamp((int) (delay * 1000.0f));
12216
12217                 if (sip->flags & SIF_SMALL_SHIP) {
12218                         if (Missiontime - aip->last_hit_time < F1_0*10)
12219                                 ai_transfer_shield(objp, aip->last_hit_quadrant);
12220                         else
12221                                 ai_balance_shield(objp);
12222                 }
12223
12224                 // nprintf(("AI", "Time: %7.3f Next: %7.3f, Shields: %7.3f %7.3f %7.3f %7.3f\n", f2fl(Missiontime), f2fl(Missiontime) + delay, objp->shields[0], objp->shields[1], objp->shields[2], objp->shields[3]));
12225         }
12226 }
12227
12228 //      See if object *objp should evade an incoming missile.
12229 //      *aip is the ai_info pointer within *objp.
12230 void ai_maybe_evade_locked_missile(object *objp, ai_info *aip)
12231 {
12232         ship                    *shipp;
12233         ship_info       *sip;
12234
12235         shipp = &Ships[objp->instance];
12236         sip = &Ship_info[shipp->ship_info_index];
12237
12238         //      Only small ships evade an incoming missile.  Why would a capital ship try to swerve?
12239         if (!(Ship_info[Ships[objp->instance].ship_info_index].flags & SIF_SMALL_SHIP)) {
12240                 return;
12241         }
12242
12243         if (aip->ai_flags & (AIF_NO_DYNAMIC | AIF_KAMIKAZE)) {  //      If not allowed to pursue dynamic objectives, don't evade.  Dumb?  Maybe change. -- MK, 3/15/98
12244                 return;
12245         }
12246
12247         if (aip->nearest_locked_object != -1) {
12248                 object  *missile_objp;
12249
12250                 missile_objp = &Objects[aip->nearest_locked_object];
12251
12252                 if (Weapons[missile_objp->instance].homing_object != objp) {
12253                         //nprintf(("AI", "\nMissile lost home!"));
12254                         aip->nearest_locked_object = -1;
12255                         return;
12256                 }
12257
12258                 if ((missile_objp->type == OBJ_WEAPON) && (Weapon_info[Weapons[missile_objp->instance].weapon_info_index].wi_flags & WIF_HOMING)) {
12259                         float dist = vm_vec_dist_quick(&missile_objp->pos, &objp->pos);
12260                         float dist2 = 4.0f  * vm_vec_mag_quick(&missile_objp->phys_info.vel);                   
12261                         if (dist < dist2) {
12262                                 switch (aip->mode) {
12263                                 //      If in AIM_STRAFE mode, don't evade if parent of weapon is targeted ship.
12264                                 case AIM_STRAFE:
12265                                         if ((missile_objp->parent != -1) && (missile_objp->parent == aip->target_objnum)) {
12266                                                 ;
12267                                         } else {
12268                                                 ;               //      Alan -- If you want to handle incoming weapons from someone other than the ship
12269                                                                 //      the strafing ship is attacking, do it here.
12270                                         }
12271                                         break;
12272                                 case AIM_CHASE:
12273                                         //      Don't always go into evade weapon mode.  Usually, a countermeasure gets launched.
12274                                         // If low on countermeasures, more likely to try to evade.  If 8+, never evade due to low cmeasures.
12275                                         if (((((Missiontime >> 18) ^ OBJ_INDEX(objp)) & 3) == 0) || 
12276                                                 (objp->phys_info.speed < 40.0f) ||
12277                                                 (frand() < 1.0f - (float) shipp->cmeasure_count/8.0f)) {
12278                                                 if (aip->submode != SM_ATTACK_FOREVER) {        //      SM_ATTACK_FOREVER means engines blown.
12279                                                         aip->submode = SM_EVADE_WEAPON;
12280                                                         aip->submode_start_time = Missiontime;
12281                                                 }
12282                                         }
12283                                         break;
12284                                 case AIM_DOCK:  //      Ships in dock mode can evade iif they are not currently repairing or docked.
12285                                         if (aip->ai_flags & (AIF_REPAIRING | AIF_DOCKED))
12286                                                 break;
12287                                 case AIM_GUARD:
12288                                         //      If in guard mode and far away from guard object, don't pursue guy that hit me.
12289                                         if ((aip->guard_objnum != -1) && (aip->guard_signature == Objects[aip->guard_objnum].signature)) {
12290                                                 if (vm_vec_dist_quick(&objp->pos, &Objects[aip->guard_objnum].pos) > 500.0f) {
12291                                                         return;
12292                                                 }
12293                                         }
12294                                 case AIM_EVADE:
12295                                 case AIM_GET_BEHIND:
12296                                 case AIM_STAY_NEAR:
12297                                 case AIM_STILL:
12298                                 case AIM_AVOID:
12299                                 case AIM_WAYPOINTS:
12300                                 case AIM_NONE:
12301                                 case AIM_BIGSHIP:
12302                                 case AIM_PATH:
12303                                 case AIM_BE_REARMED:
12304                                 case AIM_SAFETY:
12305                                 case AIM_BAY_EMERGE:
12306                                         aip->active_goal = AI_ACTIVE_GOAL_DYNAMIC;
12307                                         aip->previous_mode = aip->mode;
12308                                         aip->previous_submode = aip->submode;
12309                                         aip->mode = AIM_EVADE_WEAPON;
12310                                         aip->submode = -1;
12311                                         aip->submode_start_time = Missiontime;
12312                                         aip->mode_time = timestamp(MAX_EVADE_TIME);     //      Max time to evade.
12313                                         //nprintf(("AI", "%s Evade weapon in frame #%i\n", Ships[objp->instance].ship_name, AI_FrameCount));
12314                                         break;
12315                                 case AIM_EVADE_WEAPON:          //      Note: We don't want to change mode on another evasion, or previous_mode will get bashed.
12316                                 case AIM_PLAY_DEAD:
12317                                 case AIM_BAY_DEPART:
12318                                 case AIM_SENTRYGUN:
12319                                         break;
12320                                 case AIM_WARP_OUT:
12321                                         break;
12322                                 default:
12323                                         Int3();                 //      Hey, what mode is it?
12324                                         break;
12325                                 }
12326                         }
12327                 } else {
12328                         aip->nearest_locked_object = -1;
12329                 }
12330         }
12331 }
12332
12333 //      Maybe evade a dumbfire weapon that was fired when Pl_objp was targeted.
12334 //      Have an 80% chance of evading in a second
12335 void maybe_evade_dumbfire_weapon(ai_info *aip)
12336 {
12337         //      Only small ships evade an incoming missile.  Why would a capital ship try to swerve?
12338         if (!(Ship_info[Ships[Pl_objp->instance].ship_info_index].flags & SIF_SMALL_SHIP)) {
12339                 return;
12340         }
12341
12342         //      Make sure in a mode in which we evade dumbfire weapons.
12343         switch (aip->mode) {
12344         case AIM_CHASE:
12345                 if (aip->submode == SM_ATTACK_FOREVER) {
12346                         return;
12347                 }
12348         case AIM_GUARD:
12349                 //      If in guard mode and far away from guard object, don't pursue guy that hit me.
12350                 if ((aip->guard_objnum != -1) && (aip->guard_signature == Objects[aip->guard_objnum].signature)) {
12351                         if (vm_vec_dist_quick(&Objects[Ships[aip->shipnum].objnum].pos, &Objects[aip->guard_objnum].pos) > 500.0f) {
12352                                 return;
12353                         }
12354                 }
12355         case AIM_STILL:
12356         case AIM_STAY_NEAR:
12357         case AIM_EVADE:
12358         case AIM_GET_BEHIND:
12359         case AIM_AVOID:
12360         case AIM_PATH:
12361         case AIM_NONE:
12362         case AIM_WAYPOINTS:
12363         case AIM_SAFETY:
12364                 break;
12365         case AIM_STRAFE:
12366         case AIM_BIGSHIP:
12367         case AIM_DOCK:
12368         case AIM_PLAY_DEAD:
12369         case AIM_EVADE_WEAPON:
12370         case AIM_BAY_EMERGE:
12371         case AIM_BAY_DEPART:
12372         case AIM_SENTRYGUN:
12373         case AIM_WARP_OUT:
12374                 return;
12375         default:
12376                 Int3(); //      Bogus mode!
12377                 return;
12378         }
12379
12380         if (is_instructor(&Objects[Ships[aip->shipnum].objnum]))
12381                 return; //      Instructor doesn't evade.
12382
12383         float t = ai_endangered_by_weapon(aip);
12384         if ((t > 0.0f) && (t < 1.0f)) {
12385         // Check if this weapon is from a large ship Pl_objp is attacking... if so, enter strafe mode
12386                 if ( ai_big_maybe_enter_strafe_mode(Pl_objp, aip->danger_weapon_objnum) ) {
12387                         return;
12388                 }
12389
12390                 switch (aip->mode) {
12391                 case AIM_CHASE:
12392                         switch (aip->submode) {
12393                         case SM_EVADE:
12394                         case SM_ATTACK_FOREVER:
12395                         case SM_AVOID:
12396                         case SM_GET_AWAY:
12397                         case SM_EVADE_WEAPON:
12398                                 break;
12399                         default:
12400                                 if (ai_near_full_strength(Pl_objp, &Ship_info[Ships[Pl_objp->instance].ship_info_index])) {
12401                                         //mprintf(("Ship %s entered super mode at %7.3f\n", Ships[Pl_objp->instance].ship_name, 1.0f * Missiontime / (1<<16)));
12402                                         aip->submode = SM_SUPER_ATTACK;
12403                                         aip->submode_start_time = Missiontime;
12404                                         aip->last_attack_time = Missiontime;
12405                                 } else {
12406                                         //mprintf(("Ship %s entered dumbfire evade mode at %7.3f\n", Ships[Pl_objp->instance].ship_name, 1.0f * Missiontime / (1<<16)));
12407                                         aip->submode = SM_EVADE_WEAPON;
12408                                         aip->submode_start_time = Missiontime;
12409                                 }
12410                                 break;
12411                         }
12412                         break;
12413                 case AIM_GUARD:
12414                 case AIM_STILL:
12415                 case AIM_STAY_NEAR:
12416                 case AIM_EVADE:
12417                 case AIM_GET_BEHIND:
12418                 case AIM_AVOID:
12419                 case AIM_PATH:
12420                 case AIM_NONE:
12421                 case AIM_WAYPOINTS:
12422                 case AIM_SAFETY:
12423                         if (!(aip->ai_flags & (AIF_NO_DYNAMIC | AIF_KAMIKAZE)) && (Ship_info[Ships[aip->shipnum].ship_info_index].flags & SIF_SMALL_SHIP)) {
12424                                 aip->active_goal = AI_ACTIVE_GOAL_DYNAMIC;
12425                                 aip->previous_mode = aip->mode;
12426                                 aip->previous_submode = aip->submode;
12427                                 aip->mode = AIM_EVADE_WEAPON;
12428                                 aip->submode = -1;
12429                                 aip->submode_start_time = Missiontime;
12430                                 aip->mode_time = timestamp(MAX_EVADE_TIME);     //      Evade for up to five seconds.
12431                         }
12432                         break;
12433                 case AIM_STRAFE:
12434                 case AIM_BIGSHIP:
12435                 case AIM_DOCK:
12436                 case AIM_PLAY_DEAD:
12437                 case AIM_EVADE_WEAPON:
12438                 case AIM_BAY_EMERGE:
12439                 case AIM_BAY_DEPART:
12440                 case AIM_SENTRYGUN:
12441                         break;
12442                 default:
12443                         Int3(); //      Bogus mode!
12444                 }
12445         }
12446 }
12447
12448 // determine what path to use when emerging from a fighter bay
12449 // input:       pl_objp =>      pointer to object for ship that is arriving
12450 //                              pos             =>      output parameter, it is the starting world pos for path choosen
12451 //                              fvec            =>      output parameter, this is the forward vector that ship has when arriving
12452 //
12453 // exit:                -1              =>      path could not be located
12454 //                               0              => success
12455 int ai_acquire_emerge_path(object *pl_objp, int parent_objnum, vector *pos, vector *fvec)
12456 {
12457         int                     path_index, sb_path_index;
12458         ship                    *parent_sp = NULL;
12459         polymodel       *pm;
12460         ai_info         *aip;
12461         ship_bay                *sb;
12462         pnode                   *pnp;
12463         vector          *next_point;
12464
12465         aip = &Ai_info[Ships[pl_objp->instance].ai_index];
12466
12467         if ( parent_objnum == -1 ) {
12468                 Int3();
12469                 return -1;
12470         }
12471
12472         parent_sp = &Ships[Objects[parent_objnum].instance];
12473
12474         Assert(parent_sp != NULL);
12475         pm = model_get( parent_sp->modelnum );
12476         sb = pm->ship_bay;
12477
12478         if ( sb == NULL ) 
12479                 return -1;
12480
12481         if ( sb->num_paths <= 0 ) 
12482                 return -1;
12483
12484         // try to find a bay path that is not taken
12485         path_index = -1;
12486         sb_path_index = Ai_last_arrive_path++;
12487
12488         if ( sb_path_index >= sb->num_paths ) {
12489                 sb_path_index=0;
12490                 Ai_last_arrive_path=0;
12491         }
12492
12493         path_index = sb->paths[sb_path_index];
12494         if ( path_index == -1 ) 
12495                 return -1;
12496
12497         // create the path for pl_objp to follow
12498         create_model_exit_path(pl_objp, &Objects[parent_objnum], path_index, pm->paths[path_index].nverts);
12499         
12500         // Set this flag, so we don't bother recreating the path... we won't need to update the path
12501         // that has just been created.
12502 //      aip->ai_flags |= AIF_USE_STATIC_PATH;
12503
12504         // now return to the caller what the starting world pos and starting fvec for the ship will be
12505         Assert((aip->path_start >= 0) && (aip->path_start < MAX_PATH_POINTS));
12506         pnp = &Path_points[aip->path_start];
12507         *pos = pnp->pos;
12508
12509         // calc the forward vector using the starting two points of the path
12510         pnp = &Path_points[aip->path_start+1];
12511         next_point = &pnp->pos;
12512         vm_vec_normalized_dir(fvec, next_point, pos);
12513
12514         // record the parent objnum, since we'll need it once we're done with following the path
12515         aip->goal_objnum = parent_objnum;
12516         aip->goal_signature = Objects[parent_objnum].signature;
12517         aip->mode = AIM_BAY_EMERGE;
12518         aip->submode_start_time = Missiontime;
12519
12520         // set up starting vel
12521         vector vel;
12522         float speed;
12523         speed = Ship_info[Ships[pl_objp->instance].ship_info_index].max_speed;
12524         vel = *fvec;
12525         vm_vec_scale( &vel, speed );
12526         pl_objp->phys_info.vel = vel;
12527         pl_objp->phys_info.desired_vel = vel;
12528         pl_objp->phys_info.prev_ramp_vel.x = 0.0f;
12529         pl_objp->phys_info.prev_ramp_vel.y = 0.0f;
12530         pl_objp->phys_info.prev_ramp_vel.z = speed;
12531         pl_objp->phys_info.forward_thrust = 0.0f;               // How much the forward thruster is applied.  0-1.
12532
12533         return 0;       
12534 }
12535
12536 // clean up path data used for emerging from a fighter bay
12537 void ai_emerge_bay_path_cleanup(ai_info *aip)
12538 {
12539         aip->path_start = -1;
12540         aip->path_cur = -1;
12541         aip->path_length = 0;
12542         aip->mode = AIM_NONE;
12543 }
12544
12545 // handler for AIM_BAY_EMERGE
12546 void ai_bay_emerge()
12547 {
12548         ai_info *aip;
12549         int             parent_died=0;
12550
12551         aip = &Ai_info[Ships[Pl_objp->instance].ai_index];
12552
12553         // if no path to follow, leave this mode
12554         if ( aip->path_start < 0 ) {
12555                 aip->mode = AIM_NONE;
12556                 return;
12557         }
12558
12559         // ensure parent ship is still alive
12560         if ( aip->goal_objnum < 0 ) {
12561                 parent_died=1;
12562         } 
12563         if ( !parent_died ) {
12564                 if ( Objects[aip->goal_objnum].signature != aip->goal_signature ) {
12565                         parent_died=1;
12566                 }
12567         }
12568
12569         if ( !parent_died ) {
12570                 Assert(Objects[aip->goal_objnum].type == OBJ_SHIP);
12571                 if ( Ships[Objects[aip->goal_objnum].instance].flags & SF_DYING ) {
12572                         parent_died = 1;
12573                 }
12574         }
12575
12576         if ( parent_died ) {
12577                 ai_emerge_bay_path_cleanup(aip);
12578                 return;
12579         }
12580
12581         // follow the path to the final point
12582         ai_path();
12583
12584         // New test: must have been in AI_EMERGE mode for at least 10 seconds, and be a minimum distance from the start point
12585         if ( ( (Missiontime - aip->submode_start_time) > 10*F1_0 ) && (vm_vec_dist_quick(&Pl_objp->pos, &Objects[aip->goal_objnum].pos) > 0.75f * Objects[aip->goal_objnum].radius)) {
12586                 // erase path
12587                 ai_emerge_bay_path_cleanup(aip);
12588         }
12589
12590         // 2-25-99: Need this check to fix an assert for supercap ships... maybe we'll only do this check for supercaps 
12591         if (aip->path_cur > (aip->path_start+aip->path_length-1)) {
12592                 ai_emerge_bay_path_cleanup(aip);
12593         }       
12594 }
12595
12596 // Select the closest depart path
12597 //
12598 //      input:  aip     =>              ai info pointer to ship seeking to depart
12599 //                              pm              =>              pointer to polymodel for the ship contining the ship bay/depart paths
12600 //
12601 // exit:                >=0     =>              ship bay path index for depart path (ie index into sb->paths[])
12602 //                              -1              =>              no path could be found
12603 //
12604 // NOTE: this function should only be used for calculating closest depart paths for ai mode
12605 //                      AI_BAY_DEPART.  It tries to find the closest path that isn't already in use
12606 int ai_find_closest_depart_path(ai_info *aip, polymodel *pm)
12607 {
12608         int                     i, j, best_path, best_free_path;
12609         float                   dist, min_dist, min_free_dist;
12610         vector          *source;
12611         model_path      *mp;
12612         ship_bay                *sb;
12613
12614         sb = pm->ship_bay;
12615
12616         best_free_path = best_path = -1;
12617         min_free_dist = min_dist = 1e20f;
12618         Assert(aip->shipnum >= 0);
12619         source = &Objects[Ships[aip->shipnum].objnum].pos;
12620
12621         for ( i = 0; i < sb->num_paths; i++ ) {
12622
12623
12624                 mp = &pm->paths[sb->paths[i]];
12625                 for ( j = 0; j < mp->nverts; j++ ) {
12626                         dist = vm_vec_dist_squared(source, &mp->verts[j].pos);
12627
12628                         if ( dist < min_dist ) {
12629                                 min_dist = dist;
12630                                 best_path = i;
12631                         }
12632
12633                         // If this is a free path
12634                         if ( !(sb->depart_flags & (1<<i)) ) {
12635                                 if ( dist < min_free_dist ) {
12636                                         min_free_dist = dist;
12637                                         best_free_path = i;
12638                                 }
12639                         }
12640                 }
12641         }
12642
12643         if ( best_free_path >= 0 ) {
12644                 return best_free_path;          
12645         }
12646
12647         return best_path;
12648 }
12649
12650 // determine what path to use when trying to depart to a fighter bay
12651 // NOTE: this should be called when AIM_BAY_DEPART mode is set
12652 //
12653 // input:       pl_objp =>      pointer to object for ship that is departing
12654 //
12655 // exit:                -1      =>      could not find depart path
12656 //                              0       => found depart path
12657 int ai_acquire_depart_path(object *pl_objp, int parent_objnum)
12658 {
12659         int                     objnum, path_index;
12660         polymodel       *pm;
12661         ai_info         *aip;
12662         ship                    *sp;
12663         ship_bay                *sb;
12664
12665         aip = &Ai_info[Ships[pl_objp->instance].ai_index];
12666
12667         if ( parent_objnum == -1 ) {
12668                 ship_obj        *so;
12669
12670                 // for now just locate a captial ship on the same team:
12671                 so = GET_FIRST(&Ship_obj_list);
12672                 objnum = -1;
12673                 while(so != END_OF_LIST(&Ship_obj_list)){
12674                         sp = &Ships[Objects[so->objnum].instance];
12675                         if ( (Ship_info[sp->ship_info_index].flags & (SIF_HUGE_SHIP)) && (sp->team == Ships[pl_objp->instance].team) ) {
12676                                 objnum = so->objnum;
12677                                 break;
12678                         }
12679                         so = GET_NEXT(so);
12680                 } 
12681         } else {
12682                 objnum = parent_objnum;
12683         }
12684
12685         aip->path_start = -1;
12686
12687         if ( objnum == -1 )
12688                 return -1;
12689
12690         pm = model_get( Ships[Objects[objnum].instance].modelnum );
12691         sb = pm->ship_bay;
12692
12693         if ( sb == NULL ) 
12694                 return -1;
12695         if ( sb->num_paths <= 0 ) 
12696                 return -1;
12697
12698 /*
12699         
12700         path_index = -1;
12701         for ( i = 0; i < sb->num_paths; i++ ) {
12702                 if ( !(sb->depart_flags & (1<<i)) ) {
12703                         sb->depart_flags |= (1<<i);
12704                         path_index = sb->paths[i];
12705                         aip->submode_parm0 = i;                 // use mode-specific parameter to record ship bay path index
12706                         break;
12707                 }
12708         }
12709 */
12710         
12711         // take the closest path we can find
12712         int ship_bay_path;
12713         ship_bay_path = ai_find_closest_depart_path(aip, pm);
12714         path_index = sb->paths[ship_bay_path];
12715         aip->submode_parm0 = ship_bay_path;
12716         sb->depart_flags |= (1<<ship_bay_path);
12717
12718         if ( path_index == -1 ) {
12719                 return -1;
12720         }
12721
12722         Assert(pm->n_paths > path_index);
12723         ai_find_path(pl_objp, objnum, path_index, 0);
12724
12725         // Set this flag, so we don't bother recreating the path... we won't need to update the path
12726         // that has just been created.
12727         aip->ai_flags &= ~AIF_USE_STATIC_PATH;
12728
12729         aip->goal_objnum = objnum;
12730         aip->goal_signature = Objects[objnum].signature;
12731         aip->mode = AIM_BAY_DEPART;
12732
12733         Ships[pl_objp->instance].flags |= SF_DEPART_DOCKBAY;
12734         return 0;
12735 }
12736
12737 // handler for AIM_BAY_DEPART
12738 void ai_bay_depart()
12739 {
12740         ai_info *aip;
12741
12742         aip = &Ai_info[Ships[Pl_objp->instance].ai_index];
12743
12744         // if no path to follow, leave this mode
12745         if ( aip->path_start < 0 ) {
12746                 aip->mode = AIM_NONE;
12747                 return;
12748         }
12749
12750         // check if parent ship still exists, if not abort depart 
12751         if ( aip->goal_signature != Objects[aip->goal_objnum].signature ) {
12752                 aip->mode = AIM_NONE;
12753                 return;
12754         }
12755
12756         // follow the path to the final point
12757         ai_path();
12758
12759         // if the final point is reached, let default AI take over
12760         if ( aip->path_cur >= (aip->path_start+aip->path_length) ) {
12761                 polymodel       *pm;
12762                 ship_bay                *sb;
12763
12764                 pm = model_get( Ships[Objects[aip->goal_objnum].instance].modelnum );
12765                 sb = pm->ship_bay;
12766                 if ( sb != NULL ) {
12767                         sb->depart_flags &= ~(1<<aip->submode_parm0);
12768                 }
12769
12770                 // make ship disappear
12771                 Pl_objp->flags |= OF_SHOULD_BE_DEAD;
12772                 ship_departed( Pl_objp->instance );
12773
12774                 // clean up path stuff
12775                 aip->path_start = -1;
12776                 aip->path_cur = -1;
12777                 aip->path_length = 0;
12778                 aip->mode = AIM_NONE;
12779         }
12780 }
12781
12782 // Handler for AIM_SENTRYGUN.  This AI mode is for sentry guns only (ie floating turrets).
12783 void ai_sentrygun()
12784 {
12785         // Nothing to do here.  Turret firing is handled via process_subobjects().
12786         // If you want the sentry guns to do anything beyond firing their turrets at enemies, add it here!
12787 }
12788
12789 //      --------------------------------------------------------------------------
12790 //      Execute behavior given by aip->mode.
12791 void ai_execute_behavior(ai_info *aip)
12792 {
12793         switch (aip->mode) {
12794         case AIM_CHASE:
12795                 if (En_objp) {
12796                         ai_chase();
12797                 } else if (aip->submode == SM_EVADE_WEAPON) {
12798                         evade_weapon();
12799                         // maybe reset submode
12800                         if (aip->danger_weapon_objnum == -1) {
12801                                 aip->submode = SM_ATTACK;
12802                                 aip->submode_start_time = Missiontime;
12803                                 aip->last_attack_time = Missiontime;
12804                         }
12805                 } else {
12806                         //      Don't circle if this is the instructor.
12807                         ship    *shipp = &Ships[aip->shipnum];
12808                         ship_info       *sip = &Ship_info[shipp->ship_info_index];
12809
12810                         if (strnicmp(shipp->ship_name, INSTRUCTOR_SHIP_NAME, strlen(INSTRUCTOR_SHIP_NAME))) {
12811                                 if (sip->flags & (SIF_BIG_SHIP | SIF_HUGE_SHIP)) {
12812                                         aip->mode = AIM_NONE;
12813                                 } else {
12814                                         ai_chase_circle(Pl_objp);
12815                                 }
12816                         }
12817                 }
12818                 break;
12819         case AIM_EVADE:
12820                 if (En_objp) {
12821                         ai_evade();
12822                 } else {
12823                         vector  tvec;
12824                         vm_vec_scale_add(&tvec, &Pl_objp->pos, &Pl_objp->orient.rvec, 100.0f);
12825                         turn_towards_point(Pl_objp, &tvec, NULL, 0.0f);
12826                         accelerate_ship(aip, 0.5f);
12827                 }
12828                 break;
12829         case AIM_STILL:
12830                 ai_still();
12831                 break;
12832         case AIM_STAY_NEAR:
12833                 ai_stay_near();
12834                 break;
12835         case AIM_GUARD:
12836                 ai_guard();
12837                 break;
12838         case AIM_WAYPOINTS:
12839                 ai_waypoints();
12840                 break;
12841         case AIM_DOCK:
12842                 ai_dock();
12843                 break;
12844         case AIM_NONE:
12845                 // ai_formation();
12846                 break;
12847         case AIM_BIGSHIP:
12848                 ai_big_ship(Pl_objp);
12849                 break;
12850         case AIM_PATH: {
12851                 int path_num;
12852                 path_num = ai_return_path_num_from_dockbay(&Objects[aip->goal_objnum], 0);
12853                 ai_find_path(Pl_objp, aip->goal_objnum, path_num, 0);
12854                 ai_path();
12855                 break;
12856         }
12857         case AIM_SAFETY:
12858                 ai_safety();
12859                 break;
12860         case AIM_EVADE_WEAPON:
12861                 evade_weapon();
12862                 break;
12863         case AIM_STRAFE:
12864                 if (En_objp) {
12865                         Assert(En_objp->type == OBJ_SHIP);
12866                         ai_big_strafe();        // strafe a big ship
12867                 } else {
12868                         aip->mode = AIM_NONE;
12869                 }
12870                 break;
12871         case AIM_BAY_EMERGE:
12872                 ai_bay_emerge();
12873                 break;
12874         case AIM_BAY_DEPART:
12875                 ai_bay_depart();
12876                 break;
12877         case AIM_SENTRYGUN:
12878                 ai_sentrygun();
12879                 break;
12880         case AIM_WARP_OUT:
12881                 break;          //      Note, handled directly from ai_frame().
12882         default:
12883                 Int3();         //      This should never happen -- MK, 5/12/97 
12884                 break;
12885         }
12886
12887         if ( !(ship_get_SIF(aip->shipnum) & SIF_NOT_FLYABLE) ) {
12888                 maybe_evade_dumbfire_weapon(aip);
12889         }
12890 }
12891
12892 //      Auxiliary function for maybe_request_support.
12893 //      Return 1 if subsystem "type" is worthy of repair, else return 0.
12894 //      Since subsystems cannot be repaired if they are at 0 strength, don't return 1 if subsystem is dead.
12895 int mrs_subsystem(ship *shipp, int type)
12896 {
12897         float   t;
12898
12899         t = ship_get_subsystem_strength(shipp, type);
12900
12901         if (t > 0.0f) {
12902                 return (int) ((1.0f - t) * 3);
12903         } else {
12904                 return 3;
12905         }
12906 }
12907
12908 //      Return number of ships on *objp's team that are currently rearming.
12909 int num_allies_rearming(object *objp)
12910 {
12911         ship_obj        *so;
12912         int             team;
12913         int             count = 0;
12914
12915         team = Ships[objp->instance].team;
12916
12917         for ( so = GET_FIRST(&Ship_obj_list); so != END_OF_LIST(&Ship_obj_list); so = GET_NEXT(so) ) {
12918                 object  *A;
12919                 
12920                 Assert (so->objnum != -1);
12921                 A = &Objects[so->objnum];
12922
12923                 if (Ships[A->instance].team == team) {
12924                         if (Ai_info[Ships[A->instance].ai_index].ai_flags & (AIF_REPAIRING | AIF_AWAITING_REPAIR)) {
12925                                 count++;
12926                         }
12927                 }
12928         }
12929
12930         return count;
12931 }
12932
12933
12934 //      Maybe ship *objp should request support (rearm/repair).
12935 //      If it does, return TRUE, else return FALSE.
12936 int maybe_request_support(object *objp)
12937 {
12938         ship_info       *sip;
12939         ship                    *shipp;
12940         ai_info         *aip;
12941         int                     desire;
12942
12943         Assert(objp->type == OBJ_SHIP);
12944         shipp = &Ships[objp->instance];
12945         aip = &Ai_info[shipp->ai_index];
12946         sip = &Ship_info[shipp->ship_info_index];
12947
12948         if (!timestamp_elapsed(aip->next_rearm_request_timestamp))
12949                 return 0;
12950
12951         //      Only fighters and bombers request support.
12952         if (!(sip->flags & (SIF_FIGHTER | SIF_BOMBER)))
12953                 return 0;
12954
12955         //      A ship that is currently awaiting does not need support!
12956         if (aip->ai_flags & (AIF_AWAITING_REPAIR | AIF_BEING_REPAIRED))
12957                 return 0;
12958
12959         if (!is_support_allowed(objp))
12960                 return 0;
12961
12962         //if (shipp->team != TEAM_FRIENDLY)
12963         //      return 0;
12964
12965         //      Compute a desire value.
12966         //      Desire of 0 means no reason to request support.
12967         //      1 is slight, 2 more, etc.  Maximum is around 20.  Anything larger than 3 is pretty strong.
12968         desire = 0;
12969
12970         //      Set desire based on hull strength.
12971         //      No: We no longer repair hull, so this would cause repeated repair requests.
12972         //desire += 6 - (int) ((objp->hull_strength/sip->initial_hull_strength) * 6.0f);
12973
12974         //      Set desire based on key subsystems.
12975         desire += 2*mrs_subsystem(shipp, SUBSYSTEM_ENGINE);     //      Note, disabled engine forces repair request, regardless of nearby enemies.
12976         desire += mrs_subsystem(shipp, SUBSYSTEM_COMMUNICATION);
12977         desire += mrs_subsystem(shipp, SUBSYSTEM_WEAPONS);
12978         desire += mrs_subsystem(shipp, SUBSYSTEM_SENSORS);
12979
12980         //      Set desire based on percentage of secondary weapons.
12981         ship_weapon *swp = &shipp->weapons;
12982
12983         for ( int i = 0; i < swp->num_secondary_banks; i++ ) {
12984                 if (swp->secondary_bank_start_ammo[i] > 0) {
12985 //                      float r = (float) swp->secondary_bank_ammo[i]*Weapon_info[swp->secondary_bank_weapons[i]].cargo_size/swp->secondary_bank_capacity[i];
12986                         float r = (float) swp->secondary_bank_ammo[i]/swp->secondary_bank_start_ammo[i];
12987                         desire += (int) ((1.0f - r) * 3.0f);
12988                 }
12989         }
12990
12991         //      If no reason to repair, don't bother to see if it's safe to repair.
12992         if (desire == 0){
12993                 return 0;
12994         }
12995
12996         //      Compute danger threshold.
12997         //      Balance this with desire and maybe request support.
12998         if (ai_good_time_to_rearm( objp )) {
12999                 ai_issue_rearm_request(objp);
13000                 return 1;
13001         } else if (num_allies_rearming(objp) < 2) {
13002                 if (desire >= 8) {      //      guarantees disabled will cause repair request
13003                         ai_issue_rearm_request(objp);
13004                 } else if (desire >= 3) {               //      >= 3 means having a single subsystem fully blown will cause repair.
13005                         int     count;
13006                         int objnum = find_nearby_hostile(OBJ_INDEX(objp), get_enemy_team_mask(OBJ_INDEX(objp)), 2000.0f, &count);
13007
13008                         if ((objnum == -1) || (count < 2) || (vm_vec_dist_quick(&objp->pos, &Objects[objnum].pos) > 3000.0f*count/desire)) {
13009                                 ai_issue_rearm_request(objp);
13010                                 return 1;
13011                         } else {
13012                                 //nprintf(("AI", "Would like to rearm, but enemy only %7.3f units away.\n", vm_vec_dist_quick(&objp->pos, &Objects[objnum].pos)));
13013                         }
13014                 }
13015         }
13016
13017         return 0;
13018
13019 }
13020
13021 void ai_set_mode_warp_out(object *objp, ai_info *aip)
13022 {
13023         ai_abort_rearm_request(objp);
13024         if (aip->mode != AIM_WARP_OUT) {
13025                 aip->mode = AIM_WARP_OUT;
13026                 aip->submode = AIS_WARP_1;
13027         }
13028 }
13029
13030 //      Maybe warp ship out.
13031 //      Shivan and HoL fighter/bomber warp out if their weapons subsystems have been destroyed.
13032 void ai_maybe_warp_out(object *objp)
13033 {
13034         ship    *shipp;
13035
13036         // don't do anything if in a training mission.
13037         if ( The_mission.game_type & MISSION_TYPE_TRAINING )
13038                 return;
13039
13040         Assert(objp->type == OBJ_SHIP);
13041
13042         shipp = &Ships[objp->instance];
13043         ai_info *aip = &Ai_info[shipp->ai_index];
13044
13045         if (aip->mode == AIM_WARP_OUT)
13046                 return;
13047
13048         //      If a support ship with no goals and low hull, warp out.  Be sure that there are no pending goals
13049         // in the support ships ai_goal array.  Just process this ships goals.
13050         ship_info       *sip = &Ship_info[shipp->ship_info_index];
13051         if (sip->flags & SIF_SUPPORT) {
13052                 if ( timestamp_elapsed(aip->warp_out_timestamp) ) {
13053                         ai_process_mission_orders( OBJ_INDEX(objp), aip );
13054                         if ( (aip->dock_objnum == -1) && (objp->hull_strength/sip->initial_hull_strength < 0.25f) ) {
13055                                 ai_set_mode_warp_out(objp, aip);
13056                         }
13057                 }
13058         }
13059
13060         //      Friendly don't warp out, they'll eventually request support.
13061         if (shipp->team == TEAM_FRIENDLY)
13062                 return;
13063
13064         if (!(shipp->flags & SF_DEPARTING)) {
13065                 ship_info       *sip;
13066
13067                 sip = &Ship_info[shipp->ship_info_index];
13068                 if (sip->flags & (SIF_FIGHTER | SIF_BOMBER)) {
13069                         if (aip->warp_out_timestamp == 0) {
13070                                 //if (ship_get_subsystem_strength(shipp, SUBSYSTEM_WEAPONS) == 0.0f) {
13071                                 //      aip->warp_out_timestamp = timestamp(((myrand() % 10) + 10) * 1000);
13072                                 //}
13073                         } else if (timestamp_elapsed(aip->warp_out_timestamp)) {
13074                                 ai_set_mode_warp_out(objp, aip);
13075                         }
13076                 }
13077         }
13078 }
13079
13080 //      Warp this ship out.
13081 void ai_warp_out(object *objp)
13082 {
13083         // if dying, don't warp out.
13084         if (Ships[objp->instance].flags & SF_DYING) {
13085                 return;
13086         }
13087
13088         ai_info *aip;
13089
13090         aip = &Ai_info[Ships[objp->instance].ai_index];
13091
13092         switch (aip->submode) {
13093         case AIS_WARP_1:
13094                 aip->force_warp_time = timestamp(10*1000);      //      Try to avoid a collision for up to ten seconds.
13095                 aip->submode = AIS_WARP_2;
13096                 break;
13097         case AIS_WARP_2:                        //      Make sure won't collide with any object.
13098                 if (timestamp_elapsed(aip->force_warp_time) || !collide_predict_large_ship(objp, objp->radius*2.0f + 100.0f)) {
13099                         aip->submode = AIS_WARP_3;
13100
13101                         // maybe recalculate collision pairs.
13102                         if (ship_get_warp_speed(objp) > ship_get_max_speed(&Ships[objp->instance])) {
13103                                 // recalculate collision pairs
13104                                 OBJ_RECALC_PAIRS(objp); 
13105                         }
13106
13107                         aip->force_warp_time = timestamp(4*1000);               //      Try to attain target speed for up to 4 seconds.
13108                 } else {
13109                         vector  goal_point;
13110                         vm_vec_scale_add(&goal_point, &objp->pos, &objp->orient.uvec, 100.0f);
13111                         turn_towards_point(objp, &goal_point, NULL, 0.0f);
13112                         accelerate_ship(aip, 0.0f);
13113                 }
13114                 break;
13115         case AIS_WARP_3:
13116                 //      Rampup desired_vel in here from current to desired velocity and set PF_USE_VEL. (not sure this is the right flag)
13117                 //      desired velocity is computed in shipfx_calculate_warp_time().  See shipfx#572 for sample code.
13118                 float   speed, goal_speed;
13119                 float shipfx_calculate_warp_speed(object*);
13120                 goal_speed = shipfx_calculate_warp_speed(objp);
13121
13122                 // HUGE ships go immediately to AIS_WARP_4
13123                 if (Ship_info[Ships[objp->instance].ship_info_index].flags & SIF_HUGE_SHIP) {
13124                         aip->submode = AIS_WARP_4;
13125                         break;
13126                 }
13127                 //compute_warpout_stuff(objp, &goal_speed, &warp_time, &warp_pos);
13128                 //goal_speed = 80.0f;
13129                 //set_accel_for_target_speed(objp, 40.0f);
13130                 // DKA 8/11/99 objp->phys_info.flags |= PF_USE_VEL;     This flag is set in object code if warping out and AIS_WARP >= 3, properly accounting for blown engines
13131                 speed = goal_speed * flFrametime + objp->phys_info.speed * (1.0f - flFrametime);
13132                 vm_vec_copy_scale(&objp->phys_info.vel, &objp->orient.fvec, speed);
13133                 objp->phys_info.desired_vel = objp->phys_info.vel;
13134                 // nprintf(("AI", "Frame %i, speed = %7.3f, goal = %7.3f\n", Framecount, vm_vec_mag_quick(&objp->phys_info.vel), goal_speed));
13135                 if (timestamp_elapsed(aip->force_warp_time) || (fl_abs(objp->phys_info.speed - goal_speed) < 2.0f))
13136                         aip->submode = AIS_WARP_4;
13137                 break;
13138         case AIS_WARP_4: {
13139                 shipfx_warpout_start(objp);
13140                 aip->submode = AIS_WARP_5;
13141                 break;
13142         }
13143         case AIS_WARP_5:
13144                 break;
13145         default:
13146                 Int3();         //      Illegal submode for warping out.
13147         }
13148 }
13149
13150 //      Return object index of weapon that could produce a shockwave that should be known about to *objp.
13151 //      Return nearest one.
13152 int ai_find_shockwave_weapon(object *objp, ai_info *aip)
13153 {
13154         missile_obj     *mo;
13155         float   nearest_dist = 999999.9f;
13156         int     nearest_index = -1;
13157
13158         for ( mo = GET_NEXT(&Missile_obj_list); mo != END_OF_LIST(&Missile_obj_list); mo = GET_NEXT(mo) ) {
13159                 object          *A;
13160                 weapon          *wp;
13161                 weapon_info     *wip;
13162         
13163                 Assert(mo->objnum >= 0 && mo->objnum < MAX_OBJECTS);
13164                 A = &Objects[mo->objnum];
13165
13166                 Assert(A->type == OBJ_WEAPON);
13167                 Assert((A->instance >= 0) && (A->instance < MAX_WEAPONS));
13168                 wp = &Weapons[A->instance];
13169                 wip = &Weapon_info[wp->weapon_info_index];
13170                 Assert( wip->subtype == WP_MISSILE );
13171
13172                 if (wip->shockwave_speed > 0.0f) {
13173                         float   dist;
13174
13175                         dist = vm_vec_dist_quick(&objp->pos, &A->pos);
13176                         if (dist < nearest_dist) {
13177                                 nearest_dist = dist;
13178                                 nearest_index = mo->objnum;
13179                         }
13180                 }
13181         }
13182
13183         return nearest_index;
13184
13185 }
13186
13187 #define EVADE_SHOCKWAVE_DAMAGE_THRESHOLD                100.0f
13188
13189 //      Tell all ships to avoid a big ship that is blowing up.
13190 //      Only avoid if shockwave is fairly large.
13191 //      OK to tell everyone to avoid.  If they're too far away, that gets cleaned up in the frame interval.
13192 void ai_announce_ship_dying(object *dying_objp)
13193 {
13194         float damage = ship_get_exp_damage(dying_objp);
13195         if (damage >= EVADE_SHOCKWAVE_DAMAGE_THRESHOLD) {
13196                 ship_obj        *so;
13197
13198                 for ( so = GET_FIRST(&Ship_obj_list); so != END_OF_LIST(&Ship_obj_list); so = GET_NEXT(so) ) {
13199                         if (Ship_info[Ships[Objects[so->objnum].instance].ship_info_index].flags & (SIF_SMALL_SHIP | SIF_FREIGHTER)) {
13200                                 ai_info *aip;
13201
13202                                 aip = &Ai_info[Ships[Objects[so->objnum].instance].ai_index];
13203
13204                                 if ( !(aip->ai_flags & (AIF_DOCKED|AIF_BEING_REPAIRED)) ) {
13205                                         aip->ai_flags |= AIF_AVOID_SHOCKWAVE_SHIP;
13206                                 }
13207                         }
13208                 }
13209         }
13210 }
13211
13212
13213 //      Return object index of weapon that could produce a shockwave that should be known about to *objp.
13214 //      Return nearest one.
13215 int ai_find_shockwave_ship(object *objp, ai_info *aip)
13216 {
13217         ship_obj        *so;
13218         float   nearest_dist = 999999.9f;
13219         int     nearest_index = -1;
13220
13221         for ( so = GET_NEXT(&Ship_obj_list); so != END_OF_LIST(&Ship_obj_list); so = GET_NEXT(so) ) {
13222                 object          *A;
13223                 ship                    *shipp;
13224         
13225                 Assert(so->objnum >= 0 && so->objnum < MAX_OBJECTS);
13226                 A = &Objects[so->objnum];
13227
13228                 Assert(A->type == OBJ_SHIP);
13229                 Assert((A->instance >= 0) && (A->instance < MAX_SHIPS));
13230                 shipp = &Ships[A->instance];
13231                 //      Only look at objects in the process of dying.
13232                 if (shipp->flags & SF_DYING) {
13233                         float damage = ship_get_exp_damage(objp);
13234
13235                         if (damage >= EVADE_SHOCKWAVE_DAMAGE_THRESHOLD) {               //      Only evade quite large blasts
13236                                 float   dist;
13237
13238                                 dist = vm_vec_dist_quick(&objp->pos, &A->pos);
13239                                 if (dist < nearest_dist) {
13240                                         nearest_dist = dist;
13241                                         nearest_index = so->objnum;
13242                                 }
13243                         }
13244                 }
13245         }
13246
13247         return nearest_index;
13248
13249 }
13250
13251 int aas_1(object *objp, ai_info *aip, vector *safe_pos)
13252 {
13253         // MAKE SURE safe_pos DOES NOT TAKE US TOWARD THE A SHIP WE'RE ATTACKING.
13254         if (aip->ai_flags & AIF_AVOID_SHOCKWAVE_WEAPON) {
13255                 //      If we don't currently know of a weapon to avoid, try to find one.
13256                 //      If we can't find one, then clear the bit so we don't keep coming here.
13257                 if (aip->shockwave_object == -1) {
13258                         int shockwave_weapon = ai_find_shockwave_weapon(objp, aip);
13259                         if (shockwave_weapon == -1) {
13260                                 aip->ai_flags &= ~AIF_AVOID_SHOCKWAVE_WEAPON;
13261                                 return 0;
13262                         } else {
13263                                 aip->shockwave_object = shockwave_weapon;
13264                         }
13265                 }
13266
13267                 //      OK, we have reason to believe we should avoid aip->shockwave_object.
13268                 Assert(aip->shockwave_object > -1);
13269                 object  *weapon_objp = &Objects[aip->shockwave_object];
13270                 if (weapon_objp->type != OBJ_WEAPON) {
13271                         aip->ai_flags &= ~AIF_AVOID_SHOCKWAVE_WEAPON;
13272                         aip->shockwave_object = -1;
13273                         return 0;
13274                 }
13275
13276                 weapon  *weaponp = &Weapons[weapon_objp->instance];
13277                 weapon_info     *wip = &Weapon_info[weaponp->weapon_info_index];
13278                 object *target_ship_obj = NULL;
13279
13280                 if (wip->shockwave_speed == 0.0f) {
13281                         aip->ai_flags &= ~AIF_AVOID_SHOCKWAVE_WEAPON;
13282                         aip->shockwave_object = -1;
13283                         return 0;
13284                 }
13285
13286                 float   danger_dist;
13287                 vector  expected_pos;           //      Position at which we expect the weapon to detonate.
13288                 int             pos_set = 0;
13289
13290                 danger_dist = wip->outer_radius;
13291                 //      Set predicted position of detonation.
13292                 //      If an aspect locked missile, assume it will detonate at the homing position.
13293                 //      If not, which is not possible in a default FreeSpace weapon, then predict it will detonate at some
13294                 //      time in the future, this time based on max lifetime and life left.
13295                 if (wip->wi_flags & WIF_HOMING_ASPECT) {
13296                         expected_pos = weaponp->homing_pos;
13297                         if (weaponp->homing_object && weaponp->homing_object->type == OBJ_SHIP) {
13298                                 target_ship_obj = weaponp->homing_object;
13299                         }
13300                         pos_set = 1;
13301                         if (IS_VEC_NULL(&weaponp->homing_pos)) {
13302                                 pos_set = 0;
13303                                 if (weaponp->target_num != -1) {
13304                                         if (Objects[weaponp->target_num].type == OBJ_SHIP) {
13305                                                 target_ship_obj = &Objects[weaponp->target_num];
13306                                                 expected_pos = target_ship_obj->pos;
13307                                                 pos_set = 1;
13308                                         }
13309                                 }
13310                         }
13311                 }
13312
13313                 if (!pos_set) {
13314                         float   time_scale;
13315
13316                         if (wip->lifetime - weaponp->lifeleft > 5.0f) {
13317                                 time_scale = 1.0f;
13318                         } else {
13319                                 time_scale = weaponp->lifeleft/2.0f;
13320                         }
13321
13322                         vm_vec_scale_add(&expected_pos, &weapon_objp->pos, &weapon_objp->orient.fvec, time_scale);
13323                 }
13324
13325                 //      See if too far away to care about shockwave.
13326                 if (vm_vec_dist_quick(&objp->pos, &expected_pos) > danger_dist*2.0f) {
13327                         //aip->ai_flags &= ~AIF_AVOID_SHOCKWAVE_WEAPON;
13328                         return 0;
13329                 } else {
13330                         // try to find a safe position
13331                         vector vec_from_exp;
13332                         float dir = 1.0f;
13333                         vm_vec_sub(&vec_from_exp, &objp->pos, &expected_pos);
13334                         float dot = vm_vec_dotprod(&vec_from_exp, &weapon_objp->orient.fvec);
13335                         if (dot > -30) {
13336                                 // if we're already on the other side of the explosion, don't try to fly behind it
13337                                 dir = -1.0f;
13338                         }
13339
13340                         //      Fly towards a point behind the weapon.
13341                         vm_vec_scale_add(safe_pos, &weapon_objp->pos, &weapon_objp->orient.fvec, -50000.0f*dir);
13342
13343                         // verify safe_pos will not make us collide with our target objnum, else try 2 other vecs
13344                         // don't bang your head, else go
13345 //                      int go_safe = FALSE;
13346                         int go_safe = TRUE;
13347 /*                      if (target_ship_obj) {
13348                                 if (pp_collide(&objp->pos, safe_pos, target_ship_obj, objp->radius)) {
13349                                         // try up to 2 other random directions
13350                                         vector dir_vec, rand_vec;
13351                                         int idx;
13352                                         for (idx=0; idx<2; idx++) {
13353                                                 vm_vec_rand_vec_quick(&rand_vec);
13354                                                 vm_vec_scale_add(&dir_vec, &weapon_objp->orient.fvec, &rand_vec, 0.5f);
13355                                                 vm_vec_scale_add(safe_pos, &weapon_objp->pos, &dir_vec, -50000.0f*dir);
13356                                                 if ( !pp_collide(&objp->pos, safe_pos, target_ship_obj, objp->radius) ) {
13357                                                         go_safe = TRUE;
13358                                                         break;
13359                                                 }
13360                                         }
13361                                 } else { // direct path is safe
13362                                         go_safe = TRUE;
13363                                 }
13364                         } else { // no target_obj_ship
13365                                 go_safe = TRUE;
13366                         } */
13367
13368                         if (go_safe) {
13369                                 return 1;
13370                         } else {
13371                                 // can't figure out a good way to go
13372                                 return 0;
13373                         }
13374                 }
13375         } else if (aip->ai_flags & AIF_AVOID_SHOCKWAVE_SHIP) {
13376                 if (aip->shockwave_object == -1) {
13377                         int shockwave_ship = ai_find_shockwave_ship(objp, aip);
13378                         if (shockwave_ship == -1) {
13379                                 aip->ai_flags &= ~AIF_AVOID_SHOCKWAVE_SHIP;
13380                                 return 0;
13381                         } else {
13382                                 aip->shockwave_object = shockwave_ship;
13383                         }
13384                 }
13385
13386                 Assert(aip->shockwave_object > -1);
13387                 object  *ship_objp = &Objects[aip->shockwave_object];
13388                 if (ship_objp == objp) {
13389                         aip->shockwave_object = -1;
13390                         return 0;
13391                 }
13392
13393                 if (ship_objp->type != OBJ_SHIP) {
13394                         aip->ai_flags &= ~AIF_AVOID_SHOCKWAVE_SHIP;
13395                         return 0;
13396                 }
13397
13398                 //      Optimize note! Don't really have to normalize.  We only need a point away from the blowing-up ship.
13399                 vector safe_vec;
13400
13401                 vm_vec_normalized_dir(&safe_vec, &objp->pos, &ship_objp->pos);
13402                 vm_vec_scale_add(safe_pos, &ship_objp->pos, &safe_vec, 50000.0f);       //      Fly away from the ship.
13403
13404                 float outer_rad = ship_get_exp_outer_rad(ship_objp);
13405
13406                 if (vm_vec_dist_quick(&objp->pos, &ship_objp->pos) > outer_rad*1.5f) {
13407                         aip->ai_flags &= ~AIF_AVOID_SHOCKWAVE_WEAPON;
13408                         return 0;
13409                 }
13410
13411                 return 1;
13412
13413         } else {
13414                 Int3(); //      Illegal -- supposedly avoiding a shockwave, but neither ship nor weapon.  What is it!?
13415         }
13416
13417         return 0;
13418 }
13419
13420 /*
13421 int rct_done = 0;
13422
13423 void rand_chance_test()
13424 {
13425         int     i;
13426         float   frametime;
13427
13428         if (rct_done)
13429                 return;
13430
13431         rct_done = 1;
13432
13433         for (frametime=0.02f; frametime<0.25f; frametime *= 1.25f) {
13434                 float   chance;
13435
13436                 nprintf(("AI", "%6.4f: ", frametime));
13437                 for (chance=0.25f; chance<2.5f; chance += 0.25f) {
13438                         int count = 0;
13439
13440                         for (i=0; i<100.0f/frametime; i++) {
13441                                 if (rand_chance(frametime, chance))
13442                                         count++;
13443                         }
13444                         nprintf(("AI", "%3i ", count));
13445                 }
13446                 nprintf(("AI", "\n"));
13447         }
13448 }
13449 */
13450
13451 //      --------------------------------------------------------------------------
13452 //      Make object *objp avoid the nearest dangerous shockwave-producing weapon.
13453 //      If it looks like there is no valid shockwave-producing weapon then clear the AIF_AVOID_SHOCKWAVE_WEAPON bit in ai_flags and return.
13454 //      Return 1 if avoiding a shockwave, else return 0.
13455 int ai_avoid_shockwave(object *objp, ai_info *aip)
13456 {
13457         vector  safe_pos;
13458
13459         //rand_chance_test();
13460         // BIG|HUGE do not respond to shockwaves
13461         if (Ship_info[Ships[objp->instance].ship_info_index].flags & (SIF_BIG_SHIP|SIF_HUGE_SHIP)) {
13462                 // don't come here again
13463                 aip->ai_flags &= ~AIF_AVOID_SHOCKWAVE;
13464                 return 0;
13465         }
13466
13467         //      Don't all react right away.
13468         if (!(aip->ai_flags & AIF_AVOID_SHOCKWAVE_STARTED))
13469                 if (!rand_chance(flFrametime, (float) aip->ai_class/4.0f + 0.25f))      //      Chance to avoid in 1 second is 0.25 + ai_class/4
13470                         return 0;
13471
13472         if (!aas_1(objp, aip, &safe_pos)) {
13473                 aip->ai_flags |= AIF_AVOID_SHOCKWAVE_STARTED;
13474                 return 0;
13475         }
13476
13477         aip->ai_flags |= AIF_AVOID_SHOCKWAVE_STARTED;
13478
13479         //      OK, evade the shockwave!
13480         turn_towards_point(objp, &safe_pos, NULL, 0.0f);
13481         vector  vec_to_safe_pos;
13482         float           dot_to_goal;
13483
13484         vm_vec_normalized_dir(&vec_to_safe_pos, &safe_pos, &objp->pos);
13485
13486         dot_to_goal = vm_vec_dot(&objp->orient.fvec, &vec_to_safe_pos);
13487         if (dot_to_goal < -0.5f)
13488                 accelerate_ship(aip, 0.3f);
13489         else {
13490                 accelerate_ship(aip, 1.0f + dot_to_goal);
13491                 if (dot_to_goal > 0.2f) {
13492                         if (!(objp->phys_info.flags & PF_AFTERBURNER_ON )) {
13493                                 afterburners_start(objp);
13494                                 aip->afterburner_stop_time = Missiontime + 2*F1_0;
13495                         }
13496                 }
13497         }
13498
13499         return 1;
13500 }
13501
13502 //      Awaiting repair.  Be useful.
13503 //      Probably fly towards incoming repair ship.
13504 //      Return true if this ship is close to being repaired, else return false.
13505 int ai_await_repair_frame(object *objp, ai_info *aip)
13506 {
13507         if (!(aip->ai_flags & (AIF_AWAITING_REPAIR | AIF_BEING_REPAIRED)))
13508                 return 0;
13509
13510         if (aip->dock_objnum == -1)
13511                 return 0;
13512
13513         ship    *shipp;
13514         ship_info       *sip;
13515
13516         shipp = &Ships[Objects[aip->dock_objnum].instance];
13517         sip = &Ship_info[shipp->ship_info_index];
13518
13519         aip->ai_flags &= ~AIF_FORMATION_OBJECT; //      Prevents endless rotation.
13520
13521         if (!(sip->flags & SIF_SUPPORT))
13522                 return 0;
13523
13524         vector  goal_point;
13525         object  *repair_objp;
13526
13527         repair_objp = &Objects[aip->dock_objnum];
13528
13529         if (Ships[repair_objp->instance].team == TEAM_TRAITOR) {
13530                 ai_abort_rearm_request(repair_objp);
13531                 return 0;
13532         }
13533
13534         vm_vec_scale_add(&goal_point, &repair_objp->pos, &repair_objp->orient.uvec, -50.0f);    //      Fly towards point below repair ship.
13535
13536         vector  vtr;
13537         float dist = vm_vec_normalized_dir(&vtr, &goal_point, &objp->pos);
13538         float dot = vm_vec_dot(&vtr, &objp->orient.fvec);
13539
13540         if (dist > 200.0f) {
13541                 //nprintf(("AI", "%s flying towards %s for repair, dist = %7.3f\n", Ships[objp->instance].ship_name, &Ships[repair_objp->instance].ship_name, dist));
13542                 accelerate_ship(aip, (0.9f + dot) * dist/1500.0f);
13543                 turn_towards_point(objp, &goal_point, NULL, 0.0f);
13544         } else {
13545                 accelerate_ship(aip, 0.0f);
13546                 //nprintf(("AI", "%s sitting still awaiting repair from %s, dist = %7.3f\n", Ships[objp->instance].ship_name, &Ships[repair_objp->instance].ship_name, dist));
13547         }
13548
13549         return 1;
13550 }
13551
13552 //      Maybe cause this ship to self-destruct.
13553 //      Currently, any small ship (SIF_SMALL_SHIP) that has been disabled will self-destruct after awhile.
13554 //      Maybe should only do this if they are preventing their wing from re-entering.
13555 void ai_maybe_self_destruct(object *objp, ai_info *aip)
13556 {
13557         //      Friendly ships can be repaired, so no self-destruct.
13558         //      In multiplayer, just don't self-destruct.  I figured there would be a problem. -- MK, 3/19/98.
13559         if ((Ships[objp->instance].team == TEAM_FRIENDLY) || (Game_mode & GM_MULTIPLAYER))
13560                 return;
13561
13562         //      Small ships in a wing blow themselves up after awhile if engine or weapons system has been destroyed.
13563         //      Reason: Don't want them to prevent a re-emergence of the wing.
13564         //      Note: Don't blow up if not in a wing for two reasons: One, won't affect re-emergence of waves and (1) disable the Dragon
13565         //      mission would be broken.
13566         if ((Ship_info[Ships[objp->instance].ship_info_index].flags & SIF_SMALL_SHIP) && (Ships[objp->instance].wingnum != -1)) {
13567                 if ((ship_get_subsystem_strength(&Ships[objp->instance], SUBSYSTEM_ENGINE) <= 0.0f) ||
13568                         (ship_get_subsystem_strength(&Ships[objp->instance], SUBSYSTEM_WEAPONS) <= 0.0f)) {
13569                         if (aip->self_destruct_timestamp < 0)
13570                                 aip->self_destruct_timestamp = timestamp(90 * 1000);    //      seconds until self-destruct
13571                 } else {
13572                         aip->self_destruct_timestamp = -1;
13573                 }
13574
13575                 if (aip->self_destruct_timestamp < 0) {
13576                         return;
13577                 }
13578
13579                 if (timestamp_elapsed(aip->self_destruct_timestamp)) {
13580                         ship_apply_local_damage( objp, objp, &objp->pos, objp->hull_strength*flFrametime + 1.0f, MISS_SHIELDS);
13581                 }
13582         }
13583 }
13584
13585 // Determine if pl_objp needs a new target, called from ai_frame()
13586 int ai_need_new_target(object *pl_objp, int target_objnum)
13587 {
13588         object *objp;
13589
13590         if ( target_objnum < 0 ) {
13591                 return 1;
13592         }
13593
13594         objp = &Objects[target_objnum];
13595
13596         if ( (objp->type != OBJ_SHIP) && (objp->type != OBJ_ASTEROID) && (objp->type != OBJ_WEAPON) ) {
13597                 return 1;
13598         }
13599
13600         if ( objp->type == OBJ_SHIP ) {
13601                 if ( Ships[objp->instance].flags & SF_DYING ) {
13602                         return 1;
13603                 } else if (Ships[objp->instance].team == Ships[pl_objp->instance].team)
13604                         return 1;
13605         }
13606
13607         return 0;
13608 }
13609
13610 //      If *objp is recovering from a collision with a big ship, handle it.
13611 //      Return true if recovering.
13612 int maybe_big_ship_collide_recover_frame(object *objp, ai_info *aip)
13613 {
13614         float   dot, dist;
13615         vector  v2g;
13616         
13617         if (aip->ai_flags & AIF_BIG_SHIP_COLLIDE_RECOVER_1) {
13618                 ai_turn_towards_vector(&aip->big_recover_pos_1, objp, flFrametime, Ship_info[Ships[objp->instance].ship_info_index].srotation_time, NULL, NULL, 0.0f, 0, NULL);
13619                 dist = vm_vec_normalized_dir(&v2g, &aip->big_recover_pos_1, &objp->pos);
13620                 dot = vm_vec_dot(&objp->orient.fvec, &v2g);
13621                 accelerate_ship(aip, dot);
13622
13623                 //      If close to desired point, or 15+ seconds since entered this mode, continue to next mode.
13624                 if ((timestamp_until(aip->big_recover_timestamp) < -15*1000) || (dist < (0.5f + flFrametime) * objp->phys_info.speed)) {
13625                         aip->ai_flags &= ~AIF_BIG_SHIP_COLLIDE_RECOVER_1;
13626                         aip->ai_flags |= AIF_BIG_SHIP_COLLIDE_RECOVER_2;
13627                 }
13628
13629                 return 1;
13630
13631         } else if (aip->ai_flags & AIF_BIG_SHIP_COLLIDE_RECOVER_2) {
13632                 ai_turn_towards_vector(&aip->big_recover_pos_2, objp, flFrametime, Ship_info[Ships[objp->instance].ship_info_index].srotation_time, NULL, NULL, 0.0f, 0, NULL);
13633                 dist = vm_vec_normalized_dir(&v2g, &aip->big_recover_pos_2, &objp->pos);
13634                 dot = vm_vec_dot(&objp->orient.fvec, &v2g);
13635                 accelerate_ship(aip, dot);
13636
13637                 //      If close to desired point, or 30+ seconds since started avoiding collision, done avoiding.
13638                 if ((timestamp_until(aip->big_recover_timestamp) < -30*1000) || (dist < (0.5f + flFrametime) * objp->phys_info.speed)) {
13639                         aip->ai_flags &= ~AIF_BIG_SHIP_COLLIDE_RECOVER_2;
13640                         aip->ai_flags &= ~AIF_TARGET_COLLISION;
13641                 }
13642
13643                 return 1;
13644         }
13645
13646         if (aip->ai_flags & AIF_TARGET_COLLISION) {
13647                 aip->ai_flags &= ~AIF_TARGET_COLLISION;
13648         }
13649         return 0;
13650 }
13651
13652 void validate_mode_submode(ai_info *aip)
13653 {
13654         switch (aip->mode) {
13655         case AIM_CHASE:
13656                 // check valid submode
13657                 switch (aip->submode) {
13658                 case SM_CONTINUOUS_TURN:
13659                 case SM_ATTACK:
13660                 case SM_EVADE_SQUIGGLE:
13661                 case SM_EVADE_BRAKE:    
13662                 case SM_EVADE:          
13663                 case SM_SUPER_ATTACK:
13664                 case SM_AVOID:  
13665                 case SM_GET_BEHIND:
13666                 case SM_GET_AWAY:               
13667                 case SM_EVADE_WEAPON:
13668                 case SM_FLY_AWAY:       
13669                 case SM_ATTACK_FOREVER:
13670                         break;
13671                 default:
13672                         Int3();
13673                 }
13674                 break;
13675
13676         case AIM_STRAFE:
13677                 // check valid submode
13678                 switch(aip->submode) {
13679                 case AIS_STRAFE_ATTACK:
13680                 case AIS_STRAFE_AVOID:
13681                 case AIS_STRAFE_RETREAT1:
13682                 case AIS_STRAFE_RETREAT2:
13683                 case AIS_STRAFE_POSITION:
13684                         break;
13685                 default:
13686                         Int3();
13687                 }
13688                 break;
13689         }
13690 }
13691
13692 //      --------------------------------------------------------------------------
13693 // Process AI object "objnum".
13694 void ai_frame(int objnum)
13695 {
13696         ship            *shipp = &Ships[Objects[objnum].instance];
13697         ai_info *aip = &Ai_info[shipp->ai_index];
13698         int             target_objnum;
13699
13700 //      validate_mode_submode(aip);
13701
13702         Assert((aip->mode != AIM_WAYPOINTS) || (aip->active_goal != AI_ACTIVE_GOAL_DYNAMIC));
13703
13704         // Set globals defining the current object and its enemy object.
13705         Pl_objp = &Objects[objnum];
13706
13707         if (aip->mode == AIM_WARP_OUT) {
13708                 ai_warp_out(Pl_objp);
13709                 return;
13710         }
13711
13712 /*      //      HACK! TEST! REMOVE ME!
13713         if (Ship_info[shipp->ship_info_index].flags & SIF_BIG_SHIP)
13714                 if (shipp->team == Player_ship->team)
13715                         aip->mode = AIM_CHASE;
13716 */
13717
13718 //      if (!strnicmp(Ships[Pl_objp->instance].ship_name, "cancer", 6))
13719 //              nprintf(("AI", "Ship %s: mode = %s, submode = %i\n", Ships[Pl_objp->instance].ship_name, Mode_text[aip->mode], aip->submode));
13720
13721         ai_maybe_self_destruct(Pl_objp, aip);
13722
13723 //      if ( timestamp_elapsed(aip->goal_check_time) ) {
13724                 ai_process_mission_orders( objnum, aip );
13725 //              aip->goal_check_time = timestamp_rand(1000,2000);
13726 //      }
13727
13728         //      Avoid a shockwave, if necessary.  If a shockwave and rearming, stop rearming.
13729         if (aip->ai_flags & AIF_AVOID_SHOCKWAVE) {
13730                 if (ai_avoid_shockwave(Pl_objp, aip)) {
13731                         aip->ai_flags &= ~(AIF_BIG_SHIP_COLLIDE_RECOVER_1 | AIF_BIG_SHIP_COLLIDE_RECOVER_2);
13732                         if (aip->ai_flags & (AIF_AWAITING_REPAIR | AIF_BEING_REPAIRED))
13733                                 ai_abort_rearm_request(Pl_objp);
13734                         return;
13735                 }
13736         } else {
13737                 aip->ai_flags &= ~AIF_AVOID_SHOCKWAVE_STARTED;
13738         }
13739
13740         // moved call to ai_do_repair frame here from below because of the subsequent if statment returning
13741         // if the ship is getting repaired
13742         //      If waiting to be repaired, just stop and sit.
13743         ai_do_repair_frame(Pl_objp, aip, flFrametime);
13744         if ((aip->ai_flags & AIF_AWAITING_REPAIR) || (aip->ai_flags & AIF_BEING_REPAIRED)) {
13745                 if (ai_await_repair_frame(Pl_objp, aip))
13746                         return;
13747         }
13748
13749         if (aip->mode == AIM_PLAY_DEAD)
13750                 return;
13751
13752         //      If recovering from a collision with a big ship, don't continue.
13753         if (maybe_big_ship_collide_recover_frame(Pl_objp, aip))
13754                 return;
13755
13756         ai_preprocess_ignore_objnum(Pl_objp, aip);
13757         target_objnum = set_target_objnum(aip, aip->target_objnum);
13758
13759         // nprintf(("AI", "Frame %i: Coords = %7.3f %7.3f %7.3f\n", AI_FrameCount, Pl_objp->pos.x, Pl_objp->pos.y, Pl_objp->pos.z));
13760
13761         Assert(objnum != target_objnum);
13762
13763         ai_manage_shield(Pl_objp, aip);
13764         
13765         if ( maybe_request_support(Pl_objp) ) {
13766                 if ( Ships[Pl_objp->instance].flags & SF_FROM_PLAYER_WING ) {
13767                         ship_maybe_tell_about_rearm(shipp);
13768                 }
13769         }
13770
13771         ai_maybe_warp_out(Pl_objp);
13772
13773 /*
13774         //      If this ship is attacking an object's subsystems and someone else destroyed
13775         //      the subsystem, it could continue attacking the ship.  Need to invalidate the objnum.
13776         if (target_objnum >= 0)
13777                 if (Objects[target_objnum].flags & OF_PROTECTED) {
13778                         // if (aip->targeted_subsys != NULL)
13779                         //      ; //nprintf(("AI", "subsys hits = %7.3f\n", aip->targeted_subsys->current_hits));
13780
13781                         if ((aip->targeted_subsys == NULL) || (aip->targeted_subsys->current_hits <= 0.0f)) {
13782                                 target_objnum = -1;
13783                                 aip->target_objnum = -1;
13784                         }
13785                 }
13786 */
13787
13788
13789         //      Find an enemy if don't already have one.
13790         En_objp = NULL;
13791         if ( ai_need_new_target(Pl_objp, target_objnum) ) {
13792                 if ((aip->mode != AIM_EVADE_WEAPON) && (aip->active_goal == AI_ACTIVE_GOAL_DYNAMIC)) {
13793                         aip->resume_goal_time = -1;
13794                         aip->active_goal = AI_GOAL_NONE;
13795                 } else if (aip->resume_goal_time == -1) {
13796                         // AL 12-9-97: Don't allow cargo and navbuoys to set their aip->target_objnum
13797                         if ( !(Ship_info[shipp->ship_info_index].flags & SIF_HARMLESS) ) {
13798                                 target_objnum = find_enemy(objnum, MAX_ENEMY_DISTANCE, Skill_level_max_attackers[Game_skill_level]);            //      Attack up to 25K units away.
13799                                 if (target_objnum != -1) {
13800                                         if (aip->target_objnum != target_objnum)
13801                                                 aip->aspect_locked_time = 0.0f;
13802                                         set_target_objnum(aip, target_objnum);
13803                                         En_objp = &Objects[target_objnum];
13804                                 }
13805                         }
13806                 }
13807         } else if (target_objnum >= 0) {
13808                 En_objp = &Objects[target_objnum];
13809         }
13810
13811         // set base stealth info each frame
13812         aip->ai_flags &= ~AIF_STEALTH_PURSIUT;
13813         if (En_objp && En_objp->type == OBJ_SHIP) {
13814                 if (Ship_info[Ships[En_objp->instance].ship_info_index].flags & SIF_STEALTH) {
13815                         int stealth_state = ai_is_stealth_visible(Pl_objp, En_objp);
13816                         float dist = vm_vec_dist_quick(&En_objp->pos, &Pl_objp->pos);
13817
13818                         if (stealth_state != STEALTH_FULLY_TARGETABLE) {
13819                                 aip->ai_flags |= AIF_STEALTH_PURSIUT;
13820                         }
13821
13822                         if ( (stealth_state == STEALTH_FULLY_TARGETABLE) || (stealth_state == STEALTH_VISIBLE) ) {
13823                                 aip->stealth_last_visible_stamp = timestamp();
13824                                 aip->stealth_last_cheat_visible_stamp = aip->stealth_last_visible_stamp;
13825                                 aip->stealth_last_pos = En_objp->pos;
13826                                 aip->stealth_velocity = En_objp->phys_info.vel;
13827                         } else if (dist < 100) {
13828                                 // get cheat timestamp
13829                                 aip->stealth_last_cheat_visible_stamp = timestamp();
13830
13831                                 // set approximate pos and vel, with increasing error as time from last_visible_stamp increases
13832                                 update_ai_stealth_info_with_error(aip/*, 0*/);
13833                         }
13834                 }
13835         }
13836
13837         /*      if ((Pl_objp != NULL) && (En_objp != NULL)) {
13838                 slide_face_ship();
13839                 return;
13840         }
13841 */
13842         // AL 12-10-97: ensure that cargo and navbuoys aip->target_objnum is always -1.
13843         if ( Ship_info[shipp->ship_info_index].flags & SIF_HARMLESS ) {
13844                 aip->target_objnum = -1;
13845         }
13846
13847         if ((En_objp != NULL) && (En_objp->pos.x == Pl_objp->pos.x) && (En_objp->pos.y == Pl_objp->pos.y) && (En_objp->pos.z == Pl_objp->pos.z)) {
13848                 mprintf(("Warning: Object and its enemy have same position.  Object #%i\n", Pl_objp-Objects));
13849                 En_objp = NULL;
13850         }
13851
13852         if (aip->mode == AIM_CHASE) {
13853                 if (En_objp == NULL) {
13854                         aip->active_goal = -1;
13855                 }
13856         }
13857
13858         //      If there is a goal to resume and enough time has elapsed, resume the goal.
13859         if ((aip->resume_goal_time > 0) && (aip->resume_goal_time < Missiontime)) {
13860                 aip->active_goal = AI_GOAL_NONE;
13861                 aip->resume_goal_time = -1;
13862                 target_objnum = find_enemy(objnum, 2000.0f, Skill_level_max_attackers[Game_skill_level]);
13863                 if (target_objnum != -1) {
13864                         if (aip->target_objnum != target_objnum) {
13865                                 aip->aspect_locked_time = 0.0f;
13866                         }
13867                         set_target_objnum(aip, target_objnum);
13868                 }
13869         }
13870
13871         // check if targeted subsystem has been destroyed, if so, move onto another subsystem
13872         // if trying to disable or disarm the target
13873         if ((En_objp != NULL) && ( aip->targeted_subsys != NULL )) {
13874                 Assert(En_objp->type == OBJ_SHIP);
13875                 if ( aip->targeted_subsys->current_hits <= 0.0f ) {
13876                         int subsys_type;
13877
13878                         if ( aip->goals[0].ai_mode == AI_GOAL_DISABLE_SHIP ) {
13879                                 subsys_type = SUBSYSTEM_ENGINE;
13880                         } else if ( aip->goals[0].ai_mode == AI_GOAL_DISARM_SHIP ) {
13881                                 subsys_type = SUBSYSTEM_TURRET;
13882                         } else {
13883                                 subsys_type = -1;
13884                         }
13885
13886                         if ( subsys_type != -1 ) {
13887                                 ship_subsys *new_subsys;
13888                                 new_subsys = ship_return_next_subsys(&Ships[En_objp->instance], subsys_type, &Pl_objp->pos);
13889                                 if ( new_subsys != NULL ) {
13890                                         set_targeted_subsys(aip, new_subsys, aip->target_objnum);
13891                                 } else {
13892                                         // AL 12-16-97: no more subsystems to attack... reset targeting info
13893                                         aip->target_objnum = -1;
13894                                         set_targeted_subsys(aip, NULL, -1);
13895                                 }
13896                         } else {
13897                                 // targeted subsys is destroyed, so stop attacking it
13898                                 set_targeted_subsys(aip, NULL, -1);
13899                         }
13900                 }
13901         }
13902
13903         ai_maybe_launch_cmeasure(Pl_objp, aip);
13904         ai_maybe_evade_locked_missile(Pl_objp, aip);
13905
13906         aip->target_time += flFrametime;
13907
13908         int in_formation = 0;
13909         if (aip->ai_flags & AIF_FORMATION) {
13910                 in_formation = !ai_formation();
13911         }
13912
13913         if ( !in_formation ) {
13914                 ai_execute_behavior(aip);
13915         }
13916
13917         process_subobjects(objnum);
13918         maybe_resume_previous_mode(Pl_objp, aip);
13919         
13920         if (Pl_objp->phys_info.flags & PF_AFTERBURNER_ON ) {
13921                 if (Missiontime > aip->afterburner_stop_time) {
13922                         //nprintf(("AI", "Frame %i, turning off afterburner.\n", AI_FrameCount));
13923                         afterburners_stop(Pl_objp);
13924                 }
13925         }
13926 //      validate_mode_submode(aip);
13927 }
13928
13929 int Waypoints_created = 0;
13930
13931 //      Find the ship with the name *name in the Ship_info array.
13932 int find_ship_name(char *name)
13933 {
13934         int     i;
13935
13936         for (i=0; i<Num_ship_types; i++)
13937                 if (!strcmp(Ship_info[i].name, name))
13938                         return i;
13939
13940         return -1;
13941 }
13942
13943 void create_waypoints()
13944 {
13945         int     i, j, z;
13946
13947         // Waypoints_created = 1;
13948
13949         if (Waypoints_created)
13950                 return;
13951
13952         for (j=0; j<Num_waypoint_lists; j++)
13953                 for (i=0; i<Waypoint_lists[j].count; i++) {
13954                         z = obj_create(OBJ_WAYPOINT, 0, j * 65536 + i, NULL,
13955                                 &Waypoint_lists[j].waypoints[i], 0.0f, OF_RENDERS);
13956                 }
13957
13958         Waypoints_created = 1;
13959 }
13960
13961 int Last_ai_obj = -1;
13962
13963 void ai_process( object * obj, int ai_index, float frametime )
13964 {
13965 //      if (Ships[obj->instance].flags & SF_DYING)
13966 //              nprintf(("AI", "Frame: %i Ship %s is dying!\n", Framecount, Ships[obj->instance].ship_name));
13967
13968         if (obj->flags & OF_SHOULD_BE_DEAD)
13969                 return;
13970
13971         // return if ship is dead, unless it's a big ship...then its turrets still fire, like I was quoted in a magazine.  -- MK, 5/15/98.
13972         if ((Ships[obj->instance].flags & SF_DYING ) && !(Ship_info[Ships[obj->instance].ship_info_index].flags & (SIF_HUGE_SHIP | SIF_BIG_SHIP))){
13973                 return;
13974         }
13975
13976         int rfc = 1;            //      Assume will be Reading Flying Controls.
13977
13978         Assert( obj->type == OBJ_SHIP );
13979         Assert( ai_index >= 0 );
13980
13981         init_ship_info();
13982
13983         create_waypoints();
13984
13985         AI_frametime = frametime;
13986         if (obj-Objects <= Last_ai_obj) {
13987                 AI_FrameCount++;
13988         }
13989
13990         memset( &AI_ci, 0, sizeof(AI_ci) );
13991
13992         ai_frame(obj-Objects);
13993
13994         AI_ci.pitch = 0.0f;
13995         AI_ci.bank = 0.0f;
13996         AI_ci.heading = 0.0f;
13997
13998         // the ships maximum velocity now depends on the energy flowing to engines
13999         obj->phys_info.max_vel.z = Ships[obj->instance].current_max_speed;
14000         ai_info *aip = &Ai_info[Ships[obj->instance].ai_index];
14001
14002         //      In certain circumstances, the AI says don't fly in the normal way.
14003         //      One circumstance is in docking and undocking, when the ship is moving
14004         //      under thruster control.
14005         switch (aip->mode) {
14006         case AIM_DOCK:
14007                 if ((aip->submode >= AIS_DOCK_2) && (aip->submode != AIS_UNDOCK_3))
14008                         rfc = 0;
14009                 break;
14010         case AIM_WARP_OUT:
14011                 if (aip->submode >= AIS_WARP_3)
14012                         rfc = 0;
14013                 break;
14014 //      case AIM_NONE:
14015 //              if (aip->submode == AIS_NONE_FORMATION)
14016 //                      rfc = 0;
14017 //              break;
14018         default:
14019                 break;
14020         }
14021
14022         if (rfc == 1) {
14023                 vector copy_desired_rotvel = obj->phys_info.rotvel;
14024                 physics_read_flying_controls( &obj->orient, &obj->phys_info, &AI_ci, frametime);
14025                 // if obj is in formation and not flight leader, don't update rotvel
14026                 if (aip->ai_flags & AIF_FORMATION) {
14027                         if (&Objects[aip->goal_objnum] != obj) {
14028                                 obj->phys_info.desired_rotvel = copy_desired_rotvel;
14029                                 obj->phys_info.rotvel = copy_desired_rotvel;
14030                         }
14031                 }
14032         }
14033
14034         Last_ai_obj = obj-Objects;
14035 }
14036
14037 //      Initialize ai_info struct of object objnum.
14038 void init_ai_object(int objnum)
14039 {
14040         int     ship_index, ai_index;
14041         ai_info *aip;
14042         int ship_type;
14043         object  *objp;
14044         vector  near_vec;                       //      A vector nearby and mainly in front of this object.
14045
14046         objp = &Objects[objnum];
14047         ship_index = objp->instance;
14048         ai_index = Ships[ship_index].ai_index;
14049         Assert((ai_index >= 0) && (ai_index < MAX_AI_INFO));
14050
14051         aip = &Ai_info[ai_index];
14052
14053         ship_type = Ships[ship_index].ship_info_index;
14054
14055         vm_vec_scale_add(&near_vec, &objp->pos, &objp->orient.fvec, 100.0f);
14056         vm_vec_scale_add2(&near_vec, &objp->orient.rvec, 10.0f);
14057
14058         // Things that shouldn't have to get initialized, but initialize them just in case!
14059         aip->ai_flags = 0;
14060         aip->previous_mode = AIM_NONE;
14061         aip->mode_time = -1;
14062         aip->target_objnum = -1;
14063         aip->target_signature = -1;
14064         aip->previous_target_objnum = -1;
14065         aip->target_time = 0.0f;
14066         aip->enemy_wing = -1;
14067         aip->attacker_objnum = -1;
14068         aip->goal_objnum = -1;
14069         aip->goal_signature = -1;
14070         aip->guard_objnum = -1;
14071         aip->guard_signature = -1;
14072         aip->guard_wingnum = -1;
14073         aip->dock_signature = -1;
14074         aip->submode = 0;
14075         aip->previous_submode = 0;
14076         aip->best_dot_to_enemy = -1.0f;
14077         aip->best_dot_from_enemy = -1.0f;
14078         aip->best_dot_to_time = 0;
14079         aip->best_dot_from_time = 0;
14080         aip->submode_start_time = 0;
14081         aip->submode_parm0 = 0;
14082         aip->active_goal = -1;
14083         aip->goal_check_time = timestamp(0);
14084         aip->last_predicted_enemy_pos = near_vec;
14085         aip->prev_goal_point = near_vec;
14086         aip->goal_point = near_vec;
14087         aip->time_enemy_in_range = 0.0f;
14088         aip->last_attack_time = 0;
14089         aip->last_hit_time = 0;
14090         aip->last_hit_quadrant = 0;
14091         aip->hitter_objnum = -1;
14092         aip->hitter_signature = -1;
14093         aip->resume_goal_time = -1;
14094         aip->prev_accel = 0.0f;
14095         aip->prev_dot_to_goal = 0.0f;
14096
14097         aip->ignore_objnum = UNUSED_OBJNUM;
14098         aip->ignore_signature = -1;
14099
14100         // aip->mode = AIM_NONE;
14101
14102         // End of Things that shouldn't have to get initialized, but initialize them just in case!
14103
14104         aip->ai_courage = Ai_classes[Ship_info[ship_type].ai_class].ai_courage[Game_skill_level];
14105         aip->ai_patience = Ai_classes[Ship_info[ship_type].ai_class].ai_patience[Game_skill_level];
14106         aip->ai_evasion = Ai_classes[Ship_info[ship_type].ai_class].ai_evasion[Game_skill_level];
14107         aip->ai_accuracy = Ai_classes[Ship_info[ship_type].ai_class].ai_accuracy[Game_skill_level];
14108
14109         if (Num_waypoint_lists > 0) {
14110                 aip->wp_index = -1;
14111                 aip->wp_list = -1;
14112         } else {
14113                 aip->wp_index = -1;
14114                 aip->wp_list = -1;
14115         }
14116
14117         aip->attacker_objnum = -1;
14118         aip->goal_signature = -1;
14119
14120         Objects[objnum].phys_info.prev_fvec = Objects[objnum].orient.fvec;
14121
14122         aip->last_predicted_enemy_pos.x = 0.0f; //      Says this value needs to be recomputed!
14123         aip->time_enemy_in_range = 0.0f;
14124
14125         aip->resume_goal_time = -1;                                     //      Say there is no goal to resume.
14126
14127         aip->active_goal = -1;
14128         aip->path_start = -1;
14129         aip->path_goal_dist = -1;
14130         aip->path_length = 0;
14131         aip->path_subsystem_next_check = 1;
14132         aip->dock_path_index = -1;
14133         aip->dock_index = -1;
14134         aip->dock_objnum = -1;
14135
14136         aip->danger_weapon_objnum = -1;
14137         aip->danger_weapon_signature = -1;
14138
14139         aip->lead_scale = 0.0f;
14140         aip->last_hit_target_time = Missiontime;
14141
14142         aip->nearest_locked_object = -1;
14143         aip->nearest_locked_distance = 99999.0f;
14144
14145         aip->targeted_subsys = NULL;
14146         aip->last_subsys_target = NULL;
14147         aip->targeted_subsys_parent = -1;
14148
14149         // The next two fields are used to time the rearming to allow useful sound effects for missile rearming
14150         aip->rearm_first_missile = TRUE;                //      flag to indicate that next missile to load is the first missile
14151         aip->rearm_release_delay = 0;                   //      timestamp to delay the separation of docked ships after rearm
14152
14153         aip->next_predict_pos_time = 0;
14154
14155         aip->afterburner_stop_time = 0;
14156         aip->last_objsig_hit = -1;                              // object signature of the ship most recently hit by aip
14157
14158         aip->path_next_create_time = timestamp(1);
14159         aip->path_create_pos = Objects[objnum].pos;
14160         aip->path_create_orient = Objects[objnum].orient;
14161
14162         aip->ignore_expire_timestamp = timestamp(1);
14163         aip->warp_out_timestamp = 0;
14164         aip->next_rearm_request_timestamp = timestamp(1);
14165         aip->primary_select_timestamp = timestamp(1);
14166         aip->secondary_select_timestamp = timestamp(1);
14167         aip->scan_for_enemy_timestamp = timestamp(1);
14168
14169         aip->choose_enemy_timestamp = timestamp(3*(NUM_SKILL_LEVELS-Game_skill_level) * ((rand_alt() % 500) + 500));
14170
14171         aip->shockwave_object = -1;
14172         aip->shield_manage_timestamp = timestamp(1);
14173         aip->self_destruct_timestamp = -1;      //      This is a flag that we have not yet set this.
14174         aip->ok_to_target_timestamp = timestamp(1);
14175         aip->pick_big_attack_point_timestamp = timestamp(1);
14176         vm_vec_zero(&aip->big_attack_point);
14177
14178         aip->avoid_check_timestamp = timestamp(1);
14179
14180         aip->abort_rearm_timestamp = -1;
14181
14182         // artillery stuff
14183         aip->artillery_objnum = -1;
14184         aip->artillery_sig = -1;        
14185
14186         // waypoint speed cap
14187         aip->waypoint_speed_cap = -1;
14188
14189         // set lethality to enemy team
14190         aip->lethality = 0.0f;
14191 }
14192
14193 void init_ai_objects()
14194 {
14195         int     i;
14196
14197         for (i=0; i<num_objects; i++){
14198                 if (Objects[i].type == OBJ_SHIP){
14199                         init_ai_object(i);
14200                 }
14201         }
14202 }
14203
14204 void init_ai_system()
14205 {
14206         // MWA -- removed next line of code on 11/12/97.  When a ship is created
14207         // it calls init_ai_object() on it's objnum.  Doing this init at the point where
14208         // this function gets called messes things up.
14209         //init_ai_objects();
14210
14211         Ppfp = Path_points;
14212         Waypoints_created = 0;
14213
14214         Dock_path_warning_given = 0;
14215
14216 /*      for (int i=0; i<MAX_IGNORE_OBJECTS; i++) {
14217                 Ignore_objects[i].objnum = -1;
14218                 Ignore_objects[i].signature = -1;
14219         }
14220 */
14221
14222 }
14223
14224 void ai_set_default_behavior(object *obj, int classnum)
14225 {
14226         ai_info *aip;
14227
14228         Assert(obj != NULL);
14229         Assert(obj->instance != -1);
14230         Assert(Ships[obj->instance].ai_index != -1);
14231
14232         aip = &Ai_info[Ships[obj->instance].ai_index];
14233
14234         aip->behavior = classnum;
14235
14236 }
14237
14238 void ai_do_default_behavior(object *obj)
14239 {
14240         ai_info *aip;
14241         int             ship_flags;
14242
14243         Assert(obj != NULL);
14244         Assert(obj->instance != -1);
14245         Assert(Ships[obj->instance].ai_index != -1);
14246
14247         aip = &Ai_info[Ships[obj->instance].ai_index];
14248
14249         ship_flags = Ship_info[Ships[obj->instance].ship_info_index].flags;
14250         if (!is_instructor(obj) && (ship_flags & (SIF_FIGHTER | SIF_BOMBER))) {
14251                 int enemy_objnum = find_enemy(OBJ_INDEX(obj), 1000.0f, Skill_level_max_attackers[Game_skill_level]);
14252                 set_target_objnum(aip, enemy_objnum);
14253                 aip->mode = AIM_CHASE;
14254                 aip->submode = SM_ATTACK;
14255         } else if (ship_flags & (SIF_SUPPORT)) {
14256                 aip->mode = AIM_SAFETY;
14257                 aip->submode = AISS_1;
14258                 aip->ai_flags &= ~(AIF_REPAIRING);
14259         } else if ( ship_flags & SIF_SENTRYGUN ) {
14260                 aip->mode = AIM_SENTRYGUN;
14261         } else {
14262                 aip->mode = AIM_NONE;
14263         }
14264         
14265         aip->submode_start_time = Missiontime;
14266         aip->active_goal = AI_GOAL_NONE;
14267 }
14268
14269 #define FRIENDLY_DAMAGE_THRESHOLD       50.0f           //      Display a message at this threshold.  Note, this gets scaled by Skill_level
14270
14271 // send the given message from objp.  called from the maybe_process_friendly_hit
14272 // code below when a message must get send to the player when he fires on friendlies
14273 void process_friendly_hit_message( int message, object *objp )
14274 {
14275         int index;
14276
14277         // no traitor in multiplayer
14278         if(Game_mode & GM_MULTIPLAYER){
14279                 return;
14280         }
14281
14282         // don't send this message if a player ship was hit.
14283         if ( objp->flags & OF_PLAYER_SHIP ){
14284                 return;
14285         }
14286
14287         // check if objp is a cargo contianer -- if so, then find a new ship to send the message
14288         index = objp->instance;
14289         if ( !(Ship_info[Ships[objp->instance].ship_info_index].flags & (SIF_FIGHTER|SIF_BOMBER)) ){
14290                 index = -1;
14291         }
14292
14293         // if the message is "oops" (the don't hit me message), always make come from Terran command
14294         if ( message == MESSAGE_OOPS ){
14295                 index = -1;
14296         }
14297
14298         if ( index >= 0){
14299                 message_send_builtin_to_player( message, &Ships[index], MESSAGE_PRIORITY_HIGH, MESSAGE_TIME_ANYTIME, 0, 0, -1, -1 );
14300         } else {
14301                 message_send_builtin_to_player( message, NULL, MESSAGE_PRIORITY_HIGH, MESSAGE_TIME_ANYTIME, 0, 0, -1, -1 );
14302         }
14303 }
14304
14305 extern  void ship_set_subsystem_strength( ship *shipp, int type, float strength );
14306
14307 //      Object *objp_weapon, fired by *objp_hitter, hit object *objp_ship.
14308 void maybe_process_friendly_hit(object *objp_hitter, object *objp_hit, object *objp_weapon)
14309 {
14310         // no turning traitor in multiplayer
14311         if ( Game_mode & GM_MULTIPLAYER ) {
14312                 return;
14313         }
14314
14315         // ditto if mission says no traitors allowed
14316         if (The_mission.flags & MISSION_FLAG_NO_TRAITOR) {
14317                 return;
14318         }
14319
14320         if ((objp_hitter == Player_obj) && (Player_ship->team == TEAM_FRIENDLY)) {
14321
14322                 // AL 12-4-97: It is possible the Player is a OBJ_GHOST at this point.  If so, bail out.
14323                 if ( objp_hitter->type != OBJ_SHIP ) {
14324                         return;
14325                 }
14326
14327                 Assert(objp_hitter->type == OBJ_SHIP);
14328                 Assert(objp_hit->type == OBJ_SHIP);
14329                 Assert(objp_weapon->type == OBJ_WEAPON);
14330
14331                 ship    *shipp_hitter = &Ships[objp_hitter->instance];
14332                 ship    *shipp_hit = &Ships[objp_hit->instance];
14333
14334                 if (shipp_hitter->team != shipp_hit->team) {
14335                         return;
14336                 }
14337
14338                 // get the player
14339                 player *pp = &Players[Player_num];
14340
14341                 // wacky stuff here
14342                 if (pp->friendly_hits != 0) {
14343                         float   time_since_last_hit = f2fl(Missiontime - pp->friendly_last_hit_time);
14344                         if ((time_since_last_hit >= 0.0f) && (time_since_last_hit < 10000.0f)) {
14345                                 if (time_since_last_hit > 60.0f) {
14346                                         pp->friendly_hits = 0;
14347                                         pp->friendly_damage = 0.0f;
14348                                 } else if (time_since_last_hit > 2.0f) {
14349                                         pp->friendly_hits -= (int) time_since_last_hit/2;
14350                                         pp->friendly_damage -= time_since_last_hit;
14351                                 }
14352
14353                                 if (pp->friendly_damage < 0.0f) {
14354                                         pp->friendly_damage = 0.0f;
14355                                 }
14356
14357                                 if (pp->friendly_hits < 0) {
14358                                         pp->friendly_hits = 0;
14359                                 }
14360                         }
14361                 }
14362
14363                 float   damage;         //      Damage done by weapon.  Gets scaled down based on size of ship.
14364
14365                 damage = Weapon_info[Weapons[objp_weapon->instance].weapon_info_index].damage;
14366                 
14367                 // wacky stuff here
14368                 ship_info *sip = &Ship_info[Ships[objp_hit->instance].ship_info_index];
14369                 if (sip->initial_hull_strength > 1000.0f) {
14370                         float factor = sip->initial_hull_strength / 1000.0f;
14371                         factor = min(100.0f, factor);
14372                         damage /= factor;
14373                 }
14374
14375                 //      Don't penalize much at all for hitting cargo
14376                 if (sip->flags & (SIF_CARGO | SIF_SENTRYGUN)) {
14377                         damage /= 10.0f;
14378                 }
14379
14380                 //      Hit ship, but not targeting it, so it's not so heinous, maybe an accident.
14381                 if (Ai_info[shipp_hitter->ai_index].target_objnum != OBJ_INDEX(objp_hit)) {
14382                         damage /= 5.0f;
14383                 }
14384
14385                 pp->friendly_last_hit_time = Missiontime;
14386                 pp->friendly_hits++;
14387
14388                 // cap damage and number of hits done this frame
14389                 float accredited_damage = min(MAX_BURST_DAMAGE, pp->damage_this_burst + damage) - pp->damage_this_burst;
14390                 pp->friendly_damage += accredited_damage;
14391                 pp->damage_this_burst += accredited_damage;
14392
14393                 // Done with adjustments to damage.  Evaluate based on current friendly_damage
14394                 nprintf(("AI", "Friendly damage: %.1f, threshold: %.1f, inc damage: %.1f, max burst: %d\n", pp->friendly_damage, FRIENDLY_DAMAGE_THRESHOLD * (1.0f + (float) (NUM_SKILL_LEVELS + 1 - Game_skill_level)/3.0f), pp->damage_this_burst, MAX_BURST_DAMAGE ));
14395                 
14396                 if (is_instructor(objp_hit)) {
14397                         // it's not nice to hit your instructor
14398                         if (pp->friendly_damage > FRIENDLY_DAMAGE_THRESHOLD) {
14399                                 message_send_builtin_to_player( MESSAGE_INSTRUCTOR_ATTACK, NULL, MESSAGE_PRIORITY_HIGH, MESSAGE_TIME_IMMEDIATE, 0, 0, -1, -1);
14400                                 pp->last_warning_message_time = Missiontime;
14401                                 ship_set_subsystem_strength( Player_ship, SUBSYSTEM_WEAPONS, 0.0f);
14402
14403                                 training_fail();
14404
14405                                 //      Instructor warp out.
14406                                 ai_set_mode_warp_out(objp_hit, &Ai_info[Ships[objp_hit->instance].ai_index]);
14407                                 gameseq_post_event( GS_EVENT_PLAYER_WARPOUT_START_FORCED );     //      Force player to warp out.
14408
14409                                 //ship_apply_global_damage( objp_hitter, objp_hit, NULL, 2*(get_shield_strength(objp_hitter) + Ship_info[shipp_hitter->ship_info_index].initial_hull_strength) );
14410                                 //ship_apply_global_damage( objp_hitter, objp_hit, NULL, 1.0f );
14411                         } else if (Missiontime - pp->last_warning_message_time > F1_0*4) {
14412                                 // warning every 4 sec
14413                                 // use NULL as the message sender here since it is the Terran Command persona
14414                                 message_send_builtin_to_player( MESSAGE_INSTRUCTOR_HIT, NULL, MESSAGE_PRIORITY_HIGH, MESSAGE_TIME_IMMEDIATE, 0, 0, -1, -1);
14415                                 pp->last_warning_message_time = Missiontime;
14416                         }
14417
14418                 // not nice to hit your friends
14419                 } else if (pp->friendly_damage > FRIENDLY_DAMAGE_THRESHOLD * (1.0f + (float) (NUM_SKILL_LEVELS + 1 - Game_skill_level)/3.0f)) {
14420                         process_friendly_hit_message( MESSAGE_HAMMER_SWINE, objp_hit );
14421                         mission_goal_fail_all();
14422                         ai_abort_rearm_request( Player_obj );
14423
14424                         Player_ship->team = TEAM_TRAITOR;
14425
14426                 } else if ((damage > frand()) && (Missiontime - pp->last_warning_message_time > F1_0*4) && (pp->friendly_damage > FRIENDLY_DAMAGE_THRESHOLD)) {
14427                         // no closer than 4 sec intervals
14428                         //      Note: (damage > frand()) added on 12/9/97 by MK.  Since damage is now scaled down for big ships, we could get too
14429                         //      many warnings.  Kind of tedious.  frand() returns a value in 0..1, so this won't affect legit hits.
14430                         process_friendly_hit_message( MESSAGE_OOPS, objp_hit );
14431                         pp->last_warning_message_time = Missiontime;
14432                 }
14433         }
14434 }
14435
14436 //      Maybe make ship with ai_info *aip attack hitter_objnum as a dynamic goal
14437 void maybe_set_dynamic_chase(ai_info *aip, int hitter_objnum)
14438 {
14439         Assert(Ship_info[Ships[aip->shipnum].ship_info_index].flags & (SIF_FIGHTER | SIF_BOMBER));
14440
14441         // limit the number of ships attacking hitter_objnum (for now, only if hitter_objnum is player)
14442         if ( ai_maybe_limit_attackers(hitter_objnum) == 1 ) {
14443                 return;
14444         }
14445
14446         // only set as target if can be targeted.
14447         if (awacs_get_level(&Objects[hitter_objnum], &Ships[aip->shipnum], 1) < 1) {
14448                 return;
14449         }
14450
14451         if (aip->target_objnum != hitter_objnum)
14452                 aip->aspect_locked_time = 0.0f;
14453         set_target_objnum(aip, hitter_objnum);
14454         aip->resume_goal_time = Missiontime + i2f(20);  //      Only chase up to 20 seconds.
14455         aip->active_goal = AI_ACTIVE_GOAL_DYNAMIC;
14456
14457         set_targeted_subsys(aip, NULL, -1);             //      Say not attacking any particular subsystem.
14458
14459         aip->previous_submode = aip->mode;
14460         aip->mode = AIM_CHASE;
14461         aip->submode = SM_ATTACK;
14462 }
14463
14464
14465 //      Return true if *objp has armed an aspect seeking bomb.
14466 //      This function written so a ship with an important bomb to fire will willingly take hits in the face to fire its bomb.
14467 int firing_aspect_seeking_bomb(object *objp)
14468 {
14469         ship    *shipp;
14470         int     bank_index;
14471         ship_weapon     *swp;
14472
14473         shipp = &Ships[objp->instance];
14474
14475         swp = &shipp->weapons;
14476
14477         bank_index = swp->current_secondary_bank;
14478
14479         if (bank_index != -1)
14480                 if (swp->secondary_bank_ammo[bank_index] > 0) {
14481                         if (Weapon_info[swp->secondary_bank_weapons[bank_index]].wi_flags & WIF_BOMB) {
14482                                 if (Weapon_info[swp->secondary_bank_weapons[bank_index]].wi_flags & WIF_HOMING_ASPECT) {
14483                                         return 1;
14484                                 }
14485                         }
14486                 }
14487
14488         return 0;
14489 }
14490
14491 //      *objp collided with big ship *big_objp at global point *collide_pos
14492 //      Make it fly away from the collision point.
14493 // collision_normal is NULL, when a collision is imminent and we just want to bug out.
14494 void big_ship_collide_recover_start(object *objp, object *big_objp, vector *collide_pos, vector *collision_normal)
14495 {
14496         ai_info *aip;
14497
14498         Assert(objp->type == OBJ_SHIP);
14499
14500         aip = &Ai_info[Ships[objp->instance].ai_index];
14501
14502         if (!timestamp_elapsed(aip->big_recover_timestamp) && (aip->ai_flags & AIF_BIG_SHIP_COLLIDE_RECOVER_1))
14503                 return;
14504
14505         //nprintf(("AI", "Ship %s beginning to avoid ship %s at time %7.3f", Ships[objp->instance].ship_name, Ships[big_objp->instance].ship_name, f2fl(Missiontime)));
14506         if (collision_normal) {
14507                 aip->big_recover_timestamp = timestamp(2000);
14508                 aip->big_collision_normal = *collision_normal;
14509         //      nprintf(("AI", " normal\n"));
14510         } else {
14511                 aip->big_recover_timestamp = timestamp(500);
14512         //      nprintf(("AI", " no normal\n"));
14513         }
14514
14515
14516         aip->ai_flags &= ~AIF_BIG_SHIP_COLLIDE_RECOVER_2;
14517         aip->ai_flags |= AIF_BIG_SHIP_COLLIDE_RECOVER_1;
14518
14519 //      vector  out_vec;
14520 //      vm_vec_normalized_dir(&out_vec, &objp->pos, collide_pos);
14521
14522         // big_recover_pos_1 is 100 m out along normal
14523         vector direction;
14524         if (collision_normal) {
14525                 direction = *collision_normal;
14526         } else {
14527                 vm_vec_copy_scale(&direction, &objp->orient.fvec, -1.0f);
14528         }
14529         vm_vec_scale_add(&aip->big_recover_pos_1, &objp->pos, &direction, 100.0f);
14530
14531         // go out 200 m from box closest box point
14532         get_world_closest_box_point_with_delta(&aip->big_recover_pos_2, big_objp, &aip->big_recover_pos_1, NULL, 300.0f);
14533
14534         accelerate_ship(aip, 0.0f);
14535 /*
14536         if (vm_vec_dot(collision_normal, &objp->orient.fvec) > 0.5f) {
14537 //              vm_vec_scale_add(&aip->big_recover_pos_1, &objp->pos, &out_vec, big_objp->radius/2.0f);
14538 //              vm_vec_scale_add(&aip->big_recover_pos_2, &aip->big_recover_pos_1, &objp->orient.uvec, big_objp->radius/2.0f);
14539 //              vm_vec_scale_add(&aip->big_recover_pos_2, &objp->pos, &out_vec, big_objp->radius*2.0f);
14540                 accelerate_ship(aip, 2.0f);
14541         } else {
14542 //              vm_vec_scale_add(&aip->big_recover_pos_1, &objp->pos, &out_vec, big_objp->radius/2.0f);
14543 //              vm_vec_scale_add(&aip->big_recover_pos_2, &aip->big_recover_pos_1, &objp->orient.uvec, big_objp->radius/2.0f);
14544                 accelerate_ship(aip, 0.0f);
14545         } */
14546 }
14547
14548 float max_lethality = 0.0f;
14549
14550 void ai_update_lethality(object *ship_obj, object *other_obj, float damage)
14551 {
14552         Assert(ship_obj->type == OBJ_SHIP);
14553         Assert(other_obj->type == OBJ_WEAPON || other_obj->type == OBJ_SHOCKWAVE);
14554         int dont_count = FALSE;
14555
14556         int parent = other_obj->parent;
14557         if (Objects[parent].type == OBJ_SHIP) {
14558                 if (Objects[parent].signature == other_obj->parent_sig) {
14559
14560                         // check damage done to enemy team
14561                         if (Ships[ship_obj->instance].team != Ships[Objects[parent].instance].team) {
14562
14563                                 // other is weapon
14564                                 if (other_obj->type == OBJ_WEAPON) {
14565                                         weapon *wp = &Weapons[other_obj->instance];
14566                                         weapon_info *wif = &Weapon_info[wp->weapon_info_index];
14567
14568                                         // if parent is BIG|HUGE, don't count beam
14569                                         if (Ship_info[Ships[Objects[parent].instance].ship_info_index].flags & (SIF_BIG_SHIP|SIF_HUGE_SHIP)) {
14570                                                 if (wif->wi_flags & WIF_BEAM) {
14571                                                         dont_count = TRUE;
14572                                                 }
14573                                         }
14574                                 }
14575
14576                                 if (!dont_count) {
14577                                         float lethality = 0.025f * damage;      // 2 cyclops (@2000) put you at 100 lethality
14578
14579                                         // increase lethality weapon's parent ship
14580                                         ai_info *aip = &Ai_info[Ships[Objects[parent].instance].ai_index];
14581                                         aip->lethality += lethality;
14582                                         aip->lethality = min(110.0f, aip->lethality);
14583                                         // if you hit, don;t be less than 0
14584                                         aip->lethality = max(0.0f, aip->lethality);
14585
14586 //                                      if (aip->lethality > max_lethality) {
14587 //                                              max_lethality = aip->lethality;
14588 //                                              mprintf(("new lethalilty high: %.1f\n", max_lethality));
14589 //                                      }
14590
14591                                         // if parent is player, show his lethality
14592 //                                      if (Objects[parent].flags & OF_PLAYER_SHIP) {
14593 //                                              mprintf(("Player lethality: %.1f\n", aip->lethality));
14594 //                                      }
14595                                 }
14596                         }
14597                 }
14598         }
14599 }
14600
14601
14602 //      Object *objp_ship was hit by either weapon *objp_weapon or collided into by ship hit_objp at point *hitpos.
14603 void ai_ship_hit(object *objp_ship, object *hit_objp, vector *hitpos, int shield_quadrant, vector *hit_normal)
14604 {
14605         int             hitter_objnum = -2;
14606         object  *objp_hitter = NULL;
14607         ship            *shipp;
14608         ai_info *aip, *hitter_aip;
14609
14610         shipp = &Ships[objp_ship->instance];
14611         aip = &Ai_info[shipp->ai_index];
14612
14613         if (objp_ship->flags & OF_PLAYER_SHIP)
14614                 return;
14615
14616         if ((aip->mode == AIM_WARP_OUT) || (aip->mode == AIM_PLAY_DEAD))
14617                 return;
14618
14619         if (hit_objp->type == OBJ_SHIP) {
14620                 //      If the object that this ship collided with is a big ship
14621                 if (Ship_info[Ships[hit_objp->instance].ship_info_index].flags & (SIF_BIG_SHIP | SIF_HUGE_SHIP)) {
14622                         //      And the current object is _not_ a big ship
14623                         if (!(Ship_info[Ships[objp_ship->instance].ship_info_index].flags & (SIF_BIG_SHIP | SIF_HUGE_SHIP))) {
14624                                 //      Recover from hitting a big ship.  Note, if two big ships collide, they just pound away at each other.  Oh well.  Recovery looks dumb and it's very late.
14625                                 big_ship_collide_recover_start(objp_ship, hit_objp, hitpos, hit_normal);
14626                         }
14627                 }
14628         }
14629
14630         if (hit_objp->type == OBJ_WEAPON) {
14631                 //      Make sure the object that fired this weapon is still alive.  If not, abort.
14632                 // Assert(hit_objp->parent >= 0);
14633                 if(hit_objp->parent < 0){
14634                         return;
14635                 }
14636                 if ( hit_objp->parent_sig != Objects[hit_objp->parent].signature ){
14637                         return;
14638                 }
14639
14640                 //      Hit by a protected ship, don't attack it.
14641                 if (Objects[hit_objp->parent].flags & OF_PROTECTED) {
14642                         if ((Ship_info[shipp->ship_info_index].flags & (SIF_FIGHTER | SIF_BOMBER)) && (aip->target_objnum == -1)) {
14643                                 if (aip->mode == AIM_CHASE) {
14644                                         if (aip->submode != SM_EVADE_WEAPON) {
14645                                                 aip->mode = AIM_CHASE;
14646                                                 aip->submode = SM_EVADE_WEAPON;
14647                                                 aip->submode_start_time = Missiontime;
14648                                         }
14649                                 } else if (aip->mode != AIM_EVADE_WEAPON) {
14650                                         aip->active_goal = AI_ACTIVE_GOAL_DYNAMIC;
14651                                         aip->previous_mode = aip->mode;
14652                                         aip->previous_submode = aip->submode;
14653                                         aip->mode = AIM_EVADE_WEAPON;
14654                                         aip->submode = -1;
14655                                         aip->submode_start_time = Missiontime;
14656                                         aip->mode_time = timestamp(MAX_EVADE_TIME);     //      Evade for up to five seconds.
14657                                 }
14658
14659                         }
14660                         return;
14661                 }
14662
14663                 hitter_objnum = hit_objp->parent;
14664                 Assert((hitter_objnum >= 0) && (hitter_objnum < MAX_OBJECTS));
14665                 objp_hitter = &Objects[hitter_objnum];
14666                 maybe_process_friendly_hit(objp_hitter, objp_ship, hit_objp);           //      Deal with player's friendly fire.
14667
14668                 if ( (shipp->team & TEAM_FRIENDLY) && !(Game_mode & GM_MULTIPLAYER) ) {
14669                         ship_maybe_ask_for_help(shipp);
14670                 }
14671         } else if (hit_objp->type == OBJ_SHIP) {
14672                 if (shipp->team == Ships[hit_objp->instance].team)              //      Don't have AI react to collisions between teammates.
14673                         return;
14674                 objp_hitter = hit_objp;
14675                 hitter_objnum = hit_objp-Objects;
14676         } else {
14677                 Int3(); //      Hmm, what kind of object hit this if not weapon or ship?  Get MikeK.
14678                 return;
14679         }
14680
14681         //      Collided into a protected ship, don't attack it.
14682         if (hit_objp->flags & OF_PROTECTED)
14683                 return;
14684
14685         Assert(objp_hitter != NULL);
14686         hitter_aip = &Ai_info[Ships[objp_hitter->instance].ai_index];
14687         hitter_aip->last_hit_target_time = Missiontime;
14688         
14689         // store the object signature of objp_ship into ai_info, since we want to track the last ship hit by 'hitter_objnum'
14690         hitter_aip->last_objsig_hit = objp_ship->signature; 
14691
14692         aip->last_hit_time = Missiontime;
14693
14694         if (aip->ai_flags & (AIF_NO_DYNAMIC | AIF_KAMIKAZE))    //      If not allowed to pursue dynamic objectives, don't evade.  Dumb?  Maybe change. -- MK, 3/15/98
14695                 return;
14696
14697         //      If this ship is awaiting repair, abort!
14698         if (aip->ai_flags & (AIF_AWAITING_REPAIR | AIF_BEING_REPAIRED)) {
14699                 ship_info       *sip = &Ship_info[shipp->ship_info_index];
14700
14701                 if (objp_ship->hull_strength/sip->initial_hull_strength < 0.3f) {
14702                         //      No, only abort if hull below a certain level.
14703                         aip->next_rearm_request_timestamp = timestamp(NEXT_REARM_TIMESTAMP/2);  //      Might request again after 15 seconds.
14704                         if ( !(objp_ship->flags & OF_PLAYER_SHIP) )                                             // mwa -- don't abort rearm for a player
14705                                 ai_abort_rearm_request(objp_ship);
14706                 }
14707         }
14708
14709         //      If firing a bomb, ignore enemy fire so we can gain lock drop the bomb.
14710         //      Only ignore fire if aspect_locked_time > 0.5f, as this means we're in range.
14711         if (firing_aspect_seeking_bomb(objp_ship)) {
14712                 if ((aip->ai_flags & AIF_SEEK_LOCK) && (aip->aspect_locked_time > 0.1f))
14713                         return;
14714         }
14715
14716         //      If in AIM_STRAFE mode and got hit by target, maybe attack turret if appropriate
14717         if (aip->mode == AIM_STRAFE) {
14718                 Assert(hitter_objnum != -2);
14719                 if (aip->target_objnum == hitter_objnum) {
14720                         if ( hit_objp->type == OBJ_WEAPON ) {
14721                                 ai_big_strafe_maybe_attack_turret(objp_ship, hit_objp);
14722                         }
14723                         return;
14724                 }
14725                 else {
14726                                 // AL 11-10-97:
14727                         ;       // do nothing here, we'll attack this hitter if it is a fighter or bomber (this is handled
14728                                 // in code later in this function
14729                 }
14730         }
14731
14732         if (objp_ship == Player_obj)
14733                 return;         //      We don't do AI for the player.
14734
14735         maybe_update_guard_object(objp_ship, objp_hitter);
14736
14737         //      Big ships don't go any further.
14738         if (!(Ship_info[shipp->ship_info_index].flags & SIF_SMALL_SHIP))
14739                 return;
14740
14741         //      If the hitter object is the ignore object, don't attack it.
14742         ship_info       *sip = &Ship_info[shipp->ship_info_index];
14743         if ((is_ignore_object(aip, objp_hitter-Objects)) && (sip->flags & (SIF_BOMBER | SIF_FIGHTER))) {
14744                 if (aip->mode == AIM_NONE) {
14745                         aip->mode = AIM_CHASE;  //      This will cause the ship to move, if not attack.
14746                         aip->submode = SM_EVADE;
14747                 }
14748                 return;
14749         }
14750
14751         //      Maybe abort based on mode.
14752         switch (aip->mode) {
14753         case AIM_CHASE:
14754                 if (aip->submode == SM_ATTACK_FOREVER)
14755                         return;
14756
14757                 if ( hit_objp->type == OBJ_WEAPON ) {
14758                         if ( ai_big_maybe_enter_strafe_mode(objp_ship, OBJ_INDEX(hit_objp), 1) )
14759                                 return;
14760                 }
14761
14762         case AIM_GUARD:
14763                 //      If in guard mode and far away from guard object, don't pursue guy that hit me.
14764                         if ((aip->guard_objnum != -1) && (aip->guard_signature == Objects[aip->guard_objnum].signature)) {
14765                                 if (vm_vec_dist_quick(&objp_ship->pos, &Objects[aip->guard_objnum].pos) > 500.0f) {
14766                                         return;
14767                                 }
14768                         }
14769         case AIM_STILL:
14770         case AIM_STAY_NEAR:
14771                 // Note: Dealt with above, at very top.  case AIM_PLAY_DEAD:
14772         case AIM_STRAFE:
14773                 break;
14774         case AIM_EVADE_WEAPON:
14775         case AIM_EVADE:
14776         case AIM_GET_BEHIND:
14777         case AIM_AVOID:
14778         case AIM_DOCK:
14779         case AIM_BIGSHIP:
14780         case AIM_PATH:
14781         case AIM_NONE:
14782         case AIM_BAY_DEPART:
14783         case AIM_SENTRYGUN:
14784                 return;
14785         case AIM_BAY_EMERGE:
14786                 // If just leaving the docking bay, don't react to enemy fire... just keep flying away from docking bay
14787                 if ( (Missiontime - aip->submode_start_time) < 5*F1_0 ) {
14788                         return;
14789                 }
14790                 break;
14791         case AIM_WAYPOINTS:
14792                 if (sip->flags & (SIF_FIGHTER | SIF_BOMBER))
14793                         break;
14794                 else
14795                         return;
14796                 break;
14797         case AIM_SAFETY:
14798                 if ((aip->submode != AISS_1) || (Missiontime - aip->submode_start_time > i2f(1))) {
14799                         aip->submode = AISS_1;
14800                         aip->submode_start_time = Missiontime;
14801                 }
14802                 return;
14803                 break;
14804         case AIM_WARP_OUT:
14805                 return;
14806                 break;
14807         default:
14808                 Int3(); //      Bogus mode!
14809         }
14810
14811         if (timestamp_elapsed(aip->ok_to_target_timestamp))
14812                 aip->ai_flags &= ~AIF_FORMATION;                        //      If flying in formation, bug out!
14813
14814         aip->hitter_objnum = hitter_objnum;
14815         aip->hitter_signature = Objects[hitter_objnum].signature;
14816
14817         //      If the hitter is not on the same team as the hittee, do some stuff.
14818         if (shipp->team != Ships[objp_hitter->instance].team) {
14819                 //nprintf(("AI", "Object %i attacking %i, who just hit him!\n", objp_ship-Objects, hitter_objnum));
14820
14821                 if ((hitter_objnum != aip->target_objnum) && (sip->flags & (SIF_FIGHTER | SIF_BOMBER))) {
14822                         maybe_set_dynamic_chase(aip, hitter_objnum);
14823                         maybe_afterburner_after_ship_hit(objp_ship, aip, &Objects[hitter_objnum]);
14824                 } else {
14825                         if ((aip->mode == AIM_CHASE) && ((objp_ship->hull_strength/sip->initial_hull_strength > 0.9f) || (get_shield_strength(objp_ship)/sip->shields > 0.8f))) {
14826                                 switch (aip->submode) {
14827                                 case SM_ATTACK:
14828                                 case SM_SUPER_ATTACK:
14829                                 case SM_GET_AWAY:
14830                                         break;
14831                                 default:
14832                                         if (sip->flags & (SIF_FIGHTER | SIF_BOMBER)) {
14833                                                 maybe_set_dynamic_chase(aip, hitter_objnum);
14834                                         }
14835                                         maybe_afterburner_after_ship_hit(objp_ship, aip, &Objects[hitter_objnum]);
14836                                         break;
14837                                 }
14838                         } else if (aip->mode == AIM_CHASE) {
14839                                 switch (aip->submode) {
14840                                 case SM_ATTACK:
14841                                         aip->submode = SM_EVADE;
14842                                         aip->submode_start_time = Missiontime;
14843                                         break;
14844                                 case SM_SUPER_ATTACK:
14845                                         if (Missiontime - aip->submode_start_time > i2f(1)) {
14846                                                 aip->submode = SM_EVADE;
14847                                                 aip->submode_start_time = Missiontime;
14848                                         }
14849                                         break;
14850                                 case SM_EVADE_BRAKE:
14851                                         break;
14852                                 case SM_EVADE_SQUIGGLE:
14853                                         aip->submode = SM_EVADE;
14854                                         aip->submode_start_time = Missiontime;
14855                                         break;
14856                                 default:
14857                                         if (sip->flags & (SIF_BOMBER | SIF_FIGHTER)) {
14858                                                 maybe_set_dynamic_chase(aip, hitter_objnum);
14859                                                 maybe_afterburner_after_ship_hit(objp_ship, aip, &Objects[hitter_objnum]);
14860                                         }
14861
14862                                         break;
14863                                 }
14864                         } else {
14865                                 // AL 3-15-98: Prevent escape pods from entering chase mode
14866                                 if ( (sip->flags & (SIF_BOMBER | SIF_FIGHTER)) ) {
14867                                         maybe_set_dynamic_chase(aip, hitter_objnum);
14868                                 }
14869                                 maybe_afterburner_after_ship_hit(objp_ship, aip, &Objects[hitter_objnum]);
14870                         }
14871                 }
14872         }
14873 }
14874
14875 //      Ship shipnum has been destroyed.
14876 //      Cleanup.
14877 // the parameter 'method' is used to tell is this ship was destroyed or it departed normally.
14878 // This function will get called in either case, and there are things that should be done if
14879 // the ship actually gets destroyed which shouldn't get done if it departed.
14880 void ai_ship_destroy(int shipnum, int method)
14881 {
14882         int             objnum;
14883         object  *other_objp;
14884         ship            *shipp;
14885         ship_obj        *so;
14886         ai_info *dead_aip;
14887
14888         Assert((shipnum >= 0) && (shipnum < MAX_SHIPS));
14889         objnum = Ships[shipnum].objnum;
14890         dead_aip = &Ai_info[Ships[shipnum].ai_index];
14891
14892         // if I was getting repaired, or awaiting repair, then cleanup the repair mode.  When awaiting repair, the dock_objnum
14893         // is -1.  When the support ship is on the way, the dock_objnum >= 0 (points to support ship).
14894         if ( dead_aip->ai_flags & (AIF_AWAITING_REPAIR | AIF_BEING_REPAIRED) ) {
14895                 if ( dead_aip->dock_objnum >= 0 )
14896                         ai_do_objects_repairing_stuff( &Objects[objnum], &Objects[dead_aip->dock_objnum], REPAIR_INFO_END);
14897                 else
14898                         ai_do_objects_repairing_stuff( &Objects[objnum], NULL, REPAIR_INFO_END );
14899         }
14900
14901         //      For all objects that had this ship as a target, wipe it out, forcing find of a new enemy.
14902         for ( so = GET_FIRST(&Ship_obj_list); so != END_OF_LIST(&Ship_obj_list); so = GET_NEXT(so) ) {
14903                 other_objp = &Objects[so->objnum];
14904                 Assert(other_objp->instance != -1);
14905
14906                 shipp = &Ships[other_objp->instance];
14907                 Assert(shipp->ai_index != -1);
14908
14909                 ai_info *aip = &Ai_info[shipp->ai_index];
14910
14911                 // MWA 2/11/98
14912                 // code commented out below is taken care of in ai_cleanup_dock_mode when gets called when the
14913                 // support ship starts it's death roll.
14914
14915                 //      If the destroyed ship was on its way to repair the current ship
14916                 if (aip->dock_objnum == objnum) {
14917
14918                         // clean up the flags for any kind of docking mode.  If aip was part of a goal of dock/undock
14919                         // then it will get cleaned up by the goal code.
14920                         ai_do_objects_undocked_stuff( other_objp, NULL );
14921
14922                         if ( aip->ai_flags & (AIF_AWAITING_REPAIR | AIF_BEING_REPAIRED) ) {
14923                                 int abort_reason;
14924                                 if ( method == SEF_DEPARTED ) {
14925                                         abort_reason = REPAIR_INFO_ABORT;
14926                                 } else {
14927                                         abort_reason = REPAIR_INFO_KILLED;
14928                                 }
14929                                 ai_do_objects_repairing_stuff( other_objp, NULL, abort_reason );
14930                         }
14931                 }
14932
14933                 if (aip->target_objnum == objnum) {
14934                         set_target_objnum(aip, -1);
14935                         //      If this ship had a dynamic goal of chasing the dead ship, clear the dynamic goal.
14936                         if (aip->resume_goal_time != -1)
14937                                 aip->active_goal = AI_GOAL_NONE;
14938                 }
14939
14940                 if (aip->goal_objnum == objnum) {
14941                         aip->goal_objnum = -1;
14942                         aip->goal_signature = -1;
14943                 }
14944
14945                 if (aip->guard_objnum == objnum) {
14946                         aip->guard_objnum = -1;
14947                         aip->guard_signature = -1;
14948                 }
14949
14950                 if ((aip->guard_wingnum != -1) && (aip->guard_wingnum == Ai_info[Ships[Objects[objnum].instance].ai_index].wing)) {
14951                         if (aip->guard_wingnum != aip->wing)
14952                                 ai_set_guard_wing(other_objp, aip->guard_wingnum);
14953                 }
14954
14955                 if (aip->hitter_objnum == objnum)
14956                         aip->hitter_objnum = -1;
14957
14958         }
14959
14960 }
14961
14962 /*
14963 //      Interface function to goals code.
14964 //      Make object *objp fly to point *vp and warp out.
14965 void ai_warp_out(object *objp, vector *vp)
14966 {
14967         ai_info *aip;
14968
14969         aip = &Ai_info[Ships[objp->instance].ai_index];
14970
14971         if (aip->mode != AIM_WARP_OUT) {
14972                 ai_set_mode_warp_out(objp, aip);
14973         }
14974         float   dist;
14975         float   dot;
14976         vector  v2v;
14977         ai_info *aip;
14978
14979         dist = vm_vec_normalized_dir(&v2v, vp, &objp->pos);
14980
14981         if (dist < objp->radius + 5.0f) {
14982
14983                 // Start the warp out effect 
14984                 shipfx_warpout_start(objp);
14985
14986         } else {
14987                 dot = vm_vec_dot(&objp->orient.fvec, &v2v);
14988
14989                 aip = &Ai_info[Ships[objp->instance].ai_index];
14990
14991                 if (dist > 500.0f)
14992                         accelerate_ship(aip, 1.0f);
14993                 else
14994                         accelerate_ship(aip, (3*dot + 1.0f)/4.0f);
14995
14996                 turn_towards_point(objp, vp, NULL, 0.0f);
14997         }
14998 }
14999 */
15000
15001
15002 //      Do stuff at start of deathroll.
15003 void ai_deathroll_start(object *ship_obj)
15004 {
15005         ai_info *aip;
15006         ship            *shipp, *other_ship;
15007
15008         shipp = &Ships[ship_obj->instance];
15009         aip = &Ai_info[shipp->ai_index];
15010
15011         // mark object we are docked with so we can do damage and separate during deathroll
15012         // keep dock_objnum_when_dead from being changed if already set (only allow to be set when -1)
15013         if (Ships[ship_obj->instance].dock_objnum_when_dead == -1) {
15014                 Ships[ship_obj->instance].dock_objnum_when_dead = aip->dock_objnum;
15015                 // set other_ship dock_objnum_when_dead, if other_ship exits.
15016                 if (Ships[ship_obj->instance].dock_objnum_when_dead != -1) {
15017                         other_ship = &Ships[Objects[aip->dock_objnum].instance];
15018                         other_ship->dock_objnum_when_dead = shipp->objnum;
15019                 }
15020         }
15021
15022         ai_cleanup_dock_mode(aip, shipp);
15023
15024         aip->mode = AIM_NONE;
15025 }
15026
15027 //      Object *requester_objp tells rearm ship to abort rearm.
15028 //      Returns true if it succeeded, else false.
15029 //      To succeed means you were previously rearming.
15030 int ai_abort_rearm_request(object *requester_objp)
15031 {
15032         ship            *requester_shipp;
15033         ai_info *requester_aip;
15034
15035         Assert(requester_objp->type == OBJ_SHIP);
15036         if(requester_objp->type != OBJ_SHIP){
15037                 return 0;
15038         }
15039         Assert((requester_objp->instance >= 0) && (requester_objp->instance < MAX_SHIPS));      
15040         if((requester_objp->instance < 0) || (requester_objp->instance >= MAX_SHIPS)){
15041                 return 0;
15042         }
15043         requester_shipp = &Ships[requester_objp->instance];
15044         Assert((requester_shipp->ai_index >= 0) && (requester_shipp->ai_index < MAX_AI_INFO));          
15045         if((requester_shipp->ai_index < 0) || (requester_shipp->ai_index >= MAX_AI_INFO)){
15046                 return 0;
15047         }       
15048         requester_aip = &Ai_info[requester_shipp->ai_index];
15049         
15050         if (requester_aip->ai_flags & (AIF_AWAITING_REPAIR | AIF_BEING_REPAIRED)){
15051
15052                 // dock_objnum is always valid once a rearm repair has been requested.  It points to the
15053                 // ship that is coming to repair me.
15054                 if (requester_aip->dock_objnum != -1) {
15055                         object  *repair_objp;
15056                         ai_info *repair_aip;
15057
15058                         repair_objp = &Objects[requester_aip->dock_objnum];
15059                         repair_aip = &Ai_info[Ships[repair_objp->instance].ai_index];
15060
15061                         //      Make sure signatures match.  This prevents nasty bugs in which an object
15062                         //      that was repairing another is destroyed and is replaced by another ship
15063                         //      before this code comes around.
15064                         if (repair_objp->signature == requester_aip->dock_signature) {
15065
15066                                 Assert( repair_objp->type == OBJ_SHIP );
15067
15068                                 // if support ship is in the process of undocking, don't do anything.
15069                                 if ( repair_aip->submode < AIS_UNDOCK_0 ) {
15070                                         ai_do_objects_repairing_stuff( requester_objp, repair_objp, REPAIR_INFO_ABORT );
15071
15072                                         if ( repair_aip->submode == AIS_DOCK_4 )
15073                                                 repair_aip->submode = AIS_UNDOCK_0;
15074                                         else
15075                                                 repair_aip->submode = AIS_UNDOCK_3;
15076
15077                                         repair_aip->submode_start_time = Missiontime;
15078                                 } else {
15079                                         nprintf(("AI", "Not aborting rearm since already undocking\n"));
15080                                 }
15081                         }
15082                 } else {
15083                         // setting these flags is the safe things to do.  There may not be a corresponding repair
15084                         // ship for this guys since a repair ship may be currently repairing someone else.
15085                         ai_do_objects_repairing_stuff( requester_objp, NULL, REPAIR_INFO_ABORT );
15086
15087                         // try and remove this guy from an arriving support ship.
15088                         mission_remove_scheduled_repair(requester_objp);
15089                 }
15090
15091                 return 1;
15092         } else if ( requester_aip->ai_flags & AIF_REPAIRING ) {
15093                 // a support ship can request to abort when he is told to do something else (like warp out).
15094                 // see if this support ships goal_objnum is valid.  If so, then issue this ai_abort comment
15095                 // for the ship that he is enroute to repair
15096                 if ( requester_aip->goal_objnum != -1 ) {
15097                         int val;
15098
15099                         val = ai_abort_rearm_request( &Objects[requester_aip->goal_objnum] );
15100                         return val;
15101                 }
15102         }
15103
15104         return 0;
15105 }
15106
15107 // function which gets called from ai-issue_rearm_request and from code in missionparse.cpp
15108 // to actually issue the rearm goal (support_obj to rearm requester_obj);
15109 void ai_add_rearm_goal( object *requester_objp, object *support_objp )
15110 {
15111         ship *support_shipp, *requester_shipp;
15112         ai_info *support_aip, *requester_aip;
15113
15114         support_shipp = &Ships[support_objp->instance];
15115         requester_shipp = &Ships[requester_objp->instance];
15116         requester_aip = &Ai_info[requester_shipp->ai_index];
15117
15118         Assert( support_shipp->ai_index != -1 );
15119         support_aip = &Ai_info[support_shipp->ai_index];
15120
15121         // if the requester is a player object, issue the order as the squadmate messaging code does.  Doing so
15122         // ensures that the player get a higher priority!
15123         requester_aip->ai_flags |= AIF_AWAITING_REPAIR; //      Tell that I'm awaiting repair.
15124         if ( requester_objp->flags & OF_PLAYER_SHIP )
15125                 ai_add_ship_goal_player( AIG_TYPE_PLAYER_SHIP, AI_GOAL_REARM_REPAIR, -1, requester_shipp->ship_name, support_aip );
15126         else
15127                 ai_add_goal_ship_internal( support_aip, AI_GOAL_REARM_REPAIR, requester_shipp->ship_name, -1, -1 );
15128
15129 }
15130
15131 //      Object *requester_objp requests rearming.
15132 //      Returns objnum of ship coming to repair requester on success
15133 //      Success means you found someone to rearm you and you weren't previously rearming.
15134 int ai_issue_rearm_request(object *requester_objp)
15135 {
15136         object  *objp;
15137         ship            *requester_shipp;
15138         ai_info *requester_aip;
15139
15140         Assert(requester_objp->type == OBJ_SHIP);
15141         Assert((requester_objp->instance >= 0) && (requester_objp->instance < MAX_SHIPS));
15142         requester_shipp = &Ships[requester_objp->instance];
15143         Assert((requester_shipp->ai_index >= 0) && (requester_shipp->ai_index < MAX_AI_INFO));
15144         requester_aip = &Ai_info[requester_shipp->ai_index];
15145         
15146         //      Make sure not already awaiting repair.
15147         if (requester_aip->ai_flags & AIF_AWAITING_REPAIR) {
15148                 nprintf(("AI", "Ship %s already awaiting rearm by ship %s.\n", requester_shipp->ship_name, &Ships[Objects[requester_aip->dock_objnum].instance].ship_name));    
15149                 return -1;
15150         }
15151
15152         if ( !is_support_allowed(requester_objp) )
15153                 return -1;
15154
15155         //nprintf(("AI", "Ship %s requesting rearming.\n", requester_shipp->ship_name));
15156         requester_aip->next_rearm_request_timestamp = timestamp(NEXT_REARM_TIMESTAMP);  //      Might request again after this much time.
15157
15158         // call ship_find_repair_ship to get a support ship.  If none is found, then we will warp one in.  This
15159         // function will return the next available ship which can repair requester
15160         objp = ship_find_repair_ship( requester_objp );
15161         ai_do_objects_repairing_stuff( requester_objp, objp, REPAIR_INFO_QUEUE );
15162         if ( objp ) {
15163
15164                 // MWA 5/14/98 -- moved next item into the ai_do_objects_repairing_stuff function so that clients
15165                 // would properly update their hud support view
15166                 //ai_add_rearm_goal( requester_objp, objp );
15167                 return OBJ_INDEX(objp);
15168
15169         } else {
15170                 // call to warp in repair ship!!!!  for now, warp in any number of ships needed.  Should cap it to
15171                 // some reasonable max (or let support ships warp out).  We should assume here that ship_find_repair_ship()
15172                 // would have returned a valid object if there are too many support ships already in the mission
15173                 mission_warp_in_support_ship( requester_objp );
15174
15175                 return -1;
15176         }
15177
15178 }
15179
15180 // make objp rearm and repair goal_objp
15181 void ai_rearm_repair( object *objp, object  *goal_objp, int priority, int docker_index, int dockee_index )
15182 {
15183         ai_info *aip, *goal_aip;
15184
15185         aip = &Ai_info[Ships[objp->instance].ai_index];
15186         aip->goal_objnum = goal_objp-Objects;
15187
15188         // nprintf(("AI", "Ship %s preparing to rearm ship %s.\n", shipp->ship_name, requester_shipp->ship_name));
15189
15190         ai_dock_with_object(objp, goal_objp, priority, AIDO_DOCK, docker_index, dockee_index);
15191         aip->ai_flags |= AIF_REPAIRING;                                         //      Tell that repair guy is busy trying to repair someone.
15192
15193         goal_aip = &Ai_info[Ships[goal_objp->instance].ai_index];
15194         goal_aip->dock_objnum = objp-Objects;           //      Tell which object is coming to repair.
15195         goal_aip->dock_signature = objp->signature;
15196
15197         ai_do_objects_repairing_stuff( goal_objp, objp, REPAIR_INFO_ONWAY );
15198
15199         goal_aip->abort_rearm_timestamp = timestamp(NEXT_REARM_TIMESTAMP*3/2);
15200 }
15201
15202 // Given a dockee object and the index of the dockbay for that object (ie the dockbay index
15203 // into polymodel->dockbays[] for the model associated with the object), return the index
15204 // of a path_num associated with than dockbay (this is an index into polymodel->paths[])
15205 int ai_return_path_num_from_dockbay(object *dockee_objp, int dockbay_index)
15206 {
15207         if ( dockbay_index < 0 || dockee_objp == NULL ) {
15208                 Int3();         // should never happen
15209                 return -1;
15210         }
15211
15212         if ( dockee_objp->type == OBJ_SHIP ) {
15213                 int                     path_num;
15214                 polymodel       *pm;
15215
15216                 pm = model_get( Ships[dockee_objp->instance].modelnum );
15217
15218                 // sanity checks
15219                 Assert(pm->n_docks > dockbay_index);
15220                 Assert(pm->docking_bays[dockbay_index].num_spline_paths > 0);
15221                 Assert(pm->docking_bays[dockbay_index].splines != NULL);
15222                 if(pm->n_docks <= dockbay_index){
15223                         return -1;
15224                 }
15225                 if(pm->docking_bays[dockbay_index].num_spline_paths <= 0){
15226                         return -1;
15227                 }
15228                 if(pm->docking_bays[dockbay_index].splines == NULL){
15229                         return -1;
15230                 }
15231
15232                 // We only need to return one path for the dockbay, so return the first
15233                 path_num = pm->docking_bays[dockbay_index].splines[0];
15234                 return path_num;
15235         } else {
15236                 return -1;
15237         }
15238 }
15239
15240 //      Actually go ahead and fire the synaptics.
15241 void cheat_fire_synaptic(object *objp, ship *shipp, ai_info *aip)
15242 {
15243         ship_weapon     *swp;
15244         swp = &shipp->weapons;
15245         int     current_bank = swp->current_secondary_bank;
15246
15247         ai_select_secondary_weapon(objp, swp, WIF_SPAWN, 0);
15248         if (timestamp_elapsed(swp->next_secondary_fire_stamp[current_bank])) {
15249                 if (ship_fire_secondary(objp)) {
15250                         nprintf(("AI", "ship %s cheat fired synaptic!\n", shipp->ship_name));
15251                         swp->next_secondary_fire_stamp[current_bank] = timestamp(2500);
15252                 }
15253         }
15254 }
15255
15256 //      For the subspace mission (sm3-09a)
15257 //              for delta wing
15258 //                      if they're sufficiently far into the mission
15259 //                              if they're near one or more enemies
15260 //                                      every so often
15261 //                                              fire a synaptic if they have one.
15262 void maybe_cheat_fire_synaptic(object *objp, ai_info *aip)
15263 {
15264         //      Only do in subspace missions.
15265         if ( The_mission.flags & MISSION_FLAG_SUBSPACE )        {
15266                 ship    *shipp;
15267                 int     num, time;
15268
15269                 shipp = &Ships[objp->instance];
15270
15271                 if (!(strnicmp(shipp->ship_name, NOX("delta"), 5))) {
15272                         num = shipp->ship_name[6] - '1';
15273
15274                         if ((num >= 0) && (num <= 3)) {
15275                                 time = Missiontime >> 16;       //      Convert to seconds.
15276
15277                                 time -= 2*60;   //      Subtract off two minutes.
15278
15279                                 if (time > 0) {
15280                                         int modulus = 17 + num*3;
15281
15282                                         if ((time % modulus) < 2) {
15283                                                 int count = num_nearby_fighters(get_enemy_team_mask(OBJ_INDEX(objp)), &objp->pos, 1500.0f);
15284
15285                                                 if (count > 0) {
15286                                                         cheat_fire_synaptic(objp, shipp, aip);
15287                                                 }
15288                                         }
15289                                 }
15290                         }
15291                 }
15292         }
15293
15294 }
15295