]> icculus.org git repositories - taylor/freespace2.git/blob - src/ship/aicode.cpp
More stuff compiles
[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.2  2002/05/03 13:34:33  theoddone33
11  * More stuff compiles
12  *
13  * Revision 1.1.1.1  2002/05/03 03:28:10  root
14  * Initial import.
15  *
16  * 
17  * 107   9/15/99 4:42a Mikek
18  * Make any big ship attacking Colossus, or Colossus attacking any large
19  * ship not use big cruiser movement code.
20  * 
21  * 106   9/15/99 3:28a Jimb
22  * Make all big ships in sm3-08 not do cruiser chase code when attacking
23  * Colossus.  Added so Beast doesn't swerve away from Colossus.
24  * 
25  * 105   9/14/99 4:18p Andsager
26  * hack for mission sm3-08 to abort cruiser_chase as sathanas is about to
27  * begin circling colossus.
28  * 
29  * 104   9/08/99 10:44p Andsager
30  * Make HUGE ships not die when warping out, after warp effect started.
31  * 
32  * 103   9/03/99 11:40p Mikek
33  * Comment out an annoying nprintf().
34  * 
35  * 102   9/01/99 11:26p Dave
36  * Fixed release build warnings.
37  * 
38  * 101   9/01/99 9:12p Mikek
39  * Make it a boatload harder to become a traitor from hitting a large
40  * ship.
41  * 
42  * 100   9/01/99 4:01p Andsager
43  * Make sure BIG|HUGE ships do not respond to shockwaves
44  * 
45  * 99    9/01/99 10:09a Dave
46  * Pirate bob.
47  * 
48  * 98    8/31/99 4:24p Andsager
49  * Reduce collisions when attacking big ships.
50  * 
51  * 97    8/31/99 7:33a Mikek
52  * Improvements in formation flying, less silly behavior, especially when
53  * leader is moving very slowly.
54  * 
55  * 96    8/31/99 5:48a Mikek
56  * Making ships not overshoot so much in formation flying.  Intermediate
57  * checkin.
58  * 
59  * 95    8/30/99 12:03a Mikek
60  * Make guard behavior much less annoying.  Guarders don't get quite so
61  * close and they try to avoid striking the target they are guarding.
62  * 
63  * 94    8/29/99 4:18p Andsager
64  * New "burst" limit for friendly damage.  Also credit more damage done
65  * against large friendly ships.
66  * 
67  * 93    8/28/99 7:29p Dave
68  * Fixed wingmen persona messaging. Make sure locked turrets don't count
69  * towards the # attacking a player.
70  * 
71  * 92    8/26/99 10:46p Andsager
72  * Apply shockwave damage to lethality.
73  * 
74  * 91    8/26/99 8:52p Dave
75  * Gave multiplayer TvT messaging a heavy dose of sanity. Cheat codes.
76  * 
77  * 90    8/26/99 5:14p Andsager
78  * 
79  * 89    8/24/99 8:55p Dave
80  * Make sure nondimming pixels work properly in tech menu.
81  * 
82  * 88    8/23/99 6:21p Jefff
83  * added "no traitor" option to missions (and fred)
84  * 
85  * 87    8/20/99 3:36p Andsager
86  * Make sure we don;t miss stealth sweep points.
87  * 
88  * 86    8/16/99 8:21a Andsager
89  * fix link error
90  * 
91  * 85    8/16/99 8:19a Andsager
92  * Add project_point_onto_bbox() to fvi and include in aicode
93  * 
94  * 84    8/15/99 1:30p Dave
95  * Removed some bounding box code because of link errors. Assuming needed
96  * function just needs to get checked in by DaveA.
97  * 
98  * 83    8/15/99 11:59a Andsager
99  * For targing big/huge ships, find nearest distance to bbox, not center.
100  * 
101  * 82    8/13/99 2:20p Andsager
102  * Add speed modification to chances turret will find stealth ship
103  * 
104  * 81    8/13/99 10:49a Andsager
105  * Knossos and HUGE ship warp out.  HUGE ship warp in.  Stealth search
106  * modes dont collide big ships.
107  * 
108  * 80    8/10/99 5:02p Andsager
109  * Fix bug where AI gets stuck in SM_EVADE_WEAPON with no target.
110  * 
111  * 79    8/10/99 11:58a Andsager
112  * Allow turrets to sometimes see stealth.
113  * 
114  * 78    7/31/99 2:57p Dave
115  * Scaled flak aim and jitter by weapon subsystem strength.
116  * 
117  * 77    7/27/99 10:33p Andsager
118  * improve ai for attacking stealth.  reduced jitter in aim.  reduced
119  * error in position when avoiding.  skill level support for attacking
120  * stealth.  Made target error same for team vs. team.
121  * 
122  * 76    7/27/99 10:49a Andsager
123  * Make turret fire rate independent of team for HUGE turrets, and also
124  * for mult team vs. team.
125  * 
126  * 75    7/26/99 12:14p Andsager
127  * Apply cap to how much slower a transport flies with cargo.  Remove
128  * limit on waypoint speed for training.  Enemy ai get stealth exact pos
129  * when stealth fires
130  * 
131  * 74    7/20/99 1:49p Dave
132  * Peter Drake build. Fixed some release build warnings.
133  * 
134  * 73    7/19/99 2:13p Dave
135  * Added some new strings for Heiko.
136  * 
137  * 72    7/19/99 12:02p Andsager
138  * Allow AWACS on any ship subsystem. Fix sexp_set_subsystem_strength to
139  * only blow up subsystem if its strength is > 0
140  * 
141  * 71    7/15/99 9:20a Andsager
142  * FS2_DEMO initial checkin
143  * 
144  * 70    7/14/99 1:44p Andsager
145  * modify ai_guard for BIG ships to circle around the long axis
146  * 
147  * 69    7/09/99 5:54p Dave
148  * Seperated cruiser types into individual types. Added tons of new
149  * briefing icons. Campaign screen.
150  * 
151  * 68    7/08/99 4:32p Andsager
152  * fix bug with turret-tagged-only
153  * 
154  * 67    7/08/99 12:06p Andsager
155  * Add turret-tagged-only and turret-tagged-clear sexp.
156  * 
157  * 66    7/02/99 3:49p Andsager
158  * Remove debug code.  Allow targeting of stealth from any weapon it
159  * fires.
160  * 
161  * 65    7/02/99 2:01p Andsager
162  * Fix bug where big ship tries to evade dumpfire weapon.
163  * 
164  * 64    7/02/99 10:58a Andsager
165  * Put in big ship - big ship attack mode.  Modify stealth sweep ai.
166  * 
167  * 63    6/30/99 5:53p Dave
168  * Put in new anti-camper code.
169  * 
170  * 62    6/28/99 3:22p Anoop
171  * Fix turret optimization, where ship may not have any valid subsystems
172  * (all blown off).
173  * 
174  * 61    6/25/99 5:56p Andsager
175  * First real pass on stealth ai.
176  * 
177  * 60    6/25/99 3:08p Dave
178  * Multiple flyby sounds.
179  * 
180  * 59    6/25/99 1:12p Danw
181  * DKA:  Make sure big ship has subsystems before trying to target them.
182  * 
183  * 58    6/25/99 10:56a Johnson
184  * Fixed dumb ai code.
185  * 
186  * 57    6/24/99 5:15p Dave
187  * Make sure stride is always at least one for checking turret subsystem
188  * targets.
189  * 
190  * 56    6/24/99 4:59p Dave
191  * Significant speedups to turret firing.
192  * 
193  * 55    6/23/99 5:51p Andsager
194  * Add waypoint-cap-speed.  Checkin stealth ai - inactive.
195  * 
196  * 54    6/16/99 10:21a Dave
197  * Added send-message-list sexpression.
198  * 
199  * 53    6/15/99 9:25a Andsager
200  * Make guard and dynamic chase (who hit you) work with stealth
201  * 
202  * 52    6/14/99 3:21p Andsager
203  * Allow collisions between ship and its debris.  Fix up collision pairs
204  * when large ship is warping out.
205  * 
206  * 51    6/14/99 10:45a Dave
207  * Made beam weapons specify accuracy by skill level in the weapons.tbl
208  * 
209  * 50    6/03/99 8:11a Andsager
210  * 
211  * 49    6/02/99 5:41p Andsager
212  * Reduce range of secondary weapons not fired from turrets in nebula.
213  * Reduce range of beams fired from turrrets in nebula
214  * 
215  * 48    6/02/99 3:23p Andsager
216  * Make AI aware of team visibility.  Allow player targeting with team
217  * visibility info.  Make stealth ships not targetable by AI in nebula
218  * unless tagged.
219  * 
220  * 47    6/02/99 12:52p Andsager
221  * Added team-wide ship visibility.  Implemented for player.
222  * 
223  * 46    6/01/99 8:35p Dave
224  * Finished lockarm weapons. Added proper supercap weapons/damage. Added
225  * awacs-set-radius sexpression.
226  * 
227  * 45    5/28/99 5:35p Andsager
228  * Make ai nebula aware
229  * 
230  * 44    5/24/99 9:55a Dave
231  * Fixed stream weapon ai firing problem. ick.
232  * 
233  * 43    5/20/99 7:00p Dave
234  * Added alternate type names for ships. Changed swarm missile table
235  * entries.
236  * 
237  * 42    5/18/99 1:30p Dave
238  * Added muzzle flash table stuff.
239  * 
240  * 41    5/12/99 2:55p Andsager
241  * Implemented level 2 tag as priority in turret object selection
242  * 
243  * 40    5/12/99 10:42a Andsager
244  * Fix turret bug allowing HUGE turrets to fire at fighters
245  * 
246  * 39    5/06/99 11:46a Andsager
247  * Bug fixes.  Don't get into illegal strafe submode.  Don't choose turret
248  * enemy objnum for beam protected.
249  * 
250  * 38    5/03/99 10:50p Andsager
251  * Make Asteroid_obj_list.  Change get_nearest_turret_objnum() to use
252  * Asteroid_obj_list, Ship_obj_list and Missile_obj_list vs.
253  * obj_used_list.
254  * 
255  * 37    4/29/99 2:29p Dave
256  * Made flak work much better in multiplayer.
257  * 
258  * 36    4/28/99 11:36p Dave
259  * Tweaked up subspace missile strike a bit,
260  * 
261  * 35    4/28/99 3:11p Andsager
262  * Stagger turret weapon fire times.  Make turrets smarter when target is
263  * protected or beam protected.  Add weaopn range to weapon info struct.
264  * 
265  * 34    4/26/99 10:58a Andsager
266  * Add OF_BEAM_PROTECTED flag to keep object from being targeted for zing.
267  * 
268  * 33    4/23/99 12:12p Andsager
269  * Modify wing positions when player is wing leader to prevent some
270  * collisions.
271  * 
272  * 32    4/23/99 12:01p Johnson
273  * Added SIF_HUGE_SHIP
274  * 
275  * 31    4/22/99 11:06p Dave
276  * Final pass at beam weapons. Solidified a lot of stuff. All that remains
277  * now is to tweak and fix bugs as they come up. No new beam weapon
278  * features.
279  * 
280  * 30    4/20/99 6:39p Dave
281  * Almost done with artillery targeting. Added support for downloading
282  * images on the PXO screen.
283  * 
284  * 29    4/20/99 3:40p Andsager
285  * Changes to big ship ai.  Uses bounding box as limit where to fly to
286  * when flying away.
287  * 
288  * 28    4/16/99 5:54p Dave
289  * Support for on/off style "stream" weapons. Real early support for
290  * target-painting lasers.
291  * 
292  * 27    4/02/99 9:55a Dave
293  * Added a few more options in the weapons.tbl for beam weapons. Attempt
294  * at putting "pain" packets into multiplayer.
295  * 
296  * 26    3/28/99 5:58p Dave
297  * Added early demo code. Make objects move. Nice and framerate
298  * independant, but not much else. Don't use yet unless you're me :)
299  * 
300  * 25    3/19/99 9:51a Dave
301  * Checkin to repair massive source safe crash. Also added support for
302  * pof-style nebulae, and some new weapons code.
303  * 
304  * 24    3/08/99 7:03p Dave
305  * First run of new object update system. Looks very promising.
306  * 
307  * 23    3/05/99 3:55p Anoop
308  * Handle some asserts properly.
309  * 
310  * 22    3/04/99 6:09p Dave
311  * Added in sexpressions for firing beams and checking for if a ship is
312  * tagged.
313  * 
314  * 21    3/02/99 9:25p Dave
315  * Added a bunch of model rendering debug code. Started work on fixing
316  * beam weapon wacky firing.
317  * 
318  * 20    2/25/99 2:32p Anoop
319  * (Alan). Fixed ai path following code for AI_BAY_EMERGE. Put in sanity
320  * check so that when the last point on the path is reached, it finishes.
321  * 
322  * 19    2/19/99 2:11p Anoop
323  * Put in some nice handling code for wacky support ship problems (like no
324  * docking paths)
325  * 
326  * 18    2/17/99 2:11p Dave
327  * First full run of squad war. All freespace and tracker side stuff
328  * works.
329  * 
330  * 17    2/11/99 5:22p Andsager
331  * Fixed bugs, generalized block Sexp_variables
332  * 
333  * 16    1/29/99 5:07p Dave
334  * Fixed multiplayer stuff. Put in multiplayer support for rapid fire
335  * missiles.
336  * 
337  * 15    1/29/99 2:25p Andsager
338  * Added turret_swarm_missiles
339  * 
340  * 14    1/27/99 9:56a Dave
341  * Temporary checkin of beam weapons for Dan to make cool sounds.
342  * 
343  * 13    1/24/99 11:37p Dave
344  * First full rev of beam weapons. Very customizable. Removed some bogus
345  * Int3()'s in low level net code.
346  * 
347  * 12    1/21/99 10:44a Dave
348  * More beam weapon stuff. Put in warmdown time.
349  * 
350  * 11    1/12/99 5:45p Dave
351  * Moved weapon pipeline in multiplayer to almost exclusively client side.
352  * Very good results. Bandwidth goes down, playability goes up for crappy
353  * connections. Fixed object update problem for ship subsystems.
354  * 
355  * 10    1/08/99 2:08p Dave
356  * Fixed software rendering for pofview. Super early support for AWACS and
357  * beam weapons.
358  * 
359  * 9     12/23/98 2:53p Andsager
360  * Added ship activation and gas collection subsystems, removed bridge
361  * 
362  * 8     11/12/98 12:13a Dave
363  * Tidied code up for multiplayer test. Put in network support for flak
364  * guns.
365  * 
366  * 7     11/05/98 5:55p Dave
367  * Big pass at reducing #includes
368  * 
369  * 6     10/26/98 9:42a Dave
370  * Early flak gun support.
371  * 
372  * 5     10/23/98 3:51p Dave
373  * Full support for tstrings.tbl and foreign languages. All that remains
374  * is to make it active in Fred.
375  * 
376  * 4     10/20/98 1:39p Andsager
377  * Make so sparks follow animated ship submodels.  Modify
378  * ship_weapon_do_hit_stuff() and ship_apply_local_damage() to add
379  * submodel_num.  Add submodel_num to multiplayer hit packet.
380  * 
381  * 3     10/13/98 9:29a Dave
382  * Started neatening up freespace.h. Many variables renamed and
383  * reorganized. Added AlphaColors.[h,cpp]
384  * 
385  * 2     10/07/98 10:53a Dave
386  * Initial checkin.
387  * 
388  * 1     10/07/98 10:51a Dave
389  * 
390  * 
391  * $NoKeywords: $
392  */
393
394 // This module contains the actual AI code that does interesting stuff
395 // to objects.   The code in Ai.cpp is just for bookeeping, allocating
396 // ai slots and linking them to ships.
397
398 #include "pstypes.h"
399 #include "fix.h"
400 #include "linklist.h"
401 #include "object.h"
402 #include "physics.h"
403 #include "vecmat.h"
404 #include "ship.h"
405 #include "model.h"
406 #include "2d.h"
407 #include "3d.h"
408 #include "ai.h"
409 #include "floating.h"
410 #include "player.h"
411 #include "freespace.h"
412 #include "weapon.h"
413 #include "missiongoals.h"
414 #include "missionlog.h"
415 #include "timer.h"
416 #include "sound.h"
417 #include "aigoals.h"
418 #include "gamesnd.h"
419 #include "hudmessage.h"
420 #include "missionmessage.h"
421 #include "cmeasure.h"
422 #include "staticrand.h"
423 #include "multimsgs.h"
424 #include "afterburner.h"
425 #include "hudets.h"
426 #include "shipfx.h"
427 #include "shiphit.h"
428 #include "aibig.h"
429 #include "multiutil.h"
430 #include "hud.h"
431 #include "objcollide.h"
432 #include "asteroid.h"
433 #include "hudlock.h"
434 #include "missiontraining.h"
435 #include "gamesequence.h"
436 #include "joy_ff.h"
437 #include "localize.h"
438 #include "flak.h"
439 #include "beam.h"
440 #include "multi.h"
441 #include "swarm.h"
442 #include "multi_team.h"
443 #include "awacs.h"
444 #include "fvi.h"
445
446 #pragma optimize("", off)
447 #pragma auto_inline(off)
448
449 #define UNINITIALIZED_VALUE     -99999.9f
450
451 #define INSTRUCTOR_SHIP_NAME NOX("instructor")
452
453 #define AICODE_SMALL_MAGNITUDE  0.001f          // cosider a vector NULL if mag is less than this
454
455 #define NEXT_REARM_TIMESTAMP (60*1000)                  //      Ships will re-request rearm, typically, after this long.
456
457 #define BEAM_NEBULA_RANGE_REDUCE_FACTOR         0.8
458
459 // AIM_CHASE submode defines
460 // SM_STEALTH_FIND
461 #define SM_SF_AHEAD             0
462 #define SM_SF_BEHIND    1
463 #define SM_SF_BAIL              2
464
465 // SM_STEALTH_SWEEP
466 #define SM_SS_SET_GOAL  -1
467 #define SM_SS_BOX0              0
468 #define SM_SS_LR                        1
469 #define SM_SS_UL                        2
470 #define SM_SS_BOX1              3
471 #define SM_SS_UR                        4
472 #define SM_SS_LL                        5
473 #define SM_SS_BOX2              6
474 #define SM_SS_DONE              7
475
476 //XSTR:OFF
477
478 char *Mode_text[MAX_AI_BEHAVIORS] = {
479         "CHASE",
480         "EVADE",
481         "GET_BEHIND",
482         "CHASE_LONG",
483         "SQUIGGLE",
484         "GUARD",
485         "AVOID",
486         "WAYPOINTS",
487         "DOCK",
488         "NONE",
489         "BIGSHIP",
490         "PATH",
491         "BE_REARMED",
492         "SAFETY",
493         "EV_WEAPON",
494         "STRAFE",
495         "PLAY_DEAD",
496         "BAY_EMERGE",
497         "BAY_DEPART",
498         "SENTRYGUN",
499         "WARP_OUT",
500 };
501
502 //      Submode text is only valid for CHASE mode.
503 char *Submode_text[] = {
504 "undefined",
505 "CONT_TURN",
506 "ATTACK   ",
507 "E_SQUIG  ",
508 "E_BRAKE  ",
509 "EVADE    ",
510 "SUP_ATTAK",
511 "AVOID    ",
512 "BEHIND   ",
513 "GET_AWAY ",
514 "E_WEAPON ",
515 "FLY_AWAY ",
516 "ATK_4EVER",
517 "STLTH_FND",
518 "STLTH_SWP",
519 "BIG_APPR",
520 "BIG_CIRC",
521 "BIG_PARL"
522 };
523
524 char *Strafe_submode_text[5] = {
525 "ATTACK",
526 "AVOID",
527 "RETREAT1",
528 "RETREAT2",
529 "POSITION"
530 };
531 //XSTR:ON
532
533 /*
534 //      Used for global ignore of objects.  If an object appears in the Ignore_objects array,
535 //      no one will attack it.
536 #define MAX_IGNORE_OBJECTS      16
537 typedef struct {
538         int     objnum;
539         int     signature;
540 } ignore_object;
541
542 ignore_object   Ignore_objects[MAX_IGNORE_OBJECTS];
543 */
544
545 typedef struct eval_enemy_obj_struct {
546         int                     turret_parent_objnum;                   // parent of turret
547         float                   weapon_travel_dist;                             // max targeting range of turret weapon
548         int                     enemy_team_mask;
549         int                     weapon_system_ok;                                       // is the weapon subsystem of turret ship ok
550         int                     big_only_flag;                                          // turret fires only at big and huge ships
551         vector          *tpos;
552         vector          *tvec;
553         ship_subsys *turret_subsys;
554         int                     current_enemy;
555
556
557         float                   nearest_attacker_dist;                  // nearest ship 
558         int                     nearest_attacker_objnum;
559
560         float                   nearest_homing_bomb_dist;               // nearest homing bomb
561         int                     nearest_homing_bomb_objnum;
562
563         float                   nearest_bomb_dist;                              // nearest non-homing bomb
564         int                     nearest_bomb_objnum;
565
566         float                   nearest_dist;                                           // nearest ship attacking this turret
567         int                     nearest_objnum;
568 }       eval_enemy_obj_struct;
569
570
571 control_info    AI_ci;
572
573 object *Pl_objp;
574 object *En_objp;
575
576 waypoint_list Waypoint_lists[MAX_WAYPOINT_LISTS];
577
578 // How close a turret has to be point at its target before it
579 // can fire.  If the dot of the gun normal and the vector from gun
580 // to target is greater than this, the turret fires.  The smaller
581 // the sloppier the shooting.
582 #define AICODE_TURRET_DUMBFIRE_ANGLE            (0.8f)  
583 #define AICODE_TURRET_HEATSEEK_ANGLE            (0.7f)  
584 #define AICODE_TURRET_MAX_TIME_IN_RANGE (5.0f)
585
586 #define REARM_SOUND_DELAY               (3*F1_0)                //      Amount of time to delay rearm/repair after mode start
587 #define REARM_BREAKOFF_DELAY    (3*F1_0)                //      Amount of time to wait after fully rearmed to breakoff.
588
589 #define MIN_DIST_TO_WAYPOINT_GOAL       5.0f
590 #define MAX_GUARD_DIST                                  250.0f
591 #define BIG_GUARD_RADIUS                                500.0f
592
593 #define MAX_EVADE_TIME                  (15 * 1000)     //      Max time to evade a weapon.
594
595 // defines for repair ship stuff.
596 #define MAX_REPAIR_SPEED                        25.0f
597 #define MAX_UNDOCK_ABORT_SPEED  2.0f
598
599 // defines for EMP effect stuff
600 #define MAX_EMP_INACCURACY              50.0f
601
602 // defines for stealth
603 #define MAX_STEALTH_INACCURACY  50.0f           // at max view dist
604 #define STEALTH_MAX_VIEW_DIST   400             // dist at which 1) stealth no longer visible 2) firing inaccuracy is greatest
605 #define STEALTH_VIEW_CONE_DOT   0.707           // (half angle of 45 degrees)
606
607
608 ai_class        Ai_classes[MAX_AI_CLASSES];
609 int     Ai_firing_enabled = 1;
610 int     Num_ai_classes;
611
612 int     AI_FrameCount = 0;
613 int     Ship_info_inited = 0;
614 int     AI_watch_object = 0; // Debugging, object to spew debug info for.
615 int     Num_waypoint_lists = 0;
616 int     Mission_all_attack = 0;                                 //      !0 means all teams attack all teams.
617
618 char *Skill_level_names(int level, int translate)
619 {
620         char *str = NULL;
621
622         #if NUM_SKILL_LEVELS != 5
623         #error Number of skill levels is wrong!
624         #endif
625
626         if(translate){
627                 switch( level ) {
628                 case 0:
629                         str = XSTR("Very Easy", 469);
630                         break;
631                 case 1:
632                         str = XSTR("Easy", 470);
633                         break;
634                 case 2:
635                         str = XSTR("Medium", 471);
636                         break;
637                 case 3:
638                         str = XSTR("Hard", 472);
639                         break;
640                 case 4:
641                         str = XSTR("Insane", 473);
642                         break;
643                 default:        
644                         Int3();
645                 }
646         } else {
647                 switch( level ) {
648                 case 0:
649                         str = NOX("Very Easy");
650                         break;
651                 case 1:
652                         str = NOX("Easy");
653                         break;
654                 case 2:
655                         str = NOX("Medium");
656                         break;
657                 case 3:
658                         str = NOX("Hard");
659                         break;
660                 case 4:
661                         str = NOX("Insane");
662                         break;
663                 default:        
664                         Int3();
665                 }
666         }
667
668         return str;
669 }
670
671 #define DELAY_TARGET_TIME       (12*1000)               //      time in milliseconds until a ship can target a new enemy after an order.
672
673 //      Make enemy ships turn more slowly at lower skill levels.
674 float   Turn_time_skill_level_scale[NUM_SKILL_LEVELS] = {3.0f, 2.2f, 1.6f, 1.3f, 1.0f};
675
676 //      Maximum number of simultaneous homing weapons on player based on skill level.
677 int     Max_allowed_player_homers[NUM_SKILL_LEVELS] = {2, 3, 4, 7, 99};
678
679 //      Number of ships that can attack another ship at a given skill level.
680 int     Skill_level_max_attackers[NUM_SKILL_LEVELS] = {2, 3, 4, 5, 99};
681
682 //      How long until next predict position.
683 fix Skill_level_delay[NUM_SKILL_LEVELS] = {2*F1_0, 3*F1_0/2, 4*F1_0/3, F1_0/2, 0};
684
685 //      AI ships link primary weapons if energy levels greater than the following amounts:
686 float   Link_energy_levels_always[NUM_SKILL_LEVELS] = {100.0f, 80.0f, 60.0f, 40.0f, 20.0f};     //      always link
687 float   Link_energy_levels_maybe[NUM_SKILL_LEVELS] = {90.0f, 60.0f, 40.0f, 20.0f, 10.0f};       //      link if hull strength low
688
689 //      Seconds to add to time it takes to get enemy in range.  Only for player's enemies.
690 float   In_range_time[NUM_SKILL_LEVELS] = {2.0f, 1.4f, 0.75f, 0.0f, -1.0f};
691
692 //      No matter what, a random unit vector gets scaled by this amount in firing at an enemy.
693 //      Note that for shorter in-range times, these values get scaled, so a value of 0.5f is meaningful.
694 float   Aiming_error[NUM_SKILL_LEVELS] = {3.0f, 2.2f, 1.3f, 0.7f, 0.2f};
695
696 //      Chance a countermeasure will be fired based on skill level.
697 float Cmeasure_fire_chance[NUM_SKILL_LEVELS] = {0.2f, 0.3f, 0.5f, 0.9f, 1.1f};  //      Note, this gets scaled by ai_class
698
699 float Shield_manage_delays[NUM_SKILL_LEVELS] = {5.0f, 4.0f, 2.5f, 1.2f, 0.1f};
700
701 // accuracy we feed into the beam weapons based upon skill system
702 // float Beam_accuracy[NUM_SKILL_LEVELS] = {2.0f, 1.5f, 1.0f, 0.7f, 0.4f};
703
704 extern float Ship_fire_delay_scale_hostile[NUM_SKILL_LEVELS];
705 extern float Ship_fire_delay_scale_friendly[NUM_SKILL_LEVELS];
706
707 pnode           Path_points[MAX_PATH_POINTS];
708 pnode           *Ppfp;                  //      Free pointer in path points.
709
710 float   AI_frametime;
711
712 char *Ai_class_names[MAX_AI_CLASSES];
713
714 // global for rearm status for teams
715 int Ai_friendly_rearm_timestamp, Ai_hostile_rearm_timestamp, Ai_neutral_rearm_timestamp, Ai_traitor_rearm_timestamp, Ai_unknown_rearm_timestamp;
716
717 // globals for dealing with when to fire huge secondary weapons
718 #define MAX_HUGE_SECONDARY_INFO 10
719
720 typedef struct {
721         int team;
722         int weapon_index;
723         int max_fire_count;
724         char    *shipname;
725 } huge_fire_info;
726
727 huge_fire_info Ai_huge_fire_info[MAX_HUGE_SECONDARY_INFO];
728
729 int Ai_last_arrive_path;        // index of ship_bay path used by last arrival from a fighter bay
730
731 // forward declarations
732 int     ai_return_path_num_from_dockbay(object *dockee_objp, int dockbay_index);
733 void    create_model_exit_path(object *pl_objp, object *mobjp, int path_num, int count=1);
734 void    copy_xlate_model_path_points(object *objp, model_path *mp, int dir, int count, int path_num, pnode *pnp, int randomize_pnt=-1);
735
736 // ai_set_rearm_status takes a team (friendly, hostile, neutral) and a time.  This function
737 // sets the timestamp used to tell is it is a good time for this team to rearm.  Once the timestamp
738 // is no longer valid, then rearming is not a "good time"
739 // not safe.  Called from sexpression code.
740 void ai_set_rearm_status( int team, int time )
741 {
742         Assert( time >= 0 );
743
744         switch (team) {
745         case TEAM_FRIENDLY:
746                 Ai_friendly_rearm_timestamp = timestamp( time * 1000 );
747                 break;
748         case TEAM_HOSTILE:
749                 Ai_hostile_rearm_timestamp = timestamp( time * 1000 );
750                 break;
751         case TEAM_NEUTRAL:
752                 Ai_neutral_rearm_timestamp = timestamp( time * 1000 );
753                 break;
754         case TEAM_TRAITOR:
755                 Ai_traitor_rearm_timestamp = timestamp( time * 1000 );
756                 break;
757         case TEAM_UNKNOWN:
758                 Ai_traitor_rearm_timestamp = timestamp( time * 1000 );
759                 break;
760         default:
761                 Int3();
762                 break;
763         }
764 }
765
766 // int ai_good_time_to_rearm returns true(1) or false(0) if it is "safe" for the given
767 // object to rearm.  "safe" is currently defined by the mission designer using the good/bad
768 // time to rearm sexpressions.  This status is currently team based.  This function could
769 // be easily expended to further the definition of "safe"
770 int ai_good_time_to_rearm( object *objp )
771 {
772         int team, status;
773
774         Assert(objp->type == OBJ_SHIP);
775         team = Ships[objp->instance].team;
776         status = 0;
777
778         switch(team) {
779         case TEAM_FRIENDLY:
780                 status = timestamp_valid(Ai_friendly_rearm_timestamp);
781                 break;
782         case TEAM_HOSTILE:
783                 status = timestamp_valid(Ai_hostile_rearm_timestamp);
784                 break;
785         case TEAM_NEUTRAL:
786                 status = timestamp_valid(Ai_neutral_rearm_timestamp);
787                 break;
788         case TEAM_TRAITOR:
789                 status = timestamp_valid(Ai_traitor_rearm_timestamp);
790                 break;
791         case TEAM_UNKNOWN:
792                 status = timestamp_valid(Ai_unknown_rearm_timestamp);
793                 break;
794         default:
795                 Int3();
796                 break;
797         }
798
799         return status;
800 }
801
802 // functions to deal with letting the ai know about good times to fire powerful secondary
803 // weapons.
804
805 // this function is entry point from sexpression code to set internal data for use by ai code.
806 void ai_good_secondary_time( int team, int weapon_index, int max_fire_count, char *shipname )
807 {
808         int i, index;
809
810         // find an open slot to put this data
811         for ( i = 0; i < MAX_HUGE_SECONDARY_INFO; i++ ) {
812                 if ( Ai_huge_fire_info[i].weapon_index == -1 )
813                         break;
814         }
815
816         Assert( i < MAX_HUGE_SECONDARY_INFO );                  // we've run out of room
817
818         Ai_huge_fire_info[i].weapon_index = weapon_index;
819         Ai_huge_fire_info[i].team = team;
820         Ai_huge_fire_info[i].max_fire_count = max_fire_count;
821
822         Ai_huge_fire_info[i].shipname = ai_get_goal_ship_name( shipname, &index );
823 }
824
825 // function called internally to the ai code to tell whether or not weapon_num can be fired
826 // from firer_objp at target_objp.  This function will resolve the team for the firer.
827 // returns:
828 //              -1  -- when conditions don't allow firer to fire weapon_num on target_objp
829 //              >=0 -- when conditions allow firer to fire.  Return value is max number of weapon_nums
830 //           which can be fired on target_objp
831 int is_preferred_weapon(int weapon_num, object *firer_objp, object *target_objp)
832 {
833         int i, firer_team, target_signature;
834         ship *firer_ship;
835         huge_fire_info *hfi = NULL;
836
837         Assert( firer_objp->type == OBJ_SHIP );
838         firer_ship = &Ships[firer_objp->instance];
839         firer_team = firer_ship->team;
840
841         // get target object's signature and try to find it in the list.
842         target_signature = target_objp->signature;
843         for ( i = 0; i < MAX_HUGE_SECONDARY_INFO; i++ ) {
844                 int ship_index, signature;
845
846                 hfi = &Ai_huge_fire_info[i];
847                 if ( hfi->weapon_index == -1 )
848                         continue;
849
850                 ship_index = ship_name_lookup( hfi->shipname );
851                 if ( ship_index == -1 )
852                         continue;
853
854                 signature = Objects[Ships[ship_index].objnum].signature;
855
856                 // sigatures, weapon_index, and team must match
857                 if ( (signature == target_signature) && (hfi->weapon_index == weapon_num) && (hfi->team == firer_team) )
858                         break;
859         }
860
861         // return -1 if not found
862         if ( i == MAX_HUGE_SECONDARY_INFO )
863                 return -1;
864
865         // otherwise, we can return the max number of weapons we can fire against target_objps
866
867         return hfi->max_fire_count;
868 }
869
870 // function to clear out secondary firing infomration between levels
871 void ai_init_secondary_info()
872 {
873         int i;
874
875         // clear out the data for dealing with when ai ships can fire huge secondary weapons
876         for (i = 0; i < MAX_HUGE_SECONDARY_INFO; i++ ) {
877                 Ai_huge_fire_info[i].weapon_index = -1;
878                 Ai_huge_fire_info[i].team = -1;
879                 Ai_huge_fire_info[i].max_fire_count = -1;
880                 Ai_huge_fire_info[i].shipname = NULL;
881         }
882 }
883
884
885 //      Garbage collect the Path_points buffer.
886 //      Scans all objects, looking for used Path_points records.
887 //      Compresses Path_points buffer, updating aip->path_start and aip->path_cur indices.
888 //      Updates Ppfp to point to first free record.
889 //      This function is fairly fast.  Its worst-case running time is proportional to
890 //      3*MAX_PATH_POINTS + MAX_OBJECTS
891 //      Things to do to optimize this function:
892 //              1. if (t != 0) xlt++; can be replaced by xlt += t; assuming t can only be 0 or 1.
893 //              2. When pp_xlate is getting stuffed the first time, note highest index and use that 
894 //                      instead of MAX_PATH_POINTS in following two for loops.
895 void garbage_collect_path_points()
896 {
897         int     i;
898         int     pp_xlate[MAX_PATH_POINTS];
899         object  *A;
900         ship_obj        *so;
901
902         //      Scan all objects and create Path_points xlate table.
903         for (i=0; i<MAX_PATH_POINTS; i++)
904                 pp_xlate[i] = 0;
905
906         //      in pp_xlate, mark all used Path_point records
907         for ( so = GET_FIRST(&Ship_obj_list); so != END_OF_LIST(&Ship_obj_list); so = GET_NEXT(so) ) {
908                 A = &Objects[so->objnum];
909                 ship    *shipp = &Ships[A->instance];
910                 if (shipp->ai_index != -1) {
911                         ai_info *aip = &Ai_info[shipp->ai_index];
912
913                         if ((aip->path_length > 0) && (aip->path_start > -1)) {
914
915                                 for (int i=aip->path_start; i<aip->path_start + aip->path_length; i++) {
916                                         Assert(pp_xlate[i] == 0);       //      If this is not 0, then two paths use this point!
917                                         pp_xlate[i] = 1;
918                                 }
919                         }
920                 }
921         }
922
923         //      Now, stuff xlate index in pp_xlate.  This is the number to translate any path_start
924         //      or path_cur index to.
925         int     xlt = 0;
926         for (i=0; i<MAX_PATH_POINTS; i++) {
927                 int     t = pp_xlate[i];
928
929                 pp_xlate[i] = xlt;
930                 if (t != 0)
931                         xlt++;
932         }
933         
934         //      Update global Path_points free pointer.
935         Ppfp = &Path_points[xlt];
936
937         //      Now, using pp_xlate, fixup all aip->path_cur and aip->path_start indices
938         for ( so = GET_FIRST(&Ship_obj_list); so != END_OF_LIST(&Ship_obj_list); so = GET_NEXT(so) ) {
939                 A = &Objects[so->objnum];
940                 ship    *shipp = &Ships[A->instance];
941                 if (shipp->ai_index != -1) {
942                         ai_info *aip = &Ai_info[shipp->ai_index];
943
944                         if ((aip->path_length > 0) && (aip->path_start > -1)) {
945                                 Assert(aip->path_start < MAX_PATH_POINTS);
946                                 aip->path_start = pp_xlate[aip->path_start];
947
948                                 Assert((aip->path_cur >= 0) && (aip->path_cur < MAX_PATH_POINTS));
949                                 aip->path_cur = pp_xlate[aip->path_cur];
950                         }
951                 }
952         }
953
954         //      Now, compress the buffer.
955         for (i=0; i<MAX_PATH_POINTS; i++)
956                 if (i != pp_xlate[i])
957                         Path_points[pp_xlate[i]] = Path_points[i];
958
959 }
960
961 //      Hash two values together, return result.
962 //      Hash function: curval shifted right circular by one, newval xored in.
963 int hash(unsigned int curval, int newval)
964 {
965         int     addval = curval & 1;
966
967         curval >>= 1;
968         if (addval)
969                 curval |= 0x80000000;
970         curval ^= newval;
971
972         return curval;
973 }
974
975 //      Hash some information in an object together.
976 //      On 2/20/97, the information is position and orientation.
977 int create_object_hash(object *objp)
978 {
979         int     *ip;
980         unsigned int    hashval = 0;
981         int     i;
982
983         ip = (int *) &objp->orient;
984
985         for (i=0; i<9; i++) {
986                 hashval = hash(hashval, *ip);
987                 ip++;
988         }
989
990         ip = (int *) &objp->pos;
991
992         for (i=0; i<3; i++) {
993                 hashval = hash(hashval, *ip);
994                 ip++;
995         }
996
997         return hashval;
998 }
999
1000 //      Stuff a list of NUM_SKILL_LEVELS floats at *plist.
1001 void parse_float_list(float *plist)
1002 {
1003         int     i;
1004
1005         for (i=0; i<NUM_SKILL_LEVELS; i++) {
1006                 stuff_float(&plist[i]);
1007         }
1008 }
1009
1010 void parse_ai_class()
1011 {
1012         ai_class        *aicp = &Ai_classes[Num_ai_classes];
1013
1014         required_string("$Name:");
1015         stuff_string(aicp->name, F_NAME, NULL);
1016
1017         Ai_class_names[Num_ai_classes] = aicp->name;
1018
1019         required_string("$accuracy:");
1020         parse_float_list(aicp->ai_accuracy);
1021
1022         required_string("$evasion:");
1023         parse_float_list(aicp->ai_evasion);
1024
1025         required_string("$courage:");
1026         parse_float_list(aicp->ai_courage);
1027
1028         required_string("$patience:");
1029         parse_float_list(aicp->ai_patience);
1030 }
1031
1032 void parse_aitbl()
1033 {
1034         // open localization
1035         lcl_ext_open();
1036
1037         read_file_text("ai.tbl");
1038
1039         reset_parse();
1040
1041         Num_ai_classes = 0;
1042
1043         required_string("#AI Classes");
1044
1045         while (required_string_either("#End", "$Name:")) {
1046                 Assert( Num_ai_classes < MAX_AI_CLASSES);
1047
1048                 parse_ai_class();
1049
1050                 Num_ai_classes++;
1051         }
1052
1053         // close localization
1054         lcl_ext_close();
1055 }
1056
1057 LOCAL int ai_inited = 0;
1058
1059 //========================= BOOK-KEEPING FUNCTIONS =======================
1060
1061 // Called once at game start-up
1062 void ai_init()
1063 {
1064         if ( !ai_inited )       {
1065                 // Do the first time initialization stuff here
1066                 int     rval;
1067
1068                 if ((rval = setjmp(parse_abort)) != 0) {
1069                         Error(LOCATION, "Error parsing 'ai.tbl'\r\nError code = %i.\r\n", rval);
1070                 } else {                        
1071                         parse_aitbl();                  
1072                 }
1073
1074                 ai_inited = 1;
1075         }
1076
1077         init_semirand();
1078         
1079         ai_level_init();
1080 }
1081
1082 // this inits the ai.  You should be able to call this between
1083 // levels to reset everything.
1084 void ai_level_init()
1085 {
1086         int i;
1087  
1088         // Do the stuff to reset all ai stuff here
1089         for (i=0; i<MAX_AI_INFO ; i++) {
1090                 Ai_info[i].shipnum = -1;
1091         }
1092         Ai_goal_signature = 0;
1093         Ai_friendly_rearm_timestamp = timestamp(-1);
1094         Ai_hostile_rearm_timestamp = timestamp(-1);
1095         Ai_neutral_rearm_timestamp = timestamp(-1);
1096         Ai_traitor_rearm_timestamp = timestamp(-1);
1097
1098         // clear out the stuff needed for AI firing powerful secondary weapons
1099         ai_init_secondary_info();
1100
1101         Ai_last_arrive_path=0;
1102 }
1103
1104 // BEGIN STEALTH
1105 // -----------------------------------------------------------------------------
1106 // Check if object is a stealth ship
1107 int is_object_stealth_ship(object* objp)
1108 {
1109         if (objp->type == OBJ_SHIP) {
1110                 if (Ship_info[Ships[objp->instance].ship_info_index].flags & SIF_STEALTH) {
1111                         return 1;
1112                 }
1113         }
1114
1115         // not stealth ship
1116         return 0;
1117 }
1118
1119 // -----------------------------------------------------------------------------
1120 // Init necessary ai info for new stealth target
1121 void init_ai_stealth_info(ai_info *aip, object *stealth_objp)
1122 {
1123         Assert(is_object_stealth_ship(stealth_objp));
1124
1125         // set necessary ai info for new stealth target
1126         aip->stealth_last_pos = stealth_objp->pos;
1127         aip->stealth_velocity = stealth_objp->phys_info.vel;
1128         aip->stealth_last_visible_stamp = timestamp();
1129 }
1130
1131 // -----------------------------------------------------------------------------
1132 // Check whether Pl_objp can see a stealth ship object
1133 #define STEALTH_INVISIBLE                       0
1134 #define STEALTH_VISIBLE                         1
1135 #define STEALTH_FULLY_TARGETABLE        2
1136
1137 float get_skill_stealth_dist_scaler()
1138 {
1139         // return dist scaler based on skill level
1140         switch (Game_skill_level) {
1141         case 0: // very easy
1142                 return 0.65f;
1143
1144         case 1: // easy
1145                 return 0.9f;
1146
1147         case 2: // medium
1148                 return 1.0f;
1149
1150         case 3: // hard
1151                 return 1.1f;
1152
1153         case 4: // insane
1154                 return 1.3f;
1155
1156         default:
1157                 Int3();
1158         }
1159
1160         return 1.0f;
1161 }
1162
1163 float get_skill_stealth_dot_scaler()
1164 {
1165         // return multiplier on dot based on skill level
1166         switch (Game_skill_level) {
1167         case 0: // very easy
1168                 return 1.3f;
1169
1170         case 1: // easy
1171                 return 1.1f;
1172
1173         case 2: // medium
1174                 return 1.0f;
1175
1176         case 3: // hard
1177                 return 0.9f;
1178
1179         case 4: // insane
1180                 return 0.7f;
1181
1182         default:
1183                 Int3();
1184         }
1185
1186         return 1.0f;
1187 }
1188
1189 int ai_is_stealth_visible(object *viewer_objp, object *stealth_objp)
1190 {
1191         ship *shipp;
1192         vector vec_to_stealth;
1193         float dot_to_stealth, dist_to_stealth, max_stealth_dist;
1194
1195         Assert(stealth_objp->type == OBJ_SHIP);
1196         shipp = &Ships[stealth_objp->instance];
1197         Assert(viewer_objp->type == OBJ_SHIP);
1198
1199         // check if stealth ship
1200         Assert(Ship_info[shipp->ship_info_index].flags & SIF_STEALTH);
1201
1202         // check if in neb and below awac level for visible
1203         if ( !ship_is_visible_by_team(stealth_objp->instance, Ships[viewer_objp->instance].team) ) {
1204                 vm_vec_sub(&vec_to_stealth, &stealth_objp->pos, &viewer_objp->pos);
1205                 dist_to_stealth = vm_vec_mag_quick(&vec_to_stealth);
1206                 dot_to_stealth = vm_vec_dotprod(&viewer_objp->orient.fvec, &vec_to_stealth) / dist_to_stealth;
1207
1208                 // get max dist at which stealth is visible
1209                 max_stealth_dist = get_skill_stealth_dist_scaler() * STEALTH_MAX_VIEW_DIST;
1210
1211                 // now check if within view frustrum
1212                 float needed_dot_to_stealth;
1213                 if (dist_to_stealth < 100) {
1214                         needed_dot_to_stealth = 0.0f;
1215                 } else {
1216                         needed_dot_to_stealth = get_skill_stealth_dot_scaler() * float(STEALTH_VIEW_CONE_DOT) * (dist_to_stealth / max_stealth_dist);
1217                 }
1218                 if (dot_to_stealth > needed_dot_to_stealth) {
1219                         if (dist_to_stealth < max_stealth_dist) {
1220                                 return STEALTH_VISIBLE;
1221                         }
1222                 }
1223
1224                 // not within frustrum
1225                 return STEALTH_INVISIBLE;
1226         }
1227
1228         // visible by awacs level
1229         return STEALTH_FULLY_TARGETABLE;
1230 }
1231
1232 // END STEALTH
1233
1234 //      Compute dot product of direction vector and forward vector.
1235 //      Direction vector is vector from one object to other object.
1236 //      Forward vector is the forward vector of the ship.
1237 //      If from_dot == NULL, don't fill it in.
1238 float compute_dots(object *objp, object *other_objp, float *to_dot, float *from_dot)
1239 {
1240         vector  v2o;
1241         float           dist;
1242
1243         dist = vm_vec_normalized_dir(&v2o, &other_objp->pos, &objp->pos);
1244
1245         *to_dot = vm_vec_dot(&objp->orient.fvec, &v2o);
1246
1247         if (from_dot != NULL)
1248                 *from_dot = - vm_vec_dot(&other_objp->orient.fvec, &v2o);
1249
1250         return dist;
1251 }
1252
1253 // -----------------------------------------------------------------------------
1254 // update estimated stealth info
1255 // this is a "cheat" update
1256 // error increases with time not seen, true distance away, dot to enemey
1257 // this is done only if we can not see the stealth target
1258 // need to infer its position either by weapon fire pos or last know pos
1259 void update_ai_stealth_info_with_error(ai_info *aip/*, int no_error*/)
1260 {
1261         object *ship;
1262         object *stealth_objp;
1263         /*
1264         float error_time_mult, error_dist_mult, error_dot_mult, error_mult;
1265         float pos_error, vel_error;
1266         vector error_vec, vec_to_stealth;
1267         float dist_to_stealth, dot_to_stealth;
1268         float delta_time, delta_capped;
1269         */
1270
1271         // make sure I am targeting a stealth ship
1272         Assert( is_object_stealth_ship(&Objects[aip->target_objnum]) );
1273         stealth_objp = &Objects[aip->target_objnum];
1274
1275         // my_ship
1276         ship = &Objects[Ships[aip->shipnum].objnum];
1277
1278         // if update is due to weapon fire, get exact stealth position
1279 //      if (no_error) {
1280         aip->stealth_last_pos = stealth_objp->pos;
1281         aip->stealth_velocity = stealth_objp->phys_info.vel;
1282         aip->stealth_last_visible_stamp = timestamp();
1283 //              return;
1284 //      }
1285 /*
1286         // get time since last seen
1287         delta_time = 0.001f * (timestamp() - aip->stealth_last_visible_stamp);
1288
1289         // we don't want our "cheat" guess to more off than what we would get from extrapolating from last visible
1290         // only update if stealth info is "old"
1291         if ( (delta_time) < 0.5 ) {
1292                 return;
1293         }
1294
1295         // find vec_to_stealth and dist
1296         vm_vec_sub(&vec_to_stealth, &stealth_objp->pos, &ship->pos);
1297         dist_to_stealth = vm_vec_normalize_quick(&vec_to_stealth);
1298         dot_to_stealth = vm_vec_dotprod(&vec_to_stealth, &ship->orient.fvec);
1299
1300         // put cap on time
1301         delta_capped = delta_time;
1302         if (delta_time > 5.0) {
1303                 delta_capped = 5.0f;
1304         }
1305
1306         // erorr_time_mult (for 0-5) -> (1-6)
1307         error_time_mult = (1.0f + delta_capped);
1308
1309         // error_dot_mult (-1 to 1) -> (1-3)
1310         error_dot_mult = (2 - dot_to_stealth);
1311
1312         // error_dist_mult (0-1000+) -> (1-4)
1313         error_dist_mult = dist_to_stealth * 4.0f * 0.001f;
1314         if (error_dist_mult < 1) {
1315                 error_dist_mult = 1.0f;
1316         } else if (error_dist_mult > 4) {
1317                 error_dist_mult = 4.0f;
1318         }
1319
1320         // multiply error out
1321         error_mult = error_time_mult * error_dot_mult * error_dist_mult;
1322
1323         float base_pos_error = 10;
1324         float base_vel_error = 2;
1325
1326         // find the position and velocity error magnitude;
1327         pos_error = base_pos_error * error_mult;
1328         vel_error = base_vel_error * error_mult;
1329
1330         // get an error that changes slowly over time
1331         static_randvec( ((int)aip ^ (Missiontime >> 18)) & 7, &error_vec);
1332         vm_vec_zero(&error_vec);
1333
1334         // update pos and vel with error
1335         vm_vec_scale_add(&aip->stealth_velocity, &stealth_objp->phys_info.vel, &error_vec, vel_error);
1336
1337         // revise last "known" position to arrive at last pos with given error
1338         vm_vec_scale_add(&aip->stealth_last_pos, &stealth_objp->pos, &error_vec, pos_error);
1339         vm_vec_scale_add2(&aip->stealth_last_pos, &aip->stealth_velocity, -(0.001f * delta_time));
1340         */
1341 }
1342
1343 //      Update danger_weapon_objnum and signature in ai_info to say this weapon is to be avoided.
1344 void ai_update_danger_weapon(int attacked_objnum, int weapon_objnum)
1345 {
1346         object  *objp, *weapon_objp;
1347         ai_info *aip;
1348         float           old_dist, new_dist;
1349         float           old_dot, new_dot;
1350         object  *old_weapon_objp = NULL;
1351
1352         if ((attacked_objnum == -1) || (weapon_objnum == -1)) {
1353                 return;
1354         }
1355
1356         objp = &Objects[attacked_objnum];
1357
1358         // AL 2-24-98: If this isn't a ship, we don't need to worry about updating weapon_objnum (ie it would be
1359         //                                      an asteroid or bomb).
1360         if ( objp->type != OBJ_SHIP ) {
1361                 return;
1362         }
1363
1364         weapon_objp = &Objects[weapon_objnum];
1365
1366         aip = &Ai_info[Ships[objp->instance].ai_index];
1367
1368         // if my taraget is a stealth ship and is not visible
1369         if (aip->target_objnum >= 0) {
1370                 if ( is_object_stealth_ship(&Objects[aip->target_objnum]) ) {
1371                         if ( ai_is_stealth_visible(objp, &Objects[aip->target_objnum]) == STEALTH_INVISIBLE ) {
1372                                 // and the weapon is coming from that stealth ship
1373                                 if (weapon_objp->parent == aip->target_objnum) {
1374                                         // update my position estimate for stealth ship
1375                                         update_ai_stealth_info_with_error(aip/*, 1*/);
1376                                 }
1377                         }
1378                 }
1379         }
1380
1381         if (aip->danger_weapon_objnum != -1) {
1382                 old_weapon_objp = &Objects[aip->danger_weapon_objnum];
1383                 if ((old_weapon_objp->type == OBJ_WEAPON) && (old_weapon_objp->signature == aip->danger_weapon_signature)) {
1384                         ;
1385                 } else {
1386                         aip->danger_weapon_objnum = -1;
1387                 }
1388         }
1389
1390         new_dist = compute_dots(weapon_objp, objp, &new_dot, NULL);
1391
1392         if (aip->danger_weapon_objnum == -1) {
1393                 if (new_dist < 1500.0f) {
1394                         if (new_dot > 0.5f) {
1395                                 aip->danger_weapon_objnum = weapon_objnum;
1396                                 aip->danger_weapon_signature = weapon_objp->signature;
1397                         }
1398                 }
1399         } else {
1400                 Assert(old_weapon_objp != NULL);
1401                 old_dist = compute_dots(old_weapon_objp, objp, &old_dot, NULL);
1402         
1403                 if (old_dot < 0.5f) {
1404                         aip->danger_weapon_objnum = -1;
1405                         old_dist = 9999.9f;
1406                 }
1407
1408                 if ((new_dot > 0.5f) && (new_dot > old_dot-0.01f)) {
1409                         if (new_dist < old_dist) {
1410                                 aip->danger_weapon_objnum = weapon_objnum;
1411                                 aip->danger_weapon_signature = weapon_objp->signature;
1412                         }
1413                 }
1414         }
1415 }
1416
1417 //      If rvec != NULL, use it to match bank by calling vm_matrix_interpolate.
1418 //      (rvec defaults to NULL)
1419 void ai_turn_towards_vector(vector *dest, object *objp, 
1420                                                                          float frametime, float turn_time, vector *slide_vec, vector *rel_pos, float bank_override, int flags, vector *rvec)
1421 {
1422         //matrix        goal_orient;
1423         matrix  curr_orient;
1424         vector  vel_in, vel_out, desired_fvec, src;
1425         float           delta_time;
1426         physics_info    *pip;
1427         vector  vel_limit, acc_limit;
1428         float           delta_bank;
1429
1430         //      Don't allow a ship to turn if it has no engine strength.
1431         // AL 3-12-98: objp may not always be a ship!
1432         if ( objp->type == OBJ_SHIP ) {
1433                 if (ship_get_subsystem_strength(&Ships[objp->instance], SUBSYSTEM_ENGINE) <= 0.0f)
1434                         return;
1435         }
1436                         
1437         //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));
1438         pip = &objp->phys_info;
1439
1440         vel_in = pip->rotvel;
1441         curr_orient = objp->orient;
1442         delta_time = flFrametime;
1443
1444         Assert(turn_time > 0.0f);
1445         
1446         //      Scale turn_time based on skill level and team.
1447         if (!(flags & AITTV_FAST)){
1448                 if (objp->type == OBJ_SHIP){
1449                         if (Ships[objp->instance].team != Ships[Player_obj->instance].team){
1450                                 turn_time *= Turn_time_skill_level_scale[Game_skill_level];
1451                         }
1452                 }
1453         }
1454
1455         //      Set max turn rate.
1456         vel_limit.x = 2*PI/turn_time;
1457         vel_limit.y = 2*PI/turn_time;
1458         vel_limit.z = 2*PI/turn_time;
1459
1460         //      Set rate at which ship can accelerate to its rotational velocity.
1461         //      For now, weapons just go much faster.
1462         acc_limit = vel_limit;
1463         if (objp->type == OBJ_WEAPON)
1464                 vm_vec_scale(&acc_limit, 8.0f);
1465
1466         src = objp->pos;
1467
1468         if (rel_pos != NULL) {
1469                 vector  gun_point;
1470                 vm_vec_unrotate(&gun_point, rel_pos, &objp->orient);
1471                 vm_vec_add2(&src, &gun_point);
1472         }
1473
1474         vm_vec_normalized_dir(&desired_fvec, dest, &src);
1475
1476         //      Since ship isn't necessarily moving in the direction it's pointing, sometimes it's better
1477         //      to be moving towards goal rather than just pointing.  So, if slide_vec is !NULL, try to
1478         //      make ship move towards goal, not point at goal.
1479         if (slide_vec != NULL) {
1480                 vm_vec_add2(&desired_fvec, slide_vec);
1481                 vm_vec_normalize(&desired_fvec);
1482         }
1483
1484         //      Should be more general case here.  Currently, anything that is not a weapon will bank when it turns.
1485         if (objp->type == OBJ_WEAPON)
1486                 delta_bank = 0.0f;
1487         else if ((bank_override) && (Ships[objp->instance].team & opposing_team_mask(Player_ship->team))) {     //      Theoretically, this will only happen for Shivans.
1488                 delta_bank = bank_override;
1489                 //nprintf(("AI", "%i: %7.3f\n", Framecount, bank_override));
1490         } else {
1491                 delta_bank = vm_vec_dot(&curr_orient.rvec, &objp->last_orient.rvec);
1492                 delta_bank = 100.0f * (1.0f - delta_bank);
1493                 if (vm_vec_dot(&objp->last_orient.fvec, &objp->orient.rvec) < 0.0f)
1494                         delta_bank = -delta_bank;
1495
1496                 //nprintf(("AI", "%s: Frame %i: delta bank = %7.3f\n", Ships[objp->instance].ship_name, Framecount, delta_bank));
1497         }
1498
1499         //      Dave Andsager: The non-indented lines here are debug code to help you track down the problem in the physics
1500         //      that is causing ships to inexplicably rotate very far.  If you hit the Int3(), set the next statement to be
1501         //      the one marked "HERE".  (Do this clicking the cursor there, then right clicking.  Choose the right option.)
1502         //      This will allow you to rerun vm_forward_interpolate() with the values that caused the error.
1503         //      Note, you'll need to enable the Int3() about ten lines below.
1504 #ifndef NDEBUG
1505 vector tvec = objp->orient.fvec;
1506 vector  vel_in_copy;
1507 matrix  objp_orient_copy;
1508
1509 vel_in_copy = vel_in;
1510 objp_orient_copy = objp->orient;
1511
1512 vel_in = vel_in_copy;   //      HERE
1513 objp->orient = objp_orient_copy;
1514 #endif
1515         if (rvec != NULL) {
1516                 matrix  out_orient, goal_orient;
1517
1518                 vm_vector_2_matrix(&goal_orient, &desired_fvec, NULL, rvec);
1519                 vm_matrix_interpolate(&goal_orient, &curr_orient, &vel_in, delta_time, &out_orient, &vel_out, &vel_limit, &acc_limit);
1520                 objp->orient = out_orient;
1521         } else {
1522                 vm_forward_interpolate(&desired_fvec, &curr_orient, &vel_in, delta_time, delta_bank, &objp->orient, &vel_out, &vel_limit, &acc_limit);
1523         }
1524 #ifndef NDEBUG
1525 if (!((objp->type == OBJ_WEAPON) && (Weapon_info[Weapons[objp->instance].weapon_info_index].subtype == WP_MISSILE))) {
1526         if (delta_time < 0.25f && vm_vec_dot(&objp->orient.fvec, &tvec) < 0.1f)
1527                 Int3(); //      Get Andsager.  A ship has turned too far in one frame.
1528 }
1529 #endif
1530
1531         pip->rotvel = vel_out;
1532 }
1533
1534 void init_ship_info()
1535 {
1536         int     i;
1537
1538         if (Ship_info_inited)
1539                 return;
1540
1541         for (i=0; i<MAX_SHIP_TYPES; i++) {
1542                 Ship_info[i].min_speed = - Ship_info[i].max_rear_vel;
1543                 Ship_info[i].max_accel = Ship_info[i].max_vel.z;
1544         }
1545
1546         Ship_info_inited = 1;
1547
1548 }
1549
1550 //      Set aip->target_objnum to objnum
1551 //      Update aip->previous_target_objnum.
1552 //      If new target (objnum) is different than old target, reset target_time.
1553 int set_target_objnum(ai_info *aip, int objnum)
1554 {
1555 /*
1556         char    old_name[32], new_name[32];
1557
1558         if (!timestamp_elapsed(aip->ok_to_target_timestamp))
1559                 return aip->target_objnum;
1560
1561         if (Player_ship && (Ships[aip->shipnum].team == Player_ship->team)) {
1562                 if (aip->target_objnum == -1)
1563                         strcpy(old_name, "none");
1564                 else
1565                         strcpy(old_name, Ships[Objects[aip->target_objnum].instance].ship_name);
1566
1567                 if (objnum == -1)
1568                         strcpy(new_name, "none");
1569                 else
1570                         strcpy(new_name, Ships[Objects[objnum].instance].ship_name);
1571
1572                 nprintf(("AI", "Ship %s changing target from %s to %s\n", Ships[aip->shipnum].ship_name, old_name, new_name));
1573         }
1574 */
1575
1576         // AL 2-25-97: Ensure that a protected ship isn't being set as a target (for non-players only)
1577         /*
1578         if ( objnum >= 0 ) {
1579                 if ( !(Objects[Ships[aip->shipnum].objnum].flags & OF_PLAYER_SHIP) ) {
1580                         if ( Objects[objnum].flags & OF_PROTECTED ) {
1581                                 // AL 2-26-97: removing Int3() until issue with setting OF_PROTECTED in ai_set_attack_subsystem()
1582                                 //Int3();                                                               // this should not happen
1583                                 return aip->target_objnum;              // don't change targets
1584                         }
1585                 }
1586         }
1587         */
1588
1589         if ((aip != Player_ai) && (!timestamp_elapsed(aip->ok_to_target_timestamp))) {
1590                 return aip->target_objnum;
1591         }
1592
1593         if (aip->target_objnum == objnum) {
1594                 aip->previous_target_objnum = aip->target_objnum;
1595         } else {
1596                 aip->previous_target_objnum = aip->target_objnum;
1597
1598                 // ignore this assert if a multiplayer observer
1599                 if((Game_mode & GM_MULTIPLAYER) && (aip == Player_ai) && (Player_obj->type == OBJ_OBSERVER)){
1600                 } else {
1601                         Assert(objnum != Ships[aip->shipnum].objnum);   //      make sure not targeting self
1602                 }
1603
1604                 // if stealth target, init ai_info for stealth
1605                 if ( (objnum > 0) && is_object_stealth_ship(&Objects[objnum]) ) {
1606                         init_ai_stealth_info(aip, &Objects[objnum]);
1607                 }
1608
1609                 aip->target_objnum = objnum;
1610                 aip->target_time = 0.0f;
1611                 aip->target_signature = Objects[objnum].signature;
1612                 // clear targeted subsystem
1613                 set_targeted_subsys(aip, NULL, -1);
1614         }
1615         
1616         return aip->target_objnum;
1617 }
1618
1619 int ai_select_primary_weapon(object *objp, object *other_objp, int flags);
1620
1621 //      Make new_subsys the targeted subsystem of ship *aip.
1622 ship_subsys *set_targeted_subsys(ai_info *aip, ship_subsys *new_subsys, int parent_objnum)
1623 {
1624         Assert(aip != NULL);
1625
1626         aip->last_subsys_target = aip->targeted_subsys;
1627         aip->targeted_subsys = new_subsys;
1628         aip->targeted_subsys_parent = parent_objnum;
1629
1630         if ( new_subsys ) {
1631                 // Make new_subsys target
1632                 if (new_subsys->system_info->type == SUBSYSTEM_ENGINE) {
1633                         if ( aip != Player_ai ) {
1634                                 ai_select_primary_weapon(&Objects[Ships[aip->shipnum].objnum], &Objects[parent_objnum], WIF_PUNCTURE);
1635                                 ship_primary_changed(&Ships[aip->shipnum]);     // AL: maybe send multiplayer information when AI ship changes primaries
1636                         }
1637                 }
1638
1639                 if ( aip == Player_ai ) {
1640                         hud_lock_reset(0.5f);
1641                 }
1642
1643         } else {
1644                 // Cleanup any subsys path information if it exists
1645                 ai_big_subsys_path_cleanup(aip);
1646         }
1647         
1648         return aip->targeted_subsys;
1649 }                                                                                         
1650
1651 // called to init the data for single ai object.  At this point,
1652 // the ship and the object and the ai_info are are correctly
1653 // linked together. Ai_info[ai_index].shipnum is the only valid field 
1654 // in ai_info.
1655 //      This is called right when the object is parsed, so you can't assume much
1656 //      has been initialized.  For example, wings, waypoints, goals are probably
1657 //      not yet loaded. --MK, 10/8/96
1658 void ai_object_init(object * obj, int ai_index)
1659 {
1660         ai_info *aip;
1661         Assert(ai_index >= 0 && ai_index < MAX_AI_INFO);
1662
1663         aip = &Ai_info[ai_index];
1664
1665         aip->type = 0;          //      0 means not in use.
1666         aip->wing = -1;         //      Member of what wing? -1 means none.
1667         aip->ai_class = Ship_info[Ships[obj->instance].ship_info_index].ai_class;
1668         aip->behavior = AIM_NONE;
1669 }
1670
1671 //      If *aip is docked, set max acceleration to A->mass/(A->mass + B->mass) where A is *aip and B is dock object
1672 void adjust_accel_for_docking(ai_info *aip)
1673 {
1674         if (aip->dock_objnum != -1) {
1675                 object  *obj2p = &Objects[aip->dock_objnum];
1676                 object  *obj1p;
1677
1678                 obj1p = &Objects[Ships[aip->shipnum].objnum];
1679
1680                 if (obj2p->signature == aip->dock_signature) {
1681                         float   ratio;
1682
1683                         ratio = obj1p->phys_info.mass / (obj1p->phys_info.mass + obj2p->phys_info.mass);
1684
1685                         // put cap on how much ship can slow down
1686                         if (ratio < 0.8) {
1687                                 ratio = 0.8f;
1688                         }
1689
1690                         if (AI_ci.forward > ratio) {
1691                                 AI_ci.forward = ratio;
1692                         }
1693                 }
1694         }
1695 }
1696
1697 // -------------------------------------------------------------------
1698 void accelerate_ship(ai_info *aip, float accel)
1699 {
1700         aip->prev_accel = accel;
1701         AI_ci.forward = accel;
1702         adjust_accel_for_docking(aip);
1703 }
1704
1705 //      --------------------------------------------------------------------------
1706 void change_acceleration(ai_info *aip, float delta_accel)
1707 {
1708         float   new_accel;
1709
1710         if (delta_accel < 0.0f) {
1711                 if (aip->prev_accel > 0.0f)
1712                         aip->prev_accel = 0.0f;
1713         } else if (aip->prev_accel < 0.0f)
1714                 aip->prev_accel = 0.0f;
1715
1716         new_accel = aip->prev_accel + delta_accel * flFrametime;
1717
1718         if (new_accel > 1.0f)
1719                 new_accel = 1.0f;
1720         else if (new_accel < -1.0f)
1721                 new_accel = -1.0f;
1722         
1723         aip->prev_accel = new_accel;
1724
1725         AI_ci.forward = new_accel;
1726         adjust_accel_for_docking(aip);
1727 }
1728
1729 void set_accel_for_target_speed(object *objp, float tspeed)
1730 {
1731         float   max_speed;
1732         ai_info *aip;
1733
1734         aip = &Ai_info[Ships[objp->instance].ai_index];
1735
1736         max_speed = Ships[objp->instance].current_max_speed;
1737
1738         AI_ci.forward = tspeed/max_speed;
1739         aip->prev_accel = AI_ci.forward;
1740
1741         adjust_accel_for_docking(aip);
1742 }
1743
1744 //      Stuff perim_point with a point on the perimeter of the sphere defined by object *objp
1745 //      on the vector from the center of *objp through the point *vp.
1746 void project_point_to_perimeter(vector *perim_point, vector *pos, float radius, vector *vp)
1747 {
1748         vector  v1;
1749         float           mag;
1750
1751         vm_vec_sub(&v1, vp, pos);
1752         mag = vm_vec_mag(&v1);
1753
1754         if (mag == 0.0f) {
1755                 Warning(LOCATION, "projectable point is at center of sphere.");
1756                 vm_vec_make(&v1, 0.0f, radius, 0.0f);
1757         } else {
1758                 vm_vec_normalize(&v1);
1759                 vm_vec_scale(&v1, 1.1f * radius + 10.0f);
1760         }
1761
1762         vm_vec_add2(&v1, pos);
1763         *perim_point = v1;
1764 }
1765
1766 //      Stuff tan1 with tangent point on sphere.  tan1 is point nearer to *p1
1767 //      *p0 is point through which tangents pass.
1768 //      *centerp is center of sphere.
1769 //      *p1 is another point in space to define the plane in which tan1, tan2 reside.
1770 //      radius is the radius of the sphere.
1771 //      Note, this is a very approximate function just for AI.
1772 //      Note also: On 12/26/96, p1 is used to define the plane perpendicular to that which
1773 //      contains the tangent point.
1774 void get_tangent_point(vector *tan1, vector *p0, vector *centerp, vector *p1, float radius)
1775 {
1776         vector  dest_vec, v2c, perp_vec, temp_vec, v2;
1777         float           dist, ratio;
1778
1779         //      Detect condition of point inside sphere.
1780         if (vm_vec_dist(p0, centerp) < radius)
1781                 project_point_to_perimeter(tan1, centerp, radius, p0);
1782         else {
1783                 vm_vec_normalized_dir(&v2c, centerp, p0);
1784
1785                 //      Compute perpendicular vector using p0, centerp, p1
1786                 vm_vec_normal(&temp_vec, p0, centerp, p1);
1787                 vm_vec_sub(&v2, centerp, p0);
1788                 vm_vec_cross(&perp_vec, &temp_vec, &v2);
1789
1790                 vm_vec_normalize(&perp_vec);
1791
1792                 dist = vm_vec_dist_quick(p0, centerp);
1793                 ratio = dist / radius;
1794
1795                 if (ratio < 2.0f)
1796                         vm_vec_scale_add(&dest_vec, &perp_vec, &v2c, ratio-1.0f);
1797                 else
1798                         vm_vec_scale_add(&dest_vec, &v2c, &perp_vec, (1.0f + 1.0f/ratio));
1799
1800                 vm_vec_scale_add(tan1, p0, &dest_vec, dist + radius);
1801         }
1802 }
1803
1804 //      --------------------------------------------------------------------------
1805 //      Given an object and a point, turn towards the point, resulting in
1806 // approach behavior.
1807 void turn_towards_point(object *objp, vector *point, vector *slide_vec, float bank_override)
1808 {
1809         ai_info *aip;
1810         aip = &Ai_info[Ships[Pl_objp->instance].ai_index];
1811         
1812         // check if in formation and if not leader, don't change rotvel.z (bank to match leader elsewhere)
1813         if (aip->ai_flags & AIF_FORMATION) {
1814                 if (&Objects[aip->goal_objnum] != objp) {
1815                         float rotvel_z = objp->phys_info.rotvel.z;
1816                         ai_turn_towards_vector(point, objp, flFrametime, Ship_info[Ships[objp->instance].ship_info_index].srotation_time, slide_vec, NULL, bank_override, 0);
1817                         objp->phys_info.rotvel.z = rotvel_z;
1818                 }
1819         } else {
1820                 // normal turn
1821                 ai_turn_towards_vector(point, objp, flFrametime, Ship_info[Ships[objp->instance].ship_info_index].srotation_time, slide_vec, NULL, bank_override, 0);
1822         }
1823 }
1824
1825 //      --------------------------------------------------------------------------
1826 //      Given an object and a point, turn away from the point, resulting in avoidance behavior.
1827 //      Note: Turn away at full speed, not scaled down by skill level.
1828 void turn_away_from_point(object *objp, vector *point, float bank_override)
1829 {
1830         vector  opposite_point;
1831
1832         vm_vec_sub(&opposite_point, &objp->pos, point);
1833         vm_vec_add2(&opposite_point, &objp->pos);
1834
1835         ai_turn_towards_vector(&opposite_point, objp, flFrametime, Ship_info[Ships[objp->instance].ship_info_index].srotation_time, NULL, NULL, bank_override, AITTV_FAST);
1836 }
1837
1838
1839 //      --------------------------------------------------------------------------
1840 //      Given an object and a point, turn tangent to the point, resulting in
1841 // a circling behavior.
1842 //      Make object *objp turn around the point *point with a radius of radius.
1843 //      Note that this isn't the same as following a circle of radius radius with
1844 //      center *point, but it should be adequate.
1845 //      Note that if you want to circle an object without hitting it, you should use
1846 //      about twice that object's radius for radius, else you'll certainly bump into it.
1847 //      Return dot product to goal point.
1848 float turn_towards_tangent(object *objp, vector *point, float radius)
1849 {
1850         vector  vec_to_point;
1851         vector  goal_point;
1852         vector  perp_point;                             //      point radius away from *point on vector to objp->pos
1853         vector  up_vec, perp_vec;
1854
1855         vm_vec_normalized_dir(&vec_to_point, point, &objp->pos);
1856         vm_vec_crossprod(&up_vec, &vec_to_point, &objp->orient.fvec);
1857         vm_vec_crossprod(&perp_vec, &vec_to_point, &up_vec);
1858
1859         vm_vec_scale_add(&perp_point, point, &vec_to_point, -radius);
1860         if (vm_vec_dot(&objp->orient.fvec, &perp_vec) > 0.0f) {
1861                 vm_vec_scale_add(&goal_point, &perp_point, &perp_vec, radius);
1862         } else {
1863                 vm_vec_scale_add(&goal_point, &perp_point, &perp_vec, -radius);
1864         }
1865
1866 //      Ai_info[Ships[objp->instance].ai_index].goal_point = goal_point;
1867         turn_towards_point(objp, &goal_point, NULL, 0.0f);
1868
1869         vector  v2g;
1870
1871         vm_vec_normalized_dir(&v2g, &goal_point, &objp->pos);
1872         return vm_vec_dot(&objp->orient.fvec, &v2g);
1873 }
1874
1875 float turn_toward_tangent_with_axis(object *objp, object *center_objp, float radius)
1876 {
1877         vector r_vec, theta_vec;
1878         vector center_vec, vec_on_cylinder, sph_r_vec;
1879         float center_obj_z;
1880
1881         // find closest z of center objp
1882         vm_vec_sub(&sph_r_vec, &objp->pos, &center_objp->pos);
1883         center_obj_z = vm_vec_dotprod(&sph_r_vec, &center_objp->orient.fvec);
1884
1885         // find pt on axis with closest z
1886         vm_vec_scale_add(&center_vec, &center_objp->pos, &center_objp->orient.fvec, center_obj_z);
1887
1888         // get r_vec
1889         vm_vec_sub(&r_vec, &objp->pos, &center_vec);
1890 //      float r_mag = vm_vec_normalize_quick(&r_vec);
1891 //      mprintf(("cur_r: %.1f, desired_r: %.1f\n", r_mag, radius));
1892         Assert( (vm_vec_dotprod(&r_vec, &center_objp->orient.fvec) < 0.0001));
1893
1894         // get theta vec - perp to r_vec and z_vec
1895         vm_vec_crossprod(&theta_vec, &center_objp->orient.fvec, &r_vec);
1896
1897 #ifndef NDEBUG
1898         float mag = vm_vec_normalize(&theta_vec);
1899         Assert(mag > 0.9999 && mag < 1.0001);
1900 #endif
1901
1902         vector temp;
1903         vm_vec_crossprod(&temp, &r_vec, &theta_vec);
1904
1905 #ifndef NDEBUG
1906         float dot = vm_vec_dotprod(&temp, &center_objp->orient.fvec);
1907         Assert( dot >0.9999 && dot < 1.0001);
1908 #endif
1909
1910         // find pt on clylinder with closest z
1911         vm_vec_scale_add(&vec_on_cylinder, &center_vec, &r_vec, radius);
1912
1913         vector goal_pt, v2g;
1914         vm_vec_scale_add(&goal_pt, &vec_on_cylinder, &theta_vec, radius);
1915
1916 //      Ai_info[Ships[objp->instance].ai_index].goal_point = goal_pt;
1917         turn_towards_point(objp, &goal_pt, NULL, 0.0f);
1918
1919         vm_vec_normalized_dir(&v2g, &goal_pt, &objp->pos);
1920         return vm_vec_dot(&objp->orient.fvec, &v2g);
1921 }
1922
1923 //      Returns a point radius units away from *point that *objp should turn towards to orbit *point
1924 void get_tangent_point(vector *goal_point, object *objp, vector *point, float radius)
1925 {
1926         vector  vec_to_point;
1927         vector  perp_point;                             //      point radius away from *point on vector to objp->pos
1928         vector  up_vec, perp_vec;
1929
1930         vm_vec_normalized_dir(&vec_to_point, point, &objp->pos);
1931         vm_vec_crossprod(&up_vec, &vec_to_point, &objp->orient.fvec);
1932         vm_vec_crossprod(&perp_vec, &vec_to_point, &up_vec);
1933         vm_vec_normalize(&perp_vec);
1934
1935         vm_vec_scale_add(&perp_point, point, &vec_to_point, -radius);
1936
1937         if (vm_vec_dot(&objp->orient.fvec, &perp_vec) > 0.0f) {
1938                 vm_vec_scale_add(goal_point, &perp_point, &perp_vec, radius);
1939         } else {
1940                 vm_vec_scale_add(goal_point, &perp_point, &perp_vec, -radius);
1941         }
1942 }
1943
1944 int     Player_attacking_enabled = 1;
1945
1946 // -----------------------------------------------------------------------------
1947 // Determine whether an object is targetable within a nebula
1948 int object_is_targetable(object *target, ship *viewer)
1949 {
1950         int stealth_ship = 0;
1951
1952         // if target is ship, check if visible by team
1953         if (target->type == OBJ_SHIP) {
1954                 stealth_ship = (Ship_info[Ships[target->instance].ship_info_index].flags & SIF_STEALTH);
1955                 if ( ship_is_visible_by_team(target->instance, viewer->team) == 1) {
1956                         return 1;
1957                 }
1958         }
1959
1960         // for AI partially targetable works as fully targetable, except for stealth ship
1961         if (stealth_ship) {
1962                 // if not team targetable, check if within frustrum
1963                 if ( ai_is_stealth_visible(&Objects[viewer->objnum], target) == STEALTH_VISIBLE ) {
1964                         return 1;
1965                 } else {
1966                         return 0;
1967                 }
1968         }
1969
1970         // if not fully targetable by team, check awacs level with viewer
1971         // allow targeting even if only only partially targetable to player
1972         float radar_return = awacs_get_level(target, viewer);
1973         if ( radar_return > 0.4 ) {
1974                 return 1;
1975         } else {
1976                 return 0;
1977         }
1978 }
1979
1980 //      Return number of enemies attacking object objnum
1981 //
1982 // AL 10.26.97: Also include turrets on large ships when couting enemies attacking
1983 int num_enemies_attacking(int objnum)
1984 {
1985         object          *objp;
1986         ship                    *sp;
1987         ship_subsys     *ssp;
1988         ship_obj                *so;
1989         int                     count;
1990
1991         count = 0;
1992
1993         for ( so = GET_FIRST(&Ship_obj_list); so != END_OF_LIST(&Ship_obj_list); so = GET_NEXT(so) ) {
1994                 objp = &Objects[so->objnum];
1995                 Assert(objp->instance != -1);
1996                 sp = &Ships[objp->instance];
1997
1998                 if (Ai_info[sp->ai_index].target_objnum == objnum)
1999                         count++;
2000
2001                 // consider turrets that may be attacking objnum (but only turrets on SIF_BIG_SHIP ships)
2002                 if ( Ship_info[sp->ship_info_index].flags & SIF_BIG_SHIP ) {
2003
2004                         // loop through all the subsystems, check if turret has objnum as a target
2005                         ssp = GET_FIRST(&sp->subsys_list);
2006                         while ( ssp != END_OF_LIST( &sp->subsys_list ) ) {
2007
2008                                 if ( ssp->system_info->type == SUBSYSTEM_TURRET ) {
2009                                         if ( (ssp->turret_enemy_objnum == objnum) && (ssp->current_hits > 0) ) {
2010                                                 count++;
2011                                         }
2012                                 }
2013                                 ssp = GET_NEXT( ssp );
2014                         } // end while
2015                 }
2016         }
2017
2018         return count;
2019 }
2020
2021 //      Get the team to fire on given an object.
2022 int get_enemy_team_mask(int objnum)
2023 {
2024         int     my_team, enemy_team_mask;
2025
2026         my_team = Ships[Objects[objnum].instance].team;
2027
2028         if (Mission_all_attack) {
2029                 //      All teams attack all teams.
2030                 switch (my_team) {
2031                 case TEAM_FRIENDLY:
2032                         enemy_team_mask = TEAM_HOSTILE | TEAM_NEUTRAL | TEAM_TRAITOR;
2033                         break;
2034                 case TEAM_HOSTILE:
2035                         enemy_team_mask = TEAM_FRIENDLY | TEAM_NEUTRAL | TEAM_TRAITOR;
2036                         break;
2037                 case TEAM_NEUTRAL:
2038                         enemy_team_mask = TEAM_FRIENDLY | TEAM_HOSTILE | TEAM_TRAITOR;
2039                         break;
2040                 case TEAM_UNKNOWN:
2041                         enemy_team_mask = TEAM_HOSTILE;
2042                         break;
2043                 case TEAM_TRAITOR:
2044                         enemy_team_mask = TEAM_FRIENDLY | TEAM_NEUTRAL | TEAM_HOSTILE | TEAM_TRAITOR;
2045                         break;
2046                 default:
2047                         enemy_team_mask = TEAM_HOSTILE;
2048                         Int3();                 //      Illegal value for team!
2049                         break;
2050                 }
2051         } else {
2052                 switch (my_team) {
2053                 case TEAM_FRIENDLY:
2054                         enemy_team_mask = TEAM_HOSTILE | TEAM_NEUTRAL | TEAM_TRAITOR;
2055                         break;
2056                 case TEAM_HOSTILE:
2057                         enemy_team_mask = TEAM_FRIENDLY | TEAM_NEUTRAL | TEAM_TRAITOR;
2058                         break;
2059                 case TEAM_NEUTRAL:
2060                         enemy_team_mask = TEAM_FRIENDLY | TEAM_TRAITOR;
2061                         break;
2062                 case TEAM_UNKNOWN:
2063                         enemy_team_mask = TEAM_HOSTILE;
2064                         break;
2065                 case TEAM_TRAITOR:
2066                         enemy_team_mask = TEAM_FRIENDLY | TEAM_NEUTRAL | TEAM_HOSTILE | TEAM_TRAITOR;
2067                         break;
2068                 default:
2069                         enemy_team_mask = TEAM_HOSTILE;
2070                         Int3();                 //      Illegal value for team!
2071                         break;
2072                 }
2073         }
2074
2075         return enemy_team_mask;
2076 }
2077
2078 //      Scan all the ships in *objp's wing.
2079 //      Return the lowest maximum speed of a ship in the wing.
2080 //      Current maximum speed (based on energy settings) is shipp->current_max_speed
2081 float get_wing_lowest_max_speed(object *objp)
2082 {
2083         ship            *shipp;
2084         ai_info *aip;
2085         float           lowest_max_speed;
2086         int             wingnum;
2087         object  *o;
2088         ship_obj        *so;
2089
2090         Assert(objp->type == OBJ_SHIP);
2091         Assert((objp->instance >= 0) && (objp->instance < MAX_OBJECTS));
2092         shipp = &Ships[objp->instance];
2093         Assert((shipp->ai_index >= 0) && (shipp->ai_index < MAX_AI_INFO));
2094         aip = &Ai_info[shipp->ai_index];
2095
2096         wingnum = aip->wing;
2097
2098         lowest_max_speed = shipp->current_max_speed;
2099
2100         if ( wingnum == -1 )
2101                 return lowest_max_speed;
2102
2103         Assert(wingnum >= 0);
2104
2105         for ( so = GET_FIRST(&Ship_obj_list); so != END_OF_LIST(&Ship_obj_list); so = GET_NEXT(so) ) {
2106                 o = &Objects[so->objnum];
2107                 ship    *oshipp = &Ships[o->instance];
2108                 ai_info *oaip = &Ai_info[oshipp->ai_index];
2109
2110                 if ((oaip->mode == AIM_WAYPOINTS) && (oaip->wing == wingnum)) {
2111                         //      Note: If a ship in the wing has a super low max speed, probably its engines are disabled.  So, fly along and
2112                         //      ignore the poor guy.
2113                         float   cur_max = oshipp->current_max_speed;
2114
2115                         if (oaip->ai_flags & AIF_DOCKED) {
2116                                 if (oaip->dock_objnum > -1)
2117                                         if (Objects[oaip->dock_objnum].type == OBJ_SHIP) 
2118                                                 cur_max *= o->phys_info.mass/(o->phys_info.mass + Objects[oaip->dock_objnum].phys_info.mass);
2119                         }
2120                                                         
2121                         if ((oshipp->current_max_speed > 5.0f) && (cur_max < lowest_max_speed)) {
2122                                 lowest_max_speed = cur_max;
2123                         }
2124                 }
2125         }
2126
2127         return lowest_max_speed;
2128 }
2129
2130 /*
2131 //      Tell everyone to ignore object objnum.
2132 void set_global_ignore_object(int objnum)
2133 {
2134         int     i;
2135
2136         Assert(Objects[objnum].type == OBJ_SHIP);
2137
2138         nprintf(("AI", "Telling everyone to ignore object %s\n", Ships[Objects[objnum].instance].ship_name));
2139
2140         for (i=0; i<MAX_IGNORE_OBJECTS; i++) {
2141                 if (Ignore_objects[i].objnum == -1) {
2142                         Ignore_objects[i].objnum = objnum;
2143                         Ignore_objects[i].signature = Objects[objnum].signature;
2144                         break;
2145                 }
2146         }
2147
2148         if (i == MAX_IGNORE_OBJECTS) {
2149                 //      Couldn't find a free slot, but maybe one of these objects has died.
2150                 for (i=0; i<MAX_IGNORE_OBJECTS; i++) {
2151                         int     o = Ignore_objects[i].objnum;
2152                         if (Objects[o].type != OBJ_SHIP)
2153                                 break;          //      Not a ship, so use this slot.
2154                         if (Objects[o].signature != Ignore_objects[i].signature)
2155                                 break;          //      Signatures don't match, so use this slot.
2156                 }
2157
2158                 if (i != MAX_IGNORE_OBJECTS) {
2159                         Ignore_objects[i].objnum = objnum;
2160                         Ignore_objects[i].signature = Objects[objnum].signature;
2161                 } else {
2162                         nprintf(("Warning", "Ignore_objects buffer full.  Stealing a slot to ignore object #%i\n"));
2163                         Int3();
2164
2165                         int     r;
2166
2167                         r = objnum % MAX_IGNORE_OBJECTS;
2168
2169                         Ignore_objects[r].objnum = objnum;
2170                         Ignore_objects[r].signature = Objects[objnum].signature;
2171                 }
2172         }
2173 }
2174
2175 */
2176
2177 //      Determine if object objnum is supposed to be ignored by object with ai_info *aip.
2178 //      Return:
2179 //              TRUE    if objnum is aip->ignore_objnum (and signatures match)
2180 //                              or objnum is in ignore wing
2181 //              FALSE   otherwise
2182 int is_ignore_object(ai_info *aip, int objnum)
2183 {
2184
2185 /*      //      First, scan all objects in global array of objects to be ignored.
2186         for (int i=0; i<MAX_IGNORE_OBJECTS; i++)
2187                 if (Ignore_objects[i].objnum != -1)
2188                         if (objnum == Ignore_objects[i].objnum)
2189                                 if (Objects[Ignore_objects[i].objnum].signature == Ignore_objects[i].signature)
2190                                         return 1;
2191 */
2192
2193         //      Didn't find in global list.  Now check 
2194         if (aip->ignore_objnum == UNUSED_OBJNUM)
2195                 return 0;                                                                       //      Not ignoring anything.
2196         else if (aip->ignore_objnum >= 0) {             //      This means it's ignoring an object, not a wing.
2197                 if (aip->ignore_objnum == objnum) {
2198                         if (Objects[aip->ignore_objnum].signature == aip->ignore_signature) {
2199                                 return 1;
2200                         } else {
2201                                 aip->ignore_objnum = UNUSED_OBJNUM;
2202                                 return 0;
2203                         }
2204                 } else {
2205                         return 0;
2206                 }
2207         } else {                                                                                        //      Ignoring a wing.
2208                 Int3(); // Should never happen.  I thought I removed this behavior! -- MK, 5/17/98
2209                 return 0;
2210 /*              int     ignore_wingnum = -(aip->ignore_objnum + 1);
2211
2212                 Assert(ignore_wingnum < MAX_WINGS);
2213                 Assert(aip->shipnum >= 0);
2214                 return (Ships[Objects[objnum].instance].wingnum == ignore_wingnum);
2215 */      }
2216 }
2217
2218 // -----------------------------------------------------------------------------
2219
2220 // given a ship with bounding box and a point, find the closest point on the bbox
2221 int get_nearest_bbox_point(object *ship_obj, vector *start, vector *box_pt)
2222 {
2223         vector temp, rf_start;
2224         polymodel *pm;
2225         pm = model_get(Ship_info[Ships[ship_obj->instance].ship_info_index].modelnum);
2226
2227         // get start in ship rf
2228         vm_vec_sub(&temp, start, &ship_obj->pos);
2229         vm_vec_rotate(&rf_start, &temp, &ship_obj->orient);
2230
2231         // find box_pt
2232         int inside = project_point_onto_bbox(&pm->mins, &pm->maxs, &rf_start, &temp);
2233
2234         // get box_pt in world rf
2235         vm_vec_unrotate(box_pt, &temp, &ship_obj->orient);
2236         vm_vec_add2(box_pt, &ship_obj->pos);
2237
2238         return inside;
2239 }
2240
2241
2242 typedef struct eval_nearest_objnum {
2243         int     objnum;
2244         object *trial_objp;
2245         int     enemy_team_mask;
2246         int     enemy_wing;
2247         float   range;
2248         int     max_attackers;
2249         int     nearest_objnum;
2250         float   nearest_dist;
2251         int     check_danger_weapon_objnum;
2252 } eval_nearest_objnum;
2253
2254
2255 void evaluate_object_as_nearest_objnum(eval_nearest_objnum *eno)
2256 {
2257         ai_info *aip;
2258         ship_subsys     *attacking_subsystem;
2259
2260         aip = &Ai_info[Ships[Objects[eno->objnum].instance].ai_index];
2261
2262         attacking_subsystem = aip->targeted_subsys;
2263
2264         if ((attacking_subsystem != NULL) || !(eno->trial_objp->flags & OF_PROTECTED)) {
2265                 if ( OBJ_INDEX(eno->trial_objp) != eno->objnum ) {
2266 #ifndef NDEBUG
2267                         if (!Player_attacking_enabled && (eno->trial_objp == Player_obj))
2268                                 return;
2269 #endif
2270                         //      If only supposed to attack ship in a specific wing, don't attack other ships.
2271                         if ((eno->enemy_wing != -1) && (Ships[eno->trial_objp->instance].wingnum != eno->enemy_wing))
2272                                 return;
2273
2274                         //      Don't keep firing at a ship that is in its death throes.
2275                         if (Ships[eno->trial_objp->instance].flags & SF_DYING)
2276                                 return;
2277
2278                         if (is_ignore_object(aip, ((eno->trial_objp)-Objects)))
2279                                 return;
2280
2281                         if (eno->trial_objp->flags & OF_PROTECTED)
2282                                 return;
2283
2284                         if (Ships[eno->trial_objp->instance].flags & SF_ARRIVING)
2285                                 return;
2286
2287                         ship_info *sip = &Ship_info[Ships[eno->trial_objp->instance].ship_info_index];
2288
2289                         if (sip->flags & (SIF_NO_SHIP_TYPE | SIF_NAVBUOY))
2290                                 return;
2291
2292                         if (Ships[eno->trial_objp->instance].team & eno->enemy_team_mask) {
2293                                 float   dist;
2294                                 int     num_attacking;
2295
2296                                 // Allow targeting of stealth in nebula by his firing at me
2297                                 // This is done for a specific ship, not generally.
2298                                 if ( !eno->check_danger_weapon_objnum ) {
2299                                         // check if can be targeted if inside nebula
2300                                         if ( !object_is_targetable(eno->trial_objp, &Ships[Objects[eno->objnum].instance]) ) {
2301                                                 // check if stealth ship is visible, but not "targetable"
2302                                                 if ( !((sip->flags & SIF_STEALTH) && ai_is_stealth_visible(&Objects[eno->objnum], eno->trial_objp)) ) {
2303                                                         return;
2304                                                 }
2305                                         }
2306                                 }
2307
2308                                 // if objnum is BIG or HUGE, find distance to bbox
2309                                 if (sip->flags & (SIF_BIG_SHIP | SIF_HUGE_SHIP)) {
2310                                         vector box_pt;
2311                                         // check if inside bbox
2312                                         int inside = get_nearest_bbox_point(eno->trial_objp, &Objects[eno->objnum].pos, &box_pt);
2313                                         if (inside) {
2314                                                 dist = 10.0f;
2315                                                 // on the box
2316                                         } else {
2317                                                 dist = vm_vec_dist_quick(&Objects[eno->objnum].pos, &box_pt);
2318                                         }
2319                                 } else {
2320                                         dist = vm_vec_dist_quick(&Objects[eno->objnum].pos, &eno->trial_objp->pos);
2321                                 }
2322                                 
2323                                 //      Make it more likely that fighters (or bombers) will be picked as an enemy by scaling up distance for other types.
2324                                 if ((Ship_info[Ships[eno->trial_objp->instance].ship_info_index].flags & (SIF_FIGHTER | SIF_BOMBER))) {
2325                                         dist = dist * 0.5f;
2326                                 }
2327
2328                                 num_attacking = num_enemies_attacking(eno->trial_objp-Objects);
2329                                 if ((sip->flags & (SIF_BIG_SHIP | SIF_HUGE_SHIP)) || (num_attacking < eno->max_attackers)) {
2330                                         if (!(sip->flags & (SIF_BIG_SHIP | SIF_HUGE_SHIP))){
2331                                                 dist *= (float) (num_attacking+2)/2.0f;                         //      prevents lots of ships from attacking same target
2332                                         }
2333
2334                                         if (eno->trial_objp->flags & OF_PLAYER_SHIP){
2335                                                 dist *= 1.0f + (NUM_SKILL_LEVELS - Game_skill_level - 1)/NUM_SKILL_LEVELS;      //      Favor attacking non-players based on skill level.
2336                                         }
2337
2338                                         if (dist < eno->nearest_dist) {
2339                                                 eno->nearest_dist = dist;
2340                                                 eno->nearest_objnum = eno->trial_objp-Objects;
2341                                         }
2342                                 }
2343                         }
2344                 }
2345         }
2346
2347 }
2348
2349
2350 //      Given an object and an enemy team, return the index of the nearest enemy object.
2351 //      Unless aip->targeted_subsys != NULL, don't allow to attack objects
2352 //      with OF_PROTECTED bit set.
2353 //      Ship must be within range "range".
2354 //      Don't attack a ship that already has at least max_attackers attacking it.
2355 int get_nearest_objnum(int objnum, int enemy_team_mask, int enemy_wing, float range, int max_attackers)
2356 {
2357         object  *danger_weapon_objp;
2358         ai_info *aip;
2359         ship_obj        *so;
2360
2361         // initialize eno struct
2362         eval_nearest_objnum eno;
2363         eno.enemy_team_mask = enemy_team_mask;
2364         eno.enemy_wing = enemy_wing;
2365         eno.max_attackers = max_attackers;
2366         eno.objnum = objnum;
2367         eno.range = range;
2368         eno.nearest_dist = range;
2369         eno.nearest_objnum = -1;
2370         eno.check_danger_weapon_objnum = 0;
2371
2372         // go through the list of all ships and evaluate as potential targets
2373         for ( so = GET_FIRST(&Ship_obj_list); so != END_OF_LIST(&Ship_obj_list); so = GET_NEXT(so) ) {
2374                 eno.trial_objp = &Objects[so->objnum];
2375                 evaluate_object_as_nearest_objnum(&eno);
2376
2377         }
2378
2379         // check if danger_weapon_objnum has will show a stealth ship
2380         aip = &Ai_info[Ships[Objects[objnum].instance].ai_index];
2381         if (aip->danger_weapon_objnum >= 0) {
2382                 danger_weapon_objp = &Objects[aip->danger_weapon_objnum];
2383                 // validate weapon
2384                 if (danger_weapon_objp->signature == aip->danger_weapon_signature) {
2385                         Assert(danger_weapon_objp->type == OBJ_WEAPON);
2386                         // check if parent is a ship
2387                         if (danger_weapon_objp->parent >= 0) {
2388                                 if ( is_object_stealth_ship(&Objects[danger_weapon_objp->parent]) ) {
2389                                         // check if stealthy
2390                                         if ( ai_is_stealth_visible(&Objects[objnum], &Objects[danger_weapon_objp->parent]) != STEALTH_FULLY_TARGETABLE ) {
2391                                                 // check if weapon is laser
2392                                                 if (Weapon_info[Weapons[danger_weapon_objp->instance].weapon_info_index].subtype == WP_LASER) {
2393                                                         // check stealth ship by its laser fire
2394                                                         eno.check_danger_weapon_objnum = 1;
2395                                                         eno.trial_objp = &Objects[danger_weapon_objp->parent];
2396                                                         evaluate_object_as_nearest_objnum(&eno);
2397                                                 }
2398                                         }
2399                                 }
2400                         }
2401                 }
2402         }
2403
2404         //      If only looking for target in certain wing and couldn't find anything in
2405         //      that wing, look for any object.
2406         if ((eno.nearest_objnum == -1) && (enemy_wing != -1)) {
2407                 return get_nearest_objnum(objnum, enemy_team_mask, -1, range, max_attackers);
2408         }
2409
2410         return eno.nearest_objnum;
2411 }
2412
2413 //      Given an object and an enemy team, return the index of the nearest enemy object.
2414 //      Unlike find_enemy or find_nearest_objnum, this doesn't care about things like the protected flag or number
2415 //      of enemies attacking.
2416 //      It is used to find the nearest enemy to determine things like whether to rearm.
2417 int find_nearby_hostile(int objnum, int enemy_team_mask, float range, int *count)
2418 {
2419         int             nearest_objnum;
2420         float           nearest_dist;
2421         object  *objp;
2422         ai_info *aip;
2423         ship_obj        *so;
2424
2425         nearest_objnum = -1;
2426         nearest_dist = range;
2427
2428         aip = &Ai_info[Ships[Objects[objnum].instance].ai_index];
2429
2430         *count = 0;
2431
2432         for ( so = GET_FIRST(&Ship_obj_list); so != END_OF_LIST(&Ship_obj_list); so = GET_NEXT(so) ) {
2433                 objp = &Objects[so->objnum];
2434
2435                 if ( OBJ_INDEX(objp) != objnum ) {
2436                         if (Ships[objp->instance].flags & SF_DYING)
2437                                 continue;
2438
2439                         if (Ship_info[Ships[objp->instance].ship_info_index].flags & (SIF_NO_SHIP_TYPE | SIF_NAVBUOY))
2440                                 continue;
2441
2442                         if (Ships[objp->instance].team & enemy_team_mask) {
2443                                 float   dist;
2444
2445                                 dist = vm_vec_dist_quick(&Objects[objnum].pos, &objp->pos) - objp->radius*0.75f;
2446                                 
2447                                 if (dist < range) {
2448                                         (*count)++;
2449
2450                                         if (dist < nearest_dist) {
2451                                                 nearest_dist = dist;
2452                                                 nearest_objnum = objp-Objects;
2453                                         }
2454                                 }
2455                         }
2456                 }
2457         }
2458
2459         return nearest_objnum;
2460 }
2461
2462 // return !0 if objp can be considered for a turret target, 0 otherwise
2463 // input:       objp                            =>      object that turret is considering as an enemy
2464 //                              turret_parent   =>      object index for ship that turret sits on
2465 int valid_turret_enemy(object *objp, object *turret_parent)
2466 {
2467         if ( objp == turret_parent ) {
2468                 return 0;
2469         }
2470
2471         if ( objp->type == OBJ_ASTEROID ) {
2472                 return 1;
2473         }
2474
2475         if ( (objp->type == OBJ_SHIP) ) {
2476                 ship *shipp;
2477                 shipp = &Ships[objp->instance];
2478
2479                 // don't fire at ships with protected bit set!!!
2480                 if ( objp->flags & OF_PROTECTED ) {
2481                         return 0;
2482                 }
2483
2484                 if ( !(Ship_info[shipp->ship_info_index].flags & SIF_DO_COLLISION_CHECK)) {
2485                         return 0;
2486                 }
2487
2488                 if (shipp->flags & SF_ARRIVING) {
2489                         return 0;
2490                 }
2491
2492                 return 1;
2493         }
2494
2495         if ( objp->type == OBJ_WEAPON ) {
2496                 if ( Weapon_info[Weapons[objp->instance].weapon_info_index].wi_flags & WIF_BOMB ) {
2497                         if ( obj_team(turret_parent) != Weapons[objp->instance].team ) {
2498                                 return 1;
2499                         }
2500                 }
2501         }
2502
2503         return 0;
2504 }
2505
2506 // return 1 if objp is in fov of the specified turret, tp.  Otherwise return 0.
2507 //      dist = distance from turret to center point of object
2508 int object_in_turret_fov(object *objp, model_subsystem *tp, vector *tvec, vector *tpos, float dist)
2509 {
2510         vector  v2e;
2511         float           dot;
2512         vm_vec_normalized_dir(&v2e, &objp->pos, tpos);
2513         dot = vm_vec_dot(&v2e, tvec);
2514
2515         dot += objp->radius / (dist + objp->radius);
2516
2517         if ( dot >= tp->turret_fov ) {
2518                 return 1;
2519         }
2520
2521         return 0;
2522 }
2523
2524 // return 1 if bomb_objp is headed towards ship_objp
2525 int bomb_headed_towards_ship(object *bomb_objp, object *ship_objp)
2526 {
2527         float           dot;
2528         vector  bomb_to_ship_vector;
2529
2530         vm_vec_normalized_dir(&bomb_to_ship_vector, &ship_objp->pos, &bomb_objp->pos);
2531         dot = vm_vec_dot(&bomb_objp->orient.fvec, &bomb_to_ship_vector);
2532
2533         if ( dot > 0 ) {
2534                 return 1;
2535         }
2536
2537         return 0;
2538 }
2539
2540 // nubmer of live turrets with target_objnum 
2541 int num_turrets_attacking(object *turret_parent, int target_objnum) 
2542 {
2543         ship_subsys *ss;
2544         ship *shipp;
2545         int count = 0;
2546         shipp = &Ships[turret_parent->instance];
2547
2548         Assert(turret_parent->type == OBJ_SHIP);
2549         Assert(Objects[target_objnum].type == OBJ_SHIP);
2550
2551         for (ss=GET_FIRST(&shipp->subsys_list); ss!=END_OF_LIST(&shipp->subsys_list); ss=GET_NEXT(ss)) {
2552                 // check if subsys is alive
2553                 if (ss->current_hits <= 0.0f) {
2554                         continue;
2555                 }
2556
2557                 // check if it's a turret
2558                 if (ss->system_info->type != SUBSYSTEM_TURRET) {
2559                         continue;
2560                 }
2561
2562                 // if the turret is locked
2563                 if(ss->weapons.flags & SW_FLAG_TURRET_LOCK){
2564                         continue;
2565                 }               
2566
2567                 // check if turret is targeting target_objnum
2568                 if (ss->turret_enemy_objnum == target_objnum) {
2569                         count++;
2570                 }
2571         }
2572
2573         return count;
2574 }
2575
2576 float Lethality_range_const = 2.0f;
2577 DCF(lethality_range, "N for modifying range: 1 / (1+N) at 100")
2578 {
2579         dc_get_arg(ARG_FLOAT);
2580         Lethality_range_const = Dc_arg_float;
2581 }
2582
2583 float Player_lethality_bump[NUM_SKILL_LEVELS] = {
2584         // 0.0f, 5.0f, 10.0f, 25.0f, 40.0f
2585         0.0f, 0.0f, 0.0f, 0.0f, 0.0f
2586 };
2587
2588 // evaluate obj as posssible target for turret
2589 void evaluate_obj_as_target(object *objp, eval_enemy_obj_struct *eeo)
2590 {
2591         object  *turret_parent_obj = &Objects[eeo->turret_parent_objnum];
2592         ship            *shipp;
2593         model_subsystem *tp = eeo->turret_subsys->system_info;
2594         float dist;
2595
2596         // Don't look for bombs when weapon system is not ok
2597         if (objp->type == OBJ_WEAPON && !eeo->weapon_system_ok) {
2598                 return;
2599         }
2600
2601         if ( !valid_turret_enemy(objp, turret_parent_obj) ) {
2602                 return;
2603         }
2604
2605 #ifndef NDEBUG
2606         if (!Player_attacking_enabled && (objp == Player_obj)) {
2607                 return;
2608         }
2609 #endif
2610
2611         if ( objp->type == OBJ_SHIP ) {
2612                 shipp = &Ships[objp->instance];
2613
2614                 // check on enemy team
2615                 if ( !(shipp->team & eeo->enemy_team_mask) ) {
2616                         return;
2617                 }
2618
2619                 // check if protected
2620                 if (objp->flags & OF_PROTECTED) {
2621                         return;
2622                 }
2623
2624                 // check if beam protected
2625                 if (Weapon_info[tp->turret_weapon_type].wi_flags & WIF_BEAM) {
2626                         if (objp->flags & OF_BEAM_PROTECTED) {
2627                                 return;
2628                         }
2629                 }
2630
2631                 if (eeo->big_only_flag) {
2632                         if (!(Ship_info[shipp->ship_info_index].flags & (SIF_BIG_SHIP | SIF_HUGE_SHIP))) {
2633                                 return;
2634                         }
2635                 }
2636
2637                 // check if     turret flagged to only target tagged ships
2638                 if ( (eeo->turret_subsys->weapons.flags & SW_FLAG_TAGGED_ONLY) && !ship_is_tagged(objp) ) {
2639                         return;
2640                 }
2641
2642                 // check if valid target in nebula
2643                 if ( !object_is_targetable(objp, &Ships[Objects[eeo->turret_parent_objnum].instance]) ) {
2644                         // BYPASS ocassionally for stealth
2645                         int try_anyway = FALSE;
2646                         if ( is_object_stealth_ship(objp) ) {
2647                                 float turret_stealth_find_chance = 0.5f;
2648                                 float speed_mod = -0.1f + vm_vec_mag_quick(&objp->phys_info.vel) / 70.0f;
2649                                 if (frand() > (turret_stealth_find_chance + speed_mod)) {
2650                                         try_anyway = TRUE;
2651                                 }
2652                         }
2653
2654                         if (!try_anyway) {
2655                                 return;
2656                         }
2657                 }
2658
2659         } else {
2660                 shipp = NULL;
2661         }
2662
2663         // modify dist for BIG|HUGE, getting closest point on bbox, if not inside
2664         dist = vm_vec_dist_quick(eeo->tpos, &objp->pos) - objp->radius;
2665         if (dist < 0.0f) {
2666                 dist = 0.0f;
2667         }
2668
2669         // check if object is a bomb attacking the turret parent
2670         // check if bomb is homing on the turret parent ship
2671         if (objp->type == OBJ_WEAPON) {
2672                 if ( Weapons[objp->instance].homing_object == &Objects[eeo->turret_parent_objnum] ) {
2673                         if ( dist < eeo->nearest_homing_bomb_dist ) {
2674                                 if ( (eeo->current_enemy == -1) || object_in_turret_fov(objp, tp, eeo->tvec, eeo->tpos, dist + objp->radius) ) {
2675                                         eeo->nearest_homing_bomb_dist = dist;
2676                                         eeo->nearest_homing_bomb_objnum = OBJ_INDEX(objp);
2677                                 }
2678                         }
2679                 // if not homing, check if bomb is flying towards ship
2680                 } else if ( bomb_headed_towards_ship(objp, &Objects[eeo->turret_parent_objnum]) ) {
2681                         if ( dist < eeo->nearest_bomb_dist ) {
2682                                 if ( (eeo->current_enemy == -1) || object_in_turret_fov(objp, tp, eeo->tvec, eeo->tpos, dist + objp->radius) ) {
2683                                         eeo->nearest_bomb_dist = dist;
2684                                         eeo->nearest_bomb_objnum = OBJ_INDEX(objp);
2685                                 }
2686                         }
2687                 }
2688         } // end weapon section
2689
2690         // maybe recalculate dist for big or huge ship
2691 //      if (shipp && (Ship_info[shipp->ship_info_index].flags & (SIF_BIG_SHIP | SIF_HUGE_SHIP))) {
2692 //              fvi_ray_boundingbox(min, max, start, direction, hit);
2693 //              dist = vm_vec_dist_quick(hit, tvec);
2694 //      }
2695
2696         // check for nearest attcker
2697         if ( (shipp) && (dist < eeo->weapon_travel_dist) ) {
2698                 ai_info *aip = &Ai_info[shipp->ai_index];
2699
2700                 // modify distance based on number of turrets from my ship attacking enemy (add 10% per turret)
2701                 // dist *= (num_enemies_attacking(OBJ_INDEX(objp))+2)/2;        //      prevents lots of ships from attacking same target
2702                 int num_att_turrets = num_turrets_attacking(turret_parent_obj, OBJ_INDEX(objp));
2703                 dist *= (1.0f + 0.1f*num_att_turrets);
2704
2705                 // return if we're over the cap
2706                 int max_turrets = 3 + Game_skill_level * Game_skill_level;
2707                 if (num_att_turrets > max_turrets) {
2708                         return;
2709                 }
2710
2711                 // modify distance based on lethality of objp to my ship
2712                 float active_lethality = aip->lethality;
2713                 if (objp->flags & OF_PLAYER_SHIP) {
2714                         active_lethality += Player_lethality_bump[Game_skill_level];
2715                 }
2716
2717                 dist /= (1.0f + 0.01f*Lethality_range_const*active_lethality);
2718
2719                 // Make level 2 tagged ships more likely to be targeted
2720                 if (shipp->level2_tag_left > 0.0f) {
2721                         dist *= 0.3f;
2722                 }
2723
2724                 // check if objp is targeting the turret's ship, or if objp has just hit the turret's ship
2725                 if ( aip->target_objnum == eeo->turret_parent_objnum || aip->last_objsig_hit == Objects[eeo->turret_parent_objnum].signature ) {
2726                         // A turret will always target a ship that is attacking itself... self-preservation!
2727                         if ( aip->targeted_subsys == eeo->turret_subsys ) {
2728                                 dist *= 0.5f;   // highest priority
2729                         }
2730                 }
2731
2732                 // maybe update nearest attacker
2733                 if ( dist < eeo->nearest_attacker_dist ) {
2734                         if ( (eeo->current_enemy == -1) || object_in_turret_fov(objp, tp, eeo->tvec, eeo->tpos, dist + objp->radius) ) {
2735                                 // 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));
2736                                 eeo->nearest_attacker_dist = dist;
2737                                 eeo->nearest_attacker_objnum = OBJ_INDEX(objp);
2738                         }
2739                 }
2740         } // end ship section
2741 }
2742
2743 // return 0 only if objnum is beam protected and turret is beam turret
2744 int is_target_beam_valid(ship_subsys *turret_subsys, int objnum)
2745 {
2746         // check if turret has beam weapon
2747         model_subsystem *tp = turret_subsys->system_info;
2748
2749         if (Weapon_info[tp->turret_weapon_type].wi_flags & WIF_BEAM) {
2750                 if (Objects[objnum].flags & OF_BEAM_PROTECTED) {
2751                         return 0;
2752                 }
2753
2754                 if (Weapon_info[tp->turret_weapon_type].wi_flags & WIF_HUGE) {
2755                         if (Objects[objnum].type == OBJ_SHIP && !(Ship_info[Ships[Objects[objnum].instance].ship_info_index].flags & (SIF_BIG_SHIP|SIF_HUGE_SHIP)) ) {
2756                                 return 0;
2757                         }
2758                 }
2759         }
2760
2761         return 1;
2762 }
2763
2764
2765 //      Given an object and an enemy team, return the index of the nearest enemy object.
2766 //
2767 // input:
2768 //                              turret_parent_objnum    => parent objnum for the turret
2769 //                              turret_subsys                   => pointer to system_info for the turret subsystem
2770 //                              enemy_team_mask         => OR'ed TEAM_ flags for the enemy of the turret parent ship
2771 //                              tpos                                            => position of turret (world coords)
2772 //                              tvec                                            => forward vector of turret (world coords)
2773 //                              current_enemy                   =>      objnum of current turret target
2774 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)
2775 {
2776         float                                   weapon_travel_dist;
2777         int                                     weapon_system_ok;
2778         object                          *objp;
2779         model_subsystem *tp;
2780         eval_enemy_obj_struct eeo;
2781
2782         // list of stuff to go thru
2783         ship_obj                *so;
2784         missile_obj *mo;
2785
2786         tp = turret_subsys->system_info;
2787         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);
2788
2789         // Set flag based on strength of weapons subsystem.  If weapons subsystem is destroyed, don't let turrets fire at bombs
2790         weapon_system_ok = 0;
2791         if ( ship_get_subsystem_strength( &Ships[Objects[turret_parent_objnum].instance], SUBSYSTEM_WEAPONS ) > 0 ) {
2792                 weapon_system_ok = 1;
2793         }
2794
2795         // Initialize eeo struct.
2796         eeo.turret_parent_objnum = turret_parent_objnum;
2797         eeo.weapon_system_ok = weapon_system_ok;
2798         eeo.weapon_travel_dist = weapon_travel_dist;
2799         eeo.big_only_flag = big_only_flag;
2800         eeo.enemy_team_mask = enemy_team_mask;
2801         eeo.current_enemy = current_enemy;
2802         eeo.tpos = tpos;
2803         eeo.tvec = tvec;
2804         eeo.turret_subsys = turret_subsys;
2805
2806         eeo.nearest_attacker_dist = 99999.0f;
2807         eeo.nearest_attacker_objnum = -1;
2808
2809         eeo.nearest_homing_bomb_dist = 99999.0f;
2810         eeo.nearest_homing_bomb_objnum = -1;
2811
2812         eeo.nearest_bomb_dist = 99999.0f;
2813         eeo.nearest_bomb_objnum = -1;
2814
2815         eeo.nearest_dist = 99999.0f;
2816         eeo.nearest_objnum = -1;
2817
2818
2819         // Missile_obj_list
2820         for( mo = GET_FIRST(&Missile_obj_list); mo != END_OF_LIST(&Missile_obj_list); mo = GET_NEXT(mo) ) {
2821                 objp = &Objects[mo->objnum];
2822                 evaluate_obj_as_target(objp, &eeo);
2823         }
2824         // highest priority
2825         if ( eeo.nearest_homing_bomb_objnum != -1 ) {                                   // highest priority is an incoming homing bomb
2826                 return eeo.nearest_homing_bomb_objnum;
2827         } else if ( eeo.nearest_bomb_objnum != -1 ) {                                   // next highest priority is an incoming dumbfire bomb
2828                 return eeo.nearest_bomb_objnum;
2829         }
2830
2831
2832         // Ship_used_list
2833         for ( so = GET_FIRST(&Ship_obj_list); so != END_OF_LIST(&Ship_obj_list); so = GET_NEXT(so) ) {
2834                 objp = &Objects[so->objnum];
2835                 evaluate_obj_as_target(objp, &eeo);
2836         }
2837
2838         Assert(eeo.nearest_attacker_objnum < 0 || is_target_beam_valid(turret_subsys, eeo.nearest_attacker_objnum));
2839                 // next highest priority is attacking ship
2840         if ( eeo.nearest_attacker_objnum != -1 ) {                      // next highest priority is an attacking ship
2841                 return eeo.nearest_attacker_objnum;
2842          }
2843
2844
2845 #ifndef FS2_DEMO
2846                 asteroid_obj *ao;
2847         // Asteroid_obj_list
2848         for( ao = GET_FIRST(&Asteroid_obj_list); ao != END_OF_LIST(&Asteroid_obj_list); ao = GET_NEXT(ao) ) {
2849                 objp = &Objects[ao->objnum];
2850                 evaluate_obj_as_target(objp, &eeo);
2851         }
2852 #endif
2853
2854         return eeo.nearest_objnum;                                                                              // lowest priority is the closest enemy objnum
2855 }
2856
2857 //      Return timestamp until a ship can find an enemy.
2858 //      Yes, no parameters.  Based solely on skill level.
2859 int get_enemy_timestamp()
2860 {
2861         return (NUM_SKILL_LEVELS - Game_skill_level) * ( (myrand() % 500) + 500);
2862 }
2863
2864 // -------------------------------------------------------------------
2865 //      Return objnum if enemy found, else return -1;
2866 //      Don't attack a ship that already has at least max_attackers attacking it.
2867 int find_enemy(int objnum, float range, int max_attackers)
2868 {
2869         int     enemy_team_mask;
2870
2871         enemy_team_mask = get_enemy_team_mask(objnum);
2872
2873         //      if target_objnum != -1, use that as goal.
2874         ai_info *aip = &Ai_info[Ships[Objects[objnum].instance].ai_index];
2875         if (timestamp_elapsed(aip->choose_enemy_timestamp)) {
2876                 aip->choose_enemy_timestamp = timestamp(get_enemy_timestamp());
2877                 if (aip->target_objnum != -1) {
2878                         int     target_objnum = aip->target_objnum;
2879
2880                         // DKA don't undo object as target in nebula missions.
2881                         // This could cause attack on ship on fringe on nebula to stop if attackee moves our of nebula range.  (BAD)
2882                         if ( (Objects[target_objnum].signature == aip->target_signature) ) {
2883                                 if (Ships[Objects[target_objnum].instance].team & enemy_team_mask) {
2884                                         if (!(Objects[target_objnum].flags & OF_PROTECTED)) {
2885                                                 // nprintf(("AI", "Frame %i: Object %i resuming goal of object %i\n", AI_FrameCount, objnum, target_objnum));
2886                                                 return target_objnum;
2887                                         }
2888                                 }
2889                         } else {
2890                                 aip->target_objnum = -1;
2891                                 aip->target_signature = -1;
2892                         }
2893                 }
2894                 return get_nearest_objnum(objnum, enemy_team_mask, aip->enemy_wing, range, max_attackers);
2895         } else {
2896                 aip->target_objnum = -1;
2897                 aip->target_signature = -1;
2898                 return -1;
2899         }
2900
2901 }
2902
2903 int Use_parent_target = 0;
2904 DCF_BOOL(use_parent_target, Use_parent_target)
2905
2906 // -------------------------------------------------------------------
2907 //      Return objnum if enemy found, else return -1;
2908 //
2909 // input:
2910 //                              turret_subsys   => pointer to turret subsystem
2911 //                              objnum                  => parent objnum for the turret
2912 //                              tpos                            => position of turret (world coords)
2913 //                              tvec                            => forward vector of turret (world coords)
2914 //                              current_enemy   =>      objnum of current turret target
2915 int find_turret_enemy(ship_subsys *turret_subsys, int objnum, vector *tpos, vector *tvec, int current_enemy, float fov, int big_only_flag = 0)
2916 {
2917         int                                     enemy_team_mask, enemy_objnum;
2918         model_subsystem *tp;
2919         ship_info                       *sip;
2920
2921         tp = turret_subsys->system_info;
2922         enemy_team_mask = get_enemy_team_mask(objnum);
2923
2924         //      If a small ship and target_objnum != -1, use that as goal.
2925         ai_info *aip = &Ai_info[Ships[Objects[objnum].instance].ai_index];
2926         sip = &Ship_info[Ships[Objects[objnum].instance].ship_info_index];
2927
2928         if ((sip->flags & SIF_SMALL_SHIP) && (aip->target_objnum != -1)) {
2929                 int target_objnum = aip->target_objnum;
2930
2931                 if (Objects[target_objnum].signature == aip->target_signature) {
2932                         if (Ships[Objects[target_objnum].instance].team & enemy_team_mask) {
2933                                 if ( !(Objects[target_objnum].flags & OF_PROTECTED) ) {         // check this flag as well.
2934                                         // nprintf(("AI", "Frame %i: Object %i resuming goal of object %i\n", AI_FrameCount, objnum, target_objnum));
2935                                         return target_objnum;
2936                                 }
2937                         }
2938                 } else {
2939                         aip->target_objnum = -1;
2940                         aip->target_signature = -1;
2941                 }
2942         // Not small or small with target objnum
2943         } else {
2944                 // maybe use aip->target_objnum as next target
2945                 if ((frand() < 0.8f) && (aip->target_objnum != -1) && Use_parent_target) {
2946
2947                         //check if aip->target_objnum is valid target
2948                         int target_flags = Objects[aip->target_objnum].flags;
2949                         if ( target_flags & OF_PROTECTED ) {
2950                                 // AL 2-27-98: why is a protected ship being targeted?
2951                                 set_target_objnum(aip, -1);
2952                                 return -1;
2953                         }
2954
2955                         // maybe use ship target_objnum if valid for turret
2956                         // check for beam weapon and beam protected
2957                         if ( !((target_flags & OF_BEAM_PROTECTED) && (Weapon_info[tp->turret_weapon_type].wi_flags & WIF_BEAM)) ) {
2958                                 if ( Objects[aip->target_objnum].type == OBJ_SHIP ) {
2959                                         // check for huge weapon and huge ship
2960                                         if ( !big_only_flag || (Ship_info[Ships[Objects[aip->target_objnum].instance].ship_info_index].flags & (SIF_BIG_SHIP|SIF_HUGE_SHIP)) ) {
2961                                                 // check for tagged only and tagged ship
2962                                                 if ( (turret_subsys->weapons.flags & SW_FLAG_TAGGED_ONLY) && ship_is_tagged(&Objects[aip->target_objnum]) ) {
2963                                                         // select new target if aip->target_objnum is out of field of view
2964                                                         vector v2e;
2965                                                         float dot, dist;
2966                                                         dist = vm_vec_normalized_dir(&v2e, &Objects[aip->target_objnum].pos, tpos);
2967                                                         dot = vm_vec_dot(&v2e, tvec);
2968                                                         //      MODIFY FOR ATTACKING BIG SHIP
2969                                                         // dot += (0.5f * Objects[aip->target_objnum].radius / dist);
2970                                                         if (dot > fov) {
2971                                                                 return aip->target_objnum;
2972                                                         }
2973                                                 }
2974                                         }
2975                                 }
2976                         }
2977                 }
2978         }
2979
2980         enemy_objnum = get_nearest_turret_objnum(objnum, turret_subsys, enemy_team_mask, tpos, tvec, current_enemy, big_only_flag);
2981         if ( enemy_objnum >= 0 ) {
2982                 Assert( !((Objects[enemy_objnum].flags & OF_BEAM_PROTECTED) && (Weapon_info[tp->turret_weapon_type].wi_flags & WIF_BEAM)) );
2983                 if ( Objects[enemy_objnum].flags & OF_PROTECTED ) {
2984                         Int3();
2985                         enemy_objnum = aip->target_objnum;
2986                 }
2987         }
2988
2989         return enemy_objnum;
2990 }
2991
2992 //      If issued an order to a ship that's awaiting repair, abort that process.
2993 //      However, do not abort process for an object that is currently being repaired -- let it finish.
2994 void ai_set_goal_maybe_abort_dock(object *objp, ai_info *aip)
2995 {
2996         if (aip->ai_flags & AIF_AWAITING_REPAIR) {
2997                 object  *repair_obj;
2998
2999                 if (aip->dock_objnum == -1) {
3000                         repair_obj = NULL;
3001                 } else {
3002                         repair_obj = &Objects[aip->dock_objnum];
3003                 }
3004                 ai_do_objects_repairing_stuff( objp, repair_obj, REPAIR_INFO_ABORT );
3005         }
3006         aip->next_rearm_request_timestamp = timestamp(NEXT_REARM_TIMESTAMP);    //      Might request again after 30 seconds.
3007 }
3008
3009 void force_avoid_player_check(object *objp, ai_info *aip)
3010 {
3011         if (Ships[objp->instance].team == Player_ship->team){
3012                 aip->avoid_check_timestamp = timestamp(0);              //      Force a check for collision next frame.
3013         }
3014 }
3015
3016 //      --------------------------------------------------------------------------
3017 //      Set *attacked as object to attack for object *attacker
3018 //      If attacked == NULL, then attack any enemy object.
3019 //      Attack point *rel_pos on object.  This is for supporting attacking subsystems.
3020 void ai_attack_object(object *attacker, object *attacked, int priority, ship_subsys *ssp)
3021 {
3022         ai_info *aip;
3023
3024         Assert(attacker != NULL);
3025         Assert(attacker->instance != -1);
3026         Assert(Ships[attacker->instance].ai_index != -1);
3027
3028         aip = &Ai_info[Ships[attacker->instance].ai_index];
3029         force_avoid_player_check(attacker, aip);
3030
3031         aip->ok_to_target_timestamp = timestamp(0);             //      Guarantee we can target.
3032
3033 //      if (!strnicmp(Ships[attacker->instance].ship_name, NOX("Kami"), 4)) {
3034 //              aip->ai_flags |= AIF_KAMIKAZE;
3035 //              aip->ai_flags |= AIF_NO_DYNAMIC;
3036 //      }
3037
3038         if (attacker == attacked) {
3039                 Int3();         //      Bogus!  Who tried to get me to attack myself!  Trace out and fix!
3040                 return;
3041         }
3042
3043         //      Only set to chase if a fighter or bomber, otherwise just return.
3044         if (!(Ship_info[Ships[attacker->instance].ship_info_index].flags & SIF_SMALL_SHIP)) {
3045 //              nprintf(("AI","Note: AI ship %s refusing to set AI mode to AIM_CHASE\n", Ships[attacker->instance].ship_name));
3046 //              return;
3047                 nprintf(("AI", "AI ship %s is large ship ordered to attack %s\n", Ships[attacker->instance].ship_name, Ships[attacked->instance].ship_name));
3048         }
3049
3050         //      This is how "engage enemy" gets processed
3051         if (attacked == NULL) {
3052                 aip->choose_enemy_timestamp = timestamp(0);
3053                 // nebula safe
3054                 set_target_objnum(aip, find_enemy(attacker-Objects, 99999.9f, 4));
3055         } else {
3056                 // check if we can see atacked in nebula
3057                 if (aip->target_objnum != attacked - Objects) {
3058                         aip->aspect_locked_time = 0.0f;
3059                 }
3060                 set_target_objnum(aip, attacked - Objects);
3061         }
3062
3063         ai_set_goal_maybe_abort_dock(attacker, aip);
3064         aip->ok_to_target_timestamp = timestamp(DELAY_TARGET_TIME);     //      No dynamic targeting for 7 seconds.
3065
3066         if (is_ignore_object(aip, aip->target_objnum)) {
3067                 aip->ignore_objnum = UNUSED_OBJNUM;
3068         }
3069
3070         aip->mode = AIM_CHASE;
3071         aip->submode = SM_ATTACK;       // AL 12-15-97: need to set submode?  I got an assert() where submode was bogus
3072                                                                                 //                                       for AIM_CHASE... it may have been not set correctly here
3073         if (ssp == NULL) {
3074                 set_targeted_subsys(aip, NULL, -1);
3075                 if (aip->target_objnum != -1) {
3076                         //nprintf(("AI", "Unprotecting ship %s\n", Ships[Objects[aip->target_objnum].instance].ship_name));
3077                         Objects[aip->target_objnum].flags &= ~OF_PROTECTED;     //      If ship had been protected, unprotect it.
3078                 }
3079         } else {
3080                 Int3(); //      Not supported yet!
3081         }
3082 }
3083
3084 //      --------------------------------------------------------------------------
3085 //      Set *attacked as object to attack for object *attacker
3086 //      Attack point *rel_pos on object.  This is for supporting attacking subsystems.
3087 void ai_attack_wing(object *attacker, int wingnum, int priority)
3088 {
3089         ai_info *aip;
3090
3091         Assert(attacker != NULL);
3092         Assert(attacker->instance != -1);
3093         Assert(Ships[attacker->instance].ai_index != -1);
3094
3095         aip = &Ai_info[Ships[attacker->instance].ai_index];
3096
3097         aip->enemy_wing = wingnum;
3098         aip->mode = AIM_CHASE;
3099         aip->submode = SM_ATTACK;       // AL 12-15-97: need to set submode?  I got an assert() where submode was bogus
3100                                                                                 //                                       for AIM_CHASE... it may have been not set correctly here
3101
3102         aip->ok_to_target_timestamp = timestamp(0);             //      Guarantee we can target.
3103
3104         int count = Wings[wingnum].current_count;
3105         if (count > 0) {
3106                 int     index;
3107
3108                 index = (int) (frand() * count);
3109
3110                 if (index >= count)
3111                         index = 0;
3112
3113                 set_target_objnum(aip, Ships[Wings[wingnum].ship_index[index]].objnum);
3114
3115                 ai_set_goal_maybe_abort_dock(attacker, aip);
3116                 aip->ok_to_target_timestamp = timestamp(DELAY_TARGET_TIME);     //      No dynamic targeting for 7 seconds.
3117         }
3118 }
3119
3120 //      --------------------------------------------------------------------------
3121 //      Set *evaded as object for *evader to evade.
3122 void ai_evade_object(object *evader, object *evaded, int priority)
3123 {
3124         ai_info *aip;
3125
3126         Assert(evader != NULL);
3127         Assert(evaded != NULL);
3128         Assert(evader->instance != -1);
3129         Assert(Ships[evader->instance].ai_index != -1);
3130
3131         if (evaded == evader) {
3132                 Int3(); //      Bogus!  Who tried to get me to evade myself!  Trace out and fix!
3133                 return;
3134         }
3135
3136         aip = &Ai_info[Ships[evader->instance].ai_index];
3137
3138         set_target_objnum(aip, evaded - Objects);
3139         aip->mode = AIM_EVADE;
3140
3141 }
3142
3143 //      Ignore some object without changing mode.
3144 void ai_ignore_object(object *ignorer, object *ignored, int priority)
3145 {
3146         ai_info *aip;
3147
3148         Assert(ignorer != NULL);
3149         Assert(ignored != NULL);
3150         Assert(ignorer->instance != -1);
3151         Assert(Ships[ignorer->instance].ai_index != -1);
3152         Assert(ignorer != ignored);
3153
3154         aip = &Ai_info[Ships[ignorer->instance].ai_index];
3155
3156         //      MK, 5/17/98, removing ignoring of wings.
3157         //      It's too confusing.  It often causes mysterious behavior in which fighters unexpectedly refuse to attack anything.
3158 /*      if (Ships[ignored->instance].wingnum > -1) {
3159                 int wingnum, i;
3160
3161                 wingnum = Ships[ignored->instance].wingnum;
3162                 aip->ignore_objnum = -(wingnum+1);
3163                 // set protected bit for each ship in a wing
3164                 //      MK, 4/23/98: Only set for fighters if they are the original "ignored" object
3165                 for (i = 0; i < Wings[wingnum].current_count; i++ ) {
3166                         object  *objp;
3167
3168                         objp = &Objects[Ships[Wings[wingnum].ship_index[i]].objnum];
3169                         if (objp != ignored) {
3170                                 if (Ship_info[Ships[objp->instance].ship_info_index].flags & (SIF_FIGHTER | SIF_BOMBER))
3171                                         continue;
3172                         }
3173
3174                         Objects[Ships[Wings[wingnum].ship_index[i]].objnum].flags |= OF_PROTECTED;
3175                 }
3176
3177         } else {
3178         */ {
3179                 aip->ignore_objnum = ignored - Objects;
3180                 aip->ignore_signature = ignored->signature;
3181                 aip->ai_flags &= ~AIF_TEMPORARY_IGNORE;
3182                 ignored->flags |= OF_PROTECTED;                                 // set protected bit of ignored ship.
3183         }
3184
3185 }
3186
3187 //      Ignore some object without changing mode.
3188 void ai_ignore_wing(object *ignorer, int wingnum, int priority)
3189 {
3190         ai_info *aip;
3191
3192         Assert(ignorer != NULL);
3193         Assert(ignorer->instance != -1);
3194         Assert(Ships[ignorer->instance].ai_index != -1);
3195         Assert((wingnum >= 0) && (wingnum < MAX_WINGS));
3196
3197         aip = &Ai_info[Ships[ignorer->instance].ai_index];
3198
3199         aip->ignore_objnum = -(wingnum +1);
3200         aip->ai_flags &= ~AIF_TEMPORARY_IGNORE;
3201 }
3202
3203
3204 //      Add a path point in the global buffer Path_points.
3205 //      modify_index = index in Path_points at which to store path point.
3206 //      If modify_index == -1, then create a new point.
3207 //      If a new point is created (ie, modify_index == -1), then Ppfp is updated.
3208 void add_path_point(vector *pos, int path_num, int path_index, int modify_index)
3209 {
3210         pnode   *pnp;
3211
3212         if (modify_index == -1) {
3213                 Assert(Ppfp-Path_points < MAX_PATH_POINTS-1);
3214                 pnp = Ppfp;
3215                 Ppfp++;
3216         } else {
3217                 Assert((modify_index >= 0) && (modify_index < MAX_PATH_POINTS-1));
3218                 pnp = &Path_points[modify_index];
3219         }
3220
3221         pnp->pos = *pos;
3222         pnp->path_num = path_num;
3223         pnp->path_index = path_index;
3224 }
3225
3226 //      Given two points on a sphere, the center of the sphere and the radius, return a
3227 //      point on the vector through the midpoint of the chord on the sphere.
3228 void bisect_chord(vector *p0, vector *p1, vector *centerp, float radius)
3229 {
3230         vector  tvec;
3231         vector  new_pnt;
3232
3233         vm_vec_add(&tvec, p0, p1);
3234         vm_vec_sub2(&tvec, centerp);
3235         vm_vec_sub2(&tvec, centerp);
3236         if (vm_vec_mag_quick(&tvec) < 0.1f) {
3237                 vm_vec_sub(&tvec, p0, p1);
3238                 if (fl_abs(tvec.x) <= fl_abs(tvec.z)){
3239                         tvec.x = -tvec.z;
3240                 } else {
3241                         tvec.y = -tvec.x;
3242                 }
3243         }
3244
3245         vm_vec_normalize(&tvec);
3246         vm_vec_scale(&tvec, radius);
3247         vm_vec_add(&new_pnt, centerp, &tvec);
3248
3249         add_path_point(&new_pnt, -1, -1, -1);
3250 }
3251                         
3252 //      Create a path from the current position to a goal position.
3253 //      The current position is in the current object and the goal position is
3254 //      in the goal object.
3255 //      It is ok to intersect the current object, but not the goal object.
3256 //      This function is useful for creating a path to an initial point near a large
3257 //      object.
3258 //
3259 // input:       subsys_path:    optional param (default 0), indicates this is a path to a subsystem
3260 void create_path_to_point(vector *curpos, vector *goalpos, object *curobjp, object *goalobjp, int subsys_path)
3261 {
3262         //      If can't cast vector to goalpos, then create an intermediate point.
3263         if (pp_collide(curpos, goalpos, goalobjp, curobjp->radius)) {
3264                 vector  tan1;
3265                 float           radius;
3266
3267                 // If this is a path to a subsystem, use SUBSYS_PATH_DIST as the radius for the object you are
3268                 // trying to avoid.  This is needed since subsystem paths extend out to SUBSYS_PATH_DIST, and we
3269                 // want ships to reach their path destination without flying to points that sit on the radius of
3270                 // a small ship
3271                 radius = goalobjp->radius;
3272                 if (subsys_path) {
3273                         if ( SUBSYS_PATH_DIST > goalobjp->radius ) {
3274                                 radius = SUBSYS_PATH_DIST;
3275                         }
3276                 }
3277
3278                 //      The intermediate point is at the intersection of:
3279                 //              tangent to *goalobjp sphere at point *goalpos
3280                 //              tangent to *goalobjp sphere through point *curpos in plane defined by *curpos, *goalpos, goalobjp->pos
3281                 //      Note, there are two tangents through *curpos, unless *curpos is on the
3282                 //      sphere.  The tangent that causes the nearer intersection (to *goalpos) is chosen.
3283                 get_tangent_point(&tan1, curpos, &goalobjp->pos, goalpos, radius);
3284
3285                 //      If we can't reach tan1 from curpos, insert a new point.
3286                 if (pp_collide(&tan1, curpos, goalobjp, curobjp->radius))
3287                         bisect_chord(curpos, &tan1, &goalobjp->pos, radius);
3288
3289                 add_path_point(&tan1, -1, -1, -1);
3290
3291                 //      If we can't reach goalpos from tan1, insert a new point.
3292                 if (pp_collide(goalpos, &tan1, goalobjp, curobjp->radius))
3293                         bisect_chord(goalpos, &tan1, &goalobjp->pos, radius);
3294         }
3295
3296 }
3297
3298 //      Given an object and a model path, globalize the points on the model
3299 //      and copy into the global path list.
3300 //      If pnp != NULL, then modify, in place, the path points.  This is used to create new
3301 //      globalized points when the base object has moved.
3302 // input:       randomize_pnt   => optional parameter (default value -1), add random vector in sphere to this path point
3303 void copy_xlate_model_path_points(object *objp, model_path *mp, int dir, int count, int path_num, pnode *pnp, int randomize_pnt)
3304 {
3305         matrix  m;
3306         int             i;
3307         vector  v1;
3308         int             pp_index;               //      index in Path_points at which to store point, if this is a modify-in-place (pnp ! NULL)
3309         int             start_index, finish_index;
3310         
3311         // nprintf(("AI", "Creating path for object %s in frame #%i\n", Ships[objp->instance].ship_name, AI_FrameCount));
3312         
3313         //      Initialize pp_index.
3314         //      If pnp == NULL, that means we're creating new points.  If not NULL, then modify in place.
3315         if (pnp == NULL)
3316                 pp_index = -1;                  //      This tells add_path_point to create a new point.
3317         else
3318                 pp_index = 0;                   //      pp_index will get assigned to index in Path_points to reuse.
3319
3320         vm_copy_transpose_matrix(&m, &objp->orient);
3321
3322         if (dir == 1) {
3323                 start_index = 0;
3324                 finish_index = min(count, mp->nverts);
3325         } else {
3326                 Assert(dir == -1);      //      direction must be up by 1 or down by 1 and it's neither!
3327                 start_index = mp->nverts-1;
3328                 finish_index = max(-1, mp->nverts-1-count);
3329         }
3330
3331         int offset = 0;
3332         for (i=start_index; i != finish_index; i += dir) {
3333                 //      Globalize the point.
3334                 vm_vec_rotate(&v1, &mp->verts[i].pos, &m);
3335                 vm_vec_add2(&v1, &objp->pos);
3336
3337                 if ( randomize_pnt == i ) {
3338                         vector v_rand;
3339                         static_randvec(OBJ_INDEX(objp), &v_rand);
3340                         vm_vec_scale(&v_rand, 30.0f);
3341                         vm_vec_add2(&v1, &v_rand);
3342                 }
3343
3344                 if (pp_index != -1)
3345                         pp_index = pnp-Path_points + offset;
3346
3347                 add_path_point(&v1, path_num, i, pp_index);
3348                 offset++;
3349         }
3350 }
3351
3352
3353 //      For pl_objp, create a path along path path_num into mobjp.
3354 //      The tricky part of this problem is creating the entry to the first point on the
3355 //      predefined path.  The points on this entry path are based on the location of Pl_objp
3356 //      relative to the start of the path.
3357 //
3358 // input:
3359 //                              subsys_path:    optional param (default 0), indicating this is a path to a subsystem
3360 void create_model_path(object *pl_objp, object *mobjp, int path_num, int subsys_path)
3361 {       
3362         ship                    *shipp = &Ships[pl_objp->instance];
3363         ai_info         *aip = &Ai_info[shipp->ai_index];
3364
3365         ship_info       *osip = &Ship_info[Ships[mobjp->instance].ship_info_index];
3366         polymodel       *pm = model_get(osip->modelnum);
3367         int                     num_points;
3368         model_path      *mp;
3369         pnode                   *ppfp_start = Ppfp;
3370         matrix          m;
3371         vector          gp0;
3372
3373         Assert(path_num >= 0);
3374
3375         //      Do garbage collection if necessary.
3376         if (Ppfp-Path_points + 64 > MAX_PATH_POINTS) {
3377                 garbage_collect_path_points();
3378                 ppfp_start = Ppfp;
3379         }
3380
3381         aip->path_start = Ppfp - Path_points;
3382         Assert(path_num < pm->n_paths);
3383         
3384         mp = &pm->paths[path_num];
3385         num_points = mp->nverts;
3386
3387         Assert(Ppfp-Path_points + num_points + 4 < MAX_PATH_POINTS);
3388
3389         vm_copy_transpose_matrix(&m, &mobjp->orient);
3390         vm_vec_rotate(&gp0, &mp->verts[0].pos, &m);
3391         vm_vec_add2(&gp0, &mobjp->pos);
3392
3393         if (pp_collide(&pl_objp->pos, &gp0, mobjp, pl_objp->radius)) {
3394                 vector  perim_point1;
3395                 vector  perim_point2;
3396
3397                 perim_point2 = pl_objp->pos;
3398                 
3399                 //      If object that wants to dock is inside bounding sphere of object it wants to dock with, make it fly out.
3400                 //      Assume it can fly "straight" out to the bounding sphere.
3401                 if (vm_vec_dist_quick(&pl_objp->pos, &mobjp->pos) < mobjp->radius) {
3402                         project_point_to_perimeter(&perim_point2, &mobjp->pos, mobjp->radius, &pl_objp->pos);
3403                         add_path_point(&perim_point2, path_num, -1, -1);
3404                 }
3405
3406                 //      If last point on pre-defined path is inside bounding sphere, create a new point on the surface of the sphere.
3407                 if (vm_vec_dist_quick(&mobjp->pos, &gp0) < mobjp->radius) {
3408                         project_point_to_perimeter(&perim_point1, &mobjp->pos, mobjp->radius, &gp0);
3409                         create_path_to_point(&perim_point2, &perim_point1, pl_objp, mobjp, subsys_path);
3410                         add_path_point(&perim_point1, path_num, -1, -1);
3411                 } else {                //      The predefined path extends outside the sphere.  Create path to that point.
3412                         create_path_to_point(&perim_point2, &gp0, pl_objp, mobjp, subsys_path);
3413                 }
3414         }
3415
3416         // AL 12-31-97: If following a subsystem path, add random vector to second last path point
3417         if ( subsys_path ) {
3418                 copy_xlate_model_path_points(mobjp, mp, 1, mp->nverts, path_num, NULL, mp->nverts-2);
3419         } else {
3420                 copy_xlate_model_path_points(mobjp, mp, 1, mp->nverts, path_num, NULL);
3421         }
3422
3423         aip->path_cur = aip->path_start;
3424         aip->path_dir = PD_FORWARD;
3425         aip->path_objnum = mobjp-Objects;
3426         aip->mp_index = path_num;
3427         aip->path_length = Ppfp - ppfp_start;
3428         aip->path_next_check_time = timestamp(1);
3429
3430         aip->path_goal_obj_hash = create_object_hash(&Objects[aip->path_objnum]);
3431
3432         aip->path_next_create_time = timestamp(1000);   //      OK to try to create one second later
3433         aip->path_create_pos = pl_objp->pos;
3434         aip->path_create_orient = pl_objp->orient;
3435
3436         aip->ai_flags &= ~AIF_USE_EXIT_PATH;                    // ensure this flag is cleared
3437 }
3438
3439 //      For pl_objp, create a path along path path_num into mobjp.
3440 //      The tricky part of this problem is creating the entry to the first point on the
3441 //      predefined path.  The points on this entry path are based on the location of pl_objp
3442 //      relative to the start of the path.
3443 void create_model_exit_path(object *pl_objp, object *mobjp, int path_num, int count)
3444 {       
3445         ship                    *shipp = &Ships[pl_objp->instance];
3446         ai_info         *aip = &Ai_info[shipp->ai_index];
3447
3448         ship_info       *osip = &Ship_info[Ships[mobjp->instance].ship_info_index];
3449         polymodel       *pm = model_get(osip->modelnum);
3450         int                     num_points;
3451         model_path      *mp;
3452         pnode                   *ppfp_start = Ppfp;
3453
3454         aip->path_start = Ppfp - Path_points;
3455         Assert(path_num < pm->n_paths);
3456         
3457         mp = &pm->paths[path_num];
3458         num_points = mp->nverts;
3459
3460         Assert(Ppfp-Path_points + num_points + 4 < MAX_PATH_POINTS);
3461
3462         copy_xlate_model_path_points(mobjp, mp, -1, count, path_num, NULL);
3463
3464         aip->path_cur = aip->path_start;
3465         aip->path_dir = PD_FORWARD;
3466         aip->path_objnum = mobjp-Objects;
3467         aip->mp_index = path_num;
3468         aip->path_length = Ppfp - ppfp_start;
3469         aip->path_next_check_time = timestamp(1);
3470
3471         aip->ai_flags |= AIF_USE_EXIT_PATH;             // mark as exit path, referenced in maybe
3472 }
3473
3474 //      Return true if the vector from curpos to goalpos intersects with any ship other than the ignore objects.
3475 //      Calls pp_collide
3476 int pp_collide_any(vector *curpos, vector *goalpos, float radius, object *ignore_objp1, object *ignore_objp2, int big_only_flag)
3477 {
3478         ship_obj        *so;    
3479
3480         for ( so = GET_FIRST(&Ship_obj_list); so != END_OF_LIST(&Ship_obj_list); so = GET_NEXT(so) ) {
3481                 object *objp = &Objects[so->objnum];
3482
3483                 if (big_only_flag) {
3484                         if (!(Ship_info[Ships[objp->instance].ship_info_index].flags & (SIF_BIG_SHIP | SIF_HUGE_SHIP)))
3485                                 continue;
3486                 }
3487
3488                 if ((objp != ignore_objp1) && (objp != ignore_objp2)) {
3489                         if (pp_collide(curpos, goalpos, objp, radius))
3490                                 return OBJ_INDEX(objp);
3491                 }
3492         }
3493
3494         return -1;
3495 }
3496
3497 //      Used to create docking paths and other pre-defined paths through ships.
3498 //      Creates a path in absolute space.
3499 //      Create a path into the object objnum.
3500 //
3501 // input:
3502 //      pl_objp:                        object that will use the path
3503 //      objnum:                 Object to find path to.
3504 //      path_num:               model path index to use
3505 //      exit_flag:              true means this is an exit path in the model
3506 // subsys_path: optional param (default 0) that indicates this is a path to a subsystem
3507 //      Exit:
3508 //      ai_info struct in Pl_objp gets stuffed with information to enable Pl_objp to fly the path.
3509 void ai_find_path(object *pl_objp, int objnum, int path_num, int exit_flag, int subsys_path)
3510 {
3511         ai_info *aip = &Ai_info[Ships[pl_objp->instance].ai_index];
3512
3513         Assert(path_num >= 0);
3514
3515         //      This is test code, find an object with paths.
3516         if (objnum != -1) {
3517                 object  *objp = &Objects[objnum];
3518
3519                 if (objp->type == OBJ_SHIP) {
3520                         polymodel *pm;
3521
3522                         ship    *shipp = &Ships[objp->instance];
3523                         pm = model_get( shipp->modelnum );
3524                         Assert(pm->n_paths > path_num);
3525                         aip->goal_objnum = objp-Objects;
3526                         aip->goal_signature = objp->signature;
3527                         if (exit_flag)
3528                                 create_model_exit_path(pl_objp, objp, path_num);
3529                         else
3530                                 create_model_path(pl_objp, objp, path_num, subsys_path);
3531                         return;
3532                 }
3533
3534         }
3535 }
3536
3537 extern int vector_object_collision(vector *start_pos, vector *end_pos, object *objp, float radius_scale);
3538
3539 //      Maybe make *objp avoid a player object.
3540 //      For now, 4/6/98, only check Player_obj.
3541 //      If player collision would occur, set AIF_AVOIDING_SMALL_SHIP bit in ai_flags.
3542 //      Set aip->avoid_goal_point
3543 int maybe_avoid_player(object *objp, vector *goal_pos)
3544 {
3545         ai_info *aip;
3546         vector  cur_pos, new_goal_pos;
3547         object  *player_objp;
3548         vector  n_vec_to_goal, n_vec_to_player;
3549
3550         aip = &Ai_info[Ships[objp->instance].ai_index];
3551
3552         if (!timestamp_elapsed(aip->avoid_check_timestamp))
3553                 return 0;
3554
3555         player_objp = Player_obj;
3556
3557         float   speed_time;
3558
3559         //      How far two ships could be apart and still collide within one second.
3560         speed_time = player_objp->phys_info.speed + objp->phys_info.speed;
3561
3562         float   obj_obj_dist;
3563
3564         obj_obj_dist = vm_vec_dist_quick(&player_objp->pos, &objp->pos);
3565
3566         if (obj_obj_dist > speed_time*2.0f)
3567                 return 0;
3568
3569         cur_pos = objp->pos;
3570
3571         new_goal_pos = *goal_pos;
3572
3573         float dist = vm_vec_normalized_dir(&n_vec_to_goal, goal_pos, &objp->pos);
3574         vm_vec_normalized_dir(&n_vec_to_player, &player_objp->pos, &objp->pos);
3575
3576         if (dist > speed_time*2.0f) {
3577                 vm_vec_scale_add(&new_goal_pos, &objp->pos, &n_vec_to_goal, 200.0f);
3578         }
3579
3580         if (vector_object_collision(&objp->pos, &new_goal_pos, player_objp, 1.5f)) {
3581                 aip->ai_flags |= AIF_AVOIDING_SMALL_SHIP;
3582
3583                 vector  avoid_vec;
3584
3585                 vm_vec_sub(&avoid_vec, &n_vec_to_goal, &n_vec_to_player);
3586                 if (vm_vec_mag_quick(&avoid_vec) < 0.01f) {
3587                         vm_vec_copy_scale(&avoid_vec, &objp->orient.rvec, frand()-0.5f);
3588                         vm_vec_scale_add2(&avoid_vec, &objp->orient.uvec, frand()-0.5f);
3589                         vm_vec_normalize(&avoid_vec);
3590                 } else {
3591                         vector  tvec1;
3592                         vm_vec_normalize(&avoid_vec);
3593                         vm_vec_crossprod(&tvec1, &n_vec_to_goal, &avoid_vec);
3594                         vm_vec_crossprod(&avoid_vec, &tvec1, &n_vec_to_player);
3595                 }
3596
3597                 //      Now, avoid_vec is a vector perpendicular to the vector to the player and the direction *objp
3598                 //      should fly in to avoid the player while still approaching its goal.
3599                 vm_vec_scale_add(&aip->avoid_goal_point, &player_objp->pos, &avoid_vec, 400.0f);
3600
3601                 aip->avoid_check_timestamp = timestamp(1000);
3602
3603                 return 1;
3604         } else {
3605                 aip->ai_flags &= ~AIF_AVOIDING_SMALL_SHIP;
3606                 aip->avoid_check_timestamp = timestamp((int) (obj_obj_dist/200.0f) + 500);
3607
3608                 return 0;
3609         }
3610 }
3611
3612 //      Make object *still_objp enter AIM_STILL mode.
3613 //      Make it point at view_pos.
3614 void ai_stay_still(object *still_objp, vector *view_pos)
3615 {
3616         ship    *shipp;
3617         ai_info *aip;
3618
3619         Assert(still_objp->type == OBJ_SHIP);
3620         Assert((still_objp->instance >= 0) && (still_objp->instance < MAX_OBJECTS));
3621
3622         shipp = &Ships[still_objp->instance];
3623         Assert((shipp->ai_index >= 0) && (shipp->ai_index < MAX_AI_INFO));
3624
3625         aip = &Ai_info[shipp->ai_index];
3626
3627         aip->mode = AIM_STILL;
3628
3629         //      If view_pos not NULL, point at that point.  Else, point at a point directly in front of ship.  Ie, don't turn.
3630         if (view_pos != NULL)
3631                 aip->goal_point = *view_pos;
3632         else
3633                 vm_vec_scale_add(&aip->goal_point, &still_objp->pos, &still_objp->orient.fvec, 100.0f);
3634 }
3635
3636 // code which is called from ai_dock_with_object and ai_dock to set flags and apprioriate variable
3637 // when two objects have completed docking.  used because we can dock object initially at misison load
3638 // time (meaning that ai_dock() might never get called).  docker has docked with dockee (i.e. docker
3639 // would be a freighter and dockee would be a cargo).
3640 void ai_do_objects_docked_stuff(object *docker, object *dockee)
3641 {
3642         ai_info *aip, *other_aip;
3643
3644         aip = &Ai_info[Ships[docker->instance].ai_index];
3645         other_aip = &Ai_info[Ships[dockee->instance].ai_index];
3646
3647         // set the flags and dock_objnum for both objects
3648         aip->ai_flags |= AIF_DOCKED;
3649         aip->dock_objnum = OBJ_INDEX(dockee);
3650         other_aip->ai_flags |= AIF_DOCKED;
3651         other_aip->dock_objnum = OBJ_INDEX(docker);
3652         aip->dock_signature = dockee->signature;
3653         other_aip->dock_signature = docker->signature;
3654
3655         // add multiplayer hook here to deal with docked objects.  We need to only send information
3656         // about the object that is docking.  Both flags will get updated.
3657         if ( MULTIPLAYER_MASTER )
3658                 send_ai_info_update_packet( docker, AI_UPDATE_DOCK );
3659
3660 }
3661
3662 // code which is called when objects become undocked. Equivalent of above function.
3663 // dockee might not be valid since this code can get called to cleanup after a ship
3664 // has blown up!
3665 void ai_do_objects_undocked_stuff( object *docker, object *dockee )
3666 {
3667         ai_info *aip, *other_aip;
3668
3669         // add multiplayer hook here to deal with undocked objects.  Do it before we
3670         // do anything else.  We don't need to send info for both objects, since we can find
3671         // it be dock_objnum
3672         if ( MULTIPLAYER_MASTER )
3673                 send_ai_info_update_packet( docker, AI_UPDATE_UNDOCK );
3674
3675         aip = &Ai_info[Ships[docker->instance].ai_index];
3676
3677         // set the flags and dock_objnum for both objects
3678         aip->ai_flags &= ~(AIF_DOCKED | AIF_BEING_REPAIRED);
3679         aip->dock_objnum = -1;
3680         
3681         if ( dockee != NULL ) {
3682                 other_aip = &Ai_info[Ships[dockee->instance].ai_index];
3683                 other_aip->ai_flags &= ~(AIF_DOCKED | AIF_BEING_REPAIRED);
3684                 other_aip->dock_objnum = -1;
3685         }
3686
3687 }
3688
3689
3690 //      --------------------------------------------------------------------------
3691 //      Interface from goals code to AI.
3692 //      Cause *docker to dock with *dockee.
3693 //      priority is priority of goal from goals code.
3694 //      dock_type is:
3695 //              AIDO_DOCK               set goal of docking
3696 //              AIDO_DOCK_NOW   immediately dock, used for ships that need to be docked at mission start
3697 //              AIDO_UNDOCK             set goal of undocking
3698 void ai_dock_with_object(object *docker, object *dockee, int priority, int dock_type, int docker_index, int dockee_index)
3699 {
3700         ai_info         *aip;
3701         polymodel       *pm;
3702         ai_info         *dockee_aip;
3703
3704         Assert(docker != NULL);
3705         Assert(dockee != NULL);
3706         Assert(docker->instance != -1);
3707         Assert(Ships[docker->instance].ai_index != -1);
3708         Assert(Ships[dockee->instance].ai_index != -1);
3709         Assert( docker_index != -1 );
3710         Assert( dockee_index != -1 );
3711
3712         aip = &Ai_info[Ships[docker->instance].ai_index];
3713
3714         if ((aip->ai_flags & AIF_DOCKED) && (dock_type == AIDO_DOCK)) {
3715                 object  *dockee2;
3716                 int             docker_index2, dockee_index2;
3717
3718                 Assert(aip->dock_objnum > -1);
3719                 dockee2 = &Objects[aip->dock_objnum];
3720                 docker_index2 = aip->dock_index;
3721                 dockee_index2 = aip->dockee_index;
3722                 // MWA -- 2/9/98.  use the goal code to undock the ships since goals might need to get removed
3723                 // and that code will do it properly.  I'd actually be surprised if we got into this code anymore
3724                 // since the outer layer goal code should deal with this issue....but who knows...
3725                 ai_add_goal_ship_internal( aip, AI_GOAL_UNDOCK, NULL, -1, -1, 0 );
3726
3727                 // old code below
3728                 //ai_dock_with_object(docker, dockee2, priority, AIDO_UNDOCK, docker_index2, dockee_index2);
3729                 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));
3730                 nprintf(("AI", "...so ship %s will now undock.\n", Ships[docker->instance].ship_name));
3731                 return;
3732         }
3733
3734         dockee_aip = &Ai_info[Ships[dockee->instance].ai_index];
3735
3736         aip->goal_objnum = dockee - Objects;
3737         aip->goal_signature = dockee->signature;
3738
3739         aip->mode = AIM_DOCK;
3740
3741         switch (dock_type) {
3742         case AIDO_DOCK:
3743                 aip->submode = AIS_DOCK_0;
3744                 break;
3745         case AIDO_DOCK_NOW:
3746                 aip->submode = AIS_DOCK_3A;
3747                 break;
3748         case AIDO_UNDOCK:
3749                 aip->submode = AIS_UNDOCK_0;
3750                 break;
3751         default:
3752                 Int3();         //      Bogus dock_type.
3753         }
3754
3755         aip->submode_start_time = Missiontime;
3756         aip->dock_index = docker_index;
3757         aip->dockee_index = dockee_index;
3758
3759         dockee_aip->dock_index = dockee_index;
3760         dockee_aip->dockee_index = docker_index;
3761
3762         // get the path number to the docking point on the dockee.  Each docking point contains a list
3763         // of paths that the point can be reached by.  Pick the first path in the path list for now.
3764         // We only want to do this stuff if we are docking!!!  Be sure to set the path index
3765         if ((dock_type == AIDO_DOCK) || (dock_type == AIDO_DOCK_NOW)) {
3766                 pm = model_get( Ships[dockee->instance].modelnum );
3767                 Assert( pm->docking_bays[dockee_index].num_spline_paths > 0 );
3768
3769                 // only set the dock path index if we are docking.  undocking will assume that dock_path_index
3770                 // already set from some other docking command
3771                 aip->dock_path_index = dockee_index;
3772                 dockee_aip->dock_path_index = docker_index;
3773         }
3774
3775         if (dock_type != AIDO_DOCK_NOW) {
3776                 int path_num;
3777                 //      Note: Second parameter is dock path index.  This should be specified as an
3778                 //      _input_ to this function and passed through.  The path index should be already
3779                 // set for the undock function
3780                 path_num = ai_return_path_num_from_dockbay(dockee, dockee_index);
3781                 ai_find_path(docker, dockee-Objects, path_num, 0);
3782 //              ai_find_path(dockee-Objects, dockee_index, 0);
3783         } else {
3784                 dock_orient_and_approach(docker, dockee, DOA_DOCK_STAY);
3785                 //aip->dock_objnum = OBJ_INDEX(dockee);
3786                 ai_do_objects_docked_stuff( docker, dockee );
3787         }
3788
3789 }
3790
3791 //      Cause a ship to fly its waypoints.
3792 //      flags tells:
3793 //              WPF_REPEAT      Set -> repeat waypoints.
3794 void ai_start_waypoints(object *objp, int waypoint_list_index, int wp_flags)
3795 {
3796         ai_info *aip;
3797
3798         Assert(waypoint_list_index < Num_waypoint_lists);
3799
3800         //nprintf(("AI", "Frame %i: Ship %s instructed to fly waypoint list #%i\n", AI_FrameCount, Ships[objp->instance].ship_name, waypoint_list_index));
3801         aip = &Ai_info[Ships[objp->instance].ai_index];
3802
3803         if ( (aip->mode == AIM_WAYPOINTS) && (aip->wp_index == waypoint_list_index) )
3804                 return;
3805
3806         aip->ai_flags |= AIF_FORMATION_WING;
3807         aip->ai_flags &= ~AIF_FORMATION_OBJECT;
3808         aip->wp_list = waypoint_list_index;
3809         aip->wp_index = 0;
3810         aip->wp_flags = wp_flags;
3811         aip->mode = AIM_WAYPOINTS;
3812
3813         Assert(aip->active_goal != AI_ACTIVE_GOAL_DYNAMIC);
3814 }
3815
3816 //      Make *objp stay within dist units of *other_objp
3817 void ai_do_stay_near(object *objp, object *other_objp, float dist)
3818 {
3819         ai_info *aip;
3820
3821         Assert(objp != other_objp);             //      Bogus!  Told to stay near self.
3822         Assert(objp->type == OBJ_SHIP);
3823         Assert((objp->instance >= 0) && (objp->instance < MAX_SHIPS));
3824
3825         aip = &Ai_info[Ships[objp->instance].ai_index];
3826
3827         aip->mode = AIM_STAY_NEAR;
3828         aip->submode = -1;
3829         aip->stay_near_distance = dist;
3830         aip->goal_objnum = other_objp-Objects;
3831         aip->goal_signature = other_objp->signature;
3832
3833 }
3834
3835 //      Make object *objp form on wing of object *goal_objp
3836 void ai_form_on_wing(object *objp, object *goal_objp)
3837 {
3838         ai_info *aip;
3839         ship                    *shipp;
3840         ship_info       *sip;
3841
3842         // objp == goal_objp sometimes in multiplayer when someone leaves a game -- make a simple
3843         // out for this case.
3844         if ( Game_mode & GM_MULTIPLAYER ) {
3845                 if ( objp == goal_objp ) {
3846                         return;
3847                 }
3848         }
3849
3850         Assert(objp != goal_objp);              //      Bogus!  Told to form on own's wing!
3851
3852         shipp = &Ships[objp->instance];
3853         sip = &Ship_info[shipp->ship_info_index];
3854
3855         //      Only fighters or bombers allowed to form on wing.
3856         if (!(sip->flags & (SIF_FIGHTER | SIF_BOMBER))) {
3857                 nprintf(("AI", "Warning: Ship %s tried to form on player's wing, but not fighter or bomber.\n", shipp->ship_name));
3858                 return;
3859         }
3860
3861         aip = &Ai_info[Ships[objp->instance].ai_index];
3862
3863         aip->ai_flags &= ~AIF_FORMATION_WING;
3864         aip->ai_flags |= AIF_FORMATION_OBJECT;
3865
3866         aip->goal_objnum = goal_objp-Objects;
3867         ai_set_goal_maybe_abort_dock(objp, aip);
3868         aip->ok_to_target_timestamp = timestamp(DELAY_TARGET_TIME*4);           //      Super extra long time until can target another ship.
3869
3870 }
3871
3872 //      Given an object and an object on whose wing to form, return slot to use.
3873 //      Optimize:
3874 //              This function is called per object in formation per frame.  Should store slot in ai_info struct.
3875 int ai_formation_object_get_slotnum(int objnum, object *objp)
3876 {
3877         int     slotnum = 1;                    //      Note: Slot #0 means leader, which isn't someone who was told to form-on-wing.
3878         object *o;
3879
3880         for ( o = GET_FIRST(&obj_used_list); o != END_OF_LIST(&obj_used_list); o = GET_NEXT(o) ) {
3881                 if (objp == o)
3882                         break;
3883                 else if (o->type == OBJ_SHIP)
3884                         if (Ai_info[Ships[o->instance].ai_index].ai_flags & AIF_FORMATION_OBJECT)
3885                                 if (Ai_info[Ships[o->instance].ai_index].goal_objnum == objnum)
3886                                         slotnum++;
3887         }
3888
3889         Assert(o != END_OF_LIST(&obj_used_list));       //      Didn't find objp in list of used ships.  Impossible!
3890
3891         return slotnum;
3892 }
3893
3894 #define BIGNUM  100000.0f
3895
3896 int Debug_k = 0;
3897
3898 //      Given an attacker's position and a target's position and velocity, compute the time of
3899 //      intersection of a weapon fired by the attacker with speed weapon_speed.
3900 //      Return this value.  Return value of 0.0f means no collision is possible.
3901 float compute_collision_time(vector *targpos, vector *targvel, vector *attackpos, float weapon_speed)
3902 {
3903         vector  vec_to_target;
3904         float           pos_dot_vel;
3905         float           vel_sqr;
3906         float           discrim;
3907
3908         vm_vec_sub(&vec_to_target, targpos, attackpos);
3909         pos_dot_vel = vm_vec_dot(&vec_to_target, targvel);
3910         vel_sqr = vm_vec_dot(targvel, targvel) - weapon_speed*weapon_speed;
3911         discrim = pos_dot_vel*pos_dot_vel - vel_sqr*vm_vec_dot(&vec_to_target, &vec_to_target);
3912
3913         if (discrim > 0.0f) {
3914                 float   t1, t2, t_solve;
3915
3916                 t1 = (-pos_dot_vel + fl_sqrt(discrim)) / vel_sqr;
3917                 t2 = (-pos_dot_vel - fl_sqrt(discrim)) / vel_sqr;
3918
3919                 t_solve = BIGNUM;
3920
3921                 if (t1 > 0.0f)
3922                         t_solve = t1;
3923                 if ((t2 > 0.0f) && (t2 < t_solve))
3924                         t_solve = t2;
3925
3926                 if (t_solve < BIGNUM-1.0f) {
3927                         return t_solve + Debug_k * flFrametime;
3928                 }
3929         }
3930
3931         return 0.0f;
3932 }
3933
3934
3935 //      --------------------------------------------------------------------------
3936 //      If far away, use player's speed.
3937 //      If in between, lerp between player and laser speed
3938 //      If close, use laser speed.
3939 // Want to know how much time it will take to get to the enemy.
3940 // This function doesn't account for the fact that by the time the player
3941 // (or his laser) gets to the current enemy position, the enemy will have moved.
3942 // This is dealt with in polish_predicted_enemy_pos.
3943 float compute_time_to_enemy(float dist_to_enemy, object *pobjp, object *eobjp)
3944 {
3945         float   time_to_enemy;
3946         float   pl_speed = pobjp->phys_info.speed;
3947         float   max_laser_distance, max_laser_speed;
3948         int     bank_num, weapon_num;
3949         ship    *shipp = &Ships[pobjp->instance];
3950
3951         bank_num = shipp->weapons.current_primary_bank;
3952         weapon_num = shipp->weapons.primary_bank_weapons[bank_num];
3953         max_laser_speed = Weapon_info[weapon_num].max_speed;
3954         max_laser_distance = max_laser_speed * Weapon_info[weapon_num].lifetime;
3955
3956         //      If pretty far away, use player's speed to predict position, else
3957         //      use laser's speed because when close, we care more about hitting
3958         //      with a laser than about causing ship:ship rendezvous.
3959         if (dist_to_enemy > 1.5 * max_laser_distance) {
3960                 if (pl_speed > 0.0f)
3961                         time_to_enemy = dist_to_enemy/pl_speed;
3962                 else
3963                         time_to_enemy = 1.0f;
3964         } else if (dist_to_enemy > 1.1*max_laser_distance) {
3965                 if (pl_speed > 0.1f) {
3966                         float   scale;
3967
3968                         scale = (float) ((dist_to_enemy - max_laser_distance) / max_laser_distance);
3969                 
3970                         time_to_enemy = (float) (dist_to_enemy/(pl_speed * scale + max_laser_speed * (1.0f - scale)));
3971                 } else
3972                         time_to_enemy = 2.0f;
3973         } else
3974                 time_to_enemy = (float) (dist_to_enemy/max_laser_speed);
3975
3976         // return time_to_enemy * (1.0f + Ai_info[Ships[pobjp->instance].ai_index].lead_scale);
3977         return time_to_enemy + flFrametime;
3978 }
3979
3980 //      Stuff *dot and *tts.
3981 //      *dot is always computed.  If dot is less than zero, the magnitude is
3982 //      incorrect, not having been divided by distance.
3983 //      If *dot is > 0.0f, then tts is computed.  This is the time it will take object
3984 //      *objp to get to *pos, assuming it moves right at it.
3985 void fds_aux(float *dot, float *tts, vector *pos, float dtime, object *objp)
3986 {
3987         vector  v2s;
3988
3989         vm_vec_sub(&v2s, pos, &objp->pos);
3990         *dot = vm_vec_dot(&v2s, &objp->orient.fvec);
3991
3992         if (*dot > 0.0f) {
3993                 float   dist;
3994
3995                 dist = vm_vec_dist(&objp->pos, pos);
3996
3997                 if (dist > 0.1f)
3998                         *dot /= dist;
3999                 else
4000                         *dot = 1.0f;
4001
4002                 if (objp->phys_info.speed > 0.1f)
4003                         *tts = dist / objp->phys_info.speed;
4004                 else
4005                         *tts = dist * 100.0f;
4006         }
4007 }
4008
4009 /*
4010 //      Return index of weapon that could hit object *sobjp within dtime seconds.
4011 //      Actual time until impact returned in *atime.
4012 int find_danger_weapon(object *sobjp, float dtime, float *atime, float dot_threshhold)
4013 {
4014         object  *objp, *best_objp = NULL;
4015         float           best_tts = 1000.0f;
4016
4017         for ( objp = GET_FIRST(&obj_used_list); objp !=END_OF_LIST(&obj_used_list); objp = GET_NEXT(objp) ) {
4018                 if ((objp->type == OBJ_WEAPON) && (sobjp-Objects != objp->parent)) {
4019                         float           dot, tts;
4020                         // vector       psp;            //      Predicted ship position.
4021
4022                         //      Get dot and time to current ship position.
4023                         fds_aux(&dot, &tts, &sobjp->pos, dtime, objp);
4024
4025                         //      If dot and tts are in plausible range, do more expensive stuff.
4026                         if (dot > 0.98f) {
4027 //                              float   dot_from_sobjp;
4028                                 vector  v2e;
4029
4030                                 vm_vec_normalized_dir(&v2e, &objp->pos, &sobjp->pos);
4031 //                              dot_from_sobjp = vm_vec_dot(&sobjp->orient.fvec, &v2e);
4032 //                              if (dot_from_sobjp >= dot_threshhold)
4033                                         if (tts < dtime) {
4034                                                 if (tts < best_tts) {
4035                                                         best_tts = tts;
4036                                                         best_objp = objp;
4037                                                 }
4038                                         }
4039                         }
4040                 }
4041         }
4042
4043         *atime = best_tts;
4044
4045         if (best_objp != NULL)
4046                 return best_objp-Objects;
4047         else
4048                 return -1;
4049 }
4050 */
4051
4052 //      --------------------------------------------------------------------------
4053 void ai_set_positions(object *pl_objp, object *en_objp, ai_info *aip, vector *player_pos, vector *enemy_pos)
4054 {
4055         *player_pos = pl_objp->pos;
4056
4057         if (aip->next_predict_pos_time > Missiontime) {
4058                 *enemy_pos = aip->last_predicted_enemy_pos;
4059         } else {
4060                 *enemy_pos = en_objp->pos;
4061
4062                 aip->next_predict_pos_time = Missiontime + Skill_level_delay[Game_skill_level];
4063                 aip->last_predicted_enemy_pos = *enemy_pos;
4064         }
4065
4066
4067 }
4068
4069 //      --------------------------------------------------------------------------
4070 int find_nearest_waypoint(object *objp)
4071 {
4072         int     i;
4073         float   dist, min_dist, dot;
4074         int     min_ind;
4075         ship    *shipp;
4076         int     wp_listnum;
4077         waypoint_list   *wpl;
4078
4079         shipp = &Ships[objp->instance];
4080         wp_listnum = Ai_info[Ships[objp->instance].ai_index].wp_list;
4081         Assert(wp_listnum > 0);
4082         wpl = &Waypoint_lists[wp_listnum];
4083
4084         min_dist = 999999.0f;
4085         min_ind = -1;
4086
4087         for (i=0; i<wpl->count; i++) {
4088                 dist = vm_vec_dist_quick(&objp->pos, &wpl->waypoints[i]);
4089                 dot = vm_vec_dot_to_point(&objp->orient.fvec, &objp->pos, &wpl->waypoints[i]);
4090                 dist = (float) (dist * (1.25 - dot));
4091                 if (dist < min_dist) {
4092                         min_dist = dist;
4093                         min_ind = i;
4094                 }
4095         }
4096
4097         Assert(min_ind != -1);
4098
4099         return min_ind;
4100 }
4101
4102 //      Given an ai_info struct, by reading current goal and path information,
4103 //      extract base path information and return in pmp and pmpv.
4104 //      Return true if found, else return false.
4105 //      false means the current point is not on the original path.
4106 int get_base_path_info(int path_cur, int goal_objnum, model_path **pmp, mp_vert **pmpv)
4107 {
4108         pnode                   *pn = &Path_points[path_cur];
4109         ship_info       *sip = &Ship_info[Ships[Objects[goal_objnum].instance].ship_info_index];
4110         polymodel       *pm = model_get(sip->modelnum);
4111         static          int     debug_last_index = -1;
4112         *pmpv = NULL;
4113         *pmp = NULL;
4114
4115         if (pn->path_num != -1) {
4116                 *pmp = &pm->paths[pn->path_num];
4117                 if (pn->path_index != -1)
4118                         *pmpv = &(*pmp)->verts[pn->path_index];
4119                 else
4120                         return 0;
4121         } else
4122                 return 0;
4123
4124 /*      if (debug_last_index != *pmpv-(*pmp)->verts) {
4125                 debug_last_index = *pmpv-(*pmp)->verts;
4126                 nprintf(("AI", "Point %i has %i turrets: ", *pmpv-(*pmp)->verts, (*pmpv)->nturrets));
4127                 for (int i=0; i<(*pmpv)->nturrets; i++) {
4128                         nprintf(("AI", "%i ", (*pmpv)->turret_ids[i]));
4129                 }
4130                 nprintf(("AI", "\n"));
4131         }
4132 */
4133         return 1;
4134 }
4135
4136 //      Modify, in place, the points in a global model path.
4137 //      Only modify those points that are defined in the model path.  Don't modify the
4138 //      leadin points, such as those that are necessary to get the model on the path.
4139 void modify_model_path_points(object *objp)
4140 {       
4141         ai_info         *aip = &Ai_info[Ships[objp->instance].ai_index];
4142         object          *mobjp = &Objects[aip->path_objnum];
4143         ship_info       *osip = &Ship_info[Ships[mobjp->instance].ship_info_index];
4144         polymodel       *pm = model_get(osip->modelnum);
4145         pnode                   *pnp;
4146         int                     path_num, dir;
4147
4148         Assert((aip->path_start >= 0) && (aip->path_start < MAX_PATH_POINTS));
4149
4150         pnp = &Path_points[aip->path_start];
4151         while ((pnp->path_index == -1) && (pnp-Path_points - aip->path_start < aip->path_length))
4152                 pnp++;
4153
4154         path_num = pnp->path_num;
4155         Assert((path_num >= 0) && (path_num < pm->n_paths));
4156         
4157         Assert(pnp->path_index != -1);  //      If this is -1, that means we never found the model path points
4158
4159         dir = 1;
4160         if ( aip->ai_flags & AIF_USE_EXIT_PATH ) {
4161                 dir = -1;
4162         }
4163
4164         copy_xlate_model_path_points(mobjp, &pm->paths[path_num], dir, pm->paths[path_num].nverts, path_num, pnp);
4165 }
4166
4167 //      Return an indication of the distance between two matrices.
4168 //      This is the sum of the distances of their dot products from 1.0f.
4169 float ai_matrix_dist(matrix *mat1, matrix *mat2)
4170 {
4171         float   t;
4172
4173         t =  1.0f - vm_vec_dot(&mat1->fvec, &mat2->fvec);
4174         t += 1.0f - vm_vec_dot(&mat1->uvec, &mat2->uvec);
4175         t += 1.0f - vm_vec_dot(&mat1->rvec, &mat2->rvec);
4176
4177         return t;
4178 }
4179
4180
4181 //      Paths are created in absolute space, so a moving object needs to have model paths within it recreated.
4182 //      This uses the hash functions which means the slightest movement will cause a recreate, though the timestamp
4183 //      prevents this from happening too often.
4184 //      force_recreate_flag TRUE means to recreate regardless of timestamp.
4185 //      Returns TRUE if path recreated.
4186 float maybe_recreate_path(object *objp, ai_info *aip, int force_recreate_flag)
4187 {
4188         int     hashval;
4189
4190         Assert(&Ai_info[Ships[objp->instance].ai_index] == aip);
4191
4192         if ((aip->mode == AIM_BAY_EMERGE) || (aip->mode == AIM_BAY_DEPART))
4193                 if ((OBJ_INDEX(objp) % 4) == (Framecount % 4))
4194                         force_recreate_flag = 1;
4195
4196         //      If no path, that means we don't need one.
4197         if (aip->path_start == -1)
4198                 return 0.0f;
4199
4200         // AL 11-12-97: If AIF_USE_STATIC_PATH is set, don't try to recreate.  This is needed when ships
4201         //                                  emerge from fighter bays.  We don't need to recreate the path.. and in case the 
4202         //              parent ship dies, we still want to be able to continue on the path
4203         if ( aip->ai_flags & AIF_USE_STATIC_PATH ) 
4204                 return 0.0f;
4205
4206         if (force_recreate_flag || timestamp_elapsed(aip->path_next_create_time)) {
4207                 object  *path_objp;
4208
4209                 path_objp = &Objects[aip->path_objnum];
4210
4211                 if ((hashval = create_object_hash(path_objp)) != aip->path_goal_obj_hash) {
4212                         float dist;
4213                         
4214                         dist = vm_vec_dist_quick(&path_objp->pos, &aip->path_create_pos);
4215                         dist += ai_matrix_dist(&path_objp->orient, &aip->path_create_orient) * 25.0f;
4216
4217                         if (force_recreate_flag || (dist > 2.0f)) {
4218                                 aip->path_next_create_time = timestamp(1000);   //      Update again in as little as 1000 milliseconds, ie 1 second.
4219                                 aip->path_goal_obj_hash = hashval;
4220                                 modify_model_path_points(objp);
4221
4222                                 aip->path_create_pos = path_objp->pos;
4223                                 aip->path_create_orient = path_objp->orient;
4224                                 
4225                                 return dist;
4226                         }
4227                 }
4228         }
4229
4230         return 0.0f;
4231 }
4232
4233 //      Set acceleration for ai_dock().
4234 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)
4235 {
4236         float prev_dot_to_goal = aip->prev_dot_to_goal;
4237         
4238         aip->prev_dot_to_goal = dot;
4239
4240         if (objp->phys_info.speed < 0.0f) {
4241                 accelerate_ship(aip, 1.0f/32.0f);
4242         } else if ((prev_dot_to_goal-dot) > 0.01) {
4243                 if (prev_dot_to_goal > dot + 0.05f) {
4244                         accelerate_ship(aip, 0.0f);
4245                 } else {
4246                         change_acceleration(aip, -1.0f);        //      -1.0f means subtract off flFrametime from acceleration value in 0.0..1.0
4247                 }
4248         } else {
4249                 if ((aip->mode == AIM_DOCK) && (dist_to_next < 150.0f) && (aip->path_start + aip->path_length - 2 == aip->path_cur)) {
4250                         set_accel_for_target_speed(objp, sip->max_speed * max(dist_to_next/500.0f, 1.0f));
4251                         //mprintf(("dist = %7.3f, speed = %7.3f\n", dist_to_next, objp->phys_info.speed));
4252                 } else if ((dot_to_next >= dot * .9) || (dist_to_next > 100.0f)) {
4253                         if (dist_to_goal > 200.0f)
4254                                 set_accel_for_target_speed(objp, sip->max_speed * (dot + 1.0f) / 2.0f);
4255                         else {
4256                                 float   xdot;
4257
4258                                 xdot = (dot_to_next + dot)/2.0f;
4259                                 if (xdot < 0.0f)
4260                                         xdot = 0.0f;
4261
4262                                 // AL: if following a path not in dock mode, move full speed
4263                                 if (( aip->mode != AIM_DOCK ) && (dot > 0.9f)) {
4264                                         set_accel_for_target_speed(objp, sip->max_speed*dot*dot*dot);
4265                                 } else {
4266                                         if ((aip->path_cur - aip->path_start < aip->path_length-2) && (dist_to_goal < 2*objp->radius)) {
4267                                                 //nprintf(("AI", "Target speed = %7.3f\n", dist_to_goal/8.0f));
4268                                                 set_accel_for_target_speed(objp, dist_to_goal/8.0f + 2.0f);
4269                                         } else {
4270                                                 set_accel_for_target_speed(objp, sip->max_speed * (2*xdot + 0.25f)/4.0f);
4271                                         }
4272                                 }
4273                         }
4274                 } else {
4275                         float   xdot;
4276
4277                         xdot = max(dot_to_next, 0.1f);
4278                         if ( aip->mode != AIM_DOCK ) {
4279                                 set_accel_for_target_speed(objp, sip->max_speed);
4280                         } else {
4281                                 float   speed;
4282                                 if ((aip->path_cur - aip->path_start < aip->path_length-2) && (dist_to_goal < 2*objp->radius)) {
4283                                         speed = dist_to_goal/8.0f + 2.0f;
4284                                 } else if (dist_to_goal < 4*objp->radius + 50.0f) {
4285                                         speed = dist_to_goal/4.0f + 4.0f;
4286                                 } else {
4287                                         speed = sip->max_speed * (3*xdot + 1.0f)/4.0f;
4288                                 }
4289                                 if (aip->mode == AIM_DOCK) {
4290                                         speed = speed * 2.0f + 1.0f;
4291                                         if (aip->goal_objnum != -1) {
4292                                                 speed += Objects[aip->goal_objnum].phys_info.speed;
4293                                         }
4294                                 }
4295
4296                                 set_accel_for_target_speed(objp, speed);
4297                         }
4298                 }
4299         }
4300 }
4301
4302 //      --------------------------------------------------------------------------
4303 //      Follow a path associated with a large object, such as a capital ship.
4304 //      The points defined on the path are in the object's reference frame.
4305 //      The object of interest is goal_objnum.
4306 //      The paths are defined in the model.  The path of interest is wp_list.
4307 //      The next goal point in the path is wp_index.
4308 //      wp_flags contain special information specific to the path.
4309
4310 // The path vertices are defined by model_path structs:
4311 //              typedef struct model_path {
4312 //                      char            name[MAX_NAME_LEN];                                     // name of the subsystem.  Probably displayed on HUD
4313 //                      int             nverts;
4314 //                      vector  *verts;
4315 //              } model_path;
4316
4317 //      The polymodel struct for the object contains the following:
4318 //              int                     n_paths;
4319 //              model_path      *paths;
4320
4321 //      Returns distance to goal point.
4322 float ai_path()
4323 {
4324         polymodel       *pm;
4325         int             num_paths, num_points;
4326         float           dot, dist_to_goal, dist_to_next, speed, dot_to_next;
4327         ship            *shipp = &Ships[Pl_objp->instance];
4328         ship_info       *sip = &Ship_info[shipp->ship_info_index];
4329         ai_info *aip;
4330         vector  nvel_vec;
4331         float           mag, prev_dot_to_goal;
4332         vector  temp_vec, *slop_vec;
4333         object  *gobjp;
4334         ship            *gshipp;
4335         vector  *cvp, *nvp, next_vec, gcvp, gnvp;               //      current and next vertices in global coordinates.
4336
4337         aip = &Ai_info[Ships[Pl_objp->instance].ai_index];
4338
4339         Assert(aip->goal_objnum != -1);
4340         Assert(Objects[aip->goal_objnum].type == OBJ_SHIP);
4341
4342         gobjp = &Objects[aip->goal_objnum];
4343         gshipp = &Ships[gobjp->instance];
4344
4345         pm = model_get( gshipp->modelnum );
4346         num_paths = pm->n_paths;
4347         Assert(num_paths > 0);
4348
4349         if (aip->path_start == -1) {
4350                 int path_num;
4351                 path_num = ai_return_path_num_from_dockbay(&Objects[aip->goal_objnum], aip->dockee_index);
4352                 Assert(aip->goal_objnum >= 0 && aip->goal_objnum < MAX_OBJECTS);
4353                 ai_find_path(Pl_objp, aip->goal_objnum, path_num, 0);
4354         }
4355
4356         // nprintf(("AI", "Frame: %i, Path index = %i/%i\n", AI_FrameCount, aip->path_cur-aip->path_start, aip->path_length));
4357
4358         maybe_recreate_path(Pl_objp, aip, 0);
4359
4360         num_points = aip->path_length;
4361
4362         //      Set cvp and nvp as pointers to current and next vertices of interest on path.
4363         cvp = &Path_points[aip->path_cur].pos;
4364         if ((aip->path_cur + aip->path_dir - aip->path_start < num_points) || (aip->path_cur + aip->path_dir < aip->path_start))
4365                 nvp = &Path_points[aip->path_cur + aip->path_dir].pos;
4366         else {
4367                 //      If this is 0, then path length must be 1 which means we have no direction!
4368                 Assert((aip->path_cur - aip->path_dir >= aip->path_start) && (aip->path_cur - aip->path_dir - aip->path_start < num_points));
4369                 //      Cleanup for above Assert() which we hit too near release. -- MK, 5/24/98.
4370                 if (aip->path_cur - aip->path_dir - aip->path_start >= num_points) {
4371                         if (aip->path_dir == 1)
4372                                 aip->path_cur = aip->path_start;
4373                         else
4374                                 aip->path_cur = aip->path_start + num_points - 1;
4375                 }
4376
4377                 vector  delvec;
4378                 vm_vec_sub(&delvec, cvp, &Path_points[aip->path_cur - aip->path_dir].pos);
4379                 vm_vec_normalize(&delvec);
4380                 vm_vec_scale_add(&next_vec, cvp, &delvec, 10.0f);
4381                 nvp = &next_vec;
4382         }
4383
4384         //      Interrupt if can't get to current goal point.  Debug only.
4385 /*      if (pp_collide(&Pl_objp->pos, cvp, gobjp, Pl_objp->radius)) {
4386                 Int3();
4387         }
4388 */
4389         //      See if can reach next point (as opposed to current point)
4390         //      However, don't do this if docking and next point is last point.
4391         //      That is, we don't want to pursue the last point under control of the
4392         //      path code.  In docking, this is a special hack.
4393         if ((aip->mode != AIM_DOCK) || ((aip->path_cur-aip->path_start) < num_points - 2)) {
4394                 if ((aip->path_cur + aip->path_dir > aip->path_start) && (aip->path_cur + aip->path_dir < aip->path_start + num_points-2)) {
4395                         if ( timestamp_elapsed(aip->path_next_check_time)) {
4396                                 aip->path_next_check_time = timestamp( 3000 );
4397                                 if (!pp_collide(&Pl_objp->pos, nvp, gobjp, 1.1f * Pl_objp->radius)) {
4398                                         cvp = nvp;
4399                                         aip->path_cur += aip->path_dir;
4400                                         nvp = &Path_points[aip->path_cur].pos;
4401                                         //nprintf(("AI", "Reach: Advancing from point %i to %i of %i points.\n", aip->path_cur-aip->path_dir, aip->path_cur, num_points));
4402                                 }
4403                         }
4404                 }
4405         }
4406
4407         gcvp = *cvp;
4408         gnvp = *nvp;
4409
4410         speed = Pl_objp->phys_info.speed;
4411
4412         dist_to_goal = vm_vec_dist_quick(&Pl_objp->pos, &gcvp);
4413         dist_to_next = vm_vec_dist_quick(&Pl_objp->pos, &gnvp);
4414         //      Can't use fvec, need to use velocity vector because we aren't necessarily
4415         //      moving in the direction we're facing.
4416
4417 //      if (IS_VEC_NULL(&Pl_objp->phys_info.vel)) {
4418         if ( vm_vec_mag_quick(&Pl_objp->phys_info.vel) < AICODE_SMALL_MAGNITUDE ) {
4419                 mag = 0.0f;
4420                 vm_vec_zero(&nvel_vec);
4421         } else
4422                 mag = vm_vec_copy_normalize(&nvel_vec, &Pl_objp->phys_info.vel);
4423
4424         //      If moving not-very-slowly and sliding, then try to slide at goal, rather than
4425         //      point at goal.
4426         slop_vec = NULL;
4427         if (mag < 1.0f)
4428                 nvel_vec = Pl_objp->orient.fvec;
4429         else if (mag > 5.0f) {
4430                 float   nv_dot;
4431                 nv_dot = vm_vec_dot(&Pl_objp->orient.fvec, &nvel_vec);
4432                 if ((nv_dot > 0.5f) && (nv_dot < 0.97f)) {
4433                         slop_vec = &temp_vec;
4434                         vm_vec_sub(slop_vec, &nvel_vec, &Pl_objp->orient.fvec);
4435                 }
4436         }
4437
4438         if (dist_to_goal > 0.1f)
4439                 ai_turn_towards_vector(&gcvp, Pl_objp, flFrametime, sip->srotation_time, slop_vec, NULL, 0.0f, 0);
4440
4441         //      Code to control speed is MUCH less forgiving in path following than in waypoint
4442         //      following.  Must be very close to path or might hit objects.
4443         prev_dot_to_goal = aip->prev_dot_to_goal;
4444         dot = vm_vec_dot_to_point(&nvel_vec, &Pl_objp->pos, &gcvp);
4445         dot_to_next = vm_vec_dot_to_point(&nvel_vec, &Pl_objp->pos, &gnvp);
4446
4447         set_accel_for_docking(Pl_objp, aip, dot, dot_to_next, dist_to_next, dist_to_goal, sip);
4448         aip->prev_dot_to_goal = dot;
4449
4450 //mprintf(("Goal index = %i, dist = %7.3f, dot = %7.3f\n", wp_index, dist_to_goal, dot));
4451
4452         //      If moving at a non-tiny velocity, detect attaining path point by its being close to
4453         //      line between previous and current object location.
4454         if ((dist_to_goal < MIN_DIST_TO_WAYPOINT_GOAL) || (vm_vec_dist_quick(&Pl_objp->last_pos, &Pl_objp->pos) > 0.1f)) {
4455                 vector  nearest_point;
4456                 float           r, min_dist_to_goal;
4457
4458                 r = find_nearest_point_on_line(&nearest_point, &Pl_objp->last_pos, &Pl_objp->pos, &gcvp);
4459
4460                 //      Set min_dist_to_goal = how close must be to waypoint to pick next one.
4461                 //      If docking and this is the second last waypoint, must be very close.
4462                 if ((aip->mode == AIM_DOCK) && (aip->path_cur >= aip->path_length-2))
4463                         min_dist_to_goal = MIN_DIST_TO_WAYPOINT_GOAL;
4464                 else
4465                         min_dist_to_goal = MIN_DIST_TO_WAYPOINT_GOAL + Pl_objp->radius;
4466
4467                 if ( (vm_vec_dist_quick(&Pl_objp->pos, &gcvp) < min_dist_to_goal) ||
4468                         ((r >= 0.0f) && (r <= 1.0f)) && (vm_vec_dist_quick(&nearest_point, &gcvp) < (MIN_DIST_TO_WAYPOINT_GOAL + Pl_objp->radius))) {
4469                         aip->path_cur += aip->path_dir;
4470                         //nprintf(("AI", " Near: Advancing from point %i to %i of %i points.\n", aip->path_cur-aip->path_dir, aip->path_cur, num_points));
4471                         if (((aip->path_cur - aip->path_start) > (num_points+1)) || (aip->path_cur < aip->path_start)) {
4472                                 Assert(aip->mode != AIM_DOCK);          //      If docking, should never get this far, getting to last point handled outside ai_path()
4473                                 aip->path_dir = -aip->path_dir;
4474 //                              aip->path_cur += aip->path_dir;
4475                         }
4476                 }
4477         }
4478
4479         return dist_to_goal;
4480 }
4481
4482 void update_min_max(float val, float *min, float *max)
4483 {
4484         if (val < *min)
4485                 *min = val;
4486         else if (val > *max)
4487                 *max = val;
4488 }
4489
4490 //      Stuff bounding box of all enemy objects within "range" units of object *my_objp.
4491 //      Stuff ni min_vec and max_vec.
4492 //      Return value: Number of enemy objects in bounding box.
4493 int get_enemy_team_range(object *my_objp, float range, int enemy_team_mask, vector *min_vec, vector *max_vec)
4494 {
4495         object  *objp;
4496         ship_obj        *so;
4497         int             count = 0;
4498
4499         for ( so = GET_FIRST(&Ship_obj_list); so != END_OF_LIST(&Ship_obj_list); so = GET_NEXT(so) ) {
4500                 objp = &Objects[so->objnum];
4501                 if (Ships[objp->instance].team & enemy_team_mask) {
4502                         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))
4503                                 if (vm_vec_dist_quick(&my_objp->pos, &objp->pos) < range) {
4504                                         if (count == 0) {
4505                                                 *min_vec = objp->pos;
4506                                                 *max_vec = objp->pos;
4507                                                 count++;
4508                                         } else {
4509                                                 update_min_max(objp->pos.x, &min_vec->x, &max_vec->x);
4510                                                 update_min_max(objp->pos.y, &min_vec->y, &max_vec->y);
4511                                                 update_min_max(objp->pos.z, &min_vec->z, &max_vec->z);
4512                                         }
4513                                 }
4514
4515                 }
4516         }
4517
4518         return count;
4519 }
4520
4521 //      Pick a relatively safe spot for objp to fly to.
4522 //      Problem:
4523 //              Finds a spot away from any enemy within a bounding box.
4524 //              Doesn't verify that "safe spot" is not near some other enemy.
4525 void ai_safety_pick_spot(object *objp)
4526 {
4527         int             objnum;
4528         int             enemy_team_mask;
4529         vector  min_vec, max_vec;
4530         vector  vec_to_center, center;
4531         vector  goal_pos;
4532
4533         objnum = OBJ_INDEX(objp);
4534
4535         enemy_team_mask = get_enemy_team_mask(objnum);
4536
4537         if (get_enemy_team_range(objp, 1000.0f, enemy_team_mask, &min_vec, &max_vec)) {
4538                 vm_vec_avg(&center, &min_vec, &max_vec);
4539                 vm_vec_normalized_dir(&vec_to_center, &center, &objp->pos);
4540
4541                 vm_vec_scale_add(&goal_pos, &center, &vec_to_center, 2000.0f);
4542         } else
4543                 vm_vec_scale_add(&goal_pos, &objp->pos, &objp->orient.fvec, 100.0f);
4544
4545         Ai_info[Ships[objp->instance].ai_index].goal_point = goal_pos;
4546 }
4547
4548 //      Fly to desired safe point.
4549 // Returns distance to that point.
4550 float ai_safety_goto_spot(object *objp)
4551 {
4552         float   dot, dist;
4553         ai_info *aip;
4554         vector  vec_to_goal;
4555         ship_info       *sip;
4556         float   dot_val;
4557
4558         sip = &Ship_info[Ships[objp->instance].ship_info_index];
4559
4560         aip = &Ai_info[Ships[objp->instance].ai_index];
4561         dist = vm_vec_normalized_dir(&vec_to_goal, &aip->goal_point, &objp->pos);
4562         dot = vm_vec_dot(&vec_to_goal, &objp->orient.fvec);
4563
4564         dot_val = (1.1f + dot) / 2.0f;
4565         if (dist > 200.0f) {
4566                 set_accel_for_target_speed(objp, sip->max_speed * dot_val);
4567         } else
4568                 set_accel_for_target_speed(objp, sip->max_speed * dot_val * (dist/200.0f + 0.2f));
4569
4570         return dist;
4571 }
4572
4573 void ai_safety_circle_spot(object *objp)
4574 {
4575         vector  goal_point;
4576         ship_info       *sip;
4577         float           dot;
4578
4579         sip = &Ship_info[Ships[objp->instance].ship_info_index];
4580
4581         goal_point = Ai_info[Ships[objp->instance].ai_index].goal_point;
4582         dot = turn_towards_tangent(objp, &goal_point, 250.0f);  //      Increased from 50 to 250 to make circling not look so wacky.
4583
4584         set_accel_for_target_speed(objp, 0.5f * (1.0f + dot) * sip->max_speed/4.0f);
4585
4586 //      float dist = vm_vec_dist_quick(&goal_point, &Pl_objp->pos);
4587 //      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));
4588
4589 }
4590
4591 //      --------------------------------------------------------------------------
4592 void ai_safety()
4593 {
4594         ai_info *aip;
4595
4596         aip = &Ai_info[Ships[Pl_objp->instance].ai_index];
4597
4598         switch (aip->submode) {
4599         case AISS_1:
4600                 ai_safety_pick_spot(Pl_objp);
4601                 aip->submode = AISS_2;
4602                 aip->submode_start_time = Missiontime;
4603                 break;
4604         case AISS_1a:   //      Pick a safe point because we just got whacked!
4605                 Int3();
4606                 break;
4607         case AISS_2:
4608                 if (ai_safety_goto_spot(Pl_objp) < 25.0f) {
4609                         aip->submode = AISS_3;
4610                         aip->submode_start_time = Missiontime;
4611                 }
4612                 break;
4613         case AISS_3:
4614                 ai_safety_circle_spot(Pl_objp);
4615                 break;
4616         default:
4617                 Int3();         //      Illegal submode for ai_safety();
4618                 break;
4619         }
4620 }
4621
4622 //      --------------------------------------------------------------------------
4623 //      make Pl_objp fly waypoints.
4624 void ai_waypoints()
4625 {
4626         int             wp_index;
4627         vector  *wp_cur, *wp_next;
4628         float           dot, dist_to_goal, dist_to_next, speed, dot_to_next;
4629         ship            *shipp = &Ships[Pl_objp->instance];
4630         ship_info       *sip = &Ship_info[shipp->ship_info_index];
4631         waypoint_list   *wpl;
4632         ai_info *aip;
4633         vector  nvel_vec;
4634         float           mag;
4635         float           prev_dot_to_goal;
4636         vector  temp_vec;
4637         vector  *slop_vec;
4638
4639         aip = &Ai_info[Ships[Pl_objp->instance].ai_index];
4640
4641         wp_index = aip->wp_index;
4642
4643         if (wp_index == -1) {
4644                 ai_start_waypoints(Pl_objp, 0, WPF_REPEAT);
4645                 wp_index = aip->wp_index;
4646                 aip->wp_dir = 1;
4647         }
4648
4649         wpl = &Waypoint_lists[Ai_info[Ships[Pl_objp->instance].ai_index].wp_list];
4650
4651         Assert(wpl->count);     // What? Is this zero? Probably wp_index never got initialized!
4652
4653         wp_cur = &wpl->waypoints[wp_index];
4654         wp_next = &wpl->waypoints[(wp_index+1) % wpl->count];
4655         speed = Pl_objp->phys_info.speed;
4656
4657         dist_to_goal = vm_vec_dist_quick(&Pl_objp->pos, wp_cur);
4658         dist_to_next = vm_vec_dist_quick(&Pl_objp->pos, wp_next);
4659
4660         //      Can't use fvec, need to use velocity vector because we aren't necessarily
4661         //      moving in the direction we're facing.
4662         // AL 23-3-98: Account for very small velocities by checking result of vm_vec_mag().
4663         //                                      If we don't vm_vec_copy_normalize() will think it is normalizing a null vector.
4664 //      if (IS_VEC_NULL(&Pl_objp->phys_info.vel)) {
4665         if ( vm_vec_mag_quick(&Pl_objp->phys_info.vel) < AICODE_SMALL_MAGNITUDE ) {
4666                 mag = 0.0f;
4667                 vm_vec_zero(&nvel_vec);
4668         } else {
4669                 mag = vm_vec_copy_normalize(&nvel_vec, &Pl_objp->phys_info.vel);
4670         }
4671
4672         //      If moving not-very-slowly and sliding, then try to slide at goal, rather than
4673         //      point at goal.
4674         slop_vec = NULL;
4675         if (mag < 1.0f) {
4676                 nvel_vec = Pl_objp->orient.fvec;
4677         } else if (mag > 5.0f) {
4678                 float   nv_dot;
4679                 nv_dot = vm_vec_dot(&Pl_objp->orient.fvec, &nvel_vec);
4680                 if ((nv_dot > 0.5f) && (nv_dot < 0.97f)) {
4681                         slop_vec = &temp_vec;
4682                         vm_vec_sub(slop_vec, &nvel_vec, &Pl_objp->orient.fvec);
4683                 }
4684         }
4685
4686         //      If a wing leader, take turns more slowly, based on size of wing.
4687         int     scale;
4688
4689         if (Ai_info[Ships[Pl_objp->instance].ai_index].wing >= 0) {
4690                 scale = Wings[Ai_info[Ships[Pl_objp->instance].ai_index].wing].current_count;
4691                 scale = (int) ((scale+1)/2);
4692         } else {
4693                 scale = 1;
4694         }
4695
4696         if (dist_to_goal > 0.1f) {
4697                 ai_turn_towards_vector(wp_cur, Pl_objp, flFrametime, sip->srotation_time*3.0f*scale, slop_vec, NULL, 0.0f, 0);
4698         }
4699
4700         prev_dot_to_goal = aip->prev_dot_to_goal;
4701         dot = vm_vec_dot_to_point(&nvel_vec, &Pl_objp->pos, wp_cur);
4702         dot_to_next = vm_vec_dot_to_point(&nvel_vec, &Pl_objp->pos, wp_next);
4703         aip->prev_dot_to_goal = dot;
4704
4705         //      If there is no next point on the path, don't care about dot to next.
4706         if (wp_index + 1 >= wpl->count) {
4707                 dot_to_next = dot;
4708         }
4709
4710         // nprintf(("AI", "Wp #%i, dot = %6.3f, next dot = %6.3f, dist = %7.2f\n", wp_index, dot, dot_to_next, dist_to_goal));
4711
4712         if (Pl_objp->phys_info.speed < 0.0f) {
4713                 accelerate_ship(aip, 1.0f/32);
4714         } else if (prev_dot_to_goal > dot+0.01f) {
4715                 //      We are further from pointing at our goal this frame than last frame, so slow down.
4716                 set_accel_for_target_speed(Pl_objp, Pl_objp->phys_info.speed * 0.95f);
4717         } else if (dist_to_goal < 100.0f) {
4718                 float slew_dot = vm_vec_dot(&Pl_objp->orient.fvec, &nvel_vec);
4719                 if (fl_abs(slew_dot) < 0.9f) {
4720                         accelerate_ship(aip, 0.0f);
4721                 } else if (dot < 0.88f + 0.1f*(100.0f - dist_to_goal)/100.0f) {
4722                         accelerate_ship(aip, 0.0f);
4723                 } else {
4724                         accelerate_ship(aip, 0.5f * dot * dot);
4725                 }
4726         } else {
4727                 float   dot1;
4728                 if (dist_to_goal < 250.0f) {
4729                         dot1 = dot*dot*dot;                             //      Very important to be pointing towards goal when nearby.  Note, cubing preserves sign.
4730                 } else {
4731                         if (dot > 0.0f) {
4732                                 dot1 = dot*dot;
4733                         } else {
4734                                 dot1 = dot;
4735                         }
4736                 }
4737
4738                 if (dist_to_goal > 100.0f + Pl_objp->radius * 2) {
4739                         if (dot < 0.2f) {
4740                                 dot1 = 0.2f;
4741                         }
4742                 }
4743
4744                 if (sip->flags & SIF_SMALL_SHIP) {
4745                         set_accel_for_target_speed(Pl_objp, dot1 * dist_to_goal/5.0f);
4746                 } else {
4747                         set_accel_for_target_speed(Pl_objp, dot1 * dist_to_goal/10.0f);
4748                 }
4749         }
4750
4751         //      Make sure not travelling too fast for someone to keep up.
4752         float   max_allowed_speed = 9999.9f;
4753
4754         if (shipp->wingnum != -1) {
4755                 max_allowed_speed = 0.9f * get_wing_lowest_max_speed(Pl_objp);
4756         }
4757
4758         // check if waypoint speed cap is set and adjust max speed
4759         if (aip->waypoint_speed_cap > 0) {
4760                 max_allowed_speed = (float) aip->waypoint_speed_cap;
4761         }
4762
4763         if (aip->prev_accel * shipp->current_max_speed > max_allowed_speed) {
4764                 accelerate_ship(aip, max_allowed_speed / shipp->current_max_speed);
4765         }
4766
4767         if (vm_vec_dist_quick(&Pl_objp->last_pos, &Pl_objp->pos) > 0.1f) {
4768                 vector  nearest_point;
4769                 float           r;
4770
4771                 r = find_nearest_point_on_line(&nearest_point, &Pl_objp->last_pos, &Pl_objp->pos, wp_cur);
4772
4773                 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))) ||
4774                         ((r >= 0.0f) && (r <= 1.0f)) && (vm_vec_dist_quick(&nearest_point, wp_cur) < (MIN_DIST_TO_WAYPOINT_GOAL + fl_sqrt(Pl_objp->radius)))) {
4775                         wp_index++;
4776                         if (wp_index >= wpl->count)
4777                                 if (aip->wp_flags & WPF_REPEAT) {
4778                                         wp_index = 0;
4779                                 } else {
4780                                         int treat_as_ship;
4781
4782                                         // when not repeating waypoints -- mark the goal as done and put and entry into the mission log
4783                                         // we must be careful when dealing with wings.  A ship in a wing might be completing
4784                                         // a waypoint for for the entire wing, or it might be completing a goal for itself.  If
4785                                         // for itself and in a wing, treat the completion as we would a ship
4786                                         treat_as_ship = 1;
4787                                         if ( Ships[Pl_objp->instance].wingnum != -1 ) {
4788                                                 int type;
4789
4790                                                 // I don't think that you can fly waypoints as dynamic goals!!!
4791                                                 // -- This is legal, just stupid. -- Assert( (aip->active_goal != AI_GOAL_NONE) && (aip->active_goal != AI_ACTIVE_GOAL_DYNAMIC) );
4792                                                 
4793                                                 //      Clean up from above Assert, just in case we ship without fixing it.  (Encountered by JimB on 2/9/98)
4794                                                 if ( (aip->active_goal == AI_GOAL_NONE) || (aip->active_goal == AI_ACTIVE_GOAL_DYNAMIC) ) {
4795                                                         aip->mode = AIM_NONE;
4796                                                         Int3(); //      Look at the ship, find out of it's supposed to be flying waypoints. -- MK.
4797                                                 }
4798
4799                                                 type = aip->goals[aip->active_goal].type;
4800                                                 if ( (type == AIG_TYPE_EVENT_WING) || (type == AIG_TYPE_PLAYER_WING) ) {
4801                                                         treat_as_ship = 0;
4802                                                 } else {
4803                                                         treat_as_ship = 1;
4804                                                 }
4805                                         }
4806
4807                                         // if the ship is not in a wing, remove the goal and continue on
4808                                         if ( treat_as_ship ) {
4809                                                 ai_mission_goal_complete( aip );                                        // this call should reset the AI mode
4810                                                 mission_log_add_entry(LOG_WAYPOINTS_DONE, Ships[Pl_objp->instance].ship_name, wpl->name, -1 );
4811                                         } else {
4812                                                 // this ship is in a wing.  We must mark the goal as being completed for all ships
4813                                                 // in the wing.  We will also mark an entry in the log that the wing completed the goal
4814                                                 // not the individual ship.
4815                                                 ai_mission_wing_goal_complete( Ships[Pl_objp->instance].wingnum, &(aip->goals[aip->active_goal]) );
4816                                                 mission_log_add_entry( LOG_WAYPOINTS_DONE, Wings[Ships[Pl_objp->instance].wingnum].name, wpl->name, -1 );
4817                                         }
4818                                         //wp_index = wpl->count-1;
4819                                 }
4820
4821                         aip->wp_index = wp_index;
4822                 }
4823         }
4824 }
4825
4826 //      Make Pl_objp avoid En_objp
4827 //      Not like evading.  This is for avoiding a collision!
4828 //      Note, use sliding if available.
4829 void avoid_ship()
4830 {
4831         //      To avoid an object, turn towards right or left vector until facing away from object.
4832         //      To choose right vs. left, pick one that is further from center of avoid object.
4833         //      Keep turning away from until pointing away from ship.
4834         //      Stay in avoid mode until at least 3 enemy ship radii away.
4835
4836         //      Speed setting:
4837         //      If inside sphere, zero speed and turn towards outside.
4838         //      If outside sphere, inside 2x sphere, set speed percent of max to:
4839         //              max(away_dot, (dist-rad)/rad)
4840         //      where away_dot is dot(Pl_objp->fvec, vec_En_objp_to_Pl_objp)
4841
4842         vector  vec_to_enemy;
4843         float           away_dot;
4844         float           dist;
4845         ship            *shipp = &Ships[Pl_objp->instance];
4846         ship_info       *sip = &Ship_info[shipp->ship_info_index];
4847         ai_info *aip = &Ai_info[shipp->ai_index];
4848         vector  player_pos, enemy_pos;
4849
4850         // if we're avoiding a stealth ship, then we know where he is, update with no error
4851         if ( is_object_stealth_ship(En_objp) ) {
4852                 update_ai_stealth_info_with_error(aip/*, 1*/);
4853         }
4854
4855         ai_set_positions(Pl_objp, En_objp, aip, &player_pos, &enemy_pos);
4856         vm_vec_sub(&vec_to_enemy, &enemy_pos, &Pl_objp->pos);
4857
4858         dist = vm_vec_normalize(&vec_to_enemy);
4859         away_dot = -vm_vec_dot(&Pl_objp->orient.fvec, &vec_to_enemy);
4860         
4861         if ((sip->max_vel.x > 0.0f) || (sip->max_vel.y > 0.0f)) {
4862                 if (vm_vec_dot(&Pl_objp->orient.rvec, &vec_to_enemy) > 0.0f) {
4863                         AI_ci.sideways = -1.0f;
4864                 } else {
4865                         AI_ci.sideways = 1.0f;
4866                 }
4867                 if (vm_vec_dot(&Pl_objp->orient.uvec, &vec_to_enemy) > 0.0f) {
4868                         AI_ci.vertical = -1.0f;
4869                 } else {
4870                         AI_ci.vertical = 1.0f;
4871                 }
4872         }               
4873
4874         //nprintf(("AI", "Frame %i: Sliding: %s %s\n", Framecount, AI_ci.sideways < 0 ? "left" : "right", AI_ci.vertical < 0 ? "down" : "up" ));
4875         // nprintf(("AI", "away_dot = %6.3f, dist = %7.2f, dist/radsum = %6.3f\n", away_dot, dist, dist/(Pl_objp->radius + En_objp->radius)));
4876
4877         //      If in front of enemy, turn away from it.
4878         //      If behind enemy, try to get fully behind it.
4879         if (away_dot < 0.0f) {
4880                 turn_away_from_point(Pl_objp, &enemy_pos, Pl_objp->phys_info.speed);
4881         } else {
4882                 vector  goal_pos;
4883
4884                 vm_vec_scale_add(&goal_pos, &En_objp->pos, &En_objp->orient.fvec, -100.0f);
4885                 turn_towards_point(Pl_objp, &goal_pos, NULL, Pl_objp->phys_info.speed);
4886         }
4887
4888         //      Set speed.
4889         float   radsum = Pl_objp->radius + En_objp->radius;
4890
4891         if (dist < radsum)
4892                 accelerate_ship(aip, max(away_dot, 0.2f));
4893         else if (dist < 2*radsum)
4894                 accelerate_ship(aip, max(away_dot, (dist - radsum) / radsum)+0.2f);
4895         else
4896                 accelerate_ship(aip, 1.0f);
4897
4898 }
4899
4900 //      Maybe it's time to resume the previous AI mode in aip->previous_mode.
4901 //      Each type of previous_mode has its own criteria on when to resume.
4902 //      Return true if previous mode was resumed.
4903 int maybe_resume_previous_mode(object *objp, ai_info *aip)
4904 {
4905         //      Only (maybe) resume previous goal if current goal is dynamic.
4906         if (aip->active_goal != AI_ACTIVE_GOAL_DYNAMIC)
4907                 return 0;
4908
4909         if (aip->mode == AIM_EVADE_WEAPON) {
4910                 if (timestamp_elapsed(aip->mode_time) || (((aip->nearest_locked_object == -1) || (Objects[aip->nearest_locked_object].type != OBJ_WEAPON)) && (aip->danger_weapon_objnum == -1))) {
4911                         Assert(aip->previous_mode != AIM_EVADE_WEAPON);
4912                         aip->mode = aip->previous_mode;
4913                         aip->submode = aip->previous_submode;
4914                         aip->submode_start_time = Missiontime;
4915                         aip->active_goal = AI_GOAL_NONE;
4916                         aip->mode_time = -1;                    //      Means do forever.
4917                         return 1;
4918                 }
4919         } else if ( aip->previous_mode == AIM_GUARD) {
4920                 if ((aip->guard_objnum != -1) && (aip->guard_signature == Objects[aip->guard_objnum].signature)) {
4921                         object  *guard_objp;
4922                         float   dist;
4923
4924                         guard_objp = &Objects[aip->guard_objnum];
4925                         dist = vm_vec_dist_quick(&guard_objp->pos, &objp->pos);
4926
4927                         //      If guarding ship is far away from guardee and enemy is far away from guardee,
4928                         //      then stop chasing and resume guarding.
4929                         if (dist > (MAX_GUARD_DIST + guard_objp->radius) * 6) {
4930                                 if ((En_objp != NULL) && (En_objp->type == OBJ_SHIP)) {
4931                                         if (vm_vec_dist_quick(&guard_objp->pos, &En_objp->pos) > (MAX_GUARD_DIST + guard_objp->radius) * 6) {
4932                                                 Assert(aip->previous_mode == AIM_GUARD);
4933                                                 aip->mode = aip->previous_mode;
4934                                                 aip->submode = AIS_GUARD_PATROL;
4935                                                 aip->active_goal = AI_GOAL_NONE;
4936                                                 return 1;
4937                                         }
4938                                 }
4939                         }
4940                 }
4941         }
4942
4943         return 0;
4944
4945 }
4946
4947 //      Call this function if you want something to happen on average every N quarters of a second.
4948 //      The truth value returned by this function will be the same for any given quarter second interval.
4949 //      The value "num" is only passed in to get asynchronous behavior for different objects.
4950 //      modulus == 1 will always return true.
4951 //      modulus == 2 will return true half the time.
4952 //      modulus == 16 will return true for one quarter second interval every four seconds.
4953 int static_rand_timed(int num, int modulus)
4954 {
4955         if (modulus < 2)
4956                 return 1;
4957         else {
4958                 int     t;
4959
4960                 t = Missiontime >> 18;          //      Get time in quarters of a second
4961                 t += num;
4962
4963                 return !(t % modulus);
4964         }
4965 }
4966
4967 //      Maybe fire afterburner based on AI class
4968 int ai_maybe_fire_afterburner(object *objp, ai_info *aip)
4969 {
4970         if (aip->ai_class == 0)
4971                 return 0;               //      Lowest level never aburners away
4972         else  {
4973                 //      Maybe don't afterburner because of a potential collision with the player.
4974                 //      If not multiplayer, near player and player in front, probably don't afterburner.
4975                 if (!(Game_mode & GM_MULTIPLAYER)) {
4976                         if (Ships[objp->instance].team == Player_ship->team) {
4977                                 float   dist;
4978
4979                                 dist = vm_vec_dist_quick(&objp->pos, &Player_obj->pos) - Player_obj->radius - objp->radius;
4980                                 if (dist < 150.0f) {
4981                                         vector  v2p;
4982                                         float           dot;
4983
4984                                         vm_vec_normalized_dir(&v2p, &Player_obj->pos, &objp->pos);
4985                                         dot = vm_vec_dot(&v2p, &objp->orient.fvec);
4986
4987                                         if (dot > 0.0f) {
4988                                                 if (dot * dist > 50.0f)
4989                                                         return 0;
4990                                         }
4991                                 }
4992                         }
4993                 }
4994
4995                 if (aip->ai_class >= Num_ai_classes-2)
4996                         return 1;               //      Highest two levels always aburner away.
4997                 else {
4998                         return static_rand_timed(objp-Objects, Num_ai_classes - aip->ai_class);
4999                 }
5000         }
5001 }
5002
5003 //      Maybe engage afterburner after being hit by an object.
5004 void maybe_afterburner_after_ship_hit(object *objp, ai_info *aip, object *en_objp)
5005 {
5006         //      Only do if facing a little away.
5007         if (en_objp != NULL) {
5008                 vector  v2e;
5009
5010                 vm_vec_normalized_dir(&v2e, &en_objp->pos, &objp->pos);
5011                 if (vm_vec_dot(&v2e, &objp->orient.fvec) > -0.5f)
5012                         return;
5013         }
5014
5015         if (!( objp->phys_info.flags & PF_AFTERBURNER_ON )) {
5016                 if (ai_maybe_fire_afterburner(objp, aip)) {
5017                         afterburners_start(objp);
5018                         aip->afterburner_stop_time = Missiontime + F1_0/2;
5019                 }
5020         }
5021 }
5022
5023 //      Return true if object *objp is an instructor.
5024 //      Is an instructor if name begins INSTRUCTOR_SHIP_NAME else not.
5025 int is_instructor(object *objp)
5026 {
5027         return !strnicmp(Ships[objp->instance].ship_name, INSTRUCTOR_SHIP_NAME, strlen(INSTRUCTOR_SHIP_NAME));
5028 }
5029
5030 //      Evade the weapon aip->danger_weapon_objnum
5031 //      If it's not valid, do a quick out.
5032 //      Evade by accelerating hard.
5033 //      If necessary, turn hard left or hard right.
5034 void evade_weapon()
5035 {
5036         object  *weapon_objp = NULL;
5037         object  *unlocked_weapon_objp = NULL, *locked_weapon_objp = NULL;
5038         vector  weapon_pos, player_pos, goal_point;
5039         vector  vec_from_enemy;
5040         float           dot_from_enemy, dot_to_enemy;
5041         float           dist;
5042         ship            *shipp = &Ships[Pl_objp->instance];
5043         ai_info *aip = &Ai_info[shipp->ai_index];
5044
5045         if (is_instructor(Pl_objp))
5046                 return;
5047
5048         //      Make sure we're actually being attacked.
5049         //      Favor locked objects.
5050         if (aip->nearest_locked_object != -1) {
5051                 if (Objects[aip->nearest_locked_object].type == OBJ_WEAPON)
5052                         locked_weapon_objp = &Objects[aip->nearest_locked_object];
5053         }
5054         
5055         if (aip->danger_weapon_objnum != -1)
5056                 if (Objects[aip->danger_weapon_objnum].signature == aip->danger_weapon_signature)
5057                         unlocked_weapon_objp = &Objects[aip->danger_weapon_objnum];
5058                 else
5059                         aip->danger_weapon_objnum = -1;         //      Signatures don't match, so no longer endangered.
5060
5061         if (locked_weapon_objp != NULL) {
5062                 if (unlocked_weapon_objp != NULL) {
5063                         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))
5064                                 weapon_objp = locked_weapon_objp;
5065                         else
5066                                 weapon_objp = unlocked_weapon_objp;
5067                 } else
5068                         weapon_objp = locked_weapon_objp;
5069         } else if (unlocked_weapon_objp != NULL)
5070                 weapon_objp = unlocked_weapon_objp;
5071         else {
5072                 if (aip->mode == AIM_EVADE_WEAPON)
5073                         maybe_resume_previous_mode(Pl_objp, aip);
5074                 return;
5075         }
5076
5077         Assert(weapon_objp != NULL);
5078
5079         if (weapon_objp->type != OBJ_WEAPON) {
5080                 if (aip->mode == AIM_EVADE_WEAPON)
5081                         maybe_resume_previous_mode(Pl_objp, aip);
5082                 return;
5083         }
5084         
5085         weapon_pos = weapon_objp->pos;
5086         player_pos = Pl_objp->pos;
5087
5088         //      Make speed based on skill level, varying at highest skill level, which is harder to hit.
5089         accelerate_ship(aip, 1.0f);
5090
5091         dist = vm_vec_normalized_dir(&vec_from_enemy, &player_pos, &weapon_pos);
5092
5093         dot_to_enemy = -vm_vec_dot(&Pl_objp->orient.fvec, &vec_from_enemy);
5094         dot_from_enemy = vm_vec_dot(&weapon_objp->orient.fvec, &vec_from_enemy);
5095         //nprintf(("AI", "dot from enemy = %7.3f\n", dot_from_enemy));
5096
5097         //      If shot is incoming...
5098         if (dot_from_enemy < 0.3f) {
5099                 if (weapon_objp == unlocked_weapon_objp)
5100                         aip->danger_weapon_objnum = -1;
5101                 return;
5102         } else if (dot_from_enemy > 0.7f) {
5103                 if (dist < 200.0f) {
5104                         if (!( Pl_objp->phys_info.flags & PF_AFTERBURNER_ON )) {
5105                                 if (ai_maybe_fire_afterburner(Pl_objp, aip)) {
5106                                         //nprintf(("AI", "Frame %i, turning on afterburner.\n", AI_FrameCount));
5107                                         afterburners_start(Pl_objp);
5108                                         aip->afterburner_stop_time = Missiontime + F1_0/2;
5109                                 }
5110                         }
5111                 }
5112
5113                 //      If we're sort of pointing towards it...
5114                 if ((dot_to_enemy < -0.5f) || (dot_to_enemy > 0.5f)) {
5115                         float   rdot;
5116
5117                         //      Turn hard left or right, depending on which gets out of way quicker.
5118                         rdot = vm_vec_dot(&Pl_objp->orient.rvec, &vec_from_enemy);
5119
5120                         if ((rdot < -0.5f) || (rdot > 0.5f))
5121                                 vm_vec_scale_add(&goal_point, &Pl_objp->pos, &Pl_objp->orient.rvec, -200.0f);
5122                         else
5123                                 vm_vec_scale_add(&goal_point, &Pl_objp->pos, &Pl_objp->orient.rvec, 200.0f);
5124
5125                         turn_towards_point(Pl_objp, &goal_point, NULL, 0.0f);
5126                 }
5127         }
5128
5129 }
5130
5131 //      Use sliding and backwards moving to face enemy.
5132 //      (Coded 2/20/98.  Works fine, but it's hard to see how to integrate it into the AI system.
5133 //       Typically ships are moving so fast that a little sliding isn't enough to gain an advantage.
5134 //       It's currently used to avoid collisions and could be used to evade weapon fire, but the latter
5135 //       would be frustrating, I think.
5136 //       This function is currently not called.)
5137 void slide_face_ship()
5138 {
5139         ship_info       *sip;
5140
5141         sip = &Ship_info[Ships[Pl_objp->instance].ship_info_index];
5142
5143         //      If can't slide, return.
5144         if ((sip->max_vel.x == 0.0f) && (sip->max_vel.y == 0.0f))
5145                 return;
5146
5147         vector  goal_pos;
5148         float           dot_from_enemy, dot_to_enemy;
5149         vector  vec_from_enemy, vec_to_goal;
5150         float           dist;
5151         float           up, right;
5152         ai_info         *aip;
5153
5154         aip = &Ai_info[Ships[Pl_objp->instance].ai_index];
5155
5156         dist = vm_vec_normalized_dir(&vec_from_enemy, &Pl_objp->pos, &En_objp->pos);
5157
5158         ai_turn_towards_vector(&En_objp->pos, Pl_objp, flFrametime, sip->srotation_time, NULL, NULL, 0.0f, 0);
5159
5160         dot_from_enemy = vm_vec_dot(&vec_from_enemy, &En_objp->orient.fvec);
5161         dot_to_enemy = -vm_vec_dot(&vec_from_enemy, &Pl_objp->orient.fvec);
5162
5163         if (vm_vec_dot(&vec_from_enemy, &En_objp->orient.rvec) > 0.0f)
5164                 right = 1.0f;
5165         else
5166                 right = -1.0f;
5167
5168         if (vm_vec_dot(&vec_from_enemy, &En_objp->orient.uvec) > 0.0f)
5169                 up = 1.0f;
5170         else
5171                 up = -1.0f;
5172
5173         vm_vec_scale_add(&goal_pos, &En_objp->pos, &En_objp->orient.rvec, right * 200.0f);
5174         vm_vec_scale_add(&goal_pos, &En_objp->pos, &En_objp->orient.uvec, up * 200.0f);
5175
5176         vm_vec_normalized_dir(&vec_to_goal, &goal_pos, &Pl_objp->pos);
5177
5178         if (vm_vec_dot(&vec_to_goal, &Pl_objp->orient.rvec) > 0.0f)
5179                 AI_ci.sideways = 1.0f;
5180         else
5181                 AI_ci.sideways = -1.0f;
5182
5183         if (vm_vec_dot(&vec_to_goal, &Pl_objp->orient.uvec) > 0.0f)
5184                 AI_ci.vertical = 1.0f;
5185         else
5186                 AI_ci.vertical = -1.0f;
5187
5188         if (dist < 200.0f) {
5189                 if (dot_from_enemy < 0.7f)
5190                         accelerate_ship(aip, -1.0f);
5191                 else
5192                         accelerate_ship(aip, dot_from_enemy + 0.5f);
5193         } else {
5194                 if (dot_from_enemy < 0.7f) {
5195                         accelerate_ship(aip, 0.2f);
5196                 } else {
5197                         accelerate_ship(aip, 1.0f);
5198                 }
5199         }
5200 }
5201
5202 //      General code for handling one ship evading another.
5203 //      Problem: This code is also used for avoiding an impending collision.
5204 //      In such a case, it is not good to go to max speed, which is often good
5205 //      for a certain kind of evasion.
5206 void evade_ship()
5207 {
5208         vector  player_pos, enemy_pos, goal_point;
5209         vector  vec_from_enemy;
5210         float           dot_from_enemy;
5211         float           dist;
5212         ship            *shipp = &Ships[Pl_objp->instance];
5213         ship_info       *sip = &Ship_info[shipp->ship_info_index];
5214         ai_info *aip = &Ai_info[shipp->ai_index];
5215         float           bank_override = 0.0f;
5216
5217         ai_set_positions(Pl_objp, En_objp, aip, &player_pos, &enemy_pos);
5218
5219         //      Make speed based on skill level, varying at highest skill level, which is harder to hit.
5220         if (Game_skill_level == NUM_SKILL_LEVELS-1) {
5221                 int     rand_int;
5222                 float   accel_val;
5223
5224                 rand_int = static_rand(Pl_objp-Objects);
5225                 accel_val = (float) (((Missiontime^rand_int) >> 14) & 0x0f)/32.0f + 0.5f;
5226                 accelerate_ship(aip, accel_val);
5227                 //nprintf(("AI", "Accel value = %7.3f\n", accel_val));
5228         } else
5229                 accelerate_ship(aip, (float) (Game_skill_level+2) / (NUM_SKILL_LEVELS+1));
5230
5231         if ((Missiontime - aip->submode_start_time > F1_0/2) && (sip->afterburner_fuel_capacity > 0.0f)) {
5232                 float percent_left = 100.0f * shipp->afterburner_fuel / sip->afterburner_fuel_capacity;
5233                 if (percent_left > 30.0f + ((Pl_objp-Objects) & 0x0f)) {
5234                         afterburners_start(Pl_objp);
5235                         aip->afterburner_stop_time = Missiontime + F1_0 + static_rand(Pl_objp-Objects)/4;
5236                 }
5237         }
5238
5239         vm_vec_sub(&vec_from_enemy, &player_pos, &enemy_pos);
5240
5241         dist = vm_vec_normalize(&vec_from_enemy);
5242         dot_from_enemy = vm_vec_dot(&En_objp->orient.fvec, &vec_from_enemy);
5243
5244         if (dist > 250.0f) {
5245                 vector  gp1, gp2;
5246                 //      If far away from enemy, circle, going to nearer of point far off left or right wing
5247                 vm_vec_scale_add(&gp1, &enemy_pos, &En_objp->orient.rvec, 250.0f);
5248                 vm_vec_scale_add(&gp2, &enemy_pos, &En_objp->orient.rvec, -250.0f);
5249                 if (vm_vec_dist_quick(&gp1, &Pl_objp->pos) < vm_vec_dist_quick(&gp2, &Pl_objp->pos))
5250                         goal_point = gp1;
5251                 else
5252                         goal_point = gp2;
5253         } else if (dot_from_enemy < 0.1f) {
5254                 //      If already close to behind, goal is to get completely behind.
5255                 vm_vec_scale_add(&goal_point, &enemy_pos, &En_objp->orient.fvec, -1000.0f);
5256         } else if (dot_from_enemy > 0.9f) {
5257                 //      If enemy pointing almost right at self, and self pointing close to enemy, turn away from
5258                 vector  vec_to_enemy;
5259                 float           dot_to_enemy;
5260
5261                 vm_vec_sub(&vec_to_enemy, &enemy_pos, &player_pos);
5262
5263                 vm_vec_normalize(&vec_to_enemy);
5264                 dot_to_enemy = vm_vec_dot(&Pl_objp->orient.fvec, &vec_to_enemy);
5265                 if (dot_to_enemy > 0.75f) {
5266                         //      Used to go to En_objp's right vector, but due to banking while turning, that
5267                         //      caused flying in an odd spiral.
5268                         vm_vec_scale_add(&goal_point, &enemy_pos, &Pl_objp->orient.rvec, 1000.0f);
5269                         if (dist < 100.0f)
5270                                 bank_override = Pl_objp->phys_info.speed; 
5271                 } else {
5272                         bank_override = Pl_objp->phys_info.speed;                       //      In enemy's sights, not pointing at him, twirl away.
5273                         // nprintf(("Mike", " Do sumpin' else."));
5274                         goto evade_ship_l1;
5275                 }
5276         } else {
5277 evade_ship_l1: ;
5278                 if (aip->ai_evasion > myrand()*100.0f/32767.0f) {
5279                         int     temp;
5280                         float   scale;
5281                         float   psrandval;      //      some value close to zero to choose whether to turn right or left.
5282
5283                         psrandval = (float) (((Missiontime >> 14) & 0x0f) - 8); //      Value between -8 and 7
5284                         psrandval = psrandval/16.0f;                                                    //      Value between -1/2 and 1/2 (approx)
5285
5286                         //      If not close to behind, turn towards his right or left vector, whichever won't cross his path.
5287                         if (vm_vec_dot(&vec_from_enemy, &En_objp->orient.rvec) > psrandval) {
5288                                 scale = 1000.0f;
5289                         } else {
5290                                 scale = -1000.0f;
5291                         }
5292
5293                         vm_vec_scale_add(&goal_point, &enemy_pos, &En_objp->orient.rvec, scale);
5294
5295                         temp = ((Missiontime >> 16) & 0x07);
5296                         temp = ((temp * (temp+1)) % 16)/2 - 4;
5297                         if ((psrandval == 0) && (temp == 0))
5298                                 temp = 3;
5299
5300                         scale = 200.0f * temp;
5301
5302                         vm_vec_scale_add2(&goal_point, &En_objp->orient.uvec, scale);
5303                 } else {
5304                         //      No evasion this frame, but continue with previous turn.
5305                         //      Reason: If you don't, you lose rotational momentum.  Turning every other frame,
5306                         //      and not in between results in a very slow turn because of loss of momentum.
5307                         if ((aip->prev_goal_point.x != 0.0f) || (aip->prev_goal_point.y != 0.0f) || (aip->prev_goal_point.z != 0.0f))
5308                                 goal_point = aip->prev_goal_point;
5309                         else
5310                                 vm_vec_scale_add(&goal_point, &enemy_pos, &En_objp->orient.rvec, 100.0f);
5311                 }
5312         }
5313
5314         // nprintf(("Mike", "Goal point = %7.1f %7.1f %7.1f\n", goal_point.x, goal_point.y, goal_point.z));
5315         turn_towards_point(Pl_objp, &goal_point, NULL, bank_override);
5316
5317         aip->prev_goal_point = goal_point;
5318 }
5319
5320 //      --------------------------------------------------------------------------
5321 //      Fly in a manner making it difficult for opponent to attack.
5322 void ai_evade()
5323 {
5324         evade_ship();
5325 }
5326
5327 /*
5328 // -------------------------------------------------------------------
5329 //      Refine predicted enemy position because enemy will move while we move
5330 //      towards predicted enemy position.
5331 //      last_delta_vec is stuffed with size of polishing in last step.  This small amount
5332 //      can be used to perturb the predicted position to make firing not be exact.
5333 //      This function will almost always undershoot actual position, assuming both ships
5334 //      are moving at constant speed.  But with even one polishing step, the error should
5335 //      be under 1%. The number of polishing steps is specified in the parameter num_polish_steps.
5336 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)
5337 {
5338         int     iteration;
5339         vector  player_pos = pobjp->pos;
5340         vector  enemy_pos = *predicted_enemy_pos;
5341         physics_info    *en_physp = &eobjp->phys_info;
5342         float           time_to_enemy;
5343         vector  last_predicted_enemy_pos = *predicted_enemy_pos;
5344         
5345         vm_vec_zero(last_delta_vec);
5346
5347         for (iteration=0; iteration < num_polish_steps; iteration++) {
5348                 dist_to_enemy = vm_vec_dist_quick(predicted_enemy_pos, &player_pos);
5349                 time_to_enemy = compute_time_to_enemy(dist_to_enemy, pobjp, eobjp);
5350                 vm_vec_scale_add(predicted_enemy_pos, &eobjp->pos, &eobjp->phys_info.vel, time_to_enemy);
5351                 vm_vec_sub(last_delta_vec, predicted_enemy_pos, &last_predicted_enemy_pos);
5352                 last_predicted_enemy_pos= *predicted_enemy_pos;
5353         }
5354 }
5355 */
5356
5357 /*
5358 Relevant variables are:
5359         best_dot_to_enemy               best dot product to enemy in last BEST_DOT_TIME seconds
5360         best_dot_to_time                time at which best dot occurred
5361         best_dot_from_enemy     best dot product for enemy to player in last BEST_DOT_TIME seconds
5362         best_dot_from_time      time at which best dot occurred
5363         submode_start_time      time at which we entered the current submode
5364         previous_submode                previous submode, get it?
5365 Legal submodes are:
5366         CONTINUOUS_TURN vector_id {0..3 = right, -right, up, -up}
5367         ATTACK
5368         EVADE_SQUIGGLE
5369         EVADE_BRAKE
5370 */
5371
5372 float   G_collision_time;
5373 vector  G_predicted_pos, G_fire_pos;
5374
5375 /*
5376 void show_firing_diag()
5377 {
5378         float           dot;
5379         vector  v2t;
5380         vector  pos1, pos2;
5381         float           dist;
5382
5383         if (G_collision_time == 0.0f)
5384                 return;
5385
5386         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",
5387                 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));
5388         vm_vec_normalized_dir(&v2t, &G_predicted_pos, &G_fire_pos);
5389         dot = vm_vec_dot(&v2t, &Pl_objp->orient.fvec);
5390         mprintf(("Dot of fvec and vector to predicted position = %10.7f (%7.3f degrees)\n", dot, acos(dot)*180.0f/3.141592654f));
5391
5392         vm_vec_scale_add(&pos1, &En_objp->pos, &En_objp->phys_info.vel, G_collision_time);
5393         vm_vec_scale_add(&pos2, &G_fire_pos, &Pl_objp->orient.fvec, G_collision_time*300.0f);
5394         dist = vm_vec_dist(&pos1, &pos2);
5395
5396         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));
5397 }
5398 */
5399
5400 //      If:
5401 //              flags & WIF_PUNCTURE
5402 //      Then Select a Puncture weapon.
5403 //      Else
5404 //              Select Any ol' weapon.
5405 //      Returns primary_bank index.
5406 int ai_select_primary_weapon(object *objp, object *other_objp, int flags)
5407 {
5408         ship    *shipp = &Ships[objp->instance];
5409         ship_weapon *swp = &shipp->weapons;
5410         ship_info *sip;
5411
5412         //Assert( other_objp != NULL );
5413         Assert( shipp->ship_info_index >= 0 && shipp->ship_info_index < MAX_SHIP_TYPES);
5414
5415         sip = &Ship_info[shipp->ship_info_index];
5416
5417         if (flags & WIF_PUNCTURE) {
5418                 if (swp->current_primary_bank >= 0) {
5419                         int     bank_index;
5420
5421                         bank_index = swp->current_primary_bank;
5422
5423                         if (Weapon_info[swp->primary_bank_weapons[bank_index]].wi_flags & WIF_PUNCTURE) {
5424                                 //nprintf(("AI", "%i: Ship %s selecting weapon %s\n", Framecount, Ships[objp->instance].ship_name, Weapon_info[swp->primary_bank_weapons[bank_index]].name));
5425                                 return swp->current_primary_bank;
5426                         }
5427                 }
5428                 for (int i=0; i<swp->num_primary_banks; i++) {
5429                         int     weapon_info_index;
5430
5431                         weapon_info_index = swp->primary_bank_weapons[i];
5432
5433                         if (weapon_info_index > -1){
5434                                 if (Weapon_info[weapon_info_index].wi_flags & WIF_PUNCTURE) {
5435                                         swp->current_primary_bank = i;
5436                                         //nprintf(("AI", "%i: Ship %s selecting weapon %s\n", Framecount, Ships[objp->instance].ship_name, Weapon_info[swp->primary_bank_weapons[i]].name));
5437                                         return i;
5438                                 }
5439                         }
5440                 }
5441                 
5442                 // AL 26-3-98: If we couldn't find a puncture weapon, pick first available weapon if one isn't active
5443                 if ( swp->current_primary_bank < 0 ) {
5444                         if ( swp->num_primary_banks > 0 ) {
5445                                 swp->current_primary_bank = 0;
5446                         }
5447                 }
5448
5449         } else {                //      Don't need to be using a puncture weapon.
5450                 if (swp->current_primary_bank >= 0) {
5451                         if (!(Weapon_info[swp->primary_bank_weapons[swp->current_primary_bank]].wi_flags & WIF_PUNCTURE)){
5452                                 return swp->current_primary_bank;
5453                         }
5454                 }
5455                 for (int i=0; i<swp->num_primary_banks; i++) {
5456                         if (swp->primary_bank_weapons[i] > -1) {
5457                                 if (!(Weapon_info[swp->primary_bank_weapons[i]].wi_flags & WIF_PUNCTURE)) {
5458                                         swp->current_primary_bank = i;
5459                                         nprintf(("AI", "%i: Ship %s selecting weapon %s\n", Framecount, Ships[objp->instance].ship_name, Weapon_info[swp->primary_bank_weapons[i]].name));
5460                                         return i;
5461                                 }
5462                         }
5463                 }
5464                 //      Wasn't able to find a non-puncture weapon.  Stick with what we have.
5465         }
5466
5467         Assert( swp->current_primary_bank != -1 );              // get Alan or Allender
5468
5469         return swp->current_primary_bank;
5470 }
5471
5472 //      --------------------------------------------------------------------------
5473 //      Maybe link primary weapons.
5474 void set_primary_weapon_linkage(object *objp)
5475 {
5476         ship            *shipp;
5477         ai_info *aip;
5478
5479         shipp = &Ships[objp->instance];
5480         aip     = &Ai_info[shipp->ai_index];
5481
5482         shipp->flags &= ~SF_PRIMARY_LINKED;
5483
5484         if (Num_weapons > (int) (MAX_WEAPONS * 0.75f)) {
5485                 if (shipp->flags & SF_PRIMARY_LINKED)
5486                         nprintf(("AI", "Frame %i, ship %s: Unlinking primaries.\n", Framecount, shipp->ship_name));
5487                 shipp->flags &= ~SF_PRIMARY_LINKED;
5488                 return;         //      If low on slots, don't link.
5489         }
5490
5491         shipp->flags &= ~SF_PRIMARY_LINKED;
5492
5493         // AL: ensure target is a ship!
5494         if ( Objects[aip->target_objnum].type == OBJ_SHIP ) {
5495                 // If trying to destroy a big ship (i.e., not disable/disarm), always unleash all weapons
5496                 if ( ship_get_SIF(&Ships[Objects[aip->target_objnum].instance]) & SIF_BIG_SHIP) {
5497                         if ( aip->targeted_subsys == NULL ) {
5498                                 shipp->flags |= SF_PRIMARY_LINKED;
5499                                 shipp->flags |= SF_SECONDARY_DUAL_FIRE;
5500                                 return;
5501                         }
5502                 }
5503         }
5504
5505         // AL 2-11-98: If ship has a disarm or disable goal, don't link unless both weapons are
5506         //                                      puncture weapons
5507         if ( (aip->active_goal != AI_GOAL_NONE) && (aip->active_goal != AI_ACTIVE_GOAL_DYNAMIC) ) {
5508                 if ( aip->goals[aip->active_goal].ai_mode & (AI_GOAL_DISABLE_SHIP|AI_GOAL_DISARM_SHIP) ) {
5509                         ship_weapon     *swp;
5510                         swp = &shipp->weapons;
5511                         // only continue if both primaries are puncture weapons
5512                         if ( swp->num_primary_banks == 2 ) {
5513                                 if ( !(Weapon_info[swp->primary_bank_weapons[0]].wi_flags & WIF_PUNCTURE) ) 
5514                                         return;
5515                                 if ( !(Weapon_info[swp->primary_bank_weapons[1]].wi_flags & WIF_PUNCTURE) ) 
5516                                         return;
5517                         }
5518                 }
5519         }
5520
5521         //      Don't want all ships always linking weapons at start, so asynchronize.
5522         if (Missiontime < i2f(30))
5523                 return;
5524         else if (Missiontime < i2f(120)) {
5525                 int r = static_rand((Missiontime >> 17) ^ OBJ_INDEX(objp));
5526                 if ( (r&3) != 0)
5527                         return;
5528         }
5529
5530         if (shipp->weapon_energy > Link_energy_levels_always[Game_skill_level]) {
5531                 shipp->flags |= SF_PRIMARY_LINKED;
5532         } else if (shipp->weapon_energy > Link_energy_levels_maybe[Game_skill_level]) {
5533                 if (objp->hull_strength < Ship_info[shipp->ship_info_index].initial_hull_strength/3.0f)
5534                         shipp->flags |= SF_PRIMARY_LINKED;
5535         }
5536 }
5537
5538 //      --------------------------------------------------------------------------
5539 //      Fire the current primary weapon.
5540 //      *objp is the object to fire from.
5541 void ai_fire_primary_weapon(object *objp)
5542 {
5543         ship                    *shipp = &Ships[objp->instance];
5544         ship_weapon     *swp = &shipp->weapons;
5545         ship_info       *sip;
5546         ai_info         *aip;
5547         object          *enemy_objp;
5548
5549         Assert( shipp->ship_info_index >= 0 && shipp->ship_info_index < MAX_SHIP_TYPES);
5550         sip = &Ship_info[shipp->ship_info_index];
5551
5552         aip = &Ai_info[shipp->ai_index];
5553
5554         //      If low on slots, fire a little less often.
5555         if (Num_weapons > (int) (0.9f * MAX_WEAPONS)) {
5556                 if (frand() > 0.5f) {
5557                         nprintf(("AI", "Frame %i, %s not fire.\n", Framecount, shipp->ship_name));
5558                         return;
5559                 }
5560         }
5561
5562         if (!Ai_firing_enabled){
5563                 return;
5564         }
5565
5566         if (aip->target_objnum != -1){
5567                 enemy_objp = &Objects[aip->target_objnum];
5568         } else {
5569                 enemy_objp = NULL;
5570         }
5571
5572         if ( (swp->current_primary_bank < 0) || (swp->current_primary_bank >= swp->num_primary_banks) || timestamp_elapsed(aip->primary_select_timestamp)) {
5573                 int     flags = 0;
5574                 // AL 2-11-98: If attacking any subsystem (not just engines), use disrupter weapon
5575 //              if ((aip->targeted_subsys != NULL) && (aip->targeted_subsys->system_info->type == SUBSYSTEM_ENGINE)) {
5576                 if ( aip->targeted_subsys != NULL ) {
5577                         flags = WIF_PUNCTURE;
5578                 }
5579                 ai_select_primary_weapon(objp, enemy_objp, flags);
5580                 ship_primary_changed(shipp);    // AL: maybe send multiplayer information when AI ship changes primaries
5581                 aip->primary_select_timestamp = timestamp(5 * 1000);    //      Maybe change primary weapon five seconds from now.
5582         }
5583
5584         //      If pointing nearly at predicted collision point of target, bash orientation to be perfectly pointing.
5585         float   dot;
5586         vector  v2t;
5587
5588 //      if (!IS_VEC_NULL(&G_predicted_pos)) {
5589         if (!( vm_vec_mag_quick(&G_predicted_pos) < AICODE_SMALL_MAGNITUDE )) {
5590                 if ( !vm_vec_cmp(&G_predicted_pos, &G_fire_pos) ) {
5591                         nprintf(("Warning", "Avoid NULL vector assert.. why are G_predicted_pos and G_fire_pos the same?\n"));
5592                 } else {
5593                         vm_vec_normalized_dir(&v2t, &G_predicted_pos, &G_fire_pos);
5594                         dot = vm_vec_dot(&v2t, &objp->orient.fvec);
5595                         if (dot > .998629534f){ //      if within 3.0 degrees of desired heading, bash
5596                                 vm_vector_2_matrix(&objp->orient, &v2t, &objp->orient.uvec, NULL);
5597                         }
5598                 }
5599         }
5600
5601         //      Make sure not firing at a protected ship unless firing at a live subsystem.
5602         //      Note: This happens every time the ship tries to fire, perhaps every frame.
5603         //      Should be wrapped in a timestamp, same one that enables it to fire, but that is complicated
5604         //      by multiple banks it can fire from.
5605         if (aip->target_objnum != -1) {
5606                 object  *tobjp = &Objects[aip->target_objnum];
5607                 if (tobjp->flags & OF_PROTECTED) {
5608                         if (aip->targeted_subsys != NULL) {
5609                                 int     type;
5610
5611                                 type = aip->targeted_subsys->system_info->type;
5612                                 if (ship_get_subsystem_strength(&Ships[tobjp->instance], type) == 0.0f) {
5613                                         aip->target_objnum = -1;
5614                                         return;
5615                                 }
5616                         } else {
5617                                 aip->target_objnum = -1;
5618                                 return;
5619                         }
5620                 }
5621         }
5622
5623         //      If enemy is protected, not firing a puncture weapon and enemy's hull is low, don't fire.
5624         if ((enemy_objp != NULL) && (enemy_objp->flags & OF_PROTECTED)) {
5625                 // AL: 3-6-98: Check if current_primary_bank is valid
5626                 if ((enemy_objp->hull_strength < 750.0f) && 
5627                         ((aip->targeted_subsys == NULL) || (enemy_objp->hull_strength < aip->targeted_subsys->current_hits + 50.0f)) &&
5628                         (swp->current_primary_bank >= 0) ) {
5629                         if (!(Weapon_info[swp->primary_bank_weapons[swp->current_primary_bank]].wi_flags & WIF_PUNCTURE)) {
5630                                 //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));
5631                                 swp->next_primary_fire_stamp[swp->current_primary_bank] = timestamp(1000);
5632                                 return;
5633                         }
5634
5635                         /*
5636                         int     num_attacking;
5637                         num_attacking = num_enemies_attacking(enemy_objp-Objects);
5638                         if (enemy_objp->hull_strength / num_attacking < 200.0f) {
5639                                 if (frand() < 0.75f) {
5640                                         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));
5641                                         swp->next_primary_fire_stamp[swp->current_primary_bank] = timestamp(500);
5642                                         return;
5643                                 }
5644                         }
5645                         */
5646                 }
5647         }
5648
5649         set_primary_weapon_linkage(objp);
5650         
5651         // I think this will properly solve the problem
5652         // fire non-streaming weapons
5653         ship_fire_primary(objp, 0);
5654         
5655         // fire streaming weapons
5656         shipp->flags |= SF_TRIGGER_DOWN;
5657         ship_fire_primary(objp, 1);
5658         shipp->flags &= ~SF_TRIGGER_DOWN;
5659 }
5660
5661 //      --------------------------------------------------------------------------
5662 //      Return number of nearby enemy fighters.
5663 //      threshold is the distance within which a ship is considered near.
5664 //
5665 // input:       enemy_team_mask =>      teams that are considered as an enemy
5666 //                              pos                                     =>      world position to measure ship distances from
5667 //                              threshold                       =>      max distance from pos to be considered "near"
5668 //
5669 // exit:                number of ships within threshold units of pos
5670 int num_nearby_fighters(int enemy_team_mask, vector *pos, float threshold)
5671 {
5672         ship_obj        *so;
5673         object  *ship_objp;
5674         int             count = 0;
5675
5676         for ( so = GET_FIRST(&Ship_obj_list); so != END_OF_LIST(&Ship_obj_list); so = GET_NEXT(so) ) {
5677
5678                 ship_objp = &Objects[so->objnum];
5679
5680                 if (Ships[ship_objp->instance].team & enemy_team_mask) {
5681                         if (Ship_info[Ships[ship_objp->instance].ship_info_index].flags & (SIF_FIGHTER | SIF_BOMBER)) {
5682                                 if (vm_vec_dist_quick(pos, &ship_objp->pos) < threshold)
5683                                         count++;
5684                         }
5685                 }
5686         }
5687
5688         return count;
5689 }
5690
5691 //      --------------------------------------------------------------------------
5692 //      Select secondary weapon to fire.
5693 //      Currently, 1/16/98:
5694 //              If 0 secondary weapons available, return -1
5695 //              If 1 available, use it.
5696 //              If 2 or more, if the current weapon is one of them, stick with it, otherwise choose a random one.
5697 //      priority1 and priority2 are Weapon_info[] bitmasks such as WIF_HOMING_ASPECT.  If any weapon has any bit in priority1
5698 //      set, that weapon will be selected.  If not, apply to priority2.  If neither, return -1, meaning no weapon selected.
5699 //      Note, priorityX have default values of -1, meaning if not set, they will match any weapon.
5700 //      Return value:
5701 //              bank index
5702 //      Should do this:
5703 //              Favor aspect seekers when attacking small ships faraway.
5704 //              Favor rapid fire dumbfire when attacking a large ship.
5705 //              Ignore heat seekers because we're not sure how they'll work.
5706 void ai_select_secondary_weapon(object *objp, ship_weapon *swp, int priority1 = -1, int priority2 = -1)
5707 {
5708         int     num_weapon_types;
5709         int     weapon_id_list[MAX_WEAPON_TYPES], weapon_bank_list[MAX_WEAPON_TYPES];
5710         int     i;
5711         int     ignore_mask;
5712         int     initial_bank;
5713
5714         initial_bank = swp->current_secondary_bank;
5715
5716         //      Ignore bombs unless one of the priorities asks for them to be selected.
5717         if (WIF_HUGE & (priority1 | priority2))
5718                 ignore_mask = 0;
5719         else
5720                 ignore_mask = WIF_HUGE;
5721
5722         if (!(WIF_BOMBER_PLUS & (priority1 | priority2)))
5723                 ignore_mask |= WIF_BOMBER_PLUS;
5724
5725 #ifndef NDEBUG
5726         for (i=0; i<MAX_WEAPON_TYPES; i++) {
5727                 weapon_id_list[i] = -1;
5728                 weapon_bank_list[i] = -1;
5729         }
5730 #endif
5731
5732         //      Stuff weapon_bank_list with bank index of available weapons.
5733         num_weapon_types = get_available_secondary_weapons(objp, weapon_id_list, weapon_bank_list);
5734
5735         int     priority2_index = -1;
5736
5737         for (i=0; i<num_weapon_types; i++) {
5738                 int     wi_flags;
5739
5740                 wi_flags = Weapon_info[swp->secondary_bank_weapons[weapon_bank_list[i]]].wi_flags;
5741                 if (!(wi_flags & ignore_mask)) {                                        //      Maybe bombs are illegal.
5742                         if (wi_flags & priority1) {
5743                                 swp->current_secondary_bank = weapon_bank_list[i];                              //      Found first priority, return it.
5744                                 break;
5745                         } else if (wi_flags & priority2)
5746                                 priority2_index = weapon_bank_list[i];  //      Found second priority, but might still find first priority.
5747                 }
5748         }
5749
5750         //      If didn't find anything above, then pick any secondary weapon.
5751         if (i == num_weapon_types) {
5752                 swp->current_secondary_bank = priority2_index;  //      Assume we won't find anything.
5753                 if (priority2_index == -1) {
5754                         for (i=0; i<num_weapon_types; i++) {
5755                                 int     wi_flags;
5756
5757                                 wi_flags = Weapon_info[swp->secondary_bank_weapons[weapon_bank_list[i]]].wi_flags;
5758                                 if (!(wi_flags & ignore_mask)) {                                        //      Maybe bombs are illegal.
5759                                         if (swp->secondary_bank_ammo[i] > 0) {
5760                                                 swp->current_secondary_bank = i;
5761                                                 break;
5762                                         }
5763                                 }
5764                         }
5765                 }
5766         }
5767
5768         //      If switched banks, force reacquisition of aspect lock.
5769         if (swp->current_secondary_bank != initial_bank) {
5770                 ai_info *aip = &Ai_info[Ships[objp->instance].ai_index];
5771                 
5772                 aip->aspect_locked_time = 0.0f;
5773                 aip->current_target_is_locked = 0;
5774         }
5775
5776
5777         ship_secondary_changed(&Ships[objp->instance]); // AL: let multiplayer know if secondary bank has changed
5778         // nprintf(("AI", "Ship %s selected weapon %s\n", Ships[objp->instance].ship_name, Weapon_info[swp->secondary_bank_weapons[swp->current_secondary_bank]].name));
5779 }
5780
5781 //      Return number of objects homing on object *target_objp
5782 int compute_num_homing_objects(object *target_objp)
5783 {
5784         object  *objp;
5785         int             count = 0;
5786
5787         for ( objp = GET_FIRST(&obj_used_list); objp !=END_OF_LIST(&obj_used_list); objp = GET_NEXT(objp) ) {
5788                 if (objp->type == OBJ_WEAPON) {
5789                         if (Weapon_info[Weapons[objp->instance].weapon_info_index].wi_flags & WIF_HOMING) {
5790                                 if (Weapons[objp->instance].homing_object == target_objp) {
5791                                         count++;
5792                                 }
5793                         }
5794                 }
5795         }
5796
5797         return count;
5798 }
5799
5800 //      Object *firing_objp just fired weapon weapon_index (index in Weapon_info).
5801 //      If it's a shockwave weapon, tell your team about it!
5802 void ai_maybe_announce_shockwave_weapon(object *firing_objp, int weapon_index)
5803 {
5804         if ((firing_objp->type == OBJ_SHIP) && (Weapon_info[weapon_index].shockwave_speed > 0.0f)) {
5805                 ship_obj        *so;
5806                 int             firing_ship_team;
5807
5808                 firing_ship_team = Ships[firing_objp->instance].team;
5809
5810                 for ( so = GET_FIRST(&Ship_obj_list); so != END_OF_LIST(&Ship_obj_list); so = GET_NEXT(so) ) {
5811                         object  *A = &Objects[so->objnum];
5812                         Assert(A->type == OBJ_SHIP);
5813
5814                         if (Ships[A->instance].team == firing_ship_team) {
5815                                 ai_info *aip = &Ai_info[Ships[A->instance].ai_index];
5816                                 // AL 1-5-98: only avoid shockwave if not docked or repairing
5817                                 if ( !(aip->ai_flags & (AIF_DOCKED|AIF_BEING_REPAIRED)) ) {
5818                                         aip->ai_flags |= AIF_AVOID_SHOCKWAVE_WEAPON;
5819                                 }
5820                         }
5821                 }
5822         }
5823 }
5824
5825 //      Return total payload of all incoming missiles.
5826 float compute_incoming_payload(object *target_objp)
5827 {
5828         missile_obj     *mo;
5829         float                   payload = 0.0f;
5830
5831         for ( mo = GET_NEXT(&Missile_obj_list); mo != END_OF_LIST(&Missile_obj_list); mo = GET_NEXT(mo) ) {
5832                 object  *objp;
5833
5834                 objp = &Objects[mo->objnum];
5835                 Assert(objp->type == OBJ_WEAPON);
5836                 if (Weapons[objp->instance].homing_object == target_objp) {
5837                         payload += Weapon_info[Weapons[objp->instance].weapon_info_index].damage;
5838                 }
5839         }
5840
5841         return payload;
5842 }
5843
5844 //      --------------------------------------------------------------------------
5845 //      Return true if OK for *aip to fire its current weapon at its current target.
5846 //      Only reason this function returns false is:
5847 //              weapon is a homer
5848 //              targeted at player
5849 //                      OR:     player has too many homers targeted at him
5850 //                                      Missiontime in that dead zone in which can't fire at this player
5851 //      Note: If player is attacking a ship, that ship is allowed to fire at player.  Otherwise, we get in a situation in which
5852 //      player is attacking a large ship, but that large ship is not defending itself with missiles.
5853 int check_ok_to_fire(int objnum, int target_objnum, weapon_info *wip)
5854 {
5855         int     num_homers = 0;
5856         object  *tobjp = &Objects[target_objnum];
5857
5858         if (target_objnum > -1) {
5859                 // AL 3-4-98: Ensure objp target is a ship first 
5860                 if ( tobjp->type == OBJ_SHIP ) {
5861
5862                         // should not get this far. check if ship is protected from beam and weapon is type beam
5863                         if ( (wip->wi_flags & WIF_BEAM) && (tobjp->flags & OF_BEAM_PROTECTED) ) {
5864                                 Int3();
5865                                 return 0;
5866                         }
5867                         if (Ship_info[Ships[tobjp->instance].ship_info_index].flags & SIF_SMALL_SHIP) {
5868                                 num_homers = compute_num_homing_objects(&Objects[target_objnum]);
5869                         }
5870                 }
5871
5872                 //      If player, maybe fire based on Skill_level and number of incoming weapons.
5873                 //      If non-player, maybe fire based on payload of incoming weapons.
5874                 if (wip->wi_flags & WIF_HOMING) {
5875                         if ((target_objnum > -1) && (tobjp->flags & OF_PLAYER_SHIP)) {
5876                                 if (Ai_info[Ships[tobjp->instance].ai_index].target_objnum != objnum) {
5877                                         //      Don't allow AI ships to fire at player for fixed periods of time based on skill level.
5878                                         //      With 5 skill levels, at Very Easy, they fire in 1/7 of every 10 second interval.
5879                                         //      At Easy, 2/7...at Expert, 5/7
5880                                         int t = ((Missiontime /(65536*10)) ^ target_objnum ^ 0x01) % (NUM_SKILL_LEVELS+2);
5881                                         if (t > Game_skill_level) {
5882                                                 //nprintf(("AI", "Not OK to fire homer at time thing %i\n", t));
5883                                                 return 0;
5884                                         }
5885                                 }
5886                                 //nprintf(("AI", " IS OK to fire homer at time thing %i ***\n", t));
5887                                 int     swarmers = 0;
5888                                 if (wip->wi_flags & WIF_SWARM)
5889                                         swarmers = 2;   //      Note, always want to be able to fire swarmers if no currently incident homers.
5890                                 if (Max_allowed_player_homers[Game_skill_level] < num_homers + swarmers) {
5891                                         return 0;
5892                                 }
5893                         } else if (num_homers > 3) {
5894                                 float   incoming_payload;
5895
5896                                 incoming_payload = compute_incoming_payload(&Objects[target_objnum]);
5897
5898                                 if (incoming_payload > tobjp->hull_strength) {
5899                                         return 0;
5900                                 }
5901                         }
5902                 }
5903         }
5904
5905         return 1;
5906 }
5907
5908 //      --------------------------------------------------------------------------
5909 //      Fire a secondary weapon.
5910 //      Maybe choose to fire a different one.
5911 //      priority1 and priority2 are optional parameters with defaults = -1
5912 int ai_fire_secondary_weapon(object *objp, int priority1, int priority2)
5913 {
5914         ship_weapon *swp;
5915         ship    *shipp;
5916         ship_info *sip;
5917         int             current_bank;
5918         int             rval = 0;
5919
5920 #ifndef NDEBUG
5921         if (!Ai_firing_enabled)
5922                 return rval;
5923 #endif
5924
5925         Assert( objp != NULL );
5926         Assert(objp->type == OBJ_SHIP);
5927         shipp = &Ships[objp->instance];
5928         swp = &shipp->weapons;
5929
5930         Assert( shipp->ship_info_index >= 0 && shipp->ship_info_index < MAX_SHIP_TYPES);
5931         sip = &Ship_info[shipp->ship_info_index];
5932
5933         //      Select secondary weapon.
5934         current_bank = swp->current_secondary_bank; //ai_select_secondary_weapon(objp, swp, priority1, priority2);
5935
5936         //nprintf(("AI", "Frame %i: Current bank = %i, ammo remaining = %i\n", Framecount, current_bank, swp->secondary_bank_ammo[current_bank]));
5937         if (current_bank == -1) {
5938                 swp->next_secondary_fire_stamp[current_bank] = timestamp(500);
5939                 return rval;
5940         }
5941
5942         Assert(current_bank < shipp->weapons.num_secondary_banks);
5943
5944         weapon_info     *wip = &Weapon_info[shipp->weapons.secondary_bank_weapons[current_bank]];
5945
5946         if ((wip->wi_flags & WIF_HOMING_ASPECT) && (!Ai_info[shipp->ai_index].current_target_is_locked)) {
5947                 //nprintf(("AI", "Not firing secondary weapon because not aspect locked.\n"));
5948                 swp->next_secondary_fire_stamp[current_bank] = timestamp(250);
5949         } else if ((wip->wi_flags & WIF_BOMB) || (vm_vec_dist_quick(&objp->pos, &En_objp->pos) > 50.0f)) {
5950                 //      This might look dumb, firing a bomb even if closer than 50 meters, but the reason is, if you're carrying
5951                 //      bombs, delivering them is probably more important than surviving.
5952                 ai_info *aip;
5953
5954                 aip = &Ai_info[shipp->ai_index];
5955                 
5956                 //      Note, maybe don't fire if firing at player and any homers yet fired.
5957                 //      Decreasing chance to fire the more homers are incoming on player.
5958                 if (check_ok_to_fire(OBJ_INDEX(objp), aip->target_objnum, wip)) {
5959                         if (ship_fire_secondary(objp)) {
5960                                 rval = 1;
5961                                 swp->next_secondary_fire_stamp[current_bank] = timestamp(500);
5962                                 //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));
5963                         }
5964
5965                 } else {
5966                         swp->next_secondary_fire_stamp[current_bank] = timestamp(500);
5967                 }
5968         }
5969
5970         return rval;
5971 }
5972
5973 //      Return true if it looks like obj1, if continuing to move along current vector, will
5974 //      collide with obj2.
5975 int might_collide_with_ship(object *obj1, object *obj2, float dot_to_enemy, float dist_to_enemy, float duration)
5976 {
5977         if (obj1->phys_info.speed * duration + 2*(obj1->radius + obj2->radius) > dist_to_enemy)
5978                 if (dot_to_enemy > 0.8f - 2*(obj1->radius + obj2->radius)/dist_to_enemy)
5979                         return objects_will_collide(obj1, obj2, duration, 2.0f);
5980
5981 //              BABY - 
5982 //              CONDITION 1, dist_to_enemy < o1_rad + o2_rad + (obj1.speed + obj2.speed) * time + 50
5983         
5984         return 0;
5985
5986 }
5987
5988 //      --------------------------------------------------------------------------
5989 //      Return true if ship *objp firing a laser believes it will hit a teammate.
5990 int might_hit_teammate(object *firing_objp)
5991 {
5992         int             team;
5993         object  *objp;
5994         ship_obj        *so;
5995
5996         team = Ships[firing_objp->instance].team;
5997
5998         for ( so = GET_FIRST(&Ship_obj_list); so != END_OF_LIST(&Ship_obj_list); so = GET_NEXT(so) ) {
5999                 objp = &Objects[so->objnum];
6000                 if (Ships[objp->instance].team == team) {
6001                         float           dist, dot;
6002                         vector  vec_to_objp;
6003
6004                         vm_vec_sub(&vec_to_objp, &firing_objp->pos, &objp->pos);
6005                         dist = vm_vec_mag_quick(&vec_to_objp);
6006                         dot = vm_vec_dot(&firing_objp->orient.fvec, &vec_to_objp)/dist;
6007                         if (might_collide_with_ship(firing_objp, objp, dot, dist, 2.0f))
6008                                 return 1;
6009                 }
6010         }
6011
6012         return 0;
6013
6014 }
6015
6016 //int   Team_not_fire_count=0, Team_hit_count = 0;
6017
6018 void render_all_ship_bay_paths(object *objp)
6019 {
6020         int             i,j,color;
6021         ship            *sp = &Ships[objp->instance];
6022         polymodel       *pm;
6023         model_path      *mp;
6024
6025         pm = model_get(sp->modelnum);
6026         vector  global_path_point;
6027         vertex  v, prev_vertex;
6028
6029         if ( pm->ship_bay == NULL )
6030                 return;
6031
6032         for ( i = 0; i < pm->ship_bay->num_paths; i++ ) {
6033                 mp = &pm->paths[pm->ship_bay->paths[i]];
6034
6035                 for ( j = 0; j < mp->nverts; j++ ) {
6036                         vm_vec_unrotate(&global_path_point, &mp->verts[j].pos, &objp->orient);
6037                         vm_vec_add2(&global_path_point, &objp->pos);
6038                         g3_rotate_vertex(&v, &global_path_point);
6039                         color = 255 - j*50;
6040                         if ( color < 50 ) 
6041                                 color = 100;
6042                         gr_set_color(0, color, 0);
6043
6044                         if ( j == mp->nverts-1 ) {
6045                                 gr_set_color(255, 0, 0);
6046                         }
6047
6048                         g3_draw_sphere( &v, 1.5f);
6049
6050                         if ( j > 0 )
6051                                 g3_draw_line(&v, &prev_vertex);
6052
6053                         prev_vertex = v;
6054         
6055                 }
6056         }
6057 }
6058
6059 // debug function to show all path points associated with an object
6060 void render_all_subsys_paths(object *objp)
6061 {
6062         int             i,j,color;
6063         ship            *sp = &Ships[objp->instance];
6064         polymodel       *pm;
6065         model_path      *mp;
6066
6067         pm = model_get(sp->modelnum);
6068         vector  global_path_point;
6069         vertex  v, prev_vertex;
6070
6071         if ( pm->ship_bay == NULL )
6072                 return;
6073
6074         for ( i = 0; i < pm->n_paths; i++ ) {
6075                 mp = &pm->paths[i];
6076                 for ( j = 0; j < mp->nverts; j++ ) {
6077                         vm_vec_unrotate(&global_path_point, &mp->verts[j].pos, &objp->orient);
6078                         vm_vec_add2(&global_path_point, &objp->pos);
6079                         g3_rotate_vertex(&v, &global_path_point);
6080                         color = 255 - j*50;
6081                         if ( color < 50 ) 
6082                                 color = 100;
6083                         gr_set_color(0, color, 0);
6084
6085                         if ( j == mp->nverts-1 ) {
6086                                 gr_set_color(255, 0, 0);
6087                         }
6088
6089                         g3_draw_sphere( &v, 1.5f);
6090
6091                         if ( j > 0 )
6092                                 g3_draw_line(&v, &prev_vertex);
6093
6094                         prev_vertex = v;
6095                 }
6096         }
6097 }
6098
6099 void render_path_points(object *objp)
6100 {
6101         ship            *shipp = &Ships[objp->instance];
6102         ai_info *aip = &Ai_info[shipp->ai_index];
6103         object  *dobjp;
6104         polymodel       *pm;
6105
6106         render_all_subsys_paths(objp);
6107         render_all_ship_bay_paths(objp);
6108
6109         if (aip->goal_objnum < 0)
6110                 return;
6111
6112         dobjp = &Objects[aip->goal_objnum];
6113         pm = model_get(Ships[dobjp->instance].modelnum);
6114         vector  dock_point, global_dock_point;
6115         vertex  v;
6116
6117         ship_model_start(&Objects[aip->goal_objnum]);
6118         if (pm->n_docks) {
6119                 dock_point = pm->docking_bays[0].pnt[0];
6120                 model_find_world_point(&global_dock_point, &dock_point, Ships[dobjp->instance].modelnum, 0, &dobjp->orient, &dobjp->pos );
6121                 g3_rotate_vertex(&v, &global_dock_point);
6122                 gr_set_color(255, 255, 255);
6123                 g3_draw_sphere( &v, 1.5f);
6124         }
6125
6126         if (aip->path_start != -1) {
6127                 vertex          prev_vertex;
6128                 pnode                   *pp = &Path_points[aip->path_start];
6129                 int                     num_points = aip->path_length;
6130                 int                     i;
6131
6132                 for (i=0; i<num_points; i++) {
6133                         vertex  v0;
6134
6135                         g3_rotate_vertex( &v0, &pp->pos );
6136
6137                         gr_set_color(0, 128, 96);
6138                         if (i != 0)
6139                                 g3_draw_line(&v0, &prev_vertex);
6140
6141                         if (pp-Path_points == aip->path_cur)
6142                                 gr_set_color(255,255,0);
6143                         
6144                         g3_draw_sphere( &v0, 4.5f);
6145
6146                         //      Connect all the turrets that can fire upon this point to this point.
6147 /*                      if (0) { //pp->path_index != -1) {
6148                                 model_path      *pmp;
6149                                 mp_vert         *pmpv;
6150
6151                                 get_base_path_info(pp->path_index, aip->goal_objnum, &pmp, &pmpv);
6152
6153                                 if (pmpv->nturrets) {
6154                                         for (int j = 0; j<pmpv->nturrets; j++) {
6155                                                 vertex  v1;
6156                                                 vector  turret_pos;
6157                                                 ship_subsys     *ssp;
6158
6159                                                 ssp = ship_get_indexed_subsys(&Ships[Objects[aip->goal_objnum].instance], pmpv->turret_ids[j]);
6160
6161 model_find_world_point(&turret_pos, &ssp->system_info->pnt, Ships[dobjp->instance].modelnum, 0, &dobjp->orient, &dobjp->pos );
6162         
6163                                                 g3_rotate_vertex(&v1, &turret_pos);
6164                                                 gr_set_color(255, 255, 0);
6165                                                 g3_draw_line(&v0, &v1);
6166                                                 g3_draw_sphere( &v1, 1.5f);
6167                                         }
6168                                 }
6169                         } */
6170
6171                         prev_vertex = v0;
6172
6173                         pp++;
6174                 }
6175         }
6176
6177         ship_model_stop(&Objects[aip->goal_objnum]);
6178 }
6179
6180 // Return the distance that the current AI weapon will travel
6181 float ai_get_weapon_dist(ship_weapon *swp)
6182 {
6183         int     bank_num, weapon_num;
6184
6185         bank_num = swp->current_primary_bank;
6186         weapon_num = swp->primary_bank_weapons[bank_num];
6187
6188         //      If weapon_num is illegal, return a reasonable value.  A valid weapon
6189         //      will get selected when this ship tries to fire.
6190         if (weapon_num == -1) {
6191                 // Int3();
6192                 return 1000.0f;
6193         }
6194
6195         return Weapon_info[weapon_num].max_speed * Weapon_info[weapon_num].lifetime;
6196 }
6197
6198 float ai_get_weapon_speed(ship_weapon *swp)
6199 {
6200         int     bank_num, weapon_num;
6201
6202         bank_num = swp->current_primary_bank;
6203         if (bank_num < 0)
6204                 return 100.0f;
6205
6206         weapon_num = swp->primary_bank_weapons[bank_num];
6207
6208         if (weapon_num == -1) {
6209                 //Int3();
6210                 return 100.0f;
6211         }
6212
6213         return Weapon_info[weapon_num].max_speed;
6214 }
6215
6216 //      Compute the predicted position of a ship to be fired upon from a turret.
6217 //      This is based on position of firing gun, enemy object, weapon speed and skill level constraints.
6218 //      Return value in *predicted_enemy_pos.
6219 //      Also, stuff globals G_predicted_pos, G_collision_time and G_fire_pos.
6220 //      *pobjp          object firing the weapon
6221 //      *eobjp          object being fired upon
6222 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)
6223 {
6224         ship    *shipp = &Ships[pobjp->instance];
6225         float   range_time;
6226
6227         //weapon_speed = ai_get_weapon_speed(&shipp->weapons);
6228
6229         if (weapon_speed < 1.0f)
6230                 weapon_speed = 1.0f;
6231
6232         range_time = 2.0f;
6233
6234         //      Make it take longer for enemies to get player's allies in range based on skill level.
6235         if (Ships[pobjp->instance].team != Ships[Player_obj->instance].team)
6236                 range_time += In_range_time[Game_skill_level];
6237
6238         //nprintf(("AI", "time enemy in range = %7.3f\n", aip->time_enemy_in_range));
6239
6240         if (time_enemy_in_range < range_time) {
6241                 float   dist;
6242
6243                 dist = vm_vec_dist_quick(&pobjp->pos, enemy_pos);
6244                 vm_vec_scale_add(predicted_enemy_pos, enemy_pos, enemy_vel, time_enemy_in_range * dist/weapon_speed);
6245         } else {
6246                 float   collision_time, scale;
6247                 vector  rand_vec;
6248                 ai_info *aip = &Ai_info[shipp->ai_index];
6249
6250                 collision_time = compute_collision_time(enemy_pos, enemy_vel, gun_pos, weapon_speed);
6251
6252                 if (collision_time == 0.0f){
6253                         collision_time = 100.0f;
6254                 }
6255
6256                 vm_vec_scale_add(predicted_enemy_pos, enemy_pos, enemy_vel, collision_time);
6257                 if (time_enemy_in_range > 2*range_time){
6258                         scale = (1.0f - aip->ai_accuracy) * 4.0f;
6259                 } else {
6260                         scale = (1.0f - aip->ai_accuracy) * 4.0f * (1.0f + 4.0f * (1.0f - time_enemy_in_range/(2*range_time)));
6261                 }               
6262
6263                 static_randvec(((pobjp-Objects) ^ (Missiontime >> 16)) & 7, &rand_vec);
6264
6265                 vm_vec_scale_add2(predicted_enemy_pos, &rand_vec, scale);
6266                 G_collision_time = collision_time;
6267                 G_fire_pos = *gun_pos;
6268         }
6269
6270         G_predicted_pos = *predicted_enemy_pos;
6271 }
6272
6273 //      Compute the predicted position of a ship to be fired upon.
6274 //      This is based on current position of firing object, enemy object, relative position of gun on firing object,
6275 //      weapon speed and skill level constraints.
6276 //      Return value in *predicted_enemy_pos.
6277 //      Also, stuff globals G_predicted_pos, G_collision_time and G_fire_pos.
6278 void set_predicted_enemy_pos(vector *predicted_enemy_pos, object *pobjp, object *eobjp, ai_info *aip)
6279 {
6280         float   weapon_speed, range_time;
6281         ship    *shipp = &Ships[pobjp->instance];
6282
6283         weapon_speed = ai_get_weapon_speed(&shipp->weapons);
6284         weapon_speed = max(weapon_speed, 1.0f);         // set not less than 1
6285
6286         range_time = 2.0f;
6287
6288         //      Make it take longer for enemies to get player's allies in range based on skill level.
6289         // but don't bias team v. team missions
6290         if ( !((Game_mode & GM_MULTIPLAYER) && (Netgame.type_flags & NG_TYPE_TEAM)) ) {
6291                 if (Ships[pobjp->instance].team != Ships[Player_obj->instance].team) {
6292                         range_time += In_range_time[Game_skill_level];
6293                 }
6294         }
6295         //nprintf(("AI", "time enemy in range = %7.3f\n", aip->time_enemy_in_range));
6296
6297         if (aip->time_enemy_in_range < range_time) {
6298                 float   dist;
6299
6300                 dist = vm_vec_dist_quick(&pobjp->pos, &eobjp->pos);
6301                 vm_vec_scale_add(predicted_enemy_pos, &eobjp->pos, &eobjp->phys_info.vel, aip->time_enemy_in_range * dist/weapon_speed);
6302         } else {
6303                 float   collision_time;
6304                 vector  gun_pos, pnt;
6305                 polymodel *po = model_get( Ship_info[shipp->ship_info_index].modelnum );
6306
6307                 //      Compute position of gun in absolute space and use that as fire position.
6308                 if(po->gun_banks != NULL){
6309                         pnt = po->gun_banks[0].pnt[0];
6310                 } else {
6311                         pnt = Objects[shipp->objnum].pos;
6312                 }
6313                 vm_vec_unrotate(&gun_pos, &pnt, &pobjp->orient);
6314                 vm_vec_add2(&gun_pos, &pobjp->pos);
6315
6316                 collision_time = compute_collision_time(&eobjp->pos, &eobjp->phys_info.vel, &gun_pos, weapon_speed);
6317
6318                 if (collision_time == 0.0f) {
6319                         collision_time = 100.0f;
6320                 }
6321
6322                 vm_vec_scale_add(predicted_enemy_pos, &eobjp->pos, &eobjp->phys_info.vel, collision_time);
6323
6324                 // set globals
6325                 G_collision_time = collision_time;
6326                 G_fire_pos = gun_pos;
6327         }
6328
6329         // Now add error terms (1) regular aim (2) EMP (3) stealth
6330         float scale = 0.0f;
6331         vector rand_vec;
6332
6333         // regular skill level error in aim
6334         if (aip->time_enemy_in_range > 2*range_time) {
6335                 scale = (1.0f - aip->ai_accuracy) * 4.0f;
6336         } else {
6337                 scale = (1.0f - aip->ai_accuracy) * 4.0f * (1.0f + 4.0f * (1.0f - aip->time_enemy_in_range/(2*range_time)));
6338         }
6339
6340         // if this ship is under the effect of an EMP blast, throw his aim off a bit
6341         if (shipp->emp_intensity > 0.0f) {
6342                 // never go lower than 1/2 of the EMP effect max, otherwise things aren't noticeable
6343                 scale += (MAX_EMP_INACCURACY * (shipp->emp_intensity < 0.5f ? 0.5f : shipp->emp_intensity));
6344                 mprintf(("AI miss scale factor (EMP) %f\n",scale));
6345         }
6346
6347         // if stealthy ship, throw his aim off, more when farther away and when dot is small
6348         if ( aip->ai_flags & AIF_STEALTH_PURSIUT ) {
6349                 float dist = vm_vec_dist_quick(&pobjp->pos, &eobjp->pos);
6350                 vector temp;
6351                 vm_vec_sub(&temp, &eobjp->pos, &pobjp->pos);
6352                 vm_vec_normalize_quick(&temp);
6353                 float dot = vm_vec_dotprod(&temp, &pobjp->orient.fvec);
6354                 float st_err = 3.0f * (1.4f - dot) * (1.0f + dist / (get_skill_stealth_dist_scaler() * STEALTH_MAX_VIEW_DIST)) * (1 - aip->ai_accuracy);
6355                 scale += st_err;
6356                 // mprintf(("error term: %.1f, total %.1f, dot %.3f\n", st_err, scale, dot));
6357         }
6358
6359         // get a random vector that changes slowly over time (1x / sec)
6360         static_randvec(((pobjp-Objects) ^ (Missiontime >> 16)) & 7, &rand_vec);
6361
6362         vm_vec_scale_add2(predicted_enemy_pos, &rand_vec, scale);
6363
6364         // set global
6365         G_predicted_pos = *predicted_enemy_pos;
6366 }
6367
6368 //      Handler of submode for Chase.  Go into a continuous turn for awhile.
6369 void ai_chase_ct()
6370 {
6371         vector          tvec;
6372         ship_info       *sip;
6373         ai_info         *aip;
6374
6375         Assert(Ships[Pl_objp->instance].ship_info_index >= 0);
6376         sip = &Ship_info[Ships[Pl_objp->instance].ship_info_index];
6377         Assert(Ships[Pl_objp->instance].ai_index >= 0);
6378         aip = &Ai_info[Ships[Pl_objp->instance].ai_index];
6379
6380         //      Make a continuous turn towards any combination of possibly negated
6381         // up and right vectors.
6382         tvec = Pl_objp->pos;
6383
6384         if (aip->submode_parm0 & 0x01)
6385                 vm_vec_add2(&tvec, &Pl_objp->orient.rvec);
6386         if (aip->submode_parm0 & 0x02)
6387                 vm_vec_sub2(&tvec, &Pl_objp->orient.rvec);
6388         if (aip->submode_parm0 & 0x04)
6389                 vm_vec_add2(&tvec, &Pl_objp->orient.uvec);
6390         if (aip->submode_parm0 & 0x08)
6391                 vm_vec_sub2(&tvec, &Pl_objp->orient.uvec);
6392
6393         //      Detect degenerate cases that cause tvec to be same as player pos.
6394         if (vm_vec_dist_quick(&tvec, &Pl_objp->pos) < 0.1f) {
6395                 aip->submode_parm0 &= 0x05;
6396                 if (aip->submode_parm0 == 0)
6397                         aip->submode_parm0 = 1;
6398                 vm_vec_add2(&tvec, &Pl_objp->orient.rvec);
6399         }
6400
6401         ai_turn_towards_vector(&tvec, Pl_objp, flFrametime, sip->srotation_time, NULL, NULL, 0.0f, 0);
6402         accelerate_ship(aip, 1.0f);
6403 }
6404
6405 //      ATTACK submode handler for chase mode.
6406 void ai_chase_eb(ai_info *aip, ship_info *sip, vector *predicted_enemy_pos, float dist_to_enemy)
6407 {
6408         vector  _pep;
6409         float           dot_to_enemy, dot_from_enemy;
6410
6411         compute_dots(Pl_objp, En_objp, &dot_to_enemy, &dot_from_enemy);
6412
6413         //      If we're trying to slow down to get behind, then point to turn towards is different.
6414         _pep = *predicted_enemy_pos;
6415         if ((dot_to_enemy > dot_from_enemy + 0.1f) || (dot_to_enemy > 0.9f))
6416                 vm_vec_scale_add(&_pep, &Pl_objp->pos, &En_objp->orient.fvec, 100.0f);
6417
6418         ai_turn_towards_vector(&_pep, Pl_objp, flFrametime, sip->srotation_time, NULL, NULL, 0.0f, 0);
6419
6420         accelerate_ship(aip, 0.0f);
6421 }
6422
6423 //      Return time until weapon_objp might hit ship_objp.
6424 //      Assumes ship_objp is not moving.
6425 //      Returns negative time if not going to hit.
6426 //      This is a very approximate function, but is pretty fast.
6427 float ai_endangered_time(object *ship_objp, object *weapon_objp)
6428 {
6429         float           to_dot, from_dot, dist;
6430
6431         dist = compute_dots(ship_objp, weapon_objp, &to_dot, &from_dot);
6432
6433         //      Note, this is bogus.  It assumes only the weapon is moving.
6434         //      Only proceed if weapon sort of pointing at object and object pointing towards or away from weapon
6435         //      (Ie, if object moving at right angle to weapon, just continue for now...)
6436         if (weapon_objp->phys_info.speed < 1.0f)
6437                 return dist + 1.0f;
6438         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))
6439                 return dist / weapon_objp->phys_info.speed;
6440         else
6441                 return -1.0f;
6442 }
6443
6444 //      Return time until danger weapon could hit this ai object.
6445 //      Return negative time if not endangered.
6446 float ai_endangered_by_weapon(ai_info *aip)
6447 {
6448         object  *weapon_objp;
6449
6450         if (aip->danger_weapon_objnum == -1) {
6451                 return -1.0f;
6452         }
6453
6454         weapon_objp = &Objects[aip->danger_weapon_objnum];
6455
6456         if (weapon_objp->signature != aip->danger_weapon_signature) {
6457                 aip->danger_weapon_objnum = -1;
6458                 return -1.0f;
6459         }
6460
6461         return ai_endangered_time(&Objects[Ships[aip->shipnum].objnum], weapon_objp);
6462 }
6463
6464 //      Return true if this ship is near full strength.
6465 int ai_near_full_strength(object *objp, ship_info *sip)
6466 {
6467         return (objp->hull_strength/sip->initial_hull_strength > 0.9f) || (get_shield_strength(objp)/sip->shields > 0.8f);
6468 }
6469                                 
6470 //      Set acceleration while in attack mode.
6471 void attack_set_accel(ai_info *aip, float dist_to_enemy, float dot_to_enemy, float dot_from_enemy)
6472 {
6473         float   speed_ratio;
6474
6475         if (En_objp->phys_info.speed > 1.0f)
6476                 speed_ratio = Pl_objp->phys_info.speed/En_objp->phys_info.speed;
6477         else
6478                 speed_ratio = 5.0f;
6479
6480         //      Sometimes, told to attack slowly.  Allows to get in more hits.
6481         if (aip->ai_flags & AIF_ATTACK_SLOWLY) {
6482                 if ((dist_to_enemy > 200.0f) && (dist_to_enemy < 800.0f)) {
6483                         if ((dot_from_enemy < 0.9f) || ai_near_full_strength(Pl_objp, &Ship_info[Ships[Pl_objp->instance].ship_info_index])) {
6484                                 //nprintf(("AI", " slowly "));
6485                                 accelerate_ship(aip, max(1.0f - (dist_to_enemy-200.0f)/600.0f, 0.1f));
6486                                 return;
6487                         }
6488                 } else
6489                         aip->ai_flags &= ~AIF_ATTACK_SLOWLY;
6490         }
6491
6492         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) {
6493                 //nprintf(("AI", "1"));
6494                 if (ai_maybe_fire_afterburner(Pl_objp, aip)) {
6495                         if (dist_to_enemy > 800.0f) {
6496                                 if (!( Pl_objp->phys_info.flags & PF_AFTERBURNER_ON )) {
6497                                         float percent_left;
6498                                         ship    *shipp;
6499                                         ship_info *sip;
6500
6501                                         shipp = &Ships[Pl_objp->instance];
6502                                         sip = &Ship_info[shipp->ship_info_index];
6503
6504                                         if (sip->afterburner_fuel_capacity > 0.0f) {
6505                                                 percent_left = 100.0f * shipp->afterburner_fuel / sip->afterburner_fuel_capacity;
6506                                                 if (percent_left > 30.0f + ((Pl_objp-Objects) & 0x0f)) {
6507                                                         afterburners_start(Pl_objp);
6508                                                         aip->afterburner_stop_time = Missiontime + F1_0 + static_rand(Pl_objp-Objects)/4;
6509                                                 }
6510                                         }
6511                                 }
6512                         }
6513                 }
6514
6515                 accelerate_ship(aip, 1.0f);
6516         } else if ((Missiontime - aip->last_hit_time > F1_0*7)
6517                 && (En_objp->phys_info.speed < 10.0f) 
6518                 && (dist_to_enemy > 25.0f) 
6519                 && (dot_to_enemy > 0.8f)
6520                 && (dot_from_enemy < 0.8f)) {
6521                 accelerate_ship(aip, 0.0f);             //      No one attacking us, so don't need to move.
6522         } else if ((dot_from_enemy < 0.25f) && (dot_to_enemy > 0.5f)) {
6523                 set_accel_for_target_speed(Pl_objp, En_objp->phys_info.speed);
6524         } else if (Pl_objp->phys_info.speed < 15.0f) {
6525                 accelerate_ship(aip, 1.0f);
6526         } else if (Pl_objp->phys_info.speed > En_objp->phys_info.speed - 1.0f) {
6527                 if (dot_from_enemy > 0.75f)
6528                         accelerate_ship(aip, 1.0f);
6529                 else
6530                         set_accel_for_target_speed(Pl_objp, En_objp->phys_info.speed*0.75f + 3.0f);
6531         } else {
6532                 change_acceleration(aip, 0.5f);
6533         }
6534 }
6535
6536 //      Pl_objp (aip) tries to get behind En_objp.
6537 //      New on 2/21/98: If this ship can move backwards and slide, maybe do that to get behind.
6538 void get_behind_ship(ai_info *aip, ship_info *sip, float dist_to_enemy)
6539 {
6540         vector  new_pos;
6541         float           dot;
6542         vector  vec_from_enemy;
6543         float           dist;
6544
6545         dist = vm_vec_normalized_dir(&vec_from_enemy, &Pl_objp->pos, &En_objp->pos);
6546
6547         vm_vec_scale_add(&new_pos, &En_objp->pos, &En_objp->orient.fvec, -100.0f);              //      Pick point 100 units behind.
6548         ai_turn_towards_vector(&new_pos, Pl_objp, flFrametime, sip->srotation_time, NULL, NULL, 0.0f, 0);
6549
6550         dot = vm_vec_dot(&vec_from_enemy, &En_objp->orient.fvec);
6551
6552         if (dot > 0.25f) {
6553                 accelerate_ship(aip, 1.0f);
6554         } else {
6555                 accelerate_ship(aip, (dot + 1.0f)/2.0f);
6556         }
6557 }
6558
6559 int avoid_player(object *objp, vector *goal_pos)
6560 {
6561         maybe_avoid_player(Pl_objp, goal_pos);
6562         ai_info *aip = &Ai_info[Ships[objp->instance].ai_index];
6563
6564         if (aip->ai_flags & AIF_AVOIDING_SMALL_SHIP) {
6565                 ship_info *sip = &Ship_info[Ships[objp->instance].ship_info_index];
6566
6567                 if (aip->ai_flags & AIF_AVOIDING_SMALL_SHIP) {
6568                         ai_turn_towards_vector(&aip->avoid_goal_point, objp, flFrametime, sip->srotation_time, NULL, NULL, 0.0f, 0);
6569                         accelerate_ship(aip, 0.5f);
6570                         return 1;
6571                 }
6572         }
6573
6574         return 0;
6575 }
6576
6577 //      Determine if a cylinder of width radius from p0 to p1 will collide with big_objp.
6578 //      If so, stuff *collision_point.
6579 int will_collide_pp(vector *p0, vector *p1, float radius, object *big_objp, vector *collision_point)
6580 {
6581         mc_info mc;
6582
6583         mc.model_num = Ships[big_objp->instance].modelnum;              // Fill in the model to check
6584         mc.orient = &big_objp->orient;                  // The object's orient
6585         mc.pos = &big_objp->pos;                                        // The object's position
6586         mc.p0 = p0;                                                                             // Point 1 of ray to check
6587         mc.p1 = p1;
6588         mc.flags = MC_CHECK_MODEL | MC_CHECK_SPHERELINE | MC_SUBMODEL;                                  // flags
6589
6590         mc.radius = radius;
6591
6592         // Only check the 2nd lowest hull object
6593         polymodel *pm = model_get(Ships[big_objp->instance].modelnum);
6594         mc.submodel_num = pm->detail[0]; //pm->submodel->num_details-2];
6595         model_collide(&mc);
6596
6597         if (mc.num_hits)
6598                 *collision_point = mc.hit_point_world;
6599
6600         return mc.num_hits;
6601 }
6602
6603 //      Return true/false if *objp will collide with *big_objp
6604 //      Stuff distance in *distance to collision point if *objp will collide with *big_objp within delta_time seconds.
6605 //      Global collision point stuffed in *collision_point
6606 int will_collide_with_big_ship(object *objp, vector *goal_point, object *big_objp, vector *collision_point, float delta_time)
6607 {
6608         float           radius;
6609         vector  end_pos;
6610
6611         radius = big_objp->radius + delta_time * objp->phys_info.speed;
6612
6613         if (vm_vec_dist_quick(&big_objp->pos, &objp->pos) > radius) {
6614                 return 0;
6615         }
6616
6617         if (goal_point == NULL) {
6618                 vm_vec_scale_add(&end_pos, &objp->pos, &objp->phys_info.vel, delta_time);                                       // Point 2 of ray to check
6619         } else {
6620                 end_pos = *goal_point;
6621         }
6622
6623         return will_collide_pp(&objp->pos, &end_pos, objp->radius, big_objp, collision_point);
6624 }
6625
6626 //      Return true if *objp is expected to collide with a large ship.
6627 //      Stuff global collision point in *collision_point.
6628 //      If *goal_point is not NULL, use that as the point towards which *objp will be flying.  Don't use *objp velocity
6629 //      *ignore_objp will typically be the target this ship is pursuing, either to attack or guard.  We don't want to avoid it.
6630 int will_collide_with_big_ship_all(object *objp, object *ignore_objp, vector *goal_point, vector *collision_point, float *distance, float delta_time)
6631 {
6632         ship_obj        *so;
6633         object  *big_objp;
6634         int             collision_obj_index = -1;
6635         float           min_dist = 999999.9f;
6636         float           collision_time = -1.0f;
6637
6638         for ( so = GET_FIRST(&Ship_obj_list); so != END_OF_LIST(&Ship_obj_list); so = GET_NEXT(so) ) {
6639                 float   time = 0.0f;
6640                 big_objp = &Objects[so->objnum];
6641
6642                 if (big_objp == ignore_objp)
6643                         continue;
6644
6645                 if (Ship_info[Ships[big_objp->instance].ship_info_index].flags & (SIF_BIG_SHIP | SIF_HUGE_SHIP)) {
6646                         vector  cur_collision_point;
6647                         float           cur_dist;
6648
6649                         if (will_collide_with_big_ship(objp, goal_point, big_objp, &cur_collision_point, delta_time)) {
6650
6651                                 cur_dist = vm_vec_dist(&cur_collision_point, &objp->pos);
6652
6653                                 if (cur_dist < min_dist) {
6654                                         min_dist = cur_dist;
6655                                         *collision_point = cur_collision_point;
6656                                         collision_time = time;
6657                                         collision_obj_index = OBJ_INDEX(big_objp);
6658                                 }
6659                         }
6660                 }
6661         }
6662
6663         *distance = min_dist;
6664         return collision_obj_index;
6665
6666 }
6667
6668 typedef struct {
6669         float           dist;
6670         int             collide;
6671         vector  pos;
6672 } sgoal;
6673
6674 //int will_collide_pp(vector *p0, vector *p1, float radius, object *big_objp, vector *collision_point)
6675 //      Pick a point for *objp to fly towards to avoid a collision with *big_objp at *collision_point
6676 //      Return result in *avoid_pos
6677 void mabs_pick_goal_point(object *objp, object *big_objp, vector *collision_point, vector *avoid_pos)
6678 {
6679         matrix  mat1;
6680         sgoal           goals[4];
6681         vector  v2b;
6682
6683         vm_vec_normalized_dir(&v2b, collision_point, &objp->pos);
6684         vm_vector_2_matrix(&mat1, &v2b, NULL, NULL);
6685
6686         int     found = 0;
6687
6688         //      Try various scales, in 0.5f, 0.75f, 1.0f, 1.25f.
6689         //      First try 0.5f to see if we can find a point that near the center of the target ship, which presumably
6690         //      means less of a turn.
6691         //      Try going as far as 1.25f * radius.
6692         float   s;
6693         for (s=0.5f; s<1.3f; s += 0.25f) {
6694                 int     i;
6695                 for (i=0; i<4; i++) {
6696                         vector p = big_objp->pos;
6697                         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
6698                         float kr = big_objp->radius*s + objp->radius * ((OBJ_INDEX(objp) % 4) ^ 2)/4;
6699                         if (i&1)
6700                                 ku = -ku;
6701                         if (i&2)
6702                                 kr = -kr;
6703                         vm_vec_scale_add2(&p, &mat1.uvec, ku);
6704                         vm_vec_scale_add2(&p, &mat1.rvec, kr);
6705                         goals[i].pos = p;
6706                         goals[i].dist = vm_vec_dist_quick(&objp->pos, &p);
6707                         goals[i].collide = will_collide_pp(&objp->pos, &p, objp->radius, big_objp, collision_point);
6708                         if (!goals[i].collide)
6709                                 found = 1;
6710                 }
6711
6712                 //      If we found a point that doesn't collide, find the nearest one and make that the *avoid_pos.
6713                 if (found) {
6714                         float   min_dist = 9999999.9f;
6715                         int     min_index = -1;
6716
6717                         for (i=0; i<4; i++) {
6718                                 if (!goals[i].collide && (goals[i].dist < min_dist)) {
6719                                         min_dist = goals[i].dist;
6720                                         min_index = i;
6721                                 }
6722                         }
6723
6724                         Assert(i != -1);
6725                         if (i != -1) {
6726                                 *avoid_pos = goals[min_index].pos;
6727                                 return;
6728                         }
6729                 }
6730         }
6731
6732         //      Drat.  We tried and tried and could not find a point that did not cause a collision.
6733         //      Get this dump pilot far away from the problem ship.
6734         vector  away_vec;
6735         vm_vec_normalized_dir(&away_vec, &objp->pos, collision_point);
6736         vm_vec_scale_add(avoid_pos, &objp->pos, &away_vec, big_objp->radius*1.5f);
6737
6738 }
6739
6740 //      Return true if a large ship is being ignored.
6741 int maybe_avoid_big_ship(object *objp, object *ignore_objp, ai_info *aip, vector *goal_point, float delta_time)
6742 {
6743         if (timestamp_elapsed(aip->avoid_check_timestamp)) {
6744                 float           distance;
6745                 vector  collision_point;
6746                 int             ship_num;
6747                 if ((ship_num = will_collide_with_big_ship_all(Pl_objp, ignore_objp, goal_point, &collision_point, &distance, delta_time)) != -1) {
6748                         aip->ai_flags |= AIF_AVOIDING_BIG_SHIP;
6749                         mabs_pick_goal_point(objp, &Objects[ship_num], &collision_point, &aip->avoid_goal_point);
6750                         float dist = vm_vec_dist_quick(&aip->avoid_goal_point, &objp->pos);
6751                         aip->avoid_check_timestamp = timestamp(2000 + min(1000, (int) (dist * 2.0f)));  //      Delay until check again is based on distance to avoid point.
6752                         aip->avoid_ship_num = ship_num;
6753                 } else {
6754                         aip->ai_flags &= ~AIF_AVOIDING_BIG_SHIP;
6755                         aip->ai_flags &= ~AIF_AVOIDING_SMALL_SHIP;
6756                         aip->avoid_ship_num = -1;
6757                         aip->avoid_check_timestamp = timestamp(1500);
6758                 }
6759         }
6760         
6761         if (aip->ai_flags & AIF_AVOIDING_BIG_SHIP) {
6762                 ship_info *sip = &Ship_info[Ships[Pl_objp->instance].ship_info_index];
6763
6764                 vector  v2g;
6765
6766                 ai_turn_towards_vector(&aip->avoid_goal_point, Pl_objp, flFrametime, sip->srotation_time, NULL, NULL, 0.0f, 0);
6767                 vm_vec_normalized_dir(&v2g, &aip->avoid_goal_point, &Pl_objp->pos);
6768                 float dot = vm_vec_dot(&objp->orient.fvec, &v2g);
6769                 float d2 = (1.0f + dot) * (1.0f + dot);
6770                 accelerate_ship(aip, d2/4.0f);
6771                 return 1;
6772         }
6773
6774         return 0;
6775 }
6776
6777 //      Set desired right vector for ships flying towards another ship.
6778 //      Since this is governed only by vector to target, it causes ships to align bank and look less chaotic.
6779 void compute_desired_rvec(vector *rvec, vector *goal_pos, vector *cur_pos)
6780 {
6781         vector  v2e;
6782
6783         vm_vec_normalized_dir(&v2e, goal_pos, cur_pos);
6784         rvec->x = v2e.z;
6785         rvec->y = 0.0f;
6786         rvec->z = -v2e.x;
6787         if (vm_vec_mag_squared(rvec) < 0.001f)
6788                 rvec->y = 1.0f;
6789 }
6790
6791 // Handler for stealth find submode of Chase.
6792 void ai_stealth_find()
6793 {
6794         ai_info         *aip;
6795         ship_info       *sip;
6796
6797         vector new_pos, vec_to_enemy;
6798         float dist_to_enemy, dot_to_enemy, dot_from_enemy;
6799
6800         Assert(Ships[Pl_objp->instance].ship_info_index >= 0);
6801         sip = &Ship_info[Ships[Pl_objp->instance].ship_info_index];
6802         Assert(Ships[Pl_objp->instance].ai_index >= 0);
6803         aip = &Ai_info[Ships[Pl_objp->instance].ai_index];
6804
6805         // get time since last seen
6806         int delta_time = (timestamp() - aip->stealth_last_visible_stamp);
6807
6808         // if delta_time is really big, i'm real confused, start sweep
6809         if (delta_time > 10000) {
6810                 aip->submode_parm0 = SM_SF_BAIL;
6811         }
6812
6813         // guestimate new position
6814         vm_vec_scale_add(&new_pos, &aip->stealth_last_pos, &aip->stealth_velocity, (delta_time * 0.001f));
6815
6816         // if I think he's behind me, go to the goal point
6817         if ( aip->submode_parm0 == SM_SF_BEHIND ) {
6818                 new_pos = aip->goal_point;
6819         }
6820
6821         // check for collision with big ships
6822         if (maybe_avoid_big_ship(Pl_objp, En_objp, aip, &new_pos, 10.0f)) {
6823                 // reset ai submode to chase
6824                 return;
6825         }
6826
6827         // if dist is near max and dot is close to 1, accel, afterburn
6828         vm_vec_sub(&vec_to_enemy, &new_pos, &Pl_objp->pos);
6829         dist_to_enemy = vm_vec_normalize_quick(&vec_to_enemy);
6830         dot_to_enemy = vm_vec_dotprod(&vec_to_enemy, &Pl_objp->orient.fvec);
6831
6832         // 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
6833         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) ) {
6834                 // do turn around)
6835                 vm_vec_scale_add(&aip->goal_point, &Pl_objp->pos, &Pl_objp->orient.fvec, -300.0f);
6836                 aip->submode_parm0 = SM_SF_BEHIND;
6837                 vm_vec_sub(&vec_to_enemy, &new_pos, &Pl_objp->pos);
6838                 dist_to_enemy = vm_vec_normalize_quick(&vec_to_enemy);
6839                 dot_to_enemy = vm_vec_dotprod(&vec_to_enemy, &Pl_objp->orient.fvec);
6840         }
6841
6842         if ( (dist_to_enemy > get_skill_stealth_dist_scaler()*STEALTH_MAX_VIEW_DIST) && (dot_to_enemy > 0.94f) ) {              // 20 degree half angle
6843                 // accelerate ship
6844                 accelerate_ship(aip, 1.0f);
6845
6846                 // engage afterburner
6847                 if (!( Pl_objp->phys_info.flags & PF_AFTERBURNER_ON )) {
6848                         if (ai_maybe_fire_afterburner(Pl_objp, aip)) {
6849                                 afterburners_start(Pl_objp);
6850                                 aip->afterburner_stop_time = Missiontime + 3*F1_0/2;
6851                         }
6852                 }
6853
6854                 ai_turn_towards_vector(&new_pos, Pl_objp, flFrametime, sip->srotation_time, NULL, NULL, 0.0f, 0);
6855                 return;
6856         }
6857
6858         //      If enemy more than 500 meters away, all ships flying there will tend to match bank.
6859         //      They do this by using their vector to their target to compute their right vector and causing ai_turn_towards_vector
6860         //      to interpolate a matrix rather than just a vector.
6861         if (dist_to_enemy > 500.0f) {
6862                 vector  rvec;
6863                 compute_desired_rvec(&rvec, &new_pos, &Pl_objp->pos);
6864                 ai_turn_towards_vector(&new_pos, Pl_objp, flFrametime, sip->srotation_time, NULL, NULL, 0.0f, 0, &rvec);
6865         } else {
6866                 ai_turn_towards_vector(&new_pos, Pl_objp, flFrametime, sip->srotation_time, NULL, NULL, 0.0f, 0);
6867         }
6868
6869         dot_from_enemy = -vm_vec_dotprod(&vec_to_enemy, &En_objp->orient.fvec);
6870
6871         attack_set_accel(aip, dist_to_enemy, dot_to_enemy, dot_from_enemy);
6872 }
6873
6874 // -----------------------------------------------------------------------------
6875 // try to find stealth ship by sweeping an area
6876 void ai_stealth_sweep()
6877 {
6878         ai_info         *aip;
6879         ship_info       *sip;
6880
6881         Assert(Ships[Pl_objp->instance].ship_info_index >= 0);
6882         sip = &Ship_info[Ships[Pl_objp->instance].ship_info_index];
6883         Assert(Ships[Pl_objp->instance].ai_index >= 0);
6884         aip = &Ai_info[Ships[Pl_objp->instance].ai_index];
6885
6886         vector goal_pt;
6887         vector forward, right, up;
6888         int lost_time;
6889
6890         // time since stealth last seen
6891         lost_time = (timestamp() - aip->stealth_last_visible_stamp);
6892
6893         // determine which pt to fly to in sweep by keeping track of parm0
6894         if (aip->submode_parm0 == SM_SS_SET_GOAL) {
6895
6896                 // don't make goal pt more than 2k from current pos
6897                 vm_vec_scale_add(&goal_pt, &aip->stealth_last_pos, &aip->stealth_velocity, (0.001f * lost_time));
6898
6899                 // make box size based on speed of stealth and expected time to intercept (keep box in range 200-500)
6900                 float box_size = vm_vec_mag_quick(&aip->stealth_velocity) * (0.001f * lost_time);
6901                 box_size = min(200.0f, box_size);
6902                 box_size = max(500.0f, box_size);
6903                 aip->stealth_sweep_box_size = box_size;
6904
6905                 aip->goal_point = goal_pt;
6906                 aip->submode_parm0 = SM_SS_BOX0;
6907         }
6908
6909         // GET UP, RIGHT, FORWARD FOR BOX based on stealth ship's velocity
6910         // if velocity changes in stealth mode, then ship is *seen*, and falls out of sweep mode
6911         // if stealth has no velocity make a velocity
6912         if ( vm_vec_mag_quick(&aip->stealth_velocity) < 1 ) {
6913                 vm_vec_rand_vec_quick(&aip->stealth_velocity);
6914         }
6915
6916         // get "right" vector for box
6917         vm_vec_crossprod(&right, &aip->stealth_velocity, &vmd_y_vector);
6918
6919         if ( vm_vec_mag_quick(&right) < 0.01 ) {
6920                 vm_vec_crossprod(&right, &aip->stealth_velocity, &vmd_z_vector);
6921         }
6922
6923         vm_vec_normalize_quick(&right);
6924
6925         // get forward for box
6926         vm_vec_copy_normalize_quick(&forward, &aip->stealth_velocity);
6927
6928         // get "up" for box
6929         vm_vec_crossprod(&up, &forward, &right);
6930         
6931         // lost far away ahead (do box)
6932         switch(aip->submode_parm0) {
6933         case SM_SS_BOX0:
6934                 goal_pt = aip->goal_point;
6935                 break;
6936
6937         // pt1 -U +R
6938         case SM_SS_LR:
6939                 vm_vec_scale_add(&goal_pt, &aip->goal_point, &up, -aip->stealth_sweep_box_size);
6940                 vm_vec_scale_add2(&goal_pt, &right, aip->stealth_sweep_box_size);
6941                 vm_vec_scale_add2(&goal_pt, &forward, 0.5f*aip->stealth_sweep_box_size);
6942                 break;
6943
6944         // pt2 +U -R
6945         case SM_SS_UL:
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         // pt3 back
6952         case SM_SS_BOX1:
6953                 goal_pt = aip->goal_point;
6954                 break;
6955
6956         // pt4 +U +R
6957         case SM_SS_UR:
6958                 vm_vec_scale_add(&goal_pt, &aip->goal_point, &up, aip->stealth_sweep_box_size);
6959                 vm_vec_scale_add2(&goal_pt, &right, aip->stealth_sweep_box_size);
6960                 vm_vec_scale_add2(&goal_pt, &forward, 0.5f*aip->stealth_sweep_box_size);
6961                 break;
6962
6963         // pt5 -U -R
6964         case SM_SS_LL:
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         // pt6 back
6971         case SM_SS_BOX2:
6972                 goal_pt = aip->goal_point;
6973                 break;
6974
6975         default:
6976                 Int3();
6977
6978         }
6979
6980         // when close to goal_pt, update next goal pt
6981         float dist_to_goal = vm_vec_dist(&goal_pt, &Pl_objp->pos);
6982         if (dist_to_goal < 15) {
6983                 aip->submode_parm0++;
6984         }
6985
6986         // check for collision with big ship
6987         if (maybe_avoid_big_ship(Pl_objp, En_objp, aip, &goal_pt, 10.0f)) {
6988                 // skip to the next pt on box
6989                 aip->submode_parm0++;
6990                 return;
6991         }
6992
6993         ai_turn_towards_vector(&goal_pt, Pl_objp, flFrametime, sip->srotation_time, NULL, NULL, 0.0f, 0);
6994
6995         float dot = 1.0f;
6996         if (dist_to_goal < 100) {
6997                 vector vec_to_goal;
6998                 vm_vec_normalized_dir(&vec_to_goal, &goal_pt, &Pl_objp->pos);
6999                 dot = vm_vec_dotprod(&vec_to_goal, &Pl_objp->orient.fvec);
7000         }
7001
7002         accelerate_ship(aip, 0.8f*dot);
7003 }
7004
7005 //      ATTACK submode handler for chase mode.
7006 void ai_chase_attack(ai_info *aip, ship_info *sip, vector *predicted_enemy_pos, float dist_to_enemy)
7007 {
7008         int             start_bank;
7009         float           dot_to_enemy, dot_from_enemy; //, time_to_hit;
7010         float           bank_override = 0.0f;
7011
7012         if (avoid_player(Pl_objp, predicted_enemy_pos))
7013                 return;
7014
7015         compute_dots(Pl_objp, En_objp, &dot_to_enemy, &dot_from_enemy);
7016
7017         polymodel *po = model_get( sip->modelnum );
7018
7019         vector  *rel_pos;
7020         float           scale;
7021         vector  randvec;
7022         vector  new_pos;
7023
7024         start_bank = Ships[aip->shipnum].weapons.current_primary_bank;
7025         if (po->n_guns && start_bank != -1 ) {
7026                 rel_pos = &po->gun_banks[start_bank].pnt[0];
7027         } else
7028                 rel_pos = NULL;
7029
7030         //      If ship moving slowly relative to its size, then don't attack its center point.
7031         //      How far from center we attack is based on speed, size and distance to enemy
7032         if (En_objp->radius > En_objp->phys_info.speed) {
7033                 static_randvec(Pl_objp-Objects, &randvec);
7034                 scale = dist_to_enemy/(dist_to_enemy + En_objp->radius) * En_objp->radius;
7035                 scale *= 0.5f * En_objp->radius/(En_objp->phys_info.speed + En_objp->radius);   // scale downward by 1/2 to 1/4
7036                 vm_vec_scale_add(&new_pos, predicted_enemy_pos, &randvec, scale);
7037         } else
7038                 new_pos = *predicted_enemy_pos;
7039
7040         if (dist_to_enemy < 250.0f) {
7041                 if (dot_from_enemy > 0.7f) {
7042                         bank_override = Pl_objp->phys_info.speed;
7043                 }
7044         }
7045
7046         //      If enemy more than 500 meters away, all ships flying there will tend to match bank.
7047         //      They do this by using their vector to their target to compute their right vector and causing ai_turn_towards_vector
7048         //      to interpolate a matrix rather than just a vector.
7049         if (dist_to_enemy > 500.0f) {
7050                 vector  rvec;
7051                 compute_desired_rvec(&rvec, predicted_enemy_pos, &Pl_objp->pos);
7052                 ai_turn_towards_vector(&new_pos, Pl_objp, flFrametime, sip->srotation_time, NULL, rel_pos, bank_override, 0, &rvec);
7053         } else {
7054                 ai_turn_towards_vector(&new_pos, Pl_objp, flFrametime, sip->srotation_time, NULL, rel_pos, bank_override, 0);
7055         }
7056
7057         attack_set_accel(aip, dist_to_enemy, dot_to_enemy, dot_from_enemy);
7058 }
7059
7060 //      EVADE_SQUIGGLE submode handler for chase mode.
7061 //      Changed by MK on 5/5/97.
7062 //      Used to evade towards a point off the right or up vector.
7063 //      Now, evade straight away to try to get far away.
7064 //      The squiggling should protect against laser fire.
7065 void ai_chase_es(ai_info *aip, ship_info *sip)
7066 {
7067         vector  tvec;
7068         fix             timeslice;
7069         fix             scale;
7070         float           bank_override = 0.0f;
7071
7072         tvec = Pl_objp->pos;
7073
7074         timeslice = (Missiontime >> 16) & 0x0f;
7075         scale = ((Missiontime >> 16) & 0x0f) << 14;
7076
7077         if (timeslice & 0x01)
7078                 vm_vec_scale_add2(&tvec, &Pl_objp->orient.rvec, f2fl(scale ^ 0x10000));
7079         if (timeslice & 0x02)
7080                 vm_vec_scale_sub2(&tvec, &Pl_objp->orient.rvec, f2fl(scale));
7081         if (timeslice & 0x04)
7082                 vm_vec_scale_add2(&tvec, &Pl_objp->orient.uvec, f2fl(scale ^ 0x10000));
7083         if (timeslice & 0x08)
7084                 vm_vec_scale_sub2(&tvec, &Pl_objp->orient.uvec, f2fl(scale));
7085
7086         while (vm_vec_dist_quick(&tvec, &Pl_objp->pos) < 0.1f) {
7087                 tvec.x += frand();
7088                 tvec.y += frand();
7089         }
7090
7091         bank_override = Pl_objp->phys_info.speed;
7092
7093         ai_turn_towards_vector(&tvec, Pl_objp, flFrametime/2, sip->srotation_time, NULL, NULL, bank_override, 0);
7094         accelerate_ship(aip, 1.0f);
7095 }
7096
7097 //      Trying to get away from opponent.
7098 void ai_chase_ga(ai_info *aip, ship_info *sip)
7099 {
7100         //      If not near end of this submode, evade squiggly.  If near end, just fly straight for a bit
7101         vector  tvec;
7102         float           bank_override;
7103         vector  vec_from_enemy;
7104
7105         if (En_objp != NULL) {
7106                 vm_vec_normalized_dir(&vec_from_enemy, &Pl_objp->pos, &En_objp->pos);
7107         } else
7108                 vec_from_enemy = Pl_objp->orient.fvec;
7109
7110         static_randvec(Missiontime >> 15, &tvec);
7111         vm_vec_scale(&tvec, 100.0f);
7112         vm_vec_scale_add2(&tvec, &vec_from_enemy, 300.0f);
7113         vm_vec_add2(&tvec, &Pl_objp->pos);
7114
7115         bank_override = Pl_objp->phys_info.speed;
7116
7117         ai_turn_towards_vector(&tvec, Pl_objp, flFrametime/2, sip->srotation_time, NULL, NULL, bank_override, 0);
7118
7119         accelerate_ship(aip, 2.0f);
7120
7121         if (ai_maybe_fire_afterburner(Pl_objp, aip)) {
7122                 if (!(Pl_objp->phys_info.flags & PF_AFTERBURNER_ON )) {
7123                         float percent_left = 100.0f * Ships[Pl_objp->instance].afterburner_fuel / sip->afterburner_fuel_capacity;
7124                         if (percent_left > 30.0f + ((Pl_objp-Objects) & 0x0f)) {
7125                                 afterburners_start(Pl_objp);
7126                                 aip->afterburner_stop_time = Missiontime + 3*F1_0/2;
7127                         }
7128                         afterburners_start(Pl_objp);
7129                         aip->afterburner_stop_time = Missiontime + 3*F1_0/2;
7130                 }
7131         }
7132
7133 }
7134
7135 //      Make object *objp attack subsystem with ID = subnum.
7136 //      Return true if found a subsystem to attack, else return false.
7137 //      Note, can fail if subsystem exists, but has no hits.
7138 int ai_set_attack_subsystem(object *objp, int subnum)
7139 {
7140         ship                    *shipp, *attacker_shipp;
7141         ai_info         *aip;
7142         ship_subsys     *ssp;
7143         object          *attacked_objp;
7144
7145         Assert(objp->type == OBJ_SHIP);
7146         Assert(objp->instance >= 0);
7147
7148         attacker_shipp = &Ships[objp->instance];
7149         Assert(attacker_shipp->ai_index >= 0);
7150
7151         aip = &Ai_info[attacker_shipp->ai_index];
7152
7153         // MWA -- 2/27/98.  Due to AL's changes, target_objnum is now not always valid (at least sometimes
7154         // in terms of goals).  So, bail if we don't have a valid target.
7155         if ( aip->target_objnum == -1 )
7156                 return 0;
7157
7158         attacked_objp = &Objects[aip->target_objnum];
7159         shipp = &Ships[attacked_objp->instance];                //  need to get our target's ship pointer!!!
7160
7161         ssp = ship_get_indexed_subsys(shipp, subnum, &objp->pos);
7162         if (ssp == NULL)
7163                 return 0;
7164
7165         set_targeted_subsys(aip, ssp, aip->target_objnum);
7166         
7167         if (aip->ignore_objnum == aip->target_objnum)
7168                 aip->ignore_objnum = UNUSED_OBJNUM;
7169
7170         // -- Done at caller in ai_process_mission_orders -- attacked_objp->flags |= OF_PROTECTED;
7171
7172         ai_set_goal_maybe_abort_dock(objp, aip);
7173         aip->ok_to_target_timestamp = timestamp(DELAY_TARGET_TIME);
7174
7175         return 1;
7176 }
7177
7178 void ai_set_guard_vec(object *objp, object *guard_objp)
7179 {
7180         ai_info *aip;
7181         float   radius;
7182
7183         aip = &Ai_info[Ships[objp->instance].ai_index];
7184
7185         //      Handle case of bogus call in which ship is told to guard self.
7186         Assert(objp != guard_objp);
7187         if (objp == guard_objp) {
7188                 vm_vec_rand_vec_quick(&aip->guard_vec);
7189                 vm_vec_scale(&aip->guard_vec, 100.0f);
7190                 return;
7191         }
7192
7193         // check if guard_objp is BIG
7194         radius = 5.0f * (objp->radius + guard_objp->radius) + 50.0f;
7195         if (radius > 300.0f) {
7196                 radius = guard_objp->radius * 1.25f;
7197         }
7198
7199         vm_vec_sub(&aip->guard_vec, &objp->pos, &guard_objp->pos);
7200
7201         if (vm_vec_mag(&aip->guard_vec) > 3.0f*radius) {
7202                 //      Far away, don't just use vector to object, causes clustering of guard ships.
7203                 vector  tvec, rvec;
7204                 float   mag;
7205                 mag = vm_vec_copy_normalize(&tvec, &aip->guard_vec);
7206                 vm_vec_rand_vec_quick(&rvec);                   
7207                 vm_vec_scale_add2(&tvec, &rvec, 0.5f);
7208                 vm_vec_copy_scale(&aip->guard_vec, &tvec, mag);
7209         }
7210
7211         vm_vec_normalize_quick(&aip->guard_vec);
7212         vm_vec_scale(&aip->guard_vec, radius);
7213 }
7214
7215 //      Make object *objp guard object *other_objp.
7216 //      To be called from the goals code.
7217 void ai_set_guard_wing(object *objp, int wingnum)
7218 {
7219         ship            *shipp;
7220         ai_info *aip;
7221         int             leader_objnum, leader_shipnum;
7222
7223         Assert(wingnum >= 0);
7224
7225         Assert(objp->type == OBJ_SHIP);
7226         Assert(objp->instance >= 0);
7227
7228         // shouldn't set the ai mode for the player
7229         if ( objp == Player_obj ) {
7230                 return;
7231         }
7232
7233         shipp = &Ships[objp->instance];
7234
7235         Assert(shipp->ai_index >= 0);
7236
7237         aip = &Ai_info[shipp->ai_index];
7238         force_avoid_player_check(objp, aip);
7239
7240         ai_set_goal_maybe_abort_dock(objp, aip);
7241         aip->ok_to_target_timestamp = timestamp(DELAY_TARGET_TIME);
7242
7243         //      This function is called whenever a guarded ship is destroyed, so this code
7244         //      prevents a ship from trying to guard a non-existent wing.
7245         if (Wings[wingnum].current_count < 1) {
7246                 aip->guard_objnum = -1;
7247                 aip->guard_wingnum = -1;
7248                 aip->mode = AIM_NONE;
7249         } else {
7250                 leader_shipnum = Wings[wingnum].ship_index[0];
7251                 leader_objnum = Ships[leader_shipnum].objnum;
7252
7253                 Assert((leader_objnum >= 0) && (leader_objnum < MAX_OBJECTS));
7254                 //Assert(leader_objnum != objp-Objects);        //      Don't allow ships to guard themselves.
7255                 if (leader_objnum == OBJ_INDEX(objp)) {
7256                         //Int3();       //      Seems illegal, but let's clean up.  Get MikeK.
7257                         return;
7258                 }
7259
7260                 aip->guard_wingnum = wingnum;
7261                 aip->guard_objnum = leader_objnum;
7262                 aip->guard_signature = Objects[leader_objnum].signature;
7263                 aip->mode = AIM_GUARD;
7264                 aip->submode = AIS_GUARD_STATIC;
7265
7266                 ai_set_guard_vec(objp, &Objects[leader_objnum]);
7267         }
7268 }
7269
7270 //      Make object *objp guard object *other_objp.
7271 //      To be called from the goals code.
7272 void ai_set_evade_object(object *objp, object *other_objp)
7273 {
7274         ship            *shipp;
7275         ai_info *aip;
7276         int             other_objnum;
7277
7278         Assert(objp->type == OBJ_SHIP);
7279         Assert(objp->instance >= 0);
7280
7281         shipp = &Ships[objp->instance];
7282
7283         Assert(shipp->ai_index >= 0);
7284
7285         aip = &Ai_info[shipp->ai_index];
7286
7287         other_objnum = OBJ_INDEX(other_objp);
7288         Assert(other_objnum >= 0);
7289
7290         Assert(other_objnum != Ships[aip->shipnum].objnum);     //      make sure not targeting self
7291         aip->target_objnum = other_objnum;
7292
7293         aip->mode = AIM_EVADE;
7294 }
7295
7296 //      Make objp guard other_objp
7297 //      If other_objp is a member of a wing, objp will guard that whole wing
7298 //      UNLESS objp is also a member of the wing!
7299 void ai_set_guard_object(object *objp, object *other_objp)
7300 {
7301         ship            *shipp;
7302         ai_info *aip;
7303         int             other_objnum;
7304
7305         Assert(objp->type == OBJ_SHIP);
7306         Assert(objp->instance >= 0);
7307         Assert(objp != other_objp);
7308
7309         shipp = &Ships[objp->instance];
7310
7311         Assert(shipp->ai_index >= 0);
7312
7313         aip = &Ai_info[shipp->ai_index];
7314         aip->avoid_check_timestamp = timestamp(1);
7315
7316         //      If ship to guard is in a wing, guard that whole wing.
7317         ai_info *other_aip = &Ai_info[Ships[other_objp->instance].ai_index];
7318         if ((other_aip->wing != -1) && (other_aip->wing != aip->wing)) {
7319                 ai_set_guard_wing(objp, Ai_info[Ships[other_objp->instance].ai_index].wing);
7320         } else {
7321
7322                 other_objnum = other_objp-Objects;
7323
7324                 aip->guard_objnum = other_objnum;
7325                 aip->guard_signature = other_objp->signature;
7326                 aip->guard_wingnum = -1;
7327
7328                 aip->mode = AIM_GUARD;
7329                 aip->submode = AIS_GUARD_STATIC;
7330
7331                 Assert(other_objnum >= 0);      //      Hmm, bogus object and we need its position for guard_vec.
7332
7333                 // vm_vec_sub(&aip->guard_vec, &objp->pos, &Objects[other_objnum].pos);
7334                 ai_set_guard_vec(objp, &Objects[other_objnum]);
7335
7336                 ai_set_goal_maybe_abort_dock(objp, aip);
7337                 aip->ok_to_target_timestamp = timestamp(DELAY_TARGET_TIME);
7338         }
7339 }
7340
7341 //      Update the aspect_locked_time field based on whether enemy is in view cone.
7342 //      Also set/clear AIF_SEEK_LOCK.
7343 void update_aspect_lock_information(ai_info *aip, vector *vec_to_enemy, float dist_to_enemy, float enemy_radius)
7344 {
7345         float   dot_to_enemy;
7346         int     num_weapon_types;
7347         int     weapon_id_list[MAX_WEAPON_TYPES], weapon_bank_list[MAX_WEAPON_TYPES];
7348         ship    *shipp;
7349         ship_weapon     *swp;
7350         weapon_info     *wip;
7351
7352         shipp = &Ships[aip->shipnum];
7353         swp = &shipp->weapons;
7354
7355         // AL 3-7-98: This probably should never happen, but check to ensure that current_secondary_bank is valid
7356         if ( (swp->current_secondary_bank < 0) || (swp->current_secondary_bank > swp->num_secondary_banks) ) {
7357                 return;
7358         }
7359
7360         num_weapon_types = get_available_secondary_weapons(Pl_objp, weapon_id_list, weapon_bank_list);
7361
7362         wip = &Weapon_info[swp->secondary_bank_weapons[swp->current_secondary_bank]];
7363
7364         if (num_weapon_types && (wip->wi_flags & WIF_HOMING_ASPECT)) {
7365                 if (dist_to_enemy > 300.0f - min(enemy_radius, 100.0f))
7366                         aip->ai_flags |= AIF_SEEK_LOCK;
7367                 else
7368                         aip->ai_flags &= ~AIF_SEEK_LOCK;
7369
7370                 //      Update locking information for aspect seeking missiles.
7371                 aip->current_target_is_locked = 0;
7372                 dot_to_enemy = vm_vec_dot(vec_to_enemy, &Pl_objp->orient.fvec);
7373
7374                 float   needed_dot = 0.9f - 0.5f * enemy_radius/(dist_to_enemy + enemy_radius); //      Replaced MIN_TRACKABLE_DOT with 0.9f
7375                 if (dot_to_enemy > needed_dot) {
7376                         aip->aspect_locked_time += flFrametime;
7377                         // nprintf(("AI", "+ Lock time = %7.3f\n", aip->aspect_locked_time));
7378                         if (aip->aspect_locked_time >= wip->min_lock_time) {
7379                                 aip->aspect_locked_time = wip->min_lock_time;
7380                                 aip->current_target_is_locked = 1;
7381                         }
7382                 } else {
7383                         aip->aspect_locked_time -= flFrametime*2;
7384                         // nprintf(("AI", "- Lock time = %7.3f\n", aip->aspect_locked_time));
7385                         if (aip->aspect_locked_time < 0.0f)
7386                                 aip->aspect_locked_time = 0.0f;
7387                 }
7388                 //nprintf(("AI", "dot = %7.3f, time = %7.3f\n", dot_to_enemy, aip->aspect_locked_time));
7389         
7390         } else {
7391                 aip->current_target_is_locked = 0;
7392                 aip->aspect_locked_time = 0.0f; // Used to be this, why?: wip->min_lock_time;
7393                 aip->ai_flags &= ~AIF_SEEK_LOCK;
7394         }
7395
7396 }
7397
7398 //      We're in chase mode and we've recently collided with our target.
7399 //      Fly away from it!
7400 void ai_chase_fly_away(object *objp, ai_info *aip)
7401 {
7402         int     abort_flag = 0;
7403
7404         if (aip->ai_flags & AIF_TARGET_COLLISION) {
7405                 aip->ai_flags &= ~AIF_TARGET_COLLISION; //      Don't process this hit again next frame.
7406                 aip->submode = SM_FLY_AWAY;                                     //      Focus on avoiding target
7407                 aip->submode_start_time = Missiontime;
7408         }
7409
7410         if ((aip->target_objnum == -1) || (Objects[aip->target_objnum].signature != aip->target_signature)) {
7411                 abort_flag = 1;
7412         }
7413
7414         if (abort_flag || (Missiontime > aip->submode_start_time + F1_0)) {
7415                 aip->last_attack_time = Missiontime;
7416                 aip->submode = SM_ATTACK;
7417                 aip->submode_start_time = Missiontime;
7418         } else {
7419                 vector  v2e;
7420                 float           dot;
7421
7422                 vm_vec_normalized_dir(&v2e, &Objects[aip->target_objnum].pos, &objp->pos);
7423
7424                 dot = vm_vec_dot(&objp->orient.fvec, &v2e);
7425                 if (dot < 0.0f)
7426                         accelerate_ship(aip, 1.0f);
7427                 else
7428                         accelerate_ship(aip, 1.0f - dot);
7429                 turn_away_from_point(objp, &Objects[aip->target_objnum].pos, 0.0f);
7430         }
7431 }
7432
7433 //      Return bank index of favored secondary weapon.
7434 //      Return -1 if nothing favored.
7435 //      "favored" means SEXPs have specified the weapon as being good to fire at en_objp.
7436 int has_preferred_secondary(object *objp, object *en_objp, ship_weapon *swp)
7437 {
7438 // int is_preferred_weapon(int weapon_num, object *firer_objp, object *target_objp)
7439         int     i;
7440
7441         for (i=0; i<swp->num_secondary_banks; i++) {
7442                 if (swp->secondary_bank_capacity[i] > 0) {
7443                         if (swp->secondary_bank_ammo[i] > 0) {
7444                                 if (is_preferred_weapon(swp->secondary_bank_weapons[i], objp, en_objp) != -1){
7445                                         return i;
7446                                 }
7447                         }
7448                 }
7449         }
7450
7451         return -1;
7452 }
7453
7454 //      Choose which secondary weapon to fire.
7455 //      Note, this is not like ai_select_secondary_weapon().  "choose" means make a choice.
7456 //      "select" means execute an order.  Get it?
7457 //      This function calls ai_select_secondary_weapon() with the characteristics it should search for.
7458 void ai_choose_secondary_weapon(object *objp, ai_info *aip, object *en_objp)
7459 {
7460         float                   subsystem_strength = 0.0f;
7461         int                     is_big_ship, priority1, priority2;
7462         ship_weapon     *swp;
7463         ship_info       *esip;
7464
7465         if ( en_objp->type == OBJ_SHIP ) {
7466                 esip = &Ship_info[Ships[en_objp->instance].ship_info_index];
7467         } else {
7468                 esip = NULL;
7469         }
7470
7471         swp = &Ships[objp->instance].weapons;
7472
7473         // AL 3-5-98: do a quick out if the ship has no secondaries
7474         if ( swp->num_secondary_banks <= 0 ) {
7475                 swp->current_secondary_bank = -1;
7476                 return;
7477         }
7478
7479         int preferred_secondary = has_preferred_secondary(objp, en_objp, swp);
7480
7481         if (preferred_secondary != -1) {
7482                 if (swp->current_secondary_bank != preferred_secondary) {
7483                         aip->current_target_is_locked = 0;
7484                         aip->aspect_locked_time = 0.0f;
7485                         swp->current_secondary_bank = preferred_secondary;
7486                 }
7487                 //nprintf(("AI", "Favored secondary = %s\n", Weapon_info[swp->secondary_bank_weapons[swp->current_secondary_bank]].name));
7488                 aip->ai_flags |= AIF_UNLOAD_SECONDARIES;
7489         } else {
7490                 aip->ai_flags &= ~AIF_UNLOAD_SECONDARIES;
7491                 if (aip->targeted_subsys) {
7492                         subsystem_strength = aip->targeted_subsys->current_hits;
7493                 }
7494
7495                 if ( esip ) {
7496                         is_big_ship = esip->flags & (SIF_HUGE_SHIP | SIF_BIG_SHIP);
7497                 } else {
7498                         is_big_ship=0;
7499                 }
7500
7501                 if (is_big_ship) {
7502                         priority1 = WIF_HUGE;
7503                         priority2 = WIF_HOMING;
7504                 } else if ( (esip != NULL) && (esip->flags & SIF_BOMBER) ) {
7505                         priority1 = WIF_BOMBER_PLUS;
7506                         priority2 = WIF_HOMING;
7507                 } else if (subsystem_strength > 100.0f) {
7508                         priority1 = WIF_PUNCTURE;
7509                         priority2 = WIF_HOMING;
7510                 } else {
7511                         priority1 = WIF_HOMING;
7512                         priority2 = 0;
7513                 }
7514                 
7515                 ai_select_secondary_weapon(objp, swp, priority1, priority2);
7516         }
7517
7518         // nprintf(("AI", "Frame %i: Chose secondary %s\n", Framecount, Weapon_info[swp->secondary_bank_weapons[swp->current_secondary_bank]].name));
7519 }
7520
7521 //      Return time, in seconds, at which this ship can next fire its current secondary weapon.
7522 float set_secondary_fire_delay(ai_info *aip, ship *shipp, weapon_info *swip)
7523 {
7524         float t = swip->fire_wait;              //      Base delay for this weapon.
7525         if (shipp->team == Player_ship->team) {
7526                 //      On player's team, _lower_ skill level = faster firing
7527                 t = t * (Game_skill_level+2) / (NUM_SKILL_LEVELS);
7528         } else {                //      Not on player's team, higher skill level = faster firing
7529                 t = t * (NUM_SKILL_LEVELS - Game_skill_level+2) / (NUM_SKILL_LEVELS);
7530         }
7531
7532         t += (Num_ai_classes - aip->ai_class + 1) * 0.5f;
7533         t *= frand_range(0.8f, 1.2f);
7534
7535         //      For the missiles that fire fairly quickly, occasionally add an additional substantial delay.
7536         if (t < 5.0f)
7537                 if (frand() < 0.5f)
7538                         t = t * 2.0f + 2.0f;
7539
7540         return t;
7541 }
7542
7543
7544 void ai_chase_big_approach_set_goal(vector *goal_pos, object *attack_objp, object *target_objp, float *accel)
7545 {
7546         float dist_to_goal;
7547
7548         // head straight toward him and maybe circle later
7549         vm_vec_avg(goal_pos, &attack_objp->pos, &target_objp->pos);
7550
7551         // get distance to goal
7552         dist_to_goal = vm_vec_dist(goal_pos, &attack_objp->pos);
7553         
7554         // set accel
7555         if (dist_to_goal > 400.0f) {
7556                 *accel = 1.0f;
7557         } else {
7558                 *accel = dist_to_goal/400.0f;
7559         }
7560 }
7561
7562 void ai_chase_big_circle_set_goal(vector *goal_pos, object *attack_objp, object *target_objp, float *accel)
7563 {
7564         get_tangent_point(goal_pos, attack_objp, &target_objp->pos, attack_objp->radius + target_objp->radius + 100.0f);
7565
7566         *accel = 1.0f;
7567 }
7568
7569 // get the current and desired horizontal separations between target
7570 void ai_chase_big_get_separations(object *attack_objp, object *target_objp, vector *horz_vec_to_target, float *desired_separation, float *cur_separation)
7571 {
7572         float temp, r_target, r_attacker, h_attacker, h_target;
7573         float perp_dist;
7574         vector vec_to_target;
7575         polymodel *pm;
7576
7577         // get parameters of ships (as cylinders - radius and height)
7578         // get radius of attacker (for rotations about forward)
7579         pm = model_get(Ships[attack_objp->instance].modelnum);
7580         temp = max(pm->maxs.x, pm->maxs.y);
7581         r_attacker = max(-pm->mins.x, -pm->mins.y);
7582         r_attacker = max(temp, r_attacker);
7583         h_attacker = max(-pm->mins.z, pm->maxs.z);
7584
7585         // get radius of target (for rotations about forward)
7586         pm = model_get(Ships[attack_objp->instance].modelnum);
7587         temp = max(pm->maxs.x, pm->maxs.y);
7588         r_target = max(-pm->mins.x, -pm->mins.y);
7589         r_target = max(temp, r_target);
7590         h_target = max(-pm->mins.z, pm->maxs.z);
7591
7592         // find separation between cylinders [if parallel]
7593         vm_vec_sub(&vec_to_target, &attack_objp->pos, &target_objp->pos);
7594
7595         // find the distance between centers along forward direction of ships
7596         perp_dist = vm_vec_dotprod(&vec_to_target, &target_objp->orient.fvec);
7597
7598         // subtract off perp component to get "horizontal" separation vector between cylinders [ASSUMING parallel]
7599         vm_vec_scale_add(horz_vec_to_target, &vec_to_target, &target_objp->orient.fvec, -perp_dist);
7600         *cur_separation = vm_vec_mag_quick(horz_vec_to_target);
7601
7602         // choose "optimal" separation of 1000 + r_target + r_attacker
7603         *desired_separation = 1000 + r_target + r_attacker;
7604 }
7605
7606 void ai_chase_big_parallel_set_goal(vector *goal_pos, object *attack_objp, object *target_objp, float *accel)
7607 {
7608         int opposing;
7609         float temp, r_target, r_attacker, h_attacker, h_target;
7610         float separation, optimal_separation;
7611         vector  horz_vec_to_target;
7612         polymodel *pm;
7613
7614         // get parameters of ships (as cylinders - radius and height)
7615         // get radius of attacker (for rotations about forward)
7616         pm = model_get(Ships[attack_objp->instance].modelnum);
7617         temp = max(pm->maxs.x, pm->maxs.y);
7618         r_attacker = max(-pm->mins.x, -pm->mins.y);
7619         r_attacker = max(temp, r_attacker);
7620         h_attacker = max(-pm->mins.z, pm->maxs.z);
7621
7622         // get radius of target (for rotations about forward)
7623         pm = model_get(Ships[attack_objp->instance].modelnum);
7624         temp = max(pm->maxs.x, pm->maxs.y);
7625         r_target = max(-pm->mins.x, -pm->mins.y);
7626         r_target = max(temp, r_target);
7627         h_target = max(-pm->mins.z, pm->maxs.z);
7628
7629         // are we opposing (only when other ship is not moving)
7630         opposing = ( vm_vec_dotprod(&attack_objp->orient.fvec, &target_objp->orient.fvec) < 0 );
7631
7632         ai_chase_big_get_separations(attack_objp, target_objp, &horz_vec_to_target, &optimal_separation, &separation);
7633
7634         // choose dist (2000) so that we don't bash
7635         float dist = 2000;
7636         if (opposing) {
7637                 dist = - dist;
7638         }
7639
7640         // set the goal pos as dist forward from target along target forward
7641         vm_vec_scale_add(goal_pos, &target_objp->pos, &target_objp->orient.fvec, dist);
7642         // then add horizontal separation
7643         vm_vec_scale_add2(goal_pos, &horz_vec_to_target, optimal_separation/separation);
7644
7645         // find the distance between centers along forward direction of ships
7646         vector vec_to_target;
7647         vm_vec_sub(&vec_to_target, &target_objp->pos, &attack_objp->pos);
7648         float perp_dist = vm_vec_dotprod(&vec_to_target, &target_objp->orient.fvec);
7649
7650         float match_accel = target_objp->phys_info.vel.z / Ship_info[Ships[attack_objp->instance].ship_info_index].max_vel.z;
7651         float length_scale = attack_objp->radius;
7652
7653         // if we're heading toward enemy ship, we want to keep going if we're ahead
7654         if (opposing) {
7655                 perp_dist = -perp_dist;
7656         }
7657
7658         if (perp_dist > 0) {
7659                 // falling behind, so speed up
7660                 *accel = match_accel + (1.0f - match_accel) / length_scale * (perp_dist);
7661         } else {
7662                 // up in front, so slow down
7663                 *accel = match_accel  - match_accel / length_scale * -perp_dist;
7664                 *accel = max(0.0f, *accel);
7665         }
7666
7667 }
7668
7669
7670 //      Return *goal_pos for one cruiser to attack another (big ship).
7671 //      Choose point fairly nearby that is not occupied by another cruiser.
7672 void ai_cruiser_chase_set_goal_pos(vector *goal_pos, object *pl_objp, object *en_objp)
7673 {
7674         ai_info *aip;
7675
7676         aip = &Ai_info[Ships[pl_objp->instance].ai_index];
7677         float accel;
7678
7679         switch (aip->submode) {
7680         case SM_BIG_APPROACH:
7681                 // do approach stuff;
7682                 ai_chase_big_approach_set_goal(goal_pos, pl_objp, en_objp, &accel);
7683                 break;
7684
7685         case SM_BIG_CIRCLE:
7686                 // do circle stuff
7687                 ai_chase_big_circle_set_goal(goal_pos, pl_objp, en_objp, &accel);
7688                 break;
7689
7690         case SM_BIG_PARALLEL:
7691                 // do parallel stuff
7692                 ai_chase_big_parallel_set_goal(goal_pos, pl_objp, en_objp, &accel);
7693                 break;
7694         }
7695 }
7696
7697 int maybe_hack_cruiser_chase_abort()
7698 {
7699         ship                    *shipp = &Ships[Pl_objp->instance];     
7700         ship                    *eshipp = &Ships[En_objp->instance];
7701         ai_info         *aip = &Ai_info[shipp->ai_index];
7702
7703         // mission sm3-08, sathanos chasing collosus
7704         if ( stricmp(Mission_filename, "sm3-08.fs2") == 0 ) {
7705                 if (( stricmp(eshipp->ship_name, "colossus") == 0 ) || ( stricmp(shipp->ship_name, "colossus") == 0 )) {
7706                         // Changed so all big ships attacking the Colossus will not do the chase code.
7707                         // Did this so Beast wouldn't swerve away from Colossus. -- MK, 9/14/99
7708                         //if ( stricmp(shipp->ship_name, "Sathanas") == 0 ) {
7709                                 // do cool hack stuff here
7710                                 ai_clear_ship_goals( aip );
7711                                 aip->mode = AIM_NONE;
7712                                 return 1;
7713                         //}
7714                 }
7715         }
7716
7717         return 0;
7718 }
7719
7720 //      Make a big ship pursue another big ship.
7721 //      (Note, called "ai_cruiser_chase" because we already have ai_chase_big() which means fighter chases big ship.
7722 void ai_cruiser_chase()
7723 {
7724         ship_info       *sip = &Ship_info[Ships[Pl_objp->instance].ship_info_index];
7725         ship                    *shipp = &Ships[Pl_objp->instance];     
7726         ai_info         *aip = &Ai_info[shipp->ai_index];
7727
7728         if (!(sip->flags & (SIF_BIG_SHIP | SIF_HUGE_SHIP))) {
7729                 Int3(); //      Hmm, not a very big ship, how did we get in this function?
7730                 aip->mode = AIM_NONE;
7731                 return;
7732         }
7733
7734         if (En_objp->type != OBJ_SHIP) {
7735                 Int3();
7736                 return;
7737         }
7738
7739         if (En_objp->instance < 0) {
7740                 Int3();
7741                 return;
7742         }
7743
7744         ship                    *eshipp;
7745         ship_info       *esip;
7746
7747         eshipp = &Ships[En_objp->instance];
7748         esip = &Ship_info[eshipp->ship_info_index];
7749
7750         if (!(esip->flags & (SIF_BIG_SHIP | SIF_HUGE_SHIP))) {
7751                 // Int3();      //      Hmm, we're big and we're pursuing something other than a big ship?
7752                 aip->mode = AIM_NONE;
7753                 return;
7754         }
7755
7756         vector  goal_pos;
7757         float turn_time = Ship_info[Ships[Pl_objp->instance].ship_info_index].srotation_time;
7758
7759         // kamikaze - ram and explode
7760         if (aip->ai_flags & AIF_KAMIKAZE) {
7761                 ai_turn_towards_vector(&En_objp->pos, Pl_objp, flFrametime, turn_time, NULL, NULL, 0.0f, 0);
7762                 accelerate_ship(aip, 1.0f);
7763         } 
7764         
7765         // really track down and chase
7766         else {
7767                 // check valid submode
7768                 Assert( (aip->submode == SM_ATTACK) || (aip->submode == SM_BIG_APPROACH) || (aip->submode == SM_BIG_CIRCLE) || (aip->submode == SM_BIG_PARALLEL) );
7769
7770                 // just entering, approach enemy ship
7771                 if (aip->submode == SM_ATTACK) {
7772                         aip->submode = SM_BIG_APPROACH;
7773                 }
7774
7775                 // desired accel
7776                 float accel = 0.0f;
7777                 vector *rvecp = NULL;
7778
7779                 switch (aip->submode) {
7780                 case SM_BIG_APPROACH:
7781                         // do approach stuff;
7782                         ai_chase_big_approach_set_goal(&goal_pos, Pl_objp, En_objp, &accel);
7783                         // maybe set rvec
7784                         break;
7785
7786                 case SM_BIG_CIRCLE:
7787                         // do circle stuff
7788                         ai_chase_big_circle_set_goal(&goal_pos, Pl_objp, En_objp, &accel);
7789                         // maybe set rvec
7790                         break;
7791
7792                 case SM_BIG_PARALLEL:
7793                         // do parallel stuff
7794                         ai_chase_big_parallel_set_goal(&goal_pos, Pl_objp, En_objp, &accel);
7795                         //maybe set rvec
7796                         break;
7797                 }
7798
7799
7800                 // now move as desired
7801                 ai_turn_towards_vector(&goal_pos, Pl_objp, flFrametime, turn_time, NULL, NULL, 0.0f, 0, rvecp);
7802                 accelerate_ship(aip, accel);
7803
7804
7805                 // maybe switch to new mode
7806                 vector vec_to_enemy;
7807                 float dist_to_enemy;
7808                 int moving = (En_objp->phys_info.vel.z > 0.5f);
7809                 vm_vec_sub(&vec_to_enemy, &En_objp->pos, &Pl_objp->pos);
7810                 dist_to_enemy = vm_vec_mag_quick(&vec_to_enemy);
7811
7812                 switch (aip->submode) {
7813                 case SM_BIG_APPROACH:
7814                         if ( dist_to_enemy < (Pl_objp->radius + En_objp->radius)*1.25f + 200.0f ) {
7815                                 // moving
7816                                 if (moving) {
7817                                         // if within 90 degrees of en forward, go into parallel, otherwise circle
7818                                         if ( vm_vec_dotprod(&En_objp->orient.fvec, &Pl_objp->orient.fvec) > 0 ) {
7819                                                 aip->submode = SM_BIG_PARALLEL;
7820                                         }
7821                                 }
7822
7823                                 // otherwise cirle
7824                                 if ( !maybe_hack_cruiser_chase_abort() ) {
7825                                         aip->submode = SM_BIG_CIRCLE;
7826                                 }
7827                         }
7828                         break;
7829
7830                 case SM_BIG_CIRCLE:
7831                         // moving
7832                         if (moving) {
7833                                 vector temp;
7834                                 float desired_sep, cur_sep;
7835                                 // we're behind the enemy ship
7836                                 if (vm_vec_dotprod(&vec_to_enemy, &En_objp->orient.fvec) > 0) {
7837                                         // and we're turning toward the enemy
7838                                         if (vm_vec_dotprod(&En_objp->orient.fvec, &Pl_objp->orient.fvec) > 0) {
7839                                                 // get separation
7840                                                 ai_chase_big_get_separations(Pl_objp, En_objp, &temp, &desired_sep, &cur_sep);
7841                                                 // and the separation is > 0.9 desired
7842                                                 if (cur_sep > 0.9 * desired_sep) {
7843                                                         aip->submode = SM_BIG_PARALLEL;
7844                                                 }
7845                                         }
7846                                 }
7847                         } else {
7848                                 // still
7849                                 vector temp;
7850                                 float desired_sep, cur_sep;
7851                                 // we're behind the enemy ship
7852                                 if (vm_vec_dotprod(&vec_to_enemy, &En_objp->orient.fvec) > 0) {
7853                                         // and we're turning toward the enemy
7854                                         if (vm_vec_dotprod(&En_objp->orient.fvec, &Pl_objp->orient.fvec) > 0) {
7855                                                 // get separation
7856                                                 ai_chase_big_get_separations(Pl_objp, En_objp, &temp, &desired_sep, &cur_sep);
7857                                                 //and the separation is [0.9 to 1.1] desired
7858                                                 if ( (cur_sep > 0.9f * desired_sep) ) {
7859                                                         aip->submode = SM_BIG_PARALLEL;
7860                                                 }
7861                                         }
7862                                 }
7863                                 // in front of ship
7864                                 else {
7865                                         // and we're turning toward the enemy
7866                                         if (vm_vec_dotprod(&En_objp->orient.fvec, &Pl_objp->orient.fvec) < 0) {
7867                                                 // get separation
7868                                                 ai_chase_big_get_separations(Pl_objp, En_objp, &temp, &desired_sep, &cur_sep);
7869                                                 //and the separation is [0.9 to 1.1] desired
7870                                                 if ( (cur_sep > 0.9f * desired_sep) ) {
7871                                                         aip->submode = SM_BIG_PARALLEL;
7872                                                 }
7873                                         }
7874                                 }
7875                         }
7876                         break;
7877
7878                 case SM_BIG_PARALLEL:
7879                         // we're opposing
7880                         if ( vm_vec_dotprod(&Pl_objp->orient.fvec, &En_objp->orient.fvec) < 0 ) {
7881                                 // and the other ship is moving
7882                                 if (moving) {
7883                                         // and we no longer overlap
7884                                         if ( dist_to_enemy > (0.75 * (En_objp->radius + Pl_objp->radius)) ) {
7885                                                 aip->submode = SM_BIG_APPROACH;
7886                                         }
7887                                 }
7888                         }
7889                         break;
7890                 }
7891         }
7892 }
7893
7894 // --------------------------------------------------------------------------
7895 // Make object Pl_objp chase object En_objp
7896 void ai_chase()
7897 {
7898         float                   dist_to_enemy, time_to_enemy;
7899         float                   dot_to_enemy, dot_from_enemy, real_dot_to_enemy;
7900         vector          player_pos, enemy_pos, predicted_enemy_pos, real_vec_to_enemy, predicted_vec_to_enemy;
7901         ship_info       *sip = &Ship_info[Ships[Pl_objp->instance].ship_info_index];
7902         ship                    *shipp = &Ships[Pl_objp->instance];
7903         ship_weapon     *swp = &shipp->weapons;
7904         ai_info         *aip = &Ai_info[shipp->ai_index];
7905         int                     enemy_sip_flags;
7906
7907         if (aip->mode != AIM_CHASE) {
7908                 Int3();
7909         }
7910
7911         if (sip->flags & (SIF_BIG_SHIP | SIF_HUGE_SHIP)) {
7912                 ai_cruiser_chase();
7913                 return;
7914         }
7915
7916         if (!(sip->flags & (SIF_FIGHTER | SIF_BOMBER | SIF_ESCAPEPOD))) {
7917                 Warning(LOCATION, "Ship %s is not 'small', but is in chase mode.\nSwitching to AI=none.\n", shipp->ship_name);
7918                 aip->mode = AIM_NONE;
7919                 return;
7920         }
7921
7922         //nprintf(("AI", "%7s ", Submode_text[aip->submode]));
7923
7924         if ( En_objp->type == OBJ_SHIP ) {
7925                 enemy_sip_flags = Ship_info[Ships[En_objp->instance].ship_info_index].flags;
7926         } else {
7927                 enemy_sip_flags = 0;
7928         }
7929
7930         if ( enemy_sip_flags > 0 ) {
7931                 if (enemy_sip_flags & (SIF_BIG_SHIP | SIF_HUGE_SHIP)) {
7932                         ai_big_chase();
7933                         return;
7934                 }
7935         }
7936
7937         //      If collided with target_objnum last frame, avoid that ship.
7938         //      This should prevent the embarrassing behavior of ships getting stuck on each other
7939         //      as if they were magnetically attracted. -- MK, 11/13/97.
7940         if ((aip->ai_flags & AIF_TARGET_COLLISION) || (aip->submode == SM_FLY_AWAY)) {
7941                 ai_chase_fly_away(Pl_objp, aip);
7942                 return;
7943         }
7944
7945         ai_set_positions(Pl_objp, En_objp, aip, &player_pos, &enemy_pos);
7946         dist_to_enemy = vm_vec_dist_quick(&player_pos, &enemy_pos);
7947         time_to_enemy = compute_time_to_enemy(dist_to_enemy, Pl_objp, En_objp);
7948         vm_vec_sub(&real_vec_to_enemy, &enemy_pos, &player_pos);
7949
7950         vm_vec_normalize(&real_vec_to_enemy);
7951
7952         real_dot_to_enemy = vm_vec_dot(&real_vec_to_enemy, &Pl_objp->orient.fvec);
7953
7954         int is_stealthy_ship = 0;
7955         if ( (enemy_sip_flags > 0) && (enemy_sip_flags & SIF_STEALTH) ) {
7956                 if ( ai_is_stealth_visible(Pl_objp, En_objp) != STEALTH_FULLY_TARGETABLE ) {
7957                         is_stealthy_ship = 1;
7958                 }
7959         }
7960
7961         // Can only acquire lock on a target that isn't hidden from sensors
7962         if ( !(Ships[En_objp->instance].flags & SF_HIDDEN_FROM_SENSORS) && !is_stealthy_ship ) {
7963                 update_aspect_lock_information(aip, &real_vec_to_enemy, dist_to_enemy, En_objp->radius);
7964         } else {
7965                 aip->current_target_is_locked = 0;
7966                 aip->ai_flags &= ~AIF_SEEK_LOCK;
7967         }
7968
7969         //      If seeking lock, try to point directly at ship, else predict position so lasers can hit it.
7970         //      If just acquired target, or target is not in reasonable cone, don't refine believed enemy position.
7971         if ((real_dot_to_enemy < 0.25f) || (aip->target_time < 1.0f) || (aip->ai_flags & AIF_SEEK_LOCK)) {
7972                 predicted_enemy_pos = enemy_pos;
7973         } else {
7974                 //      Set predicted_enemy_pos.
7975                 //      See if attacking a subsystem.
7976                 if (aip->targeted_subsys != NULL) {
7977                         Assert(En_objp->type == OBJ_SHIP);
7978                         ship_info       *esip = &Ship_info[Ships[En_objp->instance].ship_info_index];
7979                         if (get_shield_strength(En_objp)/esip->shields < HULL_DAMAGE_THRESHOLD_PERCENT) {
7980                                 //int   rval;
7981
7982                                 if (aip->targeted_subsys != NULL) {
7983                                         get_subsystem_pos(&enemy_pos, En_objp, aip->targeted_subsys);
7984                                         predicted_enemy_pos = enemy_pos;
7985                                         predicted_vec_to_enemy = real_vec_to_enemy;
7986                                 } else {
7987                                         set_predicted_enemy_pos(&predicted_enemy_pos, Pl_objp, En_objp, aip);
7988                                         set_target_objnum(aip, -1);
7989                                 }
7990                                 // 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));
7991
7992                         } else {
7993                                 set_predicted_enemy_pos(&predicted_enemy_pos, Pl_objp, En_objp, aip);
7994                                 // nprintf(("AI", "Attacking subsystem: pos = %7.3f %7.3f %7.3f\n", predicted_enemy_pos.x, predicted_enemy_pos.y, predicted_enemy_pos.z));
7995                         }
7996                 } else {
7997                         set_predicted_enemy_pos(&predicted_enemy_pos, Pl_objp, En_objp, aip);
7998                 }
7999         }
8000
8001         vm_vec_sub(&predicted_vec_to_enemy, &predicted_enemy_pos, &player_pos);
8002
8003         vm_vec_normalize(&predicted_vec_to_enemy);
8004
8005         dot_to_enemy = vm_vec_dot(&Pl_objp->orient.fvec, &predicted_vec_to_enemy);
8006         dot_from_enemy= - vm_vec_dot(&En_objp->orient.fvec, &real_vec_to_enemy);
8007
8008         //
8009         //      Set turn and acceleration based on submode.
8010         //
8011         switch (aip->submode) {
8012         case SM_CONTINUOUS_TURN:
8013                 ai_chase_ct();
8014                 break;
8015
8016         case SM_STEALTH_FIND:
8017                 ai_stealth_find();
8018                 break;
8019
8020         case SM_STEALTH_SWEEP:
8021                 ai_stealth_sweep();
8022                 break;
8023
8024         case SM_ATTACK:
8025         case SM_SUPER_ATTACK:
8026         case SM_ATTACK_FOREVER:
8027                 if (vm_vec_dist_quick(&Pl_objp->pos, &predicted_enemy_pos) > 100.0f + En_objp->radius * 2.0f) {
8028                         if (maybe_avoid_big_ship(Pl_objp, En_objp, aip, &predicted_enemy_pos, 10.0f))
8029                                 return;
8030                 }
8031
8032                 ai_chase_attack(aip, sip, &predicted_enemy_pos, dist_to_enemy);
8033                 break;
8034
8035         case SM_EVADE_SQUIGGLE:
8036                 ai_chase_es(aip, sip);
8037                 break;
8038
8039         case SM_EVADE_BRAKE:
8040                 ai_chase_eb(aip, sip, &predicted_enemy_pos, dist_to_enemy);
8041                 break;
8042
8043         case SM_EVADE:
8044                 evade_ship();
8045                 break;
8046
8047         case SM_AVOID:
8048                 avoid_ship();
8049                 break;
8050
8051         case SM_GET_BEHIND:
8052                 get_behind_ship(aip, sip, dist_to_enemy);
8053                 break;
8054
8055         case SM_GET_AWAY:               //      Used to get away from opponent to prevent endless circling.
8056                 ai_chase_ga(aip, sip);
8057                 break;
8058
8059         case SM_EVADE_WEAPON:
8060                 evade_weapon();
8061                 break;
8062
8063         default:
8064                 // Int3();
8065                 aip->last_attack_time = Missiontime;
8066                 aip->submode = SM_ATTACK;
8067                 aip->submode_start_time = Missiontime;
8068         }
8069
8070         //
8071         //      Maybe choose a new submode.
8072         //
8073         if ( (aip->submode != SM_AVOID) && (aip->submode != SM_ATTACK_FOREVER) ) {
8074                 //      If a very long time since attacked, attack no matter what!
8075                 if ( (aip->submode != SM_SUPER_ATTACK) && (aip->submode != SM_GET_AWAY) && !(aip->ai_flags & AIF_STEALTH_PURSIUT) ) {
8076                         if (Missiontime - aip->last_attack_time > i2f(6)) {
8077                                 aip->submode = SM_SUPER_ATTACK;
8078                                 aip->submode_start_time = Missiontime;
8079                                 aip->last_attack_time = Missiontime;
8080                         }
8081                 }
8082
8083                 //      If a collision is expected, pull out!
8084                 //      If enemy is pointing away and moving a bit, don't worry about collision detection.
8085                 if ((dot_from_enemy > 0.5f) || (En_objp->phys_info.speed < 10.0f)) {
8086                         if (might_collide_with_ship(Pl_objp, En_objp, dot_to_enemy, dist_to_enemy, 4.0f)) {
8087                                 if ((Missiontime - aip->last_hit_time > F1_0*4) && (dist_to_enemy < Pl_objp->radius*2 + En_objp->radius*2)) {
8088                                         accelerate_ship(aip, -1.0f);
8089                                 } else {
8090                                         aip->submode = SM_AVOID;
8091                                         aip->submode_start_time = Missiontime;
8092                                 }
8093                         }
8094                 }
8095         }
8096
8097         switch (aip->submode) {
8098         case SM_CONTINUOUS_TURN:
8099                 if (Missiontime - aip->submode_start_time > i2f(3)) {
8100                         aip->last_attack_time = Missiontime;
8101                         aip->submode = SM_ATTACK;
8102                         aip->submode_start_time = Missiontime;
8103                 }
8104                 break;
8105
8106         case SM_ATTACK:
8107                 // if taraget is stealth and stealth not visible, then enter stealth find mode
8108                 if ( (aip->ai_flags & AIF_STEALTH_PURSIUT) && (ai_is_stealth_visible(Pl_objp, En_objp) == STEALTH_INVISIBLE) ) {
8109                         aip->submode = SM_STEALTH_FIND;
8110                         aip->submode_start_time = Missiontime;
8111                         aip->submode_parm0 = SM_SF_AHEAD;
8112                 } 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)) {
8113                         aip->submode = SM_SUPER_ATTACK;
8114                         aip->submode_start_time = Missiontime;
8115                         aip->last_attack_time = Missiontime;
8116                 } else if ((Missiontime - aip->last_hit_target_time > i2f(6)) &&
8117                         (dist_to_enemy < 500.0f) && (dot_to_enemy < 0.2f) &&
8118                         (frand() < (float) Game_skill_level/NUM_SKILL_LEVELS)) {
8119                         aip->submode = SM_GET_AWAY;
8120                         aip->submode_start_time = Missiontime;
8121                         aip->last_hit_target_time = Missiontime;
8122                 } else if ((enemy_sip_flags & SIF_SMALL_SHIP)
8123                         && (dot_to_enemy < dot_from_enemy)
8124                         && (En_objp->phys_info.speed > 15.0f) 
8125                         && (dist_to_enemy < 200.0f) 
8126                         && (dist_to_enemy > 50.0f)
8127                         && (dot_to_enemy < 0.1f)
8128                         && (Missiontime - aip->submode_start_time > i2f(2))) {
8129                         aip->submode = SM_EVADE_BRAKE;
8130                         aip->submode_start_time = Missiontime;
8131                 } else if ((dot_to_enemy > 0.2f) && (dot_from_enemy > -0.2f) && (dot_from_enemy < 0.1f)) {
8132                         aip->submode = SM_GET_BEHIND;
8133                         aip->submode_start_time = Missiontime;
8134                 } 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)) {
8135                         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;
8136                                 aip->submode_start_time = Missiontime;
8137                                 aip->last_hit_target_time = Missiontime;
8138                         } else {
8139                                 aip->submode = SM_EVADE_SQUIGGLE;
8140                                 aip->submode_start_time = Missiontime;
8141                         }
8142                 } else if ((enemy_sip_flags & SIF_SMALL_SHIP) && (Missiontime - aip->submode_start_time > F1_0*2)) {
8143                         if ((dot_to_enemy < 0.8f) && (dot_from_enemy > dot_to_enemy)) {
8144                                 if (frand() > 0.5f) {
8145                                         aip->submode = SM_CONTINUOUS_TURN;
8146                                         aip->submode_parm0 = myrand() & 0x0f;
8147                                         aip->submode_start_time = Missiontime;
8148                                 } else {
8149                                         aip->submode = SM_EVADE;
8150                                         aip->submode_start_time = Missiontime;
8151                                 }
8152                         } else {
8153                                 aip->submode_start_time = Missiontime;
8154                         }
8155                 }
8156
8157                 aip->last_attack_time = Missiontime;
8158
8159                 break;
8160                 
8161         case SM_EVADE_SQUIGGLE:
8162                 if ((Missiontime - aip->submode_start_time > i2f(5)) || (dist_to_enemy > 300.0f)) {
8163                         if ((dist_to_enemy < 100.0f) && (dot_to_enemy < 0.0f) && (dot_from_enemy > 0.5f)) {
8164                                 aip->submode = SM_EVADE_BRAKE;
8165                                 aip->submode_start_time = Missiontime;
8166                         } else {
8167                                 aip->last_attack_time = Missiontime;
8168                                 aip->submode = SM_ATTACK;
8169                                 aip->submode_start_time = Missiontime;
8170                         }
8171                 }
8172                 break;
8173         
8174         case SM_EVADE_BRAKE:
8175                 if ((dist_to_enemy < 15.0f) || (En_objp->phys_info.speed < 10.0f)) {
8176                         aip->submode = SM_AVOID;
8177                         aip->submode_start_time = Missiontime;
8178                 } else if ((dot_to_enemy > 0.9f) || ((dot_from_enemy > 0.9f) && (Missiontime - aip->submode_start_time > i2f(1)))) {
8179                         aip->last_attack_time = Missiontime;
8180                         aip->submode = SM_ATTACK;
8181                         aip->submode_start_time = Missiontime;
8182                 } else if (Missiontime - aip->submode_start_time > i2f(4)) {
8183                         aip->last_attack_time = Missiontime;
8184                         aip->submode = SM_ATTACK;
8185                         aip->submode_start_time = Missiontime;
8186                 }
8187                 break;
8188
8189         case SM_EVADE:
8190                 //      Modified by MK on 5/5/97 to keep trying to regain attack mode.  It's what a human would do.
8191                 if ((dot_to_enemy < 0.2f) && (dot_from_enemy < 0.8f) && (dist_to_enemy < 100.0f) && (En_objp->phys_info.speed > 15.0f)) {
8192                         aip->last_attack_time = Missiontime;
8193                         aip->submode = SM_EVADE_BRAKE;
8194                         aip->submode_start_time = Missiontime;
8195                 } else if (((dot_to_enemy > dot_from_enemy - 0.1f)
8196                         && (Missiontime > aip->submode_start_time + i2f(1)))
8197                         || (dist_to_enemy > 150.0f + 2*(Pl_objp->radius + En_objp->radius))) {
8198                         aip->last_attack_time = Missiontime;
8199                         aip->submode = SM_ATTACK;
8200                         aip->submode_start_time = Missiontime;
8201                 } else if (Missiontime - aip->submode_start_time > i2f(2))
8202                         if (dot_from_enemy > 0.8f) {
8203                                 aip->submode = SM_EVADE_SQUIGGLE;
8204                                 aip->submode_start_time = Missiontime;
8205                         }
8206
8207                 break;
8208
8209         case SM_SUPER_ATTACK:
8210                 // if stealth and invisible, enter stealth find mode
8211                 if ( (aip->ai_flags & AIF_STEALTH_PURSIUT) && (ai_is_stealth_visible(Pl_objp, En_objp) == STEALTH_INVISIBLE) ) {
8212                         aip->submode = SM_STEALTH_FIND;
8213                         aip->submode_start_time = Missiontime;
8214                         aip->submode_parm0 = SM_SF_AHEAD;
8215                 } 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) )) {
8216                         aip->ai_flags &= ~AIF_ATTACK_SLOWLY;    //      Just in case, clear here.
8217
8218                         switch (myrand() % 5) {
8219                         case 0:
8220                                 aip->submode = SM_CONTINUOUS_TURN;
8221                                 aip->submode_start_time = Missiontime;
8222                                 break;
8223                         case 1:
8224                                 aip->submode_start_time = Missiontime;  //      Stay in super attack mode
8225                                 break;
8226                         case 2:
8227                         case 3:
8228                                 if (frand() < (float) 0.5f * (aip->ai_class + Game_skill_level)/(Num_ai_classes + NUM_SKILL_LEVELS)) {
8229                                         aip->submode = SM_GET_AWAY;
8230                                         aip->submode_start_time = Missiontime;
8231                                 } else {
8232                                         aip->submode = SM_EVADE;
8233                                         aip->submode_start_time = Missiontime;
8234                                 }
8235                                 break;
8236                         case 4:
8237                                 if (dot_from_enemy + (NUM_SKILL_LEVELS - Game_skill_level) * 0.1f > dot_to_enemy) {     //      Less likely to GET_AWAY at lower skill levels.
8238                                         aip->submode = SM_EVADE;
8239                                         aip->submode_start_time = Missiontime;
8240                                 } else {
8241                                         aip->submode = SM_GET_AWAY;
8242                                         aip->submode_start_time = Missiontime;
8243                                 }
8244                                 break;
8245                         default:
8246                                 Int3(); //      Impossible!
8247                         }
8248                 }
8249
8250                 aip->last_attack_time = Missiontime;
8251
8252                 break;
8253
8254         case SM_AVOID:
8255                 if ((dot_to_enemy > -0.2f) && (dist_to_enemy / (dot_to_enemy + 0.3f) < 100.0f)) {
8256                         aip->submode_start_time = Missiontime;
8257                 } else if (Missiontime - aip->submode_start_time > i2f(1)/2)
8258                         if (might_collide_with_ship(Pl_objp, En_objp, dot_to_enemy, dist_to_enemy, 3.0f)) {
8259                                 aip->submode_start_time = Missiontime;
8260                         } else {
8261                                 aip->submode = SM_GET_BEHIND;
8262                                 aip->submode_start_time = Missiontime;
8263                         }
8264
8265                 break;
8266
8267         case SM_GET_BEHIND:
8268                 if ((dot_from_enemy < -0.7f) || (Missiontime - aip->submode_start_time > i2f(2))) {
8269                         aip->submode = SM_ATTACK;
8270                         aip->submode_start_time = Missiontime;
8271                         aip->last_attack_time = Missiontime;
8272                 }
8273                 break;
8274
8275         case SM_GET_AWAY:
8276                 if (Missiontime - aip->submode_start_time > i2f(2)) {
8277                         float   rand_dist;
8278
8279                         rand_dist = ((Missiontime >> 17) & 0x03) * 100.0f + 200.0f;     //      Some value in 200..500
8280                         if ((Missiontime - aip->submode_start_time > i2f(5)) || (dist_to_enemy > rand_dist) || (dot_from_enemy < 0.4f)) {
8281                                 aip->ai_flags |= AIF_ATTACK_SLOWLY;
8282                                 aip->submode = SM_ATTACK;
8283                                 aip->time_enemy_in_range = 2.0f;                //      Cheat.  Presumably if they were running away from you, they were monitoring you!
8284                                 aip->submode_start_time = Missiontime;
8285                                 aip->last_attack_time = Missiontime;
8286                         }
8287                 }
8288                 break;
8289
8290         case SM_EVADE_WEAPON:
8291                 if (aip->danger_weapon_objnum == -1) {
8292                         aip->submode = SM_ATTACK;
8293                         aip->submode_start_time = Missiontime;
8294                         aip->last_attack_time = Missiontime;
8295                 }
8296                 break;
8297
8298         // Either change to SM_ATTACK or AIM_FIND_STEALTH
8299         case SM_STEALTH_FIND:
8300                 // if time > 5 sec change mode to sweep
8301                 if ( !(aip->ai_flags & AIF_STEALTH_PURSIUT) || (ai_is_stealth_visible(Pl_objp, En_objp) == STEALTH_VISIBLE) ) {
8302                         aip->submode = SM_ATTACK;
8303                         aip->submode_start_time = Missiontime;
8304                         aip->last_attack_time = Missiontime;
8305                         // sweep if I can't find in 5 sec or bail from find
8306                 } else if ( ((Missiontime - aip->submode_start_time) > i2f(5)) || (aip->submode_parm0 == SM_SF_BAIL) ) {
8307                         // begin sweep mode
8308                         aip->submode = SM_STEALTH_SWEEP;
8309                         aip->submode_start_time = Missiontime;
8310                         aip->last_attack_time = Missiontime;
8311                         aip->submode_parm0 = SM_SS_SET_GOAL;
8312                 }
8313                 break;
8314
8315         case SM_STEALTH_SWEEP:
8316                 if ( !(aip->ai_flags & AIF_STEALTH_PURSIUT) || (ai_is_stealth_visible(Pl_objp, En_objp) == STEALTH_VISIBLE) ) {
8317                         aip->submode = SM_ATTACK;
8318                         aip->submode_start_time = Missiontime;
8319                         aip->last_attack_time = Missiontime;
8320                 } else if ( (timestamp() - aip->stealth_last_visible_stamp) < 5000 ) {
8321                         // go back to find mode
8322                         aip->submode = SM_STEALTH_FIND;
8323                         aip->submode_start_time = Missiontime;
8324                         aip->submode_parm0 = SM_SF_AHEAD;
8325                 } else if ( /*(Missiontime - aip->submode_start_time) > i2f(30) || */(aip->submode_parm0 == SM_SS_DONE) ) {
8326                         // set target objnum = -1
8327                         set_target_objnum(aip, -1);
8328
8329                         // set submode to attack
8330                         aip->submode = SM_ATTACK;
8331                         aip->submode_start_time = Missiontime;
8332                         aip->last_attack_time = Missiontime;
8333                 }
8334                 break;
8335
8336         case SM_ATTACK_FOREVER: //      Engines blown, just attack.
8337                 break;
8338
8339         default:
8340                 //Int3();
8341                 aip->submode = SM_ATTACK;
8342                 aip->last_attack_time = Missiontime;
8343
8344                 aip->submode_start_time = Missiontime;
8345         }
8346
8347         //
8348         //      Maybe fire primary weapon and update time_enemy_in_range
8349         //
8350         //nprintf(("AI", "time_enemy_in_range = %7.3f, dot = %7.3f\n", aip->time_enemy_in_range, dot_to_enemy));
8351
8352         if (aip->mode != AIM_EVADE) {
8353                 if (dot_to_enemy > 0.95f - 0.5f * En_objp->radius/max(1.0f, En_objp->radius + dist_to_enemy)) {
8354                         aip->time_enemy_in_range += flFrametime;
8355                         
8356                         //      Chance of hitting ship is based on dot product of firing ship's forward vector with vector to ship
8357                         //      and also the size of the target relative to distance to target.
8358                         if (dot_to_enemy > max(0.5f, 0.90f + aip->ai_accuracy/10.0f - En_objp->radius/max(1.0f,dist_to_enemy))) {
8359
8360                                 ship *temp_shipp;
8361                                 ship_weapon *tswp;
8362
8363                                 temp_shipp = &Ships[Pl_objp->instance];
8364                                 tswp = &temp_shipp->weapons;
8365                                 if ( tswp->num_primary_banks > 0 ) {
8366                                         float   scale;
8367                                         Assert(tswp->current_primary_bank < tswp->num_primary_banks);
8368                                         weapon_info     *pwip = &Weapon_info[tswp->primary_bank_weapons[tswp->current_primary_bank]];
8369
8370                                         //      Less likely to fire if far away and moving.
8371                                         scale = pwip->max_speed/(En_objp->phys_info.speed + pwip->max_speed);
8372                                         if (scale > 0.6f)
8373                                                 scale = (scale - 0.6f) * 1.5f;
8374                                         else
8375                                                 scale = 0.0f;
8376                                         if (dist_to_enemy < pwip->max_speed * (1.0f + scale)) {
8377                                                 ai_fire_primary_weapon(Pl_objp);
8378                                         }
8379
8380                                         //      Don't fire secondaries at a protected ship.
8381                                         if (!(En_objp->flags & OF_PROTECTED)) {
8382                                                 ai_choose_secondary_weapon(Pl_objp, aip, En_objp);
8383                                                 int current_bank = tswp->current_secondary_bank;
8384                                                 weapon_info     *swip = &Weapon_info[tswp->secondary_bank_weapons[tswp->current_secondary_bank]];
8385
8386                                                 if (current_bank > -1) {
8387                                                         if (aip->ai_flags & AIF_UNLOAD_SECONDARIES) {
8388                                                                 if (timestamp_until(swp->next_secondary_fire_stamp[current_bank]) > swip->fire_wait*1000.0f) {
8389                                                                         swp->next_secondary_fire_stamp[current_bank] = timestamp((int) (swip->fire_wait*1000.0f));
8390                                                                 }
8391                                                         }
8392
8393                                                         if (timestamp_elapsed(swp->next_secondary_fire_stamp[current_bank])) {
8394                                                                 if (tswp->current_secondary_bank >= 0) {
8395                                                                         weapon_info     *swip = &Weapon_info[tswp->secondary_bank_weapons[tswp->current_secondary_bank]];
8396                                                                         float firing_range;
8397                                                                         
8398                                                                         if (swip->wi_flags & WIF_BOMB)
8399                                                                                 firing_range = swip->max_speed * swip->lifetime * 0.75f;
8400                                                                         else
8401                                                                                 firing_range = swip->max_speed * swip->lifetime * (Game_skill_level + 1 + aip->ai_class/2)/NUM_SKILL_LEVELS;
8402
8403                                                                         // reduce firing range in nebula
8404                                                                         extern int Nebula_sec_range;
8405                                                                         if ((The_mission.flags & MISSION_FLAG_FULLNEB) && Nebula_sec_range) {
8406                                                                                 firing_range *= 0.8f;
8407                                                                         }
8408
8409                                                                         //      If firing a spawn weapon, distance doesn't matter.
8410                                                                         int     spawn_fire = 0;
8411
8412                                                                         if (swip->wi_flags & WIF_SPAWN) {
8413                                                                                 int     count;
8414
8415                                                                                 count = num_nearby_fighters(get_enemy_team_mask(OBJ_INDEX(Pl_objp)), &Pl_objp->pos, 1000.0f);
8416
8417                                                                                 if (count > 3)
8418                                                                                         spawn_fire = 1;
8419                                                                                 else if (count >= 1) {
8420                                                                                         float hull_percent = Pl_objp->hull_strength/sip->initial_hull_strength;
8421
8422                                                                                         if (hull_percent < 0.01f)
8423                                                                                                 hull_percent = 0.01f;
8424
8425                                                                                         if (frand() < 0.25f/(30.0f*hull_percent) * count)       //      With timestamp below, this means could fire in 30 seconds if one enemy.
8426                                                                                                 spawn_fire = 1;
8427                                                                                 }
8428                                                                         }
8429
8430                                                                         if (spawn_fire || (dist_to_enemy < firing_range)) {
8431                                                                                 if (ai_fire_secondary_weapon(Pl_objp)) {
8432                                                                                         //      Only if weapon was fired do we specify time until next fire.  If not fired, done in ai_fire_secondary...
8433                                                                                         float t;
8434                                                                                         
8435                                                                                         if (aip->ai_flags & AIF_UNLOAD_SECONDARIES) {
8436                                                                                                 t = swip->fire_wait;
8437                                                                                         } else {
8438                                                                                                 t = set_secondary_fire_delay(aip, temp_shipp, swip);
8439                                                                                         }
8440                                                                                         //nprintf(("AI", "Next secondary to be fired in %7.3f seconds.\n", t));
8441                                                                                         swp->next_secondary_fire_stamp[current_bank] = timestamp((int) (t*1000.0f));
8442                                                                                 }
8443                                                                         } else {
8444                                                                                 swp->next_secondary_fire_stamp[current_bank] = timestamp(250);
8445                                                                         }
8446                                                                 }
8447                                                         }
8448                                                 }
8449                                         }
8450                                 }
8451                         }
8452                 } else {
8453                         aip->time_enemy_in_range *= (1.0f - flFrametime);
8454                 }
8455         } else
8456                 aip->time_enemy_in_range *= (1.0f - flFrametime);
8457
8458 }
8459
8460 //      Make the object *objp move so that the point *dp on the object moves towards the point *vp
8461 //      Return distance.
8462 void dock_move_towards_point(object *objp, vector *dp, vector *vp, float speed_scale, float other_obj_speed = 0.0f)
8463 {
8464         physics_info    *pi = &objp->phys_info;
8465         float                           dist;                   //      dist to goal
8466         vector                  v2g;                    //      vector to goal
8467         vector                  abs_pnt;                //      location of dock point, ie objp->pos + db
8468
8469         if (dp == NULL)
8470                 abs_pnt = objp->pos;
8471         else
8472                 vm_vec_add(&abs_pnt, &objp->pos, dp);
8473
8474         dist = vm_vec_dist_quick(vp, &abs_pnt);
8475         if (dist > 0.0f) {
8476                 float   speed;
8477
8478                 dist = vm_vec_normalized_dir(&v2g, vp, &abs_pnt);
8479                 speed = fl_sqrt(dist) * speed_scale;
8480                 if (other_obj_speed < MAX_REPAIR_SPEED*0.75f)
8481                         speed += other_obj_speed;
8482                 else
8483                         speed += MAX_REPAIR_SPEED*0.75f;
8484
8485                 vm_vec_copy_scale(&pi->desired_vel, &v2g, speed);
8486         } else
8487                 vm_vec_zero(&pi->desired_vel);
8488 }
8489
8490 //      Set the orientation in the global reference frame for an object to attain
8491 //      to dock with another object.
8492 //      *dom            resultant global matrix
8493 //      *db_dest        pointer to destination docking bay information
8494 //      *db_src pointer to source docking bay information
8495 //      *dorient        pointer to global orientation of docking bay (ie, the dockee object's orient)
8496 //      *sorient        pointer to global orientation of docker
8497 void set_goal_dock_orient(matrix *dom, dock_bay *db_dest, dock_bay *db_src, matrix *dorient, matrix *sorient)
8498 {
8499         vector  fvec, uvec;
8500         matrix  m1, m2, m3;
8501
8502         //      Compute the global orientation of the docker's (dest) docking bay.
8503         fvec = db_dest->norm[0];
8504         vm_vec_negate(&fvec);
8505
8506         vm_vec_normalized_dir(&uvec, &db_dest->pnt[1], &db_dest->pnt[0]);
8507         vm_vector_2_matrix(&m1, &fvec, &uvec, NULL);
8508
8509         vm_matrix_x_matrix(&m3, dorient, &m1);
8510
8511         //      Compute the matrix given by the source docking bay.
8512         //      Pre-multiply the orientation of the source object (sorient) by the transpose
8513         //      of the docking bay's orientation, ie unrotate the source object's matrix.
8514         fvec = db_src->norm[0];
8515         vm_vec_normalized_dir(&uvec, &db_src->pnt[1], &db_src->pnt[0]);
8516         vm_vector_2_matrix(&m2, &fvec, &uvec, NULL);
8517         vm_transpose(&m2);
8518
8519         vm_matrix_x_matrix(dom, &m3, &m2);
8520 }
8521
8522 #define DOCK_BACKUP_RETURN_VAL  99999.9f
8523
8524 //      Make objp dock with dobjp
8525 //      Returns distance to goal, defined as distance between corresponding dock points, plus 10.0f * rotational velocity vector (DOA_DOCK only)
8526 //      DOA_APPROACH    means   approach point aip->path_cur
8527 //      DOA_DOCK                        means dock
8528 //      DOA_UNDOCK_1    means undock, moving to point nearest dock bay
8529 //      DOA_UNDOCK_2    means undock, moving to point nearest dock bay and facing away from ship
8530 //      DOA_DOCK_STAY   means rigidly maintain position in dock bay.
8531 float dock_orient_and_approach(object *objp, object *dobjp, int dock_mode)
8532 {
8533         ship_info       *sip0, *sip1;
8534         polymodel       *pm0, *pm1;
8535         ai_info         *aip;
8536         matrix          dom, nm;
8537         vector          goal_point, docker_point;
8538         float                   fdist = UNINITIALIZED_VALUE;
8539         int                     docker_index, dockee_index;             // index into docking_bays[] array for objects docking
8540                                                                                                                                 // docker is Pl_objp -- dockee is dobjp
8541         aip = &Ai_info[Ships[objp->instance].ai_index];
8542
8543         //      If dockee has moved much, then path will be recreated.
8544         //      Might need to change state if moved too far.
8545         if ((dock_mode != DOA_DOCK_STAY) && (dock_mode != DOA_DOCK)) {
8546                 if (maybe_recreate_path(objp, &Ai_info[Ships[objp->instance].ai_index], 0) > 5.0f) {
8547 /*                      if (dock_mode == DOA_APPROACH) {
8548                                 return DOCK_BACKUP_RETURN_VAL;
8549                         } else if (dock_mode == DOA_DOCK) {
8550                                 return DOCK_BACKUP_RETURN_VAL;          
8551                         }
8552 */              }
8553         }
8554
8555         objp->phys_info.forward_thrust = 0.0f;          //      Kill thrust so we don't have a sputtering thruster.
8556
8557         sip0 = &Ship_info[Ships[objp->instance].ship_info_index];
8558         sip1 = &Ship_info[Ships[dobjp->instance].ship_info_index];
8559         pm0 = model_get( sip0->modelnum );
8560         pm1 = model_get( sip1->modelnum );
8561
8562         docker_index = aip->dock_index;
8563         dockee_index = aip->dockee_index;
8564
8565         Assert( docker_index >= 0 );
8566         Assert( dockee_index >= 0 );
8567
8568         Assert(pm0->docking_bays[docker_index].num_slots == 2);
8569         Assert(pm1->docking_bays[dockee_index].num_slots == 2);
8570
8571         float speed_scale = 1.0f;
8572         if (sip0->flags & SIF_SUPPORT) {
8573                 speed_scale = 3.0f;
8574         }
8575
8576         switch (dock_mode) {
8577         case DOA_APPROACH:
8578                 {
8579                 if (ship_get_subsystem_strength(&Ships[objp->instance], SUBSYSTEM_ENGINE) <= 0.0f) {
8580                         return 9999.9f;
8581                 }
8582                 
8583                 //      Compute the desired global orientation matrix for the docker's station.
8584                 //      That is, the normal vector of the docking station must be the same as the
8585                 //      forward vector and the vector between its two points must be the uvec.
8586                 set_goal_dock_orient(&dom, &pm1->docking_bays[dockee_index], &pm0->docking_bays[docker_index], &dobjp->orient, &objp->orient);
8587
8588                 //      Compute new orientation matrix and update rotational velocity.
8589                 vector  w_in, w_out, vel_limit, acc_limit;
8590                 float           tdist, mdist, ss1;
8591
8592                 w_in = objp->phys_info.rotvel;
8593                 vel_limit = objp->phys_info.max_rotvel;
8594                 vm_vec_copy_scale(&acc_limit, &vel_limit, 0.3f);
8595                 
8596                 if (sip0->flags & SIF_SUPPORT)
8597                         vm_vec_scale(&acc_limit, 2.0f);
8598
8599                 // 1 at end of line prevent overshoot
8600                 vm_matrix_interpolate(&dom, &objp->orient, &w_in, flFrametime, &nm, &w_out, &vel_limit, &acc_limit, 1);
8601                 objp->phys_info.rotvel = w_out;
8602                 objp->orient = nm;
8603
8604                 //      Translate towards goal and note distance to goal.
8605                 goal_point = Path_points[aip->path_cur].pos;
8606                 mdist = ai_matrix_dist(&objp->orient, &dom);
8607                 tdist = vm_vec_dist_quick(&objp->pos, &goal_point);
8608
8609                 //      If translation is badly lagging rotation, speed up translation.
8610                 if (mdist > 0.1f) {
8611                         ss1 = tdist/(10.0f * mdist);
8612                         if (ss1 > 2.0f)
8613                                 ss1 = 2.0f;
8614                 } else
8615                         ss1 = 2.0f;
8616
8617                 // nprintf(("AI", "speed scale = %7.3f\n", ss1));
8618                 speed_scale *= 1.0f + ss1;
8619
8620                 dock_move_towards_point(objp, NULL, &goal_point, speed_scale, dobjp->phys_info.speed);
8621
8622                 fdist = vm_vec_dist_quick(&objp->pos, &goal_point);
8623
8624                 //      Note, we're interested in distance from goal, so if we're still turning, bash that into return value.
8625                 // nprintf(("AI", "matrix dist = %7.3f, threshold = %7.3f\n", mdist, 2*flFrametime));
8626                 fdist += 2.0f * mdist;
8627
8628                 break;
8629         }
8630         case DOA_DOCK:
8631                 if (ship_get_subsystem_strength(&Ships[objp->instance], SUBSYSTEM_ENGINE) <= 0.0f) {
8632                         return 9999.9f;
8633                 }
8634         case DOA_DOCK_STAY:
8635                 //      Compute the desired global orientation matrix for the docker's station.
8636                 //      That is, the normal vector of the docking station must be the same as the
8637                 //      forward vector and the vector between its two points must be the uvec.
8638                 set_goal_dock_orient(&dom, &pm1->docking_bays[dockee_index], &pm0->docking_bays[docker_index], &dobjp->orient, &objp->orient);
8639
8640                 //      Compute distance between dock bay points.
8641                 vector  db0, db1, db2, db3;
8642
8643                 vm_vec_unrotate(&db0, &pm0->docking_bays[docker_index].pnt[0], &objp->orient);
8644                 vm_vec_add2(&db0, &objp->pos);
8645
8646                 vm_vec_unrotate(&db1, &pm0->docking_bays[docker_index].pnt[1], &objp->orient);
8647                 vm_vec_add2(&db1, &objp->pos);
8648
8649                 vm_vec_unrotate(&db2, &pm1->docking_bays[dockee_index].pnt[0], &dobjp->orient);
8650                 vm_vec_add2(&db2, &dobjp->pos);
8651
8652                 vm_vec_unrotate(&db3, &pm1->docking_bays[dockee_index].pnt[1], &dobjp->orient);
8653                 vm_vec_add2(&db3, &dobjp->pos);
8654
8655                 vm_vec_avg(&goal_point, &db2, &db3);
8656
8657                 vm_vec_avg(&docker_point, &db0, &db1);
8658                 vm_vec_sub2(&docker_point, &objp->pos);
8659
8660                 if (dock_mode == DOA_DOCK) {
8661                         vector  t1, t2;
8662                         vector  w_in, w_out, vel_limit, acc_limit;
8663
8664                         fdist = vm_vec_dist_quick(vm_vec_avg(&t1, &db0, &db1), vm_vec_avg(&t2, &db2, &db3));
8665
8666                         //      Compute new orientation matrix and update rotational velocity.
8667                         w_in = objp->phys_info.rotvel;
8668                         vel_limit = objp->phys_info.max_rotvel;
8669                         vm_vec_copy_scale(&acc_limit, &vel_limit, 0.3f);
8670
8671                         if (sip0->flags & SIF_SUPPORT)
8672                                 vm_vec_scale(&acc_limit, 2.0f);
8673
8674                         vm_matrix_interpolate(&dom, &objp->orient, &w_in, flFrametime, &nm, &w_out, &vel_limit, &acc_limit);
8675                         objp->phys_info.rotvel = w_out;
8676                         objp->orient = nm;
8677
8678                         //      Note, we're interested in distance from goal, so if we're still turning, bash that into return value.
8679                         fdist += 10.0f * vm_vec_mag_quick(&w_out);
8680
8681                         dock_move_towards_point(objp, &docker_point, &goal_point, speed_scale, dobjp->phys_info.speed);
8682                 } else {
8683                         Assert(dock_mode == DOA_DOCK_STAY);
8684                         objp->orient = dom;
8685                         vector  temp;
8686                         vm_vec_sub(&temp, &goal_point, &docker_point);
8687                         vm_vec_sub(&objp->pos, &goal_point, &docker_point);
8688                 }
8689
8690                 break;
8691         case DOA_UNDOCK_1: {
8692                 if (ship_get_subsystem_strength(&Ships[objp->instance], SUBSYSTEM_ENGINE) <= 0.0f) {
8693                         return 9999.9f;
8694                 }
8695
8696                 //      Undocking.
8697                 //      Move to point on dock path nearest to dock station.
8698                 Assert(aip->path_length >= 2);
8699                 goal_point = Path_points[aip->path_start + aip->path_length-2].pos;
8700
8701                 vm_vec_zero(&docker_point);
8702                 fdist = vm_vec_dist_quick(&objp->pos, &goal_point);
8703
8704                 dock_move_towards_point(objp, &docker_point, &goal_point, speed_scale);
8705
8706                 break;
8707                           }
8708
8709         case DOA_UNDOCK_2: {
8710                 //      Undocking.
8711                 //      Move to point on dock path nearest to dock station and orient away from big ship.
8712                 int             desired_index;
8713
8714                 if (ship_get_subsystem_strength(&Ships[objp->instance], SUBSYSTEM_ENGINE) <= 0.0f) {
8715                         return 9999.9f;
8716                 }
8717
8718                 Assert(aip->path_length >= 2);
8719 //              if (aip->path_length >= 3)
8720 //                      desired_index = aip->path_length-3;
8721 //              else
8722                         desired_index = aip->path_length-2;
8723
8724                 goal_point = Path_points[aip->path_start + desired_index].pos;
8725
8726                 dock_move_towards_point(objp, NULL, &goal_point, speed_scale);
8727
8728                 fdist = vm_vec_dist_quick(&objp->pos, &goal_point);
8729                 break;
8730                           }
8731         case DOA_UNDOCK_3: {
8732                 float           dist, goal_dist;
8733                 vector  away_vec;
8734
8735                 goal_dist = objp->radius + dobjp->radius + 25.0f;
8736
8737                 dist = vm_vec_normalized_dir(&away_vec, &objp->pos, &dobjp->pos);
8738                 vm_vec_scale_add(&goal_point, &dobjp->pos, &away_vec, goal_dist);
8739                 if (vm_vec_dist_quick(&goal_point, &dobjp->pos) < vm_vec_dist_quick(&objp->pos, &dobjp->pos))
8740                         fdist = 0.0f;
8741                 else {
8742                         float   dot, accel;
8743                         float turn_time = Ship_info[Ships[objp->instance].ship_info_index].srotation_time;
8744                         ai_turn_towards_vector(&goal_point, objp, flFrametime, turn_time, NULL, NULL, 0.0f, 0);
8745
8746                         dot = vm_vec_dot(&objp->orient.fvec, &away_vec);
8747                         accel = 0.1f;
8748                         if (dot > accel)
8749                                 accel = dot;
8750                         if (dist > goal_dist/2)
8751                                 accel *= 1.2f - 0.5f*goal_dist/dist;
8752
8753                         accelerate_ship(aip, accel);
8754                         fdist = vm_vec_dist_quick(&objp->pos, &goal_point);
8755                 }
8756
8757                 break;
8758                                                          }
8759         }
8760
8761 #ifndef NDEBUG
8762         //      For debug purposes, compute global orientation of both dock vectors and show
8763         //      how close they are.
8764         vector  d0, d1;
8765
8766         vm_vec_unrotate(&d0, &pm0->docking_bays[docker_index].norm[0], &objp->orient);
8767         vm_vec_unrotate(&d1, &pm1->docking_bays[dockee_index].norm[0], &dobjp->orient);
8768
8769         //nprintf(("AI", "or/app: dist = %7.3f/%7.3f, dot = %7.3f, global dot = %7.3f\n", 
8770         //      vm_vec_dist_quick(&goal_point, &objp->pos), fdist,
8771         //      vm_vec_dot(&objp->orient.fvec, &dom.fvec), 
8772         //      vm_vec_dot(&d0, &d1)));
8773 #endif
8774
8775         // -- Note, A lot of callers don't care about fdist, so OK to return ERROR value: Assert(fdist != UNINITIALIZED_VALUE);
8776         return fdist;
8777
8778 }
8779
8780 void debug_find_guard_object()
8781 {
8782         ship                    *shipp = &Ships[Pl_objp->instance];     
8783         object          *objp;
8784
8785         for ( objp = GET_FIRST(&obj_used_list); objp !=END_OF_LIST(&obj_used_list); objp = GET_NEXT(objp) ) {
8786                 if ((Pl_objp != objp) && (objp->type == OBJ_SHIP)) {
8787                         if (objp->instance != -1) {
8788                                 if (Ships[objp->instance].team == shipp->team)  {
8789                                         // nprintf(("AI", "Setting guard object for %s to %s\n", shipp->ship_name, Ships[objp->instance].ship_name));
8790                                         ai_set_guard_object(Pl_objp, objp);
8791                                 }
8792                         }
8793                 }
8794         }
8795
8796 }
8797
8798 //      Given an object number, return the number of ships attacking it.
8799 int num_ships_attacking(int objnum)
8800 {
8801         object  *objp;
8802         ship_obj        *so;
8803         int             count = 0;
8804
8805         for ( so = GET_FIRST(&Ship_obj_list); so != END_OF_LIST(&Ship_obj_list); so = GET_NEXT(so) ) {
8806                 objp = &Objects[so->objnum];
8807                 if (objp->instance != -1) {
8808                         ai_info *aip;
8809                         aip = &Ai_info[Ships[objp->instance].ai_index];
8810
8811                         if ((aip->mode == AIM_CHASE) && (aip->target_objnum == objnum))
8812                                 if (Ships[objp->instance].team != Ships[Objects[objnum].instance].team)
8813                                         count++;
8814                 }
8815         }
8816
8817         return count;
8818 }
8819
8820 //      For all objects attacking object #objnum, remove the one that is farthest away.
8821 //      Do this by resuming previous behavior, if any.  If not, set target_objnum to -1.
8822 void remove_farthest_attacker(int objnum)
8823 {
8824         object  *objp, *objp2, *farthest_objp;
8825         ship_obj        *so;
8826         float           farthest_dist;
8827
8828         objp2 = &Objects[objnum];
8829
8830         farthest_dist = 9999999.9f;
8831         farthest_objp = NULL;
8832
8833         for ( so = GET_FIRST(&Ship_obj_list); so != END_OF_LIST(&Ship_obj_list); so = GET_NEXT(so) ) {
8834                 objp = &Objects[so->objnum];
8835                 if ( !(objp->flags & OF_PLAYER_SHIP)) {
8836                         if (objp->instance != -1) {
8837                                 ai_info *aip2;
8838
8839                                 aip2 = &Ai_info[Ships[objp->instance].ai_index];
8840
8841                                 if ((aip2->mode == AIM_CHASE) && (aip2->target_objnum == objnum)) {
8842                                         if (Ships[objp->instance].team != Ships[Objects[objnum].instance].team) {
8843                                                 float   dist;
8844
8845                                                 dist = vm_vec_dist_quick(&objp->pos, &objp2->pos);
8846                                                 if (dist < farthest_dist) {
8847                                                         farthest_dist = dist;
8848                                                         farthest_objp = objp;
8849                                                 }
8850                                         }
8851                                 }
8852                         }
8853                 }
8854         }
8855
8856         if (farthest_objp != NULL) {
8857                 ai_info *aip;
8858                 Assert(farthest_objp->type == OBJ_SHIP);
8859                 Assert((farthest_objp->instance > -1) && (farthest_objp->instance < MAX_SHIPS));
8860                 Assert(Ships[farthest_objp->instance].ai_index > -1);
8861
8862                 aip = &Ai_info[Ships[farthest_objp->instance].ai_index];
8863
8864                 if (!maybe_resume_previous_mode(Pl_objp, aip)) {
8865                         //      If already ignoring something under player's orders, don't ignore current target.
8866                         if ((aip->ignore_objnum == UNUSED_OBJNUM) || (aip->ai_flags & AIF_TEMPORARY_IGNORE)) {
8867                                 aip->ignore_objnum = aip->target_objnum;
8868                                 aip->ignore_signature = Objects[aip->target_objnum].signature;
8869                                 aip->ai_flags |= AIF_TEMPORARY_IGNORE;
8870                                 aip->ignore_expire_timestamp = timestamp(((myrand() % 10) + 20) * 1000);        //      OK to attack again in 20 to 24 seconds.
8871                         }
8872                         aip->target_objnum = -1;
8873                         ai_do_default_behavior(farthest_objp);
8874                 }
8875         }
8876 }
8877
8878 // Maybe limit the number of attackers on attack_objnum.  For now, only limit attackers
8879 // in attacked_objnum is the player
8880 // input:       attacked_objnum =>              object index for ship we want to limit attacks on
8881 //
8882 //      exit:                   1       =>      num attackers exceeds maximum, abort
8883 //                                      0       =>      removed the farthest attacker
8884 //                                      -1      =>      nothing was done
8885 int ai_maybe_limit_attackers(int attacked_objnum)
8886 {
8887         int rval=-1;
8888
8889         // limit the number of ships attacking the _player_ only
8890 //      if ( attacked_objnum == OBJ_INDEX(Player_obj) ) {
8891         if ( Objects[attacked_objnum].flags & OF_PLAYER_SHIP) {
8892                 int num_attacking;
8893                 num_attacking = num_ships_attacking(attacked_objnum);
8894
8895                 if (num_attacking == Skill_level_max_attackers[Game_skill_level]) {
8896                         remove_farthest_attacker(attacked_objnum);
8897                         rval=0;
8898                 } else if (num_attacking > Skill_level_max_attackers[Game_skill_level]) {
8899                         rval=1;
8900                 }
8901                 //nprintf(("AI", "Num attacking player = %i\n", num_attacking));
8902         }
8903
8904         return rval;
8905 }
8906
8907 //      Object being guarded by object *guard_objp was hit by object *hitter_objp
8908 void guard_object_was_hit(object *guard_objp, object *hitter_objp)
8909 {
8910         int             hitter_objnum;
8911         ai_info *aip;
8912
8913         aip = &Ai_info[Ships[guard_objp->instance].ai_index];
8914
8915         if (guard_objp == hitter_objp) {
8916                 // Int3();      //      Bogus!  Who tried to get me to attack myself!  Trace out and fix!
8917                 return;
8918         }
8919
8920         if (guard_objp->type == OBJ_GHOST || hitter_objp->type == OBJ_GHOST)
8921                 return;
8922
8923         if (aip->ai_flags & AIF_NO_DYNAMIC)     //      Not allowed to pursue dynamic goals.  So, why are we guarding?
8924                 return;
8925
8926         Assert( (hitter_objp->type == OBJ_SHIP) || (hitter_objp->type == OBJ_ASTEROID) || (hitter_objp->type == OBJ_WEAPON) );
8927
8928         hitter_objnum = OBJ_INDEX(hitter_objp);
8929
8930         if ( hitter_objp->type == OBJ_SHIP ) {
8931                 //      If the hitter object is the ignore object, don't attack it.
8932                 if (is_ignore_object(aip, hitter_objp-Objects))
8933                         return;
8934
8935                 //      If hitter is on same team as me, don't attack him.
8936                 if (Ships[guard_objp->instance].team == Ships[hitter_objp->instance].team)
8937                         return;
8938
8939                 // limit the number of ships attacking hitter_objnum (for now, only if hitter_objnum is player)
8940                 if ( ai_maybe_limit_attackers(hitter_objnum) == 1 ) {
8941                         return;
8942                 }
8943
8944                 // dont attack if you can't see him
8945                 if ( awacs_get_level(hitter_objp, &Ships[aip->shipnum], 1) < 1 ) {
8946                         // if he's a stealth and visible, but not targetable, ok to attack.
8947                         if ( is_object_stealth_ship(hitter_objp) ) {
8948                                 if ( ai_is_stealth_visible(guard_objp, hitter_objp) != STEALTH_VISIBLE ) {
8949                                         return;
8950                                 }
8951                         }
8952                 }
8953         }
8954
8955         if (aip->target_objnum == -1) {
8956                 aip->ok_to_target_timestamp = timestamp(0);
8957         }
8958
8959         if ((aip->submode == AIS_GUARD_PATROL) || (aip->submode == AIS_GUARD_STATIC)) {
8960
8961                 if ( hitter_objp->type == OBJ_SHIP ) {
8962                         if (!(Ship_info[Ships[guard_objp->instance].ship_info_index].flags & SIF_SMALL_SHIP)) {
8963                                 return;
8964                         }
8965
8966                         // limit the number of ships attacking hitter_objnum (for now, only if hitter_objnum is player)
8967                         if ( ai_maybe_limit_attackers(hitter_objnum) == 1 ) {
8968                                 return;
8969                         }
8970                 }
8971
8972                 if (aip->target_objnum != hitter_objnum) {
8973                         aip->aspect_locked_time = 0.0f;
8974                 }
8975
8976                 aip->ok_to_target_timestamp = timestamp(0);
8977
8978                 set_target_objnum(aip, hitter_objnum);
8979                 //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));
8980                 aip->previous_mode = AIM_GUARD;
8981                 aip->previous_submode = aip->submode;
8982                 aip->mode = AIM_CHASE;
8983                 aip->submode = SM_ATTACK;
8984                 aip->submode_start_time = Missiontime;
8985                 aip->active_goal = AI_ACTIVE_GOAL_DYNAMIC;
8986         } else if (aip->previous_mode == AIM_GUARD) {
8987                 if (aip->target_objnum == -1) {
8988
8989                         if ( hitter_objp->type == OBJ_SHIP ) {
8990                                 // limit the number of ships attacking hitter_objnum (for now, only if hitter_objnum is player)
8991                                 if ( ai_maybe_limit_attackers(hitter_objnum) == 1 ) {
8992                                         return;
8993                                 }
8994                         }
8995
8996                         set_target_objnum(aip, hitter_objnum);
8997                 //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));
8998                         aip->mode = AIM_CHASE;
8999                         aip->submode = SM_ATTACK;
9000                         aip->submode_start_time = Missiontime;
9001                         aip->active_goal = AI_ACTIVE_GOAL_DYNAMIC;
9002                 } else {
9003                         int     num_attacking_cur, num_attacking_new;
9004
9005                         num_attacking_cur = num_ships_attacking(aip->target_objnum);
9006                         if (num_attacking_cur > 1) {
9007                                 num_attacking_new = num_ships_attacking(hitter_objnum);
9008
9009                                 if (num_attacking_new < num_attacking_cur) {
9010
9011                                         if ( hitter_objp->type == OBJ_SHIP ) {
9012                                                 // limit the number of ships attacking hitter_objnum (for now, only if hitter_objnum is player)
9013                                                 if ( ai_maybe_limit_attackers(hitter_objnum) == 1 ) {
9014                                                         return;
9015                                                 }
9016                                         }
9017                                         set_target_objnum(aip, hitter_objp-Objects);
9018                 //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));
9019                                         aip->mode = AIM_CHASE;
9020                                         aip->submode = SM_ATTACK;
9021                                         aip->submode_start_time = Missiontime;
9022                                         aip->active_goal = AI_ACTIVE_GOAL_DYNAMIC;
9023                                 }
9024                         }
9025                 }
9026         }
9027 }
9028
9029 //      Ship object *hit_objp was hit by ship object *hitter_objp.
9030 //      See if anyone is guarding hit_objp and, if so, do something useful.
9031 void maybe_update_guard_object(object *hit_objp, object *hitter_objp)
9032 {
9033         object  *objp;
9034         ship_obj        *so;
9035
9036         for ( so = GET_FIRST(&Ship_obj_list); so != END_OF_LIST(&Ship_obj_list); so = GET_NEXT(so) ) {
9037                 objp = &Objects[so->objnum];
9038                 if (objp->instance != -1) {
9039                         ai_info *aip;
9040                         aip = &Ai_info[Ships[objp->instance].ai_index];
9041
9042                         if ((aip->mode == AIM_GUARD) || (aip->active_goal == AI_ACTIVE_GOAL_DYNAMIC)) {
9043                                 if (aip->guard_objnum == hit_objp-Objects) {
9044                                         guard_object_was_hit(objp, hitter_objp);
9045                                 } else if ((aip->guard_wingnum != -1) && (aip->guard_wingnum == Ai_info[Ships[hit_objp->instance].ai_index].wing)) {
9046                                         guard_object_was_hit(objp, hitter_objp);
9047                                 }
9048                         }
9049                 }
9050         }
9051 }
9052
9053 // Scan missile list looking for bombs homing on guarded_objp
9054 // return 1 if bomb is found (and targeted by guarding_objp), otherwise return 0
9055 int ai_guard_find_nearby_bomb(object *guarding_objp, object *guarded_objp)
9056 {       
9057         missile_obj     *mo;
9058         object          *bomb_objp, *closest_bomb_objp=NULL;
9059         float                   dist, dist_to_guarding_obj,closest_dist_to_guarding_obj=999999.0f;
9060         weapon          *wp;
9061         weapon_info     *wip;
9062
9063         for ( mo = GET_NEXT(&Missile_obj_list); mo != END_OF_LIST(&Missile_obj_list); mo = GET_NEXT(mo) ) {
9064                 Assert(mo->objnum >= 0 && mo->objnum < MAX_OBJECTS);
9065                 bomb_objp = &Objects[mo->objnum];
9066
9067                 wp = &Weapons[bomb_objp->instance];
9068                 wip = &Weapon_info[wp->weapon_info_index];
9069
9070                 if ( !(wip->wi_flags & WIF_BOMB) ) {
9071                         continue;
9072                 }
9073
9074                 if ( wp->homing_object != guarded_objp ) {
9075                         continue;
9076                 }
9077
9078                 dist = vm_vec_dist_quick(&bomb_objp->pos, &guarded_objp->pos);
9079
9080                 if (dist < (MAX_GUARD_DIST + guarded_objp->radius)*3) {
9081                         dist_to_guarding_obj = vm_vec_dist_quick(&bomb_objp->pos, &guarding_objp->pos);
9082                         if ( dist_to_guarding_obj < closest_dist_to_guarding_obj ) {
9083                                 closest_dist_to_guarding_obj = dist_to_guarding_obj;
9084                                 closest_bomb_objp = bomb_objp;
9085                         }
9086                 }
9087         }
9088
9089         if ( closest_bomb_objp ) {
9090                 guard_object_was_hit(guarding_objp, closest_bomb_objp);
9091                 return 1;
9092         }
9093
9094         return 0;
9095 }
9096
9097 //      Scan enemy ships and see if one is near enough to guard object to be pursued.
9098 void ai_guard_find_nearby_ship(object *guarding_objp, object *guarded_objp)
9099 {
9100         ship            *guarding_shipp = &Ships[guarding_objp->instance];
9101         ai_info *guarding_aip = &Ai_info[guarding_shipp->ai_index];
9102         ship_obj        *so;
9103         object  *enemy_objp;
9104         float           dist;
9105
9106         for ( so = GET_FIRST(&Ship_obj_list); so != END_OF_LIST(&Ship_obj_list); so = GET_NEXT(so) ) {
9107                 enemy_objp = &Objects[so->objnum];
9108
9109                 if (enemy_objp->instance < 0) {
9110                         continue;
9111                 }
9112
9113                 ship    *eshipp = &Ships[enemy_objp->instance];
9114
9115                 //      Don't attack a cargo container or other harmless ships
9116                 if (!(Ship_info[eshipp->ship_info_index].flags & SIF_HARMLESS)) {
9117                         if (guarding_shipp->team != eshipp->team)       {
9118                                 dist = vm_vec_dist_quick(&enemy_objp->pos, &guarded_objp->pos);
9119                                 if (dist < (MAX_GUARD_DIST + guarded_objp->radius)*3) {
9120                                         guard_object_was_hit(guarding_objp, enemy_objp);
9121                                 } else if ((dist < 3000.0f) && (Ai_info[eshipp->ai_index].target_objnum == guarding_aip->guard_objnum)) {
9122                                         //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));
9123                                         guard_object_was_hit(guarding_objp, enemy_objp);
9124                                 }
9125                         }
9126                 }
9127         }
9128 }
9129
9130 // Scan for nearby asteroids.  Favor asteroids which have their collide_objnum set to that of the
9131 // guarded ship.  Also, favor asteroids that are closer to the guarding ship, since it looks cooler
9132 // when a ship blows up an asteroid then goes after the pieces that break off.
9133 void ai_guard_find_nearby_asteroid(object *guarding_objp, object *guarded_objp)
9134 {       
9135         float           dist;
9136
9137         object  *closest_asteroid_objp=NULL, *danger_asteroid_objp=NULL, *asteroid_objp;
9138         float           dist_to_self, closest_danger_asteroid_dist=999999.0f, closest_asteroid_dist=999999.0f;
9139
9140         for ( asteroid_objp = GET_FIRST(&obj_used_list); asteroid_objp != END_OF_LIST(&obj_used_list); asteroid_objp = GET_NEXT(asteroid_objp) ) {
9141                 if ( asteroid_objp->type == OBJ_ASTEROID ) {
9142                         // Attack asteroid if near guarded ship
9143                         dist = vm_vec_dist_quick(&asteroid_objp->pos, &guarded_objp->pos);
9144                         if ( dist < (MAX_GUARD_DIST + guarded_objp->radius)*2) {
9145                                 dist_to_self = vm_vec_dist_quick(&asteroid_objp->pos, &guarding_objp->pos);
9146                                 if ( OBJ_INDEX(guarded_objp) == asteroid_collide_objnum(asteroid_objp) ) {
9147                                         if( dist_to_self < closest_danger_asteroid_dist ) {
9148                                                 danger_asteroid_objp=asteroid_objp;
9149                                                 closest_danger_asteroid_dist=dist_to_self;
9150                                         }
9151                                 } 
9152                                 if ( dist_to_self < closest_asteroid_dist ) {
9153                                         // only attack if moving slower than own max speed
9154                                         if ( vm_vec_mag_quick(&asteroid_objp->phys_info.vel) < guarding_objp->phys_info.max_vel.z ) {
9155                                                 closest_asteroid_dist = dist_to_self;
9156                                                 closest_asteroid_objp = asteroid_objp;
9157                                         }
9158                                 }
9159                         }
9160                 }
9161         }
9162
9163         if ( danger_asteroid_objp ) {
9164                 guard_object_was_hit(guarding_objp, danger_asteroid_objp);
9165         } else if ( closest_asteroid_objp ) {
9166                 guard_object_was_hit(guarding_objp, closest_asteroid_objp);
9167         }
9168 }
9169
9170 //      Scan potential harmful objects and see if one is near enough to guard object to be pursued.
9171 void ai_guard_find_nearby_object()
9172 {
9173         ship                    *shipp = &Ships[Pl_objp->instance];
9174         ai_info         *aip = &Ai_info[shipp->ai_index];
9175         object          *guardobjp;
9176         int                     bomb_found=0;
9177
9178         guardobjp = &Objects[aip->guard_objnum];
9179         
9180         // highest priority is a bomb fired on guarded ship
9181         bomb_found = ai_guard_find_nearby_bomb(Pl_objp, guardobjp);
9182
9183         if ( !bomb_found ) {
9184                 // check for ships if there are no bombs fired at guarded ship
9185                 ai_guard_find_nearby_ship(Pl_objp, guardobjp);
9186
9187                 // if not attacking anything, go for asteroid close to guarded ship
9188                 if ( (aip->target_objnum == -1) && asteroid_count() ) {
9189                         ai_guard_find_nearby_asteroid(Pl_objp, guardobjp);
9190                 }
9191         }
9192 }
9193
9194 // gets closest point on extended axis of cylinder, r_vec, and radius of cylinder
9195 // returns z of axis_point in cyl_objp reference frame
9196 float get_cylinder_points(object *other_objp, object *cyl_objp, vector *axis_pt, vector *r_vec, float *radius)
9197 {
9198         Assert(other_objp->type == OBJ_SHIP);
9199         Assert(cyl_objp->type == OBJ_SHIP);
9200
9201         // get radius of cylinder
9202         polymodel *pm = model_get(Ships[cyl_objp->instance].modelnum);
9203         float tempx, tempy;
9204         tempx = max(-pm->mins.x, pm->maxs.x);
9205         tempy = max(-pm->mins.y, pm->maxs.y);
9206         *radius = max(tempx, tempy);
9207
9208         // get vec from cylinder to other_obj
9209         vector r_sph;
9210         vm_vec_sub(&r_sph, &other_objp->pos, &cyl_objp->pos);
9211
9212         // get point on axis and on cylinder
9213         // extended_cylinder_z is along extended cylinder
9214         // cylinder_z is capped within cylinder
9215         float extended_cylinder_z = vm_vec_dotprod(&r_sph, &cyl_objp->orient.fvec);
9216
9217         // get pt on axis of extended cylinder
9218         vm_vec_scale_add(axis_pt, &cyl_objp->pos, &cyl_objp->orient.fvec, extended_cylinder_z);
9219
9220         // get r_vec (pos - axis_pt) normalized
9221         vm_vec_normalized_dir(r_vec, &other_objp->pos, axis_pt);
9222
9223         return extended_cylinder_z;
9224 }
9225
9226 // handler for guard behavior when guarding BIG ships
9227 //      When someone has attacked guarded ship, then attack that ship.
9228 // To attack another ship, switch out of guard mode into chase mode.
9229 void ai_big_guard()
9230 {
9231         
9232         ship                    *shipp = &Ships[Pl_objp->instance];
9233         ai_info         *aip = &Ai_info[shipp->ai_index];
9234         object          *guard_objp;
9235
9236         // sanity checks already done in ai_guard()
9237         guard_objp = &Objects[aip->guard_objnum];
9238
9239         switch (aip->submode) {
9240         case AIS_GUARD_STATIC:
9241         case AIS_GUARD_PATROL:
9242                 {
9243                 vector axis_pt, r_vec, theta_vec;
9244                 float radius, extended_z;
9245
9246                 // get random [0 to 1] based on OBJNUM
9247                 float objval = static_randf(Pl_objp-Objects);
9248
9249                 // get position relative to cylinder of guard_objp              
9250                 extended_z = get_cylinder_points(Pl_objp, guard_objp, &axis_pt, &r_vec, &radius);
9251                 vm_vec_crossprod(&theta_vec, &guard_objp->orient.fvec, &r_vec);
9252
9253                 // half ships circle each way
9254                 if (objval > 0.5f) {
9255                         vm_vec_negate(&theta_vec);
9256                 }
9257
9258                 float min_guard_dist = radius + Pl_objp->radius + 50.0f;
9259                 float desired_guard_dist = min_guard_dist + 0.5f * ((1.0f + objval) * MAX_GUARD_DIST);
9260                 float max_guard_dist =     min_guard_dist + 1.0f * ((1.0f + objval) * MAX_GUARD_DIST);
9261
9262                 // get z extents
9263                 float min_z, max_z, length;
9264                 polymodel *pm = model_get(Ships[guard_objp->instance].modelnum);
9265                 min_z = pm->mins.z;
9266                 max_z = pm->maxs.z;
9267                 length = max_z - min_z;
9268
9269                 // get desired z
9270                 // how often to choose new desired_z
9271                 // 1*(64) sec < 2000, 2*(64) < 2-4000 3*(64) > 4-8000, etc (Missiontime >> 22 is 64 sec intervals)
9272                 int time_choose = int(floor(log(length * 0.001) / log(2)));
9273                 float desired_z = min_z + length * static_randf( Pl_objp-Objects ^ (Missiontime >> (22 + time_choose)) );
9274
9275                 // get r from guard_ship
9276                 float cur_guard_rad = vm_vec_dist(&Pl_objp->pos, &axis_pt);
9277
9278                 // is ship within extents of cylinder of ship it is guarding
9279                 int inside = (extended_z > min_z) && (extended_z < min_z + length);
9280
9281                 vector goal_pt;
9282                 // maybe go into orbit mode
9283                 if (cur_guard_rad < max_guard_dist) {
9284                         if ( cur_guard_rad > min_guard_dist ) {
9285                                 if (inside) {
9286                                         // orbit
9287                                         vm_vec_scale_add(&goal_pt, &axis_pt, &r_vec, desired_guard_dist);
9288                                         vm_vec_scale_add2(&goal_pt, &theta_vec, desired_guard_dist);
9289                                 } else {
9290                                         // move to where I can orbit
9291                                         if (extended_z < min_z) {
9292                                                 vm_vec_scale_add(&goal_pt, &guard_objp->pos, &guard_objp->orient.fvec, min_z);
9293                                         } else {
9294                                                 vm_vec_scale_add(&goal_pt, &guard_objp->pos, &guard_objp->orient.fvec, max_z);
9295                                         }
9296                                         vm_vec_scale_add2(&goal_pt, &r_vec, desired_guard_dist);
9297                                         vm_vec_scale_add2(&goal_pt, &theta_vec, desired_guard_dist);
9298                                 }
9299                         } else {
9300                                 // too close for orbit mode
9301                                 if (inside) {
9302                                         // inside (fly straight out and return circle)
9303                                         vm_vec_scale_add(&goal_pt, &axis_pt, &r_vec, max_guard_dist);
9304                                 } else {
9305                                         // outside (fly to edge and circle)
9306                                         if (extended_z < min_z) {
9307                                                 vm_vec_scale_add(&goal_pt, &guard_objp->pos, &guard_objp->orient.fvec, min_z);
9308                                         } else {
9309                                                 vm_vec_scale_add(&goal_pt, &guard_objp->pos, &guard_objp->orient.fvec, max_z);
9310                                         }
9311                                         vm_vec_scale_add2(&goal_pt, &r_vec, max_guard_dist);
9312                                         vm_vec_scale_add2(&goal_pt, &theta_vec, desired_guard_dist);
9313                                 }
9314                         }
9315
9316                         if (Pl_objp->phys_info.fspeed > 0) {
9317                                 // modify goal_pt to take account moving guard objp
9318                                 float dist = vm_vec_dist_quick(&Pl_objp->pos, &goal_pt);
9319                                 float time = dist / Pl_objp->phys_info.fspeed;
9320                                 vm_vec_scale_add2(&goal_pt, &guard_objp->phys_info.vel, time);
9321
9322                                 // now modify to move to desired z (at a max of 20 m/s)
9323                                 float delta_z = desired_z - extended_z;
9324                                 float v_z = delta_z * 0.2f;
9325                                 if (v_z < -20) {
9326                                         v_z = -20.0f;
9327                                 } else if (v_z > 20) {
9328                                         v_z = 20.0f;
9329                                 }
9330
9331                                 vm_vec_scale_add2(&goal_pt, &guard_objp->orient.fvec, v_z*time);
9332                         }
9333
9334                 } else {
9335                         // cast vector to center of guard_ship adjusted by desired_z
9336                         float delta_z = desired_z - extended_z;
9337                         vm_vec_scale_add(&goal_pt, &guard_objp->pos, &guard_objp->orient.fvec, delta_z);
9338                 }
9339
9340                 // try not to bump into things along the way
9341                 if ( (cur_guard_rad > max_guard_dist) || (extended_z < min_z) || (extended_z > max_z) ) {
9342                         if (maybe_avoid_big_ship(Pl_objp, guard_objp, aip, &goal_pt, 5.0f)) {
9343                                 return;
9344                         }
9345
9346                         if (avoid_player(Pl_objp, &goal_pt)) {
9347                                 return;
9348                         }
9349                 } else {
9350                         if (maybe_avoid_big_ship(Pl_objp, guard_objp, aip, &goal_pt, 5.0f)) {
9351                                 return;
9352                         }
9353                 }
9354
9355                 // got the point, now let's go there
9356                 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);
9357 //              aip->goal_point = goal_pt;
9358                 accelerate_ship(aip, 1.0f);
9359
9360                 //      Periodically, scan for a nearby ship to attack.
9361                 if (((AI_FrameCount ^ (Pl_objp-Objects)) & 0x07) == 0) {
9362                         ai_guard_find_nearby_object();
9363                 }
9364                 }
9365                 break;
9366
9367         case AIS_GUARD_ATTACK:
9368                 //      The guarded ship has been attacked.  Do something useful!
9369                 ai_chase();
9370                 break;
9371
9372         default:
9373                 //Int3();       //      Illegal submode for Guard mode.
9374                 // AL 06/03/97 comment out Int3() to allow milestone to get out the door
9375                 aip->submode = AIS_GUARD_PATROL;
9376                 break;
9377         }
9378 }
9379
9380 //      Main handler for guard behavior.
9381 //      When someone has attacked guarded ship, then attack that ship.
9382 // To attack another ship, switch out of guard mode into chase mode.
9383 void ai_guard()
9384 {
9385         ship                    *shipp = &Ships[Pl_objp->instance];
9386         ai_info         *aip = &Ai_info[shipp->ai_index];
9387         object          *guard_objp;    
9388         ship                    *gshipp;
9389         float                   dist_to_guardobj, dot_to_guardobj;
9390         vector          vec_to_guardobj;
9391
9392         /*      //      Debug code, find an object to guard.
9393         int finding_guard_objnum = 0;   //      Debug code, to see if body of "if" below gets executed. 
9394         if (aip->guard_objnum == -1) {
9395                 finding_guard_objnum = 1;
9396                 debug_find_guard_object();
9397                 if (aip->guard_objnum == -1)
9398                         return;
9399         }
9400 */
9401         if (aip->guard_objnum == -1) {
9402                 aip->mode = AIM_NONE;
9403                 return;
9404         }
9405
9406         Assert(aip->guard_objnum != -1);
9407
9408         guard_objp = &Objects[aip->guard_objnum];
9409
9410         if (guard_objp == Pl_objp) {
9411                 Int3();         //      This seems illegal.  Why is a ship guarding itself?
9412                 aip->guard_objnum = -1;
9413                 return;
9414         }
9415
9416         // check that I have someone to guard
9417         if (guard_objp->instance == -1) {
9418                 return;
9419         }
9420
9421         //      Not sure whether this should be impossible, or a reasonable cleanup condition.
9422         //      For now (3/31/97), it's getting trapped by an Assert, so clean it up.
9423         if (guard_objp->type != OBJ_SHIP) {
9424                 aip->guard_objnum = -1;
9425                 return;
9426         }
9427
9428         // handler for gurad object with BIG radius
9429         if (guard_objp->radius > BIG_GUARD_RADIUS) {
9430                 ai_big_guard();
9431                 return;
9432         }
9433
9434         gshipp = &Ships[guard_objp->instance];
9435
9436         float                   objval;
9437         vector          goal_point;
9438         vector          rel_vec;
9439         float                   dist_to_goal_point, dot_to_goal_point, accel_scale;
9440         vector          v2g, rvec;
9441
9442         // get random [0 to 1] based on OBJNUM
9443         objval = static_randf(Pl_objp-Objects);
9444
9445         switch (aip->submode) {
9446         case AIS_GUARD_STATIC:
9447         case AIS_GUARD_PATROL:
9448                 //      Stay near ship
9449                 dist_to_guardobj = vm_vec_normalized_dir(&vec_to_guardobj, &guard_objp->pos, &Pl_objp->pos);
9450                 dot_to_guardobj = vm_vec_dot(&Pl_objp->orient.fvec, &vec_to_guardobj);
9451
9452                 rel_vec = aip->guard_vec;
9453                 vm_vec_add(&goal_point, &guard_objp->pos, &rel_vec);
9454
9455                 vm_vec_normalized_dir(&v2g, &goal_point, &Pl_objp->pos);
9456                 dist_to_goal_point = vm_vec_dist_quick(&goal_point, &Pl_objp->pos);
9457                 dot_to_goal_point = vm_vec_dot(&v2g, &Pl_objp->orient.fvec);
9458                 accel_scale = (1.0f + dot_to_goal_point)/2.0f;
9459
9460                 //      If far away, get closer
9461                 if (dist_to_goal_point > MAX_GUARD_DIST + 1.5 * (Pl_objp->radius + guard_objp->radius)) {
9462                         if (maybe_avoid_big_ship(Pl_objp, guard_objp, aip, &goal_point, 5.0f)) {
9463                                 return;
9464                         }
9465
9466                         if (avoid_player(Pl_objp, &goal_point)) {
9467                                 return;
9468                         }
9469
9470                         // quite far away, so try to go straight to 
9471                         compute_desired_rvec(&rvec, &goal_point, &Pl_objp->pos);
9472                         ai_turn_towards_vector(&goal_point, Pl_objp, flFrametime, Ship_info[shipp->ship_info_index].srotation_time, NULL, NULL, 0.0f, 0, &rvec);
9473
9474                         accelerate_ship(aip, accel_scale * (0.25f + dist_to_goal_point/700.0f));
9475                 } else {
9476                         if (maybe_avoid_big_ship(Pl_objp, guard_objp, aip, &goal_point, 2.0f)) {
9477                                 return;
9478                         }
9479
9480                         // get max of guard_objp (1) normal speed (2) dock speed
9481                         float speed = guard_objp->phys_info.speed;
9482
9483                         if (guard_objp->type == OBJ_SHIP) {
9484                                 ai_info *guard_aip = &Ai_info[Ships[guard_objp->instance].ai_index];
9485
9486                                 if (guard_aip->dock_objnum != -1) {
9487                                         speed = max(speed, Objects[guard_aip->dock_objnum].phys_info.speed);
9488                                 }
9489                         }
9490                         
9491                         //      Deal with guarding a small object.
9492                         //      If going to guard_vec might cause a collision with guarded object, pick a new guard point.
9493                         if (vm_vec_dot(&v2g, &vec_to_guardobj) > 0.8f) {
9494                                 if (dist_to_guardobj < dist_to_goal_point) {
9495                                         ai_set_guard_vec(Pl_objp, guard_objp);  //      OK to return here.
9496                                         return;
9497                                 }
9498                         } 
9499
9500                         if (speed > 10.0f) {
9501                                 //      If goal ship is moving more than a tiny bit, don't orbit it, get near it.
9502                                 if (vm_vec_dist_quick(&goal_point, &Pl_objp->pos) > 40.0f) {
9503                                         if (vm_vec_dot(&Pl_objp->orient.fvec, &v2g) < 0.0f) {
9504                                                 //      Just slow down, don't turn.
9505                                                 set_accel_for_target_speed(Pl_objp, guard_objp->phys_info.speed - dist_to_goal_point/10.0f);
9506                                         } else {
9507                                                 //      Goal point is in front.
9508
9509                                                 //      If close to goal point, don't change direction, just change speed.
9510                                                 if (dist_to_goal_point > Pl_objp->radius + 10.0f) {
9511                                                         turn_towards_point(Pl_objp, &goal_point, NULL, 0.0f);
9512                                                 }
9513                                                 
9514                                                 set_accel_for_target_speed(Pl_objp, guard_objp->phys_info.speed + (dist_to_goal_point-40.0f)/20.0f);
9515                                         }
9516                                 } else {
9517                                         if (dot_to_goal_point > 0.8f) {
9518                                                 turn_towards_point(Pl_objp, &goal_point, NULL, 0.0f);
9519                                                 set_accel_for_target_speed(Pl_objp, guard_objp->phys_info.speed + dist_to_goal_point*0.1f);
9520                                         } else {
9521                                                 set_accel_for_target_speed(Pl_objp, guard_objp->phys_info.speed - dist_to_goal_point*0.1f - 1.0f);
9522                                         }
9523                                 }
9524                         // consider guard object STILL
9525                         } else if (guard_objp->radius < 50.0f) {
9526                                 if (dist_to_goal_point > 15.0f) {
9527                                         turn_towards_point(Pl_objp, &goal_point, NULL, 0.0f);
9528                                         set_accel_for_target_speed(Pl_objp, (dist_to_goal_point-10.0f)/2.0f);
9529                                 } else if (Pl_objp->phys_info.speed < 1.0f) {
9530                                         turn_away_from_point(Pl_objp, &guard_objp->pos, 0.0f);
9531                                 }
9532                                 //      It's a big ship
9533                         } else if (dist_to_guardobj > MAX_GUARD_DIST + Pl_objp->radius + guard_objp->radius) {
9534                                 //      Orbiting ship, too far away
9535                                 float dot = turn_towards_tangent(Pl_objp, &guard_objp->pos, (1.0f + objval/2) * guard_objp->radius);
9536                                 accelerate_ship(aip, (1.0f + dot)/2.0f);
9537                         } else if (dist_to_guardobj < Pl_objp->radius + guard_objp->radius) {
9538                                 //      Orbiting ship, got too close
9539                                 turn_away_from_point(Pl_objp, &guard_objp->pos, 0.0f);
9540                                 if ((dist_to_guardobj > guard_objp->radius + Pl_objp->radius + 50.0f) && (guard_objp->phys_info.speed > Pl_objp->phys_info.speed - 1.0f))
9541                                         change_acceleration(aip, 0.25f);
9542                                 else
9543                                         accelerate_ship(aip, 0.5f + objval/4.0f);
9544                         } else {
9545                                 //      Orbiting ship, about the right distance away.
9546                                 float dot = turn_towards_tangent(Pl_objp, &guard_objp->pos, (1.5f + objval/2.0f)*guard_objp->radius);
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                                         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));
9549                                 else
9550                                         accelerate_ship(aip, 0.5f * (1.0f + dot) * (0.3f + objval/3.0f));
9551                         }
9552                 }
9553
9554                 //      Periodically, scan for a nearby ship to attack.
9555                 if (((AI_FrameCount ^ (Pl_objp-Objects)) & 0x07) == 0) {
9556                         ai_guard_find_nearby_object();
9557                 }
9558                 break;
9559
9560         case AIS_GUARD_ATTACK:
9561                 //      The guarded ship has been attacked.  Do something useful!
9562                 ai_chase();
9563
9564                 break;
9565         default:
9566                 //Int3();       //      Illegal submode for Guard mode.
9567                 // AL 06/03/97 comment out Int3() to allow milestone to get out the door
9568                 aip->submode = AIS_GUARD_PATROL;
9569                 break;
9570         }
9571
9572 }
9573
9574 // Return the object of the ship that the given object is docked
9575 // with.  Currently, we know a ship is docked when his ai_mode is AIM_DOCK,
9576 // and his submode is AIS_DOCK_3.  I suppose that this is likely to change though.
9577 // Also, the objnum that was is passed in may not be the object that actually
9578 // performed the docking maneuver.  This code will account for that case.
9579 object *ai_find_docked_object( object *docker )
9580 {
9581         ai_info *aip;
9582
9583         // we are trying to find the dockee of docker.  (Note that that these terms
9584         // are totally relative to what is passed in as a parameter.)
9585
9586         // first thing to attempt is to check and see if this object is docked with something.
9587         Assert( docker->type == OBJ_SHIP );             // this had probably better be a ship!!!
9588         aip = &Ai_info[Ships[docker->instance].ai_index];
9589         if ( !(aip->ai_flags & AIF_DOCKED) )            // flag not set if not docked with anything
9590                 return NULL;
9591
9592         if ( aip->dock_objnum == -1 ) {
9593                 Int3();                                                                                 // mwa says this is wrong wrong wrong
9594                 ai_do_objects_undocked_stuff( docker, NULL );
9595                 return NULL;
9596         }
9597
9598         return &Objects[aip->dock_objnum];
9599
9600 }
9601
9602
9603 // define for the points subtracted from score for a rearm started on a player.
9604 #define REPAIR_PENALTY          50
9605
9606
9607 // function to clean up ai flags, variables, and other interesting information
9608 // for a ship that was getting repaired.  The how parameter is useful for multiplayer
9609 // only in that it tells us why the repaired ship is being cleaned up.
9610 void ai_do_objects_repairing_stuff( object *repaired_objp, object *repair_objp, int how )
9611 {
9612         ai_info *aip, *repair_aip;
9613         int             stamp = -1;
9614
9615         Assert( repaired_objp->type == OBJ_SHIP);
9616         aip = &Ai_info[Ships[repaired_objp->instance].ai_index];
9617
9618         // multiplayer
9619         int p_index;
9620         p_index = -1;
9621         if(Game_mode & GM_MULTIPLAYER){
9622                 p_index = multi_find_player_by_object(repaired_objp);           
9623         }               
9624         else {          
9625                 if(repaired_objp == Player_obj){
9626                         p_index = Player_num;
9627                 }
9628         }
9629
9630         switch( how ) {
9631         case REPAIR_INFO_BEGIN:
9632                 aip->ai_flags |= AIF_BEING_REPAIRED;
9633                 aip->ai_flags &= ~AIF_AWAITING_REPAIR;
9634                 stamp = timestamp(-1);
9635
9636                 // if this is a player ship, then subtract the repair penalty from this player's score
9637                 if ( repaired_objp->flags & OF_PLAYER_SHIP ) {
9638                         if ( !(Game_mode & GM_MULTIPLAYER) ) {
9639                                 Player->stats.m_score -= (int)(REPAIR_PENALTY * scoring_get_scale_factor());                    // subtract the penalty
9640                         } else {
9641                                 /*
9642                                 int pnum;
9643
9644                                 // multiplayer game -- find the player, then subtract the score
9645                                 pnum = multi_find_player_by_object( repaired_objp );
9646                                 if ( pnum != -1 ) {
9647                                         Net_players[pnum].player->stats.m_score -= (int)(REPAIR_PENALTY * scoring_get_scale_factor());
9648
9649                                         // squad war
9650                                         multi_team_maybe_add_score(-(int)(REPAIR_PENALTY * scoring_get_scale_factor()), Net_players[pnum].p_info.team);
9651                                 } else {
9652                                         nprintf(("Network", "Couldn't find player for ship %s for repair penalty\n", Ships[repaired_objp->instance].ship_name));
9653                                 }
9654                                 */
9655                         }
9656                 }
9657                 break;
9658
9659         case REPAIR_INFO_BROKEN:
9660                 aip->ai_flags &= ~AIF_BEING_REPAIRED;
9661                 aip->ai_flags |= AIF_AWAITING_REPAIR;
9662                 stamp = timestamp((int) ((30 + 10*frand()) * 1000));
9663                 break;
9664
9665         case REPAIR_INFO_END:
9666                 // when only awaiting repair, and the repair is ended, then set dock_objnum to -1.
9667                 if ( aip->ai_flags & AIF_AWAITING_REPAIR ){
9668                         aip->dock_objnum = -1;
9669                 }
9670                 aip->ai_flags &= ~(AIF_AWAITING_REPAIR | AIF_BEING_REPAIRED );
9671                 stamp = timestamp((int) ((30 + 10*frand()) * 1000));
9672                 break;
9673
9674         case REPAIR_INFO_QUEUE:
9675                 aip->ai_flags |= AIF_AWAITING_REPAIR;
9676                 if ( aip == Player_ai ){
9677                         hud_support_view_start();
9678                 }
9679                 stamp = timestamp(-1);
9680                 break;
9681
9682         case REPAIR_INFO_ABORT:
9683         case REPAIR_INFO_KILLED:
9684                 // 5/4/98 -- MWA -- Need to set dock objnum to -1 to let code know this guy who was getting
9685                 // repaired (or queued for repair), isn't really going to be docked with anyone anymore.
9686                 aip->dock_objnum = -1;
9687                 aip->ai_flags &= ~AIF_DOCKED;
9688                 aip->ai_flags &= ~(AIF_AWAITING_REPAIR | AIF_BEING_REPAIRED );
9689                 if (repair_objp != NULL) {
9690                         repair_aip = &Ai_info[Ships[repair_objp->instance].ai_index];
9691                         repair_aip->ai_flags &= ~(AIF_AWAITING_REPAIR | AIF_BEING_REPAIRED );
9692                 }               
9693
9694                 if ( p_index >= 0 ) {
9695                         hud_support_view_abort();
9696
9697                         // send appropriate message to player here
9698                         if ( how == REPAIR_INFO_KILLED ){
9699                                 message_send_builtin_to_player( MESSAGE_SUPPORT_KILLED, NULL, MESSAGE_PRIORITY_HIGH, MESSAGE_TIME_SOON, 0, 0, p_index, -1 );
9700                         } else {
9701                                 if ( repair_objp ){
9702                                         message_send_builtin_to_player( MESSAGE_REPAIR_ABORTED, &Ships[repair_objp->instance], MESSAGE_PRIORITY_NORMAL, MESSAGE_TIME_SOON, 0, 0, p_index, -1 );
9703                                 }
9704                         }
9705                 }
9706
9707                 // add log entry if this is a player
9708                 if ( repaired_objp->flags & OF_PLAYER_SHIP ){
9709                         mission_log_add_entry(LOG_PLAYER_REARM_ABORT, Ships[repaired_objp->instance].ship_name, NULL);
9710                 }
9711
9712                 stamp = timestamp((int) ((30 + 10*frand()) * 1000));
9713                 break;
9714
9715         case REPAIR_INFO_COMPLETE:
9716                 // clear the being repaired flag -- and 
9717                 if ( p_index >= 0 ) {
9718                         Assert( repair_objp );
9719                         
9720                         hud_support_view_stop();                        
9721
9722                         message_send_builtin_to_player(MESSAGE_REPAIR_DONE, &Ships[repair_objp->instance], MESSAGE_PRIORITY_LOW, MESSAGE_TIME_SOON, 0, 0, p_index, -1);
9723                 }
9724                 stamp = timestamp((int) ((30 + 10*frand()) * 1000));
9725                 break;
9726
9727         case REPAIR_INFO_ONWAY:
9728                 // need to set the dock_signature so that clients in multiplayer games rearm correctly
9729                 Assert( repair_objp );
9730                 aip->dock_signature = repair_objp->signature; 
9731                 aip->dock_objnum = OBJ_INDEX(repair_objp);
9732                 stamp = timestamp(-1);
9733                 break;
9734
9735         default:
9736                 Int3();                 // bogus type of repair info
9737         }
9738
9739         if (repair_objp){
9740                 Ai_info[Ships[repair_objp->instance].ai_index].warp_out_timestamp = stamp;
9741         }
9742
9743         // repair_objp might be NULL is we are cleaning up this mode because of the support ship
9744         // getting killed.
9745         if ( repair_objp ) {
9746                 aip = &Ai_info[Ships[repair_objp->instance].ai_index];
9747                 switch ( how ) {
9748                 case REPAIR_INFO_ONWAY:
9749                         Assert( repaired_objp != NULL );
9750                         aip->goal_objnum = OBJ_INDEX(repaired_objp);
9751                         aip->ai_flags |= AIF_REPAIRING;
9752                         break;
9753
9754                 case REPAIR_INFO_BROKEN:
9755                         break;
9756
9757                 case REPAIR_INFO_END:
9758                 case REPAIR_INFO_ABORT:
9759                 case REPAIR_INFO_KILLED:
9760                         if ( how == REPAIR_INFO_ABORT )
9761                                 aip->goal_objnum = -1;
9762
9763                         aip->ai_flags &= ~AIF_REPAIRING;
9764                         break;
9765                         
9766                 case REPAIR_INFO_QUEUE:
9767                         ai_add_rearm_goal( repaired_objp, repair_objp );
9768                         break;
9769
9770                 case REPAIR_INFO_BEGIN:
9771                 case REPAIR_INFO_COMPLETE:
9772                         break;
9773
9774                 default:
9775                         Int3();         // bogus type of repair info
9776                 }
9777         }
9778
9779         multi_maybe_send_repair_info( repaired_objp, repair_objp, how );
9780 }
9781
9782 //      Cleanup AI stuff for when a ship was supposed to dock with another, but the ship
9783 //      it was supposed to dock with is no longer valid.
9784 void ai_cleanup_dock_mode(ai_info *aip, ship *shipp)
9785 {
9786         object *objp;
9787
9788         objp = &Objects[shipp->objnum];
9789         aip->mode = AIM_NONE;
9790
9791         if (aip->ai_flags & AIF_REPAIRING) {
9792                 Assert( aip->goal_objnum != -1 );
9793                 ai_do_objects_repairing_stuff( &Objects[aip->goal_objnum], &Objects[shipp->objnum], REPAIR_INFO_KILLED );
9794         } else if ( aip->ai_flags & AIF_BEING_REPAIRED ) {
9795                 // MWA -- note that we have to use dock_objnum here instead of goal_objnum.
9796                 Assert( aip->dock_objnum != -1 );
9797                 ai_do_objects_repairing_stuff( &Objects[shipp->objnum], &Objects[aip->dock_objnum], REPAIR_INFO_KILLED );
9798         } else if ( aip->ai_flags & AIF_AWAITING_REPAIR ) {
9799                 // need to find the support ship that has me as a goal_objnum
9800                 // MWA -- note that we have to use dock_objnum here instead of goal_objnum.
9801                 // MWA -- 3/38/98  Check to see if this guy is queued for a support ship, or there is already
9802                 // one in the mission
9803                 if ( mission_is_repair_scheduled(objp) ) {
9804                         mission_remove_scheduled_repair( objp );                        // this function will notify multiplayer clients.
9805                 } else {
9806                         if ( aip->dock_objnum != -1 )
9807                                 ai_do_objects_repairing_stuff( objp, &Objects[aip->dock_objnum], REPAIR_INFO_ABORT );
9808                         else
9809                                 ai_do_objects_repairing_stuff( objp, NULL, REPAIR_INFO_ABORT );
9810                 }
9811         }
9812
9813         if ( aip->ai_flags & AIF_DOCKED ) {
9814                 ai_info *other_aip;
9815
9816                 Assert( aip->dock_objnum != -1 );
9817
9818                 // if docked, and the dock_objnum is not undocking, force them to near last stage
9819                 other_aip = &Ai_info[Ships[Objects[aip->dock_objnum].instance].ai_index];
9820                 if ( (other_aip->mode == AIM_DOCK) && (other_aip->submode < AIS_UNDOCK_3) )
9821                         other_aip->submode = AIS_UNDOCK_3;
9822                 ai_do_objects_undocked_stuff( objp, &Objects[aip->dock_objnum] );
9823         }
9824 }
9825
9826 /*
9827 //      Make dockee_objp shake a bit due to docking.
9828 void ai_dock_shake(object *docker_objp, object *dockee_objp)
9829 {
9830         vector  tangles;
9831         matrix  rotmat, tmp;
9832         float           scale;
9833         angles  *ap;
9834
9835         scale = 0.25f;          //      Compute this based on mass and speed at time of docking.
9836
9837         vm_vec_rand_vec_quick(&tangles);
9838         vm_vec_scale(&tangles, scale);
9839
9840         ap = (angles *) &tangles;
9841
9842         vm_angles_2_matrix(&rotmat, ap);
9843         vm_matrix_x_matrix( &tmp, &dockee_objp->orient, &rotmat );
9844         dockee_objp->orient = tmp;
9845
9846         vm_orthogonalize_matrix(&dockee_objp->orient);
9847
9848         dock_orient_and_approach(docker_objp, dockee_objp, DOA_DOCK_STAY);
9849
9850 }
9851 */
9852
9853 //      Make Pl_objp point at aip->goal_point.
9854 void ai_still()
9855 {
9856         ship    *shipp;
9857         ai_info *aip;
9858
9859         Assert(Pl_objp->type == OBJ_SHIP);
9860         Assert((Pl_objp->instance >= 0) && (Pl_objp->instance < MAX_OBJECTS));
9861
9862         shipp = &Ships[Pl_objp->instance];
9863         Assert((shipp->ai_index >= 0) && (shipp->ai_index < MAX_AI_INFO));
9864
9865         aip = &Ai_info[shipp->ai_index];
9866
9867         turn_towards_point(Pl_objp, &aip->goal_point, NULL, 0.0f);
9868 }
9869
9870 //      Make *Pl_objp stay near another ship.
9871 void ai_stay_near()
9872 {
9873         ai_info *aip;
9874         int             goal_objnum;
9875
9876         aip = &Ai_info[Ships[Pl_objp->instance].ai_index];
9877
9878         goal_objnum = aip->goal_objnum;
9879
9880         if ((goal_objnum < 0) || (Objects[goal_objnum].type != OBJ_SHIP) || (Objects[goal_objnum].signature != aip->goal_signature)) {
9881                 aip->mode = AIM_NONE;
9882         } else {
9883                 float           dist, max_dist, scale;
9884                 vector  rand_vec, goal_pos, vec_to_goal;
9885                 object  *goal_objp;
9886
9887                 goal_objp = &Objects[goal_objnum];
9888
9889                 //      Make not all ships pursue same point.
9890                 static_randvec(Pl_objp-Objects, &rand_vec);
9891
9892                 //      Make sure point is in front hemisphere (relative to Pl_objp's position.
9893                 vm_vec_sub(&vec_to_goal, &goal_objp->pos, &Pl_objp->pos);
9894                 if (vm_vec_dot(&rand_vec, &vec_to_goal) > 1.0f) {
9895                         vm_vec_negate(&rand_vec);
9896                 }
9897
9898                 //      Scale the random vector by an amount proportional to the distance from Pl_objp to the true goal.
9899                 dist = vm_vec_dist_quick(&goal_objp->pos, &Pl_objp->pos);
9900                 max_dist = aip->stay_near_distance;
9901                 scale = dist - max_dist/2;
9902                 if (scale < 0.0f)
9903                         scale = 0.0f;
9904
9905                 vm_vec_scale_add(&goal_pos, &goal_objp->pos, &rand_vec, scale);
9906
9907                 if (max_dist < Pl_objp->radius + goal_objp->radius + 25.0f)
9908                         max_dist = Pl_objp->radius + goal_objp->radius + 25.0f;
9909
9910                 if (dist > max_dist) {
9911                         turn_towards_point(Pl_objp, &goal_pos, NULL, 0.0f);
9912                         accelerate_ship(aip, dist / max_dist - 0.8f);
9913                 }
9914         
9915         }
9916
9917 }
9918
9919 //      Warn player if dock path is obstructed.
9920 int maybe_dock_obstructed(object *cur_objp, object *goal_objp, int big_only_flag)
9921 {
9922         vector  *goalpos, *curpos;
9923         float           radius;
9924         ai_info *aip;
9925         int             collide_objnum;
9926
9927         aip = &Ai_info[Ships[cur_objp->instance].ai_index];
9928
9929         Ai_info[Ships[goal_objp->instance].ai_index].ai_flags &= ~AIF_REPAIR_OBSTRUCTED;
9930
9931         if (goal_objp != Player_obj)
9932                 return -1;
9933
9934         curpos = &cur_objp->pos;
9935         radius = cur_objp->radius;
9936         goalpos = &Path_points[aip->path_cur].pos;
9937         collide_objnum = pp_collide_any(curpos, goalpos, radius, cur_objp, goal_objp, big_only_flag);
9938
9939         if (collide_objnum != -1)
9940                 Ai_info[Ships[goal_objp->instance].ai_index].ai_flags |= AIF_REPAIR_OBSTRUCTED;
9941
9942         return collide_objnum;
9943 }
9944
9945
9946 int Dock_path_warning_given = 0;
9947
9948 //      Docking behavior.
9949 //      Approach a ship, follow path to docking platform, approach platform, after awhile,
9950 //      undock.
9951 void ai_dock()
9952 {
9953         ship                    *shipp = &Ships[Pl_objp->instance];
9954         ai_info         *aip = &Ai_info[shipp->ai_index];
9955         object          *goal_objp;
9956         ship_info       *sip = &Ship_info[shipp->ship_info_index];
9957
9958         //      Make sure object we're supposed to dock with still exists.
9959         if ((aip->goal_objnum == -1) || (Objects[aip->goal_objnum].signature != aip->goal_signature)) {
9960                 ai_cleanup_dock_mode(aip, shipp);
9961                 return;
9962         }
9963
9964         goal_objp = &Objects[aip->goal_objnum];
9965
9966         //      For docking submodes (ie, not undocking), follow path.  Once at second last
9967         //      point on path (point just before point on dock platform), orient into position.
9968         // For undocking, first mode pushes docked ship straight back from docking point
9969         // second mode turns ship and moves to point on docking radius
9970         switch (aip->submode) {
9971
9972                 //      This mode means to find the path to the docking point.
9973         case AIS_DOCK_0:
9974                 //aip->path_start = -1;
9975                 //nprintf(("AI", "Time = %7.3f, submode = %i\n", f2fl(Missiontime), aip->submode));
9976                 ai_path();
9977                 if (!Dock_path_warning_given && (aip->path_length < 4)) {
9978                         Warning( LOCATION, "Ship '%s' has only %i points on dock path.  Docking will look strange.  Contact Adam.", shipp->ship_name, aip->path_length );
9979                         Dock_path_warning_given = 1;            //      This is on a mission-wide basis, but it's just a hack for now...
9980                 }
9981
9982                 aip->submode = AIS_DOCK_1;
9983                 aip->path_start = -1;
9984                 aip->submode_start_time = Missiontime;
9985                 break;
9986
9987                 //      This mode means to follow the path until just before the end.
9988         case AIS_DOCK_1: {
9989                 float   dist;
9990                 int     r;
9991
9992                 if ((r = maybe_dock_obstructed(Pl_objp, goal_objp, 1)) != -1) {
9993                         int     r1;
9994                         if ((r1 = maybe_avoid_big_ship(Pl_objp, goal_objp, aip, &goal_objp->pos, 7.0f)) != 0) {
9995                                 nprintf(("AI", "Support ship %s avoiding large ship %s\n", Ships[Pl_objp->instance].ship_name, Ships[Objects[r1].instance].ship_name));
9996                                 break;
9997                         } /*else {
9998                                 nprintf(("AI", "Dock 1: Obstructed by %s\n", Ships[Objects[r].instance].ship_name));
9999                                 accelerate_ship(aip, 0.0f);
10000                                 aip->submode = AIS_DOCK_0;
10001                         } */
10002                 } //else {
10003                 {
10004                         dist = ai_path();
10005                         //nprintf(("AI", "Time = %7.3f, submode = %i\n", f2fl(Missiontime), aip->submode));
10006                         //nprintf(("AI", "Dock 1: Frame: %i, goal point = %i, dist = %7.3f\n", Framecount, aip->path_cur-aip->path_start, dist));
10007
10008                         if (aip->path_cur-aip->path_start >= aip->path_length-1) {              //      If got this far, advance no matter what.
10009                                 aip->submode = AIS_DOCK_2;
10010                                 aip->submode_start_time = Missiontime;
10011                                 aip->path_cur--;
10012                                 Assert(aip->path_cur-aip->path_start >= 0);
10013                         } else if (aip->path_cur-aip->path_start >= aip->path_length-2) {
10014                                 if (Pl_objp->phys_info.speed > goal_objp->phys_info.speed + 1.5f) {
10015                                         set_accel_for_target_speed(Pl_objp, goal_objp->phys_info.speed);
10016                                 } else {
10017                                         aip->submode = AIS_DOCK_2;
10018                                         aip->submode_start_time = Missiontime;
10019                                 }
10020                         }
10021                 }
10022                 break;
10023                                           }
10024         //      This mode means to drag oneself right to the second last point on the path.
10025         //      Path code allows it to overshoot.
10026         case AIS_DOCK_2: {
10027                 float           dist;
10028                 int     r;
10029
10030                 if ((r = maybe_dock_obstructed(Pl_objp, goal_objp,0)) != -1) {
10031                         nprintf(("AI", "Dock 2: Obstructed by %s\n", Ships[Objects[r].instance].ship_name));
10032                         accelerate_ship(aip, 0.0f);
10033                         aip->submode = AIS_DOCK_1;
10034                 } else {
10035                         //nprintf(("AI", "Time = %7.3f, submode = %i\n", f2fl(Missiontime), aip->submode));
10036                         dist = dock_orient_and_approach(Pl_objp, &Objects[aip->goal_objnum], DOA_APPROACH);
10037                         Assert(dist != UNINITIALIZED_VALUE);
10038
10039                         if (dist == DOCK_BACKUP_RETURN_VAL) {
10040                                 int path_num;
10041                                 aip->submode = AIS_DOCK_1;
10042                                 path_num = ai_return_path_num_from_dockbay(&Objects[aip->goal_objnum], aip->dockee_index);
10043                                 Assert(aip->goal_objnum >= 0 && aip->goal_objnum < MAX_OBJECTS);
10044                                 ai_find_path(Pl_objp, aip->goal_objnum, path_num, 0);
10045                                 break;
10046                         }
10047
10048                         //nprintf(("AI", "Dock 2: dist = %7.3f\n", vm_vec_dist_quick(&Pl_objp->pos, &goal_point)));
10049                         float   tolerance;
10050                         if (Objects[aip->goal_objnum].flags & OF_PLAYER_SHIP)
10051                                 tolerance = 6*flFrametime + 1.0f;
10052                         else
10053                                 tolerance = 4*flFrametime + 0.5f;
10054
10055                         if ( dist < tolerance) {
10056                                 aip->submode = AIS_DOCK_3;
10057                                 aip->submode_start_time = Missiontime;
10058                                 aip->path_cur++;
10059                         }
10060                 }
10061                 break;
10062                                                   }
10063
10064         case AIS_DOCK_3:
10065         case AIS_DOCK_3A:
10066                 {
10067                 Assert(aip->goal_objnum != -1);
10068                 int     r;
10069
10070                 if ((r = maybe_dock_obstructed(Pl_objp, goal_objp,0)) != -1) {
10071                         nprintf(("AI", "Dock 1: Obstructed by %s\n", Ships[Objects[r].instance].ship_name));
10072                         accelerate_ship(aip, 0.0f);
10073                         aip->submode = AIS_DOCK_2;
10074                 } else {
10075
10076                         //nprintf(("AI", "Time = %7.3f, submode = %i\n", f2fl(Missiontime), aip->submode));
10077                         float dist = dock_orient_and_approach(Pl_objp, &Objects[aip->goal_objnum], DOA_DOCK);
10078                         Assert(dist != UNINITIALIZED_VALUE);
10079
10080                         if (dist == DOCK_BACKUP_RETURN_VAL) {
10081                                 aip->submode = AIS_DOCK_2;
10082                                 break;
10083                         }
10084
10085                         //nprintf(("AI", "Dock 3: dist = %7.3f\n", dist));
10086
10087                         if (dist < 2*flFrametime * (1.0f + fl_sqrt(goal_objp->phys_info.speed))) {
10088                                 // - Removed by MK on 11/7/97, causes errors for ships docked at mission start: maybe_recreate_path(Pl_objp, aip, 1);
10089                                 dist = dock_orient_and_approach(Pl_objp, &Objects[aip->goal_objnum], DOA_DOCK);
10090                                 Assert(dist != UNINITIALIZED_VALUE);
10091
10092                                 physics_ship_init(Pl_objp);
10093
10094                                 ai_do_objects_docked_stuff( Pl_objp, goal_objp );
10095
10096                                 if (aip->submode == AIS_DOCK_3) {
10097                                         snd_play_3d( &Snds[SND_DOCK_ATTACH], &Pl_objp->pos, &View_position );
10098                                         hud_maybe_flash_docking_text(Pl_objp);
10099                                         // ai_dock_shake(Pl_objp, goal_objp);
10100
10101                                         if ((Pl_objp == Player_obj) || (goal_objp == Player_obj))
10102                                                 joy_ff_docked();  // shake player's joystick a little
10103                                 }
10104
10105                                 //      If this ship is repairing another ship...
10106                                 if (aip->ai_flags & AIF_REPAIRING) {
10107                                         aip->submode = AIS_DOCK_4;                      //      Special rearming only dock mode.
10108                                         aip->submode_start_time = Missiontime;
10109                                 } else {
10110                                         aip->submode = AIS_DOCK_4A;
10111                                         aip->submode_start_time = Missiontime;
10112                                 }
10113                         }
10114                 }
10115                 break;
10116                 }
10117
10118                 //      Yes, we just sit here.  We wait for further orders.  No, it's not a bug.
10119         case AIS_DOCK_4A:
10120                 //nprintf(("AI", "Time = %7.3f, submode = %i\n", f2fl(Missiontime), aip->submode));
10121                 //nprintf(("AI", "."));
10122                 if (aip->active_goal >= 0) {
10123                         mission_log_add_entry(LOG_SHIP_DOCK, Ships[Pl_objp->instance].ship_name, Ships[goal_objp->instance].ship_name);
10124
10125                         if (aip->goals[aip->active_goal].ai_mode == AI_GOAL_DOCK) {
10126                                 ai_mission_goal_complete( aip );                                        // Note, this calls ai_set_default_behavior().
10127                         } 
10128                 } else {        //      Can happen for initially docked ships.
10129                         ai_do_default_behavior( &Objects[Ships[aip->shipnum].objnum] );         // do the default behavior
10130                 }
10131                 
10132                 break;
10133
10134         case AIS_DOCK_4: {
10135                 //      This mode is only for rearming/repairing.
10136                 //      The ship that is performing the rearm enters this mode after it docks.
10137                 Assert((aip->goal_objnum >= -1) && (aip->goal_objnum < MAX_OBJECTS));
10138
10139                 //nprintf(("AI", "Time = %7.3f, submode = %i\n", f2fl(Missiontime), aip->submode));
10140                 float dist = dock_orient_and_approach(Pl_objp, &Objects[aip->goal_objnum], DOA_DOCK);
10141                 Assert(dist != UNINITIALIZED_VALUE);
10142
10143                 object  *goal_objp = &Objects[aip->goal_objnum];
10144                 Assert(goal_objp->type == OBJ_SHIP);
10145                 ship                    *goal_shipp = &Ships[goal_objp->instance];              
10146                 ai_info         *goal_aip = &Ai_info[goal_shipp->ai_index];
10147
10148                 //nprintf(("AI", "Dock 4: dist = %7.3f\n", dist));
10149
10150                 //      Make sure repair has not broken off.
10151                 if (dist > 5.0f) {      //      Oops, too far away!
10152                         if ( goal_aip->ai_flags & AIF_BEING_REPAIRED )
10153                                 ai_do_objects_repairing_stuff( goal_objp, Pl_objp, REPAIR_INFO_BROKEN);
10154
10155                         if (dist > Pl_objp->radius*2 + goal_objp->radius*2) {
10156                                 //      Got real far away from goal, so move back a couple modes and try again.
10157                                 aip->submode = AIS_DOCK_2;
10158                                 aip->submode_start_time = Missiontime;
10159                         }
10160                 } else {
10161                         if ( goal_aip->ai_flags & AIF_AWAITING_REPAIR )
10162                                 ai_do_objects_repairing_stuff( goal_objp, Pl_objp, REPAIR_INFO_BEGIN );
10163                 }
10164
10165                 break;
10166                                                   }
10167
10168         case AIS_UNDOCK_0: {
10169                 int path_num;
10170                 //      First stage of undocking.
10171
10172                 //nprintf(("AI", "Undock 0:\n"));
10173
10174                 aip->submode = AIS_UNDOCK_1;
10175                 aip->submode_start_time = Missiontime;
10176                 if (aip->dock_objnum == -1) {
10177                         aip->submode = AIS_UNDOCK_3;
10178                 } else {
10179
10180                         // set up the path points for the undocking procedure.  dock_path_index member should
10181                         // have gotten set in the docking code.
10182                         Assert( aip->dock_path_index != -1 );
10183                         path_num = ai_return_path_num_from_dockbay(goal_objp, aip->dock_path_index);
10184                         ai_find_path(Pl_objp, goal_objp-Objects, path_num, 0);
10185
10186                         // Play a ship docking detach sound
10187                         snd_play_3d( &Snds[SND_DOCK_DETACH], &Pl_objp->pos, &View_position );
10188                 }
10189                 break;
10190                                                          }
10191         case AIS_UNDOCK_1: {
10192                 //      Using thrusters, exit from dock station to nearest next dock path point.
10193                 float   dist;
10194                 
10195                 //nprintf(("AI", "Undock 1: time in this mode = %7.3f\n", f2fl(Missiontime - aip->submode_start_time)));
10196
10197                 if (Missiontime - aip->submode_start_time < REARM_BREAKOFF_DELAY) {
10198                         break;          //      Waiting for one second to elapse to let detach sound effect play out.
10199                 }
10200                 else {  // AL - added 05/16/97.  Hack to play depart sound.  Will probably take out.
10201                                         // Assumes that the submode_start_time is not used for AIS_UNDOCK_1 anymore
10202                         if ( aip->submode_start_time != 0 )
10203                                 snd_play_3d( &Snds[SND_DOCK_DEPART], &Pl_objp->pos, &View_position );
10204                         aip->submode_start_time = 0;
10205                 }
10206
10207                 dist = dock_orient_and_approach(Pl_objp, &Objects[aip->goal_objnum], DOA_UNDOCK_1);
10208                 Assert(dist != UNINITIALIZED_VALUE);
10209
10210                 float dist_to_dock_obj = vm_vec_dist_quick(&Pl_objp->pos, &Objects[aip->goal_objnum].pos);
10211
10212                 //      Move to within 0.1 units of second last point on path before orienting, or just plain far away from docked-to ship.
10213                 //      This allows undock to complete if first ship flies away.
10214                 if ((dist < 2*flFrametime) || (dist_to_dock_obj > 2*Pl_objp->radius)) {
10215                         aip->submode = AIS_UNDOCK_2;
10216                         aip->submode_start_time = Missiontime;
10217                 }
10218                 break;
10219                                                          }
10220         case AIS_UNDOCK_2: {
10221                 float dist;
10222                 ai_info *other_aip;
10223
10224                 // get pointer to docked object's aip to reset flags, etc
10225                 Assert( aip->dock_objnum != -1 );
10226                 other_aip = &Ai_info[Ships[Objects[aip->dock_objnum].instance].ai_index];
10227
10228                 //      Second stage of undocking.
10229                 dist = dock_orient_and_approach(Pl_objp, &Objects[aip->goal_objnum], DOA_UNDOCK_2);
10230                 Assert(dist != UNINITIALIZED_VALUE);
10231
10232
10233                 //nprintf(("AI", "Undock 2: dist = %7.3f\n", dist));
10234                 
10235                 //      If at goal point, or quite far away from dock object
10236                 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) ) {
10237                         // reset the dock flags.  If rearm/repair, reset rearm repair flags for those ships as well.
10238                         if ( sip->flags & SIF_SUPPORT ) {
10239                                 ai_do_objects_repairing_stuff( &Objects[aip->dock_objnum], Pl_objp, REPAIR_INFO_END );
10240                         }
10241
10242                         // clear out flags for AIF_DOCKED for both objects.
10243                         ai_do_objects_undocked_stuff( Pl_objp, goal_objp );
10244                         physics_ship_init(Pl_objp);
10245                         aip->submode = AIS_UNDOCK_3;                            //      The do-nothing mode, until another order is issued
10246
10247                         //aip->ai_flags &= ~AIF_DOCKED;         //      @MK, 9/18/97
10248                         //other_aip->ai_flags &= ~AIF_DOCKED;
10249                         //aip->dock_objnum = -1;                                        // invalidate who obj is docked with
10250                         //other_aip->dock_objnum = -1;                  // MWA 10/07/97 invalide docked objects dock_objnum value as well
10251
10252                         // don't add undock log entries for support ships.
10253                         if ( !(sip->flags & SIF_SUPPORT) )
10254                                 mission_log_add_entry(LOG_SHIP_UNDOCK, Ships[Pl_objp->instance].ship_name, Ships[goal_objp->instance].ship_name);
10255
10256                 }
10257                 break;
10258                 }
10259         case AIS_UNDOCK_3: {
10260                 float dist = dock_orient_and_approach(Pl_objp, &Objects[aip->goal_objnum], DOA_UNDOCK_3);
10261                 Assert(dist != UNINITIALIZED_VALUE);
10262
10263                 if (dist < Pl_objp->radius/2 + 5.0f) {
10264                         aip->submode = AIS_UNDOCK_4;
10265                 }
10266
10267                 // possible that this flag hasn't been cleared yet.  When aborting a rearm, this submode might
10268                 // be entered directly.
10269                 if ( (sip->flags & SIF_SUPPORT) && (aip->ai_flags & AIF_REPAIRING) ) {
10270                         ai_do_objects_repairing_stuff( &Objects[aip->goal_objnum], Pl_objp, REPAIR_INFO_ABORT );
10271                 }
10272
10273                 break;
10274                                                  }
10275         case AIS_UNDOCK_4: {
10276                 ai_info *other_aip;
10277
10278                 // MWA 10/07/97  I'm slightly confused by the dual use of goal_objnum and dock_objnum.  Seems to me
10279                 // that goal_objnum and dock_objnum are the same through this whole docking/undocking process, although
10280                 // I could be wrong.  dock_objnum was reset in undock_2 submode so try to use goal_objnum here to
10281                 // get other ships ai_info pointer
10282                 Assert( aip->goal_objnum != -1 );
10283                 other_aip = &Ai_info[Ships[Objects[aip->goal_objnum].instance].ai_index];
10284
10285                 aip->mode = AIM_NONE;
10286                 aip->dock_path_index = -1;              // invalidate the docking path index
10287
10288                 // these flags should have been cleared long ago!
10289                 // Get Allender if you hit one of these!!!!!
10290                 // removed by allender on 2/16 since a ship may be docked with some other ship, but still be the
10291                 // goal_objnum of this ship ending it's undocking mode.
10292                 //Assert( !(aip->ai_flags & AIF_DOCKED) );
10293                 //Assert( !(other_aip->ai_flags & AIF_DOCKED) );
10294                 //Assert( !(aip->ai_flags & AIF_REPAIRING) );
10295                 //Assert( !(other_aip->ai_flags & AIF_BEING_REPAIRED) );
10296                 //Assert( !(other_aip->ai_flags & AIF_AWAITING_REPAIR) );
10297
10298                 // only call mission goal complete if this was indeed an undock goal
10299                 if ( aip->active_goal > -1 ) {
10300                         if ( aip->goals[aip->active_goal].ai_mode == AI_GOAL_UNDOCK )
10301                                 ai_mission_goal_complete( aip );                        // this call should reset the AI mode
10302                         //else
10303                         //      aip->active_goal = -1;                                          // this ensures that this ship might get new goal
10304                 }
10305
10306                 break;
10307                                                          }
10308         default:
10309                 Int3(); //      Error, bogus submode
10310         }
10311
10312 }
10313
10314 // TURRET BEGIN
10315
10316 //      Given an object and a turret on that object, return the global position and forward vector
10317 //      of the turret.   The gun normal is the unrotated gun normal, (the center of the FOV cone), not
10318 // the actual gun normal given using the current turret heading.  But it _is_ rotated into the model's orientation
10319 //      in global space.
10320 void ship_get_global_turret_info(object *objp, model_subsystem *tp, vector *gpos, vector *gvec)
10321 {
10322         matrix  m;
10323         vm_copy_transpose_matrix(&m, &objp->orient);
10324 //      vm_vec_rotate(gpos, &tp->turret_avg_firing_point, &m);
10325         vm_vec_rotate(gpos, &tp->pnt, &m);
10326         vm_vec_add2(gpos, &objp->pos);
10327         vm_vec_rotate(gvec, &tp->turret_norm, &m);      
10328 }
10329
10330 // Given an object and a turret on that object, return the actual firing point of the gun
10331 // and its normal.   This uses the current turret angles.  We are keeping track of which
10332 // gun to fire next in the ship specific info for this turret subobject.  Use this info
10333 // to determine which position to fire from next.
10334 //      Stuffs:
10335 //              *gpos: absolute position of gun firing point
10336 //              *gvec: vector fro *gpos to *targetp
10337 void ship_get_global_turret_gun_info(object *objp, ship_subsys *ssp, vector *gpos, vector *gvec, int use_angles, vector *targetp)
10338 {
10339         vector * gun_pos;
10340         model_subsystem *tp = ssp->system_info;
10341
10342         ship_model_start(objp);
10343
10344         gun_pos = &tp->turret_firing_point[ssp->turret_next_fire_pos % tp->turret_num_firing_points];
10345
10346         model_find_world_point(gpos, gun_pos, tp->model_num, tp->turret_gun_sobj, &objp->orient, &objp->pos );
10347
10348         if (use_angles)
10349                 model_find_world_dir(gvec, &tp->turret_norm, tp->model_num, tp->turret_gun_sobj, &objp->orient, &objp->pos );
10350         else {
10351                 //vector        gun_pos2;
10352                 //vm_vec_add(&gun_pos2, gpos, gun_pos);
10353                 vm_vec_normalized_dir(gvec, targetp, gpos);
10354         }
10355
10356         ship_model_stop(objp);  
10357 }
10358
10359 //      Rotate a turret towards an enemy.
10360 //      Return TRUE if caller should use angles in subsequent rotations.
10361 //      Some obscure model thing only John Slagel knows about.
10362 //      Sets predicted enemy position.
10363 //      If the turret (*ss) has a subsystem targeted, the subsystem is used as the predicted point.
10364 int aifft_rotate_turret(ship *shipp, int parent_objnum, ship_subsys *ss, object *objp, object *lep, vector *predicted_enemy_pos, vector *gvec)
10365 {
10366         if (ss->turret_enemy_objnum != -1)      {
10367                 model_subsystem *tp = ss->system_info;
10368                 vector  gun_pos, gun_vec;
10369                 float           weapon_speed;
10370                 float           weapon_system_strength;
10371
10372                 //      weapon_system_strength scales time enemy in range in 0..1.  So, the lower this is, the worse the aiming will be.
10373                 weapon_system_strength = ship_get_subsystem_strength(shipp, SUBSYSTEM_WEAPONS);
10374
10375                 ship_get_global_turret_info(&Objects[parent_objnum], tp, &gun_pos, &gun_vec);
10376
10377                 weapon_speed = Weapon_info[tp->turret_weapon_type].max_speed;
10378                 float weapon_travel_dist = weapon_speed * Weapon_info[tp->turret_weapon_type].lifetime;
10379
10380                 vector  enemy_point;
10381                 if (ss->targeted_subsys != NULL) {
10382                         if (ss->turret_enemy_objnum != -1) {
10383                                 vm_vec_unrotate(&enemy_point, &ss->targeted_subsys->system_info->pnt, &Objects[ss->turret_enemy_objnum].orient);
10384                                 vm_vec_add2(&enemy_point, &Objects[ss->turret_enemy_objnum].pos);
10385                         }
10386                 } else {
10387                         if ((lep->type == OBJ_SHIP) && (Ship_info[Ships[lep->instance].ship_info_index].flags & (SIF_BIG_SHIP | SIF_HUGE_SHIP))) {
10388                                 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));
10389                         } else {
10390                                 enemy_point = lep->pos;
10391                         }
10392                 }
10393
10394                 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);
10395
10396                 if (weapon_system_strength < 0.7f) {
10397                         vector  rand_vec;
10398
10399                         static_randvec(Missiontime >> 18, &rand_vec);   //      Return same random number for two seconds.
10400                         //      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.
10401                         vm_vec_scale_add2(predicted_enemy_pos, &rand_vec, (1.0f - weapon_system_strength)*1.5f * lep->radius);
10402                 }
10403
10404                 vector  v2e;
10405                 vm_vec_normalized_dir(&v2e, predicted_enemy_pos, &gun_pos);
10406                 if (vm_vec_dot(&v2e, gvec) > tp->turret_fov) {
10407                         int     rval;
10408
10409                         rval = model_rotate_gun(shipp->modelnum, ss->system_info, &Objects[parent_objnum].orient, 
10410                                 &ss->submodel_info_1.angs, &ss->submodel_info_2.angs,
10411                                 &Objects[parent_objnum].pos, predicted_enemy_pos);
10412                 }
10413         }
10414
10415         return 0;
10416 }
10417
10418 //      Determine if subsystem *enemy_subsysp is hittable from objp.
10419 //      If so, return dot product of vector from point abs_gunposp to *enemy_subsysp
10420 float   aifft_compute_turret_dot(object *objp, object *enemy_objp, vector *abs_gunposp, ship_subsys *turret_subsysp, ship_subsys *enemy_subsysp)
10421 {
10422         float   dot_out;
10423         vector  subobj_pos, vector_out;
10424
10425         vm_vec_unrotate(&subobj_pos, &enemy_subsysp->system_info->pnt, &enemy_objp->orient);
10426         vm_vec_add2(&subobj_pos, &enemy_objp->pos);
10427
10428         if (ship_subsystem_in_sight(enemy_objp, enemy_subsysp, abs_gunposp, &subobj_pos, 1, &dot_out, &vector_out)) {
10429                 vector  turret_norm;
10430
10431                 vm_vec_rotate(&turret_norm, &turret_subsysp->system_info->turret_norm, &objp->orient);
10432                 return vm_vec_dot(&turret_norm, &vector_out);
10433         } else
10434                 return -1.0f;
10435
10436 }
10437
10438 #define MAX_AIFFT_TURRETS                       60
10439 ship_subsys *aifft_list[MAX_AIFFT_TURRETS];
10440 float aifft_rank[MAX_AIFFT_TURRETS];
10441 int aifft_list_size = 0;
10442 int aifft_max_checks = 5;
10443 DCF(mf, "")
10444 {
10445         dc_get_arg(ARG_INT);
10446         aifft_max_checks = Dc_arg_int;
10447 }
10448
10449
10450 //      Pick a subsystem to attack on enemy_objp.
10451 //      Only pick one if enemy_objp is a big ship or a capital ship.
10452 //      Returns dot product from turret to subsystem in *dot_out
10453 ship_subsys *aifft_find_turret_subsys(object *objp, ship_subsys *ssp, object *enemy_objp, float *dot_out)
10454 {
10455         ship    *eshipp, *shipp;
10456         ship_info       *esip;
10457         ship_subsys     *best_subsysp = NULL;
10458         float dot;
10459
10460         Assert(enemy_objp->type == OBJ_SHIP);
10461
10462         eshipp = &Ships[enemy_objp->instance];
10463         esip = &Ship_info[eshipp->ship_info_index];
10464
10465         shipp = &Ships[objp->instance];
10466
10467         float   best_dot = 0.0f;
10468         *dot_out = best_dot;
10469
10470         //      Compute absolute gun position.
10471         vector  abs_gun_pos;
10472         vm_vec_unrotate(&abs_gun_pos, &ssp->system_info->pnt, &objp->orient);
10473         vm_vec_add2(&abs_gun_pos, &objp->pos);
10474
10475         //      Only pick a turret to attack on large ships.
10476         if (!(esip->flags & (SIF_BIG_SHIP | SIF_HUGE_SHIP)))
10477                 return best_subsysp;
10478
10479         // Make sure big or huge ship *actually* has subsystems  (ie, knossos)
10480         if (esip->n_subsystems == 0) {
10481                 return best_subsysp;
10482         }
10483
10484         // first build up a list subsystems to traverse
10485         ship_subsys     *pss;
10486         aifft_list_size = 0;
10487         for ( pss = GET_FIRST(&eshipp->subsys_list); pss !=END_OF_LIST(&eshipp->subsys_list); pss = GET_NEXT(pss) ) {
10488                 model_subsystem *psub = pss->system_info;
10489
10490                 // if we've reached max turrets bail
10491                 if(aifft_list_size >= MAX_AIFFT_TURRETS){
10492                         break;
10493                 }
10494
10495                 // Don't process destroyed objects
10496                 if ( pss->current_hits <= 0.0f ){
10497                         continue;
10498                 }
10499                 
10500                 switch (psub->type) {
10501                 case SUBSYSTEM_WEAPONS:
10502                         aifft_list[aifft_list_size] = pss;
10503                         aifft_rank[aifft_list_size++] = 1.4f;
10504                         break;
10505
10506                 case SUBSYSTEM_TURRET:
10507                         aifft_list[aifft_list_size] = pss;
10508                         aifft_rank[aifft_list_size++] = 1.2f;
10509                         break;
10510
10511                 case SUBSYSTEM_SENSORS:
10512                 case SUBSYSTEM_ENGINE:
10513                         aifft_list[aifft_list_size] = pss;
10514                         aifft_rank[aifft_list_size++] = 1.0f;
10515                         break;
10516                 }
10517         }
10518
10519         // DKA:  6/28/99 all subsystems can be destroyed.
10520         //Assert(aifft_list_size > 0);
10521         if (aifft_list_size == 0) {
10522                 return best_subsysp;
10523         }
10524
10525         // determine a stride value so we're not checking too many turrets
10526         int stride = aifft_list_size > aifft_max_checks ? aifft_list_size / aifft_max_checks : 1;
10527         if(stride <= 0){
10528                 stride = 1;
10529         }
10530         int offset = (int)frand_range(0.0f, (float)(aifft_list_size % stride));
10531         int idx;
10532         for(idx=offset; idx<aifft_list_size; idx+=stride){
10533                 dot = aifft_compute_turret_dot(objp, enemy_objp, &abs_gun_pos, ssp, aifft_list[idx]);                   
10534
10535                 if (dot* aifft_rank[idx] > best_dot) {
10536                         best_dot = dot*aifft_rank[idx];
10537                         best_subsysp = aifft_list[idx];
10538                 }
10539         }
10540
10541         Assert(best_subsysp != &eshipp->subsys_list);
10542
10543         *dot_out = best_dot;
10544         return best_subsysp;
10545 }
10546
10547 // Set active weapon for turret
10548 void ai_turret_select_default_weapon(ship_subsys *turret)
10549 {
10550         ship_weapon *twp;
10551
10552         twp = &turret->weapons;
10553
10554         // If a primary weapon is available, select it
10555         if ( twp->num_primary_banks > 0 ) {
10556                 turret->system_info->turret_weapon_type = twp->primary_bank_weapons[0];
10557         } else if ( twp->num_secondary_banks > 0 ) {
10558                 turret->system_info->turret_weapon_type = twp->secondary_bank_weapons[0];
10559         }
10560 }
10561
10562 // return !0 if the specified target should scan for a new target, otherwise return 0
10563 int turret_should_pick_new_target(ship_subsys *turret)
10564 {
10565 //      int target_type;
10566
10567         if ( timestamp_elapsed(turret->turret_next_enemy_check_stamp) ) {
10568                 return 1;
10569         }
10570
10571         return 0;
10572
10573 /*
10574         if ( turret->turret_enemy_objnum == -1 ) {
10575                 return 1;
10576         }
10577                 
10578         target_type = Objects[turret->turret_enemy_objnum].type;
10579         if ( (target_type != OBJ_SHIP) && (target_type != OBJ_ASTEROID) ) {
10580                 return 1;
10581         }
10582
10583         return 0;
10584 */
10585 }
10586
10587 // Set the next fire timestamp for a turret, based on weapon type and ai class
10588 void turret_set_next_fire_timestamp(ship_subsys *turret, ai_info *aip)
10589 {
10590         float   wait;
10591         int     weapon_id;
10592
10593         weapon_id = turret->system_info->turret_weapon_type;
10594
10595         wait = Weapon_info[weapon_id].fire_wait * 1000.0f;
10596
10597         // make side even for team vs. team
10598         if ((Game_mode & GM_MULTIPLAYER) && (Netgame.type_flags & NG_TYPE_TEAM)) {
10599                 // flak guns need to fire more rapidly
10600                 if (Weapon_info[weapon_id].wi_flags & WIF_FLAK) {
10601                         wait *= Ship_fire_delay_scale_friendly[Game_skill_level] * 0.5f;
10602                         wait += (Num_ai_classes - aip->ai_class - 1) * 40.0f;
10603                 } else {
10604                         wait *= Ship_fire_delay_scale_friendly[Game_skill_level];
10605                         wait += (Num_ai_classes - aip->ai_class - 1) * 100.0f;
10606                 }
10607         } else {
10608                 // flak guns need to fire more rapidly
10609                 if (Weapon_info[weapon_id].wi_flags & WIF_FLAK) {
10610                         if (Ships[aip->shipnum].team == TEAM_FRIENDLY) {
10611                                 wait *= Ship_fire_delay_scale_friendly[Game_skill_level] * 0.5f;
10612                         } else {
10613                                 wait *= Ship_fire_delay_scale_hostile[Game_skill_level] * 0.5f;
10614                         }       
10615                         wait += (Num_ai_classes - aip->ai_class - 1) * 40.0f;
10616
10617                 } else if (Weapon_info[weapon_id].wi_flags & WIF_HUGE) {
10618                         // make huge weapons fire independently of team
10619                         wait *= Ship_fire_delay_scale_friendly[Game_skill_level];
10620                         wait += (Num_ai_classes - aip->ai_class - 1) * 100.0f;
10621                 } else {
10622                         // give team friendly an advantage
10623                         if (Ships[aip->shipnum].team == TEAM_FRIENDLY) {
10624                                 wait *= Ship_fire_delay_scale_friendly[Game_skill_level];
10625                         } else {
10626                                 wait *= Ship_fire_delay_scale_hostile[Game_skill_level];
10627                         }       
10628                         wait += (Num_ai_classes - aip->ai_class - 1) * 100.0f;
10629                 }
10630         }
10631
10632         // vary wait time +/- 10%
10633         wait *= frand_range(0.9f, 1.1f);
10634         turret->turret_next_fire_stamp = timestamp((int) wait);
10635 }
10636
10637 // Decide  if a turret should launch an aspect seeking missile
10638 int turret_should_fire_aspect(ship_subsys *turret, float dot, int weapon_class)
10639 {
10640         weapon_info *wip;
10641
10642         wip = &Weapon_info[weapon_class];
10643
10644         if ( (dot > AICODE_TURRET_DUMBFIRE_ANGLE) && (turret->turret_time_enemy_in_range >= min(wip->min_lock_time,AICODE_TURRET_MAX_TIME_IN_RANGE)) ) {
10645                 return 1;
10646         }
10647
10648         return 0;
10649 }
10650
10651 // Update how long current target has been in this turrets range
10652 void turret_update_enemy_in_range(ship_subsys *turret, float seconds)
10653 {
10654         turret->turret_time_enemy_in_range += seconds;
10655
10656         if ( turret->turret_time_enemy_in_range < 0.0f ) {
10657                 turret->turret_time_enemy_in_range = 0.0f;
10658         }
10659
10660         if ( turret->turret_time_enemy_in_range > AICODE_TURRET_MAX_TIME_IN_RANGE ) {
10661                 turret->turret_time_enemy_in_range = AICODE_TURRET_MAX_TIME_IN_RANGE;
10662         }
10663 }
10664
10665
10666
10667 // Fire a weapon from a turret
10668 void turret_fire_weapon(ship_subsys *turret, int parent_objnum, vector *turret_pos, vector *turret_fvec, vector *predicted_pos = NULL)
10669 {
10670         matrix  turret_orient;
10671         int             turret_weapon_class, weapon_objnum;
10672         ai_info *parent_aip;
10673         ship            *parent_ship;
10674         beam_fire_info fire_info;
10675         float flak_range = 0.0f;
10676
10677         parent_aip = &Ai_info[Ships[Objects[parent_objnum].instance].ai_index];
10678         parent_ship = &Ships[Objects[parent_objnum].instance];
10679         turret_weapon_class = turret->system_info->turret_weapon_type;
10680
10681         if (check_ok_to_fire(parent_objnum, turret->turret_enemy_objnum, &Weapon_info[turret_weapon_class])) {
10682                 vm_vector_2_matrix(&turret_orient, turret_fvec, NULL, NULL);
10683                 turret->turret_last_fire_direction = *turret_fvec;
10684
10685                 // set next fire timestamp for the turret
10686                 turret_set_next_fire_timestamp(turret, parent_aip);
10687
10688                 // if this weapon is a beam weapon, handle it specially
10689                 if(Weapon_info[turret_weapon_class].wi_flags & WIF_BEAM){
10690                         // if this beam isn't free to fire
10691                         if (!(turret->weapons.flags & SW_FLAG_BEAM_FREE)) {
10692                                 Int3(); // should never get this far
10693                                 return;
10694                         }
10695
10696                         // stuff beam firing info
10697                         memset(&fire_info, 0, sizeof(beam_fire_info));
10698                         fire_info.accuracy = 1.0f;
10699                         fire_info.beam_info_index = turret_weapon_class;
10700                         fire_info.beam_info_override = NULL;
10701                         fire_info.shooter = &Objects[parent_objnum];
10702                         fire_info.target = &Objects[turret->turret_enemy_objnum];
10703                         fire_info.target_subsys = NULL;
10704                         fire_info.turret = turret;
10705
10706                         // fire a beam weapon
10707                         beam_fire(&fire_info);
10708                 } else {
10709
10710                         // don't fire swarm, but set up swarm info
10711                         if (Weapon_info[turret_weapon_class].wi_flags & WIF_SWARM) {
10712                                 turret_swarm_set_up_info(parent_objnum, turret, turret_weapon_class);
10713                                 return;
10714                         } else {
10715                                 weapon_objnum = weapon_create( turret_pos, &turret_orient, turret_weapon_class, parent_objnum, 0, -1, 1);
10716                                 weapon_set_tracking_info(weapon_objnum, parent_objnum, turret->turret_enemy_objnum, 1, turret->targeted_subsys);                
10717                         }
10718
10719                         //nprintf(("AI", "Turret_time_enemy_in_range = %7.3f\n", ss->turret_time_enemy_in_range));              
10720                         if (weapon_objnum != -1) {
10721                                 Weapons[Objects[weapon_objnum].instance].target_num = turret->turret_enemy_objnum;
10722                                 // AL 1-6-97: Store pointer to turret subsystem
10723                                 Weapons[Objects[weapon_objnum].instance].turret_subsys = turret;
10724
10725                                 if ( Weapon_info[turret_weapon_class].launch_snd != -1 ) {
10726                                         // Don't play turret firing sound if turret sits on player ship... it gets annoying.
10727                                         if ( parent_objnum != OBJ_INDEX(Player_obj) ) {                                         
10728                                                 snd_play_3d( &Snds[Weapon_info[turret_weapon_class].launch_snd], turret_pos, &View_position );                                          
10729                                         }
10730                                 }               
10731
10732                                 // if the gun is a flak gun
10733                                 if(Weapon_info[turret_weapon_class].wi_flags & WIF_FLAK){                       
10734                                         // show a muzzle flash
10735                                         flak_muzzle_flash(turret_pos, turret_fvec, turret_weapon_class);
10736
10737                                         // pick a firing range so that it detonates properly                    
10738                                         flak_pick_range(&Objects[weapon_objnum], predicted_pos, ship_get_subsystem_strength(parent_ship, SUBSYSTEM_WEAPONS));
10739
10740                                         // determine what that range was
10741                                         flak_range = flak_get_range(&Objects[weapon_objnum]);
10742                                 }
10743
10744                                 // in multiplayer (and the master), then send a turret fired packet.
10745                                 if ( MULTIPLAYER_MASTER && (weapon_objnum != -1) ) {
10746                                         int subsys_index;
10747
10748                                         subsys_index = ship_get_index_from_subsys(turret, parent_objnum );
10749                                         Assert( subsys_index != -1 );
10750                                         if(Weapon_info[turret_weapon_class].wi_flags & WIF_FLAK){                       
10751                                                 send_flak_fired_packet( parent_objnum, subsys_index, weapon_objnum, flak_range );
10752                                         } else {
10753                                                 send_turret_fired_packet( parent_objnum, subsys_index, weapon_objnum );
10754                                         }
10755                                 }
10756                         }
10757                 }
10758         } else {
10759                 float wait = 1000.0f * frand_range(0.9f, 1.1f);
10760                 turret->turret_next_fire_stamp = timestamp((int) wait);
10761         }
10762 }
10763
10764 void turret_swarm_fire_from_turret(ship_subsys *turret, int parent_objnum, int target_objnum, ship_subsys *target_subsys)
10765 {
10766         int turret_weapon_class, weapon_objnum;
10767         matrix turret_orient;
10768         vector turret_pos, turret_fvec;
10769
10770         // parent not alive, quick out.
10771         if (Objects[parent_objnum].type != OBJ_SHIP) {
10772                 return;
10773         }
10774
10775         //      change firing point
10776         ship_get_global_turret_gun_info(&Objects[parent_objnum], turret, &turret_pos, &turret_fvec, 1, NULL);
10777         turret->turret_next_fire_pos++;
10778
10779         // get class [index into Weapon_info array
10780         turret_weapon_class = turret->system_info->turret_weapon_type;
10781         Assert(Weapon_info[turret_weapon_class].wi_flags & WIF_SWARM);
10782
10783         // make turret_orient from turret_fvec -- turret->turret_last_fire_direction
10784         vm_vector_2_matrix(&turret_orient, &turret_fvec, NULL, NULL);
10785
10786         // create weapon and homing info
10787         weapon_objnum = weapon_create(&turret_pos, &turret_orient, turret_weapon_class, parent_objnum, 0, -1, 1);
10788         weapon_set_tracking_info(weapon_objnum, parent_objnum, target_objnum, 1, target_subsys);
10789
10790         // do other cool stuff if weapon is created.
10791         if (weapon_objnum > -1) {
10792                 Weapons[Objects[weapon_objnum].instance].turret_subsys = turret;
10793                 Weapons[Objects[weapon_objnum].instance].target_num = turret->turret_enemy_objnum;
10794
10795                 // maybe sound
10796                 if ( Weapon_info[turret_weapon_class].launch_snd != -1 ) {
10797                         // Don't play turret firing sound if turret sits on player ship... it gets annoying.
10798                         if ( parent_objnum != OBJ_INDEX(Player_obj) ) {
10799                                 snd_play_3d( &Snds[Weapon_info[turret_weapon_class].launch_snd], &turret_pos, &View_position );
10800                         }
10801                 }
10802                 
10803                 // in multiplayer (and the master), then send a turret fired packet.
10804                 if ( MULTIPLAYER_MASTER && (weapon_objnum != -1) ) {
10805                         int subsys_index;
10806
10807                         subsys_index = ship_get_index_from_subsys(turret, parent_objnum );
10808                         Assert( subsys_index != -1 );
10809                         send_turret_fired_packet( parent_objnum, subsys_index, weapon_objnum );
10810                 }
10811         }
10812 }
10813
10814 int Num_ai_firing = 0;
10815 int Num_find_turret_enemy = 0;
10816 int Num_turrets_fired = 0;
10817 //      Given a turret tp and its parent parent_objnum, fire from the turret at its enemy.
10818 void ai_fire_from_turret(ship *shipp, ship_subsys *ss, int parent_objnum)
10819 {
10820         float           weapon_firing_range;
10821         vector  v2e;
10822         object  *lep;           //      Last enemy pointer
10823         model_subsystem *tp = ss->system_info;
10824         int             use_angles, turret_weapon_class;
10825         vector  predicted_enemy_pos;
10826         object  *objp;
10827         ai_info *aip;
10828
10829         if (!Ai_firing_enabled) {
10830                 return;
10831         }
10832
10833         if (ss->current_hits < 0.0f) {
10834                 return;
10835         }
10836
10837         if ( ship_subsys_disrupted(ss) ){               // AL 1/19/98: Make sure turret isn't suffering disruption effects
10838                 return;
10839         }
10840
10841         // Check turret free
10842         if (ss->weapons.flags & SW_FLAG_TURRET_LOCK) {
10843                 return;
10844         }
10845
10846         // If beam weapon, check beam free
10847         if ( (Weapon_info[tp->turret_weapon_type].wi_flags & WIF_BEAM) && !(ss->weapons.flags & SW_FLAG_BEAM_FREE) ) {
10848                 return;
10849         }
10850
10851         Assert( shipp->objnum == parent_objnum );
10852
10853         if ( tp->turret_weapon_type < 0 ){
10854                 return;
10855         }
10856
10857         // Monitor number of calls to ai_fire_from_turret
10858         Num_ai_firing++;
10859
10860         turret_weapon_class = tp->turret_weapon_type;
10861
10862         // AL 09/14/97: ensure ss->turret_enemy_objnum != -1 before setting lep
10863         if ( (ss->turret_enemy_objnum >= 0 && ss->turret_enemy_objnum < MAX_OBJECTS) && (ss->turret_enemy_sig == Objects[ss->turret_enemy_objnum].signature)) {
10864                 lep = &Objects[ss->turret_enemy_objnum];
10865
10866                 // MK -- here is where turret is targeting a bomb.  I simply return for now.  We should force
10867                 // a target change -- or better yet, never pick a weapon when this turret has a "huge" weapon
10868                 // loaded.
10869
10870                 // we only care about targets which are ships.
10871                 //if ( lep->type != OBJ_SHIP )
10872                 //      return;
10873
10874                 //      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.
10875                 if ( Weapon_info[turret_weapon_class].wi_flags & WIF_HUGE ) {
10876                         if ( lep->type != OBJ_SHIP ) {
10877                                 return;
10878                         }
10879                         if ( !(Ship_info[Ships[lep->instance].ship_info_index].flags & (SIF_BIG_SHIP | SIF_HUGE_SHIP)) ) {
10880                                 return;
10881                         }
10882                 }
10883
10884                 // If targeting protected or beam protected ship, don't fire.  Reset enemy objnum
10885                 if (lep->type == OBJ_SHIP) {
10886                         // Check if we're targeting a protected ship
10887                         if (lep->flags & OF_PROTECTED) {
10888                                 ss->turret_enemy_objnum = -1;
10889                                 ss->turret_time_enemy_in_range = 0.0f;
10890                                 return;
10891                         }
10892
10893                         // Check if we're targeting a beam protected ship with a beam weapon
10894                         if ( (lep->flags & OF_BEAM_PROTECTED) && (Weapon_info[turret_weapon_class].wi_flags & WIF_BEAM) ) {
10895                                 ss->turret_enemy_objnum = -1;
10896                                 ss->turret_time_enemy_in_range = 0.0f;
10897                                 return;
10898                         }
10899                 }
10900         } else {
10901                 ss->turret_enemy_objnum = -1;
10902                 lep = NULL;
10903         }
10904         
10905         Assert((parent_objnum >= 0) && (parent_objnum < MAX_OBJECTS));
10906         objp = &Objects[parent_objnum];
10907         Assert(objp->type == OBJ_SHIP);
10908         aip = &Ai_info[Ships[objp->instance].ai_index];
10909
10910         // Use the turret info for all guns, not one gun in particular.
10911         vector   gvec, gpos;
10912         ship_get_global_turret_info(&Objects[parent_objnum], tp, &gpos, &gvec);
10913
10914         // Rotate the turret even if time hasn't elapsed, since it needs to turn to face its target.
10915         use_angles = aifft_rotate_turret(shipp, parent_objnum, ss, objp, lep, &predicted_enemy_pos, &gvec);
10916
10917         if ( !timestamp_elapsed(ss->turret_next_fire_stamp)){
10918                 return;
10919         }
10920
10921         // Don't try to fire beyond weapon_limit_range
10922         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);
10923
10924         // if beam weapon in nebula and target not tagged, decrase firing range
10925         extern int Nebula_sec_range;
10926         if (Weapon_info[turret_weapon_class].wi_flags & WIF_BEAM) {
10927                 if ( !((shipp->tag_left > 0) || (shipp->level2_tag_left > 0)) ) {
10928                         if (Nebula_sec_range) {
10929                                 weapon_firing_range *= float(BEAM_NEBULA_RANGE_REDUCE_FACTOR);
10930                         }
10931                 }
10932         }
10933
10934         if (ss->turret_enemy_objnum != -1) {
10935                 float dist_to_enemy = vm_vec_normalized_dir(&v2e, &predicted_enemy_pos, &gpos) - lep->radius;
10936                 if (dist_to_enemy > weapon_firing_range) {
10937                         ss->turret_enemy_objnum = -1;           //      Force picking of new enemy.
10938                 }
10939         }
10940
10941         // Turret spawn weapons are a special case.  They fire if there are enough enemies in the 
10942         // immediate area (not necessarily in the turret fov).
10943         if ( Weapon_info[turret_weapon_class].wi_flags & WIF_SPAWN ) {
10944                 int num_ships_nearby;
10945                 num_ships_nearby = num_nearby_fighters(get_enemy_team_mask(parent_objnum), &gpos, 1500.0f);
10946                 if (( num_ships_nearby >= 3 ) || ((num_ships_nearby >= 2) && (frand() < 0.1f))) {
10947                         turret_fire_weapon(ss, parent_objnum, &gpos, &ss->turret_last_fire_direction);
10948                 } else {
10949                         ss->turret_next_fire_stamp = timestamp(1000);   //      Regardless of firing rate, don't check whether should fire for awhile.
10950                 }
10951                 return;
10952         }
10953
10954         //      Maybe pick a new enemy.
10955         if ( turret_should_pick_new_target(ss) ) {
10956                 Num_find_turret_enemy++;
10957                 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);
10958                 Assert(objnum < 0 || is_target_beam_valid(ss, objnum));
10959
10960                 if (objnum != -1) {
10961                         if (ss->turret_enemy_objnum == -1) {
10962                                 ss->turret_enemy_objnum = objnum;
10963                                 ss->turret_enemy_sig = Objects[objnum].signature;
10964                                 // why return?
10965                                 return;
10966                         } else {
10967                                 ss->turret_enemy_objnum = objnum;
10968                                 ss->turret_enemy_sig = Objects[objnum].signature;
10969                         }
10970                 } else {
10971                         ss->turret_enemy_objnum = -1;
10972                 }
10973
10974                 if (ss->turret_enemy_objnum != -1) {
10975                         float   dot = 1.0f;
10976                         lep = &Objects[ss->turret_enemy_objnum];
10977                         if ( lep->type == OBJ_SHIP ) {
10978                                 ss->targeted_subsys = aifft_find_turret_subsys(objp, ss, lep, &dot);                            
10979                         }
10980                         ss->turret_next_enemy_check_stamp = timestamp((int) (max(dot, 0.5f)*2000.0f) + 1000);
10981                 } else {
10982                         ss->turret_next_enemy_check_stamp = timestamp((int) (2000.0f * frand_range(0.9f, 1.1f)));       //      Check every two seconds
10983                 }
10984         }
10985
10986         //      If still don't have an enemy, return.  Or, if enemy is protected, return.
10987         if (ss->turret_enemy_objnum != -1) {
10988                 //      Don't shoot at ship we're going to dock with.
10989                 if (ss->turret_enemy_objnum == aip->dock_objnum) {
10990                         ss->turret_enemy_objnum = -1;
10991                         return;
10992                 }
10993
10994                 if (Objects[ss->turret_enemy_objnum].flags & OF_PROTECTED) {
10995                         //      This can happen if the enemy was selected before it became protected.
10996                         ss->turret_enemy_objnum = -1;
10997                         return;
10998                 }
10999                 lep = &Objects[ss->turret_enemy_objnum];
11000         } else {
11001                 if (timestamp_until(ss->turret_next_fire_stamp) < 500) {
11002                         ss->turret_next_fire_stamp = timestamp(500);
11003                 }
11004                 return;
11005         }
11006
11007         if ( lep == NULL ){
11008                 return;
11009         }
11010
11011         Assert(ss->turret_enemy_objnum != -1);
11012
11013         float dot = vm_vec_dot(&v2e, &gvec);
11014
11015         if (dot > tp->turret_fov ) {
11016                 // Ok, the turret is lined up... now line up a particular gun.
11017                 int ok_to_fire = 0;
11018                 float dist_to_enemy;
11019
11020                 // We're ready to fire... now get down to specifics, like where is the
11021                 // actual gun point and normal, not just the one for whole turret.
11022                 ship_get_global_turret_gun_info(&Objects[parent_objnum], ss, &gpos, &gvec, use_angles, &predicted_enemy_pos);
11023                 ss->turret_next_fire_pos++;
11024
11025                 // Fire in the direction the turret is facing, not right at the target regardless of turret dir.
11026                 vm_vec_sub(&v2e, &predicted_enemy_pos, &gpos);
11027                 dist_to_enemy = vm_vec_normalize(&v2e);
11028                 dot = vm_vec_dot(&v2e, &gvec);
11029
11030                 // 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
11031                 // and make them less lethal
11032                 if(Weapon_info[turret_weapon_class].wi_flags & WIF_FLAK){
11033                         flak_jitter_aim(&v2e, dist_to_enemy, ship_get_subsystem_strength(shipp, SUBSYSTEM_WEAPONS));
11034                 }
11035
11036                 // Fire if:
11037                 //              dumbfire and nearly pointing at target.
11038                 //              heat seeking and target in a fairly wide cone.
11039                 //              aspect seeking and target is locked.
11040                 turret_weapon_class = tp->turret_weapon_type;
11041
11042                 // if dumbfire (lasers and non-homing missiles)
11043                 if ( !(Weapon_info[turret_weapon_class].wi_flags & WIF_HOMING) ) {
11044                         if ((dist_to_enemy < 75.0f) || (dot > AICODE_TURRET_DUMBFIRE_ANGLE )) {
11045                                 turret_update_enemy_in_range(ss, 2*Weapon_info[turret_weapon_class].fire_wait);
11046                                 ok_to_fire = 1;
11047                         }
11048                 } else if ( Weapon_info[turret_weapon_class].wi_flags & WIF_HOMING_HEAT ) {     // if heat seekers
11049                         if ((dist_to_enemy < 50.0f) || (dot > AICODE_TURRET_HEATSEEK_ANGLE )) {
11050                                 turret_update_enemy_in_range(ss, 2*Weapon_info[turret_weapon_class].fire_wait);
11051                                 ok_to_fire = 1;
11052                         }
11053                 } else if ( Weapon_info[turret_weapon_class].wi_flags & WIF_HOMING_ASPECT ) {   // if aspect seeker
11054                         if ((dist_to_enemy < 50.0f) || (dot > AICODE_TURRET_DUMBFIRE_ANGLE )) {
11055                                 turret_update_enemy_in_range(ss, 2*Weapon_info[turret_weapon_class].fire_wait);
11056                         }
11057                         if ( turret_should_fire_aspect(ss, dot, turret_weapon_class) ) {
11058                                 ok_to_fire = 1;
11059                         }
11060                 }
11061
11062                 if ( ok_to_fire ) {
11063                         Num_turrets_fired++;
11064                         
11065                         turret_fire_weapon(ss, parent_objnum, &gpos, &v2e, &predicted_enemy_pos);                                               
11066                 } else {
11067                         turret_update_enemy_in_range(ss, -4*Weapon_info[tp->turret_weapon_type].fire_wait);
11068                         ss->turret_next_fire_stamp = timestamp(500);
11069                 }
11070         } else {
11071                 // Lost him!
11072                 ss->turret_enemy_objnum = -1;           //      Reset enemy objnum, find a new one next frame.
11073                 ss->turret_time_enemy_in_range = 0.0f;
11074         }
11075 }
11076
11077 // TURRET END
11078
11079 #ifndef NDEBUG
11080 #define MAX_AI_DEBUG_RENDER_STUFF       100
11081 typedef struct ai_render_stuff {
11082         ship_subsys     *ss;
11083         int                     parent_objnum;
11084 } ai_render_stuff;
11085
11086 ai_render_stuff AI_debug_render_stuff[MAX_AI_DEBUG_RENDER_STUFF];
11087
11088 int     Num_AI_debug_render_stuff = 0;
11089
11090 void ai_debug_render_stuff()
11091 {
11092         vertex  vert1, vert2;
11093         vector  gpos2;
11094         int             i;
11095
11096         for (i=0; i<Num_AI_debug_render_stuff; i++) {
11097                 ship_subsys     *ss;
11098                 int     parent_objnum;
11099                 vector  gpos, gvec;
11100                 model_subsystem *tp;
11101
11102                 ss = AI_debug_render_stuff[i].ss;
11103                 tp = ss->system_info;
11104
11105                 parent_objnum = AI_debug_render_stuff[i].parent_objnum;
11106
11107                 ship_get_global_turret_info(&Objects[parent_objnum], tp, &gpos, &gvec);
11108                 g3_rotate_vertex(&vert1, &gpos);
11109                 vm_vec_scale_add(&gpos2, &gpos, &gvec, 20.0f);
11110                 g3_rotate_vertex(&vert2, &gpos2);
11111                 gr_set_color(0, 0, 255);
11112                 g3_draw_sphere(&vert1, 2.0f);
11113                 gr_set_color(255, 0, 255);
11114                 g3_draw_sphere(&vert2, 2.0f);
11115                 g3_draw_line(&vert1, &vert2);
11116         }
11117
11118         // draw from beta to its goal point
11119 /*      for (i=0; i<6; i++) {
11120                 ai_info *aip = &Ai_info[i];
11121                 gr_set_color(0, 0, 255);
11122                 g3_rotate_vertex(&vert1, &Objects[i].pos);
11123                 g3_rotate_vertex(&vert2, &aip->goal_point);
11124                 g3_draw_line(&vert1, &vert2);
11125         } */
11126         
11127
11128         Num_AI_debug_render_stuff = 0;
11129 }
11130
11131 #endif
11132
11133 #ifndef NDEBUG
11134 int     Msg_count_4996 = 0;
11135 #endif
11136
11137 //      --------------------------------------------------------------------------
11138 // Process subobjects of object objnum.
11139 //      Deal with engines disabled.
11140 void process_subobjects(int objnum)
11141 {
11142         model_subsystem *psub;
11143         ship_subsys     *pss;
11144         object  *objp = &Objects[objnum];
11145         ship            *shipp = &Ships[objp->instance];
11146         ai_info *aip = &Ai_info[shipp->ai_index];
11147         ship_info       *sip = &Ship_info[shipp->ship_info_index];
11148
11149         for ( pss = GET_FIRST(&shipp->subsys_list); pss !=END_OF_LIST(&shipp->subsys_list); pss = GET_NEXT(pss) ) {
11150                 psub = pss->system_info;
11151
11152                 // Don't process destroyed objects
11153                 if ( pss->current_hits <= 0.0f ) 
11154                         continue;
11155
11156                 switch (psub->type) {
11157                 case SUBSYSTEM_TURRET:
11158                         if ( psub->turret_num_firing_points > 0 )       {
11159                                 ai_fire_from_turret(shipp, pss, objnum);
11160                         } else {
11161 #ifndef NDEBUG
11162                                 if (!Msg_count_4996) {
11163                                         Warning( LOCATION, "Ship '%s' has turrets with no guns!\nProbably a model problem, so get an artist!", shipp->ship_name );
11164                                         Msg_count_4996++;
11165                                 }
11166 #endif
11167                                 }
11168                         break;
11169
11170                 case SUBSYSTEM_ENGINE:
11171                 case SUBSYSTEM_NAVIGATION:
11172                 case SUBSYSTEM_COMMUNICATION:
11173                 case SUBSYSTEM_WEAPONS:
11174                 case SUBSYSTEM_SENSORS:
11175                 case SUBSYSTEM_UNKNOWN:
11176                         break;
11177
11178                 // next set of subsystems may rotation
11179                 case SUBSYSTEM_RADAR:
11180                 case SUBSYSTEM_SOLAR:
11181                 case SUBSYSTEM_GAS_COLLECT:
11182                 case SUBSYSTEM_ACTIVATION:
11183                         break;
11184                 default:
11185                         Error(LOCATION, "Illegal subsystem type.\n");
11186                 }
11187
11188                 // do solar/radar/gas/activator rotation here
11189                 if ( psub->flags & MSS_FLAG_ROTATES )   {
11190                         if (psub->flags & MSS_FLAG_STEPPED_ROTATE       ) {
11191                                 submodel_stepped_rotate(psub, &pss->submodel_info_1);
11192                         } else {
11193                                 submodel_rotate(psub, &pss->submodel_info_1 );
11194                         }
11195                 }
11196
11197         }
11198
11199         //      Deal with a ship with blown out engines.
11200         if (ship_get_subsystem_strength(shipp, SUBSYSTEM_ENGINE) == 0.0f) {
11201                 if (sip->flags & (SIF_FIGHTER | SIF_BOMBER)) {
11202                         // AL: Only attack forever if not trying to depart to a docking bay.  Need to have this in, since
11203                         //     a ship may get repaired... and it should still try to depart.  Since docking bay departures
11204                         //     are not handled as goals, we don't want to leave the AIM_BAY_DEPART mode.
11205                         if ( aip->mode != AIM_BAY_DEPART ) {
11206                                 ai_attack_object(objp, NULL, 99, NULL);         //      Regardless of current mode, enter attack mode.
11207                                 aip->submode = SM_ATTACK_FOREVER;                               //      Never leave attack submode, don't avoid, evade, etc.
11208                         }
11209                 }
11210         }
11211
11212
11213 }
11214
11215 //      Given an object and the wing it's in, return its index in the wing list.
11216 //      This defines its location in the wing formation.
11217 //      If the object can't be found in the wing, return -1.
11218 //      *objp           object of interest
11219 //      wingnum the wing *objp is in
11220 int get_wing_index(object *objp, int wingnum)
11221 {
11222         wing    *wingp;
11223         int     i;
11224
11225         Assert((wingnum >= 0) && (wingnum < MAX_WINGS));
11226
11227         wingp = &Wings[wingnum];
11228
11229         for (i=wingp->current_count-1; i>=0; i--)
11230                 if ( objp->instance == wingp->ship_index[i] )
11231                         break;
11232
11233         return i;               //      Note, returns -1 if string not found.
11234 }
11235
11236 //      Given a wing, return a pointer to the object of its leader.
11237 //      Asserts if object not found.
11238 //      Currently, the wing leader is defined as the first object in the wing.
11239 //      wingnum         Wing number in Wings array.
11240 //      If wing leader is disabled, swap it with another ship.
11241 object * get_wing_leader(int wingnum)
11242 {
11243         wing            *wingp;
11244         int             ship_num;
11245
11246         Assert((wingnum >= 0) && (wingnum < MAX_WINGS));
11247
11248         wingp = &Wings[wingnum];
11249
11250         Assert(wingp->current_count != 0);                      //      Make sure there is a leader
11251
11252         ship_num = wingp->ship_index[0];
11253
11254         //      If this ship is disabled, try another ship in the wing.
11255         int n = 0;
11256         while (ship_get_subsystem_strength(&Ships[ship_num], SUBSYSTEM_ENGINE) == 0.0f) {
11257                 n++;
11258                 if (n >= wingp->current_count)
11259                         break;  
11260                 ship_num = wingp->ship_index[n];
11261         }
11262
11263         if (( n != 0) && (n != wingp->current_count)) {
11264                 int t = wingp->ship_index[0];
11265                 wingp->ship_index[0] = wingp->ship_index[n];
11266                 wingp->ship_index[n] = t;
11267         }
11268
11269         return &Objects[Ships[ship_num].objnum];
11270 }
11271
11272 #define DEFAULT_WING_X_DELTA            1.0f
11273 #define DEFAULT_WING_Y_DELTA            0.25f
11274 #define DEFAULT_WING_Z_DELTA            0.75f
11275 #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))
11276 // next constant is higher that MAX_SHIPS_IN_WINGS to deal with forming on player's wing
11277 #define MAX_FORMATION_ROWS              4
11278
11279 //      Given a position in a wing, return the desired location of the ship relative to the leader
11280 //      *_delta_vec             OUTPUT.  delta vector based on wing_index
11281 //      wing_index              position in wing.
11282 void get_wing_delta(vector *_delta_vec, int wing_index)
11283 {
11284         int     wi0;
11285
11286         Assert(wing_index >= 0);
11287
11288         int     k, row, column;
11289
11290         int bank = wing_index / (MAX_FORMATION_ROWS*(MAX_FORMATION_ROWS+1)/2);
11291         wi0 = wing_index % (MAX_FORMATION_ROWS * (MAX_FORMATION_ROWS+1)/2);
11292
11293         k = 0;
11294         for (row=1; row<MAX_FORMATION_ROWS+1; row++) {
11295                 k += row;
11296                 if (wi0 < k)
11297                         break;
11298         }
11299
11300         row--;
11301         column = wi0 - k + row + 1;
11302
11303         _delta_vec->x = ((float) column - (float) row/2.0f) * DEFAULT_WING_X_DELTA/DEFAULT_WING_MAG;
11304         _delta_vec->y = ((float)row + (float)bank*2.25f) * DEFAULT_WING_Y_DELTA/DEFAULT_WING_MAG;
11305         _delta_vec->z = - ((float)row + 0.5f * (float) bank) * DEFAULT_WING_Z_DELTA/DEFAULT_WING_MAG;
11306         
11307 }
11308
11309 //      Compute the largest radius of a ship in a *objp's wing.
11310 float gwlr_1(object *objp, ai_info *aip)
11311 {
11312         int             wingnum = aip->wing;
11313         float           max_radius;
11314         object  *o;
11315         ship_obj        *so;
11316
11317         Assert(wingnum >= 0);
11318
11319         max_radius = objp->radius;
11320
11321         for ( so = GET_FIRST(&Ship_obj_list); so != END_OF_LIST(&Ship_obj_list); so = GET_NEXT(so) ) {
11322                 o = &Objects[so->objnum];
11323                 if (Ai_info[Ships[o->instance].ai_index].wing == wingnum)
11324                         if (o->radius > max_radius)
11325                                 max_radius = o->radius;
11326         }
11327
11328         return max_radius;
11329 }
11330
11331 //      Compute the largest radius of a ship forming on *objp's wing.
11332 float gwlr_object_1(object *objp, ai_info *aip)
11333 {
11334         float           max_radius;
11335         object  *o;
11336         ship_obj        *so;
11337
11338         max_radius = objp->radius;
11339
11340         for ( so = GET_FIRST(&Ship_obj_list); so != END_OF_LIST(&Ship_obj_list); so = GET_NEXT(so) ) {
11341                 o = &Objects[so->objnum];
11342                 if (Ai_info[Ships[o->instance].ai_index].goal_objnum == OBJ_INDEX(objp))
11343                         if (o->radius > max_radius)
11344                                 max_radius = o->radius;
11345         }
11346
11347         return max_radius;
11348 }
11349
11350 //      For the wing that *objp is part of, return the largest ship radius in that wing.
11351 float get_wing_largest_radius(object *objp, int formation_object_flag)
11352 {
11353         ship            *shipp;
11354         ai_info *aip;
11355
11356         Assert(objp->type == OBJ_SHIP);
11357         Assert((objp->instance >= 0) && (objp->instance < MAX_OBJECTS));
11358         shipp = &Ships[objp->instance];
11359         Assert((shipp->ai_index >= 0) && (shipp->ai_index < MAX_AI_INFO));
11360         aip = &Ai_info[shipp->ai_index];
11361
11362         if (formation_object_flag) {
11363                 return gwlr_object_1(objp, aip);
11364         } else {
11365                 return gwlr_1(objp, aip);
11366         }
11367
11368 }
11369
11370 float Wing_y_scale = 2.0f;
11371 float Wing_scale = 1.0f;
11372 DCF(wing_y_scale, "")
11373 {
11374         dc_get_arg(ARG_FLOAT);
11375         Wing_y_scale = Dc_arg_float;
11376 }
11377
11378 DCF(wing_scale, "")
11379 {
11380         dc_get_arg(ARG_FLOAT);
11381         Wing_scale = Dc_arg_float;
11382 }
11383
11384 // Given a wing leader and a position in the wing formation, return the desired absolute location to fly to.
11385 //      Returns result in *result_pos.
11386 void get_absolute_wing_pos(vector *result_pos, object *leader_objp, int wing_index, int formation_object_flag)
11387 {
11388         vector  wing_delta, rotated_wing_delta;
11389         float           wing_spread_size;
11390
11391         get_wing_delta(&wing_delta, wing_index);                //      Desired location in leader's reference frame
11392
11393         wing_spread_size = max(50.0f, 3.0f * get_wing_largest_radius(leader_objp, formation_object_flag) + 15.0f);
11394
11395         // for player obj (1) move ships up 20% (2) scale formation up 20%
11396         if (leader_objp->flags & OF_PLAYER_SHIP) {
11397                 wing_delta.y *= Wing_y_scale;
11398                 wing_spread_size *= Wing_scale;
11399         }
11400
11401         vm_vec_scale(&wing_delta, wing_spread_size * (1.0f + leader_objp->phys_info.speed/70.0f));
11402
11403         vm_vec_unrotate(&rotated_wing_delta, &wing_delta, &leader_objp->orient);        //      Rotate into leader's reference.
11404
11405         vm_vec_add(result_pos, &leader_objp->pos, &rotated_wing_delta); //      goal_point is absolute 3-space point.
11406 }
11407
11408 #ifndef NDEBUG
11409 int Debug_render_wing_phantoms;
11410
11411 void render_wing_phantoms(object *objp)
11412 {
11413         int             i;
11414         ship            *shipp;
11415         ai_info *aip;
11416         int             wingnum;
11417         int             wing_index;             //      Index in wing struct, defines 3-space location in wing.
11418         vector  goal_point;
11419         
11420         Assert(objp->type == OBJ_SHIP);
11421         Assert((objp->instance >= 0) && (objp->instance < MAX_SHIPS));
11422
11423         shipp = &Ships[objp->instance];
11424         Assert((shipp->ai_index >= 0) && (shipp->ai_index < MAX_AI_INFO));
11425
11426         aip = &Ai_info[shipp->ai_index];
11427
11428         wingnum = aip->wing;
11429
11430         if (wingnum == -1)
11431                 return;
11432
11433         wing_index = get_wing_index(objp, wingnum);
11434
11435         //      If this ship is NOT the leader, abort.
11436         if (wing_index != 0)
11437                 return;
11438
11439         for (i=0; i<32; i++)
11440                 if (Debug_render_wing_phantoms & (1 << i)) {
11441                         get_absolute_wing_pos(&goal_point, objp, i, 0);
11442         
11443                         vertex  vert;
11444                         gr_set_color(255, 0, 128);
11445                         g3_rotate_vertex(&vert, &goal_point);
11446                         g3_draw_sphere(&vert, 2.0f);
11447                 }
11448
11449         Debug_render_wing_phantoms = 0;
11450
11451 }
11452
11453 void render_wing_phantoms_all()
11454 {
11455         object  *objp;
11456         ship_obj        *so;
11457
11458         for ( so = GET_FIRST(&Ship_obj_list); so != END_OF_LIST(&Ship_obj_list); so = GET_NEXT(so) ) {
11459                 ship            *shipp;
11460                 ai_info *aip;
11461                 int             wingnum;
11462                 int             wing_index;             //      Index in wing struct, defines 3-space location in wing.
11463
11464                 objp = &Objects[so->objnum];
11465                 
11466                 Assert((objp->instance >= 0) && (objp->instance < MAX_SHIPS));
11467                 shipp = &Ships[objp->instance];
11468                 Assert((shipp->ai_index >= 0) && (shipp->ai_index < MAX_AI_INFO));
11469
11470                 aip = &Ai_info[shipp->ai_index];
11471
11472                 wingnum = aip->wing;
11473
11474                 if (wingnum == -1)
11475                         continue;
11476
11477                 wing_index = get_wing_index(objp, wingnum);
11478
11479                 //      If this ship is NOT the leader, abort.
11480                 if (wing_index != 0)
11481                         continue;
11482                 
11483                 render_wing_phantoms(objp);
11484
11485                 return;
11486         }
11487 }
11488
11489 #endif
11490
11491 //      Hook from goals code to AI.
11492 //      Force a wing to fly in formation.
11493 //      Sets AIF_FORMATION bit in ai_flags.
11494 //      wingnum         Wing to force to fly in formation
11495 void ai_fly_in_formation(int wingnum)
11496 {
11497         object  *objp;
11498         ship            *shipp;
11499         ship_obj        *so;
11500
11501         for ( so = GET_FIRST(&Ship_obj_list); so != END_OF_LIST(&Ship_obj_list); so = GET_NEXT(so) ) {
11502                 objp = &Objects[so->objnum];
11503                 Assert((objp->instance >= 0) && (objp->instance < MAX_SHIPS));
11504
11505                 shipp = &Ships[objp->instance];
11506                 Assert((shipp->ai_index >= 0) && (shipp->ai_index < MAX_AI_INFO));
11507
11508                 if (Ai_info[shipp->ai_index].wing == wingnum) {
11509                         Ai_info[shipp->ai_index].ai_flags |= AIF_FORMATION_WING;
11510                         Ai_info[shipp->ai_index].ai_flags &= ~AIF_FORMATION_OBJECT;
11511                 }
11512         }
11513 }
11514
11515 //      Hook from goals code to AI.
11516 //      Force a wing to abandon formation flying.
11517 //      Clears AIF_FORMATION bit in ai_flags.
11518 //      wingnum         Wing to force to fly in formation
11519 void ai_disband_formation(int wingnum)
11520 {
11521         object  *objp;
11522         ship            *shipp;
11523         ship_obj        *so;
11524
11525         for ( so = GET_FIRST(&Ship_obj_list); so != END_OF_LIST(&Ship_obj_list); so = GET_NEXT(so) ) {
11526                 objp = &Objects[so->objnum];
11527                 Assert((objp->instance >= 0) && (objp->instance < MAX_SHIPS));
11528
11529                 shipp = &Ships[objp->instance];
11530                 Assert((shipp->ai_index >= 0) && (shipp->ai_index < MAX_AI_INFO));
11531
11532                 if (Ai_info[shipp->ai_index].wing == wingnum) {
11533                         Ai_info[shipp->ai_index].ai_flags &= ~AIF_FORMATION_WING;
11534                 }
11535         }
11536 }
11537
11538 float   Leader_chaos = 0.0f;
11539 int Chaos_frame = -1;
11540
11541 //      Return true if objp is flying in an erratic manner
11542 //      Only true if objp is a player
11543 int formation_is_leader_chaotic(object *objp)
11544 {
11545         if (Game_mode & GM_MULTIPLAYER)
11546                 return 0;
11547
11548         if (objp != Player_obj)
11549                 return 0;
11550
11551         if (Framecount != Chaos_frame) {
11552                 float   speed_scale;
11553                 float   fdot, udot;
11554
11555                 speed_scale = 3.0f + objp->phys_info.speed * 0.1f;
11556
11557                 fdot = 5.0f * (1.0f - vm_vec_dot(&objp->orient.fvec, &objp->last_orient.fvec)) * flFrametime;
11558                 udot = 8.0f * (1.0f - vm_vec_dot(&objp->orient.uvec, &objp->last_orient.uvec)) * flFrametime;
11559
11560                 Leader_chaos += fdot * speed_scale + udot * speed_scale;
11561
11562                 Leader_chaos *= (1.0f - flFrametime*0.2f);
11563
11564                 if (Leader_chaos < 0.0f)
11565                         Leader_chaos = 0.0f;
11566                 else if (Leader_chaos > 1.7f)
11567                         Leader_chaos = 1.7f;
11568
11569                 //nprintf(("AI", "Frame %i: chaos = %7.4f\n", Framecount, Leader_chaos));
11570
11571                 Chaos_frame = Framecount;
11572         }
11573
11574         return (Leader_chaos > 1.0f);
11575 }
11576
11577 // Fly in formation.
11578 //      Make Pl_objp assume its proper place in formation.
11579 //      If the leader of the wing is doing something stupid, like fighting a battle,
11580 //      then the poor sap wingmates will be in for a "world of hurt"
11581 //      Return TRUE if we need to process this object's normal mode
11582 int ai_formation()
11583 {
11584         object  *leader_objp;
11585         ship            *shipp;
11586         ai_info *aip, *laip;
11587         int             wingnum;
11588         int             wing_index;             //      Index in wing struct, defines 3-space location in wing.
11589         int             player_wing;    // index of the players wingnum
11590         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;
11591         float           dot_to_goal, dist_to_goal, leader_speed;
11592
11593         Assert(Pl_objp->type == OBJ_SHIP);
11594         Assert((Pl_objp->instance >= 0) && (Pl_objp->instance < MAX_SHIPS));
11595
11596         shipp = &Ships[Pl_objp->instance];
11597
11598         Assert((shipp->ai_index >= 0) && (shipp->ai_index < MAX_AI_INFO));
11599
11600         aip = &Ai_info[shipp->ai_index];
11601
11602         Assert((aip->ai_flags & AIF_FORMATION) != AIF_FORMATION);       //      Make sure not both types of formation flying in effect.
11603
11604         //      Determine which kind of formation flying.
11605         //      If tracking an object, not in waypoint mode:
11606         if (aip->ai_flags & AIF_FORMATION_OBJECT) {
11607                 if ((aip->goal_objnum < 0) || (aip->goal_objnum >= MAX_OBJECTS)) {
11608                         aip->ai_flags &= ~AIF_FORMATION_OBJECT;
11609                         return 1;
11610                 }
11611                 
11612                 wing_index = ai_formation_object_get_slotnum(aip->goal_objnum, Pl_objp);
11613                 leader_objp = &Objects[aip->goal_objnum];
11614         } else {        //      Formation flying in waypoint mode.
11615                 Assert(aip->ai_flags & AIF_FORMATION_WING);
11616                 if (aip->mode != AIM_WAYPOINTS) {
11617                         aip->ai_flags &= ~AIF_FORMATION_WING;
11618                         return 1;
11619                 }
11620
11621                 wingnum = aip->wing;
11622
11623                 if (wingnum == -1)
11624                         return 1;
11625
11626                 // disable formation flying for any ship in the players wing
11627                 player_wing = Ships[Player_obj->instance].wingnum;
11628                 if ( (player_wing != -1) && (wingnum == player_wing) )
11629                         return 1;
11630
11631                 wing_index = get_wing_index(Pl_objp, wingnum);
11632
11633                 leader_objp = get_wing_leader(wingnum);
11634
11635         }
11636
11637         //      If docked with a ship in this wing, only the more massive one actually flies in formation.
11638         if (aip->dock_objnum != -1) {
11639                 object  *other_objp = &Objects[aip->dock_objnum];
11640                 ai_info *other_aip = &Ai_info[Ships[other_objp->instance].ai_index];
11641
11642                 if (aip->wing == other_aip->wing) {
11643                         if (Pl_objp->phys_info.mass < other_objp->phys_info.mass)
11644                                 return 0;
11645                         else if (Pl_objp->phys_info.mass == other_objp->phys_info.mass) {
11646                                 if (Pl_objp->signature < other_objp->signature)
11647                                         return 0;
11648                         }
11649                 }
11650         }
11651
11652         Assert(leader_objp != NULL);
11653         laip = &Ai_info[Ships[leader_objp->instance].ai_index];
11654
11655         //      Make sure we're really in this wing.
11656         if (wing_index == -1)
11657                 return 1;
11658
11659         //      If this ship is the leader, abort, as he doesn't have to follow anyone.
11660         if (wing_index == 0) {
11661                 // nprintf(("AI", "Hmm, wing leader %s in ai_formation for no good reason.\n", shipp->ship_name));
11662                 return 1;
11663         }
11664
11665         if (aip->mode == AIM_WAYPOINTS) {
11666                 aip->wp_list = laip->wp_list;
11667                 if (laip->wp_index < Waypoint_lists[laip->wp_list].count)
11668                         aip->wp_index = laip->wp_index;
11669                 else
11670                         aip->wp_index = Waypoint_lists[laip->wp_list].count - 1;
11671                 aip->wp_flags = laip->wp_flags;
11672                 aip->wp_dir = laip->wp_dir;
11673         }
11674
11675         #ifndef NDEBUG
11676         Debug_render_wing_phantoms |= (1 << wing_index);
11677         #endif
11678
11679         leader_speed = leader_objp->phys_info.speed;
11680         vector leader_vec = leader_objp->phys_info.vel;
11681
11682         get_absolute_wing_pos(&goal_point, leader_objp, wing_index, aip->ai_flags & AIF_FORMATION_OBJECT);
11683         vm_vec_scale_add(&future_goal_point_5, &goal_point, &leader_vec, 10.0f);
11684         vm_vec_scale_add(&future_goal_point_2, &goal_point, &leader_vec, 5.0f);
11685         vm_vec_scale_add(&future_goal_point_x, &goal_point, &leader_objp->orient.fvec, 10.0f);  //      used when very close to destination
11686         vm_vec_scale_add(&future_goal_point_1000x, &goal_point, &leader_objp->orient.fvec, 1000.0f);    //      used when very close to destination
11687
11688         //      Now, get information telling this object how to turn and accelerate to get to its
11689         //      desired location.
11690         vm_vec_sub(&vec_to_goal, &goal_point, &Pl_objp->pos);
11691         if ( vm_vec_mag_quick(&vec_to_goal) < AICODE_SMALL_MAGNITUDE )
11692                 vec_to_goal.x += 0.1f;
11693
11694         vm_vec_copy_normalize(&dir_to_goal, &vec_to_goal);
11695         //dot_to_goal = vm_vec_dot(&dir_to_goal, &leader_objp->orient.fvec);
11696         dot_to_goal = vm_vec_dot(&dir_to_goal, &Pl_objp->orient.fvec);
11697         dist_to_goal = vm_vec_dist_quick(&Pl_objp->pos, &goal_point);
11698         float   dist_to_goal_2 = vm_vec_dist_quick(&Pl_objp->pos, &future_goal_point_2);
11699
11700         // 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));
11701
11702         int     chaotic_leader = 0;
11703
11704         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.
11705
11706         if (dist_to_goal > 500.0f) {
11707                 turn_towards_point(Pl_objp, &goal_point, NULL, 0.0f);
11708                 accelerate_ship(aip, 1.0f);
11709         } else if (dist_to_goal > 200.0f) {
11710                 if (dot_to_goal > -0.5f) {
11711                         turn_towards_point(Pl_objp, &goal_point, NULL, 0.0f);
11712                         float range_speed = shipp->current_max_speed - leader_speed;
11713                         if (range_speed > 0.0f)
11714                                 set_accel_for_target_speed(Pl_objp, leader_speed + range_speed * (dist_to_goal+100.0f)/500.0f);
11715                         else
11716                                 set_accel_for_target_speed(Pl_objp, shipp->current_max_speed);
11717                 } else {
11718                         turn_towards_point(Pl_objp, &future_goal_point_5, NULL, 0.0f);
11719                         if (leader_speed > 10.0f)
11720                                 set_accel_for_target_speed(Pl_objp, leader_speed *(1.0f + dot_to_goal));
11721                         else
11722                                 set_accel_for_target_speed(Pl_objp, 10.0f);
11723                 }
11724         } else {
11725                 vector  v2f2;
11726                 float   dot_to_f2;
11727                 float   dist_to_f2;
11728
11729                 dist_to_f2 = vm_vec_normalized_dir(&v2f2, &future_goal_point_2, &Pl_objp->pos);
11730                 dot_to_f2 = vm_vec_dot(&v2f2, &Pl_objp->orient.fvec);
11731
11732                 //      Leader flying like a maniac.  Don't try hard to form on wing.
11733                 if (chaotic_leader) {
11734                         turn_towards_point(Pl_objp, &future_goal_point_2, NULL, 0.0f);
11735                         set_accel_for_target_speed(Pl_objp, min(leader_speed*0.8f, 20.0f));
11736                 } else if (dist_to_goal > 75.0f) {
11737                         turn_towards_point(Pl_objp, &future_goal_point_2, NULL, 0.0f);
11738                         float   delta_speed;
11739                         float range_speed = shipp->current_max_speed - leader_speed;
11740                         if (range_speed > 0.0f)
11741                                 delta_speed = dist_to_goal_2/500.0f * range_speed;
11742                         else
11743                                 delta_speed = shipp->current_max_speed - leader_speed;
11744                         if (dot_to_goal < 0.0f) {
11745                                 delta_speed = -delta_speed;
11746                                 if (-delta_speed > leader_speed/2)
11747                                         delta_speed = -leader_speed/2;
11748                         }
11749
11750                         if (leader_speed < 5.0f)
11751                                 if (delta_speed < 5.0f)
11752                                         delta_speed = 5.0f;
11753
11754                         float scale = dot_to_f2;
11755                         if (scale < 0.1f)
11756                                 scale = 0.0f;
11757                         else
11758                                 scale *= scale;
11759
11760                         set_accel_for_target_speed(Pl_objp, scale * (leader_speed + delta_speed));
11761                 } else {
11762                         //nprintf(("AI", "Dot = %7.3f\n", dot_to_goal));
11763
11764                         if (leader_speed < 5.0f) {
11765                                 //      Leader very slow.  If not close to goal point, get very close.  Note, keep trying to get close unless
11766                                 //      moving very slowly, else momentum can carry far away from goal.
11767
11768                                 if ((dist_to_goal > 10.0f) || ((Pl_objp->phys_info.speed > leader_speed + 2.5f) && (dot_to_goal > 0.5f))) {
11769                                         //nprintf(("MK", "(1) "));
11770                                         turn_towards_point(Pl_objp, &goal_point, NULL, 0.0f);
11771                                         set_accel_for_target_speed(Pl_objp, leader_speed + dist_to_goal/10.0f);
11772                                 } else {
11773                                         if (Pl_objp->phys_info.speed < 0.5f) {
11774                                                 //nprintf(("MK", "(2) "));
11775                                                 turn_towards_point(Pl_objp, &future_goal_point_1000x, NULL, 0.0f);
11776                                         } else {
11777                                                 //nprintf(("MK", "(3) "));
11778                                         }
11779                                         set_accel_for_target_speed(Pl_objp, leader_speed);
11780                                 }
11781                                 //nprintf(("MK", "dist: %7.3f, dot: %6.3f, speed: %7.3f\n", dist_to_goal, dot_to_goal, Pl_objp->phys_info.speed));
11782                         } else if (dist_to_goal > 10.0f) {
11783                                 float   dv;
11784
11785                                 future_goal_point_2;
11786
11787                                 turn_towards_point(Pl_objp, &future_goal_point_2, NULL, 0.0f);
11788
11789                                 if (dist_to_goal > 25.0f) {
11790                                         if (dot_to_goal < 0.3f)
11791                                                 dv = -0.1f;
11792                                         else
11793                                                 dv = dot_to_goal - 0.2f;
11794
11795                                         set_accel_for_target_speed(Pl_objp, leader_speed + dist_to_goal/5.0f * dv);
11796                                 } else {
11797                                         set_accel_for_target_speed(Pl_objp, leader_speed + 1.5f * dot_to_goal - 1.0f);
11798                                 }
11799                         } else {
11800                                 if (Pl_objp->phys_info.speed < 0.1f)
11801                                         turn_towards_point(Pl_objp, &future_goal_point_1000x, NULL, 0.0f);
11802                                 else
11803                                         turn_towards_point(Pl_objp, &future_goal_point_x, NULL, 0.0f);
11804                                 set_accel_for_target_speed(Pl_objp, 0.0f);
11805                         }
11806                 }
11807
11808         }
11809
11810         //      See how different this ship's bank is relative to wing leader
11811         float   up_dot = vm_vec_dot(&leader_objp->orient.uvec, &Pl_objp->orient.uvec);
11812         if (up_dot < 0.996f) {
11813                 vector  w_out;
11814                 matrix  new_orient;
11815                 vector  angular_accel;
11816
11817                 vm_vec_copy_scale(&angular_accel, &Pl_objp->phys_info.max_rotvel, 0.2f);
11818                 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);
11819
11820         //      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)));
11821                 Pl_objp->orient = new_orient;
11822                 Pl_objp->phys_info.rotvel = w_out;
11823         //      Pl_objp->phys_info.desired_rotvel = w_out;
11824         } else {
11825                 Pl_objp->phys_info.rotvel.z = 0.0f;
11826         }
11827
11828         return 0;
11829 }
11830
11831 //      Return index of object repairing object objnum.
11832 int find_repairing_objnum(int objnum)
11833 {
11834         object          *objp;
11835         ship                    *shipp;
11836         ship_info       *sip;
11837         ship_obj                *so;
11838
11839         for ( so = GET_FIRST(&Ship_obj_list); so != END_OF_LIST(&Ship_obj_list); so = GET_NEXT(so) ) {
11840                 objp = &Objects[so->objnum];
11841
11842                 Assert((objp->instance >= 0) && (objp->instance < MAX_SHIPS));
11843
11844                 shipp = &Ships[objp->instance];
11845                 sip = &Ship_info[shipp->ship_info_index];
11846
11847                 if (sip->flags & SIF_SUPPORT) {
11848                         ai_info *aip;
11849
11850                         aip = &Ai_info[shipp->ai_index];
11851
11852                         if (aip->goal_objnum == objnum) {
11853                                 return objp-Objects;
11854                         }
11855                 }
11856         }
11857
11858         return -1;
11859 }
11860
11861 //      If object *objp is being repaired, deal with it!
11862 void ai_do_repair_frame(object *objp, ai_info *aip, float frametime)
11863 {
11864         if (Ships[objp->instance].team == TEAM_TRAITOR) {
11865                 ai_abort_rearm_request(objp);
11866                 return;
11867         }
11868
11869         if (aip->ai_flags & (AIF_BEING_REPAIRED | AIF_AWAITING_REPAIR)) {
11870                 int     dock_objnum;
11871                 ai_info *repair_aip;
11872
11873                 dock_objnum = aip->dock_objnum; // find_repairing_objnum(objp-Objects);
11874                 //Assert(dock_objnum != -1);
11875                 if (dock_objnum == -1)
11876                         return;
11877                 if (Objects[dock_objnum].signature != aip->dock_signature) {
11878                         Int3();         //      Curious -- object numbers match, but signatures do not.
11879                                                         //      Must mean original repair ship died and was replaced by current ship.
11880                         return;
11881                 }
11882         
11883                 repair_aip = &Ai_info[Ships[Objects[dock_objnum].instance].ai_index];
11884                 //Assert(repair_aip->mode == AIM_DOCK);
11885
11886                 if (aip->ai_flags & AIF_BEING_REPAIRED) {
11887                         // Assert(repair_aip->submode == AIS_DOCK_4);
11888
11889                         //      Wait awhile into the mode to synchronize with sound effect.
11890                         if (Missiontime - repair_aip->submode_start_time > REARM_SOUND_DELAY) {
11891                                 int repaired;
11892
11893                                 repaired = ship_do_rearm_frame( objp, frametime );              // hook to do missile rearming
11894
11895                                 //      See if fully repaired.  If so, cause process to stop.
11896                                 if ( repaired && (repair_aip->submode == AIS_DOCK_4)) {
11897
11898                                         repair_aip->submode = AIS_UNDOCK_0;
11899                                         repair_aip->submode_start_time = Missiontime;
11900
11901                                         // if repairing player object -- tell him done with repair
11902                                         if ( !MULTIPLAYER_CLIENT ){
11903                                                 ai_do_objects_repairing_stuff( objp, &Objects[dock_objnum], REPAIR_INFO_COMPLETE );
11904                                         }
11905                                 }
11906                         }
11907                 } else if (aip->ai_flags & AIF_AWAITING_REPAIR) {
11908                         //      If this ship has been awaiting repair for 90+ seconds, abort.
11909                         if ( !MULTIPLAYER_CLIENT ) {
11910                                 if ((Game_mode & GM_MULTIPLAYER) || (objp != Player_obj)) {
11911                                         if ((repair_aip->goal_objnum == OBJ_INDEX(objp)) && (timestamp_elapsed(aip->abort_rearm_timestamp))) {
11912                                                 ai_abort_rearm_request(objp);
11913                                                 aip->next_rearm_request_timestamp = timestamp(NEXT_REARM_TIMESTAMP);
11914                                         }
11915                                 }
11916                         }
11917                 }
11918         } else {
11919                 // AL 11-24-97: If this is the player ship, ensure the repair sound isn't playing.  We need to
11920                 //              do this check, since this is a looping sound, and may continue on if rearm/repair
11921                 //              finishes abnormally once sound begins looping.
11922                 if ( objp == Player_obj ) {
11923                         player_stop_repair_sound();
11924                 }
11925         }
11926 }
11927
11928 //      Shell around dock_orient_and_approach to detect whether dock process should be aborted.
11929 //      obj1 is the ship performing the repair.
11930 //      obj2 is the ship being repaired.
11931 void call_doa(object *obj1, object *obj2, ship_info *sip1)
11932 {
11933         if (sip1->flags & SIF_SUPPORT) {
11934                 if (obj2->phys_info.speed > MAX_REPAIR_SPEED) {
11935
11936                         // call the ai_abort rearm request code
11937                         ai_abort_rearm_request( obj2 );
11938                 } else
11939                         dock_orient_and_approach(obj1, obj2, DOA_DOCK_STAY);
11940         } else {
11941                 if (Ship_info[Ships[obj1->instance].ship_info_index].flags & SIF_CARGO)
11942                         dock_orient_and_approach(obj1, obj2, DOA_DOCK_STAY);
11943                 else if (Ship_info[Ships[obj2->instance].ship_info_index].flags & SIF_CARGO)
11944                         dock_orient_and_approach(obj2, obj1, DOA_DOCK_STAY);
11945                 else {
11946                         //mprintf(("Warning: Not sure, but making %s [%s] move to stay docked with %s [%s]\n",
11947                         //      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));
11948                         dock_orient_and_approach(obj1, obj2, DOA_DOCK_STAY);
11949
11950                 }
11951         }
11952
11953 }
11954
11955 //      Maybe launch a countermeasure.
11956 //      Also, detect a supposed homing missile that no longer exists.
11957 void ai_maybe_launch_cmeasure(object *objp, ai_info *aip)
11958 {
11959         float                   dist;
11960         ship_info       *sip;
11961         ship                    *shipp;
11962
11963         shipp = &Ships[objp->instance];
11964         sip = &Ship_info[shipp->ship_info_index];
11965
11966         if (!(sip->flags & (SIF_SMALL_SHIP | SIF_TRANSPORT)))
11967                 return;
11968
11969         if (!shipp->cmeasure_count)
11970                 return;
11971
11972         if ( !timestamp_elapsed(shipp->cmeasure_fire_stamp) )
11973                 return;
11974
11975         //      If not on player's team and Skill_level + ai_class is low, never fire a countermeasure.  The ship is too dumb.
11976         if (shipp->team != Player_ship->team) {
11977                 if (Game_skill_level + aip->ai_class < 4){
11978                         return;
11979                 }
11980         }
11981
11982         if ((aip->nearest_locked_object != -1) && (Objects[aip->nearest_locked_object].type == OBJ_WEAPON)) {
11983                 object  *weapon_objp;
11984                 weapon  *weaponp;
11985                 weapon_info     *wip;
11986
11987                 weapon_objp = &Objects[aip->nearest_locked_object];
11988                 weaponp = &Weapons[weapon_objp->instance];
11989                 wip = &Weapon_info[weaponp->weapon_info_index];
11990
11991                 if ((dist = vm_vec_dist_quick(&objp->pos, &weapon_objp->pos)) < weapon_objp->phys_info.speed*2.0f) {
11992         
11993                         aip->nearest_locked_distance = dist;
11994                         //      Verify that this object is really homing on us.
11995                         object  *weapon_objp;
11996
11997                         weapon_objp = &Objects[aip->nearest_locked_object];
11998
11999                         float   fire_chance;
12000
12001                         //      For ships on player's team, have constant, average chance to fire.
12002                         //      For enemies, increasing chance with higher skill level.
12003                         if (shipp->team == Player_ship->team)
12004                                 fire_chance = Cmeasure_fire_chance[NUM_SKILL_LEVELS/2];
12005                         else
12006                                 fire_chance = Cmeasure_fire_chance[Game_skill_level];
12007
12008                         //      Decrease chance to fire at lower ai class.
12009                         fire_chance *= (float) aip->ai_class/Num_ai_classes;
12010
12011                         float r = frand();
12012                         if (fire_chance < r) {
12013                                 //nprintf(("AI", "Not firing countermeasure due to skill level: %7.3f < %7.3f\n", fire_chance, r));
12014                                 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.
12015                                 return;
12016                         }
12017
12018                         if (weapon_objp->type == OBJ_WEAPON) {
12019                                 if (weapon_objp->instance >= 0) {
12020                                         //nprintf(("AI", "Firing countermeasure at time t=%7.3f\n", f2fl(Missiontime)));
12021                                         ship_launch_countermeasure(objp);
12022                                         shipp->cmeasure_fire_stamp = timestamp(2*CMEASURE_WAIT);
12023                                         return;
12024                                 }
12025                         }
12026         
12027                 }
12028         }
12029
12030         return;
12031 }
12032
12033 //      --------------------------------------------------------------------------
12034 void ai_preprocess_ignore_objnum(object *objp, ai_info *aip)
12035 {
12036 //      if (aip->ignore_objnum == UNUSED_OBJNUM)
12037 //              return;
12038
12039         if (aip->ai_flags & AIF_TEMPORARY_IGNORE) {
12040                 if (timestamp_elapsed(aip->ignore_expire_timestamp)) {
12041                         aip->ignore_objnum = UNUSED_OBJNUM;
12042                 }
12043         }
12044
12045         if (is_ignore_object(aip, aip->goal_objnum)) {
12046                 aip->goal_objnum = -1;
12047                 // AL 12-11-97: If in STRAFE mode, we need to ensure that target_objnum is also
12048                 //              set to -1
12049                 if ( aip->mode == AIM_STRAFE ) {
12050                         aip->target_objnum = -1;
12051                 }
12052         }
12053
12054         if (is_ignore_object(aip, aip->target_objnum))
12055                 aip->target_objnum = -1;
12056 }
12057
12058 /*
12059 void ai_safety_circle_spot()
12060 {
12061         vector  goal_point;
12062         ship_info       *sip;
12063
12064         sip = &Ship_info[Ships[Pl_objp->instance].ship_info_index];
12065
12066         goal_point = Ai_info[Ships[Pl_objp->instance].ai_index].goal_point;
12067         turn_towards_tangent(Pl_objp, &goal_point, 50.0f);
12068
12069         set_accel_for_target_speed(Pl_objp, sip->max_speed/4.0f);
12070
12071 //      float dist = vm_vec_dist_quick(&goal_point, &Pl_objp->pos);
12072 //      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));
12073
12074 }
12075 */
12076
12077 #define CHASE_CIRCLE_DIST               100.0f
12078
12079 void ai_chase_circle(object *objp)
12080 {
12081         float           dist_to_goal;
12082         float           target_speed;
12083         vector  goal_point;
12084         ship_info       *sip;
12085         ai_info         *aip;
12086
12087         sip = &Ship_info[Ships[Pl_objp->instance].ship_info_index];
12088
12089         target_speed = sip->max_speed/4.0f;
12090         aip = &Ai_info[Ships[Pl_objp->instance].ai_index];
12091
12092         Assert(vm_vec_mag(&aip->goal_point) >= 0.0f);           //      Supposedly detects bogus vector
12093
12094         goal_point = aip->goal_point;
12095
12096         if (aip->ignore_objnum == UNUSED_OBJNUM) {
12097                 dist_to_goal = vm_vec_dist_quick(&aip->goal_point, &objp->pos);
12098
12099                 if (dist_to_goal > 2*CHASE_CIRCLE_DIST) {
12100                         vector  vec_to_goal;
12101                         //      Too far from circle goal, create a new goal point.
12102                         vm_vec_normalized_dir(&vec_to_goal, &aip->goal_point, &objp->pos);
12103                         vm_vec_scale_add(&aip->goal_point, &objp->pos, &vec_to_goal, CHASE_CIRCLE_DIST);
12104                 }
12105
12106                 goal_point = aip->goal_point;
12107         } else if (is_ignore_object(aip, aip->ignore_objnum)) {
12108                 object  *ignore_objp = &Objects[aip->ignore_objnum];
12109
12110                 vector  tvec1;
12111                 float           dist;
12112
12113                 dist = vm_vec_normalized_dir(&tvec1, &Pl_objp->pos, &ignore_objp->pos);
12114
12115                 if (dist < ignore_objp->radius*2 + 1500.0f) {
12116                         vm_vec_scale_add(&goal_point, &Pl_objp->pos, &tvec1, ignore_objp->radius*2 + 1400.0f);
12117                         if (dist < ignore_objp->radius*2 + 1300.0f)
12118                                 target_speed = sip->max_speed * (1.25f - dist/(ignore_objp->radius*2 + 1500.0f));
12119                 }
12120         }
12121
12122         Assert(vm_vec_mag(&aip->goal_point) >= 0.0f);           //      Supposedly detects bogus vector
12123
12124         turn_towards_tangent(Pl_objp, &goal_point, 10*objp->radius + 200.0f);
12125
12126         set_accel_for_target_speed(Pl_objp, target_speed);
12127
12128 }
12129
12130 #define SHIELD_BALANCE_RATE     0.2f            //      0.1f -> takes 10 seconds to equalize shield.
12131
12132 //      Transfer shield energy to most recently hit section from others.
12133 void ai_transfer_shield(object *objp, int quadrant_num)
12134 {
12135         int     i;
12136         float   transfer_amount;
12137         float   transfer_delta;
12138         ship_info       *sip;
12139         float   max_quadrant_strength;
12140
12141         sip = &Ship_info[Ships[objp->instance].ship_info_index];
12142         max_quadrant_strength = sip->shields/MAX_SHIELD_SECTIONS;
12143
12144         transfer_amount = 0.0f;
12145         transfer_delta = (SHIELD_BALANCE_RATE/2) * max_quadrant_strength;
12146
12147         if (objp->shields[quadrant_num] + (MAX_SHIELD_SECTIONS-1)*transfer_delta > max_quadrant_strength)
12148                 transfer_delta = (max_quadrant_strength - objp->shields[quadrant_num])/(MAX_SHIELD_SECTIONS-1);
12149
12150         for (i=0; i<MAX_SHIELD_SECTIONS; i++)
12151                 if (i != quadrant_num) {
12152                         if (objp->shields[i] >= transfer_delta) {
12153                                 objp->shields[i] -= transfer_delta;
12154                                 transfer_amount += transfer_delta;
12155                         } else {
12156                                 transfer_amount += objp->shields[i];
12157                                 objp->shields[i] = 0.0f;
12158                         }
12159                 }
12160
12161         objp->shields[quadrant_num] += transfer_amount;
12162 }
12163
12164 void ai_balance_shield(object *objp)
12165 {
12166         int     i;
12167         float   shield_strength_avg;
12168         float   delta;
12169
12170
12171         shield_strength_avg = get_shield_strength(objp)/MAX_SHIELD_SECTIONS;
12172
12173         delta = SHIELD_BALANCE_RATE * shield_strength_avg;
12174
12175         for (i=0; i<MAX_SHIELD_SECTIONS; i++)
12176                 if (objp->shields[i] < shield_strength_avg) {
12177                         add_shield_strength(objp, delta);
12178                         if (objp->shields[i] > shield_strength_avg)
12179                                 objp->shields[i] = shield_strength_avg;
12180                 } else {
12181                         add_shield_strength(objp, -delta);
12182                         if (objp->shields[i] < shield_strength_avg)
12183                                 objp->shields[i] = shield_strength_avg;
12184                 }
12185 }
12186
12187 //      Manage the shield for this ship.
12188 //      Try to max out the side that was most recently hit.
12189 void ai_manage_shield(object *objp, ai_info *aip)
12190 {
12191         ship_info *sip;
12192
12193         sip = &Ship_info[Ships[objp->instance].ship_info_index];
12194
12195         if (timestamp_elapsed(aip->shield_manage_timestamp)) {
12196                 float           delay;
12197
12198                 //      Scale time until next manage shield based on Skill_level.
12199                 //      Ships on player's team are treated as if Skill_level is average.
12200                 if (Ships[objp->instance].team != Player_ship->team){
12201                         delay = Shield_manage_delays[Game_skill_level];
12202                 } else {
12203                         delay = Shield_manage_delays[NUM_SKILL_LEVELS/2];
12204                 }
12205
12206                 //      Scale between 1x and 3x based on ai_class
12207                 delay = delay + delay * (float) (3*(Num_ai_classes - aip->ai_class - 1) / (Num_ai_classes - 1));
12208                 aip->shield_manage_timestamp = timestamp((int) (delay * 1000.0f));
12209
12210                 if (sip->flags & SIF_SMALL_SHIP) {
12211                         if (Missiontime - aip->last_hit_time < F1_0*10)
12212                                 ai_transfer_shield(objp, aip->last_hit_quadrant);
12213                         else
12214                                 ai_balance_shield(objp);
12215                 }
12216
12217                 // 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]));
12218         }
12219 }
12220
12221 //      See if object *objp should evade an incoming missile.
12222 //      *aip is the ai_info pointer within *objp.
12223 void ai_maybe_evade_locked_missile(object *objp, ai_info *aip)
12224 {
12225         ship                    *shipp;
12226         ship_info       *sip;
12227
12228         shipp = &Ships[objp->instance];
12229         sip = &Ship_info[shipp->ship_info_index];
12230
12231         //      Only small ships evade an incoming missile.  Why would a capital ship try to swerve?
12232         if (!(Ship_info[Ships[objp->instance].ship_info_index].flags & SIF_SMALL_SHIP)) {
12233                 return;
12234         }
12235
12236         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
12237                 return;
12238         }
12239
12240         if (aip->nearest_locked_object != -1) {
12241                 object  *missile_objp;
12242
12243                 missile_objp = &Objects[aip->nearest_locked_object];
12244
12245                 if (Weapons[missile_objp->instance].homing_object != objp) {
12246                         //nprintf(("AI", "\nMissile lost home!"));
12247                         aip->nearest_locked_object = -1;
12248                         return;
12249                 }
12250
12251                 if ((missile_objp->type == OBJ_WEAPON) && (Weapon_info[Weapons[missile_objp->instance].weapon_info_index].wi_flags & WIF_HOMING)) {
12252                         float dist = vm_vec_dist_quick(&missile_objp->pos, &objp->pos);
12253                         float dist2 = 4.0f  * vm_vec_mag_quick(&missile_objp->phys_info.vel);                   
12254                         if (dist < dist2) {
12255                                 switch (aip->mode) {
12256                                 //      If in AIM_STRAFE mode, don't evade if parent of weapon is targeted ship.
12257                                 case AIM_STRAFE:
12258                                         if ((missile_objp->parent != -1) && (missile_objp->parent == aip->target_objnum)) {
12259                                                 ;
12260                                         } else {
12261                                                 ;               //      Alan -- If you want to handle incoming weapons from someone other than the ship
12262                                                                 //      the strafing ship is attacking, do it here.
12263                                         }
12264                                         break;
12265                                 case AIM_CHASE:
12266                                         //      Don't always go into evade weapon mode.  Usually, a countermeasure gets launched.
12267                                         // If low on countermeasures, more likely to try to evade.  If 8+, never evade due to low cmeasures.
12268                                         if (((((Missiontime >> 18) ^ OBJ_INDEX(objp)) & 3) == 0) || 
12269                                                 (objp->phys_info.speed < 40.0f) ||
12270                                                 (frand() < 1.0f - (float) shipp->cmeasure_count/8.0f)) {
12271                                                 if (aip->submode != SM_ATTACK_FOREVER) {        //      SM_ATTACK_FOREVER means engines blown.
12272                                                         aip->submode = SM_EVADE_WEAPON;
12273                                                         aip->submode_start_time = Missiontime;
12274                                                 }
12275                                         }
12276                                         break;
12277                                 case AIM_DOCK:  //      Ships in dock mode can evade iif they are not currently repairing or docked.
12278                                         if (aip->ai_flags & (AIF_REPAIRING | AIF_DOCKED))
12279                                                 break;
12280                                 case AIM_GUARD:
12281                                         //      If in guard mode and far away from guard object, don't pursue guy that hit me.
12282                                         if ((aip->guard_objnum != -1) && (aip->guard_signature == Objects[aip->guard_objnum].signature)) {
12283                                                 if (vm_vec_dist_quick(&objp->pos, &Objects[aip->guard_objnum].pos) > 500.0f) {
12284                                                         return;
12285                                                 }
12286                                         }
12287                                 case AIM_EVADE:
12288                                 case AIM_GET_BEHIND:
12289                                 case AIM_STAY_NEAR:
12290                                 case AIM_STILL:
12291                                 case AIM_AVOID:
12292                                 case AIM_WAYPOINTS:
12293                                 case AIM_NONE:
12294                                 case AIM_BIGSHIP:
12295                                 case AIM_PATH:
12296                                 case AIM_BE_REARMED:
12297                                 case AIM_SAFETY:
12298                                 case AIM_BAY_EMERGE:
12299                                         aip->active_goal = AI_ACTIVE_GOAL_DYNAMIC;
12300                                         aip->previous_mode = aip->mode;
12301                                         aip->previous_submode = aip->submode;
12302                                         aip->mode = AIM_EVADE_WEAPON;
12303                                         aip->submode = -1;
12304                                         aip->submode_start_time = Missiontime;
12305                                         aip->mode_time = timestamp(MAX_EVADE_TIME);     //      Max time to evade.
12306                                         //nprintf(("AI", "%s Evade weapon in frame #%i\n", Ships[objp->instance].ship_name, AI_FrameCount));
12307                                         break;
12308                                 case AIM_EVADE_WEAPON:          //      Note: We don't want to change mode on another evasion, or previous_mode will get bashed.
12309                                 case AIM_PLAY_DEAD:
12310                                 case AIM_BAY_DEPART:
12311                                 case AIM_SENTRYGUN:
12312                                         break;
12313                                 case AIM_WARP_OUT:
12314                                         break;
12315                                 default:
12316                                         Int3();                 //      Hey, what mode is it?
12317                                         break;
12318                                 }
12319                         }
12320                 } else {
12321                         aip->nearest_locked_object = -1;
12322                 }
12323         }
12324 }
12325
12326 //      Maybe evade a dumbfire weapon that was fired when Pl_objp was targeted.
12327 //      Have an 80% chance of evading in a second
12328 void maybe_evade_dumbfire_weapon(ai_info *aip)
12329 {
12330         //      Only small ships evade an incoming missile.  Why would a capital ship try to swerve?
12331         if (!(Ship_info[Ships[Pl_objp->instance].ship_info_index].flags & SIF_SMALL_SHIP)) {
12332                 return;
12333         }
12334
12335         //      Make sure in a mode in which we evade dumbfire weapons.
12336         switch (aip->mode) {
12337         case AIM_CHASE:
12338                 if (aip->submode == SM_ATTACK_FOREVER) {
12339                         return;
12340                 }
12341         case AIM_GUARD:
12342                 //      If in guard mode and far away from guard object, don't pursue guy that hit me.
12343                 if ((aip->guard_objnum != -1) && (aip->guard_signature == Objects[aip->guard_objnum].signature)) {
12344                         if (vm_vec_dist_quick(&Objects[Ships[aip->shipnum].objnum].pos, &Objects[aip->guard_objnum].pos) > 500.0f) {
12345                                 return;
12346                         }
12347                 }
12348         case AIM_STILL:
12349         case AIM_STAY_NEAR:
12350         case AIM_EVADE:
12351         case AIM_GET_BEHIND:
12352         case AIM_AVOID:
12353         case AIM_PATH:
12354         case AIM_NONE:
12355         case AIM_WAYPOINTS:
12356         case AIM_SAFETY:
12357                 break;
12358         case AIM_STRAFE:
12359         case AIM_BIGSHIP:
12360         case AIM_DOCK:
12361         case AIM_PLAY_DEAD:
12362         case AIM_EVADE_WEAPON:
12363         case AIM_BAY_EMERGE:
12364         case AIM_BAY_DEPART:
12365         case AIM_SENTRYGUN:
12366         case AIM_WARP_OUT:
12367                 return;
12368         default:
12369                 Int3(); //      Bogus mode!
12370                 return;
12371         }
12372
12373         if (is_instructor(&Objects[Ships[aip->shipnum].objnum]))
12374                 return; //      Instructor doesn't evade.
12375
12376         float t = ai_endangered_by_weapon(aip);
12377         if ((t > 0.0f) && (t < 1.0f)) {
12378         // Check if this weapon is from a large ship Pl_objp is attacking... if so, enter strafe mode
12379                 if ( ai_big_maybe_enter_strafe_mode(Pl_objp, aip->danger_weapon_objnum) ) {
12380                         return;
12381                 }
12382
12383                 switch (aip->mode) {
12384                 case AIM_CHASE:
12385                         switch (aip->submode) {
12386                         case SM_EVADE:
12387                         case SM_ATTACK_FOREVER:
12388                         case SM_AVOID:
12389                         case SM_GET_AWAY:
12390                         case SM_EVADE_WEAPON:
12391                                 break;
12392                         default:
12393                                 if (ai_near_full_strength(Pl_objp, &Ship_info[Ships[Pl_objp->instance].ship_info_index])) {
12394                                         //mprintf(("Ship %s entered super mode at %7.3f\n", Ships[Pl_objp->instance].ship_name, 1.0f * Missiontime / (1<<16)));
12395                                         aip->submode = SM_SUPER_ATTACK;
12396                                         aip->submode_start_time = Missiontime;
12397                                         aip->last_attack_time = Missiontime;
12398                                 } else {
12399                                         //mprintf(("Ship %s entered dumbfire evade mode at %7.3f\n", Ships[Pl_objp->instance].ship_name, 1.0f * Missiontime / (1<<16)));
12400                                         aip->submode = SM_EVADE_WEAPON;
12401                                         aip->submode_start_time = Missiontime;
12402                                 }
12403                                 break;
12404                         }
12405                         break;
12406                 case AIM_GUARD:
12407                 case AIM_STILL:
12408                 case AIM_STAY_NEAR:
12409                 case AIM_EVADE:
12410                 case AIM_GET_BEHIND:
12411                 case AIM_AVOID:
12412                 case AIM_PATH:
12413                 case AIM_NONE:
12414                 case AIM_WAYPOINTS:
12415                 case AIM_SAFETY:
12416                         if (!(aip->ai_flags & (AIF_NO_DYNAMIC | AIF_KAMIKAZE)) && (Ship_info[Ships[aip->shipnum].ship_info_index].flags & SIF_SMALL_SHIP)) {
12417                                 aip->active_goal = AI_ACTIVE_GOAL_DYNAMIC;
12418                                 aip->previous_mode = aip->mode;
12419                                 aip->previous_submode = aip->submode;
12420                                 aip->mode = AIM_EVADE_WEAPON;
12421                                 aip->submode = -1;
12422                                 aip->submode_start_time = Missiontime;
12423                                 aip->mode_time = timestamp(MAX_EVADE_TIME);     //      Evade for up to five seconds.
12424                         }
12425                         break;
12426                 case AIM_STRAFE:
12427                 case AIM_BIGSHIP:
12428                 case AIM_DOCK:
12429                 case AIM_PLAY_DEAD:
12430                 case AIM_EVADE_WEAPON:
12431                 case AIM_BAY_EMERGE:
12432                 case AIM_BAY_DEPART:
12433                 case AIM_SENTRYGUN:
12434                         break;
12435                 default:
12436                         Int3(); //      Bogus mode!
12437                 }
12438         }
12439 }
12440
12441 // determine what path to use when emerging from a fighter bay
12442 // input:       pl_objp =>      pointer to object for ship that is arriving
12443 //                              pos             =>      output parameter, it is the starting world pos for path choosen
12444 //                              fvec            =>      output parameter, this is the forward vector that ship has when arriving
12445 //
12446 // exit:                -1              =>      path could not be located
12447 //                               0              => success
12448 int ai_acquire_emerge_path(object *pl_objp, int parent_objnum, vector *pos, vector *fvec)
12449 {
12450         int                     path_index, sb_path_index;
12451         ship                    *parent_sp = NULL;
12452         polymodel       *pm;
12453         ai_info         *aip;
12454         ship_bay                *sb;
12455         pnode                   *pnp;
12456         vector          *next_point;
12457
12458         aip = &Ai_info[Ships[pl_objp->instance].ai_index];
12459
12460         if ( parent_objnum == -1 ) {
12461                 Int3();
12462                 return -1;
12463         }
12464
12465         parent_sp = &Ships[Objects[parent_objnum].instance];
12466
12467         Assert(parent_sp != NULL);
12468         pm = model_get( parent_sp->modelnum );
12469         sb = pm->ship_bay;
12470
12471         if ( sb == NULL ) 
12472                 return -1;
12473
12474         if ( sb->num_paths <= 0 ) 
12475                 return -1;
12476
12477         // try to find a bay path that is not taken
12478         path_index = -1;
12479         sb_path_index = Ai_last_arrive_path++;
12480
12481         if ( sb_path_index >= sb->num_paths ) {
12482                 sb_path_index=0;
12483                 Ai_last_arrive_path=0;
12484         }
12485
12486         path_index = sb->paths[sb_path_index];
12487         if ( path_index == -1 ) 
12488                 return -1;
12489
12490         // create the path for pl_objp to follow
12491         create_model_exit_path(pl_objp, &Objects[parent_objnum], path_index, pm->paths[path_index].nverts);
12492         
12493         // Set this flag, so we don't bother recreating the path... we won't need to update the path
12494         // that has just been created.
12495 //      aip->ai_flags |= AIF_USE_STATIC_PATH;
12496
12497         // now return to the caller what the starting world pos and starting fvec for the ship will be
12498         Assert((aip->path_start >= 0) && (aip->path_start < MAX_PATH_POINTS));
12499         pnp = &Path_points[aip->path_start];
12500         *pos = pnp->pos;
12501
12502         // calc the forward vector using the starting two points of the path
12503         pnp = &Path_points[aip->path_start+1];
12504         next_point = &pnp->pos;
12505         vm_vec_normalized_dir(fvec, next_point, pos);
12506
12507         // record the parent objnum, since we'll need it once we're done with following the path
12508         aip->goal_objnum = parent_objnum;
12509         aip->goal_signature = Objects[parent_objnum].signature;
12510         aip->mode = AIM_BAY_EMERGE;
12511         aip->submode_start_time = Missiontime;
12512
12513         // set up starting vel
12514         vector vel;
12515         float speed;
12516         speed = Ship_info[Ships[pl_objp->instance].ship_info_index].max_speed;
12517         vel = *fvec;
12518         vm_vec_scale( &vel, speed );
12519         pl_objp->phys_info.vel = vel;
12520         pl_objp->phys_info.desired_vel = vel;
12521         pl_objp->phys_info.prev_ramp_vel.x = 0.0f;
12522         pl_objp->phys_info.prev_ramp_vel.y = 0.0f;
12523         pl_objp->phys_info.prev_ramp_vel.z = speed;
12524         pl_objp->phys_info.forward_thrust = 0.0f;               // How much the forward thruster is applied.  0-1.
12525
12526         return 0;       
12527 }
12528
12529 // clean up path data used for emerging from a fighter bay
12530 void ai_emerge_bay_path_cleanup(ai_info *aip)
12531 {
12532         aip->path_start = -1;
12533         aip->path_cur = -1;
12534         aip->path_length = 0;
12535         aip->mode = AIM_NONE;
12536 }
12537
12538 // handler for AIM_BAY_EMERGE
12539 void ai_bay_emerge()
12540 {
12541         ai_info *aip;
12542         int             parent_died=0;
12543
12544         aip = &Ai_info[Ships[Pl_objp->instance].ai_index];
12545
12546         // if no path to follow, leave this mode
12547         if ( aip->path_start < 0 ) {
12548                 aip->mode = AIM_NONE;
12549                 return;
12550         }
12551
12552         // ensure parent ship is still alive
12553         if ( aip->goal_objnum < 0 ) {
12554                 parent_died=1;
12555         } 
12556         if ( !parent_died ) {
12557                 if ( Objects[aip->goal_objnum].signature != aip->goal_signature ) {
12558                         parent_died=1;
12559                 }
12560         }
12561
12562         if ( !parent_died ) {
12563                 Assert(Objects[aip->goal_objnum].type == OBJ_SHIP);
12564                 if ( Ships[Objects[aip->goal_objnum].instance].flags & SF_DYING ) {
12565                         parent_died = 1;
12566                 }
12567         }
12568
12569         if ( parent_died ) {
12570                 ai_emerge_bay_path_cleanup(aip);
12571                 return;
12572         }
12573
12574         // follow the path to the final point
12575         ai_path();
12576
12577         // New test: must have been in AI_EMERGE mode for at least 10 seconds, and be a minimum distance from the start point
12578         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)) {
12579                 // erase path
12580                 ai_emerge_bay_path_cleanup(aip);
12581         }
12582
12583         // 2-25-99: Need this check to fix an assert for supercap ships... maybe we'll only do this check for supercaps 
12584         if (aip->path_cur > (aip->path_start+aip->path_length-1)) {
12585                 ai_emerge_bay_path_cleanup(aip);
12586         }       
12587 }
12588
12589 // Select the closest depart path
12590 //
12591 //      input:  aip     =>              ai info pointer to ship seeking to depart
12592 //                              pm              =>              pointer to polymodel for the ship contining the ship bay/depart paths
12593 //
12594 // exit:                >=0     =>              ship bay path index for depart path (ie index into sb->paths[])
12595 //                              -1              =>              no path could be found
12596 //
12597 // NOTE: this function should only be used for calculating closest depart paths for ai mode
12598 //                      AI_BAY_DEPART.  It tries to find the closest path that isn't already in use
12599 int ai_find_closest_depart_path(ai_info *aip, polymodel *pm)
12600 {
12601         int                     i, j, best_path, best_free_path;
12602         float                   dist, min_dist, min_free_dist;
12603         vector          *source;
12604         model_path      *mp;
12605         ship_bay                *sb;
12606
12607         sb = pm->ship_bay;
12608
12609         best_free_path = best_path = -1;
12610         min_free_dist = min_dist = 1e20f;
12611         Assert(aip->shipnum >= 0);
12612         source = &Objects[Ships[aip->shipnum].objnum].pos;
12613
12614         for ( i = 0; i < sb->num_paths; i++ ) {
12615
12616
12617                 mp = &pm->paths[sb->paths[i]];
12618                 for ( j = 0; j < mp->nverts; j++ ) {
12619                         dist = vm_vec_dist_squared(source, &mp->verts[j].pos);
12620
12621                         if ( dist < min_dist ) {
12622                                 min_dist = dist;
12623                                 best_path = i;
12624                         }
12625
12626                         // If this is a free path
12627                         if ( !(sb->depart_flags & (1<<i)) ) {
12628                                 if ( dist < min_free_dist ) {
12629                                         min_free_dist = dist;
12630                                         best_free_path = i;
12631                                 }
12632                         }
12633                 }
12634         }
12635
12636         if ( best_free_path >= 0 ) {
12637                 return best_free_path;          
12638         }
12639
12640         return best_path;
12641 }
12642
12643 // determine what path to use when trying to depart to a fighter bay
12644 // NOTE: this should be called when AIM_BAY_DEPART mode is set
12645 //
12646 // input:       pl_objp =>      pointer to object for ship that is departing
12647 //
12648 // exit:                -1      =>      could not find depart path
12649 //                              0       => found depart path
12650 int ai_acquire_depart_path(object *pl_objp, int parent_objnum)
12651 {
12652         int                     objnum, path_index;
12653         polymodel       *pm;
12654         ai_info         *aip;
12655         ship                    *sp;
12656         ship_bay                *sb;
12657
12658         aip = &Ai_info[Ships[pl_objp->instance].ai_index];
12659
12660         if ( parent_objnum == -1 ) {
12661                 ship_obj        *so;
12662
12663                 // for now just locate a captial ship on the same team:
12664                 so = GET_FIRST(&Ship_obj_list);
12665                 objnum = -1;
12666                 while(so != END_OF_LIST(&Ship_obj_list)){
12667                         sp = &Ships[Objects[so->objnum].instance];
12668                         if ( (Ship_info[sp->ship_info_index].flags & (SIF_HUGE_SHIP)) && (sp->team == Ships[pl_objp->instance].team) ) {
12669                                 objnum = so->objnum;
12670                                 break;
12671                         }
12672                         so = GET_NEXT(so);
12673                 } 
12674         } else {
12675                 objnum = parent_objnum;
12676         }
12677
12678         aip->path_start = -1;
12679
12680         if ( objnum == -1 )
12681                 return -1;
12682
12683         pm = model_get( Ships[Objects[objnum].instance].modelnum );
12684         sb = pm->ship_bay;
12685
12686         if ( sb == NULL ) 
12687                 return -1;
12688         if ( sb->num_paths <= 0 ) 
12689                 return -1;
12690
12691 /*
12692         
12693         path_index = -1;
12694         for ( i = 0; i < sb->num_paths; i++ ) {
12695                 if ( !(sb->depart_flags & (1<<i)) ) {
12696                         sb->depart_flags |= (1<<i);
12697                         path_index = sb->paths[i];
12698                         aip->submode_parm0 = i;                 // use mode-specific parameter to record ship bay path index
12699                         break;
12700                 }
12701         }
12702 */
12703         
12704         // take the closest path we can find
12705         int ship_bay_path;
12706         ship_bay_path = ai_find_closest_depart_path(aip, pm);
12707         path_index = sb->paths[ship_bay_path];
12708         aip->submode_parm0 = ship_bay_path;
12709         sb->depart_flags |= (1<<ship_bay_path);
12710
12711         if ( path_index == -1 ) {
12712                 return -1;
12713         }
12714
12715         Assert(pm->n_paths > path_index);
12716         ai_find_path(pl_objp, objnum, path_index, 0);
12717
12718         // Set this flag, so we don't bother recreating the path... we won't need to update the path
12719         // that has just been created.
12720         aip->ai_flags &= ~AIF_USE_STATIC_PATH;
12721
12722         aip->goal_objnum = objnum;
12723         aip->goal_signature = Objects[objnum].signature;
12724         aip->mode = AIM_BAY_DEPART;
12725
12726         Ships[pl_objp->instance].flags |= SF_DEPART_DOCKBAY;
12727         return 0;
12728 }
12729
12730 // handler for AIM_BAY_DEPART
12731 void ai_bay_depart()
12732 {
12733         ai_info *aip;
12734
12735         aip = &Ai_info[Ships[Pl_objp->instance].ai_index];
12736
12737         // if no path to follow, leave this mode
12738         if ( aip->path_start < 0 ) {
12739                 aip->mode = AIM_NONE;
12740                 return;
12741         }
12742
12743         // check if parent ship still exists, if not abort depart 
12744         if ( aip->goal_signature != Objects[aip->goal_objnum].signature ) {
12745                 aip->mode = AIM_NONE;
12746                 return;
12747         }
12748
12749         // follow the path to the final point
12750         ai_path();
12751
12752         // if the final point is reached, let default AI take over
12753         if ( aip->path_cur >= (aip->path_start+aip->path_length) ) {
12754                 polymodel       *pm;
12755                 ship_bay                *sb;
12756
12757                 pm = model_get( Ships[Objects[aip->goal_objnum].instance].modelnum );
12758                 sb = pm->ship_bay;
12759                 if ( sb != NULL ) {
12760                         sb->depart_flags &= ~(1<<aip->submode_parm0);
12761                 }
12762
12763                 // make ship disappear
12764                 Pl_objp->flags |= OF_SHOULD_BE_DEAD;
12765                 ship_departed( Pl_objp->instance );
12766
12767                 // clean up path stuff
12768                 aip->path_start = -1;
12769                 aip->path_cur = -1;
12770                 aip->path_length = 0;
12771                 aip->mode = AIM_NONE;
12772         }
12773 }
12774
12775 // Handler for AIM_SENTRYGUN.  This AI mode is for sentry guns only (ie floating turrets).
12776 void ai_sentrygun()
12777 {
12778         // Nothing to do here.  Turret firing is handled via process_subobjects().
12779         // If you want the sentry guns to do anything beyond firing their turrets at enemies, add it here!
12780 }
12781
12782 //      --------------------------------------------------------------------------
12783 //      Execute behavior given by aip->mode.
12784 void ai_execute_behavior(ai_info *aip)
12785 {
12786         switch (aip->mode) {
12787         case AIM_CHASE:
12788                 if (En_objp) {
12789                         ai_chase();
12790                 } else if (aip->submode == SM_EVADE_WEAPON) {
12791                         evade_weapon();
12792                         // maybe reset submode
12793                         if (aip->danger_weapon_objnum == -1) {
12794                                 aip->submode = SM_ATTACK;
12795                                 aip->submode_start_time = Missiontime;
12796                                 aip->last_attack_time = Missiontime;
12797                         }
12798                 } else {
12799                         //      Don't circle if this is the instructor.
12800                         ship    *shipp = &Ships[aip->shipnum];
12801                         ship_info       *sip = &Ship_info[shipp->ship_info_index];
12802
12803                         if (strnicmp(shipp->ship_name, INSTRUCTOR_SHIP_NAME, strlen(INSTRUCTOR_SHIP_NAME))) {
12804                                 if (sip->flags & (SIF_BIG_SHIP | SIF_HUGE_SHIP)) {
12805                                         aip->mode = AIM_NONE;
12806                                 } else {
12807                                         ai_chase_circle(Pl_objp);
12808                                 }
12809                         }
12810                 }
12811                 break;
12812         case AIM_EVADE:
12813                 if (En_objp) {
12814                         ai_evade();
12815                 } else {
12816                         vector  tvec;
12817                         vm_vec_scale_add(&tvec, &Pl_objp->pos, &Pl_objp->orient.rvec, 100.0f);
12818                         turn_towards_point(Pl_objp, &tvec, NULL, 0.0f);
12819                         accelerate_ship(aip, 0.5f);
12820                 }
12821                 break;
12822         case AIM_STILL:
12823                 ai_still();
12824                 break;
12825         case AIM_STAY_NEAR:
12826                 ai_stay_near();
12827                 break;
12828         case AIM_GUARD:
12829                 ai_guard();
12830                 break;
12831         case AIM_WAYPOINTS:
12832                 ai_waypoints();
12833                 break;
12834         case AIM_DOCK:
12835                 ai_dock();
12836                 break;
12837         case AIM_NONE:
12838                 // ai_formation();
12839                 break;
12840         case AIM_BIGSHIP:
12841                 ai_big_ship(Pl_objp);
12842                 break;
12843         case AIM_PATH: {
12844                 int path_num;
12845                 path_num = ai_return_path_num_from_dockbay(&Objects[aip->goal_objnum], 0);
12846                 ai_find_path(Pl_objp, aip->goal_objnum, path_num, 0);
12847                 ai_path();
12848                 break;
12849         }
12850         case AIM_SAFETY:
12851                 ai_safety();
12852                 break;
12853         case AIM_EVADE_WEAPON:
12854                 evade_weapon();
12855                 break;
12856         case AIM_STRAFE:
12857                 if (En_objp) {
12858                         Assert(En_objp->type == OBJ_SHIP);
12859                         ai_big_strafe();        // strafe a big ship
12860                 } else {
12861                         aip->mode = AIM_NONE;
12862                 }
12863                 break;
12864         case AIM_BAY_EMERGE:
12865                 ai_bay_emerge();
12866                 break;
12867         case AIM_BAY_DEPART:
12868                 ai_bay_depart();
12869                 break;
12870         case AIM_SENTRYGUN:
12871                 ai_sentrygun();
12872                 break;
12873         case AIM_WARP_OUT:
12874                 break;          //      Note, handled directly from ai_frame().
12875         default:
12876                 Int3();         //      This should never happen -- MK, 5/12/97 
12877                 break;
12878         }
12879
12880         if ( !(ship_get_SIF(aip->shipnum) & SIF_NOT_FLYABLE) ) {
12881                 maybe_evade_dumbfire_weapon(aip);
12882         }
12883 }
12884
12885 //      Auxiliary function for maybe_request_support.
12886 //      Return 1 if subsystem "type" is worthy of repair, else return 0.
12887 //      Since subsystems cannot be repaired if they are at 0 strength, don't return 1 if subsystem is dead.
12888 int mrs_subsystem(ship *shipp, int type)
12889 {
12890         float   t;
12891
12892         t = ship_get_subsystem_strength(shipp, type);
12893
12894         if (t > 0.0f) {
12895                 return (int) ((1.0f - t) * 3);
12896         } else {
12897                 return 3;
12898         }
12899 }
12900
12901 //      Return number of ships on *objp's team that are currently rearming.
12902 int num_allies_rearming(object *objp)
12903 {
12904         ship_obj        *so;
12905         int             team;
12906         int             count = 0;
12907
12908         team = Ships[objp->instance].team;
12909
12910         for ( so = GET_FIRST(&Ship_obj_list); so != END_OF_LIST(&Ship_obj_list); so = GET_NEXT(so) ) {
12911                 object  *A;
12912                 
12913                 Assert (so->objnum != -1);
12914                 A = &Objects[so->objnum];
12915
12916                 if (Ships[A->instance].team == team) {
12917                         if (Ai_info[Ships[A->instance].ai_index].ai_flags & (AIF_REPAIRING | AIF_AWAITING_REPAIR)) {
12918                                 count++;
12919                         }
12920                 }
12921         }
12922
12923         return count;
12924 }
12925
12926
12927 //      Maybe ship *objp should request support (rearm/repair).
12928 //      If it does, return TRUE, else return FALSE.
12929 int maybe_request_support(object *objp)
12930 {
12931         ship_info       *sip;
12932         ship                    *shipp;
12933         ai_info         *aip;
12934         int                     desire;
12935
12936         Assert(objp->type == OBJ_SHIP);
12937         shipp = &Ships[objp->instance];
12938         aip = &Ai_info[shipp->ai_index];
12939         sip = &Ship_info[shipp->ship_info_index];
12940
12941         if (!timestamp_elapsed(aip->next_rearm_request_timestamp))
12942                 return 0;
12943
12944         //      Only fighters and bombers request support.
12945         if (!(sip->flags & (SIF_FIGHTER | SIF_BOMBER)))
12946                 return 0;
12947
12948         //      A ship that is currently awaiting does not need support!
12949         if (aip->ai_flags & (AIF_AWAITING_REPAIR | AIF_BEING_REPAIRED))
12950                 return 0;
12951
12952         if (!is_support_allowed(objp))
12953                 return 0;
12954
12955         //if (shipp->team != TEAM_FRIENDLY)
12956         //      return 0;
12957
12958         //      Compute a desire value.
12959         //      Desire of 0 means no reason to request support.
12960         //      1 is slight, 2 more, etc.  Maximum is around 20.  Anything larger than 3 is pretty strong.
12961         desire = 0;
12962
12963         //      Set desire based on hull strength.
12964         //      No: We no longer repair hull, so this would cause repeated repair requests.
12965         //desire += 6 - (int) ((objp->hull_strength/sip->initial_hull_strength) * 6.0f);
12966
12967         //      Set desire based on key subsystems.
12968         desire += 2*mrs_subsystem(shipp, SUBSYSTEM_ENGINE);     //      Note, disabled engine forces repair request, regardless of nearby enemies.
12969         desire += mrs_subsystem(shipp, SUBSYSTEM_COMMUNICATION);
12970         desire += mrs_subsystem(shipp, SUBSYSTEM_WEAPONS);
12971         desire += mrs_subsystem(shipp, SUBSYSTEM_SENSORS);
12972
12973         //      Set desire based on percentage of secondary weapons.
12974         ship_weapon *swp = &shipp->weapons;
12975
12976         for ( int i = 0; i < swp->num_secondary_banks; i++ ) {
12977                 if (swp->secondary_bank_start_ammo[i] > 0) {
12978 //                      float r = (float) swp->secondary_bank_ammo[i]*Weapon_info[swp->secondary_bank_weapons[i]].cargo_size/swp->secondary_bank_capacity[i];
12979                         float r = (float) swp->secondary_bank_ammo[i]/swp->secondary_bank_start_ammo[i];
12980                         desire += (int) ((1.0f - r) * 3.0f);
12981                 }
12982         }
12983
12984         //      If no reason to repair, don't bother to see if it's safe to repair.
12985         if (desire == 0){
12986                 return 0;
12987         }
12988
12989         //      Compute danger threshold.
12990         //      Balance this with desire and maybe request support.
12991         if (ai_good_time_to_rearm( objp )) {
12992                 ai_issue_rearm_request(objp);
12993                 return 1;
12994         } else if (num_allies_rearming(objp) < 2) {
12995                 if (desire >= 8) {      //      guarantees disabled will cause repair request
12996                         ai_issue_rearm_request(objp);
12997                 } else if (desire >= 3) {               //      >= 3 means having a single subsystem fully blown will cause repair.
12998                         int     count;
12999                         int objnum = find_nearby_hostile(OBJ_INDEX(objp), get_enemy_team_mask(OBJ_INDEX(objp)), 2000.0f, &count);
13000
13001                         if ((objnum == -1) || (count < 2) || (vm_vec_dist_quick(&objp->pos, &Objects[objnum].pos) > 3000.0f*count/desire)) {
13002                                 ai_issue_rearm_request(objp);
13003                                 return 1;
13004                         } else {
13005                                 //nprintf(("AI", "Would like to rearm, but enemy only %7.3f units away.\n", vm_vec_dist_quick(&objp->pos, &Objects[objnum].pos)));
13006                         }
13007                 }
13008         }
13009
13010         return 0;
13011
13012 }
13013
13014 void ai_set_mode_warp_out(object *objp, ai_info *aip)
13015 {
13016         ai_abort_rearm_request(objp);
13017         if (aip->mode != AIM_WARP_OUT) {
13018                 aip->mode = AIM_WARP_OUT;
13019                 aip->submode = AIS_WARP_1;
13020         }
13021 }
13022
13023 //      Maybe warp ship out.
13024 //      Shivan and HoL fighter/bomber warp out if their weapons subsystems have been destroyed.
13025 void ai_maybe_warp_out(object *objp)
13026 {
13027         ship    *shipp;
13028
13029         // don't do anything if in a training mission.
13030         if ( The_mission.game_type & MISSION_TYPE_TRAINING )
13031                 return;
13032
13033         Assert(objp->type == OBJ_SHIP);
13034
13035         shipp = &Ships[objp->instance];
13036         ai_info *aip = &Ai_info[shipp->ai_index];
13037
13038         if (aip->mode == AIM_WARP_OUT)
13039                 return;
13040
13041         //      If a support ship with no goals and low hull, warp out.  Be sure that there are no pending goals
13042         // in the support ships ai_goal array.  Just process this ships goals.
13043         ship_info       *sip = &Ship_info[shipp->ship_info_index];
13044         if (sip->flags & SIF_SUPPORT) {
13045                 if ( timestamp_elapsed(aip->warp_out_timestamp) ) {
13046                         ai_process_mission_orders( OBJ_INDEX(objp), aip );
13047                         if ( (aip->dock_objnum == -1) && (objp->hull_strength/sip->initial_hull_strength < 0.25f) ) {
13048                                 ai_set_mode_warp_out(objp, aip);
13049                         }
13050                 }
13051         }
13052
13053         //      Friendly don't warp out, they'll eventually request support.
13054         if (shipp->team == TEAM_FRIENDLY)
13055                 return;
13056
13057         if (!(shipp->flags & SF_DEPARTING)) {
13058                 ship_info       *sip;
13059
13060                 sip = &Ship_info[shipp->ship_info_index];
13061                 if (sip->flags & (SIF_FIGHTER | SIF_BOMBER)) {
13062                         if (aip->warp_out_timestamp == 0) {
13063                                 //if (ship_get_subsystem_strength(shipp, SUBSYSTEM_WEAPONS) == 0.0f) {
13064                                 //      aip->warp_out_timestamp = timestamp(((myrand() % 10) + 10) * 1000);
13065                                 //}
13066                         } else if (timestamp_elapsed(aip->warp_out_timestamp)) {
13067                                 ai_set_mode_warp_out(objp, aip);
13068                         }
13069                 }
13070         }
13071 }
13072
13073 //      Warp this ship out.
13074 void ai_warp_out(object *objp)
13075 {
13076         // if dying, don't warp out.
13077         if (Ships[objp->instance].flags & SF_DYING) {
13078                 return;
13079         }
13080
13081         ai_info *aip;
13082
13083         aip = &Ai_info[Ships[objp->instance].ai_index];
13084
13085         switch (aip->submode) {
13086         case AIS_WARP_1:
13087                 aip->force_warp_time = timestamp(10*1000);      //      Try to avoid a collision for up to ten seconds.
13088                 aip->submode = AIS_WARP_2;
13089                 break;
13090         case AIS_WARP_2:                        //      Make sure won't collide with any object.
13091                 if (timestamp_elapsed(aip->force_warp_time) || !collide_predict_large_ship(objp, objp->radius*2.0f + 100.0f)) {
13092                         aip->submode = AIS_WARP_3;
13093
13094                         // maybe recalculate collision pairs.
13095                         if (ship_get_warp_speed(objp) > ship_get_max_speed(&Ships[objp->instance])) {
13096                                 // recalculate collision pairs
13097                                 OBJ_RECALC_PAIRS(objp); 
13098                         }
13099
13100                         aip->force_warp_time = timestamp(4*1000);               //      Try to attain target speed for up to 4 seconds.
13101                 } else {
13102                         vector  goal_point;
13103                         vm_vec_scale_add(&goal_point, &objp->pos, &objp->orient.uvec, 100.0f);
13104                         turn_towards_point(objp, &goal_point, NULL, 0.0f);
13105                         accelerate_ship(aip, 0.0f);
13106                 }
13107                 break;
13108         case AIS_WARP_3:
13109                 //      Rampup desired_vel in here from current to desired velocity and set PF_USE_VEL. (not sure this is the right flag)
13110                 //      desired velocity is computed in shipfx_calculate_warp_time().  See shipfx#572 for sample code.
13111                 float   speed, goal_speed;
13112                 float shipfx_calculate_warp_speed(object*);
13113                 goal_speed = shipfx_calculate_warp_speed(objp);
13114
13115                 // HUGE ships go immediately to AIS_WARP_4
13116                 if (Ship_info[Ships[objp->instance].ship_info_index].flags & SIF_HUGE_SHIP) {
13117                         aip->submode = AIS_WARP_4;
13118                         break;
13119                 }
13120                 //compute_warpout_stuff(objp, &goal_speed, &warp_time, &warp_pos);
13121                 //goal_speed = 80.0f;
13122                 //set_accel_for_target_speed(objp, 40.0f);
13123                 // 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
13124                 speed = goal_speed * flFrametime + objp->phys_info.speed * (1.0f - flFrametime);
13125                 vm_vec_copy_scale(&objp->phys_info.vel, &objp->orient.fvec, speed);
13126                 objp->phys_info.desired_vel = objp->phys_info.vel;
13127                 // nprintf(("AI", "Frame %i, speed = %7.3f, goal = %7.3f\n", Framecount, vm_vec_mag_quick(&objp->phys_info.vel), goal_speed));
13128                 if (timestamp_elapsed(aip->force_warp_time) || (fl_abs(objp->phys_info.speed - goal_speed) < 2.0f))
13129                         aip->submode = AIS_WARP_4;
13130                 break;
13131         case AIS_WARP_4: {
13132                 shipfx_warpout_start(objp);
13133                 aip->submode = AIS_WARP_5;
13134                 break;
13135         }
13136         case AIS_WARP_5:
13137                 break;
13138         default:
13139                 Int3();         //      Illegal submode for warping out.
13140         }
13141 }
13142
13143 //      Return object index of weapon that could produce a shockwave that should be known about to *objp.
13144 //      Return nearest one.
13145 int ai_find_shockwave_weapon(object *objp, ai_info *aip)
13146 {
13147         missile_obj     *mo;
13148         float   nearest_dist = 999999.9f;
13149         int     nearest_index = -1;
13150
13151         for ( mo = GET_NEXT(&Missile_obj_list); mo != END_OF_LIST(&Missile_obj_list); mo = GET_NEXT(mo) ) {
13152                 object          *A;
13153                 weapon          *wp;
13154                 weapon_info     *wip;
13155         
13156                 Assert(mo->objnum >= 0 && mo->objnum < MAX_OBJECTS);
13157                 A = &Objects[mo->objnum];
13158
13159                 Assert(A->type == OBJ_WEAPON);
13160                 Assert((A->instance >= 0) && (A->instance < MAX_WEAPONS));
13161                 wp = &Weapons[A->instance];
13162                 wip = &Weapon_info[wp->weapon_info_index];
13163                 Assert( wip->subtype == WP_MISSILE );
13164
13165                 if (wip->shockwave_speed > 0.0f) {
13166                         float   dist;
13167
13168                         dist = vm_vec_dist_quick(&objp->pos, &A->pos);
13169                         if (dist < nearest_dist) {
13170                                 nearest_dist = dist;
13171                                 nearest_index = mo->objnum;
13172                         }
13173                 }
13174         }
13175
13176         return nearest_index;
13177
13178 }
13179
13180 #define EVADE_SHOCKWAVE_DAMAGE_THRESHOLD                100.0f
13181
13182 //      Tell all ships to avoid a big ship that is blowing up.
13183 //      Only avoid if shockwave is fairly large.
13184 //      OK to tell everyone to avoid.  If they're too far away, that gets cleaned up in the frame interval.
13185 void ai_announce_ship_dying(object *dying_objp)
13186 {
13187         float damage = ship_get_exp_damage(dying_objp);
13188         if (damage >= EVADE_SHOCKWAVE_DAMAGE_THRESHOLD) {
13189                 ship_obj        *so;
13190
13191                 for ( so = GET_FIRST(&Ship_obj_list); so != END_OF_LIST(&Ship_obj_list); so = GET_NEXT(so) ) {
13192                         if (Ship_info[Ships[Objects[so->objnum].instance].ship_info_index].flags & (SIF_SMALL_SHIP | SIF_FREIGHTER)) {
13193                                 ai_info *aip;
13194
13195                                 aip = &Ai_info[Ships[Objects[so->objnum].instance].ai_index];
13196
13197                                 if ( !(aip->ai_flags & (AIF_DOCKED|AIF_BEING_REPAIRED)) ) {
13198                                         aip->ai_flags |= AIF_AVOID_SHOCKWAVE_SHIP;
13199                                 }
13200                         }
13201                 }
13202         }
13203 }
13204
13205
13206 //      Return object index of weapon that could produce a shockwave that should be known about to *objp.
13207 //      Return nearest one.
13208 int ai_find_shockwave_ship(object *objp, ai_info *aip)
13209 {
13210         ship_obj        *so;
13211         float   nearest_dist = 999999.9f;
13212         int     nearest_index = -1;
13213
13214         for ( so = GET_NEXT(&Ship_obj_list); so != END_OF_LIST(&Ship_obj_list); so = GET_NEXT(so) ) {
13215                 object          *A;
13216                 ship                    *shipp;
13217         
13218                 Assert(so->objnum >= 0 && so->objnum < MAX_OBJECTS);
13219                 A = &Objects[so->objnum];
13220
13221                 Assert(A->type == OBJ_SHIP);
13222                 Assert((A->instance >= 0) && (A->instance < MAX_SHIPS));
13223                 shipp = &Ships[A->instance];
13224                 //      Only look at objects in the process of dying.
13225                 if (shipp->flags & SF_DYING) {
13226                         float damage = ship_get_exp_damage(objp);
13227
13228                         if (damage >= EVADE_SHOCKWAVE_DAMAGE_THRESHOLD) {               //      Only evade quite large blasts
13229                                 float   dist;
13230
13231                                 dist = vm_vec_dist_quick(&objp->pos, &A->pos);
13232                                 if (dist < nearest_dist) {
13233                                         nearest_dist = dist;
13234                                         nearest_index = so->objnum;
13235                                 }
13236                         }
13237                 }
13238         }
13239
13240         return nearest_index;
13241
13242 }
13243
13244 int aas_1(object *objp, ai_info *aip, vector *safe_pos)
13245 {
13246         // MAKE SURE safe_pos DOES NOT TAKE US TOWARD THE A SHIP WE'RE ATTACKING.
13247         if (aip->ai_flags & AIF_AVOID_SHOCKWAVE_WEAPON) {
13248                 //      If we don't currently know of a weapon to avoid, try to find one.
13249                 //      If we can't find one, then clear the bit so we don't keep coming here.
13250                 if (aip->shockwave_object == -1) {
13251                         int shockwave_weapon = ai_find_shockwave_weapon(objp, aip);
13252                         if (shockwave_weapon == -1) {
13253                                 aip->ai_flags &= ~AIF_AVOID_SHOCKWAVE_WEAPON;
13254                                 return 0;
13255                         } else {
13256                                 aip->shockwave_object = shockwave_weapon;
13257                         }
13258                 }
13259
13260                 //      OK, we have reason to believe we should avoid aip->shockwave_object.
13261                 Assert(aip->shockwave_object > -1);
13262                 object  *weapon_objp = &Objects[aip->shockwave_object];
13263                 if (weapon_objp->type != OBJ_WEAPON) {
13264                         aip->ai_flags &= ~AIF_AVOID_SHOCKWAVE_WEAPON;
13265                         aip->shockwave_object = -1;
13266                         return 0;
13267                 }
13268
13269                 weapon  *weaponp = &Weapons[weapon_objp->instance];
13270                 weapon_info     *wip = &Weapon_info[weaponp->weapon_info_index];
13271                 object *target_ship_obj = NULL;
13272
13273                 if (wip->shockwave_speed == 0.0f) {
13274                         aip->ai_flags &= ~AIF_AVOID_SHOCKWAVE_WEAPON;
13275                         aip->shockwave_object = -1;
13276                         return 0;
13277                 }
13278
13279                 float   danger_dist;
13280                 vector  expected_pos;           //      Position at which we expect the weapon to detonate.
13281                 int             pos_set = 0;
13282
13283                 danger_dist = wip->outer_radius;
13284                 //      Set predicted position of detonation.
13285                 //      If an aspect locked missile, assume it will detonate at the homing position.
13286                 //      If not, which is not possible in a default FreeSpace weapon, then predict it will detonate at some
13287                 //      time in the future, this time based on max lifetime and life left.
13288                 if (wip->wi_flags & WIF_HOMING_ASPECT) {
13289                         expected_pos = weaponp->homing_pos;
13290                         if (weaponp->homing_object && weaponp->homing_object->type == OBJ_SHIP) {
13291                                 target_ship_obj = weaponp->homing_object;
13292                         }
13293                         pos_set = 1;
13294                         if (IS_VEC_NULL(&weaponp->homing_pos)) {
13295                                 pos_set = 0;
13296                                 if (weaponp->target_num != -1) {
13297                                         if (Objects[weaponp->target_num].type == OBJ_SHIP) {
13298                                                 target_ship_obj = &Objects[weaponp->target_num];
13299                                                 expected_pos = target_ship_obj->pos;
13300                                                 pos_set = 1;
13301                                         }
13302                                 }
13303                         }
13304                 }
13305
13306                 if (!pos_set) {
13307                         float   time_scale;
13308
13309                         if (wip->lifetime - weaponp->lifeleft > 5.0f) {
13310                                 time_scale = 1.0f;
13311                         } else {
13312                                 time_scale = weaponp->lifeleft/2.0f;
13313                         }
13314
13315                         vm_vec_scale_add(&expected_pos, &weapon_objp->pos, &weapon_objp->orient.fvec, time_scale);
13316                 }
13317
13318                 //      See if too far away to care about shockwave.
13319                 if (vm_vec_dist_quick(&objp->pos, &expected_pos) > danger_dist*2.0f) {
13320                         //aip->ai_flags &= ~AIF_AVOID_SHOCKWAVE_WEAPON;
13321                         return 0;
13322                 } else {
13323                         // try to find a safe position
13324                         vector vec_from_exp;
13325                         float dir = 1.0f;
13326                         vm_vec_sub(&vec_from_exp, &objp->pos, &expected_pos);
13327                         float dot = vm_vec_dotprod(&vec_from_exp, &weapon_objp->orient.fvec);
13328                         if (dot > -30) {
13329                                 // if we're already on the other side of the explosion, don't try to fly behind it
13330                                 dir = -1.0f;
13331                         }
13332
13333                         //      Fly towards a point behind the weapon.
13334                         vm_vec_scale_add(safe_pos, &weapon_objp->pos, &weapon_objp->orient.fvec, -50000.0f*dir);
13335
13336                         // verify safe_pos will not make us collide with our target objnum, else try 2 other vecs
13337                         // don't bang your head, else go
13338 //                      int go_safe = FALSE;
13339                         int go_safe = TRUE;
13340 /*                      if (target_ship_obj) {
13341                                 if (pp_collide(&objp->pos, safe_pos, target_ship_obj, objp->radius)) {
13342                                         // try up to 2 other random directions
13343                                         vector dir_vec, rand_vec;
13344                                         int idx;
13345                                         for (idx=0; idx<2; idx++) {
13346                                                 vm_vec_rand_vec_quick(&rand_vec);
13347                                                 vm_vec_scale_add(&dir_vec, &weapon_objp->orient.fvec, &rand_vec, 0.5f);
13348                                                 vm_vec_scale_add(safe_pos, &weapon_objp->pos, &dir_vec, -50000.0f*dir);
13349                                                 if ( !pp_collide(&objp->pos, safe_pos, target_ship_obj, objp->radius) ) {
13350                                                         go_safe = TRUE;
13351                                                         break;
13352                                                 }
13353                                         }
13354                                 } else { // direct path is safe
13355                                         go_safe = TRUE;
13356                                 }
13357                         } else { // no target_obj_ship
13358                                 go_safe = TRUE;
13359                         } */
13360
13361                         if (go_safe) {
13362                                 return 1;
13363                         } else {
13364                                 // can't figure out a good way to go
13365                                 return 0;
13366                         }
13367                 }
13368         } else if (aip->ai_flags & AIF_AVOID_SHOCKWAVE_SHIP) {
13369                 if (aip->shockwave_object == -1) {
13370                         int shockwave_ship = ai_find_shockwave_ship(objp, aip);
13371                         if (shockwave_ship == -1) {
13372                                 aip->ai_flags &= ~AIF_AVOID_SHOCKWAVE_SHIP;
13373                                 return 0;
13374                         } else {
13375                                 aip->shockwave_object = shockwave_ship;
13376                         }
13377                 }
13378
13379                 Assert(aip->shockwave_object > -1);
13380                 object  *ship_objp = &Objects[aip->shockwave_object];
13381                 if (ship_objp == objp) {
13382                         aip->shockwave_object = -1;
13383                         return 0;
13384                 }
13385
13386                 if (ship_objp->type != OBJ_SHIP) {
13387                         aip->ai_flags &= ~AIF_AVOID_SHOCKWAVE_SHIP;
13388                         return 0;
13389                 }
13390
13391                 //      Optimize note! Don't really have to normalize.  We only need a point away from the blowing-up ship.
13392                 vector safe_vec;
13393
13394                 vm_vec_normalized_dir(&safe_vec, &objp->pos, &ship_objp->pos);
13395                 vm_vec_scale_add(safe_pos, &ship_objp->pos, &safe_vec, 50000.0f);       //      Fly away from the ship.
13396
13397                 float outer_rad = ship_get_exp_outer_rad(ship_objp);
13398
13399                 if (vm_vec_dist_quick(&objp->pos, &ship_objp->pos) > outer_rad*1.5f) {
13400                         aip->ai_flags &= ~AIF_AVOID_SHOCKWAVE_WEAPON;
13401                         return 0;
13402                 }
13403
13404                 return 1;
13405
13406         } else {
13407                 Int3(); //      Illegal -- supposedly avoiding a shockwave, but neither ship nor weapon.  What is it!?
13408         }
13409
13410         return 0;
13411 }
13412
13413 /*
13414 int rct_done = 0;
13415
13416 void rand_chance_test()
13417 {
13418         int     i;
13419         float   frametime;
13420
13421         if (rct_done)
13422                 return;
13423
13424         rct_done = 1;
13425
13426         for (frametime=0.02f; frametime<0.25f; frametime *= 1.25f) {
13427                 float   chance;
13428
13429                 nprintf(("AI", "%6.4f: ", frametime));
13430                 for (chance=0.25f; chance<2.5f; chance += 0.25f) {
13431                         int count = 0;
13432
13433                         for (i=0; i<100.0f/frametime; i++) {
13434                                 if (rand_chance(frametime, chance))
13435                                         count++;
13436                         }
13437                         nprintf(("AI", "%3i ", count));
13438                 }
13439                 nprintf(("AI", "\n"));
13440         }
13441 }
13442 */
13443
13444 //      --------------------------------------------------------------------------
13445 //      Make object *objp avoid the nearest dangerous shockwave-producing weapon.
13446 //      If it looks like there is no valid shockwave-producing weapon then clear the AIF_AVOID_SHOCKWAVE_WEAPON bit in ai_flags and return.
13447 //      Return 1 if avoiding a shockwave, else return 0.
13448 int ai_avoid_shockwave(object *objp, ai_info *aip)
13449 {
13450         vector  safe_pos;
13451
13452         //rand_chance_test();
13453         // BIG|HUGE do not respond to shockwaves
13454         if (Ship_info[Ships[objp->instance].ship_info_index].flags & (SIF_BIG_SHIP|SIF_HUGE_SHIP)) {
13455                 // don't come here again
13456                 aip->ai_flags &= ~AIF_AVOID_SHOCKWAVE;
13457                 return 0;
13458         }
13459
13460         //      Don't all react right away.
13461         if (!(aip->ai_flags & AIF_AVOID_SHOCKWAVE_STARTED))
13462                 if (!rand_chance(flFrametime, (float) aip->ai_class/4.0f + 0.25f))      //      Chance to avoid in 1 second is 0.25 + ai_class/4
13463                         return 0;
13464
13465         if (!aas_1(objp, aip, &safe_pos)) {
13466                 aip->ai_flags |= AIF_AVOID_SHOCKWAVE_STARTED;
13467                 return 0;
13468         }
13469
13470         aip->ai_flags |= AIF_AVOID_SHOCKWAVE_STARTED;
13471
13472         //      OK, evade the shockwave!
13473         turn_towards_point(objp, &safe_pos, NULL, 0.0f);
13474         vector  vec_to_safe_pos;
13475         float           dot_to_goal;
13476
13477         vm_vec_normalized_dir(&vec_to_safe_pos, &safe_pos, &objp->pos);
13478
13479         dot_to_goal = vm_vec_dot(&objp->orient.fvec, &vec_to_safe_pos);
13480         if (dot_to_goal < -0.5f)
13481                 accelerate_ship(aip, 0.3f);
13482         else {
13483                 accelerate_ship(aip, 1.0f + dot_to_goal);
13484                 if (dot_to_goal > 0.2f) {
13485                         if (!(objp->phys_info.flags & PF_AFTERBURNER_ON )) {
13486                                 afterburners_start(objp);
13487                                 aip->afterburner_stop_time = Missiontime + 2*F1_0;
13488                         }
13489                 }
13490         }
13491
13492         return 1;
13493 }
13494
13495 //      Awaiting repair.  Be useful.
13496 //      Probably fly towards incoming repair ship.
13497 //      Return true if this ship is close to being repaired, else return false.
13498 int ai_await_repair_frame(object *objp, ai_info *aip)
13499 {
13500         if (!(aip->ai_flags & (AIF_AWAITING_REPAIR | AIF_BEING_REPAIRED)))
13501                 return 0;
13502
13503         if (aip->dock_objnum == -1)
13504                 return 0;
13505
13506         ship    *shipp;
13507         ship_info       *sip;
13508
13509         shipp = &Ships[Objects[aip->dock_objnum].instance];
13510         sip = &Ship_info[shipp->ship_info_index];
13511
13512         aip->ai_flags &= ~AIF_FORMATION_OBJECT; //      Prevents endless rotation.
13513
13514         if (!(sip->flags & SIF_SUPPORT))
13515                 return 0;
13516
13517         vector  goal_point;
13518         object  *repair_objp;
13519
13520         repair_objp = &Objects[aip->dock_objnum];
13521
13522         if (Ships[repair_objp->instance].team == TEAM_TRAITOR) {
13523                 ai_abort_rearm_request(repair_objp);
13524                 return 0;
13525         }
13526
13527         vm_vec_scale_add(&goal_point, &repair_objp->pos, &repair_objp->orient.uvec, -50.0f);    //      Fly towards point below repair ship.
13528
13529         vector  vtr;
13530         float dist = vm_vec_normalized_dir(&vtr, &goal_point, &objp->pos);
13531         float dot = vm_vec_dot(&vtr, &objp->orient.fvec);
13532
13533         if (dist > 200.0f) {
13534                 //nprintf(("AI", "%s flying towards %s for repair, dist = %7.3f\n", Ships[objp->instance].ship_name, &Ships[repair_objp->instance].ship_name, dist));
13535                 accelerate_ship(aip, (0.9f + dot) * dist/1500.0f);
13536                 turn_towards_point(objp, &goal_point, NULL, 0.0f);
13537         } else {
13538                 accelerate_ship(aip, 0.0f);
13539                 //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));
13540         }
13541
13542         return 1;
13543 }
13544
13545 //      Maybe cause this ship to self-destruct.
13546 //      Currently, any small ship (SIF_SMALL_SHIP) that has been disabled will self-destruct after awhile.
13547 //      Maybe should only do this if they are preventing their wing from re-entering.
13548 void ai_maybe_self_destruct(object *objp, ai_info *aip)
13549 {
13550         //      Friendly ships can be repaired, so no self-destruct.
13551         //      In multiplayer, just don't self-destruct.  I figured there would be a problem. -- MK, 3/19/98.
13552         if ((Ships[objp->instance].team == TEAM_FRIENDLY) || (Game_mode & GM_MULTIPLAYER))
13553                 return;
13554
13555         //      Small ships in a wing blow themselves up after awhile if engine or weapons system has been destroyed.
13556         //      Reason: Don't want them to prevent a re-emergence of the wing.
13557         //      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
13558         //      mission would be broken.
13559         if ((Ship_info[Ships[objp->instance].ship_info_index].flags & SIF_SMALL_SHIP) && (Ships[objp->instance].wingnum != -1)) {
13560                 if ((ship_get_subsystem_strength(&Ships[objp->instance], SUBSYSTEM_ENGINE) <= 0.0f) ||
13561                         (ship_get_subsystem_strength(&Ships[objp->instance], SUBSYSTEM_WEAPONS) <= 0.0f)) {
13562                         if (aip->self_destruct_timestamp < 0)
13563                                 aip->self_destruct_timestamp = timestamp(90 * 1000);    //      seconds until self-destruct
13564                 } else {
13565                         aip->self_destruct_timestamp = -1;
13566                 }
13567
13568                 if (aip->self_destruct_timestamp < 0) {
13569                         return;
13570                 }
13571
13572                 if (timestamp_elapsed(aip->self_destruct_timestamp)) {
13573                         ship_apply_local_damage( objp, objp, &objp->pos, objp->hull_strength*flFrametime + 1.0f, MISS_SHIELDS);
13574                 }
13575         }
13576 }
13577
13578 // Determine if pl_objp needs a new target, called from ai_frame()
13579 int ai_need_new_target(object *pl_objp, int target_objnum)
13580 {
13581         object *objp;
13582
13583         if ( target_objnum < 0 ) {
13584                 return 1;
13585         }
13586
13587         objp = &Objects[target_objnum];
13588
13589         if ( (objp->type != OBJ_SHIP) && (objp->type != OBJ_ASTEROID) && (objp->type != OBJ_WEAPON) ) {
13590                 return 1;
13591         }
13592
13593         if ( objp->type == OBJ_SHIP ) {
13594                 if ( Ships[objp->instance].flags & SF_DYING ) {
13595                         return 1;
13596                 } else if (Ships[objp->instance].team == Ships[pl_objp->instance].team)
13597                         return 1;
13598         }
13599
13600         return 0;
13601 }
13602
13603 //      If *objp is recovering from a collision with a big ship, handle it.
13604 //      Return true if recovering.
13605 int maybe_big_ship_collide_recover_frame(object *objp, ai_info *aip)
13606 {
13607         float   dot, dist;
13608         vector  v2g;
13609         
13610         if (aip->ai_flags & AIF_BIG_SHIP_COLLIDE_RECOVER_1) {
13611                 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);
13612                 dist = vm_vec_normalized_dir(&v2g, &aip->big_recover_pos_1, &objp->pos);
13613                 dot = vm_vec_dot(&objp->orient.fvec, &v2g);
13614                 accelerate_ship(aip, dot);
13615
13616                 //      If close to desired point, or 15+ seconds since entered this mode, continue to next mode.
13617                 if ((timestamp_until(aip->big_recover_timestamp) < -15*1000) || (dist < (0.5f + flFrametime) * objp->phys_info.speed)) {
13618                         aip->ai_flags &= ~AIF_BIG_SHIP_COLLIDE_RECOVER_1;
13619                         aip->ai_flags |= AIF_BIG_SHIP_COLLIDE_RECOVER_2;
13620                 }
13621
13622                 return 1;
13623
13624         } else if (aip->ai_flags & AIF_BIG_SHIP_COLLIDE_RECOVER_2) {
13625                 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);
13626                 dist = vm_vec_normalized_dir(&v2g, &aip->big_recover_pos_2, &objp->pos);
13627                 dot = vm_vec_dot(&objp->orient.fvec, &v2g);
13628                 accelerate_ship(aip, dot);
13629
13630                 //      If close to desired point, or 30+ seconds since started avoiding collision, done avoiding.
13631                 if ((timestamp_until(aip->big_recover_timestamp) < -30*1000) || (dist < (0.5f + flFrametime) * objp->phys_info.speed)) {
13632                         aip->ai_flags &= ~AIF_BIG_SHIP_COLLIDE_RECOVER_2;
13633                         aip->ai_flags &= ~AIF_TARGET_COLLISION;
13634                 }
13635
13636                 return 1;
13637         }
13638
13639         if (aip->ai_flags & AIF_TARGET_COLLISION) {
13640                 aip->ai_flags &= ~AIF_TARGET_COLLISION;
13641         }
13642         return 0;
13643 }
13644
13645 void validate_mode_submode(ai_info *aip)
13646 {
13647         switch (aip->mode) {
13648         case AIM_CHASE:
13649                 // check valid submode
13650                 switch (aip->submode) {
13651                 case SM_CONTINUOUS_TURN:
13652                 case SM_ATTACK:
13653                 case SM_EVADE_SQUIGGLE:
13654                 case SM_EVADE_BRAKE:    
13655                 case SM_EVADE:          
13656                 case SM_SUPER_ATTACK:
13657                 case SM_AVOID:  
13658                 case SM_GET_BEHIND:
13659                 case SM_GET_AWAY:               
13660                 case SM_EVADE_WEAPON:
13661                 case SM_FLY_AWAY:       
13662                 case SM_ATTACK_FOREVER:
13663                         break;
13664                 default:
13665                         Int3();
13666                 }
13667                 break;
13668
13669         case AIM_STRAFE:
13670                 // check valid submode
13671                 switch(aip->submode) {
13672                 case AIS_STRAFE_ATTACK:
13673                 case AIS_STRAFE_AVOID:
13674                 case AIS_STRAFE_RETREAT1:
13675                 case AIS_STRAFE_RETREAT2:
13676                 case AIS_STRAFE_POSITION:
13677                         break;
13678                 default:
13679                         Int3();
13680                 }
13681                 break;
13682         }
13683 }
13684
13685 //      --------------------------------------------------------------------------
13686 // Process AI object "objnum".
13687 void ai_frame(int objnum)
13688 {
13689         ship            *shipp = &Ships[Objects[objnum].instance];
13690         ai_info *aip = &Ai_info[shipp->ai_index];
13691         int             target_objnum;
13692
13693 //      validate_mode_submode(aip);
13694
13695         Assert((aip->mode != AIM_WAYPOINTS) || (aip->active_goal != AI_ACTIVE_GOAL_DYNAMIC));
13696
13697         // Set globals defining the current object and its enemy object.
13698         Pl_objp = &Objects[objnum];
13699
13700         if (aip->mode == AIM_WARP_OUT) {
13701                 ai_warp_out(Pl_objp);
13702                 return;
13703         }
13704
13705 /*      //      HACK! TEST! REMOVE ME!
13706         if (Ship_info[shipp->ship_info_index].flags & SIF_BIG_SHIP)
13707                 if (shipp->team == Player_ship->team)
13708                         aip->mode = AIM_CHASE;
13709 */
13710
13711 //      if (!strnicmp(Ships[Pl_objp->instance].ship_name, "cancer", 6))
13712 //              nprintf(("AI", "Ship %s: mode = %s, submode = %i\n", Ships[Pl_objp->instance].ship_name, Mode_text[aip->mode], aip->submode));
13713
13714         ai_maybe_self_destruct(Pl_objp, aip);
13715
13716 //      if ( timestamp_elapsed(aip->goal_check_time) ) {
13717                 ai_process_mission_orders( objnum, aip );
13718 //              aip->goal_check_time = timestamp_rand(1000,2000);
13719 //      }
13720
13721         //      Avoid a shockwave, if necessary.  If a shockwave and rearming, stop rearming.
13722         if (aip->ai_flags & AIF_AVOID_SHOCKWAVE) {
13723                 if (ai_avoid_shockwave(Pl_objp, aip)) {
13724                         aip->ai_flags &= ~(AIF_BIG_SHIP_COLLIDE_RECOVER_1 | AIF_BIG_SHIP_COLLIDE_RECOVER_2);
13725                         if (aip->ai_flags & (AIF_AWAITING_REPAIR | AIF_BEING_REPAIRED))
13726                                 ai_abort_rearm_request(Pl_objp);
13727                         return;
13728                 }
13729         } else {
13730                 aip->ai_flags &= ~AIF_AVOID_SHOCKWAVE_STARTED;
13731         }
13732
13733         // moved call to ai_do_repair frame here from below because of the subsequent if statment returning
13734         // if the ship is getting repaired
13735         //      If waiting to be repaired, just stop and sit.
13736         ai_do_repair_frame(Pl_objp, aip, flFrametime);
13737         if ((aip->ai_flags & AIF_AWAITING_REPAIR) || (aip->ai_flags & AIF_BEING_REPAIRED)) {
13738                 if (ai_await_repair_frame(Pl_objp, aip))
13739                         return;
13740         }
13741
13742         if (aip->mode == AIM_PLAY_DEAD)
13743                 return;
13744
13745         //      If recovering from a collision with a big ship, don't continue.
13746         if (maybe_big_ship_collide_recover_frame(Pl_objp, aip))
13747                 return;
13748
13749         ai_preprocess_ignore_objnum(Pl_objp, aip);
13750         target_objnum = set_target_objnum(aip, aip->target_objnum);
13751
13752         // 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));
13753
13754         Assert(objnum != target_objnum);
13755
13756         ai_manage_shield(Pl_objp, aip);
13757         
13758         if ( maybe_request_support(Pl_objp) ) {
13759                 if ( Ships[Pl_objp->instance].flags & SF_FROM_PLAYER_WING ) {
13760                         ship_maybe_tell_about_rearm(shipp);
13761                 }
13762         }
13763
13764         ai_maybe_warp_out(Pl_objp);
13765
13766 /*
13767         //      If this ship is attacking an object's subsystems and someone else destroyed
13768         //      the subsystem, it could continue attacking the ship.  Need to invalidate the objnum.
13769         if (target_objnum >= 0)
13770                 if (Objects[target_objnum].flags & OF_PROTECTED) {
13771                         // if (aip->targeted_subsys != NULL)
13772                         //      ; //nprintf(("AI", "subsys hits = %7.3f\n", aip->targeted_subsys->current_hits));
13773
13774                         if ((aip->targeted_subsys == NULL) || (aip->targeted_subsys->current_hits <= 0.0f)) {
13775                                 target_objnum = -1;
13776                                 aip->target_objnum = -1;
13777                         }
13778                 }
13779 */
13780
13781
13782         //      Find an enemy if don't already have one.
13783         En_objp = NULL;
13784         if ( ai_need_new_target(Pl_objp, target_objnum) ) {
13785                 if ((aip->mode != AIM_EVADE_WEAPON) && (aip->active_goal == AI_ACTIVE_GOAL_DYNAMIC)) {
13786                         aip->resume_goal_time = -1;
13787                         aip->active_goal = AI_GOAL_NONE;
13788                 } else if (aip->resume_goal_time == -1) {
13789                         // AL 12-9-97: Don't allow cargo and navbuoys to set their aip->target_objnum
13790                         if ( !(Ship_info[shipp->ship_info_index].flags & SIF_HARMLESS) ) {
13791                                 target_objnum = find_enemy(objnum, MAX_ENEMY_DISTANCE, Skill_level_max_attackers[Game_skill_level]);            //      Attack up to 25K units away.
13792                                 if (target_objnum != -1) {
13793                                         if (aip->target_objnum != target_objnum)
13794                                                 aip->aspect_locked_time = 0.0f;
13795                                         set_target_objnum(aip, target_objnum);
13796                                         En_objp = &Objects[target_objnum];
13797                                 }
13798                         }
13799                 }
13800         } else if (target_objnum >= 0) {
13801                 En_objp = &Objects[target_objnum];
13802         }
13803
13804         // set base stealth info each frame
13805         aip->ai_flags &= ~AIF_STEALTH_PURSIUT;
13806         if (En_objp && En_objp->type == OBJ_SHIP) {
13807                 if (Ship_info[Ships[En_objp->instance].ship_info_index].flags & SIF_STEALTH) {
13808                         int stealth_state = ai_is_stealth_visible(Pl_objp, En_objp);
13809                         float dist = vm_vec_dist_quick(&En_objp->pos, &Pl_objp->pos);
13810
13811                         if (stealth_state != STEALTH_FULLY_TARGETABLE) {
13812                                 aip->ai_flags |= AIF_STEALTH_PURSIUT;
13813                         }
13814
13815                         if ( (stealth_state == STEALTH_FULLY_TARGETABLE) || (stealth_state == STEALTH_VISIBLE) ) {
13816                                 aip->stealth_last_visible_stamp = timestamp();
13817                                 aip->stealth_last_cheat_visible_stamp = aip->stealth_last_visible_stamp;
13818                                 aip->stealth_last_pos = En_objp->pos;
13819                                 aip->stealth_velocity = En_objp->phys_info.vel;
13820                         } else if (dist < 100) {
13821                                 // get cheat timestamp
13822                                 aip->stealth_last_cheat_visible_stamp = timestamp();
13823
13824                                 // set approximate pos and vel, with increasing error as time from last_visible_stamp increases
13825                                 update_ai_stealth_info_with_error(aip/*, 0*/);
13826                         }
13827                 }
13828         }
13829
13830         /*      if ((Pl_objp != NULL) && (En_objp != NULL)) {
13831                 slide_face_ship();
13832                 return;
13833         }
13834 */
13835         // AL 12-10-97: ensure that cargo and navbuoys aip->target_objnum is always -1.
13836         if ( Ship_info[shipp->ship_info_index].flags & SIF_HARMLESS ) {
13837                 aip->target_objnum = -1;
13838         }
13839
13840         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)) {
13841                 mprintf(("Warning: Object and its enemy have same position.  Object #%i\n", Pl_objp-Objects));
13842                 En_objp = NULL;
13843         }
13844
13845         if (aip->mode == AIM_CHASE) {
13846                 if (En_objp == NULL) {
13847                         aip->active_goal = -1;
13848                 }
13849         }
13850
13851         //      If there is a goal to resume and enough time has elapsed, resume the goal.
13852         if ((aip->resume_goal_time > 0) && (aip->resume_goal_time < Missiontime)) {
13853                 aip->active_goal = AI_GOAL_NONE;
13854                 aip->resume_goal_time = -1;
13855                 target_objnum = find_enemy(objnum, 2000.0f, Skill_level_max_attackers[Game_skill_level]);
13856                 if (target_objnum != -1) {
13857                         if (aip->target_objnum != target_objnum) {
13858                                 aip->aspect_locked_time = 0.0f;
13859                         }
13860                         set_target_objnum(aip, target_objnum);
13861                 }
13862         }
13863
13864         // check if targeted subsystem has been destroyed, if so, move onto another subsystem
13865         // if trying to disable or disarm the target
13866         if ((En_objp != NULL) && ( aip->targeted_subsys != NULL )) {
13867                 Assert(En_objp->type == OBJ_SHIP);
13868                 if ( aip->targeted_subsys->current_hits <= 0.0f ) {
13869                         int subsys_type;
13870
13871                         if ( aip->goals[0].ai_mode == AI_GOAL_DISABLE_SHIP ) {
13872                                 subsys_type = SUBSYSTEM_ENGINE;
13873                         } else if ( aip->goals[0].ai_mode == AI_GOAL_DISARM_SHIP ) {
13874                                 subsys_type = SUBSYSTEM_TURRET;
13875                         } else {
13876                                 subsys_type = -1;
13877                         }
13878
13879                         if ( subsys_type != -1 ) {
13880                                 ship_subsys *new_subsys;
13881                                 new_subsys = ship_return_next_subsys(&Ships[En_objp->instance], subsys_type, &Pl_objp->pos);
13882                                 if ( new_subsys != NULL ) {
13883                                         set_targeted_subsys(aip, new_subsys, aip->target_objnum);
13884                                 } else {
13885                                         // AL 12-16-97: no more subsystems to attack... reset targeting info
13886                                         aip->target_objnum = -1;
13887                                         set_targeted_subsys(aip, NULL, -1);
13888                                 }
13889                         } else {
13890                                 // targeted subsys is destroyed, so stop attacking it
13891                                 set_targeted_subsys(aip, NULL, -1);
13892                         }
13893                 }
13894         }
13895
13896         ai_maybe_launch_cmeasure(Pl_objp, aip);
13897         ai_maybe_evade_locked_missile(Pl_objp, aip);
13898
13899         aip->target_time += flFrametime;
13900
13901         int in_formation = 0;
13902         if (aip->ai_flags & AIF_FORMATION) {
13903                 in_formation = !ai_formation();
13904         }
13905
13906         if ( !in_formation ) {
13907                 ai_execute_behavior(aip);
13908         }
13909
13910         process_subobjects(objnum);
13911         maybe_resume_previous_mode(Pl_objp, aip);
13912         
13913         if (Pl_objp->phys_info.flags & PF_AFTERBURNER_ON ) {
13914                 if (Missiontime > aip->afterburner_stop_time) {
13915                         //nprintf(("AI", "Frame %i, turning off afterburner.\n", AI_FrameCount));
13916                         afterburners_stop(Pl_objp);
13917                 }
13918         }
13919 //      validate_mode_submode(aip);
13920 }
13921
13922 int Waypoints_created = 0;
13923
13924 //      Find the ship with the name *name in the Ship_info array.
13925 int find_ship_name(char *name)
13926 {
13927         int     i;
13928
13929         for (i=0; i<Num_ship_types; i++)
13930                 if (!strcmp(Ship_info[i].name, name))
13931                         return i;
13932
13933         return -1;
13934 }
13935
13936 void create_waypoints()
13937 {
13938         int     i, j, z;
13939
13940         // Waypoints_created = 1;
13941
13942         if (Waypoints_created)
13943                 return;
13944
13945         for (j=0; j<Num_waypoint_lists; j++)
13946                 for (i=0; i<Waypoint_lists[j].count; i++) {
13947                         z = obj_create(OBJ_WAYPOINT, 0, j * 65536 + i, NULL,
13948                                 &Waypoint_lists[j].waypoints[i], 0.0f, OF_RENDERS);
13949                 }
13950
13951         Waypoints_created = 1;
13952 }
13953
13954 int Last_ai_obj = -1;
13955
13956 void ai_process( object * obj, int ai_index, float frametime )
13957 {
13958 //      if (Ships[obj->instance].flags & SF_DYING)
13959 //              nprintf(("AI", "Frame: %i Ship %s is dying!\n", Framecount, Ships[obj->instance].ship_name));
13960
13961         if (obj->flags & OF_SHOULD_BE_DEAD)
13962                 return;
13963
13964         // 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.
13965         if ((Ships[obj->instance].flags & SF_DYING ) && !(Ship_info[Ships[obj->instance].ship_info_index].flags & (SIF_HUGE_SHIP | SIF_BIG_SHIP))){
13966                 return;
13967         }
13968
13969         int rfc = 1;            //      Assume will be Reading Flying Controls.
13970
13971         Assert( obj->type == OBJ_SHIP );
13972         Assert( ai_index >= 0 );
13973
13974         init_ship_info();
13975
13976         create_waypoints();
13977
13978         AI_frametime = frametime;
13979         if (obj-Objects <= Last_ai_obj) {
13980                 AI_FrameCount++;
13981         }
13982
13983         memset( &AI_ci, 0, sizeof(AI_ci) );
13984
13985         ai_frame(obj-Objects);
13986
13987         AI_ci.pitch = 0.0f;
13988         AI_ci.bank = 0.0f;
13989         AI_ci.heading = 0.0f;
13990
13991         // the ships maximum velocity now depends on the energy flowing to engines
13992         obj->phys_info.max_vel.z = Ships[obj->instance].current_max_speed;
13993         ai_info *aip = &Ai_info[Ships[obj->instance].ai_index];
13994
13995         //      In certain circumstances, the AI says don't fly in the normal way.
13996         //      One circumstance is in docking and undocking, when the ship is moving
13997         //      under thruster control.
13998         switch (aip->mode) {
13999         case AIM_DOCK:
14000                 if ((aip->submode >= AIS_DOCK_2) && (aip->submode != AIS_UNDOCK_3))
14001                         rfc = 0;
14002                 break;
14003         case AIM_WARP_OUT:
14004                 if (aip->submode >= AIS_WARP_3)
14005                         rfc = 0;
14006                 break;
14007 //      case AIM_NONE:
14008 //              if (aip->submode == AIS_NONE_FORMATION)
14009 //                      rfc = 0;
14010 //              break;
14011         default:
14012                 break;
14013         }
14014
14015         if (rfc == 1) {
14016                 vector copy_desired_rotvel = obj->phys_info.rotvel;
14017                 physics_read_flying_controls( &obj->orient, &obj->phys_info, &AI_ci, frametime);
14018                 // if obj is in formation and not flight leader, don't update rotvel
14019                 if (aip->ai_flags & AIF_FORMATION) {
14020                         if (&Objects[aip->goal_objnum] != obj) {
14021                                 obj->phys_info.desired_rotvel = copy_desired_rotvel;
14022                                 obj->phys_info.rotvel = copy_desired_rotvel;
14023                         }
14024                 }
14025         }
14026
14027         Last_ai_obj = obj-Objects;
14028 }
14029
14030 //      Initialize ai_info struct of object objnum.
14031 void init_ai_object(int objnum)
14032 {
14033         int     ship_index, ai_index;
14034         ai_info *aip;
14035         int ship_type;
14036         object  *objp;
14037         vector  near_vec;                       //      A vector nearby and mainly in front of this object.
14038
14039         objp = &Objects[objnum];
14040         ship_index = objp->instance;
14041         ai_index = Ships[ship_index].ai_index;
14042         Assert((ai_index >= 0) && (ai_index < MAX_AI_INFO));
14043
14044         aip = &Ai_info[ai_index];
14045
14046         ship_type = Ships[ship_index].ship_info_index;
14047
14048         vm_vec_scale_add(&near_vec, &objp->pos, &objp->orient.fvec, 100.0f);
14049         vm_vec_scale_add2(&near_vec, &objp->orient.rvec, 10.0f);
14050
14051         // Things that shouldn't have to get initialized, but initialize them just in case!
14052         aip->ai_flags = 0;
14053         aip->previous_mode = AIM_NONE;
14054         aip->mode_time = -1;
14055         aip->target_objnum = -1;
14056         aip->target_signature = -1;
14057         aip->previous_target_objnum = -1;
14058         aip->target_time = 0.0f;
14059         aip->enemy_wing = -1;
14060         aip->attacker_objnum = -1;
14061         aip->goal_objnum = -1;
14062         aip->goal_signature = -1;
14063         aip->guard_objnum = -1;
14064         aip->guard_signature = -1;
14065         aip->guard_wingnum = -1;
14066         aip->dock_signature = -1;
14067         aip->submode = 0;
14068         aip->previous_submode = 0;
14069         aip->best_dot_to_enemy = -1.0f;
14070         aip->best_dot_from_enemy = -1.0f;
14071         aip->best_dot_to_time = 0;
14072         aip->best_dot_from_time = 0;
14073         aip->submode_start_time = 0;
14074         aip->submode_parm0 = 0;
14075         aip->active_goal = -1;
14076         aip->goal_check_time = timestamp(0);
14077         aip->last_predicted_enemy_pos = near_vec;
14078         aip->prev_goal_point = near_vec;
14079         aip->goal_point = near_vec;
14080         aip->time_enemy_in_range = 0.0f;
14081         aip->last_attack_time = 0;
14082         aip->last_hit_time = 0;
14083         aip->last_hit_quadrant = 0;
14084         aip->hitter_objnum = -1;
14085         aip->hitter_signature = -1;
14086         aip->resume_goal_time = -1;
14087         aip->prev_accel = 0.0f;
14088         aip->prev_dot_to_goal = 0.0f;
14089
14090         aip->ignore_objnum = UNUSED_OBJNUM;
14091         aip->ignore_signature = -1;
14092
14093         // aip->mode = AIM_NONE;
14094
14095         // End of Things that shouldn't have to get initialized, but initialize them just in case!
14096
14097         aip->ai_courage = Ai_classes[Ship_info[ship_type].ai_class].ai_courage[Game_skill_level];
14098         aip->ai_patience = Ai_classes[Ship_info[ship_type].ai_class].ai_patience[Game_skill_level];
14099         aip->ai_evasion = Ai_classes[Ship_info[ship_type].ai_class].ai_evasion[Game_skill_level];
14100         aip->ai_accuracy = Ai_classes[Ship_info[ship_type].ai_class].ai_accuracy[Game_skill_level];
14101
14102         if (Num_waypoint_lists > 0) {
14103                 aip->wp_index = -1;
14104                 aip->wp_list = -1;
14105         } else {
14106                 aip->wp_index = -1;
14107                 aip->wp_list = -1;
14108         }
14109
14110         aip->attacker_objnum = -1;
14111         aip->goal_signature = -1;
14112
14113         Objects[objnum].phys_info.prev_fvec = Objects[objnum].orient.fvec;
14114
14115         aip->last_predicted_enemy_pos.x = 0.0f; //      Says this value needs to be recomputed!
14116         aip->time_enemy_in_range = 0.0f;
14117
14118         aip->resume_goal_time = -1;                                     //      Say there is no goal to resume.
14119
14120         aip->active_goal = -1;
14121         aip->path_start = -1;
14122         aip->path_goal_dist = -1;
14123         aip->path_length = 0;
14124         aip->path_subsystem_next_check = 1;
14125         aip->dock_path_index = -1;
14126         aip->dock_index = -1;
14127         aip->dock_objnum = -1;
14128
14129         aip->danger_weapon_objnum = -1;
14130         aip->danger_weapon_signature = -1;
14131
14132         aip->lead_scale = 0.0f;
14133         aip->last_hit_target_time = Missiontime;
14134
14135         aip->nearest_locked_object = -1;
14136         aip->nearest_locked_distance = 99999.0f;
14137
14138         aip->targeted_subsys = NULL;
14139         aip->last_subsys_target = NULL;
14140         aip->targeted_subsys_parent = -1;
14141
14142         // The next two fields are used to time the rearming to allow useful sound effects for missile rearming
14143         aip->rearm_first_missile = TRUE;                //      flag to indicate that next missile to load is the first missile
14144         aip->rearm_release_delay = 0;                   //      timestamp to delay the separation of docked ships after rearm
14145
14146         aip->next_predict_pos_time = 0;
14147
14148         aip->afterburner_stop_time = 0;
14149         aip->last_objsig_hit = -1;                              // object signature of the ship most recently hit by aip
14150
14151         aip->path_next_create_time = timestamp(1);
14152         aip->path_create_pos = Objects[objnum].pos;
14153         aip->path_create_orient = Objects[objnum].orient;
14154
14155         aip->ignore_expire_timestamp = timestamp(1);
14156         aip->warp_out_timestamp = 0;
14157         aip->next_rearm_request_timestamp = timestamp(1);
14158         aip->primary_select_timestamp = timestamp(1);
14159         aip->secondary_select_timestamp = timestamp(1);
14160         aip->scan_for_enemy_timestamp = timestamp(1);
14161
14162         aip->choose_enemy_timestamp = timestamp(3*(NUM_SKILL_LEVELS-Game_skill_level) * ((rand_alt() % 500) + 500));
14163
14164         aip->shockwave_object = -1;
14165         aip->shield_manage_timestamp = timestamp(1);
14166         aip->self_destruct_timestamp = -1;      //      This is a flag that we have not yet set this.
14167         aip->ok_to_target_timestamp = timestamp(1);
14168         aip->pick_big_attack_point_timestamp = timestamp(1);
14169         vm_vec_zero(&aip->big_attack_point);
14170
14171         aip->avoid_check_timestamp = timestamp(1);
14172
14173         aip->abort_rearm_timestamp = -1;
14174
14175         // artillery stuff
14176         aip->artillery_objnum = -1;
14177         aip->artillery_sig = -1;        
14178
14179         // waypoint speed cap
14180         aip->waypoint_speed_cap = -1;
14181
14182         // set lethality to enemy team
14183         aip->lethality = 0.0f;
14184 }
14185
14186 void init_ai_objects()
14187 {
14188         int     i;
14189
14190         for (i=0; i<num_objects; i++){
14191                 if (Objects[i].type == OBJ_SHIP){
14192                         init_ai_object(i);
14193                 }
14194         }
14195 }
14196
14197 void init_ai_system()
14198 {
14199         // MWA -- removed next line of code on 11/12/97.  When a ship is created
14200         // it calls init_ai_object() on it's objnum.  Doing this init at the point where
14201         // this function gets called messes things up.
14202         //init_ai_objects();
14203
14204         Ppfp = Path_points;
14205         Waypoints_created = 0;
14206
14207         Dock_path_warning_given = 0;
14208
14209 /*      for (int i=0; i<MAX_IGNORE_OBJECTS; i++) {
14210                 Ignore_objects[i].objnum = -1;
14211                 Ignore_objects[i].signature = -1;
14212         }
14213 */
14214
14215 }
14216
14217 void ai_set_default_behavior(object *obj, int classnum)
14218 {
14219         ai_info *aip;
14220
14221         Assert(obj != NULL);
14222         Assert(obj->instance != -1);
14223         Assert(Ships[obj->instance].ai_index != -1);
14224
14225         aip = &Ai_info[Ships[obj->instance].ai_index];
14226
14227         aip->behavior = classnum;
14228
14229 }
14230
14231 void ai_do_default_behavior(object *obj)
14232 {
14233         ai_info *aip;
14234         int             ship_flags;
14235
14236         Assert(obj != NULL);
14237         Assert(obj->instance != -1);
14238         Assert(Ships[obj->instance].ai_index != -1);
14239
14240         aip = &Ai_info[Ships[obj->instance].ai_index];
14241
14242         ship_flags = Ship_info[Ships[obj->instance].ship_info_index].flags;
14243         if (!is_instructor(obj) && (ship_flags & (SIF_FIGHTER | SIF_BOMBER))) {
14244                 int enemy_objnum = find_enemy(OBJ_INDEX(obj), 1000.0f, Skill_level_max_attackers[Game_skill_level]);
14245                 set_target_objnum(aip, enemy_objnum);
14246                 aip->mode = AIM_CHASE;
14247                 aip->submode = SM_ATTACK;
14248         } else if (ship_flags & (SIF_SUPPORT)) {
14249                 aip->mode = AIM_SAFETY;
14250                 aip->submode = AISS_1;
14251                 aip->ai_flags &= ~(AIF_REPAIRING);
14252         } else if ( ship_flags & SIF_SENTRYGUN ) {
14253                 aip->mode = AIM_SENTRYGUN;
14254         } else {
14255                 aip->mode = AIM_NONE;
14256         }
14257         
14258         aip->submode_start_time = Missiontime;
14259         aip->active_goal = AI_GOAL_NONE;
14260 }
14261
14262 #define FRIENDLY_DAMAGE_THRESHOLD       50.0f           //      Display a message at this threshold.  Note, this gets scaled by Skill_level
14263
14264 // send the given message from objp.  called from the maybe_process_friendly_hit
14265 // code below when a message must get send to the player when he fires on friendlies
14266 void process_friendly_hit_message( int message, object *objp )
14267 {
14268         int index;
14269
14270         // no traitor in multiplayer
14271         if(Game_mode & GM_MULTIPLAYER){
14272                 return;
14273         }
14274
14275         // don't send this message if a player ship was hit.
14276         if ( objp->flags & OF_PLAYER_SHIP ){
14277                 return;
14278         }
14279
14280         // check if objp is a cargo contianer -- if so, then find a new ship to send the message
14281         index = objp->instance;
14282         if ( !(Ship_info[Ships[objp->instance].ship_info_index].flags & (SIF_FIGHTER|SIF_BOMBER)) ){
14283                 index = -1;
14284         }
14285
14286         // if the message is "oops" (the don't hit me message), always make come from Terran command
14287         if ( message == MESSAGE_OOPS ){
14288                 index = -1;
14289         }
14290
14291         if ( index >= 0){
14292                 message_send_builtin_to_player( message, &Ships[index], MESSAGE_PRIORITY_HIGH, MESSAGE_TIME_ANYTIME, 0, 0, -1, -1 );
14293         } else {
14294                 message_send_builtin_to_player( message, NULL, MESSAGE_PRIORITY_HIGH, MESSAGE_TIME_ANYTIME, 0, 0, -1, -1 );
14295         }
14296 }
14297
14298 extern  void ship_set_subsystem_strength( ship *shipp, int type, float strength );
14299
14300 //      Object *objp_weapon, fired by *objp_hitter, hit object *objp_ship.
14301 void maybe_process_friendly_hit(object *objp_hitter, object *objp_hit, object *objp_weapon)
14302 {
14303         // no turning traitor in multiplayer
14304         if ( Game_mode & GM_MULTIPLAYER ) {
14305                 return;
14306         }
14307
14308         // ditto if mission says no traitors allowed
14309         if (The_mission.flags & MISSION_FLAG_NO_TRAITOR) {
14310                 return;
14311         }
14312
14313         if ((objp_hitter == Player_obj) && (Player_ship->team == TEAM_FRIENDLY)) {
14314
14315                 // AL 12-4-97: It is possible the Player is a OBJ_GHOST at this point.  If so, bail out.
14316                 if ( objp_hitter->type != OBJ_SHIP ) {
14317                         return;
14318                 }
14319
14320                 Assert(objp_hitter->type == OBJ_SHIP);
14321                 Assert(objp_hit->type == OBJ_SHIP);
14322                 Assert(objp_weapon->type == OBJ_WEAPON);
14323
14324                 ship    *shipp_hitter = &Ships[objp_hitter->instance];
14325                 ship    *shipp_hit = &Ships[objp_hit->instance];
14326
14327                 if (shipp_hitter->team != shipp_hit->team) {
14328                         return;
14329                 }
14330
14331                 // get the player
14332                 player *pp = &Players[Player_num];
14333
14334                 // wacky stuff here
14335                 if (pp->friendly_hits != 0) {
14336                         float   time_since_last_hit = f2fl(Missiontime - pp->friendly_last_hit_time);
14337                         if ((time_since_last_hit >= 0.0f) && (time_since_last_hit < 10000.0f)) {
14338                                 if (time_since_last_hit > 60.0f) {
14339                                         pp->friendly_hits = 0;
14340                                         pp->friendly_damage = 0.0f;
14341                                 } else if (time_since_last_hit > 2.0f) {
14342                                         pp->friendly_hits -= (int) time_since_last_hit/2;
14343                                         pp->friendly_damage -= time_since_last_hit;
14344                                 }
14345
14346                                 if (pp->friendly_damage < 0.0f) {
14347                                         pp->friendly_damage = 0.0f;
14348                                 }
14349
14350                                 if (pp->friendly_hits < 0) {
14351                                         pp->friendly_hits = 0;
14352                                 }
14353                         }
14354                 }
14355
14356                 float   damage;         //      Damage done by weapon.  Gets scaled down based on size of ship.
14357
14358                 damage = Weapon_info[Weapons[objp_weapon->instance].weapon_info_index].damage;
14359                 
14360                 // wacky stuff here
14361                 ship_info *sip = &Ship_info[Ships[objp_hit->instance].ship_info_index];
14362                 if (sip->initial_hull_strength > 1000.0f) {
14363                         float factor = sip->initial_hull_strength / 1000.0f;
14364                         factor = min(100.0f, factor);
14365                         damage /= factor;
14366                 }
14367
14368                 //      Don't penalize much at all for hitting cargo
14369                 if (sip->flags & (SIF_CARGO | SIF_SENTRYGUN)) {
14370                         damage /= 10.0f;
14371                 }
14372
14373                 //      Hit ship, but not targeting it, so it's not so heinous, maybe an accident.
14374                 if (Ai_info[shipp_hitter->ai_index].target_objnum != OBJ_INDEX(objp_hit)) {
14375                         damage /= 5.0f;
14376                 }
14377
14378                 pp->friendly_last_hit_time = Missiontime;
14379                 pp->friendly_hits++;
14380
14381                 // cap damage and number of hits done this frame
14382                 float accredited_damage = min(MAX_BURST_DAMAGE, pp->damage_this_burst + damage) - pp->damage_this_burst;
14383                 pp->friendly_damage += accredited_damage;
14384                 pp->damage_this_burst += accredited_damage;
14385
14386                 // Done with adjustments to damage.  Evaluate based on current friendly_damage
14387                 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 ));
14388                 
14389                 if (is_instructor(objp_hit)) {
14390                         // it's not nice to hit your instructor
14391                         if (pp->friendly_damage > FRIENDLY_DAMAGE_THRESHOLD) {
14392                                 message_send_builtin_to_player( MESSAGE_INSTRUCTOR_ATTACK, NULL, MESSAGE_PRIORITY_HIGH, MESSAGE_TIME_IMMEDIATE, 0, 0, -1, -1);
14393                                 pp->last_warning_message_time = Missiontime;
14394                                 ship_set_subsystem_strength( Player_ship, SUBSYSTEM_WEAPONS, 0.0f);
14395
14396                                 training_fail();
14397
14398                                 //      Instructor warp out.
14399                                 ai_set_mode_warp_out(objp_hit, &Ai_info[Ships[objp_hit->instance].ai_index]);
14400                                 gameseq_post_event( GS_EVENT_PLAYER_WARPOUT_START_FORCED );     //      Force player to warp out.
14401
14402                                 //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) );
14403                                 //ship_apply_global_damage( objp_hitter, objp_hit, NULL, 1.0f );
14404                         } else if (Missiontime - pp->last_warning_message_time > F1_0*4) {
14405                                 // warning every 4 sec
14406                                 // use NULL as the message sender here since it is the Terran Command persona
14407                                 message_send_builtin_to_player( MESSAGE_INSTRUCTOR_HIT, NULL, MESSAGE_PRIORITY_HIGH, MESSAGE_TIME_IMMEDIATE, 0, 0, -1, -1);
14408                                 pp->last_warning_message_time = Missiontime;
14409                         }
14410
14411                 // not nice to hit your friends
14412                 } else if (pp->friendly_damage > FRIENDLY_DAMAGE_THRESHOLD * (1.0f + (float) (NUM_SKILL_LEVELS + 1 - Game_skill_level)/3.0f)) {
14413                         process_friendly_hit_message( MESSAGE_HAMMER_SWINE, objp_hit );
14414                         mission_goal_fail_all();
14415                         ai_abort_rearm_request( Player_obj );
14416
14417                         Player_ship->team = TEAM_TRAITOR;
14418
14419                 } else if ((damage > frand()) && (Missiontime - pp->last_warning_message_time > F1_0*4) && (pp->friendly_damage > FRIENDLY_DAMAGE_THRESHOLD)) {
14420                         // no closer than 4 sec intervals
14421                         //      Note: (damage > frand()) added on 12/9/97 by MK.  Since damage is now scaled down for big ships, we could get too
14422                         //      many warnings.  Kind of tedious.  frand() returns a value in 0..1, so this won't affect legit hits.
14423                         process_friendly_hit_message( MESSAGE_OOPS, objp_hit );
14424                         pp->last_warning_message_time = Missiontime;
14425                 }
14426         }
14427 }
14428
14429 //      Maybe make ship with ai_info *aip attack hitter_objnum as a dynamic goal
14430 void maybe_set_dynamic_chase(ai_info *aip, int hitter_objnum)
14431 {
14432         Assert(Ship_info[Ships[aip->shipnum].ship_info_index].flags & (SIF_FIGHTER | SIF_BOMBER));
14433
14434         // limit the number of ships attacking hitter_objnum (for now, only if hitter_objnum is player)
14435         if ( ai_maybe_limit_attackers(hitter_objnum) == 1 ) {
14436                 return;
14437         }
14438
14439         // only set as target if can be targeted.
14440         if (awacs_get_level(&Objects[hitter_objnum], &Ships[aip->shipnum], 1) < 1) {
14441                 return;
14442         }
14443
14444         if (aip->target_objnum != hitter_objnum)
14445                 aip->aspect_locked_time = 0.0f;
14446         set_target_objnum(aip, hitter_objnum);
14447         aip->resume_goal_time = Missiontime + i2f(20);  //      Only chase up to 20 seconds.
14448         aip->active_goal = AI_ACTIVE_GOAL_DYNAMIC;
14449
14450         set_targeted_subsys(aip, NULL, -1);             //      Say not attacking any particular subsystem.
14451
14452         aip->previous_submode = aip->mode;
14453         aip->mode = AIM_CHASE;
14454         aip->submode = SM_ATTACK;
14455 }
14456
14457
14458 //      Return true if *objp has armed an aspect seeking bomb.
14459 //      This function written so a ship with an important bomb to fire will willingly take hits in the face to fire its bomb.
14460 int firing_aspect_seeking_bomb(object *objp)
14461 {
14462         ship    *shipp;
14463         int     bank_index;
14464         ship_weapon     *swp;
14465
14466         shipp = &Ships[objp->instance];
14467
14468         swp = &shipp->weapons;
14469
14470         bank_index = swp->current_secondary_bank;
14471
14472         if (bank_index != -1)
14473                 if (swp->secondary_bank_ammo[bank_index] > 0) {
14474                         if (Weapon_info[swp->secondary_bank_weapons[bank_index]].wi_flags & WIF_BOMB) {
14475                                 if (Weapon_info[swp->secondary_bank_weapons[bank_index]].wi_flags & WIF_HOMING_ASPECT) {
14476                                         return 1;
14477                                 }
14478                         }
14479                 }
14480
14481         return 0;
14482 }
14483
14484 //      *objp collided with big ship *big_objp at global point *collide_pos
14485 //      Make it fly away from the collision point.
14486 // collision_normal is NULL, when a collision is imminent and we just want to bug out.
14487 void big_ship_collide_recover_start(object *objp, object *big_objp, vector *collide_pos, vector *collision_normal)
14488 {
14489         ai_info *aip;
14490
14491         Assert(objp->type == OBJ_SHIP);
14492
14493         aip = &Ai_info[Ships[objp->instance].ai_index];
14494
14495         if (!timestamp_elapsed(aip->big_recover_timestamp) && (aip->ai_flags & AIF_BIG_SHIP_COLLIDE_RECOVER_1))
14496                 return;
14497
14498         //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)));
14499         if (collision_normal) {
14500                 aip->big_recover_timestamp = timestamp(2000);
14501                 aip->big_collision_normal = *collision_normal;
14502         //      nprintf(("AI", " normal\n"));
14503         } else {
14504                 aip->big_recover_timestamp = timestamp(500);
14505         //      nprintf(("AI", " no normal\n"));
14506         }
14507
14508
14509         aip->ai_flags &= ~AIF_BIG_SHIP_COLLIDE_RECOVER_2;
14510         aip->ai_flags |= AIF_BIG_SHIP_COLLIDE_RECOVER_1;
14511
14512 //      vector  out_vec;
14513 //      vm_vec_normalized_dir(&out_vec, &objp->pos, collide_pos);
14514
14515         // big_recover_pos_1 is 100 m out along normal
14516         vector direction;
14517         if (collision_normal) {
14518                 direction = *collision_normal;
14519         } else {
14520                 vm_vec_copy_scale(&direction, &objp->orient.fvec, -1.0f);
14521         }
14522         vm_vec_scale_add(&aip->big_recover_pos_1, &objp->pos, &direction, 100.0f);
14523
14524         // go out 200 m from box closest box point
14525         get_world_closest_box_point_with_delta(&aip->big_recover_pos_2, big_objp, &aip->big_recover_pos_1, NULL, 300.0f);
14526
14527         accelerate_ship(aip, 0.0f);
14528 /*
14529         if (vm_vec_dot(collision_normal, &objp->orient.fvec) > 0.5f) {
14530 //              vm_vec_scale_add(&aip->big_recover_pos_1, &objp->pos, &out_vec, big_objp->radius/2.0f);
14531 //              vm_vec_scale_add(&aip->big_recover_pos_2, &aip->big_recover_pos_1, &objp->orient.uvec, big_objp->radius/2.0f);
14532 //              vm_vec_scale_add(&aip->big_recover_pos_2, &objp->pos, &out_vec, big_objp->radius*2.0f);
14533                 accelerate_ship(aip, 2.0f);
14534         } else {
14535 //              vm_vec_scale_add(&aip->big_recover_pos_1, &objp->pos, &out_vec, big_objp->radius/2.0f);
14536 //              vm_vec_scale_add(&aip->big_recover_pos_2, &aip->big_recover_pos_1, &objp->orient.uvec, big_objp->radius/2.0f);
14537                 accelerate_ship(aip, 0.0f);
14538         } */
14539 }
14540
14541 float max_lethality = 0.0f;
14542
14543 void ai_update_lethality(object *ship_obj, object *other_obj, float damage)
14544 {
14545         Assert(ship_obj->type == OBJ_SHIP);
14546         Assert(other_obj->type == OBJ_WEAPON || other_obj->type == OBJ_SHOCKWAVE);
14547         int dont_count = FALSE;
14548
14549         int parent = other_obj->parent;
14550         if (Objects[parent].type == OBJ_SHIP) {
14551                 if (Objects[parent].signature == other_obj->parent_sig) {
14552
14553                         // check damage done to enemy team
14554                         if (Ships[ship_obj->instance].team != Ships[Objects[parent].instance].team) {
14555
14556                                 // other is weapon
14557                                 if (other_obj->type == OBJ_WEAPON) {
14558                                         weapon *wp = &Weapons[other_obj->instance];
14559                                         weapon_info *wif = &Weapon_info[wp->weapon_info_index];
14560
14561                                         // if parent is BIG|HUGE, don't count beam
14562                                         if (Ship_info[Ships[Objects[parent].instance].ship_info_index].flags & (SIF_BIG_SHIP|SIF_HUGE_SHIP)) {
14563                                                 if (wif->wi_flags & WIF_BEAM) {
14564                                                         dont_count = TRUE;
14565                                                 }
14566                                         }
14567                                 }
14568
14569                                 if (!dont_count) {
14570                                         float lethality = 0.025f * damage;      // 2 cyclops (@2000) put you at 100 lethality
14571
14572                                         // increase lethality weapon's parent ship
14573                                         ai_info *aip = &Ai_info[Ships[Objects[parent].instance].ai_index];
14574                                         aip->lethality += lethality;
14575                                         aip->lethality = min(110.0f, aip->lethality);
14576                                         // if you hit, don;t be less than 0
14577                                         aip->lethality = max(0.0f, aip->lethality);
14578
14579 //                                      if (aip->lethality > max_lethality) {
14580 //                                              max_lethality = aip->lethality;
14581 //                                              mprintf(("new lethalilty high: %.1f\n", max_lethality));
14582 //                                      }
14583
14584                                         // if parent is player, show his lethality
14585 //                                      if (Objects[parent].flags & OF_PLAYER_SHIP) {
14586 //                                              mprintf(("Player lethality: %.1f\n", aip->lethality));
14587 //                                      }
14588                                 }
14589                         }
14590                 }
14591         }
14592 }
14593
14594
14595 //      Object *objp_ship was hit by either weapon *objp_weapon or collided into by ship hit_objp at point *hitpos.
14596 void ai_ship_hit(object *objp_ship, object *hit_objp, vector *hitpos, int shield_quadrant, vector *hit_normal)
14597 {
14598         int             hitter_objnum = -2;
14599         object  *objp_hitter = NULL;
14600         ship            *shipp;
14601         ai_info *aip, *hitter_aip;
14602
14603         shipp = &Ships[objp_ship->instance];
14604         aip = &Ai_info[shipp->ai_index];
14605
14606         if (objp_ship->flags & OF_PLAYER_SHIP)
14607                 return;
14608
14609         if ((aip->mode == AIM_WARP_OUT) || (aip->mode == AIM_PLAY_DEAD))
14610                 return;
14611
14612         if (hit_objp->type == OBJ_SHIP) {
14613                 //      If the object that this ship collided with is a big ship
14614                 if (Ship_info[Ships[hit_objp->instance].ship_info_index].flags & (SIF_BIG_SHIP | SIF_HUGE_SHIP)) {
14615                         //      And the current object is _not_ a big ship
14616                         if (!(Ship_info[Ships[objp_ship->instance].ship_info_index].flags & (SIF_BIG_SHIP | SIF_HUGE_SHIP))) {
14617                                 //      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.
14618                                 big_ship_collide_recover_start(objp_ship, hit_objp, hitpos, hit_normal);
14619                         }
14620                 }
14621         }
14622
14623         if (hit_objp->type == OBJ_WEAPON) {
14624                 //      Make sure the object that fired this weapon is still alive.  If not, abort.
14625                 // Assert(hit_objp->parent >= 0);
14626                 if(hit_objp->parent < 0){
14627                         return;
14628                 }
14629                 if ( hit_objp->parent_sig != Objects[hit_objp->parent].signature ){
14630                         return;
14631                 }
14632
14633                 //      Hit by a protected ship, don't attack it.
14634                 if (Objects[hit_objp->parent].flags & OF_PROTECTED) {
14635                         if ((Ship_info[shipp->ship_info_index].flags & (SIF_FIGHTER | SIF_BOMBER)) && (aip->target_objnum == -1)) {
14636                                 if (aip->mode == AIM_CHASE) {
14637                                         if (aip->submode != SM_EVADE_WEAPON) {
14638                                                 aip->mode = AIM_CHASE;
14639                                                 aip->submode = SM_EVADE_WEAPON;
14640                                                 aip->submode_start_time = Missiontime;
14641                                         }
14642                                 } else if (aip->mode != AIM_EVADE_WEAPON) {
14643                                         aip->active_goal = AI_ACTIVE_GOAL_DYNAMIC;
14644                                         aip->previous_mode = aip->mode;
14645                                         aip->previous_submode = aip->submode;
14646                                         aip->mode = AIM_EVADE_WEAPON;
14647                                         aip->submode = -1;
14648                                         aip->submode_start_time = Missiontime;
14649                                         aip->mode_time = timestamp(MAX_EVADE_TIME);     //      Evade for up to five seconds.
14650                                 }
14651
14652                         }
14653                         return;
14654                 }
14655
14656                 hitter_objnum = hit_objp->parent;
14657                 Assert((hitter_objnum >= 0) && (hitter_objnum < MAX_OBJECTS));
14658                 objp_hitter = &Objects[hitter_objnum];
14659                 maybe_process_friendly_hit(objp_hitter, objp_ship, hit_objp);           //      Deal with player's friendly fire.
14660
14661                 if ( (shipp->team & TEAM_FRIENDLY) && !(Game_mode & GM_MULTIPLAYER) ) {
14662                         ship_maybe_ask_for_help(shipp);
14663                 }
14664         } else if (hit_objp->type == OBJ_SHIP) {
14665                 if (shipp->team == Ships[hit_objp->instance].team)              //      Don't have AI react to collisions between teammates.
14666                         return;
14667                 objp_hitter = hit_objp;
14668                 hitter_objnum = hit_objp-Objects;
14669         } else {
14670                 Int3(); //      Hmm, what kind of object hit this if not weapon or ship?  Get MikeK.
14671                 return;
14672         }
14673
14674         //      Collided into a protected ship, don't attack it.
14675         if (hit_objp->flags & OF_PROTECTED)
14676                 return;
14677
14678         Assert(objp_hitter != NULL);
14679         hitter_aip = &Ai_info[Ships[objp_hitter->instance].ai_index];
14680         hitter_aip->last_hit_target_time = Missiontime;
14681         
14682         // store the object signature of objp_ship into ai_info, since we want to track the last ship hit by 'hitter_objnum'
14683         hitter_aip->last_objsig_hit = objp_ship->signature; 
14684
14685         aip->last_hit_time = Missiontime;
14686
14687         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
14688                 return;
14689
14690         //      If this ship is awaiting repair, abort!
14691         if (aip->ai_flags & (AIF_AWAITING_REPAIR | AIF_BEING_REPAIRED)) {
14692                 ship_info       *sip = &Ship_info[shipp->ship_info_index];
14693
14694                 if (objp_ship->hull_strength/sip->initial_hull_strength < 0.3f) {
14695                         //      No, only abort if hull below a certain level.
14696                         aip->next_rearm_request_timestamp = timestamp(NEXT_REARM_TIMESTAMP/2);  //      Might request again after 15 seconds.
14697                         if ( !(objp_ship->flags & OF_PLAYER_SHIP) )                                             // mwa -- don't abort rearm for a player
14698                                 ai_abort_rearm_request(objp_ship);
14699                 }
14700         }
14701
14702         //      If firing a bomb, ignore enemy fire so we can gain lock drop the bomb.
14703         //      Only ignore fire if aspect_locked_time > 0.5f, as this means we're in range.
14704         if (firing_aspect_seeking_bomb(objp_ship)) {
14705                 if ((aip->ai_flags & AIF_SEEK_LOCK) && (aip->aspect_locked_time > 0.1f))
14706                         return;
14707         }
14708
14709         //      If in AIM_STRAFE mode and got hit by target, maybe attack turret if appropriate
14710         if (aip->mode == AIM_STRAFE) {
14711                 Assert(hitter_objnum != -2);
14712                 if (aip->target_objnum == hitter_objnum) {
14713                         if ( hit_objp->type == OBJ_WEAPON ) {
14714                                 ai_big_strafe_maybe_attack_turret(objp_ship, hit_objp);
14715                         }
14716                         return;
14717                 }
14718                 else {
14719                                 // AL 11-10-97:
14720                         ;       // do nothing here, we'll attack this hitter if it is a fighter or bomber (this is handled
14721                                 // in code later in this function
14722                 }
14723         }
14724
14725         if (objp_ship == Player_obj)
14726                 return;         //      We don't do AI for the player.
14727
14728         maybe_update_guard_object(objp_ship, objp_hitter);
14729
14730         //      Big ships don't go any further.
14731         if (!(Ship_info[shipp->ship_info_index].flags & SIF_SMALL_SHIP))
14732                 return;
14733
14734         //      If the hitter object is the ignore object, don't attack it.
14735         ship_info       *sip = &Ship_info[shipp->ship_info_index];
14736         if ((is_ignore_object(aip, objp_hitter-Objects)) && (sip->flags & (SIF_BOMBER | SIF_FIGHTER))) {
14737                 if (aip->mode == AIM_NONE) {
14738                         aip->mode = AIM_CHASE;  //      This will cause the ship to move, if not attack.
14739                         aip->submode = SM_EVADE;
14740                 }
14741                 return;
14742         }
14743
14744         //      Maybe abort based on mode.
14745         switch (aip->mode) {
14746         case AIM_CHASE:
14747                 if (aip->submode == SM_ATTACK_FOREVER)
14748                         return;
14749
14750                 if ( hit_objp->type == OBJ_WEAPON ) {
14751                         if ( ai_big_maybe_enter_strafe_mode(objp_ship, OBJ_INDEX(hit_objp), 1) )
14752                                 return;
14753                 }
14754
14755         case AIM_GUARD:
14756                 //      If in guard mode and far away from guard object, don't pursue guy that hit me.
14757                         if ((aip->guard_objnum != -1) && (aip->guard_signature == Objects[aip->guard_objnum].signature)) {
14758                                 if (vm_vec_dist_quick(&objp_ship->pos, &Objects[aip->guard_objnum].pos) > 500.0f) {
14759                                         return;
14760                                 }
14761                         }
14762         case AIM_STILL:
14763         case AIM_STAY_NEAR:
14764                 // Note: Dealt with above, at very top.  case AIM_PLAY_DEAD:
14765         case AIM_STRAFE:
14766                 break;
14767         case AIM_EVADE_WEAPON:
14768         case AIM_EVADE:
14769         case AIM_GET_BEHIND:
14770         case AIM_AVOID:
14771         case AIM_DOCK:
14772         case AIM_BIGSHIP:
14773         case AIM_PATH:
14774         case AIM_NONE:
14775         case AIM_BAY_DEPART:
14776         case AIM_SENTRYGUN:
14777                 return;
14778         case AIM_BAY_EMERGE:
14779                 // If just leaving the docking bay, don't react to enemy fire... just keep flying away from docking bay
14780                 if ( (Missiontime - aip->submode_start_time) < 5*F1_0 ) {
14781                         return;
14782                 }
14783                 break;
14784         case AIM_WAYPOINTS:
14785                 if (sip->flags & (SIF_FIGHTER | SIF_BOMBER))
14786                         break;
14787                 else
14788                         return;
14789                 break;
14790         case AIM_SAFETY:
14791                 if ((aip->submode != AISS_1) || (Missiontime - aip->submode_start_time > i2f(1))) {
14792                         aip->submode = AISS_1;
14793                         aip->submode_start_time = Missiontime;
14794                 }
14795                 return;
14796                 break;
14797         case AIM_WARP_OUT:
14798                 return;
14799                 break;
14800         default:
14801                 Int3(); //      Bogus mode!
14802         }
14803
14804         if (timestamp_elapsed(aip->ok_to_target_timestamp))
14805                 aip->ai_flags &= ~AIF_FORMATION;                        //      If flying in formation, bug out!
14806
14807         aip->hitter_objnum = hitter_objnum;
14808         aip->hitter_signature = Objects[hitter_objnum].signature;
14809
14810         //      If the hitter is not on the same team as the hittee, do some stuff.
14811         if (shipp->team != Ships[objp_hitter->instance].team) {
14812                 //nprintf(("AI", "Object %i attacking %i, who just hit him!\n", objp_ship-Objects, hitter_objnum));
14813
14814                 if ((hitter_objnum != aip->target_objnum) && (sip->flags & (SIF_FIGHTER | SIF_BOMBER))) {
14815                         maybe_set_dynamic_chase(aip, hitter_objnum);
14816                         maybe_afterburner_after_ship_hit(objp_ship, aip, &Objects[hitter_objnum]);
14817                 } else {
14818                         if ((aip->mode == AIM_CHASE) && ((objp_ship->hull_strength/sip->initial_hull_strength > 0.9f) || (get_shield_strength(objp_ship)/sip->shields > 0.8f))) {
14819                                 switch (aip->submode) {
14820                                 case SM_ATTACK:
14821                                 case SM_SUPER_ATTACK:
14822                                 case SM_GET_AWAY:
14823                                         break;
14824                                 default:
14825                                         if (sip->flags & (SIF_FIGHTER | SIF_BOMBER)) {
14826                                                 maybe_set_dynamic_chase(aip, hitter_objnum);
14827                                         }
14828                                         maybe_afterburner_after_ship_hit(objp_ship, aip, &Objects[hitter_objnum]);
14829                                         break;
14830                                 }
14831                         } else if (aip->mode == AIM_CHASE) {
14832                                 switch (aip->submode) {
14833                                 case SM_ATTACK:
14834                                         aip->submode = SM_EVADE;
14835                                         aip->submode_start_time = Missiontime;
14836                                         break;
14837                                 case SM_SUPER_ATTACK:
14838                                         if (Missiontime - aip->submode_start_time > i2f(1)) {
14839                                                 aip->submode = SM_EVADE;
14840                                                 aip->submode_start_time = Missiontime;
14841                                         }
14842                                         break;
14843                                 case SM_EVADE_BRAKE:
14844                                         break;
14845                                 case SM_EVADE_SQUIGGLE:
14846                                         aip->submode = SM_EVADE;
14847                                         aip->submode_start_time = Missiontime;
14848                                         break;
14849                                 default:
14850                                         if (sip->flags & (SIF_BOMBER | SIF_FIGHTER)) {
14851                                                 maybe_set_dynamic_chase(aip, hitter_objnum);
14852                                                 maybe_afterburner_after_ship_hit(objp_ship, aip, &Objects[hitter_objnum]);
14853                                         }
14854
14855                                         break;
14856                                 }
14857                         } else {
14858                                 // AL 3-15-98: Prevent escape pods from entering chase mode
14859                                 if ( (sip->flags & (SIF_BOMBER | SIF_FIGHTER)) ) {
14860                                         maybe_set_dynamic_chase(aip, hitter_objnum);
14861                                 }
14862                                 maybe_afterburner_after_ship_hit(objp_ship, aip, &Objects[hitter_objnum]);
14863                         }
14864                 }
14865         }
14866 }
14867
14868 //      Ship shipnum has been destroyed.
14869 //      Cleanup.
14870 // the parameter 'method' is used to tell is this ship was destroyed or it departed normally.
14871 // This function will get called in either case, and there are things that should be done if
14872 // the ship actually gets destroyed which shouldn't get done if it departed.
14873 void ai_ship_destroy(int shipnum, int method)
14874 {
14875         int             objnum;
14876         object  *other_objp;
14877         ship            *shipp;
14878         ship_obj        *so;
14879         ai_info *dead_aip;
14880
14881         Assert((shipnum >= 0) && (shipnum < MAX_SHIPS));
14882         objnum = Ships[shipnum].objnum;
14883         dead_aip = &Ai_info[Ships[shipnum].ai_index];
14884
14885         // if I was getting repaired, or awaiting repair, then cleanup the repair mode.  When awaiting repair, the dock_objnum
14886         // is -1.  When the support ship is on the way, the dock_objnum >= 0 (points to support ship).
14887         if ( dead_aip->ai_flags & (AIF_AWAITING_REPAIR | AIF_BEING_REPAIRED) ) {
14888                 if ( dead_aip->dock_objnum >= 0 )
14889                         ai_do_objects_repairing_stuff( &Objects[objnum], &Objects[dead_aip->dock_objnum], REPAIR_INFO_END);
14890                 else
14891                         ai_do_objects_repairing_stuff( &Objects[objnum], NULL, REPAIR_INFO_END );
14892         }
14893
14894         //      For all objects that had this ship as a target, wipe it out, forcing find of a new enemy.
14895         for ( so = GET_FIRST(&Ship_obj_list); so != END_OF_LIST(&Ship_obj_list); so = GET_NEXT(so) ) {
14896                 other_objp = &Objects[so->objnum];
14897                 Assert(other_objp->instance != -1);
14898
14899                 shipp = &Ships[other_objp->instance];
14900                 Assert(shipp->ai_index != -1);
14901
14902                 ai_info *aip = &Ai_info[shipp->ai_index];
14903
14904                 // MWA 2/11/98
14905                 // code commented out below is taken care of in ai_cleanup_dock_mode when gets called when the
14906                 // support ship starts it's death roll.
14907
14908                 //      If the destroyed ship was on its way to repair the current ship
14909                 if (aip->dock_objnum == objnum) {
14910
14911                         // clean up the flags for any kind of docking mode.  If aip was part of a goal of dock/undock
14912                         // then it will get cleaned up by the goal code.
14913                         ai_do_objects_undocked_stuff( other_objp, NULL );
14914
14915                         if ( aip->ai_flags & (AIF_AWAITING_REPAIR | AIF_BEING_REPAIRED) ) {
14916                                 int abort_reason;
14917                                 if ( method == SEF_DEPARTED ) {
14918                                         abort_reason = REPAIR_INFO_ABORT;
14919                                 } else {
14920                                         abort_reason = REPAIR_INFO_KILLED;
14921                                 }
14922                                 ai_do_objects_repairing_stuff( other_objp, NULL, abort_reason );
14923                         }
14924                 }
14925
14926                 if (aip->target_objnum == objnum) {
14927                         set_target_objnum(aip, -1);
14928                         //      If this ship had a dynamic goal of chasing the dead ship, clear the dynamic goal.
14929                         if (aip->resume_goal_time != -1)
14930                                 aip->active_goal = AI_GOAL_NONE;
14931                 }
14932
14933                 if (aip->goal_objnum == objnum) {
14934                         aip->goal_objnum = -1;
14935                         aip->goal_signature = -1;
14936                 }
14937
14938                 if (aip->guard_objnum == objnum) {
14939                         aip->guard_objnum = -1;
14940                         aip->guard_signature = -1;
14941                 }
14942
14943                 if ((aip->guard_wingnum != -1) && (aip->guard_wingnum == Ai_info[Ships[Objects[objnum].instance].ai_index].wing)) {
14944                         if (aip->guard_wingnum != aip->wing)
14945                                 ai_set_guard_wing(other_objp, aip->guard_wingnum);
14946                 }
14947
14948                 if (aip->hitter_objnum == objnum)
14949                         aip->hitter_objnum = -1;
14950
14951         }
14952
14953 }
14954
14955 /*
14956 //      Interface function to goals code.
14957 //      Make object *objp fly to point *vp and warp out.
14958 void ai_warp_out(object *objp, vector *vp)
14959 {
14960         ai_info *aip;
14961
14962         aip = &Ai_info[Ships[objp->instance].ai_index];
14963
14964         if (aip->mode != AIM_WARP_OUT) {
14965                 ai_set_mode_warp_out(objp, aip);
14966         }
14967         float   dist;
14968         float   dot;
14969         vector  v2v;
14970         ai_info *aip;
14971
14972         dist = vm_vec_normalized_dir(&v2v, vp, &objp->pos);
14973
14974         if (dist < objp->radius + 5.0f) {
14975
14976                 // Start the warp out effect 
14977                 shipfx_warpout_start(objp);
14978
14979         } else {
14980                 dot = vm_vec_dot(&objp->orient.fvec, &v2v);
14981
14982                 aip = &Ai_info[Ships[objp->instance].ai_index];
14983
14984                 if (dist > 500.0f)
14985                         accelerate_ship(aip, 1.0f);
14986                 else
14987                         accelerate_ship(aip, (3*dot + 1.0f)/4.0f);
14988
14989                 turn_towards_point(objp, vp, NULL, 0.0f);
14990         }
14991 }
14992 */
14993
14994
14995 //      Do stuff at start of deathroll.
14996 void ai_deathroll_start(object *ship_obj)
14997 {
14998         ai_info *aip;
14999         ship            *shipp, *other_ship;
15000
15001         shipp = &Ships[ship_obj->instance];
15002         aip = &Ai_info[shipp->ai_index];
15003
15004         // mark object we are docked with so we can do damage and separate during deathroll
15005         // keep dock_objnum_when_dead from being changed if already set (only allow to be set when -1)
15006         if (Ships[ship_obj->instance].dock_objnum_when_dead == -1) {
15007                 Ships[ship_obj->instance].dock_objnum_when_dead = aip->dock_objnum;
15008                 // set other_ship dock_objnum_when_dead, if other_ship exits.
15009                 if (Ships[ship_obj->instance].dock_objnum_when_dead != -1) {
15010                         other_ship = &Ships[Objects[aip->dock_objnum].instance];
15011                         other_ship->dock_objnum_when_dead = shipp->objnum;
15012                 }
15013         }
15014
15015         ai_cleanup_dock_mode(aip, shipp);
15016
15017         aip->mode = AIM_NONE;
15018 }
15019
15020 //      Object *requester_objp tells rearm ship to abort rearm.
15021 //      Returns true if it succeeded, else false.
15022 //      To succeed means you were previously rearming.
15023 int ai_abort_rearm_request(object *requester_objp)
15024 {
15025         ship            *requester_shipp;
15026         ai_info *requester_aip;
15027
15028         Assert(requester_objp->type == OBJ_SHIP);
15029         if(requester_objp->type != OBJ_SHIP){
15030                 return 0;
15031         }
15032         Assert((requester_objp->instance >= 0) && (requester_objp->instance < MAX_SHIPS));      
15033         if((requester_objp->instance < 0) || (requester_objp->instance >= MAX_SHIPS)){
15034                 return 0;
15035         }
15036         requester_shipp = &Ships[requester_objp->instance];
15037         Assert((requester_shipp->ai_index >= 0) && (requester_shipp->ai_index < MAX_AI_INFO));          
15038         if((requester_shipp->ai_index < 0) || (requester_shipp->ai_index >= MAX_AI_INFO)){
15039                 return 0;
15040         }       
15041         requester_aip = &Ai_info[requester_shipp->ai_index];
15042         
15043         if (requester_aip->ai_flags & (AIF_AWAITING_REPAIR | AIF_BEING_REPAIRED)){
15044
15045                 // dock_objnum is always valid once a rearm repair has been requested.  It points to the
15046                 // ship that is coming to repair me.
15047                 if (requester_aip->dock_objnum != -1) {
15048                         object  *repair_objp;
15049                         ai_info *repair_aip;
15050
15051                         repair_objp = &Objects[requester_aip->dock_objnum];
15052                         repair_aip = &Ai_info[Ships[repair_objp->instance].ai_index];
15053
15054                         //      Make sure signatures match.  This prevents nasty bugs in which an object
15055                         //      that was repairing another is destroyed and is replaced by another ship
15056                         //      before this code comes around.
15057                         if (repair_objp->signature == requester_aip->dock_signature) {
15058
15059                                 Assert( repair_objp->type == OBJ_SHIP );
15060
15061                                 // if support ship is in the process of undocking, don't do anything.
15062                                 if ( repair_aip->submode < AIS_UNDOCK_0 ) {
15063                                         ai_do_objects_repairing_stuff( requester_objp, repair_objp, REPAIR_INFO_ABORT );
15064
15065                                         if ( repair_aip->submode == AIS_DOCK_4 )
15066                                                 repair_aip->submode = AIS_UNDOCK_0;
15067                                         else
15068                                                 repair_aip->submode = AIS_UNDOCK_3;
15069
15070                                         repair_aip->submode_start_time = Missiontime;
15071                                 } else {
15072                                         nprintf(("AI", "Not aborting rearm since already undocking\n"));
15073                                 }
15074                         }
15075                 } else {
15076                         // setting these flags is the safe things to do.  There may not be a corresponding repair
15077                         // ship for this guys since a repair ship may be currently repairing someone else.
15078                         ai_do_objects_repairing_stuff( requester_objp, NULL, REPAIR_INFO_ABORT );
15079
15080                         // try and remove this guy from an arriving support ship.
15081                         mission_remove_scheduled_repair(requester_objp);
15082                 }
15083
15084                 return 1;
15085         } else if ( requester_aip->ai_flags & AIF_REPAIRING ) {
15086                 // a support ship can request to abort when he is told to do something else (like warp out).
15087                 // see if this support ships goal_objnum is valid.  If so, then issue this ai_abort comment
15088                 // for the ship that he is enroute to repair
15089                 if ( requester_aip->goal_objnum != -1 ) {
15090                         int val;
15091
15092                         val = ai_abort_rearm_request( &Objects[requester_aip->goal_objnum] );
15093                         return val;
15094                 }
15095         }
15096
15097         return 0;
15098 }
15099
15100 // function which gets called from ai-issue_rearm_request and from code in missionparse.cpp
15101 // to actually issue the rearm goal (support_obj to rearm requester_obj);
15102 void ai_add_rearm_goal( object *requester_objp, object *support_objp )
15103 {
15104         ship *support_shipp, *requester_shipp;
15105         ai_info *support_aip, *requester_aip;
15106
15107         support_shipp = &Ships[support_objp->instance];
15108         requester_shipp = &Ships[requester_objp->instance];
15109         requester_aip = &Ai_info[requester_shipp->ai_index];
15110
15111         Assert( support_shipp->ai_index != -1 );
15112         support_aip = &Ai_info[support_shipp->ai_index];
15113
15114         // if the requester is a player object, issue the order as the squadmate messaging code does.  Doing so
15115         // ensures that the player get a higher priority!
15116         requester_aip->ai_flags |= AIF_AWAITING_REPAIR; //      Tell that I'm awaiting repair.
15117         if ( requester_objp->flags & OF_PLAYER_SHIP )
15118                 ai_add_ship_goal_player( AIG_TYPE_PLAYER_SHIP, AI_GOAL_REARM_REPAIR, -1, requester_shipp->ship_name, support_aip );
15119         else
15120                 ai_add_goal_ship_internal( support_aip, AI_GOAL_REARM_REPAIR, requester_shipp->ship_name, -1, -1 );
15121
15122 }
15123
15124 //      Object *requester_objp requests rearming.
15125 //      Returns objnum of ship coming to repair requester on success
15126 //      Success means you found someone to rearm you and you weren't previously rearming.
15127 int ai_issue_rearm_request(object *requester_objp)
15128 {
15129         object  *objp;
15130         ship            *requester_shipp;
15131         ai_info *requester_aip;
15132
15133         Assert(requester_objp->type == OBJ_SHIP);
15134         Assert((requester_objp->instance >= 0) && (requester_objp->instance < MAX_SHIPS));
15135         requester_shipp = &Ships[requester_objp->instance];
15136         Assert((requester_shipp->ai_index >= 0) && (requester_shipp->ai_index < MAX_AI_INFO));
15137         requester_aip = &Ai_info[requester_shipp->ai_index];
15138         
15139         //      Make sure not already awaiting repair.
15140         if (requester_aip->ai_flags & AIF_AWAITING_REPAIR) {
15141                 nprintf(("AI", "Ship %s already awaiting rearm by ship %s.\n", requester_shipp->ship_name, &Ships[Objects[requester_aip->dock_objnum].instance].ship_name));    
15142                 return -1;
15143         }
15144
15145         if ( !is_support_allowed(requester_objp) )
15146                 return -1;
15147
15148         //nprintf(("AI", "Ship %s requesting rearming.\n", requester_shipp->ship_name));
15149         requester_aip->next_rearm_request_timestamp = timestamp(NEXT_REARM_TIMESTAMP);  //      Might request again after this much time.
15150
15151         // call ship_find_repair_ship to get a support ship.  If none is found, then we will warp one in.  This
15152         // function will return the next available ship which can repair requester
15153         objp = ship_find_repair_ship( requester_objp );
15154         ai_do_objects_repairing_stuff( requester_objp, objp, REPAIR_INFO_QUEUE );
15155         if ( objp ) {
15156
15157                 // MWA 5/14/98 -- moved next item into the ai_do_objects_repairing_stuff function so that clients
15158                 // would properly update their hud support view
15159                 //ai_add_rearm_goal( requester_objp, objp );
15160                 return OBJ_INDEX(objp);
15161
15162         } else {
15163                 // call to warp in repair ship!!!!  for now, warp in any number of ships needed.  Should cap it to
15164                 // some reasonable max (or let support ships warp out).  We should assume here that ship_find_repair_ship()
15165                 // would have returned a valid object if there are too many support ships already in the mission
15166                 mission_warp_in_support_ship( requester_objp );
15167
15168                 return -1;
15169         }
15170
15171 }
15172
15173 // make objp rearm and repair goal_objp
15174 void ai_rearm_repair( object *objp, object  *goal_objp, int priority, int docker_index, int dockee_index )
15175 {
15176         ai_info *aip, *goal_aip;
15177
15178         aip = &Ai_info[Ships[objp->instance].ai_index];
15179         aip->goal_objnum = goal_objp-Objects;
15180
15181         // nprintf(("AI", "Ship %s preparing to rearm ship %s.\n", shipp->ship_name, requester_shipp->ship_name));
15182
15183         ai_dock_with_object(objp, goal_objp, priority, AIDO_DOCK, docker_index, dockee_index);
15184         aip->ai_flags |= AIF_REPAIRING;                                         //      Tell that repair guy is busy trying to repair someone.
15185
15186         goal_aip = &Ai_info[Ships[goal_objp->instance].ai_index];
15187         goal_aip->dock_objnum = objp-Objects;           //      Tell which object is coming to repair.
15188         goal_aip->dock_signature = objp->signature;
15189
15190         ai_do_objects_repairing_stuff( goal_objp, objp, REPAIR_INFO_ONWAY );
15191
15192         goal_aip->abort_rearm_timestamp = timestamp(NEXT_REARM_TIMESTAMP*3/2);
15193 }
15194
15195 // Given a dockee object and the index of the dockbay for that object (ie the dockbay index
15196 // into polymodel->dockbays[] for the model associated with the object), return the index
15197 // of a path_num associated with than dockbay (this is an index into polymodel->paths[])
15198 int ai_return_path_num_from_dockbay(object *dockee_objp, int dockbay_index)
15199 {
15200         if ( dockbay_index < 0 || dockee_objp == NULL ) {
15201                 Int3();         // should never happen
15202                 return -1;
15203         }
15204
15205         if ( dockee_objp->type == OBJ_SHIP ) {
15206                 int                     path_num;
15207                 polymodel       *pm;
15208
15209                 pm = model_get( Ships[dockee_objp->instance].modelnum );
15210
15211                 // sanity checks
15212                 Assert(pm->n_docks > dockbay_index);
15213                 Assert(pm->docking_bays[dockbay_index].num_spline_paths > 0);
15214                 Assert(pm->docking_bays[dockbay_index].splines != NULL);
15215                 if(pm->n_docks <= dockbay_index){
15216                         return -1;
15217                 }
15218                 if(pm->docking_bays[dockbay_index].num_spline_paths <= 0){
15219                         return -1;
15220                 }
15221                 if(pm->docking_bays[dockbay_index].splines == NULL){
15222                         return -1;
15223                 }
15224
15225                 // We only need to return one path for the dockbay, so return the first
15226                 path_num = pm->docking_bays[dockbay_index].splines[0];
15227                 return path_num;
15228         } else {
15229                 return -1;
15230         }
15231 }
15232
15233 //      Actually go ahead and fire the synaptics.
15234 void cheat_fire_synaptic(object *objp, ship *shipp, ai_info *aip)
15235 {
15236         ship_weapon     *swp;
15237         swp = &shipp->weapons;
15238         int     current_bank = swp->current_secondary_bank;
15239
15240         ai_select_secondary_weapon(objp, swp, WIF_SPAWN, 0);
15241         if (timestamp_elapsed(swp->next_secondary_fire_stamp[current_bank])) {
15242                 if (ship_fire_secondary(objp)) {
15243                         nprintf(("AI", "ship %s cheat fired synaptic!\n", shipp->ship_name));
15244                         swp->next_secondary_fire_stamp[current_bank] = timestamp(2500);
15245                 }
15246         }
15247 }
15248
15249 //      For the subspace mission (sm3-09a)
15250 //              for delta wing
15251 //                      if they're sufficiently far into the mission
15252 //                              if they're near one or more enemies
15253 //                                      every so often
15254 //                                              fire a synaptic if they have one.
15255 void maybe_cheat_fire_synaptic(object *objp, ai_info *aip)
15256 {
15257         //      Only do in subspace missions.
15258         if ( The_mission.flags & MISSION_FLAG_SUBSPACE )        {
15259                 ship    *shipp;
15260                 int     num, time;
15261
15262                 shipp = &Ships[objp->instance];
15263
15264                 if (!(strnicmp(shipp->ship_name, NOX("delta"), 5))) {
15265                         num = shipp->ship_name[6] - '1';
15266
15267                         if ((num >= 0) && (num <= 3)) {
15268                                 time = Missiontime >> 16;       //      Convert to seconds.
15269
15270                                 time -= 2*60;   //      Subtract off two minutes.
15271
15272                                 if (time > 0) {
15273                                         int modulus = 17 + num*3;
15274
15275                                         if ((time % modulus) < 2) {
15276                                                 int count = num_nearby_fighters(get_enemy_team_mask(OBJ_INDEX(objp)), &objp->pos, 1500.0f);
15277
15278                                                 if (count > 0) {
15279                                                         cheat_fire_synaptic(objp, shipp, aip);
15280                                                 }
15281                                         }
15282                                 }
15283                         }
15284                 }
15285         }
15286
15287 }
15288