]> icculus.org git repositories - taylor/freespace2.git/blob - src/ship/aicode.cpp
Initial revision
[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.1  2002/05/03 03:28:10  root
11  * Initial revision
12  *
13  * 
14  * 107   9/15/99 4:42a Mikek
15  * Make any big ship attacking Colossus, or Colossus attacking any large
16  * ship not use big cruiser movement code.
17  * 
18  * 106   9/15/99 3:28a Jimb
19  * Make all big ships in sm3-08 not do cruiser chase code when attacking
20  * Colossus.  Added so Beast doesn't swerve away from Colossus.
21  * 
22  * 105   9/14/99 4:18p Andsager
23  * hack for mission sm3-08 to abort cruiser_chase as sathanas is about to
24  * begin circling colossus.
25  * 
26  * 104   9/08/99 10:44p Andsager
27  * Make HUGE ships not die when warping out, after warp effect started.
28  * 
29  * 103   9/03/99 11:40p Mikek
30  * Comment out an annoying nprintf().
31  * 
32  * 102   9/01/99 11:26p Dave
33  * Fixed release build warnings.
34  * 
35  * 101   9/01/99 9:12p Mikek
36  * Make it a boatload harder to become a traitor from hitting a large
37  * ship.
38  * 
39  * 100   9/01/99 4:01p Andsager
40  * Make sure BIG|HUGE ships do not respond to shockwaves
41  * 
42  * 99    9/01/99 10:09a Dave
43  * Pirate bob.
44  * 
45  * 98    8/31/99 4:24p Andsager
46  * Reduce collisions when attacking big ships.
47  * 
48  * 97    8/31/99 7:33a Mikek
49  * Improvements in formation flying, less silly behavior, especially when
50  * leader is moving very slowly.
51  * 
52  * 96    8/31/99 5:48a Mikek
53  * Making ships not overshoot so much in formation flying.  Intermediate
54  * checkin.
55  * 
56  * 95    8/30/99 12:03a Mikek
57  * Make guard behavior much less annoying.  Guarders don't get quite so
58  * close and they try to avoid striking the target they are guarding.
59  * 
60  * 94    8/29/99 4:18p Andsager
61  * New "burst" limit for friendly damage.  Also credit more damage done
62  * against large friendly ships.
63  * 
64  * 93    8/28/99 7:29p Dave
65  * Fixed wingmen persona messaging. Make sure locked turrets don't count
66  * towards the # attacking a player.
67  * 
68  * 92    8/26/99 10:46p Andsager
69  * Apply shockwave damage to lethality.
70  * 
71  * 91    8/26/99 8:52p Dave
72  * Gave multiplayer TvT messaging a heavy dose of sanity. Cheat codes.
73  * 
74  * 90    8/26/99 5:14p Andsager
75  * 
76  * 89    8/24/99 8:55p Dave
77  * Make sure nondimming pixels work properly in tech menu.
78  * 
79  * 88    8/23/99 6:21p Jefff
80  * added "no traitor" option to missions (and fred)
81  * 
82  * 87    8/20/99 3:36p Andsager
83  * Make sure we don;t miss stealth sweep points.
84  * 
85  * 86    8/16/99 8:21a Andsager
86  * fix link error
87  * 
88  * 85    8/16/99 8:19a Andsager
89  * Add project_point_onto_bbox() to fvi and include in aicode
90  * 
91  * 84    8/15/99 1:30p Dave
92  * Removed some bounding box code because of link errors. Assuming needed
93  * function just needs to get checked in by DaveA.
94  * 
95  * 83    8/15/99 11:59a Andsager
96  * For targing big/huge ships, find nearest distance to bbox, not center.
97  * 
98  * 82    8/13/99 2:20p Andsager
99  * Add speed modification to chances turret will find stealth ship
100  * 
101  * 81    8/13/99 10:49a Andsager
102  * Knossos and HUGE ship warp out.  HUGE ship warp in.  Stealth search
103  * modes dont collide big ships.
104  * 
105  * 80    8/10/99 5:02p Andsager
106  * Fix bug where AI gets stuck in SM_EVADE_WEAPON with no target.
107  * 
108  * 79    8/10/99 11:58a Andsager
109  * Allow turrets to sometimes see stealth.
110  * 
111  * 78    7/31/99 2:57p Dave
112  * Scaled flak aim and jitter by weapon subsystem strength.
113  * 
114  * 77    7/27/99 10:33p Andsager
115  * improve ai for attacking stealth.  reduced jitter in aim.  reduced
116  * error in position when avoiding.  skill level support for attacking
117  * stealth.  Made target error same for team vs. team.
118  * 
119  * 76    7/27/99 10:49a Andsager
120  * Make turret fire rate independent of team for HUGE turrets, and also
121  * for mult team vs. team.
122  * 
123  * 75    7/26/99 12:14p Andsager
124  * Apply cap to how much slower a transport flies with cargo.  Remove
125  * limit on waypoint speed for training.  Enemy ai get stealth exact pos
126  * when stealth fires
127  * 
128  * 74    7/20/99 1:49p Dave
129  * Peter Drake build. Fixed some release build warnings.
130  * 
131  * 73    7/19/99 2:13p Dave
132  * Added some new strings for Heiko.
133  * 
134  * 72    7/19/99 12:02p Andsager
135  * Allow AWACS on any ship subsystem. Fix sexp_set_subsystem_strength to
136  * only blow up subsystem if its strength is > 0
137  * 
138  * 71    7/15/99 9:20a Andsager
139  * FS2_DEMO initial checkin
140  * 
141  * 70    7/14/99 1:44p Andsager
142  * modify ai_guard for BIG ships to circle around the long axis
143  * 
144  * 69    7/09/99 5:54p Dave
145  * Seperated cruiser types into individual types. Added tons of new
146  * briefing icons. Campaign screen.
147  * 
148  * 68    7/08/99 4:32p Andsager
149  * fix bug with turret-tagged-only
150  * 
151  * 67    7/08/99 12:06p Andsager
152  * Add turret-tagged-only and turret-tagged-clear sexp.
153  * 
154  * 66    7/02/99 3:49p Andsager
155  * Remove debug code.  Allow targeting of stealth from any weapon it
156  * fires.
157  * 
158  * 65    7/02/99 2:01p Andsager
159  * Fix bug where big ship tries to evade dumpfire weapon.
160  * 
161  * 64    7/02/99 10:58a Andsager
162  * Put in big ship - big ship attack mode.  Modify stealth sweep ai.
163  * 
164  * 63    6/30/99 5:53p Dave
165  * Put in new anti-camper code.
166  * 
167  * 62    6/28/99 3:22p Anoop
168  * Fix turret optimization, where ship may not have any valid subsystems
169  * (all blown off).
170  * 
171  * 61    6/25/99 5:56p Andsager
172  * First real pass on stealth ai.
173  * 
174  * 60    6/25/99 3:08p Dave
175  * Multiple flyby sounds.
176  * 
177  * 59    6/25/99 1:12p Danw
178  * DKA:  Make sure big ship has subsystems before trying to target them.
179  * 
180  * 58    6/25/99 10:56a Johnson
181  * Fixed dumb ai code.
182  * 
183  * 57    6/24/99 5:15p Dave
184  * Make sure stride is always at least one for checking turret subsystem
185  * targets.
186  * 
187  * 56    6/24/99 4:59p Dave
188  * Significant speedups to turret firing.
189  * 
190  * 55    6/23/99 5:51p Andsager
191  * Add waypoint-cap-speed.  Checkin stealth ai - inactive.
192  * 
193  * 54    6/16/99 10:21a Dave
194  * Added send-message-list sexpression.
195  * 
196  * 53    6/15/99 9:25a Andsager
197  * Make guard and dynamic chase (who hit you) work with stealth
198  * 
199  * 52    6/14/99 3:21p Andsager
200  * Allow collisions between ship and its debris.  Fix up collision pairs
201  * when large ship is warping out.
202  * 
203  * 51    6/14/99 10:45a Dave
204  * Made beam weapons specify accuracy by skill level in the weapons.tbl
205  * 
206  * 50    6/03/99 8:11a Andsager
207  * 
208  * 49    6/02/99 5:41p Andsager
209  * Reduce range of secondary weapons not fired from turrets in nebula.
210  * Reduce range of beams fired from turrrets in nebula
211  * 
212  * 48    6/02/99 3:23p Andsager
213  * Make AI aware of team visibility.  Allow player targeting with team
214  * visibility info.  Make stealth ships not targetable by AI in nebula
215  * unless tagged.
216  * 
217  * 47    6/02/99 12:52p Andsager
218  * Added team-wide ship visibility.  Implemented for player.
219  * 
220  * 46    6/01/99 8:35p Dave
221  * Finished lockarm weapons. Added proper supercap weapons/damage. Added
222  * awacs-set-radius sexpression.
223  * 
224  * 45    5/28/99 5:35p Andsager
225  * Make ai nebula aware
226  * 
227  * 44    5/24/99 9:55a Dave
228  * Fixed stream weapon ai firing problem. ick.
229  * 
230  * 43    5/20/99 7:00p Dave
231  * Added alternate type names for ships. Changed swarm missile table
232  * entries.
233  * 
234  * 42    5/18/99 1:30p Dave
235  * Added muzzle flash table stuff.
236  * 
237  * 41    5/12/99 2:55p Andsager
238  * Implemented level 2 tag as priority in turret object selection
239  * 
240  * 40    5/12/99 10:42a Andsager
241  * Fix turret bug allowing HUGE turrets to fire at fighters
242  * 
243  * 39    5/06/99 11:46a Andsager
244  * Bug fixes.  Don't get into illegal strafe submode.  Don't choose turret
245  * enemy objnum for beam protected.
246  * 
247  * 38    5/03/99 10:50p Andsager
248  * Make Asteroid_obj_list.  Change get_nearest_turret_objnum() to use
249  * Asteroid_obj_list, Ship_obj_list and Missile_obj_list vs.
250  * obj_used_list.
251  * 
252  * 37    4/29/99 2:29p Dave
253  * Made flak work much better in multiplayer.
254  * 
255  * 36    4/28/99 11:36p Dave
256  * Tweaked up subspace missile strike a bit,
257  * 
258  * 35    4/28/99 3:11p Andsager
259  * Stagger turret weapon fire times.  Make turrets smarter when target is
260  * protected or beam protected.  Add weaopn range to weapon info struct.
261  * 
262  * 34    4/26/99 10:58a Andsager
263  * Add OF_BEAM_PROTECTED flag to keep object from being targeted for zing.
264  * 
265  * 33    4/23/99 12:12p Andsager
266  * Modify wing positions when player is wing leader to prevent some
267  * collisions.
268  * 
269  * 32    4/23/99 12:01p Johnson
270  * Added SIF_HUGE_SHIP
271  * 
272  * 31    4/22/99 11:06p Dave
273  * Final pass at beam weapons. Solidified a lot of stuff. All that remains
274  * now is to tweak and fix bugs as they come up. No new beam weapon
275  * features.
276  * 
277  * 30    4/20/99 6:39p Dave
278  * Almost done with artillery targeting. Added support for downloading
279  * images on the PXO screen.
280  * 
281  * 29    4/20/99 3:40p Andsager
282  * Changes to big ship ai.  Uses bounding box as limit where to fly to
283  * when flying away.
284  * 
285  * 28    4/16/99 5:54p Dave
286  * Support for on/off style "stream" weapons. Real early support for
287  * target-painting lasers.
288  * 
289  * 27    4/02/99 9:55a Dave
290  * Added a few more options in the weapons.tbl for beam weapons. Attempt
291  * at putting "pain" packets into multiplayer.
292  * 
293  * 26    3/28/99 5:58p Dave
294  * Added early demo code. Make objects move. Nice and framerate
295  * independant, but not much else. Don't use yet unless you're me :)
296  * 
297  * 25    3/19/99 9:51a Dave
298  * Checkin to repair massive source safe crash. Also added support for
299  * pof-style nebulae, and some new weapons code.
300  * 
301  * 24    3/08/99 7:03p Dave
302  * First run of new object update system. Looks very promising.
303  * 
304  * 23    3/05/99 3:55p Anoop
305  * Handle some asserts properly.
306  * 
307  * 22    3/04/99 6:09p Dave
308  * Added in sexpressions for firing beams and checking for if a ship is
309  * tagged.
310  * 
311  * 21    3/02/99 9:25p Dave
312  * Added a bunch of model rendering debug code. Started work on fixing
313  * beam weapon wacky firing.
314  * 
315  * 20    2/25/99 2:32p Anoop
316  * (Alan). Fixed ai path following code for AI_BAY_EMERGE. Put in sanity
317  * check so that when the last point on the path is reached, it finishes.
318  * 
319  * 19    2/19/99 2:11p Anoop
320  * Put in some nice handling code for wacky support ship problems (like no
321  * docking paths)
322  * 
323  * 18    2/17/99 2:11p Dave
324  * First full run of squad war. All freespace and tracker side stuff
325  * works.
326  * 
327  * 17    2/11/99 5:22p Andsager
328  * Fixed bugs, generalized block Sexp_variables
329  * 
330  * 16    1/29/99 5:07p Dave
331  * Fixed multiplayer stuff. Put in multiplayer support for rapid fire
332  * missiles.
333  * 
334  * 15    1/29/99 2:25p Andsager
335  * Added turret_swarm_missiles
336  * 
337  * 14    1/27/99 9:56a Dave
338  * Temporary checkin of beam weapons for Dan to make cool sounds.
339  * 
340  * 13    1/24/99 11:37p Dave
341  * First full rev of beam weapons. Very customizable. Removed some bogus
342  * Int3()'s in low level net code.
343  * 
344  * 12    1/21/99 10:44a Dave
345  * More beam weapon stuff. Put in warmdown time.
346  * 
347  * 11    1/12/99 5:45p Dave
348  * Moved weapon pipeline in multiplayer to almost exclusively client side.
349  * Very good results. Bandwidth goes down, playability goes up for crappy
350  * connections. Fixed object update problem for ship subsystems.
351  * 
352  * 10    1/08/99 2:08p Dave
353  * Fixed software rendering for pofview. Super early support for AWACS and
354  * beam weapons.
355  * 
356  * 9     12/23/98 2:53p Andsager
357  * Added ship activation and gas collection subsystems, removed bridge
358  * 
359  * 8     11/12/98 12:13a Dave
360  * Tidied code up for multiplayer test. Put in network support for flak
361  * guns.
362  * 
363  * 7     11/05/98 5:55p Dave
364  * Big pass at reducing #includes
365  * 
366  * 6     10/26/98 9:42a Dave
367  * Early flak gun support.
368  * 
369  * 5     10/23/98 3:51p Dave
370  * Full support for tstrings.tbl and foreign languages. All that remains
371  * is to make it active in Fred.
372  * 
373  * 4     10/20/98 1:39p Andsager
374  * Make so sparks follow animated ship submodels.  Modify
375  * ship_weapon_do_hit_stuff() and ship_apply_local_damage() to add
376  * submodel_num.  Add submodel_num to multiplayer hit packet.
377  * 
378  * 3     10/13/98 9:29a Dave
379  * Started neatening up freespace.h. Many variables renamed and
380  * reorganized. Added AlphaColors.[h,cpp]
381  * 
382  * 2     10/07/98 10:53a Dave
383  * Initial checkin.
384  * 
385  * 1     10/07/98 10:51a Dave
386  * 
387  * 
388  * $NoKeywords: $
389  */
390
391 // This module contains the actual AI code that does interesting stuff
392 // to objects.   The code in Ai.cpp is just for bookeeping, allocating
393 // ai slots and linking them to ships.
394
395 #include "pstypes.h"
396 #include "fix.h"
397 #include "linklist.h"
398 #include "object.h"
399 #include "physics.h"
400 #include "vecmat.h"
401 #include "ship.h"
402 #include "model.h"
403 #include "2d.h"
404 #include "3d.h"
405 #include "ai.h"
406 #include "floating.h"
407 #include "player.h"
408 #include "freespace.h"
409 #include "weapon.h"
410 #include "missiongoals.h"
411 #include "missionlog.h"
412 #include "timer.h"
413 #include "sound.h"
414 #include "aigoals.h"
415 #include "gamesnd.h"
416 #include "hudmessage.h"
417 #include "missionmessage.h"
418 #include "cmeasure.h"
419 #include "staticrand.h"
420 #include "multimsgs.h"
421 #include "afterburner.h"
422 #include "hudets.h"
423 #include "shipfx.h"
424 #include "shiphit.h"
425 #include "aibig.h"
426 #include "multiutil.h"
427 #include "hud.h"
428 #include "objcollide.h"
429 #include "asteroid.h"
430 #include "hudlock.h"
431 #include "missiontraining.h"
432 #include "gamesequence.h"
433 #include "joy_ff.h"
434 #include "localize.h"
435 #include "flak.h"
436 #include "beam.h"
437 #include "multi.h"
438 #include "swarm.h"
439 #include "multi_team.h"
440 #include "awacs.h"
441 #include "fvi.h"
442
443 #pragma optimize("", off)
444 #pragma auto_inline(off)
445
446 #define UNINITIALIZED_VALUE     -99999.9f
447
448 #define INSTRUCTOR_SHIP_NAME NOX("instructor")
449
450 #define AICODE_SMALL_MAGNITUDE  0.001f          // cosider a vector NULL if mag is less than this
451
452 #define NEXT_REARM_TIMESTAMP (60*1000)                  //      Ships will re-request rearm, typically, after this long.
453
454 #define BEAM_NEBULA_RANGE_REDUCE_FACTOR         0.8
455
456 // AIM_CHASE submode defines
457 // SM_STEALTH_FIND
458 #define SM_SF_AHEAD             0
459 #define SM_SF_BEHIND    1
460 #define SM_SF_BAIL              2
461
462 // SM_STEALTH_SWEEP
463 #define SM_SS_SET_GOAL  -1
464 #define SM_SS_BOX0              0
465 #define SM_SS_LR                        1
466 #define SM_SS_UL                        2
467 #define SM_SS_BOX1              3
468 #define SM_SS_UR                        4
469 #define SM_SS_LL                        5
470 #define SM_SS_BOX2              6
471 #define SM_SS_DONE              7
472
473 //XSTR:OFF
474
475 char *Mode_text[MAX_AI_BEHAVIORS] = {
476         "CHASE",
477         "EVADE",
478         "GET_BEHIND",
479         "CHASE_LONG",
480         "SQUIGGLE",
481         "GUARD",
482         "AVOID",
483         "WAYPOINTS",
484         "DOCK",
485         "NONE",
486         "BIGSHIP",
487         "PATH",
488         "BE_REARMED",
489         "SAFETY",
490         "EV_WEAPON",
491         "STRAFE",
492         "PLAY_DEAD",
493         "BAY_EMERGE",
494         "BAY_DEPART",
495         "SENTRYGUN",
496         "WARP_OUT",
497 };
498
499 //      Submode text is only valid for CHASE mode.
500 char *Submode_text[] = {
501 "undefined",
502 "CONT_TURN",
503 "ATTACK   ",
504 "E_SQUIG  ",
505 "E_BRAKE  ",
506 "EVADE    ",
507 "SUP_ATTAK",
508 "AVOID    ",
509 "BEHIND   ",
510 "GET_AWAY ",
511 "E_WEAPON ",
512 "FLY_AWAY ",
513 "ATK_4EVER",
514 "STLTH_FND",
515 "STLTH_SWP",
516 "BIG_APPR",
517 "BIG_CIRC",
518 "BIG_PARL"
519 };
520
521 char *Strafe_submode_text[5] = {
522 "ATTACK",
523 "AVOID",
524 "RETREAT1",
525 "RETREAT2",
526 "POSITION"
527 };
528 //XSTR:ON
529
530 /*
531 //      Used for global ignore of objects.  If an object appears in the Ignore_objects array,
532 //      no one will attack it.
533 #define MAX_IGNORE_OBJECTS      16
534 typedef struct {
535         int     objnum;
536         int     signature;
537 } ignore_object;
538
539 ignore_object   Ignore_objects[MAX_IGNORE_OBJECTS];
540 */
541
542 typedef struct eval_enemy_obj_struct {
543         int                     turret_parent_objnum;                   // parent of turret
544         float                   weapon_travel_dist;                             // max targeting range of turret weapon
545         int                     enemy_team_mask;
546         int                     weapon_system_ok;                                       // is the weapon subsystem of turret ship ok
547         int                     big_only_flag;                                          // turret fires only at big and huge ships
548         vector          *tpos;
549         vector          *tvec;
550         ship_subsys *turret_subsys;
551         int                     current_enemy;
552
553
554         float                   nearest_attacker_dist;                  // nearest ship 
555         int                     nearest_attacker_objnum;
556
557         float                   nearest_homing_bomb_dist;               // nearest homing bomb
558         int                     nearest_homing_bomb_objnum;
559
560         float                   nearest_bomb_dist;                              // nearest non-homing bomb
561         int                     nearest_bomb_objnum;
562
563         float                   nearest_dist;                                           // nearest ship attacking this turret
564         int                     nearest_objnum;
565 }       eval_enemy_obj_struct;
566
567
568 control_info    AI_ci;
569
570 object *Pl_objp;
571 object *En_objp;
572
573 waypoint_list Waypoint_lists[MAX_WAYPOINT_LISTS];
574
575 // How close a turret has to be point at its target before it
576 // can fire.  If the dot of the gun normal and the vector from gun
577 // to target is greater than this, the turret fires.  The smaller
578 // the sloppier the shooting.
579 #define AICODE_TURRET_DUMBFIRE_ANGLE            (0.8f)  
580 #define AICODE_TURRET_HEATSEEK_ANGLE            (0.7f)  
581 #define AICODE_TURRET_MAX_TIME_IN_RANGE (5.0f)
582
583 #define REARM_SOUND_DELAY               (3*F1_0)                //      Amount of time to delay rearm/repair after mode start
584 #define REARM_BREAKOFF_DELAY    (3*F1_0)                //      Amount of time to wait after fully rearmed to breakoff.
585
586 #define MIN_DIST_TO_WAYPOINT_GOAL       5.0f
587 #define MAX_GUARD_DIST                                  250.0f
588 #define BIG_GUARD_RADIUS                                500.0f
589
590 #define MAX_EVADE_TIME                  (15 * 1000)     //      Max time to evade a weapon.
591
592 // defines for repair ship stuff.
593 #define MAX_REPAIR_SPEED                        25.0f
594 #define MAX_UNDOCK_ABORT_SPEED  2.0f
595
596 // defines for EMP effect stuff
597 #define MAX_EMP_INACCURACY              50.0f
598
599 // defines for stealth
600 #define MAX_STEALTH_INACCURACY  50.0f           // at max view dist
601 #define STEALTH_MAX_VIEW_DIST   400             // dist at which 1) stealth no longer visible 2) firing inaccuracy is greatest
602 #define STEALTH_VIEW_CONE_DOT   0.707           // (half angle of 45 degrees)
603
604
605 ai_class        Ai_classes[MAX_AI_CLASSES];
606 int     Ai_firing_enabled = 1;
607 int     Num_ai_classes;
608
609 int     AI_FrameCount = 0;
610 int     Ship_info_inited = 0;
611 int     AI_watch_object = 0; // Debugging, object to spew debug info for.
612 int     Num_waypoint_lists = 0;
613 int     Mission_all_attack = 0;                                 //      !0 means all teams attack all teams.
614
615 char *Skill_level_names(int level, int translate)
616 {
617         char *str = NULL;
618
619         #if NUM_SKILL_LEVELS != 5
620         #error Number of skill levels is wrong!
621         #endif
622
623         if(translate){
624                 switch( level ) {
625                 case 0:
626                         str = XSTR("Very Easy", 469);
627                         break;
628                 case 1:
629                         str = XSTR("Easy", 470);
630                         break;
631                 case 2:
632                         str = XSTR("Medium", 471);
633                         break;
634                 case 3:
635                         str = XSTR("Hard", 472);
636                         break;
637                 case 4:
638                         str = XSTR("Insane", 473);
639                         break;
640                 default:        
641                         Int3();
642                 }
643         } else {
644                 switch( level ) {
645                 case 0:
646                         str = NOX("Very Easy");
647                         break;
648                 case 1:
649                         str = NOX("Easy");
650                         break;
651                 case 2:
652                         str = NOX("Medium");
653                         break;
654                 case 3:
655                         str = NOX("Hard");
656                         break;
657                 case 4:
658                         str = NOX("Insane");
659                         break;
660                 default:        
661                         Int3();
662                 }
663         }
664
665         return str;
666 }
667
668 #define DELAY_TARGET_TIME       (12*1000)               //      time in milliseconds until a ship can target a new enemy after an order.
669
670 //      Make enemy ships turn more slowly at lower skill levels.
671 float   Turn_time_skill_level_scale[NUM_SKILL_LEVELS] = {3.0f, 2.2f, 1.6f, 1.3f, 1.0f};
672
673 //      Maximum number of simultaneous homing weapons on player based on skill level.
674 int     Max_allowed_player_homers[NUM_SKILL_LEVELS] = {2, 3, 4, 7, 99};
675
676 //      Number of ships that can attack another ship at a given skill level.
677 int     Skill_level_max_attackers[NUM_SKILL_LEVELS] = {2, 3, 4, 5, 99};
678
679 //      How long until next predict position.
680 fix Skill_level_delay[NUM_SKILL_LEVELS] = {2*F1_0, 3*F1_0/2, 4*F1_0/3, F1_0/2, 0};
681
682 //      AI ships link primary weapons if energy levels greater than the following amounts:
683 float   Link_energy_levels_always[NUM_SKILL_LEVELS] = {100.0f, 80.0f, 60.0f, 40.0f, 20.0f};     //      always link
684 float   Link_energy_levels_maybe[NUM_SKILL_LEVELS] = {90.0f, 60.0f, 40.0f, 20.0f, 10.0f};       //      link if hull strength low
685
686 //      Seconds to add to time it takes to get enemy in range.  Only for player's enemies.
687 float   In_range_time[NUM_SKILL_LEVELS] = {2.0f, 1.4f, 0.75f, 0.0f, -1.0f};
688
689 //      No matter what, a random unit vector gets scaled by this amount in firing at an enemy.
690 //      Note that for shorter in-range times, these values get scaled, so a value of 0.5f is meaningful.
691 float   Aiming_error[NUM_SKILL_LEVELS] = {3.0f, 2.2f, 1.3f, 0.7f, 0.2f};
692
693 //      Chance a countermeasure will be fired based on skill level.
694 float Cmeasure_fire_chance[NUM_SKILL_LEVELS] = {0.2f, 0.3f, 0.5f, 0.9f, 1.1f};  //      Note, this gets scaled by ai_class
695
696 float Shield_manage_delays[NUM_SKILL_LEVELS] = {5.0f, 4.0f, 2.5f, 1.2f, 0.1f};
697
698 // accuracy we feed into the beam weapons based upon skill system
699 // float Beam_accuracy[NUM_SKILL_LEVELS] = {2.0f, 1.5f, 1.0f, 0.7f, 0.4f};
700
701 extern float Ship_fire_delay_scale_hostile[NUM_SKILL_LEVELS];
702 extern float Ship_fire_delay_scale_friendly[NUM_SKILL_LEVELS];
703
704 pnode           Path_points[MAX_PATH_POINTS];
705 pnode           *Ppfp;                  //      Free pointer in path points.
706
707 float   AI_frametime;
708
709 char *Ai_class_names[MAX_AI_CLASSES];
710
711 // global for rearm status for teams
712 int Ai_friendly_rearm_timestamp, Ai_hostile_rearm_timestamp, Ai_neutral_rearm_timestamp, Ai_traitor_rearm_timestamp, Ai_unknown_rearm_timestamp;
713
714 // globals for dealing with when to fire huge secondary weapons
715 #define MAX_HUGE_SECONDARY_INFO 10
716
717 typedef struct {
718         int team;
719         int weapon_index;
720         int max_fire_count;
721         char    *shipname;
722 } huge_fire_info;
723
724 huge_fire_info Ai_huge_fire_info[MAX_HUGE_SECONDARY_INFO];
725
726 int Ai_last_arrive_path;        // index of ship_bay path used by last arrival from a fighter bay
727
728 // forward declarations
729 int     ai_return_path_num_from_dockbay(object *dockee_objp, int dockbay_index);
730 void    create_model_exit_path(object *pl_objp, object *mobjp, int path_num, int count=1);
731 void    copy_xlate_model_path_points(object *objp, model_path *mp, int dir, int count, int path_num, pnode *pnp, int randomize_pnt=-1);
732
733 // ai_set_rearm_status takes a team (friendly, hostile, neutral) and a time.  This function
734 // sets the timestamp used to tell is it is a good time for this team to rearm.  Once the timestamp
735 // is no longer valid, then rearming is not a "good time"
736 // not safe.  Called from sexpression code.
737 void ai_set_rearm_status( int team, int time )
738 {
739         Assert( time >= 0 );
740
741         switch (team) {
742         case TEAM_FRIENDLY:
743                 Ai_friendly_rearm_timestamp = timestamp( time * 1000 );
744                 break;
745         case TEAM_HOSTILE:
746                 Ai_hostile_rearm_timestamp = timestamp( time * 1000 );
747                 break;
748         case TEAM_NEUTRAL:
749                 Ai_neutral_rearm_timestamp = timestamp( time * 1000 );
750                 break;
751         case TEAM_TRAITOR:
752                 Ai_traitor_rearm_timestamp = timestamp( time * 1000 );
753                 break;
754         case TEAM_UNKNOWN:
755                 Ai_traitor_rearm_timestamp = timestamp( time * 1000 );
756                 break;
757         default:
758                 Int3();
759                 break;
760         }
761 }
762
763 // int ai_good_time_to_rearm returns true(1) or false(0) if it is "safe" for the given
764 // object to rearm.  "safe" is currently defined by the mission designer using the good/bad
765 // time to rearm sexpressions.  This status is currently team based.  This function could
766 // be easily expended to further the definition of "safe"
767 int ai_good_time_to_rearm( object *objp )
768 {
769         int team, status;
770
771         Assert(objp->type == OBJ_SHIP);
772         team = Ships[objp->instance].team;
773         status = 0;
774
775         switch(team) {
776         case TEAM_FRIENDLY:
777                 status = timestamp_valid(Ai_friendly_rearm_timestamp);
778                 break;
779         case TEAM_HOSTILE:
780                 status = timestamp_valid(Ai_hostile_rearm_timestamp);
781                 break;
782         case TEAM_NEUTRAL:
783                 status = timestamp_valid(Ai_neutral_rearm_timestamp);
784                 break;
785         case TEAM_TRAITOR:
786                 status = timestamp_valid(Ai_traitor_rearm_timestamp);
787                 break;
788         case TEAM_UNKNOWN:
789                 status = timestamp_valid(Ai_unknown_rearm_timestamp);
790                 break;
791         default:
792                 Int3();
793                 break;
794         }
795
796         return status;
797 }
798
799 // functions to deal with letting the ai know about good times to fire powerful secondary
800 // weapons.
801
802 // this function is entry point from sexpression code to set internal data for use by ai code.
803 void ai_good_secondary_time( int team, int weapon_index, int max_fire_count, char *shipname )
804 {
805         int i, index;
806
807         // find an open slot to put this data
808         for ( i = 0; i < MAX_HUGE_SECONDARY_INFO; i++ ) {
809                 if ( Ai_huge_fire_info[i].weapon_index == -1 )
810                         break;
811         }
812
813         Assert( i < MAX_HUGE_SECONDARY_INFO );                  // we've run out of room
814
815         Ai_huge_fire_info[i].weapon_index = weapon_index;
816         Ai_huge_fire_info[i].team = team;
817         Ai_huge_fire_info[i].max_fire_count = max_fire_count;
818
819         Ai_huge_fire_info[i].shipname = ai_get_goal_ship_name( shipname, &index );
820 }
821
822 // function called internally to the ai code to tell whether or not weapon_num can be fired
823 // from firer_objp at target_objp.  This function will resolve the team for the firer.
824 // returns:
825 //              -1  -- when conditions don't allow firer to fire weapon_num on target_objp
826 //              >=0 -- when conditions allow firer to fire.  Return value is max number of weapon_nums
827 //           which can be fired on target_objp
828 int is_preferred_weapon(int weapon_num, object *firer_objp, object *target_objp)
829 {
830         int i, firer_team, target_signature;
831         ship *firer_ship;
832         huge_fire_info *hfi = NULL;
833
834         Assert( firer_objp->type == OBJ_SHIP );
835         firer_ship = &Ships[firer_objp->instance];
836         firer_team = firer_ship->team;
837
838         // get target object's signature and try to find it in the list.
839         target_signature = target_objp->signature;
840         for ( i = 0; i < MAX_HUGE_SECONDARY_INFO; i++ ) {
841                 int ship_index, signature;
842
843                 hfi = &Ai_huge_fire_info[i];
844                 if ( hfi->weapon_index == -1 )
845                         continue;
846
847                 ship_index = ship_name_lookup( hfi->shipname );
848                 if ( ship_index == -1 )
849                         continue;
850
851                 signature = Objects[Ships[ship_index].objnum].signature;
852
853                 // sigatures, weapon_index, and team must match
854                 if ( (signature == target_signature) && (hfi->weapon_index == weapon_num) && (hfi->team == firer_team) )
855                         break;
856         }
857
858         // return -1 if not found
859         if ( i == MAX_HUGE_SECONDARY_INFO )
860                 return -1;
861
862         // otherwise, we can return the max number of weapons we can fire against target_objps
863
864         return hfi->max_fire_count;
865 }
866
867 // function to clear out secondary firing infomration between levels
868 void ai_init_secondary_info()
869 {
870         int i;
871
872         // clear out the data for dealing with when ai ships can fire huge secondary weapons
873         for (i = 0; i < MAX_HUGE_SECONDARY_INFO; i++ ) {
874                 Ai_huge_fire_info[i].weapon_index = -1;
875                 Ai_huge_fire_info[i].team = -1;
876                 Ai_huge_fire_info[i].max_fire_count = -1;
877                 Ai_huge_fire_info[i].shipname = NULL;
878         }
879 }
880
881
882 //      Garbage collect the Path_points buffer.
883 //      Scans all objects, looking for used Path_points records.
884 //      Compresses Path_points buffer, updating aip->path_start and aip->path_cur indices.
885 //      Updates Ppfp to point to first free record.
886 //      This function is fairly fast.  Its worst-case running time is proportional to
887 //      3*MAX_PATH_POINTS + MAX_OBJECTS
888 //      Things to do to optimize this function:
889 //              1. if (t != 0) xlt++; can be replaced by xlt += t; assuming t can only be 0 or 1.
890 //              2. When pp_xlate is getting stuffed the first time, note highest index and use that 
891 //                      instead of MAX_PATH_POINTS in following two for loops.
892 void garbage_collect_path_points()
893 {
894         int     i;
895         int     pp_xlate[MAX_PATH_POINTS];
896         object  *A;
897         ship_obj        *so;
898
899         //      Scan all objects and create Path_points xlate table.
900         for (i=0; i<MAX_PATH_POINTS; i++)
901                 pp_xlate[i] = 0;
902
903         //      in pp_xlate, mark all used Path_point records
904         for ( so = GET_FIRST(&Ship_obj_list); so != END_OF_LIST(&Ship_obj_list); so = GET_NEXT(so) ) {
905                 A = &Objects[so->objnum];
906                 ship    *shipp = &Ships[A->instance];
907                 if (shipp->ai_index != -1) {
908                         ai_info *aip = &Ai_info[shipp->ai_index];
909
910                         if ((aip->path_length > 0) && (aip->path_start > -1)) {
911
912                                 for (int i=aip->path_start; i<aip->path_start + aip->path_length; i++) {
913                                         Assert(pp_xlate[i] == 0);       //      If this is not 0, then two paths use this point!
914                                         pp_xlate[i] = 1;
915                                 }
916                         }
917                 }
918         }
919
920         //      Now, stuff xlate index in pp_xlate.  This is the number to translate any path_start
921         //      or path_cur index to.
922         int     xlt = 0;
923         for (i=0; i<MAX_PATH_POINTS; i++) {
924                 int     t = pp_xlate[i];
925
926                 pp_xlate[i] = xlt;
927                 if (t != 0)
928                         xlt++;
929         }
930         
931         //      Update global Path_points free pointer.
932         Ppfp = &Path_points[xlt];
933
934         //      Now, using pp_xlate, fixup all aip->path_cur and aip->path_start indices
935         for ( so = GET_FIRST(&Ship_obj_list); so != END_OF_LIST(&Ship_obj_list); so = GET_NEXT(so) ) {
936                 A = &Objects[so->objnum];
937                 ship    *shipp = &Ships[A->instance];
938                 if (shipp->ai_index != -1) {
939                         ai_info *aip = &Ai_info[shipp->ai_index];
940
941                         if ((aip->path_length > 0) && (aip->path_start > -1)) {
942                                 Assert(aip->path_start < MAX_PATH_POINTS);
943                                 aip->path_start = pp_xlate[aip->path_start];
944
945                                 Assert((aip->path_cur >= 0) && (aip->path_cur < MAX_PATH_POINTS));
946                                 aip->path_cur = pp_xlate[aip->path_cur];
947                         }
948                 }
949         }
950
951         //      Now, compress the buffer.
952         for (i=0; i<MAX_PATH_POINTS; i++)
953                 if (i != pp_xlate[i])
954                         Path_points[pp_xlate[i]] = Path_points[i];
955
956 }
957
958 //      Hash two values together, return result.
959 //      Hash function: curval shifted right circular by one, newval xored in.
960 int hash(unsigned int curval, int newval)
961 {
962         int     addval = curval & 1;
963
964         curval >>= 1;
965         if (addval)
966                 curval |= 0x80000000;
967         curval ^= newval;
968
969         return curval;
970 }
971
972 //      Hash some information in an object together.
973 //      On 2/20/97, the information is position and orientation.
974 int create_object_hash(object *objp)
975 {
976         int     *ip;
977         unsigned int    hashval = 0;
978         int     i;
979
980         ip = (int *) &objp->orient;
981
982         for (i=0; i<9; i++) {
983                 hashval = hash(hashval, *ip);
984                 ip++;
985         }
986
987         ip = (int *) &objp->pos;
988
989         for (i=0; i<3; i++) {
990                 hashval = hash(hashval, *ip);
991                 ip++;
992         }
993
994         return hashval;
995 }
996
997 //      Stuff a list of NUM_SKILL_LEVELS floats at *plist.
998 void parse_float_list(float *plist)
999 {
1000         int     i;
1001
1002         for (i=0; i<NUM_SKILL_LEVELS; i++) {
1003                 stuff_float(&plist[i]);
1004         }
1005 }
1006
1007 void parse_ai_class()
1008 {
1009         ai_class        *aicp = &Ai_classes[Num_ai_classes];
1010
1011         required_string("$Name:");
1012         stuff_string(aicp->name, F_NAME, NULL);
1013
1014         Ai_class_names[Num_ai_classes] = aicp->name;
1015
1016         required_string("$accuracy:");
1017         parse_float_list(aicp->ai_accuracy);
1018
1019         required_string("$evasion:");
1020         parse_float_list(aicp->ai_evasion);
1021
1022         required_string("$courage:");
1023         parse_float_list(aicp->ai_courage);
1024
1025         required_string("$patience:");
1026         parse_float_list(aicp->ai_patience);
1027 }
1028
1029 void parse_aitbl()
1030 {
1031         // open localization
1032         lcl_ext_open();
1033
1034         read_file_text("ai.tbl");
1035
1036         reset_parse();
1037
1038         Num_ai_classes = 0;
1039
1040         required_string("#AI Classes");
1041
1042         while (required_string_either("#End", "$Name:")) {
1043                 Assert( Num_ai_classes < MAX_AI_CLASSES);
1044
1045                 parse_ai_class();
1046
1047                 Num_ai_classes++;
1048         }
1049
1050         // close localization
1051         lcl_ext_close();
1052 }
1053
1054 LOCAL int ai_inited = 0;
1055
1056 //========================= BOOK-KEEPING FUNCTIONS =======================
1057
1058 // Called once at game start-up
1059 void ai_init()
1060 {
1061         if ( !ai_inited )       {
1062                 // Do the first time initialization stuff here
1063                 int     rval;
1064
1065                 if ((rval = setjmp(parse_abort)) != 0) {
1066                         Error(LOCATION, "Error parsing 'ai.tbl'\r\nError code = %i.\r\n", rval);
1067                 } else {                        
1068                         parse_aitbl();                  
1069                 }
1070
1071                 ai_inited = 1;
1072         }
1073
1074         init_semirand();
1075         
1076         ai_level_init();
1077 }
1078
1079 // this inits the ai.  You should be able to call this between
1080 // levels to reset everything.
1081 void ai_level_init()
1082 {
1083         int i;
1084  
1085         // Do the stuff to reset all ai stuff here
1086         for (i=0; i<MAX_AI_INFO ; i++) {
1087                 Ai_info[i].shipnum = -1;
1088         }
1089         Ai_goal_signature = 0;
1090         Ai_friendly_rearm_timestamp = timestamp(-1);
1091         Ai_hostile_rearm_timestamp = timestamp(-1);
1092         Ai_neutral_rearm_timestamp = timestamp(-1);
1093         Ai_traitor_rearm_timestamp = timestamp(-1);
1094
1095         // clear out the stuff needed for AI firing powerful secondary weapons
1096         ai_init_secondary_info();
1097
1098         Ai_last_arrive_path=0;
1099 }
1100
1101 // BEGIN STEALTH
1102 // -----------------------------------------------------------------------------
1103 // Check if object is a stealth ship
1104 int is_object_stealth_ship(object* objp)
1105 {
1106         if (objp->type == OBJ_SHIP) {
1107                 if (Ship_info[Ships[objp->instance].ship_info_index].flags & SIF_STEALTH) {
1108                         return 1;
1109                 }
1110         }
1111
1112         // not stealth ship
1113         return 0;
1114 }
1115
1116 // -----------------------------------------------------------------------------
1117 // Init necessary ai info for new stealth target
1118 void init_ai_stealth_info(ai_info *aip, object *stealth_objp)
1119 {
1120         Assert(is_object_stealth_ship(stealth_objp));
1121
1122         // set necessary ai info for new stealth target
1123         aip->stealth_last_pos = stealth_objp->pos;
1124         aip->stealth_velocity = stealth_objp->phys_info.vel;
1125         aip->stealth_last_visible_stamp = timestamp();
1126 }
1127
1128 // -----------------------------------------------------------------------------
1129 // Check whether Pl_objp can see a stealth ship object
1130 #define STEALTH_INVISIBLE                       0
1131 #define STEALTH_VISIBLE                         1
1132 #define STEALTH_FULLY_TARGETABLE        2
1133
1134 float get_skill_stealth_dist_scaler()
1135 {
1136         // return dist scaler based on skill level
1137         switch (Game_skill_level) {
1138         case 0: // very easy
1139                 return 0.65f;
1140
1141         case 1: // easy
1142                 return 0.9f;
1143
1144         case 2: // medium
1145                 return 1.0f;
1146
1147         case 3: // hard
1148                 return 1.1f;
1149
1150         case 4: // insane
1151                 return 1.3f;
1152
1153         default:
1154                 Int3();
1155         }
1156
1157         return 1.0f;
1158 }
1159
1160 float get_skill_stealth_dot_scaler()
1161 {
1162         // return multiplier on dot based on skill level
1163         switch (Game_skill_level) {
1164         case 0: // very easy
1165                 return 1.3f;
1166
1167         case 1: // easy
1168                 return 1.1f;
1169
1170         case 2: // medium
1171                 return 1.0f;
1172
1173         case 3: // hard
1174                 return 0.9f;
1175
1176         case 4: // insane
1177                 return 0.7f;
1178
1179         default:
1180                 Int3();
1181         }
1182
1183         return 1.0f;
1184 }
1185
1186 int ai_is_stealth_visible(object *viewer_objp, object *stealth_objp)
1187 {
1188         ship *shipp;
1189         vector vec_to_stealth;
1190         float dot_to_stealth, dist_to_stealth, max_stealth_dist;
1191
1192         Assert(stealth_objp->type == OBJ_SHIP);
1193         shipp = &Ships[stealth_objp->instance];
1194         Assert(viewer_objp->type == OBJ_SHIP);
1195
1196         // check if stealth ship
1197         Assert(Ship_info[shipp->ship_info_index].flags & SIF_STEALTH);
1198
1199         // check if in neb and below awac level for visible
1200         if ( !ship_is_visible_by_team(stealth_objp->instance, Ships[viewer_objp->instance].team) ) {
1201                 vm_vec_sub(&vec_to_stealth, &stealth_objp->pos, &viewer_objp->pos);
1202                 dist_to_stealth = vm_vec_mag_quick(&vec_to_stealth);
1203                 dot_to_stealth = vm_vec_dotprod(&viewer_objp->orient.fvec, &vec_to_stealth) / dist_to_stealth;
1204
1205                 // get max dist at which stealth is visible
1206                 max_stealth_dist = get_skill_stealth_dist_scaler() * STEALTH_MAX_VIEW_DIST;
1207
1208                 // now check if within view frustrum
1209                 float needed_dot_to_stealth;
1210                 if (dist_to_stealth < 100) {
1211                         needed_dot_to_stealth = 0.0f;
1212                 } else {
1213                         needed_dot_to_stealth = get_skill_stealth_dot_scaler() * float(STEALTH_VIEW_CONE_DOT) * (dist_to_stealth / max_stealth_dist);
1214                 }
1215                 if (dot_to_stealth > needed_dot_to_stealth) {
1216                         if (dist_to_stealth < max_stealth_dist) {
1217                                 return STEALTH_VISIBLE;
1218                         }
1219                 }
1220
1221                 // not within frustrum
1222                 return STEALTH_INVISIBLE;
1223         }
1224
1225         // visible by awacs level
1226         return STEALTH_FULLY_TARGETABLE;
1227 }
1228
1229 // END STEALTH
1230
1231 //      Compute dot product of direction vector and forward vector.
1232 //      Direction vector is vector from one object to other object.
1233 //      Forward vector is the forward vector of the ship.
1234 //      If from_dot == NULL, don't fill it in.
1235 float compute_dots(object *objp, object *other_objp, float *to_dot, float *from_dot)
1236 {
1237         vector  v2o;
1238         float           dist;
1239
1240         dist = vm_vec_normalized_dir(&v2o, &other_objp->pos, &objp->pos);
1241
1242         *to_dot = vm_vec_dot(&objp->orient.fvec, &v2o);
1243
1244         if (from_dot != NULL)
1245                 *from_dot = - vm_vec_dot(&other_objp->orient.fvec, &v2o);
1246
1247         return dist;
1248 }
1249
1250 // -----------------------------------------------------------------------------
1251 // update estimated stealth info
1252 // this is a "cheat" update
1253 // error increases with time not seen, true distance away, dot to enemey
1254 // this is done only if we can not see the stealth target
1255 // need to infer its position either by weapon fire pos or last know pos
1256 void update_ai_stealth_info_with_error(ai_info *aip/*, int no_error*/)
1257 {
1258         object *ship;
1259         object *stealth_objp;
1260         /*
1261         float error_time_mult, error_dist_mult, error_dot_mult, error_mult;
1262         float pos_error, vel_error;
1263         vector error_vec, vec_to_stealth;
1264         float dist_to_stealth, dot_to_stealth;
1265         float delta_time, delta_capped;
1266         */
1267
1268         // make sure I am targeting a stealth ship
1269         Assert( is_object_stealth_ship(&Objects[aip->target_objnum]) );
1270         stealth_objp = &Objects[aip->target_objnum];
1271
1272         // my_ship
1273         ship = &Objects[Ships[aip->shipnum].objnum];
1274
1275         // if update is due to weapon fire, get exact stealth position
1276 //      if (no_error) {
1277         aip->stealth_last_pos = stealth_objp->pos;
1278         aip->stealth_velocity = stealth_objp->phys_info.vel;
1279         aip->stealth_last_visible_stamp = timestamp();
1280 //              return;
1281 //      }
1282 /*
1283         // get time since last seen
1284         delta_time = 0.001f * (timestamp() - aip->stealth_last_visible_stamp);
1285
1286         // we don't want our "cheat" guess to more off than what we would get from extrapolating from last visible
1287         // only update if stealth info is "old"
1288         if ( (delta_time) < 0.5 ) {
1289                 return;
1290         }
1291
1292         // find vec_to_stealth and dist
1293         vm_vec_sub(&vec_to_stealth, &stealth_objp->pos, &ship->pos);
1294         dist_to_stealth = vm_vec_normalize_quick(&vec_to_stealth);
1295         dot_to_stealth = vm_vec_dotprod(&vec_to_stealth, &ship->orient.fvec);
1296
1297         // put cap on time
1298         delta_capped = delta_time;
1299         if (delta_time > 5.0) {
1300                 delta_capped = 5.0f;
1301         }
1302
1303         // erorr_time_mult (for 0-5) -> (1-6)
1304         error_time_mult = (1.0f + delta_capped);
1305
1306         // error_dot_mult (-1 to 1) -> (1-3)
1307         error_dot_mult = (2 - dot_to_stealth);
1308
1309         // error_dist_mult (0-1000+) -> (1-4)
1310         error_dist_mult = dist_to_stealth * 4.0f * 0.001f;
1311         if (error_dist_mult < 1) {
1312                 error_dist_mult = 1.0f;
1313         } else if (error_dist_mult > 4) {
1314                 error_dist_mult = 4.0f;
1315         }
1316
1317         // multiply error out
1318         error_mult = error_time_mult * error_dot_mult * error_dist_mult;
1319
1320         float base_pos_error = 10;
1321         float base_vel_error = 2;
1322
1323         // find the position and velocity error magnitude;
1324         pos_error = base_pos_error * error_mult;
1325         vel_error = base_vel_error * error_mult;
1326
1327         // get an error that changes slowly over time
1328         static_randvec( ((int)aip ^ (Missiontime >> 18)) & 7, &error_vec);
1329         vm_vec_zero(&error_vec);
1330
1331         // update pos and vel with error
1332         vm_vec_scale_add(&aip->stealth_velocity, &stealth_objp->phys_info.vel, &error_vec, vel_error);
1333
1334         // revise last "known" position to arrive at last pos with given error
1335         vm_vec_scale_add(&aip->stealth_last_pos, &stealth_objp->pos, &error_vec, pos_error);
1336         vm_vec_scale_add2(&aip->stealth_last_pos, &aip->stealth_velocity, -(0.001f * delta_time));
1337         */
1338 }
1339
1340 //      Update danger_weapon_objnum and signature in ai_info to say this weapon is to be avoided.
1341 void ai_update_danger_weapon(int attacked_objnum, int weapon_objnum)
1342 {
1343         object  *objp, *weapon_objp;
1344         ai_info *aip;
1345         float           old_dist, new_dist;
1346         float           old_dot, new_dot;
1347         object  *old_weapon_objp = NULL;
1348
1349         if ((attacked_objnum == -1) || (weapon_objnum == -1)) {
1350                 return;
1351         }
1352
1353         objp = &Objects[attacked_objnum];
1354
1355         // AL 2-24-98: If this isn't a ship, we don't need to worry about updating weapon_objnum (ie it would be
1356         //                                      an asteroid or bomb).
1357         if ( objp->type != OBJ_SHIP ) {
1358                 return;
1359         }
1360
1361         weapon_objp = &Objects[weapon_objnum];
1362
1363         aip = &Ai_info[Ships[objp->instance].ai_index];
1364
1365         // if my taraget is a stealth ship and is not visible
1366         if (aip->target_objnum >= 0) {
1367                 if ( is_object_stealth_ship(&Objects[aip->target_objnum]) ) {
1368                         if ( ai_is_stealth_visible(objp, &Objects[aip->target_objnum]) == STEALTH_INVISIBLE ) {
1369                                 // and the weapon is coming from that stealth ship
1370                                 if (weapon_objp->parent == aip->target_objnum) {
1371                                         // update my position estimate for stealth ship
1372                                         update_ai_stealth_info_with_error(aip/*, 1*/);
1373                                 }
1374                         }
1375                 }
1376         }
1377
1378         if (aip->danger_weapon_objnum != -1) {
1379                 old_weapon_objp = &Objects[aip->danger_weapon_objnum];
1380                 if ((old_weapon_objp->type == OBJ_WEAPON) && (old_weapon_objp->signature == aip->danger_weapon_signature)) {
1381                         ;
1382                 } else {
1383                         aip->danger_weapon_objnum = -1;
1384                 }
1385         }
1386
1387         new_dist = compute_dots(weapon_objp, objp, &new_dot, NULL);
1388
1389         if (aip->danger_weapon_objnum == -1) {
1390                 if (new_dist < 1500.0f) {
1391                         if (new_dot > 0.5f) {
1392                                 aip->danger_weapon_objnum = weapon_objnum;
1393                                 aip->danger_weapon_signature = weapon_objp->signature;
1394                         }
1395                 }
1396         } else {
1397                 Assert(old_weapon_objp != NULL);
1398                 old_dist = compute_dots(old_weapon_objp, objp, &old_dot, NULL);
1399         
1400                 if (old_dot < 0.5f) {
1401                         aip->danger_weapon_objnum = -1;
1402                         old_dist = 9999.9f;
1403                 }
1404
1405                 if ((new_dot > 0.5f) && (new_dot > old_dot-0.01f)) {
1406                         if (new_dist < old_dist) {
1407                                 aip->danger_weapon_objnum = weapon_objnum;
1408                                 aip->danger_weapon_signature = weapon_objp->signature;
1409                         }
1410                 }
1411         }
1412 }
1413
1414 //      If rvec != NULL, use it to match bank by calling vm_matrix_interpolate.
1415 //      (rvec defaults to NULL)
1416 void ai_turn_towards_vector(vector *dest, object *objp, 
1417                                                                          float frametime, float turn_time, vector *slide_vec, vector *rel_pos, float bank_override, int flags, vector *rvec)
1418 {
1419         //matrix        goal_orient;
1420         matrix  curr_orient;
1421         vector  vel_in, vel_out, desired_fvec, src;
1422         float           delta_time;
1423         physics_info    *pip;
1424         vector  vel_limit, acc_limit;
1425         float           delta_bank;
1426
1427         //      Don't allow a ship to turn if it has no engine strength.
1428         // AL 3-12-98: objp may not always be a ship!
1429         if ( objp->type == OBJ_SHIP ) {
1430                 if (ship_get_subsystem_strength(&Ships[objp->instance], SUBSYSTEM_ENGINE) <= 0.0f)
1431                         return;
1432         }
1433                         
1434         //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));
1435         pip = &objp->phys_info;
1436
1437         vel_in = pip->rotvel;
1438         curr_orient = objp->orient;
1439         delta_time = flFrametime;
1440
1441         Assert(turn_time > 0.0f);
1442         
1443         //      Scale turn_time based on skill level and team.
1444         if (!(flags & AITTV_FAST)){
1445                 if (objp->type == OBJ_SHIP){
1446                         if (Ships[objp->instance].team != Ships[Player_obj->instance].team){
1447                                 turn_time *= Turn_time_skill_level_scale[Game_skill_level];
1448                         }
1449                 }
1450         }
1451
1452         //      Set max turn rate.
1453         vel_limit.x = 2*PI/turn_time;
1454         vel_limit.y = 2*PI/turn_time;
1455         vel_limit.z = 2*PI/turn_time;
1456
1457         //      Set rate at which ship can accelerate to its rotational velocity.
1458         //      For now, weapons just go much faster.
1459         acc_limit = vel_limit;
1460         if (objp->type == OBJ_WEAPON)
1461                 vm_vec_scale(&acc_limit, 8.0f);
1462
1463         src = objp->pos;
1464
1465         if (rel_pos != NULL) {
1466                 vector  gun_point;
1467                 vm_vec_unrotate(&gun_point, rel_pos, &objp->orient);
1468                 vm_vec_add2(&src, &gun_point);
1469         }
1470
1471         vm_vec_normalized_dir(&desired_fvec, dest, &src);
1472
1473         //      Since ship isn't necessarily moving in the direction it's pointing, sometimes it's better
1474         //      to be moving towards goal rather than just pointing.  So, if slide_vec is !NULL, try to
1475         //      make ship move towards goal, not point at goal.
1476         if (slide_vec != NULL) {
1477                 vm_vec_add2(&desired_fvec, slide_vec);
1478                 vm_vec_normalize(&desired_fvec);
1479         }
1480
1481         //      Should be more general case here.  Currently, anything that is not a weapon will bank when it turns.
1482         if (objp->type == OBJ_WEAPON)
1483                 delta_bank = 0.0f;
1484         else if ((bank_override) && (Ships[objp->instance].team & opposing_team_mask(Player_ship->team))) {     //      Theoretically, this will only happen for Shivans.
1485                 delta_bank = bank_override;
1486                 //nprintf(("AI", "%i: %7.3f\n", Framecount, bank_override));
1487         } else {
1488                 delta_bank = vm_vec_dot(&curr_orient.rvec, &objp->last_orient.rvec);
1489                 delta_bank = 100.0f * (1.0f - delta_bank);
1490                 if (vm_vec_dot(&objp->last_orient.fvec, &objp->orient.rvec) < 0.0f)
1491                         delta_bank = -delta_bank;
1492
1493                 //nprintf(("AI", "%s: Frame %i: delta bank = %7.3f\n", Ships[objp->instance].ship_name, Framecount, delta_bank));
1494         }
1495
1496         //      Dave Andsager: The non-indented lines here are debug code to help you track down the problem in the physics
1497         //      that is causing ships to inexplicably rotate very far.  If you hit the Int3(), set the next statement to be
1498         //      the one marked "HERE".  (Do this clicking the cursor there, then right clicking.  Choose the right option.)
1499         //      This will allow you to rerun vm_forward_interpolate() with the values that caused the error.
1500         //      Note, you'll need to enable the Int3() about ten lines below.
1501 #ifndef NDEBUG
1502 vector tvec = objp->orient.fvec;
1503 vector  vel_in_copy;
1504 matrix  objp_orient_copy;
1505
1506 vel_in_copy = vel_in;
1507 objp_orient_copy = objp->orient;
1508
1509 vel_in = vel_in_copy;   //      HERE
1510 objp->orient = objp_orient_copy;
1511 #endif
1512         if (rvec != NULL) {
1513                 matrix  out_orient, goal_orient;
1514
1515                 vm_vector_2_matrix(&goal_orient, &desired_fvec, NULL, rvec);
1516                 vm_matrix_interpolate(&goal_orient, &curr_orient, &vel_in, delta_time, &out_orient, &vel_out, &vel_limit, &acc_limit);
1517                 objp->orient = out_orient;
1518         } else {
1519                 vm_forward_interpolate(&desired_fvec, &curr_orient, &vel_in, delta_time, delta_bank, &objp->orient, &vel_out, &vel_limit, &acc_limit);
1520         }
1521 #ifndef NDEBUG
1522 if (!((objp->type == OBJ_WEAPON) && (Weapon_info[Weapons[objp->instance].weapon_info_index].subtype == WP_MISSILE))) {
1523         if (delta_time < 0.25f && vm_vec_dot(&objp->orient.fvec, &tvec) < 0.1f)
1524                 Int3(); //      Get Andsager.  A ship has turned too far in one frame.
1525 }
1526 #endif
1527
1528         pip->rotvel = vel_out;
1529 }
1530
1531 void init_ship_info()
1532 {
1533         int     i;
1534
1535         if (Ship_info_inited)
1536                 return;
1537
1538         for (i=0; i<MAX_SHIP_TYPES; i++) {
1539                 Ship_info[i].min_speed = - Ship_info[i].max_rear_vel;
1540                 Ship_info[i].max_accel = Ship_info[i].max_vel.z;
1541         }
1542
1543         Ship_info_inited = 1;
1544
1545 }
1546
1547 //      Set aip->target_objnum to objnum
1548 //      Update aip->previous_target_objnum.
1549 //      If new target (objnum) is different than old target, reset target_time.
1550 int set_target_objnum(ai_info *aip, int objnum)
1551 {
1552 /*
1553         char    old_name[32], new_name[32];
1554
1555         if (!timestamp_elapsed(aip->ok_to_target_timestamp))
1556                 return aip->target_objnum;
1557
1558         if (Player_ship && (Ships[aip->shipnum].team == Player_ship->team)) {
1559                 if (aip->target_objnum == -1)
1560                         strcpy(old_name, "none");
1561                 else
1562                         strcpy(old_name, Ships[Objects[aip->target_objnum].instance].ship_name);
1563
1564                 if (objnum == -1)
1565                         strcpy(new_name, "none");
1566                 else
1567                         strcpy(new_name, Ships[Objects[objnum].instance].ship_name);
1568
1569                 nprintf(("AI", "Ship %s changing target from %s to %s\n", Ships[aip->shipnum].ship_name, old_name, new_name));
1570         }
1571 */
1572
1573         // AL 2-25-97: Ensure that a protected ship isn't being set as a target (for non-players only)
1574         /*
1575         if ( objnum >= 0 ) {
1576                 if ( !(Objects[Ships[aip->shipnum].objnum].flags & OF_PLAYER_SHIP) ) {
1577                         if ( Objects[objnum].flags & OF_PROTECTED ) {
1578                                 // AL 2-26-97: removing Int3() until issue with setting OF_PROTECTED in ai_set_attack_subsystem()
1579                                 //Int3();                                                               // this should not happen
1580                                 return aip->target_objnum;              // don't change targets
1581                         }
1582                 }
1583         }
1584         */
1585
1586         if ((aip != Player_ai) && (!timestamp_elapsed(aip->ok_to_target_timestamp))) {
1587                 return aip->target_objnum;
1588         }
1589
1590         if (aip->target_objnum == objnum) {
1591                 aip->previous_target_objnum = aip->target_objnum;
1592         } else {
1593                 aip->previous_target_objnum = aip->target_objnum;
1594
1595                 // ignore this assert if a multiplayer observer
1596                 if((Game_mode & GM_MULTIPLAYER) && (aip == Player_ai) && (Player_obj->type == OBJ_OBSERVER)){
1597                 } else {
1598                         Assert(objnum != Ships[aip->shipnum].objnum);   //      make sure not targeting self
1599                 }
1600
1601                 // if stealth target, init ai_info for stealth
1602                 if ( (objnum > 0) && is_object_stealth_ship(&Objects[objnum]) ) {
1603                         init_ai_stealth_info(aip, &Objects[objnum]);
1604                 }
1605
1606                 aip->target_objnum = objnum;
1607                 aip->target_time = 0.0f;
1608                 aip->target_signature = Objects[objnum].signature;
1609                 // clear targeted subsystem
1610                 set_targeted_subsys(aip, NULL, -1);
1611         }
1612         
1613         return aip->target_objnum;
1614 }
1615
1616 int ai_select_primary_weapon(object *objp, object *other_objp, int flags);
1617
1618 //      Make new_subsys the targeted subsystem of ship *aip.
1619 ship_subsys *set_targeted_subsys(ai_info *aip, ship_subsys *new_subsys, int parent_objnum)
1620 {
1621         Assert(aip != NULL);
1622
1623         aip->last_subsys_target = aip->targeted_subsys;
1624         aip->targeted_subsys = new_subsys;
1625         aip->targeted_subsys_parent = parent_objnum;
1626
1627         if ( new_subsys ) {
1628                 // Make new_subsys target
1629                 if (new_subsys->system_info->type == SUBSYSTEM_ENGINE) {
1630                         if ( aip != Player_ai ) {
1631                                 ai_select_primary_weapon(&Objects[Ships[aip->shipnum].objnum], &Objects[parent_objnum], WIF_PUNCTURE);
1632                                 ship_primary_changed(&Ships[aip->shipnum]);     // AL: maybe send multiplayer information when AI ship changes primaries
1633                         }
1634                 }
1635
1636                 if ( aip == Player_ai ) {
1637                         hud_lock_reset(0.5f);
1638                 }
1639
1640         } else {
1641                 // Cleanup any subsys path information if it exists
1642                 ai_big_subsys_path_cleanup(aip);
1643         }
1644         
1645         return aip->targeted_subsys;
1646 }                                                                                         
1647
1648 // called to init the data for single ai object.  At this point,
1649 // the ship and the object and the ai_info are are correctly
1650 // linked together. Ai_info[ai_index].shipnum is the only valid field 
1651 // in ai_info.
1652 //      This is called right when the object is parsed, so you can't assume much
1653 //      has been initialized.  For example, wings, waypoints, goals are probably
1654 //      not yet loaded. --MK, 10/8/96
1655 void ai_object_init(object * obj, int ai_index)
1656 {
1657         ai_info *aip;
1658         Assert(ai_index >= 0 && ai_index < MAX_AI_INFO);
1659
1660         aip = &Ai_info[ai_index];
1661
1662         aip->type = 0;          //      0 means not in use.
1663         aip->wing = -1;         //      Member of what wing? -1 means none.
1664         aip->ai_class = Ship_info[Ships[obj->instance].ship_info_index].ai_class;
1665         aip->behavior = AIM_NONE;
1666 }
1667
1668 //      If *aip is docked, set max acceleration to A->mass/(A->mass + B->mass) where A is *aip and B is dock object
1669 void adjust_accel_for_docking(ai_info *aip)
1670 {
1671         if (aip->dock_objnum != -1) {
1672                 object  *obj2p = &Objects[aip->dock_objnum];
1673                 object  *obj1p;
1674
1675                 obj1p = &Objects[Ships[aip->shipnum].objnum];
1676
1677                 if (obj2p->signature == aip->dock_signature) {
1678                         float   ratio;
1679
1680                         ratio = obj1p->phys_info.mass / (obj1p->phys_info.mass + obj2p->phys_info.mass);
1681
1682                         // put cap on how much ship can slow down
1683                         if (ratio < 0.8) {
1684                                 ratio = 0.8f;
1685                         }
1686
1687                         if (AI_ci.forward > ratio) {
1688                                 AI_ci.forward = ratio;
1689                         }
1690                 }
1691         }
1692 }
1693
1694 // -------------------------------------------------------------------
1695 void accelerate_ship(ai_info *aip, float accel)
1696 {
1697         aip->prev_accel = accel;
1698         AI_ci.forward = accel;
1699         adjust_accel_for_docking(aip);
1700 }
1701
1702 //      --------------------------------------------------------------------------
1703 void change_acceleration(ai_info *aip, float delta_accel)
1704 {
1705         float   new_accel;
1706
1707         if (delta_accel < 0.0f) {
1708                 if (aip->prev_accel > 0.0f)
1709                         aip->prev_accel = 0.0f;
1710         } else if (aip->prev_accel < 0.0f)
1711                 aip->prev_accel = 0.0f;
1712
1713         new_accel = aip->prev_accel + delta_accel * flFrametime;
1714
1715         if (new_accel > 1.0f)
1716                 new_accel = 1.0f;
1717         else if (new_accel < -1.0f)
1718                 new_accel = -1.0f;
1719         
1720         aip->prev_accel = new_accel;
1721
1722         AI_ci.forward = new_accel;
1723         adjust_accel_for_docking(aip);
1724 }
1725
1726 void set_accel_for_target_speed(object *objp, float tspeed)
1727 {
1728         float   max_speed;
1729         ai_info *aip;
1730
1731         aip = &Ai_info[Ships[objp->instance].ai_index];
1732
1733         max_speed = Ships[objp->instance].current_max_speed;
1734
1735         AI_ci.forward = tspeed/max_speed;
1736         aip->prev_accel = AI_ci.forward;
1737
1738         adjust_accel_for_docking(aip);
1739 }
1740
1741 //      Stuff perim_point with a point on the perimeter of the sphere defined by object *objp
1742 //      on the vector from the center of *objp through the point *vp.
1743 void project_point_to_perimeter(vector *perim_point, vector *pos, float radius, vector *vp)
1744 {
1745         vector  v1;
1746         float           mag;
1747
1748         vm_vec_sub(&v1, vp, pos);
1749         mag = vm_vec_mag(&v1);
1750
1751         if (mag == 0.0f) {
1752                 Warning(LOCATION, "projectable point is at center of sphere.");
1753                 vm_vec_make(&v1, 0.0f, radius, 0.0f);
1754         } else {
1755                 vm_vec_normalize(&v1);
1756                 vm_vec_scale(&v1, 1.1f * radius + 10.0f);
1757         }
1758
1759         vm_vec_add2(&v1, pos);
1760         *perim_point = v1;
1761 }
1762
1763 //      Stuff tan1 with tangent point on sphere.  tan1 is point nearer to *p1
1764 //      *p0 is point through which tangents pass.
1765 //      *centerp is center of sphere.
1766 //      *p1 is another point in space to define the plane in which tan1, tan2 reside.
1767 //      radius is the radius of the sphere.
1768 //      Note, this is a very approximate function just for AI.
1769 //      Note also: On 12/26/96, p1 is used to define the plane perpendicular to that which
1770 //      contains the tangent point.
1771 void get_tangent_point(vector *tan1, vector *p0, vector *centerp, vector *p1, float radius)
1772 {
1773         vector  dest_vec, v2c, perp_vec, temp_vec, v2;
1774         float           dist, ratio;
1775
1776         //      Detect condition of point inside sphere.
1777         if (vm_vec_dist(p0, centerp) < radius)
1778                 project_point_to_perimeter(tan1, centerp, radius, p0);
1779         else {
1780                 vm_vec_normalized_dir(&v2c, centerp, p0);
1781
1782                 //      Compute perpendicular vector using p0, centerp, p1
1783                 vm_vec_normal(&temp_vec, p0, centerp, p1);
1784                 vm_vec_sub(&v2, centerp, p0);
1785                 vm_vec_cross(&perp_vec, &temp_vec, &v2);
1786
1787                 vm_vec_normalize(&perp_vec);
1788
1789                 dist = vm_vec_dist_quick(p0, centerp);
1790                 ratio = dist / radius;
1791
1792                 if (ratio < 2.0f)
1793                         vm_vec_scale_add(&dest_vec, &perp_vec, &v2c, ratio-1.0f);
1794                 else
1795                         vm_vec_scale_add(&dest_vec, &v2c, &perp_vec, (1.0f + 1.0f/ratio));
1796
1797                 vm_vec_scale_add(tan1, p0, &dest_vec, dist + radius);
1798         }
1799 }
1800
1801 //      --------------------------------------------------------------------------
1802 //      Given an object and a point, turn towards the point, resulting in
1803 // approach behavior.
1804 void turn_towards_point(object *objp, vector *point, vector *slide_vec, float bank_override)
1805 {
1806         ai_info *aip;
1807         aip = &Ai_info[Ships[Pl_objp->instance].ai_index];
1808         
1809         // check if in formation and if not leader, don't change rotvel.z (bank to match leader elsewhere)
1810         if (aip->ai_flags & AIF_FORMATION) {
1811                 if (&Objects[aip->goal_objnum] != objp) {
1812                         float rotvel_z = objp->phys_info.rotvel.z;
1813                         ai_turn_towards_vector(point, objp, flFrametime, Ship_info[Ships[objp->instance].ship_info_index].srotation_time, slide_vec, NULL, bank_override, 0);
1814                         objp->phys_info.rotvel.z = rotvel_z;
1815                 }
1816         } else {
1817                 // normal turn
1818                 ai_turn_towards_vector(point, objp, flFrametime, Ship_info[Ships[objp->instance].ship_info_index].srotation_time, slide_vec, NULL, bank_override, 0);
1819         }
1820 }
1821
1822 //      --------------------------------------------------------------------------
1823 //      Given an object and a point, turn away from the point, resulting in avoidance behavior.
1824 //      Note: Turn away at full speed, not scaled down by skill level.
1825 void turn_away_from_point(object *objp, vector *point, float bank_override)
1826 {
1827         vector  opposite_point;
1828
1829         vm_vec_sub(&opposite_point, &objp->pos, point);
1830         vm_vec_add2(&opposite_point, &objp->pos);
1831
1832         ai_turn_towards_vector(&opposite_point, objp, flFrametime, Ship_info[Ships[objp->instance].ship_info_index].srotation_time, NULL, NULL, bank_override, AITTV_FAST);
1833 }
1834
1835
1836 //      --------------------------------------------------------------------------
1837 //      Given an object and a point, turn tangent to the point, resulting in
1838 // a circling behavior.
1839 //      Make object *objp turn around the point *point with a radius of radius.
1840 //      Note that this isn't the same as following a circle of radius radius with
1841 //      center *point, but it should be adequate.
1842 //      Note that if you want to circle an object without hitting it, you should use
1843 //      about twice that object's radius for radius, else you'll certainly bump into it.
1844 //      Return dot product to goal point.
1845 float turn_towards_tangent(object *objp, vector *point, float radius)
1846 {
1847         vector  vec_to_point;
1848         vector  goal_point;
1849         vector  perp_point;                             //      point radius away from *point on vector to objp->pos
1850         vector  up_vec, perp_vec;
1851
1852         vm_vec_normalized_dir(&vec_to_point, point, &objp->pos);
1853         vm_vec_crossprod(&up_vec, &vec_to_point, &objp->orient.fvec);
1854         vm_vec_crossprod(&perp_vec, &vec_to_point, &up_vec);
1855
1856         vm_vec_scale_add(&perp_point, point, &vec_to_point, -radius);
1857         if (vm_vec_dot(&objp->orient.fvec, &perp_vec) > 0.0f) {
1858                 vm_vec_scale_add(&goal_point, &perp_point, &perp_vec, radius);
1859         } else {
1860                 vm_vec_scale_add(&goal_point, &perp_point, &perp_vec, -radius);
1861         }
1862
1863 //      Ai_info[Ships[objp->instance].ai_index].goal_point = goal_point;
1864         turn_towards_point(objp, &goal_point, NULL, 0.0f);
1865
1866         vector  v2g;
1867
1868         vm_vec_normalized_dir(&v2g, &goal_point, &objp->pos);
1869         return vm_vec_dot(&objp->orient.fvec, &v2g);
1870 }
1871
1872 float turn_toward_tangent_with_axis(object *objp, object *center_objp, float radius)
1873 {
1874         vector r_vec, theta_vec;
1875         vector center_vec, vec_on_cylinder, sph_r_vec;
1876         float center_obj_z;
1877
1878         // find closest z of center objp
1879         vm_vec_sub(&sph_r_vec, &objp->pos, &center_objp->pos);
1880         center_obj_z = vm_vec_dotprod(&sph_r_vec, &center_objp->orient.fvec);
1881
1882         // find pt on axis with closest z
1883         vm_vec_scale_add(&center_vec, &center_objp->pos, &center_objp->orient.fvec, center_obj_z);
1884
1885         // get r_vec
1886         vm_vec_sub(&r_vec, &objp->pos, &center_vec);
1887 //      float r_mag = vm_vec_normalize_quick(&r_vec);
1888 //      mprintf(("cur_r: %.1f, desired_r: %.1f\n", r_mag, radius));
1889         Assert( (vm_vec_dotprod(&r_vec, &center_objp->orient.fvec) < 0.0001));
1890
1891         // get theta vec - perp to r_vec and z_vec
1892         vm_vec_crossprod(&theta_vec, &center_objp->orient.fvec, &r_vec);
1893
1894 #ifndef NDEBUG
1895         float mag = vm_vec_normalize(&theta_vec);
1896         Assert(mag > 0.9999 && mag < 1.0001);
1897 #endif
1898
1899         vector temp;
1900         vm_vec_crossprod(&temp, &r_vec, &theta_vec);
1901
1902 #ifndef NDEBUG
1903         float dot = vm_vec_dotprod(&temp, &center_objp->orient.fvec);
1904         Assert( dot >0.9999 && dot < 1.0001);
1905 #endif
1906
1907         // find pt on clylinder with closest z
1908         vm_vec_scale_add(&vec_on_cylinder, &center_vec, &r_vec, radius);
1909
1910         vector goal_pt, v2g;
1911         vm_vec_scale_add(&goal_pt, &vec_on_cylinder, &theta_vec, radius);
1912
1913 //      Ai_info[Ships[objp->instance].ai_index].goal_point = goal_pt;
1914         turn_towards_point(objp, &goal_pt, NULL, 0.0f);
1915
1916         vm_vec_normalized_dir(&v2g, &goal_pt, &objp->pos);
1917         return vm_vec_dot(&objp->orient.fvec, &v2g);
1918 }
1919
1920 //      Returns a point radius units away from *point that *objp should turn towards to orbit *point
1921 void get_tangent_point(vector *goal_point, object *objp, vector *point, float radius)
1922 {
1923         vector  vec_to_point;
1924         vector  perp_point;                             //      point radius away from *point on vector to objp->pos
1925         vector  up_vec, perp_vec;
1926
1927         vm_vec_normalized_dir(&vec_to_point, point, &objp->pos);
1928         vm_vec_crossprod(&up_vec, &vec_to_point, &objp->orient.fvec);
1929         vm_vec_crossprod(&perp_vec, &vec_to_point, &up_vec);
1930         vm_vec_normalize(&perp_vec);
1931
1932         vm_vec_scale_add(&perp_point, point, &vec_to_point, -radius);
1933
1934         if (vm_vec_dot(&objp->orient.fvec, &perp_vec) > 0.0f) {
1935                 vm_vec_scale_add(goal_point, &perp_point, &perp_vec, radius);
1936         } else {
1937                 vm_vec_scale_add(goal_point, &perp_point, &perp_vec, -radius);
1938         }
1939 }
1940
1941 int     Player_attacking_enabled = 1;
1942
1943 // -----------------------------------------------------------------------------
1944 // Determine whether an object is targetable within a nebula
1945 int object_is_targetable(object *target, ship *viewer)
1946 {
1947         int stealth_ship = 0;
1948
1949         // if target is ship, check if visible by team
1950         if (target->type == OBJ_SHIP) {
1951                 stealth_ship = (Ship_info[Ships[target->instance].ship_info_index].flags & SIF_STEALTH);
1952                 if ( ship_is_visible_by_team(target->instance, viewer->team) == 1) {
1953                         return 1;
1954                 }
1955         }
1956
1957         // for AI partially targetable works as fully targetable, except for stealth ship
1958         if (stealth_ship) {
1959                 // if not team targetable, check if within frustrum
1960                 if ( ai_is_stealth_visible(&Objects[viewer->objnum], target) == STEALTH_VISIBLE ) {
1961                         return 1;
1962                 } else {
1963                         return 0;
1964                 }
1965         }
1966
1967         // if not fully targetable by team, check awacs level with viewer
1968         // allow targeting even if only only partially targetable to player
1969         float radar_return = awacs_get_level(target, viewer);
1970         if ( radar_return > 0.4 ) {
1971                 return 1;
1972         } else {
1973                 return 0;
1974         }
1975 }
1976
1977 //      Return number of enemies attacking object objnum
1978 //
1979 // AL 10.26.97: Also include turrets on large ships when couting enemies attacking
1980 int num_enemies_attacking(int objnum)
1981 {
1982         object          *objp;
1983         ship                    *sp;
1984         ship_subsys     *ssp;
1985         ship_obj                *so;
1986         int                     count;
1987
1988         count = 0;
1989
1990         for ( so = GET_FIRST(&Ship_obj_list); so != END_OF_LIST(&Ship_obj_list); so = GET_NEXT(so) ) {
1991                 objp = &Objects[so->objnum];
1992                 Assert(objp->instance != -1);
1993                 sp = &Ships[objp->instance];
1994
1995                 if (Ai_info[sp->ai_index].target_objnum == objnum)
1996                         count++;
1997
1998                 // consider turrets that may be attacking objnum (but only turrets on SIF_BIG_SHIP ships)
1999                 if ( Ship_info[sp->ship_info_index].flags & SIF_BIG_SHIP ) {
2000
2001                         // loop through all the subsystems, check if turret has objnum as a target
2002                         ssp = GET_FIRST(&sp->subsys_list);
2003                         while ( ssp != END_OF_LIST( &sp->subsys_list ) ) {
2004
2005                                 if ( ssp->system_info->type == SUBSYSTEM_TURRET ) {
2006                                         if ( (ssp->turret_enemy_objnum == objnum) && (ssp->current_hits > 0) ) {
2007                                                 count++;
2008                                         }
2009                                 }
2010                                 ssp = GET_NEXT( ssp );
2011                         } // end while
2012                 }
2013         }
2014
2015         return count;
2016 }
2017
2018 //      Get the team to fire on given an object.
2019 int get_enemy_team_mask(int objnum)
2020 {
2021         int     my_team, enemy_team_mask;
2022
2023         my_team = Ships[Objects[objnum].instance].team;
2024
2025         if (Mission_all_attack) {
2026                 //      All teams attack all teams.
2027                 switch (my_team) {
2028                 case TEAM_FRIENDLY:
2029                         enemy_team_mask = TEAM_HOSTILE | TEAM_NEUTRAL | TEAM_TRAITOR;
2030                         break;
2031                 case TEAM_HOSTILE:
2032                         enemy_team_mask = TEAM_FRIENDLY | TEAM_NEUTRAL | TEAM_TRAITOR;
2033                         break;
2034                 case TEAM_NEUTRAL:
2035                         enemy_team_mask = TEAM_FRIENDLY | TEAM_HOSTILE | TEAM_TRAITOR;
2036                         break;
2037                 case TEAM_UNKNOWN:
2038                         enemy_team_mask = TEAM_HOSTILE;
2039                         break;
2040                 case TEAM_TRAITOR:
2041                         enemy_team_mask = TEAM_FRIENDLY | TEAM_NEUTRAL | TEAM_HOSTILE | TEAM_TRAITOR;
2042                         break;
2043                 default:
2044                         enemy_team_mask = TEAM_HOSTILE;
2045                         Int3();                 //      Illegal value for team!
2046                         break;
2047                 }
2048         } else {
2049                 switch (my_team) {
2050                 case TEAM_FRIENDLY:
2051                         enemy_team_mask = TEAM_HOSTILE | TEAM_NEUTRAL | TEAM_TRAITOR;
2052                         break;
2053                 case TEAM_HOSTILE:
2054                         enemy_team_mask = TEAM_FRIENDLY | TEAM_NEUTRAL | TEAM_TRAITOR;
2055                         break;
2056                 case TEAM_NEUTRAL:
2057                         enemy_team_mask = TEAM_FRIENDLY | TEAM_TRAITOR;
2058                         break;
2059                 case TEAM_UNKNOWN:
2060                         enemy_team_mask = TEAM_HOSTILE;
2061                         break;
2062                 case TEAM_TRAITOR:
2063                         enemy_team_mask = TEAM_FRIENDLY | TEAM_NEUTRAL | TEAM_HOSTILE | TEAM_TRAITOR;
2064                         break;
2065                 default:
2066                         enemy_team_mask = TEAM_HOSTILE;
2067                         Int3();                 //      Illegal value for team!
2068                         break;
2069                 }
2070         }
2071
2072         return enemy_team_mask;
2073 }
2074
2075 //      Scan all the ships in *objp's wing.
2076 //      Return the lowest maximum speed of a ship in the wing.
2077 //      Current maximum speed (based on energy settings) is shipp->current_max_speed
2078 float get_wing_lowest_max_speed(object *objp)
2079 {
2080         ship            *shipp;
2081         ai_info *aip;
2082         float           lowest_max_speed;
2083         int             wingnum;
2084         object  *o;
2085         ship_obj        *so;
2086
2087         Assert(objp->type == OBJ_SHIP);
2088         Assert((objp->instance >= 0) && (objp->instance < MAX_OBJECTS));
2089         shipp = &Ships[objp->instance];
2090         Assert((shipp->ai_index >= 0) && (shipp->ai_index < MAX_AI_INFO));
2091         aip = &Ai_info[shipp->ai_index];
2092
2093         wingnum = aip->wing;
2094
2095         lowest_max_speed = shipp->current_max_speed;
2096
2097         if ( wingnum == -1 )
2098                 return lowest_max_speed;
2099
2100         Assert(wingnum >= 0);
2101
2102         for ( so = GET_FIRST(&Ship_obj_list); so != END_OF_LIST(&Ship_obj_list); so = GET_NEXT(so) ) {
2103                 o = &Objects[so->objnum];
2104                 ship    *oshipp = &Ships[o->instance];
2105                 ai_info *oaip = &Ai_info[oshipp->ai_index];
2106
2107                 if ((oaip->mode == AIM_WAYPOINTS) && (oaip->wing == wingnum)) {
2108                         //      Note: If a ship in the wing has a super low max speed, probably its engines are disabled.  So, fly along and
2109                         //      ignore the poor guy.
2110                         float   cur_max = oshipp->current_max_speed;
2111
2112                         if (oaip->ai_flags & AIF_DOCKED) {
2113                                 if (oaip->dock_objnum > -1)
2114                                         if (Objects[oaip->dock_objnum].type == OBJ_SHIP) 
2115                                                 cur_max *= o->phys_info.mass/(o->phys_info.mass + Objects[oaip->dock_objnum].phys_info.mass);
2116                         }
2117                                                         
2118                         if ((oshipp->current_max_speed > 5.0f) && (cur_max < lowest_max_speed)) {
2119                                 lowest_max_speed = cur_max;
2120                         }
2121                 }
2122         }
2123
2124         return lowest_max_speed;
2125 }
2126
2127 /*
2128 //      Tell everyone to ignore object objnum.
2129 void set_global_ignore_object(int objnum)
2130 {
2131         int     i;
2132
2133         Assert(Objects[objnum].type == OBJ_SHIP);
2134
2135         nprintf(("AI", "Telling everyone to ignore object %s\n", Ships[Objects[objnum].instance].ship_name));
2136
2137         for (i=0; i<MAX_IGNORE_OBJECTS; i++) {
2138                 if (Ignore_objects[i].objnum == -1) {
2139                         Ignore_objects[i].objnum = objnum;
2140                         Ignore_objects[i].signature = Objects[objnum].signature;
2141                         break;
2142                 }
2143         }
2144
2145         if (i == MAX_IGNORE_OBJECTS) {
2146                 //      Couldn't find a free slot, but maybe one of these objects has died.
2147                 for (i=0; i<MAX_IGNORE_OBJECTS; i++) {
2148                         int     o = Ignore_objects[i].objnum;
2149                         if (Objects[o].type != OBJ_SHIP)
2150                                 break;          //      Not a ship, so use this slot.
2151                         if (Objects[o].signature != Ignore_objects[i].signature)
2152                                 break;          //      Signatures don't match, so use this slot.
2153                 }
2154
2155                 if (i != MAX_IGNORE_OBJECTS) {
2156                         Ignore_objects[i].objnum = objnum;
2157                         Ignore_objects[i].signature = Objects[objnum].signature;
2158                 } else {
2159                         nprintf(("Warning", "Ignore_objects buffer full.  Stealing a slot to ignore object #%i\n"));
2160                         Int3();
2161
2162                         int     r;
2163
2164                         r = objnum % MAX_IGNORE_OBJECTS;
2165
2166                         Ignore_objects[r].objnum = objnum;
2167                         Ignore_objects[r].signature = Objects[objnum].signature;
2168                 }
2169         }
2170 }
2171
2172 */
2173
2174 //      Determine if object objnum is supposed to be ignored by object with ai_info *aip.
2175 //      Return:
2176 //              TRUE    if objnum is aip->ignore_objnum (and signatures match)
2177 //                              or objnum is in ignore wing
2178 //              FALSE   otherwise
2179 int is_ignore_object(ai_info *aip, int objnum)
2180 {
2181
2182 /*      //      First, scan all objects in global array of objects to be ignored.
2183         for (int i=0; i<MAX_IGNORE_OBJECTS; i++)
2184                 if (Ignore_objects[i].objnum != -1)
2185                         if (objnum == Ignore_objects[i].objnum)
2186                                 if (Objects[Ignore_objects[i].objnum].signature == Ignore_objects[i].signature)
2187                                         return 1;
2188 */
2189
2190         //      Didn't find in global list.  Now check 
2191         if (aip->ignore_objnum == UNUSED_OBJNUM)
2192                 return 0;                                                                       //      Not ignoring anything.
2193         else if (aip->ignore_objnum >= 0) {             //      This means it's ignoring an object, not a wing.
2194                 if (aip->ignore_objnum == objnum) {
2195                         if (Objects[aip->ignore_objnum].signature == aip->ignore_signature) {
2196                                 return 1;
2197                         } else {
2198                                 aip->ignore_objnum = UNUSED_OBJNUM;
2199                                 return 0;
2200                         }
2201                 } else {
2202                         return 0;
2203                 }
2204         } else {                                                                                        //      Ignoring a wing.
2205                 Int3(); // Should never happen.  I thought I removed this behavior! -- MK, 5/17/98
2206                 return 0;
2207 /*              int     ignore_wingnum = -(aip->ignore_objnum + 1);
2208
2209                 Assert(ignore_wingnum < MAX_WINGS);
2210                 Assert(aip->shipnum >= 0);
2211                 return (Ships[Objects[objnum].instance].wingnum == ignore_wingnum);
2212 */      }
2213 }
2214
2215 // -----------------------------------------------------------------------------
2216
2217 // given a ship with bounding box and a point, find the closest point on the bbox
2218 int get_nearest_bbox_point(object *ship_obj, vector *start, vector *box_pt)
2219 {
2220         vector temp, rf_start;
2221         polymodel *pm;
2222         pm = model_get(Ship_info[Ships[ship_obj->instance].ship_info_index].modelnum);
2223
2224         // get start in ship rf
2225         vm_vec_sub(&temp, start, &ship_obj->pos);
2226         vm_vec_rotate(&rf_start, &temp, &ship_obj->orient);
2227
2228         // find box_pt
2229         int inside = project_point_onto_bbox(&pm->mins, &pm->maxs, &rf_start, &temp);
2230
2231         // get box_pt in world rf
2232         vm_vec_unrotate(box_pt, &temp, &ship_obj->orient);
2233         vm_vec_add2(box_pt, &ship_obj->pos);
2234
2235         return inside;
2236 }
2237
2238
2239 typedef struct eval_nearest_objnum {
2240         int     objnum;
2241         object *trial_objp;
2242         int     enemy_team_mask;
2243         int     enemy_wing;
2244         float   range;
2245         int     max_attackers;
2246         int     nearest_objnum;
2247         float   nearest_dist;
2248         int     check_danger_weapon_objnum;
2249 } eval_nearest_objnum;
2250
2251
2252 void evaluate_object_as_nearest_objnum(eval_nearest_objnum *eno)
2253 {
2254         ai_info *aip;
2255         ship_subsys     *attacking_subsystem;
2256
2257         aip = &Ai_info[Ships[Objects[eno->objnum].instance].ai_index];
2258
2259         attacking_subsystem = aip->targeted_subsys;
2260
2261         if ((attacking_subsystem != NULL) || !(eno->trial_objp->flags & OF_PROTECTED)) {
2262                 if ( OBJ_INDEX(eno->trial_objp) != eno->objnum ) {
2263 #ifndef NDEBUG
2264                         if (!Player_attacking_enabled && (eno->trial_objp == Player_obj))
2265                                 return;
2266 #endif
2267                         //      If only supposed to attack ship in a specific wing, don't attack other ships.
2268                         if ((eno->enemy_wing != -1) && (Ships[eno->trial_objp->instance].wingnum != eno->enemy_wing))
2269                                 return;
2270
2271                         //      Don't keep firing at a ship that is in its death throes.
2272                         if (Ships[eno->trial_objp->instance].flags & SF_DYING)
2273                                 return;
2274
2275                         if (is_ignore_object(aip, ((eno->trial_objp)-Objects)))
2276                                 return;
2277
2278                         if (eno->trial_objp->flags & OF_PROTECTED)
2279                                 return;
2280
2281                         if (Ships[eno->trial_objp->instance].flags & SF_ARRIVING)
2282                                 return;
2283
2284                         ship_info *sip = &Ship_info[Ships[eno->trial_objp->instance].ship_info_index];
2285
2286                         if (sip->flags & (SIF_NO_SHIP_TYPE | SIF_NAVBUOY))
2287                                 return;
2288
2289                         if (Ships[eno->trial_objp->instance].team & eno->enemy_team_mask) {
2290                                 float   dist;
2291                                 int     num_attacking;
2292
2293                                 // Allow targeting of stealth in nebula by his firing at me
2294                                 // This is done for a specific ship, not generally.
2295                                 if ( !eno->check_danger_weapon_objnum ) {
2296                                         // check if can be targeted if inside nebula
2297                                         if ( !object_is_targetable(eno->trial_objp, &Ships[Objects[eno->objnum].instance]) ) {
2298                                                 // check if stealth ship is visible, but not "targetable"
2299                                                 if ( !((sip->flags & SIF_STEALTH) && ai_is_stealth_visible(&Objects[eno->objnum], eno->trial_objp)) ) {
2300                                                         return;
2301                                                 }
2302                                         }
2303                                 }
2304
2305                                 // if objnum is BIG or HUGE, find distance to bbox
2306                                 if (sip->flags & (SIF_BIG_SHIP | SIF_HUGE_SHIP)) {
2307                                         vector box_pt;
2308                                         // check if inside bbox
2309                                         int inside = get_nearest_bbox_point(eno->trial_objp, &Objects[eno->objnum].pos, &box_pt);
2310                                         if (inside) {
2311                                                 dist = 10.0f;
2312                                                 // on the box
2313                                         } else {
2314                                                 dist = vm_vec_dist_quick(&Objects[eno->objnum].pos, &box_pt);
2315                                         }
2316                                 } else {
2317                                         dist = vm_vec_dist_quick(&Objects[eno->objnum].pos, &eno->trial_objp->pos);
2318                                 }
2319                                 
2320                                 //      Make it more likely that fighters (or bombers) will be picked as an enemy by scaling up distance for other types.
2321                                 if ((Ship_info[Ships[eno->trial_objp->instance].ship_info_index].flags & (SIF_FIGHTER | SIF_BOMBER))) {
2322                                         dist = dist * 0.5f;
2323                                 }
2324
2325                                 num_attacking = num_enemies_attacking(eno->trial_objp-Objects);
2326                                 if ((sip->flags & (SIF_BIG_SHIP | SIF_HUGE_SHIP)) || (num_attacking < eno->max_attackers)) {
2327                                         if (!(sip->flags & (SIF_BIG_SHIP | SIF_HUGE_SHIP))){
2328                                                 dist *= (float) (num_attacking+2)/2.0f;                         //      prevents lots of ships from attacking same target
2329                                         }
2330
2331                                         if (eno->trial_objp->flags & OF_PLAYER_SHIP){
2332                                                 dist *= 1.0f + (NUM_SKILL_LEVELS - Game_skill_level - 1)/NUM_SKILL_LEVELS;      //      Favor attacking non-players based on skill level.
2333                                         }
2334
2335                                         if (dist < eno->nearest_dist) {
2336                                                 eno->nearest_dist = dist;
2337                                                 eno->nearest_objnum = eno->trial_objp-Objects;
2338                                         }
2339                                 }
2340                         }
2341                 }
2342         }
2343
2344 }
2345
2346
2347 //      Given an object and an enemy team, return the index of the nearest enemy object.
2348 //      Unless aip->targeted_subsys != NULL, don't allow to attack objects
2349 //      with OF_PROTECTED bit set.
2350 //      Ship must be within range "range".
2351 //      Don't attack a ship that already has at least max_attackers attacking it.
2352 int get_nearest_objnum(int objnum, int enemy_team_mask, int enemy_wing, float range, int max_attackers)
2353 {
2354         object  *danger_weapon_objp;
2355         ai_info *aip;
2356         ship_obj        *so;
2357
2358         // initialize eno struct
2359         eval_nearest_objnum eno;
2360         eno.enemy_team_mask = enemy_team_mask;
2361         eno.enemy_wing = enemy_wing;
2362         eno.max_attackers = max_attackers;
2363         eno.objnum = objnum;
2364         eno.range = range;
2365         eno.nearest_dist = range;
2366         eno.nearest_objnum = -1;
2367         eno.check_danger_weapon_objnum = 0;
2368
2369         // go through the list of all ships and evaluate as potential targets
2370         for ( so = GET_FIRST(&Ship_obj_list); so != END_OF_LIST(&Ship_obj_list); so = GET_NEXT(so) ) {
2371                 eno.trial_objp = &Objects[so->objnum];
2372                 evaluate_object_as_nearest_objnum(&eno);
2373
2374         }
2375
2376         // check if danger_weapon_objnum has will show a stealth ship
2377         aip = &Ai_info[Ships[Objects[objnum].instance].ai_index];
2378         if (aip->danger_weapon_objnum >= 0) {
2379                 danger_weapon_objp = &Objects[aip->danger_weapon_objnum];
2380                 // validate weapon
2381                 if (danger_weapon_objp->signature == aip->danger_weapon_signature) {
2382                         Assert(danger_weapon_objp->type == OBJ_WEAPON);
2383                         // check if parent is a ship
2384                         if (danger_weapon_objp->parent >= 0) {
2385                                 if ( is_object_stealth_ship(&Objects[danger_weapon_objp->parent]) ) {
2386                                         // check if stealthy
2387                                         if ( ai_is_stealth_visible(&Objects[objnum], &Objects[danger_weapon_objp->parent]) != STEALTH_FULLY_TARGETABLE ) {
2388                                                 // check if weapon is laser
2389                                                 if (Weapon_info[Weapons[danger_weapon_objp->instance].weapon_info_index].subtype == WP_LASER) {
2390                                                         // check stealth ship by its laser fire
2391                                                         eno.check_danger_weapon_objnum = 1;
2392                                                         eno.trial_objp = &Objects[danger_weapon_objp->parent];
2393                                                         evaluate_object_as_nearest_objnum(&eno);
2394                                                 }
2395                                         }
2396                                 }
2397                         }
2398                 }
2399         }
2400
2401         //      If only looking for target in certain wing and couldn't find anything in
2402         //      that wing, look for any object.
2403         if ((eno.nearest_objnum == -1) && (enemy_wing != -1)) {
2404                 return get_nearest_objnum(objnum, enemy_team_mask, -1, range, max_attackers);
2405         }
2406
2407         return eno.nearest_objnum;
2408 }
2409
2410 //      Given an object and an enemy team, return the index of the nearest enemy object.
2411 //      Unlike find_enemy or find_nearest_objnum, this doesn't care about things like the protected flag or number
2412 //      of enemies attacking.
2413 //      It is used to find the nearest enemy to determine things like whether to rearm.
2414 int find_nearby_hostile(int objnum, int enemy_team_mask, float range, int *count)
2415 {
2416         int             nearest_objnum;
2417         float           nearest_dist;
2418         object  *objp;
2419         ai_info *aip;
2420         ship_obj        *so;
2421
2422         nearest_objnum = -1;
2423         nearest_dist = range;
2424
2425         aip = &Ai_info[Ships[Objects[objnum].instance].ai_index];
2426
2427         *count = 0;
2428
2429         for ( so = GET_FIRST(&Ship_obj_list); so != END_OF_LIST(&Ship_obj_list); so = GET_NEXT(so) ) {
2430                 objp = &Objects[so->objnum];
2431
2432                 if ( OBJ_INDEX(objp) != objnum ) {
2433                         if (Ships[objp->instance].flags & SF_DYING)
2434                                 continue;
2435
2436                         if (Ship_info[Ships[objp->instance].ship_info_index].flags & (SIF_NO_SHIP_TYPE | SIF_NAVBUOY))
2437                                 continue;
2438
2439                         if (Ships[objp->instance].team & enemy_team_mask) {
2440                                 float   dist;
2441
2442                                 dist = vm_vec_dist_quick(&Objects[objnum].pos, &objp->pos) - objp->radius*0.75f;
2443                                 
2444                                 if (dist < range) {
2445                                         (*count)++;
2446
2447                                         if (dist < nearest_dist) {
2448                                                 nearest_dist = dist;
2449                                                 nearest_objnum = objp-Objects;
2450                                         }
2451                                 }
2452                         }
2453                 }
2454         }
2455
2456         return nearest_objnum;
2457 }
2458
2459 // return !0 if objp can be considered for a turret target, 0 otherwise
2460 // input:       objp                            =>      object that turret is considering as an enemy
2461 //                              turret_parent   =>      object index for ship that turret sits on
2462 int valid_turret_enemy(object *objp, object *turret_parent)
2463 {
2464         if ( objp == turret_parent ) {
2465                 return 0;
2466         }
2467
2468         if ( objp->type == OBJ_ASTEROID ) {
2469                 return 1;
2470         }
2471
2472         if ( (objp->type == OBJ_SHIP) ) {
2473                 ship *shipp;
2474                 shipp = &Ships[objp->instance];
2475
2476                 // don't fire at ships with protected bit set!!!
2477                 if ( objp->flags & OF_PROTECTED ) {
2478                         return 0;
2479                 }
2480
2481                 if ( !(Ship_info[shipp->ship_info_index].flags & SIF_DO_COLLISION_CHECK)) {
2482                         return 0;
2483                 }
2484
2485                 if (shipp->flags & SF_ARRIVING) {
2486                         return 0;
2487                 }
2488
2489                 return 1;
2490         }
2491
2492         if ( objp->type == OBJ_WEAPON ) {
2493                 if ( Weapon_info[Weapons[objp->instance].weapon_info_index].wi_flags & WIF_BOMB ) {
2494                         if ( obj_team(turret_parent) != Weapons[objp->instance].team ) {
2495                                 return 1;
2496                         }
2497                 }
2498         }
2499
2500         return 0;
2501 }
2502
2503 // return 1 if objp is in fov of the specified turret, tp.  Otherwise return 0.
2504 //      dist = distance from turret to center point of object
2505 int object_in_turret_fov(object *objp, model_subsystem *tp, vector *tvec, vector *tpos, float dist)
2506 {
2507         vector  v2e;
2508         float           dot;
2509         vm_vec_normalized_dir(&v2e, &objp->pos, tpos);
2510         dot = vm_vec_dot(&v2e, tvec);
2511
2512         dot += objp->radius / (dist + objp->radius);
2513
2514         if ( dot >= tp->turret_fov ) {
2515                 return 1;
2516         }
2517
2518         return 0;
2519 }
2520
2521 // return 1 if bomb_objp is headed towards ship_objp
2522 int bomb_headed_towards_ship(object *bomb_objp, object *ship_objp)
2523 {
2524         float           dot;
2525         vector  bomb_to_ship_vector;
2526
2527         vm_vec_normalized_dir(&bomb_to_ship_vector, &ship_objp->pos, &bomb_objp->pos);
2528         dot = vm_vec_dot(&bomb_objp->orient.fvec, &bomb_to_ship_vector);
2529
2530         if ( dot > 0 ) {
2531                 return 1;
2532         }
2533
2534         return 0;
2535 }
2536
2537 // nubmer of live turrets with target_objnum 
2538 int num_turrets_attacking(object *turret_parent, int target_objnum) 
2539 {
2540         ship_subsys *ss;
2541         ship *shipp;
2542         int count = 0;
2543         shipp = &Ships[turret_parent->instance];
2544
2545         Assert(turret_parent->type == OBJ_SHIP);
2546         Assert(Objects[target_objnum].type == OBJ_SHIP);
2547
2548         for (ss=GET_FIRST(&shipp->subsys_list); ss!=END_OF_LIST(&shipp->subsys_list); ss=GET_NEXT(ss)) {
2549                 // check if subsys is alive
2550                 if (ss->current_hits <= 0.0f) {
2551                         continue;
2552                 }
2553
2554                 // check if it's a turret
2555                 if (ss->system_info->type != SUBSYSTEM_TURRET) {
2556                         continue;
2557                 }
2558
2559                 // if the turret is locked
2560                 if(ss->weapons.flags & SW_FLAG_TURRET_LOCK){
2561                         continue;
2562                 }               
2563
2564                 // check if turret is targeting target_objnum
2565                 if (ss->turret_enemy_objnum == target_objnum) {
2566                         count++;
2567                 }
2568         }
2569
2570         return count;
2571 }
2572
2573 float Lethality_range_const = 2.0f;
2574 DCF(lethality_range, "N for modifying range: 1 / (1+N) at 100")
2575 {
2576         dc_get_arg(ARG_FLOAT);
2577         Lethality_range_const = Dc_arg_float;
2578 }
2579
2580 float Player_lethality_bump[NUM_SKILL_LEVELS] = {
2581         // 0.0f, 5.0f, 10.0f, 25.0f, 40.0f
2582         0.0f, 0.0f, 0.0f, 0.0f, 0.0f
2583 };
2584
2585 // evaluate obj as posssible target for turret
2586 void evaluate_obj_as_target(object *objp, eval_enemy_obj_struct *eeo)
2587 {
2588         object  *turret_parent_obj = &Objects[eeo->turret_parent_objnum];
2589         ship            *shipp;
2590         model_subsystem *tp = eeo->turret_subsys->system_info;
2591         float dist;
2592
2593         // Don't look for bombs when weapon system is not ok
2594         if (objp->type == OBJ_WEAPON && !eeo->weapon_system_ok) {
2595                 return;
2596         }
2597
2598         if ( !valid_turret_enemy(objp, turret_parent_obj) ) {
2599                 return;
2600         }
2601
2602 #ifndef NDEBUG
2603         if (!Player_attacking_enabled && (objp == Player_obj)) {
2604                 return;
2605         }
2606 #endif
2607
2608         if ( objp->type == OBJ_SHIP ) {
2609                 shipp = &Ships[objp->instance];
2610
2611                 // check on enemy team
2612                 if ( !(shipp->team & eeo->enemy_team_mask) ) {
2613                         return;
2614                 }
2615
2616                 // check if protected
2617                 if (objp->flags & OF_PROTECTED) {
2618                         return;
2619                 }
2620
2621                 // check if beam protected
2622                 if (Weapon_info[tp->turret_weapon_type].wi_flags & WIF_BEAM) {
2623                         if (objp->flags & OF_BEAM_PROTECTED) {
2624                                 return;
2625                         }
2626                 }
2627
2628                 if (eeo->big_only_flag) {
2629                         if (!(Ship_info[shipp->ship_info_index].flags & (SIF_BIG_SHIP | SIF_HUGE_SHIP))) {
2630                                 return;
2631                         }
2632                 }
2633
2634                 // check if     turret flagged to only target tagged ships
2635                 if ( (eeo->turret_subsys->weapons.flags & SW_FLAG_TAGGED_ONLY) && !ship_is_tagged(objp) ) {
2636                         return;
2637                 }
2638
2639                 // check if valid target in nebula
2640                 if ( !object_is_targetable(objp, &Ships[Objects[eeo->turret_parent_objnum].instance]) ) {
2641                         // BYPASS ocassionally for stealth
2642                         int try_anyway = FALSE;
2643                         if ( is_object_stealth_ship(objp) ) {
2644                                 float turret_stealth_find_chance = 0.5f;
2645                                 float speed_mod = -0.1f + vm_vec_mag_quick(&objp->phys_info.vel) / 70.0f;
2646                                 if (frand() > (turret_stealth_find_chance + speed_mod)) {
2647                                         try_anyway = TRUE;
2648                                 }
2649                         }
2650
2651                         if (!try_anyway) {
2652                                 return;
2653                         }
2654                 }
2655
2656         } else {
2657                 shipp = NULL;
2658         }
2659
2660         // modify dist for BIG|HUGE, getting closest point on bbox, if not inside
2661         dist = vm_vec_dist_quick(eeo->tpos, &objp->pos) - objp->radius;
2662         if (dist < 0.0f) {
2663                 dist = 0.0f;
2664         }
2665
2666         // check if object is a bomb attacking the turret parent
2667         // check if bomb is homing on the turret parent ship
2668         if (objp->type == OBJ_WEAPON) {
2669                 if ( Weapons[objp->instance].homing_object == &Objects[eeo->turret_parent_objnum] ) {
2670                         if ( dist < eeo->nearest_homing_bomb_dist ) {
2671                                 if ( (eeo->current_enemy == -1) || object_in_turret_fov(objp, tp, eeo->tvec, eeo->tpos, dist + objp->radius) ) {
2672                                         eeo->nearest_homing_bomb_dist = dist;
2673                                         eeo->nearest_homing_bomb_objnum = OBJ_INDEX(objp);
2674                                 }
2675                         }
2676                 // if not homing, check if bomb is flying towards ship
2677                 } else if ( bomb_headed_towards_ship(objp, &Objects[eeo->turret_parent_objnum]) ) {
2678                         if ( dist < eeo->nearest_bomb_dist ) {
2679                                 if ( (eeo->current_enemy == -1) || object_in_turret_fov(objp, tp, eeo->tvec, eeo->tpos, dist + objp->radius) ) {
2680                                         eeo->nearest_bomb_dist = dist;
2681                                         eeo->nearest_bomb_objnum = OBJ_INDEX(objp);
2682                                 }
2683                         }
2684                 }
2685         } // end weapon section
2686
2687         // maybe recalculate dist for big or huge ship
2688 //      if (shipp && (Ship_info[shipp->ship_info_index].flags & (SIF_BIG_SHIP | SIF_HUGE_SHIP))) {
2689 //              fvi_ray_boundingbox(min, max, start, direction, hit);
2690 //              dist = vm_vec_dist_quick(hit, tvec);
2691 //      }
2692
2693         // check for nearest attcker
2694         if ( (shipp) && (dist < eeo->weapon_travel_dist) ) {
2695                 ai_info *aip = &Ai_info[shipp->ai_index];
2696
2697                 // modify distance based on number of turrets from my ship attacking enemy (add 10% per turret)
2698                 // dist *= (num_enemies_attacking(OBJ_INDEX(objp))+2)/2;        //      prevents lots of ships from attacking same target
2699                 int num_att_turrets = num_turrets_attacking(turret_parent_obj, OBJ_INDEX(objp));
2700                 dist *= (1.0f + 0.1f*num_att_turrets);
2701
2702                 // return if we're over the cap
2703                 int max_turrets = 3 + Game_skill_level * Game_skill_level;
2704                 if (num_att_turrets > max_turrets) {
2705                         return;
2706                 }
2707
2708                 // modify distance based on lethality of objp to my ship
2709                 float active_lethality = aip->lethality;
2710                 if (objp->flags & OF_PLAYER_SHIP) {
2711                         active_lethality += Player_lethality_bump[Game_skill_level];
2712                 }
2713
2714                 dist /= (1.0f + 0.01f*Lethality_range_const*active_lethality);
2715
2716                 // Make level 2 tagged ships more likely to be targeted
2717                 if (shipp->level2_tag_left > 0.0f) {
2718                         dist *= 0.3f;
2719                 }
2720
2721                 // check if objp is targeting the turret's ship, or if objp has just hit the turret's ship
2722                 if ( aip->target_objnum == eeo->turret_parent_objnum || aip->last_objsig_hit == Objects[eeo->turret_parent_objnum].signature ) {
2723                         // A turret will always target a ship that is attacking itself... self-preservation!
2724                         if ( aip->targeted_subsys == eeo->turret_subsys ) {
2725                                 dist *= 0.5f;   // highest priority
2726                         }
2727                 }
2728
2729                 // maybe update nearest attacker
2730                 if ( dist < eeo->nearest_attacker_dist ) {
2731                         if ( (eeo->current_enemy == -1) || object_in_turret_fov(objp, tp, eeo->tvec, eeo->tpos, dist + objp->radius) ) {
2732                                 // 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));
2733                                 eeo->nearest_attacker_dist = dist;
2734                                 eeo->nearest_attacker_objnum = OBJ_INDEX(objp);
2735                         }
2736                 }
2737         } // end ship section
2738 }
2739
2740 // return 0 only if objnum is beam protected and turret is beam turret
2741 int is_target_beam_valid(ship_subsys *turret_subsys, int objnum)
2742 {
2743         // check if turret has beam weapon
2744         model_subsystem *tp = turret_subsys->system_info;
2745
2746         if (Weapon_info[tp->turret_weapon_type].wi_flags & WIF_BEAM) {
2747                 if (Objects[objnum].flags & OF_BEAM_PROTECTED) {
2748                         return 0;
2749                 }
2750
2751                 if (Weapon_info[tp->turret_weapon_type].wi_flags & WIF_HUGE) {
2752                         if (Objects[objnum].type == OBJ_SHIP && !(Ship_info[Ships[Objects[objnum].instance].ship_info_index].flags & (SIF_BIG_SHIP|SIF_HUGE_SHIP)) ) {
2753                                 return 0;
2754                         }
2755                 }
2756         }
2757
2758         return 1;
2759 }
2760
2761
2762 //      Given an object and an enemy team, return the index of the nearest enemy object.
2763 //
2764 // input:
2765 //                              turret_parent_objnum    => parent objnum for the turret
2766 //                              turret_subsys                   => pointer to system_info for the turret subsystem
2767 //                              enemy_team_mask         => OR'ed TEAM_ flags for the enemy of the turret parent ship
2768 //                              tpos                                            => position of turret (world coords)
2769 //                              tvec                                            => forward vector of turret (world coords)
2770 //                              current_enemy                   =>      objnum of current turret target
2771 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)
2772 {
2773         float                                   weapon_travel_dist;
2774         int                                     weapon_system_ok;
2775         object                          *objp;
2776         model_subsystem *tp;
2777         eval_enemy_obj_struct eeo;
2778
2779         // list of stuff to go thru
2780         ship_obj                *so;
2781         missile_obj *mo;
2782
2783         tp = turret_subsys->system_info;
2784         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);
2785
2786         // Set flag based on strength of weapons subsystem.  If weapons subsystem is destroyed, don't let turrets fire at bombs
2787         weapon_system_ok = 0;
2788         if ( ship_get_subsystem_strength( &Ships[Objects[turret_parent_objnum].instance], SUBSYSTEM_WEAPONS ) > 0 ) {
2789                 weapon_system_ok = 1;
2790         }
2791
2792         // Initialize eeo struct.
2793         eeo.turret_parent_objnum = turret_parent_objnum;
2794         eeo.weapon_system_ok = weapon_system_ok;
2795         eeo.weapon_travel_dist = weapon_travel_dist;
2796         eeo.big_only_flag = big_only_flag;
2797         eeo.enemy_team_mask = enemy_team_mask;
2798         eeo.current_enemy = current_enemy;
2799         eeo.tpos = tpos;
2800         eeo.tvec = tvec;
2801         eeo.turret_subsys = turret_subsys;
2802
2803         eeo.nearest_attacker_dist = 99999.0f;
2804         eeo.nearest_attacker_objnum = -1;
2805
2806         eeo.nearest_homing_bomb_dist = 99999.0f;
2807         eeo.nearest_homing_bomb_objnum = -1;
2808
2809         eeo.nearest_bomb_dist = 99999.0f;
2810         eeo.nearest_bomb_objnum = -1;
2811
2812         eeo.nearest_dist = 99999.0f;
2813         eeo.nearest_objnum = -1;
2814
2815
2816         // Missile_obj_list
2817         for( mo = GET_FIRST(&Missile_obj_list); mo != END_OF_LIST(&Missile_obj_list); mo = GET_NEXT(mo) ) {
2818                 objp = &Objects[mo->objnum];
2819                 evaluate_obj_as_target(objp, &eeo);
2820         }
2821         // highest priority
2822         if ( eeo.nearest_homing_bomb_objnum != -1 ) {                                   // highest priority is an incoming homing bomb
2823                 return eeo.nearest_homing_bomb_objnum;
2824         } else if ( eeo.nearest_bomb_objnum != -1 ) {                                   // next highest priority is an incoming dumbfire bomb
2825                 return eeo.nearest_bomb_objnum;
2826         }
2827
2828
2829         // Ship_used_list
2830         for ( so = GET_FIRST(&Ship_obj_list); so != END_OF_LIST(&Ship_obj_list); so = GET_NEXT(so) ) {
2831                 objp = &Objects[so->objnum];
2832                 evaluate_obj_as_target(objp, &eeo);
2833         }
2834
2835         Assert(eeo.nearest_attacker_objnum < 0 || is_target_beam_valid(turret_subsys, eeo.nearest_attacker_objnum));
2836                 // next highest priority is attacking ship
2837         if ( eeo.nearest_attacker_objnum != -1 ) {                      // next highest priority is an attacking ship
2838                 return eeo.nearest_attacker_objnum;
2839          }
2840
2841
2842 #ifndef FS2_DEMO
2843                 asteroid_obj *ao;
2844         // Asteroid_obj_list
2845         for( ao = GET_FIRST(&Asteroid_obj_list); ao != END_OF_LIST(&Asteroid_obj_list); ao = GET_NEXT(ao) ) {
2846                 objp = &Objects[ao->objnum];
2847                 evaluate_obj_as_target(objp, &eeo);
2848         }
2849 #endif
2850
2851         return eeo.nearest_objnum;                                                                              // lowest priority is the closest enemy objnum
2852 }
2853
2854 //      Return timestamp until a ship can find an enemy.
2855 //      Yes, no parameters.  Based solely on skill level.
2856 int get_enemy_timestamp()
2857 {
2858         return (NUM_SKILL_LEVELS - Game_skill_level) * ( (myrand() % 500) + 500);
2859 }
2860
2861 // -------------------------------------------------------------------
2862 //      Return objnum if enemy found, else return -1;
2863 //      Don't attack a ship that already has at least max_attackers attacking it.
2864 int find_enemy(int objnum, float range, int max_attackers)
2865 {
2866         int     enemy_team_mask;
2867
2868         enemy_team_mask = get_enemy_team_mask(objnum);
2869
2870         //      if target_objnum != -1, use that as goal.
2871         ai_info *aip = &Ai_info[Ships[Objects[objnum].instance].ai_index];
2872         if (timestamp_elapsed(aip->choose_enemy_timestamp)) {
2873                 aip->choose_enemy_timestamp = timestamp(get_enemy_timestamp());
2874                 if (aip->target_objnum != -1) {
2875                         int     target_objnum = aip->target_objnum;
2876
2877                         // DKA don't undo object as target in nebula missions.
2878                         // This could cause attack on ship on fringe on nebula to stop if attackee moves our of nebula range.  (BAD)
2879                         if ( (Objects[target_objnum].signature == aip->target_signature) ) {
2880                                 if (Ships[Objects[target_objnum].instance].team & enemy_team_mask) {
2881                                         if (!(Objects[target_objnum].flags & OF_PROTECTED)) {
2882                                                 // nprintf(("AI", "Frame %i: Object %i resuming goal of object %i\n", AI_FrameCount, objnum, target_objnum));
2883                                                 return target_objnum;
2884                                         }
2885                                 }
2886                         } else {
2887                                 aip->target_objnum = -1;
2888                                 aip->target_signature = -1;
2889                         }
2890                 }
2891                 return get_nearest_objnum(objnum, enemy_team_mask, aip->enemy_wing, range, max_attackers);
2892         } else {
2893                 aip->target_objnum = -1;
2894                 aip->target_signature = -1;
2895                 return -1;
2896         }
2897
2898 }
2899
2900 int Use_parent_target = 0;
2901 DCF_BOOL(use_parent_target, Use_parent_target)
2902
2903 // -------------------------------------------------------------------
2904 //      Return objnum if enemy found, else return -1;
2905 //
2906 // input:
2907 //                              turret_subsys   => pointer to turret subsystem
2908 //                              objnum                  => parent objnum for the turret
2909 //                              tpos                            => position of turret (world coords)
2910 //                              tvec                            => forward vector of turret (world coords)
2911 //                              current_enemy   =>      objnum of current turret target
2912 int find_turret_enemy(ship_subsys *turret_subsys, int objnum, vector *tpos, vector *tvec, int current_enemy, float fov, int big_only_flag = 0)
2913 {
2914         int                                     enemy_team_mask, enemy_objnum;
2915         model_subsystem *tp;
2916         ship_info                       *sip;
2917
2918         tp = turret_subsys->system_info;
2919         enemy_team_mask = get_enemy_team_mask(objnum);
2920
2921         //      If a small ship and target_objnum != -1, use that as goal.
2922         ai_info *aip = &Ai_info[Ships[Objects[objnum].instance].ai_index];
2923         sip = &Ship_info[Ships[Objects[objnum].instance].ship_info_index];
2924
2925         if ((sip->flags & SIF_SMALL_SHIP) && (aip->target_objnum != -1)) {
2926                 int target_objnum = aip->target_objnum;
2927
2928                 if (Objects[target_objnum].signature == aip->target_signature) {
2929                         if (Ships[Objects[target_objnum].instance].team & enemy_team_mask) {
2930                                 if ( !(Objects[target_objnum].flags & OF_PROTECTED) ) {         // check this flag as well.
2931                                         // nprintf(("AI", "Frame %i: Object %i resuming goal of object %i\n", AI_FrameCount, objnum, target_objnum));
2932                                         return target_objnum;
2933                                 }
2934                         }
2935                 } else {
2936                         aip->target_objnum = -1;
2937                         aip->target_signature = -1;
2938                 }
2939         // Not small or small with target objnum
2940         } else {
2941                 // maybe use aip->target_objnum as next target
2942                 if ((frand() < 0.8f) && (aip->target_objnum != -1) && Use_parent_target) {
2943
2944                         //check if aip->target_objnum is valid target
2945                         int target_flags = Objects[aip->target_objnum].flags;
2946                         if ( target_flags & OF_PROTECTED ) {
2947                                 // AL 2-27-98: why is a protected ship being targeted?
2948                                 set_target_objnum(aip, -1);
2949                                 return -1;
2950                         }
2951
2952                         // maybe use ship target_objnum if valid for turret
2953                         // check for beam weapon and beam protected
2954                         if ( !((target_flags & OF_BEAM_PROTECTED) && (Weapon_info[tp->turret_weapon_type].wi_flags & WIF_BEAM)) ) {
2955                                 if ( Objects[aip->target_objnum].type == OBJ_SHIP ) {
2956                                         // check for huge weapon and huge ship
2957                                         if ( !big_only_flag || (Ship_info[Ships[Objects[aip->target_objnum].instance].ship_info_index].flags & (SIF_BIG_SHIP|SIF_HUGE_SHIP)) ) {
2958                                                 // check for tagged only and tagged ship
2959                                                 if ( (turret_subsys->weapons.flags & SW_FLAG_TAGGED_ONLY) && ship_is_tagged(&Objects[aip->target_objnum]) ) {
2960                                                         // select new target if aip->target_objnum is out of field of view
2961                                                         vector v2e;
2962                                                         float dot, dist;
2963                                                         dist = vm_vec_normalized_dir(&v2e, &Objects[aip->target_objnum].pos, tpos);
2964                                                         dot = vm_vec_dot(&v2e, tvec);
2965                                                         //      MODIFY FOR ATTACKING BIG SHIP
2966                                                         // dot += (0.5f * Objects[aip->target_objnum].radius / dist);
2967                                                         if (dot > fov) {
2968                                                                 return aip->target_objnum;
2969                                                         }
2970                                                 }
2971                                         }
2972                                 }
2973                         }
2974                 }
2975         }
2976
2977         enemy_objnum = get_nearest_turret_objnum(objnum, turret_subsys, enemy_team_mask, tpos, tvec, current_enemy, big_only_flag);
2978         if ( enemy_objnum >= 0 ) {
2979                 Assert( !((Objects[enemy_objnum].flags & OF_BEAM_PROTECTED) && (Weapon_info[tp->turret_weapon_type].wi_flags & WIF_BEAM)) );
2980                 if ( Objects[enemy_objnum].flags & OF_PROTECTED ) {
2981                         Int3();
2982                         enemy_objnum = aip->target_objnum;
2983                 }
2984         }
2985
2986         return enemy_objnum;
2987 }
2988
2989 //      If issued an order to a ship that's awaiting repair, abort that process.
2990 //      However, do not abort process for an object that is currently being repaired -- let it finish.
2991 void ai_set_goal_maybe_abort_dock(object *objp, ai_info *aip)
2992 {
2993         if (aip->ai_flags & AIF_AWAITING_REPAIR) {
2994                 object  *repair_obj;
2995
2996                 if (aip->dock_objnum == -1) {
2997                         repair_obj = NULL;
2998                 } else {
2999                         repair_obj = &Objects[aip->dock_objnum];
3000                 }
3001                 ai_do_objects_repairing_stuff( objp, repair_obj, REPAIR_INFO_ABORT );
3002         }
3003         aip->next_rearm_request_timestamp = timestamp(NEXT_REARM_TIMESTAMP);    //      Might request again after 30 seconds.
3004 }
3005
3006 void force_avoid_player_check(object *objp, ai_info *aip)
3007 {
3008         if (Ships[objp->instance].team == Player_ship->team){
3009                 aip->avoid_check_timestamp = timestamp(0);              //      Force a check for collision next frame.
3010         }
3011 }
3012
3013 //      --------------------------------------------------------------------------
3014 //      Set *attacked as object to attack for object *attacker
3015 //      If attacked == NULL, then attack any enemy object.
3016 //      Attack point *rel_pos on object.  This is for supporting attacking subsystems.
3017 void ai_attack_object(object *attacker, object *attacked, int priority, ship_subsys *ssp)
3018 {
3019         ai_info *aip;
3020
3021         Assert(attacker != NULL);
3022         Assert(attacker->instance != -1);
3023         Assert(Ships[attacker->instance].ai_index != -1);
3024
3025         aip = &Ai_info[Ships[attacker->instance].ai_index];
3026         force_avoid_player_check(attacker, aip);
3027
3028         aip->ok_to_target_timestamp = timestamp(0);             //      Guarantee we can target.
3029
3030 //      if (!strnicmp(Ships[attacker->instance].ship_name, NOX("Kami"), 4)) {
3031 //              aip->ai_flags |= AIF_KAMIKAZE;
3032 //              aip->ai_flags |= AIF_NO_DYNAMIC;
3033 //      }
3034
3035         if (attacker == attacked) {
3036                 Int3();         //      Bogus!  Who tried to get me to attack myself!  Trace out and fix!
3037                 return;
3038         }
3039
3040         //      Only set to chase if a fighter or bomber, otherwise just return.
3041         if (!(Ship_info[Ships[attacker->instance].ship_info_index].flags & SIF_SMALL_SHIP)) {
3042 //              nprintf(("AI","Note: AI ship %s refusing to set AI mode to AIM_CHASE\n", Ships[attacker->instance].ship_name));
3043 //              return;
3044                 nprintf(("AI", "AI ship %s is large ship ordered to attack %s\n", Ships[attacker->instance].ship_name, Ships[attacked->instance].ship_name));
3045         }
3046
3047         //      This is how "engage enemy" gets processed
3048         if (attacked == NULL) {
3049                 aip->choose_enemy_timestamp = timestamp(0);
3050                 // nebula safe
3051                 set_target_objnum(aip, find_enemy(attacker-Objects, 99999.9f, 4));
3052         } else {
3053                 // check if we can see atacked in nebula
3054                 if (aip->target_objnum != attacked - Objects) {
3055                         aip->aspect_locked_time = 0.0f;
3056                 }
3057                 set_target_objnum(aip, attacked - Objects);
3058         }
3059
3060         ai_set_goal_maybe_abort_dock(attacker, aip);
3061         aip->ok_to_target_timestamp = timestamp(DELAY_TARGET_TIME);     //      No dynamic targeting for 7 seconds.
3062
3063         if (is_ignore_object(aip, aip->target_objnum)) {
3064                 aip->ignore_objnum = UNUSED_OBJNUM;
3065         }
3066
3067         aip->mode = AIM_CHASE;
3068         aip->submode = SM_ATTACK;       // AL 12-15-97: need to set submode?  I got an assert() where submode was bogus
3069                                                                                 //                                       for AIM_CHASE... it may have been not set correctly here
3070         if (ssp == NULL) {
3071                 set_targeted_subsys(aip, NULL, -1);
3072                 if (aip->target_objnum != -1) {
3073                         //nprintf(("AI", "Unprotecting ship %s\n", Ships[Objects[aip->target_objnum].instance].ship_name));
3074                         Objects[aip->target_objnum].flags &= ~OF_PROTECTED;     //      If ship had been protected, unprotect it.
3075                 }
3076         } else {
3077                 Int3(); //      Not supported yet!
3078         }
3079 }
3080
3081 //      --------------------------------------------------------------------------
3082 //      Set *attacked as object to attack for object *attacker
3083 //      Attack point *rel_pos on object.  This is for supporting attacking subsystems.
3084 void ai_attack_wing(object *attacker, int wingnum, int priority)
3085 {
3086         ai_info *aip;
3087
3088         Assert(attacker != NULL);
3089         Assert(attacker->instance != -1);
3090         Assert(Ships[attacker->instance].ai_index != -1);
3091
3092         aip = &Ai_info[Ships[attacker->instance].ai_index];
3093
3094         aip->enemy_wing = wingnum;
3095         aip->mode = AIM_CHASE;
3096         aip->submode = SM_ATTACK;       // AL 12-15-97: need to set submode?  I got an assert() where submode was bogus
3097                                                                                 //                                       for AIM_CHASE... it may have been not set correctly here
3098
3099         aip->ok_to_target_timestamp = timestamp(0);             //      Guarantee we can target.
3100
3101         int count = Wings[wingnum].current_count;
3102         if (count > 0) {
3103                 int     index;
3104
3105                 index = (int) (frand() * count);
3106
3107                 if (index >= count)
3108                         index = 0;
3109
3110                 set_target_objnum(aip, Ships[Wings[wingnum].ship_index[index]].objnum);
3111
3112                 ai_set_goal_maybe_abort_dock(attacker, aip);
3113                 aip->ok_to_target_timestamp = timestamp(DELAY_TARGET_TIME);     //      No dynamic targeting for 7 seconds.
3114         }
3115 }
3116
3117 //      --------------------------------------------------------------------------
3118 //      Set *evaded as object for *evader to evade.
3119 void ai_evade_object(object *evader, object *evaded, int priority)
3120 {
3121         ai_info *aip;
3122
3123         Assert(evader != NULL);
3124         Assert(evaded != NULL);
3125         Assert(evader->instance != -1);
3126         Assert(Ships[evader->instance].ai_index != -1);
3127
3128         if (evaded == evader) {
3129                 Int3(); //      Bogus!  Who tried to get me to evade myself!  Trace out and fix!
3130                 return;
3131         }
3132
3133         aip = &Ai_info[Ships[evader->instance].ai_index];
3134
3135         set_target_objnum(aip, evaded - Objects);
3136         aip->mode = AIM_EVADE;
3137
3138 }
3139
3140 //      Ignore some object without changing mode.
3141 void ai_ignore_object(object *ignorer, object *ignored, int priority)
3142 {
3143         ai_info *aip;
3144
3145         Assert(ignorer != NULL);
3146         Assert(ignored != NULL);
3147         Assert(ignorer->instance != -1);
3148         Assert(Ships[ignorer->instance].ai_index != -1);
3149         Assert(ignorer != ignored);
3150
3151         aip = &Ai_info[Ships[ignorer->instance].ai_index];
3152
3153         //      MK, 5/17/98, removing ignoring of wings.
3154         //      It's too confusing.  It often causes mysterious behavior in which fighters unexpectedly refuse to attack anything.
3155 /*      if (Ships[ignored->instance].wingnum > -1) {
3156                 int wingnum, i;
3157
3158                 wingnum = Ships[ignored->instance].wingnum;
3159                 aip->ignore_objnum = -(wingnum+1);
3160                 // set protected bit for each ship in a wing
3161                 //      MK, 4/23/98: Only set for fighters if they are the original "ignored" object
3162                 for (i = 0; i < Wings[wingnum].current_count; i++ ) {
3163                         object  *objp;
3164
3165                         objp = &Objects[Ships[Wings[wingnum].ship_index[i]].objnum];
3166                         if (objp != ignored) {
3167                                 if (Ship_info[Ships[objp->instance].ship_info_index].flags & (SIF_FIGHTER | SIF_BOMBER))
3168                                         continue;
3169                         }
3170
3171                         Objects[Ships[Wings[wingnum].ship_index[i]].objnum].flags |= OF_PROTECTED;
3172                 }
3173
3174         } else {
3175         */ {
3176                 aip->ignore_objnum = ignored - Objects;
3177                 aip->ignore_signature = ignored->signature;
3178                 aip->ai_flags &= ~AIF_TEMPORARY_IGNORE;
3179                 ignored->flags |= OF_PROTECTED;                                 // set protected bit of ignored ship.
3180         }
3181
3182 }
3183
3184 //      Ignore some object without changing mode.
3185 void ai_ignore_wing(object *ignorer, int wingnum, int priority)
3186 {
3187         ai_info *aip;
3188
3189         Assert(ignorer != NULL);
3190         Assert(ignorer->instance != -1);
3191         Assert(Ships[ignorer->instance].ai_index != -1);
3192         Assert((wingnum >= 0) && (wingnum < MAX_WINGS));
3193
3194         aip = &Ai_info[Ships[ignorer->instance].ai_index];
3195
3196         aip->ignore_objnum = -(wingnum +1);
3197         aip->ai_flags &= ~AIF_TEMPORARY_IGNORE;
3198 }
3199
3200
3201 //      Add a path point in the global buffer Path_points.
3202 //      modify_index = index in Path_points at which to store path point.
3203 //      If modify_index == -1, then create a new point.
3204 //      If a new point is created (ie, modify_index == -1), then Ppfp is updated.
3205 void add_path_point(vector *pos, int path_num, int path_index, int modify_index)
3206 {
3207         pnode   *pnp;
3208
3209         if (modify_index == -1) {
3210                 Assert(Ppfp-Path_points < MAX_PATH_POINTS-1);
3211                 pnp = Ppfp;
3212                 Ppfp++;
3213         } else {
3214                 Assert((modify_index >= 0) && (modify_index < MAX_PATH_POINTS-1));
3215                 pnp = &Path_points[modify_index];
3216         }
3217
3218         pnp->pos = *pos;
3219         pnp->path_num = path_num;
3220         pnp->path_index = path_index;
3221 }
3222
3223 //      Given two points on a sphere, the center of the sphere and the radius, return a
3224 //      point on the vector through the midpoint of the chord on the sphere.
3225 void bisect_chord(vector *p0, vector *p1, vector *centerp, float radius)
3226 {
3227         vector  tvec;
3228         vector  new_pnt;
3229
3230         vm_vec_add(&tvec, p0, p1);
3231         vm_vec_sub2(&tvec, centerp);
3232         vm_vec_sub2(&tvec, centerp);
3233         if (vm_vec_mag_quick(&tvec) < 0.1f) {
3234                 vm_vec_sub(&tvec, p0, p1);
3235                 if (fl_abs(tvec.x) <= fl_abs(tvec.z)){
3236                         tvec.x = -tvec.z;
3237                 } else {
3238                         tvec.y = -tvec.x;
3239                 }
3240         }
3241
3242         vm_vec_normalize(&tvec);
3243         vm_vec_scale(&tvec, radius);
3244         vm_vec_add(&new_pnt, centerp, &tvec);
3245
3246         add_path_point(&new_pnt, -1, -1, -1);
3247 }
3248                         
3249 //      Create a path from the current position to a goal position.
3250 //      The current position is in the current object and the goal position is
3251 //      in the goal object.
3252 //      It is ok to intersect the current object, but not the goal object.
3253 //      This function is useful for creating a path to an initial point near a large
3254 //      object.
3255 //
3256 // input:       subsys_path:    optional param (default 0), indicates this is a path to a subsystem
3257 void create_path_to_point(vector *curpos, vector *goalpos, object *curobjp, object *goalobjp, int subsys_path)
3258 {
3259         //      If can't cast vector to goalpos, then create an intermediate point.
3260         if (pp_collide(curpos, goalpos, goalobjp, curobjp->radius)) {
3261                 vector  tan1;
3262                 float           radius;
3263
3264                 // If this is a path to a subsystem, use SUBSYS_PATH_DIST as the radius for the object you are
3265                 // trying to avoid.  This is needed since subsystem paths extend out to SUBSYS_PATH_DIST, and we
3266                 // want ships to reach their path destination without flying to points that sit on the radius of
3267                 // a small ship
3268                 radius = goalobjp->radius;
3269                 if (subsys_path) {
3270                         if ( SUBSYS_PATH_DIST > goalobjp->radius ) {
3271                                 radius = SUBSYS_PATH_DIST;
3272                         }
3273                 }
3274
3275                 //      The intermediate point is at the intersection of:
3276                 //              tangent to *goalobjp sphere at point *goalpos
3277                 //              tangent to *goalobjp sphere through point *curpos in plane defined by *curpos, *goalpos, goalobjp->pos
3278                 //      Note, there are two tangents through *curpos, unless *curpos is on the
3279                 //      sphere.  The tangent that causes the nearer intersection (to *goalpos) is chosen.
3280                 get_tangent_point(&tan1, curpos, &goalobjp->pos, goalpos, radius);
3281
3282                 //      If we can't reach tan1 from curpos, insert a new point.
3283                 if (pp_collide(&tan1, curpos, goalobjp, curobjp->radius))
3284                         bisect_chord(curpos, &tan1, &goalobjp->pos, radius);
3285
3286                 add_path_point(&tan1, -1, -1, -1);
3287
3288                 //      If we can't reach goalpos from tan1, insert a new point.
3289                 if (pp_collide(goalpos, &tan1, goalobjp, curobjp->radius))
3290                         bisect_chord(goalpos, &tan1, &goalobjp->pos, radius);
3291         }
3292
3293 }
3294
3295 //      Given an object and a model path, globalize the points on the model
3296 //      and copy into the global path list.
3297 //      If pnp != NULL, then modify, in place, the path points.  This is used to create new
3298 //      globalized points when the base object has moved.
3299 // input:       randomize_pnt   => optional parameter (default value -1), add random vector in sphere to this path point
3300 void copy_xlate_model_path_points(object *objp, model_path *mp, int dir, int count, int path_num, pnode *pnp, int randomize_pnt)
3301 {
3302         matrix  m;
3303         int             i;
3304         vector  v1;
3305         int             pp_index;               //      index in Path_points at which to store point, if this is a modify-in-place (pnp ! NULL)
3306         int             start_index, finish_index;
3307         
3308         // nprintf(("AI", "Creating path for object %s in frame #%i\n", Ships[objp->instance].ship_name, AI_FrameCount));
3309         
3310         //      Initialize pp_index.
3311         //      If pnp == NULL, that means we're creating new points.  If not NULL, then modify in place.
3312         if (pnp == NULL)
3313                 pp_index = -1;                  //      This tells add_path_point to create a new point.
3314         else
3315                 pp_index = 0;                   //      pp_index will get assigned to index in Path_points to reuse.
3316
3317         vm_copy_transpose_matrix(&m, &objp->orient);
3318
3319         if (dir == 1) {
3320                 start_index = 0;
3321                 finish_index = min(count, mp->nverts);
3322         } else {
3323                 Assert(dir == -1);      //      direction must be up by 1 or down by 1 and it's neither!
3324                 start_index = mp->nverts-1;
3325                 finish_index = max(-1, mp->nverts-1-count);
3326         }
3327
3328         int offset = 0;
3329         for (i=start_index; i != finish_index; i += dir) {
3330                 //      Globalize the point.
3331                 vm_vec_rotate(&v1, &mp->verts[i].pos, &m);
3332                 vm_vec_add2(&v1, &objp->pos);
3333
3334                 if ( randomize_pnt == i ) {
3335                         vector v_rand;
3336                         static_randvec(OBJ_INDEX(objp), &v_rand);
3337                         vm_vec_scale(&v_rand, 30.0f);
3338                         vm_vec_add2(&v1, &v_rand);
3339                 }
3340
3341                 if (pp_index != -1)
3342                         pp_index = pnp-Path_points + offset;
3343
3344                 add_path_point(&v1, path_num, i, pp_index);
3345                 offset++;
3346         }
3347 }
3348
3349
3350 //      For pl_objp, create a path along path path_num into mobjp.
3351 //      The tricky part of this problem is creating the entry to the first point on the
3352 //      predefined path.  The points on this entry path are based on the location of Pl_objp
3353 //      relative to the start of the path.
3354 //
3355 // input:
3356 //                              subsys_path:    optional param (default 0), indicating this is a path to a subsystem
3357 void create_model_path(object *pl_objp, object *mobjp, int path_num, int subsys_path)
3358 {       
3359         ship                    *shipp = &Ships[pl_objp->instance];
3360         ai_info         *aip = &Ai_info[shipp->ai_index];
3361
3362         ship_info       *osip = &Ship_info[Ships[mobjp->instance].ship_info_index];
3363         polymodel       *pm = model_get(osip->modelnum);
3364         int                     num_points;
3365         model_path      *mp;
3366         pnode                   *ppfp_start = Ppfp;
3367         matrix          m;
3368         vector          gp0;
3369
3370         Assert(path_num >= 0);
3371
3372         //      Do garbage collection if necessary.
3373         if (Ppfp-Path_points + 64 > MAX_PATH_POINTS) {
3374                 garbage_collect_path_points();
3375                 ppfp_start = Ppfp;
3376         }
3377
3378         aip->path_start = Ppfp - Path_points;
3379         Assert(path_num < pm->n_paths);
3380         
3381         mp = &pm->paths[path_num];
3382         num_points = mp->nverts;
3383
3384         Assert(Ppfp-Path_points + num_points + 4 < MAX_PATH_POINTS);
3385
3386         vm_copy_transpose_matrix(&m, &mobjp->orient);
3387         vm_vec_rotate(&gp0, &mp->verts[0].pos, &m);
3388         vm_vec_add2(&gp0, &mobjp->pos);
3389
3390         if (pp_collide(&pl_objp->pos, &gp0, mobjp, pl_objp->radius)) {
3391                 vector  perim_point1;
3392                 vector  perim_point2;
3393
3394                 perim_point2 = pl_objp->pos;
3395                 
3396                 //      If object that wants to dock is inside bounding sphere of object it wants to dock with, make it fly out.
3397                 //      Assume it can fly "straight" out to the bounding sphere.
3398                 if (vm_vec_dist_quick(&pl_objp->pos, &mobjp->pos) < mobjp->radius) {
3399                         project_point_to_perimeter(&perim_point2, &mobjp->pos, mobjp->radius, &pl_objp->pos);
3400                         add_path_point(&perim_point2, path_num, -1, -1);
3401                 }
3402
3403                 //      If last point on pre-defined path is inside bounding sphere, create a new point on the surface of the sphere.
3404                 if (vm_vec_dist_quick(&mobjp->pos, &gp0) < mobjp->radius) {
3405                         project_point_to_perimeter(&perim_point1, &mobjp->pos, mobjp->radius, &gp0);
3406                         create_path_to_point(&perim_point2, &perim_point1, pl_objp, mobjp, subsys_path);
3407                         add_path_point(&perim_point1, path_num, -1, -1);
3408                 } else {                //      The predefined path extends outside the sphere.  Create path to that point.
3409                         create_path_to_point(&perim_point2, &gp0, pl_objp, mobjp, subsys_path);
3410                 }
3411         }
3412
3413         // AL 12-31-97: If following a subsystem path, add random vector to second last path point
3414         if ( subsys_path ) {
3415                 copy_xlate_model_path_points(mobjp, mp, 1, mp->nverts, path_num, NULL, mp->nverts-2);
3416         } else {
3417                 copy_xlate_model_path_points(mobjp, mp, 1, mp->nverts, path_num, NULL);
3418         }
3419
3420         aip->path_cur = aip->path_start;
3421         aip->path_dir = PD_FORWARD;
3422         aip->path_objnum = mobjp-Objects;
3423         aip->mp_index = path_num;
3424         aip->path_length = Ppfp - ppfp_start;
3425         aip->path_next_check_time = timestamp(1);
3426
3427         aip->path_goal_obj_hash = create_object_hash(&Objects[aip->path_objnum]);
3428
3429         aip->path_next_create_time = timestamp(1000);   //      OK to try to create one second later
3430         aip->path_create_pos = pl_objp->pos;
3431         aip->path_create_orient = pl_objp->orient;
3432
3433         aip->ai_flags &= ~AIF_USE_EXIT_PATH;                    // ensure this flag is cleared
3434 }
3435
3436 //      For pl_objp, create a path along path path_num into mobjp.
3437 //      The tricky part of this problem is creating the entry to the first point on the
3438 //      predefined path.  The points on this entry path are based on the location of pl_objp
3439 //      relative to the start of the path.
3440 void create_model_exit_path(object *pl_objp, object *mobjp, int path_num, int count)
3441 {       
3442         ship                    *shipp = &Ships[pl_objp->instance];
3443         ai_info         *aip = &Ai_info[shipp->ai_index];
3444
3445         ship_info       *osip = &Ship_info[Ships[mobjp->instance].ship_info_index];
3446         polymodel       *pm = model_get(osip->modelnum);
3447         int                     num_points;
3448         model_path      *mp;
3449         pnode                   *ppfp_start = Ppfp;
3450
3451         aip->path_start = Ppfp - Path_points;
3452         Assert(path_num < pm->n_paths);
3453         
3454         mp = &pm->paths[path_num];
3455         num_points = mp->nverts;
3456
3457         Assert(Ppfp-Path_points + num_points + 4 < MAX_PATH_POINTS);
3458
3459         copy_xlate_model_path_points(mobjp, mp, -1, count, path_num, NULL);
3460
3461         aip->path_cur = aip->path_start;
3462         aip->path_dir = PD_FORWARD;
3463         aip->path_objnum = mobjp-Objects;
3464         aip->mp_index = path_num;
3465         aip->path_length = Ppfp - ppfp_start;
3466         aip->path_next_check_time = timestamp(1);
3467
3468         aip->ai_flags |= AIF_USE_EXIT_PATH;             // mark as exit path, referenced in maybe
3469 }
3470
3471 //      Return true if the vector from curpos to goalpos intersects with any ship other than the ignore objects.
3472 //      Calls pp_collide
3473 int pp_collide_any(vector *curpos, vector *goalpos, float radius, object *ignore_objp1, object *ignore_objp2, int big_only_flag)
3474 {
3475         ship_obj        *so;    
3476
3477         for ( so = GET_FIRST(&Ship_obj_list); so != END_OF_LIST(&Ship_obj_list); so = GET_NEXT(so) ) {
3478                 object *objp = &Objects[so->objnum];
3479
3480                 if (big_only_flag) {
3481                         if (!(Ship_info[Ships[objp->instance].ship_info_index].flags & (SIF_BIG_SHIP | SIF_HUGE_SHIP)))
3482                                 continue;
3483                 }
3484
3485                 if ((objp != ignore_objp1) && (objp != ignore_objp2)) {
3486                         if (pp_collide(curpos, goalpos, objp, radius))
3487                                 return OBJ_INDEX(objp);
3488                 }
3489         }
3490
3491         return -1;
3492 }
3493
3494 //      Used to create docking paths and other pre-defined paths through ships.
3495 //      Creates a path in absolute space.
3496 //      Create a path into the object objnum.
3497 //
3498 // input:
3499 //      pl_objp:                        object that will use the path
3500 //      objnum:                 Object to find path to.
3501 //      path_num:               model path index to use
3502 //      exit_flag:              true means this is an exit path in the model
3503 // subsys_path: optional param (default 0) that indicates this is a path to a subsystem
3504 //      Exit:
3505 //      ai_info struct in Pl_objp gets stuffed with information to enable Pl_objp to fly the path.
3506 void ai_find_path(object *pl_objp, int objnum, int path_num, int exit_flag, int subsys_path)
3507 {
3508         ai_info *aip = &Ai_info[Ships[pl_objp->instance].ai_index];
3509
3510         Assert(path_num >= 0);
3511
3512         //      This is test code, find an object with paths.
3513         if (objnum != -1) {
3514                 object  *objp = &Objects[objnum];
3515
3516                 if (objp->type == OBJ_SHIP) {
3517                         polymodel *pm;
3518
3519                         ship    *shipp = &Ships[objp->instance];
3520                         pm = model_get( shipp->modelnum );
3521                         Assert(pm->n_paths > path_num);
3522                         aip->goal_objnum = objp-Objects;
3523                         aip->goal_signature = objp->signature;
3524                         if (exit_flag)
3525                                 create_model_exit_path(pl_objp, objp, path_num);
3526                         else
3527                                 create_model_path(pl_objp, objp, path_num, subsys_path);
3528                         return;
3529                 }
3530
3531         }
3532 }
3533
3534 extern int vector_object_collision(vector *start_pos, vector *end_pos, object *objp, float radius_scale);
3535
3536 //      Maybe make *objp avoid a player object.
3537 //      For now, 4/6/98, only check Player_obj.
3538 //      If player collision would occur, set AIF_AVOIDING_SMALL_SHIP bit in ai_flags.
3539 //      Set aip->avoid_goal_point
3540 int maybe_avoid_player(object *objp, vector *goal_pos)
3541 {
3542         ai_info *aip;
3543         vector  cur_pos, new_goal_pos;
3544         object  *player_objp;
3545         vector  n_vec_to_goal, n_vec_to_player;
3546
3547         aip = &Ai_info[Ships[objp->instance].ai_index];
3548
3549         if (!timestamp_elapsed(aip->avoid_check_timestamp))
3550                 return 0;
3551
3552         player_objp = Player_obj;
3553
3554         float   speed_time;
3555
3556         //      How far two ships could be apart and still collide within one second.
3557         speed_time = player_objp->phys_info.speed + objp->phys_info.speed;
3558
3559         float   obj_obj_dist;
3560
3561         obj_obj_dist = vm_vec_dist_quick(&player_objp->pos, &objp->pos);
3562
3563         if (obj_obj_dist > speed_time*2.0f)
3564                 return 0;
3565
3566         cur_pos = objp->pos;
3567
3568         new_goal_pos = *goal_pos;
3569
3570         float dist = vm_vec_normalized_dir(&n_vec_to_goal, goal_pos, &objp->pos);
3571         vm_vec_normalized_dir(&n_vec_to_player, &player_objp->pos, &objp->pos);
3572
3573         if (dist > speed_time*2.0f) {
3574                 vm_vec_scale_add(&new_goal_pos, &objp->pos, &n_vec_to_goal, 200.0f);
3575         }
3576
3577         if (vector_object_collision(&objp->pos, &new_goal_pos, player_objp, 1.5f)) {
3578                 aip->ai_flags |= AIF_AVOIDING_SMALL_SHIP;
3579
3580                 vector  avoid_vec;
3581
3582                 vm_vec_sub(&avoid_vec, &n_vec_to_goal, &n_vec_to_player);
3583                 if (vm_vec_mag_quick(&avoid_vec) < 0.01f) {
3584                         vm_vec_copy_scale(&avoid_vec, &objp->orient.rvec, frand()-0.5f);
3585                         vm_vec_scale_add2(&avoid_vec, &objp->orient.uvec, frand()-0.5f);
3586                         vm_vec_normalize(&avoid_vec);
3587                 } else {
3588                         vector  tvec1;
3589                         vm_vec_normalize(&avoid_vec);
3590                         vm_vec_crossprod(&tvec1, &n_vec_to_goal, &avoid_vec);
3591                         vm_vec_crossprod(&avoid_vec, &tvec1, &n_vec_to_player);
3592                 }
3593
3594                 //      Now, avoid_vec is a vector perpendicular to the vector to the player and the direction *objp
3595                 //      should fly in to avoid the player while still approaching its goal.
3596                 vm_vec_scale_add(&aip->avoid_goal_point, &player_objp->pos, &avoid_vec, 400.0f);
3597
3598                 aip->avoid_check_timestamp = timestamp(1000);
3599
3600                 return 1;
3601         } else {
3602                 aip->ai_flags &= ~AIF_AVOIDING_SMALL_SHIP;
3603                 aip->avoid_check_timestamp = timestamp((int) (obj_obj_dist/200.0f) + 500);
3604
3605                 return 0;
3606         }
3607 }
3608
3609 //      Make object *still_objp enter AIM_STILL mode.
3610 //      Make it point at view_pos.
3611 void ai_stay_still(object *still_objp, vector *view_pos)
3612 {
3613         ship    *shipp;
3614         ai_info *aip;
3615
3616         Assert(still_objp->type == OBJ_SHIP);
3617         Assert((still_objp->instance >= 0) && (still_objp->instance < MAX_OBJECTS));
3618
3619         shipp = &Ships[still_objp->instance];
3620         Assert((shipp->ai_index >= 0) && (shipp->ai_index < MAX_AI_INFO));
3621
3622         aip = &Ai_info[shipp->ai_index];
3623
3624         aip->mode = AIM_STILL;
3625
3626         //      If view_pos not NULL, point at that point.  Else, point at a point directly in front of ship.  Ie, don't turn.
3627         if (view_pos != NULL)
3628                 aip->goal_point = *view_pos;
3629         else
3630                 vm_vec_scale_add(&aip->goal_point, &still_objp->pos, &still_objp->orient.fvec, 100.0f);
3631 }
3632
3633 // code which is called from ai_dock_with_object and ai_dock to set flags and apprioriate variable
3634 // when two objects have completed docking.  used because we can dock object initially at misison load
3635 // time (meaning that ai_dock() might never get called).  docker has docked with dockee (i.e. docker
3636 // would be a freighter and dockee would be a cargo).
3637 void ai_do_objects_docked_stuff(object *docker, object *dockee)
3638 {
3639         ai_info *aip, *other_aip;
3640
3641         aip = &Ai_info[Ships[docker->instance].ai_index];
3642         other_aip = &Ai_info[Ships[dockee->instance].ai_index];
3643
3644         // set the flags and dock_objnum for both objects
3645         aip->ai_flags |= AIF_DOCKED;
3646         aip->dock_objnum = OBJ_INDEX(dockee);
3647         other_aip->ai_flags |= AIF_DOCKED;
3648         other_aip->dock_objnum = OBJ_INDEX(docker);
3649         aip->dock_signature = dockee->signature;
3650         other_aip->dock_signature = docker->signature;
3651
3652         // add multiplayer hook here to deal with docked objects.  We need to only send information
3653         // about the object that is docking.  Both flags will get updated.
3654         if ( MULTIPLAYER_MASTER )
3655                 send_ai_info_update_packet( docker, AI_UPDATE_DOCK );
3656
3657 }
3658
3659 // code which is called when objects become undocked. Equivalent of above function.
3660 // dockee might not be valid since this code can get called to cleanup after a ship
3661 // has blown up!
3662 void ai_do_objects_undocked_stuff( object *docker, object *dockee )
3663 {
3664         ai_info *aip, *other_aip;
3665
3666         // add multiplayer hook here to deal with undocked objects.  Do it before we
3667         // do anything else.  We don't need to send info for both objects, since we can find
3668         // it be dock_objnum
3669         if ( MULTIPLAYER_MASTER )
3670                 send_ai_info_update_packet( docker, AI_UPDATE_UNDOCK );
3671
3672         aip = &Ai_info[Ships[docker->instance].ai_index];
3673
3674         // set the flags and dock_objnum for both objects
3675         aip->ai_flags &= ~(AIF_DOCKED | AIF_BEING_REPAIRED);
3676         aip->dock_objnum = -1;
3677         
3678         if ( dockee != NULL ) {
3679                 other_aip = &Ai_info[Ships[dockee->instance].ai_index];
3680                 other_aip->ai_flags &= ~(AIF_DOCKED | AIF_BEING_REPAIRED);
3681                 other_aip->dock_objnum = -1;
3682         }
3683
3684 }
3685
3686
3687 //      --------------------------------------------------------------------------
3688 //      Interface from goals code to AI.
3689 //      Cause *docker to dock with *dockee.
3690 //      priority is priority of goal from goals code.
3691 //      dock_type is:
3692 //              AIDO_DOCK               set goal of docking
3693 //              AIDO_DOCK_NOW   immediately dock, used for ships that need to be docked at mission start
3694 //              AIDO_UNDOCK             set goal of undocking
3695 void ai_dock_with_object(object *docker, object *dockee, int priority, int dock_type, int docker_index, int dockee_index)
3696 {
3697         ai_info         *aip;
3698         polymodel       *pm;
3699         ai_info         *dockee_aip;
3700
3701         Assert(docker != NULL);
3702         Assert(dockee != NULL);
3703         Assert(docker->instance != -1);
3704         Assert(Ships[docker->instance].ai_index != -1);
3705         Assert(Ships[dockee->instance].ai_index != -1);
3706         Assert( docker_index != -1 );
3707         Assert( dockee_index != -1 );
3708
3709         aip = &Ai_info[Ships[docker->instance].ai_index];
3710
3711         if ((aip->ai_flags & AIF_DOCKED) && (dock_type == AIDO_DOCK)) {
3712                 object  *dockee2;
3713                 int             docker_index2, dockee_index2;
3714
3715                 Assert(aip->dock_objnum > -1);
3716                 dockee2 = &Objects[aip->dock_objnum];
3717                 docker_index2 = aip->dock_index;
3718                 dockee_index2 = aip->dockee_index;
3719                 // MWA -- 2/9/98.  use the goal code to undock the ships since goals might need to get removed
3720                 // and that code will do it properly.  I'd actually be surprised if we got into this code anymore
3721                 // since the outer layer goal code should deal with this issue....but who knows...
3722                 ai_add_goal_ship_internal( aip, AI_GOAL_UNDOCK, NULL, -1, -1, 0 );
3723
3724                 // old code below
3725                 //ai_dock_with_object(docker, dockee2, priority, AIDO_UNDOCK, docker_index2, dockee_index2);
3726                 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));
3727                 nprintf(("AI", "...so ship %s will now undock.\n", Ships[docker->instance].ship_name));
3728                 return;
3729         }
3730
3731         dockee_aip = &Ai_info[Ships[dockee->instance].ai_index];
3732
3733         aip->goal_objnum = dockee - Objects;
3734         aip->goal_signature = dockee->signature;
3735
3736         aip->mode = AIM_DOCK;
3737
3738         switch (dock_type) {
3739         case AIDO_DOCK:
3740                 aip->submode = AIS_DOCK_0;
3741                 break;
3742         case AIDO_DOCK_NOW:
3743                 aip->submode = AIS_DOCK_3A;
3744                 break;
3745         case AIDO_UNDOCK:
3746                 aip->submode = AIS_UNDOCK_0;
3747                 break;
3748         default:
3749                 Int3();         //      Bogus dock_type.
3750         }
3751
3752         aip->submode_start_time = Missiontime;
3753         aip->dock_index = docker_index;
3754         aip->dockee_index = dockee_index;
3755
3756         dockee_aip->dock_index = dockee_index;
3757         dockee_aip->dockee_index = docker_index;
3758
3759         // get the path number to the docking point on the dockee.  Each docking point contains a list
3760         // of paths that the point can be reached by.  Pick the first path in the path list for now.
3761         // We only want to do this stuff if we are docking!!!  Be sure to set the path index
3762         if ((dock_type == AIDO_DOCK) || (dock_type == AIDO_DOCK_NOW)) {
3763                 pm = model_get( Ships[dockee->instance].modelnum );
3764                 Assert( pm->docking_bays[dockee_index].num_spline_paths > 0 );
3765
3766                 // only set the dock path index if we are docking.  undocking will assume that dock_path_index
3767                 // already set from some other docking command
3768                 aip->dock_path_index = dockee_index;
3769                 dockee_aip->dock_path_index = docker_index;
3770         }
3771
3772         if (dock_type != AIDO_DOCK_NOW) {
3773                 int path_num;
3774                 //      Note: Second parameter is dock path index.  This should be specified as an
3775                 //      _input_ to this function and passed through.  The path index should be already
3776                 // set for the undock function
3777                 path_num = ai_return_path_num_from_dockbay(dockee, dockee_index);
3778                 ai_find_path(docker, dockee-Objects, path_num, 0);
3779 //              ai_find_path(dockee-Objects, dockee_index, 0);
3780         } else {
3781                 dock_orient_and_approach(docker, dockee, DOA_DOCK_STAY);
3782                 //aip->dock_objnum = OBJ_INDEX(dockee);
3783                 ai_do_objects_docked_stuff( docker, dockee );
3784         }
3785
3786 }
3787
3788 //      Cause a ship to fly its waypoints.
3789 //      flags tells:
3790 //              WPF_REPEAT      Set -> repeat waypoints.
3791 void ai_start_waypoints(object *objp, int waypoint_list_index, int wp_flags)
3792 {
3793         ai_info *aip;
3794
3795         Assert(waypoint_list_index < Num_waypoint_lists);
3796
3797         //nprintf(("AI", "Frame %i: Ship %s instructed to fly waypoint list #%i\n", AI_FrameCount, Ships[objp->instance].ship_name, waypoint_list_index));
3798         aip = &Ai_info[Ships[objp->instance].ai_index];
3799
3800         if ( (aip->mode == AIM_WAYPOINTS) && (aip->wp_index == waypoint_list_index) )
3801                 return;
3802
3803         aip->ai_flags |= AIF_FORMATION_WING;
3804         aip->ai_flags &= ~AIF_FORMATION_OBJECT;
3805         aip->wp_list = waypoint_list_index;
3806         aip->wp_index = 0;
3807         aip->wp_flags = wp_flags;
3808         aip->mode = AIM_WAYPOINTS;
3809
3810         Assert(aip->active_goal != AI_ACTIVE_GOAL_DYNAMIC);
3811 }
3812
3813 //      Make *objp stay within dist units of *other_objp
3814 void ai_do_stay_near(object *objp, object *other_objp, float dist)
3815 {
3816         ai_info *aip;
3817
3818         Assert(objp != other_objp);             //      Bogus!  Told to stay near self.
3819         Assert(objp->type == OBJ_SHIP);
3820         Assert((objp->instance >= 0) && (objp->instance < MAX_SHIPS));
3821
3822         aip = &Ai_info[Ships[objp->instance].ai_index];
3823
3824         aip->mode = AIM_STAY_NEAR;
3825         aip->submode = -1;
3826         aip->stay_near_distance = dist;
3827         aip->goal_objnum = other_objp-Objects;
3828         aip->goal_signature = other_objp->signature;
3829
3830 }
3831
3832 //      Make object *objp form on wing of object *goal_objp
3833 void ai_form_on_wing(object *objp, object *goal_objp)
3834 {
3835         ai_info *aip;
3836         ship                    *shipp;
3837         ship_info       *sip;
3838
3839         // objp == goal_objp sometimes in multiplayer when someone leaves a game -- make a simple
3840         // out for this case.
3841         if ( Game_mode & GM_MULTIPLAYER ) {
3842                 if ( objp == goal_objp ) {
3843                         return;
3844                 }
3845         }
3846
3847         Assert(objp != goal_objp);              //      Bogus!  Told to form on own's wing!
3848
3849         shipp = &Ships[objp->instance];
3850         sip = &Ship_info[shipp->ship_info_index];
3851
3852         //      Only fighters or bombers allowed to form on wing.
3853         if (!(sip->flags & (SIF_FIGHTER | SIF_BOMBER))) {
3854                 nprintf(("AI", "Warning: Ship %s tried to form on player's wing, but not fighter or bomber.\n", shipp->ship_name));
3855                 return;
3856         }
3857
3858         aip = &Ai_info[Ships[objp->instance].ai_index];
3859
3860         aip->ai_flags &= ~AIF_FORMATION_WING;
3861         aip->ai_flags |= AIF_FORMATION_OBJECT;
3862
3863         aip->goal_objnum = goal_objp-Objects;
3864         ai_set_goal_maybe_abort_dock(objp, aip);
3865         aip->ok_to_target_timestamp = timestamp(DELAY_TARGET_TIME*4);           //      Super extra long time until can target another ship.
3866
3867 }
3868
3869 //      Given an object and an object on whose wing to form, return slot to use.
3870 //      Optimize:
3871 //              This function is called per object in formation per frame.  Should store slot in ai_info struct.
3872 int ai_formation_object_get_slotnum(int objnum, object *objp)
3873 {
3874         int     slotnum = 1;                    //      Note: Slot #0 means leader, which isn't someone who was told to form-on-wing.
3875
3876         for ( object *o = GET_FIRST(&obj_used_list); o != END_OF_LIST(&obj_used_list); o = GET_NEXT(o) ) {
3877                 if (objp == o)
3878                         break;
3879                 else if (o->type == OBJ_SHIP)
3880                         if (Ai_info[Ships[o->instance].ai_index].ai_flags & AIF_FORMATION_OBJECT)
3881                                 if (Ai_info[Ships[o->instance].ai_index].goal_objnum == objnum)
3882                                         slotnum++;
3883         }
3884
3885         Assert(o != END_OF_LIST(&obj_used_list));       //      Didn't find objp in list of used ships.  Impossible!
3886
3887         return slotnum;
3888 }
3889
3890 #define BIGNUM  100000.0f
3891
3892 int Debug_k = 0;
3893
3894 //      Given an attacker's position and a target's position and velocity, compute the time of
3895 //      intersection of a weapon fired by the attacker with speed weapon_speed.
3896 //      Return this value.  Return value of 0.0f means no collision is possible.
3897 float compute_collision_time(vector *targpos, vector *targvel, vector *attackpos, float weapon_speed)
3898 {
3899         vector  vec_to_target;
3900         float           pos_dot_vel;
3901         float           vel_sqr;
3902         float           discrim;
3903
3904         vm_vec_sub(&vec_to_target, targpos, attackpos);
3905         pos_dot_vel = vm_vec_dot(&vec_to_target, targvel);
3906         vel_sqr = vm_vec_dot(targvel, targvel) - weapon_speed*weapon_speed;
3907         discrim = pos_dot_vel*pos_dot_vel - vel_sqr*vm_vec_dot(&vec_to_target, &vec_to_target);
3908
3909         if (discrim > 0.0f) {
3910                 float   t1, t2, t_solve;
3911
3912                 t1 = (-pos_dot_vel + fl_sqrt(discrim)) / vel_sqr;
3913                 t2 = (-pos_dot_vel - fl_sqrt(discrim)) / vel_sqr;
3914
3915                 t_solve = BIGNUM;
3916
3917                 if (t1 > 0.0f)
3918                         t_solve = t1;
3919                 if ((t2 > 0.0f) && (t2 < t_solve))
3920                         t_solve = t2;
3921
3922                 if (t_solve < BIGNUM-1.0f) {
3923                         return t_solve + Debug_k * flFrametime;
3924                 }
3925         }
3926
3927         return 0.0f;
3928 }
3929
3930
3931 //      --------------------------------------------------------------------------
3932 //      If far away, use player's speed.
3933 //      If in between, lerp between player and laser speed
3934 //      If close, use laser speed.
3935 // Want to know how much time it will take to get to the enemy.
3936 // This function doesn't account for the fact that by the time the player
3937 // (or his laser) gets to the current enemy position, the enemy will have moved.
3938 // This is dealt with in polish_predicted_enemy_pos.
3939 float compute_time_to_enemy(float dist_to_enemy, object *pobjp, object *eobjp)
3940 {
3941         float   time_to_enemy;
3942         float   pl_speed = pobjp->phys_info.speed;
3943         float   max_laser_distance, max_laser_speed;
3944         int     bank_num, weapon_num;
3945         ship    *shipp = &Ships[pobjp->instance];
3946
3947         bank_num = shipp->weapons.current_primary_bank;
3948         weapon_num = shipp->weapons.primary_bank_weapons[bank_num];
3949         max_laser_speed = Weapon_info[weapon_num].max_speed;
3950         max_laser_distance = max_laser_speed * Weapon_info[weapon_num].lifetime;
3951
3952         //      If pretty far away, use player's speed to predict position, else
3953         //      use laser's speed because when close, we care more about hitting
3954         //      with a laser than about causing ship:ship rendezvous.
3955         if (dist_to_enemy > 1.5 * max_laser_distance) {
3956                 if (pl_speed > 0.0f)
3957                         time_to_enemy = dist_to_enemy/pl_speed;
3958                 else
3959                         time_to_enemy = 1.0f;
3960         } else if (dist_to_enemy > 1.1*max_laser_distance) {
3961                 if (pl_speed > 0.1f) {
3962                         float   scale;
3963
3964                         scale = (float) ((dist_to_enemy - max_laser_distance) / max_laser_distance);
3965                 
3966                         time_to_enemy = (float) (dist_to_enemy/(pl_speed * scale + max_laser_speed * (1.0f - scale)));
3967                 } else
3968                         time_to_enemy = 2.0f;
3969         } else
3970                 time_to_enemy = (float) (dist_to_enemy/max_laser_speed);
3971
3972         // return time_to_enemy * (1.0f + Ai_info[Ships[pobjp->instance].ai_index].lead_scale);
3973         return time_to_enemy + flFrametime;
3974 }
3975
3976 //      Stuff *dot and *tts.
3977 //      *dot is always computed.  If dot is less than zero, the magnitude is
3978 //      incorrect, not having been divided by distance.
3979 //      If *dot is > 0.0f, then tts is computed.  This is the time it will take object
3980 //      *objp to get to *pos, assuming it moves right at it.
3981 void fds_aux(float *dot, float *tts, vector *pos, float dtime, object *objp)
3982 {
3983         vector  v2s;
3984
3985         vm_vec_sub(&v2s, pos, &objp->pos);
3986         *dot = vm_vec_dot(&v2s, &objp->orient.fvec);
3987
3988         if (*dot > 0.0f) {
3989                 float   dist;
3990
3991                 dist = vm_vec_dist(&objp->pos, pos);
3992
3993                 if (dist > 0.1f)
3994                         *dot /= dist;
3995                 else
3996                         *dot = 1.0f;
3997
3998                 if (objp->phys_info.speed > 0.1f)
3999                         *tts = dist / objp->phys_info.speed;
4000                 else
4001                         *tts = dist * 100.0f;
4002         }
4003 }
4004
4005 /*
4006 //      Return index of weapon that could hit object *sobjp within dtime seconds.
4007 //      Actual time until impact returned in *atime.
4008 int find_danger_weapon(object *sobjp, float dtime, float *atime, float dot_threshhold)
4009 {
4010         object  *objp, *best_objp = NULL;
4011         float           best_tts = 1000.0f;
4012
4013         for ( objp = GET_FIRST(&obj_used_list); objp !=END_OF_LIST(&obj_used_list); objp = GET_NEXT(objp) ) {
4014                 if ((objp->type == OBJ_WEAPON) && (sobjp-Objects != objp->parent)) {
4015                         float           dot, tts;
4016                         // vector       psp;            //      Predicted ship position.
4017
4018                         //      Get dot and time to current ship position.
4019                         fds_aux(&dot, &tts, &sobjp->pos, dtime, objp);
4020
4021                         //      If dot and tts are in plausible range, do more expensive stuff.
4022                         if (dot > 0.98f) {
4023 //                              float   dot_from_sobjp;
4024                                 vector  v2e;
4025
4026                                 vm_vec_normalized_dir(&v2e, &objp->pos, &sobjp->pos);
4027 //                              dot_from_sobjp = vm_vec_dot(&sobjp->orient.fvec, &v2e);
4028 //                              if (dot_from_sobjp >= dot_threshhold)
4029                                         if (tts < dtime) {
4030                                                 if (tts < best_tts) {
4031                                                         best_tts = tts;
4032                                                         best_objp = objp;
4033                                                 }
4034                                         }
4035                         }
4036                 }
4037         }
4038
4039         *atime = best_tts;
4040
4041         if (best_objp != NULL)
4042                 return best_objp-Objects;
4043         else
4044                 return -1;
4045 }
4046 */
4047
4048 //      --------------------------------------------------------------------------
4049 void ai_set_positions(object *pl_objp, object *en_objp, ai_info *aip, vector *player_pos, vector *enemy_pos)
4050 {
4051         *player_pos = pl_objp->pos;
4052
4053         if (aip->next_predict_pos_time > Missiontime) {
4054                 *enemy_pos = aip->last_predicted_enemy_pos;
4055         } else {
4056                 *enemy_pos = en_objp->pos;
4057
4058                 aip->next_predict_pos_time = Missiontime + Skill_level_delay[Game_skill_level];
4059                 aip->last_predicted_enemy_pos = *enemy_pos;
4060         }
4061
4062
4063 }
4064
4065 //      --------------------------------------------------------------------------
4066 int find_nearest_waypoint(object *objp)
4067 {
4068         int     i;
4069         float   dist, min_dist, dot;
4070         int     min_ind;
4071         ship    *shipp;
4072         int     wp_listnum;
4073         waypoint_list   *wpl;
4074
4075         shipp = &Ships[objp->instance];
4076         wp_listnum = Ai_info[Ships[objp->instance].ai_index].wp_list;
4077         Assert(wp_listnum > 0);
4078         wpl = &Waypoint_lists[wp_listnum];
4079
4080         min_dist = 999999.0f;
4081         min_ind = -1;
4082
4083         for (i=0; i<wpl->count; i++) {
4084                 dist = vm_vec_dist_quick(&objp->pos, &wpl->waypoints[i]);
4085                 dot = vm_vec_dot_to_point(&objp->orient.fvec, &objp->pos, &wpl->waypoints[i]);
4086                 dist = (float) (dist * (1.25 - dot));
4087                 if (dist < min_dist) {
4088                         min_dist = dist;
4089                         min_ind = i;
4090                 }
4091         }
4092
4093         Assert(min_ind != -1);
4094
4095         return min_ind;
4096 }
4097
4098 //      Given an ai_info struct, by reading current goal and path information,
4099 //      extract base path information and return in pmp and pmpv.
4100 //      Return true if found, else return false.
4101 //      false means the current point is not on the original path.
4102 int get_base_path_info(int path_cur, int goal_objnum, model_path **pmp, mp_vert **pmpv)
4103 {
4104         pnode                   *pn = &Path_points[path_cur];
4105         ship_info       *sip = &Ship_info[Ships[Objects[goal_objnum].instance].ship_info_index];
4106         polymodel       *pm = model_get(sip->modelnum);
4107         static          int     debug_last_index = -1;
4108         *pmpv = NULL;
4109         *pmp = NULL;
4110
4111         if (pn->path_num != -1) {
4112                 *pmp = &pm->paths[pn->path_num];
4113                 if (pn->path_index != -1)
4114                         *pmpv = &(*pmp)->verts[pn->path_index];
4115                 else
4116                         return 0;
4117         } else
4118                 return 0;
4119
4120 /*      if (debug_last_index != *pmpv-(*pmp)->verts) {
4121                 debug_last_index = *pmpv-(*pmp)->verts;
4122                 nprintf(("AI", "Point %i has %i turrets: ", *pmpv-(*pmp)->verts, (*pmpv)->nturrets));
4123                 for (int i=0; i<(*pmpv)->nturrets; i++) {
4124                         nprintf(("AI", "%i ", (*pmpv)->turret_ids[i]));
4125                 }
4126                 nprintf(("AI", "\n"));
4127         }
4128 */
4129         return 1;
4130 }
4131
4132 //      Modify, in place, the points in a global model path.
4133 //      Only modify those points that are defined in the model path.  Don't modify the
4134 //      leadin points, such as those that are necessary to get the model on the path.
4135 void modify_model_path_points(object *objp)
4136 {       
4137         ai_info         *aip = &Ai_info[Ships[objp->instance].ai_index];
4138         object          *mobjp = &Objects[aip->path_objnum];
4139         ship_info       *osip = &Ship_info[Ships[mobjp->instance].ship_info_index];
4140         polymodel       *pm = model_get(osip->modelnum);
4141         pnode                   *pnp;
4142         int                     path_num, dir;
4143
4144         Assert((aip->path_start >= 0) && (aip->path_start < MAX_PATH_POINTS));
4145
4146         pnp = &Path_points[aip->path_start];
4147         while ((pnp->path_index == -1) && (pnp-Path_points - aip->path_start < aip->path_length))
4148                 pnp++;
4149
4150         path_num = pnp->path_num;
4151         Assert((path_num >= 0) && (path_num < pm->n_paths));
4152         
4153         Assert(pnp->path_index != -1);  //      If this is -1, that means we never found the model path points
4154
4155         dir = 1;
4156         if ( aip->ai_flags & AIF_USE_EXIT_PATH ) {
4157                 dir = -1;
4158         }
4159
4160         copy_xlate_model_path_points(mobjp, &pm->paths[path_num], dir, pm->paths[path_num].nverts, path_num, pnp);
4161 }
4162
4163 //      Return an indication of the distance between two matrices.
4164 //      This is the sum of the distances of their dot products from 1.0f.
4165 float ai_matrix_dist(matrix *mat1, matrix *mat2)
4166 {
4167         float   t;
4168
4169         t =  1.0f - vm_vec_dot(&mat1->fvec, &mat2->fvec);
4170         t += 1.0f - vm_vec_dot(&mat1->uvec, &mat2->uvec);
4171         t += 1.0f - vm_vec_dot(&mat1->rvec, &mat2->rvec);
4172
4173         return t;
4174 }
4175
4176
4177 //      Paths are created in absolute space, so a moving object needs to have model paths within it recreated.
4178 //      This uses the hash functions which means the slightest movement will cause a recreate, though the timestamp
4179 //      prevents this from happening too often.
4180 //      force_recreate_flag TRUE means to recreate regardless of timestamp.
4181 //      Returns TRUE if path recreated.
4182 float maybe_recreate_path(object *objp, ai_info *aip, int force_recreate_flag)
4183 {
4184         int     hashval;
4185
4186         Assert(&Ai_info[Ships[objp->instance].ai_index] == aip);
4187
4188         if ((aip->mode == AIM_BAY_EMERGE) || (aip->mode == AIM_BAY_DEPART))
4189                 if ((OBJ_INDEX(objp) % 4) == (Framecount % 4))
4190                         force_recreate_flag = 1;
4191
4192         //      If no path, that means we don't need one.
4193         if (aip->path_start == -1)
4194                 return 0.0f;
4195
4196         // AL 11-12-97: If AIF_USE_STATIC_PATH is set, don't try to recreate.  This is needed when ships
4197         //                                  emerge from fighter bays.  We don't need to recreate the path.. and in case the 
4198         //              parent ship dies, we still want to be able to continue on the path
4199         if ( aip->ai_flags & AIF_USE_STATIC_PATH ) 
4200                 return 0.0f;
4201
4202         if (force_recreate_flag || timestamp_elapsed(aip->path_next_create_time)) {
4203                 object  *path_objp;
4204
4205                 path_objp = &Objects[aip->path_objnum];
4206
4207                 if ((hashval = create_object_hash(path_objp)) != aip->path_goal_obj_hash) {
4208                         float dist;
4209                         
4210                         dist = vm_vec_dist_quick(&path_objp->pos, &aip->path_create_pos);
4211                         dist += ai_matrix_dist(&path_objp->orient, &aip->path_create_orient) * 25.0f;
4212
4213                         if (force_recreate_flag || (dist > 2.0f)) {
4214                                 aip->path_next_create_time = timestamp(1000);   //      Update again in as little as 1000 milliseconds, ie 1 second.
4215                                 aip->path_goal_obj_hash = hashval;
4216                                 modify_model_path_points(objp);
4217
4218                                 aip->path_create_pos = path_objp->pos;
4219                                 aip->path_create_orient = path_objp->orient;
4220                                 
4221                                 return dist;
4222                         }
4223                 }
4224         }
4225
4226         return 0.0f;
4227 }
4228
4229 //      Set acceleration for ai_dock().
4230 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)
4231 {
4232         float prev_dot_to_goal = aip->prev_dot_to_goal;
4233         
4234         aip->prev_dot_to_goal = dot;
4235
4236         if (objp->phys_info.speed < 0.0f) {
4237                 accelerate_ship(aip, 1.0f/32.0f);
4238         } else if ((prev_dot_to_goal-dot) > 0.01) {
4239                 if (prev_dot_to_goal > dot + 0.05f) {
4240                         accelerate_ship(aip, 0.0f);
4241                 } else {
4242                         change_acceleration(aip, -1.0f);        //      -1.0f means subtract off flFrametime from acceleration value in 0.0..1.0
4243                 }
4244         } else {
4245                 if ((aip->mode == AIM_DOCK) && (dist_to_next < 150.0f) && (aip->path_start + aip->path_length - 2 == aip->path_cur)) {
4246                         set_accel_for_target_speed(objp, sip->max_speed * max(dist_to_next/500.0f, 1.0f));
4247                         //mprintf(("dist = %7.3f, speed = %7.3f\n", dist_to_next, objp->phys_info.speed));
4248                 } else if ((dot_to_next >= dot * .9) || (dist_to_next > 100.0f)) {
4249                         if (dist_to_goal > 200.0f)
4250                                 set_accel_for_target_speed(objp, sip->max_speed * (dot + 1.0f) / 2.0f);
4251                         else {
4252                                 float   xdot;
4253
4254                                 xdot = (dot_to_next + dot)/2.0f;
4255                                 if (xdot < 0.0f)
4256                                         xdot = 0.0f;
4257
4258                                 // AL: if following a path not in dock mode, move full speed
4259                                 if (( aip->mode != AIM_DOCK ) && (dot > 0.9f)) {
4260                                         set_accel_for_target_speed(objp, sip->max_speed*dot*dot*dot);
4261                                 } else {
4262                                         if ((aip->path_cur - aip->path_start < aip->path_length-2) && (dist_to_goal < 2*objp->radius)) {
4263                                                 //nprintf(("AI", "Target speed = %7.3f\n", dist_to_goal/8.0f));
4264                                                 set_accel_for_target_speed(objp, dist_to_goal/8.0f + 2.0f);
4265                                         } else {
4266                                                 set_accel_for_target_speed(objp, sip->max_speed * (2*xdot + 0.25f)/4.0f);
4267                                         }
4268                                 }
4269                         }
4270                 } else {
4271                         float   xdot;
4272
4273                         xdot = max(dot_to_next, 0.1f);
4274                         if ( aip->mode != AIM_DOCK ) {
4275                                 set_accel_for_target_speed(objp, sip->max_speed);
4276                         } else {
4277                                 float   speed;
4278                                 if ((aip->path_cur - aip->path_start < aip->path_length-2) && (dist_to_goal < 2*objp->radius)) {
4279                                         speed = dist_to_goal/8.0f + 2.0f;
4280                                 } else if (dist_to_goal < 4*objp->radius + 50.0f) {
4281                                         speed = dist_to_goal/4.0f + 4.0f;
4282                                 } else {
4283                                         speed = sip->max_speed * (3*xdot + 1.0f)/4.0f;
4284                                 }
4285                                 if (aip->mode == AIM_DOCK) {
4286                                         speed = speed * 2.0f + 1.0f;
4287                                         if (aip->goal_objnum != -1) {
4288                                                 speed += Objects[aip->goal_objnum].phys_info.speed;
4289                                         }
4290                                 }
4291
4292                                 set_accel_for_target_speed(objp, speed);
4293                         }
4294                 }
4295         }
4296 }
4297
4298 //      --------------------------------------------------------------------------
4299 //      Follow a path associated with a large object, such as a capital ship.
4300 //      The points defined on the path are in the object's reference frame.
4301 //      The object of interest is goal_objnum.
4302 //      The paths are defined in the model.  The path of interest is wp_list.
4303 //      The next goal point in the path is wp_index.
4304 //      wp_flags contain special information specific to the path.
4305
4306 // The path vertices are defined by model_path structs:
4307 //              typedef struct model_path {
4308 //                      char            name[MAX_NAME_LEN];                                     // name of the subsystem.  Probably displayed on HUD
4309 //                      int             nverts;
4310 //                      vector  *verts;
4311 //              } model_path;
4312
4313 //      The polymodel struct for the object contains the following:
4314 //              int                     n_paths;
4315 //              model_path      *paths;
4316
4317 //      Returns distance to goal point.
4318 float ai_path()
4319 {
4320         polymodel       *pm;
4321         int             num_paths, num_points;
4322         float           dot, dist_to_goal, dist_to_next, speed, dot_to_next;
4323         ship            *shipp = &Ships[Pl_objp->instance];
4324         ship_info       *sip = &Ship_info[shipp->ship_info_index];
4325         ai_info *aip;
4326         vector  nvel_vec;
4327         float           mag, prev_dot_to_goal;
4328         vector  temp_vec, *slop_vec;
4329         object  *gobjp;
4330         ship            *gshipp;
4331         vector  *cvp, *nvp, next_vec, gcvp, gnvp;               //      current and next vertices in global coordinates.
4332
4333         aip = &Ai_info[Ships[Pl_objp->instance].ai_index];
4334
4335         Assert(aip->goal_objnum != -1);
4336         Assert(Objects[aip->goal_objnum].type == OBJ_SHIP);
4337
4338         gobjp = &Objects[aip->goal_objnum];
4339         gshipp = &Ships[gobjp->instance];
4340
4341         pm = model_get( gshipp->modelnum );
4342         num_paths = pm->n_paths;
4343         Assert(num_paths > 0);
4344
4345         if (aip->path_start == -1) {
4346                 int path_num;
4347                 path_num = ai_return_path_num_from_dockbay(&Objects[aip->goal_objnum], aip->dockee_index);
4348                 Assert(aip->goal_objnum >= 0 && aip->goal_objnum < MAX_OBJECTS);
4349                 ai_find_path(Pl_objp, aip->goal_objnum, path_num, 0);
4350         }
4351
4352         // nprintf(("AI", "Frame: %i, Path index = %i/%i\n", AI_FrameCount, aip->path_cur-aip->path_start, aip->path_length));
4353
4354         maybe_recreate_path(Pl_objp, aip, 0);
4355
4356         num_points = aip->path_length;
4357
4358         //      Set cvp and nvp as pointers to current and next vertices of interest on path.
4359         cvp = &Path_points[aip->path_cur].pos;
4360         if ((aip->path_cur + aip->path_dir - aip->path_start < num_points) || (aip->path_cur + aip->path_dir < aip->path_start))
4361                 nvp = &Path_points[aip->path_cur + aip->path_dir].pos;
4362         else {
4363                 //      If this is 0, then path length must be 1 which means we have no direction!
4364                 Assert((aip->path_cur - aip->path_dir >= aip->path_start) && (aip->path_cur - aip->path_dir - aip->path_start < num_points));
4365                 //      Cleanup for above Assert() which we hit too near release. -- MK, 5/24/98.
4366                 if (aip->path_cur - aip->path_dir - aip->path_start >= num_points) {
4367                         if (aip->path_dir == 1)
4368                                 aip->path_cur = aip->path_start;
4369                         else
4370                                 aip->path_cur = aip->path_start + num_points - 1;
4371                 }
4372
4373                 vector  delvec;
4374                 vm_vec_sub(&delvec, cvp, &Path_points[aip->path_cur - aip->path_dir].pos);
4375                 vm_vec_normalize(&delvec);
4376                 vm_vec_scale_add(&next_vec, cvp, &delvec, 10.0f);
4377                 nvp = &next_vec;
4378         }
4379
4380         //      Interrupt if can't get to current goal point.  Debug only.
4381 /*      if (pp_collide(&Pl_objp->pos, cvp, gobjp, Pl_objp->radius)) {
4382                 Int3();
4383         }
4384 */
4385         //      See if can reach next point (as opposed to current point)
4386         //      However, don't do this if docking and next point is last point.
4387         //      That is, we don't want to pursue the last point under control of the
4388         //      path code.  In docking, this is a special hack.
4389         if ((aip->mode != AIM_DOCK) || ((aip->path_cur-aip->path_start) < num_points - 2)) {
4390                 if ((aip->path_cur + aip->path_dir > aip->path_start) && (aip->path_cur + aip->path_dir < aip->path_start + num_points-2)) {
4391                         if ( timestamp_elapsed(aip->path_next_check_time)) {
4392                                 aip->path_next_check_time = timestamp( 3000 );
4393                                 if (!pp_collide(&Pl_objp->pos, nvp, gobjp, 1.1f * Pl_objp->radius)) {
4394                                         cvp = nvp;
4395                                         aip->path_cur += aip->path_dir;
4396                                         nvp = &Path_points[aip->path_cur].pos;
4397                                         //nprintf(("AI", "Reach: Advancing from point %i to %i of %i points.\n", aip->path_cur-aip->path_dir, aip->path_cur, num_points));
4398                                 }
4399                         }
4400                 }
4401         }
4402
4403         gcvp = *cvp;
4404         gnvp = *nvp;
4405
4406         speed = Pl_objp->phys_info.speed;
4407
4408         dist_to_goal = vm_vec_dist_quick(&Pl_objp->pos, &gcvp);
4409         dist_to_next = vm_vec_dist_quick(&Pl_objp->pos, &gnvp);
4410         //      Can't use fvec, need to use velocity vector because we aren't necessarily
4411         //      moving in the direction we're facing.
4412
4413 //      if (IS_VEC_NULL(&Pl_objp->phys_info.vel)) {
4414         if ( vm_vec_mag_quick(&Pl_objp->phys_info.vel) < AICODE_SMALL_MAGNITUDE ) {
4415                 mag = 0.0f;
4416                 vm_vec_zero(&nvel_vec);
4417         } else
4418                 mag = vm_vec_copy_normalize(&nvel_vec, &Pl_objp->phys_info.vel);
4419
4420         //      If moving not-very-slowly and sliding, then try to slide at goal, rather than
4421         //      point at goal.
4422         slop_vec = NULL;
4423         if (mag < 1.0f)
4424                 nvel_vec = Pl_objp->orient.fvec;
4425         else if (mag > 5.0f) {
4426                 float   nv_dot;
4427                 nv_dot = vm_vec_dot(&Pl_objp->orient.fvec, &nvel_vec);
4428                 if ((nv_dot > 0.5f) && (nv_dot < 0.97f)) {
4429                         slop_vec = &temp_vec;
4430                         vm_vec_sub(slop_vec, &nvel_vec, &Pl_objp->orient.fvec);
4431                 }
4432         }
4433
4434         if (dist_to_goal > 0.1f)
4435                 ai_turn_towards_vector(&gcvp, Pl_objp, flFrametime, sip->srotation_time, slop_vec, NULL, 0.0f, 0);
4436
4437         //      Code to control speed is MUCH less forgiving in path following than in waypoint
4438         //      following.  Must be very close to path or might hit objects.
4439         prev_dot_to_goal = aip->prev_dot_to_goal;
4440         dot = vm_vec_dot_to_point(&nvel_vec, &Pl_objp->pos, &gcvp);
4441         dot_to_next = vm_vec_dot_to_point(&nvel_vec, &Pl_objp->pos, &gnvp);
4442
4443         set_accel_for_docking(Pl_objp, aip, dot, dot_to_next, dist_to_next, dist_to_goal, sip);
4444         aip->prev_dot_to_goal = dot;
4445
4446 //mprintf(("Goal index = %i, dist = %7.3f, dot = %7.3f\n", wp_index, dist_to_goal, dot));
4447
4448         //      If moving at a non-tiny velocity, detect attaining path point by its being close to
4449         //      line between previous and current object location.
4450         if ((dist_to_goal < MIN_DIST_TO_WAYPOINT_GOAL) || (vm_vec_dist_quick(&Pl_objp->last_pos, &Pl_objp->pos) > 0.1f)) {
4451                 vector  nearest_point;
4452                 float           r, min_dist_to_goal;
4453
4454                 r = find_nearest_point_on_line(&nearest_point, &Pl_objp->last_pos, &Pl_objp->pos, &gcvp);
4455
4456                 //      Set min_dist_to_goal = how close must be to waypoint to pick next one.
4457                 //      If docking and this is the second last waypoint, must be very close.
4458                 if ((aip->mode == AIM_DOCK) && (aip->path_cur >= aip->path_length-2))
4459                         min_dist_to_goal = MIN_DIST_TO_WAYPOINT_GOAL;
4460                 else
4461                         min_dist_to_goal = MIN_DIST_TO_WAYPOINT_GOAL + Pl_objp->radius;
4462
4463                 if ( (vm_vec_dist_quick(&Pl_objp->pos, &gcvp) < min_dist_to_goal) ||
4464                         ((r >= 0.0f) && (r <= 1.0f)) && (vm_vec_dist_quick(&nearest_point, &gcvp) < (MIN_DIST_TO_WAYPOINT_GOAL + Pl_objp->radius))) {
4465                         aip->path_cur += aip->path_dir;
4466                         //nprintf(("AI", " Near: Advancing from point %i to %i of %i points.\n", aip->path_cur-aip->path_dir, aip->path_cur, num_points));
4467                         if (((aip->path_cur - aip->path_start) > (num_points+1)) || (aip->path_cur < aip->path_start)) {
4468                                 Assert(aip->mode != AIM_DOCK);          //      If docking, should never get this far, getting to last point handled outside ai_path()
4469                                 aip->path_dir = -aip->path_dir;
4470 //                              aip->path_cur += aip->path_dir;
4471                         }
4472                 }
4473         }
4474
4475         return dist_to_goal;
4476 }
4477
4478 void update_min_max(float val, float *min, float *max)
4479 {
4480         if (val < *min)
4481                 *min = val;
4482         else if (val > *max)
4483                 *max = val;
4484 }
4485
4486 //      Stuff bounding box of all enemy objects within "range" units of object *my_objp.
4487 //      Stuff ni min_vec and max_vec.
4488 //      Return value: Number of enemy objects in bounding box.
4489 int get_enemy_team_range(object *my_objp, float range, int enemy_team_mask, vector *min_vec, vector *max_vec)
4490 {
4491         object  *objp;
4492         ship_obj        *so;
4493         int             count = 0;
4494
4495         for ( so = GET_FIRST(&Ship_obj_list); so != END_OF_LIST(&Ship_obj_list); so = GET_NEXT(so) ) {
4496                 objp = &Objects[so->objnum];
4497                 if (Ships[objp->instance].team & enemy_team_mask) {
4498                         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))
4499                                 if (vm_vec_dist_quick(&my_objp->pos, &objp->pos) < range) {
4500                                         if (count == 0) {
4501                                                 *min_vec = objp->pos;
4502                                                 *max_vec = objp->pos;
4503                                                 count++;
4504                                         } else {
4505                                                 update_min_max(objp->pos.x, &min_vec->x, &max_vec->x);
4506                                                 update_min_max(objp->pos.y, &min_vec->y, &max_vec->y);
4507                                                 update_min_max(objp->pos.z, &min_vec->z, &max_vec->z);
4508                                         }
4509                                 }
4510
4511                 }
4512         }
4513
4514         return count;
4515 }
4516
4517 //      Pick a relatively safe spot for objp to fly to.
4518 //      Problem:
4519 //              Finds a spot away from any enemy within a bounding box.
4520 //              Doesn't verify that "safe spot" is not near some other enemy.
4521 void ai_safety_pick_spot(object *objp)
4522 {
4523         int             objnum;
4524         int             enemy_team_mask;
4525         vector  min_vec, max_vec;
4526         vector  vec_to_center, center;
4527         vector  goal_pos;
4528
4529         objnum = OBJ_INDEX(objp);
4530
4531         enemy_team_mask = get_enemy_team_mask(objnum);
4532
4533         if (get_enemy_team_range(objp, 1000.0f, enemy_team_mask, &min_vec, &max_vec)) {
4534                 vm_vec_avg(&center, &min_vec, &max_vec);
4535                 vm_vec_normalized_dir(&vec_to_center, &center, &objp->pos);
4536
4537                 vm_vec_scale_add(&goal_pos, &center, &vec_to_center, 2000.0f);
4538         } else
4539                 vm_vec_scale_add(&goal_pos, &objp->pos, &objp->orient.fvec, 100.0f);
4540
4541         Ai_info[Ships[objp->instance].ai_index].goal_point = goal_pos;
4542 }
4543
4544 //      Fly to desired safe point.
4545 // Returns distance to that point.
4546 float ai_safety_goto_spot(object *objp)
4547 {
4548         float   dot, dist;
4549         ai_info *aip;
4550         vector  vec_to_goal;
4551         ship_info       *sip;
4552         float   dot_val;
4553
4554         sip = &Ship_info[Ships[objp->instance].ship_info_index];
4555
4556         aip = &Ai_info[Ships[objp->instance].ai_index];
4557         dist = vm_vec_normalized_dir(&vec_to_goal, &aip->goal_point, &objp->pos);
4558         dot = vm_vec_dot(&vec_to_goal, &objp->orient.fvec);
4559
4560         dot_val = (1.1f + dot) / 2.0f;
4561         if (dist > 200.0f) {
4562                 set_accel_for_target_speed(objp, sip->max_speed * dot_val);
4563         } else
4564                 set_accel_for_target_speed(objp, sip->max_speed * dot_val * (dist/200.0f + 0.2f));
4565
4566         return dist;
4567 }
4568
4569 void ai_safety_circle_spot(object *objp)
4570 {
4571         vector  goal_point;
4572         ship_info       *sip;
4573         float           dot;
4574
4575         sip = &Ship_info[Ships[objp->instance].ship_info_index];
4576
4577         goal_point = Ai_info[Ships[objp->instance].ai_index].goal_point;
4578         dot = turn_towards_tangent(objp, &goal_point, 250.0f);  //      Increased from 50 to 250 to make circling not look so wacky.
4579
4580         set_accel_for_target_speed(objp, 0.5f * (1.0f + dot) * sip->max_speed/4.0f);
4581
4582 //      float dist = vm_vec_dist_quick(&goal_point, &Pl_objp->pos);
4583 //      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));
4584
4585 }
4586
4587 //      --------------------------------------------------------------------------
4588 void ai_safety()
4589 {
4590         ai_info *aip;
4591
4592         aip = &Ai_info[Ships[Pl_objp->instance].ai_index];
4593
4594         switch (aip->submode) {
4595         case AISS_1:
4596                 ai_safety_pick_spot(Pl_objp);
4597                 aip->submode = AISS_2;
4598                 aip->submode_start_time = Missiontime;
4599                 break;
4600         case AISS_1a:   //      Pick a safe point because we just got whacked!
4601                 Int3();
4602                 break;
4603         case AISS_2:
4604                 if (ai_safety_goto_spot(Pl_objp) < 25.0f) {
4605                         aip->submode = AISS_3;
4606                         aip->submode_start_time = Missiontime;
4607                 }
4608                 break;
4609         case AISS_3:
4610                 ai_safety_circle_spot(Pl_objp);
4611                 break;
4612         default:
4613                 Int3();         //      Illegal submode for ai_safety();
4614                 break;
4615         }
4616 }
4617
4618 //      --------------------------------------------------------------------------
4619 //      make Pl_objp fly waypoints.
4620 void ai_waypoints()
4621 {
4622         int             wp_index;
4623         vector  *wp_cur, *wp_next;
4624         float           dot, dist_to_goal, dist_to_next, speed, dot_to_next;
4625         ship            *shipp = &Ships[Pl_objp->instance];
4626         ship_info       *sip = &Ship_info[shipp->ship_info_index];
4627         waypoint_list   *wpl;
4628         ai_info *aip;
4629         vector  nvel_vec;
4630         float           mag;
4631         float           prev_dot_to_goal;
4632         vector  temp_vec;
4633         vector  *slop_vec;
4634
4635         aip = &Ai_info[Ships[Pl_objp->instance].ai_index];
4636
4637         wp_index = aip->wp_index;
4638
4639         if (wp_index == -1) {
4640                 ai_start_waypoints(Pl_objp, 0, WPF_REPEAT);
4641                 wp_index = aip->wp_index;
4642                 aip->wp_dir = 1;
4643         }
4644
4645         wpl = &Waypoint_lists[Ai_info[Ships[Pl_objp->instance].ai_index].wp_list];
4646
4647         Assert(wpl->count);     // What? Is this zero? Probably wp_index never got initialized!
4648
4649         wp_cur = &wpl->waypoints[wp_index];
4650         wp_next = &wpl->waypoints[(wp_index+1) % wpl->count];
4651         speed = Pl_objp->phys_info.speed;
4652
4653         dist_to_goal = vm_vec_dist_quick(&Pl_objp->pos, wp_cur);
4654         dist_to_next = vm_vec_dist_quick(&Pl_objp->pos, wp_next);
4655
4656         //      Can't use fvec, need to use velocity vector because we aren't necessarily
4657         //      moving in the direction we're facing.
4658         // AL 23-3-98: Account for very small velocities by checking result of vm_vec_mag().
4659         //                                      If we don't vm_vec_copy_normalize() will think it is normalizing a null vector.
4660 //      if (IS_VEC_NULL(&Pl_objp->phys_info.vel)) {
4661         if ( vm_vec_mag_quick(&Pl_objp->phys_info.vel) < AICODE_SMALL_MAGNITUDE ) {
4662                 mag = 0.0f;
4663                 vm_vec_zero(&nvel_vec);
4664         } else {
4665                 mag = vm_vec_copy_normalize(&nvel_vec, &Pl_objp->phys_info.vel);
4666         }
4667
4668         //      If moving not-very-slowly and sliding, then try to slide at goal, rather than
4669         //      point at goal.
4670         slop_vec = NULL;
4671         if (mag < 1.0f) {
4672                 nvel_vec = Pl_objp->orient.fvec;
4673         } else if (mag > 5.0f) {
4674                 float   nv_dot;
4675                 nv_dot = vm_vec_dot(&Pl_objp->orient.fvec, &nvel_vec);
4676                 if ((nv_dot > 0.5f) && (nv_dot < 0.97f)) {
4677                         slop_vec = &temp_vec;
4678                         vm_vec_sub(slop_vec, &nvel_vec, &Pl_objp->orient.fvec);
4679                 }
4680         }
4681
4682         //      If a wing leader, take turns more slowly, based on size of wing.
4683         int     scale;
4684
4685         if (Ai_info[Ships[Pl_objp->instance].ai_index].wing >= 0) {
4686                 scale = Wings[Ai_info[Ships[Pl_objp->instance].ai_index].wing].current_count;
4687                 scale = (int) ((scale+1)/2);
4688         } else {
4689                 scale = 1;
4690         }
4691
4692         if (dist_to_goal > 0.1f) {
4693                 ai_turn_towards_vector(wp_cur, Pl_objp, flFrametime, sip->srotation_time*3.0f*scale, slop_vec, NULL, 0.0f, 0);
4694         }
4695
4696         prev_dot_to_goal = aip->prev_dot_to_goal;
4697         dot = vm_vec_dot_to_point(&nvel_vec, &Pl_objp->pos, wp_cur);
4698         dot_to_next = vm_vec_dot_to_point(&nvel_vec, &Pl_objp->pos, wp_next);
4699         aip->prev_dot_to_goal = dot;
4700
4701         //      If there is no next point on the path, don't care about dot to next.
4702         if (wp_index + 1 >= wpl->count) {
4703                 dot_to_next = dot;
4704         }
4705
4706         // nprintf(("AI", "Wp #%i, dot = %6.3f, next dot = %6.3f, dist = %7.2f\n", wp_index, dot, dot_to_next, dist_to_goal));
4707
4708         if (Pl_objp->phys_info.speed < 0.0f) {
4709                 accelerate_ship(aip, 1.0f/32);
4710         } else if (prev_dot_to_goal > dot+0.01f) {
4711                 //      We are further from pointing at our goal this frame than last frame, so slow down.
4712                 set_accel_for_target_speed(Pl_objp, Pl_objp->phys_info.speed * 0.95f);
4713         } else if (dist_to_goal < 100.0f) {
4714                 float slew_dot = vm_vec_dot(&Pl_objp->orient.fvec, &nvel_vec);
4715                 if (fl_abs(slew_dot) < 0.9f) {
4716                         accelerate_ship(aip, 0.0f);
4717                 } else if (dot < 0.88f + 0.1f*(100.0f - dist_to_goal)/100.0f) {
4718                         accelerate_ship(aip, 0.0f);
4719                 } else {
4720                         accelerate_ship(aip, 0.5f * dot * dot);
4721                 }
4722         } else {
4723                 float   dot1;
4724                 if (dist_to_goal < 250.0f) {
4725                         dot1 = dot*dot*dot;                             //      Very important to be pointing towards goal when nearby.  Note, cubing preserves sign.
4726                 } else {
4727                         if (dot > 0.0f) {
4728                                 dot1 = dot*dot;
4729                         } else {
4730                                 dot1 = dot;
4731                         }
4732                 }
4733
4734                 if (dist_to_goal > 100.0f + Pl_objp->radius * 2) {
4735                         if (dot < 0.2f) {
4736                                 dot1 = 0.2f;
4737                         }
4738                 }
4739
4740                 if (sip->flags & SIF_SMALL_SHIP) {
4741                         set_accel_for_target_speed(Pl_objp, dot1 * dist_to_goal/5.0f);
4742                 } else {
4743                         set_accel_for_target_speed(Pl_objp, dot1 * dist_to_goal/10.0f);
4744                 }
4745         }
4746
4747         //      Make sure not travelling too fast for someone to keep up.
4748         float   max_allowed_speed = 9999.9f;
4749
4750         if (shipp->wingnum != -1) {
4751                 max_allowed_speed = 0.9f * get_wing_lowest_max_speed(Pl_objp);
4752         }
4753
4754         // check if waypoint speed cap is set and adjust max speed
4755         if (aip->waypoint_speed_cap > 0) {
4756                 max_allowed_speed = (float) aip->waypoint_speed_cap;
4757         }
4758
4759         if (aip->prev_accel * shipp->current_max_speed > max_allowed_speed) {
4760                 accelerate_ship(aip, max_allowed_speed / shipp->current_max_speed);
4761         }
4762
4763         if (vm_vec_dist_quick(&Pl_objp->last_pos, &Pl_objp->pos) > 0.1f) {
4764                 vector  nearest_point;
4765                 float           r;
4766
4767                 r = find_nearest_point_on_line(&nearest_point, &Pl_objp->last_pos, &Pl_objp->pos, wp_cur);
4768
4769                 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))) ||
4770                         ((r >= 0.0f) && (r <= 1.0f)) && (vm_vec_dist_quick(&nearest_point, wp_cur) < (MIN_DIST_TO_WAYPOINT_GOAL + fl_sqrt(Pl_objp->radius)))) {
4771                         wp_index++;
4772                         if (wp_index >= wpl->count)
4773                                 if (aip->wp_flags & WPF_REPEAT) {
4774                                         wp_index = 0;
4775                                 } else {
4776                                         int treat_as_ship;
4777
4778                                         // when not repeating waypoints -- mark the goal as done and put and entry into the mission log
4779                                         // we must be careful when dealing with wings.  A ship in a wing might be completing
4780                                         // a waypoint for for the entire wing, or it might be completing a goal for itself.  If
4781                                         // for itself and in a wing, treat the completion as we would a ship
4782                                         treat_as_ship = 1;
4783                                         if ( Ships[Pl_objp->instance].wingnum != -1 ) {
4784                                                 int type;
4785
4786                                                 // I don't think that you can fly waypoints as dynamic goals!!!
4787                                                 // -- This is legal, just stupid. -- Assert( (aip->active_goal != AI_GOAL_NONE) && (aip->active_goal != AI_ACTIVE_GOAL_DYNAMIC) );
4788                                                 
4789                                                 //      Clean up from above Assert, just in case we ship without fixing it.  (Encountered by JimB on 2/9/98)
4790                                                 if ( (aip->active_goal == AI_GOAL_NONE) || (aip->active_goal == AI_ACTIVE_GOAL_DYNAMIC) ) {
4791                                                         aip->mode = AIM_NONE;
4792                                                         Int3(); //      Look at the ship, find out of it's supposed to be flying waypoints. -- MK.
4793                                                 }
4794
4795                                                 type = aip->goals[aip->active_goal].type;
4796                                                 if ( (type == AIG_TYPE_EVENT_WING) || (type == AIG_TYPE_PLAYER_WING) ) {
4797                                                         treat_as_ship = 0;
4798                                                 } else {
4799                                                         treat_as_ship = 1;
4800                                                 }
4801                                         }
4802
4803                                         // if the ship is not in a wing, remove the goal and continue on
4804                                         if ( treat_as_ship ) {
4805                                                 ai_mission_goal_complete( aip );                                        // this call should reset the AI mode
4806                                                 mission_log_add_entry(LOG_WAYPOINTS_DONE, Ships[Pl_objp->instance].ship_name, wpl->name, -1 );
4807                                         } else {
4808                                                 // this ship is in a wing.  We must mark the goal as being completed for all ships
4809                                                 // in the wing.  We will also mark an entry in the log that the wing completed the goal
4810                                                 // not the individual ship.
4811                                                 ai_mission_wing_goal_complete( Ships[Pl_objp->instance].wingnum, &(aip->goals[aip->active_goal]) );
4812                                                 mission_log_add_entry( LOG_WAYPOINTS_DONE, Wings[Ships[Pl_objp->instance].wingnum].name, wpl->name, -1 );
4813                                         }
4814                                         //wp_index = wpl->count-1;
4815                                 }
4816
4817                         aip->wp_index = wp_index;
4818                 }
4819         }
4820 }
4821
4822 //      Make Pl_objp avoid En_objp
4823 //      Not like evading.  This is for avoiding a collision!
4824 //      Note, use sliding if available.
4825 void avoid_ship()
4826 {
4827         //      To avoid an object, turn towards right or left vector until facing away from object.
4828         //      To choose right vs. left, pick one that is further from center of avoid object.
4829         //      Keep turning away from until pointing away from ship.
4830         //      Stay in avoid mode until at least 3 enemy ship radii away.
4831
4832         //      Speed setting:
4833         //      If inside sphere, zero speed and turn towards outside.
4834         //      If outside sphere, inside 2x sphere, set speed percent of max to:
4835         //              max(away_dot, (dist-rad)/rad)
4836         //      where away_dot is dot(Pl_objp->fvec, vec_En_objp_to_Pl_objp)
4837
4838         vector  vec_to_enemy;
4839         float           away_dot;
4840         float           dist;
4841         ship            *shipp = &Ships[Pl_objp->instance];
4842         ship_info       *sip = &Ship_info[shipp->ship_info_index];
4843         ai_info *aip = &Ai_info[shipp->ai_index];
4844         vector  player_pos, enemy_pos;
4845
4846         // if we're avoiding a stealth ship, then we know where he is, update with no error
4847         if ( is_object_stealth_ship(En_objp) ) {
4848                 update_ai_stealth_info_with_error(aip/*, 1*/);
4849         }
4850
4851         ai_set_positions(Pl_objp, En_objp, aip, &player_pos, &enemy_pos);
4852         vm_vec_sub(&vec_to_enemy, &enemy_pos, &Pl_objp->pos);
4853
4854         dist = vm_vec_normalize(&vec_to_enemy);
4855         away_dot = -vm_vec_dot(&Pl_objp->orient.fvec, &vec_to_enemy);
4856         
4857         if ((sip->max_vel.x > 0.0f) || (sip->max_vel.y > 0.0f)) {
4858                 if (vm_vec_dot(&Pl_objp->orient.rvec, &vec_to_enemy) > 0.0f) {
4859                         AI_ci.sideways = -1.0f;
4860                 } else {
4861                         AI_ci.sideways = 1.0f;
4862                 }
4863                 if (vm_vec_dot(&Pl_objp->orient.uvec, &vec_to_enemy) > 0.0f) {
4864                         AI_ci.vertical = -1.0f;
4865                 } else {
4866                         AI_ci.vertical = 1.0f;
4867                 }
4868         }               
4869
4870         //nprintf(("AI", "Frame %i: Sliding: %s %s\n", Framecount, AI_ci.sideways < 0 ? "left" : "right", AI_ci.vertical < 0 ? "down" : "up" ));
4871         // nprintf(("AI", "away_dot = %6.3f, dist = %7.2f, dist/radsum = %6.3f\n", away_dot, dist, dist/(Pl_objp->radius + En_objp->radius)));
4872
4873         //      If in front of enemy, turn away from it.
4874         //      If behind enemy, try to get fully behind it.
4875         if (away_dot < 0.0f) {
4876                 turn_away_from_point(Pl_objp, &enemy_pos, Pl_objp->phys_info.speed);
4877         } else {
4878                 vector  goal_pos;
4879
4880                 vm_vec_scale_add(&goal_pos, &En_objp->pos, &En_objp->orient.fvec, -100.0f);
4881                 turn_towards_point(Pl_objp, &goal_pos, NULL, Pl_objp->phys_info.speed);
4882         }
4883
4884         //      Set speed.
4885         float   radsum = Pl_objp->radius + En_objp->radius;
4886
4887         if (dist < radsum)
4888                 accelerate_ship(aip, max(away_dot, 0.2f));
4889         else if (dist < 2*radsum)
4890                 accelerate_ship(aip, max(away_dot, (dist - radsum) / radsum)+0.2f);
4891         else
4892                 accelerate_ship(aip, 1.0f);
4893
4894 }
4895
4896 //      Maybe it's time to resume the previous AI mode in aip->previous_mode.
4897 //      Each type of previous_mode has its own criteria on when to resume.
4898 //      Return true if previous mode was resumed.
4899 int maybe_resume_previous_mode(object *objp, ai_info *aip)
4900 {
4901         //      Only (maybe) resume previous goal if current goal is dynamic.
4902         if (aip->active_goal != AI_ACTIVE_GOAL_DYNAMIC)
4903                 return 0;
4904
4905         if (aip->mode == AIM_EVADE_WEAPON) {
4906                 if (timestamp_elapsed(aip->mode_time) || (((aip->nearest_locked_object == -1) || (Objects[aip->nearest_locked_object].type != OBJ_WEAPON)) && (aip->danger_weapon_objnum == -1))) {
4907                         Assert(aip->previous_mode != AIM_EVADE_WEAPON);
4908                         aip->mode = aip->previous_mode;
4909                         aip->submode = aip->previous_submode;
4910                         aip->submode_start_time = Missiontime;
4911                         aip->active_goal = AI_GOAL_NONE;
4912                         aip->mode_time = -1;                    //      Means do forever.
4913                         return 1;
4914                 }
4915         } else if ( aip->previous_mode == AIM_GUARD) {
4916                 if ((aip->guard_objnum != -1) && (aip->guard_signature == Objects[aip->guard_objnum].signature)) {
4917                         object  *guard_objp;
4918                         float   dist;
4919
4920                         guard_objp = &Objects[aip->guard_objnum];
4921                         dist = vm_vec_dist_quick(&guard_objp->pos, &objp->pos);
4922
4923                         //      If guarding ship is far away from guardee and enemy is far away from guardee,
4924                         //      then stop chasing and resume guarding.
4925                         if (dist > (MAX_GUARD_DIST + guard_objp->radius) * 6) {
4926                                 if ((En_objp != NULL) && (En_objp->type == OBJ_SHIP)) {
4927                                         if (vm_vec_dist_quick(&guard_objp->pos, &En_objp->pos) > (MAX_GUARD_DIST + guard_objp->radius) * 6) {
4928                                                 Assert(aip->previous_mode == AIM_GUARD);
4929                                                 aip->mode = aip->previous_mode;
4930                                                 aip->submode = AIS_GUARD_PATROL;
4931                                                 aip->active_goal = AI_GOAL_NONE;
4932                                                 return 1;
4933                                         }
4934                                 }
4935                         }
4936                 }
4937         }
4938
4939         return 0;
4940
4941 }
4942
4943 //      Call this function if you want something to happen on average every N quarters of a second.
4944 //      The truth value returned by this function will be the same for any given quarter second interval.
4945 //      The value "num" is only passed in to get asynchronous behavior for different objects.
4946 //      modulus == 1 will always return true.
4947 //      modulus == 2 will return true half the time.
4948 //      modulus == 16 will return true for one quarter second interval every four seconds.
4949 int static_rand_timed(int num, int modulus)
4950 {
4951         if (modulus < 2)
4952                 return 1;
4953         else {
4954                 int     t;
4955
4956                 t = Missiontime >> 18;          //      Get time in quarters of a second
4957                 t += num;
4958
4959                 return !(t % modulus);
4960         }
4961 }
4962
4963 //      Maybe fire afterburner based on AI class
4964 int ai_maybe_fire_afterburner(object *objp, ai_info *aip)
4965 {
4966         if (aip->ai_class == 0)
4967                 return 0;               //      Lowest level never aburners away
4968         else  {
4969                 //      Maybe don't afterburner because of a potential collision with the player.
4970                 //      If not multiplayer, near player and player in front, probably don't afterburner.
4971                 if (!(Game_mode & GM_MULTIPLAYER)) {
4972                         if (Ships[objp->instance].team == Player_ship->team) {
4973                                 float   dist;
4974
4975                                 dist = vm_vec_dist_quick(&objp->pos, &Player_obj->pos) - Player_obj->radius - objp->radius;
4976                                 if (dist < 150.0f) {
4977                                         vector  v2p;
4978                                         float           dot;
4979
4980                                         vm_vec_normalized_dir(&v2p, &Player_obj->pos, &objp->pos);
4981                                         dot = vm_vec_dot(&v2p, &objp->orient.fvec);
4982
4983                                         if (dot > 0.0f) {
4984                                                 if (dot * dist > 50.0f)
4985                                                         return 0;
4986                                         }
4987                                 }
4988                         }
4989                 }
4990
4991                 if (aip->ai_class >= Num_ai_classes-2)
4992                         return 1;               //      Highest two levels always aburner away.
4993                 else {
4994                         return static_rand_timed(objp-Objects, Num_ai_classes - aip->ai_class);
4995                 }
4996         }
4997 }
4998
4999 //      Maybe engage afterburner after being hit by an object.
5000 void maybe_afterburner_after_ship_hit(object *objp, ai_info *aip, object *en_objp)
5001 {
5002         //      Only do if facing a little away.
5003         if (en_objp != NULL) {
5004                 vector  v2e;
5005
5006                 vm_vec_normalized_dir(&v2e, &en_objp->pos, &objp->pos);
5007                 if (vm_vec_dot(&v2e, &objp->orient.fvec) > -0.5f)
5008                         return;
5009         }
5010
5011         if (!( objp->phys_info.flags & PF_AFTERBURNER_ON )) {
5012                 if (ai_maybe_fire_afterburner(objp, aip)) {
5013                         afterburners_start(objp);
5014                         aip->afterburner_stop_time = Missiontime + F1_0/2;
5015                 }
5016         }
5017 }
5018
5019 //      Return true if object *objp is an instructor.
5020 //      Is an instructor if name begins INSTRUCTOR_SHIP_NAME else not.
5021 int is_instructor(object *objp)
5022 {
5023         return !strnicmp(Ships[objp->instance].ship_name, INSTRUCTOR_SHIP_NAME, strlen(INSTRUCTOR_SHIP_NAME));
5024 }
5025
5026 //      Evade the weapon aip->danger_weapon_objnum
5027 //      If it's not valid, do a quick out.
5028 //      Evade by accelerating hard.
5029 //      If necessary, turn hard left or hard right.
5030 void evade_weapon()
5031 {
5032         object  *weapon_objp = NULL;
5033         object  *unlocked_weapon_objp = NULL, *locked_weapon_objp = NULL;
5034         vector  weapon_pos, player_pos, goal_point;
5035         vector  vec_from_enemy;
5036         float           dot_from_enemy, dot_to_enemy;
5037         float           dist;
5038         ship            *shipp = &Ships[Pl_objp->instance];
5039         ai_info *aip = &Ai_info[shipp->ai_index];
5040
5041         if (is_instructor(Pl_objp))
5042                 return;
5043
5044         //      Make sure we're actually being attacked.
5045         //      Favor locked objects.
5046         if (aip->nearest_locked_object != -1) {
5047                 if (Objects[aip->nearest_locked_object].type == OBJ_WEAPON)
5048                         locked_weapon_objp = &Objects[aip->nearest_locked_object];
5049         }
5050         
5051         if (aip->danger_weapon_objnum != -1)
5052                 if (Objects[aip->danger_weapon_objnum].signature == aip->danger_weapon_signature)
5053                         unlocked_weapon_objp = &Objects[aip->danger_weapon_objnum];
5054                 else
5055                         aip->danger_weapon_objnum = -1;         //      Signatures don't match, so no longer endangered.
5056
5057         if (locked_weapon_objp != NULL) {
5058                 if (unlocked_weapon_objp != NULL) {
5059                         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))
5060                                 weapon_objp = locked_weapon_objp;
5061                         else
5062                                 weapon_objp = unlocked_weapon_objp;
5063                 } else
5064                         weapon_objp = locked_weapon_objp;
5065         } else if (unlocked_weapon_objp != NULL)
5066                 weapon_objp = unlocked_weapon_objp;
5067         else {
5068                 if (aip->mode == AIM_EVADE_WEAPON)
5069                         maybe_resume_previous_mode(Pl_objp, aip);
5070                 return;
5071         }
5072
5073         Assert(weapon_objp != NULL);
5074
5075         if (weapon_objp->type != OBJ_WEAPON) {
5076                 if (aip->mode == AIM_EVADE_WEAPON)
5077                         maybe_resume_previous_mode(Pl_objp, aip);
5078                 return;
5079         }
5080         
5081         weapon_pos = weapon_objp->pos;
5082         player_pos = Pl_objp->pos;
5083
5084         //      Make speed based on skill level, varying at highest skill level, which is harder to hit.
5085         accelerate_ship(aip, 1.0f);
5086
5087         dist = vm_vec_normalized_dir(&vec_from_enemy, &player_pos, &weapon_pos);
5088
5089         dot_to_enemy = -vm_vec_dot(&Pl_objp->orient.fvec, &vec_from_enemy);
5090         dot_from_enemy = vm_vec_dot(&weapon_objp->orient.fvec, &vec_from_enemy);
5091         //nprintf(("AI", "dot from enemy = %7.3f\n", dot_from_enemy));
5092
5093         //      If shot is incoming...
5094         if (dot_from_enemy < 0.3f) {
5095                 if (weapon_objp == unlocked_weapon_objp)
5096                         aip->danger_weapon_objnum = -1;
5097                 return;
5098         } else if (dot_from_enemy > 0.7f) {
5099                 if (dist < 200.0f) {
5100                         if (!( Pl_objp->phys_info.flags & PF_AFTERBURNER_ON )) {
5101                                 if (ai_maybe_fire_afterburner(Pl_objp, aip)) {
5102                                         //nprintf(("AI", "Frame %i, turning on afterburner.\n", AI_FrameCount));
5103                                         afterburners_start(Pl_objp);
5104                                         aip->afterburner_stop_time = Missiontime + F1_0/2;
5105                                 }
5106                         }
5107                 }
5108
5109                 //      If we're sort of pointing towards it...
5110                 if ((dot_to_enemy < -0.5f) || (dot_to_enemy > 0.5f)) {
5111                         float   rdot;
5112
5113                         //      Turn hard left or right, depending on which gets out of way quicker.
5114                         rdot = vm_vec_dot(&Pl_objp->orient.rvec, &vec_from_enemy);
5115
5116                         if ((rdot < -0.5f) || (rdot > 0.5f))
5117                                 vm_vec_scale_add(&goal_point, &Pl_objp->pos, &Pl_objp->orient.rvec, -200.0f);
5118                         else
5119                                 vm_vec_scale_add(&goal_point, &Pl_objp->pos, &Pl_objp->orient.rvec, 200.0f);
5120
5121                         turn_towards_point(Pl_objp, &goal_point, NULL, 0.0f);
5122                 }
5123         }
5124
5125 }
5126
5127 //      Use sliding and backwards moving to face enemy.
5128 //      (Coded 2/20/98.  Works fine, but it's hard to see how to integrate it into the AI system.
5129 //       Typically ships are moving so fast that a little sliding isn't enough to gain an advantage.
5130 //       It's currently used to avoid collisions and could be used to evade weapon fire, but the latter
5131 //       would be frustrating, I think.
5132 //       This function is currently not called.)
5133 void slide_face_ship()
5134 {
5135         ship_info       *sip;
5136
5137         sip = &Ship_info[Ships[Pl_objp->instance].ship_info_index];
5138
5139         //      If can't slide, return.
5140         if ((sip->max_vel.x == 0.0f) && (sip->max_vel.y == 0.0f))
5141                 return;
5142
5143         vector  goal_pos;
5144         float           dot_from_enemy, dot_to_enemy;
5145         vector  vec_from_enemy, vec_to_goal;
5146         float           dist;
5147         float           up, right;
5148         ai_info         *aip;
5149
5150         aip = &Ai_info[Ships[Pl_objp->instance].ai_index];
5151
5152         dist = vm_vec_normalized_dir(&vec_from_enemy, &Pl_objp->pos, &En_objp->pos);
5153
5154         ai_turn_towards_vector(&En_objp->pos, Pl_objp, flFrametime, sip->srotation_time, NULL, NULL, 0.0f, 0);
5155
5156         dot_from_enemy = vm_vec_dot(&vec_from_enemy, &En_objp->orient.fvec);
5157         dot_to_enemy = -vm_vec_dot(&vec_from_enemy, &Pl_objp->orient.fvec);
5158
5159         if (vm_vec_dot(&vec_from_enemy, &En_objp->orient.rvec) > 0.0f)
5160                 right = 1.0f;
5161         else
5162                 right = -1.0f;
5163
5164         if (vm_vec_dot(&vec_from_enemy, &En_objp->orient.uvec) > 0.0f)
5165                 up = 1.0f;
5166         else
5167                 up = -1.0f;
5168
5169         vm_vec_scale_add(&goal_pos, &En_objp->pos, &En_objp->orient.rvec, right * 200.0f);
5170         vm_vec_scale_add(&goal_pos, &En_objp->pos, &En_objp->orient.uvec, up * 200.0f);
5171
5172         vm_vec_normalized_dir(&vec_to_goal, &goal_pos, &Pl_objp->pos);
5173
5174         if (vm_vec_dot(&vec_to_goal, &Pl_objp->orient.rvec) > 0.0f)
5175                 AI_ci.sideways = 1.0f;
5176         else
5177                 AI_ci.sideways = -1.0f;
5178
5179         if (vm_vec_dot(&vec_to_goal, &Pl_objp->orient.uvec) > 0.0f)
5180                 AI_ci.vertical = 1.0f;
5181         else
5182                 AI_ci.vertical = -1.0f;
5183
5184         if (dist < 200.0f) {
5185                 if (dot_from_enemy < 0.7f)
5186                         accelerate_ship(aip, -1.0f);
5187                 else
5188                         accelerate_ship(aip, dot_from_enemy + 0.5f);
5189         } else {
5190                 if (dot_from_enemy < 0.7f) {
5191                         accelerate_ship(aip, 0.2f);
5192                 } else {
5193                         accelerate_ship(aip, 1.0f);
5194                 }
5195         }
5196 }
5197
5198 //      General code for handling one ship evading another.
5199 //      Problem: This code is also used for avoiding an impending collision.
5200 //      In such a case, it is not good to go to max speed, which is often good
5201 //      for a certain kind of evasion.
5202 void evade_ship()
5203 {
5204         vector  player_pos, enemy_pos, goal_point;
5205         vector  vec_from_enemy;
5206         float           dot_from_enemy;
5207         float           dist;
5208         ship            *shipp = &Ships[Pl_objp->instance];
5209         ship_info       *sip = &Ship_info[shipp->ship_info_index];
5210         ai_info *aip = &Ai_info[shipp->ai_index];
5211         float           bank_override = 0.0f;
5212
5213         ai_set_positions(Pl_objp, En_objp, aip, &player_pos, &enemy_pos);
5214
5215         //      Make speed based on skill level, varying at highest skill level, which is harder to hit.
5216         if (Game_skill_level == NUM_SKILL_LEVELS-1) {
5217                 int     rand_int;
5218                 float   accel_val;
5219
5220                 rand_int = static_rand(Pl_objp-Objects);
5221                 accel_val = (float) (((Missiontime^rand_int) >> 14) & 0x0f)/32.0f + 0.5f;
5222                 accelerate_ship(aip, accel_val);
5223                 //nprintf(("AI", "Accel value = %7.3f\n", accel_val));
5224         } else
5225                 accelerate_ship(aip, (float) (Game_skill_level+2) / (NUM_SKILL_LEVELS+1));
5226
5227         if ((Missiontime - aip->submode_start_time > F1_0/2) && (sip->afterburner_fuel_capacity > 0.0f)) {
5228                 float percent_left = 100.0f * shipp->afterburner_fuel / sip->afterburner_fuel_capacity;
5229                 if (percent_left > 30.0f + ((Pl_objp-Objects) & 0x0f)) {
5230                         afterburners_start(Pl_objp);
5231                         aip->afterburner_stop_time = Missiontime + F1_0 + static_rand(Pl_objp-Objects)/4;
5232                 }
5233         }
5234
5235         vm_vec_sub(&vec_from_enemy, &player_pos, &enemy_pos);
5236
5237         dist = vm_vec_normalize(&vec_from_enemy);
5238         dot_from_enemy = vm_vec_dot(&En_objp->orient.fvec, &vec_from_enemy);
5239
5240         if (dist > 250.0f) {
5241                 vector  gp1, gp2;
5242                 //      If far away from enemy, circle, going to nearer of point far off left or right wing
5243                 vm_vec_scale_add(&gp1, &enemy_pos, &En_objp->orient.rvec, 250.0f);
5244                 vm_vec_scale_add(&gp2, &enemy_pos, &En_objp->orient.rvec, -250.0f);
5245                 if (vm_vec_dist_quick(&gp1, &Pl_objp->pos) < vm_vec_dist_quick(&gp2, &Pl_objp->pos))
5246                         goal_point = gp1;
5247                 else
5248                         goal_point = gp2;
5249         } else if (dot_from_enemy < 0.1f) {
5250                 //      If already close to behind, goal is to get completely behind.
5251                 vm_vec_scale_add(&goal_point, &enemy_pos, &En_objp->orient.fvec, -1000.0f);
5252         } else if (dot_from_enemy > 0.9f) {
5253                 //      If enemy pointing almost right at self, and self pointing close to enemy, turn away from
5254                 vector  vec_to_enemy;
5255                 float           dot_to_enemy;
5256
5257                 vm_vec_sub(&vec_to_enemy, &enemy_pos, &player_pos);
5258
5259                 vm_vec_normalize(&vec_to_enemy);
5260                 dot_to_enemy = vm_vec_dot(&Pl_objp->orient.fvec, &vec_to_enemy);
5261                 if (dot_to_enemy > 0.75f) {
5262                         //      Used to go to En_objp's right vector, but due to banking while turning, that
5263                         //      caused flying in an odd spiral.
5264                         vm_vec_scale_add(&goal_point, &enemy_pos, &Pl_objp->orient.rvec, 1000.0f);
5265                         if (dist < 100.0f)
5266                                 bank_override = Pl_objp->phys_info.speed; 
5267                 } else {
5268                         bank_override = Pl_objp->phys_info.speed;                       //      In enemy's sights, not pointing at him, twirl away.
5269                         // nprintf(("Mike", " Do sumpin' else."));
5270                         goto evade_ship_l1;
5271                 }
5272         } else {
5273 evade_ship_l1: ;
5274                 if (aip->ai_evasion > myrand()*100.0f/32767.0f) {
5275                         int     temp;
5276                         float   scale;
5277                         float   psrandval;      //      some value close to zero to choose whether to turn right or left.
5278
5279                         psrandval = (float) (((Missiontime >> 14) & 0x0f) - 8); //      Value between -8 and 7
5280                         psrandval = psrandval/16.0f;                                                    //      Value between -1/2 and 1/2 (approx)
5281
5282                         //      If not close to behind, turn towards his right or left vector, whichever won't cross his path.
5283                         if (vm_vec_dot(&vec_from_enemy, &En_objp->orient.rvec) > psrandval) {
5284                                 scale = 1000.0f;
5285                         } else {
5286                                 scale = -1000.0f;
5287                         }
5288
5289                         vm_vec_scale_add(&goal_point, &enemy_pos, &En_objp->orient.rvec, scale);
5290
5291                         temp = ((Missiontime >> 16) & 0x07);
5292                         temp = ((temp * (temp+1)) % 16)/2 - 4;
5293                         if ((psrandval == 0) && (temp == 0))
5294                                 temp = 3;
5295
5296                         scale = 200.0f * temp;
5297
5298                         vm_vec_scale_add2(&goal_point, &En_objp->orient.uvec, scale);
5299                 } else {
5300                         //      No evasion this frame, but continue with previous turn.
5301                         //      Reason: If you don't, you lose rotational momentum.  Turning every other frame,
5302                         //      and not in between results in a very slow turn because of loss of momentum.
5303                         if ((aip->prev_goal_point.x != 0.0f) || (aip->prev_goal_point.y != 0.0f) || (aip->prev_goal_point.z != 0.0f))
5304                                 goal_point = aip->prev_goal_point;
5305                         else
5306                                 vm_vec_scale_add(&goal_point, &enemy_pos, &En_objp->orient.rvec, 100.0f);
5307                 }
5308         }
5309
5310         // nprintf(("Mike", "Goal point = %7.1f %7.1f %7.1f\n", goal_point.x, goal_point.y, goal_point.z));
5311         turn_towards_point(Pl_objp, &goal_point, NULL, bank_override);
5312
5313         aip->prev_goal_point = goal_point;
5314 }
5315
5316 //      --------------------------------------------------------------------------
5317 //      Fly in a manner making it difficult for opponent to attack.
5318 void ai_evade()
5319 {
5320         evade_ship();
5321 }
5322
5323 /*
5324 // -------------------------------------------------------------------
5325 //      Refine predicted enemy position because enemy will move while we move
5326 //      towards predicted enemy position.
5327 //      last_delta_vec is stuffed with size of polishing in last step.  This small amount
5328 //      can be used to perturb the predicted position to make firing not be exact.
5329 //      This function will almost always undershoot actual position, assuming both ships
5330 //      are moving at constant speed.  But with even one polishing step, the error should
5331 //      be under 1%. The number of polishing steps is specified in the parameter num_polish_steps.
5332 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)
5333 {
5334         int     iteration;
5335         vector  player_pos = pobjp->pos;
5336         vector  enemy_pos = *predicted_enemy_pos;
5337         physics_info    *en_physp = &eobjp->phys_info;
5338         float           time_to_enemy;
5339         vector  last_predicted_enemy_pos = *predicted_enemy_pos;
5340         
5341         vm_vec_zero(last_delta_vec);
5342
5343         for (iteration=0; iteration < num_polish_steps; iteration++) {
5344                 dist_to_enemy = vm_vec_dist_quick(predicted_enemy_pos, &player_pos);
5345                 time_to_enemy = compute_time_to_enemy(dist_to_enemy, pobjp, eobjp);
5346                 vm_vec_scale_add(predicted_enemy_pos, &eobjp->pos, &eobjp->phys_info.vel, time_to_enemy);
5347                 vm_vec_sub(last_delta_vec, predicted_enemy_pos, &last_predicted_enemy_pos);
5348                 last_predicted_enemy_pos= *predicted_enemy_pos;
5349         }
5350 }
5351 */
5352
5353 /*
5354 Relevant variables are:
5355         best_dot_to_enemy               best dot product to enemy in last BEST_DOT_TIME seconds
5356         best_dot_to_time                time at which best dot occurred
5357         best_dot_from_enemy     best dot product for enemy to player in last BEST_DOT_TIME seconds
5358         best_dot_from_time      time at which best dot occurred
5359         submode_start_time      time at which we entered the current submode
5360         previous_submode                previous submode, get it?
5361 Legal submodes are:
5362         CONTINUOUS_TURN vector_id {0..3 = right, -right, up, -up}
5363         ATTACK
5364         EVADE_SQUIGGLE
5365         EVADE_BRAKE
5366 */
5367
5368 float   G_collision_time;
5369 vector  G_predicted_pos, G_fire_pos;
5370
5371 /*
5372 void show_firing_diag()
5373 {
5374         float           dot;
5375         vector  v2t;
5376         vector  pos1, pos2;
5377         float           dist;
5378
5379         if (G_collision_time == 0.0f)
5380                 return;
5381
5382         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",
5383                 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));
5384         vm_vec_normalized_dir(&v2t, &G_predicted_pos, &G_fire_pos);
5385         dot = vm_vec_dot(&v2t, &Pl_objp->orient.fvec);
5386         mprintf(("Dot of fvec and vector to predicted position = %10.7f (%7.3f degrees)\n", dot, acos(dot)*180.0f/3.141592654f));
5387
5388         vm_vec_scale_add(&pos1, &En_objp->pos, &En_objp->phys_info.vel, G_collision_time);
5389         vm_vec_scale_add(&pos2, &G_fire_pos, &Pl_objp->orient.fvec, G_collision_time*300.0f);
5390         dist = vm_vec_dist(&pos1, &pos2);
5391
5392         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));
5393 }
5394 */
5395
5396 //      If:
5397 //              flags & WIF_PUNCTURE
5398 //      Then Select a Puncture weapon.
5399 //      Else
5400 //              Select Any ol' weapon.
5401 //      Returns primary_bank index.
5402 int ai_select_primary_weapon(object *objp, object *other_objp, int flags)
5403 {
5404         ship    *shipp = &Ships[objp->instance];
5405         ship_weapon *swp = &shipp->weapons;
5406         ship_info *sip;
5407
5408         //Assert( other_objp != NULL );
5409         Assert( shipp->ship_info_index >= 0 && shipp->ship_info_index < MAX_SHIP_TYPES);
5410
5411         sip = &Ship_info[shipp->ship_info_index];
5412
5413         if (flags & WIF_PUNCTURE) {
5414                 if (swp->current_primary_bank >= 0) {
5415                         int     bank_index;
5416
5417                         bank_index = swp->current_primary_bank;
5418
5419                         if (Weapon_info[swp->primary_bank_weapons[bank_index]].wi_flags & WIF_PUNCTURE) {
5420                                 //nprintf(("AI", "%i: Ship %s selecting weapon %s\n", Framecount, Ships[objp->instance].ship_name, Weapon_info[swp->primary_bank_weapons[bank_index]].name));
5421                                 return swp->current_primary_bank;
5422                         }
5423                 }
5424                 for (int i=0; i<swp->num_primary_banks; i++) {
5425                         int     weapon_info_index;
5426
5427                         weapon_info_index = swp->primary_bank_weapons[i];
5428
5429                         if (weapon_info_index > -1){
5430                                 if (Weapon_info[weapon_info_index].wi_flags & WIF_PUNCTURE) {
5431                                         swp->current_primary_bank = i;
5432                                         //nprintf(("AI", "%i: Ship %s selecting weapon %s\n", Framecount, Ships[objp->instance].ship_name, Weapon_info[swp->primary_bank_weapons[i]].name));
5433                                         return i;
5434                                 }
5435                         }
5436                 }
5437                 
5438                 // AL 26-3-98: If we couldn't find a puncture weapon, pick first available weapon if one isn't active
5439                 if ( swp->current_primary_bank < 0 ) {
5440                         if ( swp->num_primary_banks > 0 ) {
5441                                 swp->current_primary_bank = 0;
5442                         }
5443                 }
5444
5445         } else {                //      Don't need to be using a puncture weapon.
5446                 if (swp->current_primary_bank >= 0) {
5447                         if (!(Weapon_info[swp->primary_bank_weapons[swp->current_primary_bank]].wi_flags & WIF_PUNCTURE)){
5448                                 return swp->current_primary_bank;
5449                         }
5450                 }
5451                 for (int i=0; i<swp->num_primary_banks; i++) {
5452                         if (swp->primary_bank_weapons[i] > -1) {
5453                                 if (!(Weapon_info[swp->primary_bank_weapons[i]].wi_flags & WIF_PUNCTURE)) {
5454                                         swp->current_primary_bank = i;
5455                                         nprintf(("AI", "%i: Ship %s selecting weapon %s\n", Framecount, Ships[objp->instance].ship_name, Weapon_info[swp->primary_bank_weapons[i]].name));
5456                                         return i;
5457                                 }
5458                         }
5459                 }
5460                 //      Wasn't able to find a non-puncture weapon.  Stick with what we have.
5461         }
5462
5463         Assert( swp->current_primary_bank != -1 );              // get Alan or Allender
5464
5465         return swp->current_primary_bank;
5466 }
5467
5468 //      --------------------------------------------------------------------------
5469 //      Maybe link primary weapons.
5470 void set_primary_weapon_linkage(object *objp)
5471 {
5472         ship            *shipp;
5473         ai_info *aip;
5474
5475         shipp = &Ships[objp->instance];
5476         aip     = &Ai_info[shipp->ai_index];
5477
5478         shipp->flags &= ~SF_PRIMARY_LINKED;
5479
5480         if (Num_weapons > (int) (MAX_WEAPONS * 0.75f)) {
5481                 if (shipp->flags & SF_PRIMARY_LINKED)
5482                         nprintf(("AI", "Frame %i, ship %s: Unlinking primaries.\n", Framecount, shipp->ship_name));
5483                 shipp->flags &= ~SF_PRIMARY_LINKED;
5484                 return;         //      If low on slots, don't link.
5485         }
5486
5487         shipp->flags &= ~SF_PRIMARY_LINKED;
5488
5489         // AL: ensure target is a ship!
5490         if ( Objects[aip->target_objnum].type == OBJ_SHIP ) {
5491                 // If trying to destroy a big ship (i.e., not disable/disarm), always unleash all weapons
5492                 if ( ship_get_SIF(&Ships[Objects[aip->target_objnum].instance]) & SIF_BIG_SHIP) {
5493                         if ( aip->targeted_subsys == NULL ) {
5494                                 shipp->flags |= SF_PRIMARY_LINKED;
5495                                 shipp->flags |= SF_SECONDARY_DUAL_FIRE;
5496                                 return;
5497                         }
5498                 }
5499         }
5500
5501         // AL 2-11-98: If ship has a disarm or disable goal, don't link unless both weapons are
5502         //                                      puncture weapons
5503         if ( (aip->active_goal != AI_GOAL_NONE) && (aip->active_goal != AI_ACTIVE_GOAL_DYNAMIC) ) {
5504                 if ( aip->goals[aip->active_goal].ai_mode & (AI_GOAL_DISABLE_SHIP|AI_GOAL_DISARM_SHIP) ) {
5505                         ship_weapon     *swp;
5506                         swp = &shipp->weapons;
5507                         // only continue if both primaries are puncture weapons
5508                         if ( swp->num_primary_banks == 2 ) {
5509                                 if ( !(Weapon_info[swp->primary_bank_weapons[0]].wi_flags & WIF_PUNCTURE) ) 
5510                                         return;
5511                                 if ( !(Weapon_info[swp->primary_bank_weapons[1]].wi_flags & WIF_PUNCTURE) ) 
5512                                         return;
5513                         }
5514                 }
5515         }
5516
5517         //      Don't want all ships always linking weapons at start, so asynchronize.
5518         if (Missiontime < i2f(30))
5519                 return;
5520         else if (Missiontime < i2f(120)) {
5521                 int r = static_rand((Missiontime >> 17) ^ OBJ_INDEX(objp));
5522                 if ( (r&3) != 0)
5523                         return;
5524         }
5525
5526         if (shipp->weapon_energy > Link_energy_levels_always[Game_skill_level]) {
5527                 shipp->flags |= SF_PRIMARY_LINKED;
5528         } else if (shipp->weapon_energy > Link_energy_levels_maybe[Game_skill_level]) {
5529                 if (objp->hull_strength < Ship_info[shipp->ship_info_index].initial_hull_strength/3.0f)
5530                         shipp->flags |= SF_PRIMARY_LINKED;
5531         }
5532 }
5533
5534 //      --------------------------------------------------------------------------
5535 //      Fire the current primary weapon.
5536 //      *objp is the object to fire from.
5537 void ai_fire_primary_weapon(object *objp)
5538 {
5539         ship                    *shipp = &Ships[objp->instance];
5540         ship_weapon     *swp = &shipp->weapons;
5541         ship_info       *sip;
5542         ai_info         *aip;
5543         object          *enemy_objp;
5544
5545         Assert( shipp->ship_info_index >= 0 && shipp->ship_info_index < MAX_SHIP_TYPES);
5546         sip = &Ship_info[shipp->ship_info_index];
5547
5548         aip = &Ai_info[shipp->ai_index];
5549
5550         //      If low on slots, fire a little less often.
5551         if (Num_weapons > (int) (0.9f * MAX_WEAPONS)) {
5552                 if (frand() > 0.5f) {
5553                         nprintf(("AI", "Frame %i, %s not fire.\n", Framecount, shipp->ship_name));
5554                         return;
5555                 }
5556         }
5557
5558         if (!Ai_firing_enabled){
5559                 return;
5560         }
5561
5562         if (aip->target_objnum != -1){
5563                 enemy_objp = &Objects[aip->target_objnum];
5564         } else {
5565                 enemy_objp = NULL;
5566         }
5567
5568         if ( (swp->current_primary_bank < 0) || (swp->current_primary_bank >= swp->num_primary_banks) || timestamp_elapsed(aip->primary_select_timestamp)) {
5569                 int     flags = 0;
5570                 // AL 2-11-98: If attacking any subsystem (not just engines), use disrupter weapon
5571 //              if ((aip->targeted_subsys != NULL) && (aip->targeted_subsys->system_info->type == SUBSYSTEM_ENGINE)) {
5572                 if ( aip->targeted_subsys != NULL ) {
5573                         flags = WIF_PUNCTURE;
5574                 }
5575                 ai_select_primary_weapon(objp, enemy_objp, flags);
5576                 ship_primary_changed(shipp);    // AL: maybe send multiplayer information when AI ship changes primaries
5577                 aip->primary_select_timestamp = timestamp(5 * 1000);    //      Maybe change primary weapon five seconds from now.
5578         }
5579
5580         //      If pointing nearly at predicted collision point of target, bash orientation to be perfectly pointing.
5581         float   dot;
5582         vector  v2t;
5583
5584 //      if (!IS_VEC_NULL(&G_predicted_pos)) {
5585         if (!( vm_vec_mag_quick(&G_predicted_pos) < AICODE_SMALL_MAGNITUDE )) {
5586                 if ( !vm_vec_cmp(&G_predicted_pos, &G_fire_pos) ) {
5587                         nprintf(("Warning", "Avoid NULL vector assert.. why are G_predicted_pos and G_fire_pos the same?\n"));
5588                 } else {
5589                         vm_vec_normalized_dir(&v2t, &G_predicted_pos, &G_fire_pos);
5590                         dot = vm_vec_dot(&v2t, &objp->orient.fvec);
5591                         if (dot > .998629534f){ //      if within 3.0 degrees of desired heading, bash
5592                                 vm_vector_2_matrix(&objp->orient, &v2t, &objp->orient.uvec, NULL);
5593                         }
5594                 }
5595         }
5596
5597         //      Make sure not firing at a protected ship unless firing at a live subsystem.
5598         //      Note: This happens every time the ship tries to fire, perhaps every frame.
5599         //      Should be wrapped in a timestamp, same one that enables it to fire, but that is complicated
5600         //      by multiple banks it can fire from.
5601         if (aip->target_objnum != -1) {
5602                 object  *tobjp = &Objects[aip->target_objnum];
5603                 if (tobjp->flags & OF_PROTECTED) {
5604                         if (aip->targeted_subsys != NULL) {
5605                                 int     type;
5606
5607                                 type = aip->targeted_subsys->system_info->type;
5608                                 if (ship_get_subsystem_strength(&Ships[tobjp->instance], type) == 0.0f) {
5609                                         aip->target_objnum = -1;
5610                                         return;
5611                                 }
5612                         } else {
5613                                 aip->target_objnum = -1;
5614                                 return;
5615                         }
5616                 }
5617         }
5618
5619         //      If enemy is protected, not firing a puncture weapon and enemy's hull is low, don't fire.
5620         if ((enemy_objp != NULL) && (enemy_objp->flags & OF_PROTECTED)) {
5621                 // AL: 3-6-98: Check if current_primary_bank is valid
5622                 if ((enemy_objp->hull_strength < 750.0f) && 
5623                         ((aip->targeted_subsys == NULL) || (enemy_objp->hull_strength < aip->targeted_subsys->current_hits + 50.0f)) &&
5624                         (swp->current_primary_bank >= 0) ) {
5625                         if (!(Weapon_info[swp->primary_bank_weapons[swp->current_primary_bank]].wi_flags & WIF_PUNCTURE)) {
5626                                 //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));
5627                                 swp->next_primary_fire_stamp[swp->current_primary_bank] = timestamp(1000);
5628                                 return;
5629                         }
5630
5631                         /*
5632                         int     num_attacking;
5633                         num_attacking = num_enemies_attacking(enemy_objp-Objects);
5634                         if (enemy_objp->hull_strength / num_attacking < 200.0f) {
5635                                 if (frand() < 0.75f) {
5636                                         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));
5637                                         swp->next_primary_fire_stamp[swp->current_primary_bank] = timestamp(500);
5638                                         return;
5639                                 }
5640                         }
5641                         */
5642                 }
5643         }
5644
5645         set_primary_weapon_linkage(objp);
5646         
5647         // I think this will properly solve the problem
5648         // fire non-streaming weapons
5649         ship_fire_primary(objp, 0);
5650         
5651         // fire streaming weapons
5652         shipp->flags |= SF_TRIGGER_DOWN;
5653         ship_fire_primary(objp, 1);
5654         shipp->flags &= ~SF_TRIGGER_DOWN;
5655 }
5656
5657 //      --------------------------------------------------------------------------
5658 //      Return number of nearby enemy fighters.
5659 //      threshold is the distance within which a ship is considered near.
5660 //
5661 // input:       enemy_team_mask =>      teams that are considered as an enemy
5662 //                              pos                                     =>      world position to measure ship distances from
5663 //                              threshold                       =>      max distance from pos to be considered "near"
5664 //
5665 // exit:                number of ships within threshold units of pos
5666 int num_nearby_fighters(int enemy_team_mask, vector *pos, float threshold)
5667 {
5668         ship_obj        *so;
5669         object  *ship_objp;
5670         int             count = 0;
5671
5672         for ( so = GET_FIRST(&Ship_obj_list); so != END_OF_LIST(&Ship_obj_list); so = GET_NEXT(so) ) {
5673
5674                 ship_objp = &Objects[so->objnum];
5675
5676                 if (Ships[ship_objp->instance].team & enemy_team_mask) {
5677                         if (Ship_info[Ships[ship_objp->instance].ship_info_index].flags & (SIF_FIGHTER | SIF_BOMBER)) {
5678                                 if (vm_vec_dist_quick(pos, &ship_objp->pos) < threshold)
5679                                         count++;
5680                         }
5681                 }
5682         }
5683
5684         return count;
5685 }
5686
5687 //      --------------------------------------------------------------------------
5688 //      Select secondary weapon to fire.
5689 //      Currently, 1/16/98:
5690 //              If 0 secondary weapons available, return -1
5691 //              If 1 available, use it.
5692 //              If 2 or more, if the current weapon is one of them, stick with it, otherwise choose a random one.
5693 //      priority1 and priority2 are Weapon_info[] bitmasks such as WIF_HOMING_ASPECT.  If any weapon has any bit in priority1
5694 //      set, that weapon will be selected.  If not, apply to priority2.  If neither, return -1, meaning no weapon selected.
5695 //      Note, priorityX have default values of -1, meaning if not set, they will match any weapon.
5696 //      Return value:
5697 //              bank index
5698 //      Should do this:
5699 //              Favor aspect seekers when attacking small ships faraway.
5700 //              Favor rapid fire dumbfire when attacking a large ship.
5701 //              Ignore heat seekers because we're not sure how they'll work.
5702 void ai_select_secondary_weapon(object *objp, ship_weapon *swp, int priority1 = -1, int priority2 = -1)
5703 {
5704         int     num_weapon_types;
5705         int     weapon_id_list[MAX_WEAPON_TYPES], weapon_bank_list[MAX_WEAPON_TYPES];
5706         int     i;
5707         int     ignore_mask;
5708         int     initial_bank;
5709
5710         initial_bank = swp->current_secondary_bank;
5711
5712         //      Ignore bombs unless one of the priorities asks for them to be selected.
5713         if (WIF_HUGE & (priority1 | priority2))
5714                 ignore_mask = 0;
5715         else
5716                 ignore_mask = WIF_HUGE;
5717
5718         if (!(WIF_BOMBER_PLUS & (priority1 | priority2)))
5719                 ignore_mask |= WIF_BOMBER_PLUS;
5720
5721 #ifndef NDEBUG
5722         for (i=0; i<MAX_WEAPON_TYPES; i++) {
5723                 weapon_id_list[i] = -1;
5724                 weapon_bank_list[i] = -1;
5725         }
5726 #endif
5727
5728         //      Stuff weapon_bank_list with bank index of available weapons.
5729         num_weapon_types = get_available_secondary_weapons(objp, weapon_id_list, weapon_bank_list);
5730
5731         int     priority2_index = -1;
5732
5733         for (i=0; i<num_weapon_types; i++) {
5734                 int     wi_flags;
5735
5736                 wi_flags = Weapon_info[swp->secondary_bank_weapons[weapon_bank_list[i]]].wi_flags;
5737                 if (!(wi_flags & ignore_mask)) {                                        //      Maybe bombs are illegal.
5738                         if (wi_flags & priority1) {
5739                                 swp->current_secondary_bank = weapon_bank_list[i];                              //      Found first priority, return it.
5740                                 break;
5741                         } else if (wi_flags & priority2)
5742                                 priority2_index = weapon_bank_list[i];  //      Found second priority, but might still find first priority.
5743                 }
5744         }
5745
5746         //      If didn't find anything above, then pick any secondary weapon.
5747         if (i == num_weapon_types) {
5748                 swp->current_secondary_bank = priority2_index;  //      Assume we won't find anything.
5749                 if (priority2_index == -1) {
5750                         for (i=0; i<num_weapon_types; i++) {
5751                                 int     wi_flags;
5752
5753                                 wi_flags = Weapon_info[swp->secondary_bank_weapons[weapon_bank_list[i]]].wi_flags;
5754                                 if (!(wi_flags & ignore_mask)) {                                        //      Maybe bombs are illegal.
5755                                         if (swp->secondary_bank_ammo[i] > 0) {
5756                                                 swp->current_secondary_bank = i;
5757                                                 break;
5758                                         }
5759                                 }
5760                         }
5761                 }
5762         }
5763
5764         //      If switched banks, force reacquisition of aspect lock.
5765         if (swp->current_secondary_bank != initial_bank) {
5766                 ai_info *aip = &Ai_info[Ships[objp->instance].ai_index];
5767                 
5768                 aip->aspect_locked_time = 0.0f;
5769                 aip->current_target_is_locked = 0;
5770         }
5771
5772
5773         ship_secondary_changed(&Ships[objp->instance]); // AL: let multiplayer know if secondary bank has changed
5774         // nprintf(("AI", "Ship %s selected weapon %s\n", Ships[objp->instance].ship_name, Weapon_info[swp->secondary_bank_weapons[swp->current_secondary_bank]].name));
5775 }
5776
5777 //      Return number of objects homing on object *target_objp
5778 int compute_num_homing_objects(object *target_objp)
5779 {
5780         object  *objp;
5781         int             count = 0;
5782
5783         for ( objp = GET_FIRST(&obj_used_list); objp !=END_OF_LIST(&obj_used_list); objp = GET_NEXT(objp) ) {
5784                 if (objp->type == OBJ_WEAPON) {
5785                         if (Weapon_info[Weapons[objp->instance].weapon_info_index].wi_flags & WIF_HOMING) {
5786                                 if (Weapons[objp->instance].homing_object == target_objp) {
5787                                         count++;
5788                                 }
5789                         }
5790                 }
5791         }
5792
5793         return count;
5794 }
5795
5796 //      Object *firing_objp just fired weapon weapon_index (index in Weapon_info).
5797 //      If it's a shockwave weapon, tell your team about it!
5798 void ai_maybe_announce_shockwave_weapon(object *firing_objp, int weapon_index)
5799 {
5800         if ((firing_objp->type == OBJ_SHIP) && (Weapon_info[weapon_index].shockwave_speed > 0.0f)) {
5801                 ship_obj        *so;
5802                 int             firing_ship_team;
5803
5804                 firing_ship_team = Ships[firing_objp->instance].team;
5805
5806                 for ( so = GET_FIRST(&Ship_obj_list); so != END_OF_LIST(&Ship_obj_list); so = GET_NEXT(so) ) {
5807                         object  *A = &Objects[so->objnum];
5808                         Assert(A->type == OBJ_SHIP);
5809
5810                         if (Ships[A->instance].team == firing_ship_team) {
5811                                 ai_info *aip = &Ai_info[Ships[A->instance].ai_index];
5812                                 // AL 1-5-98: only avoid shockwave if not docked or repairing
5813                                 if ( !(aip->ai_flags & (AIF_DOCKED|AIF_BEING_REPAIRED)) ) {
5814                                         aip->ai_flags |= AIF_AVOID_SHOCKWAVE_WEAPON;
5815                                 }
5816                         }
5817                 }
5818         }
5819 }
5820
5821 //      Return total payload of all incoming missiles.
5822 float compute_incoming_payload(object *target_objp)
5823 {
5824         missile_obj     *mo;
5825         float                   payload = 0.0f;
5826
5827         for ( mo = GET_NEXT(&Missile_obj_list); mo != END_OF_LIST(&Missile_obj_list); mo = GET_NEXT(mo) ) {
5828                 object  *objp;
5829
5830                 objp = &Objects[mo->objnum];
5831                 Assert(objp->type == OBJ_WEAPON);
5832                 if (Weapons[objp->instance].homing_object == target_objp) {
5833                         payload += Weapon_info[Weapons[objp->instance].weapon_info_index].damage;
5834                 }
5835         }
5836
5837         return payload;
5838 }
5839
5840 //      --------------------------------------------------------------------------
5841 //      Return true if OK for *aip to fire its current weapon at its current target.
5842 //      Only reason this function returns false is:
5843 //              weapon is a homer
5844 //              targeted at player
5845 //                      OR:     player has too many homers targeted at him
5846 //                                      Missiontime in that dead zone in which can't fire at this player
5847 //      Note: If player is attacking a ship, that ship is allowed to fire at player.  Otherwise, we get in a situation in which
5848 //      player is attacking a large ship, but that large ship is not defending itself with missiles.
5849 int check_ok_to_fire(int objnum, int target_objnum, weapon_info *wip)
5850 {
5851         int     num_homers = 0;
5852         object  *tobjp = &Objects[target_objnum];
5853
5854         if (target_objnum > -1) {
5855                 // AL 3-4-98: Ensure objp target is a ship first 
5856                 if ( tobjp->type == OBJ_SHIP ) {
5857
5858                         // should not get this far. check if ship is protected from beam and weapon is type beam
5859                         if ( (wip->wi_flags & WIF_BEAM) && (tobjp->flags & OF_BEAM_PROTECTED) ) {
5860                                 Int3();
5861                                 return 0;
5862                         }
5863                         if (Ship_info[Ships[tobjp->instance].ship_info_index].flags & SIF_SMALL_SHIP) {
5864                                 num_homers = compute_num_homing_objects(&Objects[target_objnum]);
5865                         }
5866                 }
5867
5868                 //      If player, maybe fire based on Skill_level and number of incoming weapons.
5869                 //      If non-player, maybe fire based on payload of incoming weapons.
5870                 if (wip->wi_flags & WIF_HOMING) {
5871                         if ((target_objnum > -1) && (tobjp->flags & OF_PLAYER_SHIP)) {
5872                                 if (Ai_info[Ships[tobjp->instance].ai_index].target_objnum != objnum) {
5873                                         //      Don't allow AI ships to fire at player for fixed periods of time based on skill level.
5874                                         //      With 5 skill levels, at Very Easy, they fire in 1/7 of every 10 second interval.
5875                                         //      At Easy, 2/7...at Expert, 5/7
5876                                         int t = ((Missiontime /(65536*10)) ^ target_objnum ^ 0x01) % (NUM_SKILL_LEVELS+2);
5877                                         if (t > Game_skill_level) {
5878                                                 //nprintf(("AI", "Not OK to fire homer at time thing %i\n", t));
5879                                                 return 0;
5880                                         }
5881                                 }
5882                                 //nprintf(("AI", " IS OK to fire homer at time thing %i ***\n", t));
5883                                 int     swarmers = 0;
5884                                 if (wip->wi_flags & WIF_SWARM)
5885                                         swarmers = 2;   //      Note, always want to be able to fire swarmers if no currently incident homers.
5886                                 if (Max_allowed_player_homers[Game_skill_level] < num_homers + swarmers) {
5887                                         return 0;
5888                                 }
5889                         } else if (num_homers > 3) {
5890                                 float   incoming_payload;
5891
5892                                 incoming_payload = compute_incoming_payload(&Objects[target_objnum]);
5893
5894                                 if (incoming_payload > tobjp->hull_strength) {
5895                                         return 0;
5896                                 }
5897                         }
5898                 }
5899         }
5900
5901         return 1;
5902 }
5903
5904 //      --------------------------------------------------------------------------
5905 //      Fire a secondary weapon.
5906 //      Maybe choose to fire a different one.
5907 //      priority1 and priority2 are optional parameters with defaults = -1
5908 int ai_fire_secondary_weapon(object *objp, int priority1, int priority2)
5909 {
5910         ship_weapon *swp;
5911         ship    *shipp;
5912         ship_info *sip;
5913         int             current_bank;
5914         int             rval = 0;
5915
5916 #ifndef NDEBUG
5917         if (!Ai_firing_enabled)
5918                 return rval;
5919 #endif
5920
5921         Assert( objp != NULL );
5922         Assert(objp->type == OBJ_SHIP);
5923         shipp = &Ships[objp->instance];
5924         swp = &shipp->weapons;
5925
5926         Assert( shipp->ship_info_index >= 0 && shipp->ship_info_index < MAX_SHIP_TYPES);
5927         sip = &Ship_info[shipp->ship_info_index];
5928
5929         //      Select secondary weapon.
5930         current_bank = swp->current_secondary_bank; //ai_select_secondary_weapon(objp, swp, priority1, priority2);
5931
5932         //nprintf(("AI", "Frame %i: Current bank = %i, ammo remaining = %i\n", Framecount, current_bank, swp->secondary_bank_ammo[current_bank]));
5933         if (current_bank == -1) {
5934                 swp->next_secondary_fire_stamp[current_bank] = timestamp(500);
5935                 return rval;
5936         }
5937
5938         Assert(current_bank < shipp->weapons.num_secondary_banks);
5939
5940         weapon_info     *wip = &Weapon_info[shipp->weapons.secondary_bank_weapons[current_bank]];
5941
5942         if ((wip->wi_flags & WIF_HOMING_ASPECT) && (!Ai_info[shipp->ai_index].current_target_is_locked)) {
5943                 //nprintf(("AI", "Not firing secondary weapon because not aspect locked.\n"));
5944                 swp->next_secondary_fire_stamp[current_bank] = timestamp(250);
5945         } else if ((wip->wi_flags & WIF_BOMB) || (vm_vec_dist_quick(&objp->pos, &En_objp->pos) > 50.0f)) {
5946                 //      This might look dumb, firing a bomb even if closer than 50 meters, but the reason is, if you're carrying
5947                 //      bombs, delivering them is probably more important than surviving.
5948                 ai_info *aip;
5949
5950                 aip = &Ai_info[shipp->ai_index];
5951                 
5952                 //      Note, maybe don't fire if firing at player and any homers yet fired.
5953                 //      Decreasing chance to fire the more homers are incoming on player.
5954                 if (check_ok_to_fire(OBJ_INDEX(objp), aip->target_objnum, wip)) {
5955                         if (ship_fire_secondary(objp)) {
5956                                 rval = 1;
5957                                 swp->next_secondary_fire_stamp[current_bank] = timestamp(500);
5958                                 //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));
5959                         }
5960
5961                 } else {
5962                         swp->next_secondary_fire_stamp[current_bank] = timestamp(500);
5963                 }
5964         }
5965
5966         return rval;
5967 }
5968
5969 //      Return true if it looks like obj1, if continuing to move along current vector, will
5970 //      collide with obj2.
5971 int might_collide_with_ship(object *obj1, object *obj2, float dot_to_enemy, float dist_to_enemy, float duration)
5972 {
5973         if (obj1->phys_info.speed * duration + 2*(obj1->radius + obj2->radius) > dist_to_enemy)
5974                 if (dot_to_enemy > 0.8f - 2*(obj1->radius + obj2->radius)/dist_to_enemy)
5975                         return objects_will_collide(obj1, obj2, duration, 2.0f);
5976
5977 //              BABY - 
5978 //              CONDITION 1, dist_to_enemy < o1_rad + o2_rad + (obj1.speed + obj2.speed) * time + 50
5979         
5980         return 0;
5981
5982 }
5983
5984 //      --------------------------------------------------------------------------
5985 //      Return true if ship *objp firing a laser believes it will hit a teammate.
5986 int might_hit_teammate(object *firing_objp)
5987 {
5988         int             team;
5989         object  *objp;
5990         ship_obj        *so;
5991
5992         team = Ships[firing_objp->instance].team;
5993
5994         for ( so = GET_FIRST(&Ship_obj_list); so != END_OF_LIST(&Ship_obj_list); so = GET_NEXT(so) ) {
5995                 objp = &Objects[so->objnum];
5996                 if (Ships[objp->instance].team == team) {
5997                         float           dist, dot;
5998                         vector  vec_to_objp;
5999
6000                         vm_vec_sub(&vec_to_objp, &firing_objp->pos, &objp->pos);
6001                         dist = vm_vec_mag_quick(&vec_to_objp);
6002                         dot = vm_vec_dot(&firing_objp->orient.fvec, &vec_to_objp)/dist;
6003                         if (might_collide_with_ship(firing_objp, objp, dot, dist, 2.0f))
6004                                 return 1;
6005                 }
6006         }
6007
6008         return 0;
6009
6010 }
6011
6012 //int   Team_not_fire_count=0, Team_hit_count = 0;
6013
6014 void render_all_ship_bay_paths(object *objp)
6015 {
6016         int             i,j,color;
6017         ship            *sp = &Ships[objp->instance];
6018         polymodel       *pm;
6019         model_path      *mp;
6020
6021         pm = model_get(sp->modelnum);
6022         vector  global_path_point;
6023         vertex  v, prev_vertex;
6024
6025         if ( pm->ship_bay == NULL )
6026                 return;
6027
6028         for ( i = 0; i < pm->ship_bay->num_paths; i++ ) {
6029                 mp = &pm->paths[pm->ship_bay->paths[i]];
6030
6031                 for ( j = 0; j < mp->nverts; j++ ) {
6032                         vm_vec_unrotate(&global_path_point, &mp->verts[j].pos, &objp->orient);
6033                         vm_vec_add2(&global_path_point, &objp->pos);
6034                         g3_rotate_vertex(&v, &global_path_point);
6035                         color = 255 - j*50;
6036                         if ( color < 50 ) 
6037                                 color = 100;
6038                         gr_set_color(0, color, 0);
6039
6040                         if ( j == mp->nverts-1 ) {
6041                                 gr_set_color(255, 0, 0);
6042                         }
6043
6044                         g3_draw_sphere( &v, 1.5f);
6045
6046                         if ( j > 0 )
6047                                 g3_draw_line(&v, &prev_vertex);
6048
6049                         prev_vertex = v;
6050         
6051                 }
6052         }
6053 }
6054
6055 // debug function to show all path points associated with an object
6056 void render_all_subsys_paths(object *objp)
6057 {
6058         int             i,j,color;
6059         ship            *sp = &Ships[objp->instance];
6060         polymodel       *pm;
6061         model_path      *mp;
6062
6063         pm = model_get(sp->modelnum);
6064         vector  global_path_point;
6065         vertex  v, prev_vertex;
6066
6067         if ( pm->ship_bay == NULL )
6068                 return;
6069
6070         for ( i = 0; i < pm->n_paths; i++ ) {
6071                 mp = &pm->paths[i];
6072                 for ( j = 0; j < mp->nverts; j++ ) {
6073                         vm_vec_unrotate(&global_path_point, &mp->verts[j].pos, &objp->orient);
6074                         vm_vec_add2(&global_path_point, &objp->pos);
6075                         g3_rotate_vertex(&v, &global_path_point);
6076                         color = 255 - j*50;
6077                         if ( color < 50 ) 
6078                                 color = 100;
6079                         gr_set_color(0, color, 0);
6080
6081                         if ( j == mp->nverts-1 ) {
6082                                 gr_set_color(255, 0, 0);
6083                         }
6084
6085                         g3_draw_sphere( &v, 1.5f);
6086
6087                         if ( j > 0 )
6088                                 g3_draw_line(&v, &prev_vertex);
6089
6090                         prev_vertex = v;
6091                 }
6092         }
6093 }
6094
6095 void render_path_points(object *objp)
6096 {
6097         ship            *shipp = &Ships[objp->instance];
6098         ai_info *aip = &Ai_info[shipp->ai_index];
6099         object  *dobjp;
6100         polymodel       *pm;
6101
6102         render_all_subsys_paths(objp);
6103         render_all_ship_bay_paths(objp);
6104
6105         if (aip->goal_objnum < 0)
6106                 return;
6107
6108         dobjp = &Objects[aip->goal_objnum];
6109         pm = model_get(Ships[dobjp->instance].modelnum);
6110         vector  dock_point, global_dock_point;
6111         vertex  v;
6112
6113         ship_model_start(&Objects[aip->goal_objnum]);
6114         if (pm->n_docks) {
6115                 dock_point = pm->docking_bays[0].pnt[0];
6116                 model_find_world_point(&global_dock_point, &dock_point, Ships[dobjp->instance].modelnum, 0, &dobjp->orient, &dobjp->pos );
6117                 g3_rotate_vertex(&v, &global_dock_point);
6118                 gr_set_color(255, 255, 255);
6119                 g3_draw_sphere( &v, 1.5f);
6120         }
6121
6122         if (aip->path_start != -1) {
6123                 vertex          prev_vertex;
6124                 pnode                   *pp = &Path_points[aip->path_start];
6125                 int                     num_points = aip->path_length;
6126                 int                     i;
6127
6128                 for (i=0; i<num_points; i++) {
6129                         vertex  v0;
6130
6131                         g3_rotate_vertex( &v0, &pp->pos );
6132
6133                         gr_set_color(0, 128, 96);
6134                         if (i != 0)
6135                                 g3_draw_line(&v0, &prev_vertex);
6136
6137                         if (pp-Path_points == aip->path_cur)
6138                                 gr_set_color(255,255,0);
6139                         
6140                         g3_draw_sphere( &v0, 4.5f);
6141
6142                         //      Connect all the turrets that can fire upon this point to this point.
6143 /*                      if (0) { //pp->path_index != -1) {
6144                                 model_path      *pmp;
6145                                 mp_vert         *pmpv;
6146
6147                                 get_base_path_info(pp->path_index, aip->goal_objnum, &pmp, &pmpv);
6148
6149                                 if (pmpv->nturrets) {
6150                                         for (int j = 0; j<pmpv->nturrets; j++) {
6151                                                 vertex  v1;
6152                                                 vector  turret_pos;
6153                                                 ship_subsys     *ssp;
6154
6155                                                 ssp = ship_get_indexed_subsys(&Ships[Objects[aip->goal_objnum].instance], pmpv->turret_ids[j]);
6156
6157 model_find_world_point(&turret_pos, &ssp->system_info->pnt, Ships[dobjp->instance].modelnum, 0, &dobjp->orient, &dobjp->pos );
6158         
6159                                                 g3_rotate_vertex(&v1, &turret_pos);
6160                                                 gr_set_color(255, 255, 0);
6161                                                 g3_draw_line(&v0, &v1);
6162                                                 g3_draw_sphere( &v1, 1.5f);
6163                                         }
6164                                 }
6165                         } */
6166
6167                         prev_vertex = v0;
6168
6169                         pp++;
6170                 }
6171         }
6172
6173         ship_model_stop(&Objects[aip->goal_objnum]);
6174 }
6175
6176 // Return the distance that the current AI weapon will travel
6177 float ai_get_weapon_dist(ship_weapon *swp)
6178 {
6179         int     bank_num, weapon_num;
6180
6181         bank_num = swp->current_primary_bank;
6182         weapon_num = swp->primary_bank_weapons[bank_num];
6183
6184         //      If weapon_num is illegal, return a reasonable value.  A valid weapon
6185         //      will get selected when this ship tries to fire.
6186         if (weapon_num == -1) {
6187                 // Int3();
6188                 return 1000.0f;
6189         }
6190
6191         return Weapon_info[weapon_num].max_speed * Weapon_info[weapon_num].lifetime;
6192 }
6193
6194 float ai_get_weapon_speed(ship_weapon *swp)
6195 {
6196         int     bank_num, weapon_num;
6197
6198         bank_num = swp->current_primary_bank;
6199         if (bank_num < 0)
6200                 return 100.0f;
6201
6202         weapon_num = swp->primary_bank_weapons[bank_num];
6203
6204         if (weapon_num == -1) {
6205                 //Int3();
6206                 return 100.0f;
6207         }
6208
6209         return Weapon_info[weapon_num].max_speed;
6210 }
6211
6212 //      Compute the predicted position of a ship to be fired upon from a turret.
6213 //      This is based on position of firing gun, enemy object, weapon speed and skill level constraints.
6214 //      Return value in *predicted_enemy_pos.
6215 //      Also, stuff globals G_predicted_pos, G_collision_time and G_fire_pos.
6216 //      *pobjp          object firing the weapon
6217 //      *eobjp          object being fired upon
6218 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)
6219 {
6220         ship    *shipp = &Ships[pobjp->instance];
6221         float   range_time;
6222
6223         //weapon_speed = ai_get_weapon_speed(&shipp->weapons);
6224
6225         if (weapon_speed < 1.0f)
6226                 weapon_speed = 1.0f;
6227
6228         range_time = 2.0f;
6229
6230         //      Make it take longer for enemies to get player's allies in range based on skill level.
6231         if (Ships[pobjp->instance].team != Ships[Player_obj->instance].team)
6232                 range_time += In_range_time[Game_skill_level];
6233
6234         //nprintf(("AI", "time enemy in range = %7.3f\n", aip->time_enemy_in_range));
6235
6236         if (time_enemy_in_range < range_time) {
6237                 float   dist;
6238
6239                 dist = vm_vec_dist_quick(&pobjp->pos, enemy_pos);
6240                 vm_vec_scale_add(predicted_enemy_pos, enemy_pos, enemy_vel, time_enemy_in_range * dist/weapon_speed);
6241         } else {
6242                 float   collision_time, scale;
6243                 vector  rand_vec;
6244                 ai_info *aip = &Ai_info[shipp->ai_index];
6245
6246                 collision_time = compute_collision_time(enemy_pos, enemy_vel, gun_pos, weapon_speed);
6247
6248                 if (collision_time == 0.0f){
6249                         collision_time = 100.0f;
6250                 }
6251
6252                 vm_vec_scale_add(predicted_enemy_pos, enemy_pos, enemy_vel, collision_time);
6253                 if (time_enemy_in_range > 2*range_time){
6254                         scale = (1.0f - aip->ai_accuracy) * 4.0f;
6255                 } else {
6256                         scale = (1.0f - aip->ai_accuracy) * 4.0f * (1.0f + 4.0f * (1.0f - time_enemy_in_range/(2*range_time)));
6257                 }               
6258
6259                 static_randvec(((pobjp-Objects) ^ (Missiontime >> 16)) & 7, &rand_vec);
6260
6261                 vm_vec_scale_add2(predicted_enemy_pos, &rand_vec, scale);
6262                 G_collision_time = collision_time;
6263                 G_fire_pos = *gun_pos;
6264         }
6265
6266         G_predicted_pos = *predicted_enemy_pos;
6267 }
6268
6269 //      Compute the predicted position of a ship to be fired upon.
6270 //      This is based on current position of firing object, enemy object, relative position of gun on firing object,
6271 //      weapon speed and skill level constraints.
6272 //      Return value in *predicted_enemy_pos.
6273 //      Also, stuff globals G_predicted_pos, G_collision_time and G_fire_pos.
6274 void set_predicted_enemy_pos(vector *predicted_enemy_pos, object *pobjp, object *eobjp, ai_info *aip)
6275 {
6276         float   weapon_speed, range_time;
6277         ship    *shipp = &Ships[pobjp->instance];
6278
6279         weapon_speed = ai_get_weapon_speed(&shipp->weapons);
6280         weapon_speed = max(weapon_speed, 1.0f);         // set not less than 1
6281
6282         range_time = 2.0f;
6283
6284         //      Make it take longer for enemies to get player's allies in range based on skill level.
6285         // but don't bias team v. team missions
6286         if ( !((Game_mode & GM_MULTIPLAYER) && (Netgame.type_flags & NG_TYPE_TEAM)) ) {
6287                 if (Ships[pobjp->instance].team != Ships[Player_obj->instance].team) {
6288                         range_time += In_range_time[Game_skill_level];
6289                 }
6290         }
6291         //nprintf(("AI", "time enemy in range = %7.3f\n", aip->time_enemy_in_range));
6292
6293         if (aip->time_enemy_in_range < range_time) {
6294                 float   dist;
6295
6296                 dist = vm_vec_dist_quick(&pobjp->pos, &eobjp->pos);
6297                 vm_vec_scale_add(predicted_enemy_pos, &eobjp->pos, &eobjp->phys_info.vel, aip->time_enemy_in_range * dist/weapon_speed);
6298         } else {
6299                 float   collision_time;
6300                 vector  gun_pos, pnt;
6301                 polymodel *po = model_get( Ship_info[shipp->ship_info_index].modelnum );
6302
6303                 //      Compute position of gun in absolute space and use that as fire position.
6304                 if(po->gun_banks != NULL){
6305                         pnt = po->gun_banks[0].pnt[0];
6306                 } else {
6307                         pnt = Objects[shipp->objnum].pos;
6308                 }
6309                 vm_vec_unrotate(&gun_pos, &pnt, &pobjp->orient);
6310                 vm_vec_add2(&gun_pos, &pobjp->pos);
6311
6312                 collision_time = compute_collision_time(&eobjp->pos, &eobjp->phys_info.vel, &gun_pos, weapon_speed);
6313
6314                 if (collision_time == 0.0f) {
6315                         collision_time = 100.0f;
6316                 }
6317
6318                 vm_vec_scale_add(predicted_enemy_pos, &eobjp->pos, &eobjp->phys_info.vel, collision_time);
6319
6320                 // set globals
6321                 G_collision_time = collision_time;
6322                 G_fire_pos = gun_pos;
6323         }
6324
6325         // Now add error terms (1) regular aim (2) EMP (3) stealth
6326         float scale = 0.0f;
6327         vector rand_vec;
6328
6329         // regular skill level error in aim
6330         if (aip->time_enemy_in_range > 2*range_time) {
6331                 scale = (1.0f - aip->ai_accuracy) * 4.0f;
6332         } else {
6333                 scale = (1.0f - aip->ai_accuracy) * 4.0f * (1.0f + 4.0f * (1.0f - aip->time_enemy_in_range/(2*range_time)));
6334         }
6335
6336         // if this ship is under the effect of an EMP blast, throw his aim off a bit
6337         if (shipp->emp_intensity > 0.0f) {
6338                 // never go lower than 1/2 of the EMP effect max, otherwise things aren't noticeable
6339                 scale += (MAX_EMP_INACCURACY * (shipp->emp_intensity < 0.5f ? 0.5f : shipp->emp_intensity));
6340                 mprintf(("AI miss scale factor (EMP) %f\n",scale));
6341         }
6342
6343         // if stealthy ship, throw his aim off, more when farther away and when dot is small
6344         if ( aip->ai_flags & AIF_STEALTH_PURSIUT ) {
6345                 float dist = vm_vec_dist_quick(&pobjp->pos, &eobjp->pos);
6346                 vector temp;
6347                 vm_vec_sub(&temp, &eobjp->pos, &pobjp->pos);
6348                 vm_vec_normalize_quick(&temp);
6349                 float dot = vm_vec_dotprod(&temp, &pobjp->orient.fvec);
6350                 float st_err = 3.0f * (1.4f - dot) * (1.0f + dist / (get_skill_stealth_dist_scaler() * STEALTH_MAX_VIEW_DIST)) * (1 - aip->ai_accuracy);
6351                 scale += st_err;
6352                 // mprintf(("error term: %.1f, total %.1f, dot %.3f\n", st_err, scale, dot));
6353         }
6354
6355         // get a random vector that changes slowly over time (1x / sec)
6356         static_randvec(((pobjp-Objects) ^ (Missiontime >> 16)) & 7, &rand_vec);
6357
6358         vm_vec_scale_add2(predicted_enemy_pos, &rand_vec, scale);
6359
6360         // set global
6361         G_predicted_pos = *predicted_enemy_pos;
6362 }
6363
6364 //      Handler of submode for Chase.  Go into a continuous turn for awhile.
6365 void ai_chase_ct()
6366 {
6367         vector          tvec;
6368         ship_info       *sip;
6369         ai_info         *aip;
6370
6371         Assert(Ships[Pl_objp->instance].ship_info_index >= 0);
6372         sip = &Ship_info[Ships[Pl_objp->instance].ship_info_index];
6373         Assert(Ships[Pl_objp->instance].ai_index >= 0);
6374         aip = &Ai_info[Ships[Pl_objp->instance].ai_index];
6375
6376         //      Make a continuous turn towards any combination of possibly negated
6377         // up and right vectors.
6378         tvec = Pl_objp->pos;
6379
6380         if (aip->submode_parm0 & 0x01)
6381                 vm_vec_add2(&tvec, &Pl_objp->orient.rvec);
6382         if (aip->submode_parm0 & 0x02)
6383                 vm_vec_sub2(&tvec, &Pl_objp->orient.rvec);
6384         if (aip->submode_parm0 & 0x04)
6385                 vm_vec_add2(&tvec, &Pl_objp->orient.uvec);
6386         if (aip->submode_parm0 & 0x08)
6387                 vm_vec_sub2(&tvec, &Pl_objp->orient.uvec);
6388
6389         //      Detect degenerate cases that cause tvec to be same as player pos.
6390         if (vm_vec_dist_quick(&tvec, &Pl_objp->pos) < 0.1f) {
6391                 aip->submode_parm0 &= 0x05;
6392                 if (aip->submode_parm0 == 0)
6393                         aip->submode_parm0 = 1;
6394                 vm_vec_add2(&tvec, &Pl_objp->orient.rvec);
6395         }
6396
6397         ai_turn_towards_vector(&tvec, Pl_objp, flFrametime, sip->srotation_time, NULL, NULL, 0.0f, 0);
6398         accelerate_ship(aip, 1.0f);
6399 }
6400
6401 //      ATTACK submode handler for chase mode.
6402 void ai_chase_eb(ai_info *aip, ship_info *sip, vector *predicted_enemy_pos, float dist_to_enemy)
6403 {
6404         vector  _pep;
6405         float           dot_to_enemy, dot_from_enemy;
6406
6407         compute_dots(Pl_objp, En_objp, &dot_to_enemy, &dot_from_enemy);
6408
6409         //      If we're trying to slow down to get behind, then point to turn towards is different.
6410         _pep = *predicted_enemy_pos;
6411         if ((dot_to_enemy > dot_from_enemy + 0.1f) || (dot_to_enemy > 0.9f))
6412                 vm_vec_scale_add(&_pep, &Pl_objp->pos, &En_objp->orient.fvec, 100.0f);
6413
6414         ai_turn_towards_vector(&_pep, Pl_objp, flFrametime, sip->srotation_time, NULL, NULL, 0.0f, 0);
6415
6416         accelerate_ship(aip, 0.0f);
6417 }
6418
6419 //      Return time until weapon_objp might hit ship_objp.
6420 //      Assumes ship_objp is not moving.
6421 //      Returns negative time if not going to hit.
6422 //      This is a very approximate function, but is pretty fast.
6423 float ai_endangered_time(object *ship_objp, object *weapon_objp)
6424 {
6425         float           to_dot, from_dot, dist;
6426
6427         dist = compute_dots(ship_objp, weapon_objp, &to_dot, &from_dot);
6428
6429         //      Note, this is bogus.  It assumes only the weapon is moving.
6430         //      Only proceed if weapon sort of pointing at object and object pointing towards or away from weapon
6431         //      (Ie, if object moving at right angle to weapon, just continue for now...)
6432         if (weapon_objp->phys_info.speed < 1.0f)
6433                 return dist + 1.0f;
6434         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))
6435                 return dist / weapon_objp->phys_info.speed;
6436         else
6437                 return -1.0f;
6438 }
6439
6440 //      Return time until danger weapon could hit this ai object.
6441 //      Return negative time if not endangered.
6442 float ai_endangered_by_weapon(ai_info *aip)
6443 {
6444         object  *weapon_objp;
6445
6446         if (aip->danger_weapon_objnum == -1) {
6447                 return -1.0f;
6448         }
6449
6450         weapon_objp = &Objects[aip->danger_weapon_objnum];
6451
6452         if (weapon_objp->signature != aip->danger_weapon_signature) {
6453                 aip->danger_weapon_objnum = -1;
6454                 return -1.0f;
6455         }
6456
6457         return ai_endangered_time(&Objects[Ships[aip->shipnum].objnum], weapon_objp);
6458 }
6459
6460 //      Return true if this ship is near full strength.
6461 int ai_near_full_strength(object *objp, ship_info *sip)
6462 {
6463         return (objp->hull_strength/sip->initial_hull_strength > 0.9f) || (get_shield_strength(objp)/sip->shields > 0.8f);
6464 }
6465                                 
6466 //      Set acceleration while in attack mode.
6467 void attack_set_accel(ai_info *aip, float dist_to_enemy, float dot_to_enemy, float dot_from_enemy)
6468 {
6469         float   speed_ratio;
6470
6471         if (En_objp->phys_info.speed > 1.0f)
6472                 speed_ratio = Pl_objp->phys_info.speed/En_objp->phys_info.speed;
6473         else
6474                 speed_ratio = 5.0f;
6475
6476         //      Sometimes, told to attack slowly.  Allows to get in more hits.
6477         if (aip->ai_flags & AIF_ATTACK_SLOWLY) {
6478                 if ((dist_to_enemy > 200.0f) && (dist_to_enemy < 800.0f)) {
6479                         if ((dot_from_enemy < 0.9f) || ai_near_full_strength(Pl_objp, &Ship_info[Ships[Pl_objp->instance].ship_info_index])) {
6480                                 //nprintf(("AI", " slowly "));
6481                                 accelerate_ship(aip, max(1.0f - (dist_to_enemy-200.0f)/600.0f, 0.1f));
6482                                 return;
6483                         }
6484                 } else
6485                         aip->ai_flags &= ~AIF_ATTACK_SLOWLY;
6486         }
6487
6488         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) {
6489                 //nprintf(("AI", "1"));
6490                 if (ai_maybe_fire_afterburner(Pl_objp, aip)) {
6491                         if (dist_to_enemy > 800.0f) {
6492                                 if (!( Pl_objp->phys_info.flags & PF_AFTERBURNER_ON )) {
6493                                         float percent_left;
6494                                         ship    *shipp;
6495                                         ship_info *sip;
6496
6497                                         shipp = &Ships[Pl_objp->instance];
6498                                         sip = &Ship_info[shipp->ship_info_index];
6499
6500                                         if (sip->afterburner_fuel_capacity > 0.0f) {
6501                                                 percent_left = 100.0f * shipp->afterburner_fuel / sip->afterburner_fuel_capacity;
6502                                                 if (percent_left > 30.0f + ((Pl_objp-Objects) & 0x0f)) {
6503                                                         afterburners_start(Pl_objp);
6504                                                         aip->afterburner_stop_time = Missiontime + F1_0 + static_rand(Pl_objp-Objects)/4;
6505                                                 }
6506                                         }
6507                                 }
6508                         }
6509                 }
6510
6511                 accelerate_ship(aip, 1.0f);
6512         } else if ((Missiontime - aip->last_hit_time > F1_0*7)
6513                 && (En_objp->phys_info.speed < 10.0f) 
6514                 && (dist_to_enemy > 25.0f) 
6515                 && (dot_to_enemy > 0.8f)
6516                 && (dot_from_enemy < 0.8f)) {
6517                 accelerate_ship(aip, 0.0f);             //      No one attacking us, so don't need to move.
6518         } else if ((dot_from_enemy < 0.25f) && (dot_to_enemy > 0.5f)) {
6519                 set_accel_for_target_speed(Pl_objp, En_objp->phys_info.speed);
6520         } else if (Pl_objp->phys_info.speed < 15.0f) {
6521                 accelerate_ship(aip, 1.0f);
6522         } else if (Pl_objp->phys_info.speed > En_objp->phys_info.speed - 1.0f) {
6523                 if (dot_from_enemy > 0.75f)
6524                         accelerate_ship(aip, 1.0f);
6525                 else
6526                         set_accel_for_target_speed(Pl_objp, En_objp->phys_info.speed*0.75f + 3.0f);
6527         } else {
6528                 change_acceleration(aip, 0.5f);
6529         }
6530 }
6531
6532 //      Pl_objp (aip) tries to get behind En_objp.
6533 //      New on 2/21/98: If this ship can move backwards and slide, maybe do that to get behind.
6534 void get_behind_ship(ai_info *aip, ship_info *sip, float dist_to_enemy)
6535 {
6536         vector  new_pos;
6537         float           dot;
6538         vector  vec_from_enemy;
6539         float           dist;
6540
6541         dist = vm_vec_normalized_dir(&vec_from_enemy, &Pl_objp->pos, &En_objp->pos);
6542
6543         vm_vec_scale_add(&new_pos, &En_objp->pos, &En_objp->orient.fvec, -100.0f);              //      Pick point 100 units behind.
6544         ai_turn_towards_vector(&new_pos, Pl_objp, flFrametime, sip->srotation_time, NULL, NULL, 0.0f, 0);
6545
6546         dot = vm_vec_dot(&vec_from_enemy, &En_objp->orient.fvec);
6547
6548         if (dot > 0.25f) {
6549                 accelerate_ship(aip, 1.0f);
6550         } else {
6551                 accelerate_ship(aip, (dot + 1.0f)/2.0f);
6552         }
6553 }
6554
6555 int avoid_player(object *objp, vector *goal_pos)
6556 {
6557         maybe_avoid_player(Pl_objp, goal_pos);
6558         ai_info *aip = &Ai_info[Ships[objp->instance].ai_index];
6559
6560         if (aip->ai_flags & AIF_AVOIDING_SMALL_SHIP) {
6561                 ship_info *sip = &Ship_info[Ships[objp->instance].ship_info_index];
6562
6563                 if (aip->ai_flags & AIF_AVOIDING_SMALL_SHIP) {
6564                         ai_turn_towards_vector(&aip->avoid_goal_point, objp, flFrametime, sip->srotation_time, NULL, NULL, 0.0f, 0);
6565                         accelerate_ship(aip, 0.5f);
6566                         return 1;
6567                 }
6568         }
6569
6570         return 0;
6571 }
6572
6573 //      Determine if a cylinder of width radius from p0 to p1 will collide with big_objp.
6574 //      If so, stuff *collision_point.
6575 int will_collide_pp(vector *p0, vector *p1, float radius, object *big_objp, vector *collision_point)
6576 {
6577         mc_info mc;
6578
6579         mc.model_num = Ships[big_objp->instance].modelnum;              // Fill in the model to check
6580         mc.orient = &big_objp->orient;                  // The object's orient
6581         mc.pos = &big_objp->pos;                                        // The object's position
6582         mc.p0 = p0;                                                                             // Point 1 of ray to check
6583         mc.p1 = p1;
6584         mc.flags = MC_CHECK_MODEL | MC_CHECK_SPHERELINE | MC_SUBMODEL;                                  // flags
6585
6586         mc.radius = radius;
6587
6588         // Only check the 2nd lowest hull object
6589         polymodel *pm = model_get(Ships[big_objp->instance].modelnum);
6590         mc.submodel_num = pm->detail[0]; //pm->submodel->num_details-2];
6591         model_collide(&mc);
6592
6593         if (mc.num_hits)
6594                 *collision_point = mc.hit_point_world;
6595
6596         return mc.num_hits;
6597 }
6598
6599 //      Return true/false if *objp will collide with *big_objp
6600 //      Stuff distance in *distance to collision point if *objp will collide with *big_objp within delta_time seconds.
6601 //      Global collision point stuffed in *collision_point
6602 int will_collide_with_big_ship(object *objp, vector *goal_point, object *big_objp, vector *collision_point, float delta_time)
6603 {
6604         float           radius;
6605         vector  end_pos;
6606
6607         radius = big_objp->radius + delta_time * objp->phys_info.speed;
6608
6609         if (vm_vec_dist_quick(&big_objp->pos, &objp->pos) > radius) {
6610                 return 0;
6611         }
6612
6613         if (goal_point == NULL) {
6614                 vm_vec_scale_add(&end_pos, &objp->pos, &objp->phys_info.vel, delta_time);                                       // Point 2 of ray to check
6615         } else {
6616                 end_pos = *goal_point;
6617         }
6618
6619         return will_collide_pp(&objp->pos, &end_pos, objp->radius, big_objp, collision_point);
6620 }
6621
6622 //      Return true if *objp is expected to collide with a large ship.
6623 //      Stuff global collision point in *collision_point.
6624 //      If *goal_point is not NULL, use that as the point towards which *objp will be flying.  Don't use *objp velocity
6625 //      *ignore_objp will typically be the target this ship is pursuing, either to attack or guard.  We don't want to avoid it.
6626 int will_collide_with_big_ship_all(object *objp, object *ignore_objp, vector *goal_point, vector *collision_point, float *distance, float delta_time)
6627 {
6628         ship_obj        *so;
6629         object  *big_objp;
6630         int             collision_obj_index = -1;
6631         float           min_dist = 999999.9f;
6632         float           collision_time = -1.0f;
6633
6634         for ( so = GET_FIRST(&Ship_obj_list); so != END_OF_LIST(&Ship_obj_list); so = GET_NEXT(so) ) {
6635                 float   time = 0.0f;
6636                 big_objp = &Objects[so->objnum];
6637
6638                 if (big_objp == ignore_objp)
6639                         continue;
6640
6641                 if (Ship_info[Ships[big_objp->instance].ship_info_index].flags & (SIF_BIG_SHIP | SIF_HUGE_SHIP)) {
6642                         vector  cur_collision_point;
6643                         float           cur_dist;
6644
6645                         if (will_collide_with_big_ship(objp, goal_point, big_objp, &cur_collision_point, delta_time)) {
6646
6647                                 cur_dist = vm_vec_dist(&cur_collision_point, &objp->pos);
6648
6649                                 if (cur_dist < min_dist) {
6650                                         min_dist = cur_dist;
6651                                         *collision_point = cur_collision_point;
6652                                         collision_time = time;
6653                                         collision_obj_index = OBJ_INDEX(big_objp);
6654                                 }
6655                         }
6656                 }
6657         }
6658
6659         *distance = min_dist;
6660         return collision_obj_index;
6661
6662 }
6663
6664 typedef struct {
6665         float           dist;
6666         int             collide;
6667         vector  pos;
6668 } sgoal;
6669
6670 //int will_collide_pp(vector *p0, vector *p1, float radius, object *big_objp, vector *collision_point)
6671 //      Pick a point for *objp to fly towards to avoid a collision with *big_objp at *collision_point
6672 //      Return result in *avoid_pos
6673 void mabs_pick_goal_point(object *objp, object *big_objp, vector *collision_point, vector *avoid_pos)
6674 {
6675         matrix  mat1;
6676         sgoal           goals[4];
6677         vector  v2b;
6678
6679         vm_vec_normalized_dir(&v2b, collision_point, &objp->pos);
6680         vm_vector_2_matrix(&mat1, &v2b, NULL, NULL);
6681
6682         int     found = 0;
6683
6684         //      Try various scales, in 0.5f, 0.75f, 1.0f, 1.25f.
6685         //      First try 0.5f to see if we can find a point that near the center of the target ship, which presumably
6686         //      means less of a turn.
6687         //      Try going as far as 1.25f * radius.
6688         float   s;
6689         for (s=0.5f; s<1.3f; s += 0.25f) {
6690                 int     i;
6691                 for (i=0; i<4; i++) {
6692                         vector p = big_objp->pos;
6693                         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
6694                         float kr = big_objp->radius*s + objp->radius * ((OBJ_INDEX(objp) % 4) ^ 2)/4;
6695                         if (i&1)
6696                                 ku = -ku;
6697                         if (i&2)
6698                                 kr = -kr;
6699                         vm_vec_scale_add2(&p, &mat1.uvec, ku);
6700                         vm_vec_scale_add2(&p, &mat1.rvec, kr);
6701                         goals[i].pos = p;
6702                         goals[i].dist = vm_vec_dist_quick(&objp->pos, &p);
6703                         goals[i].collide = will_collide_pp(&objp->pos, &p, objp->radius, big_objp, collision_point);
6704                         if (!goals[i].collide)
6705                                 found = 1;
6706                 }
6707
6708                 //      If we found a point that doesn't collide, find the nearest one and make that the *avoid_pos.
6709                 if (found) {
6710                         float   min_dist = 9999999.9f;
6711                         int     min_index = -1;
6712
6713                         for (i=0; i<4; i++) {
6714                                 if (!goals[i].collide && (goals[i].dist < min_dist)) {
6715                                         min_dist = goals[i].dist;
6716                                         min_index = i;
6717                                 }
6718                         }
6719
6720                         Assert(i != -1);
6721                         if (i != -1) {
6722                                 *avoid_pos = goals[min_index].pos;
6723                                 return;
6724                         }
6725                 }
6726         }
6727
6728         //      Drat.  We tried and tried and could not find a point that did not cause a collision.
6729         //      Get this dump pilot far away from the problem ship.
6730         vector  away_vec;
6731         vm_vec_normalized_dir(&away_vec, &objp->pos, collision_point);
6732         vm_vec_scale_add(avoid_pos, &objp->pos, &away_vec, big_objp->radius*1.5f);
6733
6734 }
6735
6736 //      Return true if a large ship is being ignored.
6737 int maybe_avoid_big_ship(object *objp, object *ignore_objp, ai_info *aip, vector *goal_point, float delta_time)
6738 {
6739         if (timestamp_elapsed(aip->avoid_check_timestamp)) {
6740                 float           distance;
6741                 vector  collision_point;
6742                 int             ship_num;
6743                 if ((ship_num = will_collide_with_big_ship_all(Pl_objp, ignore_objp, goal_point, &collision_point, &distance, delta_time)) != -1) {
6744                         aip->ai_flags |= AIF_AVOIDING_BIG_SHIP;
6745                         mabs_pick_goal_point(objp, &Objects[ship_num], &collision_point, &aip->avoid_goal_point);
6746                         float dist = vm_vec_dist_quick(&aip->avoid_goal_point, &objp->pos);
6747                         aip->avoid_check_timestamp = timestamp(2000 + min(1000, (int) (dist * 2.0f)));  //      Delay until check again is based on distance to avoid point.
6748                         aip->avoid_ship_num = ship_num;
6749                 } else {
6750                         aip->ai_flags &= ~AIF_AVOIDING_BIG_SHIP;
6751                         aip->ai_flags &= ~AIF_AVOIDING_SMALL_SHIP;
6752                         aip->avoid_ship_num = -1;
6753                         aip->avoid_check_timestamp = timestamp(1500);
6754                 }
6755         }
6756         
6757         if (aip->ai_flags & AIF_AVOIDING_BIG_SHIP) {
6758                 ship_info *sip = &Ship_info[Ships[Pl_objp->instance].ship_info_index];
6759
6760                 vector  v2g;
6761
6762                 ai_turn_towards_vector(&aip->avoid_goal_point, Pl_objp, flFrametime, sip->srotation_time, NULL, NULL, 0.0f, 0);
6763                 vm_vec_normalized_dir(&v2g, &aip->avoid_goal_point, &Pl_objp->pos);
6764                 float dot = vm_vec_dot(&objp->orient.fvec, &v2g);
6765                 float d2 = (1.0f + dot) * (1.0f + dot);
6766                 accelerate_ship(aip, d2/4.0f);
6767                 return 1;
6768         }
6769
6770         return 0;
6771 }
6772
6773 //      Set desired right vector for ships flying towards another ship.
6774 //      Since this is governed only by vector to target, it causes ships to align bank and look less chaotic.
6775 void compute_desired_rvec(vector *rvec, vector *goal_pos, vector *cur_pos)
6776 {
6777         vector  v2e;
6778
6779         vm_vec_normalized_dir(&v2e, goal_pos, cur_pos);
6780         rvec->x = v2e.z;
6781         rvec->y = 0.0f;
6782         rvec->z = -v2e.x;
6783         if (vm_vec_mag_squared(rvec) < 0.001f)
6784                 rvec->y = 1.0f;
6785 }
6786
6787 // Handler for stealth find submode of Chase.
6788 void ai_stealth_find()
6789 {
6790         ai_info         *aip;
6791         ship_info       *sip;
6792
6793         vector new_pos, vec_to_enemy;
6794         float dist_to_enemy, dot_to_enemy, dot_from_enemy;
6795
6796         Assert(Ships[Pl_objp->instance].ship_info_index >= 0);
6797         sip = &Ship_info[Ships[Pl_objp->instance].ship_info_index];
6798         Assert(Ships[Pl_objp->instance].ai_index >= 0);
6799         aip = &Ai_info[Ships[Pl_objp->instance].ai_index];
6800
6801         // get time since last seen
6802         int delta_time = (timestamp() - aip->stealth_last_visible_stamp);
6803
6804         // if delta_time is really big, i'm real confused, start sweep
6805         if (delta_time > 10000) {
6806                 aip->submode_parm0 = SM_SF_BAIL;
6807         }
6808
6809         // guestimate new position
6810         vm_vec_scale_add(&new_pos, &aip->stealth_last_pos, &aip->stealth_velocity, (delta_time * 0.001f));
6811
6812         // if I think he's behind me, go to the goal point
6813         if ( aip->submode_parm0 == SM_SF_BEHIND ) {
6814                 new_pos = aip->goal_point;
6815         }
6816
6817         // check for collision with big ships
6818         if (maybe_avoid_big_ship(Pl_objp, En_objp, aip, &new_pos, 10.0f)) {
6819                 // reset ai submode to chase
6820                 return;
6821         }
6822
6823         // if dist is near max and dot is close to 1, accel, afterburn
6824         vm_vec_sub(&vec_to_enemy, &new_pos, &Pl_objp->pos);
6825         dist_to_enemy = vm_vec_normalize_quick(&vec_to_enemy);
6826         dot_to_enemy = vm_vec_dotprod(&vec_to_enemy, &Pl_objp->orient.fvec);
6827
6828         // 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
6829         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) ) {
6830                 // do turn around)
6831                 vm_vec_scale_add(&aip->goal_point, &Pl_objp->pos, &Pl_objp->orient.fvec, -300.0f);
6832                 aip->submode_parm0 = SM_SF_BEHIND;
6833                 vm_vec_sub(&vec_to_enemy, &new_pos, &Pl_objp->pos);
6834                 dist_to_enemy = vm_vec_normalize_quick(&vec_to_enemy);
6835                 dot_to_enemy = vm_vec_dotprod(&vec_to_enemy, &Pl_objp->orient.fvec);
6836         }
6837
6838         if ( (dist_to_enemy > get_skill_stealth_dist_scaler()*STEALTH_MAX_VIEW_DIST) && (dot_to_enemy > 0.94f) ) {              // 20 degree half angle
6839                 // accelerate ship
6840                 accelerate_ship(aip, 1.0f);
6841
6842                 // engage afterburner
6843                 if (!( Pl_objp->phys_info.flags & PF_AFTERBURNER_ON )) {
6844                         if (ai_maybe_fire_afterburner(Pl_objp, aip)) {
6845                                 afterburners_start(Pl_objp);
6846                                 aip->afterburner_stop_time = Missiontime + 3*F1_0/2;
6847                         }
6848                 }
6849
6850                 ai_turn_towards_vector(&new_pos, Pl_objp, flFrametime, sip->srotation_time, NULL, NULL, 0.0f, 0);
6851                 return;
6852         }
6853
6854         //      If enemy more than 500 meters away, all ships flying there will tend to match bank.
6855         //      They do this by using their vector to their target to compute their right vector and causing ai_turn_towards_vector
6856         //      to interpolate a matrix rather than just a vector.
6857         if (dist_to_enemy > 500.0f) {
6858                 vector  rvec;
6859                 compute_desired_rvec(&rvec, &new_pos, &Pl_objp->pos);
6860                 ai_turn_towards_vector(&new_pos, Pl_objp, flFrametime, sip->srotation_time, NULL, NULL, 0.0f, 0, &rvec);
6861         } else {
6862                 ai_turn_towards_vector(&new_pos, Pl_objp, flFrametime, sip->srotation_time, NULL, NULL, 0.0f, 0);
6863         }
6864
6865         dot_from_enemy = -vm_vec_dotprod(&vec_to_enemy, &En_objp->orient.fvec);
6866
6867         attack_set_accel(aip, dist_to_enemy, dot_to_enemy, dot_from_enemy);
6868 }
6869
6870 // -----------------------------------------------------------------------------
6871 // try to find stealth ship by sweeping an area
6872 void ai_stealth_sweep()
6873 {
6874         ai_info         *aip;
6875         ship_info       *sip;
6876
6877         Assert(Ships[Pl_objp->instance].ship_info_index >= 0);
6878         sip = &Ship_info[Ships[Pl_objp->instance].ship_info_index];
6879         Assert(Ships[Pl_objp->instance].ai_index >= 0);
6880         aip = &Ai_info[Ships[Pl_objp->instance].ai_index];
6881
6882         vector goal_pt;
6883         vector forward, right, up;
6884         int lost_time;
6885
6886         // time since stealth last seen
6887         lost_time = (timestamp() - aip->stealth_last_visible_stamp);
6888
6889         // determine which pt to fly to in sweep by keeping track of parm0
6890         if (aip->submode_parm0 == SM_SS_SET_GOAL) {
6891
6892                 // don't make goal pt more than 2k from current pos
6893                 vm_vec_scale_add(&goal_pt, &aip->stealth_last_pos, &aip->stealth_velocity, (0.001f * lost_time));
6894
6895                 // make box size based on speed of stealth and expected time to intercept (keep box in range 200-500)
6896                 float box_size = vm_vec_mag_quick(&aip->stealth_velocity) * (0.001f * lost_time);
6897                 box_size = min(200.0f, box_size);
6898                 box_size = max(500.0f, box_size);
6899                 aip->stealth_sweep_box_size = box_size;
6900
6901                 aip->goal_point = goal_pt;
6902                 aip->submode_parm0 = SM_SS_BOX0;
6903         }
6904
6905         // GET UP, RIGHT, FORWARD FOR BOX based on stealth ship's velocity
6906         // if velocity changes in stealth mode, then ship is *seen*, and falls out of sweep mode
6907         // if stealth has no velocity make a velocity
6908         if ( vm_vec_mag_quick(&aip->stealth_velocity) < 1 ) {
6909                 vm_vec_rand_vec_quick(&aip->stealth_velocity);
6910         }
6911
6912         // get "right" vector for box
6913         vm_vec_crossprod(&right, &aip->stealth_velocity, &vmd_y_vector);
6914
6915         if ( vm_vec_mag_quick(&right) < 0.01 ) {
6916                 vm_vec_crossprod(&right, &aip->stealth_velocity, &vmd_z_vector);
6917         }
6918
6919         vm_vec_normalize_quick(&right);
6920
6921         // get forward for box
6922         vm_vec_copy_normalize_quick(&forward, &aip->stealth_velocity);
6923
6924         // get "up" for box
6925         vm_vec_crossprod(&up, &forward, &right);
6926         
6927         // lost far away ahead (do box)
6928         switch(aip->submode_parm0) {
6929         case SM_SS_BOX0:
6930                 goal_pt = aip->goal_point;
6931                 break;
6932
6933         // pt1 -U +R
6934         case SM_SS_LR:
6935                 vm_vec_scale_add(&goal_pt, &aip->goal_point, &up, -aip->stealth_sweep_box_size);
6936                 vm_vec_scale_add2(&goal_pt, &right, aip->stealth_sweep_box_size);
6937                 vm_vec_scale_add2(&goal_pt, &forward, 0.5f*aip->stealth_sweep_box_size);
6938                 break;
6939
6940         // pt2 +U -R
6941         case SM_SS_UL:
6942                 vm_vec_scale_add(&goal_pt, &aip->goal_point, &up, aip->stealth_sweep_box_size);
6943                 vm_vec_scale_add2(&goal_pt, &right, -aip->stealth_sweep_box_size);
6944                 vm_vec_scale_add2(&goal_pt, &forward, 0.5f*aip->stealth_sweep_box_size);
6945                 break;
6946
6947         // pt3 back
6948         case SM_SS_BOX1:
6949                 goal_pt = aip->goal_point;
6950                 break;
6951
6952         // pt4 +U +R
6953         case SM_SS_UR:
6954                 vm_vec_scale_add(&goal_pt, &aip->goal_point, &up, aip->stealth_sweep_box_size);
6955                 vm_vec_scale_add2(&goal_pt, &right, aip->stealth_sweep_box_size);
6956                 vm_vec_scale_add2(&goal_pt, &forward, 0.5f*aip->stealth_sweep_box_size);
6957                 break;
6958
6959         // pt5 -U -R
6960         case SM_SS_LL:
6961                 vm_vec_scale_add(&goal_pt, &aip->goal_point, &up, -aip->stealth_sweep_box_size);
6962                 vm_vec_scale_add2(&goal_pt, &right, -aip->stealth_sweep_box_size);
6963                 vm_vec_scale_add2(&goal_pt, &forward, 0.5f*aip->stealth_sweep_box_size);
6964                 break;
6965
6966         // pt6 back
6967         case SM_SS_BOX2:
6968                 goal_pt = aip->goal_point;
6969                 break;
6970
6971         default:
6972                 Int3();
6973
6974         }
6975
6976         // when close to goal_pt, update next goal pt
6977         float dist_to_goal = vm_vec_dist(&goal_pt, &Pl_objp->pos);
6978         if (dist_to_goal < 15) {
6979                 aip->submode_parm0++;
6980         }
6981
6982         // check for collision with big ship
6983         if (maybe_avoid_big_ship(Pl_objp, En_objp, aip, &goal_pt, 10.0f)) {
6984                 // skip to the next pt on box
6985                 aip->submode_parm0++;
6986                 return;
6987         }
6988
6989         ai_turn_towards_vector(&goal_pt, Pl_objp, flFrametime, sip->srotation_time, NULL, NULL, 0.0f, 0);
6990
6991         float dot = 1.0f;
6992         if (dist_to_goal < 100) {
6993                 vector vec_to_goal;
6994                 vm_vec_normalized_dir(&vec_to_goal, &goal_pt, &Pl_objp->pos);
6995                 dot = vm_vec_dotprod(&vec_to_goal, &Pl_objp->orient.fvec);
6996         }
6997
6998         accelerate_ship(aip, 0.8f*dot);
6999 }
7000
7001 //      ATTACK submode handler for chase mode.
7002 void ai_chase_attack(ai_info *aip, ship_info *sip, vector *predicted_enemy_pos, float dist_to_enemy)
7003 {
7004         int             start_bank;
7005         float           dot_to_enemy, dot_from_enemy; //, time_to_hit;
7006         float           bank_override = 0.0f;
7007
7008         if (avoid_player(Pl_objp, predicted_enemy_pos))
7009                 return;
7010
7011         compute_dots(Pl_objp, En_objp, &dot_to_enemy, &dot_from_enemy);
7012
7013         polymodel *po = model_get( sip->modelnum );
7014
7015         vector  *rel_pos;
7016         float           scale;
7017         vector  randvec;
7018         vector  new_pos;
7019
7020         start_bank = Ships[aip->shipnum].weapons.current_primary_bank;
7021         if (po->n_guns && start_bank != -1 ) {
7022                 rel_pos = &po->gun_banks[start_bank].pnt[0];
7023         } else
7024                 rel_pos = NULL;
7025
7026         //      If ship moving slowly relative to its size, then don't attack its center point.
7027         //      How far from center we attack is based on speed, size and distance to enemy
7028         if (En_objp->radius > En_objp->phys_info.speed) {
7029                 static_randvec(Pl_objp-Objects, &randvec);
7030                 scale = dist_to_enemy/(dist_to_enemy + En_objp->radius) * En_objp->radius;
7031                 scale *= 0.5f * En_objp->radius/(En_objp->phys_info.speed + En_objp->radius);   // scale downward by 1/2 to 1/4
7032                 vm_vec_scale_add(&new_pos, predicted_enemy_pos, &randvec, scale);
7033         } else
7034                 new_pos = *predicted_enemy_pos;
7035
7036         if (dist_to_enemy < 250.0f) {
7037                 if (dot_from_enemy > 0.7f) {
7038                         bank_override = Pl_objp->phys_info.speed;
7039                 }
7040         }
7041
7042         //      If enemy more than 500 meters away, all ships flying there will tend to match bank.
7043         //      They do this by using their vector to their target to compute their right vector and causing ai_turn_towards_vector
7044         //      to interpolate a matrix rather than just a vector.
7045         if (dist_to_enemy > 500.0f) {
7046                 vector  rvec;
7047                 compute_desired_rvec(&rvec, predicted_enemy_pos, &Pl_objp->pos);
7048                 ai_turn_towards_vector(&new_pos, Pl_objp, flFrametime, sip->srotation_time, NULL, rel_pos, bank_override, 0, &rvec);
7049         } else {
7050                 ai_turn_towards_vector(&new_pos, Pl_objp, flFrametime, sip->srotation_time, NULL, rel_pos, bank_override, 0);
7051         }
7052
7053         attack_set_accel(aip, dist_to_enemy, dot_to_enemy, dot_from_enemy);
7054 }
7055
7056 //      EVADE_SQUIGGLE submode handler for chase mode.
7057 //      Changed by MK on 5/5/97.
7058 //      Used to evade towards a point off the right or up vector.
7059 //      Now, evade straight away to try to get far away.
7060 //      The squiggling should protect against laser fire.
7061 void ai_chase_es(ai_info *aip, ship_info *sip)
7062 {
7063         vector  tvec;
7064         fix             timeslice;
7065         fix             scale;
7066         float           bank_override = 0.0f;
7067
7068         tvec = Pl_objp->pos;
7069
7070         timeslice = (Missiontime >> 16) & 0x0f;
7071         scale = ((Missiontime >> 16) & 0x0f) << 14;
7072
7073         if (timeslice & 0x01)
7074                 vm_vec_scale_add2(&tvec, &Pl_objp->orient.rvec, f2fl(scale ^ 0x10000));
7075         if (timeslice & 0x02)
7076                 vm_vec_scale_sub2(&tvec, &Pl_objp->orient.rvec, f2fl(scale));
7077         if (timeslice & 0x04)
7078                 vm_vec_scale_add2(&tvec, &Pl_objp->orient.uvec, f2fl(scale ^ 0x10000));
7079         if (timeslice & 0x08)
7080                 vm_vec_scale_sub2(&tvec, &Pl_objp->orient.uvec, f2fl(scale));
7081
7082         while (vm_vec_dist_quick(&tvec, &Pl_objp->pos) < 0.1f) {
7083                 tvec.x += frand();
7084                 tvec.y += frand();
7085         }
7086
7087         bank_override = Pl_objp->phys_info.speed;
7088
7089         ai_turn_towards_vector(&tvec, Pl_objp, flFrametime/2, sip->srotation_time, NULL, NULL, bank_override, 0);
7090         accelerate_ship(aip, 1.0f);
7091 }
7092
7093 //      Trying to get away from opponent.
7094 void ai_chase_ga(ai_info *aip, ship_info *sip)
7095 {
7096         //      If not near end of this submode, evade squiggly.  If near end, just fly straight for a bit
7097         vector  tvec;
7098         float           bank_override;
7099         vector  vec_from_enemy;
7100
7101         if (En_objp != NULL) {
7102                 vm_vec_normalized_dir(&vec_from_enemy, &Pl_objp->pos, &En_objp->pos);
7103         } else
7104                 vec_from_enemy = Pl_objp->orient.fvec;
7105
7106         static_randvec(Missiontime >> 15, &tvec);
7107         vm_vec_scale(&tvec, 100.0f);
7108         vm_vec_scale_add2(&tvec, &vec_from_enemy, 300.0f);
7109         vm_vec_add2(&tvec, &Pl_objp->pos);
7110
7111         bank_override = Pl_objp->phys_info.speed;
7112
7113         ai_turn_towards_vector(&tvec, Pl_objp, flFrametime/2, sip->srotation_time, NULL, NULL, bank_override, 0);
7114
7115         accelerate_ship(aip, 2.0f);
7116
7117         if (ai_maybe_fire_afterburner(Pl_objp, aip)) {
7118                 if (!(Pl_objp->phys_info.flags & PF_AFTERBURNER_ON )) {
7119                         float percent_left = 100.0f * Ships[Pl_objp->instance].afterburner_fuel / sip->afterburner_fuel_capacity;
7120                         if (percent_left > 30.0f + ((Pl_objp-Objects) & 0x0f)) {
7121                                 afterburners_start(Pl_objp);
7122                                 aip->afterburner_stop_time = Missiontime + 3*F1_0/2;
7123                         }
7124                         afterburners_start(Pl_objp);
7125                         aip->afterburner_stop_time = Missiontime + 3*F1_0/2;
7126                 }
7127         }
7128
7129 }
7130
7131 //      Make object *objp attack subsystem with ID = subnum.
7132 //      Return true if found a subsystem to attack, else return false.
7133 //      Note, can fail if subsystem exists, but has no hits.
7134 int ai_set_attack_subsystem(object *objp, int subnum)
7135 {
7136         ship                    *shipp, *attacker_shipp;
7137         ai_info         *aip;
7138         ship_subsys     *ssp;
7139         object          *attacked_objp;
7140
7141         Assert(objp->type == OBJ_SHIP);
7142         Assert(objp->instance >= 0);
7143
7144         attacker_shipp = &Ships[objp->instance];
7145         Assert(attacker_shipp->ai_index >= 0);
7146
7147         aip = &Ai_info[attacker_shipp->ai_index];
7148
7149         // MWA -- 2/27/98.  Due to AL's changes, target_objnum is now not always valid (at least sometimes
7150         // in terms of goals).  So, bail if we don't have a valid target.
7151         if ( aip->target_objnum == -1 )
7152                 return 0;
7153
7154         attacked_objp = &Objects[aip->target_objnum];
7155         shipp = &Ships[attacked_objp->instance];                //  need to get our target's ship pointer!!!
7156
7157         ssp = ship_get_indexed_subsys(shipp, subnum, &objp->pos);
7158         if (ssp == NULL)
7159                 return 0;
7160
7161         set_targeted_subsys(aip, ssp, aip->target_objnum);
7162         
7163         if (aip->ignore_objnum == aip->target_objnum)
7164                 aip->ignore_objnum = UNUSED_OBJNUM;
7165
7166         // -- Done at caller in ai_process_mission_orders -- attacked_objp->flags |= OF_PROTECTED;
7167
7168         ai_set_goal_maybe_abort_dock(objp, aip);
7169         aip->ok_to_target_timestamp = timestamp(DELAY_TARGET_TIME);
7170
7171         return 1;
7172 }
7173
7174 void ai_set_guard_vec(object *objp, object *guard_objp)
7175 {
7176         ai_info *aip;
7177         float   radius;
7178
7179         aip = &Ai_info[Ships[objp->instance].ai_index];
7180
7181         //      Handle case of bogus call in which ship is told to guard self.
7182         Assert(objp != guard_objp);
7183         if (objp == guard_objp) {
7184                 vm_vec_rand_vec_quick(&aip->guard_vec);
7185                 vm_vec_scale(&aip->guard_vec, 100.0f);
7186                 return;
7187         }
7188
7189         // check if guard_objp is BIG
7190         radius = 5.0f * (objp->radius + guard_objp->radius) + 50.0f;
7191         if (radius > 300.0f) {
7192                 radius = guard_objp->radius * 1.25f;
7193         }
7194
7195         vm_vec_sub(&aip->guard_vec, &objp->pos, &guard_objp->pos);
7196
7197         if (vm_vec_mag(&aip->guard_vec) > 3.0f*radius) {
7198                 //      Far away, don't just use vector to object, causes clustering of guard ships.
7199                 vector  tvec, rvec;
7200                 float   mag;
7201                 mag = vm_vec_copy_normalize(&tvec, &aip->guard_vec);
7202                 vm_vec_rand_vec_quick(&rvec);                   
7203                 vm_vec_scale_add2(&tvec, &rvec, 0.5f);
7204                 vm_vec_copy_scale(&aip->guard_vec, &tvec, mag);
7205         }
7206
7207         vm_vec_normalize_quick(&aip->guard_vec);
7208         vm_vec_scale(&aip->guard_vec, radius);
7209 }
7210
7211 //      Make object *objp guard object *other_objp.
7212 //      To be called from the goals code.
7213 void ai_set_guard_wing(object *objp, int wingnum)
7214 {
7215         ship            *shipp;
7216         ai_info *aip;
7217         int             leader_objnum, leader_shipnum;
7218
7219         Assert(wingnum >= 0);
7220
7221         Assert(objp->type == OBJ_SHIP);
7222         Assert(objp->instance >= 0);
7223
7224         // shouldn't set the ai mode for the player
7225         if ( objp == Player_obj ) {
7226                 return;
7227         }
7228
7229         shipp = &Ships[objp->instance];
7230
7231         Assert(shipp->ai_index >= 0);
7232
7233         aip = &Ai_info[shipp->ai_index];
7234         force_avoid_player_check(objp, aip);
7235
7236         ai_set_goal_maybe_abort_dock(objp, aip);
7237         aip->ok_to_target_timestamp = timestamp(DELAY_TARGET_TIME);
7238
7239         //      This function is called whenever a guarded ship is destroyed, so this code
7240         //      prevents a ship from trying to guard a non-existent wing.
7241         if (Wings[wingnum].current_count < 1) {
7242                 aip->guard_objnum = -1;
7243                 aip->guard_wingnum = -1;
7244                 aip->mode = AIM_NONE;
7245         } else {
7246                 leader_shipnum = Wings[wingnum].ship_index[0];
7247                 leader_objnum = Ships[leader_shipnum].objnum;
7248
7249                 Assert((leader_objnum >= 0) && (leader_objnum < MAX_OBJECTS));
7250                 //Assert(leader_objnum != objp-Objects);        //      Don't allow ships to guard themselves.
7251                 if (leader_objnum == OBJ_INDEX(objp)) {
7252                         //Int3();       //      Seems illegal, but let's clean up.  Get MikeK.
7253                         return;
7254                 }
7255
7256                 aip->guard_wingnum = wingnum;
7257                 aip->guard_objnum = leader_objnum;
7258                 aip->guard_signature = Objects[leader_objnum].signature;
7259                 aip->mode = AIM_GUARD;
7260                 aip->submode = AIS_GUARD_STATIC;
7261
7262                 ai_set_guard_vec(objp, &Objects[leader_objnum]);
7263         }
7264 }
7265
7266 //      Make object *objp guard object *other_objp.
7267 //      To be called from the goals code.
7268 void ai_set_evade_object(object *objp, object *other_objp)
7269 {
7270         ship            *shipp;
7271         ai_info *aip;
7272         int             other_objnum;
7273
7274         Assert(objp->type == OBJ_SHIP);
7275         Assert(objp->instance >= 0);
7276
7277         shipp = &Ships[objp->instance];
7278
7279         Assert(shipp->ai_index >= 0);
7280
7281         aip = &Ai_info[shipp->ai_index];
7282
7283         other_objnum = OBJ_INDEX(other_objp);
7284         Assert(other_objnum >= 0);
7285
7286         Assert(other_objnum != Ships[aip->shipnum].objnum);     //      make sure not targeting self
7287         aip->target_objnum = other_objnum;
7288
7289         aip->mode = AIM_EVADE;
7290 }
7291
7292 //      Make objp guard other_objp
7293 //      If other_objp is a member of a wing, objp will guard that whole wing
7294 //      UNLESS objp is also a member of the wing!
7295 void ai_set_guard_object(object *objp, object *other_objp)
7296 {
7297         ship            *shipp;
7298         ai_info *aip;
7299         int             other_objnum;
7300
7301         Assert(objp->type == OBJ_SHIP);
7302         Assert(objp->instance >= 0);
7303         Assert(objp != other_objp);
7304
7305         shipp = &Ships[objp->instance];
7306
7307         Assert(shipp->ai_index >= 0);
7308
7309         aip = &Ai_info[shipp->ai_index];
7310         aip->avoid_check_timestamp = timestamp(1);
7311
7312         //      If ship to guard is in a wing, guard that whole wing.
7313         ai_info *other_aip = &Ai_info[Ships[other_objp->instance].ai_index];
7314         if ((other_aip->wing != -1) && (other_aip->wing != aip->wing)) {
7315                 ai_set_guard_wing(objp, Ai_info[Ships[other_objp->instance].ai_index].wing);
7316         } else {
7317
7318                 other_objnum = other_objp-Objects;
7319
7320                 aip->guard_objnum = other_objnum;
7321                 aip->guard_signature = other_objp->signature;
7322                 aip->guard_wingnum = -1;
7323
7324                 aip->mode = AIM_GUARD;
7325                 aip->submode = AIS_GUARD_STATIC;
7326
7327                 Assert(other_objnum >= 0);      //      Hmm, bogus object and we need its position for guard_vec.
7328
7329                 // vm_vec_sub(&aip->guard_vec, &objp->pos, &Objects[other_objnum].pos);
7330                 ai_set_guard_vec(objp, &Objects[other_objnum]);
7331
7332                 ai_set_goal_maybe_abort_dock(objp, aip);
7333                 aip->ok_to_target_timestamp = timestamp(DELAY_TARGET_TIME);
7334         }
7335 }
7336
7337 //      Update the aspect_locked_time field based on whether enemy is in view cone.
7338 //      Also set/clear AIF_SEEK_LOCK.
7339 void update_aspect_lock_information(ai_info *aip, vector *vec_to_enemy, float dist_to_enemy, float enemy_radius)
7340 {
7341         float   dot_to_enemy;
7342         int     num_weapon_types;
7343         int     weapon_id_list[MAX_WEAPON_TYPES], weapon_bank_list[MAX_WEAPON_TYPES];
7344         ship    *shipp;
7345         ship_weapon     *swp;
7346         weapon_info     *wip;
7347
7348         shipp = &Ships[aip->shipnum];
7349         swp = &shipp->weapons;
7350
7351         // AL 3-7-98: This probably should never happen, but check to ensure that current_secondary_bank is valid
7352         if ( (swp->current_secondary_bank < 0) || (swp->current_secondary_bank > swp->num_secondary_banks) ) {
7353                 return;
7354         }
7355
7356         num_weapon_types = get_available_secondary_weapons(Pl_objp, weapon_id_list, weapon_bank_list);
7357
7358         wip = &Weapon_info[swp->secondary_bank_weapons[swp->current_secondary_bank]];
7359
7360         if (num_weapon_types && (wip->wi_flags & WIF_HOMING_ASPECT)) {
7361                 if (dist_to_enemy > 300.0f - min(enemy_radius, 100.0f))
7362                         aip->ai_flags |= AIF_SEEK_LOCK;
7363                 else
7364                         aip->ai_flags &= ~AIF_SEEK_LOCK;
7365
7366                 //      Update locking information for aspect seeking missiles.
7367                 aip->current_target_is_locked = 0;
7368                 dot_to_enemy = vm_vec_dot(vec_to_enemy, &Pl_objp->orient.fvec);
7369
7370                 float   needed_dot = 0.9f - 0.5f * enemy_radius/(dist_to_enemy + enemy_radius); //      Replaced MIN_TRACKABLE_DOT with 0.9f
7371                 if (dot_to_enemy > needed_dot) {
7372                         aip->aspect_locked_time += flFrametime;
7373                         // nprintf(("AI", "+ Lock time = %7.3f\n", aip->aspect_locked_time));
7374                         if (aip->aspect_locked_time >= wip->min_lock_time) {
7375                                 aip->aspect_locked_time = wip->min_lock_time;
7376                                 aip->current_target_is_locked = 1;
7377                         }
7378                 } else {
7379                         aip->aspect_locked_time -= flFrametime*2;
7380                         // nprintf(("AI", "- Lock time = %7.3f\n", aip->aspect_locked_time));
7381                         if (aip->aspect_locked_time < 0.0f)
7382                                 aip->aspect_locked_time = 0.0f;
7383                 }
7384                 //nprintf(("AI", "dot = %7.3f, time = %7.3f\n", dot_to_enemy, aip->aspect_locked_time));
7385         
7386         } else {
7387                 aip->current_target_is_locked = 0;
7388                 aip->aspect_locked_time = 0.0f; // Used to be this, why?: wip->min_lock_time;
7389                 aip->ai_flags &= ~AIF_SEEK_LOCK;
7390         }
7391
7392 }
7393
7394 //      We're in chase mode and we've recently collided with our target.
7395 //      Fly away from it!
7396 void ai_chase_fly_away(object *objp, ai_info *aip)
7397 {
7398         int     abort_flag = 0;
7399
7400         if (aip->ai_flags & AIF_TARGET_COLLISION) {
7401                 aip->ai_flags &= ~AIF_TARGET_COLLISION; //      Don't process this hit again next frame.
7402                 aip->submode = SM_FLY_AWAY;                                     //      Focus on avoiding target
7403                 aip->submode_start_time = Missiontime;
7404         }
7405
7406         if ((aip->target_objnum == -1) || (Objects[aip->target_objnum].signature != aip->target_signature)) {
7407                 abort_flag = 1;
7408         }
7409
7410         if (abort_flag || (Missiontime > aip->submode_start_time + F1_0)) {
7411                 aip->last_attack_time = Missiontime;
7412                 aip->submode = SM_ATTACK;
7413                 aip->submode_start_time = Missiontime;
7414         } else {
7415                 vector  v2e;
7416                 float           dot;
7417
7418                 vm_vec_normalized_dir(&v2e, &Objects[aip->target_objnum].pos, &objp->pos);
7419
7420                 dot = vm_vec_dot(&objp->orient.fvec, &v2e);
7421                 if (dot < 0.0f)
7422                         accelerate_ship(aip, 1.0f);
7423                 else
7424                         accelerate_ship(aip, 1.0f - dot);
7425                 turn_away_from_point(objp, &Objects[aip->target_objnum].pos, 0.0f);
7426         }
7427 }
7428
7429 //      Return bank index of favored secondary weapon.
7430 //      Return -1 if nothing favored.
7431 //      "favored" means SEXPs have specified the weapon as being good to fire at en_objp.
7432 int has_preferred_secondary(object *objp, object *en_objp, ship_weapon *swp)
7433 {
7434 // int is_preferred_weapon(int weapon_num, object *firer_objp, object *target_objp)
7435         int     i;
7436
7437         for (i=0; i<swp->num_secondary_banks; i++) {
7438                 if (swp->secondary_bank_capacity[i] > 0) {
7439                         if (swp->secondary_bank_ammo[i] > 0) {
7440                                 if (is_preferred_weapon(swp->secondary_bank_weapons[i], objp, en_objp) != -1){
7441                                         return i;
7442                                 }
7443                         }
7444                 }
7445         }
7446
7447         return -1;
7448 }
7449
7450 //      Choose which secondary weapon to fire.
7451 //      Note, this is not like ai_select_secondary_weapon().  "choose" means make a choice.
7452 //      "select" means execute an order.  Get it?
7453 //      This function calls ai_select_secondary_weapon() with the characteristics it should search for.
7454 void ai_choose_secondary_weapon(object *objp, ai_info *aip, object *en_objp)
7455 {
7456         float                   subsystem_strength = 0.0f;
7457         int                     is_big_ship, priority1, priority2;
7458         ship_weapon     *swp;
7459         ship_info       *esip;
7460
7461         if ( en_objp->type == OBJ_SHIP ) {
7462                 esip = &Ship_info[Ships[en_objp->instance].ship_info_index];
7463         } else {
7464                 esip = NULL;
7465         }
7466
7467         swp = &Ships[objp->instance].weapons;
7468
7469         // AL 3-5-98: do a quick out if the ship has no secondaries
7470         if ( swp->num_secondary_banks <= 0 ) {
7471                 swp->current_secondary_bank = -1;
7472                 return;
7473         }
7474
7475         int preferred_secondary = has_preferred_secondary(objp, en_objp, swp);
7476
7477         if (preferred_secondary != -1) {
7478                 if (swp->current_secondary_bank != preferred_secondary) {
7479                         aip->current_target_is_locked = 0;
7480                         aip->aspect_locked_time = 0.0f;
7481                         swp->current_secondary_bank = preferred_secondary;
7482                 }
7483                 //nprintf(("AI", "Favored secondary = %s\n", Weapon_info[swp->secondary_bank_weapons[swp->current_secondary_bank]].name));
7484                 aip->ai_flags |= AIF_UNLOAD_SECONDARIES;
7485         } else {
7486                 aip->ai_flags &= ~AIF_UNLOAD_SECONDARIES;
7487                 if (aip->targeted_subsys) {
7488                         subsystem_strength = aip->targeted_subsys->current_hits;
7489                 }
7490
7491                 if ( esip ) {
7492                         is_big_ship = esip->flags & (SIF_HUGE_SHIP | SIF_BIG_SHIP);
7493                 } else {
7494                         is_big_ship=0;
7495                 }
7496
7497                 if (is_big_ship) {
7498                         priority1 = WIF_HUGE;
7499                         priority2 = WIF_HOMING;
7500                 } else if ( (esip != NULL) && (esip->flags & SIF_BOMBER) ) {
7501                         priority1 = WIF_BOMBER_PLUS;
7502                         priority2 = WIF_HOMING;
7503                 } else if (subsystem_strength > 100.0f) {
7504                         priority1 = WIF_PUNCTURE;
7505                         priority2 = WIF_HOMING;
7506                 } else {
7507                         priority1 = WIF_HOMING;
7508                         priority2 = 0;
7509                 }
7510                 
7511                 ai_select_secondary_weapon(objp, swp, priority1, priority2);
7512         }
7513
7514         // nprintf(("AI", "Frame %i: Chose secondary %s\n", Framecount, Weapon_info[swp->secondary_bank_weapons[swp->current_secondary_bank]].name));
7515 }
7516
7517 //      Return time, in seconds, at which this ship can next fire its current secondary weapon.
7518 float set_secondary_fire_delay(ai_info *aip, ship *shipp, weapon_info *swip)
7519 {
7520         float t = swip->fire_wait;              //      Base delay for this weapon.
7521         if (shipp->team == Player_ship->team) {
7522                 //      On player's team, _lower_ skill level = faster firing
7523                 t = t * (Game_skill_level+2) / (NUM_SKILL_LEVELS);
7524         } else {                //      Not on player's team, higher skill level = faster firing
7525                 t = t * (NUM_SKILL_LEVELS - Game_skill_level+2) / (NUM_SKILL_LEVELS);
7526         }
7527
7528         t += (Num_ai_classes - aip->ai_class + 1) * 0.5f;
7529         t *= frand_range(0.8f, 1.2f);
7530
7531         //      For the missiles that fire fairly quickly, occasionally add an additional substantial delay.
7532         if (t < 5.0f)
7533                 if (frand() < 0.5f)
7534                         t = t * 2.0f + 2.0f;
7535
7536         return t;
7537 }
7538
7539
7540 void ai_chase_big_approach_set_goal(vector *goal_pos, object *attack_objp, object *target_objp, float *accel)
7541 {
7542         float dist_to_goal;
7543
7544         // head straight toward him and maybe circle later
7545         vm_vec_avg(goal_pos, &attack_objp->pos, &target_objp->pos);
7546
7547         // get distance to goal
7548         dist_to_goal = vm_vec_dist(goal_pos, &attack_objp->pos);
7549         
7550         // set accel
7551         if (dist_to_goal > 400.0f) {
7552                 *accel = 1.0f;
7553         } else {
7554                 *accel = dist_to_goal/400.0f;
7555         }
7556 }
7557
7558 void ai_chase_big_circle_set_goal(vector *goal_pos, object *attack_objp, object *target_objp, float *accel)
7559 {
7560         get_tangent_point(goal_pos, attack_objp, &target_objp->pos, attack_objp->radius + target_objp->radius + 100.0f);
7561
7562         *accel = 1.0f;
7563 }
7564
7565 // get the current and desired horizontal separations between target
7566 void ai_chase_big_get_separations(object *attack_objp, object *target_objp, vector *horz_vec_to_target, float *desired_separation, float *cur_separation)
7567 {
7568         float temp, r_target, r_attacker, h_attacker, h_target;
7569         float perp_dist;
7570         vector vec_to_target;
7571         polymodel *pm;
7572
7573         // get parameters of ships (as cylinders - radius and height)
7574         // get radius of attacker (for rotations about forward)
7575         pm = model_get(Ships[attack_objp->instance].modelnum);
7576         temp = max(pm->maxs.x, pm->maxs.y);
7577         r_attacker = max(-pm->mins.x, -pm->mins.y);
7578         r_attacker = max(temp, r_attacker);
7579         h_attacker = max(-pm->mins.z, pm->maxs.z);
7580
7581         // get radius of target (for rotations about forward)
7582         pm = model_get(Ships[attack_objp->instance].modelnum);
7583         temp = max(pm->maxs.x, pm->maxs.y);
7584         r_target = max(-pm->mins.x, -pm->mins.y);
7585         r_target = max(temp, r_target);
7586         h_target = max(-pm->mins.z, pm->maxs.z);
7587
7588         // find separation between cylinders [if parallel]
7589         vm_vec_sub(&vec_to_target, &attack_objp->pos, &target_objp->pos);
7590
7591         // find the distance between centers along forward direction of ships
7592         perp_dist = vm_vec_dotprod(&vec_to_target, &target_objp->orient.fvec);
7593
7594         // subtract off perp component to get "horizontal" separation vector between cylinders [ASSUMING parallel]
7595         vm_vec_scale_add(horz_vec_to_target, &vec_to_target, &target_objp->orient.fvec, -perp_dist);
7596         *cur_separation = vm_vec_mag_quick(horz_vec_to_target);
7597
7598         // choose "optimal" separation of 1000 + r_target + r_attacker
7599         *desired_separation = 1000 + r_target + r_attacker;
7600 }
7601
7602 void ai_chase_big_parallel_set_goal(vector *goal_pos, object *attack_objp, object *target_objp, float *accel)
7603 {
7604         int opposing;
7605         float temp, r_target, r_attacker, h_attacker, h_target;
7606         float separation, optimal_separation;
7607         vector  horz_vec_to_target;
7608         polymodel *pm;
7609
7610         // get parameters of ships (as cylinders - radius and height)
7611         // get radius of attacker (for rotations about forward)
7612         pm = model_get(Ships[attack_objp->instance].modelnum);
7613         temp = max(pm->maxs.x, pm->maxs.y);
7614         r_attacker = max(-pm->mins.x, -pm->mins.y);
7615         r_attacker = max(temp, r_attacker);
7616         h_attacker = max(-pm->mins.z, pm->maxs.z);
7617
7618         // get radius of target (for rotations about forward)
7619         pm = model_get(Ships[attack_objp->instance].modelnum);
7620         temp = max(pm->maxs.x, pm->maxs.y);
7621         r_target = max(-pm->mins.x, -pm->mins.y);
7622         r_target = max(temp, r_target);
7623         h_target = max(-pm->mins.z, pm->maxs.z);
7624
7625         // are we opposing (only when other ship is not moving)
7626         opposing = ( vm_vec_dotprod(&attack_objp->orient.fvec, &target_objp->orient.fvec) < 0 );
7627
7628         ai_chase_big_get_separations(attack_objp, target_objp, &horz_vec_to_target, &optimal_separation, &separation);
7629
7630         // choose dist (2000) so that we don't bash
7631         float dist = 2000;
7632         if (opposing) {
7633                 dist = - dist;
7634         }
7635
7636         // set the goal pos as dist forward from target along target forward
7637         vm_vec_scale_add(goal_pos, &target_objp->pos, &target_objp->orient.fvec, dist);
7638         // then add horizontal separation
7639         vm_vec_scale_add2(goal_pos, &horz_vec_to_target, optimal_separation/separation);
7640
7641         // find the distance between centers along forward direction of ships
7642         vector vec_to_target;
7643         vm_vec_sub(&vec_to_target, &target_objp->pos, &attack_objp->pos);
7644         float perp_dist = vm_vec_dotprod(&vec_to_target, &target_objp->orient.fvec);
7645
7646         float match_accel = target_objp->phys_info.vel.z / Ship_info[Ships[attack_objp->instance].ship_info_index].max_vel.z;
7647         float length_scale = attack_objp->radius;
7648
7649         // if we're heading toward enemy ship, we want to keep going if we're ahead
7650         if (opposing) {
7651                 perp_dist = -perp_dist;
7652         }
7653
7654         if (perp_dist > 0) {
7655                 // falling behind, so speed up
7656                 *accel = match_accel + (1.0f - match_accel) / length_scale * (perp_dist);
7657         } else {
7658                 // up in front, so slow down
7659                 *accel = match_accel  - match_accel / length_scale * -perp_dist;
7660                 *accel = max(0.0f, *accel);
7661         }
7662
7663 }
7664
7665
7666 //      Return *goal_pos for one cruiser to attack another (big ship).
7667 //      Choose point fairly nearby that is not occupied by another cruiser.
7668 void ai_cruiser_chase_set_goal_pos(vector *goal_pos, object *pl_objp, object *en_objp)
7669 {
7670         ai_info *aip;
7671
7672         aip = &Ai_info[Ships[pl_objp->instance].ai_index];
7673         float accel;
7674
7675         switch (aip->submode) {
7676         case SM_BIG_APPROACH:
7677                 // do approach stuff;
7678                 ai_chase_big_approach_set_goal(goal_pos, pl_objp, en_objp, &accel);
7679                 break;
7680
7681         case SM_BIG_CIRCLE:
7682                 // do circle stuff
7683                 ai_chase_big_circle_set_goal(goal_pos, pl_objp, en_objp, &accel);
7684                 break;
7685
7686         case SM_BIG_PARALLEL:
7687                 // do parallel stuff
7688                 ai_chase_big_parallel_set_goal(goal_pos, pl_objp, en_objp, &accel);
7689                 break;
7690         }
7691 }
7692
7693 int maybe_hack_cruiser_chase_abort()
7694 {
7695         ship                    *shipp = &Ships[Pl_objp->instance];     
7696         ship                    *eshipp = &Ships[En_objp->instance];
7697         ai_info         *aip = &Ai_info[shipp->ai_index];
7698
7699         // mission sm3-08, sathanos chasing collosus
7700         if ( stricmp(Mission_filename, "sm3-08.fs2") == 0 ) {
7701                 if (( stricmp(eshipp->ship_name, "colossus") == 0 ) || ( stricmp(shipp->ship_name, "colossus") == 0 )) {
7702                         // Changed so all big ships attacking the Colossus will not do the chase code.
7703                         // Did this so Beast wouldn't swerve away from Colossus. -- MK, 9/14/99
7704                         //if ( stricmp(shipp->ship_name, "Sathanas") == 0 ) {
7705                                 // do cool hack stuff here
7706                                 ai_clear_ship_goals( aip );
7707                                 aip->mode = AIM_NONE;
7708                                 return 1;
7709                         //}
7710                 }
7711         }
7712
7713         return 0;
7714 }
7715
7716 //      Make a big ship pursue another big ship.
7717 //      (Note, called "ai_cruiser_chase" because we already have ai_chase_big() which means fighter chases big ship.
7718 void ai_cruiser_chase()
7719 {
7720         ship_info       *sip = &Ship_info[Ships[Pl_objp->instance].ship_info_index];
7721         ship                    *shipp = &Ships[Pl_objp->instance];     
7722         ai_info         *aip = &Ai_info[shipp->ai_index];
7723
7724         if (!(sip->flags & (SIF_BIG_SHIP | SIF_HUGE_SHIP))) {
7725                 Int3(); //      Hmm, not a very big ship, how did we get in this function?
7726                 aip->mode = AIM_NONE;
7727                 return;
7728         }
7729
7730         if (En_objp->type != OBJ_SHIP) {
7731                 Int3();
7732                 return;
7733         }
7734
7735         if (En_objp->instance < 0) {
7736                 Int3();
7737                 return;
7738         }
7739
7740         ship                    *eshipp;
7741         ship_info       *esip;
7742
7743         eshipp = &Ships[En_objp->instance];
7744         esip = &Ship_info[eshipp->ship_info_index];
7745
7746         if (!(esip->flags & (SIF_BIG_SHIP | SIF_HUGE_SHIP))) {
7747                 // Int3();      //      Hmm, we're big and we're pursuing something other than a big ship?
7748                 aip->mode = AIM_NONE;
7749                 return;
7750         }
7751
7752         vector  goal_pos;
7753         float turn_time = Ship_info[Ships[Pl_objp->instance].ship_info_index].srotation_time;
7754
7755         // kamikaze - ram and explode
7756         if (aip->ai_flags & AIF_KAMIKAZE) {
7757                 ai_turn_towards_vector(&En_objp->pos, Pl_objp, flFrametime, turn_time, NULL, NULL, 0.0f, 0);
7758                 accelerate_ship(aip, 1.0f);
7759         } 
7760         
7761         // really track down and chase
7762         else {
7763                 // check valid submode
7764                 Assert( (aip->submode == SM_ATTACK) || (aip->submode == SM_BIG_APPROACH) || (aip->submode == SM_BIG_CIRCLE) || (aip->submode == SM_BIG_PARALLEL) );
7765
7766                 // just entering, approach enemy ship
7767                 if (aip->submode == SM_ATTACK) {
7768                         aip->submode = SM_BIG_APPROACH;
7769                 }
7770
7771                 // desired accel
7772                 float accel = 0.0f;
7773                 vector *rvecp = NULL;
7774
7775                 switch (aip->submode) {
7776                 case SM_BIG_APPROACH:
7777                         // do approach stuff;
7778                         ai_chase_big_approach_set_goal(&goal_pos, Pl_objp, En_objp, &accel);
7779                         // maybe set rvec
7780                         break;
7781
7782                 case SM_BIG_CIRCLE:
7783                         // do circle stuff
7784                         ai_chase_big_circle_set_goal(&goal_pos, Pl_objp, En_objp, &accel);
7785                         // maybe set rvec
7786                         break;
7787
7788                 case SM_BIG_PARALLEL:
7789                         // do parallel stuff
7790                         ai_chase_big_parallel_set_goal(&goal_pos, Pl_objp, En_objp, &accel);
7791                         //maybe set rvec
7792                         break;
7793                 }
7794
7795
7796                 // now move as desired
7797                 ai_turn_towards_vector(&goal_pos, Pl_objp, flFrametime, turn_time, NULL, NULL, 0.0f, 0, rvecp);
7798                 accelerate_ship(aip, accel);
7799
7800
7801                 // maybe switch to new mode
7802                 vector vec_to_enemy;
7803                 float dist_to_enemy;
7804                 int moving = (En_objp->phys_info.vel.z > 0.5f);
7805                 vm_vec_sub(&vec_to_enemy, &En_objp->pos, &Pl_objp->pos);
7806                 dist_to_enemy = vm_vec_mag_quick(&vec_to_enemy);
7807
7808                 switch (aip->submode) {
7809                 case SM_BIG_APPROACH:
7810                         if ( dist_to_enemy < (Pl_objp->radius + En_objp->radius)*1.25f + 200.0f ) {
7811                                 // moving
7812                                 if (moving) {
7813                                         // if within 90 degrees of en forward, go into parallel, otherwise circle
7814                                         if ( vm_vec_dotprod(&En_objp->orient.fvec, &Pl_objp->orient.fvec) > 0 ) {
7815                                                 aip->submode = SM_BIG_PARALLEL;
7816                                         }
7817                                 }
7818
7819                                 // otherwise cirle
7820                                 if ( !maybe_hack_cruiser_chase_abort() ) {
7821                                         aip->submode = SM_BIG_CIRCLE;
7822                                 }
7823                         }
7824                         break;
7825
7826                 case SM_BIG_CIRCLE:
7827                         // moving
7828                         if (moving) {
7829                                 vector temp;
7830                                 float desired_sep, cur_sep;
7831                                 // we're behind the enemy ship
7832                                 if (vm_vec_dotprod(&vec_to_enemy, &En_objp->orient.fvec) > 0) {
7833                                         // and we're turning toward the enemy
7834                                         if (vm_vec_dotprod(&En_objp->orient.fvec, &Pl_objp->orient.fvec) > 0) {
7835                                                 // get separation
7836                                                 ai_chase_big_get_separations(Pl_objp, En_objp, &temp, &desired_sep, &cur_sep);
7837                                                 // and the separation is > 0.9 desired
7838                                                 if (cur_sep > 0.9 * desired_sep) {
7839                                                         aip->submode = SM_BIG_PARALLEL;
7840                                                 }
7841                                         }
7842                                 }
7843                         } else {
7844                                 // still
7845                                 vector temp;
7846                                 float desired_sep, cur_sep;
7847                                 // we're behind the enemy ship
7848                                 if (vm_vec_dotprod(&vec_to_enemy, &En_objp->orient.fvec) > 0) {
7849                                         // and we're turning toward the enemy
7850                                         if (vm_vec_dotprod(&En_objp->orient.fvec, &Pl_objp->orient.fvec) > 0) {
7851                                                 // get separation
7852                                                 ai_chase_big_get_separations(Pl_objp, En_objp, &temp, &desired_sep, &cur_sep);
7853                                                 //and the separation is [0.9 to 1.1] desired
7854                                                 if ( (cur_sep > 0.9f * desired_sep) ) {
7855                                                         aip->submode = SM_BIG_PARALLEL;
7856                                                 }
7857                                         }
7858                                 }
7859                                 // in front of ship
7860                                 else {
7861                                         // and we're turning toward the enemy
7862                                         if (vm_vec_dotprod(&En_objp->orient.fvec, &Pl_objp->orient.fvec) < 0) {
7863                                                 // get separation
7864                                                 ai_chase_big_get_separations(Pl_objp, En_objp, &temp, &desired_sep, &cur_sep);
7865                                                 //and the separation is [0.9 to 1.1] desired
7866                                                 if ( (cur_sep > 0.9f * desired_sep) ) {
7867                                                         aip->submode = SM_BIG_PARALLEL;
7868                                                 }
7869                                         }
7870                                 }
7871                         }
7872                         break;
7873
7874                 case SM_BIG_PARALLEL:
7875                         // we're opposing
7876                         if ( vm_vec_dotprod(&Pl_objp->orient.fvec, &En_objp->orient.fvec) < 0 ) {
7877                                 // and the other ship is moving
7878                                 if (moving) {
7879                                         // and we no longer overlap
7880                                         if ( dist_to_enemy > (0.75 * (En_objp->radius + Pl_objp->radius)) ) {
7881                                                 aip->submode = SM_BIG_APPROACH;
7882                                         }
7883                                 }
7884                         }
7885                         break;
7886                 }
7887         }
7888 }
7889
7890 // --------------------------------------------------------------------------
7891 // Make object Pl_objp chase object En_objp
7892 void ai_chase()
7893 {
7894         float                   dist_to_enemy, time_to_enemy;
7895         float                   dot_to_enemy, dot_from_enemy, real_dot_to_enemy;
7896         vector          player_pos, enemy_pos, predicted_enemy_pos, real_vec_to_enemy, predicted_vec_to_enemy;
7897         ship_info       *sip = &Ship_info[Ships[Pl_objp->instance].ship_info_index];
7898         ship                    *shipp = &Ships[Pl_objp->instance];
7899         ship_weapon     *swp = &shipp->weapons;
7900         ai_info         *aip = &Ai_info[shipp->ai_index];
7901         int                     enemy_sip_flags;
7902
7903         if (aip->mode != AIM_CHASE) {
7904                 Int3();
7905         }
7906
7907         if (sip->flags & (SIF_BIG_SHIP | SIF_HUGE_SHIP)) {
7908                 ai_cruiser_chase();
7909                 return;
7910         }
7911
7912         if (!(sip->flags & (SIF_FIGHTER | SIF_BOMBER | SIF_ESCAPEPOD))) {
7913                 Warning(LOCATION, "Ship %s is not 'small', but is in chase mode.\nSwitching to AI=none.\n", shipp->ship_name);
7914                 aip->mode = AIM_NONE;
7915                 return;
7916         }
7917
7918         //nprintf(("AI", "%7s ", Submode_text[aip->submode]));
7919
7920         if ( En_objp->type == OBJ_SHIP ) {
7921                 enemy_sip_flags = Ship_info[Ships[En_objp->instance].ship_info_index].flags;
7922         } else {
7923                 enemy_sip_flags = 0;
7924         }
7925
7926         if ( enemy_sip_flags > 0 ) {
7927                 if (enemy_sip_flags & (SIF_BIG_SHIP | SIF_HUGE_SHIP)) {
7928                         ai_big_chase();
7929                         return;
7930                 }
7931         }
7932
7933         //      If collided with target_objnum last frame, avoid that ship.
7934         //      This should prevent the embarrassing behavior of ships getting stuck on each other
7935         //      as if they were magnetically attracted. -- MK, 11/13/97.
7936         if ((aip->ai_flags & AIF_TARGET_COLLISION) || (aip->submode == SM_FLY_AWAY)) {
7937                 ai_chase_fly_away(Pl_objp, aip);
7938                 return;
7939         }
7940
7941         ai_set_positions(Pl_objp, En_objp, aip, &player_pos, &enemy_pos);
7942         dist_to_enemy = vm_vec_dist_quick(&player_pos, &enemy_pos);
7943         time_to_enemy = compute_time_to_enemy(dist_to_enemy, Pl_objp, En_objp);
7944         vm_vec_sub(&real_vec_to_enemy, &enemy_pos, &player_pos);
7945
7946         vm_vec_normalize(&real_vec_to_enemy);
7947
7948         real_dot_to_enemy = vm_vec_dot(&real_vec_to_enemy, &Pl_objp->orient.fvec);
7949
7950         int is_stealthy_ship = 0;
7951         if ( (enemy_sip_flags > 0) && (enemy_sip_flags & SIF_STEALTH) ) {
7952                 if ( ai_is_stealth_visible(Pl_objp, En_objp) != STEALTH_FULLY_TARGETABLE ) {
7953                         is_stealthy_ship = 1;
7954                 }
7955         }
7956
7957         // Can only acquire lock on a target that isn't hidden from sensors
7958         if ( !(Ships[En_objp->instance].flags & SF_HIDDEN_FROM_SENSORS) && !is_stealthy_ship ) {
7959                 update_aspect_lock_information(aip, &real_vec_to_enemy, dist_to_enemy, En_objp->radius);
7960         } else {
7961                 aip->current_target_is_locked = 0;
7962                 aip->ai_flags &= ~AIF_SEEK_LOCK;
7963         }
7964
7965         //      If seeking lock, try to point directly at ship, else predict position so lasers can hit it.
7966         //      If just acquired target, or target is not in reasonable cone, don't refine believed enemy position.
7967         if ((real_dot_to_enemy < 0.25f) || (aip->target_time < 1.0f) || (aip->ai_flags & AIF_SEEK_LOCK)) {
7968                 predicted_enemy_pos = enemy_pos;
7969         } else {
7970                 //      Set predicted_enemy_pos.
7971                 //      See if attacking a subsystem.
7972                 if (aip->targeted_subsys != NULL) {
7973                         Assert(En_objp->type == OBJ_SHIP);
7974                         ship_info       *esip = &Ship_info[Ships[En_objp->instance].ship_info_index];
7975                         if (get_shield_strength(En_objp)/esip->shields < HULL_DAMAGE_THRESHOLD_PERCENT) {
7976                                 //int   rval;
7977
7978                                 if (aip->targeted_subsys != NULL) {
7979                                         get_subsystem_pos(&enemy_pos, En_objp, aip->targeted_subsys);
7980                                         predicted_enemy_pos = enemy_pos;
7981                                         predicted_vec_to_enemy = real_vec_to_enemy;
7982                                 } else {
7983                                         set_predicted_enemy_pos(&predicted_enemy_pos, Pl_objp, En_objp, aip);
7984                                         set_target_objnum(aip, -1);
7985                                 }
7986                                 // 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));
7987
7988                         } else {
7989                                 set_predicted_enemy_pos(&predicted_enemy_pos, Pl_objp, En_objp, aip);
7990                                 // nprintf(("AI", "Attacking subsystem: pos = %7.3f %7.3f %7.3f\n", 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                 }
7995         }
7996
7997         vm_vec_sub(&predicted_vec_to_enemy, &predicted_enemy_pos, &player_pos);
7998
7999         vm_vec_normalize(&predicted_vec_to_enemy);
8000
8001         dot_to_enemy = vm_vec_dot(&Pl_objp->orient.fvec, &predicted_vec_to_enemy);
8002         dot_from_enemy= - vm_vec_dot(&En_objp->orient.fvec, &real_vec_to_enemy);
8003
8004         //
8005         //      Set turn and acceleration based on submode.
8006         //
8007         switch (aip->submode) {
8008         case SM_CONTINUOUS_TURN:
8009                 ai_chase_ct();
8010                 break;
8011
8012         case SM_STEALTH_FIND:
8013                 ai_stealth_find();
8014                 break;
8015
8016         case SM_STEALTH_SWEEP:
8017                 ai_stealth_sweep();
8018                 break;
8019
8020         case SM_ATTACK:
8021         case SM_SUPER_ATTACK:
8022         case SM_ATTACK_FOREVER:
8023                 if (vm_vec_dist_quick(&Pl_objp->pos, &predicted_enemy_pos) > 100.0f + En_objp->radius * 2.0f) {
8024                         if (maybe_avoid_big_ship(Pl_objp, En_objp, aip, &predicted_enemy_pos, 10.0f))
8025                                 return;
8026                 }
8027
8028                 ai_chase_attack(aip, sip, &predicted_enemy_pos, dist_to_enemy);
8029                 break;
8030
8031         case SM_EVADE_SQUIGGLE:
8032                 ai_chase_es(aip, sip);
8033                 break;
8034
8035         case SM_EVADE_BRAKE:
8036                 ai_chase_eb(aip, sip, &predicted_enemy_pos, dist_to_enemy);
8037                 break;
8038
8039         case SM_EVADE:
8040                 evade_ship();
8041                 break;
8042
8043         case SM_AVOID:
8044                 avoid_ship();
8045                 break;
8046
8047         case SM_GET_BEHIND:
8048                 get_behind_ship(aip, sip, dist_to_enemy);
8049                 break;
8050
8051         case SM_GET_AWAY:               //      Used to get away from opponent to prevent endless circling.
8052                 ai_chase_ga(aip, sip);
8053                 break;
8054
8055         case SM_EVADE_WEAPON:
8056                 evade_weapon();
8057                 break;
8058
8059         default:
8060                 // Int3();
8061                 aip->last_attack_time = Missiontime;
8062                 aip->submode = SM_ATTACK;
8063                 aip->submode_start_time = Missiontime;
8064         }
8065
8066         //
8067         //      Maybe choose a new submode.
8068         //
8069         if ( (aip->submode != SM_AVOID) && (aip->submode != SM_ATTACK_FOREVER) ) {
8070                 //      If a very long time since attacked, attack no matter what!
8071                 if ( (aip->submode != SM_SUPER_ATTACK) && (aip->submode != SM_GET_AWAY) && !(aip->ai_flags & AIF_STEALTH_PURSIUT) ) {
8072                         if (Missiontime - aip->last_attack_time > i2f(6)) {
8073                                 aip->submode = SM_SUPER_ATTACK;
8074                                 aip->submode_start_time = Missiontime;
8075                                 aip->last_attack_time = Missiontime;
8076                         }
8077                 }
8078
8079                 //      If a collision is expected, pull out!
8080                 //      If enemy is pointing away and moving a bit, don't worry about collision detection.
8081                 if ((dot_from_enemy > 0.5f) || (En_objp->phys_info.speed < 10.0f)) {
8082                         if (might_collide_with_ship(Pl_objp, En_objp, dot_to_enemy, dist_to_enemy, 4.0f)) {
8083                                 if ((Missiontime - aip->last_hit_time > F1_0*4) && (dist_to_enemy < Pl_objp->radius*2 + En_objp->radius*2)) {
8084                                         accelerate_ship(aip, -1.0f);
8085                                 } else {
8086                                         aip->submode = SM_AVOID;
8087                                         aip->submode_start_time = Missiontime;
8088                                 }
8089                         }
8090                 }
8091         }
8092
8093         switch (aip->submode) {
8094         case SM_CONTINUOUS_TURN:
8095                 if (Missiontime - aip->submode_start_time > i2f(3)) {
8096                         aip->last_attack_time = Missiontime;
8097                         aip->submode = SM_ATTACK;
8098                         aip->submode_start_time = Missiontime;
8099                 }
8100                 break;
8101
8102         case SM_ATTACK:
8103                 // if taraget is stealth and stealth not visible, then enter stealth find mode
8104                 if ( (aip->ai_flags & AIF_STEALTH_PURSIUT) && (ai_is_stealth_visible(Pl_objp, En_objp) == STEALTH_INVISIBLE) ) {
8105                         aip->submode = SM_STEALTH_FIND;
8106                         aip->submode_start_time = Missiontime;
8107                         aip->submode_parm0 = SM_SF_AHEAD;
8108                 } 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)) {
8109                         aip->submode = SM_SUPER_ATTACK;
8110                         aip->submode_start_time = Missiontime;
8111                         aip->last_attack_time = Missiontime;
8112                 } else if ((Missiontime - aip->last_hit_target_time > i2f(6)) &&
8113                         (dist_to_enemy < 500.0f) && (dot_to_enemy < 0.2f) &&
8114                         (frand() < (float) Game_skill_level/NUM_SKILL_LEVELS)) {
8115                         aip->submode = SM_GET_AWAY;
8116                         aip->submode_start_time = Missiontime;
8117                         aip->last_hit_target_time = Missiontime;
8118                 } else if ((enemy_sip_flags & SIF_SMALL_SHIP)
8119                         && (dot_to_enemy < dot_from_enemy)
8120                         && (En_objp->phys_info.speed > 15.0f) 
8121                         && (dist_to_enemy < 200.0f) 
8122                         && (dist_to_enemy > 50.0f)
8123                         && (dot_to_enemy < 0.1f)
8124                         && (Missiontime - aip->submode_start_time > i2f(2))) {
8125                         aip->submode = SM_EVADE_BRAKE;
8126                         aip->submode_start_time = Missiontime;
8127                 } else if ((dot_to_enemy > 0.2f) && (dot_from_enemy > -0.2f) && (dot_from_enemy < 0.1f)) {
8128                         aip->submode = SM_GET_BEHIND;
8129                         aip->submode_start_time = Missiontime;
8130                 } 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)) {
8131                         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;
8132                                 aip->submode_start_time = Missiontime;
8133                                 aip->last_hit_target_time = Missiontime;
8134                         } else {
8135                                 aip->submode = SM_EVADE_SQUIGGLE;
8136                                 aip->submode_start_time = Missiontime;
8137                         }
8138                 } else if ((enemy_sip_flags & SIF_SMALL_SHIP) && (Missiontime - aip->submode_start_time > F1_0*2)) {
8139                         if ((dot_to_enemy < 0.8f) && (dot_from_enemy > dot_to_enemy)) {
8140                                 if (frand() > 0.5f) {
8141                                         aip->submode = SM_CONTINUOUS_TURN;
8142                                         aip->submode_parm0 = myrand() & 0x0f;
8143                                         aip->submode_start_time = Missiontime;
8144                                 } else {
8145                                         aip->submode = SM_EVADE;
8146                                         aip->submode_start_time = Missiontime;
8147                                 }
8148                         } else {
8149                                 aip->submode_start_time = Missiontime;
8150                         }
8151                 }
8152
8153                 aip->last_attack_time = Missiontime;
8154
8155                 break;
8156                 
8157         case SM_EVADE_SQUIGGLE:
8158                 if ((Missiontime - aip->submode_start_time > i2f(5)) || (dist_to_enemy > 300.0f)) {
8159                         if ((dist_to_enemy < 100.0f) && (dot_to_enemy < 0.0f) && (dot_from_enemy > 0.5f)) {
8160                                 aip->submode = SM_EVADE_BRAKE;
8161                                 aip->submode_start_time = Missiontime;
8162                         } else {
8163                                 aip->last_attack_time = Missiontime;
8164                                 aip->submode = SM_ATTACK;
8165                                 aip->submode_start_time = Missiontime;
8166                         }
8167                 }
8168                 break;
8169         
8170         case SM_EVADE_BRAKE:
8171                 if ((dist_to_enemy < 15.0f) || (En_objp->phys_info.speed < 10.0f)) {
8172                         aip->submode = SM_AVOID;
8173                         aip->submode_start_time = Missiontime;
8174                 } else if ((dot_to_enemy > 0.9f) || ((dot_from_enemy > 0.9f) && (Missiontime - aip->submode_start_time > i2f(1)))) {
8175                         aip->last_attack_time = Missiontime;
8176                         aip->submode = SM_ATTACK;
8177                         aip->submode_start_time = Missiontime;
8178                 } else if (Missiontime - aip->submode_start_time > i2f(4)) {
8179                         aip->last_attack_time = Missiontime;
8180                         aip->submode = SM_ATTACK;
8181                         aip->submode_start_time = Missiontime;
8182                 }
8183                 break;
8184
8185         case SM_EVADE:
8186                 //      Modified by MK on 5/5/97 to keep trying to regain attack mode.  It's what a human would do.
8187                 if ((dot_to_enemy < 0.2f) && (dot_from_enemy < 0.8f) && (dist_to_enemy < 100.0f) && (En_objp->phys_info.speed > 15.0f)) {
8188                         aip->last_attack_time = Missiontime;
8189                         aip->submode = SM_EVADE_BRAKE;
8190                         aip->submode_start_time = Missiontime;
8191                 } else if (((dot_to_enemy > dot_from_enemy - 0.1f)
8192                         && (Missiontime > aip->submode_start_time + i2f(1)))
8193                         || (dist_to_enemy > 150.0f + 2*(Pl_objp->radius + En_objp->radius))) {
8194                         aip->last_attack_time = Missiontime;
8195                         aip->submode = SM_ATTACK;
8196                         aip->submode_start_time = Missiontime;
8197                 } else if (Missiontime - aip->submode_start_time > i2f(2))
8198                         if (dot_from_enemy > 0.8f) {
8199                                 aip->submode = SM_EVADE_SQUIGGLE;
8200                                 aip->submode_start_time = Missiontime;
8201                         }
8202
8203                 break;
8204
8205         case SM_SUPER_ATTACK:
8206                 // if stealth and invisible, enter stealth find mode
8207                 if ( (aip->ai_flags & AIF_STEALTH_PURSIUT) && (ai_is_stealth_visible(Pl_objp, En_objp) == STEALTH_INVISIBLE) ) {
8208                         aip->submode = SM_STEALTH_FIND;
8209                         aip->submode_start_time = Missiontime;
8210                         aip->submode_parm0 = SM_SF_AHEAD;
8211                 } 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) )) {
8212                         aip->ai_flags &= ~AIF_ATTACK_SLOWLY;    //      Just in case, clear here.
8213
8214                         switch (myrand() % 5) {
8215                         case 0:
8216                                 aip->submode = SM_CONTINUOUS_TURN;
8217                                 aip->submode_start_time = Missiontime;
8218                                 break;
8219                         case 1:
8220                                 aip->submode_start_time = Missiontime;  //      Stay in super attack mode
8221                                 break;
8222                         case 2:
8223                         case 3:
8224                                 if (frand() < (float) 0.5f * (aip->ai_class + Game_skill_level)/(Num_ai_classes + NUM_SKILL_LEVELS)) {
8225                                         aip->submode = SM_GET_AWAY;
8226                                         aip->submode_start_time = Missiontime;
8227                                 } else {
8228                                         aip->submode = SM_EVADE;
8229                                         aip->submode_start_time = Missiontime;
8230                                 }
8231                                 break;
8232                         case 4:
8233                                 if (dot_from_enemy + (NUM_SKILL_LEVELS - Game_skill_level) * 0.1f > dot_to_enemy) {     //      Less likely to GET_AWAY at lower skill levels.
8234                                         aip->submode = SM_EVADE;
8235                                         aip->submode_start_time = Missiontime;
8236                                 } else {
8237                                         aip->submode = SM_GET_AWAY;
8238                                         aip->submode_start_time = Missiontime;
8239                                 }
8240                                 break;
8241                         default:
8242                                 Int3(); //      Impossible!
8243                         }
8244                 }
8245
8246                 aip->last_attack_time = Missiontime;
8247
8248                 break;
8249
8250         case SM_AVOID:
8251                 if ((dot_to_enemy > -0.2f) && (dist_to_enemy / (dot_to_enemy + 0.3f) < 100.0f)) {
8252                         aip->submode_start_time = Missiontime;
8253                 } else if (Missiontime - aip->submode_start_time > i2f(1)/2)
8254                         if (might_collide_with_ship(Pl_objp, En_objp, dot_to_enemy, dist_to_enemy, 3.0f)) {
8255                                 aip->submode_start_time = Missiontime;
8256                         } else {
8257                                 aip->submode = SM_GET_BEHIND;
8258                                 aip->submode_start_time = Missiontime;
8259                         }
8260
8261                 break;
8262
8263         case SM_GET_BEHIND:
8264                 if ((dot_from_enemy < -0.7f) || (Missiontime - aip->submode_start_time > i2f(2))) {
8265                         aip->submode = SM_ATTACK;
8266                         aip->submode_start_time = Missiontime;
8267                         aip->last_attack_time = Missiontime;
8268                 }
8269                 break;
8270
8271         case SM_GET_AWAY:
8272                 if (Missiontime - aip->submode_start_time > i2f(2)) {
8273                         float   rand_dist;
8274
8275                         rand_dist = ((Missiontime >> 17) & 0x03) * 100.0f + 200.0f;     //      Some value in 200..500
8276                         if ((Missiontime - aip->submode_start_time > i2f(5)) || (dist_to_enemy > rand_dist) || (dot_from_enemy < 0.4f)) {
8277                                 aip->ai_flags |= AIF_ATTACK_SLOWLY;
8278                                 aip->submode = SM_ATTACK;
8279                                 aip->time_enemy_in_range = 2.0f;                //      Cheat.  Presumably if they were running away from you, they were monitoring you!
8280                                 aip->submode_start_time = Missiontime;
8281                                 aip->last_attack_time = Missiontime;
8282                         }
8283                 }
8284                 break;
8285
8286         case SM_EVADE_WEAPON:
8287                 if (aip->danger_weapon_objnum == -1) {
8288                         aip->submode = SM_ATTACK;
8289                         aip->submode_start_time = Missiontime;
8290                         aip->last_attack_time = Missiontime;
8291                 }
8292                 break;
8293
8294         // Either change to SM_ATTACK or AIM_FIND_STEALTH
8295         case SM_STEALTH_FIND:
8296                 // if time > 5 sec change mode to sweep
8297                 if ( !(aip->ai_flags & AIF_STEALTH_PURSIUT) || (ai_is_stealth_visible(Pl_objp, En_objp) == STEALTH_VISIBLE) ) {
8298                         aip->submode = SM_ATTACK;
8299                         aip->submode_start_time = Missiontime;
8300                         aip->last_attack_time = Missiontime;
8301                         // sweep if I can't find in 5 sec or bail from find
8302                 } else if ( ((Missiontime - aip->submode_start_time) > i2f(5)) || (aip->submode_parm0 == SM_SF_BAIL) ) {
8303                         // begin sweep mode
8304                         aip->submode = SM_STEALTH_SWEEP;
8305                         aip->submode_start_time = Missiontime;
8306                         aip->last_attack_time = Missiontime;
8307                         aip->submode_parm0 = SM_SS_SET_GOAL;
8308                 }
8309                 break;
8310
8311         case SM_STEALTH_SWEEP:
8312                 if ( !(aip->ai_flags & AIF_STEALTH_PURSIUT) || (ai_is_stealth_visible(Pl_objp, En_objp) == STEALTH_VISIBLE) ) {
8313                         aip->submode = SM_ATTACK;
8314                         aip->submode_start_time = Missiontime;
8315                         aip->last_attack_time = Missiontime;
8316                 } else if ( (timestamp() - aip->stealth_last_visible_stamp) < 5000 ) {
8317                         // go back to find mode
8318                         aip->submode = SM_STEALTH_FIND;
8319                         aip->submode_start_time = Missiontime;
8320                         aip->submode_parm0 = SM_SF_AHEAD;
8321                 } else if ( /*(Missiontime - aip->submode_start_time) > i2f(30) || */(aip->submode_parm0 == SM_SS_DONE) ) {
8322                         // set target objnum = -1
8323                         set_target_objnum(aip, -1);
8324
8325                         // set submode to attack
8326                         aip->submode = SM_ATTACK;
8327                         aip->submode_start_time = Missiontime;
8328                         aip->last_attack_time = Missiontime;
8329                 }
8330                 break;
8331
8332         case SM_ATTACK_FOREVER: //      Engines blown, just attack.
8333                 break;
8334
8335         default:
8336                 //Int3();
8337                 aip->submode = SM_ATTACK;
8338                 aip->last_attack_time = Missiontime;
8339
8340                 aip->submode_start_time = Missiontime;
8341         }
8342
8343         //
8344         //      Maybe fire primary weapon and update time_enemy_in_range
8345         //
8346         //nprintf(("AI", "time_enemy_in_range = %7.3f, dot = %7.3f\n", aip->time_enemy_in_range, dot_to_enemy));
8347
8348         if (aip->mode != AIM_EVADE) {
8349                 if (dot_to_enemy > 0.95f - 0.5f * En_objp->radius/max(1.0f, En_objp->radius + dist_to_enemy)) {
8350                         aip->time_enemy_in_range += flFrametime;
8351                         
8352                         //      Chance of hitting ship is based on dot product of firing ship's forward vector with vector to ship
8353                         //      and also the size of the target relative to distance to target.
8354                         if (dot_to_enemy > max(0.5f, 0.90f + aip->ai_accuracy/10.0f - En_objp->radius/max(1.0f,dist_to_enemy))) {
8355
8356                                 ship *temp_shipp;
8357                                 ship_weapon *tswp;
8358
8359                                 temp_shipp = &Ships[Pl_objp->instance];
8360                                 tswp = &temp_shipp->weapons;
8361                                 if ( tswp->num_primary_banks > 0 ) {
8362                                         float   scale;
8363                                         Assert(tswp->current_primary_bank < tswp->num_primary_banks);
8364                                         weapon_info     *pwip = &Weapon_info[tswp->primary_bank_weapons[tswp->current_primary_bank]];
8365
8366                                         //      Less likely to fire if far away and moving.
8367                                         scale = pwip->max_speed/(En_objp->phys_info.speed + pwip->max_speed);
8368                                         if (scale > 0.6f)
8369                                                 scale = (scale - 0.6f) * 1.5f;
8370                                         else
8371                                                 scale = 0.0f;
8372                                         if (dist_to_enemy < pwip->max_speed * (1.0f + scale)) {
8373                                                 ai_fire_primary_weapon(Pl_objp);
8374                                         }
8375
8376                                         //      Don't fire secondaries at a protected ship.
8377                                         if (!(En_objp->flags & OF_PROTECTED)) {
8378                                                 ai_choose_secondary_weapon(Pl_objp, aip, En_objp);
8379                                                 int current_bank = tswp->current_secondary_bank;
8380                                                 weapon_info     *swip = &Weapon_info[tswp->secondary_bank_weapons[tswp->current_secondary_bank]];
8381
8382                                                 if (current_bank > -1) {
8383                                                         if (aip->ai_flags & AIF_UNLOAD_SECONDARIES) {
8384                                                                 if (timestamp_until(swp->next_secondary_fire_stamp[current_bank]) > swip->fire_wait*1000.0f) {
8385                                                                         swp->next_secondary_fire_stamp[current_bank] = timestamp((int) (swip->fire_wait*1000.0f));
8386                                                                 }
8387                                                         }
8388
8389                                                         if (timestamp_elapsed(swp->next_secondary_fire_stamp[current_bank])) {
8390                                                                 if (tswp->current_secondary_bank >= 0) {
8391                                                                         weapon_info     *swip = &Weapon_info[tswp->secondary_bank_weapons[tswp->current_secondary_bank]];
8392                                                                         float firing_range;
8393                                                                         
8394                                                                         if (swip->wi_flags & WIF_BOMB)
8395                                                                                 firing_range = swip->max_speed * swip->lifetime * 0.75f;
8396                                                                         else
8397                                                                                 firing_range = swip->max_speed * swip->lifetime * (Game_skill_level + 1 + aip->ai_class/2)/NUM_SKILL_LEVELS;
8398
8399                                                                         // reduce firing range in nebula
8400                                                                         extern int Nebula_sec_range;
8401                                                                         if ((The_mission.flags & MISSION_FLAG_FULLNEB) && Nebula_sec_range) {
8402                                                                                 firing_range *= 0.8f;
8403                                                                         }
8404
8405                                                                         //      If firing a spawn weapon, distance doesn't matter.
8406                                                                         int     spawn_fire = 0;
8407
8408                                                                         if (swip->wi_flags & WIF_SPAWN) {
8409                                                                                 int     count;
8410
8411                                                                                 count = num_nearby_fighters(get_enemy_team_mask(OBJ_INDEX(Pl_objp)), &Pl_objp->pos, 1000.0f);
8412
8413                                                                                 if (count > 3)
8414                                                                                         spawn_fire = 1;
8415                                                                                 else if (count >= 1) {
8416                                                                                         float hull_percent = Pl_objp->hull_strength/sip->initial_hull_strength;
8417
8418                                                                                         if (hull_percent < 0.01f)
8419                                                                                                 hull_percent = 0.01f;
8420
8421                                                                                         if (frand() < 0.25f/(30.0f*hull_percent) * count)       //      With timestamp below, this means could fire in 30 seconds if one enemy.
8422                                                                                                 spawn_fire = 1;
8423                                                                                 }
8424                                                                         }
8425
8426                                                                         if (spawn_fire || (dist_to_enemy < firing_range)) {
8427                                                                                 if (ai_fire_secondary_weapon(Pl_objp)) {
8428                                                                                         //      Only if weapon was fired do we specify time until next fire.  If not fired, done in ai_fire_secondary...
8429                                                                                         float t;
8430                                                                                         
8431                                                                                         if (aip->ai_flags & AIF_UNLOAD_SECONDARIES) {
8432                                                                                                 t = swip->fire_wait;
8433                                                                                         } else {
8434                                                                                                 t = set_secondary_fire_delay(aip, temp_shipp, swip);
8435                                                                                         }
8436                                                                                         //nprintf(("AI", "Next secondary to be fired in %7.3f seconds.\n", t));
8437                                                                                         swp->next_secondary_fire_stamp[current_bank] = timestamp((int) (t*1000.0f));
8438                                                                                 }
8439                                                                         } else {
8440                                                                                 swp->next_secondary_fire_stamp[current_bank] = timestamp(250);
8441                                                                         }
8442                                                                 }
8443                                                         }
8444                                                 }
8445                                         }
8446                                 }
8447                         }
8448                 } else {
8449                         aip->time_enemy_in_range *= (1.0f - flFrametime);
8450                 }
8451         } else
8452                 aip->time_enemy_in_range *= (1.0f - flFrametime);
8453
8454 }
8455
8456 //      Make the object *objp move so that the point *dp on the object moves towards the point *vp
8457 //      Return distance.
8458 void dock_move_towards_point(object *objp, vector *dp, vector *vp, float speed_scale, float other_obj_speed = 0.0f)
8459 {
8460         physics_info    *pi = &objp->phys_info;
8461         float                           dist;                   //      dist to goal
8462         vector                  v2g;                    //      vector to goal
8463         vector                  abs_pnt;                //      location of dock point, ie objp->pos + db
8464
8465         if (dp == NULL)
8466                 abs_pnt = objp->pos;
8467         else
8468                 vm_vec_add(&abs_pnt, &objp->pos, dp);
8469
8470         dist = vm_vec_dist_quick(vp, &abs_pnt);
8471         if (dist > 0.0f) {
8472                 float   speed;
8473
8474                 dist = vm_vec_normalized_dir(&v2g, vp, &abs_pnt);
8475                 speed = fl_sqrt(dist) * speed_scale;
8476                 if (other_obj_speed < MAX_REPAIR_SPEED*0.75f)
8477                         speed += other_obj_speed;
8478                 else
8479                         speed += MAX_REPAIR_SPEED*0.75f;
8480
8481                 vm_vec_copy_scale(&pi->desired_vel, &v2g, speed);
8482         } else
8483                 vm_vec_zero(&pi->desired_vel);
8484 }
8485
8486 //      Set the orientation in the global reference frame for an object to attain
8487 //      to dock with another object.
8488 //      *dom            resultant global matrix
8489 //      *db_dest        pointer to destination docking bay information
8490 //      *db_src pointer to source docking bay information
8491 //      *dorient        pointer to global orientation of docking bay (ie, the dockee object's orient)
8492 //      *sorient        pointer to global orientation of docker
8493 void set_goal_dock_orient(matrix *dom, dock_bay *db_dest, dock_bay *db_src, matrix *dorient, matrix *sorient)
8494 {
8495         vector  fvec, uvec;
8496         matrix  m1, m2, m3;
8497
8498         //      Compute the global orientation of the docker's (dest) docking bay.
8499         fvec = db_dest->norm[0];
8500         vm_vec_negate(&fvec);
8501
8502         vm_vec_normalized_dir(&uvec, &db_dest->pnt[1], &db_dest->pnt[0]);
8503         vm_vector_2_matrix(&m1, &fvec, &uvec, NULL);
8504
8505         vm_matrix_x_matrix(&m3, dorient, &m1);
8506
8507         //      Compute the matrix given by the source docking bay.
8508         //      Pre-multiply the orientation of the source object (sorient) by the transpose
8509         //      of the docking bay's orientation, ie unrotate the source object's matrix.
8510         fvec = db_src->norm[0];
8511         vm_vec_normalized_dir(&uvec, &db_src->pnt[1], &db_src->pnt[0]);
8512         vm_vector_2_matrix(&m2, &fvec, &uvec, NULL);
8513         vm_transpose(&m2);
8514
8515         vm_matrix_x_matrix(dom, &m3, &m2);
8516 }
8517
8518 #define DOCK_BACKUP_RETURN_VAL  99999.9f
8519
8520 //      Make objp dock with dobjp
8521 //      Returns distance to goal, defined as distance between corresponding dock points, plus 10.0f * rotational velocity vector (DOA_DOCK only)
8522 //      DOA_APPROACH    means   approach point aip->path_cur
8523 //      DOA_DOCK                        means dock
8524 //      DOA_UNDOCK_1    means undock, moving to point nearest dock bay
8525 //      DOA_UNDOCK_2    means undock, moving to point nearest dock bay and facing away from ship
8526 //      DOA_DOCK_STAY   means rigidly maintain position in dock bay.
8527 float dock_orient_and_approach(object *objp, object *dobjp, int dock_mode)
8528 {
8529         ship_info       *sip0, *sip1;
8530         polymodel       *pm0, *pm1;
8531         ai_info         *aip;
8532         matrix          dom, nm;
8533         vector          goal_point, docker_point;
8534         float                   fdist = UNINITIALIZED_VALUE;
8535         int                     docker_index, dockee_index;             // index into docking_bays[] array for objects docking
8536                                                                                                                                 // docker is Pl_objp -- dockee is dobjp
8537         aip = &Ai_info[Ships[objp->instance].ai_index];
8538
8539         //      If dockee has moved much, then path will be recreated.
8540         //      Might need to change state if moved too far.
8541         if ((dock_mode != DOA_DOCK_STAY) && (dock_mode != DOA_DOCK)) {
8542                 if (maybe_recreate_path(objp, &Ai_info[Ships[objp->instance].ai_index], 0) > 5.0f) {
8543 /*                      if (dock_mode == DOA_APPROACH) {
8544                                 return DOCK_BACKUP_RETURN_VAL;
8545                         } else if (dock_mode == DOA_DOCK) {
8546                                 return DOCK_BACKUP_RETURN_VAL;          
8547                         }
8548 */              }
8549         }
8550
8551         objp->phys_info.forward_thrust = 0.0f;          //      Kill thrust so we don't have a sputtering thruster.
8552
8553         sip0 = &Ship_info[Ships[objp->instance].ship_info_index];
8554         sip1 = &Ship_info[Ships[dobjp->instance].ship_info_index];
8555         pm0 = model_get( sip0->modelnum );
8556         pm1 = model_get( sip1->modelnum );
8557
8558         docker_index = aip->dock_index;
8559         dockee_index = aip->dockee_index;
8560
8561         Assert( docker_index >= 0 );
8562         Assert( dockee_index >= 0 );
8563
8564         Assert(pm0->docking_bays[docker_index].num_slots == 2);
8565         Assert(pm1->docking_bays[dockee_index].num_slots == 2);
8566
8567         float speed_scale = 1.0f;
8568         if (sip0->flags & SIF_SUPPORT) {
8569                 speed_scale = 3.0f;
8570         }
8571
8572         switch (dock_mode) {
8573         case DOA_APPROACH:
8574                 {
8575                 if (ship_get_subsystem_strength(&Ships[objp->instance], SUBSYSTEM_ENGINE) <= 0.0f) {
8576                         return 9999.9f;
8577                 }
8578                 
8579                 //      Compute the desired global orientation matrix for the docker's station.
8580                 //      That is, the normal vector of the docking station must be the same as the
8581                 //      forward vector and the vector between its two points must be the uvec.
8582                 set_goal_dock_orient(&dom, &pm1->docking_bays[dockee_index], &pm0->docking_bays[docker_index], &dobjp->orient, &objp->orient);
8583
8584                 //      Compute new orientation matrix and update rotational velocity.
8585                 vector  w_in, w_out, vel_limit, acc_limit;
8586                 float           tdist, mdist, ss1;
8587
8588                 w_in = objp->phys_info.rotvel;
8589                 vel_limit = objp->phys_info.max_rotvel;
8590                 vm_vec_copy_scale(&acc_limit, &vel_limit, 0.3f);
8591                 
8592                 if (sip0->flags & SIF_SUPPORT)
8593                         vm_vec_scale(&acc_limit, 2.0f);
8594
8595                 // 1 at end of line prevent overshoot
8596                 vm_matrix_interpolate(&dom, &objp->orient, &w_in, flFrametime, &nm, &w_out, &vel_limit, &acc_limit, 1);
8597                 objp->phys_info.rotvel = w_out;
8598                 objp->orient = nm;
8599
8600                 //      Translate towards goal and note distance to goal.
8601                 goal_point = Path_points[aip->path_cur].pos;
8602                 mdist = ai_matrix_dist(&objp->orient, &dom);
8603                 tdist = vm_vec_dist_quick(&objp->pos, &goal_point);
8604
8605                 //      If translation is badly lagging rotation, speed up translation.
8606                 if (mdist > 0.1f) {
8607                         ss1 = tdist/(10.0f * mdist);
8608                         if (ss1 > 2.0f)
8609                                 ss1 = 2.0f;
8610                 } else
8611                         ss1 = 2.0f;
8612
8613                 // nprintf(("AI", "speed scale = %7.3f\n", ss1));
8614                 speed_scale *= 1.0f + ss1;
8615
8616                 dock_move_towards_point(objp, NULL, &goal_point, speed_scale, dobjp->phys_info.speed);
8617
8618                 fdist = vm_vec_dist_quick(&objp->pos, &goal_point);
8619
8620                 //      Note, we're interested in distance from goal, so if we're still turning, bash that into return value.
8621                 // nprintf(("AI", "matrix dist = %7.3f, threshold = %7.3f\n", mdist, 2*flFrametime));
8622                 fdist += 2.0f * mdist;
8623
8624                 break;
8625         }
8626         case DOA_DOCK:
8627                 if (ship_get_subsystem_strength(&Ships[objp->instance], SUBSYSTEM_ENGINE) <= 0.0f) {
8628                         return 9999.9f;
8629                 }
8630         case DOA_DOCK_STAY:
8631                 //      Compute the desired global orientation matrix for the docker's station.
8632                 //      That is, the normal vector of the docking station must be the same as the
8633                 //      forward vector and the vector between its two points must be the uvec.
8634                 set_goal_dock_orient(&dom, &pm1->docking_bays[dockee_index], &pm0->docking_bays[docker_index], &dobjp->orient, &objp->orient);
8635
8636                 //      Compute distance between dock bay points.
8637                 vector  db0, db1, db2, db3;
8638
8639                 vm_vec_unrotate(&db0, &pm0->docking_bays[docker_index].pnt[0], &objp->orient);
8640                 vm_vec_add2(&db0, &objp->pos);
8641
8642                 vm_vec_unrotate(&db1, &pm0->docking_bays[docker_index].pnt[1], &objp->orient);
8643                 vm_vec_add2(&db1, &objp->pos);
8644
8645                 vm_vec_unrotate(&db2, &pm1->docking_bays[dockee_index].pnt[0], &dobjp->orient);
8646                 vm_vec_add2(&db2, &dobjp->pos);
8647
8648                 vm_vec_unrotate(&db3, &pm1->docking_bays[dockee_index].pnt[1], &dobjp->orient);
8649                 vm_vec_add2(&db3, &dobjp->pos);
8650
8651                 vm_vec_avg(&goal_point, &db2, &db3);
8652
8653                 vm_vec_avg(&docker_point, &db0, &db1);
8654                 vm_vec_sub2(&docker_point, &objp->pos);
8655
8656                 if (dock_mode == DOA_DOCK) {
8657                         vector  t1, t2;
8658                         vector  w_in, w_out, vel_limit, acc_limit;
8659
8660                         fdist = vm_vec_dist_quick(vm_vec_avg(&t1, &db0, &db1), vm_vec_avg(&t2, &db2, &db3));
8661
8662                         //      Compute new orientation matrix and update rotational velocity.
8663                         w_in = objp->phys_info.rotvel;
8664                         vel_limit = objp->phys_info.max_rotvel;
8665                         vm_vec_copy_scale(&acc_limit, &vel_limit, 0.3f);
8666
8667                         if (sip0->flags & SIF_SUPPORT)
8668                                 vm_vec_scale(&acc_limit, 2.0f);
8669
8670                         vm_matrix_interpolate(&dom, &objp->orient, &w_in, flFrametime, &nm, &w_out, &vel_limit, &acc_limit);
8671                         objp->phys_info.rotvel = w_out;
8672                         objp->orient = nm;
8673
8674                         //      Note, we're interested in distance from goal, so if we're still turning, bash that into return value.
8675                         fdist += 10.0f * vm_vec_mag_quick(&w_out);
8676
8677                         dock_move_towards_point(objp, &docker_point, &goal_point, speed_scale, dobjp->phys_info.speed);
8678                 } else {
8679                         Assert(dock_mode == DOA_DOCK_STAY);
8680                         objp->orient = dom;
8681                         vector  temp;
8682                         vm_vec_sub(&temp, &goal_point, &docker_point);
8683                         vm_vec_sub(&objp->pos, &goal_point, &docker_point);
8684                 }
8685
8686                 break;
8687         case DOA_UNDOCK_1: {
8688                 if (ship_get_subsystem_strength(&Ships[objp->instance], SUBSYSTEM_ENGINE) <= 0.0f) {
8689                         return 9999.9f;
8690                 }
8691
8692                 //      Undocking.
8693                 //      Move to point on dock path nearest to dock station.
8694                 Assert(aip->path_length >= 2);
8695                 goal_point = Path_points[aip->path_start + aip->path_length-2].pos;
8696
8697                 vm_vec_zero(&docker_point);
8698                 fdist = vm_vec_dist_quick(&objp->pos, &goal_point);
8699
8700                 dock_move_towards_point(objp, &docker_point, &goal_point, speed_scale);
8701
8702                 break;
8703                           }
8704
8705         case DOA_UNDOCK_2: {
8706                 //      Undocking.
8707                 //      Move to point on dock path nearest to dock station and orient away from big ship.
8708                 int             desired_index;
8709
8710                 if (ship_get_subsystem_strength(&Ships[objp->instance], SUBSYSTEM_ENGINE) <= 0.0f) {
8711                         return 9999.9f;
8712                 }
8713
8714                 Assert(aip->path_length >= 2);
8715 //              if (aip->path_length >= 3)
8716 //                      desired_index = aip->path_length-3;
8717 //              else
8718                         desired_index = aip->path_length-2;
8719
8720                 goal_point = Path_points[aip->path_start + desired_index].pos;
8721
8722                 dock_move_towards_point(objp, NULL, &goal_point, speed_scale);
8723
8724                 fdist = vm_vec_dist_quick(&objp->pos, &goal_point);
8725                 break;
8726                           }
8727         case DOA_UNDOCK_3: {
8728                 float           dist, goal_dist;
8729                 vector  away_vec;
8730
8731                 goal_dist = objp->radius + dobjp->radius + 25.0f;
8732
8733                 dist = vm_vec_normalized_dir(&away_vec, &objp->pos, &dobjp->pos);
8734                 vm_vec_scale_add(&goal_point, &dobjp->pos, &away_vec, goal_dist);
8735                 if (vm_vec_dist_quick(&goal_point, &dobjp->pos) < vm_vec_dist_quick(&objp->pos, &dobjp->pos))
8736                         fdist = 0.0f;
8737                 else {
8738                         float   dot, accel;
8739                         float turn_time = Ship_info[Ships[objp->instance].ship_info_index].srotation_time;
8740                         ai_turn_towards_vector(&goal_point, objp, flFrametime, turn_time, NULL, NULL, 0.0f, 0);
8741
8742                         dot = vm_vec_dot(&objp->orient.fvec, &away_vec);
8743                         accel = 0.1f;
8744                         if (dot > accel)
8745                                 accel = dot;
8746                         if (dist > goal_dist/2)
8747                                 accel *= 1.2f - 0.5f*goal_dist/dist;
8748
8749                         accelerate_ship(aip, accel);
8750                         fdist = vm_vec_dist_quick(&objp->pos, &goal_point);
8751                 }
8752
8753                 break;
8754                                                          }
8755         }
8756
8757 #ifndef NDEBUG
8758         //      For debug purposes, compute global orientation of both dock vectors and show
8759         //      how close they are.
8760         vector  d0, d1;
8761
8762         vm_vec_unrotate(&d0, &pm0->docking_bays[docker_index].norm[0], &objp->orient);
8763         vm_vec_unrotate(&d1, &pm1->docking_bays[dockee_index].norm[0], &dobjp->orient);
8764
8765         //nprintf(("AI", "or/app: dist = %7.3f/%7.3f, dot = %7.3f, global dot = %7.3f\n", 
8766         //      vm_vec_dist_quick(&goal_point, &objp->pos), fdist,
8767         //      vm_vec_dot(&objp->orient.fvec, &dom.fvec), 
8768         //      vm_vec_dot(&d0, &d1)));
8769 #endif
8770
8771         // -- Note, A lot of callers don't care about fdist, so OK to return ERROR value: Assert(fdist != UNINITIALIZED_VALUE);
8772         return fdist;
8773
8774 }
8775
8776 void debug_find_guard_object()
8777 {
8778         ship                    *shipp = &Ships[Pl_objp->instance];     
8779         object          *objp;
8780
8781         for ( objp = GET_FIRST(&obj_used_list); objp !=END_OF_LIST(&obj_used_list); objp = GET_NEXT(objp) ) {
8782                 if ((Pl_objp != objp) && (objp->type == OBJ_SHIP)) {
8783                         if (objp->instance != -1) {
8784                                 if (Ships[objp->instance].team == shipp->team)  {
8785                                         // nprintf(("AI", "Setting guard object for %s to %s\n", shipp->ship_name, Ships[objp->instance].ship_name));
8786                                         ai_set_guard_object(Pl_objp, objp);
8787                                 }
8788                         }
8789                 }
8790         }
8791
8792 }
8793
8794 //      Given an object number, return the number of ships attacking it.
8795 int num_ships_attacking(int objnum)
8796 {
8797         object  *objp;
8798         ship_obj        *so;
8799         int             count = 0;
8800
8801         for ( so = GET_FIRST(&Ship_obj_list); so != END_OF_LIST(&Ship_obj_list); so = GET_NEXT(so) ) {
8802                 objp = &Objects[so->objnum];
8803                 if (objp->instance != -1) {
8804                         ai_info *aip;
8805                         aip = &Ai_info[Ships[objp->instance].ai_index];
8806
8807                         if ((aip->mode == AIM_CHASE) && (aip->target_objnum == objnum))
8808                                 if (Ships[objp->instance].team != Ships[Objects[objnum].instance].team)
8809                                         count++;
8810                 }
8811         }
8812
8813         return count;
8814 }
8815
8816 //      For all objects attacking object #objnum, remove the one that is farthest away.
8817 //      Do this by resuming previous behavior, if any.  If not, set target_objnum to -1.
8818 void remove_farthest_attacker(int objnum)
8819 {
8820         object  *objp, *objp2, *farthest_objp;
8821         ship_obj        *so;
8822         float           farthest_dist;
8823
8824         objp2 = &Objects[objnum];
8825
8826         farthest_dist = 9999999.9f;
8827         farthest_objp = NULL;
8828
8829         for ( so = GET_FIRST(&Ship_obj_list); so != END_OF_LIST(&Ship_obj_list); so = GET_NEXT(so) ) {
8830                 objp = &Objects[so->objnum];
8831                 if ( !(objp->flags & OF_PLAYER_SHIP)) {
8832                         if (objp->instance != -1) {
8833                                 ai_info *aip2;
8834
8835                                 aip2 = &Ai_info[Ships[objp->instance].ai_index];
8836
8837                                 if ((aip2->mode == AIM_CHASE) && (aip2->target_objnum == objnum)) {
8838                                         if (Ships[objp->instance].team != Ships[Objects[objnum].instance].team) {
8839                                                 float   dist;
8840
8841                                                 dist = vm_vec_dist_quick(&objp->pos, &objp2->pos);
8842                                                 if (dist < farthest_dist) {
8843                                                         farthest_dist = dist;
8844                                                         farthest_objp = objp;
8845                                                 }
8846                                         }
8847                                 }
8848                         }
8849                 }
8850         }
8851
8852         if (farthest_objp != NULL) {
8853                 ai_info *aip;
8854                 Assert(farthest_objp->type == OBJ_SHIP);
8855                 Assert((farthest_objp->instance > -1) && (farthest_objp->instance < MAX_SHIPS));
8856                 Assert(Ships[farthest_objp->instance].ai_index > -1);
8857
8858                 aip = &Ai_info[Ships[farthest_objp->instance].ai_index];
8859
8860                 if (!maybe_resume_previous_mode(Pl_objp, aip)) {
8861                         //      If already ignoring something under player's orders, don't ignore current target.
8862                         if ((aip->ignore_objnum == UNUSED_OBJNUM) || (aip->ai_flags & AIF_TEMPORARY_IGNORE)) {
8863                                 aip->ignore_objnum = aip->target_objnum;
8864                                 aip->ignore_signature = Objects[aip->target_objnum].signature;
8865                                 aip->ai_flags |= AIF_TEMPORARY_IGNORE;
8866                                 aip->ignore_expire_timestamp = timestamp(((myrand() % 10) + 20) * 1000);        //      OK to attack again in 20 to 24 seconds.
8867                         }
8868                         aip->target_objnum = -1;
8869                         ai_do_default_behavior(farthest_objp);
8870                 }
8871         }
8872 }
8873
8874 // Maybe limit the number of attackers on attack_objnum.  For now, only limit attackers
8875 // in attacked_objnum is the player
8876 // input:       attacked_objnum =>              object index for ship we want to limit attacks on
8877 //
8878 //      exit:                   1       =>      num attackers exceeds maximum, abort
8879 //                                      0       =>      removed the farthest attacker
8880 //                                      -1      =>      nothing was done
8881 int ai_maybe_limit_attackers(int attacked_objnum)
8882 {
8883         int rval=-1;
8884
8885         // limit the number of ships attacking the _player_ only
8886 //      if ( attacked_objnum == OBJ_INDEX(Player_obj) ) {
8887         if ( Objects[attacked_objnum].flags & OF_PLAYER_SHIP) {
8888                 int num_attacking;
8889                 num_attacking = num_ships_attacking(attacked_objnum);
8890
8891                 if (num_attacking == Skill_level_max_attackers[Game_skill_level]) {
8892                         remove_farthest_attacker(attacked_objnum);
8893                         rval=0;
8894                 } else if (num_attacking > Skill_level_max_attackers[Game_skill_level]) {
8895                         rval=1;
8896                 }
8897                 //nprintf(("AI", "Num attacking player = %i\n", num_attacking));
8898         }
8899
8900         return rval;
8901 }
8902
8903 //      Object being guarded by object *guard_objp was hit by object *hitter_objp
8904 void guard_object_was_hit(object *guard_objp, object *hitter_objp)
8905 {
8906         int             hitter_objnum;
8907         ai_info *aip;
8908
8909         aip = &Ai_info[Ships[guard_objp->instance].ai_index];
8910
8911         if (guard_objp == hitter_objp) {
8912                 // Int3();      //      Bogus!  Who tried to get me to attack myself!  Trace out and fix!
8913                 return;
8914         }
8915
8916         if (guard_objp->type == OBJ_GHOST || hitter_objp->type == OBJ_GHOST)
8917                 return;
8918
8919         if (aip->ai_flags & AIF_NO_DYNAMIC)     //      Not allowed to pursue dynamic goals.  So, why are we guarding?
8920                 return;
8921
8922         Assert( (hitter_objp->type == OBJ_SHIP) || (hitter_objp->type == OBJ_ASTEROID) || (hitter_objp->type == OBJ_WEAPON) );
8923
8924         hitter_objnum = OBJ_INDEX(hitter_objp);
8925
8926         if ( hitter_objp->type == OBJ_SHIP ) {
8927                 //      If the hitter object is the ignore object, don't attack it.
8928                 if (is_ignore_object(aip, hitter_objp-Objects))
8929                         return;
8930
8931                 //      If hitter is on same team as me, don't attack him.
8932                 if (Ships[guard_objp->instance].team == Ships[hitter_objp->instance].team)
8933                         return;
8934
8935                 // limit the number of ships attacking hitter_objnum (for now, only if hitter_objnum is player)
8936                 if ( ai_maybe_limit_attackers(hitter_objnum) == 1 ) {
8937                         return;
8938                 }
8939
8940                 // dont attack if you can't see him
8941                 if ( awacs_get_level(hitter_objp, &Ships[aip->shipnum], 1) < 1 ) {
8942                         // if he's a stealth and visible, but not targetable, ok to attack.
8943                         if ( is_object_stealth_ship(hitter_objp) ) {
8944                                 if ( ai_is_stealth_visible(guard_objp, hitter_objp) != STEALTH_VISIBLE ) {
8945                                         return;
8946                                 }
8947                         }
8948                 }
8949         }
8950
8951         if (aip->target_objnum == -1) {
8952                 aip->ok_to_target_timestamp = timestamp(0);
8953         }
8954
8955         if ((aip->submode == AIS_GUARD_PATROL) || (aip->submode == AIS_GUARD_STATIC)) {
8956
8957                 if ( hitter_objp->type == OBJ_SHIP ) {
8958                         if (!(Ship_info[Ships[guard_objp->instance].ship_info_index].flags & SIF_SMALL_SHIP)) {
8959                                 return;
8960                         }
8961
8962                         // limit the number of ships attacking hitter_objnum (for now, only if hitter_objnum is player)
8963                         if ( ai_maybe_limit_attackers(hitter_objnum) == 1 ) {
8964                                 return;
8965                         }
8966                 }
8967
8968                 if (aip->target_objnum != hitter_objnum) {
8969                         aip->aspect_locked_time = 0.0f;
8970                 }
8971
8972                 aip->ok_to_target_timestamp = timestamp(0);
8973
8974                 set_target_objnum(aip, hitter_objnum);
8975                 //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));
8976                 aip->previous_mode = AIM_GUARD;
8977                 aip->previous_submode = aip->submode;
8978                 aip->mode = AIM_CHASE;
8979                 aip->submode = SM_ATTACK;
8980                 aip->submode_start_time = Missiontime;
8981                 aip->active_goal = AI_ACTIVE_GOAL_DYNAMIC;
8982         } else if (aip->previous_mode == AIM_GUARD) {
8983                 if (aip->target_objnum == -1) {
8984
8985                         if ( hitter_objp->type == OBJ_SHIP ) {
8986                                 // limit the number of ships attacking hitter_objnum (for now, only if hitter_objnum is player)
8987                                 if ( ai_maybe_limit_attackers(hitter_objnum) == 1 ) {
8988                                         return;
8989                                 }
8990                         }
8991
8992                         set_target_objnum(aip, hitter_objnum);
8993                 //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));
8994                         aip->mode = AIM_CHASE;
8995                         aip->submode = SM_ATTACK;
8996                         aip->submode_start_time = Missiontime;
8997                         aip->active_goal = AI_ACTIVE_GOAL_DYNAMIC;
8998                 } else {
8999                         int     num_attacking_cur, num_attacking_new;
9000
9001                         num_attacking_cur = num_ships_attacking(aip->target_objnum);
9002                         if (num_attacking_cur > 1) {
9003                                 num_attacking_new = num_ships_attacking(hitter_objnum);
9004
9005                                 if (num_attacking_new < num_attacking_cur) {
9006
9007                                         if ( hitter_objp->type == OBJ_SHIP ) {
9008                                                 // limit the number of ships attacking hitter_objnum (for now, only if hitter_objnum is player)
9009                                                 if ( ai_maybe_limit_attackers(hitter_objnum) == 1 ) {
9010                                                         return;
9011                                                 }
9012                                         }
9013                                         set_target_objnum(aip, hitter_objp-Objects);
9014                 //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));
9015                                         aip->mode = AIM_CHASE;
9016                                         aip->submode = SM_ATTACK;
9017                                         aip->submode_start_time = Missiontime;
9018                                         aip->active_goal = AI_ACTIVE_GOAL_DYNAMIC;
9019                                 }
9020                         }
9021                 }
9022         }
9023 }
9024
9025 //      Ship object *hit_objp was hit by ship object *hitter_objp.
9026 //      See if anyone is guarding hit_objp and, if so, do something useful.
9027 void maybe_update_guard_object(object *hit_objp, object *hitter_objp)
9028 {
9029         object  *objp;
9030         ship_obj        *so;
9031
9032         for ( so = GET_FIRST(&Ship_obj_list); so != END_OF_LIST(&Ship_obj_list); so = GET_NEXT(so) ) {
9033                 objp = &Objects[so->objnum];
9034                 if (objp->instance != -1) {
9035                         ai_info *aip;
9036                         aip = &Ai_info[Ships[objp->instance].ai_index];
9037
9038                         if ((aip->mode == AIM_GUARD) || (aip->active_goal == AI_ACTIVE_GOAL_DYNAMIC)) {
9039                                 if (aip->guard_objnum == hit_objp-Objects) {
9040                                         guard_object_was_hit(objp, hitter_objp);
9041                                 } else if ((aip->guard_wingnum != -1) && (aip->guard_wingnum == Ai_info[Ships[hit_objp->instance].ai_index].wing)) {
9042                                         guard_object_was_hit(objp, hitter_objp);
9043                                 }
9044                         }
9045                 }
9046         }
9047 }
9048
9049 // Scan missile list looking for bombs homing on guarded_objp
9050 // return 1 if bomb is found (and targeted by guarding_objp), otherwise return 0
9051 int ai_guard_find_nearby_bomb(object *guarding_objp, object *guarded_objp)
9052 {       
9053         missile_obj     *mo;
9054         object          *bomb_objp, *closest_bomb_objp=NULL;
9055         float                   dist, dist_to_guarding_obj,closest_dist_to_guarding_obj=999999.0f;
9056         weapon          *wp;
9057         weapon_info     *wip;
9058
9059         for ( mo = GET_NEXT(&Missile_obj_list); mo != END_OF_LIST(&Missile_obj_list); mo = GET_NEXT(mo) ) {
9060                 Assert(mo->objnum >= 0 && mo->objnum < MAX_OBJECTS);
9061                 bomb_objp = &Objects[mo->objnum];
9062
9063                 wp = &Weapons[bomb_objp->instance];
9064                 wip = &Weapon_info[wp->weapon_info_index];
9065
9066                 if ( !(wip->wi_flags & WIF_BOMB) ) {
9067                         continue;
9068                 }
9069
9070                 if ( wp->homing_object != guarded_objp ) {
9071                         continue;
9072                 }
9073
9074                 dist = vm_vec_dist_quick(&bomb_objp->pos, &guarded_objp->pos);
9075
9076                 if (dist < (MAX_GUARD_DIST + guarded_objp->radius)*3) {
9077                         dist_to_guarding_obj = vm_vec_dist_quick(&bomb_objp->pos, &guarding_objp->pos);
9078                         if ( dist_to_guarding_obj < closest_dist_to_guarding_obj ) {
9079                                 closest_dist_to_guarding_obj = dist_to_guarding_obj;
9080                                 closest_bomb_objp = bomb_objp;
9081                         }
9082                 }
9083         }
9084
9085         if ( closest_bomb_objp ) {
9086                 guard_object_was_hit(guarding_objp, closest_bomb_objp);
9087                 return 1;
9088         }
9089
9090         return 0;
9091 }
9092
9093 //      Scan enemy ships and see if one is near enough to guard object to be pursued.
9094 void ai_guard_find_nearby_ship(object *guarding_objp, object *guarded_objp)
9095 {
9096         ship            *guarding_shipp = &Ships[guarding_objp->instance];
9097         ai_info *guarding_aip = &Ai_info[guarding_shipp->ai_index];
9098         ship_obj        *so;
9099         object  *enemy_objp;
9100         float           dist;
9101
9102         for ( so = GET_FIRST(&Ship_obj_list); so != END_OF_LIST(&Ship_obj_list); so = GET_NEXT(so) ) {
9103                 enemy_objp = &Objects[so->objnum];
9104
9105                 if (enemy_objp->instance < 0) {
9106                         continue;
9107                 }
9108
9109                 ship    *eshipp = &Ships[enemy_objp->instance];
9110
9111                 //      Don't attack a cargo container or other harmless ships
9112                 if (!(Ship_info[eshipp->ship_info_index].flags & SIF_HARMLESS)) {
9113                         if (guarding_shipp->team != eshipp->team)       {
9114                                 dist = vm_vec_dist_quick(&enemy_objp->pos, &guarded_objp->pos);
9115                                 if (dist < (MAX_GUARD_DIST + guarded_objp->radius)*3) {
9116                                         guard_object_was_hit(guarding_objp, enemy_objp);
9117                                 } else if ((dist < 3000.0f) && (Ai_info[eshipp->ai_index].target_objnum == guarding_aip->guard_objnum)) {
9118                                         //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));
9119                                         guard_object_was_hit(guarding_objp, enemy_objp);
9120                                 }
9121                         }
9122                 }
9123         }
9124 }
9125
9126 // Scan for nearby asteroids.  Favor asteroids which have their collide_objnum set to that of the
9127 // guarded ship.  Also, favor asteroids that are closer to the guarding ship, since it looks cooler
9128 // when a ship blows up an asteroid then goes after the pieces that break off.
9129 void ai_guard_find_nearby_asteroid(object *guarding_objp, object *guarded_objp)
9130 {       
9131         float           dist;
9132
9133         object  *closest_asteroid_objp=NULL, *danger_asteroid_objp=NULL, *asteroid_objp;
9134         float           dist_to_self, closest_danger_asteroid_dist=999999.0f, closest_asteroid_dist=999999.0f;
9135
9136         for ( asteroid_objp = GET_FIRST(&obj_used_list); asteroid_objp != END_OF_LIST(&obj_used_list); asteroid_objp = GET_NEXT(asteroid_objp) ) {
9137                 if ( asteroid_objp->type == OBJ_ASTEROID ) {
9138                         // Attack asteroid if near guarded ship
9139                         dist = vm_vec_dist_quick(&asteroid_objp->pos, &guarded_objp->pos);
9140                         if ( dist < (MAX_GUARD_DIST + guarded_objp->radius)*2) {
9141                                 dist_to_self = vm_vec_dist_quick(&asteroid_objp->pos, &guarding_objp->pos);
9142                                 if ( OBJ_INDEX(guarded_objp) == asteroid_collide_objnum(asteroid_objp) ) {
9143                                         if( dist_to_self < closest_danger_asteroid_dist ) {
9144                                                 danger_asteroid_objp=asteroid_objp;
9145                                                 closest_danger_asteroid_dist=dist_to_self;
9146                                         }
9147                                 } 
9148                                 if ( dist_to_self < closest_asteroid_dist ) {
9149                                         // only attack if moving slower than own max speed
9150                                         if ( vm_vec_mag_quick(&asteroid_objp->phys_info.vel) < guarding_objp->phys_info.max_vel.z ) {
9151                                                 closest_asteroid_dist = dist_to_self;
9152                                                 closest_asteroid_objp = asteroid_objp;
9153                                         }
9154                                 }
9155                         }
9156                 }
9157         }
9158
9159         if ( danger_asteroid_objp ) {
9160                 guard_object_was_hit(guarding_objp, danger_asteroid_objp);
9161         } else if ( closest_asteroid_objp ) {
9162                 guard_object_was_hit(guarding_objp, closest_asteroid_objp);
9163         }
9164 }
9165
9166 //      Scan potential harmful objects and see if one is near enough to guard object to be pursued.
9167 void ai_guard_find_nearby_object()
9168 {
9169         ship                    *shipp = &Ships[Pl_objp->instance];
9170         ai_info         *aip = &Ai_info[shipp->ai_index];
9171         object          *guardobjp;
9172         int                     bomb_found=0;
9173
9174         guardobjp = &Objects[aip->guard_objnum];
9175         
9176         // highest priority is a bomb fired on guarded ship
9177         bomb_found = ai_guard_find_nearby_bomb(Pl_objp, guardobjp);
9178
9179         if ( !bomb_found ) {
9180                 // check for ships if there are no bombs fired at guarded ship
9181                 ai_guard_find_nearby_ship(Pl_objp, guardobjp);
9182
9183                 // if not attacking anything, go for asteroid close to guarded ship
9184                 if ( (aip->target_objnum == -1) && asteroid_count() ) {
9185                         ai_guard_find_nearby_asteroid(Pl_objp, guardobjp);
9186                 }
9187         }
9188 }
9189
9190 // gets closest point on extended axis of cylinder, r_vec, and radius of cylinder
9191 // returns z of axis_point in cyl_objp reference frame
9192 float get_cylinder_points(object *other_objp, object *cyl_objp, vector *axis_pt, vector *r_vec, float *radius)
9193 {
9194         Assert(other_objp->type == OBJ_SHIP);
9195         Assert(cyl_objp->type == OBJ_SHIP);
9196
9197         // get radius of cylinder
9198         polymodel *pm = model_get(Ships[cyl_objp->instance].modelnum);
9199         float tempx, tempy;
9200         tempx = max(-pm->mins.x, pm->maxs.x);
9201         tempy = max(-pm->mins.y, pm->maxs.y);
9202         *radius = max(tempx, tempy);
9203
9204         // get vec from cylinder to other_obj
9205         vector r_sph;
9206         vm_vec_sub(&r_sph, &other_objp->pos, &cyl_objp->pos);
9207
9208         // get point on axis and on cylinder
9209         // extended_cylinder_z is along extended cylinder
9210         // cylinder_z is capped within cylinder
9211         float extended_cylinder_z = vm_vec_dotprod(&r_sph, &cyl_objp->orient.fvec);
9212
9213         // get pt on axis of extended cylinder
9214         vm_vec_scale_add(axis_pt, &cyl_objp->pos, &cyl_objp->orient.fvec, extended_cylinder_z);
9215
9216         // get r_vec (pos - axis_pt) normalized
9217         vm_vec_normalized_dir(r_vec, &other_objp->pos, axis_pt);
9218
9219         return extended_cylinder_z;
9220 }
9221
9222 // handler for guard behavior when guarding BIG ships
9223 //      When someone has attacked guarded ship, then attack that ship.
9224 // To attack another ship, switch out of guard mode into chase mode.
9225 void ai_big_guard()
9226 {
9227         
9228         ship                    *shipp = &Ships[Pl_objp->instance];
9229         ai_info         *aip = &Ai_info[shipp->ai_index];
9230         object          *guard_objp;
9231
9232         // sanity checks already done in ai_guard()
9233         guard_objp = &Objects[aip->guard_objnum];
9234
9235         switch (aip->submode) {
9236         case AIS_GUARD_STATIC:
9237         case AIS_GUARD_PATROL:
9238                 {
9239                 vector axis_pt, r_vec, theta_vec;
9240                 float radius, extended_z;
9241
9242                 // get random [0 to 1] based on OBJNUM
9243                 float objval = static_randf(Pl_objp-Objects);
9244
9245                 // get position relative to cylinder of guard_objp              
9246                 extended_z = get_cylinder_points(Pl_objp, guard_objp, &axis_pt, &r_vec, &radius);
9247                 vm_vec_crossprod(&theta_vec, &guard_objp->orient.fvec, &r_vec);
9248
9249                 // half ships circle each way
9250                 if (objval > 0.5f) {
9251                         vm_vec_negate(&theta_vec);
9252                 }
9253
9254                 float min_guard_dist = radius + Pl_objp->radius + 50.0f;
9255                 float desired_guard_dist = min_guard_dist + 0.5f * ((1.0f + objval) * MAX_GUARD_DIST);
9256                 float max_guard_dist =     min_guard_dist + 1.0f * ((1.0f + objval) * MAX_GUARD_DIST);
9257
9258                 // get z extents
9259                 float min_z, max_z, length;
9260                 polymodel *pm = model_get(Ships[guard_objp->instance].modelnum);
9261                 min_z = pm->mins.z;
9262                 max_z = pm->maxs.z;
9263                 length = max_z - min_z;
9264
9265                 // get desired z
9266                 // how often to choose new desired_z
9267                 // 1*(64) sec < 2000, 2*(64) < 2-4000 3*(64) > 4-8000, etc (Missiontime >> 22 is 64 sec intervals)
9268                 int time_choose = int(floor(log(length * 0.001) / log(2)));
9269                 float desired_z = min_z + length * static_randf( Pl_objp-Objects ^ (Missiontime >> (22 + time_choose)) );
9270
9271                 // get r from guard_ship
9272                 float cur_guard_rad = vm_vec_dist(&Pl_objp->pos, &axis_pt);
9273
9274                 // is ship within extents of cylinder of ship it is guarding
9275                 int inside = (extended_z > min_z) && (extended_z < min_z + length);
9276
9277                 vector goal_pt;
9278                 // maybe go into orbit mode
9279                 if (cur_guard_rad < max_guard_dist) {
9280                         if ( cur_guard_rad > min_guard_dist ) {
9281                                 if (inside) {
9282                                         // orbit
9283                                         vm_vec_scale_add(&goal_pt, &axis_pt, &r_vec, desired_guard_dist);
9284                                         vm_vec_scale_add2(&goal_pt, &theta_vec, desired_guard_dist);
9285                                 } else {
9286                                         // move to where I can orbit
9287                                         if (extended_z < min_z) {
9288                                                 vm_vec_scale_add(&goal_pt, &guard_objp->pos, &guard_objp->orient.fvec, min_z);
9289                                         } else {
9290                                                 vm_vec_scale_add(&goal_pt, &guard_objp->pos, &guard_objp->orient.fvec, max_z);
9291                                         }
9292                                         vm_vec_scale_add2(&goal_pt, &r_vec, desired_guard_dist);
9293                                         vm_vec_scale_add2(&goal_pt, &theta_vec, desired_guard_dist);
9294                                 }
9295                         } else {
9296                                 // too close for orbit mode
9297                                 if (inside) {
9298                                         // inside (fly straight out and return circle)
9299                                         vm_vec_scale_add(&goal_pt, &axis_pt, &r_vec, max_guard_dist);
9300                                 } else {
9301                                         // outside (fly to edge and circle)
9302                                         if (extended_z < min_z) {
9303                                                 vm_vec_scale_add(&goal_pt, &guard_objp->pos, &guard_objp->orient.fvec, min_z);
9304                                         } else {
9305                                                 vm_vec_scale_add(&goal_pt, &guard_objp->pos, &guard_objp->orient.fvec, max_z);
9306                                         }
9307                                         vm_vec_scale_add2(&goal_pt, &r_vec, max_guard_dist);
9308                                         vm_vec_scale_add2(&goal_pt, &theta_vec, desired_guard_dist);
9309                                 }
9310                         }
9311
9312                         if (Pl_objp->phys_info.fspeed > 0) {
9313                                 // modify goal_pt to take account moving guard objp
9314                                 float dist = vm_vec_dist_quick(&Pl_objp->pos, &goal_pt);
9315                                 float time = dist / Pl_objp->phys_info.fspeed;
9316                                 vm_vec_scale_add2(&goal_pt, &guard_objp->phys_info.vel, time);
9317
9318                                 // now modify to move to desired z (at a max of 20 m/s)
9319                                 float delta_z = desired_z - extended_z;
9320                                 float v_z = delta_z * 0.2f;
9321                                 if (v_z < -20) {
9322                                         v_z = -20.0f;
9323                                 } else if (v_z > 20) {
9324                                         v_z = 20.0f;
9325                                 }
9326
9327                                 vm_vec_scale_add2(&goal_pt, &guard_objp->orient.fvec, v_z*time);
9328                         }
9329
9330                 } else {
9331                         // cast vector to center of guard_ship adjusted by desired_z
9332                         float delta_z = desired_z - extended_z;
9333                         vm_vec_scale_add(&goal_pt, &guard_objp->pos, &guard_objp->orient.fvec, delta_z);
9334                 }
9335
9336                 // try not to bump into things along the way
9337                 if ( (cur_guard_rad > max_guard_dist) || (extended_z < min_z) || (extended_z > max_z) ) {
9338                         if (maybe_avoid_big_ship(Pl_objp, guard_objp, aip, &goal_pt, 5.0f)) {
9339                                 return;
9340                         }
9341
9342                         if (avoid_player(Pl_objp, &goal_pt)) {
9343                                 return;
9344                         }
9345                 } else {
9346                         if (maybe_avoid_big_ship(Pl_objp, guard_objp, aip, &goal_pt, 5.0f)) {
9347                                 return;
9348                         }
9349                 }
9350
9351                 // got the point, now let's go there
9352                 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);
9353 //              aip->goal_point = goal_pt;
9354                 accelerate_ship(aip, 1.0f);
9355
9356                 //      Periodically, scan for a nearby ship to attack.
9357                 if (((AI_FrameCount ^ (Pl_objp-Objects)) & 0x07) == 0) {
9358                         ai_guard_find_nearby_object();
9359                 }
9360                 }
9361                 break;
9362
9363         case AIS_GUARD_ATTACK:
9364                 //      The guarded ship has been attacked.  Do something useful!
9365                 ai_chase();
9366                 break;
9367
9368         default:
9369                 //Int3();       //      Illegal submode for Guard mode.
9370                 // AL 06/03/97 comment out Int3() to allow milestone to get out the door
9371                 aip->submode = AIS_GUARD_PATROL;
9372                 break;
9373         }
9374 }
9375
9376 //      Main handler for guard behavior.
9377 //      When someone has attacked guarded ship, then attack that ship.
9378 // To attack another ship, switch out of guard mode into chase mode.
9379 void ai_guard()
9380 {
9381         ship                    *shipp = &Ships[Pl_objp->instance];
9382         ai_info         *aip = &Ai_info[shipp->ai_index];
9383         object          *guard_objp;    
9384         ship                    *gshipp;
9385         float                   dist_to_guardobj, dot_to_guardobj;
9386         vector          vec_to_guardobj;
9387
9388         /*      //      Debug code, find an object to guard.
9389         int finding_guard_objnum = 0;   //      Debug code, to see if body of "if" below gets executed. 
9390         if (aip->guard_objnum == -1) {
9391                 finding_guard_objnum = 1;
9392                 debug_find_guard_object();
9393                 if (aip->guard_objnum == -1)
9394                         return;
9395         }
9396 */
9397         if (aip->guard_objnum == -1) {
9398                 aip->mode = AIM_NONE;
9399                 return;
9400         }
9401
9402         Assert(aip->guard_objnum != -1);
9403
9404         guard_objp = &Objects[aip->guard_objnum];
9405
9406         if (guard_objp == Pl_objp) {
9407                 Int3();         //      This seems illegal.  Why is a ship guarding itself?
9408                 aip->guard_objnum = -1;
9409                 return;
9410         }
9411
9412         // check that I have someone to guard
9413         if (guard_objp->instance == -1) {
9414                 return;
9415         }
9416
9417         //      Not sure whether this should be impossible, or a reasonable cleanup condition.
9418         //      For now (3/31/97), it's getting trapped by an Assert, so clean it up.
9419         if (guard_objp->type != OBJ_SHIP) {
9420                 aip->guard_objnum = -1;
9421                 return;
9422         }
9423
9424         // handler for gurad object with BIG radius
9425         if (guard_objp->radius > BIG_GUARD_RADIUS) {
9426                 ai_big_guard();
9427                 return;
9428         }
9429
9430         gshipp = &Ships[guard_objp->instance];
9431
9432         float                   objval;
9433         vector          goal_point;
9434         vector          rel_vec;
9435         float                   dist_to_goal_point, dot_to_goal_point, accel_scale;
9436         vector          v2g, rvec;
9437
9438         // get random [0 to 1] based on OBJNUM
9439         objval = static_randf(Pl_objp-Objects);
9440
9441         switch (aip->submode) {
9442         case AIS_GUARD_STATIC:
9443         case AIS_GUARD_PATROL:
9444                 //      Stay near ship
9445                 dist_to_guardobj = vm_vec_normalized_dir(&vec_to_guardobj, &guard_objp->pos, &Pl_objp->pos);
9446                 dot_to_guardobj = vm_vec_dot(&Pl_objp->orient.fvec, &vec_to_guardobj);
9447
9448                 rel_vec = aip->guard_vec;
9449                 vm_vec_add(&goal_point, &guard_objp->pos, &rel_vec);
9450
9451                 vm_vec_normalized_dir(&v2g, &goal_point, &Pl_objp->pos);
9452                 dist_to_goal_point = vm_vec_dist_quick(&goal_point, &Pl_objp->pos);
9453                 dot_to_goal_point = vm_vec_dot(&v2g, &Pl_objp->orient.fvec);
9454                 accel_scale = (1.0f + dot_to_goal_point)/2.0f;
9455
9456                 //      If far away, get closer
9457                 if (dist_to_goal_point > MAX_GUARD_DIST + 1.5 * (Pl_objp->radius + guard_objp->radius)) {
9458                         if (maybe_avoid_big_ship(Pl_objp, guard_objp, aip, &goal_point, 5.0f)) {
9459                                 return;
9460                         }
9461
9462                         if (avoid_player(Pl_objp, &goal_point)) {
9463                                 return;
9464                         }
9465
9466                         // quite far away, so try to go straight to 
9467                         compute_desired_rvec(&rvec, &goal_point, &Pl_objp->pos);
9468                         ai_turn_towards_vector(&goal_point, Pl_objp, flFrametime, Ship_info[shipp->ship_info_index].srotation_time, NULL, NULL, 0.0f, 0, &rvec);
9469
9470                         accelerate_ship(aip, accel_scale * (0.25f + dist_to_goal_point/700.0f));
9471                 } else {
9472                         if (maybe_avoid_big_ship(Pl_objp, guard_objp, aip, &goal_point, 2.0f)) {
9473                                 return;
9474                         }
9475
9476                         // get max of guard_objp (1) normal speed (2) dock speed
9477                         float speed = guard_objp->phys_info.speed;
9478
9479                         if (guard_objp->type == OBJ_SHIP) {
9480                                 ai_info *guard_aip = &Ai_info[Ships[guard_objp->instance].ai_index];
9481
9482                                 if (guard_aip->dock_objnum != -1) {
9483                                         speed = max(speed, Objects[guard_aip->dock_objnum].phys_info.speed);
9484                                 }
9485                         }
9486                         
9487                         //      Deal with guarding a small object.
9488                         //      If going to guard_vec might cause a collision with guarded object, pick a new guard point.
9489                         if (vm_vec_dot(&v2g, &vec_to_guardobj) > 0.8f) {
9490                                 if (dist_to_guardobj < dist_to_goal_point) {
9491                                         ai_set_guard_vec(Pl_objp, guard_objp);  //      OK to return here.
9492                                         return;
9493                                 }
9494                         } 
9495
9496                         if (speed > 10.0f) {
9497                                 //      If goal ship is moving more than a tiny bit, don't orbit it, get near it.
9498                                 if (vm_vec_dist_quick(&goal_point, &Pl_objp->pos) > 40.0f) {
9499                                         if (vm_vec_dot(&Pl_objp->orient.fvec, &v2g) < 0.0f) {
9500                                                 //      Just slow down, don't turn.
9501                                                 set_accel_for_target_speed(Pl_objp, guard_objp->phys_info.speed - dist_to_goal_point/10.0f);
9502                                         } else {
9503                                                 //      Goal point is in front.
9504
9505                                                 //      If close to goal point, don't change direction, just change speed.
9506                                                 if (dist_to_goal_point > Pl_objp->radius + 10.0f) {
9507                                                         turn_towards_point(Pl_objp, &goal_point, NULL, 0.0f);
9508                                                 }
9509                                                 
9510                                                 set_accel_for_target_speed(Pl_objp, guard_objp->phys_info.speed + (dist_to_goal_point-40.0f)/20.0f);
9511                                         }
9512                                 } else {
9513                                         if (dot_to_goal_point > 0.8f) {
9514                                                 turn_towards_point(Pl_objp, &goal_point, NULL, 0.0f);
9515                                                 set_accel_for_target_speed(Pl_objp, guard_objp->phys_info.speed + dist_to_goal_point*0.1f);
9516                                         } else {
9517                                                 set_accel_for_target_speed(Pl_objp, guard_objp->phys_info.speed - dist_to_goal_point*0.1f - 1.0f);
9518                                         }
9519                                 }
9520                         // consider guard object STILL
9521                         } else if (guard_objp->radius < 50.0f) {
9522                                 if (dist_to_goal_point > 15.0f) {
9523                                         turn_towards_point(Pl_objp, &goal_point, NULL, 0.0f);
9524                                         set_accel_for_target_speed(Pl_objp, (dist_to_goal_point-10.0f)/2.0f);
9525                                 } else if (Pl_objp->phys_info.speed < 1.0f) {
9526                                         turn_away_from_point(Pl_objp, &guard_objp->pos, 0.0f);
9527                                 }
9528                                 //      It's a big ship
9529                         } else if (dist_to_guardobj > MAX_GUARD_DIST + Pl_objp->radius + guard_objp->radius) {
9530                                 //      Orbiting ship, too far away
9531                                 float dot = turn_towards_tangent(Pl_objp, &guard_objp->pos, (1.0f + objval/2) * guard_objp->radius);
9532                                 accelerate_ship(aip, (1.0f + dot)/2.0f);
9533                         } else if (dist_to_guardobj < Pl_objp->radius + guard_objp->radius) {
9534                                 //      Orbiting ship, got too close
9535                                 turn_away_from_point(Pl_objp, &guard_objp->pos, 0.0f);
9536                                 if ((dist_to_guardobj > guard_objp->radius + Pl_objp->radius + 50.0f) && (guard_objp->phys_info.speed > Pl_objp->phys_info.speed - 1.0f))
9537                                         change_acceleration(aip, 0.25f);
9538                                 else
9539                                         accelerate_ship(aip, 0.5f + objval/4.0f);
9540                         } else {
9541                                 //      Orbiting ship, about the right distance away.
9542                                 float dot = turn_towards_tangent(Pl_objp, &guard_objp->pos, (1.5f + objval/2.0f)*guard_objp->radius);
9543                                 if ((dist_to_guardobj > guard_objp->radius + Pl_objp->radius + 50.0f) && (guard_objp->phys_info.speed > Pl_objp->phys_info.speed - 1.0f))
9544                                         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));
9545                                 else
9546                                         accelerate_ship(aip, 0.5f * (1.0f + dot) * (0.3f + objval/3.0f));
9547                         }
9548                 }
9549
9550                 //      Periodically, scan for a nearby ship to attack.
9551                 if (((AI_FrameCount ^ (Pl_objp-Objects)) & 0x07) == 0) {
9552                         ai_guard_find_nearby_object();
9553                 }
9554                 break;
9555
9556         case AIS_GUARD_ATTACK:
9557                 //      The guarded ship has been attacked.  Do something useful!
9558                 ai_chase();
9559
9560                 break;
9561         default:
9562                 //Int3();       //      Illegal submode for Guard mode.
9563                 // AL 06/03/97 comment out Int3() to allow milestone to get out the door
9564                 aip->submode = AIS_GUARD_PATROL;
9565                 break;
9566         }
9567
9568 }
9569
9570 // Return the object of the ship that the given object is docked
9571 // with.  Currently, we know a ship is docked when his ai_mode is AIM_DOCK,
9572 // and his submode is AIS_DOCK_3.  I suppose that this is likely to change though.
9573 // Also, the objnum that was is passed in may not be the object that actually
9574 // performed the docking maneuver.  This code will account for that case.
9575 object *ai_find_docked_object( object *docker )
9576 {
9577         ai_info *aip;
9578
9579         // we are trying to find the dockee of docker.  (Note that that these terms
9580         // are totally relative to what is passed in as a parameter.)
9581
9582         // first thing to attempt is to check and see if this object is docked with something.
9583         Assert( docker->type == OBJ_SHIP );             // this had probably better be a ship!!!
9584         aip = &Ai_info[Ships[docker->instance].ai_index];
9585         if ( !(aip->ai_flags & AIF_DOCKED) )            // flag not set if not docked with anything
9586                 return NULL;
9587
9588         if ( aip->dock_objnum == -1 ) {
9589                 Int3();                                                                                 // mwa says this is wrong wrong wrong
9590                 ai_do_objects_undocked_stuff( docker, NULL );
9591                 return NULL;
9592         }
9593
9594         return &Objects[aip->dock_objnum];
9595
9596 }
9597
9598
9599 // define for the points subtracted from score for a rearm started on a player.
9600 #define REPAIR_PENALTY          50
9601
9602
9603 // function to clean up ai flags, variables, and other interesting information
9604 // for a ship that was getting repaired.  The how parameter is useful for multiplayer
9605 // only in that it tells us why the repaired ship is being cleaned up.
9606 void ai_do_objects_repairing_stuff( object *repaired_objp, object *repair_objp, int how )
9607 {
9608         ai_info *aip, *repair_aip;
9609         int             stamp = -1;
9610
9611         Assert( repaired_objp->type == OBJ_SHIP);
9612         aip = &Ai_info[Ships[repaired_objp->instance].ai_index];
9613
9614         // multiplayer
9615         int p_index;
9616         p_index = -1;
9617         if(Game_mode & GM_MULTIPLAYER){
9618                 p_index = multi_find_player_by_object(repaired_objp);           
9619         }               
9620         else {          
9621                 if(repaired_objp == Player_obj){
9622                         p_index = Player_num;
9623                 }
9624         }
9625
9626         switch( how ) {
9627         case REPAIR_INFO_BEGIN:
9628                 aip->ai_flags |= AIF_BEING_REPAIRED;
9629                 aip->ai_flags &= ~AIF_AWAITING_REPAIR;
9630                 stamp = timestamp(-1);
9631
9632                 // if this is a player ship, then subtract the repair penalty from this player's score
9633                 if ( repaired_objp->flags & OF_PLAYER_SHIP ) {
9634                         if ( !(Game_mode & GM_MULTIPLAYER) ) {
9635                                 Player->stats.m_score -= (int)(REPAIR_PENALTY * scoring_get_scale_factor());                    // subtract the penalty
9636                         } else {
9637                                 /*
9638                                 int pnum;
9639
9640                                 // multiplayer game -- find the player, then subtract the score
9641                                 pnum = multi_find_player_by_object( repaired_objp );
9642                                 if ( pnum != -1 ) {
9643                                         Net_players[pnum].player->stats.m_score -= (int)(REPAIR_PENALTY * scoring_get_scale_factor());
9644
9645                                         // squad war
9646                                         multi_team_maybe_add_score(-(int)(REPAIR_PENALTY * scoring_get_scale_factor()), Net_players[pnum].p_info.team);
9647                                 } else {
9648                                         nprintf(("Network", "Couldn't find player for ship %s for repair penalty\n", Ships[repaired_objp->instance].ship_name));
9649                                 }
9650                                 */
9651                         }
9652                 }
9653                 break;
9654
9655         case REPAIR_INFO_BROKEN:
9656                 aip->ai_flags &= ~AIF_BEING_REPAIRED;
9657                 aip->ai_flags |= AIF_AWAITING_REPAIR;
9658                 stamp = timestamp((int) ((30 + 10*frand()) * 1000));
9659                 break;
9660
9661         case REPAIR_INFO_END:
9662                 // when only awaiting repair, and the repair is ended, then set dock_objnum to -1.
9663                 if ( aip->ai_flags & AIF_AWAITING_REPAIR ){
9664                         aip->dock_objnum = -1;
9665                 }
9666                 aip->ai_flags &= ~(AIF_AWAITING_REPAIR | AIF_BEING_REPAIRED );
9667                 stamp = timestamp((int) ((30 + 10*frand()) * 1000));
9668                 break;
9669
9670         case REPAIR_INFO_QUEUE:
9671                 aip->ai_flags |= AIF_AWAITING_REPAIR;
9672                 if ( aip == Player_ai ){
9673                         hud_support_view_start();
9674                 }
9675                 stamp = timestamp(-1);
9676                 break;
9677
9678         case REPAIR_INFO_ABORT:
9679         case REPAIR_INFO_KILLED:
9680                 // 5/4/98 -- MWA -- Need to set dock objnum to -1 to let code know this guy who was getting
9681                 // repaired (or queued for repair), isn't really going to be docked with anyone anymore.
9682                 aip->dock_objnum = -1;
9683                 aip->ai_flags &= ~AIF_DOCKED;
9684                 aip->ai_flags &= ~(AIF_AWAITING_REPAIR | AIF_BEING_REPAIRED );
9685                 if (repair_objp != NULL) {
9686                         repair_aip = &Ai_info[Ships[repair_objp->instance].ai_index];
9687                         repair_aip->ai_flags &= ~(AIF_AWAITING_REPAIR | AIF_BEING_REPAIRED );
9688                 }               
9689
9690                 if ( p_index >= 0 ) {
9691                         hud_support_view_abort();
9692
9693                         // send appropriate message to player here
9694                         if ( how == REPAIR_INFO_KILLED ){
9695                                 message_send_builtin_to_player( MESSAGE_SUPPORT_KILLED, NULL, MESSAGE_PRIORITY_HIGH, MESSAGE_TIME_SOON, 0, 0, p_index, -1 );
9696                         } else {
9697                                 if ( repair_objp ){
9698                                         message_send_builtin_to_player( MESSAGE_REPAIR_ABORTED, &Ships[repair_objp->instance], MESSAGE_PRIORITY_NORMAL, MESSAGE_TIME_SOON, 0, 0, p_index, -1 );
9699                                 }
9700                         }
9701                 }
9702
9703                 // add log entry if this is a player
9704                 if ( repaired_objp->flags & OF_PLAYER_SHIP ){
9705                         mission_log_add_entry(LOG_PLAYER_REARM_ABORT, Ships[repaired_objp->instance].ship_name, NULL);
9706                 }
9707
9708                 stamp = timestamp((int) ((30 + 10*frand()) * 1000));
9709                 break;
9710
9711         case REPAIR_INFO_COMPLETE:
9712                 // clear the being repaired flag -- and 
9713                 if ( p_index >= 0 ) {
9714                         Assert( repair_objp );
9715                         
9716                         hud_support_view_stop();                        
9717
9718                         message_send_builtin_to_player(MESSAGE_REPAIR_DONE, &Ships[repair_objp->instance], MESSAGE_PRIORITY_LOW, MESSAGE_TIME_SOON, 0, 0, p_index, -1);
9719                 }
9720                 stamp = timestamp((int) ((30 + 10*frand()) * 1000));
9721                 break;
9722
9723         case REPAIR_INFO_ONWAY:
9724                 // need to set the dock_signature so that clients in multiplayer games rearm correctly
9725                 Assert( repair_objp );
9726                 aip->dock_signature = repair_objp->signature; 
9727                 aip->dock_objnum = OBJ_INDEX(repair_objp);
9728                 stamp = timestamp(-1);
9729                 break;
9730
9731         default:
9732                 Int3();                 // bogus type of repair info
9733         }
9734
9735         if (repair_objp){
9736                 Ai_info[Ships[repair_objp->instance].ai_index].warp_out_timestamp = stamp;
9737         }
9738
9739         // repair_objp might be NULL is we are cleaning up this mode because of the support ship
9740         // getting killed.
9741         if ( repair_objp ) {
9742                 aip = &Ai_info[Ships[repair_objp->instance].ai_index];
9743                 switch ( how ) {
9744                 case REPAIR_INFO_ONWAY:
9745                         Assert( repaired_objp != NULL );
9746                         aip->goal_objnum = OBJ_INDEX(repaired_objp);
9747                         aip->ai_flags |= AIF_REPAIRING;
9748                         break;
9749
9750                 case REPAIR_INFO_BROKEN:
9751                         break;
9752
9753                 case REPAIR_INFO_END:
9754                 case REPAIR_INFO_ABORT:
9755                 case REPAIR_INFO_KILLED:
9756                         if ( how == REPAIR_INFO_ABORT )
9757                                 aip->goal_objnum = -1;
9758
9759                         aip->ai_flags &= ~AIF_REPAIRING;
9760                         break;
9761                         
9762                 case REPAIR_INFO_QUEUE:
9763                         ai_add_rearm_goal( repaired_objp, repair_objp );
9764                         break;
9765
9766                 case REPAIR_INFO_BEGIN:
9767                 case REPAIR_INFO_COMPLETE:
9768                         break;
9769
9770                 default:
9771                         Int3();         // bogus type of repair info
9772                 }
9773         }
9774
9775         multi_maybe_send_repair_info( repaired_objp, repair_objp, how );
9776 }
9777
9778 //      Cleanup AI stuff for when a ship was supposed to dock with another, but the ship
9779 //      it was supposed to dock with is no longer valid.
9780 void ai_cleanup_dock_mode(ai_info *aip, ship *shipp)
9781 {
9782         object *objp;
9783
9784         objp = &Objects[shipp->objnum];
9785         aip->mode = AIM_NONE;
9786
9787         if (aip->ai_flags & AIF_REPAIRING) {
9788                 Assert( aip->goal_objnum != -1 );
9789                 ai_do_objects_repairing_stuff( &Objects[aip->goal_objnum], &Objects[shipp->objnum], REPAIR_INFO_KILLED );
9790         } else if ( aip->ai_flags & AIF_BEING_REPAIRED ) {
9791                 // MWA -- note that we have to use dock_objnum here instead of goal_objnum.
9792                 Assert( aip->dock_objnum != -1 );
9793                 ai_do_objects_repairing_stuff( &Objects[shipp->objnum], &Objects[aip->dock_objnum], REPAIR_INFO_KILLED );
9794         } else if ( aip->ai_flags & AIF_AWAITING_REPAIR ) {
9795                 // need to find the support ship that has me as a goal_objnum
9796                 // MWA -- note that we have to use dock_objnum here instead of goal_objnum.
9797                 // MWA -- 3/38/98  Check to see if this guy is queued for a support ship, or there is already
9798                 // one in the mission
9799                 if ( mission_is_repair_scheduled(objp) ) {
9800                         mission_remove_scheduled_repair( objp );                        // this function will notify multiplayer clients.
9801                 } else {
9802                         if ( aip->dock_objnum != -1 )
9803                                 ai_do_objects_repairing_stuff( objp, &Objects[aip->dock_objnum], REPAIR_INFO_ABORT );
9804                         else
9805                                 ai_do_objects_repairing_stuff( objp, NULL, REPAIR_INFO_ABORT );
9806                 }
9807         }
9808
9809         if ( aip->ai_flags & AIF_DOCKED ) {
9810                 ai_info *other_aip;
9811
9812                 Assert( aip->dock_objnum != -1 );
9813
9814                 // if docked, and the dock_objnum is not undocking, force them to near last stage
9815                 other_aip = &Ai_info[Ships[Objects[aip->dock_objnum].instance].ai_index];
9816                 if ( (other_aip->mode == AIM_DOCK) && (other_aip->submode < AIS_UNDOCK_3) )
9817                         other_aip->submode = AIS_UNDOCK_3;
9818                 ai_do_objects_undocked_stuff( objp, &Objects[aip->dock_objnum] );
9819         }
9820 }
9821
9822 /*
9823 //      Make dockee_objp shake a bit due to docking.
9824 void ai_dock_shake(object *docker_objp, object *dockee_objp)
9825 {
9826         vector  tangles;
9827         matrix  rotmat, tmp;
9828         float           scale;
9829         angles  *ap;
9830
9831         scale = 0.25f;          //      Compute this based on mass and speed at time of docking.
9832
9833         vm_vec_rand_vec_quick(&tangles);
9834         vm_vec_scale(&tangles, scale);
9835
9836         ap = (angles *) &tangles;
9837
9838         vm_angles_2_matrix(&rotmat, ap);
9839         vm_matrix_x_matrix( &tmp, &dockee_objp->orient, &rotmat );
9840         dockee_objp->orient = tmp;
9841
9842         vm_orthogonalize_matrix(&dockee_objp->orient);
9843
9844         dock_orient_and_approach(docker_objp, dockee_objp, DOA_DOCK_STAY);
9845
9846 }
9847 */
9848
9849 //      Make Pl_objp point at aip->goal_point.
9850 void ai_still()
9851 {
9852         ship    *shipp;
9853         ai_info *aip;
9854
9855         Assert(Pl_objp->type == OBJ_SHIP);
9856         Assert((Pl_objp->instance >= 0) && (Pl_objp->instance < MAX_OBJECTS));
9857
9858         shipp = &Ships[Pl_objp->instance];
9859         Assert((shipp->ai_index >= 0) && (shipp->ai_index < MAX_AI_INFO));
9860
9861         aip = &Ai_info[shipp->ai_index];
9862
9863         turn_towards_point(Pl_objp, &aip->goal_point, NULL, 0.0f);
9864 }
9865
9866 //      Make *Pl_objp stay near another ship.
9867 void ai_stay_near()
9868 {
9869         ai_info *aip;
9870         int             goal_objnum;
9871
9872         aip = &Ai_info[Ships[Pl_objp->instance].ai_index];
9873
9874         goal_objnum = aip->goal_objnum;
9875
9876         if ((goal_objnum < 0) || (Objects[goal_objnum].type != OBJ_SHIP) || (Objects[goal_objnum].signature != aip->goal_signature)) {
9877                 aip->mode = AIM_NONE;
9878         } else {
9879                 float           dist, max_dist, scale;
9880                 vector  rand_vec, goal_pos, vec_to_goal;
9881                 object  *goal_objp;
9882
9883                 goal_objp = &Objects[goal_objnum];
9884
9885                 //      Make not all ships pursue same point.
9886                 static_randvec(Pl_objp-Objects, &rand_vec);
9887
9888                 //      Make sure point is in front hemisphere (relative to Pl_objp's position.
9889                 vm_vec_sub(&vec_to_goal, &goal_objp->pos, &Pl_objp->pos);
9890                 if (vm_vec_dot(&rand_vec, &vec_to_goal) > 1.0f) {
9891                         vm_vec_negate(&rand_vec);
9892                 }
9893
9894                 //      Scale the random vector by an amount proportional to the distance from Pl_objp to the true goal.
9895                 dist = vm_vec_dist_quick(&goal_objp->pos, &Pl_objp->pos);
9896                 max_dist = aip->stay_near_distance;
9897                 scale = dist - max_dist/2;
9898                 if (scale < 0.0f)
9899                         scale = 0.0f;
9900
9901                 vm_vec_scale_add(&goal_pos, &goal_objp->pos, &rand_vec, scale);
9902
9903                 if (max_dist < Pl_objp->radius + goal_objp->radius + 25.0f)
9904                         max_dist = Pl_objp->radius + goal_objp->radius + 25.0f;
9905
9906                 if (dist > max_dist) {
9907                         turn_towards_point(Pl_objp, &goal_pos, NULL, 0.0f);
9908                         accelerate_ship(aip, dist / max_dist - 0.8f);
9909                 }
9910         
9911         }
9912
9913 }
9914
9915 //      Warn player if dock path is obstructed.
9916 int maybe_dock_obstructed(object *cur_objp, object *goal_objp, int big_only_flag)
9917 {
9918         vector  *goalpos, *curpos;
9919         float           radius;
9920         ai_info *aip;
9921         int             collide_objnum;
9922
9923         aip = &Ai_info[Ships[cur_objp->instance].ai_index];
9924
9925         Ai_info[Ships[goal_objp->instance].ai_index].ai_flags &= ~AIF_REPAIR_OBSTRUCTED;
9926
9927         if (goal_objp != Player_obj)
9928                 return -1;
9929
9930         curpos = &cur_objp->pos;
9931         radius = cur_objp->radius;
9932         goalpos = &Path_points[aip->path_cur].pos;
9933         collide_objnum = pp_collide_any(curpos, goalpos, radius, cur_objp, goal_objp, big_only_flag);
9934
9935         if (collide_objnum != -1)
9936                 Ai_info[Ships[goal_objp->instance].ai_index].ai_flags |= AIF_REPAIR_OBSTRUCTED;
9937
9938         return collide_objnum;
9939 }
9940
9941
9942 int Dock_path_warning_given = 0;
9943
9944 //      Docking behavior.
9945 //      Approach a ship, follow path to docking platform, approach platform, after awhile,
9946 //      undock.
9947 void ai_dock()
9948 {
9949         ship                    *shipp = &Ships[Pl_objp->instance];
9950         ai_info         *aip = &Ai_info[shipp->ai_index];
9951         object          *goal_objp;
9952         ship_info       *sip = &Ship_info[shipp->ship_info_index];
9953
9954         //      Make sure object we're supposed to dock with still exists.
9955         if ((aip->goal_objnum == -1) || (Objects[aip->goal_objnum].signature != aip->goal_signature)) {
9956                 ai_cleanup_dock_mode(aip, shipp);
9957                 return;
9958         }
9959
9960         goal_objp = &Objects[aip->goal_objnum];
9961
9962         //      For docking submodes (ie, not undocking), follow path.  Once at second last
9963         //      point on path (point just before point on dock platform), orient into position.
9964         // For undocking, first mode pushes docked ship straight back from docking point
9965         // second mode turns ship and moves to point on docking radius
9966         switch (aip->submode) {
9967
9968                 //      This mode means to find the path to the docking point.
9969         case AIS_DOCK_0:
9970                 //aip->path_start = -1;
9971                 //nprintf(("AI", "Time = %7.3f, submode = %i\n", f2fl(Missiontime), aip->submode));
9972                 ai_path();
9973                 if (!Dock_path_warning_given && (aip->path_length < 4)) {
9974                         Warning( LOCATION, "Ship '%s' has only %i points on dock path.  Docking will look strange.  Contact Adam.", shipp->ship_name, aip->path_length );
9975                         Dock_path_warning_given = 1;            //      This is on a mission-wide basis, but it's just a hack for now...
9976                 }
9977
9978                 aip->submode = AIS_DOCK_1;
9979                 aip->path_start = -1;
9980                 aip->submode_start_time = Missiontime;
9981                 break;
9982
9983                 //      This mode means to follow the path until just before the end.
9984         case AIS_DOCK_1: {
9985                 float   dist;
9986                 int     r;
9987
9988                 if ((r = maybe_dock_obstructed(Pl_objp, goal_objp, 1)) != -1) {
9989                         int     r1;
9990                         if ((r1 = maybe_avoid_big_ship(Pl_objp, goal_objp, aip, &goal_objp->pos, 7.0f)) != 0) {
9991                                 nprintf(("AI", "Support ship %s avoiding large ship %s\n", Ships[Pl_objp->instance].ship_name, Ships[Objects[r1].instance].ship_name));
9992                                 break;
9993                         } /*else {
9994                                 nprintf(("AI", "Dock 1: Obstructed by %s\n", Ships[Objects[r].instance].ship_name));
9995                                 accelerate_ship(aip, 0.0f);
9996                                 aip->submode = AIS_DOCK_0;
9997                         } */
9998                 } //else {
9999                 {
10000                         dist = ai_path();
10001                         //nprintf(("AI", "Time = %7.3f, submode = %i\n", f2fl(Missiontime), aip->submode));
10002                         //nprintf(("AI", "Dock 1: Frame: %i, goal point = %i, dist = %7.3f\n", Framecount, aip->path_cur-aip->path_start, dist));
10003
10004                         if (aip->path_cur-aip->path_start >= aip->path_length-1) {              //      If got this far, advance no matter what.
10005                                 aip->submode = AIS_DOCK_2;
10006                                 aip->submode_start_time = Missiontime;
10007                                 aip->path_cur--;
10008                                 Assert(aip->path_cur-aip->path_start >= 0);
10009                         } else if (aip->path_cur-aip->path_start >= aip->path_length-2) {
10010                                 if (Pl_objp->phys_info.speed > goal_objp->phys_info.speed + 1.5f) {
10011                                         set_accel_for_target_speed(Pl_objp, goal_objp->phys_info.speed);
10012                                 } else {
10013                                         aip->submode = AIS_DOCK_2;
10014                                         aip->submode_start_time = Missiontime;
10015                                 }
10016                         }
10017                 }
10018                 break;
10019                                           }
10020         //      This mode means to drag oneself right to the second last point on the path.
10021         //      Path code allows it to overshoot.
10022         case AIS_DOCK_2: {
10023                 float           dist;
10024                 int     r;
10025
10026                 if ((r = maybe_dock_obstructed(Pl_objp, goal_objp,0)) != -1) {
10027                         nprintf(("AI", "Dock 2: Obstructed by %s\n", Ships[Objects[r].instance].ship_name));
10028                         accelerate_ship(aip, 0.0f);
10029                         aip->submode = AIS_DOCK_1;
10030                 } else {
10031                         //nprintf(("AI", "Time = %7.3f, submode = %i\n", f2fl(Missiontime), aip->submode));
10032                         dist = dock_orient_and_approach(Pl_objp, &Objects[aip->goal_objnum], DOA_APPROACH);
10033                         Assert(dist != UNINITIALIZED_VALUE);
10034
10035                         if (dist == DOCK_BACKUP_RETURN_VAL) {
10036                                 int path_num;
10037                                 aip->submode = AIS_DOCK_1;
10038                                 path_num = ai_return_path_num_from_dockbay(&Objects[aip->goal_objnum], aip->dockee_index);
10039                                 Assert(aip->goal_objnum >= 0 && aip->goal_objnum < MAX_OBJECTS);
10040                                 ai_find_path(Pl_objp, aip->goal_objnum, path_num, 0);
10041                                 break;
10042                         }
10043
10044                         //nprintf(("AI", "Dock 2: dist = %7.3f\n", vm_vec_dist_quick(&Pl_objp->pos, &goal_point)));
10045                         float   tolerance;
10046                         if (Objects[aip->goal_objnum].flags & OF_PLAYER_SHIP)
10047                                 tolerance = 6*flFrametime + 1.0f;
10048                         else
10049                                 tolerance = 4*flFrametime + 0.5f;
10050
10051                         if ( dist < tolerance) {
10052                                 aip->submode = AIS_DOCK_3;
10053                                 aip->submode_start_time = Missiontime;
10054                                 aip->path_cur++;
10055                         }
10056                 }
10057                 break;
10058                                                   }
10059
10060         case AIS_DOCK_3:
10061         case AIS_DOCK_3A:
10062                 {
10063                 Assert(aip->goal_objnum != -1);
10064                 int     r;
10065
10066                 if ((r = maybe_dock_obstructed(Pl_objp, goal_objp,0)) != -1) {
10067                         nprintf(("AI", "Dock 1: Obstructed by %s\n", Ships[Objects[r].instance].ship_name));
10068                         accelerate_ship(aip, 0.0f);
10069                         aip->submode = AIS_DOCK_2;
10070                 } else {
10071
10072                         //nprintf(("AI", "Time = %7.3f, submode = %i\n", f2fl(Missiontime), aip->submode));
10073                         float dist = dock_orient_and_approach(Pl_objp, &Objects[aip->goal_objnum], DOA_DOCK);
10074                         Assert(dist != UNINITIALIZED_VALUE);
10075
10076                         if (dist == DOCK_BACKUP_RETURN_VAL) {
10077                                 aip->submode = AIS_DOCK_2;
10078                                 break;
10079                         }
10080
10081                         //nprintf(("AI", "Dock 3: dist = %7.3f\n", dist));
10082
10083                         if (dist < 2*flFrametime * (1.0f + fl_sqrt(goal_objp->phys_info.speed))) {
10084                                 // - Removed by MK on 11/7/97, causes errors for ships docked at mission start: maybe_recreate_path(Pl_objp, aip, 1);
10085                                 dist = dock_orient_and_approach(Pl_objp, &Objects[aip->goal_objnum], DOA_DOCK);
10086                                 Assert(dist != UNINITIALIZED_VALUE);
10087
10088                                 physics_ship_init(Pl_objp);
10089
10090                                 ai_do_objects_docked_stuff( Pl_objp, goal_objp );
10091
10092                                 if (aip->submode == AIS_DOCK_3) {
10093                                         snd_play_3d( &Snds[SND_DOCK_ATTACH], &Pl_objp->pos, &View_position );
10094                                         hud_maybe_flash_docking_text(Pl_objp);
10095                                         // ai_dock_shake(Pl_objp, goal_objp);
10096
10097                                         if ((Pl_objp == Player_obj) || (goal_objp == Player_obj))
10098                                                 joy_ff_docked();  // shake player's joystick a little
10099                                 }
10100
10101                                 //      If this ship is repairing another ship...
10102                                 if (aip->ai_flags & AIF_REPAIRING) {
10103                                         aip->submode = AIS_DOCK_4;                      //      Special rearming only dock mode.
10104                                         aip->submode_start_time = Missiontime;
10105                                 } else {
10106                                         aip->submode = AIS_DOCK_4A;
10107                                         aip->submode_start_time = Missiontime;
10108                                 }
10109                         }
10110                 }
10111                 break;
10112                 }
10113
10114                 //      Yes, we just sit here.  We wait for further orders.  No, it's not a bug.
10115         case AIS_DOCK_4A:
10116                 //nprintf(("AI", "Time = %7.3f, submode = %i\n", f2fl(Missiontime), aip->submode));
10117                 //nprintf(("AI", "."));
10118                 if (aip->active_goal >= 0) {
10119                         mission_log_add_entry(LOG_SHIP_DOCK, Ships[Pl_objp->instance].ship_name, Ships[goal_objp->instance].ship_name);
10120
10121                         if (aip->goals[aip->active_goal].ai_mode == AI_GOAL_DOCK) {
10122                                 ai_mission_goal_complete( aip );                                        // Note, this calls ai_set_default_behavior().
10123                         } 
10124                 } else {        //      Can happen for initially docked ships.
10125                         ai_do_default_behavior( &Objects[Ships[aip->shipnum].objnum] );         // do the default behavior
10126                 }
10127                 
10128                 break;
10129
10130         case AIS_DOCK_4: {
10131                 //      This mode is only for rearming/repairing.
10132                 //      The ship that is performing the rearm enters this mode after it docks.
10133                 Assert((aip->goal_objnum >= -1) && (aip->goal_objnum < MAX_OBJECTS));
10134
10135                 //nprintf(("AI", "Time = %7.3f, submode = %i\n", f2fl(Missiontime), aip->submode));
10136                 float dist = dock_orient_and_approach(Pl_objp, &Objects[aip->goal_objnum], DOA_DOCK);
10137                 Assert(dist != UNINITIALIZED_VALUE);
10138
10139                 object  *goal_objp = &Objects[aip->goal_objnum];
10140                 Assert(goal_objp->type == OBJ_SHIP);
10141                 ship                    *goal_shipp = &Ships[goal_objp->instance];              
10142                 ai_info         *goal_aip = &Ai_info[goal_shipp->ai_index];
10143
10144                 //nprintf(("AI", "Dock 4: dist = %7.3f\n", dist));
10145
10146                 //      Make sure repair has not broken off.
10147                 if (dist > 5.0f) {      //      Oops, too far away!
10148                         if ( goal_aip->ai_flags & AIF_BEING_REPAIRED )
10149                                 ai_do_objects_repairing_stuff( goal_objp, Pl_objp, REPAIR_INFO_BROKEN);
10150
10151                         if (dist > Pl_objp->radius*2 + goal_objp->radius*2) {
10152                                 //      Got real far away from goal, so move back a couple modes and try again.
10153                                 aip->submode = AIS_DOCK_2;
10154                                 aip->submode_start_time = Missiontime;
10155                         }
10156                 } else {
10157                         if ( goal_aip->ai_flags & AIF_AWAITING_REPAIR )
10158                                 ai_do_objects_repairing_stuff( goal_objp, Pl_objp, REPAIR_INFO_BEGIN );
10159                 }
10160
10161                 break;
10162                                                   }
10163
10164         case AIS_UNDOCK_0: {
10165                 int path_num;
10166                 //      First stage of undocking.
10167
10168                 //nprintf(("AI", "Undock 0:\n"));
10169
10170                 aip->submode = AIS_UNDOCK_1;
10171                 aip->submode_start_time = Missiontime;
10172                 if (aip->dock_objnum == -1) {
10173                         aip->submode = AIS_UNDOCK_3;
10174                 } else {
10175
10176                         // set up the path points for the undocking procedure.  dock_path_index member should
10177                         // have gotten set in the docking code.
10178                         Assert( aip->dock_path_index != -1 );
10179                         path_num = ai_return_path_num_from_dockbay(goal_objp, aip->dock_path_index);
10180                         ai_find_path(Pl_objp, goal_objp-Objects, path_num, 0);
10181
10182                         // Play a ship docking detach sound
10183                         snd_play_3d( &Snds[SND_DOCK_DETACH], &Pl_objp->pos, &View_position );
10184                 }
10185                 break;
10186                                                          }
10187         case AIS_UNDOCK_1: {
10188                 //      Using thrusters, exit from dock station to nearest next dock path point.
10189                 float   dist;
10190                 
10191                 //nprintf(("AI", "Undock 1: time in this mode = %7.3f\n", f2fl(Missiontime - aip->submode_start_time)));
10192
10193                 if (Missiontime - aip->submode_start_time < REARM_BREAKOFF_DELAY) {
10194                         break;          //      Waiting for one second to elapse to let detach sound effect play out.
10195                 }
10196                 else {  // AL - added 05/16/97.  Hack to play depart sound.  Will probably take out.
10197                                         // Assumes that the submode_start_time is not used for AIS_UNDOCK_1 anymore
10198                         if ( aip->submode_start_time != 0 )
10199                                 snd_play_3d( &Snds[SND_DOCK_DEPART], &Pl_objp->pos, &View_position );
10200                         aip->submode_start_time = 0;
10201                 }
10202
10203                 dist = dock_orient_and_approach(Pl_objp, &Objects[aip->goal_objnum], DOA_UNDOCK_1);
10204                 Assert(dist != UNINITIALIZED_VALUE);
10205
10206                 float dist_to_dock_obj = vm_vec_dist_quick(&Pl_objp->pos, &Objects[aip->goal_objnum].pos);
10207
10208                 //      Move to within 0.1 units of second last point on path before orienting, or just plain far away from docked-to ship.
10209                 //      This allows undock to complete if first ship flies away.
10210                 if ((dist < 2*flFrametime) || (dist_to_dock_obj > 2*Pl_objp->radius)) {
10211                         aip->submode = AIS_UNDOCK_2;
10212                         aip->submode_start_time = Missiontime;
10213                 }
10214                 break;
10215                                                          }
10216         case AIS_UNDOCK_2: {
10217                 float dist;
10218                 ai_info *other_aip;
10219
10220                 // get pointer to docked object's aip to reset flags, etc
10221                 Assert( aip->dock_objnum != -1 );
10222                 other_aip = &Ai_info[Ships[Objects[aip->dock_objnum].instance].ai_index];
10223
10224                 //      Second stage of undocking.
10225                 dist = dock_orient_and_approach(Pl_objp, &Objects[aip->goal_objnum], DOA_UNDOCK_2);
10226                 Assert(dist != UNINITIALIZED_VALUE);
10227
10228
10229                 //nprintf(("AI", "Undock 2: dist = %7.3f\n", dist));
10230                 
10231                 //      If at goal point, or quite far away from dock object
10232                 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) ) {
10233                         // reset the dock flags.  If rearm/repair, reset rearm repair flags for those ships as well.
10234                         if ( sip->flags & SIF_SUPPORT ) {
10235                                 ai_do_objects_repairing_stuff( &Objects[aip->dock_objnum], Pl_objp, REPAIR_INFO_END );
10236                         }
10237
10238                         // clear out flags for AIF_DOCKED for both objects.
10239                         ai_do_objects_undocked_stuff( Pl_objp, goal_objp );
10240                         physics_ship_init(Pl_objp);
10241                         aip->submode = AIS_UNDOCK_3;                            //      The do-nothing mode, until another order is issued
10242
10243                         //aip->ai_flags &= ~AIF_DOCKED;         //      @MK, 9/18/97
10244                         //other_aip->ai_flags &= ~AIF_DOCKED;
10245                         //aip->dock_objnum = -1;                                        // invalidate who obj is docked with
10246                         //other_aip->dock_objnum = -1;                  // MWA 10/07/97 invalide docked objects dock_objnum value as well
10247
10248                         // don't add undock log entries for support ships.
10249                         if ( !(sip->flags & SIF_SUPPORT) )
10250                                 mission_log_add_entry(LOG_SHIP_UNDOCK, Ships[Pl_objp->instance].ship_name, Ships[goal_objp->instance].ship_name);
10251
10252                 }
10253                 break;
10254                 }
10255         case AIS_UNDOCK_3: {
10256                 float dist = dock_orient_and_approach(Pl_objp, &Objects[aip->goal_objnum], DOA_UNDOCK_3);
10257                 Assert(dist != UNINITIALIZED_VALUE);
10258
10259                 if (dist < Pl_objp->radius/2 + 5.0f) {
10260                         aip->submode = AIS_UNDOCK_4;
10261                 }
10262
10263                 // possible that this flag hasn't been cleared yet.  When aborting a rearm, this submode might
10264                 // be entered directly.
10265                 if ( (sip->flags & SIF_SUPPORT) && (aip->ai_flags & AIF_REPAIRING) ) {
10266                         ai_do_objects_repairing_stuff( &Objects[aip->goal_objnum], Pl_objp, REPAIR_INFO_ABORT );
10267                 }
10268
10269                 break;
10270                                                  }
10271         case AIS_UNDOCK_4: {
10272                 ai_info *other_aip;
10273
10274                 // MWA 10/07/97  I'm slightly confused by the dual use of goal_objnum and dock_objnum.  Seems to me
10275                 // that goal_objnum and dock_objnum are the same through this whole docking/undocking process, although
10276                 // I could be wrong.  dock_objnum was reset in undock_2 submode so try to use goal_objnum here to
10277                 // get other ships ai_info pointer
10278                 Assert( aip->goal_objnum != -1 );
10279                 other_aip = &Ai_info[Ships[Objects[aip->goal_objnum].instance].ai_index];
10280
10281                 aip->mode = AIM_NONE;
10282                 aip->dock_path_index = -1;              // invalidate the docking path index
10283
10284                 // these flags should have been cleared long ago!
10285                 // Get Allender if you hit one of these!!!!!
10286                 // removed by allender on 2/16 since a ship may be docked with some other ship, but still be the
10287                 // goal_objnum of this ship ending it's undocking mode.
10288                 //Assert( !(aip->ai_flags & AIF_DOCKED) );
10289                 //Assert( !(other_aip->ai_flags & AIF_DOCKED) );
10290                 //Assert( !(aip->ai_flags & AIF_REPAIRING) );
10291                 //Assert( !(other_aip->ai_flags & AIF_BEING_REPAIRED) );
10292                 //Assert( !(other_aip->ai_flags & AIF_AWAITING_REPAIR) );
10293
10294                 // only call mission goal complete if this was indeed an undock goal
10295                 if ( aip->active_goal > -1 ) {
10296                         if ( aip->goals[aip->active_goal].ai_mode == AI_GOAL_UNDOCK )
10297                                 ai_mission_goal_complete( aip );                        // this call should reset the AI mode
10298                         //else
10299                         //      aip->active_goal = -1;                                          // this ensures that this ship might get new goal
10300                 }
10301
10302                 break;
10303                                                          }
10304         default:
10305                 Int3(); //      Error, bogus submode
10306         }
10307
10308 }
10309
10310 // TURRET BEGIN
10311
10312 //      Given an object and a turret on that object, return the global position and forward vector
10313 //      of the turret.   The gun normal is the unrotated gun normal, (the center of the FOV cone), not
10314 // the actual gun normal given using the current turret heading.  But it _is_ rotated into the model's orientation
10315 //      in global space.
10316 void ship_get_global_turret_info(object *objp, model_subsystem *tp, vector *gpos, vector *gvec)
10317 {
10318         matrix  m;
10319         vm_copy_transpose_matrix(&m, &objp->orient);
10320 //      vm_vec_rotate(gpos, &tp->turret_avg_firing_point, &m);
10321         vm_vec_rotate(gpos, &tp->pnt, &m);
10322         vm_vec_add2(gpos, &objp->pos);
10323         vm_vec_rotate(gvec, &tp->turret_norm, &m);      
10324 }
10325
10326 // Given an object and a turret on that object, return the actual firing point of the gun
10327 // and its normal.   This uses the current turret angles.  We are keeping track of which
10328 // gun to fire next in the ship specific info for this turret subobject.  Use this info
10329 // to determine which position to fire from next.
10330 //      Stuffs:
10331 //              *gpos: absolute position of gun firing point
10332 //              *gvec: vector fro *gpos to *targetp
10333 void ship_get_global_turret_gun_info(object *objp, ship_subsys *ssp, vector *gpos, vector *gvec, int use_angles, vector *targetp)
10334 {
10335         vector * gun_pos;
10336         model_subsystem *tp = ssp->system_info;
10337
10338         ship_model_start(objp);
10339
10340         gun_pos = &tp->turret_firing_point[ssp->turret_next_fire_pos % tp->turret_num_firing_points];
10341
10342         model_find_world_point(gpos, gun_pos, tp->model_num, tp->turret_gun_sobj, &objp->orient, &objp->pos );
10343
10344         if (use_angles)
10345                 model_find_world_dir(gvec, &tp->turret_norm, tp->model_num, tp->turret_gun_sobj, &objp->orient, &objp->pos );
10346         else {
10347                 //vector        gun_pos2;
10348                 //vm_vec_add(&gun_pos2, gpos, gun_pos);
10349                 vm_vec_normalized_dir(gvec, targetp, gpos);
10350         }
10351
10352         ship_model_stop(objp);  
10353 }
10354
10355 //      Rotate a turret towards an enemy.
10356 //      Return TRUE if caller should use angles in subsequent rotations.
10357 //      Some obscure model thing only John Slagel knows about.
10358 //      Sets predicted enemy position.
10359 //      If the turret (*ss) has a subsystem targeted, the subsystem is used as the predicted point.
10360 int aifft_rotate_turret(ship *shipp, int parent_objnum, ship_subsys *ss, object *objp, object *lep, vector *predicted_enemy_pos, vector *gvec)
10361 {
10362         if (ss->turret_enemy_objnum != -1)      {
10363                 model_subsystem *tp = ss->system_info;
10364                 vector  gun_pos, gun_vec;
10365                 float           weapon_speed;
10366                 float           weapon_system_strength;
10367
10368                 //      weapon_system_strength scales time enemy in range in 0..1.  So, the lower this is, the worse the aiming will be.
10369                 weapon_system_strength = ship_get_subsystem_strength(shipp, SUBSYSTEM_WEAPONS);
10370
10371                 ship_get_global_turret_info(&Objects[parent_objnum], tp, &gun_pos, &gun_vec);
10372
10373                 weapon_speed = Weapon_info[tp->turret_weapon_type].max_speed;
10374                 float weapon_travel_dist = weapon_speed * Weapon_info[tp->turret_weapon_type].lifetime;
10375
10376                 vector  enemy_point;
10377                 if (ss->targeted_subsys != NULL) {
10378                         if (ss->turret_enemy_objnum != -1) {
10379                                 vm_vec_unrotate(&enemy_point, &ss->targeted_subsys->system_info->pnt, &Objects[ss->turret_enemy_objnum].orient);
10380                                 vm_vec_add2(&enemy_point, &Objects[ss->turret_enemy_objnum].pos);
10381                         }
10382                 } else {
10383                         if ((lep->type == OBJ_SHIP) && (Ship_info[Ships[lep->instance].ship_info_index].flags & (SIF_BIG_SHIP | SIF_HUGE_SHIP))) {
10384                                 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));
10385                         } else {
10386                                 enemy_point = lep->pos;
10387                         }
10388                 }
10389
10390                 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);
10391
10392                 if (weapon_system_strength < 0.7f) {
10393                         vector  rand_vec;
10394
10395                         static_randvec(Missiontime >> 18, &rand_vec);   //      Return same random number for two seconds.
10396                         //      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.
10397                         vm_vec_scale_add2(predicted_enemy_pos, &rand_vec, (1.0f - weapon_system_strength)*1.5f * lep->radius);
10398                 }
10399
10400                 vector  v2e;
10401                 vm_vec_normalized_dir(&v2e, predicted_enemy_pos, &gun_pos);
10402                 if (vm_vec_dot(&v2e, gvec) > tp->turret_fov) {
10403                         int     rval;
10404
10405                         rval = model_rotate_gun(shipp->modelnum, ss->system_info, &Objects[parent_objnum].orient, 
10406                                 &ss->submodel_info_1.angs, &ss->submodel_info_2.angs,
10407                                 &Objects[parent_objnum].pos, predicted_enemy_pos);
10408                 }
10409         }
10410
10411         return 0;
10412 }
10413
10414 //      Determine if subsystem *enemy_subsysp is hittable from objp.
10415 //      If so, return dot product of vector from point abs_gunposp to *enemy_subsysp
10416 float   aifft_compute_turret_dot(object *objp, object *enemy_objp, vector *abs_gunposp, ship_subsys *turret_subsysp, ship_subsys *enemy_subsysp)
10417 {
10418         float   dot_out;
10419         vector  subobj_pos, vector_out;
10420
10421         vm_vec_unrotate(&subobj_pos, &enemy_subsysp->system_info->pnt, &enemy_objp->orient);
10422         vm_vec_add2(&subobj_pos, &enemy_objp->pos);
10423
10424         if (ship_subsystem_in_sight(enemy_objp, enemy_subsysp, abs_gunposp, &subobj_pos, 1, &dot_out, &vector_out)) {
10425                 vector  turret_norm;
10426
10427                 vm_vec_rotate(&turret_norm, &turret_subsysp->system_info->turret_norm, &objp->orient);
10428                 return vm_vec_dot(&turret_norm, &vector_out);
10429         } else
10430                 return -1.0f;
10431
10432 }
10433
10434 #define MAX_AIFFT_TURRETS                       60
10435 ship_subsys *aifft_list[MAX_AIFFT_TURRETS];
10436 float aifft_rank[MAX_AIFFT_TURRETS];
10437 int aifft_list_size = 0;
10438 int aifft_max_checks = 5;
10439 DCF(mf, "")
10440 {
10441         dc_get_arg(ARG_INT);
10442         aifft_max_checks = Dc_arg_int;
10443 }
10444
10445
10446 //      Pick a subsystem to attack on enemy_objp.
10447 //      Only pick one if enemy_objp is a big ship or a capital ship.
10448 //      Returns dot product from turret to subsystem in *dot_out
10449 ship_subsys *aifft_find_turret_subsys(object *objp, ship_subsys *ssp, object *enemy_objp, float *dot_out)
10450 {
10451         ship    *eshipp, *shipp;
10452         ship_info       *esip;
10453         ship_subsys     *best_subsysp = NULL;
10454         float dot;
10455
10456         Assert(enemy_objp->type == OBJ_SHIP);
10457
10458         eshipp = &Ships[enemy_objp->instance];
10459         esip = &Ship_info[eshipp->ship_info_index];
10460
10461         shipp = &Ships[objp->instance];
10462
10463         float   best_dot = 0.0f;
10464         *dot_out = best_dot;
10465
10466         //      Compute absolute gun position.
10467         vector  abs_gun_pos;
10468         vm_vec_unrotate(&abs_gun_pos, &ssp->system_info->pnt, &objp->orient);
10469         vm_vec_add2(&abs_gun_pos, &objp->pos);
10470
10471         //      Only pick a turret to attack on large ships.
10472         if (!(esip->flags & (SIF_BIG_SHIP | SIF_HUGE_SHIP)))
10473                 return best_subsysp;
10474
10475         // Make sure big or huge ship *actually* has subsystems  (ie, knossos)
10476         if (esip->n_subsystems == 0) {
10477                 return best_subsysp;
10478         }
10479
10480         // first build up a list subsystems to traverse
10481         ship_subsys     *pss;
10482         aifft_list_size = 0;
10483         for ( pss = GET_FIRST(&eshipp->subsys_list); pss !=END_OF_LIST(&eshipp->subsys_list); pss = GET_NEXT(pss) ) {
10484                 model_subsystem *psub = pss->system_info;
10485
10486                 // if we've reached max turrets bail
10487                 if(aifft_list_size >= MAX_AIFFT_TURRETS){
10488                         break;
10489                 }
10490
10491                 // Don't process destroyed objects
10492                 if ( pss->current_hits <= 0.0f ){
10493                         continue;
10494                 }
10495                 
10496                 switch (psub->type) {
10497                 case SUBSYSTEM_WEAPONS:
10498                         aifft_list[aifft_list_size] = pss;
10499                         aifft_rank[aifft_list_size++] = 1.4f;
10500                         break;
10501
10502                 case SUBSYSTEM_TURRET:
10503                         aifft_list[aifft_list_size] = pss;
10504                         aifft_rank[aifft_list_size++] = 1.2f;
10505                         break;
10506
10507                 case SUBSYSTEM_SENSORS:
10508                 case SUBSYSTEM_ENGINE:
10509                         aifft_list[aifft_list_size] = pss;
10510                         aifft_rank[aifft_list_size++] = 1.0f;
10511                         break;
10512                 }
10513         }
10514
10515         // DKA:  6/28/99 all subsystems can be destroyed.
10516         //Assert(aifft_list_size > 0);
10517         if (aifft_list_size == 0) {
10518                 return best_subsysp;
10519         }
10520
10521         // determine a stride value so we're not checking too many turrets
10522         int stride = aifft_list_size > aifft_max_checks ? aifft_list_size / aifft_max_checks : 1;
10523         if(stride <= 0){
10524                 stride = 1;
10525         }
10526         int offset = (int)frand_range(0.0f, (float)(aifft_list_size % stride));
10527         int idx;
10528         for(idx=offset; idx<aifft_list_size; idx+=stride){
10529                 dot = aifft_compute_turret_dot(objp, enemy_objp, &abs_gun_pos, ssp, aifft_list[idx]);                   
10530
10531                 if (dot* aifft_rank[idx] > best_dot) {
10532                         best_dot = dot*aifft_rank[idx];
10533                         best_subsysp = aifft_list[idx];
10534                 }
10535         }
10536
10537         Assert(best_subsysp != &eshipp->subsys_list);
10538
10539         *dot_out = best_dot;
10540         return best_subsysp;
10541 }
10542
10543 // Set active weapon for turret
10544 void ai_turret_select_default_weapon(ship_subsys *turret)
10545 {
10546         ship_weapon *twp;
10547
10548         twp = &turret->weapons;
10549
10550         // If a primary weapon is available, select it
10551         if ( twp->num_primary_banks > 0 ) {
10552                 turret->system_info->turret_weapon_type = twp->primary_bank_weapons[0];
10553         } else if ( twp->num_secondary_banks > 0 ) {
10554                 turret->system_info->turret_weapon_type = twp->secondary_bank_weapons[0];
10555         }
10556 }
10557
10558 // return !0 if the specified target should scan for a new target, otherwise return 0
10559 int turret_should_pick_new_target(ship_subsys *turret)
10560 {
10561 //      int target_type;
10562
10563         if ( timestamp_elapsed(turret->turret_next_enemy_check_stamp) ) {
10564                 return 1;
10565         }
10566
10567         return 0;
10568
10569 /*
10570         if ( turret->turret_enemy_objnum == -1 ) {
10571                 return 1;
10572         }
10573                 
10574         target_type = Objects[turret->turret_enemy_objnum].type;
10575         if ( (target_type != OBJ_SHIP) && (target_type != OBJ_ASTEROID) ) {
10576                 return 1;
10577         }
10578
10579         return 0;
10580 */
10581 }
10582
10583 // Set the next fire timestamp for a turret, based on weapon type and ai class
10584 void turret_set_next_fire_timestamp(ship_subsys *turret, ai_info *aip)
10585 {
10586         float   wait;
10587         int     weapon_id;
10588
10589         weapon_id = turret->system_info->turret_weapon_type;
10590
10591         wait = Weapon_info[weapon_id].fire_wait * 1000.0f;
10592
10593         // make side even for team vs. team
10594         if ((Game_mode & GM_MULTIPLAYER) && (Netgame.type_flags & NG_TYPE_TEAM)) {
10595                 // flak guns need to fire more rapidly
10596                 if (Weapon_info[weapon_id].wi_flags & WIF_FLAK) {
10597                         wait *= Ship_fire_delay_scale_friendly[Game_skill_level] * 0.5f;
10598                         wait += (Num_ai_classes - aip->ai_class - 1) * 40.0f;
10599                 } else {
10600                         wait *= Ship_fire_delay_scale_friendly[Game_skill_level];
10601                         wait += (Num_ai_classes - aip->ai_class - 1) * 100.0f;
10602                 }
10603         } else {
10604                 // flak guns need to fire more rapidly
10605                 if (Weapon_info[weapon_id].wi_flags & WIF_FLAK) {
10606                         if (Ships[aip->shipnum].team == TEAM_FRIENDLY) {
10607                                 wait *= Ship_fire_delay_scale_friendly[Game_skill_level] * 0.5f;
10608                         } else {
10609                                 wait *= Ship_fire_delay_scale_hostile[Game_skill_level] * 0.5f;
10610                         }       
10611                         wait += (Num_ai_classes - aip->ai_class - 1) * 40.0f;
10612
10613                 } else if (Weapon_info[weapon_id].wi_flags & WIF_HUGE) {
10614                         // make huge weapons fire independently of team
10615                         wait *= Ship_fire_delay_scale_friendly[Game_skill_level];
10616                         wait += (Num_ai_classes - aip->ai_class - 1) * 100.0f;
10617                 } else {
10618                         // give team friendly an advantage
10619                         if (Ships[aip->shipnum].team == TEAM_FRIENDLY) {
10620                                 wait *= Ship_fire_delay_scale_friendly[Game_skill_level];
10621                         } else {
10622                                 wait *= Ship_fire_delay_scale_hostile[Game_skill_level];
10623                         }       
10624                         wait += (Num_ai_classes - aip->ai_class - 1) * 100.0f;
10625                 }
10626         }
10627
10628         // vary wait time +/- 10%
10629         wait *= frand_range(0.9f, 1.1f);
10630         turret->turret_next_fire_stamp = timestamp((int) wait);
10631 }
10632
10633 // Decide  if a turret should launch an aspect seeking missile
10634 int turret_should_fire_aspect(ship_subsys *turret, float dot, int weapon_class)
10635 {
10636         weapon_info *wip;
10637
10638         wip = &Weapon_info[weapon_class];
10639
10640         if ( (dot > AICODE_TURRET_DUMBFIRE_ANGLE) && (turret->turret_time_enemy_in_range >= min(wip->min_lock_time,AICODE_TURRET_MAX_TIME_IN_RANGE)) ) {
10641                 return 1;
10642         }
10643
10644         return 0;
10645 }
10646
10647 // Update how long current target has been in this turrets range
10648 void turret_update_enemy_in_range(ship_subsys *turret, float seconds)
10649 {
10650         turret->turret_time_enemy_in_range += seconds;
10651
10652         if ( turret->turret_time_enemy_in_range < 0.0f ) {
10653                 turret->turret_time_enemy_in_range = 0.0f;
10654         }
10655
10656         if ( turret->turret_time_enemy_in_range > AICODE_TURRET_MAX_TIME_IN_RANGE ) {
10657                 turret->turret_time_enemy_in_range = AICODE_TURRET_MAX_TIME_IN_RANGE;
10658         }
10659 }
10660
10661
10662
10663 // Fire a weapon from a turret
10664 void turret_fire_weapon(ship_subsys *turret, int parent_objnum, vector *turret_pos, vector *turret_fvec, vector *predicted_pos = NULL)
10665 {
10666         matrix  turret_orient;
10667         int             turret_weapon_class, weapon_objnum;
10668         ai_info *parent_aip;
10669         ship            *parent_ship;
10670         beam_fire_info fire_info;
10671         float flak_range = 0.0f;
10672
10673         parent_aip = &Ai_info[Ships[Objects[parent_objnum].instance].ai_index];
10674         parent_ship = &Ships[Objects[parent_objnum].instance];
10675         turret_weapon_class = turret->system_info->turret_weapon_type;
10676
10677         if (check_ok_to_fire(parent_objnum, turret->turret_enemy_objnum, &Weapon_info[turret_weapon_class])) {
10678                 vm_vector_2_matrix(&turret_orient, turret_fvec, NULL, NULL);
10679                 turret->turret_last_fire_direction = *turret_fvec;
10680
10681                 // set next fire timestamp for the turret
10682                 turret_set_next_fire_timestamp(turret, parent_aip);
10683
10684                 // if this weapon is a beam weapon, handle it specially
10685                 if(Weapon_info[turret_weapon_class].wi_flags & WIF_BEAM){
10686                         // if this beam isn't free to fire
10687                         if (!(turret->weapons.flags & SW_FLAG_BEAM_FREE)) {
10688                                 Int3(); // should never get this far
10689                                 return;
10690                         }
10691
10692                         // stuff beam firing info
10693                         memset(&fire_info, 0, sizeof(beam_fire_info));
10694                         fire_info.accuracy = 1.0f;
10695                         fire_info.beam_info_index = turret_weapon_class;
10696                         fire_info.beam_info_override = NULL;
10697                         fire_info.shooter = &Objects[parent_objnum];
10698                         fire_info.target = &Objects[turret->turret_enemy_objnum];
10699                         fire_info.target_subsys = NULL;
10700                         fire_info.turret = turret;
10701
10702                         // fire a beam weapon
10703                         beam_fire(&fire_info);
10704                 } else {
10705
10706                         // don't fire swarm, but set up swarm info
10707                         if (Weapon_info[turret_weapon_class].wi_flags & WIF_SWARM) {
10708                                 turret_swarm_set_up_info(parent_objnum, turret, turret_weapon_class);
10709                                 return;
10710                         } else {
10711                                 weapon_objnum = weapon_create( turret_pos, &turret_orient, turret_weapon_class, parent_objnum, 0, -1, 1);
10712                                 weapon_set_tracking_info(weapon_objnum, parent_objnum, turret->turret_enemy_objnum, 1, turret->targeted_subsys);                
10713                         }
10714
10715                         //nprintf(("AI", "Turret_time_enemy_in_range = %7.3f\n", ss->turret_time_enemy_in_range));              
10716                         if (weapon_objnum != -1) {
10717                                 Weapons[Objects[weapon_objnum].instance].target_num = turret->turret_enemy_objnum;
10718                                 // AL 1-6-97: Store pointer to turret subsystem
10719                                 Weapons[Objects[weapon_objnum].instance].turret_subsys = turret;
10720
10721                                 if ( Weapon_info[turret_weapon_class].launch_snd != -1 ) {
10722                                         // Don't play turret firing sound if turret sits on player ship... it gets annoying.
10723                                         if ( parent_objnum != OBJ_INDEX(Player_obj) ) {                                         
10724                                                 snd_play_3d( &Snds[Weapon_info[turret_weapon_class].launch_snd], turret_pos, &View_position );                                          
10725                                         }
10726                                 }               
10727
10728                                 // if the gun is a flak gun
10729                                 if(Weapon_info[turret_weapon_class].wi_flags & WIF_FLAK){                       
10730                                         // show a muzzle flash
10731                                         flak_muzzle_flash(turret_pos, turret_fvec, turret_weapon_class);
10732
10733                                         // pick a firing range so that it detonates properly                    
10734                                         flak_pick_range(&Objects[weapon_objnum], predicted_pos, ship_get_subsystem_strength(parent_ship, SUBSYSTEM_WEAPONS));
10735
10736                                         // determine what that range was
10737                                         flak_range = flak_get_range(&Objects[weapon_objnum]);
10738                                 }
10739
10740                                 // in multiplayer (and the master), then send a turret fired packet.
10741                                 if ( MULTIPLAYER_MASTER && (weapon_objnum != -1) ) {
10742                                         int subsys_index;
10743
10744                                         subsys_index = ship_get_index_from_subsys(turret, parent_objnum );
10745                                         Assert( subsys_index != -1 );
10746                                         if(Weapon_info[turret_weapon_class].wi_flags & WIF_FLAK){                       
10747                                                 send_flak_fired_packet( parent_objnum, subsys_index, weapon_objnum, flak_range );
10748                                         } else {
10749                                                 send_turret_fired_packet( parent_objnum, subsys_index, weapon_objnum );
10750                                         }
10751                                 }
10752                         }
10753                 }
10754         } else {
10755                 float wait = 1000.0f * frand_range(0.9f, 1.1f);
10756                 turret->turret_next_fire_stamp = timestamp((int) wait);
10757         }
10758 }
10759
10760 void turret_swarm_fire_from_turret(ship_subsys *turret, int parent_objnum, int target_objnum, ship_subsys *target_subsys)
10761 {
10762         int turret_weapon_class, weapon_objnum;
10763         matrix turret_orient;
10764         vector turret_pos, turret_fvec;
10765
10766         // parent not alive, quick out.
10767         if (Objects[parent_objnum].type != OBJ_SHIP) {
10768                 return;
10769         }
10770
10771         //      change firing point
10772         ship_get_global_turret_gun_info(&Objects[parent_objnum], turret, &turret_pos, &turret_fvec, 1, NULL);
10773         turret->turret_next_fire_pos++;
10774
10775         // get class [index into Weapon_info array
10776         turret_weapon_class = turret->system_info->turret_weapon_type;
10777         Assert(Weapon_info[turret_weapon_class].wi_flags & WIF_SWARM);
10778
10779         // make turret_orient from turret_fvec -- turret->turret_last_fire_direction
10780         vm_vector_2_matrix(&turret_orient, &turret_fvec, NULL, NULL);
10781
10782         // create weapon and homing info
10783         weapon_objnum = weapon_create(&turret_pos, &turret_orient, turret_weapon_class, parent_objnum, 0, -1, 1);
10784         weapon_set_tracking_info(weapon_objnum, parent_objnum, target_objnum, 1, target_subsys);
10785
10786         // do other cool stuff if weapon is created.
10787         if (weapon_objnum > -1) {
10788                 Weapons[Objects[weapon_objnum].instance].turret_subsys = turret;
10789                 Weapons[Objects[weapon_objnum].instance].target_num = turret->turret_enemy_objnum;
10790
10791                 // maybe sound
10792                 if ( Weapon_info[turret_weapon_class].launch_snd != -1 ) {
10793                         // Don't play turret firing sound if turret sits on player ship... it gets annoying.
10794                         if ( parent_objnum != OBJ_INDEX(Player_obj) ) {
10795                                 snd_play_3d( &Snds[Weapon_info[turret_weapon_class].launch_snd], &turret_pos, &View_position );
10796                         }
10797                 }
10798                 
10799                 // in multiplayer (and the master), then send a turret fired packet.
10800                 if ( MULTIPLAYER_MASTER && (weapon_objnum != -1) ) {
10801                         int subsys_index;
10802
10803                         subsys_index = ship_get_index_from_subsys(turret, parent_objnum );
10804                         Assert( subsys_index != -1 );
10805                         send_turret_fired_packet( parent_objnum, subsys_index, weapon_objnum );
10806                 }
10807         }
10808 }
10809
10810 int Num_ai_firing = 0;
10811 int Num_find_turret_enemy = 0;
10812 int Num_turrets_fired = 0;
10813 //      Given a turret tp and its parent parent_objnum, fire from the turret at its enemy.
10814 void ai_fire_from_turret(ship *shipp, ship_subsys *ss, int parent_objnum)
10815 {
10816         float           weapon_firing_range;
10817         vector  v2e;
10818         object  *lep;           //      Last enemy pointer
10819         model_subsystem *tp = ss->system_info;
10820         int             use_angles, turret_weapon_class;
10821         vector  predicted_enemy_pos;
10822         object  *objp;
10823         ai_info *aip;
10824
10825         if (!Ai_firing_enabled) {
10826                 return;
10827         }
10828
10829         if (ss->current_hits < 0.0f) {
10830                 return;
10831         }
10832
10833         if ( ship_subsys_disrupted(ss) ){               // AL 1/19/98: Make sure turret isn't suffering disruption effects
10834                 return;
10835         }
10836
10837         // Check turret free
10838         if (ss->weapons.flags & SW_FLAG_TURRET_LOCK) {
10839                 return;
10840         }
10841
10842         // If beam weapon, check beam free
10843         if ( (Weapon_info[tp->turret_weapon_type].wi_flags & WIF_BEAM) && !(ss->weapons.flags & SW_FLAG_BEAM_FREE) ) {
10844                 return;
10845         }
10846
10847         Assert( shipp->objnum == parent_objnum );
10848
10849         if ( tp->turret_weapon_type < 0 ){
10850                 return;
10851         }
10852
10853         // Monitor number of calls to ai_fire_from_turret
10854         Num_ai_firing++;
10855
10856         turret_weapon_class = tp->turret_weapon_type;
10857
10858         // AL 09/14/97: ensure ss->turret_enemy_objnum != -1 before setting lep
10859         if ( (ss->turret_enemy_objnum >= 0 && ss->turret_enemy_objnum < MAX_OBJECTS) && (ss->turret_enemy_sig == Objects[ss->turret_enemy_objnum].signature)) {
10860                 lep = &Objects[ss->turret_enemy_objnum];
10861
10862                 // MK -- here is where turret is targeting a bomb.  I simply return for now.  We should force
10863                 // a target change -- or better yet, never pick a weapon when this turret has a "huge" weapon
10864                 // loaded.
10865
10866                 // we only care about targets which are ships.
10867                 //if ( lep->type != OBJ_SHIP )
10868                 //      return;
10869
10870                 //      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.
10871                 if ( Weapon_info[turret_weapon_class].wi_flags & WIF_HUGE ) {
10872                         if ( lep->type != OBJ_SHIP ) {
10873                                 return;
10874                         }
10875                         if ( !(Ship_info[Ships[lep->instance].ship_info_index].flags & (SIF_BIG_SHIP | SIF_HUGE_SHIP)) ) {
10876                                 return;
10877                         }
10878                 }
10879
10880                 // If targeting protected or beam protected ship, don't fire.  Reset enemy objnum
10881                 if (lep->type == OBJ_SHIP) {
10882                         // Check if we're targeting a protected ship
10883                         if (lep->flags & OF_PROTECTED) {
10884                                 ss->turret_enemy_objnum = -1;
10885                                 ss->turret_time_enemy_in_range = 0.0f;
10886                                 return;
10887                         }
10888
10889                         // Check if we're targeting a beam protected ship with a beam weapon
10890                         if ( (lep->flags & OF_BEAM_PROTECTED) && (Weapon_info[turret_weapon_class].wi_flags & WIF_BEAM) ) {
10891                                 ss->turret_enemy_objnum = -1;
10892                                 ss->turret_time_enemy_in_range = 0.0f;
10893                                 return;
10894                         }
10895                 }
10896         } else {
10897                 ss->turret_enemy_objnum = -1;
10898                 lep = NULL;
10899         }
10900         
10901         Assert((parent_objnum >= 0) && (parent_objnum < MAX_OBJECTS));
10902         objp = &Objects[parent_objnum];
10903         Assert(objp->type == OBJ_SHIP);
10904         aip = &Ai_info[Ships[objp->instance].ai_index];
10905
10906         // Use the turret info for all guns, not one gun in particular.
10907         vector   gvec, gpos;
10908         ship_get_global_turret_info(&Objects[parent_objnum], tp, &gpos, &gvec);
10909
10910         // Rotate the turret even if time hasn't elapsed, since it needs to turn to face its target.
10911         use_angles = aifft_rotate_turret(shipp, parent_objnum, ss, objp, lep, &predicted_enemy_pos, &gvec);
10912
10913         if ( !timestamp_elapsed(ss->turret_next_fire_stamp)){
10914                 return;
10915         }
10916
10917         // Don't try to fire beyond weapon_limit_range
10918         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);
10919
10920         // if beam weapon in nebula and target not tagged, decrase firing range
10921         extern int Nebula_sec_range;
10922         if (Weapon_info[turret_weapon_class].wi_flags & WIF_BEAM) {
10923                 if ( !((shipp->tag_left > 0) || (shipp->level2_tag_left > 0)) ) {
10924                         if (Nebula_sec_range) {
10925                                 weapon_firing_range *= float(BEAM_NEBULA_RANGE_REDUCE_FACTOR);
10926                         }
10927                 }
10928         }
10929
10930         if (ss->turret_enemy_objnum != -1) {
10931                 float dist_to_enemy = vm_vec_normalized_dir(&v2e, &predicted_enemy_pos, &gpos) - lep->radius;
10932                 if (dist_to_enemy > weapon_firing_range) {
10933                         ss->turret_enemy_objnum = -1;           //      Force picking of new enemy.
10934                 }
10935         }
10936
10937         // Turret spawn weapons are a special case.  They fire if there are enough enemies in the 
10938         // immediate area (not necessarily in the turret fov).
10939         if ( Weapon_info[turret_weapon_class].wi_flags & WIF_SPAWN ) {
10940                 int num_ships_nearby;
10941                 num_ships_nearby = num_nearby_fighters(get_enemy_team_mask(parent_objnum), &gpos, 1500.0f);
10942                 if (( num_ships_nearby >= 3 ) || ((num_ships_nearby >= 2) && (frand() < 0.1f))) {
10943                         turret_fire_weapon(ss, parent_objnum, &gpos, &ss->turret_last_fire_direction);
10944                 } else {
10945                         ss->turret_next_fire_stamp = timestamp(1000);   //      Regardless of firing rate, don't check whether should fire for awhile.
10946                 }
10947                 return;
10948         }
10949
10950         //      Maybe pick a new enemy.
10951         if ( turret_should_pick_new_target(ss) ) {
10952                 Num_find_turret_enemy++;
10953                 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);
10954                 Assert(objnum < 0 || is_target_beam_valid(ss, objnum));
10955
10956                 if (objnum != -1) {
10957                         if (ss->turret_enemy_objnum == -1) {
10958                                 ss->turret_enemy_objnum = objnum;
10959                                 ss->turret_enemy_sig = Objects[objnum].signature;
10960                                 // why return?
10961                                 return;
10962                         } else {
10963                                 ss->turret_enemy_objnum = objnum;
10964                                 ss->turret_enemy_sig = Objects[objnum].signature;
10965                         }
10966                 } else {
10967                         ss->turret_enemy_objnum = -1;
10968                 }
10969
10970                 if (ss->turret_enemy_objnum != -1) {
10971                         float   dot = 1.0f;
10972                         lep = &Objects[ss->turret_enemy_objnum];
10973                         if ( lep->type == OBJ_SHIP ) {
10974                                 ss->targeted_subsys = aifft_find_turret_subsys(objp, ss, lep, &dot);                            
10975                         }
10976                         ss->turret_next_enemy_check_stamp = timestamp((int) (max(dot, 0.5f)*2000.0f) + 1000);
10977                 } else {
10978                         ss->turret_next_enemy_check_stamp = timestamp((int) (2000.0f * frand_range(0.9f, 1.1f)));       //      Check every two seconds
10979                 }
10980         }
10981
10982         //      If still don't have an enemy, return.  Or, if enemy is protected, return.
10983         if (ss->turret_enemy_objnum != -1) {
10984                 //      Don't shoot at ship we're going to dock with.
10985                 if (ss->turret_enemy_objnum == aip->dock_objnum) {
10986                         ss->turret_enemy_objnum = -1;
10987                         return;
10988                 }
10989
10990                 if (Objects[ss->turret_enemy_objnum].flags & OF_PROTECTED) {
10991                         //      This can happen if the enemy was selected before it became protected.
10992                         ss->turret_enemy_objnum = -1;
10993                         return;
10994                 }
10995                 lep = &Objects[ss->turret_enemy_objnum];
10996         } else {
10997                 if (timestamp_until(ss->turret_next_fire_stamp) < 500) {
10998                         ss->turret_next_fire_stamp = timestamp(500);
10999                 }
11000                 return;
11001         }
11002
11003         if ( lep == NULL ){
11004                 return;
11005         }
11006
11007         Assert(ss->turret_enemy_objnum != -1);
11008
11009         float dot = vm_vec_dot(&v2e, &gvec);
11010
11011         if (dot > tp->turret_fov ) {
11012                 // Ok, the turret is lined up... now line up a particular gun.
11013                 int ok_to_fire = 0;
11014                 float dist_to_enemy;
11015
11016                 // We're ready to fire... now get down to specifics, like where is the
11017                 // actual gun point and normal, not just the one for whole turret.
11018                 ship_get_global_turret_gun_info(&Objects[parent_objnum], ss, &gpos, &gvec, use_angles, &predicted_enemy_pos);
11019                 ss->turret_next_fire_pos++;
11020
11021                 // Fire in the direction the turret is facing, not right at the target regardless of turret dir.
11022                 vm_vec_sub(&v2e, &predicted_enemy_pos, &gpos);
11023                 dist_to_enemy = vm_vec_normalize(&v2e);
11024                 dot = vm_vec_dot(&v2e, &gvec);
11025
11026                 // 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
11027                 // and make them less lethal
11028                 if(Weapon_info[turret_weapon_class].wi_flags & WIF_FLAK){
11029                         flak_jitter_aim(&v2e, dist_to_enemy, ship_get_subsystem_strength(shipp, SUBSYSTEM_WEAPONS));
11030                 }
11031
11032                 // Fire if:
11033                 //              dumbfire and nearly pointing at target.
11034                 //              heat seeking and target in a fairly wide cone.
11035                 //              aspect seeking and target is locked.
11036                 turret_weapon_class = tp->turret_weapon_type;
11037
11038                 // if dumbfire (lasers and non-homing missiles)
11039                 if ( !(Weapon_info[turret_weapon_class].wi_flags & WIF_HOMING) ) {
11040                         if ((dist_to_enemy < 75.0f) || (dot > AICODE_TURRET_DUMBFIRE_ANGLE )) {
11041                                 turret_update_enemy_in_range(ss, 2*Weapon_info[turret_weapon_class].fire_wait);
11042                                 ok_to_fire = 1;
11043                         }
11044                 } else if ( Weapon_info[turret_weapon_class].wi_flags & WIF_HOMING_HEAT ) {     // if heat seekers
11045                         if ((dist_to_enemy < 50.0f) || (dot > AICODE_TURRET_HEATSEEK_ANGLE )) {
11046                                 turret_update_enemy_in_range(ss, 2*Weapon_info[turret_weapon_class].fire_wait);
11047                                 ok_to_fire = 1;
11048                         }
11049                 } else if ( Weapon_info[turret_weapon_class].wi_flags & WIF_HOMING_ASPECT ) {   // if aspect seeker
11050                         if ((dist_to_enemy < 50.0f) || (dot > AICODE_TURRET_DUMBFIRE_ANGLE )) {
11051                                 turret_update_enemy_in_range(ss, 2*Weapon_info[turret_weapon_class].fire_wait);
11052                         }
11053                         if ( turret_should_fire_aspect(ss, dot, turret_weapon_class) ) {
11054                                 ok_to_fire = 1;
11055                         }
11056                 }
11057
11058                 if ( ok_to_fire ) {
11059                         Num_turrets_fired++;
11060                         
11061                         turret_fire_weapon(ss, parent_objnum, &gpos, &v2e, &predicted_enemy_pos);                                               
11062                 } else {
11063                         turret_update_enemy_in_range(ss, -4*Weapon_info[tp->turret_weapon_type].fire_wait);
11064                         ss->turret_next_fire_stamp = timestamp(500);
11065                 }
11066         } else {
11067                 // Lost him!
11068                 ss->turret_enemy_objnum = -1;           //      Reset enemy objnum, find a new one next frame.
11069                 ss->turret_time_enemy_in_range = 0.0f;
11070         }
11071 }
11072
11073 // TURRET END
11074
11075 #ifndef NDEBUG
11076 #define MAX_AI_DEBUG_RENDER_STUFF       100
11077 typedef struct ai_render_stuff {
11078         ship_subsys     *ss;
11079         int                     parent_objnum;
11080 } ai_render_stuff;
11081
11082 ai_render_stuff AI_debug_render_stuff[MAX_AI_DEBUG_RENDER_STUFF];
11083
11084 int     Num_AI_debug_render_stuff = 0;
11085
11086 void ai_debug_render_stuff()
11087 {
11088         vertex  vert1, vert2;
11089         vector  gpos2;
11090         int             i;
11091
11092         for (i=0; i<Num_AI_debug_render_stuff; i++) {
11093                 ship_subsys     *ss;
11094                 int     parent_objnum;
11095                 vector  gpos, gvec;
11096                 model_subsystem *tp;
11097
11098                 ss = AI_debug_render_stuff[i].ss;
11099                 tp = ss->system_info;
11100
11101                 parent_objnum = AI_debug_render_stuff[i].parent_objnum;
11102
11103                 ship_get_global_turret_info(&Objects[parent_objnum], tp, &gpos, &gvec);
11104                 g3_rotate_vertex(&vert1, &gpos);
11105                 vm_vec_scale_add(&gpos2, &gpos, &gvec, 20.0f);
11106                 g3_rotate_vertex(&vert2, &gpos2);
11107                 gr_set_color(0, 0, 255);
11108                 g3_draw_sphere(&vert1, 2.0f);
11109                 gr_set_color(255, 0, 255);
11110                 g3_draw_sphere(&vert2, 2.0f);
11111                 g3_draw_line(&vert1, &vert2);
11112         }
11113
11114         // draw from beta to its goal point
11115 /*      for (i=0; i<6; i++) {
11116                 ai_info *aip = &Ai_info[i];
11117                 gr_set_color(0, 0, 255);
11118                 g3_rotate_vertex(&vert1, &Objects[i].pos);
11119                 g3_rotate_vertex(&vert2, &aip->goal_point);
11120                 g3_draw_line(&vert1, &vert2);
11121         } */
11122         
11123
11124         Num_AI_debug_render_stuff = 0;
11125 }
11126
11127 #endif
11128
11129 #ifndef NDEBUG
11130 int     Msg_count_4996 = 0;
11131 #endif
11132
11133 //      --------------------------------------------------------------------------
11134 // Process subobjects of object objnum.
11135 //      Deal with engines disabled.
11136 void process_subobjects(int objnum)
11137 {
11138         model_subsystem *psub;
11139         ship_subsys     *pss;
11140         object  *objp = &Objects[objnum];
11141         ship            *shipp = &Ships[objp->instance];
11142         ai_info *aip = &Ai_info[shipp->ai_index];
11143         ship_info       *sip = &Ship_info[shipp->ship_info_index];
11144
11145         for ( pss = GET_FIRST(&shipp->subsys_list); pss !=END_OF_LIST(&shipp->subsys_list); pss = GET_NEXT(pss) ) {
11146                 psub = pss->system_info;
11147
11148                 // Don't process destroyed objects
11149                 if ( pss->current_hits <= 0.0f ) 
11150                         continue;
11151
11152                 switch (psub->type) {
11153                 case SUBSYSTEM_TURRET:
11154                         if ( psub->turret_num_firing_points > 0 )       {
11155                                 ai_fire_from_turret(shipp, pss, objnum);
11156                         } else {
11157 #ifndef NDEBUG
11158                                 if (!Msg_count_4996) {
11159                                         Warning( LOCATION, "Ship '%s' has turrets with no guns!\nProbably a model problem, so get an artist!", shipp->ship_name );
11160                                         Msg_count_4996++;
11161                                 }
11162 #endif
11163                                 }
11164                         break;
11165
11166                 case SUBSYSTEM_ENGINE:
11167                 case SUBSYSTEM_NAVIGATION:
11168                 case SUBSYSTEM_COMMUNICATION:
11169                 case SUBSYSTEM_WEAPONS:
11170                 case SUBSYSTEM_SENSORS:
11171                 case SUBSYSTEM_UNKNOWN:
11172                         break;
11173
11174                 // next set of subsystems may rotation
11175                 case SUBSYSTEM_RADAR:
11176                 case SUBSYSTEM_SOLAR:
11177                 case SUBSYSTEM_GAS_COLLECT:
11178                 case SUBSYSTEM_ACTIVATION:
11179                         break;
11180                 default:
11181                         Error(LOCATION, "Illegal subsystem type.\n");
11182                 }
11183
11184                 // do solar/radar/gas/activator rotation here
11185                 if ( psub->flags & MSS_FLAG_ROTATES )   {
11186                         if (psub->flags & MSS_FLAG_STEPPED_ROTATE       ) {
11187                                 submodel_stepped_rotate(psub, &pss->submodel_info_1);
11188                         } else {
11189                                 submodel_rotate(psub, &pss->submodel_info_1 );
11190                         }
11191                 }
11192
11193         }
11194
11195         //      Deal with a ship with blown out engines.
11196         if (ship_get_subsystem_strength(shipp, SUBSYSTEM_ENGINE) == 0.0f) {
11197                 if (sip->flags & (SIF_FIGHTER | SIF_BOMBER)) {
11198                         // AL: Only attack forever if not trying to depart to a docking bay.  Need to have this in, since
11199                         //     a ship may get repaired... and it should still try to depart.  Since docking bay departures
11200                         //     are not handled as goals, we don't want to leave the AIM_BAY_DEPART mode.
11201                         if ( aip->mode != AIM_BAY_DEPART ) {
11202                                 ai_attack_object(objp, NULL, 99, NULL);         //      Regardless of current mode, enter attack mode.
11203                                 aip->submode = SM_ATTACK_FOREVER;                               //      Never leave attack submode, don't avoid, evade, etc.
11204                         }
11205                 }
11206         }
11207
11208
11209 }
11210
11211 //      Given an object and the wing it's in, return its index in the wing list.
11212 //      This defines its location in the wing formation.
11213 //      If the object can't be found in the wing, return -1.
11214 //      *objp           object of interest
11215 //      wingnum the wing *objp is in
11216 int get_wing_index(object *objp, int wingnum)
11217 {
11218         wing    *wingp;
11219         int     i;
11220
11221         Assert((wingnum >= 0) && (wingnum < MAX_WINGS));
11222
11223         wingp = &Wings[wingnum];
11224
11225         for (i=wingp->current_count-1; i>=0; i--)
11226                 if ( objp->instance == wingp->ship_index[i] )
11227                         break;
11228
11229         return i;               //      Note, returns -1 if string not found.
11230 }
11231
11232 //      Given a wing, return a pointer to the object of its leader.
11233 //      Asserts if object not found.
11234 //      Currently, the wing leader is defined as the first object in the wing.
11235 //      wingnum         Wing number in Wings array.
11236 //      If wing leader is disabled, swap it with another ship.
11237 object * get_wing_leader(int wingnum)
11238 {
11239         wing            *wingp;
11240         int             ship_num;
11241
11242         Assert((wingnum >= 0) && (wingnum < MAX_WINGS));
11243
11244         wingp = &Wings[wingnum];
11245
11246         Assert(wingp->current_count != 0);                      //      Make sure there is a leader
11247
11248         ship_num = wingp->ship_index[0];
11249
11250         //      If this ship is disabled, try another ship in the wing.
11251         int n = 0;
11252         while (ship_get_subsystem_strength(&Ships[ship_num], SUBSYSTEM_ENGINE) == 0.0f) {
11253                 n++;
11254                 if (n >= wingp->current_count)
11255                         break;  
11256                 ship_num = wingp->ship_index[n];
11257         }
11258
11259         if (( n != 0) && (n != wingp->current_count)) {
11260                 int t = wingp->ship_index[0];
11261                 wingp->ship_index[0] = wingp->ship_index[n];
11262                 wingp->ship_index[n] = t;
11263         }
11264
11265         return &Objects[Ships[ship_num].objnum];
11266 }
11267
11268 #define DEFAULT_WING_X_DELTA            1.0f
11269 #define DEFAULT_WING_Y_DELTA            0.25f
11270 #define DEFAULT_WING_Z_DELTA            0.75f
11271 #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))
11272 // next constant is higher that MAX_SHIPS_IN_WINGS to deal with forming on player's wing
11273 #define MAX_FORMATION_ROWS              4
11274
11275 //      Given a position in a wing, return the desired location of the ship relative to the leader
11276 //      *_delta_vec             OUTPUT.  delta vector based on wing_index
11277 //      wing_index              position in wing.
11278 void get_wing_delta(vector *_delta_vec, int wing_index)
11279 {
11280         int     wi0;
11281
11282         Assert(wing_index >= 0);
11283
11284         int     k, row, column;
11285
11286         int bank = wing_index / (MAX_FORMATION_ROWS*(MAX_FORMATION_ROWS+1)/2);
11287         wi0 = wing_index % (MAX_FORMATION_ROWS * (MAX_FORMATION_ROWS+1)/2);
11288
11289         k = 0;
11290         for (row=1; row<MAX_FORMATION_ROWS+1; row++) {
11291                 k += row;
11292                 if (wi0 < k)
11293                         break;
11294         }
11295
11296         row--;
11297         column = wi0 - k + row + 1;
11298
11299         _delta_vec->x = ((float) column - (float) row/2.0f) * DEFAULT_WING_X_DELTA/DEFAULT_WING_MAG;
11300         _delta_vec->y = ((float)row + (float)bank*2.25f) * DEFAULT_WING_Y_DELTA/DEFAULT_WING_MAG;
11301         _delta_vec->z = - ((float)row + 0.5f * (float) bank) * DEFAULT_WING_Z_DELTA/DEFAULT_WING_MAG;
11302         
11303 }
11304
11305 //      Compute the largest radius of a ship in a *objp's wing.
11306 float gwlr_1(object *objp, ai_info *aip)
11307 {
11308         int             wingnum = aip->wing;
11309         float           max_radius;
11310         object  *o;
11311         ship_obj        *so;
11312
11313         Assert(wingnum >= 0);
11314
11315         max_radius = objp->radius;
11316
11317         for ( so = GET_FIRST(&Ship_obj_list); so != END_OF_LIST(&Ship_obj_list); so = GET_NEXT(so) ) {
11318                 o = &Objects[so->objnum];
11319                 if (Ai_info[Ships[o->instance].ai_index].wing == wingnum)
11320                         if (o->radius > max_radius)
11321                                 max_radius = o->radius;
11322         }
11323
11324         return max_radius;
11325 }
11326
11327 //      Compute the largest radius of a ship forming on *objp's wing.
11328 float gwlr_object_1(object *objp, ai_info *aip)
11329 {
11330         float           max_radius;
11331         object  *o;
11332         ship_obj        *so;
11333
11334         max_radius = objp->radius;
11335
11336         for ( so = GET_FIRST(&Ship_obj_list); so != END_OF_LIST(&Ship_obj_list); so = GET_NEXT(so) ) {
11337                 o = &Objects[so->objnum];
11338                 if (Ai_info[Ships[o->instance].ai_index].goal_objnum == OBJ_INDEX(objp))
11339                         if (o->radius > max_radius)
11340                                 max_radius = o->radius;
11341         }
11342
11343         return max_radius;
11344 }
11345
11346 //      For the wing that *objp is part of, return the largest ship radius in that wing.
11347 float get_wing_largest_radius(object *objp, int formation_object_flag)
11348 {
11349         ship            *shipp;
11350         ai_info *aip;
11351
11352         Assert(objp->type == OBJ_SHIP);
11353         Assert((objp->instance >= 0) && (objp->instance < MAX_OBJECTS));
11354         shipp = &Ships[objp->instance];
11355         Assert((shipp->ai_index >= 0) && (shipp->ai_index < MAX_AI_INFO));
11356         aip = &Ai_info[shipp->ai_index];
11357
11358         if (formation_object_flag) {
11359                 return gwlr_object_1(objp, aip);
11360         } else {
11361                 return gwlr_1(objp, aip);
11362         }
11363
11364 }
11365
11366 float Wing_y_scale = 2.0f;
11367 float Wing_scale = 1.0f;
11368 DCF(wing_y_scale, "")
11369 {
11370         dc_get_arg(ARG_FLOAT);
11371         Wing_y_scale = Dc_arg_float;
11372 }
11373
11374 DCF(wing_scale, "")
11375 {
11376         dc_get_arg(ARG_FLOAT);
11377         Wing_scale = Dc_arg_float;
11378 }
11379
11380 // Given a wing leader and a position in the wing formation, return the desired absolute location to fly to.
11381 //      Returns result in *result_pos.
11382 void get_absolute_wing_pos(vector *result_pos, object *leader_objp, int wing_index, int formation_object_flag)
11383 {
11384         vector  wing_delta, rotated_wing_delta;
11385         float           wing_spread_size;
11386
11387         get_wing_delta(&wing_delta, wing_index);                //      Desired location in leader's reference frame
11388
11389         wing_spread_size = max(50.0f, 3.0f * get_wing_largest_radius(leader_objp, formation_object_flag) + 15.0f);
11390
11391         // for player obj (1) move ships up 20% (2) scale formation up 20%
11392         if (leader_objp->flags & OF_PLAYER_SHIP) {
11393                 wing_delta.y *= Wing_y_scale;
11394                 wing_spread_size *= Wing_scale;
11395         }
11396
11397         vm_vec_scale(&wing_delta, wing_spread_size * (1.0f + leader_objp->phys_info.speed/70.0f));
11398
11399         vm_vec_unrotate(&rotated_wing_delta, &wing_delta, &leader_objp->orient);        //      Rotate into leader's reference.
11400
11401         vm_vec_add(result_pos, &leader_objp->pos, &rotated_wing_delta); //      goal_point is absolute 3-space point.
11402 }
11403
11404 #ifndef NDEBUG
11405 int Debug_render_wing_phantoms;
11406
11407 void render_wing_phantoms(object *objp)
11408 {
11409         int             i;
11410         ship            *shipp;
11411         ai_info *aip;
11412         int             wingnum;
11413         int             wing_index;             //      Index in wing struct, defines 3-space location in wing.
11414         vector  goal_point;
11415         
11416         Assert(objp->type == OBJ_SHIP);
11417         Assert((objp->instance >= 0) && (objp->instance < MAX_SHIPS));
11418
11419         shipp = &Ships[objp->instance];
11420         Assert((shipp->ai_index >= 0) && (shipp->ai_index < MAX_AI_INFO));
11421
11422         aip = &Ai_info[shipp->ai_index];
11423
11424         wingnum = aip->wing;
11425
11426         if (wingnum == -1)
11427                 return;
11428
11429         wing_index = get_wing_index(objp, wingnum);
11430
11431         //      If this ship is NOT the leader, abort.
11432         if (wing_index != 0)
11433                 return;
11434
11435         for (i=0; i<32; i++)
11436                 if (Debug_render_wing_phantoms & (1 << i)) {
11437                         get_absolute_wing_pos(&goal_point, objp, i, 0);
11438         
11439                         vertex  vert;
11440                         gr_set_color(255, 0, 128);
11441                         g3_rotate_vertex(&vert, &goal_point);
11442                         g3_draw_sphere(&vert, 2.0f);
11443                 }
11444
11445         Debug_render_wing_phantoms = 0;
11446
11447 }
11448
11449 void render_wing_phantoms_all()
11450 {
11451         object  *objp;
11452         ship_obj        *so;
11453
11454         for ( so = GET_FIRST(&Ship_obj_list); so != END_OF_LIST(&Ship_obj_list); so = GET_NEXT(so) ) {
11455                 ship            *shipp;
11456                 ai_info *aip;
11457                 int             wingnum;
11458                 int             wing_index;             //      Index in wing struct, defines 3-space location in wing.
11459
11460                 objp = &Objects[so->objnum];
11461                 
11462                 Assert((objp->instance >= 0) && (objp->instance < MAX_SHIPS));
11463                 shipp = &Ships[objp->instance];
11464                 Assert((shipp->ai_index >= 0) && (shipp->ai_index < MAX_AI_INFO));
11465
11466                 aip = &Ai_info[shipp->ai_index];
11467
11468                 wingnum = aip->wing;
11469
11470                 if (wingnum == -1)
11471                         continue;
11472
11473                 wing_index = get_wing_index(objp, wingnum);
11474
11475                 //      If this ship is NOT the leader, abort.
11476                 if (wing_index != 0)
11477                         continue;
11478                 
11479                 render_wing_phantoms(objp);
11480
11481                 return;
11482         }
11483 }
11484
11485 #endif
11486
11487 //      Hook from goals code to AI.
11488 //      Force a wing to fly in formation.
11489 //      Sets AIF_FORMATION bit in ai_flags.
11490 //      wingnum         Wing to force to fly in formation
11491 void ai_fly_in_formation(int wingnum)
11492 {
11493         object  *objp;
11494         ship            *shipp;
11495         ship_obj        *so;
11496
11497         for ( so = GET_FIRST(&Ship_obj_list); so != END_OF_LIST(&Ship_obj_list); so = GET_NEXT(so) ) {
11498                 objp = &Objects[so->objnum];
11499                 Assert((objp->instance >= 0) && (objp->instance < MAX_SHIPS));
11500
11501                 shipp = &Ships[objp->instance];
11502                 Assert((shipp->ai_index >= 0) && (shipp->ai_index < MAX_AI_INFO));
11503
11504                 if (Ai_info[shipp->ai_index].wing == wingnum) {
11505                         Ai_info[shipp->ai_index].ai_flags |= AIF_FORMATION_WING;
11506                         Ai_info[shipp->ai_index].ai_flags &= ~AIF_FORMATION_OBJECT;
11507                 }
11508         }
11509 }
11510
11511 //      Hook from goals code to AI.
11512 //      Force a wing to abandon formation flying.
11513 //      Clears AIF_FORMATION bit in ai_flags.
11514 //      wingnum         Wing to force to fly in formation
11515 void ai_disband_formation(int wingnum)
11516 {
11517         object  *objp;
11518         ship            *shipp;
11519         ship_obj        *so;
11520
11521         for ( so = GET_FIRST(&Ship_obj_list); so != END_OF_LIST(&Ship_obj_list); so = GET_NEXT(so) ) {
11522                 objp = &Objects[so->objnum];
11523                 Assert((objp->instance >= 0) && (objp->instance < MAX_SHIPS));
11524
11525                 shipp = &Ships[objp->instance];
11526                 Assert((shipp->ai_index >= 0) && (shipp->ai_index < MAX_AI_INFO));
11527
11528                 if (Ai_info[shipp->ai_index].wing == wingnum) {
11529                         Ai_info[shipp->ai_index].ai_flags &= ~AIF_FORMATION_WING;
11530                 }
11531         }
11532 }
11533
11534 float   Leader_chaos = 0.0f;
11535 int Chaos_frame = -1;
11536
11537 //      Return true if objp is flying in an erratic manner
11538 //      Only true if objp is a player
11539 int formation_is_leader_chaotic(object *objp)
11540 {
11541         if (Game_mode & GM_MULTIPLAYER)
11542                 return 0;
11543
11544         if (objp != Player_obj)
11545                 return 0;
11546
11547         if (Framecount != Chaos_frame) {
11548                 float   speed_scale;
11549                 float   fdot, udot;
11550
11551                 speed_scale = 3.0f + objp->phys_info.speed * 0.1f;
11552
11553                 fdot = 5.0f * (1.0f - vm_vec_dot(&objp->orient.fvec, &objp->last_orient.fvec)) * flFrametime;
11554                 udot = 8.0f * (1.0f - vm_vec_dot(&objp->orient.uvec, &objp->last_orient.uvec)) * flFrametime;
11555
11556                 Leader_chaos += fdot * speed_scale + udot * speed_scale;
11557
11558                 Leader_chaos *= (1.0f - flFrametime*0.2f);
11559
11560                 if (Leader_chaos < 0.0f)
11561                         Leader_chaos = 0.0f;
11562                 else if (Leader_chaos > 1.7f)
11563                         Leader_chaos = 1.7f;
11564
11565                 //nprintf(("AI", "Frame %i: chaos = %7.4f\n", Framecount, Leader_chaos));
11566
11567                 Chaos_frame = Framecount;
11568         }
11569
11570         return (Leader_chaos > 1.0f);
11571 }
11572
11573 // Fly in formation.
11574 //      Make Pl_objp assume its proper place in formation.
11575 //      If the leader of the wing is doing something stupid, like fighting a battle,
11576 //      then the poor sap wingmates will be in for a "world of hurt"
11577 //      Return TRUE if we need to process this object's normal mode
11578 int ai_formation()
11579 {
11580         object  *leader_objp;
11581         ship            *shipp;
11582         ai_info *aip, *laip;
11583         int             wingnum;
11584         int             wing_index;             //      Index in wing struct, defines 3-space location in wing.
11585         int             player_wing;    // index of the players wingnum
11586         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;
11587         float           dot_to_goal, dist_to_goal, leader_speed;
11588
11589         Assert(Pl_objp->type == OBJ_SHIP);
11590         Assert((Pl_objp->instance >= 0) && (Pl_objp->instance < MAX_SHIPS));
11591
11592         shipp = &Ships[Pl_objp->instance];
11593
11594         Assert((shipp->ai_index >= 0) && (shipp->ai_index < MAX_AI_INFO));
11595
11596         aip = &Ai_info[shipp->ai_index];
11597
11598         Assert((aip->ai_flags & AIF_FORMATION) != AIF_FORMATION);       //      Make sure not both types of formation flying in effect.
11599
11600         //      Determine which kind of formation flying.
11601         //      If tracking an object, not in waypoint mode:
11602         if (aip->ai_flags & AIF_FORMATION_OBJECT) {
11603                 if ((aip->goal_objnum < 0) || (aip->goal_objnum >= MAX_OBJECTS)) {
11604                         aip->ai_flags &= ~AIF_FORMATION_OBJECT;
11605                         return 1;
11606                 }
11607                 
11608                 wing_index = ai_formation_object_get_slotnum(aip->goal_objnum, Pl_objp);
11609                 leader_objp = &Objects[aip->goal_objnum];
11610         } else {        //      Formation flying in waypoint mode.
11611                 Assert(aip->ai_flags & AIF_FORMATION_WING);
11612                 if (aip->mode != AIM_WAYPOINTS) {
11613                         aip->ai_flags &= ~AIF_FORMATION_WING;
11614                         return 1;
11615                 }
11616
11617                 wingnum = aip->wing;
11618
11619                 if (wingnum == -1)
11620                         return 1;
11621
11622                 // disable formation flying for any ship in the players wing
11623                 player_wing = Ships[Player_obj->instance].wingnum;
11624                 if ( (player_wing != -1) && (wingnum == player_wing) )
11625                         return 1;
11626
11627                 wing_index = get_wing_index(Pl_objp, wingnum);
11628
11629                 leader_objp = get_wing_leader(wingnum);
11630
11631         }
11632
11633         //      If docked with a ship in this wing, only the more massive one actually flies in formation.
11634         if (aip->dock_objnum != -1) {
11635                 object  *other_objp = &Objects[aip->dock_objnum];
11636                 ai_info *other_aip = &Ai_info[Ships[other_objp->instance].ai_index];
11637
11638                 if (aip->wing == other_aip->wing) {
11639                         if (Pl_objp->phys_info.mass < other_objp->phys_info.mass)
11640                                 return 0;
11641                         else if (Pl_objp->phys_info.mass == other_objp->phys_info.mass) {
11642                                 if (Pl_objp->signature < other_objp->signature)
11643                                         return 0;
11644                         }
11645                 }
11646         }
11647
11648         Assert(leader_objp != NULL);
11649         laip = &Ai_info[Ships[leader_objp->instance].ai_index];
11650
11651         //      Make sure we're really in this wing.
11652         if (wing_index == -1)
11653                 return 1;
11654
11655         //      If this ship is the leader, abort, as he doesn't have to follow anyone.
11656         if (wing_index == 0) {
11657                 // nprintf(("AI", "Hmm, wing leader %s in ai_formation for no good reason.\n", shipp->ship_name));
11658                 return 1;
11659         }
11660
11661         if (aip->mode == AIM_WAYPOINTS) {
11662                 aip->wp_list = laip->wp_list;
11663                 if (laip->wp_index < Waypoint_lists[laip->wp_list].count)
11664                         aip->wp_index = laip->wp_index;
11665                 else
11666                         aip->wp_index = Waypoint_lists[laip->wp_list].count - 1;
11667                 aip->wp_flags = laip->wp_flags;
11668                 aip->wp_dir = laip->wp_dir;
11669         }
11670
11671         #ifndef NDEBUG
11672         Debug_render_wing_phantoms |= (1 << wing_index);
11673         #endif
11674
11675         leader_speed = leader_objp->phys_info.speed;
11676         vector leader_vec = leader_objp->phys_info.vel;
11677
11678         get_absolute_wing_pos(&goal_point, leader_objp, wing_index, aip->ai_flags & AIF_FORMATION_OBJECT);
11679         vm_vec_scale_add(&future_goal_point_5, &goal_point, &leader_vec, 10.0f);
11680         vm_vec_scale_add(&future_goal_point_2, &goal_point, &leader_vec, 5.0f);
11681         vm_vec_scale_add(&future_goal_point_x, &goal_point, &leader_objp->orient.fvec, 10.0f);  //      used when very close to destination
11682         vm_vec_scale_add(&future_goal_point_1000x, &goal_point, &leader_objp->orient.fvec, 1000.0f);    //      used when very close to destination
11683
11684         //      Now, get information telling this object how to turn and accelerate to get to its
11685         //      desired location.
11686         vm_vec_sub(&vec_to_goal, &goal_point, &Pl_objp->pos);
11687         if ( vm_vec_mag_quick(&vec_to_goal) < AICODE_SMALL_MAGNITUDE )
11688                 vec_to_goal.x += 0.1f;
11689
11690         vm_vec_copy_normalize(&dir_to_goal, &vec_to_goal);
11691         //dot_to_goal = vm_vec_dot(&dir_to_goal, &leader_objp->orient.fvec);
11692         dot_to_goal = vm_vec_dot(&dir_to_goal, &Pl_objp->orient.fvec);
11693         dist_to_goal = vm_vec_dist_quick(&Pl_objp->pos, &goal_point);
11694         float   dist_to_goal_2 = vm_vec_dist_quick(&Pl_objp->pos, &future_goal_point_2);
11695
11696         // 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));
11697
11698         int     chaotic_leader = 0;
11699
11700         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.
11701
11702         if (dist_to_goal > 500.0f) {
11703                 turn_towards_point(Pl_objp, &goal_point, NULL, 0.0f);
11704                 accelerate_ship(aip, 1.0f);
11705         } else if (dist_to_goal > 200.0f) {
11706                 if (dot_to_goal > -0.5f) {
11707                         turn_towards_point(Pl_objp, &goal_point, NULL, 0.0f);
11708                         float range_speed = shipp->current_max_speed - leader_speed;
11709                         if (range_speed > 0.0f)
11710                                 set_accel_for_target_speed(Pl_objp, leader_speed + range_speed * (dist_to_goal+100.0f)/500.0f);
11711                         else
11712                                 set_accel_for_target_speed(Pl_objp, shipp->current_max_speed);
11713                 } else {
11714                         turn_towards_point(Pl_objp, &future_goal_point_5, NULL, 0.0f);
11715                         if (leader_speed > 10.0f)
11716                                 set_accel_for_target_speed(Pl_objp, leader_speed *(1.0f + dot_to_goal));
11717                         else
11718                                 set_accel_for_target_speed(Pl_objp, 10.0f);
11719                 }
11720         } else {
11721                 vector  v2f2;
11722                 float   dot_to_f2;
11723                 float   dist_to_f2;
11724
11725                 dist_to_f2 = vm_vec_normalized_dir(&v2f2, &future_goal_point_2, &Pl_objp->pos);
11726                 dot_to_f2 = vm_vec_dot(&v2f2, &Pl_objp->orient.fvec);
11727
11728                 //      Leader flying like a maniac.  Don't try hard to form on wing.
11729                 if (chaotic_leader) {
11730                         turn_towards_point(Pl_objp, &future_goal_point_2, NULL, 0.0f);
11731                         set_accel_for_target_speed(Pl_objp, min(leader_speed*0.8f, 20.0f));
11732                 } else if (dist_to_goal > 75.0f) {
11733                         turn_towards_point(Pl_objp, &future_goal_point_2, NULL, 0.0f);
11734                         float   delta_speed;
11735                         float range_speed = shipp->current_max_speed - leader_speed;
11736                         if (range_speed > 0.0f)
11737                                 delta_speed = dist_to_goal_2/500.0f * range_speed;
11738                         else
11739                                 delta_speed = shipp->current_max_speed - leader_speed;
11740                         if (dot_to_goal < 0.0f) {
11741                                 delta_speed = -delta_speed;
11742                                 if (-delta_speed > leader_speed/2)
11743                                         delta_speed = -leader_speed/2;
11744                         }
11745
11746                         if (leader_speed < 5.0f)
11747                                 if (delta_speed < 5.0f)
11748                                         delta_speed = 5.0f;
11749
11750                         float scale = dot_to_f2;
11751                         if (scale < 0.1f)
11752                                 scale = 0.0f;
11753                         else
11754                                 scale *= scale;
11755
11756                         set_accel_for_target_speed(Pl_objp, scale * (leader_speed + delta_speed));
11757                 } else {
11758                         //nprintf(("AI", "Dot = %7.3f\n", dot_to_goal));
11759
11760                         if (leader_speed < 5.0f) {
11761                                 //      Leader very slow.  If not close to goal point, get very close.  Note, keep trying to get close unless
11762                                 //      moving very slowly, else momentum can carry far away from goal.
11763
11764                                 if ((dist_to_goal > 10.0f) || ((Pl_objp->phys_info.speed > leader_speed + 2.5f) && (dot_to_goal > 0.5f))) {
11765                                         //nprintf(("MK", "(1) "));
11766                                         turn_towards_point(Pl_objp, &goal_point, NULL, 0.0f);
11767                                         set_accel_for_target_speed(Pl_objp, leader_speed + dist_to_goal/10.0f);
11768                                 } else {
11769                                         if (Pl_objp->phys_info.speed < 0.5f) {
11770                                                 //nprintf(("MK", "(2) "));
11771                                                 turn_towards_point(Pl_objp, &future_goal_point_1000x, NULL, 0.0f);
11772                                         } else {
11773                                                 //nprintf(("MK", "(3) "));
11774                                         }
11775                                         set_accel_for_target_speed(Pl_objp, leader_speed);
11776                                 }
11777                                 //nprintf(("MK", "dist: %7.3f, dot: %6.3f, speed: %7.3f\n", dist_to_goal, dot_to_goal, Pl_objp->phys_info.speed));
11778                         } else if (dist_to_goal > 10.0f) {
11779                                 float   dv;
11780
11781                                 future_goal_point_2;
11782
11783                                 turn_towards_point(Pl_objp, &future_goal_point_2, NULL, 0.0f);
11784
11785                                 if (dist_to_goal > 25.0f) {
11786                                         if (dot_to_goal < 0.3f)
11787                                                 dv = -0.1f;
11788                                         else
11789                                                 dv = dot_to_goal - 0.2f;
11790
11791                                         set_accel_for_target_speed(Pl_objp, leader_speed + dist_to_goal/5.0f * dv);
11792                                 } else {
11793                                         set_accel_for_target_speed(Pl_objp, leader_speed + 1.5f * dot_to_goal - 1.0f);
11794                                 }
11795                         } else {
11796                                 if (Pl_objp->phys_info.speed < 0.1f)
11797                                         turn_towards_point(Pl_objp, &future_goal_point_1000x, NULL, 0.0f);
11798                                 else
11799                                         turn_towards_point(Pl_objp, &future_goal_point_x, NULL, 0.0f);
11800                                 set_accel_for_target_speed(Pl_objp, 0.0f);
11801                         }
11802                 }
11803
11804         }
11805
11806         //      See how different this ship's bank is relative to wing leader
11807         float   up_dot = vm_vec_dot(&leader_objp->orient.uvec, &Pl_objp->orient.uvec);
11808         if (up_dot < 0.996f) {
11809                 vector  w_out;
11810                 matrix  new_orient;
11811                 vector  angular_accel;
11812
11813                 vm_vec_copy_scale(&angular_accel, &Pl_objp->phys_info.max_rotvel, 0.2f);
11814                 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);
11815
11816         //      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)));
11817                 Pl_objp->orient = new_orient;
11818                 Pl_objp->phys_info.rotvel = w_out;
11819         //      Pl_objp->phys_info.desired_rotvel = w_out;
11820         } else {
11821                 Pl_objp->phys_info.rotvel.z = 0.0f;
11822         }
11823
11824         return 0;
11825 }
11826
11827 //      Return index of object repairing object objnum.
11828 int find_repairing_objnum(int objnum)
11829 {
11830         object          *objp;
11831         ship                    *shipp;
11832         ship_info       *sip;
11833         ship_obj                *so;
11834
11835         for ( so = GET_FIRST(&Ship_obj_list); so != END_OF_LIST(&Ship_obj_list); so = GET_NEXT(so) ) {
11836                 objp = &Objects[so->objnum];
11837
11838                 Assert((objp->instance >= 0) && (objp->instance < MAX_SHIPS));
11839
11840                 shipp = &Ships[objp->instance];
11841                 sip = &Ship_info[shipp->ship_info_index];
11842
11843                 if (sip->flags & SIF_SUPPORT) {
11844                         ai_info *aip;
11845
11846                         aip = &Ai_info[shipp->ai_index];
11847
11848                         if (aip->goal_objnum == objnum) {
11849                                 return objp-Objects;
11850                         }
11851                 }
11852         }
11853
11854         return -1;
11855 }
11856
11857 //      If object *objp is being repaired, deal with it!
11858 void ai_do_repair_frame(object *objp, ai_info *aip, float frametime)
11859 {
11860         if (Ships[objp->instance].team == TEAM_TRAITOR) {
11861                 ai_abort_rearm_request(objp);
11862                 return;
11863         }
11864
11865         if (aip->ai_flags & (AIF_BEING_REPAIRED | AIF_AWAITING_REPAIR)) {
11866                 int     dock_objnum;
11867                 ai_info *repair_aip;
11868
11869                 dock_objnum = aip->dock_objnum; // find_repairing_objnum(objp-Objects);
11870                 //Assert(dock_objnum != -1);
11871                 if (dock_objnum == -1)
11872                         return;
11873                 if (Objects[dock_objnum].signature != aip->dock_signature) {
11874                         Int3();         //      Curious -- object numbers match, but signatures do not.
11875                                                         //      Must mean original repair ship died and was replaced by current ship.
11876                         return;
11877                 }
11878         
11879                 repair_aip = &Ai_info[Ships[Objects[dock_objnum].instance].ai_index];
11880                 //Assert(repair_aip->mode == AIM_DOCK);
11881
11882                 if (aip->ai_flags & AIF_BEING_REPAIRED) {
11883                         // Assert(repair_aip->submode == AIS_DOCK_4);
11884
11885                         //      Wait awhile into the mode to synchronize with sound effect.
11886                         if (Missiontime - repair_aip->submode_start_time > REARM_SOUND_DELAY) {
11887                                 int repaired;
11888
11889                                 repaired = ship_do_rearm_frame( objp, frametime );              // hook to do missile rearming
11890
11891                                 //      See if fully repaired.  If so, cause process to stop.
11892                                 if ( repaired && (repair_aip->submode == AIS_DOCK_4)) {
11893
11894                                         repair_aip->submode = AIS_UNDOCK_0;
11895                                         repair_aip->submode_start_time = Missiontime;
11896
11897                                         // if repairing player object -- tell him done with repair
11898                                         if ( !MULTIPLAYER_CLIENT ){
11899                                                 ai_do_objects_repairing_stuff( objp, &Objects[dock_objnum], REPAIR_INFO_COMPLETE );
11900                                         }
11901                                 }
11902                         }
11903                 } else if (aip->ai_flags & AIF_AWAITING_REPAIR) {
11904                         //      If this ship has been awaiting repair for 90+ seconds, abort.
11905                         if ( !MULTIPLAYER_CLIENT ) {
11906                                 if ((Game_mode & GM_MULTIPLAYER) || (objp != Player_obj)) {
11907                                         if ((repair_aip->goal_objnum == OBJ_INDEX(objp)) && (timestamp_elapsed(aip->abort_rearm_timestamp))) {
11908                                                 ai_abort_rearm_request(objp);
11909                                                 aip->next_rearm_request_timestamp = timestamp(NEXT_REARM_TIMESTAMP);
11910                                         }
11911                                 }
11912                         }
11913                 }
11914         } else {
11915                 // AL 11-24-97: If this is the player ship, ensure the repair sound isn't playing.  We need to
11916                 //              do this check, since this is a looping sound, and may continue on if rearm/repair
11917                 //              finishes abnormally once sound begins looping.
11918                 if ( objp == Player_obj ) {
11919                         player_stop_repair_sound();
11920                 }
11921         }
11922 }
11923
11924 //      Shell around dock_orient_and_approach to detect whether dock process should be aborted.
11925 //      obj1 is the ship performing the repair.
11926 //      obj2 is the ship being repaired.
11927 void call_doa(object *obj1, object *obj2, ship_info *sip1)
11928 {
11929         if (sip1->flags & SIF_SUPPORT) {
11930                 if (obj2->phys_info.speed > MAX_REPAIR_SPEED) {
11931
11932                         // call the ai_abort rearm request code
11933                         ai_abort_rearm_request( obj2 );
11934                 } else
11935                         dock_orient_and_approach(obj1, obj2, DOA_DOCK_STAY);
11936         } else {
11937                 if (Ship_info[Ships[obj1->instance].ship_info_index].flags & SIF_CARGO)
11938                         dock_orient_and_approach(obj1, obj2, DOA_DOCK_STAY);
11939                 else if (Ship_info[Ships[obj2->instance].ship_info_index].flags & SIF_CARGO)
11940                         dock_orient_and_approach(obj2, obj1, DOA_DOCK_STAY);
11941                 else {
11942                         //mprintf(("Warning: Not sure, but making %s [%s] move to stay docked with %s [%s]\n",
11943                         //      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));
11944                         dock_orient_and_approach(obj1, obj2, DOA_DOCK_STAY);
11945
11946                 }
11947         }
11948
11949 }
11950
11951 //      Maybe launch a countermeasure.
11952 //      Also, detect a supposed homing missile that no longer exists.
11953 void ai_maybe_launch_cmeasure(object *objp, ai_info *aip)
11954 {
11955         float                   dist;
11956         ship_info       *sip;
11957         ship                    *shipp;
11958
11959         shipp = &Ships[objp->instance];
11960         sip = &Ship_info[shipp->ship_info_index];
11961
11962         if (!(sip->flags & (SIF_SMALL_SHIP | SIF_TRANSPORT)))
11963                 return;
11964
11965         if (!shipp->cmeasure_count)
11966                 return;
11967
11968         if ( !timestamp_elapsed(shipp->cmeasure_fire_stamp) )
11969                 return;
11970
11971         //      If not on player's team and Skill_level + ai_class is low, never fire a countermeasure.  The ship is too dumb.
11972         if (shipp->team != Player_ship->team) {
11973                 if (Game_skill_level + aip->ai_class < 4){
11974                         return;
11975                 }
11976         }
11977
11978         if ((aip->nearest_locked_object != -1) && (Objects[aip->nearest_locked_object].type == OBJ_WEAPON)) {
11979                 object  *weapon_objp;
11980                 weapon  *weaponp;
11981                 weapon_info     *wip;
11982
11983                 weapon_objp = &Objects[aip->nearest_locked_object];
11984                 weaponp = &Weapons[weapon_objp->instance];
11985                 wip = &Weapon_info[weaponp->weapon_info_index];
11986
11987                 if ((dist = vm_vec_dist_quick(&objp->pos, &weapon_objp->pos)) < weapon_objp->phys_info.speed*2.0f) {
11988         
11989                         aip->nearest_locked_distance = dist;
11990                         //      Verify that this object is really homing on us.
11991                         object  *weapon_objp;
11992
11993                         weapon_objp = &Objects[aip->nearest_locked_object];
11994
11995                         float   fire_chance;
11996
11997                         //      For ships on player's team, have constant, average chance to fire.
11998                         //      For enemies, increasing chance with higher skill level.
11999                         if (shipp->team == Player_ship->team)
12000                                 fire_chance = Cmeasure_fire_chance[NUM_SKILL_LEVELS/2];
12001                         else
12002                                 fire_chance = Cmeasure_fire_chance[Game_skill_level];
12003
12004                         //      Decrease chance to fire at lower ai class.
12005                         fire_chance *= (float) aip->ai_class/Num_ai_classes;
12006
12007                         float r = frand();
12008                         if (fire_chance < r) {
12009                                 //nprintf(("AI", "Not firing countermeasure due to skill level: %7.3f < %7.3f\n", fire_chance, r));
12010                                 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.
12011                                 return;
12012                         }
12013
12014                         if (weapon_objp->type == OBJ_WEAPON) {
12015                                 if (weapon_objp->instance >= 0) {
12016                                         //nprintf(("AI", "Firing countermeasure at time t=%7.3f\n", f2fl(Missiontime)));
12017                                         ship_launch_countermeasure(objp);
12018                                         shipp->cmeasure_fire_stamp = timestamp(2*CMEASURE_WAIT);
12019                                         return;
12020                                 }
12021                         }
12022         
12023                 }
12024         }
12025
12026         return;
12027 }
12028
12029 //      --------------------------------------------------------------------------
12030 void ai_preprocess_ignore_objnum(object *objp, ai_info *aip)
12031 {
12032 //      if (aip->ignore_objnum == UNUSED_OBJNUM)
12033 //              return;
12034
12035         if (aip->ai_flags & AIF_TEMPORARY_IGNORE) {
12036                 if (timestamp_elapsed(aip->ignore_expire_timestamp)) {
12037                         aip->ignore_objnum = UNUSED_OBJNUM;
12038                 }
12039         }
12040
12041         if (is_ignore_object(aip, aip->goal_objnum)) {
12042                 aip->goal_objnum = -1;
12043                 // AL 12-11-97: If in STRAFE mode, we need to ensure that target_objnum is also
12044                 //              set to -1
12045                 if ( aip->mode == AIM_STRAFE ) {
12046                         aip->target_objnum = -1;
12047                 }
12048         }
12049
12050         if (is_ignore_object(aip, aip->target_objnum))
12051                 aip->target_objnum = -1;
12052 }
12053
12054 /*
12055 void ai_safety_circle_spot()
12056 {
12057         vector  goal_point;
12058         ship_info       *sip;
12059
12060         sip = &Ship_info[Ships[Pl_objp->instance].ship_info_index];
12061
12062         goal_point = Ai_info[Ships[Pl_objp->instance].ai_index].goal_point;
12063         turn_towards_tangent(Pl_objp, &goal_point, 50.0f);
12064
12065         set_accel_for_target_speed(Pl_objp, sip->max_speed/4.0f);
12066
12067 //      float dist = vm_vec_dist_quick(&goal_point, &Pl_objp->pos);
12068 //      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));
12069
12070 }
12071 */
12072
12073 #define CHASE_CIRCLE_DIST               100.0f
12074
12075 void ai_chase_circle(object *objp)
12076 {
12077         float           dist_to_goal;
12078         float           target_speed;
12079         vector  goal_point;
12080         ship_info       *sip;
12081         ai_info         *aip;
12082
12083         sip = &Ship_info[Ships[Pl_objp->instance].ship_info_index];
12084
12085         target_speed = sip->max_speed/4.0f;
12086         aip = &Ai_info[Ships[Pl_objp->instance].ai_index];
12087
12088         Assert(vm_vec_mag(&aip->goal_point) >= 0.0f);           //      Supposedly detects bogus vector
12089
12090         goal_point = aip->goal_point;
12091
12092         if (aip->ignore_objnum == UNUSED_OBJNUM) {
12093                 dist_to_goal = vm_vec_dist_quick(&aip->goal_point, &objp->pos);
12094
12095                 if (dist_to_goal > 2*CHASE_CIRCLE_DIST) {
12096                         vector  vec_to_goal;
12097                         //      Too far from circle goal, create a new goal point.
12098                         vm_vec_normalized_dir(&vec_to_goal, &aip->goal_point, &objp->pos);
12099                         vm_vec_scale_add(&aip->goal_point, &objp->pos, &vec_to_goal, CHASE_CIRCLE_DIST);
12100                 }
12101
12102                 goal_point = aip->goal_point;
12103         } else if (is_ignore_object(aip, aip->ignore_objnum)) {
12104                 object  *ignore_objp = &Objects[aip->ignore_objnum];
12105
12106                 vector  tvec1;
12107                 float           dist;
12108
12109                 dist = vm_vec_normalized_dir(&tvec1, &Pl_objp->pos, &ignore_objp->pos);
12110
12111                 if (dist < ignore_objp->radius*2 + 1500.0f) {
12112                         vm_vec_scale_add(&goal_point, &Pl_objp->pos, &tvec1, ignore_objp->radius*2 + 1400.0f);
12113                         if (dist < ignore_objp->radius*2 + 1300.0f)
12114                                 target_speed = sip->max_speed * (1.25f - dist/(ignore_objp->radius*2 + 1500.0f));
12115                 }
12116         }
12117
12118         Assert(vm_vec_mag(&aip->goal_point) >= 0.0f);           //      Supposedly detects bogus vector
12119
12120         turn_towards_tangent(Pl_objp, &goal_point, 10*objp->radius + 200.0f);
12121
12122         set_accel_for_target_speed(Pl_objp, target_speed);
12123
12124 }
12125
12126 #define SHIELD_BALANCE_RATE     0.2f            //      0.1f -> takes 10 seconds to equalize shield.
12127
12128 //      Transfer shield energy to most recently hit section from others.
12129 void ai_transfer_shield(object *objp, int quadrant_num)
12130 {
12131         int     i;
12132         float   transfer_amount;
12133         float   transfer_delta;
12134         ship_info       *sip;
12135         float   max_quadrant_strength;
12136
12137         sip = &Ship_info[Ships[objp->instance].ship_info_index];
12138         max_quadrant_strength = sip->shields/MAX_SHIELD_SECTIONS;
12139
12140         transfer_amount = 0.0f;
12141         transfer_delta = (SHIELD_BALANCE_RATE/2) * max_quadrant_strength;
12142
12143         if (objp->shields[quadrant_num] + (MAX_SHIELD_SECTIONS-1)*transfer_delta > max_quadrant_strength)
12144                 transfer_delta = (max_quadrant_strength - objp->shields[quadrant_num])/(MAX_SHIELD_SECTIONS-1);
12145
12146         for (i=0; i<MAX_SHIELD_SECTIONS; i++)
12147                 if (i != quadrant_num) {
12148                         if (objp->shields[i] >= transfer_delta) {
12149                                 objp->shields[i] -= transfer_delta;
12150                                 transfer_amount += transfer_delta;
12151                         } else {
12152                                 transfer_amount += objp->shields[i];
12153                                 objp->shields[i] = 0.0f;
12154                         }
12155                 }
12156
12157         objp->shields[quadrant_num] += transfer_amount;
12158 }
12159
12160 void ai_balance_shield(object *objp)
12161 {
12162         int     i;
12163         float   shield_strength_avg;
12164         float   delta;
12165
12166
12167         shield_strength_avg = get_shield_strength(objp)/MAX_SHIELD_SECTIONS;
12168
12169         delta = SHIELD_BALANCE_RATE * shield_strength_avg;
12170
12171         for (i=0; i<MAX_SHIELD_SECTIONS; i++)
12172                 if (objp->shields[i] < shield_strength_avg) {
12173                         add_shield_strength(objp, delta);
12174                         if (objp->shields[i] > shield_strength_avg)
12175                                 objp->shields[i] = shield_strength_avg;
12176                 } else {
12177                         add_shield_strength(objp, -delta);
12178                         if (objp->shields[i] < shield_strength_avg)
12179                                 objp->shields[i] = shield_strength_avg;
12180                 }
12181 }
12182
12183 //      Manage the shield for this ship.
12184 //      Try to max out the side that was most recently hit.
12185 void ai_manage_shield(object *objp, ai_info *aip)
12186 {
12187         ship_info *sip;
12188
12189         sip = &Ship_info[Ships[objp->instance].ship_info_index];
12190
12191         if (timestamp_elapsed(aip->shield_manage_timestamp)) {
12192                 float           delay;
12193
12194                 //      Scale time until next manage shield based on Skill_level.
12195                 //      Ships on player's team are treated as if Skill_level is average.
12196                 if (Ships[objp->instance].team != Player_ship->team){
12197                         delay = Shield_manage_delays[Game_skill_level];
12198                 } else {
12199                         delay = Shield_manage_delays[NUM_SKILL_LEVELS/2];
12200                 }
12201
12202                 //      Scale between 1x and 3x based on ai_class
12203                 delay = delay + delay * (float) (3*(Num_ai_classes - aip->ai_class - 1) / (Num_ai_classes - 1));
12204                 aip->shield_manage_timestamp = timestamp((int) (delay * 1000.0f));
12205
12206                 if (sip->flags & SIF_SMALL_SHIP) {
12207                         if (Missiontime - aip->last_hit_time < F1_0*10)
12208                                 ai_transfer_shield(objp, aip->last_hit_quadrant);
12209                         else
12210                                 ai_balance_shield(objp);
12211                 }
12212
12213                 // 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]));
12214         }
12215 }
12216
12217 //      See if object *objp should evade an incoming missile.
12218 //      *aip is the ai_info pointer within *objp.
12219 void ai_maybe_evade_locked_missile(object *objp, ai_info *aip)
12220 {
12221         ship                    *shipp;
12222         ship_info       *sip;
12223
12224         shipp = &Ships[objp->instance];
12225         sip = &Ship_info[shipp->ship_info_index];
12226
12227         //      Only small ships evade an incoming missile.  Why would a capital ship try to swerve?
12228         if (!(Ship_info[Ships[objp->instance].ship_info_index].flags & SIF_SMALL_SHIP)) {
12229                 return;
12230         }
12231
12232         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
12233                 return;
12234         }
12235
12236         if (aip->nearest_locked_object != -1) {
12237                 object  *missile_objp;
12238
12239                 missile_objp = &Objects[aip->nearest_locked_object];
12240
12241                 if (Weapons[missile_objp->instance].homing_object != objp) {
12242                         //nprintf(("AI", "\nMissile lost home!"));
12243                         aip->nearest_locked_object = -1;
12244                         return;
12245                 }
12246
12247                 if ((missile_objp->type == OBJ_WEAPON) && (Weapon_info[Weapons[missile_objp->instance].weapon_info_index].wi_flags & WIF_HOMING)) {
12248                         float dist = vm_vec_dist_quick(&missile_objp->pos, &objp->pos);
12249                         float dist2 = 4.0f  * vm_vec_mag_quick(&missile_objp->phys_info.vel);                   
12250                         if (dist < dist2) {
12251                                 switch (aip->mode) {
12252                                 //      If in AIM_STRAFE mode, don't evade if parent of weapon is targeted ship.
12253                                 case AIM_STRAFE:
12254                                         if ((missile_objp->parent != -1) && (missile_objp->parent == aip->target_objnum)) {
12255                                                 ;
12256                                         } else {
12257                                                 ;               //      Alan -- If you want to handle incoming weapons from someone other than the ship
12258                                                                 //      the strafing ship is attacking, do it here.
12259                                         }
12260                                         break;
12261                                 case AIM_CHASE:
12262                                         //      Don't always go into evade weapon mode.  Usually, a countermeasure gets launched.
12263                                         // If low on countermeasures, more likely to try to evade.  If 8+, never evade due to low cmeasures.
12264                                         if (((((Missiontime >> 18) ^ OBJ_INDEX(objp)) & 3) == 0) || 
12265                                                 (objp->phys_info.speed < 40.0f) ||
12266                                                 (frand() < 1.0f - (float) shipp->cmeasure_count/8.0f)) {
12267                                                 if (aip->submode != SM_ATTACK_FOREVER) {        //      SM_ATTACK_FOREVER means engines blown.
12268                                                         aip->submode = SM_EVADE_WEAPON;
12269                                                         aip->submode_start_time = Missiontime;
12270                                                 }
12271                                         }
12272                                         break;
12273                                 case AIM_DOCK:  //      Ships in dock mode can evade iif they are not currently repairing or docked.
12274                                         if (aip->ai_flags & (AIF_REPAIRING | AIF_DOCKED))
12275                                                 break;
12276                                 case AIM_GUARD:
12277                                         //      If in guard mode and far away from guard object, don't pursue guy that hit me.
12278                                         if ((aip->guard_objnum != -1) && (aip->guard_signature == Objects[aip->guard_objnum].signature)) {
12279                                                 if (vm_vec_dist_quick(&objp->pos, &Objects[aip->guard_objnum].pos) > 500.0f) {
12280                                                         return;
12281                                                 }
12282                                         }
12283                                 case AIM_EVADE:
12284                                 case AIM_GET_BEHIND:
12285                                 case AIM_STAY_NEAR:
12286                                 case AIM_STILL:
12287                                 case AIM_AVOID:
12288                                 case AIM_WAYPOINTS:
12289                                 case AIM_NONE:
12290                                 case AIM_BIGSHIP:
12291                                 case AIM_PATH:
12292                                 case AIM_BE_REARMED:
12293                                 case AIM_SAFETY:
12294                                 case AIM_BAY_EMERGE:
12295                                         aip->active_goal = AI_ACTIVE_GOAL_DYNAMIC;
12296                                         aip->previous_mode = aip->mode;
12297                                         aip->previous_submode = aip->submode;
12298                                         aip->mode = AIM_EVADE_WEAPON;
12299                                         aip->submode = -1;
12300                                         aip->submode_start_time = Missiontime;
12301                                         aip->mode_time = timestamp(MAX_EVADE_TIME);     //      Max time to evade.
12302                                         //nprintf(("AI", "%s Evade weapon in frame #%i\n", Ships[objp->instance].ship_name, AI_FrameCount));
12303                                         break;
12304                                 case AIM_EVADE_WEAPON:          //      Note: We don't want to change mode on another evasion, or previous_mode will get bashed.
12305                                 case AIM_PLAY_DEAD:
12306                                 case AIM_BAY_DEPART:
12307                                 case AIM_SENTRYGUN:
12308                                         break;
12309                                 case AIM_WARP_OUT:
12310                                         break;
12311                                 default:
12312                                         Int3();                 //      Hey, what mode is it?
12313                                         break;
12314                                 }
12315                         }
12316                 } else {
12317                         aip->nearest_locked_object = -1;
12318                 }
12319         }
12320 }
12321
12322 //      Maybe evade a dumbfire weapon that was fired when Pl_objp was targeted.
12323 //      Have an 80% chance of evading in a second
12324 void maybe_evade_dumbfire_weapon(ai_info *aip)
12325 {
12326         //      Only small ships evade an incoming missile.  Why would a capital ship try to swerve?
12327         if (!(Ship_info[Ships[Pl_objp->instance].ship_info_index].flags & SIF_SMALL_SHIP)) {
12328                 return;
12329         }
12330
12331         //      Make sure in a mode in which we evade dumbfire weapons.
12332         switch (aip->mode) {
12333         case AIM_CHASE:
12334                 if (aip->submode == SM_ATTACK_FOREVER) {
12335                         return;
12336                 }
12337         case AIM_GUARD:
12338                 //      If in guard mode and far away from guard object, don't pursue guy that hit me.
12339                 if ((aip->guard_objnum != -1) && (aip->guard_signature == Objects[aip->guard_objnum].signature)) {
12340                         if (vm_vec_dist_quick(&Objects[Ships[aip->shipnum].objnum].pos, &Objects[aip->guard_objnum].pos) > 500.0f) {
12341                                 return;
12342                         }
12343                 }
12344         case AIM_STILL:
12345         case AIM_STAY_NEAR:
12346         case AIM_EVADE:
12347         case AIM_GET_BEHIND:
12348         case AIM_AVOID:
12349         case AIM_PATH:
12350         case AIM_NONE:
12351         case AIM_WAYPOINTS:
12352         case AIM_SAFETY:
12353                 break;
12354         case AIM_STRAFE:
12355         case AIM_BIGSHIP:
12356         case AIM_DOCK:
12357         case AIM_PLAY_DEAD:
12358         case AIM_EVADE_WEAPON:
12359         case AIM_BAY_EMERGE:
12360         case AIM_BAY_DEPART:
12361         case AIM_SENTRYGUN:
12362         case AIM_WARP_OUT:
12363                 return;
12364         default:
12365                 Int3(); //      Bogus mode!
12366                 return;
12367         }
12368
12369         if (is_instructor(&Objects[Ships[aip->shipnum].objnum]))
12370                 return; //      Instructor doesn't evade.
12371
12372         float t = ai_endangered_by_weapon(aip);
12373         if ((t > 0.0f) && (t < 1.0f)) {
12374         // Check if this weapon is from a large ship Pl_objp is attacking... if so, enter strafe mode
12375                 if ( ai_big_maybe_enter_strafe_mode(Pl_objp, aip->danger_weapon_objnum) ) {
12376                         return;
12377                 }
12378
12379                 switch (aip->mode) {
12380                 case AIM_CHASE:
12381                         switch (aip->submode) {
12382                         case SM_EVADE:
12383                         case SM_ATTACK_FOREVER:
12384                         case SM_AVOID:
12385                         case SM_GET_AWAY:
12386                         case SM_EVADE_WEAPON:
12387                                 break;
12388                         default:
12389                                 if (ai_near_full_strength(Pl_objp, &Ship_info[Ships[Pl_objp->instance].ship_info_index])) {
12390                                         //mprintf(("Ship %s entered super mode at %7.3f\n", Ships[Pl_objp->instance].ship_name, 1.0f * Missiontime / (1<<16)));
12391                                         aip->submode = SM_SUPER_ATTACK;
12392                                         aip->submode_start_time = Missiontime;
12393                                         aip->last_attack_time = Missiontime;
12394                                 } else {
12395                                         //mprintf(("Ship %s entered dumbfire evade mode at %7.3f\n", Ships[Pl_objp->instance].ship_name, 1.0f * Missiontime / (1<<16)));
12396                                         aip->submode = SM_EVADE_WEAPON;
12397                                         aip->submode_start_time = Missiontime;
12398                                 }
12399                                 break;
12400                         }
12401                         break;
12402                 case AIM_GUARD:
12403                 case AIM_STILL:
12404                 case AIM_STAY_NEAR:
12405                 case AIM_EVADE:
12406                 case AIM_GET_BEHIND:
12407                 case AIM_AVOID:
12408                 case AIM_PATH:
12409                 case AIM_NONE:
12410                 case AIM_WAYPOINTS:
12411                 case AIM_SAFETY:
12412                         if (!(aip->ai_flags & (AIF_NO_DYNAMIC | AIF_KAMIKAZE)) && (Ship_info[Ships[aip->shipnum].ship_info_index].flags & SIF_SMALL_SHIP)) {
12413                                 aip->active_goal = AI_ACTIVE_GOAL_DYNAMIC;
12414                                 aip->previous_mode = aip->mode;
12415                                 aip->previous_submode = aip->submode;
12416                                 aip->mode = AIM_EVADE_WEAPON;
12417                                 aip->submode = -1;
12418                                 aip->submode_start_time = Missiontime;
12419                                 aip->mode_time = timestamp(MAX_EVADE_TIME);     //      Evade for up to five seconds.
12420                         }
12421                         break;
12422                 case AIM_STRAFE:
12423                 case AIM_BIGSHIP:
12424                 case AIM_DOCK:
12425                 case AIM_PLAY_DEAD:
12426                 case AIM_EVADE_WEAPON:
12427                 case AIM_BAY_EMERGE:
12428                 case AIM_BAY_DEPART:
12429                 case AIM_SENTRYGUN:
12430                         break;
12431                 default:
12432                         Int3(); //      Bogus mode!
12433                 }
12434         }
12435 }
12436
12437 // determine what path to use when emerging from a fighter bay
12438 // input:       pl_objp =>      pointer to object for ship that is arriving
12439 //                              pos             =>      output parameter, it is the starting world pos for path choosen
12440 //                              fvec            =>      output parameter, this is the forward vector that ship has when arriving
12441 //
12442 // exit:                -1              =>      path could not be located
12443 //                               0              => success
12444 int ai_acquire_emerge_path(object *pl_objp, int parent_objnum, vector *pos, vector *fvec)
12445 {
12446         int                     path_index, sb_path_index;
12447         ship                    *parent_sp = NULL;
12448         polymodel       *pm;
12449         ai_info         *aip;
12450         ship_bay                *sb;
12451         pnode                   *pnp;
12452         vector          *next_point;
12453
12454         aip = &Ai_info[Ships[pl_objp->instance].ai_index];
12455
12456         if ( parent_objnum == -1 ) {
12457                 Int3();
12458                 return -1;
12459         }
12460
12461         parent_sp = &Ships[Objects[parent_objnum].instance];
12462
12463         Assert(parent_sp != NULL);
12464         pm = model_get( parent_sp->modelnum );
12465         sb = pm->ship_bay;
12466
12467         if ( sb == NULL ) 
12468                 return -1;
12469
12470         if ( sb->num_paths <= 0 ) 
12471                 return -1;
12472
12473         // try to find a bay path that is not taken
12474         path_index = -1;
12475         sb_path_index = Ai_last_arrive_path++;
12476
12477         if ( sb_path_index >= sb->num_paths ) {
12478                 sb_path_index=0;
12479                 Ai_last_arrive_path=0;
12480         }
12481
12482         path_index = sb->paths[sb_path_index];
12483         if ( path_index == -1 ) 
12484                 return -1;
12485
12486         // create the path for pl_objp to follow
12487         create_model_exit_path(pl_objp, &Objects[parent_objnum], path_index, pm->paths[path_index].nverts);
12488         
12489         // Set this flag, so we don't bother recreating the path... we won't need to update the path
12490         // that has just been created.
12491 //      aip->ai_flags |= AIF_USE_STATIC_PATH;
12492
12493         // now return to the caller what the starting world pos and starting fvec for the ship will be
12494         Assert((aip->path_start >= 0) && (aip->path_start < MAX_PATH_POINTS));
12495         pnp = &Path_points[aip->path_start];
12496         *pos = pnp->pos;
12497
12498         // calc the forward vector using the starting two points of the path
12499         pnp = &Path_points[aip->path_start+1];
12500         next_point = &pnp->pos;
12501         vm_vec_normalized_dir(fvec, next_point, pos);
12502
12503         // record the parent objnum, since we'll need it once we're done with following the path
12504         aip->goal_objnum = parent_objnum;
12505         aip->goal_signature = Objects[parent_objnum].signature;
12506         aip->mode = AIM_BAY_EMERGE;
12507         aip->submode_start_time = Missiontime;
12508
12509         // set up starting vel
12510         vector vel;
12511         float speed;
12512         speed = Ship_info[Ships[pl_objp->instance].ship_info_index].max_speed;
12513         vel = *fvec;
12514         vm_vec_scale( &vel, speed );
12515         pl_objp->phys_info.vel = vel;
12516         pl_objp->phys_info.desired_vel = vel;
12517         pl_objp->phys_info.prev_ramp_vel.x = 0.0f;
12518         pl_objp->phys_info.prev_ramp_vel.y = 0.0f;
12519         pl_objp->phys_info.prev_ramp_vel.z = speed;
12520         pl_objp->phys_info.forward_thrust = 0.0f;               // How much the forward thruster is applied.  0-1.
12521
12522         return 0;       
12523 }
12524
12525 // clean up path data used for emerging from a fighter bay
12526 void ai_emerge_bay_path_cleanup(ai_info *aip)
12527 {
12528         aip->path_start = -1;
12529         aip->path_cur = -1;
12530         aip->path_length = 0;
12531         aip->mode = AIM_NONE;
12532 }
12533
12534 // handler for AIM_BAY_EMERGE
12535 void ai_bay_emerge()
12536 {
12537         ai_info *aip;
12538         int             parent_died=0;
12539
12540         aip = &Ai_info[Ships[Pl_objp->instance].ai_index];
12541
12542         // if no path to follow, leave this mode
12543         if ( aip->path_start < 0 ) {
12544                 aip->mode = AIM_NONE;
12545                 return;
12546         }
12547
12548         // ensure parent ship is still alive
12549         if ( aip->goal_objnum < 0 ) {
12550                 parent_died=1;
12551         } 
12552         if ( !parent_died ) {
12553                 if ( Objects[aip->goal_objnum].signature != aip->goal_signature ) {
12554                         parent_died=1;
12555                 }
12556         }
12557
12558         if ( !parent_died ) {
12559                 Assert(Objects[aip->goal_objnum].type == OBJ_SHIP);
12560                 if ( Ships[Objects[aip->goal_objnum].instance].flags & SF_DYING ) {
12561                         parent_died = 1;
12562                 }
12563         }
12564
12565         if ( parent_died ) {
12566                 ai_emerge_bay_path_cleanup(aip);
12567                 return;
12568         }
12569
12570         // follow the path to the final point
12571         ai_path();
12572
12573         // New test: must have been in AI_EMERGE mode for at least 10 seconds, and be a minimum distance from the start point
12574         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)) {
12575                 // erase path
12576                 ai_emerge_bay_path_cleanup(aip);
12577         }
12578
12579         // 2-25-99: Need this check to fix an assert for supercap ships... maybe we'll only do this check for supercaps 
12580         if (aip->path_cur > (aip->path_start+aip->path_length-1)) {
12581                 ai_emerge_bay_path_cleanup(aip);
12582         }       
12583 }
12584
12585 // Select the closest depart path
12586 //
12587 //      input:  aip     =>              ai info pointer to ship seeking to depart
12588 //                              pm              =>              pointer to polymodel for the ship contining the ship bay/depart paths
12589 //
12590 // exit:                >=0     =>              ship bay path index for depart path (ie index into sb->paths[])
12591 //                              -1              =>              no path could be found
12592 //
12593 // NOTE: this function should only be used for calculating closest depart paths for ai mode
12594 //                      AI_BAY_DEPART.  It tries to find the closest path that isn't already in use
12595 int ai_find_closest_depart_path(ai_info *aip, polymodel *pm)
12596 {
12597         int                     i, j, best_path, best_free_path;
12598         float                   dist, min_dist, min_free_dist;
12599         vector          *source;
12600         model_path      *mp;
12601         ship_bay                *sb;
12602
12603         sb = pm->ship_bay;
12604
12605         best_free_path = best_path = -1;
12606         min_free_dist = min_dist = 1e20f;
12607         Assert(aip->shipnum >= 0);
12608         source = &Objects[Ships[aip->shipnum].objnum].pos;
12609
12610         for ( i = 0; i < sb->num_paths; i++ ) {
12611
12612
12613                 mp = &pm->paths[sb->paths[i]];
12614                 for ( j = 0; j < mp->nverts; j++ ) {
12615                         dist = vm_vec_dist_squared(source, &mp->verts[j].pos);
12616
12617                         if ( dist < min_dist ) {
12618                                 min_dist = dist;
12619                                 best_path = i;
12620                         }
12621
12622                         // If this is a free path
12623                         if ( !(sb->depart_flags & (1<<i)) ) {
12624                                 if ( dist < min_free_dist ) {
12625                                         min_free_dist = dist;
12626                                         best_free_path = i;
12627                                 }
12628                         }
12629                 }
12630         }
12631
12632         if ( best_free_path >= 0 ) {
12633                 return best_free_path;          
12634         }
12635
12636         return best_path;
12637 }
12638
12639 // determine what path to use when trying to depart to a fighter bay
12640 // NOTE: this should be called when AIM_BAY_DEPART mode is set
12641 //
12642 // input:       pl_objp =>      pointer to object for ship that is departing
12643 //
12644 // exit:                -1      =>      could not find depart path
12645 //                              0       => found depart path
12646 int ai_acquire_depart_path(object *pl_objp, int parent_objnum)
12647 {
12648         int                     objnum, path_index;
12649         polymodel       *pm;
12650         ai_info         *aip;
12651         ship                    *sp;
12652         ship_bay                *sb;
12653
12654         aip = &Ai_info[Ships[pl_objp->instance].ai_index];
12655
12656         if ( parent_objnum == -1 ) {
12657                 ship_obj        *so;
12658
12659                 // for now just locate a captial ship on the same team:
12660                 so = GET_FIRST(&Ship_obj_list);
12661                 objnum = -1;
12662                 while(so != END_OF_LIST(&Ship_obj_list)){
12663                         sp = &Ships[Objects[so->objnum].instance];
12664                         if ( (Ship_info[sp->ship_info_index].flags & (SIF_HUGE_SHIP)) && (sp->team == Ships[pl_objp->instance].team) ) {
12665                                 objnum = so->objnum;
12666                                 break;
12667                         }
12668                         so = GET_NEXT(so);
12669                 } 
12670         } else {
12671                 objnum = parent_objnum;
12672         }
12673
12674         aip->path_start = -1;
12675
12676         if ( objnum == -1 )
12677                 return -1;
12678
12679         pm = model_get( Ships[Objects[objnum].instance].modelnum );
12680         sb = pm->ship_bay;
12681
12682         if ( sb == NULL ) 
12683                 return -1;
12684         if ( sb->num_paths <= 0 ) 
12685                 return -1;
12686
12687 /*
12688         
12689         path_index = -1;
12690         for ( i = 0; i < sb->num_paths; i++ ) {
12691                 if ( !(sb->depart_flags & (1<<i)) ) {
12692                         sb->depart_flags |= (1<<i);
12693                         path_index = sb->paths[i];
12694                         aip->submode_parm0 = i;                 // use mode-specific parameter to record ship bay path index
12695                         break;
12696                 }
12697         }
12698 */
12699         
12700         // take the closest path we can find
12701         int ship_bay_path;
12702         ship_bay_path = ai_find_closest_depart_path(aip, pm);
12703         path_index = sb->paths[ship_bay_path];
12704         aip->submode_parm0 = ship_bay_path;
12705         sb->depart_flags |= (1<<ship_bay_path);
12706
12707         if ( path_index == -1 ) {
12708                 return -1;
12709         }
12710
12711         Assert(pm->n_paths > path_index);
12712         ai_find_path(pl_objp, objnum, path_index, 0);
12713
12714         // Set this flag, so we don't bother recreating the path... we won't need to update the path
12715         // that has just been created.
12716         aip->ai_flags &= ~AIF_USE_STATIC_PATH;
12717
12718         aip->goal_objnum = objnum;
12719         aip->goal_signature = Objects[objnum].signature;
12720         aip->mode = AIM_BAY_DEPART;
12721
12722         Ships[pl_objp->instance].flags |= SF_DEPART_DOCKBAY;
12723         return 0;
12724 }
12725
12726 // handler for AIM_BAY_DEPART
12727 void ai_bay_depart()
12728 {
12729         ai_info *aip;
12730
12731         aip = &Ai_info[Ships[Pl_objp->instance].ai_index];
12732
12733         // if no path to follow, leave this mode
12734         if ( aip->path_start < 0 ) {
12735                 aip->mode = AIM_NONE;
12736                 return;
12737         }
12738
12739         // check if parent ship still exists, if not abort depart 
12740         if ( aip->goal_signature != Objects[aip->goal_objnum].signature ) {
12741                 aip->mode = AIM_NONE;
12742                 return;
12743         }
12744
12745         // follow the path to the final point
12746         ai_path();
12747
12748         // if the final point is reached, let default AI take over
12749         if ( aip->path_cur >= (aip->path_start+aip->path_length) ) {
12750                 polymodel       *pm;
12751                 ship_bay                *sb;
12752
12753                 pm = model_get( Ships[Objects[aip->goal_objnum].instance].modelnum );
12754                 sb = pm->ship_bay;
12755                 if ( sb != NULL ) {
12756                         sb->depart_flags &= ~(1<<aip->submode_parm0);
12757                 }
12758
12759                 // make ship disappear
12760                 Pl_objp->flags |= OF_SHOULD_BE_DEAD;
12761                 ship_departed( Pl_objp->instance );
12762
12763                 // clean up path stuff
12764                 aip->path_start = -1;
12765                 aip->path_cur = -1;
12766                 aip->path_length = 0;
12767                 aip->mode = AIM_NONE;
12768         }
12769 }
12770
12771 // Handler for AIM_SENTRYGUN.  This AI mode is for sentry guns only (ie floating turrets).
12772 void ai_sentrygun()
12773 {
12774         // Nothing to do here.  Turret firing is handled via process_subobjects().
12775         // If you want the sentry guns to do anything beyond firing their turrets at enemies, add it here!
12776 }
12777
12778 //      --------------------------------------------------------------------------
12779 //      Execute behavior given by aip->mode.
12780 void ai_execute_behavior(ai_info *aip)
12781 {
12782         switch (aip->mode) {
12783         case AIM_CHASE:
12784                 if (En_objp) {
12785                         ai_chase();
12786                 } else if (aip->submode == SM_EVADE_WEAPON) {
12787                         evade_weapon();
12788                         // maybe reset submode
12789                         if (aip->danger_weapon_objnum == -1) {
12790                                 aip->submode = SM_ATTACK;
12791                                 aip->submode_start_time = Missiontime;
12792                                 aip->last_attack_time = Missiontime;
12793                         }
12794                 } else {
12795                         //      Don't circle if this is the instructor.
12796                         ship    *shipp = &Ships[aip->shipnum];
12797                         ship_info       *sip = &Ship_info[shipp->ship_info_index];
12798
12799                         if (strnicmp(shipp->ship_name, INSTRUCTOR_SHIP_NAME, strlen(INSTRUCTOR_SHIP_NAME))) {
12800                                 if (sip->flags & (SIF_BIG_SHIP | SIF_HUGE_SHIP)) {
12801                                         aip->mode = AIM_NONE;
12802                                 } else {
12803                                         ai_chase_circle(Pl_objp);
12804                                 }
12805                         }
12806                 }
12807                 break;
12808         case AIM_EVADE:
12809                 if (En_objp) {
12810                         ai_evade();
12811                 } else {
12812                         vector  tvec;
12813                         vm_vec_scale_add(&tvec, &Pl_objp->pos, &Pl_objp->orient.rvec, 100.0f);
12814                         turn_towards_point(Pl_objp, &tvec, NULL, 0.0f);
12815                         accelerate_ship(aip, 0.5f);
12816                 }
12817                 break;
12818         case AIM_STILL:
12819                 ai_still();
12820                 break;
12821         case AIM_STAY_NEAR:
12822                 ai_stay_near();
12823                 break;
12824         case AIM_GUARD:
12825                 ai_guard();
12826                 break;
12827         case AIM_WAYPOINTS:
12828                 ai_waypoints();
12829                 break;
12830         case AIM_DOCK:
12831                 ai_dock();
12832                 break;
12833         case AIM_NONE:
12834                 // ai_formation();
12835                 break;
12836         case AIM_BIGSHIP:
12837                 ai_big_ship(Pl_objp);
12838                 break;
12839         case AIM_PATH: {
12840                 int path_num;
12841                 path_num = ai_return_path_num_from_dockbay(&Objects[aip->goal_objnum], 0);
12842                 ai_find_path(Pl_objp, aip->goal_objnum, path_num, 0);
12843                 ai_path();
12844                 break;
12845         }
12846         case AIM_SAFETY:
12847                 ai_safety();
12848                 break;
12849         case AIM_EVADE_WEAPON:
12850                 evade_weapon();
12851                 break;
12852         case AIM_STRAFE:
12853                 if (En_objp) {
12854                         Assert(En_objp->type == OBJ_SHIP);
12855                         ai_big_strafe();        // strafe a big ship
12856                 } else {
12857                         aip->mode = AIM_NONE;
12858                 }
12859                 break;
12860         case AIM_BAY_EMERGE:
12861                 ai_bay_emerge();
12862                 break;
12863         case AIM_BAY_DEPART:
12864                 ai_bay_depart();
12865                 break;
12866         case AIM_SENTRYGUN:
12867                 ai_sentrygun();
12868                 break;
12869         case AIM_WARP_OUT:
12870                 break;          //      Note, handled directly from ai_frame().
12871         default:
12872                 Int3();         //      This should never happen -- MK, 5/12/97 
12873                 break;
12874         }
12875
12876         if ( !(ship_get_SIF(aip->shipnum) & SIF_NOT_FLYABLE) ) {
12877                 maybe_evade_dumbfire_weapon(aip);
12878         }
12879 }
12880
12881 //      Auxiliary function for maybe_request_support.
12882 //      Return 1 if subsystem "type" is worthy of repair, else return 0.
12883 //      Since subsystems cannot be repaired if they are at 0 strength, don't return 1 if subsystem is dead.
12884 int mrs_subsystem(ship *shipp, int type)
12885 {
12886         float   t;
12887
12888         t = ship_get_subsystem_strength(shipp, type);
12889
12890         if (t > 0.0f) {
12891                 return (int) ((1.0f - t) * 3);
12892         } else {
12893                 return 3;
12894         }
12895 }
12896
12897 //      Return number of ships on *objp's team that are currently rearming.
12898 int num_allies_rearming(object *objp)
12899 {
12900         ship_obj        *so;
12901         int             team;
12902         int             count = 0;
12903
12904         team = Ships[objp->instance].team;
12905
12906         for ( so = GET_FIRST(&Ship_obj_list); so != END_OF_LIST(&Ship_obj_list); so = GET_NEXT(so) ) {
12907                 object  *A;
12908                 
12909                 Assert (so->objnum != -1);
12910                 A = &Objects[so->objnum];
12911
12912                 if (Ships[A->instance].team == team) {
12913                         if (Ai_info[Ships[A->instance].ai_index].ai_flags & (AIF_REPAIRING | AIF_AWAITING_REPAIR)) {
12914                                 count++;
12915                         }
12916                 }
12917         }
12918
12919         return count;
12920 }
12921
12922
12923 //      Maybe ship *objp should request support (rearm/repair).
12924 //      If it does, return TRUE, else return FALSE.
12925 int maybe_request_support(object *objp)
12926 {
12927         ship_info       *sip;
12928         ship                    *shipp;
12929         ai_info         *aip;
12930         int                     desire;
12931
12932         Assert(objp->type == OBJ_SHIP);
12933         shipp = &Ships[objp->instance];
12934         aip = &Ai_info[shipp->ai_index];
12935         sip = &Ship_info[shipp->ship_info_index];
12936
12937         if (!timestamp_elapsed(aip->next_rearm_request_timestamp))
12938                 return 0;
12939
12940         //      Only fighters and bombers request support.
12941         if (!(sip->flags & (SIF_FIGHTER | SIF_BOMBER)))
12942                 return 0;
12943
12944         //      A ship that is currently awaiting does not need support!
12945         if (aip->ai_flags & (AIF_AWAITING_REPAIR | AIF_BEING_REPAIRED))
12946                 return 0;
12947
12948         if (!is_support_allowed(objp))
12949                 return 0;
12950
12951         //if (shipp->team != TEAM_FRIENDLY)
12952         //      return 0;
12953
12954         //      Compute a desire value.
12955         //      Desire of 0 means no reason to request support.
12956         //      1 is slight, 2 more, etc.  Maximum is around 20.  Anything larger than 3 is pretty strong.
12957         desire = 0;
12958
12959         //      Set desire based on hull strength.
12960         //      No: We no longer repair hull, so this would cause repeated repair requests.
12961         //desire += 6 - (int) ((objp->hull_strength/sip->initial_hull_strength) * 6.0f);
12962
12963         //      Set desire based on key subsystems.
12964         desire += 2*mrs_subsystem(shipp, SUBSYSTEM_ENGINE);     //      Note, disabled engine forces repair request, regardless of nearby enemies.
12965         desire += mrs_subsystem(shipp, SUBSYSTEM_COMMUNICATION);
12966         desire += mrs_subsystem(shipp, SUBSYSTEM_WEAPONS);
12967         desire += mrs_subsystem(shipp, SUBSYSTEM_SENSORS);
12968
12969         //      Set desire based on percentage of secondary weapons.
12970         ship_weapon *swp = &shipp->weapons;
12971
12972         for ( int i = 0; i < swp->num_secondary_banks; i++ ) {
12973                 if (swp->secondary_bank_start_ammo[i] > 0) {
12974 //                      float r = (float) swp->secondary_bank_ammo[i]*Weapon_info[swp->secondary_bank_weapons[i]].cargo_size/swp->secondary_bank_capacity[i];
12975                         float r = (float) swp->secondary_bank_ammo[i]/swp->secondary_bank_start_ammo[i];
12976                         desire += (int) ((1.0f - r) * 3.0f);
12977                 }
12978         }
12979
12980         //      If no reason to repair, don't bother to see if it's safe to repair.
12981         if (desire == 0){
12982                 return 0;
12983         }
12984
12985         //      Compute danger threshold.
12986         //      Balance this with desire and maybe request support.
12987         if (ai_good_time_to_rearm( objp )) {
12988                 ai_issue_rearm_request(objp);
12989                 return 1;
12990         } else if (num_allies_rearming(objp) < 2) {
12991                 if (desire >= 8) {      //      guarantees disabled will cause repair request
12992                         ai_issue_rearm_request(objp);
12993                 } else if (desire >= 3) {               //      >= 3 means having a single subsystem fully blown will cause repair.
12994                         int     count;
12995                         int objnum = find_nearby_hostile(OBJ_INDEX(objp), get_enemy_team_mask(OBJ_INDEX(objp)), 2000.0f, &count);
12996
12997                         if ((objnum == -1) || (count < 2) || (vm_vec_dist_quick(&objp->pos, &Objects[objnum].pos) > 3000.0f*count/desire)) {
12998                                 ai_issue_rearm_request(objp);
12999                                 return 1;
13000                         } else {
13001                                 //nprintf(("AI", "Would like to rearm, but enemy only %7.3f units away.\n", vm_vec_dist_quick(&objp->pos, &Objects[objnum].pos)));
13002                         }
13003                 }
13004         }
13005
13006         return 0;
13007
13008 }
13009
13010 void ai_set_mode_warp_out(object *objp, ai_info *aip)
13011 {
13012         ai_abort_rearm_request(objp);
13013         if (aip->mode != AIM_WARP_OUT) {
13014                 aip->mode = AIM_WARP_OUT;
13015                 aip->submode = AIS_WARP_1;
13016         }
13017 }
13018
13019 //      Maybe warp ship out.
13020 //      Shivan and HoL fighter/bomber warp out if their weapons subsystems have been destroyed.
13021 void ai_maybe_warp_out(object *objp)
13022 {
13023         ship    *shipp;
13024
13025         // don't do anything if in a training mission.
13026         if ( The_mission.game_type & MISSION_TYPE_TRAINING )
13027                 return;
13028
13029         Assert(objp->type == OBJ_SHIP);
13030
13031         shipp = &Ships[objp->instance];
13032         ai_info *aip = &Ai_info[shipp->ai_index];
13033
13034         if (aip->mode == AIM_WARP_OUT)
13035                 return;
13036
13037         //      If a support ship with no goals and low hull, warp out.  Be sure that there are no pending goals
13038         // in the support ships ai_goal array.  Just process this ships goals.
13039         ship_info       *sip = &Ship_info[shipp->ship_info_index];
13040         if (sip->flags & SIF_SUPPORT) {
13041                 if ( timestamp_elapsed(aip->warp_out_timestamp) ) {
13042                         ai_process_mission_orders( OBJ_INDEX(objp), aip );
13043                         if ( (aip->dock_objnum == -1) && (objp->hull_strength/sip->initial_hull_strength < 0.25f) ) {
13044                                 ai_set_mode_warp_out(objp, aip);
13045                         }
13046                 }
13047         }
13048
13049         //      Friendly don't warp out, they'll eventually request support.
13050         if (shipp->team == TEAM_FRIENDLY)
13051                 return;
13052
13053         if (!(shipp->flags & SF_DEPARTING)) {
13054                 ship_info       *sip;
13055
13056                 sip = &Ship_info[shipp->ship_info_index];
13057                 if (sip->flags & (SIF_FIGHTER | SIF_BOMBER)) {
13058                         if (aip->warp_out_timestamp == 0) {
13059                                 //if (ship_get_subsystem_strength(shipp, SUBSYSTEM_WEAPONS) == 0.0f) {
13060                                 //      aip->warp_out_timestamp = timestamp(((myrand() % 10) + 10) * 1000);
13061                                 //}
13062                         } else if (timestamp_elapsed(aip->warp_out_timestamp)) {
13063                                 ai_set_mode_warp_out(objp, aip);
13064                         }
13065                 }
13066         }
13067 }
13068
13069 //      Warp this ship out.
13070 void ai_warp_out(object *objp)
13071 {
13072         // if dying, don't warp out.
13073         if (Ships[objp->instance].flags & SF_DYING) {
13074                 return;
13075         }
13076
13077         ai_info *aip;
13078
13079         aip = &Ai_info[Ships[objp->instance].ai_index];
13080
13081         switch (aip->submode) {
13082         case AIS_WARP_1:
13083                 aip->force_warp_time = timestamp(10*1000);      //      Try to avoid a collision for up to ten seconds.
13084                 aip->submode = AIS_WARP_2;
13085                 break;
13086         case AIS_WARP_2:                        //      Make sure won't collide with any object.
13087                 if (timestamp_elapsed(aip->force_warp_time) || !collide_predict_large_ship(objp, objp->radius*2.0f + 100.0f)) {
13088                         aip->submode = AIS_WARP_3;
13089
13090                         // maybe recalculate collision pairs.
13091                         if (ship_get_warp_speed(objp) > ship_get_max_speed(&Ships[objp->instance])) {
13092                                 // recalculate collision pairs
13093                                 OBJ_RECALC_PAIRS(objp); 
13094                         }
13095
13096                         aip->force_warp_time = timestamp(4*1000);               //      Try to attain target speed for up to 4 seconds.
13097                 } else {
13098                         vector  goal_point;
13099                         vm_vec_scale_add(&goal_point, &objp->pos, &objp->orient.uvec, 100.0f);
13100                         turn_towards_point(objp, &goal_point, NULL, 0.0f);
13101                         accelerate_ship(aip, 0.0f);
13102                 }
13103                 break;
13104         case AIS_WARP_3:
13105                 //      Rampup desired_vel in here from current to desired velocity and set PF_USE_VEL. (not sure this is the right flag)
13106                 //      desired velocity is computed in shipfx_calculate_warp_time().  See shipfx#572 for sample code.
13107                 float   speed, goal_speed;
13108                 float shipfx_calculate_warp_speed(object*);
13109                 goal_speed = shipfx_calculate_warp_speed(objp);
13110
13111                 // HUGE ships go immediately to AIS_WARP_4
13112                 if (Ship_info[Ships[objp->instance].ship_info_index].flags & SIF_HUGE_SHIP) {
13113                         aip->submode = AIS_WARP_4;
13114                         break;
13115                 }
13116                 //compute_warpout_stuff(objp, &goal_speed, &warp_time, &warp_pos);
13117                 //goal_speed = 80.0f;
13118                 //set_accel_for_target_speed(objp, 40.0f);
13119                 // 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
13120                 speed = goal_speed * flFrametime + objp->phys_info.speed * (1.0f - flFrametime);
13121                 vm_vec_copy_scale(&objp->phys_info.vel, &objp->orient.fvec, speed);
13122                 objp->phys_info.desired_vel = objp->phys_info.vel;
13123                 // nprintf(("AI", "Frame %i, speed = %7.3f, goal = %7.3f\n", Framecount, vm_vec_mag_quick(&objp->phys_info.vel), goal_speed));
13124                 if (timestamp_elapsed(aip->force_warp_time) || (fl_abs(objp->phys_info.speed - goal_speed) < 2.0f))
13125                         aip->submode = AIS_WARP_4;
13126                 break;
13127         case AIS_WARP_4: {
13128                 shipfx_warpout_start(objp);
13129                 aip->submode = AIS_WARP_5;
13130                 break;
13131         }
13132         case AIS_WARP_5:
13133                 break;
13134         default:
13135                 Int3();         //      Illegal submode for warping out.
13136         }
13137 }
13138
13139 //      Return object index of weapon that could produce a shockwave that should be known about to *objp.
13140 //      Return nearest one.
13141 int ai_find_shockwave_weapon(object *objp, ai_info *aip)
13142 {
13143         missile_obj     *mo;
13144         float   nearest_dist = 999999.9f;
13145         int     nearest_index = -1;
13146
13147         for ( mo = GET_NEXT(&Missile_obj_list); mo != END_OF_LIST(&Missile_obj_list); mo = GET_NEXT(mo) ) {
13148                 object          *A;
13149                 weapon          *wp;
13150                 weapon_info     *wip;
13151         
13152                 Assert(mo->objnum >= 0 && mo->objnum < MAX_OBJECTS);
13153                 A = &Objects[mo->objnum];
13154
13155                 Assert(A->type == OBJ_WEAPON);
13156                 Assert((A->instance >= 0) && (A->instance < MAX_WEAPONS));
13157                 wp = &Weapons[A->instance];
13158                 wip = &Weapon_info[wp->weapon_info_index];
13159                 Assert( wip->subtype == WP_MISSILE );
13160
13161                 if (wip->shockwave_speed > 0.0f) {
13162                         float   dist;
13163
13164                         dist = vm_vec_dist_quick(&objp->pos, &A->pos);
13165                         if (dist < nearest_dist) {
13166                                 nearest_dist = dist;
13167                                 nearest_index = mo->objnum;
13168                         }
13169                 }
13170         }
13171
13172         return nearest_index;
13173
13174 }
13175
13176 #define EVADE_SHOCKWAVE_DAMAGE_THRESHOLD                100.0f
13177
13178 //      Tell all ships to avoid a big ship that is blowing up.
13179 //      Only avoid if shockwave is fairly large.
13180 //      OK to tell everyone to avoid.  If they're too far away, that gets cleaned up in the frame interval.
13181 void ai_announce_ship_dying(object *dying_objp)
13182 {
13183         float damage = ship_get_exp_damage(dying_objp);
13184         if (damage >= EVADE_SHOCKWAVE_DAMAGE_THRESHOLD) {
13185                 ship_obj        *so;
13186
13187                 for ( so = GET_FIRST(&Ship_obj_list); so != END_OF_LIST(&Ship_obj_list); so = GET_NEXT(so) ) {
13188                         if (Ship_info[Ships[Objects[so->objnum].instance].ship_info_index].flags & (SIF_SMALL_SHIP | SIF_FREIGHTER)) {
13189                                 ai_info *aip;
13190
13191                                 aip = &Ai_info[Ships[Objects[so->objnum].instance].ai_index];
13192
13193                                 if ( !(aip->ai_flags & (AIF_DOCKED|AIF_BEING_REPAIRED)) ) {
13194                                         aip->ai_flags |= AIF_AVOID_SHOCKWAVE_SHIP;
13195                                 }
13196                         }
13197                 }
13198         }
13199 }
13200
13201
13202 //      Return object index of weapon that could produce a shockwave that should be known about to *objp.
13203 //      Return nearest one.
13204 int ai_find_shockwave_ship(object *objp, ai_info *aip)
13205 {
13206         ship_obj        *so;
13207         float   nearest_dist = 999999.9f;
13208         int     nearest_index = -1;
13209
13210         for ( so = GET_NEXT(&Ship_obj_list); so != END_OF_LIST(&Ship_obj_list); so = GET_NEXT(so) ) {
13211                 object          *A;
13212                 ship                    *shipp;
13213         
13214                 Assert(so->objnum >= 0 && so->objnum < MAX_OBJECTS);
13215                 A = &Objects[so->objnum];
13216
13217                 Assert(A->type == OBJ_SHIP);
13218                 Assert((A->instance >= 0) && (A->instance < MAX_SHIPS));
13219                 shipp = &Ships[A->instance];
13220                 //      Only look at objects in the process of dying.
13221                 if (shipp->flags & SF_DYING) {
13222                         float damage = ship_get_exp_damage(objp);
13223
13224                         if (damage >= EVADE_SHOCKWAVE_DAMAGE_THRESHOLD) {               //      Only evade quite large blasts
13225                                 float   dist;
13226
13227                                 dist = vm_vec_dist_quick(&objp->pos, &A->pos);
13228                                 if (dist < nearest_dist) {
13229                                         nearest_dist = dist;
13230                                         nearest_index = so->objnum;
13231                                 }
13232                         }
13233                 }
13234         }
13235
13236         return nearest_index;
13237
13238 }
13239
13240 int aas_1(object *objp, ai_info *aip, vector *safe_pos)
13241 {
13242         // MAKE SURE safe_pos DOES NOT TAKE US TOWARD THE A SHIP WE'RE ATTACKING.
13243         if (aip->ai_flags & AIF_AVOID_SHOCKWAVE_WEAPON) {
13244                 //      If we don't currently know of a weapon to avoid, try to find one.
13245                 //      If we can't find one, then clear the bit so we don't keep coming here.
13246                 if (aip->shockwave_object == -1) {
13247                         int shockwave_weapon = ai_find_shockwave_weapon(objp, aip);
13248                         if (shockwave_weapon == -1) {
13249                                 aip->ai_flags &= ~AIF_AVOID_SHOCKWAVE_WEAPON;
13250                                 return 0;
13251                         } else {
13252                                 aip->shockwave_object = shockwave_weapon;
13253                         }
13254                 }
13255
13256                 //      OK, we have reason to believe we should avoid aip->shockwave_object.
13257                 Assert(aip->shockwave_object > -1);
13258                 object  *weapon_objp = &Objects[aip->shockwave_object];
13259                 if (weapon_objp->type != OBJ_WEAPON) {
13260                         aip->ai_flags &= ~AIF_AVOID_SHOCKWAVE_WEAPON;
13261                         aip->shockwave_object = -1;
13262                         return 0;
13263                 }
13264
13265                 weapon  *weaponp = &Weapons[weapon_objp->instance];
13266                 weapon_info     *wip = &Weapon_info[weaponp->weapon_info_index];
13267                 object *target_ship_obj = NULL;
13268
13269                 if (wip->shockwave_speed == 0.0f) {
13270                         aip->ai_flags &= ~AIF_AVOID_SHOCKWAVE_WEAPON;
13271                         aip->shockwave_object = -1;
13272                         return 0;
13273                 }
13274
13275                 float   danger_dist;
13276                 vector  expected_pos;           //      Position at which we expect the weapon to detonate.
13277                 int             pos_set = 0;
13278
13279                 danger_dist = wip->outer_radius;
13280                 //      Set predicted position of detonation.
13281                 //      If an aspect locked missile, assume it will detonate at the homing position.
13282                 //      If not, which is not possible in a default FreeSpace weapon, then predict it will detonate at some
13283                 //      time in the future, this time based on max lifetime and life left.
13284                 if (wip->wi_flags & WIF_HOMING_ASPECT) {
13285                         expected_pos = weaponp->homing_pos;
13286                         if (weaponp->homing_object && weaponp->homing_object->type == OBJ_SHIP) {
13287                                 target_ship_obj = weaponp->homing_object;
13288                         }
13289                         pos_set = 1;
13290                         if (IS_VEC_NULL(&weaponp->homing_pos)) {
13291                                 pos_set = 0;
13292                                 if (weaponp->target_num != -1) {
13293                                         if (Objects[weaponp->target_num].type == OBJ_SHIP) {
13294                                                 target_ship_obj = &Objects[weaponp->target_num];
13295                                                 expected_pos = target_ship_obj->pos;
13296                                                 pos_set = 1;
13297                                         }
13298                                 }
13299                         }
13300                 }
13301
13302                 if (!pos_set) {
13303                         float   time_scale;
13304
13305                         if (wip->lifetime - weaponp->lifeleft > 5.0f) {
13306                                 time_scale = 1.0f;
13307                         } else {
13308                                 time_scale = weaponp->lifeleft/2.0f;
13309                         }
13310
13311                         vm_vec_scale_add(&expected_pos, &weapon_objp->pos, &weapon_objp->orient.fvec, time_scale);
13312                 }
13313
13314                 //      See if too far away to care about shockwave.
13315                 if (vm_vec_dist_quick(&objp->pos, &expected_pos) > danger_dist*2.0f) {
13316                         //aip->ai_flags &= ~AIF_AVOID_SHOCKWAVE_WEAPON;
13317                         return 0;
13318                 } else {
13319                         // try to find a safe position
13320                         vector vec_from_exp;
13321                         float dir = 1.0f;
13322                         vm_vec_sub(&vec_from_exp, &objp->pos, &expected_pos);
13323                         float dot = vm_vec_dotprod(&vec_from_exp, &weapon_objp->orient.fvec);
13324                         if (dot > -30) {
13325                                 // if we're already on the other side of the explosion, don't try to fly behind it
13326                                 dir = -1.0f;
13327                         }
13328
13329                         //      Fly towards a point behind the weapon.
13330                         vm_vec_scale_add(safe_pos, &weapon_objp->pos, &weapon_objp->orient.fvec, -50000.0f*dir);
13331
13332                         // verify safe_pos will not make us collide with our target objnum, else try 2 other vecs
13333                         // don't bang your head, else go
13334 //                      int go_safe = FALSE;
13335                         int go_safe = TRUE;
13336 /*                      if (target_ship_obj) {
13337                                 if (pp_collide(&objp->pos, safe_pos, target_ship_obj, objp->radius)) {
13338                                         // try up to 2 other random directions
13339                                         vector dir_vec, rand_vec;
13340                                         int idx;
13341                                         for (idx=0; idx<2; idx++) {
13342                                                 vm_vec_rand_vec_quick(&rand_vec);
13343                                                 vm_vec_scale_add(&dir_vec, &weapon_objp->orient.fvec, &rand_vec, 0.5f);
13344                                                 vm_vec_scale_add(safe_pos, &weapon_objp->pos, &dir_vec, -50000.0f*dir);
13345                                                 if ( !pp_collide(&objp->pos, safe_pos, target_ship_obj, objp->radius) ) {
13346                                                         go_safe = TRUE;
13347                                                         break;
13348                                                 }
13349                                         }
13350                                 } else { // direct path is safe
13351                                         go_safe = TRUE;
13352                                 }
13353                         } else { // no target_obj_ship
13354                                 go_safe = TRUE;
13355                         } */
13356
13357                         if (go_safe) {
13358                                 return 1;
13359                         } else {
13360                                 // can't figure out a good way to go
13361                                 return 0;
13362                         }
13363                 }
13364         } else if (aip->ai_flags & AIF_AVOID_SHOCKWAVE_SHIP) {
13365                 if (aip->shockwave_object == -1) {
13366                         int shockwave_ship = ai_find_shockwave_ship(objp, aip);
13367                         if (shockwave_ship == -1) {
13368                                 aip->ai_flags &= ~AIF_AVOID_SHOCKWAVE_SHIP;
13369                                 return 0;
13370                         } else {
13371                                 aip->shockwave_object = shockwave_ship;
13372                         }
13373                 }
13374
13375                 Assert(aip->shockwave_object > -1);
13376                 object  *ship_objp = &Objects[aip->shockwave_object];
13377                 if (ship_objp == objp) {
13378                         aip->shockwave_object = -1;
13379                         return 0;
13380                 }
13381
13382                 if (ship_objp->type != OBJ_SHIP) {
13383                         aip->ai_flags &= ~AIF_AVOID_SHOCKWAVE_SHIP;
13384                         return 0;
13385                 }
13386
13387                 //      Optimize note! Don't really have to normalize.  We only need a point away from the blowing-up ship.
13388                 vector safe_vec;
13389
13390                 vm_vec_normalized_dir(&safe_vec, &objp->pos, &ship_objp->pos);
13391                 vm_vec_scale_add(safe_pos, &ship_objp->pos, &safe_vec, 50000.0f);       //      Fly away from the ship.
13392
13393                 float outer_rad = ship_get_exp_outer_rad(ship_objp);
13394
13395                 if (vm_vec_dist_quick(&objp->pos, &ship_objp->pos) > outer_rad*1.5f) {
13396                         aip->ai_flags &= ~AIF_AVOID_SHOCKWAVE_WEAPON;
13397                         return 0;
13398                 }
13399
13400                 return 1;
13401
13402         } else {
13403                 Int3(); //      Illegal -- supposedly avoiding a shockwave, but neither ship nor weapon.  What is it!?
13404         }
13405
13406         return 0;
13407 }
13408
13409 /*
13410 int rct_done = 0;
13411
13412 void rand_chance_test()
13413 {
13414         int     i;
13415         float   frametime;
13416
13417         if (rct_done)
13418                 return;
13419
13420         rct_done = 1;
13421
13422         for (frametime=0.02f; frametime<0.25f; frametime *= 1.25f) {
13423                 float   chance;
13424
13425                 nprintf(("AI", "%6.4f: ", frametime));
13426                 for (chance=0.25f; chance<2.5f; chance += 0.25f) {
13427                         int count = 0;
13428
13429                         for (i=0; i<100.0f/frametime; i++) {
13430                                 if (rand_chance(frametime, chance))
13431                                         count++;
13432                         }
13433                         nprintf(("AI", "%3i ", count));
13434                 }
13435                 nprintf(("AI", "\n"));
13436         }
13437 }
13438 */
13439
13440 //      --------------------------------------------------------------------------
13441 //      Make object *objp avoid the nearest dangerous shockwave-producing weapon.
13442 //      If it looks like there is no valid shockwave-producing weapon then clear the AIF_AVOID_SHOCKWAVE_WEAPON bit in ai_flags and return.
13443 //      Return 1 if avoiding a shockwave, else return 0.
13444 int ai_avoid_shockwave(object *objp, ai_info *aip)
13445 {
13446         vector  safe_pos;
13447
13448         //rand_chance_test();
13449         // BIG|HUGE do not respond to shockwaves
13450         if (Ship_info[Ships[objp->instance].ship_info_index].flags & (SIF_BIG_SHIP|SIF_HUGE_SHIP)) {
13451                 // don't come here again
13452                 aip->ai_flags &= ~AIF_AVOID_SHOCKWAVE;
13453                 return 0;
13454         }
13455
13456         //      Don't all react right away.
13457         if (!(aip->ai_flags & AIF_AVOID_SHOCKWAVE_STARTED))
13458                 if (!rand_chance(flFrametime, (float) aip->ai_class/4.0f + 0.25f))      //      Chance to avoid in 1 second is 0.25 + ai_class/4
13459                         return 0;
13460
13461         if (!aas_1(objp, aip, &safe_pos)) {
13462                 aip->ai_flags |= AIF_AVOID_SHOCKWAVE_STARTED;
13463                 return 0;
13464         }
13465
13466         aip->ai_flags |= AIF_AVOID_SHOCKWAVE_STARTED;
13467
13468         //      OK, evade the shockwave!
13469         turn_towards_point(objp, &safe_pos, NULL, 0.0f);
13470         vector  vec_to_safe_pos;
13471         float           dot_to_goal;
13472
13473         vm_vec_normalized_dir(&vec_to_safe_pos, &safe_pos, &objp->pos);
13474
13475         dot_to_goal = vm_vec_dot(&objp->orient.fvec, &vec_to_safe_pos);
13476         if (dot_to_goal < -0.5f)
13477                 accelerate_ship(aip, 0.3f);
13478         else {
13479                 accelerate_ship(aip, 1.0f + dot_to_goal);
13480                 if (dot_to_goal > 0.2f) {
13481                         if (!(objp->phys_info.flags & PF_AFTERBURNER_ON )) {
13482                                 afterburners_start(objp);
13483                                 aip->afterburner_stop_time = Missiontime + 2*F1_0;
13484                         }
13485                 }
13486         }
13487
13488         return 1;
13489 }
13490
13491 //      Awaiting repair.  Be useful.
13492 //      Probably fly towards incoming repair ship.
13493 //      Return true if this ship is close to being repaired, else return false.
13494 int ai_await_repair_frame(object *objp, ai_info *aip)
13495 {
13496         if (!(aip->ai_flags & (AIF_AWAITING_REPAIR | AIF_BEING_REPAIRED)))
13497                 return 0;
13498
13499         if (aip->dock_objnum == -1)
13500                 return 0;
13501
13502         ship    *shipp;
13503         ship_info       *sip;
13504
13505         shipp = &Ships[Objects[aip->dock_objnum].instance];
13506         sip = &Ship_info[shipp->ship_info_index];
13507
13508         aip->ai_flags &= ~AIF_FORMATION_OBJECT; //      Prevents endless rotation.
13509
13510         if (!(sip->flags & SIF_SUPPORT))
13511                 return 0;
13512
13513         vector  goal_point;
13514         object  *repair_objp;
13515
13516         repair_objp = &Objects[aip->dock_objnum];
13517
13518         if (Ships[repair_objp->instance].team == TEAM_TRAITOR) {
13519                 ai_abort_rearm_request(repair_objp);
13520                 return 0;
13521         }
13522
13523         vm_vec_scale_add(&goal_point, &repair_objp->pos, &repair_objp->orient.uvec, -50.0f);    //      Fly towards point below repair ship.
13524
13525         vector  vtr;
13526         float dist = vm_vec_normalized_dir(&vtr, &goal_point, &objp->pos);
13527         float dot = vm_vec_dot(&vtr, &objp->orient.fvec);
13528
13529         if (dist > 200.0f) {
13530                 //nprintf(("AI", "%s flying towards %s for repair, dist = %7.3f\n", Ships[objp->instance].ship_name, &Ships[repair_objp->instance].ship_name, dist));
13531                 accelerate_ship(aip, (0.9f + dot) * dist/1500.0f);
13532                 turn_towards_point(objp, &goal_point, NULL, 0.0f);
13533         } else {
13534                 accelerate_ship(aip, 0.0f);
13535                 //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));
13536         }
13537
13538         return 1;
13539 }
13540
13541 //      Maybe cause this ship to self-destruct.
13542 //      Currently, any small ship (SIF_SMALL_SHIP) that has been disabled will self-destruct after awhile.
13543 //      Maybe should only do this if they are preventing their wing from re-entering.
13544 void ai_maybe_self_destruct(object *objp, ai_info *aip)
13545 {
13546         //      Friendly ships can be repaired, so no self-destruct.
13547         //      In multiplayer, just don't self-destruct.  I figured there would be a problem. -- MK, 3/19/98.
13548         if ((Ships[objp->instance].team == TEAM_FRIENDLY) || (Game_mode & GM_MULTIPLAYER))
13549                 return;
13550
13551         //      Small ships in a wing blow themselves up after awhile if engine or weapons system has been destroyed.
13552         //      Reason: Don't want them to prevent a re-emergence of the wing.
13553         //      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
13554         //      mission would be broken.
13555         if ((Ship_info[Ships[objp->instance].ship_info_index].flags & SIF_SMALL_SHIP) && (Ships[objp->instance].wingnum != -1)) {
13556                 if ((ship_get_subsystem_strength(&Ships[objp->instance], SUBSYSTEM_ENGINE) <= 0.0f) ||
13557                         (ship_get_subsystem_strength(&Ships[objp->instance], SUBSYSTEM_WEAPONS) <= 0.0f)) {
13558                         if (aip->self_destruct_timestamp < 0)
13559                                 aip->self_destruct_timestamp = timestamp(90 * 1000);    //      seconds until self-destruct
13560                 } else {
13561                         aip->self_destruct_timestamp = -1;
13562                 }
13563
13564                 if (aip->self_destruct_timestamp < 0) {
13565                         return;
13566                 }
13567
13568                 if (timestamp_elapsed(aip->self_destruct_timestamp)) {
13569                         ship_apply_local_damage( objp, objp, &objp->pos, objp->hull_strength*flFrametime + 1.0f, MISS_SHIELDS);
13570                 }
13571         }
13572 }
13573
13574 // Determine if pl_objp needs a new target, called from ai_frame()
13575 int ai_need_new_target(object *pl_objp, int target_objnum)
13576 {
13577         object *objp;
13578
13579         if ( target_objnum < 0 ) {
13580                 return 1;
13581         }
13582
13583         objp = &Objects[target_objnum];
13584
13585         if ( (objp->type != OBJ_SHIP) && (objp->type != OBJ_ASTEROID) && (objp->type != OBJ_WEAPON) ) {
13586                 return 1;
13587         }
13588
13589         if ( objp->type == OBJ_SHIP ) {
13590                 if ( Ships[objp->instance].flags & SF_DYING ) {
13591                         return 1;
13592                 } else if (Ships[objp->instance].team == Ships[pl_objp->instance].team)
13593                         return 1;
13594         }
13595
13596         return 0;
13597 }
13598
13599 //      If *objp is recovering from a collision with a big ship, handle it.
13600 //      Return true if recovering.
13601 int maybe_big_ship_collide_recover_frame(object *objp, ai_info *aip)
13602 {
13603         float   dot, dist;
13604         vector  v2g;
13605         
13606         if (aip->ai_flags & AIF_BIG_SHIP_COLLIDE_RECOVER_1) {
13607                 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);
13608                 dist = vm_vec_normalized_dir(&v2g, &aip->big_recover_pos_1, &objp->pos);
13609                 dot = vm_vec_dot(&objp->orient.fvec, &v2g);
13610                 accelerate_ship(aip, dot);
13611
13612                 //      If close to desired point, or 15+ seconds since entered this mode, continue to next mode.
13613                 if ((timestamp_until(aip->big_recover_timestamp) < -15*1000) || (dist < (0.5f + flFrametime) * objp->phys_info.speed)) {
13614                         aip->ai_flags &= ~AIF_BIG_SHIP_COLLIDE_RECOVER_1;
13615                         aip->ai_flags |= AIF_BIG_SHIP_COLLIDE_RECOVER_2;
13616                 }
13617
13618                 return 1;
13619
13620         } else if (aip->ai_flags & AIF_BIG_SHIP_COLLIDE_RECOVER_2) {
13621                 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);
13622                 dist = vm_vec_normalized_dir(&v2g, &aip->big_recover_pos_2, &objp->pos);
13623                 dot = vm_vec_dot(&objp->orient.fvec, &v2g);
13624                 accelerate_ship(aip, dot);
13625
13626                 //      If close to desired point, or 30+ seconds since started avoiding collision, done avoiding.
13627                 if ((timestamp_until(aip->big_recover_timestamp) < -30*1000) || (dist < (0.5f + flFrametime) * objp->phys_info.speed)) {
13628                         aip->ai_flags &= ~AIF_BIG_SHIP_COLLIDE_RECOVER_2;
13629                         aip->ai_flags &= ~AIF_TARGET_COLLISION;
13630                 }
13631
13632                 return 1;
13633         }
13634
13635         if (aip->ai_flags & AIF_TARGET_COLLISION) {
13636                 aip->ai_flags &= ~AIF_TARGET_COLLISION;
13637         }
13638         return 0;
13639 }
13640
13641 void validate_mode_submode(ai_info *aip)
13642 {
13643         switch (aip->mode) {
13644         case AIM_CHASE:
13645                 // check valid submode
13646                 switch (aip->submode) {
13647                 case SM_CONTINUOUS_TURN:
13648                 case SM_ATTACK:
13649                 case SM_EVADE_SQUIGGLE:
13650                 case SM_EVADE_BRAKE:    
13651                 case SM_EVADE:          
13652                 case SM_SUPER_ATTACK:
13653                 case SM_AVOID:  
13654                 case SM_GET_BEHIND:
13655                 case SM_GET_AWAY:               
13656                 case SM_EVADE_WEAPON:
13657                 case SM_FLY_AWAY:       
13658                 case SM_ATTACK_FOREVER:
13659                         break;
13660                 default:
13661                         Int3();
13662                 }
13663                 break;
13664
13665         case AIM_STRAFE:
13666                 // check valid submode
13667                 switch(aip->submode) {
13668                 case AIS_STRAFE_ATTACK:
13669                 case AIS_STRAFE_AVOID:
13670                 case AIS_STRAFE_RETREAT1:
13671                 case AIS_STRAFE_RETREAT2:
13672                 case AIS_STRAFE_POSITION:
13673                         break;
13674                 default:
13675                         Int3();
13676                 }
13677                 break;
13678         }
13679 }
13680
13681 //      --------------------------------------------------------------------------
13682 // Process AI object "objnum".
13683 void ai_frame(int objnum)
13684 {
13685         ship            *shipp = &Ships[Objects[objnum].instance];
13686         ai_info *aip = &Ai_info[shipp->ai_index];
13687         int             target_objnum;
13688
13689 //      validate_mode_submode(aip);
13690
13691         Assert((aip->mode != AIM_WAYPOINTS) || (aip->active_goal != AI_ACTIVE_GOAL_DYNAMIC));
13692
13693         // Set globals defining the current object and its enemy object.
13694         Pl_objp = &Objects[objnum];
13695
13696         if (aip->mode == AIM_WARP_OUT) {
13697                 ai_warp_out(Pl_objp);
13698                 return;
13699         }
13700
13701 /*      //      HACK! TEST! REMOVE ME!
13702         if (Ship_info[shipp->ship_info_index].flags & SIF_BIG_SHIP)
13703                 if (shipp->team == Player_ship->team)
13704                         aip->mode = AIM_CHASE;
13705 */
13706
13707 //      if (!strnicmp(Ships[Pl_objp->instance].ship_name, "cancer", 6))
13708 //              nprintf(("AI", "Ship %s: mode = %s, submode = %i\n", Ships[Pl_objp->instance].ship_name, Mode_text[aip->mode], aip->submode));
13709
13710         ai_maybe_self_destruct(Pl_objp, aip);
13711
13712 //      if ( timestamp_elapsed(aip->goal_check_time) ) {
13713                 ai_process_mission_orders( objnum, aip );
13714 //              aip->goal_check_time = timestamp_rand(1000,2000);
13715 //      }
13716
13717         //      Avoid a shockwave, if necessary.  If a shockwave and rearming, stop rearming.
13718         if (aip->ai_flags & AIF_AVOID_SHOCKWAVE) {
13719                 if (ai_avoid_shockwave(Pl_objp, aip)) {
13720                         aip->ai_flags &= ~(AIF_BIG_SHIP_COLLIDE_RECOVER_1 | AIF_BIG_SHIP_COLLIDE_RECOVER_2);
13721                         if (aip->ai_flags & (AIF_AWAITING_REPAIR | AIF_BEING_REPAIRED))
13722                                 ai_abort_rearm_request(Pl_objp);
13723                         return;
13724                 }
13725         } else {
13726                 aip->ai_flags &= ~AIF_AVOID_SHOCKWAVE_STARTED;
13727         }
13728
13729         // moved call to ai_do_repair frame here from below because of the subsequent if statment returning
13730         // if the ship is getting repaired
13731         //      If waiting to be repaired, just stop and sit.
13732         ai_do_repair_frame(Pl_objp, aip, flFrametime);
13733         if ((aip->ai_flags & AIF_AWAITING_REPAIR) || (aip->ai_flags & AIF_BEING_REPAIRED)) {
13734                 if (ai_await_repair_frame(Pl_objp, aip))
13735                         return;
13736         }
13737
13738         if (aip->mode == AIM_PLAY_DEAD)
13739                 return;
13740
13741         //      If recovering from a collision with a big ship, don't continue.
13742         if (maybe_big_ship_collide_recover_frame(Pl_objp, aip))
13743                 return;
13744
13745         ai_preprocess_ignore_objnum(Pl_objp, aip);
13746         target_objnum = set_target_objnum(aip, aip->target_objnum);
13747
13748         // 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));
13749
13750         Assert(objnum != target_objnum);
13751
13752         ai_manage_shield(Pl_objp, aip);
13753         
13754         if ( maybe_request_support(Pl_objp) ) {
13755                 if ( Ships[Pl_objp->instance].flags & SF_FROM_PLAYER_WING ) {
13756                         ship_maybe_tell_about_rearm(shipp);
13757                 }
13758         }
13759
13760         ai_maybe_warp_out(Pl_objp);
13761
13762 /*
13763         //      If this ship is attacking an object's subsystems and someone else destroyed
13764         //      the subsystem, it could continue attacking the ship.  Need to invalidate the objnum.
13765         if (target_objnum >= 0)
13766                 if (Objects[target_objnum].flags & OF_PROTECTED) {
13767                         // if (aip->targeted_subsys != NULL)
13768                         //      ; //nprintf(("AI", "subsys hits = %7.3f\n", aip->targeted_subsys->current_hits));
13769
13770                         if ((aip->targeted_subsys == NULL) || (aip->targeted_subsys->current_hits <= 0.0f)) {
13771                                 target_objnum = -1;
13772                                 aip->target_objnum = -1;
13773                         }
13774                 }
13775 */
13776
13777
13778         //      Find an enemy if don't already have one.
13779         En_objp = NULL;
13780         if ( ai_need_new_target(Pl_objp, target_objnum) ) {
13781                 if ((aip->mode != AIM_EVADE_WEAPON) && (aip->active_goal == AI_ACTIVE_GOAL_DYNAMIC)) {
13782                         aip->resume_goal_time = -1;
13783                         aip->active_goal = AI_GOAL_NONE;
13784                 } else if (aip->resume_goal_time == -1) {
13785                         // AL 12-9-97: Don't allow cargo and navbuoys to set their aip->target_objnum
13786                         if ( !(Ship_info[shipp->ship_info_index].flags & SIF_HARMLESS) ) {
13787                                 target_objnum = find_enemy(objnum, MAX_ENEMY_DISTANCE, Skill_level_max_attackers[Game_skill_level]);            //      Attack up to 25K units away.
13788                                 if (target_objnum != -1) {
13789                                         if (aip->target_objnum != target_objnum)
13790                                                 aip->aspect_locked_time = 0.0f;
13791                                         set_target_objnum(aip, target_objnum);
13792                                         En_objp = &Objects[target_objnum];
13793                                 }
13794                         }
13795                 }
13796         } else if (target_objnum >= 0) {
13797                 En_objp = &Objects[target_objnum];
13798         }
13799
13800         // set base stealth info each frame
13801         aip->ai_flags &= ~AIF_STEALTH_PURSIUT;
13802         if (En_objp && En_objp->type == OBJ_SHIP) {
13803                 if (Ship_info[Ships[En_objp->instance].ship_info_index].flags & SIF_STEALTH) {
13804                         int stealth_state = ai_is_stealth_visible(Pl_objp, En_objp);
13805                         float dist = vm_vec_dist_quick(&En_objp->pos, &Pl_objp->pos);
13806
13807                         if (stealth_state != STEALTH_FULLY_TARGETABLE) {
13808                                 aip->ai_flags |= AIF_STEALTH_PURSIUT;
13809                         }
13810
13811                         if ( (stealth_state == STEALTH_FULLY_TARGETABLE) || (stealth_state == STEALTH_VISIBLE) ) {
13812                                 aip->stealth_last_visible_stamp = timestamp();
13813                                 aip->stealth_last_cheat_visible_stamp = aip->stealth_last_visible_stamp;
13814                                 aip->stealth_last_pos = En_objp->pos;
13815                                 aip->stealth_velocity = En_objp->phys_info.vel;
13816                         } else if (dist < 100) {
13817                                 // get cheat timestamp
13818                                 aip->stealth_last_cheat_visible_stamp = timestamp();
13819
13820                                 // set approximate pos and vel, with increasing error as time from last_visible_stamp increases
13821                                 update_ai_stealth_info_with_error(aip/*, 0*/);
13822                         }
13823                 }
13824         }
13825
13826         /*      if ((Pl_objp != NULL) && (En_objp != NULL)) {
13827                 slide_face_ship();
13828                 return;
13829         }
13830 */
13831         // AL 12-10-97: ensure that cargo and navbuoys aip->target_objnum is always -1.
13832         if ( Ship_info[shipp->ship_info_index].flags & SIF_HARMLESS ) {
13833                 aip->target_objnum = -1;
13834         }
13835
13836         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)) {
13837                 mprintf(("Warning: Object and its enemy have same position.  Object #%i\n", Pl_objp-Objects));
13838                 En_objp = NULL;
13839         }
13840
13841         if (aip->mode == AIM_CHASE) {
13842                 if (En_objp == NULL) {
13843                         aip->active_goal = -1;
13844                 }
13845         }
13846
13847         //      If there is a goal to resume and enough time has elapsed, resume the goal.
13848         if ((aip->resume_goal_time > 0) && (aip->resume_goal_time < Missiontime)) {
13849                 aip->active_goal = AI_GOAL_NONE;
13850                 aip->resume_goal_time = -1;
13851                 target_objnum = find_enemy(objnum, 2000.0f, Skill_level_max_attackers[Game_skill_level]);
13852                 if (target_objnum != -1) {
13853                         if (aip->target_objnum != target_objnum) {
13854                                 aip->aspect_locked_time = 0.0f;
13855                         }
13856                         set_target_objnum(aip, target_objnum);
13857                 }
13858         }
13859
13860         // check if targeted subsystem has been destroyed, if so, move onto another subsystem
13861         // if trying to disable or disarm the target
13862         if ((En_objp != NULL) && ( aip->targeted_subsys != NULL )) {
13863                 Assert(En_objp->type == OBJ_SHIP);
13864                 if ( aip->targeted_subsys->current_hits <= 0.0f ) {
13865                         int subsys_type;
13866
13867                         if ( aip->goals[0].ai_mode == AI_GOAL_DISABLE_SHIP ) {
13868                                 subsys_type = SUBSYSTEM_ENGINE;
13869                         } else if ( aip->goals[0].ai_mode == AI_GOAL_DISARM_SHIP ) {
13870                                 subsys_type = SUBSYSTEM_TURRET;
13871                         } else {
13872                                 subsys_type = -1;
13873                         }
13874
13875                         if ( subsys_type != -1 ) {
13876                                 ship_subsys *new_subsys;
13877                                 new_subsys = ship_return_next_subsys(&Ships[En_objp->instance], subsys_type, &Pl_objp->pos);
13878                                 if ( new_subsys != NULL ) {
13879                                         set_targeted_subsys(aip, new_subsys, aip->target_objnum);
13880                                 } else {
13881                                         // AL 12-16-97: no more subsystems to attack... reset targeting info
13882                                         aip->target_objnum = -1;
13883                                         set_targeted_subsys(aip, NULL, -1);
13884                                 }
13885                         } else {
13886                                 // targeted subsys is destroyed, so stop attacking it
13887                                 set_targeted_subsys(aip, NULL, -1);
13888                         }
13889                 }
13890         }
13891
13892         ai_maybe_launch_cmeasure(Pl_objp, aip);
13893         ai_maybe_evade_locked_missile(Pl_objp, aip);
13894
13895         aip->target_time += flFrametime;
13896
13897         int in_formation = 0;
13898         if (aip->ai_flags & AIF_FORMATION) {
13899                 in_formation = !ai_formation();
13900         }
13901
13902         if ( !in_formation ) {
13903                 ai_execute_behavior(aip);
13904         }
13905
13906         process_subobjects(objnum);
13907         maybe_resume_previous_mode(Pl_objp, aip);
13908         
13909         if (Pl_objp->phys_info.flags & PF_AFTERBURNER_ON ) {
13910                 if (Missiontime > aip->afterburner_stop_time) {
13911                         //nprintf(("AI", "Frame %i, turning off afterburner.\n", AI_FrameCount));
13912                         afterburners_stop(Pl_objp);
13913                 }
13914         }
13915 //      validate_mode_submode(aip);
13916 }
13917
13918 int Waypoints_created = 0;
13919
13920 //      Find the ship with the name *name in the Ship_info array.
13921 int find_ship_name(char *name)
13922 {
13923         int     i;
13924
13925         for (i=0; i<Num_ship_types; i++)
13926                 if (!strcmp(Ship_info[i].name, name))
13927                         return i;
13928
13929         return -1;
13930 }
13931
13932 void create_waypoints()
13933 {
13934         int     i, j, z;
13935
13936         // Waypoints_created = 1;
13937
13938         if (Waypoints_created)
13939                 return;
13940
13941         for (j=0; j<Num_waypoint_lists; j++)
13942                 for (i=0; i<Waypoint_lists[j].count; i++) {
13943                         z = obj_create(OBJ_WAYPOINT, 0, j * 65536 + i, NULL,
13944                                 &Waypoint_lists[j].waypoints[i], 0.0f, OF_RENDERS);
13945                 }
13946
13947         Waypoints_created = 1;
13948 }
13949
13950 int Last_ai_obj = -1;
13951
13952 void ai_process( object * obj, int ai_index, float frametime )
13953 {
13954 //      if (Ships[obj->instance].flags & SF_DYING)
13955 //              nprintf(("AI", "Frame: %i Ship %s is dying!\n", Framecount, Ships[obj->instance].ship_name));
13956
13957         if (obj->flags & OF_SHOULD_BE_DEAD)
13958                 return;
13959
13960         // 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.
13961         if ((Ships[obj->instance].flags & SF_DYING ) && !(Ship_info[Ships[obj->instance].ship_info_index].flags & (SIF_HUGE_SHIP | SIF_BIG_SHIP))){
13962                 return;
13963         }
13964
13965         int rfc = 1;            //      Assume will be Reading Flying Controls.
13966
13967         Assert( obj->type == OBJ_SHIP );
13968         Assert( ai_index >= 0 );
13969
13970         init_ship_info();
13971
13972         create_waypoints();
13973
13974         AI_frametime = frametime;
13975         if (obj-Objects <= Last_ai_obj) {
13976                 AI_FrameCount++;
13977         }
13978
13979         memset( &AI_ci, 0, sizeof(AI_ci) );
13980
13981         ai_frame(obj-Objects);
13982
13983         AI_ci.pitch = 0.0f;
13984         AI_ci.bank = 0.0f;
13985         AI_ci.heading = 0.0f;
13986
13987         // the ships maximum velocity now depends on the energy flowing to engines
13988         obj->phys_info.max_vel.z = Ships[obj->instance].current_max_speed;
13989         ai_info *aip = &Ai_info[Ships[obj->instance].ai_index];
13990
13991         //      In certain circumstances, the AI says don't fly in the normal way.
13992         //      One circumstance is in docking and undocking, when the ship is moving
13993         //      under thruster control.
13994         switch (aip->mode) {
13995         case AIM_DOCK:
13996                 if ((aip->submode >= AIS_DOCK_2) && (aip->submode != AIS_UNDOCK_3))
13997                         rfc = 0;
13998                 break;
13999         case AIM_WARP_OUT:
14000                 if (aip->submode >= AIS_WARP_3)
14001                         rfc = 0;
14002                 break;
14003 //      case AIM_NONE:
14004 //              if (aip->submode == AIS_NONE_FORMATION)
14005 //                      rfc = 0;
14006 //              break;
14007         default:
14008                 break;
14009         }
14010
14011         if (rfc == 1) {
14012                 vector copy_desired_rotvel = obj->phys_info.rotvel;
14013                 physics_read_flying_controls( &obj->orient, &obj->phys_info, &AI_ci, frametime);
14014                 // if obj is in formation and not flight leader, don't update rotvel
14015                 if (aip->ai_flags & AIF_FORMATION) {
14016                         if (&Objects[aip->goal_objnum] != obj) {
14017                                 obj->phys_info.desired_rotvel = copy_desired_rotvel;
14018                                 obj->phys_info.rotvel = copy_desired_rotvel;
14019                         }
14020                 }
14021         }
14022
14023         Last_ai_obj = obj-Objects;
14024 }
14025
14026 //      Initialize ai_info struct of object objnum.
14027 void init_ai_object(int objnum)
14028 {
14029         int     ship_index, ai_index;
14030         ai_info *aip;
14031         int ship_type;
14032         object  *objp;
14033         vector  near_vec;                       //      A vector nearby and mainly in front of this object.
14034
14035         objp = &Objects[objnum];
14036         ship_index = objp->instance;
14037         ai_index = Ships[ship_index].ai_index;
14038         Assert((ai_index >= 0) && (ai_index < MAX_AI_INFO));
14039
14040         aip = &Ai_info[ai_index];
14041
14042         ship_type = Ships[ship_index].ship_info_index;
14043
14044         vm_vec_scale_add(&near_vec, &objp->pos, &objp->orient.fvec, 100.0f);
14045         vm_vec_scale_add2(&near_vec, &objp->orient.rvec, 10.0f);
14046
14047         // Things that shouldn't have to get initialized, but initialize them just in case!
14048         aip->ai_flags = 0;
14049         aip->previous_mode = AIM_NONE;
14050         aip->mode_time = -1;
14051         aip->target_objnum = -1;
14052         aip->target_signature = -1;
14053         aip->previous_target_objnum = -1;
14054         aip->target_time = 0.0f;
14055         aip->enemy_wing = -1;
14056         aip->attacker_objnum = -1;
14057         aip->goal_objnum = -1;
14058         aip->goal_signature = -1;
14059         aip->guard_objnum = -1;
14060         aip->guard_signature = -1;
14061         aip->guard_wingnum = -1;
14062         aip->dock_signature = -1;
14063         aip->submode = 0;
14064         aip->previous_submode = 0;
14065         aip->best_dot_to_enemy = -1.0f;
14066         aip->best_dot_from_enemy = -1.0f;
14067         aip->best_dot_to_time = 0;
14068         aip->best_dot_from_time = 0;
14069         aip->submode_start_time = 0;
14070         aip->submode_parm0 = 0;
14071         aip->active_goal = -1;
14072         aip->goal_check_time = timestamp(0);
14073         aip->last_predicted_enemy_pos = near_vec;
14074         aip->prev_goal_point = near_vec;
14075         aip->goal_point = near_vec;
14076         aip->time_enemy_in_range = 0.0f;
14077         aip->last_attack_time = 0;
14078         aip->last_hit_time = 0;
14079         aip->last_hit_quadrant = 0;
14080         aip->hitter_objnum = -1;
14081         aip->hitter_signature = -1;
14082         aip->resume_goal_time = -1;
14083         aip->prev_accel = 0.0f;
14084         aip->prev_dot_to_goal = 0.0f;
14085
14086         aip->ignore_objnum = UNUSED_OBJNUM;
14087         aip->ignore_signature = -1;
14088
14089         // aip->mode = AIM_NONE;
14090
14091         // End of Things that shouldn't have to get initialized, but initialize them just in case!
14092
14093         aip->ai_courage = Ai_classes[Ship_info[ship_type].ai_class].ai_courage[Game_skill_level];
14094         aip->ai_patience = Ai_classes[Ship_info[ship_type].ai_class].ai_patience[Game_skill_level];
14095         aip->ai_evasion = Ai_classes[Ship_info[ship_type].ai_class].ai_evasion[Game_skill_level];
14096         aip->ai_accuracy = Ai_classes[Ship_info[ship_type].ai_class].ai_accuracy[Game_skill_level];
14097
14098         if (Num_waypoint_lists > 0) {
14099                 aip->wp_index = -1;
14100                 aip->wp_list = -1;
14101         } else {
14102                 aip->wp_index = -1;
14103                 aip->wp_list = -1;
14104         }
14105
14106         aip->attacker_objnum = -1;
14107         aip->goal_signature = -1;
14108
14109         Objects[objnum].phys_info.prev_fvec = Objects[objnum].orient.fvec;
14110
14111         aip->last_predicted_enemy_pos.x = 0.0f; //      Says this value needs to be recomputed!
14112         aip->time_enemy_in_range = 0.0f;
14113
14114         aip->resume_goal_time = -1;                                     //      Say there is no goal to resume.
14115
14116         aip->active_goal = -1;
14117         aip->path_start = -1;
14118         aip->path_goal_dist = -1;
14119         aip->path_length = 0;
14120         aip->path_subsystem_next_check = 1;
14121         aip->dock_path_index = -1;
14122         aip->dock_index = -1;
14123         aip->dock_objnum = -1;
14124
14125         aip->danger_weapon_objnum = -1;
14126         aip->danger_weapon_signature = -1;
14127
14128         aip->lead_scale = 0.0f;
14129         aip->last_hit_target_time = Missiontime;
14130
14131         aip->nearest_locked_object = -1;
14132         aip->nearest_locked_distance = 99999.0f;
14133
14134         aip->targeted_subsys = NULL;
14135         aip->last_subsys_target = NULL;
14136         aip->targeted_subsys_parent = -1;
14137
14138         // The next two fields are used to time the rearming to allow useful sound effects for missile rearming
14139         aip->rearm_first_missile = TRUE;                //      flag to indicate that next missile to load is the first missile
14140         aip->rearm_release_delay = 0;                   //      timestamp to delay the separation of docked ships after rearm
14141
14142         aip->next_predict_pos_time = 0;
14143
14144         aip->afterburner_stop_time = 0;
14145         aip->last_objsig_hit = -1;                              // object signature of the ship most recently hit by aip
14146
14147         aip->path_next_create_time = timestamp(1);
14148         aip->path_create_pos = Objects[objnum].pos;
14149         aip->path_create_orient = Objects[objnum].orient;
14150
14151         aip->ignore_expire_timestamp = timestamp(1);
14152         aip->warp_out_timestamp = 0;
14153         aip->next_rearm_request_timestamp = timestamp(1);
14154         aip->primary_select_timestamp = timestamp(1);
14155         aip->secondary_select_timestamp = timestamp(1);
14156         aip->scan_for_enemy_timestamp = timestamp(1);
14157
14158         aip->choose_enemy_timestamp = timestamp(3*(NUM_SKILL_LEVELS-Game_skill_level) * ((rand_alt() % 500) + 500));
14159
14160         aip->shockwave_object = -1;
14161         aip->shield_manage_timestamp = timestamp(1);
14162         aip->self_destruct_timestamp = -1;      //      This is a flag that we have not yet set this.
14163         aip->ok_to_target_timestamp = timestamp(1);
14164         aip->pick_big_attack_point_timestamp = timestamp(1);
14165         vm_vec_zero(&aip->big_attack_point);
14166
14167         aip->avoid_check_timestamp = timestamp(1);
14168
14169         aip->abort_rearm_timestamp = -1;
14170
14171         // artillery stuff
14172         aip->artillery_objnum = -1;
14173         aip->artillery_sig = -1;        
14174
14175         // waypoint speed cap
14176         aip->waypoint_speed_cap = -1;
14177
14178         // set lethality to enemy team
14179         aip->lethality = 0.0f;
14180 }
14181
14182 void init_ai_objects()
14183 {
14184         int     i;
14185
14186         for (i=0; i<num_objects; i++){
14187                 if (Objects[i].type == OBJ_SHIP){
14188                         init_ai_object(i);
14189                 }
14190         }
14191 }
14192
14193 void init_ai_system()
14194 {
14195         // MWA -- removed next line of code on 11/12/97.  When a ship is created
14196         // it calls init_ai_object() on it's objnum.  Doing this init at the point where
14197         // this function gets called messes things up.
14198         //init_ai_objects();
14199
14200         Ppfp = Path_points;
14201         Waypoints_created = 0;
14202
14203         Dock_path_warning_given = 0;
14204
14205 /*      for (int i=0; i<MAX_IGNORE_OBJECTS; i++) {
14206                 Ignore_objects[i].objnum = -1;
14207                 Ignore_objects[i].signature = -1;
14208         }
14209 */
14210
14211 }
14212
14213 void ai_set_default_behavior(object *obj, int classnum)
14214 {
14215         ai_info *aip;
14216
14217         Assert(obj != NULL);
14218         Assert(obj->instance != -1);
14219         Assert(Ships[obj->instance].ai_index != -1);
14220
14221         aip = &Ai_info[Ships[obj->instance].ai_index];
14222
14223         aip->behavior = classnum;
14224
14225 }
14226
14227 void ai_do_default_behavior(object *obj)
14228 {
14229         ai_info *aip;
14230         int             ship_flags;
14231
14232         Assert(obj != NULL);
14233         Assert(obj->instance != -1);
14234         Assert(Ships[obj->instance].ai_index != -1);
14235
14236         aip = &Ai_info[Ships[obj->instance].ai_index];
14237
14238         ship_flags = Ship_info[Ships[obj->instance].ship_info_index].flags;
14239         if (!is_instructor(obj) && (ship_flags & (SIF_FIGHTER | SIF_BOMBER))) {
14240                 int enemy_objnum = find_enemy(OBJ_INDEX(obj), 1000.0f, Skill_level_max_attackers[Game_skill_level]);
14241                 set_target_objnum(aip, enemy_objnum);
14242                 aip->mode = AIM_CHASE;
14243                 aip->submode = SM_ATTACK;
14244         } else if (ship_flags & (SIF_SUPPORT)) {
14245                 aip->mode = AIM_SAFETY;
14246                 aip->submode = AISS_1;
14247                 aip->ai_flags &= ~(AIF_REPAIRING);
14248         } else if ( ship_flags & SIF_SENTRYGUN ) {
14249                 aip->mode = AIM_SENTRYGUN;
14250         } else {
14251                 aip->mode = AIM_NONE;
14252         }
14253         
14254         aip->submode_start_time = Missiontime;
14255         aip->active_goal = AI_GOAL_NONE;
14256 }
14257
14258 #define FRIENDLY_DAMAGE_THRESHOLD       50.0f           //      Display a message at this threshold.  Note, this gets scaled by Skill_level
14259
14260 // send the given message from objp.  called from the maybe_process_friendly_hit
14261 // code below when a message must get send to the player when he fires on friendlies
14262 void process_friendly_hit_message( int message, object *objp )
14263 {
14264         int index;
14265
14266         // no traitor in multiplayer
14267         if(Game_mode & GM_MULTIPLAYER){
14268                 return;
14269         }
14270
14271         // don't send this message if a player ship was hit.
14272         if ( objp->flags & OF_PLAYER_SHIP ){
14273                 return;
14274         }
14275
14276         // check if objp is a cargo contianer -- if so, then find a new ship to send the message
14277         index = objp->instance;
14278         if ( !(Ship_info[Ships[objp->instance].ship_info_index].flags & (SIF_FIGHTER|SIF_BOMBER)) ){
14279                 index = -1;
14280         }
14281
14282         // if the message is "oops" (the don't hit me message), always make come from Terran command
14283         if ( message == MESSAGE_OOPS ){
14284                 index = -1;
14285         }
14286
14287         if ( index >= 0){
14288                 message_send_builtin_to_player( message, &Ships[index], MESSAGE_PRIORITY_HIGH, MESSAGE_TIME_ANYTIME, 0, 0, -1, -1 );
14289         } else {
14290                 message_send_builtin_to_player( message, NULL, MESSAGE_PRIORITY_HIGH, MESSAGE_TIME_ANYTIME, 0, 0, -1, -1 );
14291         }
14292 }
14293
14294 extern  void ship_set_subsystem_strength( ship *shipp, int type, float strength );
14295
14296 //      Object *objp_weapon, fired by *objp_hitter, hit object *objp_ship.
14297 void maybe_process_friendly_hit(object *objp_hitter, object *objp_hit, object *objp_weapon)
14298 {
14299         // no turning traitor in multiplayer
14300         if ( Game_mode & GM_MULTIPLAYER ) {
14301                 return;
14302         }
14303
14304         // ditto if mission says no traitors allowed
14305         if (The_mission.flags & MISSION_FLAG_NO_TRAITOR) {
14306                 return;
14307         }
14308
14309         if ((objp_hitter == Player_obj) && (Player_ship->team == TEAM_FRIENDLY)) {
14310
14311                 // AL 12-4-97: It is possible the Player is a OBJ_GHOST at this point.  If so, bail out.
14312                 if ( objp_hitter->type != OBJ_SHIP ) {
14313                         return;
14314                 }
14315
14316                 Assert(objp_hitter->type == OBJ_SHIP);
14317                 Assert(objp_hit->type == OBJ_SHIP);
14318                 Assert(objp_weapon->type == OBJ_WEAPON);
14319
14320                 ship    *shipp_hitter = &Ships[objp_hitter->instance];
14321                 ship    *shipp_hit = &Ships[objp_hit->instance];
14322
14323                 if (shipp_hitter->team != shipp_hit->team) {
14324                         return;
14325                 }
14326
14327                 // get the player
14328                 player *pp = &Players[Player_num];
14329
14330                 // wacky stuff here
14331                 if (pp->friendly_hits != 0) {
14332                         float   time_since_last_hit = f2fl(Missiontime - pp->friendly_last_hit_time);
14333                         if ((time_since_last_hit >= 0.0f) && (time_since_last_hit < 10000.0f)) {
14334                                 if (time_since_last_hit > 60.0f) {
14335                                         pp->friendly_hits = 0;
14336                                         pp->friendly_damage = 0.0f;
14337                                 } else if (time_since_last_hit > 2.0f) {
14338                                         pp->friendly_hits -= (int) time_since_last_hit/2;
14339                                         pp->friendly_damage -= time_since_last_hit;
14340                                 }
14341
14342                                 if (pp->friendly_damage < 0.0f) {
14343                                         pp->friendly_damage = 0.0f;
14344                                 }
14345
14346                                 if (pp->friendly_hits < 0) {
14347                                         pp->friendly_hits = 0;
14348                                 }
14349                         }
14350                 }
14351
14352                 float   damage;         //      Damage done by weapon.  Gets scaled down based on size of ship.
14353
14354                 damage = Weapon_info[Weapons[objp_weapon->instance].weapon_info_index].damage;
14355                 
14356                 // wacky stuff here
14357                 ship_info *sip = &Ship_info[Ships[objp_hit->instance].ship_info_index];
14358                 if (sip->initial_hull_strength > 1000.0f) {
14359                         float factor = sip->initial_hull_strength / 1000.0f;
14360                         factor = min(100.0f, factor);
14361                         damage /= factor;
14362                 }
14363
14364                 //      Don't penalize much at all for hitting cargo
14365                 if (sip->flags & (SIF_CARGO | SIF_SENTRYGUN)) {
14366                         damage /= 10.0f;
14367                 }
14368
14369                 //      Hit ship, but not targeting it, so it's not so heinous, maybe an accident.
14370                 if (Ai_info[shipp_hitter->ai_index].target_objnum != OBJ_INDEX(objp_hit)) {
14371                         damage /= 5.0f;
14372                 }
14373
14374                 pp->friendly_last_hit_time = Missiontime;
14375                 pp->friendly_hits++;
14376
14377                 // cap damage and number of hits done this frame
14378                 float accredited_damage = min(MAX_BURST_DAMAGE, pp->damage_this_burst + damage) - pp->damage_this_burst;
14379                 pp->friendly_damage += accredited_damage;
14380                 pp->damage_this_burst += accredited_damage;
14381
14382                 // Done with adjustments to damage.  Evaluate based on current friendly_damage
14383                 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 ));
14384                 
14385                 if (is_instructor(objp_hit)) {
14386                         // it's not nice to hit your instructor
14387                         if (pp->friendly_damage > FRIENDLY_DAMAGE_THRESHOLD) {
14388                                 message_send_builtin_to_player( MESSAGE_INSTRUCTOR_ATTACK, NULL, MESSAGE_PRIORITY_HIGH, MESSAGE_TIME_IMMEDIATE, 0, 0, -1, -1);
14389                                 pp->last_warning_message_time = Missiontime;
14390                                 ship_set_subsystem_strength( Player_ship, SUBSYSTEM_WEAPONS, 0.0f);
14391
14392                                 training_fail();
14393
14394                                 //      Instructor warp out.
14395                                 ai_set_mode_warp_out(objp_hit, &Ai_info[Ships[objp_hit->instance].ai_index]);
14396                                 gameseq_post_event( GS_EVENT_PLAYER_WARPOUT_START_FORCED );     //      Force player to warp out.
14397
14398                                 //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) );
14399                                 //ship_apply_global_damage( objp_hitter, objp_hit, NULL, 1.0f );
14400                         } else if (Missiontime - pp->last_warning_message_time > F1_0*4) {
14401                                 // warning every 4 sec
14402                                 // use NULL as the message sender here since it is the Terran Command persona
14403                                 message_send_builtin_to_player( MESSAGE_INSTRUCTOR_HIT, NULL, MESSAGE_PRIORITY_HIGH, MESSAGE_TIME_IMMEDIATE, 0, 0, -1, -1);
14404                                 pp->last_warning_message_time = Missiontime;
14405                         }
14406
14407                 // not nice to hit your friends
14408                 } else if (pp->friendly_damage > FRIENDLY_DAMAGE_THRESHOLD * (1.0f + (float) (NUM_SKILL_LEVELS + 1 - Game_skill_level)/3.0f)) {
14409                         process_friendly_hit_message( MESSAGE_HAMMER_SWINE, objp_hit );
14410                         mission_goal_fail_all();
14411                         ai_abort_rearm_request( Player_obj );
14412
14413                         Player_ship->team = TEAM_TRAITOR;
14414
14415                 } else if ((damage > frand()) && (Missiontime - pp->last_warning_message_time > F1_0*4) && (pp->friendly_damage > FRIENDLY_DAMAGE_THRESHOLD)) {
14416                         // no closer than 4 sec intervals
14417                         //      Note: (damage > frand()) added on 12/9/97 by MK.  Since damage is now scaled down for big ships, we could get too
14418                         //      many warnings.  Kind of tedious.  frand() returns a value in 0..1, so this won't affect legit hits.
14419                         process_friendly_hit_message( MESSAGE_OOPS, objp_hit );
14420                         pp->last_warning_message_time = Missiontime;
14421                 }
14422         }
14423 }
14424
14425 //      Maybe make ship with ai_info *aip attack hitter_objnum as a dynamic goal
14426 void maybe_set_dynamic_chase(ai_info *aip, int hitter_objnum)
14427 {
14428         Assert(Ship_info[Ships[aip->shipnum].ship_info_index].flags & (SIF_FIGHTER | SIF_BOMBER));
14429
14430         // limit the number of ships attacking hitter_objnum (for now, only if hitter_objnum is player)
14431         if ( ai_maybe_limit_attackers(hitter_objnum) == 1 ) {
14432                 return;
14433         }
14434
14435         // only set as target if can be targeted.
14436         if (awacs_get_level(&Objects[hitter_objnum], &Ships[aip->shipnum], 1) < 1) {
14437                 return;
14438         }
14439
14440         if (aip->target_objnum != hitter_objnum)
14441                 aip->aspect_locked_time = 0.0f;
14442         set_target_objnum(aip, hitter_objnum);
14443         aip->resume_goal_time = Missiontime + i2f(20);  //      Only chase up to 20 seconds.
14444         aip->active_goal = AI_ACTIVE_GOAL_DYNAMIC;
14445
14446         set_targeted_subsys(aip, NULL, -1);             //      Say not attacking any particular subsystem.
14447
14448         aip->previous_submode = aip->mode;
14449         aip->mode = AIM_CHASE;
14450         aip->submode = SM_ATTACK;
14451 }
14452
14453
14454 //      Return true if *objp has armed an aspect seeking bomb.
14455 //      This function written so a ship with an important bomb to fire will willingly take hits in the face to fire its bomb.
14456 int firing_aspect_seeking_bomb(object *objp)
14457 {
14458         ship    *shipp;
14459         int     bank_index;
14460         ship_weapon     *swp;
14461
14462         shipp = &Ships[objp->instance];
14463
14464         swp = &shipp->weapons;
14465
14466         bank_index = swp->current_secondary_bank;
14467
14468         if (bank_index != -1)
14469                 if (swp->secondary_bank_ammo[bank_index] > 0) {
14470                         if (Weapon_info[swp->secondary_bank_weapons[bank_index]].wi_flags & WIF_BOMB) {
14471                                 if (Weapon_info[swp->secondary_bank_weapons[bank_index]].wi_flags & WIF_HOMING_ASPECT) {
14472                                         return 1;
14473                                 }
14474                         }
14475                 }
14476
14477         return 0;
14478 }
14479
14480 //      *objp collided with big ship *big_objp at global point *collide_pos
14481 //      Make it fly away from the collision point.
14482 // collision_normal is NULL, when a collision is imminent and we just want to bug out.
14483 void big_ship_collide_recover_start(object *objp, object *big_objp, vector *collide_pos, vector *collision_normal)
14484 {
14485         ai_info *aip;
14486
14487         Assert(objp->type == OBJ_SHIP);
14488
14489         aip = &Ai_info[Ships[objp->instance].ai_index];
14490
14491         if (!timestamp_elapsed(aip->big_recover_timestamp) && (aip->ai_flags & AIF_BIG_SHIP_COLLIDE_RECOVER_1))
14492                 return;
14493
14494         //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)));
14495         if (collision_normal) {
14496                 aip->big_recover_timestamp = timestamp(2000);
14497                 aip->big_collision_normal = *collision_normal;
14498         //      nprintf(("AI", " normal\n"));
14499         } else {
14500                 aip->big_recover_timestamp = timestamp(500);
14501         //      nprintf(("AI", " no normal\n"));
14502         }
14503
14504
14505         aip->ai_flags &= ~AIF_BIG_SHIP_COLLIDE_RECOVER_2;
14506         aip->ai_flags |= AIF_BIG_SHIP_COLLIDE_RECOVER_1;
14507
14508 //      vector  out_vec;
14509 //      vm_vec_normalized_dir(&out_vec, &objp->pos, collide_pos);
14510
14511         // big_recover_pos_1 is 100 m out along normal
14512         vector direction;
14513         if (collision_normal) {
14514                 direction = *collision_normal;
14515         } else {
14516                 vm_vec_copy_scale(&direction, &objp->orient.fvec, -1.0f);
14517         }
14518         vm_vec_scale_add(&aip->big_recover_pos_1, &objp->pos, &direction, 100.0f);
14519
14520         // go out 200 m from box closest box point
14521         get_world_closest_box_point_with_delta(&aip->big_recover_pos_2, big_objp, &aip->big_recover_pos_1, NULL, 300.0f);
14522
14523         accelerate_ship(aip, 0.0f);
14524 /*
14525         if (vm_vec_dot(collision_normal, &objp->orient.fvec) > 0.5f) {
14526 //              vm_vec_scale_add(&aip->big_recover_pos_1, &objp->pos, &out_vec, big_objp->radius/2.0f);
14527 //              vm_vec_scale_add(&aip->big_recover_pos_2, &aip->big_recover_pos_1, &objp->orient.uvec, big_objp->radius/2.0f);
14528 //              vm_vec_scale_add(&aip->big_recover_pos_2, &objp->pos, &out_vec, big_objp->radius*2.0f);
14529                 accelerate_ship(aip, 2.0f);
14530         } else {
14531 //              vm_vec_scale_add(&aip->big_recover_pos_1, &objp->pos, &out_vec, big_objp->radius/2.0f);
14532 //              vm_vec_scale_add(&aip->big_recover_pos_2, &aip->big_recover_pos_1, &objp->orient.uvec, big_objp->radius/2.0f);
14533                 accelerate_ship(aip, 0.0f);
14534         } */
14535 }
14536
14537 float max_lethality = 0.0f;
14538
14539 void ai_update_lethality(object *ship_obj, object *other_obj, float damage)
14540 {
14541         Assert(ship_obj->type == OBJ_SHIP);
14542         Assert(other_obj->type == OBJ_WEAPON || other_obj->type == OBJ_SHOCKWAVE);
14543         int dont_count = FALSE;
14544
14545         int parent = other_obj->parent;
14546         if (Objects[parent].type == OBJ_SHIP) {
14547                 if (Objects[parent].signature == other_obj->parent_sig) {
14548
14549                         // check damage done to enemy team
14550                         if (Ships[ship_obj->instance].team != Ships[Objects[parent].instance].team) {
14551
14552                                 // other is weapon
14553                                 if (other_obj->type == OBJ_WEAPON) {
14554                                         weapon *wp = &Weapons[other_obj->instance];
14555                                         weapon_info *wif = &Weapon_info[wp->weapon_info_index];
14556
14557                                         // if parent is BIG|HUGE, don't count beam
14558                                         if (Ship_info[Ships[Objects[parent].instance].ship_info_index].flags & (SIF_BIG_SHIP|SIF_HUGE_SHIP)) {
14559                                                 if (wif->wi_flags & WIF_BEAM) {
14560                                                         dont_count = TRUE;
14561                                                 }
14562                                         }
14563                                 }
14564
14565                                 if (!dont_count) {
14566                                         float lethality = 0.025f * damage;      // 2 cyclops (@2000) put you at 100 lethality
14567
14568                                         // increase lethality weapon's parent ship
14569                                         ai_info *aip = &Ai_info[Ships[Objects[parent].instance].ai_index];
14570                                         aip->lethality += lethality;
14571                                         aip->lethality = min(110.0f, aip->lethality);
14572                                         // if you hit, don;t be less than 0
14573                                         aip->lethality = max(0.0f, aip->lethality);
14574
14575 //                                      if (aip->lethality > max_lethality) {
14576 //                                              max_lethality = aip->lethality;
14577 //                                              mprintf(("new lethalilty high: %.1f\n", max_lethality));
14578 //                                      }
14579
14580                                         // if parent is player, show his lethality
14581 //                                      if (Objects[parent].flags & OF_PLAYER_SHIP) {
14582 //                                              mprintf(("Player lethality: %.1f\n", aip->lethality));
14583 //                                      }
14584                                 }
14585                         }
14586                 }
14587         }
14588 }
14589
14590
14591 //      Object *objp_ship was hit by either weapon *objp_weapon or collided into by ship hit_objp at point *hitpos.
14592 void ai_ship_hit(object *objp_ship, object *hit_objp, vector *hitpos, int shield_quadrant, vector *hit_normal)
14593 {
14594         int             hitter_objnum = -2;
14595         object  *objp_hitter = NULL;
14596         ship            *shipp;
14597         ai_info *aip, *hitter_aip;
14598
14599         shipp = &Ships[objp_ship->instance];
14600         aip = &Ai_info[shipp->ai_index];
14601
14602         if (objp_ship->flags & OF_PLAYER_SHIP)
14603                 return;
14604
14605         if ((aip->mode == AIM_WARP_OUT) || (aip->mode == AIM_PLAY_DEAD))
14606                 return;
14607
14608         if (hit_objp->type == OBJ_SHIP) {
14609                 //      If the object that this ship collided with is a big ship
14610                 if (Ship_info[Ships[hit_objp->instance].ship_info_index].flags & (SIF_BIG_SHIP | SIF_HUGE_SHIP)) {
14611                         //      And the current object is _not_ a big ship
14612                         if (!(Ship_info[Ships[objp_ship->instance].ship_info_index].flags & (SIF_BIG_SHIP | SIF_HUGE_SHIP))) {
14613                                 //      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.
14614                                 big_ship_collide_recover_start(objp_ship, hit_objp, hitpos, hit_normal);
14615                         }
14616                 }
14617         }
14618
14619         if (hit_objp->type == OBJ_WEAPON) {
14620                 //      Make sure the object that fired this weapon is still alive.  If not, abort.
14621                 // Assert(hit_objp->parent >= 0);
14622                 if(hit_objp->parent < 0){
14623                         return;
14624                 }
14625                 if ( hit_objp->parent_sig != Objects[hit_objp->parent].signature ){
14626                         return;
14627                 }
14628
14629                 //      Hit by a protected ship, don't attack it.
14630                 if (Objects[hit_objp->parent].flags & OF_PROTECTED) {
14631                         if ((Ship_info[shipp->ship_info_index].flags & (SIF_FIGHTER | SIF_BOMBER)) && (aip->target_objnum == -1)) {
14632                                 if (aip->mode == AIM_CHASE) {
14633                                         if (aip->submode != SM_EVADE_WEAPON) {
14634                                                 aip->mode = AIM_CHASE;
14635                                                 aip->submode = SM_EVADE_WEAPON;
14636                                                 aip->submode_start_time = Missiontime;
14637                                         }
14638                                 } else if (aip->mode != AIM_EVADE_WEAPON) {
14639                                         aip->active_goal = AI_ACTIVE_GOAL_DYNAMIC;
14640                                         aip->previous_mode = aip->mode;
14641                                         aip->previous_submode = aip->submode;
14642                                         aip->mode = AIM_EVADE_WEAPON;
14643                                         aip->submode = -1;
14644                                         aip->submode_start_time = Missiontime;
14645                                         aip->mode_time = timestamp(MAX_EVADE_TIME);     //      Evade for up to five seconds.
14646                                 }
14647
14648                         }
14649                         return;
14650                 }
14651
14652                 hitter_objnum = hit_objp->parent;
14653                 Assert((hitter_objnum >= 0) && (hitter_objnum < MAX_OBJECTS));
14654                 objp_hitter = &Objects[hitter_objnum];
14655                 maybe_process_friendly_hit(objp_hitter, objp_ship, hit_objp);           //      Deal with player's friendly fire.
14656
14657                 if ( (shipp->team & TEAM_FRIENDLY) && !(Game_mode & GM_MULTIPLAYER) ) {
14658                         ship_maybe_ask_for_help(shipp);
14659                 }
14660         } else if (hit_objp->type == OBJ_SHIP) {
14661                 if (shipp->team == Ships[hit_objp->instance].team)              //      Don't have AI react to collisions between teammates.
14662                         return;
14663                 objp_hitter = hit_objp;
14664                 hitter_objnum = hit_objp-Objects;
14665         } else {
14666                 Int3(); //      Hmm, what kind of object hit this if not weapon or ship?  Get MikeK.
14667                 return;
14668         }
14669
14670         //      Collided into a protected ship, don't attack it.
14671         if (hit_objp->flags & OF_PROTECTED)
14672                 return;
14673
14674         Assert(objp_hitter != NULL);
14675         hitter_aip = &Ai_info[Ships[objp_hitter->instance].ai_index];
14676         hitter_aip->last_hit_target_time = Missiontime;
14677         
14678         // store the object signature of objp_ship into ai_info, since we want to track the last ship hit by 'hitter_objnum'
14679         hitter_aip->last_objsig_hit = objp_ship->signature; 
14680
14681         aip->last_hit_time = Missiontime;
14682
14683         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
14684                 return;
14685
14686         //      If this ship is awaiting repair, abort!
14687         if (aip->ai_flags & (AIF_AWAITING_REPAIR | AIF_BEING_REPAIRED)) {
14688                 ship_info       *sip = &Ship_info[shipp->ship_info_index];
14689
14690                 if (objp_ship->hull_strength/sip->initial_hull_strength < 0.3f) {
14691                         //      No, only abort if hull below a certain level.
14692                         aip->next_rearm_request_timestamp = timestamp(NEXT_REARM_TIMESTAMP/2);  //      Might request again after 15 seconds.
14693                         if ( !(objp_ship->flags & OF_PLAYER_SHIP) )                                             // mwa -- don't abort rearm for a player
14694                                 ai_abort_rearm_request(objp_ship);
14695                 }
14696         }
14697
14698         //      If firing a bomb, ignore enemy fire so we can gain lock drop the bomb.
14699         //      Only ignore fire if aspect_locked_time > 0.5f, as this means we're in range.
14700         if (firing_aspect_seeking_bomb(objp_ship)) {
14701                 if ((aip->ai_flags & AIF_SEEK_LOCK) && (aip->aspect_locked_time > 0.1f))
14702                         return;
14703         }
14704
14705         //      If in AIM_STRAFE mode and got hit by target, maybe attack turret if appropriate
14706         if (aip->mode == AIM_STRAFE) {
14707                 Assert(hitter_objnum != -2);
14708                 if (aip->target_objnum == hitter_objnum) {
14709                         if ( hit_objp->type == OBJ_WEAPON ) {
14710                                 ai_big_strafe_maybe_attack_turret(objp_ship, hit_objp);
14711                         }
14712                         return;
14713                 }
14714                 else {
14715                                 // AL 11-10-97:
14716                         ;       // do nothing here, we'll attack this hitter if it is a fighter or bomber (this is handled
14717                                 // in code later in this function
14718                 }
14719         }
14720
14721         if (objp_ship == Player_obj)
14722                 return;         //      We don't do AI for the player.
14723
14724         maybe_update_guard_object(objp_ship, objp_hitter);
14725
14726         //      Big ships don't go any further.
14727         if (!(Ship_info[shipp->ship_info_index].flags & SIF_SMALL_SHIP))
14728                 return;
14729
14730         //      If the hitter object is the ignore object, don't attack it.
14731         ship_info       *sip = &Ship_info[shipp->ship_info_index];
14732         if ((is_ignore_object(aip, objp_hitter-Objects)) && (sip->flags & (SIF_BOMBER | SIF_FIGHTER))) {
14733                 if (aip->mode == AIM_NONE) {
14734                         aip->mode = AIM_CHASE;  //      This will cause the ship to move, if not attack.
14735                         aip->submode = SM_EVADE;
14736                 }
14737                 return;
14738         }
14739
14740         //      Maybe abort based on mode.
14741         switch (aip->mode) {
14742         case AIM_CHASE:
14743                 if (aip->submode == SM_ATTACK_FOREVER)
14744                         return;
14745
14746                 if ( hit_objp->type == OBJ_WEAPON ) {
14747                         if ( ai_big_maybe_enter_strafe_mode(objp_ship, OBJ_INDEX(hit_objp), 1) )
14748                                 return;
14749                 }
14750
14751         case AIM_GUARD:
14752                 //      If in guard mode and far away from guard object, don't pursue guy that hit me.
14753                         if ((aip->guard_objnum != -1) && (aip->guard_signature == Objects[aip->guard_objnum].signature)) {
14754                                 if (vm_vec_dist_quick(&objp_ship->pos, &Objects[aip->guard_objnum].pos) > 500.0f) {
14755                                         return;
14756                                 }
14757                         }
14758         case AIM_STILL:
14759         case AIM_STAY_NEAR:
14760                 // Note: Dealt with above, at very top.  case AIM_PLAY_DEAD:
14761         case AIM_STRAFE:
14762                 break;
14763         case AIM_EVADE_WEAPON:
14764         case AIM_EVADE:
14765         case AIM_GET_BEHIND:
14766         case AIM_AVOID:
14767         case AIM_DOCK:
14768         case AIM_BIGSHIP:
14769         case AIM_PATH:
14770         case AIM_NONE:
14771         case AIM_BAY_DEPART:
14772         case AIM_SENTRYGUN:
14773                 return;
14774         case AIM_BAY_EMERGE:
14775                 // If just leaving the docking bay, don't react to enemy fire... just keep flying away from docking bay
14776                 if ( (Missiontime - aip->submode_start_time) < 5*F1_0 ) {
14777                         return;
14778                 }
14779                 break;
14780         case AIM_WAYPOINTS:
14781                 if (sip->flags & (SIF_FIGHTER | SIF_BOMBER))
14782                         break;
14783                 else
14784                         return;
14785                 break;
14786         case AIM_SAFETY:
14787                 if ((aip->submode != AISS_1) || (Missiontime - aip->submode_start_time > i2f(1))) {
14788                         aip->submode = AISS_1;
14789                         aip->submode_start_time = Missiontime;
14790                 }
14791                 return;
14792                 break;
14793         case AIM_WARP_OUT:
14794                 return;
14795                 break;
14796         default:
14797                 Int3(); //      Bogus mode!
14798         }
14799
14800         if (timestamp_elapsed(aip->ok_to_target_timestamp))
14801                 aip->ai_flags &= ~AIF_FORMATION;                        //      If flying in formation, bug out!
14802
14803         aip->hitter_objnum = hitter_objnum;
14804         aip->hitter_signature = Objects[hitter_objnum].signature;
14805
14806         //      If the hitter is not on the same team as the hittee, do some stuff.
14807         if (shipp->team != Ships[objp_hitter->instance].team) {
14808                 //nprintf(("AI", "Object %i attacking %i, who just hit him!\n", objp_ship-Objects, hitter_objnum));
14809
14810                 if ((hitter_objnum != aip->target_objnum) && (sip->flags & (SIF_FIGHTER | SIF_BOMBER))) {
14811                         maybe_set_dynamic_chase(aip, hitter_objnum);
14812                         maybe_afterburner_after_ship_hit(objp_ship, aip, &Objects[hitter_objnum]);
14813                 } else {
14814                         if ((aip->mode == AIM_CHASE) && ((objp_ship->hull_strength/sip->initial_hull_strength > 0.9f) || (get_shield_strength(objp_ship)/sip->shields > 0.8f))) {
14815                                 switch (aip->submode) {
14816                                 case SM_ATTACK:
14817                                 case SM_SUPER_ATTACK:
14818                                 case SM_GET_AWAY:
14819                                         break;
14820                                 default:
14821                                         if (sip->flags & (SIF_FIGHTER | SIF_BOMBER)) {
14822                                                 maybe_set_dynamic_chase(aip, hitter_objnum);
14823                                         }
14824                                         maybe_afterburner_after_ship_hit(objp_ship, aip, &Objects[hitter_objnum]);
14825                                         break;
14826                                 }
14827                         } else if (aip->mode == AIM_CHASE) {
14828                                 switch (aip->submode) {
14829                                 case SM_ATTACK:
14830                                         aip->submode = SM_EVADE;
14831                                         aip->submode_start_time = Missiontime;
14832                                         break;
14833                                 case SM_SUPER_ATTACK:
14834                                         if (Missiontime - aip->submode_start_time > i2f(1)) {
14835                                                 aip->submode = SM_EVADE;
14836                                                 aip->submode_start_time = Missiontime;
14837                                         }
14838                                         break;
14839                                 case SM_EVADE_BRAKE:
14840                                         break;
14841                                 case SM_EVADE_SQUIGGLE:
14842                                         aip->submode = SM_EVADE;
14843                                         aip->submode_start_time = Missiontime;
14844                                         break;
14845                                 default:
14846                                         if (sip->flags & (SIF_BOMBER | SIF_FIGHTER)) {
14847                                                 maybe_set_dynamic_chase(aip, hitter_objnum);
14848                                                 maybe_afterburner_after_ship_hit(objp_ship, aip, &Objects[hitter_objnum]);
14849                                         }
14850
14851                                         break;
14852                                 }
14853                         } else {
14854                                 // AL 3-15-98: Prevent escape pods from entering chase mode
14855                                 if ( (sip->flags & (SIF_BOMBER | SIF_FIGHTER)) ) {
14856                                         maybe_set_dynamic_chase(aip, hitter_objnum);
14857                                 }
14858                                 maybe_afterburner_after_ship_hit(objp_ship, aip, &Objects[hitter_objnum]);
14859                         }
14860                 }
14861         }
14862 }
14863
14864 //      Ship shipnum has been destroyed.
14865 //      Cleanup.
14866 // the parameter 'method' is used to tell is this ship was destroyed or it departed normally.
14867 // This function will get called in either case, and there are things that should be done if
14868 // the ship actually gets destroyed which shouldn't get done if it departed.
14869 void ai_ship_destroy(int shipnum, int method)
14870 {
14871         int             objnum;
14872         object  *other_objp;
14873         ship            *shipp;
14874         ship_obj        *so;
14875         ai_info *dead_aip;
14876
14877         Assert((shipnum >= 0) && (shipnum < MAX_SHIPS));
14878         objnum = Ships[shipnum].objnum;
14879         dead_aip = &Ai_info[Ships[shipnum].ai_index];
14880
14881         // if I was getting repaired, or awaiting repair, then cleanup the repair mode.  When awaiting repair, the dock_objnum
14882         // is -1.  When the support ship is on the way, the dock_objnum >= 0 (points to support ship).
14883         if ( dead_aip->ai_flags & (AIF_AWAITING_REPAIR | AIF_BEING_REPAIRED) ) {
14884                 if ( dead_aip->dock_objnum >= 0 )
14885                         ai_do_objects_repairing_stuff( &Objects[objnum], &Objects[dead_aip->dock_objnum], REPAIR_INFO_END);
14886                 else
14887                         ai_do_objects_repairing_stuff( &Objects[objnum], NULL, REPAIR_INFO_END );
14888         }
14889
14890         //      For all objects that had this ship as a target, wipe it out, forcing find of a new enemy.
14891         for ( so = GET_FIRST(&Ship_obj_list); so != END_OF_LIST(&Ship_obj_list); so = GET_NEXT(so) ) {
14892                 other_objp = &Objects[so->objnum];
14893                 Assert(other_objp->instance != -1);
14894
14895                 shipp = &Ships[other_objp->instance];
14896                 Assert(shipp->ai_index != -1);
14897
14898                 ai_info *aip = &Ai_info[shipp->ai_index];
14899
14900                 // MWA 2/11/98
14901                 // code commented out below is taken care of in ai_cleanup_dock_mode when gets called when the
14902                 // support ship starts it's death roll.
14903
14904                 //      If the destroyed ship was on its way to repair the current ship
14905                 if (aip->dock_objnum == objnum) {
14906
14907                         // clean up the flags for any kind of docking mode.  If aip was part of a goal of dock/undock
14908                         // then it will get cleaned up by the goal code.
14909                         ai_do_objects_undocked_stuff( other_objp, NULL );
14910
14911                         if ( aip->ai_flags & (AIF_AWAITING_REPAIR | AIF_BEING_REPAIRED) ) {
14912                                 int abort_reason;
14913                                 if ( method == SEF_DEPARTED ) {
14914                                         abort_reason = REPAIR_INFO_ABORT;
14915                                 } else {
14916                                         abort_reason = REPAIR_INFO_KILLED;
14917                                 }
14918                                 ai_do_objects_repairing_stuff( other_objp, NULL, abort_reason );
14919                         }
14920                 }
14921
14922                 if (aip->target_objnum == objnum) {
14923                         set_target_objnum(aip, -1);
14924                         //      If this ship had a dynamic goal of chasing the dead ship, clear the dynamic goal.
14925                         if (aip->resume_goal_time != -1)
14926                                 aip->active_goal = AI_GOAL_NONE;
14927                 }
14928
14929                 if (aip->goal_objnum == objnum) {
14930                         aip->goal_objnum = -1;
14931                         aip->goal_signature = -1;
14932                 }
14933
14934                 if (aip->guard_objnum == objnum) {
14935                         aip->guard_objnum = -1;
14936                         aip->guard_signature = -1;
14937                 }
14938
14939                 if ((aip->guard_wingnum != -1) && (aip->guard_wingnum == Ai_info[Ships[Objects[objnum].instance].ai_index].wing)) {
14940                         if (aip->guard_wingnum != aip->wing)
14941                                 ai_set_guard_wing(other_objp, aip->guard_wingnum);
14942                 }
14943
14944                 if (aip->hitter_objnum == objnum)
14945                         aip->hitter_objnum = -1;
14946
14947         }
14948
14949 }
14950
14951 /*
14952 //      Interface function to goals code.
14953 //      Make object *objp fly to point *vp and warp out.
14954 void ai_warp_out(object *objp, vector *vp)
14955 {
14956         ai_info *aip;
14957
14958         aip = &Ai_info[Ships[objp->instance].ai_index];
14959
14960         if (aip->mode != AIM_WARP_OUT) {
14961                 ai_set_mode_warp_out(objp, aip);
14962         }
14963         float   dist;
14964         float   dot;
14965         vector  v2v;
14966         ai_info *aip;
14967
14968         dist = vm_vec_normalized_dir(&v2v, vp, &objp->pos);
14969
14970         if (dist < objp->radius + 5.0f) {
14971
14972                 // Start the warp out effect 
14973                 shipfx_warpout_start(objp);
14974
14975         } else {
14976                 dot = vm_vec_dot(&objp->orient.fvec, &v2v);
14977
14978                 aip = &Ai_info[Ships[objp->instance].ai_index];
14979
14980                 if (dist > 500.0f)
14981                         accelerate_ship(aip, 1.0f);
14982                 else
14983                         accelerate_ship(aip, (3*dot + 1.0f)/4.0f);
14984
14985                 turn_towards_point(objp, vp, NULL, 0.0f);
14986         }
14987 }
14988 */
14989
14990
14991 //      Do stuff at start of deathroll.
14992 void ai_deathroll_start(object *ship_obj)
14993 {
14994         ai_info *aip;
14995         ship            *shipp, *other_ship;
14996
14997         shipp = &Ships[ship_obj->instance];
14998         aip = &Ai_info[shipp->ai_index];
14999
15000         // mark object we are docked with so we can do damage and separate during deathroll
15001         // keep dock_objnum_when_dead from being changed if already set (only allow to be set when -1)
15002         if (Ships[ship_obj->instance].dock_objnum_when_dead == -1) {
15003                 Ships[ship_obj->instance].dock_objnum_when_dead = aip->dock_objnum;
15004                 // set other_ship dock_objnum_when_dead, if other_ship exits.
15005                 if (Ships[ship_obj->instance].dock_objnum_when_dead != -1) {
15006                         other_ship = &Ships[Objects[aip->dock_objnum].instance];
15007                         other_ship->dock_objnum_when_dead = shipp->objnum;
15008                 }
15009         }
15010
15011         ai_cleanup_dock_mode(aip, shipp);
15012
15013         aip->mode = AIM_NONE;
15014 }
15015
15016 //      Object *requester_objp tells rearm ship to abort rearm.
15017 //      Returns true if it succeeded, else false.
15018 //      To succeed means you were previously rearming.
15019 int ai_abort_rearm_request(object *requester_objp)
15020 {
15021         ship            *requester_shipp;
15022         ai_info *requester_aip;
15023
15024         Assert(requester_objp->type == OBJ_SHIP);
15025         if(requester_objp->type != OBJ_SHIP){
15026                 return 0;
15027         }
15028         Assert((requester_objp->instance >= 0) && (requester_objp->instance < MAX_SHIPS));      
15029         if((requester_objp->instance < 0) || (requester_objp->instance >= MAX_SHIPS)){
15030                 return 0;
15031         }
15032         requester_shipp = &Ships[requester_objp->instance];
15033         Assert((requester_shipp->ai_index >= 0) && (requester_shipp->ai_index < MAX_AI_INFO));          
15034         if((requester_shipp->ai_index < 0) || (requester_shipp->ai_index >= MAX_AI_INFO)){
15035                 return 0;
15036         }       
15037         requester_aip = &Ai_info[requester_shipp->ai_index];
15038         
15039         if (requester_aip->ai_flags & (AIF_AWAITING_REPAIR | AIF_BEING_REPAIRED)){
15040
15041                 // dock_objnum is always valid once a rearm repair has been requested.  It points to the
15042                 // ship that is coming to repair me.
15043                 if (requester_aip->dock_objnum != -1) {
15044                         object  *repair_objp;
15045                         ai_info *repair_aip;
15046
15047                         repair_objp = &Objects[requester_aip->dock_objnum];
15048                         repair_aip = &Ai_info[Ships[repair_objp->instance].ai_index];
15049
15050                         //      Make sure signatures match.  This prevents nasty bugs in which an object
15051                         //      that was repairing another is destroyed and is replaced by another ship
15052                         //      before this code comes around.
15053                         if (repair_objp->signature == requester_aip->dock_signature) {
15054
15055                                 Assert( repair_objp->type == OBJ_SHIP );
15056
15057                                 // if support ship is in the process of undocking, don't do anything.
15058                                 if ( repair_aip->submode < AIS_UNDOCK_0 ) {
15059                                         ai_do_objects_repairing_stuff( requester_objp, repair_objp, REPAIR_INFO_ABORT );
15060
15061                                         if ( repair_aip->submode == AIS_DOCK_4 )
15062                                                 repair_aip->submode = AIS_UNDOCK_0;
15063                                         else
15064                                                 repair_aip->submode = AIS_UNDOCK_3;
15065
15066                                         repair_aip->submode_start_time = Missiontime;
15067                                 } else {
15068                                         nprintf(("AI", "Not aborting rearm since already undocking\n"));
15069                                 }
15070                         }
15071                 } else {
15072                         // setting these flags is the safe things to do.  There may not be a corresponding repair
15073                         // ship for this guys since a repair ship may be currently repairing someone else.
15074                         ai_do_objects_repairing_stuff( requester_objp, NULL, REPAIR_INFO_ABORT );
15075
15076                         // try and remove this guy from an arriving support ship.
15077                         mission_remove_scheduled_repair(requester_objp);
15078                 }
15079
15080                 return 1;
15081         } else if ( requester_aip->ai_flags & AIF_REPAIRING ) {
15082                 // a support ship can request to abort when he is told to do something else (like warp out).
15083                 // see if this support ships goal_objnum is valid.  If so, then issue this ai_abort comment
15084                 // for the ship that he is enroute to repair
15085                 if ( requester_aip->goal_objnum != -1 ) {
15086                         int val;
15087
15088                         val = ai_abort_rearm_request( &Objects[requester_aip->goal_objnum] );
15089                         return val;
15090                 }
15091         }
15092
15093         return 0;
15094 }
15095
15096 // function which gets called from ai-issue_rearm_request and from code in missionparse.cpp
15097 // to actually issue the rearm goal (support_obj to rearm requester_obj);
15098 void ai_add_rearm_goal( object *requester_objp, object *support_objp )
15099 {
15100         ship *support_shipp, *requester_shipp;
15101         ai_info *support_aip, *requester_aip;
15102
15103         support_shipp = &Ships[support_objp->instance];
15104         requester_shipp = &Ships[requester_objp->instance];
15105         requester_aip = &Ai_info[requester_shipp->ai_index];
15106
15107         Assert( support_shipp->ai_index != -1 );
15108         support_aip = &Ai_info[support_shipp->ai_index];
15109
15110         // if the requester is a player object, issue the order as the squadmate messaging code does.  Doing so
15111         // ensures that the player get a higher priority!
15112         requester_aip->ai_flags |= AIF_AWAITING_REPAIR; //      Tell that I'm awaiting repair.
15113         if ( requester_objp->flags & OF_PLAYER_SHIP )
15114                 ai_add_ship_goal_player( AIG_TYPE_PLAYER_SHIP, AI_GOAL_REARM_REPAIR, -1, requester_shipp->ship_name, support_aip );
15115         else
15116                 ai_add_goal_ship_internal( support_aip, AI_GOAL_REARM_REPAIR, requester_shipp->ship_name, -1, -1 );
15117
15118 }
15119
15120 //      Object *requester_objp requests rearming.
15121 //      Returns objnum of ship coming to repair requester on success
15122 //      Success means you found someone to rearm you and you weren't previously rearming.
15123 int ai_issue_rearm_request(object *requester_objp)
15124 {
15125         object  *objp;
15126         ship            *requester_shipp;
15127         ai_info *requester_aip;
15128
15129         Assert(requester_objp->type == OBJ_SHIP);
15130         Assert((requester_objp->instance >= 0) && (requester_objp->instance < MAX_SHIPS));
15131         requester_shipp = &Ships[requester_objp->instance];
15132         Assert((requester_shipp->ai_index >= 0) && (requester_shipp->ai_index < MAX_AI_INFO));
15133         requester_aip = &Ai_info[requester_shipp->ai_index];
15134         
15135         //      Make sure not already awaiting repair.
15136         if (requester_aip->ai_flags & AIF_AWAITING_REPAIR) {
15137                 nprintf(("AI", "Ship %s already awaiting rearm by ship %s.\n", requester_shipp->ship_name, &Ships[Objects[requester_aip->dock_objnum].instance].ship_name));    
15138                 return -1;
15139         }
15140
15141         if ( !is_support_allowed(requester_objp) )
15142                 return -1;
15143
15144         //nprintf(("AI", "Ship %s requesting rearming.\n", requester_shipp->ship_name));
15145         requester_aip->next_rearm_request_timestamp = timestamp(NEXT_REARM_TIMESTAMP);  //      Might request again after this much time.
15146
15147         // call ship_find_repair_ship to get a support ship.  If none is found, then we will warp one in.  This
15148         // function will return the next available ship which can repair requester
15149         objp = ship_find_repair_ship( requester_objp );
15150         ai_do_objects_repairing_stuff( requester_objp, objp, REPAIR_INFO_QUEUE );
15151         if ( objp ) {
15152
15153                 // MWA 5/14/98 -- moved next item into the ai_do_objects_repairing_stuff function so that clients
15154                 // would properly update their hud support view
15155                 //ai_add_rearm_goal( requester_objp, objp );
15156                 return OBJ_INDEX(objp);
15157
15158         } else {
15159                 // call to warp in repair ship!!!!  for now, warp in any number of ships needed.  Should cap it to
15160                 // some reasonable max (or let support ships warp out).  We should assume here that ship_find_repair_ship()
15161                 // would have returned a valid object if there are too many support ships already in the mission
15162                 mission_warp_in_support_ship( requester_objp );
15163
15164                 return -1;
15165         }
15166
15167 }
15168
15169 // make objp rearm and repair goal_objp
15170 void ai_rearm_repair( object *objp, object  *goal_objp, int priority, int docker_index, int dockee_index )
15171 {
15172         ai_info *aip, *goal_aip;
15173
15174         aip = &Ai_info[Ships[objp->instance].ai_index];
15175         aip->goal_objnum = goal_objp-Objects;
15176
15177         // nprintf(("AI", "Ship %s preparing to rearm ship %s.\n", shipp->ship_name, requester_shipp->ship_name));
15178
15179         ai_dock_with_object(objp, goal_objp, priority, AIDO_DOCK, docker_index, dockee_index);
15180         aip->ai_flags |= AIF_REPAIRING;                                         //      Tell that repair guy is busy trying to repair someone.
15181
15182         goal_aip = &Ai_info[Ships[goal_objp->instance].ai_index];
15183         goal_aip->dock_objnum = objp-Objects;           //      Tell which object is coming to repair.
15184         goal_aip->dock_signature = objp->signature;
15185
15186         ai_do_objects_repairing_stuff( goal_objp, objp, REPAIR_INFO_ONWAY );
15187
15188         goal_aip->abort_rearm_timestamp = timestamp(NEXT_REARM_TIMESTAMP*3/2);
15189 }
15190
15191 // Given a dockee object and the index of the dockbay for that object (ie the dockbay index
15192 // into polymodel->dockbays[] for the model associated with the object), return the index
15193 // of a path_num associated with than dockbay (this is an index into polymodel->paths[])
15194 int ai_return_path_num_from_dockbay(object *dockee_objp, int dockbay_index)
15195 {
15196         if ( dockbay_index < 0 || dockee_objp == NULL ) {
15197                 Int3();         // should never happen
15198                 return -1;
15199         }
15200
15201         if ( dockee_objp->type == OBJ_SHIP ) {
15202                 int                     path_num;
15203                 polymodel       *pm;
15204
15205                 pm = model_get( Ships[dockee_objp->instance].modelnum );
15206
15207                 // sanity checks
15208                 Assert(pm->n_docks > dockbay_index);
15209                 Assert(pm->docking_bays[dockbay_index].num_spline_paths > 0);
15210                 Assert(pm->docking_bays[dockbay_index].splines != NULL);
15211                 if(pm->n_docks <= dockbay_index){
15212                         return -1;
15213                 }
15214                 if(pm->docking_bays[dockbay_index].num_spline_paths <= 0){
15215                         return -1;
15216                 }
15217                 if(pm->docking_bays[dockbay_index].splines == NULL){
15218                         return -1;
15219                 }
15220
15221                 // We only need to return one path for the dockbay, so return the first
15222                 path_num = pm->docking_bays[dockbay_index].splines[0];
15223                 return path_num;
15224         } else {
15225                 return -1;
15226         }
15227 }
15228
15229 //      Actually go ahead and fire the synaptics.
15230 void cheat_fire_synaptic(object *objp, ship *shipp, ai_info *aip)
15231 {
15232         ship_weapon     *swp;
15233         swp = &shipp->weapons;
15234         int     current_bank = swp->current_secondary_bank;
15235
15236         ai_select_secondary_weapon(objp, swp, WIF_SPAWN, 0);
15237         if (timestamp_elapsed(swp->next_secondary_fire_stamp[current_bank])) {
15238                 if (ship_fire_secondary(objp)) {
15239                         nprintf(("AI", "ship %s cheat fired synaptic!\n", shipp->ship_name));
15240                         swp->next_secondary_fire_stamp[current_bank] = timestamp(2500);
15241                 }
15242         }
15243 }
15244
15245 //      For the subspace mission (sm3-09a)
15246 //              for delta wing
15247 //                      if they're sufficiently far into the mission
15248 //                              if they're near one or more enemies
15249 //                                      every so often
15250 //                                              fire a synaptic if they have one.
15251 void maybe_cheat_fire_synaptic(object *objp, ai_info *aip)
15252 {
15253         //      Only do in subspace missions.
15254         if ( The_mission.flags & MISSION_FLAG_SUBSPACE )        {
15255                 ship    *shipp;
15256                 int     num, time;
15257
15258                 shipp = &Ships[objp->instance];
15259
15260                 if (!(strnicmp(shipp->ship_name, NOX("delta"), 5))) {
15261                         num = shipp->ship_name[6] - '1';
15262
15263                         if ((num >= 0) && (num <= 3)) {
15264                                 time = Missiontime >> 16;       //      Convert to seconds.
15265
15266                                 time -= 2*60;   //      Subtract off two minutes.
15267
15268                                 if (time > 0) {
15269                                         int modulus = 17 + num*3;
15270
15271                                         if ((time % modulus) < 2) {
15272                                                 int count = num_nearby_fighters(get_enemy_team_mask(OBJ_INDEX(objp)), &objp->pos, 1500.0f);
15273
15274                                                 if (count > 0) {
15275                                                         cheat_fire_synaptic(objp, shipp, aip);
15276                                                 }
15277                                         }
15278                                 }
15279                         }
15280                 }
15281         }
15282
15283 }
15284